typebulb 0.14.4 → 0.14.6
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/README.md +93 -1
- package/dist/agents/claude/client.js +122 -122
- package/dist/agents/claude/styles.css +112 -55
- package/dist/index.js +185 -184
- package/package.json +1 -1
|
@@ -155,6 +155,17 @@ body {
|
|
|
155
155
|
cluster spaces evenly regardless of which pills are present. */
|
|
156
156
|
.token-wrap:empty, .sid-wrap:empty, .servers-wrap:empty { display: none; }
|
|
157
157
|
.picker, .servers-pop {
|
|
158
|
+
--menu-h: 428px; /* one height for both statusbar menus — they sit side by side, so a
|
|
159
|
+
mismatched cap reads as a glitch; .picker grows to it, .servers-pop
|
|
160
|
+
is fixed at it (see its comment). Sized to show exactly 12 rows
|
|
161
|
+
(12 × --menu-row-h + 11 gaps + filter + padding) — recognition fades
|
|
162
|
+
past ~12 and search owns the tail. Plain px: desktop-only surface,
|
|
163
|
+
every desktop window fits it. 428, not 427: at 427 the two menus land
|
|
164
|
+
on different device-pixel roundings, a 1px sibling mismatch — the
|
|
165
|
+
lesser evil is 1px off the ideal height. */
|
|
166
|
+
--menu-row-h: 29px; /* one row height for both menus' lists — rows must match pitch exactly
|
|
167
|
+
or the side-by-side menus read as two designs. Whole px, eyeball-tuned;
|
|
168
|
+
the row paddings are kept below it so this floor is what governs. */
|
|
158
169
|
position: absolute;
|
|
159
170
|
bottom: calc(100% + .35rem);
|
|
160
171
|
right: 0;
|
|
@@ -165,56 +176,86 @@ body {
|
|
|
165
176
|
box-shadow: 0 8px 30px rgba(0, 0, 0, .45);
|
|
166
177
|
}
|
|
167
178
|
|
|
179
|
+
/* ---- Shimmer — the one "working" cue. One gradient, one keyframes, composed via class, never
|
|
180
|
+
re-pasted (paste-drift is how each new shimmer kept landing at its own speed). `.shimmer` sweeps
|
|
181
|
+
the element's background; `.shimmer-text` clips the sweep to the glyphs alone. Knobs, set by the
|
|
182
|
+
using rule: --shimmer-base (the gradient's resting color — transparent for see-through chrome, an
|
|
183
|
+
opaque surface for pills that must cover content) and --shimmer-mix (accent strength). Pick the
|
|
184
|
+
cadence by the band's LINEAR speed, which is what reads consistent: travel is proportional to
|
|
185
|
+
element width (background-size 250%), and ~2rem/s is the calibrated pace — so narrow chrome
|
|
186
|
+
(a 1.7rem play button) takes the 2.2s default and anything text-width or wider takes
|
|
187
|
+
`.shimmer-slow` (7s). A fast sweep reads as urgency, not progress. */
|
|
188
|
+
/* The state longhands are !important: this class composes onto elements whose resting rules set a
|
|
189
|
+
`background:` shorthand at arbitrary specificity (button resets, .overlay-pill, :hover tints) —
|
|
190
|
+
any of which would silently kill the gradient (the bug that ate the play button's shimmer). A
|
|
191
|
+
working cue must win while it's on; resting background-color still shows beneath the sweep. */
|
|
192
|
+
.shimmer, .shimmer-text { background-size: 250% 100% !important; animation: bulb-shimmer var(--shimmer-speed, 2.2s) linear infinite !important; }
|
|
193
|
+
.shimmer {
|
|
194
|
+
background-image: linear-gradient(100deg, var(--shimmer-base, transparent) 30%, color-mix(in srgb, var(--accent) var(--shimmer-mix, 35%), var(--shimmer-base, transparent)) 50%, var(--shimmer-base, transparent) 70%) !important;
|
|
195
|
+
background-color: var(--shimmer-base, transparent);
|
|
196
|
+
}
|
|
197
|
+
.shimmer-text {
|
|
198
|
+
/* Shrink-to-fit, so the sweep paces the text's own length, not the container's — on a full-width
|
|
199
|
+
box the band spends most of its run off-glyph (same move as typebulb.com's shimmer.ts). */
|
|
200
|
+
display: inline-block;
|
|
201
|
+
background-image: linear-gradient(100deg, currentColor 35%, var(--accent) 50%, currentColor 65%) !important;
|
|
202
|
+
-webkit-background-clip: text;
|
|
203
|
+
background-clip: text;
|
|
204
|
+
-webkit-text-fill-color: transparent;
|
|
205
|
+
}
|
|
206
|
+
.shimmer-slow { --shimmer-speed: 7s; }
|
|
207
|
+
@keyframes bulb-shimmer { from { background-position: 250% 0; } to { background-position: -250% 0; } }
|
|
208
|
+
@media (prefers-reduced-motion: reduce) {
|
|
209
|
+
.shimmer { animation: none !important; background: color-mix(in srgb, var(--accent) 15%, var(--shimmer-base, transparent)) !important; }
|
|
210
|
+
.shimmer-text { animation: none !important; background: none !important; -webkit-text-fill-color: currentColor; }
|
|
211
|
+
}
|
|
212
|
+
|
|
168
213
|
/* CC mid-turn: the token pill doubles as the working indicator — an accent shimmer
|
|
169
214
|
over the chip, the count frozen until the turn flushes. Passive — driven by the
|
|
170
215
|
live-chain leaf, not a live token stream (the JSONL is flushed in batches, so this
|
|
171
|
-
can sit lit through a multi-minute buffering window). The sweep
|
|
172
|
-
--panel, not transparent like
|
|
173
|
-
the message text the strip lets through. */
|
|
174
|
-
.token.busy {
|
|
175
|
-
border-color: var(--accent);
|
|
176
|
-
background: linear-gradient(100deg, var(--panel) 30%, color-mix(in srgb, var(--accent) 25%, var(--panel)) 50%, var(--panel) 70%);
|
|
177
|
-
background-size: 250% 100%;
|
|
178
|
-
/* Much slower than .launching's sweep: that one paces a ~2s wait, this can sit lit for
|
|
179
|
-
minutes — at that dwell a fast sweep reads as urgency, not progress. */
|
|
180
|
-
animation: bulb-shimmer 7s linear infinite;
|
|
181
|
-
}
|
|
182
|
-
@media (prefers-reduced-motion: reduce) {
|
|
183
|
-
.token.busy { animation: none; background: color-mix(in srgb, var(--accent) 15%, var(--panel)); }
|
|
184
|
-
}
|
|
216
|
+
can sit lit through a multi-minute buffering window — hence shimmer-slow). The sweep
|
|
217
|
+
blends with --panel, not transparent like the launch buttons': statusbar pills must
|
|
218
|
+
stay opaque over the message text the strip lets through. */
|
|
219
|
+
.token.busy { border-color: var(--accent); --shimmer-base: var(--panel); --shimmer-mix: 25%; }
|
|
185
220
|
|
|
186
221
|
/* Session list — shares the popover chrome (.picker above); these are
|
|
187
222
|
just its own dimensions. */
|
|
188
223
|
.picker {
|
|
189
224
|
width: min(440px, calc(100vw - 2rem));
|
|
190
|
-
max-height:
|
|
225
|
+
max-height: var(--menu-h);
|
|
191
226
|
overflow: hidden;
|
|
192
227
|
padding: .35rem;
|
|
193
228
|
display: flex;
|
|
194
229
|
flex-direction: column;
|
|
195
230
|
gap: .15rem;
|
|
196
231
|
}
|
|
197
|
-
/* The filter (.bulb-filter-control) pins at the
|
|
198
|
-
|
|
232
|
+
/* The filter (.bulb-filter-control) pins at the bottom — the popover's anchored edge, so it never
|
|
233
|
+
jumps as the filtered list changes height; the row list scrolls inside the capped box, resting at
|
|
234
|
+
its end (newest-at-bottom) — same shape as the launcher's .servers-pop / .bulb-list. */
|
|
199
235
|
.picker-list { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-height: 0; overflow-y: auto; padding-right: 4px; }
|
|
200
236
|
.picker-empty { padding: .8rem; color: var(--muted); font-size: .85rem; text-align: center; }
|
|
201
237
|
.picker-row {
|
|
202
238
|
display: flex;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
239
|
+
flex-direction: column;
|
|
240
|
+
/* flex:none — the explicit min-height below replaces flexbox's implicit min-height:auto floor,
|
|
241
|
+
which would otherwise let an overflowing list squash rows below their content (snippet rows
|
|
242
|
+
rendered as overlapping text). The list scrolls; rows never compress. */
|
|
243
|
+
flex: none;
|
|
244
|
+
gap: .2rem;
|
|
245
|
+
/* min-height, not height: a full-text result row grows for its snippet line. */
|
|
246
|
+
padding: .35rem .55rem;
|
|
247
|
+
min-height: var(--menu-row-h);
|
|
248
|
+
box-sizing: border-box;
|
|
249
|
+
justify-content: center;
|
|
206
250
|
border-radius: 6px;
|
|
207
251
|
cursor: pointer;
|
|
208
|
-
border: 1px solid transparent;
|
|
209
252
|
font-size: .85rem;
|
|
210
253
|
}
|
|
254
|
+
.picker-row-main { display: flex; gap: .6rem; align-items: baseline; }
|
|
211
255
|
.picker:focus { outline: none; } /* the list owns its own highlight; no focus ring */
|
|
212
256
|
.picker-row:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }
|
|
213
|
-
/* Keyboard cursor (arrow keys / hover) —
|
|
214
|
-
.picker-row.active {
|
|
215
|
-
background: color-mix(in srgb, var(--accent) 18%, transparent);
|
|
216
|
-
border-color: color-mix(in srgb, var(--accent) 40%, transparent);
|
|
217
|
-
}
|
|
257
|
+
/* Keyboard cursor (arrow keys / hover) — same borderless tint as the launcher's .server-row.active. */
|
|
258
|
+
.picker-row.active { background: color-mix(in srgb, var(--accent) 16%, transparent); }
|
|
218
259
|
/* The attached session — a leading accent dot in its own gutter slot, reserved (transparent) on
|
|
219
260
|
every row so titles align whether or not it's shown, and filled only on the attached row so it
|
|
220
261
|
reads as "you are here" even when the cursor has moved off. */
|
|
@@ -237,6 +278,20 @@ body {
|
|
|
237
278
|
font-size: .72rem;
|
|
238
279
|
min-width: 2.6rem; text-align: right;
|
|
239
280
|
}
|
|
281
|
+
.picker-hits { color: var(--muted); font-size: .72rem; white-space: nowrap; }
|
|
282
|
+
/* Full-text result context: first matching turn, indented past the dot gutter into the title column. */
|
|
283
|
+
.picker-snippet {
|
|
284
|
+
color: var(--muted);
|
|
285
|
+
font-size: .78rem;
|
|
286
|
+
line-height: 1.4;
|
|
287
|
+
margin-left: calc(7px + .6rem);
|
|
288
|
+
overflow-wrap: anywhere;
|
|
289
|
+
}
|
|
290
|
+
.picker-mark {
|
|
291
|
+
background: color-mix(in srgb, var(--accent) 30%, transparent);
|
|
292
|
+
border-radius: 2px;
|
|
293
|
+
color: var(--fg);
|
|
294
|
+
}
|
|
240
295
|
|
|
241
296
|
/* Running-breakouts list — shares the popover chrome (.servers-pop above); its own dimensions +
|
|
242
297
|
per-row layout: the play/stop button + name read together on the left (▶/■ Title, name flex:1),
|
|
@@ -248,7 +303,7 @@ body {
|
|
|
248
303
|
scrolls inside (see .bulb-list). */
|
|
249
304
|
.servers-pop {
|
|
250
305
|
width: min(560px, calc(100vw - 2rem));
|
|
251
|
-
height:
|
|
306
|
+
height: var(--menu-h);
|
|
252
307
|
overflow: hidden;
|
|
253
308
|
padding: .35rem;
|
|
254
309
|
display: flex;
|
|
@@ -271,7 +326,8 @@ body {
|
|
|
271
326
|
the left, an asymmetry). Text gets its breathing room inside the highlight via end-item margins
|
|
272
327
|
(.server-stop/.bulb-launch on the left, .server-port/.bulb-time on the right) rather than row
|
|
273
328
|
padding, which would shift the subgrid tracks and break alignment. */
|
|
274
|
-
padding: .
|
|
329
|
+
padding: .05rem 0;
|
|
330
|
+
min-height: var(--menu-row-h);
|
|
275
331
|
border-radius: 6px;
|
|
276
332
|
font-size: .85rem;
|
|
277
333
|
}
|
|
@@ -294,7 +350,7 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
294
350
|
/* Launcher filter box — pinned above the list; type to narrow (matches name + path). The control
|
|
295
351
|
wrapper pins to the top and anchors the clear (×) at the far right (position:relative + the
|
|
296
352
|
input's right padding reserves its room). */
|
|
297
|
-
.bulb-filter-control { position: relative; flex: none; margin-
|
|
353
|
+
.bulb-filter-control { position: relative; flex: none; margin-top: .35rem; }
|
|
298
354
|
.bulb-filter {
|
|
299
355
|
font: inherit;
|
|
300
356
|
font-size: .82rem;
|
|
@@ -315,11 +371,29 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
315
371
|
color: var(--muted); font-size: 1.05rem; line-height: 1; padding: 0 .3rem; border-radius: 4px;
|
|
316
372
|
}
|
|
317
373
|
.bulb-filter-clear:hover { color: var(--accent); }
|
|
374
|
+
/* Filter-mode toggle (full-text search) — inset at the input's right edge; the clear × shifts
|
|
375
|
+
inward beside it. Only rendered when the host passes a trailing control (the session picker). */
|
|
376
|
+
.bulb-filter-control.has-trailing .bulb-filter { padding-right: 3.8rem; }
|
|
377
|
+
.bulb-filter-control.has-trailing .bulb-filter-clear { right: 2.5rem; }
|
|
378
|
+
.bulb-filter-mode {
|
|
379
|
+
/* Stretches flush to the input's top/right/bottom, inside its 1px border; the right corners
|
|
380
|
+
match the input's 6px radius so the latched fill reads as part of the box. */
|
|
381
|
+
position: absolute; right: 1px; top: 1px; bottom: 1px;
|
|
382
|
+
appearance: none; border: none; background: transparent; cursor: pointer;
|
|
383
|
+
font-size: .8rem; line-height: 1; padding: 0 .45rem;
|
|
384
|
+
border-radius: 0 5px 5px 0;
|
|
385
|
+
}
|
|
386
|
+
/* Same state cue as the prose pill's glyph: the emoji can't take currentColor, so it goes
|
|
387
|
+
grayscale at rest, color while hovered or on — filtered on the inner span, never the button. */
|
|
388
|
+
.bulb-filter-mode .glyph-img { filter: grayscale(1); opacity: .75; }
|
|
389
|
+
.bulb-filter-mode:hover .glyph-img, .bulb-filter-mode.on .glyph-img { filter: none; opacity: 1; }
|
|
390
|
+
/* Latched: a --panel fill — the popover's own surface, one step off the input's --bg. */
|
|
391
|
+
.bulb-filter-mode.on { background: var(--panel); }
|
|
318
392
|
/* Scrolls within the fixed-height popover so the filter stays put and the box never grows. */
|
|
319
393
|
/* The shared grid for the launcher rows: each .server-row is a subgrid spanning these tracks, so
|
|
320
394
|
columns line up row-to-row. auto cols size to their widest content (tight, no slack); 1fr is the
|
|
321
395
|
name. A small right padding keeps the selected-row highlight off the scrollbar. */
|
|
322
|
-
.bulb-list { display: grid; grid-template-columns: auto 1fr auto auto auto; column-gap: .6rem; row-gap: .
|
|
396
|
+
.bulb-list { display: grid; grid-template-columns: auto 1fr auto auto auto; column-gap: .6rem; row-gap: .15rem; align-items: center; align-content: start; padding-right: 4px; flex: 1; min-height: 0; overflow-y: auto; }
|
|
323
397
|
/* A stopped bulb's name isn't a link (no running URL), so it stays plain foreground. */
|
|
324
398
|
.server-name.stopped { color: var(--fg); }
|
|
325
399
|
.bulb-time { color: var(--muted); font-size: .72rem; font-variant-numeric: tabular-nums; text-align: right; margin-right: .4rem; }
|
|
@@ -331,7 +405,9 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
331
405
|
background: transparent; border: 1px solid var(--border); border-radius: 5px;
|
|
332
406
|
height: 1.7rem; display: inline-flex; align-items: center; justify-content: center;
|
|
333
407
|
}
|
|
334
|
-
|
|
408
|
+
/* Shorter than the icon buttons: a bordered text pill at full row height reads as cramped, where
|
|
409
|
+
the round play/stop icons are meant to fill it. */
|
|
410
|
+
.trust-toggle { font-size: .76rem; height: 1.45rem; padding: 0 .5rem; color: var(--muted); justify-self: center; }
|
|
335
411
|
.trust-toggle:hover, .trust-toggle.on { color: var(--accent); border-color: var(--accent); }
|
|
336
412
|
.server-stop, .bulb-launch { width: 1.7rem; padding: 0; border-radius: 50%; margin-left: .4rem; }
|
|
337
413
|
.btn-icon { display: block; }
|
|
@@ -344,18 +420,7 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
344
420
|
/* Launching: stays visible (hover-independent) and shimmers until the row flips to a stop button,
|
|
345
421
|
so the ~2s wait reads as "working" rather than a dead click. Transient — driven by the in-memory
|
|
346
422
|
launching set, never persisted. */
|
|
347
|
-
.bulb-launch.launching {
|
|
348
|
-
visibility: visible; border-color: var(--accent);
|
|
349
|
-
background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--accent) 35%, transparent) 50%, transparent 70%);
|
|
350
|
-
background-size: 250% 100%;
|
|
351
|
-
/* One full pass per ~2s wait — slow enough to stay calm, fast enough that the band
|
|
352
|
-
is guaranteed to cross while the user is actually watching. */
|
|
353
|
-
animation: bulb-shimmer 2.2s linear infinite;
|
|
354
|
-
}
|
|
355
|
-
@keyframes bulb-shimmer { from { background-position: 250% 0; } to { background-position: -250% 0; } }
|
|
356
|
-
@media (prefers-reduced-motion: reduce) {
|
|
357
|
-
.bulb-launch.launching { animation: none; background: color-mix(in srgb, var(--accent) 18%, transparent); }
|
|
358
|
-
}
|
|
423
|
+
.bulb-launch.launching { visibility: visible; border-color: var(--accent); }
|
|
359
424
|
.server-stop { color: var(--muted); }
|
|
360
425
|
.server-stop:hover { color: var(--err); border-color: var(--err); }
|
|
361
426
|
/* Elevation prompt (VS-Code-Workspace-Trust style) — a modal over the whole view, since a
|
|
@@ -789,18 +854,10 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
789
854
|
.md .bulb-embed:hover .bulb-controls .overlay-pill,
|
|
790
855
|
.md .bulb-controls .overlay-pill:focus-visible { opacity: 1; }
|
|
791
856
|
.md .bulb-breakout:disabled { cursor: default; opacity: 1; }
|
|
792
|
-
/* Breakout in flight:
|
|
793
|
-
rather than dead —
|
|
794
|
-
|
|
795
|
-
.md .bulb-breakout.launching {
|
|
796
|
-
color: var(--accent); border-color: var(--accent);
|
|
797
|
-
background: linear-gradient(100deg, transparent 30%, color-mix(in srgb, var(--accent) 35%, transparent) 50%, transparent 70%);
|
|
798
|
-
background-size: 250% 100%;
|
|
799
|
-
animation: bulb-shimmer 2.2s linear infinite;
|
|
800
|
-
}
|
|
801
|
-
@media (prefers-reduced-motion: reduce) {
|
|
802
|
-
.md .bulb-breakout.launching { animation: none; background: color-mix(in srgb, var(--accent) 18%, transparent); }
|
|
803
|
-
}
|
|
857
|
+
/* Breakout in flight: shimmers through the ~2s write+spawn wait so the click reads as "working"
|
|
858
|
+
rather than dead — same treatment as the launcher's play button (.bulb-launch.launching).
|
|
859
|
+
:disabled already keeps it visible if the pointer leaves. */
|
|
860
|
+
.md .bulb-breakout.launching { color: var(--accent); border-color: var(--accent); }
|
|
804
861
|
/* Copy pill "copied" flash — mirrors the message .copy.done accent cue, scoped to the
|
|
805
862
|
embed control so it doesn't pick up the bubble-hover reveal. */
|
|
806
863
|
.md .bulb-copy.done { color: var(--accent); border-color: var(--accent); }
|