typebulb 0.60.0 → 0.60.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/dist/agents/claude/client.js +206 -206
- package/dist/agents/claude/styles.css +152 -39
- package/dist/agents/codex/client.js +228 -228
- package/dist/agents/codex/styles.css +152 -39
- package/dist/agents/pi/client.js +259 -259
- package/dist/agents/pi/styles.css +152 -39
- package/dist/index.js +273 -269
- package/dist/servers.js +24 -24
- package/package.json +1 -1
|
@@ -181,13 +181,13 @@ body {
|
|
|
181
181
|
session list). Wrap = positioning context; the popover anchors bottom/right and
|
|
182
182
|
grows up-and-left — the chips sit in the right-aligned cluster, so left:0 would
|
|
183
183
|
run them off the viewport's right edge. */
|
|
184
|
-
.token-wrap, .sid-wrap, .servers-wrap, .model-wrap, .gitdiff-wrap { position: relative; display: inline-block; }
|
|
184
|
+
.token-wrap, .sid-wrap, .servers-wrap, .model-wrap, .gitdiff-wrap, .children-wrap { position: relative; display: inline-block; }
|
|
185
185
|
/* Each pill renders its wrap unconditionally, emitting an empty div when it has
|
|
186
186
|
nothing to show (no tokens / no session / nothing running). An empty wrap is
|
|
187
187
|
still a flex child, so .statusbar-actions' gap lands on both its sides —
|
|
188
188
|
doubling the visible gap between its neighbours. Collapse the empties so the
|
|
189
189
|
cluster spaces evenly regardless of which pills are present. */
|
|
190
|
-
.token-wrap:empty, .sid-wrap:empty, .servers-wrap:empty, .model-wrap:empty, .gitdiff-wrap:empty { display: none; }
|
|
190
|
+
.token-wrap:empty, .sid-wrap:empty, .servers-wrap:empty, .model-wrap:empty, .gitdiff-wrap:empty, .children-wrap:empty { display: none; }
|
|
191
191
|
.picker, .servers-pop {
|
|
192
192
|
--menu-h: 428px; /* one height for both statusbar menus — they sit side by side, so a
|
|
193
193
|
mismatched cap reads as a glitch; .picker grows to it, .servers-pop
|
|
@@ -644,9 +644,11 @@ body.tips-off [data-tip]::after { display: none; }
|
|
|
644
644
|
.picker-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
645
645
|
/* Keyboard cursor (arrow keys / hover) — same borderless tint as the launcher's .server-row.active. */
|
|
646
646
|
.picker-row.active { background: color-mix(in srgb, var(--accent) 16%, transparent); }
|
|
647
|
-
/*
|
|
648
|
-
|
|
649
|
-
|
|
647
|
+
/* One dot in a reserved gutter slot, transparent on every row so titles align whether or not it
|
|
648
|
+
shows. Green = the harness reports this session mid-turn (the live-app axis, the launcher's
|
|
649
|
+
running-:port green); accent = "you are here", and it wins, because the row you are reading is
|
|
650
|
+
self-evidently working and a second mark at 7px would say nothing new. Deliberately NOT process
|
|
651
|
+
liveness, which is true of every window left open — see TB-Agent-Mirror-Ready.md. */
|
|
650
652
|
.picker-dot {
|
|
651
653
|
flex: none;
|
|
652
654
|
align-self: center;
|
|
@@ -655,6 +657,8 @@ body.tips-off [data-tip]::after { display: none; }
|
|
|
655
657
|
border-radius: 50%;
|
|
656
658
|
background: transparent;
|
|
657
659
|
}
|
|
660
|
+
.picker-dot.working { background: var(--fold-marker); }
|
|
661
|
+
/* Higher specificity than .working on purpose: attachment outranks the turn state. */
|
|
658
662
|
.picker-row.current .picker-dot { background: var(--accent); }
|
|
659
663
|
.picker-preview {
|
|
660
664
|
flex: 1; min-width: 0;
|
|
@@ -670,6 +674,12 @@ body.tips-off [data-tip]::after { display: none; }
|
|
|
670
674
|
(--fold-marker) when the session changed since the mirror opened and hasn't been viewed. */
|
|
671
675
|
.picker-time.unseen { color: var(--fold-marker); }
|
|
672
676
|
.picker-hits { color: var(--muted); font-size: .72rem; white-space: nowrap; }
|
|
677
|
+
/* A full-text hit inside a session's child transcript (TB-Agent-Children.md): indented under its
|
|
678
|
+
parent's row and muted, at the SAME size — the row's distinguishing content is the agent's
|
|
679
|
+
description, so shrinking it is the last thing that should happen to it. */
|
|
680
|
+
.picker-row.child { padding-left: 1.9rem; }
|
|
681
|
+
.picker-row.child .picker-preview { color: var(--muted); }
|
|
682
|
+
.picker-child-kind { flex: none; max-width: 7rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); opacity: .8; }
|
|
673
683
|
/* Full-text result context: first matching turn, indented past the dot gutter into the title column. */
|
|
674
684
|
.picker-snippet {
|
|
675
685
|
color: var(--muted);
|
|
@@ -947,21 +957,20 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
947
957
|
}
|
|
948
958
|
.bulb-filter::placeholder { color: var(--muted); }
|
|
949
959
|
.bulb-filter:focus { outline: none; border-color: var(--accent); }
|
|
960
|
+
/* The inset controls (clear ×, then the mode toggle) flow in one cluster flush to the input's
|
|
961
|
+
top/right/bottom, inside its 1px border — so neither carries an offset for the other's width. */
|
|
962
|
+
.bulb-filter-inset { position: absolute; right: 1px; top: 1px; bottom: 1px; display: flex; align-items: stretch; }
|
|
950
963
|
/* Clear (×) — only rendered when the filter is non-empty; wipes it and refocuses the box. */
|
|
951
964
|
.bulb-filter-clear {
|
|
952
|
-
position: absolute; right: .3rem; top: 50%; transform: translateY(-50%);
|
|
953
965
|
appearance: none; border: none; background: transparent; cursor: pointer;
|
|
954
966
|
color: var(--muted); font-size: 1.05rem; line-height: 1; padding: 0 .3rem; border-radius: 4px;
|
|
955
967
|
}
|
|
956
968
|
.bulb-filter-clear:hover { color: var(--accent); }
|
|
957
|
-
/* Filter-mode toggle (full-text search) —
|
|
958
|
-
|
|
969
|
+
/* Filter-mode toggle (full-text search) — the cluster's right end. Both comboboxes render it
|
|
970
|
+
(ComboboxPill's modeToggle). */
|
|
959
971
|
.bulb-filter-control.has-trailing .bulb-filter { padding-right: 3.8rem; }
|
|
960
|
-
.bulb-filter-control.has-trailing .bulb-filter-clear { right: 2.5rem; }
|
|
961
972
|
.bulb-filter-mode {
|
|
962
|
-
/*
|
|
963
|
-
match the input's 6px radius so the latched fill reads as part of the box. */
|
|
964
|
-
position: absolute; right: 1px; top: 1px; bottom: 1px;
|
|
973
|
+
/* The right corners match the input's 6px radius so the latched fill reads as part of the box. */
|
|
965
974
|
appearance: none; border: none; background: transparent; cursor: pointer;
|
|
966
975
|
font-size: .8rem; line-height: 1; padding: 0 .45rem;
|
|
967
976
|
border-radius: 0 5px 5px 0;
|
|
@@ -1164,10 +1173,13 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
1164
1173
|
.gitdiff-pill .icon { width: 8px; height: 12px; }
|
|
1165
1174
|
.gitdiff-count { font-size: var(--pill-font); font-variant-numeric: tabular-nums; }
|
|
1166
1175
|
.gitdiff-pop { width: min(480px, calc(100vw - 2rem)); }
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
.gitdiff-
|
|
1176
|
+
/* The single-line menu-row geometry both swap-pills use (the session picker's rows are a column —
|
|
1177
|
+
they grow for a search snippet — so they keep their own). Merged selectors, the convention the
|
|
1178
|
+
.*-wrap rules above already follow: a third pill adds its name here rather than a fourth copy. */
|
|
1179
|
+
.gitdiff-list, .children-list { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-height: 0; overflow-y: scroll; padding-right: 4px; }
|
|
1180
|
+
.gitdiff-row, .children-row { display: flex; align-items: center; gap: .55rem; flex: none; min-height: var(--menu-row-h); padding: .05rem .45rem; border-radius: 6px; font-size: .85rem; cursor: pointer; }
|
|
1181
|
+
.gitdiff-row:hover, .children-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
1182
|
+
.gitdiff-row.active, .children-row.active { background: color-mix(in srgb, var(--accent) 16%, transparent); }
|
|
1171
1183
|
.gitdiff-row.viewing .gitdiff-path { color: var(--accent); }
|
|
1172
1184
|
/* One status letter, VS Code's palette: modified/renamed amber, added/untracked green, deleted red. */
|
|
1173
1185
|
.gitdiff-status { flex: none; width: 1rem; text-align: center; font: 600 .74rem ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--muted); }
|
|
@@ -1189,13 +1201,53 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
1189
1201
|
.gitdiff-pill.viewing :is(.gitdiff-status, .gitdiff-doc-path, .gitdiff-counts span) { text-box: trim-both cap alphabetic; }
|
|
1190
1202
|
.gitdiff-doc-path { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; direction: rtl; text-align: left; font-size: var(--pill-font); color: var(--accent); }
|
|
1191
1203
|
.gitdiff-doc-path:hover { text-decoration: underline; }
|
|
1192
|
-
|
|
1193
|
-
.
|
|
1194
|
-
.
|
|
1204
|
+
/* The × on a pill holding the transcript's slot (ui.ts closeChip) — one rule, both swap-pills. */
|
|
1205
|
+
.pill-close { flex: none; display: inline-flex; padding: 0 .15rem; color: var(--muted); }
|
|
1206
|
+
.pill-close .icon { width: 10px; height: 10px; }
|
|
1207
|
+
.pill-close:hover { color: var(--accent); }
|
|
1195
1208
|
/* Doc open ⇒ transcript-scoped pills hide: model switcher and session picker act on a conversation
|
|
1196
1209
|
that is not on screen (the token pill stays — its shimmer says the live diff may still grow).
|
|
1197
1210
|
Display only, never unmount — the model pill's superSelect must keep its mount. */
|
|
1198
|
-
.statusbar-actions.doc-open :is(.model-wrap, .sid-wrap) { display: none; }
|
|
1211
|
+
.statusbar-actions.doc-open :is(.model-wrap, .sid-wrap, .children-wrap) { display: none; }
|
|
1212
|
+
|
|
1213
|
+
/* ── the agents pill (TB-Agent-Children.md): this session's child transcripts. The git-diff pill's
|
|
1214
|
+
shape throughout — presence is the signal (a session that spawned nothing has no pill), picking a
|
|
1215
|
+
row swaps the transcript for that child's conversation, and the pill then wears its identity so
|
|
1216
|
+
the child view carries no chrome of its own. ── */
|
|
1217
|
+
/* Only the VIEWING form is a glyph pill (it needs the fixed square's width freed); the chip is an
|
|
1218
|
+
ordinary word pill, `🤖${n}🤖`, so it takes .pill's centering recipe untouched. */
|
|
1219
|
+
.pill.glyph.children-pill { width: auto; padding: 0 .45rem; gap: .3rem; }
|
|
1220
|
+
/* The chip is a word pill, so it sits outside `.pill.glyph` twice over: its emoji keep their colour
|
|
1221
|
+
at rest (they read as the agents themselves, not as chrome), and they take none of the glyph
|
|
1222
|
+
padding. `.pill`'s .7rem is measured for letters; an emoji's advance already carries its own side
|
|
1223
|
+
bearings, so that padding lands outside whitespace the glyph brought with it and reads as a gap.
|
|
1224
|
+
The margin then goes on each glyph's INNER side only: the padding already owns the outer gap, so a
|
|
1225
|
+
symmetric one doubles up exactly where it shows. Chip only, since the viewing form is a flex row
|
|
1226
|
+
with a gap of its own. */
|
|
1227
|
+
.children-pill { padding: 0 .3rem; }
|
|
1228
|
+
.children-pill:not(.glyph) .glyph-img:first-child { margin-right: .12rem; }
|
|
1229
|
+
.children-pill:not(.glyph) .glyph-img:last-child { margin-left: .12rem; }
|
|
1230
|
+
.children-pop { width: min(520px, calc(100vw - 2rem)); }
|
|
1231
|
+
.children-row.viewing .children-label { color: var(--accent); }
|
|
1232
|
+
/* One dot carries the state: still working (the fold-marker green), finished (dim), killed by the
|
|
1233
|
+
user (warn). Green because running is the LIVE-APP axis, the same one the launcher's running
|
|
1234
|
+
:port and play glyph sit on; the blue accent is the source/name axis and would read as "selected"
|
|
1235
|
+
here, which the row already says by other means. */
|
|
1236
|
+
.children-dot { flex: none; align-self: center; width: 7px; height: 7px; border-radius: 50%; background: var(--muted); opacity: .4; }
|
|
1237
|
+
.children-dot.running { background: var(--fold-marker); opacity: 1; }
|
|
1238
|
+
.children-dot.stopped { background: var(--warn); opacity: .85; }
|
|
1239
|
+
/* The agent's type, capped so the common long one (general-purpose) can't crowd out the
|
|
1240
|
+
description — which is the only thing telling eleven siblings apart. Muted by colour, not size. */
|
|
1241
|
+
.children-kind { flex: none; max-width: 7rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
|
|
1242
|
+
.children-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1243
|
+
.children-model { flex: none; color: var(--muted); opacity: .8; }
|
|
1244
|
+
.children-time { flex: none; color: var(--muted); font-size: .72rem; min-width: 2.6rem; text-align: right; }
|
|
1245
|
+
/* The viewing form: the open child's identity in place of the icon+count, with the × that returns.
|
|
1246
|
+
Same seating rule as the diff pill's — .pill.glyph opts out of the text-box trim, so each TEXT
|
|
1247
|
+
flex item takes it back or the words sit low against the icon. */
|
|
1248
|
+
.pill.glyph.children-pill.viewing { max-width: min(18rem, 35vw); gap: .45rem; padding: 0 .35rem 0 .5rem; }
|
|
1249
|
+
.children-pill.viewing .children-doc-label { text-box: trim-both cap alphabetic; }
|
|
1250
|
+
.children-doc-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--pill-font); color: var(--accent); }
|
|
1199
1251
|
|
|
1200
1252
|
/* The diff doc — takes the transcript's slot in .chat (same flex geometry as .messages). Full-bleed
|
|
1201
1253
|
like a code surface (no --content-max column: diff lines want the width); the body is the scroll
|
|
@@ -1303,6 +1355,27 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1303
1355
|
border-radius: 12px;
|
|
1304
1356
|
padding: .7rem .9rem;
|
|
1305
1357
|
}
|
|
1358
|
+
/* A user bubble is a panel, so its hover copy pill clears the border instead of sitting on it.
|
|
1359
|
+
.5rem matches the controls' own `bottom`, so the pill stands equally clear on both axes.
|
|
1360
|
+
Inherited by the agent variant below, and by any later bubble that grows a surface. */
|
|
1361
|
+
.bubble.user { --pill-inset: .5rem; }
|
|
1362
|
+
/* A sub-agent's hand-back (TB-Agent-Children.md): a user turn the harness delivered on an agent's
|
|
1363
|
+
behalf, never one the user typed, so it wears its own outline instead of the plain send's. The
|
|
1364
|
+
accent is the source/name axis (the same one the agents pill's open-child label sits on); green
|
|
1365
|
+
belongs to the live axis and would read as "still running", which a report never is. */
|
|
1366
|
+
.bubble.user.agent {
|
|
1367
|
+
border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
|
|
1368
|
+
background: color-mix(in srgb, var(--accent) 7%, transparent);
|
|
1369
|
+
}
|
|
1370
|
+
/* The frame's header: the agents pill's glyph, the agent's type, then its description, which is the
|
|
1371
|
+
link into its transcript. Muted by colour and NOT by size — the description is the one thing that
|
|
1372
|
+
tells eleven siblings apart, so it stays at the body's size (the picker's child-row rule). */
|
|
1373
|
+
.agent-head { display: flex; align-items: baseline; gap: .4rem; margin-bottom: .55rem; }
|
|
1374
|
+
.agent-head .glyph-img { flex: none; align-self: center; }
|
|
1375
|
+
.agent-kind { flex: none; color: var(--muted); }
|
|
1376
|
+
.agent-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1377
|
+
a.agent-name { color: var(--accent); cursor: pointer; }
|
|
1378
|
+
a.agent-name:hover { text-decoration: underline; }
|
|
1306
1379
|
/* Consecutive tool-only bubbles sit tight via negative margin (flex `gap`
|
|
1307
1380
|
can't be overridden per sibling pair). The adjacent-sibling selector leaves
|
|
1308
1381
|
the first tools-only bubble's gap intact, preserving the turn boundary. */
|
|
@@ -1435,8 +1508,26 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1435
1508
|
inline bulb and a spread bulb). `100%` in --lane-ml resolves against each user's own containing block
|
|
1436
1509
|
(prose width) at point of use, so both centre on the column axis — which is the viewport centre,
|
|
1437
1510
|
since the gutters are symmetric. One home so the geometry can't drift between the two (the bug
|
|
1438
|
-
that let an off-centre nudge survive in one rule after being removed from the other).
|
|
1439
|
-
|
|
1511
|
+
that let an off-centre nudge survive in one rule after being removed from the other).
|
|
1512
|
+
--chop-inset (the stripe-chop's reach into the gutter) and --chop-bg (the fill that covers the
|
|
1513
|
+
stripe) live here too: all four describe the transcript column, so all four are the container's
|
|
1514
|
+
to declare rather than the artifact's. */
|
|
1515
|
+
.md {
|
|
1516
|
+
--lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2);
|
|
1517
|
+
--chop-inset: 1rem; --chop-bg: var(--bg);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
/* A padded panel that holds rendered markdown redeclares all four as its own box: a sub-agent
|
|
1521
|
+
hand-back frame (TB-Agent-Children.md), a disclosure, a blockquote. Inside one there is no turn
|
|
1522
|
+
stripe to reach for or to cover, so the reach and the fill go to zero and an embed keeps only its
|
|
1523
|
+
vertical band, while the lane collapses to the panel: a wide table then shares the panel's width
|
|
1524
|
+
through its two ceilings and scrolls in .table-scroll, instead of a band that bleeds over the
|
|
1525
|
+
card's border and paints page colour across its tint. The reset must land on the `.md` element
|
|
1526
|
+
itself, since `.md` declares the defaults on itself and would beat a rule on an ancestor. The
|
|
1527
|
+
next panel is one more selector here. */
|
|
1528
|
+
.bubble.user > .md, .md details, .md blockquote {
|
|
1529
|
+
--lane-w: 100%; --lane-ml: 0px; --chop-inset: 0px; --chop-bg: transparent;
|
|
1530
|
+
}
|
|
1440
1531
|
|
|
1441
1532
|
/* Full-lane breakout — the mermaid embed (whole element) and a SPREAD bulb's iframe step out of
|
|
1442
1533
|
the prose column to the transcript width so wide content gets room. Both use --lane-w/--lane-ml
|
|
@@ -1468,7 +1559,7 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1468
1559
|
overflow-x: auto;
|
|
1469
1560
|
display: flex;
|
|
1470
1561
|
justify-content: safe center;
|
|
1471
|
-
box-shadow: 0 0 0 1rem var(--bg);
|
|
1562
|
+
box-shadow: 0 0 0 1rem var(--chop-bg);
|
|
1472
1563
|
}
|
|
1473
1564
|
/* flex-shrink:0: otherwise the SVG shrinks to the flex container and its viewBox
|
|
1474
1565
|
scales it down — no overflow, no scrollbar. Natural width is what scrolls. */
|
|
@@ -1480,17 +1571,20 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1480
1571
|
.md .svg-embed { justify-content: center; }
|
|
1481
1572
|
.md .svg-embed svg { max-width: 100%; height: auto; }
|
|
1482
1573
|
|
|
1483
|
-
/* Markdown tables: chop in-column (the `.embed` rule below)
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1574
|
+
/* Markdown tables: chop in-column (the `.embed` rule below). An IN-COLUMN table starts where the prose
|
|
1575
|
+
starts — it is part of the paragraph flow, and a narrow one centred in the column reads as shifted
|
|
1576
|
+
right against the left-aligned text above and below it, which is the whole of that bug. Only a
|
|
1577
|
+
SPREAD table centres, and it has earned it: spreading means it is already wider than the column, so
|
|
1578
|
+
centring keeps it symmetric about the column axis exactly as the mermaid lane does. `safe` on both
|
|
1579
|
+
means one that overflows its box falls back to the start and scrolls from its first column rather
|
|
1580
|
+
than clipping. The scroll lives on the inner `.table-scroll`, not the `.embed`
|
|
1487
1581
|
wrapper, so a horizontal scrollbar rides above the chop's bottom padding instead of flush against the
|
|
1488
1582
|
next line (a scrollbar sits at its own container's bottom edge). It's the centred flex child, capped at
|
|
1489
1583
|
the box so it shrinks-to-fit and scrolls only what overflows; `max-width:100%` + the overflow-induced
|
|
1490
1584
|
0 min-width let it shrink below the table's natural width. `spread` widens the wrapper to the lane (the
|
|
1491
1585
|
mermaid geometry) for a table fitTableEmbeds found too wide for the column. The opaque `.embed`
|
|
1492
1586
|
padding/z-index still occludes the turn stripe the spread crosses — no box-shadow needed. */
|
|
1493
|
-
.md .table-embed { justify-content: safe
|
|
1587
|
+
.md .table-embed { justify-content: safe flex-start; }
|
|
1494
1588
|
.md .table-embed .table-scroll { overflow-x: auto; max-width: 100%; }
|
|
1495
1589
|
/* max-content keeps the table at its natural (unwrapped) width so it overflows `.table-scroll` and earns
|
|
1496
1590
|
a scrollbar, rather than wrapping its cells to fit — the bar is the point, and it now rides inside the
|
|
@@ -1511,15 +1605,24 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1511
1605
|
shattering text; a pathological no-space token (a long URL) then scrolls rather than breaking. */
|
|
1512
1606
|
.md .table-embed table { margin: 0; width: max-content; max-width: var(--lane-w); }
|
|
1513
1607
|
.md .table-embed th, .md .table-embed td { max-width: 40ch; word-break: normal; }
|
|
1608
|
+
/* The spread band's comfort goes on the RIGHT only, which looks asymmetric written down and is what
|
|
1609
|
+
reads as even on screen. `.messages` reserves a scrollbar gutter at BOTH edges (to hold the column
|
|
1610
|
+
on the window axis), but only the right one ever contains a scrollbar: the left gutter is dead
|
|
1611
|
+
space that already reads as margin, while on the right the lane's own 1rem inset is all there is
|
|
1612
|
+
between the table and the bar. Inset both sides equally and the left gap measures a gutter wider
|
|
1613
|
+
than the right; inset neither and the table sits flush against the bar, which is the bug. So the
|
|
1614
|
+
band adds back one inset on the right, and the table's cap comes down by that same inset or it
|
|
1615
|
+
would overflow the band's content box by exactly that at every width — the permanent scrollbar the
|
|
1616
|
+
zeroed inset was avoiding. */
|
|
1514
1617
|
.md .table-embed.spread {
|
|
1618
|
+
justify-content: safe center;
|
|
1515
1619
|
width: var(--lane-w);
|
|
1516
1620
|
margin-left: var(--lane-ml);
|
|
1517
1621
|
margin-right: auto;
|
|
1518
|
-
/* Zero the `.embed` inset: spread makes the border box the lane, so a 1rem left padding would
|
|
1519
|
-
leave the scroller 1rem narrower than the table's own lane cap — a capped table then overflows
|
|
1520
|
-
by exactly that inset at every window width, which is the permanent 16px-of-travel scrollbar. */
|
|
1521
1622
|
padding-left: 0;
|
|
1623
|
+
padding-right: var(--chop-inset);
|
|
1522
1624
|
}
|
|
1625
|
+
.md .table-embed.spread table { max-width: calc(var(--lane-w) - var(--chop-inset)); }
|
|
1523
1626
|
|
|
1524
1627
|
/* ````bulb```` inline bulbs: a sandboxed nested app. createBulbFrame owns the iframe (auto-height,
|
|
1525
1628
|
borderless). No frame of our own — like the mermaid/svg embeds, it sits in the flow rather
|
|
@@ -1531,9 +1634,10 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1531
1634
|
under a live inline bulb (both monospace, muted red). */
|
|
1532
1635
|
/* The stripe-chop, shared by every opaque in-column visual artifact (the fit bulb and a raw
|
|
1533
1636
|
`svg` — each opts in by carrying `.embed`). An opaque bg over position:relative + z-index:0
|
|
1534
|
-
paints above .bubble::before; a -
|
|
1535
|
-
full breakout (padding-left
|
|
1536
|
-
stays at prose-right, so a bulb's controls anchored there don't move)
|
|
1637
|
+
paints above .bubble::before; a --chop-inset left extension reaches the stripe in the gutter
|
|
1638
|
+
without a full breakout (the matching padding-left keeps the content in the prose column, and the
|
|
1639
|
+
box's right edge stays at prose-right, so a bulb's controls anchored there don't move); inside a
|
|
1640
|
+
panel both zero out (the nesting reset above). The vertical gap is opaque
|
|
1537
1641
|
padding, not margin: it occludes the stripe top and bottom so the cut is flush with the prose (no
|
|
1538
1642
|
stub in a transparent gap), symmetric above/below to separate the artifact from surrounding text.
|
|
1539
1643
|
margin top/bottom are zeroed — and the abutting prose margins too (below) — so the gap butts the
|
|
@@ -1543,9 +1647,9 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1543
1647
|
display: flex;
|
|
1544
1648
|
position: relative;
|
|
1545
1649
|
z-index: 0;
|
|
1546
|
-
background: var(--bg);
|
|
1547
|
-
margin: 0 0 0 -
|
|
1548
|
-
padding: 1.1rem 0 1.1rem
|
|
1650
|
+
background: var(--chop-bg);
|
|
1651
|
+
margin: 0 0 0 calc(-1 * var(--chop-inset));
|
|
1652
|
+
padding: 1.1rem 0 1.1rem var(--chop-inset);
|
|
1549
1653
|
}
|
|
1550
1654
|
/* A bulb stacks its controls/frame/code vertically; in spread mode the inner iframe's own
|
|
1551
1655
|
breakout covers the stripe too (the wrapper still chops in column). */
|
|
@@ -1595,7 +1699,12 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1595
1699
|
.overlay-pill {
|
|
1596
1700
|
position: absolute;
|
|
1597
1701
|
bottom: .5rem;
|
|
1598
|
-
|
|
1702
|
+
/* Flush with the content column's right edge (prose / statusbar pills) when the pill sits on the
|
|
1703
|
+
page's own background. A PANEL sets --pill-inset instead, so the pill clears its edge rather
|
|
1704
|
+
than landing on the border: `right: 0` resolves against the containing block's padding box, so
|
|
1705
|
+
inside a bordered box it means "touching the border". Only this axis needed it — `bottom` has
|
|
1706
|
+
always stood the pill off, which is why the defect read as a right-edge one. */
|
|
1707
|
+
right: var(--pill-inset, 0px);
|
|
1599
1708
|
font: inherit;
|
|
1600
1709
|
font-size: var(--pill-font);
|
|
1601
1710
|
padding: 0 .45rem;
|
|
@@ -1614,6 +1723,10 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1614
1723
|
the chop/breakout; the code-block wrapper needs it for the absolute pill. The pill is positioned
|
|
1615
1724
|
(absolute), so it paints over the static fence content regardless of DOM order. */
|
|
1616
1725
|
.md .copyable { position: relative; }
|
|
1726
|
+
/* A code fence IS a panel (the <pre> carries the border and background, the wrapper is the
|
|
1727
|
+
positioning context), so its copy pill takes the inset. The svg and mermaid embeds are
|
|
1728
|
+
transparent bands on the page background and keep the flush edge. */
|
|
1729
|
+
.md .code-block { --pill-inset: .5rem; }
|
|
1617
1730
|
.md .copyable:hover .copy-src,
|
|
1618
1731
|
.md .copy-src:focus-visible { opacity: 1; }
|
|
1619
1732
|
.md .copy-src.done { color: var(--accent); border-color: var(--accent); }
|
|
@@ -1974,7 +2087,7 @@ html[data-theme="dark"] .masthead-logo.dark { display: block; }
|
|
|
1974
2087
|
.bubble-controls {
|
|
1975
2088
|
position: absolute;
|
|
1976
2089
|
bottom: .5rem;
|
|
1977
|
-
right:
|
|
2090
|
+
right: var(--pill-inset, 0px);
|
|
1978
2091
|
display: flex;
|
|
1979
2092
|
gap: .4rem;
|
|
1980
2093
|
}
|