@north-light/crouter 0.3.192 → 0.3.193

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.
Files changed (167) hide show
  1. package/dist/api/client.d.ts +31 -10
  2. package/dist/api/client.js +83 -24
  3. package/dist/api/dto/bash-jobs.d.ts +22 -0
  4. package/dist/api/dto/bash-jobs.js +9 -0
  5. package/dist/api/dto/human.d.ts +3 -4
  6. package/dist/api/dto/inbox.d.ts +106 -65
  7. package/dist/api/dto/inbox.js +2 -8
  8. package/dist/api/index.d.ts +1 -0
  9. package/dist/api/index.js +1 -0
  10. package/dist/api/routes.d.ts +7 -0
  11. package/dist/api/routes.js +7 -0
  12. package/dist/builtin-memory/internal/storage-tiers.md +1 -1
  13. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/serial/provider-rotation.test.ts +101 -3
  14. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.d.ts +6 -2
  15. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +68 -14
  16. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +87 -13
  17. package/dist/clients/attach/__tests__/chat-view-snapshot-ordering.test.js +2 -2
  18. package/dist/clients/attach/__tests__/group-activity.test.js +2 -2
  19. package/dist/clients/attach/chrome/canvas-panels.d.ts +1 -1
  20. package/dist/clients/attach/chrome/canvas-panels.js +1 -1
  21. package/dist/clients/attach/input/overlay-owner.js +1 -1
  22. package/dist/clients/attach/overlays/auth.js +34 -8
  23. package/dist/clients/attach/overlays/pickers.d.ts +5 -8
  24. package/dist/clients/attach/overlays/pickers.js +37 -43
  25. package/dist/clients/attach/viewer.js +643 -620
  26. package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +38 -128
  27. package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +72 -148
  28. package/dist/clients/inbox/controller.d.ts +2 -3
  29. package/dist/clients/inbox/controller.js +41 -42
  30. package/dist/clients/inbox/page-adapter.d.ts +33 -0
  31. package/dist/clients/inbox/page-adapter.js +54 -0
  32. package/dist/clients/inbox/resolve.d.ts +2 -2
  33. package/dist/clients/inbox/resolve.js +1 -1
  34. package/dist/clients/inbox/review-visibility.d.ts +1 -1
  35. package/dist/clients/inbox/review-visibility.js +1 -1
  36. package/dist/clients/inbox/tui/ansi.d.ts +1 -1
  37. package/dist/clients/inbox/tui/ansi.js +2 -2
  38. package/dist/clients/inbox/tui/input.d.ts +1 -3
  39. package/dist/clients/inbox/tui/input.js +242 -553
  40. package/dist/clients/inbox/tui/panel.d.ts +3 -1
  41. package/dist/clients/inbox/tui/panel.js +59 -187
  42. package/dist/clients/inbox/tui/render.d.ts +0 -25
  43. package/dist/clients/inbox/tui/render.js +165 -416
  44. package/dist/clients/inbox/tui/slots.d.ts +32 -0
  45. package/dist/clients/inbox/tui/slots.js +156 -0
  46. package/dist/clients/inbox/tui/types.d.ts +25 -13
  47. package/dist/clients/inbox/tui.js +5 -3
  48. package/dist/commands/attention.js +2 -2
  49. package/dist/commands/human/doc.js +66 -20
  50. package/dist/commands/human/prompts.d.ts +3 -3
  51. package/dist/commands/human/prompts.js +158 -112
  52. package/dist/commands/human/queue.d.ts +0 -1
  53. package/dist/commands/human/queue.js +34 -94
  54. package/dist/commands/human/shared.d.ts +10 -4
  55. package/dist/commands/human/shared.js +62 -13
  56. package/dist/commands/human.js +8 -4
  57. package/dist/commands/memory/read.js +13 -5
  58. package/dist/commands/node/bash.js +15 -28
  59. package/dist/commands/surface/node/focus.js +1 -1
  60. package/dist/core/__tests__/human-deliver.test.js +40 -46
  61. package/dist/core/__tests__/seam/broker-attach-stream.test.js +7 -1
  62. package/dist/core/__tests__/serial/human-deliver-e2e.test.js +40 -31
  63. package/dist/core/__tests__/serial/spawn-root.test.js +3 -1
  64. package/dist/core/__tests__/spawn-worktree-id-conflict.test.js +3 -1
  65. package/dist/core/auth-interaction.d.ts +2 -2
  66. package/dist/core/bash-jobs.d.ts +32 -0
  67. package/dist/core/bash-jobs.js +72 -1
  68. package/dist/core/canvas/__tests__/attention.test.js +16 -10
  69. package/dist/core/canvas/__tests__/render-remote.test.js +9 -3
  70. package/dist/core/canvas/attention.js +1 -1
  71. package/dist/core/canvas/browse/app.js +1 -1
  72. package/dist/core/canvas/nav-model.d.ts +1 -1
  73. package/dist/core/canvas/nav-model.js +1 -1
  74. package/dist/core/canvas/remote-canvas-source.js +1 -1
  75. package/dist/core/config.js +4 -1
  76. package/dist/core/human/__tests__/page-render.test.d.ts +1 -0
  77. package/dist/core/human/__tests__/page-render.test.js +49 -0
  78. package/dist/core/human/__tests__/page-tickets.test.d.ts +1 -0
  79. package/dist/core/human/__tests__/page-tickets.test.js +112 -0
  80. package/dist/core/human/__tests__/page.test.d.ts +1 -0
  81. package/dist/core/human/__tests__/page.test.js +139 -0
  82. package/dist/core/human/__tests__/serial/inbox-core.test.js +42 -32
  83. package/dist/core/human/claim.js +2 -2
  84. package/dist/core/human/convention.d.ts +4 -4
  85. package/dist/core/human/convention.js +6 -13
  86. package/dist/core/human/markdown-html.d.ts +8 -0
  87. package/dist/core/human/markdown-html.js +581 -0
  88. package/dist/core/human/page-catalog.d.ts +6 -0
  89. package/dist/core/human/page-catalog.js +43 -0
  90. package/dist/core/human/page-render.d.ts +3 -0
  91. package/dist/core/human/page-render.js +131 -0
  92. package/dist/core/human/page-schema.d.ts +312 -0
  93. package/dist/core/human/page-schema.js +291 -0
  94. package/dist/core/human/page-synth.d.ts +9 -0
  95. package/dist/core/human/page-synth.js +17 -0
  96. package/dist/core/human/page.d.ts +22 -0
  97. package/dist/core/human/page.js +427 -0
  98. package/dist/core/human/review-schema.d.ts +19 -0
  99. package/dist/core/human/review-schema.js +34 -0
  100. package/dist/core/human/scan.d.ts +12 -3
  101. package/dist/core/human/scan.js +69 -26
  102. package/dist/core/human/summary.d.ts +5 -6
  103. package/dist/core/human/summary.js +54 -37
  104. package/dist/core/human/tickets.d.ts +61 -11
  105. package/dist/core/human/tickets.js +100 -57
  106. package/dist/core/human/types.d.ts +21 -66
  107. package/dist/core/human/types.js +1 -5
  108. package/dist/core/preview-registry.js +0 -1
  109. package/dist/core/review/ticket-filter.d.ts +1 -1
  110. package/dist/core/review/ticket-filter.js +2 -8
  111. package/dist/core/runtime/broker/read-ops.js +3 -0
  112. package/dist/core/runtime/broker.js +19 -10
  113. package/dist/core/runtime/package-health.js +1 -1
  114. package/dist/core/termrender/termrender.d.ts +1 -1
  115. package/dist/core/termrender/termrender.js +2 -2
  116. package/dist/core/tui/page-host.d.ts +1 -1
  117. package/dist/core/tui/page-host.js +2 -2
  118. package/dist/core/user-settings.d.ts +19 -0
  119. package/dist/core/user-settings.js +22 -0
  120. package/dist/daemon/api/handlers/bash-jobs.d.ts +2 -0
  121. package/dist/daemon/api/handlers/bash-jobs.js +89 -0
  122. package/dist/daemon/api/handlers/human.js +16 -14
  123. package/dist/daemon/api/handlers/inbox.js +311 -227
  124. package/dist/daemon/api/router.d.ts +5 -1
  125. package/dist/daemon/api/router.js +5 -0
  126. package/dist/daemon/api/server.js +2 -0
  127. package/dist/daemon/cron-run.js +23 -10
  128. package/dist/daemon/human/finish.d.ts +8 -3
  129. package/dist/daemon/human/finish.js +35 -15
  130. package/dist/pages/bundle.css +1 -0
  131. package/dist/pages/bundle.js +1347 -0
  132. package/dist/pages/comments.d.ts +34 -0
  133. package/dist/pages/comments.js +72 -0
  134. package/dist/pages/elements/cards.d.ts +17 -0
  135. package/dist/pages/elements/cards.js +785 -0
  136. package/dist/pages/elements/chart.d.ts +1 -0
  137. package/dist/pages/elements/chart.js +648 -0
  138. package/dist/pages/elements/options.d.ts +21 -0
  139. package/dist/pages/elements/options.js +604 -0
  140. package/dist/pages/elements/pages.d.ts +24 -0
  141. package/dist/pages/elements/pages.js +355 -0
  142. package/dist/pages/elements/slot.d.ts +21 -0
  143. package/dist/pages/elements/slot.js +105 -0
  144. package/dist/pages/elements/table.d.ts +19 -0
  145. package/dist/pages/elements/table.js +906 -0
  146. package/dist/pages/elements/text.d.ts +25 -0
  147. package/dist/pages/elements/text.js +951 -0
  148. package/dist/pages/entry.d.ts +8 -0
  149. package/dist/pages/entry.js +8 -0
  150. package/dist/pages/host.d.ts +132 -0
  151. package/dist/pages/host.js +149 -0
  152. package/dist/pages/register.d.ts +2 -0
  153. package/dist/pages/register.js +3 -0
  154. package/dist/pages/slot-config.d.ts +36 -0
  155. package/dist/pages/slot-config.js +50 -0
  156. package/dist/pages/types.d.ts +98 -0
  157. package/dist/pages/types.js +23 -0
  158. package/dist/types.d.ts +4 -0
  159. package/dist/types.js +2 -0
  160. package/package.json +8 -8
  161. package/runtime.lock.json +156 -101
  162. package/dist/clients/inbox/deck-adapter.d.ts +0 -29
  163. package/dist/clients/inbox/deck-adapter.js +0 -63
  164. package/dist/core/human/deck-factories.d.ts +0 -8
  165. package/dist/core/human/deck-factories.js +0 -15
  166. package/dist/core/human/deck-schema.d.ts +0 -70
  167. package/dist/core/human/deck-schema.js +0 -92
@@ -0,0 +1,355 @@
1
+ /**
2
+ * `<crtr-pages>` / `<crtr-page>` — the page's own pager, and the only thing that submits.
3
+ *
4
+ * A response-bearing page wraps its slots in exactly one `<crtr-pages>` containing one or
5
+ * more `<crtr-page>` steps. This element shows one step at a time, carries the step
6
+ * progress and the forward/back affordances, and renders the submit affordance on the
7
+ * final step — the in-document chrome, and nothing else. Dismiss and save are host chrome
8
+ * (a frame around the page, not markup inside it) and never appear here.
9
+ *
10
+ * **Flipping steps is the same motion as flipping tickets, one level down.** The inbox is
11
+ * a pager over pending pages; this is a pager over the steps of one page. So the grammar is
12
+ * deliberately the narrow one both levels share — go to the next unit of work, go back to
13
+ * the previous one, submit — with no third way to move. `n` / `p` are the keys, matching the
14
+ * terminal page panel's within-page motion (`src/clients/inbox/tui/input.ts`), because the
15
+ * kit's `j` / `k` / `x` / `c` belong to the items *inside* a slot and a pager that stole them
16
+ * would make one keystroke mean two levels at once. Submit is `⌘`/`Ctrl` + `Enter` — the
17
+ * inbox's own submit chord, unchanged, because submitting an answer is literally the same
18
+ * act at both levels. The step dots are an indicator, not a control: a page is answered by
19
+ * walking it, not by jumping around it.
20
+ *
21
+ * A single-step page renders no pager chrome at all — no progress, no forward/back — just
22
+ * the submit affordance, because there is nothing to flip.
23
+ */
24
+ import { registerElement } from '../register.js';
25
+ function hostBridge() {
26
+ return window.crtr;
27
+ }
28
+ const NO_HOST_REASON = 'Submit is unavailable — this page is not connected to a host.';
29
+ const PAGE_STYLE = `
30
+ :host {
31
+ display: block;
32
+ }
33
+
34
+ :host([hidden]) {
35
+ display: none;
36
+ }
37
+
38
+ [part="page"] {
39
+ display: grid;
40
+ gap: var(--crtr-space-3);
41
+ }
42
+
43
+ [part="page"]:focus {
44
+ outline: none;
45
+ }
46
+ `;
47
+ const PAGES_STYLE = `
48
+ :host {
49
+ display: block;
50
+ }
51
+
52
+ [part="container"] {
53
+ display: grid;
54
+ gap: var(--crtr-space-4);
55
+ }
56
+
57
+ [part="chrome"] {
58
+ display: flex;
59
+ flex-wrap: wrap;
60
+ align-items: center;
61
+ justify-content: space-between;
62
+ gap: var(--crtr-space-3);
63
+ padding-top: var(--crtr-space-3);
64
+ border-top: 1px solid var(--crtr-color-border);
65
+ }
66
+
67
+ [part="container"][data-steps="1"] [part="chrome"] {
68
+ justify-content: flex-end;
69
+ }
70
+
71
+ [part="progress"] {
72
+ display: flex;
73
+ align-items: center;
74
+ gap: var(--crtr-space-2);
75
+ color: var(--crtr-color-text-muted);
76
+ }
77
+
78
+ [part="progress-track"] {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: var(--crtr-space-1);
82
+ }
83
+
84
+ [part="progress-step"] {
85
+ width: var(--crtr-space-2);
86
+ height: var(--crtr-space-1);
87
+ border-radius: var(--crtr-radius-small);
88
+ background: var(--crtr-color-border);
89
+ }
90
+
91
+ [part="progress-step"][data-state="complete"] {
92
+ background: var(--crtr-color-text-muted);
93
+ }
94
+
95
+ [part="progress-step"][data-state="current"] {
96
+ width: var(--crtr-space-4);
97
+ background: var(--crtr-color-accent);
98
+ }
99
+
100
+ [part="actions"] {
101
+ display: flex;
102
+ align-items: center;
103
+ gap: var(--crtr-space-2);
104
+ }
105
+
106
+ button {
107
+ font: inherit;
108
+ color: var(--crtr-color-text);
109
+ background: var(--crtr-color-surface);
110
+ border: 1px solid var(--crtr-color-border);
111
+ border-radius: var(--crtr-radius-small);
112
+ padding: var(--crtr-space-2) var(--crtr-space-3);
113
+ cursor: pointer;
114
+ }
115
+
116
+ button:hover:not(:disabled) {
117
+ background: var(--crtr-color-surface-hover);
118
+ }
119
+
120
+ button:focus-visible {
121
+ outline: 2px solid var(--crtr-color-focus);
122
+ outline-offset: 2px;
123
+ }
124
+
125
+ button:disabled {
126
+ cursor: not-allowed;
127
+ opacity: 0.5;
128
+ }
129
+
130
+ [part="submit"] {
131
+ color: var(--crtr-color-accent-contrast);
132
+ background: var(--crtr-color-accent);
133
+ border-color: transparent;
134
+ }
135
+
136
+ [part="submit"]:hover:not(:disabled) {
137
+ background: var(--crtr-color-accent);
138
+ filter: brightness(1.08);
139
+ }
140
+
141
+ [part="notice"] {
142
+ color: var(--crtr-color-text-muted);
143
+ }
144
+
145
+ [part="notice"][data-state="error"] {
146
+ color: var(--crtr-color-danger);
147
+ }
148
+
149
+ [hidden] {
150
+ display: none !important;
151
+ }
152
+ `;
153
+ /** One step of a page. Bare on its own; the pager shows and hides it. */
154
+ class CrtrPageElement extends HTMLElement {
155
+ constructor() {
156
+ super();
157
+ const root = this.attachShadow({ mode: 'open' });
158
+ const style = document.createElement('style');
159
+ style.textContent = PAGE_STYLE;
160
+ const page = document.createElement('div');
161
+ page.part.add('page');
162
+ page.append(document.createElement('slot'));
163
+ root.append(style, page);
164
+ }
165
+ }
166
+ class CrtrPagesElement extends HTMLElement {
167
+ static observedAttributes = ['disabled', 'submit-label'];
168
+ #steps = [];
169
+ #index = 0;
170
+ #state = 'idle';
171
+ #error = null;
172
+ #container = document.createElement('div');
173
+ #chrome = document.createElement('div');
174
+ #progress = document.createElement('div');
175
+ #progressLabel = document.createElement('span');
176
+ #progressTrack = document.createElement('span');
177
+ #actions = document.createElement('div');
178
+ #notice = document.createElement('span');
179
+ #prev = document.createElement('button');
180
+ #next = document.createElement('button');
181
+ #submit = document.createElement('button');
182
+ #children = new MutationObserver(() => {
183
+ this.#collectSteps();
184
+ this.#render();
185
+ });
186
+ constructor() {
187
+ super();
188
+ const root = this.attachShadow({ mode: 'open' });
189
+ const style = document.createElement('style');
190
+ style.textContent = PAGES_STYLE;
191
+ const steps = document.createElement('div');
192
+ steps.part.add('steps');
193
+ steps.append(document.createElement('slot'));
194
+ this.#container.part.add('container');
195
+ this.#chrome.part.add('chrome');
196
+ this.#progress.part.add('progress');
197
+ this.#progress.setAttribute('role', 'status');
198
+ this.#progress.setAttribute('aria-live', 'polite');
199
+ this.#progressLabel.part.add('progress-label');
200
+ this.#progressTrack.part.add('progress-track');
201
+ this.#progressTrack.setAttribute('aria-hidden', 'true');
202
+ this.#progress.append(this.#progressLabel, this.#progressTrack);
203
+ this.#prev.part.add('prev');
204
+ this.#prev.type = 'button';
205
+ this.#prev.textContent = 'Back';
206
+ this.#prev.addEventListener('click', () => this.#go(-1));
207
+ this.#next.part.add('next');
208
+ this.#next.type = 'button';
209
+ this.#next.textContent = 'Next';
210
+ this.#next.addEventListener('click', () => this.#go(1));
211
+ this.#submit.part.add('submit');
212
+ this.#submit.type = 'button';
213
+ this.#submit.addEventListener('click', () => void this.#runSubmit());
214
+ this.#notice.part.add('notice');
215
+ this.#actions.part.add('actions');
216
+ this.#actions.append(this.#notice, this.#prev, this.#next, this.#submit);
217
+ this.#chrome.append(this.#progress, this.#actions);
218
+ this.#container.append(steps, this.#chrome);
219
+ root.append(style, this.#container);
220
+ }
221
+ connectedCallback() {
222
+ this.#collectSteps();
223
+ this.#children.observe(this, { childList: true });
224
+ this.ownerDocument.addEventListener('keydown', this.#onKeydown);
225
+ this.#render();
226
+ }
227
+ disconnectedCallback() {
228
+ this.#children.disconnect();
229
+ this.ownerDocument.removeEventListener('keydown', this.#onKeydown);
230
+ }
231
+ attributeChangedCallback() {
232
+ if (this.isConnected)
233
+ this.#render();
234
+ }
235
+ #collectSteps() {
236
+ this.#steps = [...this.children].filter((child) => child instanceof HTMLElement && child.tagName === 'CRTR-PAGE');
237
+ for (const step of this.#steps) {
238
+ if (!step.hasAttribute('tabindex'))
239
+ step.setAttribute('tabindex', '-1');
240
+ }
241
+ this.#index = Math.min(this.#index, Math.max(0, this.#steps.length - 1));
242
+ }
243
+ get #isFinalStep() {
244
+ return this.#index >= this.#steps.length - 1;
245
+ }
246
+ #go(delta, focus = true) {
247
+ const next = this.#index + delta;
248
+ if (next < 0 || next >= this.#steps.length || next === this.#index)
249
+ return;
250
+ this.#index = next;
251
+ this.#error = null;
252
+ this.#render();
253
+ if (focus)
254
+ this.#steps[this.#index]?.focus();
255
+ }
256
+ async #runSubmit() {
257
+ if (this.#state !== 'idle' || this.hasAttribute('disabled') || !this.#isFinalStep)
258
+ return;
259
+ const bridge = hostBridge();
260
+ if (typeof bridge?.submit !== 'function') {
261
+ this.#error = NO_HOST_REASON;
262
+ this.#render();
263
+ return;
264
+ }
265
+ this.#state = 'submitting';
266
+ this.#error = null;
267
+ this.#render();
268
+ try {
269
+ await bridge.submit();
270
+ this.#state = 'submitted';
271
+ }
272
+ catch (error) {
273
+ this.#state = 'idle';
274
+ this.#error = error instanceof Error ? error.message : 'Submit failed.';
275
+ }
276
+ this.#render();
277
+ }
278
+ #onKeydown = (event) => {
279
+ if (event.defaultPrevented)
280
+ return;
281
+ const path = event.composedPath();
282
+ // One `<crtr-pages>` per document, so the pager listens document-wide — but never steals
283
+ // a key that belongs to another pager's subtree (a page rendering a page).
284
+ if (path.some((node) => node instanceof HTMLElement && node.tagName === 'CRTR-PAGES' && node !== this))
285
+ return;
286
+ // The submit chord is modified, so it cannot type a character and fires even from a
287
+ // textarea — exactly the rule the inbox's own ⌘↵ follows.
288
+ if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
289
+ if (!this.#isFinalStep)
290
+ return;
291
+ event.preventDefault();
292
+ void this.#runSubmit();
293
+ return;
294
+ }
295
+ if (event.metaKey || event.ctrlKey || event.altKey || isTextEntry(path))
296
+ return;
297
+ const key = event.key.toLowerCase();
298
+ if (key !== 'n' && key !== 'p')
299
+ return;
300
+ event.preventDefault();
301
+ this.#go(key === 'n' ? 1 : -1);
302
+ };
303
+ #render() {
304
+ const total = this.#steps.length;
305
+ const multiStep = total > 1;
306
+ const current = Math.min(this.#index, Math.max(0, total - 1));
307
+ for (const [position, step] of this.#steps.entries()) {
308
+ step.toggleAttribute('hidden', position !== current);
309
+ }
310
+ this.#container.dataset['steps'] = String(Math.max(1, total));
311
+ this.#container.dataset['step'] = String(current + 1);
312
+ this.#progress.hidden = !multiStep;
313
+ if (multiStep) {
314
+ const label = this.#steps[current]?.getAttribute('label')?.trim();
315
+ this.#progressLabel.textContent = label
316
+ ? `Step ${current + 1} of ${total} · ${label}`
317
+ : `Step ${current + 1} of ${total}`;
318
+ this.#progressTrack.replaceChildren(...this.#steps.map((_step, position) => {
319
+ const dot = document.createElement('span');
320
+ dot.part.add('progress-step');
321
+ dot.dataset['state'] = position === current ? 'current' : position < current ? 'complete' : 'upcoming';
322
+ return dot;
323
+ }));
324
+ }
325
+ this.#prev.hidden = !multiStep;
326
+ this.#prev.disabled = current === 0;
327
+ this.#next.hidden = !multiStep || this.#isFinalStep;
328
+ this.#next.disabled = false;
329
+ this.#submit.hidden = !this.#isFinalStep;
330
+ const bridgeReady = typeof hostBridge()?.submit === 'function';
331
+ this.#submit.disabled = !bridgeReady || this.#state !== 'idle' || this.hasAttribute('disabled');
332
+ this.#submit.textContent =
333
+ this.#state === 'submitting'
334
+ ? 'Submitting…'
335
+ : this.#state === 'submitted'
336
+ ? 'Submitted'
337
+ : this.getAttribute('submit-label')?.trim() || 'Submit';
338
+ const notice = this.#error ??
339
+ (this.#state === 'submitted'
340
+ ? 'Your response was submitted.'
341
+ : !bridgeReady && this.#isFinalStep
342
+ ? NO_HOST_REASON
343
+ : null);
344
+ this.#notice.hidden = notice === null;
345
+ this.#notice.textContent = notice ?? '';
346
+ this.#notice.dataset['state'] = this.#error === null ? 'info' : 'error';
347
+ }
348
+ }
349
+ /** A key typed into writing is writing, never navigation. */
350
+ function isTextEntry(path) {
351
+ return path.some((node) => node instanceof HTMLElement &&
352
+ (node instanceof HTMLInputElement || node instanceof HTMLTextAreaElement || node.isContentEditable));
353
+ }
354
+ registerElement('crtr-page', CrtrPageElement);
355
+ registerElement('crtr-pages', CrtrPagesElement);
@@ -0,0 +1,21 @@
1
+ /**
2
+ * `<crtr-slot>` — the one escape element, for kinds crtr does not own.
3
+ *
4
+ * A page may carry a slot whose `kind` belongs to the product hosting the page (a Northlight
5
+ * workspace component, say) rather than to this kit. crtr validated only that such a slot has
6
+ * an id, a kind, and an object config; it never invents an element for a kind it cannot
7
+ * render. So every one of them arrives as
8
+ * `<crtr-slot kind="product-kind" slot-id="…"><script type="application/json">…</script></crtr-slot>`,
9
+ * and this element does exactly two things: ask the host to render the kind into a mount
10
+ * point, and — when the host has no renderer for it, or there is no host at all — say so by
11
+ * name instead of leaving a hole in the page.
12
+ *
13
+ * The mount point is a light-DOM child, surfaced through the shadow root's `<slot>`. Host
14
+ * content styles itself from the document it is mounted into, and putting it behind this
15
+ * element's shadow boundary would cut it off from that; the JSON config child stays where it
16
+ * is, untouched, so a re-mount reads the same config.
17
+ *
18
+ * A mounted component owns its own answer: it talks to `window.crtr` itself, and this element
19
+ * never invents a response shape for a kind whose shape only the product knows.
20
+ */
21
+ export {};
@@ -0,0 +1,105 @@
1
+ /**
2
+ * `<crtr-slot>` — the one escape element, for kinds crtr does not own.
3
+ *
4
+ * A page may carry a slot whose `kind` belongs to the product hosting the page (a Northlight
5
+ * workspace component, say) rather than to this kit. crtr validated only that such a slot has
6
+ * an id, a kind, and an object config; it never invents an element for a kind it cannot
7
+ * render. So every one of them arrives as
8
+ * `<crtr-slot kind="product-kind" slot-id="…"><script type="application/json">…</script></crtr-slot>`,
9
+ * and this element does exactly two things: ask the host to render the kind into a mount
10
+ * point, and — when the host has no renderer for it, or there is no host at all — say so by
11
+ * name instead of leaving a hole in the page.
12
+ *
13
+ * The mount point is a light-DOM child, surfaced through the shadow root's `<slot>`. Host
14
+ * content styles itself from the document it is mounted into, and putting it behind this
15
+ * element's shadow boundary would cut it off from that; the JSON config child stays where it
16
+ * is, untouched, so a re-mount reads the same config.
17
+ *
18
+ * A mounted component owns its own answer: it talks to `window.crtr` itself, and this element
19
+ * never invents a response shape for a kind whose shape only the product knows.
20
+ */
21
+ import { mountWorkspaceComponent } from '../host.js';
22
+ import { registerElement } from '../register.js';
23
+ import { readSlotConfig } from '../slot-config.js';
24
+ const STYLE = `
25
+ :host {
26
+ display: block;
27
+ }
28
+
29
+ [part="unavailable"] {
30
+ padding: var(--crtr-space-3);
31
+ color: var(--crtr-color-text-muted);
32
+ background: var(--crtr-color-surface);
33
+ border: 1px solid var(--crtr-color-border);
34
+ border-radius: var(--crtr-radius-medium);
35
+ box-shadow: var(--crtr-shadow);
36
+ }
37
+
38
+ [part="unavailable"][hidden] {
39
+ display: none;
40
+ }
41
+
42
+ [part="kind"] {
43
+ color: var(--crtr-color-text);
44
+ font-family: var(--crtr-font-mono);
45
+ }
46
+
47
+ [part="reason"] {
48
+ display: block;
49
+ margin-top: var(--crtr-space-1);
50
+ font-size: 0.9em;
51
+ }
52
+ `;
53
+ class CrtrSlotElement extends HTMLElement {
54
+ #unavailable = document.createElement('div');
55
+ #kind = document.createElement('span');
56
+ #reason = document.createElement('span');
57
+ #mount = document.createElement('div');
58
+ #started = false;
59
+ constructor() {
60
+ super();
61
+ const root = this.attachShadow({ mode: 'open' });
62
+ const style = document.createElement('style');
63
+ style.textContent = STYLE;
64
+ this.#unavailable.part.add('unavailable');
65
+ this.#unavailable.setAttribute('role', 'status');
66
+ this.#kind.part.add('kind');
67
+ this.#reason.part.add('reason');
68
+ this.#unavailable.append('Component unavailable: ', this.#kind, this.#reason);
69
+ // Hidden until the mount attempt resolves, so a host that does render this kind never
70
+ // flashes an unavailable state on the way in.
71
+ this.#unavailable.hidden = true;
72
+ this.#mount.part.add('mount');
73
+ root.append(style, document.createElement('slot'), this.#unavailable);
74
+ }
75
+ connectedCallback() {
76
+ if (this.#started)
77
+ return;
78
+ this.#started = true;
79
+ const kind = this.getAttribute('kind')?.trim() ?? '';
80
+ this.#kind.textContent = kind === '' ? 'unknown' : kind;
81
+ if (kind === '') {
82
+ this.#showUnavailable('This slot names no component kind.');
83
+ return;
84
+ }
85
+ const read = readSlotConfig(this);
86
+ if (read.status !== 'ok') {
87
+ this.#showUnavailable(read.reason);
88
+ return;
89
+ }
90
+ this.append(this.#mount);
91
+ void this.#attemptMount(kind, read.config);
92
+ }
93
+ async #attemptMount(kind, config) {
94
+ const outcome = await mountWorkspaceComponent(this.#mount, kind, config);
95
+ if (outcome === 'mounted')
96
+ return;
97
+ this.#mount.remove();
98
+ this.#showUnavailable('This page is open somewhere that cannot render it.');
99
+ }
100
+ #showUnavailable(reason) {
101
+ this.#reason.textContent = reason;
102
+ this.#unavailable.hidden = false;
103
+ }
104
+ }
105
+ registerElement('crtr-slot', CrtrSlotElement);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `<crtr-table>` — a dataset the agent wants the user to pick from.
3
+ *
4
+ * Rows and columns are independently selectable, single or multi, because the two questions
5
+ * an agent asks of a table are "which of these records" and "which of these fields", and they
6
+ * are not the same question. Row selection wears a real checkbox or radio in a leading cell;
7
+ * column selection turns the header into a pressed toggle, since a column has no natural row
8
+ * to hang a checkbox on and the header IS the column.
9
+ *
10
+ * Anchored comments use the same one-per-anchor algebra as every other element: a row anchor
11
+ * in each row's trailing cell, a column anchor beside each header label, and a `whole` anchor
12
+ * in the trailing header cell for the table itself. Triggers stay invisible until the row is
13
+ * hovered or focused, or until a comment is actually there.
14
+ *
15
+ * Ported from the agent interaction kit's `agent-table.tsx` (northlight `3705fe9e2`), with the
16
+ * kit's `classic-list-keys` grammar — j/k move, x selects, c comments — reimplemented here
17
+ * over the shadow root.
18
+ */
19
+ export {};