typebulb 0.55.1 → 0.55.3
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 +251 -167
- package/dist/agents/claude/styles.css +75 -0
- package/dist/agents/codex/client.js +253 -169
- package/dist/agents/codex/styles.css +75 -0
- package/dist/agents/pi/client.js +306 -222
- package/dist/agents/pi/styles.css +75 -0
- package/dist/format/index.d.ts +1 -0
- package/dist/format/index.d.ts.map +1 -1
- package/dist/format/index.js +1 -0
- package/dist/format/index.js.map +1 -1
- package/dist/format/scrollRestore.d.ts +21 -0
- package/dist/format/scrollRestore.d.ts.map +1 -0
- package/dist/format/scrollRestore.js +89 -0
- package/dist/format/scrollRestore.js.map +1 -0
- package/dist/index.js +247 -164
- package/dist/render.js +151 -68
- package/dist/servers.js +36 -36
- package/package.json +1 -1
|
@@ -337,6 +337,81 @@ html:not([data-theme="dark"]) .turn-view > .shimmer-text {
|
|
|
337
337
|
rides a whole buffering window). */
|
|
338
338
|
.pill-busy { border-color: var(--accent); --shimmer-base: var(--panel); --shimmer-mix: 25%; }
|
|
339
339
|
|
|
340
|
+
/* ── Tooltip — the styled stand-in for native `title`, and only on FIXED EXPLANATORY copy: text
|
|
341
|
+
that teaches what a control does. Dynamic identifiers (a path, a model id, an mcp tool's full
|
|
342
|
+
name) keep the native attribute, which is better at them: they run long, they want to wrap, and
|
|
343
|
+
`title` is the reliable "show me the whole string" affordance a bounded box is worse at. One
|
|
344
|
+
attribute opts a control in, no wrapper component and no JS, and it shows on :focus-visible,
|
|
345
|
+
which native `title` never does for a keyboard user. `data-tip` is not an accessible name, so a
|
|
346
|
+
glyph-only control still needs its own ariaLabel.
|
|
347
|
+
Three non-obvious bits. :where() zeroes the position rule's specificity, so a control that is
|
|
348
|
+
ALREADY positioned (the filter's inset × and 🔬) keeps its own and is its own containing block
|
|
349
|
+
anyway. display:none at rest rather than opacity:0, because an absolutely positioned box still
|
|
350
|
+
counts toward a scroll container's scrollable overflow and .messages' stuck-to-bottom test is
|
|
351
|
+
measured in pixels. The open delay therefore rides an animation (fill-mode both holds the
|
|
352
|
+
from-state through the delay), since a display switch cannot transition. ── */
|
|
353
|
+
:where([data-tip]) { position: relative; }
|
|
354
|
+
[data-tip]::after {
|
|
355
|
+
content: attr(data-tip);
|
|
356
|
+
display: none;
|
|
357
|
+
position: absolute;
|
|
358
|
+
z-index: 40;
|
|
359
|
+
top: calc(100% + 7px);
|
|
360
|
+
left: 50%;
|
|
361
|
+
transform: translateX(-50%);
|
|
362
|
+
width: max-content;
|
|
363
|
+
max-width: min(22rem, 60vw);
|
|
364
|
+
padding: .3rem .55rem;
|
|
365
|
+
background: var(--panel);
|
|
366
|
+
color: var(--fg);
|
|
367
|
+
border: 1px solid var(--border);
|
|
368
|
+
border-radius: 8px;
|
|
369
|
+
box-shadow: 0 8px 30px rgba(0, 0, 0, .45);
|
|
370
|
+
/* Its own typography, never the host's (a stub label is italic, a pill is not) — but the same
|
|
371
|
+
SIZE as the chrome it explains, .8rem, which is both the statusbar's and the tab row's. A tip
|
|
372
|
+
set larger than the control it belongs to reads as shouting. Full --fg carries it instead. */
|
|
373
|
+
font-size: .8rem;
|
|
374
|
+
font-style: normal;
|
|
375
|
+
font-weight: 400;
|
|
376
|
+
line-height: 1.4;
|
|
377
|
+
/* Centred and balanced, because the box CANNOT hug wrapped text: `width: max-content` capped by
|
|
378
|
+
max-width means any tip too long for one line sits at exactly max-width, whatever it wraps to.
|
|
379
|
+
Left-aligned, the leftover on the last line then reads as dead space (the model pill's tip, the
|
|
380
|
+
only one long enough to wrap). Balancing evens the lines and centring splits what's left to both
|
|
381
|
+
sides, so a two-line tip reads as a block instead of a full line plus a stub. */
|
|
382
|
+
text-align: center;
|
|
383
|
+
text-wrap: balance;
|
|
384
|
+
/* pre-line, not normal: a tip built from two lines (the session pill's cwd over its id) keeps the
|
|
385
|
+
break native `title` gave it, and everything else still wraps at the max-width. */
|
|
386
|
+
white-space: pre-line;
|
|
387
|
+
pointer-events: none;
|
|
388
|
+
}
|
|
389
|
+
[data-tip]:hover::after,
|
|
390
|
+
[data-tip]:focus-visible::after { display: block; animation: tip-in .12s ease .4s both; }
|
|
391
|
+
@keyframes tip-in { from { opacity: 0; } to { opacity: 1; } }
|
|
392
|
+
/* One tip nested in another (the diff pill's × sits inside the pill body): hovering the inner one
|
|
393
|
+
hovers the outer too, so both boxes would open. Only the innermost shows, which is how native
|
|
394
|
+
`title` resolves it. */
|
|
395
|
+
[data-tip]:has([data-tip]:hover)::after { display: none; }
|
|
396
|
+
/* Pressed: tooltips are dismissed until the pointer reaches a different one (ui.ts
|
|
397
|
+
armTooltipDismiss). Out-specifies the :hover rule on its own, no !important needed. */
|
|
398
|
+
body.tips-off [data-tip]::after { display: none; }
|
|
399
|
+
/* Bottom-anchored surfaces open upward, like their own menus: under the statusbar and under the
|
|
400
|
+
composer's action row there is no room to drop into. */
|
|
401
|
+
:is(.statusbar, .composer) [data-tip]::after { top: auto; bottom: calc(100% + 7px); }
|
|
402
|
+
/* ...and grow LEFT from the control's right edge, which is the "end" alignment the statusbar menus
|
|
403
|
+
already take by default (.pop-center / .pop-start above are their opt-outs). Every one of these
|
|
404
|
+
clusters is right-aligned, so a centred box runs past the viewport — the bulbs pill's tip was
|
|
405
|
+
clipped — and inside a menu it is also the direction that stays within its overflow:hidden for a
|
|
406
|
+
control inset at the right edge. The transcript's own tips stay centred: they sit mid-column. */
|
|
407
|
+
:is(.statusbar, .composer, .picker, .servers-pop) [data-tip]::after { left: auto; right: 0; transform: none; }
|
|
408
|
+
/* ...except where the pill's WRAP already names an alignment for its menu. A tip and the menu it
|
|
409
|
+
belongs to must open the same way, so both read off the same tag (.pop-center / .pop-start above)
|
|
410
|
+
and neither can be set on its own. Direct child, like the menu rules: a control nested inside a
|
|
411
|
+
pill (the diff pill's ×) keeps the cluster's end alignment, which is right at a pill's far edge. */
|
|
412
|
+
.pop-center > [data-tip]::after { right: auto; left: 50%; transform: translateX(-50%); }
|
|
413
|
+
.pop-start > [data-tip]::after { right: auto; left: 0; transform: none; }
|
|
414
|
+
|
|
340
415
|
/* ── The composer (TB-Agent-Composer.md): the prompt panel under the transcript. In flow (not
|
|
341
416
|
overlaid like the statusbar) — it owns real height and must never cover content. ── */
|
|
342
417
|
.composer { position: relative; flex: none; background: var(--bg); padding-inline: 1.75rem; }
|
package/dist/format/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../format/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../format/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA"}
|
package/dist/format/index.js
CHANGED
package/dist/format/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../format/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../format/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scroll-restoration engine a rendered bulb document ships (specs/Scroll-Restoration.md).
|
|
3
|
+
* ONE copy, beside pageShell.ts and on its argument: both hosts must emit the same behavior or a
|
|
4
|
+
* bulb restores differently local vs published. A script string for the page <head>; the host's
|
|
5
|
+
* own snippet then calls `window.__tbScroll.init({ initialY, save })` to say where the offset
|
|
6
|
+
* comes from and where reports go (the CLI: the page's sessionStorage; the sandbox: a postMessage
|
|
7
|
+
* to the parent).
|
|
8
|
+
*
|
|
9
|
+
* init({ initialY, save }) -> arm the re-apply loop at initialY (0: nothing to do); save(y) is
|
|
10
|
+
* the throttled reporter's sink
|
|
11
|
+
* restore(y) -> re-arm at y (a traversal landed); undefined or 0 scrolls to top
|
|
12
|
+
* top() -> a page-shaped move landed: disarm, scroll to 0, report 0
|
|
13
|
+
* flush() -> report the pending offset now (before a route change goes out)
|
|
14
|
+
*
|
|
15
|
+
* While armed, every frame writes `y` once the document is tall enough, and the loop stands down
|
|
16
|
+
* on the first scroll it did not author (input, the browser's scroll anchoring, a bulb scrolling
|
|
17
|
+
* itself) or TAIL_MS after `load`. A clamp (the document shrank under us) is exempt. Native
|
|
18
|
+
* restoration gives up at `load`; this covers the tail a bulb that renders after load needs.
|
|
19
|
+
*/
|
|
20
|
+
export declare const scrollRestoreEngine = "(function () {\n var W = window, D = document;\n var TAIL_MS = 1500, TOL = 2, THROTTLE_MS = 200;\n var SCROLL_KEYS = { ' ': 1, ArrowUp: 1, ArrowDown: 1, PageUp: 1, PageDown: 1, Home: 1, End: 1 };\n var save = null;\n var target = 0, armed = false, applied = false, lastWritten = -1, raf = 0, deadline = 0;\n var pending, timer = 0;\n\n var maxY = function () { var d = D.documentElement; return Math.max(0, d.scrollHeight - d.clientHeight); };\n var write = function (y) {\n lastWritten = y;\n try { W.scrollTo({ top: y, left: 0, behavior: 'instant' }); } catch (e) { W.scrollTo(0, y); }\n };\n var disarm = function () {\n armed = false;\n if (raf) { cancelAnimationFrame(raf); raf = 0; }\n if (deadline) { clearTimeout(deadline); deadline = 0; }\n };\n var tick = function () {\n if (!armed) return;\n if (target <= maxY()) { applied = true; if (Math.abs(W.scrollY - target) > TOL) write(target); }\n raf = requestAnimationFrame(tick);\n };\n var expire = function () {\n if (!armed) return;\n if (!applied) { var y = Math.min(target, maxY()); if (y > 0) write(y); }\n disarm();\n };\n var arm = function (y) {\n disarm();\n target = y; applied = false;\n if (!(y > 0)) return;\n armed = true;\n raf = requestAnimationFrame(tick);\n var start = function () { if (armed && !deadline) deadline = setTimeout(expire, TAIL_MS); };\n if (D.readyState === 'complete') start(); else W.addEventListener('load', start, { once: true });\n };\n\n var report = function (y) { if (save) { try { save(y); } catch (e) {} } };\n var flush = function () {\n if (timer) { clearTimeout(timer); timer = 0; }\n if (pending !== undefined) { var y = pending; pending = undefined; report(y); }\n };\n\n W.addEventListener('scroll', function () {\n var y = W.scrollY;\n pending = y;\n if (!timer) timer = setTimeout(flush, THROTTLE_MS);\n if (!armed || Math.abs(y - lastWritten) <= TOL) return;\n if (y < target && Math.abs(y - maxY()) <= TOL) return;\n disarm();\n }, { passive: true });\n W.addEventListener('wheel', disarm, { capture: true, passive: true });\n W.addEventListener('touchstart', disarm, { capture: true, passive: true });\n W.addEventListener('keydown', function (e) { if (SCROLL_KEYS[e.key]) disarm(); }, { capture: true });\n\n W.__tbScroll = {\n init: function (o) {\n save = o && typeof o.save === 'function' ? o.save : null;\n arm(o ? Number(o.initialY) : 0);\n },\n restore: function (y) {\n y = Number(y);\n if (y > 0 && isFinite(y)) arm(y); else { disarm(); write(0); }\n },\n top: function () { disarm(); write(0); report(0); },\n flush: flush\n };\n})();";
|
|
21
|
+
//# sourceMappingURL=scrollRestore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrollRestore.d.ts","sourceRoot":"","sources":["../../format/src/scrollRestore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,kpFAoE1B,CAAA"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The scroll-restoration engine a rendered bulb document ships (specs/Scroll-Restoration.md).
|
|
3
|
+
* ONE copy, beside pageShell.ts and on its argument: both hosts must emit the same behavior or a
|
|
4
|
+
* bulb restores differently local vs published. A script string for the page <head>; the host's
|
|
5
|
+
* own snippet then calls `window.__tbScroll.init({ initialY, save })` to say where the offset
|
|
6
|
+
* comes from and where reports go (the CLI: the page's sessionStorage; the sandbox: a postMessage
|
|
7
|
+
* to the parent).
|
|
8
|
+
*
|
|
9
|
+
* init({ initialY, save }) -> arm the re-apply loop at initialY (0: nothing to do); save(y) is
|
|
10
|
+
* the throttled reporter's sink
|
|
11
|
+
* restore(y) -> re-arm at y (a traversal landed); undefined or 0 scrolls to top
|
|
12
|
+
* top() -> a page-shaped move landed: disarm, scroll to 0, report 0
|
|
13
|
+
* flush() -> report the pending offset now (before a route change goes out)
|
|
14
|
+
*
|
|
15
|
+
* While armed, every frame writes `y` once the document is tall enough, and the loop stands down
|
|
16
|
+
* on the first scroll it did not author (input, the browser's scroll anchoring, a bulb scrolling
|
|
17
|
+
* itself) or TAIL_MS after `load`. A clamp (the document shrank under us) is exempt. Native
|
|
18
|
+
* restoration gives up at `load`; this covers the tail a bulb that renders after load needs.
|
|
19
|
+
*/
|
|
20
|
+
export const scrollRestoreEngine = `(function () {
|
|
21
|
+
var W = window, D = document;
|
|
22
|
+
var TAIL_MS = 1500, TOL = 2, THROTTLE_MS = 200;
|
|
23
|
+
var SCROLL_KEYS = { ' ': 1, ArrowUp: 1, ArrowDown: 1, PageUp: 1, PageDown: 1, Home: 1, End: 1 };
|
|
24
|
+
var save = null;
|
|
25
|
+
var target = 0, armed = false, applied = false, lastWritten = -1, raf = 0, deadline = 0;
|
|
26
|
+
var pending, timer = 0;
|
|
27
|
+
|
|
28
|
+
var maxY = function () { var d = D.documentElement; return Math.max(0, d.scrollHeight - d.clientHeight); };
|
|
29
|
+
var write = function (y) {
|
|
30
|
+
lastWritten = y;
|
|
31
|
+
try { W.scrollTo({ top: y, left: 0, behavior: 'instant' }); } catch (e) { W.scrollTo(0, y); }
|
|
32
|
+
};
|
|
33
|
+
var disarm = function () {
|
|
34
|
+
armed = false;
|
|
35
|
+
if (raf) { cancelAnimationFrame(raf); raf = 0; }
|
|
36
|
+
if (deadline) { clearTimeout(deadline); deadline = 0; }
|
|
37
|
+
};
|
|
38
|
+
var tick = function () {
|
|
39
|
+
if (!armed) return;
|
|
40
|
+
if (target <= maxY()) { applied = true; if (Math.abs(W.scrollY - target) > TOL) write(target); }
|
|
41
|
+
raf = requestAnimationFrame(tick);
|
|
42
|
+
};
|
|
43
|
+
var expire = function () {
|
|
44
|
+
if (!armed) return;
|
|
45
|
+
if (!applied) { var y = Math.min(target, maxY()); if (y > 0) write(y); }
|
|
46
|
+
disarm();
|
|
47
|
+
};
|
|
48
|
+
var arm = function (y) {
|
|
49
|
+
disarm();
|
|
50
|
+
target = y; applied = false;
|
|
51
|
+
if (!(y > 0)) return;
|
|
52
|
+
armed = true;
|
|
53
|
+
raf = requestAnimationFrame(tick);
|
|
54
|
+
var start = function () { if (armed && !deadline) deadline = setTimeout(expire, TAIL_MS); };
|
|
55
|
+
if (D.readyState === 'complete') start(); else W.addEventListener('load', start, { once: true });
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
var report = function (y) { if (save) { try { save(y); } catch (e) {} } };
|
|
59
|
+
var flush = function () {
|
|
60
|
+
if (timer) { clearTimeout(timer); timer = 0; }
|
|
61
|
+
if (pending !== undefined) { var y = pending; pending = undefined; report(y); }
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
W.addEventListener('scroll', function () {
|
|
65
|
+
var y = W.scrollY;
|
|
66
|
+
pending = y;
|
|
67
|
+
if (!timer) timer = setTimeout(flush, THROTTLE_MS);
|
|
68
|
+
if (!armed || Math.abs(y - lastWritten) <= TOL) return;
|
|
69
|
+
if (y < target && Math.abs(y - maxY()) <= TOL) return;
|
|
70
|
+
disarm();
|
|
71
|
+
}, { passive: true });
|
|
72
|
+
W.addEventListener('wheel', disarm, { capture: true, passive: true });
|
|
73
|
+
W.addEventListener('touchstart', disarm, { capture: true, passive: true });
|
|
74
|
+
W.addEventListener('keydown', function (e) { if (SCROLL_KEYS[e.key]) disarm(); }, { capture: true });
|
|
75
|
+
|
|
76
|
+
W.__tbScroll = {
|
|
77
|
+
init: function (o) {
|
|
78
|
+
save = o && typeof o.save === 'function' ? o.save : null;
|
|
79
|
+
arm(o ? Number(o.initialY) : 0);
|
|
80
|
+
},
|
|
81
|
+
restore: function (y) {
|
|
82
|
+
y = Number(y);
|
|
83
|
+
if (y > 0 && isFinite(y)) arm(y); else { disarm(); write(0); }
|
|
84
|
+
},
|
|
85
|
+
top: function () { disarm(); write(0); report(0); },
|
|
86
|
+
flush: flush
|
|
87
|
+
};
|
|
88
|
+
})();`;
|
|
89
|
+
//# sourceMappingURL=scrollRestore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrollRestore.js","sourceRoot":"","sources":["../../format/src/scrollRestore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MAoE7B,CAAA"}
|