Something went wrong. Try again.
Monorepo for the nibling.run platform
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598/* bomb.sh-style neon + deep black + minimal restyle of the harness. Everything new lives here — demo.css stays untouched for the other demo pages (this sheet is only linked from harness.html). Dependency-free plain DOM; every control keeps an id or data-attribute anchor so tests and humans target the same things.
Palette (neon on deep black): surface #000000 · panel #0a0a0a · hairline #1c1c1c text #e0e0e0 · muted #6a6a6a · hover surface #111111 accent neon #00ff87 (mint) · secondary neon #00e5ff (cyan) */
/* display:flex/grid below would beat the UA's [hidden] rule otherwise. */[hidden] { display: none !important; }
/* ---- shell (overrides the shared demo.css body chrome) ---- */html, body { height: 100%; }body { margin: 0; background: #000000; color: #e0e0e0; font-family: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace; font-size: 13px; overflow: hidden;}
#app { display: grid; height: 100vh; grid-template-columns: 44px 250px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto 24px; grid-template-areas: "activity sidebar main" "activity sidebar output" "statusbar statusbar statusbar";}/* Explorer toggled off from the activity bar: the sidebar column collapses. */#app.sidebar-hidden { grid-template-columns: 44px 0 minmax(0, 1fr); }
/* Thin dark scrollbars, neon thumb on hover. */::-webkit-scrollbar { width: 8px; height: 8px; }::-webkit-scrollbar-thumb { background: #1c1c1c; }::-webkit-scrollbar-thumb:hover { background: #00ff87; }::-webkit-scrollbar-track, ::-webkit-scrollbar-corner { background: #000000; }
/* ---- base controls (scoped to this page's roots) ---- */#app button, #context-menu button { font: inherit; color: #e0e0e0; background: #0a0a0a; border: 1px solid #1c1c1c; border-radius: 0; padding: 3px 10px; cursor: pointer; transition: color .15s ease, border-color .15s ease, background .15s ease;}#app button:hover:not(:disabled) { color: #00ff87; border-color: #00ff87; background: #000000; }#app button:focus-visible, #context-menu button:focus-visible { outline: 1px solid #00e5ff; outline-offset: 0;}#app button:disabled { opacity: 0.35; cursor: default; }
#app input { font: inherit; color: #e0e0e0; background: #000000; border: 1px solid #1c1c1c; border-radius: 0; padding: 3px 8px;}#app input:focus { outline: none; border-color: #00ff87; }
/* ---- activity bar: two icon buttons, Explorer (default active) + Output ---- */#activity-bar { grid-area: 1 / 1 / 3 / 2; background: #000000; border-right: 1px solid #1c1c1c; display: flex; flex-direction: column; align-items: center; gap: 2px; padding-top: 2px;}.activity-btn { position: relative; width: 44px; height: 44px; display: grid; place-items: center; background: none !important; border: none !important; border-radius: 0 !important; color: #6a6a6a;}.activity-btn:hover { color: #00ff87 !important; background: #0a0a0a !important; }.activity-btn:focus-visible { outline: 1px solid #00e5ff !important; outline-offset: -2px; }.activity-btn.active { color: #00ff87; }/* bomb.sh active-view indicator: a 2px neon bar hugging the left edge. */.activity-btn.active::before { content: ""; position: absolute; left: 0; top: 6px; bottom: 6px; width: 2px; background: #00ff87;}
/* files icon: three stacked lines of decreasing width */.icon-files { display: block; width: 16px; height: 16px; background: linear-gradient(currentColor, currentColor) 1px 2px / 14px 2px no-repeat, linear-gradient(currentColor, currentColor) 1px 7px / 10px 2px no-repeat, linear-gradient(currentColor, currentColor) 1px 12px / 12px 2px no-repeat;}/* output icon: terminal chevron + underscore, pure CSS */.icon-output { position: relative; display: block; width: 16px; height: 16px; }.icon-output::before { content: ""; position: absolute; left: 3px; top: 4px; width: 7px; height: 7px; border-top: 2px solid currentColor; border-right: 2px solid currentColor; transform: rotate(-45deg); /* the ">" chevron */}.icon-output::after { content: ""; position: absolute; left: 4px; bottom: 3px; width: 9px; height: 2px; background: currentColor; /* the "_" underscore */}
/* ---- main pane: the app grid's middle column. Rows: 35px tab bar, the editor (flexible), and the OUTPUT panel docked at the bottom (collapses to zero when toggled off from the activity bar). Anchors the #status toast (absolute, top: 35px). */#main { display: grid; grid-template-columns: minmax(0, 1fr); grid-template-rows: 35px minmax(0, 1fr) auto; position: relative; min-height: 0; min-width: 0;}
/* ---- tabs + editor pane: one tab per open file; the editor is gutter + textarea. Tab styling: active tab #000000 (merges with editor bg) with a 1px top accent (#00ff87), inactive #0a0a0a; dirty indicator (● dot) when the textarea content differs from the saved value (also in the tab title). *//* #editor-body is a transparent wrapper: display:contents promotes the gutter, the highlight mirror, and the textarea into grid items of #editor (placed explicitly below). [hidden] still collapses it because display:none !important wins. */#editor-body { display: contents; }#file-note { grid-row: 1; grid-column: 1 / -1; }#gutter { grid-row: 2; grid-column: 1; }#hl-wrap, #content { grid-row: 2; grid-column: 2; }#tab-bar { grid-row: 1; display: flex; align-items: stretch; background: #0a0a0a; border-bottom: 1px solid #1c1c1c; min-width: 0;}#tabs { flex: 1; display: flex; min-width: 0; overflow-x: auto; scrollbar-width: none;}#tabs::-webkit-scrollbar { display: none; }.tab { display: flex; align-items: center; gap: 6px; max-width: 240px; padding: 0 8px 0 14px; background: #0a0a0a; border-right: 1px solid #1c1c1c; border-top: 2px solid transparent; color: #6a6a6a; cursor: pointer; user-select: none; white-space: nowrap;}.tab.open { background: #000000; color: #e0e0e0; }.tab.active { background: #000000; color: #00ff87; border-top: 2px solid #00ff87;}.tab:hover { background: #111111; }.tab-label { max-width: 180px; overflow: hidden; text-overflow: ellipsis; pointer-events: none;}/* ● dirty marker: rendered on every tab, shown only while buffer ≠ saved */.tab-dirty-dot { display: none; width: 8px; height: 8px; border-radius: 50%; background: #00e5ff; flex: none; }.tab.dirty .tab-dirty-dot { display: block; }.tab-close { background: none !important; border: none !important; padding: 0 2px !important; color: inherit; width: 20px; height: 20px; line-height: 20px; text-align: center; border-radius: 0 !important;}.tab-close:hover { color: #ff3860; }#tab-actions { display: flex; align-items: center; gap: 4px; padding: 0 6px;}#btn-save, #btn-editor-delete { background: none !important; border: none !important; padding: 4px 6px !important; color: #6a6a6a;}#btn-save:hover, #btn-editor-delete:hover { color: #00ff87; }
/* ---- editor: gutter + textarea, monospace on pure black ---- */#editor { grid-row: 2; display: grid; grid-template-columns: 4ch minmax(0, 1fr); grid-template-rows: auto minmax(0, 1fr); /* file-note banner row, then the edit area */ overflow: hidden; position: relative; min-height: 0; background: #000000;}#gutter { overflow: hidden; padding: 6px 0; text-align: right; color: #1f3d2e; background: #000000; border-right: 1px solid #1c1c1c; user-select: none;}#gutter-inner { padding-right: 8px; transform: translateY(0); will-change: transform; }#content { font: inherit; color: #e0e0e0; background: #000000; border: none; outline: none; resize: none; padding: 6px 0 24px 12px; white-space: pre; overflow: auto; caret-color: #00ff87;}#content::selection { background: rgba(0, 255, 135, 0.25); color: #e0e0e0; }
/* ---- output panel: guest runner, bottom dock (inside #main's third row; grid-area 3/1 replaces the stale 2/3/3/4 — #output lives inside #main, not directly in #app's grid. [hidden] still wins via !important. */#output { grid-area: 3 / 1; display: grid; grid-template-rows: 35px minmax(0, 1fr); border-top: 1px solid #1c1c1c; background: #0a0a0a; min-height: 140px;}#output-header { display: flex; align-items: center; gap: 10px; padding: 0 12px; background: #000000; border-bottom: 1px solid #1c1c1c;}#output-label { color: #00ff87; text-transform: uppercase; letter-spacing: 2px; font-size: 11px; font-weight: bold;}#entry { flex: 1; max-width: 400px;}#btn-run { color: #00ff87; border-color: #00ff87; font-weight: bold; }#btn-run:hover:not(:disabled) { background: #00ff87 !important; color: #000000 !important; }#exit-badge { color: #6a6a6a; font-size: 11px; }#output-log { overflow: auto; padding: 8px 12px; font-size: 12px; line-height: 1.5;}#stdout { white-space: pre-wrap; color: #e0e0e0; }#stdout:empty::after { content: "— no output —"; color: #333333; font-style: italic; }#stderr { white-space: pre-wrap; color: #ff3860; margin-top: 4px; }
/* ---- status bar (bomb.sh style: neon accents on black) ---- */#statusbar { grid-area: 3 / 1 / 4 / 4; display: flex; align-items: center; gap: 16px; padding: 0 12px; background: #000000; border-top: 1px solid #1c1c1c; color: #6a6a6a; font-size: 11px; text-transform: uppercase; letter-spacing: 1px;}#sb-left, #sb-right { display: flex; align-items: center; gap: 12px; }#sb-left { flex: 1; }#sb-right { justify-content: flex-end; }#sb-brand { color: #00ff87; font-weight: bold; }#sb-boot { color: #e0e0e0; }#sb-boot.error { color: #ff3860; }#sb-encoding { color: #00e5ff; }#sb-exit.ok { color: #00ff87; }#sb-exit.err { color: #ff3860; }#sb-selected { max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ---- inline prompt row: one shared row for new-file/new-folder/rename ---- Auto-placed into the sidebar's third row; the tree flows to the 1fr row. *//* Two-row grid: the label gets a full-width line of its own and the input shares the second line with the buttons. One flex line can't fit label (~112px basis) + buttons (138px) + a usable input in 225px — with the input at flex-basis 0% it wins no shrink and no grow, ending at 18px of pure padding/border. Stacking the label above the controls gives the input ~57px of real typing width and keeps the path context visible. */#inline-row { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 4px 6px; align-items: center; padding: 6px 12px; background: #0a0a0a; border-bottom: 1px solid #00ff87;}#inline-label { grid-column: 1 / -1; color: #00e5ff; font-size: 11px; text-transform: uppercase; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;}/* min-width:0 lets the input shrink inside the 250px sidebar — without it the row overflows under #editor and swallows clicks meant for its buttons */#inline-input { grid-column: 1; min-width: 0; }#inline-ok { grid-column: 2; color: #00ff87; border-color: #00ff87; }#inline-cancel { grid-column: 3; color: #ff3860; border-color: #1c1c1c; }
/* ---- confirmation row (destructive ops) ---- */#confirm-row { display: flex; align-items: center; gap: 8px; padding: 6px 12px; background: #0a0a0a; border-bottom: 1px solid #ff3860;}#confirm-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #ff3860; font-size: 11px; text-transform: uppercase; }#confirm-yes { color: #ff3860; border-color: #ff3860; }#confirm-yes:hover { background: #ff3860 !important; color: #000000 !important; }#confirm-no { color: #6a6a6a; border-color: #1c1c1c; }
/* ---- status line (kept for tests; styled as a toast near the editor top) ---- */#status { position: absolute; top: 35px; left: 50%; transform: translateX(-50%); background: #0a0a0a; border: 1px solid #ff3860; color: #ff3860; padding: 6px 14px; z-index: 100; font-size: 12px; max-width: 80%; text-align: center; /* passive toast — must never swallow clicks aimed at tabs/editor underneath */ pointer-events: none;}#status:empty { display: none; }
/* ---- context menu ---- */#context-menu { position: fixed; z-index: 200; background: #0a0a0a; border: 1px solid #00ff87; min-width: 160px; padding: 4px 0; box-shadow: 0 0 20px rgba(0, 255, 135, 0.15);}#context-menu button { display: block; width: 100%; text-align: left; background: none !important; border: none !important; padding: 6px 16px; color: #e0e0e0;}#context-menu button:hover { background: #00ff87 !important; color: #000000 !important; }#context-menu .menu-sep { height: 1px; background: #1c1c1c; margin: 4px 0; }
/* ---- sidebar (explorer) ---- */#sidebar { grid-area: 1 / 2 / 3 / 3; display: grid; /* Explicit single column pinned to the sidebar's width. Without it the implicit "auto" column track grows to the max-content of its rows — e.g. the 470px-wide inline/confirm rows overflow into #editor, which then intercepts the pointer over the row's buttons (Playwright + humans both hit the editor instead of Create/Delete). minmax(0,1fr) lets flex rows shrink; .tree-row ellipsizes and #tree scrolls. */ grid-template-columns: minmax(0, 1fr); /* title | section title | inline create row | confirm row | tree — the inline/confirm rows are [hidden] most of the time and collapse to 0. */ grid-template-rows: 35px auto auto auto minmax(0, 1fr); background: #050505; border-right: 1px solid #1c1c1c; min-width: 0;}#sidebar-title { display: flex; align-items: center; padding: 0 16px; color: #6a6a6a; text-transform: uppercase; letter-spacing: 2px; font-size: 11px; border-bottom: 1px solid #1c1c1c;}#section-title { padding: 8px 16px 4px; color: #00ff87; text-transform: uppercase; letter-spacing: 1px; font-size: 12px; font-weight: bold;}#tree { grid-row: 5; overflow: auto; padding-bottom: 20px;}
/* ---- tree rows ---- */.tree-row { display: flex; align-items: center; gap: 4px; height: 24px; padding: 0 8px; cursor: pointer; color: #e0e0e0; white-space: nowrap;}.tree-row:hover { background: #111111; }.tree-row.selected { background: rgba(0, 255, 135, 0.08); color: #00ff87; }.tree-row .row-icon { margin-right: 4px; }.tree-row .twisty { width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center; color: #6a6a6a; font-size: 10px; transition: transform .15s ease;}.tree-row .twisty.open { transform: rotate(90deg); }.tree-label { overflow: hidden; text-overflow: ellipsis;}
/* file icons — colored dots/diamonds for a minimal look */.row-icon { width: 8px; height: 8px; display: inline-block; flex: none; transform: rotate(45deg);}.icon-folder { transform: none; background: #6a6a6a; width: 12px; height: 10px; border-bottom: 2px solid currentColor; }.ext-js, .ext-cjs, .ext-mjs { background: #ffd700; } /* amber */.ext-ts, .ext-mts, .ext-cts { background: #00e5ff; } /* neon cyan */.ext-json { background: #ffbf00; }.ext-md { background: #b3b3ff; }.ext-txt { background: #00ff87; } /* mint */.ext-other { background: #444444; }
/* ---- empty editor state ---- */#editor-empty { position: absolute; inset: 0; display: grid; place-items: center; color: #333333; font-size: 14px; letter-spacing: 1px; text-transform: uppercase; pointer-events: none;}#empty-title { color: #1c1c1c; font-size: 24px; font-weight: bold; }#file-note { color: #6a6a6a; font-size: 11px; margin-top: 8px; }
/* ---- download button (neon outline) ---- */#btn-download { color: #00ff87; border-color: #00ff87; background: #000000; font-weight: bold;}#btn-download:hover:not(:disabled) { background: #00ff87 !important; color: #000000 !important; box-shadow: 0 0 12px rgba(0, 255, 135, 0.4);}
/* ---- microlighter highlight mirror (code files) ---- A <pre><code> sits in #content's grid cell BEHIND the textarea: identical font, line-height, padding and white-space, scroll synced via its own scrollTop/scrollLeft. Tokens are painted through the CSS Custom Highlight API, so the mirror's plain text color matches the textarea's and the textarea text goes transparent (caret stays mint) while hl-on. */#hl-wrap, #hl-mirror { margin: 0; border: 0; padding: 0; font: inherit; line-height: 1.5; white-space: pre; tab-size: 8;}#hl-wrap { overflow: hidden; padding: 6px 0 24px 12px; color: #e0e0e0; background: #000000; pointer-events: none; user-select: none;}#hl-mirror { display: block; }/* While the mirror is live, the textarea renders nothing but the caret and the native selection band — the mirror supplies every glyph. */#editor-body.hl-on #content { background: transparent; color: transparent; }#editor-body.hl-on #content::selection { background: rgba(0, 255, 135, 0.25); color: transparent; }/* Line-height on both surfaces (and the gutter) keeps glyph rows aligned. */#content, #gutter { line-height: 1.5; }
/* ---- neon ::highlight() theme (our own; no stock microlighter theme) ---- microlighter flattens TextMate scopes into these categories and registers each as a CSS Custom Highlight over the mirror text. Palette: mint keywords, cyan strings, dim italic comments, grey punctuation/numbers, pale mint/cyan tints for functions/types. */::highlight(comment), ::highlight(quote) { color: #4a4a4a; font-style: italic; }::highlight(keyword), ::highlight(storage), ::highlight(at-rule), ::highlight(doctype),::highlight(important), ::highlight(section) { color: #00ff87; }::highlight(operator) { color: #b0b0b0; }::highlight(punctuation) { color: #6a6a6a; }::highlight(string), ::highlight(regexp), ::highlight(attribute-value), ::highlight(link),::highlight(raw), ::highlight(character-entity) { color: #00e5ff; }::highlight(numeric), ::highlight(boolean), ::highlight(constant), ::highlight(symbol),::highlight(anchor), ::highlight(entity) { color: #e0e0e0; }::highlight(function), ::highlight(decorator), ::highlight(animation),::highlight(selector) { color: #7dffc4; }::highlight(type), ::highlight(support) { color: #86e3f2; }::highlight(variable), ::highlight(interpolation) { color: #e0e0e0; }::highlight(property), ::highlight(key), ::highlight(attribute-name) { color: #00e5ff; }::highlight(tag) { color: #00ff87; }::highlight(inserted) { color: #00ff87; }::highlight(deleted) { color: #ff3860; }