@cplieger/web-terminal-ui 8.6.0-dev.1 → 8.6.0-dev.10

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.
@@ -240,6 +240,12 @@ export interface AttentionReporter {
240
240
  /** Render `state`. Idempotent: a state equal to the last one reported touches
241
241
  * nothing, so a status sweep may call it every tick. */
242
242
  report(state: AttentionState): void;
243
+ /** Write the latest reported count to the app badge again on the next task,
244
+ * changed or not; a `report` that writes the badge before then satisfies it.
245
+ * The page cannot read the badge back and a platform may drop a write
246
+ * silently, so a caller resyncs wherever the two may have diverged, ahead of
247
+ * the report that follows. A no-op before the first report. */
248
+ resync(): void;
243
249
  }
244
250
 
245
251
  /** The notification a session status event carries (OSC 9 `<message>`). Both
@@ -276,7 +282,10 @@ export interface Notifier {
276
282
  arm(): void;
277
283
  /** Note a user gesture: the ONLY moment a permission prompt may be raised. */
278
284
  gesture(): void;
279
- /** Drop a closed session's dedupe state. */
285
+ /** Close the notification delivered for `id`, if one is still up: the user has
286
+ * seen that session, so the banner no longer reports anything unread. */
287
+ retract(id: string): void;
288
+ /** Drop a closed session's dedupe state and retract its notification. */
280
289
  forget(id: string): void;
281
290
  }
282
291
 
package/src/predict.ts CHANGED
@@ -86,7 +86,7 @@ export function createPredictor(): Predictor {
86
86
  if (predCol > 0) {
87
87
  predCol--;
88
88
  } else if (predRow > 0) {
89
- // kiro-cli's prompt wraps a Backspace at col 0 to the previous row.
89
+ // A line editor wraps a Backspace at col 0 to the previous row.
90
90
  predRow--;
91
91
  predCol = cols - 1;
92
92
  }
@@ -10,14 +10,12 @@ import { type TabbedPresetOptions, buildTabbed } from "./tabbed.js";
10
10
  /** Tabbed UI for an agent shell. Same features as presetTabbed, tuned for
11
11
  * sessions that ARE agents:
12
12
  * - `presumeReports`: every session's program is an agent that WILL report
13
- * OSC 9;4 progress, so the idle activity dot shows from tab creation
14
- * instead of popping in seconds later when the agent has booted far enough
15
- * to first report; the server's sticky reportsActivity flag then merely
16
- * confirms. (presetTabbed keeps the evidence-driven reveal instead: a
17
- * plain shell never grows a meaningless dot.)
18
- * One agent-vs-generic distinction lives SERVER-side rather than here: the
19
- * status classifier that maps OSC 9 notifications to done/needs-input. Tab
20
- * names are server-side too but are not agent-specific — see buildTabbed. */
13
+ * activity (OSC 7501 status, OSC 9;4 progress or a classified OSC 9
14
+ * notification), so the idle activity dot
15
+ * shows from tab creation instead of popping in when the agent first
16
+ * reports; the server's sticky reportsActivity flag then merely confirms.
17
+ * (presetTabbed keeps the evidence-driven reveal: a plain shell never grows
18
+ * a meaningless dot.) */
21
19
  export function presetAgentTabbed(opts: TabbedPresetOptions = {}): TerminalFeature<unknown>[] {
22
20
  return buildTabbed(true, opts);
23
21
  }
@@ -1,8 +1,7 @@
1
1
  /**
2
2
  * presetTabbed (and the shared tabbed composition builder): the full
3
3
  * multi-session UI. Requires a server that speaks the session API
4
- * (`/api/sessions`, `/ws?session=`, and the status SSE `/api/sessions/events`),
5
- * such as `web-terminal-server` or `web-terminal-kiro`.
4
+ * (`/api/sessions`, `/ws?session=`, and the status SSE `/api/sessions/events`).
6
5
  *
7
6
  * @module
8
7
  */
@@ -26,22 +25,15 @@ export interface TabbedPresetOptions {
26
25
  attentionIcons?: boolean;
27
26
  }
28
27
 
29
- /** buildTabbed composes the tabbed UI: the single-pane touch features, an
30
- * externally-driven mobile toolbar (its grid is opened from a keyboard button
31
- * in the tab bar, not its own toggle), the activity monitor (the status-SSE
32
- * data source), tabs wired to both, and animations. The activity dot reveals
33
- * itself per tab only when a session reports activity (OSC 9;4) — or from tab
34
- * creation when the agent composition presumes it — so the monitor is always
35
- * included; a plain shell under presetTabbed just never shows a dot. The
36
- * toolbar and monitor are ordered before tabs because tabs reads their APIs
37
- * via ctx.use. Shared by presetTabbed and presetAgentTabbed (agent-tabbed.ts),
38
- * which now differ only in presumed activity reporting (presumeReports).
39
- *
40
- * Titles are not a preset concern. The ENGINE resolves a session's name — its
41
- * pinned name, a title its host pushed, the program's OSC window title, or its
42
- * own foreground-process/cwd inference — and both presets render what it reports. A
43
- * browser that re-derived any of that could only disagree with the server and
44
- * with every other client attached to the same session. */
28
+ /** buildTabbed composes the tabbed UI: the single-pane touch features, a mobile
29
+ * toolbar opened from the tab bar's keyboard button, the activity monitor (the
30
+ * status SSE), tabs wired to both, and animations. The monitor is always
31
+ * included: a tab's dot shows only once its session reports activity (OSC
32
+ * 7501 status, OSC 9;4 progress or a classified OSC 9 notification), or from
33
+ * creation under presumeReports.
34
+ * The toolbar and monitor precede tabs, which reads their APIs via ctx.use.
35
+ * Titles are the engine's: a browser that re-derived one would disagree with
36
+ * the server and every other client. Shared with presetAgentTabbed. */
45
37
  export function buildTabbed(
46
38
  agentShell: boolean,
47
39
  opts: TabbedPresetOptions = {},
package/src/presets.ts CHANGED
@@ -11,13 +11,14 @@
11
11
  * The tabbed presets (presetTabbed / presetAgentTabbed) share the same feature
12
12
  * set (both include the activity monitor, which drives the per-tab activity
13
13
  * dot). They differ in one flag, `presumeReports`: presetAgentTabbed presumes
14
- * every session's program is an agent that WILL report OSC 9;4 progress, so the
15
- * idle dot shows from tab creation instead of appearing seconds later on the
16
- * first report, while presetTabbed keeps the evidence-driven reveal so a plain
17
- * shell never grows a meaningless dot. Session NAMES are not a preset concern:
18
- * the engine resolves them, including a title its host pushes.
19
- * The generic-vs-agent STATUS distinction stays server-side — an agent server
20
- * sets a classifier mapping OSC 9 notifications to done/needs-input.
14
+ * every session's program is an agent that WILL report activity (OSC 7501
15
+ * status, OSC 9;4 progress or a classified OSC 9 notification), so the idle dot
16
+ * shows from tab creation instead of appearing seconds later on the first
17
+ * report, while presetTabbed keeps the evidence-driven reveal so a plain shell
18
+ * never grows a meaningless dot.
19
+ * Session NAMES are not a preset concern: the engine resolves them, including a
20
+ * title its host pushes. Which status a session holds is the server's: the UI
21
+ * renders the status it is sent.
21
22
  *
22
23
  * @module
23
24
  */
@@ -1,56 +0,0 @@
1
- // A box that starts between device pixels draws every edge of a filled shape
2
- // as a partial pixel. Layout decides where the box starts, and CSS cannot round
3
- // a position to the device grid, so the correction is measured and written.
4
-
5
- import { windowOf } from "../../kernel/realm.js";
6
-
7
- /** CSS px that move `v` onto the nearest whole device pixel. `v` is read to
8
- * 1/1000 of a device pixel first: subtracting an applied offset leaves float
9
- * residue, which would tip a half-pixel position either way. */
10
- function toWholeDevicePx(v: number, dpr: number): number {
11
- const device = Math.round(v * dpr * 1000) / 1000;
12
- return (Math.round(device) - device) / dpr;
13
- }
14
-
15
- /** Keeps `el`'s box on whole device pixels, moved by at most half of one
16
- * through its `translate`, which it owns from then on. Re-measured when `el`
17
- * or an ancestor up to `root` changes size and when the device pixel ratio
18
- * changes; a move that resizes nothing on that chain is not followed.
19
- * @returns the release, which stops following and keeps the last offset. */
20
- export function snapToDevicePixels(el: SVGElement, root: HTMLElement): () => void {
21
- const win = windowOf(el.ownerDocument);
22
- let applied = { x: 0, y: 0 };
23
- const snap = (): void => {
24
- const box = el.getBoundingClientRect();
25
- const dpr = win.devicePixelRatio;
26
- const x = toWholeDevicePx(box.left - applied.x, dpr);
27
- const y = toWholeDevicePx(box.top - applied.y, dpr);
28
- applied = { x, y };
29
- el.style.translate = x === 0 && y === 0 ? "" : `${String(x)}px ${String(y)}px`;
30
- };
31
-
32
- const resize = new win.ResizeObserver(snap);
33
- for (let node: Element | null = el; node !== null; node = node.parentElement) {
34
- resize.observe(node);
35
- if (node === root) {
36
- break;
37
- }
38
- }
39
- // A ratio change resizes nothing in CSS px, and each query matches one ratio.
40
- let ratio: MediaQueryList | undefined;
41
- const onRatio = (): void => {
42
- watchRatio();
43
- snap();
44
- };
45
- function watchRatio(): void {
46
- ratio?.removeEventListener("change", onRatio);
47
- ratio = win.matchMedia(`(resolution: ${String(win.devicePixelRatio)}dppx)`);
48
- ratio.addEventListener("change", onRatio);
49
- }
50
- watchRatio();
51
-
52
- return () => {
53
- resize.disconnect();
54
- ratio?.removeEventListener("change", onRatio);
55
- };
56
- }