@pragma-sh/scratchpad 0.1.0

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/dist/ui.js ADDED
@@ -0,0 +1,1313 @@
1
+ // src/index.ts
2
+ export * from "@pragma-sh/sdk";
3
+ function bridge() {
4
+ const value = globalThis.pragmaScratchpad;
5
+ if (!value) {
6
+ throw new Error("@pragma-sh/scratchpad can only prompt agents inside a Pragma scratchpad");
7
+ }
8
+ return value;
9
+ }
10
+ function scratchpadBridge() {
11
+ return bridge();
12
+ }
13
+ async function promptAgent(text, options = {}) {
14
+ const value = text.trim();
15
+ if (!value)
16
+ return false;
17
+ const host = bridge();
18
+ const firstResult = await host.promptAgent(value);
19
+ if (firstResult === "sent")
20
+ return true;
21
+ if (firstResult === "cancelled")
22
+ return false;
23
+ const attach = () => host.requestAgentAttachment();
24
+ const customResult = await options.onMissingAgent?.({ text: value, attach });
25
+ const attached = options.onMissingAgent ? customResult === true : await attach();
26
+ if (!attached)
27
+ return false;
28
+ const retryResult = await host.promptAgent(value);
29
+ if (retryResult === "sent")
30
+ return true;
31
+ if (retryResult === "cancelled")
32
+ return false;
33
+ throw new Error("Selected agent tab is no longer available");
34
+ }
35
+
36
+ // src/styles.ts
37
+ var SCRATCHPAD_STYLE_ELEMENT_ID = "pragma-scratchpad-styles";
38
+ var SCRATCHPAD_STYLES = `
39
+ .pragma-card {
40
+ display: grid;
41
+ gap: 0.75rem;
42
+ margin: 1rem 0;
43
+ padding: 0.875rem 1rem;
44
+ border: 1px solid var(--border, #3f3f46);
45
+ border-radius: var(--radius-lg, 10px);
46
+ background: var(--card, #18181b);
47
+ color: var(--card-foreground, #fafafa);
48
+ box-shadow: var(--shadow-raised, 0 1px 2px rgb(0 0 0 / 0.3));
49
+ font-family: var(--font-sans, system-ui, sans-serif);
50
+ font-size: 0.8125rem;
51
+ line-height: 1.55;
52
+ }
53
+ .pragma-card > :first-child { margin-top: 0; }
54
+ .pragma-card > :last-child { margin-bottom: 0; }
55
+
56
+ .pragma-eyebrow {
57
+ font-size: 0.6875rem;
58
+ font-weight: 600;
59
+ letter-spacing: 0.06em;
60
+ text-transform: uppercase;
61
+ color: var(--muted-foreground, #a1a1aa);
62
+ }
63
+ .pragma-title {
64
+ font-size: 0.9375rem;
65
+ font-weight: 600;
66
+ line-height: 1.4;
67
+ color: var(--card-foreground, #fafafa);
68
+ }
69
+ .pragma-hint { font-size: 0.75rem; color: var(--muted-foreground, #a1a1aa); }
70
+ .pragma-stack { display: grid; gap: 0.5rem; }
71
+ .pragma-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
72
+ .pragma-row--between { justify-content: space-between; }
73
+ .pragma-row--end { justify-content: flex-end; }
74
+ .pragma-empty {
75
+ padding: 0.75rem;
76
+ border: 1px dashed var(--border, #3f3f46);
77
+ border-radius: var(--radius-md, 8px);
78
+ font-size: 0.75rem;
79
+ color: var(--muted-foreground, #a1a1aa);
80
+ text-align: center;
81
+ }
82
+ .pragma-chip {
83
+ padding: 0.0625rem 0.375rem;
84
+ border-radius: var(--radius-sm, 6px);
85
+ background: var(--muted, #27272a);
86
+ color: var(--muted-foreground, #a1a1aa);
87
+ font-family: var(--font-mono, ui-monospace, monospace);
88
+ font-size: 0.6875rem;
89
+ }
90
+
91
+ .pragma-button {
92
+ display: inline-flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ gap: 0.375rem;
96
+ min-height: 2rem;
97
+ padding: 0 0.75rem;
98
+ border: 1px solid transparent;
99
+ border-radius: var(--radius-md, 8px);
100
+ font-family: inherit;
101
+ font-size: 0.8125rem;
102
+ font-weight: 500;
103
+ line-height: 1;
104
+ cursor: pointer;
105
+ white-space: nowrap;
106
+ transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
107
+ }
108
+ .pragma-button:focus-visible { outline: 2px solid var(--ring, #22d3ee); outline-offset: 2px; }
109
+ .pragma-button:disabled { cursor: not-allowed; opacity: 0.5; }
110
+ .pragma-button--sm { min-height: 1.75rem; padding: 0 0.5rem; font-size: 0.75rem; }
111
+ .pragma-button--primary {
112
+ background: var(--primary, #2563eb);
113
+ color: var(--primary-foreground, #fafafa);
114
+ }
115
+ .pragma-button--primary:hover:not(:disabled) { background: var(--primary-hover, #1d4ed8); }
116
+ .pragma-button--secondary {
117
+ background: var(--secondary, #27272a);
118
+ color: var(--secondary-foreground, #fafafa);
119
+ }
120
+ .pragma-button--secondary:hover:not(:disabled) { background: var(--accent, #3f3f46); }
121
+ .pragma-button--outline {
122
+ border-color: var(--input, #3f3f46);
123
+ background: transparent;
124
+ color: var(--foreground, #fafafa);
125
+ }
126
+ .pragma-button--outline:hover:not(:disabled) {
127
+ background: var(--accent, #27272a);
128
+ color: var(--accent-foreground, #fafafa);
129
+ }
130
+ .pragma-button--ghost { background: transparent; color: var(--muted-foreground, #a1a1aa); }
131
+ .pragma-button--ghost:hover:not(:disabled) {
132
+ background: var(--accent, #27272a);
133
+ color: var(--accent-foreground, #fafafa);
134
+ }
135
+ .pragma-button--danger {
136
+ background: var(--destructive, #ef4444);
137
+ color: var(--destructive-foreground, #fafafa);
138
+ }
139
+ .pragma-button--danger:hover:not(:disabled) { filter: brightness(1.08); }
140
+
141
+ .pragma-input,
142
+ .pragma-textarea {
143
+ width: 100%;
144
+ padding: 0.4375rem 0.625rem;
145
+ border: 1px solid var(--input, #3f3f46);
146
+ border-radius: var(--radius-md, 8px);
147
+ background: var(--background, #09090b);
148
+ color: var(--foreground, #fafafa);
149
+ font-family: inherit;
150
+ font-size: 0.8125rem;
151
+ line-height: 1.5;
152
+ transition: border-color 0.12s ease;
153
+ }
154
+ .pragma-input::placeholder,
155
+ .pragma-textarea::placeholder { color: var(--muted-foreground, #71717a); }
156
+ .pragma-input:focus-visible,
157
+ .pragma-textarea:focus-visible {
158
+ border-color: var(--ring, #22d3ee);
159
+ outline: 2px solid var(--ring, #22d3ee);
160
+ outline-offset: -1px;
161
+ }
162
+ .pragma-input:disabled,
163
+ .pragma-textarea:disabled { cursor: not-allowed; opacity: 0.5; }
164
+ .pragma-textarea { min-height: 5rem; resize: vertical; }
165
+
166
+ /*
167
+ * Choice list shared by every AskQuestion shape: a circular radio for a single
168
+ * answer, a square checkbox for multi-select. The native control is restyled
169
+ * (\`appearance: none\`) rather than replaced, so keyboard roving, labels and
170
+ * form semantics keep working inside the sandboxed frame.
171
+ */
172
+ .pragma-choices { display: grid; gap: 0.25rem; }
173
+ .pragma-choice {
174
+ display: flex;
175
+ align-items: center;
176
+ gap: 0.5rem;
177
+ padding: 0.4375rem 0.5rem;
178
+ border: 1px solid transparent;
179
+ border-radius: var(--radius-md, 8px);
180
+ cursor: pointer;
181
+ transition: background-color 0.12s ease, border-color 0.12s ease;
182
+ }
183
+ .pragma-choice:hover { background: var(--accent, #27272a); }
184
+ .pragma-choice--checked {
185
+ border-color: color-mix(in oklab, var(--primary, #2563eb) 45%, transparent);
186
+ background: color-mix(in oklab, var(--primary, #2563eb) 10%, transparent);
187
+ }
188
+ .pragma-choice:has(:disabled) { cursor: not-allowed; opacity: 0.5; }
189
+ .pragma-choice:has(:disabled):hover { background: transparent; }
190
+ .pragma-choice__control {
191
+ appearance: none;
192
+ -webkit-appearance: none;
193
+ display: grid;
194
+ place-content: center;
195
+ flex: none;
196
+ width: 1rem;
197
+ height: 1rem;
198
+ margin: 0;
199
+ border: 1px solid var(--input, #3f3f46);
200
+ background: var(--background, #09090b);
201
+ cursor: inherit;
202
+ transition: background-color 0.12s ease, border-color 0.12s ease;
203
+ }
204
+ .pragma-choice__control--radio { border-radius: 999px; }
205
+ .pragma-choice__control--check { border-radius: var(--radius-sm, 6px); }
206
+ .pragma-choice__control:focus-visible { outline: 2px solid var(--ring, #22d3ee); outline-offset: 2px; }
207
+ .pragma-choice__control:checked { border-color: var(--primary, #2563eb); }
208
+ .pragma-choice__control--radio:checked::after {
209
+ content: "";
210
+ width: 0.5rem;
211
+ height: 0.5rem;
212
+ border-radius: 999px;
213
+ background: var(--primary, #2563eb);
214
+ }
215
+ .pragma-choice__control--check:checked { background: var(--primary, #2563eb); }
216
+ .pragma-choice__control--check:checked::after {
217
+ content: "";
218
+ width: 0.3125rem;
219
+ height: 0.5625rem;
220
+ margin-top: -0.125rem;
221
+ border: solid var(--primary-foreground, #fafafa);
222
+ border-width: 0 2px 2px 0;
223
+ transform: rotate(45deg);
224
+ }
225
+ .pragma-choice__label { flex: 1; min-width: 0; font-size: 0.8125rem; }
226
+ /* The "Other" row swaps its text for this field, so it has to sit on the row. */
227
+ .pragma-choice__other {
228
+ padding: 0.125rem 0.375rem;
229
+ font-size: 0.8125rem;
230
+ cursor: text;
231
+ }
232
+
233
+ .pragma-progress {
234
+ appearance: none;
235
+ width: 100%;
236
+ height: 0.375rem;
237
+ border: 0;
238
+ border-radius: 999px;
239
+ overflow: hidden;
240
+ background: var(--muted, #27272a);
241
+ color: var(--pragma-tone, var(--primary, #22d3ee));
242
+ }
243
+ .pragma-progress::-webkit-progress-bar { background: var(--muted, #27272a); border-radius: 999px; }
244
+ .pragma-progress::-webkit-progress-value {
245
+ background: var(--pragma-tone, var(--primary, #22d3ee));
246
+ border-radius: 999px;
247
+ transition: width 0.25s ease;
248
+ }
249
+ .pragma-progress::-moz-progress-bar {
250
+ background: var(--pragma-tone, var(--primary, #22d3ee));
251
+ border-radius: 999px;
252
+ }
253
+
254
+ .pragma-badge {
255
+ display: inline-flex;
256
+ align-items: center;
257
+ gap: 0.25rem;
258
+ padding: 0.0625rem 0.4375rem;
259
+ border: 1px solid currentColor;
260
+ border-radius: 999px;
261
+ background: transparent;
262
+ color: var(--muted-foreground, #a1a1aa);
263
+ font-size: 0.6875rem;
264
+ font-weight: 500;
265
+ line-height: 1.5;
266
+ }
267
+ .pragma-badge--primary { color: var(--primary, #22d3ee); }
268
+ .pragma-badge--success { color: var(--success, #22c55e); }
269
+ .pragma-badge--warning { color: var(--warning, #eab308); }
270
+ .pragma-badge--danger { color: var(--destructive, #ef4444); }
271
+
272
+ .pragma-whiteboard__open {
273
+ display: block;
274
+ width: 100%;
275
+ padding: 0;
276
+ border: 0;
277
+ border-radius: var(--radius-md, 8px);
278
+ background: transparent;
279
+ cursor: pointer;
280
+ }
281
+ .pragma-whiteboard__open:focus-visible {
282
+ outline: 2px solid var(--ring, #22d3ee);
283
+ outline-offset: 2px;
284
+ }
285
+ .pragma-whiteboard__open:hover .pragma-whiteboard__image {
286
+ border-color: var(--primary, #22d3ee);
287
+ }
288
+ .pragma-whiteboard__image {
289
+ display: block;
290
+ width: 100%;
291
+ height: auto;
292
+ max-height: 36rem;
293
+ object-fit: contain;
294
+ border: 1px solid var(--border, #3f3f46);
295
+ border-radius: var(--radius-md, 8px);
296
+ background: var(--background, #09090b);
297
+ transition: border-color 0.15s ease;
298
+ }
299
+ .pragma-whiteboard__error {
300
+ margin: 0;
301
+ color: var(--destructive, #ef4444);
302
+ font-size: 0.75rem;
303
+ }
304
+
305
+ /*
306
+ * Side-by-side diff modelled on the desktop's \`@codemirror/merge\` view: one
307
+ * scroll container so both panes stay row-aligned, a line-number gutter per
308
+ * pane, whole-line tinting for inserted/deleted lines and a stronger tint on
309
+ * the words that actually changed (the equivalent of \`cm-changedText\`).
310
+ */
311
+ .pragma-diff {
312
+ display: grid;
313
+ grid-template-columns: repeat(2, minmax(0, 1fr));
314
+ max-height: 24rem;
315
+ overflow-y: auto;
316
+ border: 1px solid var(--border, #3f3f46);
317
+ border-radius: var(--radius-md, 8px);
318
+ background: var(--background, #09090b);
319
+ font-family: var(--font-mono, ui-monospace, monospace);
320
+ font-size: 0.75rem;
321
+ line-height: 1.6;
322
+ --pragma-diff-added: var(--diff-added, #22c55e);
323
+ --pragma-diff-removed: var(--diff-removed, #ef4444);
324
+ }
325
+ @media (max-width: 34rem) { .pragma-diff { grid-template-columns: minmax(0, 1fr); } }
326
+ .pragma-diff__pane { min-width: 0; overflow-x: auto; }
327
+ .pragma-diff__pane--after { border-left: 1px solid var(--border, #3f3f46); }
328
+ @media (max-width: 34rem) {
329
+ .pragma-diff__pane--after {
330
+ border-left: 0;
331
+ border-top: 1px solid var(--border, #3f3f46);
332
+ }
333
+ }
334
+ .pragma-diff__label {
335
+ display: flex;
336
+ justify-content: space-between;
337
+ gap: 0.5rem;
338
+ padding: 0.25rem 0.625rem;
339
+ border-bottom: 1px solid var(--border, #3f3f46);
340
+ background: var(--card, #18181b);
341
+ font-family: var(--font-sans, system-ui, sans-serif);
342
+ font-size: 0.6875rem;
343
+ font-weight: 600;
344
+ letter-spacing: 0.06em;
345
+ text-transform: uppercase;
346
+ }
347
+ .pragma-diff__pane--before .pragma-diff__label { color: var(--pragma-diff-removed); }
348
+ .pragma-diff__pane--after .pragma-diff__label { color: var(--pragma-diff-added); }
349
+
350
+ .pragma-diff__lines { display: grid; padding: 0.25rem 0; min-width: max-content; }
351
+ .pragma-diff__line {
352
+ display: grid;
353
+ grid-template-columns: 2.75rem minmax(0, 1fr);
354
+ min-height: 1.6em;
355
+ white-space: pre;
356
+ }
357
+ .pragma-diff__gutter {
358
+ padding-right: 0.625rem;
359
+ color: var(--muted-foreground, #71717a);
360
+ opacity: 0.7;
361
+ text-align: right;
362
+ user-select: none;
363
+ }
364
+ .pragma-diff__text { padding-right: 0.75rem; font: inherit; }
365
+ .pragma-diff__line--removed {
366
+ background: color-mix(in oklab, var(--pragma-diff-removed) 12%, transparent);
367
+ }
368
+ .pragma-diff__line--added {
369
+ background: color-mix(in oklab, var(--pragma-diff-added) 12%, transparent);
370
+ }
371
+ .pragma-diff__pane--before .pragma-diff__line--changed {
372
+ background: color-mix(in oklab, var(--pragma-diff-removed) 12%, transparent);
373
+ }
374
+ .pragma-diff__pane--after .pragma-diff__line--changed {
375
+ background: color-mix(in oklab, var(--pragma-diff-added) 12%, transparent);
376
+ }
377
+ .pragma-diff__line--spacer { background: var(--muted, #27272a); opacity: 0.35; }
378
+ .pragma-diff__pane--before .pragma-diff__changed {
379
+ background: color-mix(in oklab, var(--pragma-diff-removed) 30%, transparent);
380
+ }
381
+ .pragma-diff__pane--after .pragma-diff__changed {
382
+ background: color-mix(in oklab, var(--pragma-diff-added) 30%, transparent);
383
+ }
384
+
385
+ .pragma-progress-row { display: grid; gap: 0.3125rem; }
386
+ .pragma-progress-row + .pragma-progress-row {
387
+ padding-top: 0.625rem;
388
+ border-top: 1px solid var(--border, #3f3f46);
389
+ }
390
+ .pragma-progress-row__name {
391
+ min-width: 0;
392
+ overflow: hidden;
393
+ font-weight: 500;
394
+ text-overflow: ellipsis;
395
+ white-space: nowrap;
396
+ }
397
+ `;
398
+ function ensureScratchpadStyles() {
399
+ if (typeof document === "undefined")
400
+ return;
401
+ if (document.getElementById(SCRATCHPAD_STYLE_ELEMENT_ID))
402
+ return;
403
+ const element = document.createElement("style");
404
+ element.id = SCRATCHPAD_STYLE_ELEMENT_ID;
405
+ element.textContent = SCRATCHPAD_STYLES;
406
+ document.head.append(element);
407
+ }
408
+
409
+ // src/primitives.tsx
410
+ import { jsx } from "react/jsx-runtime";
411
+ var TONE_VARIABLE = {
412
+ neutral: "var(--muted-foreground)",
413
+ primary: "var(--primary)",
414
+ success: "var(--success)",
415
+ warning: "var(--warning)",
416
+ danger: "var(--destructive)"
417
+ };
418
+ function classNames(...values) {
419
+ return values.filter(Boolean).join(" ");
420
+ }
421
+ function Button({
422
+ className,
423
+ size = "md",
424
+ type = "button",
425
+ variant = "secondary",
426
+ ...rest
427
+ }) {
428
+ ensureScratchpadStyles();
429
+ return /* @__PURE__ */ jsx("button", {
430
+ ...rest,
431
+ className: classNames("pragma-button", `pragma-button--${variant}`, size === "sm" && "pragma-button--sm", className),
432
+ type
433
+ });
434
+ }
435
+ function Input({
436
+ className,
437
+ ...rest
438
+ }) {
439
+ ensureScratchpadStyles();
440
+ return /* @__PURE__ */ jsx("input", {
441
+ ...rest,
442
+ className: classNames("pragma-input", className)
443
+ });
444
+ }
445
+ function Textarea({
446
+ className,
447
+ ...rest
448
+ }) {
449
+ ensureScratchpadStyles();
450
+ return /* @__PURE__ */ jsx("textarea", {
451
+ ...rest,
452
+ className: classNames("pragma-textarea", className)
453
+ });
454
+ }
455
+ function Progress({
456
+ className,
457
+ style,
458
+ tone = "primary",
459
+ ...rest
460
+ }) {
461
+ ensureScratchpadStyles();
462
+ return /* @__PURE__ */ jsx("progress", {
463
+ ...rest,
464
+ className: classNames("pragma-progress", className),
465
+ style: { ["--pragma-tone"]: TONE_VARIABLE[tone], ...style }
466
+ });
467
+ }
468
+ function Card({ className, ...rest }) {
469
+ ensureScratchpadStyles();
470
+ return /* @__PURE__ */ jsx("section", {
471
+ ...rest,
472
+ className: classNames("pragma-card", className)
473
+ });
474
+ }
475
+ function Badge({ className, tone = "neutral", ...rest }) {
476
+ ensureScratchpadStyles();
477
+ return /* @__PURE__ */ jsx("span", {
478
+ ...rest,
479
+ className: classNames("pragma-badge", tone !== "neutral" && `pragma-badge--${tone}`, className)
480
+ });
481
+ }
482
+
483
+ // src/ui.tsx
484
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
485
+
486
+ // src/diff.ts
487
+ var MAX_DP_CELLS = 250000;
488
+ function spacer(facing) {
489
+ return { key: `spacer-${facing}`, number: null, kind: "spacer", segments: [] };
490
+ }
491
+ function alignDiffLines(before, after) {
492
+ const rows = [];
493
+ let beforeNumber = 0;
494
+ let afterNumber = 0;
495
+ let removed = [];
496
+ let added = [];
497
+ const flush = () => {
498
+ const paired = Math.min(removed.length, added.length);
499
+ for (let index = 0;index < paired; index += 1) {
500
+ const [left, right] = changedSegments(removed[index] ?? "", added[index] ?? "");
501
+ beforeNumber += 1;
502
+ afterNumber += 1;
503
+ rows.push({
504
+ before: { key: `b${beforeNumber}`, number: beforeNumber, kind: "changed", segments: left },
505
+ after: { key: `a${afterNumber}`, number: afterNumber, kind: "changed", segments: right }
506
+ });
507
+ }
508
+ for (const text of removed.slice(paired)) {
509
+ beforeNumber += 1;
510
+ rows.push({
511
+ before: {
512
+ key: `b${beforeNumber}`,
513
+ number: beforeNumber,
514
+ kind: "removed",
515
+ segments: wholeLine(text)
516
+ },
517
+ after: spacer(beforeNumber)
518
+ });
519
+ }
520
+ for (const text of added.slice(paired)) {
521
+ afterNumber += 1;
522
+ rows.push({
523
+ before: spacer(afterNumber),
524
+ after: {
525
+ key: `a${afterNumber}`,
526
+ number: afterNumber,
527
+ kind: "added",
528
+ segments: wholeLine(text)
529
+ }
530
+ });
531
+ }
532
+ removed = [];
533
+ added = [];
534
+ };
535
+ for (const edit of diffSequences(splitLines(before), splitLines(after))) {
536
+ if (edit.kind === "remove") {
537
+ removed.push(edit.value);
538
+ continue;
539
+ }
540
+ if (edit.kind === "insert") {
541
+ added.push(edit.value);
542
+ continue;
543
+ }
544
+ flush();
545
+ beforeNumber += 1;
546
+ afterNumber += 1;
547
+ rows.push({
548
+ before: {
549
+ key: `b${beforeNumber}`,
550
+ number: beforeNumber,
551
+ kind: "same",
552
+ segments: wholeLine(edit.value)
553
+ },
554
+ after: {
555
+ key: `a${afterNumber}`,
556
+ number: afterNumber,
557
+ kind: "same",
558
+ segments: wholeLine(edit.value)
559
+ }
560
+ });
561
+ }
562
+ flush();
563
+ return rows;
564
+ }
565
+ function splitLines(text) {
566
+ const normalized = text.replace(/\r\n/g, `
567
+ `).replace(/\n$/, "");
568
+ return normalized === "" ? [] : normalized.split(`
569
+ `);
570
+ }
571
+ function wholeLine(text) {
572
+ return text === "" ? [] : [{ text, changed: false }];
573
+ }
574
+ function changedSegments(before, after) {
575
+ const left = [];
576
+ const right = [];
577
+ for (const edit of diffSequences(splitWords(before), splitWords(after))) {
578
+ if (edit.kind !== "insert") {
579
+ push(left, edit.value, edit.kind === "remove");
580
+ }
581
+ if (edit.kind !== "remove") {
582
+ push(right, edit.value, edit.kind === "insert");
583
+ }
584
+ }
585
+ return [left, right];
586
+ }
587
+ function push(segments, text, changed) {
588
+ const last = segments.at(-1);
589
+ if (last && last.changed === changed) {
590
+ last.text += text;
591
+ return;
592
+ }
593
+ segments.push({ text, changed });
594
+ }
595
+ function splitWords(line) {
596
+ return line.split(/(\s+)/).filter((part) => part !== "");
597
+ }
598
+ function diffSequences(a, b) {
599
+ let start = 0;
600
+ while (start < a.length && start < b.length && a[start] === b[start]) {
601
+ start += 1;
602
+ }
603
+ let end = 0;
604
+ while (end < a.length - start && end < b.length - start && a[a.length - 1 - end] === b[b.length - 1 - end]) {
605
+ end += 1;
606
+ }
607
+ const head = a.slice(0, start).map((value) => ({ kind: "equal", value }));
608
+ const tail = a.slice(a.length - end).map((value) => ({ kind: "equal", value }));
609
+ const midA = a.slice(start, a.length - end);
610
+ const midB = b.slice(start, b.length - end);
611
+ return [...head, ...diffMiddle(midA, midB), ...tail];
612
+ }
613
+ function replaceAll(a, b) {
614
+ return [
615
+ ...a.map((value) => ({ kind: "remove", value })),
616
+ ...b.map((value) => ({ kind: "insert", value }))
617
+ ];
618
+ }
619
+ function lcsTable(a, b) {
620
+ const width = b.length + 1;
621
+ const lengths = new Uint32Array((a.length + 1) * width);
622
+ for (let i = a.length - 1;i >= 0; i -= 1) {
623
+ for (let j = b.length - 1;j >= 0; j -= 1) {
624
+ lengths[i * width + j] = a[i] === b[j] ? (lengths[(i + 1) * width + j + 1] ?? 0) + 1 : Math.max(lengths[(i + 1) * width + j] ?? 0, lengths[i * width + j + 1] ?? 0);
625
+ }
626
+ }
627
+ return lengths;
628
+ }
629
+ function diffMiddle(a, b) {
630
+ if (a.length === 0 || b.length === 0 || a.length * b.length > MAX_DP_CELLS) {
631
+ return replaceAll(a, b);
632
+ }
633
+ return walkLcsTable(a, b, lcsTable(a, b));
634
+ }
635
+ function walkLcsTable(a, b, lengths) {
636
+ const width = b.length + 1;
637
+ const edits = [];
638
+ let i = 0;
639
+ let j = 0;
640
+ while (i < a.length && j < b.length) {
641
+ const left = a[i];
642
+ const right = b[j];
643
+ if (left === right) {
644
+ edits.push({ kind: "equal", value: left });
645
+ i += 1;
646
+ j += 1;
647
+ } else if ((lengths[(i + 1) * width + j] ?? 0) >= (lengths[i * width + j + 1] ?? 0)) {
648
+ edits.push({ kind: "remove", value: left });
649
+ i += 1;
650
+ } else {
651
+ edits.push({ kind: "insert", value: right });
652
+ j += 1;
653
+ }
654
+ }
655
+ return [...edits, ...replaceAll(a.slice(i), b.slice(j))];
656
+ }
657
+
658
+ // src/question.ts
659
+ var OTHER_VALUE = "pragma:other";
660
+ function toggleChoice(current, value, multiple) {
661
+ if (!multiple)
662
+ return [value];
663
+ return current.includes(value) ? current.filter((entry) => entry !== value) : [...current, value];
664
+ }
665
+ function composeAnswer(selected, other) {
666
+ return selected.map((value) => value === OTHER_VALUE ? other.trim() : value).filter((value) => value !== "").join(", ");
667
+ }
668
+
669
+ // src/ui.tsx
670
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
671
+ var EMPTY_OPTIONS = [];
672
+ function Whiteboard({ id, refreshIntervalMs = 3000 }) {
673
+ const [snapshot, setSnapshot] = useState(null);
674
+ const [error, setError] = useState(null);
675
+ const [loading, setLoading] = useState(true);
676
+ const versionRef = useRef(undefined);
677
+ const darkRef = useRef(undefined);
678
+ const mountedRef = useRef(true);
679
+ const inFlightRef = useRef(false);
680
+ const openWhiteboard = globalThis.pragmaScratchpad?.openWhiteboard;
681
+ const open = () => {
682
+ if (!openWhiteboard)
683
+ return;
684
+ openWhiteboard(id).catch((cause) => {
685
+ if (mountedRef.current)
686
+ setError(cause instanceof Error ? cause.message : String(cause));
687
+ });
688
+ };
689
+ const refresh = useCallback(async () => {
690
+ if (inFlightRef.current)
691
+ return;
692
+ inFlightRef.current = true;
693
+ try {
694
+ const dark = document.documentElement.classList.contains("dark");
695
+ const knownVersion = darkRef.current === dark ? versionRef.current : undefined;
696
+ const next = await scratchpadBridge().getWhiteboardSnapshot(id, knownVersion, dark);
697
+ if (!mountedRef.current)
698
+ return;
699
+ if (next) {
700
+ versionRef.current = next.version;
701
+ darkRef.current = dark;
702
+ setSnapshot(next);
703
+ }
704
+ setError(null);
705
+ } catch (cause) {
706
+ if (mountedRef.current)
707
+ setError(cause instanceof Error ? cause.message : String(cause));
708
+ } finally {
709
+ inFlightRef.current = false;
710
+ if (mountedRef.current)
711
+ setLoading(false);
712
+ }
713
+ }, [id]);
714
+ useEffect(() => {
715
+ mountedRef.current = true;
716
+ inFlightRef.current = false;
717
+ versionRef.current = undefined;
718
+ darkRef.current = undefined;
719
+ setSnapshot(null);
720
+ setLoading(true);
721
+ refresh();
722
+ const interval = refreshIntervalMs > 0 ? globalThis.setInterval(() => void refresh(), Math.max(1000, refreshIntervalMs)) : null;
723
+ return () => {
724
+ mountedRef.current = false;
725
+ if (interval !== null)
726
+ globalThis.clearInterval(interval);
727
+ };
728
+ }, [refresh, refreshIntervalMs]);
729
+ return /* @__PURE__ */ jsxs(Card, {
730
+ children: [
731
+ /* @__PURE__ */ jsxs("div", {
732
+ className: "pragma-row pragma-row--between",
733
+ children: [
734
+ /* @__PURE__ */ jsxs("div", {
735
+ children: [
736
+ /* @__PURE__ */ jsx2("span", {
737
+ className: "pragma-eyebrow",
738
+ children: "Whiteboard"
739
+ }),
740
+ /* @__PURE__ */ jsx2("p", {
741
+ className: "pragma-title",
742
+ children: snapshot?.title ?? id
743
+ })
744
+ ]
745
+ }),
746
+ /* @__PURE__ */ jsxs("div", {
747
+ className: "pragma-row",
748
+ children: [
749
+ /* @__PURE__ */ jsx2(Badge, {
750
+ children: "Read only"
751
+ }),
752
+ openWhiteboard ? /* @__PURE__ */ jsx2(Button, {
753
+ onClick: open,
754
+ size: "sm",
755
+ variant: "ghost",
756
+ children: "Open"
757
+ }) : null,
758
+ /* @__PURE__ */ jsx2(Button, {
759
+ disabled: loading,
760
+ onClick: () => void refresh(),
761
+ size: "sm",
762
+ variant: "ghost",
763
+ children: "Refresh"
764
+ })
765
+ ]
766
+ })
767
+ ]
768
+ }),
769
+ error ? /* @__PURE__ */ jsx2("p", {
770
+ className: "pragma-whiteboard__error",
771
+ role: "alert",
772
+ children: error
773
+ }) : snapshot ? openWhiteboard ? /* @__PURE__ */ jsx2("button", {
774
+ "aria-label": `Open ${snapshot.title} whiteboard`,
775
+ className: "pragma-whiteboard__open",
776
+ onClick: open,
777
+ type: "button",
778
+ children: /* @__PURE__ */ jsx2("img", {
779
+ alt: `${snapshot.title} whiteboard`,
780
+ className: "pragma-whiteboard__image",
781
+ src: snapshot.dataUrl
782
+ })
783
+ }) : /* @__PURE__ */ jsx2("img", {
784
+ alt: `${snapshot.title} whiteboard`,
785
+ className: "pragma-whiteboard__image",
786
+ src: snapshot.dataUrl
787
+ }) : /* @__PURE__ */ jsx2("div", {
788
+ className: "pragma-empty",
789
+ children: "Loading whiteboard..."
790
+ })
791
+ ]
792
+ });
793
+ }
794
+ function useAgentAction(options) {
795
+ const [pending, setPending] = useState(false);
796
+ const run = async (text, onSent) => {
797
+ if (pending)
798
+ return false;
799
+ setPending(true);
800
+ try {
801
+ const delivered = await promptAgent(text, options);
802
+ if (delivered)
803
+ await onSent();
804
+ return delivered;
805
+ } finally {
806
+ setPending(false);
807
+ }
808
+ };
809
+ return { pending, run };
810
+ }
811
+ function AskQuestion({
812
+ question,
813
+ type = "options",
814
+ options = EMPTY_OPTIONS,
815
+ allowOpenResponse = false,
816
+ submitLabel = "Send answer",
817
+ onAnswer,
818
+ onMissingAgent
819
+ }) {
820
+ const [sent, setSent] = useState(null);
821
+ const { pending, run } = useAgentAction({ onMissingAgent });
822
+ const submit = (value) => run(`Answer to scratchpad question "${question}": ${value}`, async () => {
823
+ await onAnswer?.(value);
824
+ setSent(value);
825
+ });
826
+ return /* @__PURE__ */ jsxs(Card, {
827
+ children: [
828
+ /* @__PURE__ */ jsxs("div", {
829
+ className: "pragma-row pragma-row--between",
830
+ children: [
831
+ /* @__PURE__ */ jsx2("span", {
832
+ className: "pragma-eyebrow",
833
+ children: "Question"
834
+ }),
835
+ sent === null ? null : /* @__PURE__ */ jsx2(Badge, {
836
+ tone: "success",
837
+ children: "Answered"
838
+ })
839
+ ]
840
+ }),
841
+ /* @__PURE__ */ jsx2("p", {
842
+ className: "pragma-title",
843
+ children: question
844
+ }),
845
+ sent === null ? /* @__PURE__ */ jsx2(AskQuestionControls, {
846
+ allowOpenResponse,
847
+ options,
848
+ pending,
849
+ submit,
850
+ submitLabel,
851
+ type
852
+ }) : /* @__PURE__ */ jsx2(SettledNotice, {
853
+ detail: `Answered “${sent}”.`,
854
+ label: "Answer again",
855
+ onReset: () => setSent(null)
856
+ })
857
+ ]
858
+ });
859
+ }
860
+ function AskQuestionControls({
861
+ allowOpenResponse,
862
+ options,
863
+ pending,
864
+ submit,
865
+ submitLabel,
866
+ type
867
+ }) {
868
+ if (type === "text") {
869
+ return /* @__PURE__ */ jsx2(AskQuestionForm, {
870
+ pending,
871
+ submit,
872
+ submitLabel
873
+ });
874
+ }
875
+ return /* @__PURE__ */ jsx2(AskQuestionChoices, {
876
+ allowOpenResponse,
877
+ choices: type === "yes-no" ? YES_NO_OPTIONS : options,
878
+ multiple: type === "multi-select",
879
+ pending,
880
+ submit,
881
+ submitLabel
882
+ });
883
+ }
884
+ var YES_NO_OPTIONS = [{ label: "Yes" }, { label: "No" }];
885
+ var OTHER_OPTION = { label: "Other", value: OTHER_VALUE };
886
+ function choiceValue(option) {
887
+ return option.value ?? option.label;
888
+ }
889
+ function AskQuestionChoices({
890
+ allowOpenResponse,
891
+ choices,
892
+ multiple,
893
+ pending,
894
+ submit,
895
+ submitLabel
896
+ }) {
897
+ const group = useId();
898
+ const [selected, setSelected] = useState([]);
899
+ const [other, setOther] = useState("");
900
+ const items = allowOpenResponse ? [...choices, OTHER_OPTION] : choices;
901
+ const toggle = (value) => setSelected((current) => toggleChoice(current, value, multiple));
902
+ const answer = composeAnswer(selected, other);
903
+ const send = () => {
904
+ if (!answer)
905
+ return;
906
+ submit(answer).then((delivered) => {
907
+ if (delivered) {
908
+ setSelected([]);
909
+ setOther("");
910
+ }
911
+ return delivered;
912
+ });
913
+ };
914
+ return /* @__PURE__ */ jsxs("form", {
915
+ className: "pragma-stack",
916
+ onSubmit: (event) => {
917
+ event.preventDefault();
918
+ send();
919
+ },
920
+ children: [
921
+ /* @__PURE__ */ jsx2("div", {
922
+ className: "pragma-choices",
923
+ role: choiceGroupRole(multiple),
924
+ children: items.map((option) => /* @__PURE__ */ jsx2(ChoiceRow, {
925
+ checked: selected.includes(choiceValue(option)),
926
+ disabled: pending,
927
+ group,
928
+ multiple,
929
+ onOtherChange: setOther,
930
+ onSelect: toggle,
931
+ option,
932
+ other
933
+ }, choiceValue(option)))
934
+ }),
935
+ /* @__PURE__ */ jsx2(ChoiceSubmit, {
936
+ answer,
937
+ label: submitLabel,
938
+ pending
939
+ })
940
+ ]
941
+ });
942
+ }
943
+ function choiceGroupRole(multiple) {
944
+ return multiple ? "group" : "radiogroup";
945
+ }
946
+ function ChoiceSubmit({
947
+ answer,
948
+ label,
949
+ pending
950
+ }) {
951
+ return /* @__PURE__ */ jsx2("div", {
952
+ className: "pragma-row pragma-row--end",
953
+ children: /* @__PURE__ */ jsx2(Button, {
954
+ disabled: pending || !answer,
955
+ type: "submit",
956
+ variant: "primary",
957
+ children: pending ? "Sending…" : label
958
+ })
959
+ });
960
+ }
961
+ function ChoiceRow(props) {
962
+ const { checked, disabled, group, multiple, onOtherChange, onSelect, option, other } = props;
963
+ const value = choiceValue(option);
964
+ const isOtherField = value === OTHER_VALUE && checked;
965
+ return /* @__PURE__ */ jsx2(Choice, {
966
+ checked,
967
+ disabled,
968
+ label: isOtherField ? /* @__PURE__ */ jsx2(Input, {
969
+ className: "pragma-choice__other",
970
+ disabled,
971
+ onClick: (event) => event.stopPropagation(),
972
+ onChange: (event) => onOtherChange(event.target.value),
973
+ placeholder: "Answer in your own words",
974
+ value: other
975
+ }) : option.label,
976
+ multiple,
977
+ name: group,
978
+ onSelect: () => onSelect(value)
979
+ });
980
+ }
981
+ function Choice({
982
+ checked,
983
+ disabled,
984
+ label,
985
+ multiple,
986
+ name,
987
+ onSelect
988
+ }) {
989
+ const shape = multiple ? "check" : "radio";
990
+ return /* @__PURE__ */ jsxs("label", {
991
+ className: `pragma-choice${checked ? " pragma-choice--checked" : ""}`,
992
+ children: [
993
+ /* @__PURE__ */ jsx2("input", {
994
+ checked,
995
+ className: `pragma-choice__control pragma-choice__control--${shape}`,
996
+ disabled,
997
+ name,
998
+ onChange: onSelect,
999
+ type: multiple ? "checkbox" : "radio"
1000
+ }),
1001
+ /* @__PURE__ */ jsx2("span", {
1002
+ className: "pragma-choice__label",
1003
+ children: label
1004
+ })
1005
+ ]
1006
+ });
1007
+ }
1008
+ function AskQuestionForm({
1009
+ pending,
1010
+ submit,
1011
+ submitLabel
1012
+ }) {
1013
+ const [answer, setAnswer] = useState("");
1014
+ const send = () => {
1015
+ const value = answer.trim();
1016
+ if (!value)
1017
+ return;
1018
+ submit(value).then((delivered) => {
1019
+ if (delivered)
1020
+ setAnswer("");
1021
+ return delivered;
1022
+ });
1023
+ };
1024
+ return /* @__PURE__ */ jsxs("form", {
1025
+ className: "pragma-stack",
1026
+ onSubmit: (event) => {
1027
+ event.preventDefault();
1028
+ send();
1029
+ },
1030
+ children: [
1031
+ /* @__PURE__ */ jsx2(Textarea, {
1032
+ disabled: pending,
1033
+ onChange: (event) => setAnswer(event.target.value),
1034
+ placeholder: "Type your answer",
1035
+ value: answer
1036
+ }),
1037
+ /* @__PURE__ */ jsx2("div", {
1038
+ className: "pragma-row pragma-row--end",
1039
+ children: /* @__PURE__ */ jsx2(Button, {
1040
+ disabled: pending || !answer.trim(),
1041
+ type: "submit",
1042
+ variant: "primary",
1043
+ children: pending ? "Sending…" : submitLabel
1044
+ })
1045
+ })
1046
+ ]
1047
+ });
1048
+ }
1049
+ function DiffReview({
1050
+ title = "Review change",
1051
+ before,
1052
+ after,
1053
+ file,
1054
+ onDecision,
1055
+ onMissingAgent
1056
+ }) {
1057
+ const [decision, setDecision] = useState(null);
1058
+ const { pending, run } = useAgentAction({ onMissingAgent });
1059
+ const decide = (accepted) => run(`Scratchpad diff${file ? ` for ${file}` : ""} was ${verdict(accepted)}.`, async () => {
1060
+ await onDecision?.(accepted);
1061
+ setDecision(accepted);
1062
+ });
1063
+ return /* @__PURE__ */ jsxs(Card, {
1064
+ children: [
1065
+ /* @__PURE__ */ jsx2(DiffHeader, {
1066
+ decision,
1067
+ file,
1068
+ title
1069
+ }),
1070
+ /* @__PURE__ */ jsx2(DiffPanes, {
1071
+ after,
1072
+ before
1073
+ }),
1074
+ decision === null ? /* @__PURE__ */ jsx2(DiffActions, {
1075
+ decide,
1076
+ pending
1077
+ }) : /* @__PURE__ */ jsx2(SettledNotice, {
1078
+ detail: `Change ${verdict(decision)}.`,
1079
+ label: "Decide again",
1080
+ onReset: () => setDecision(null)
1081
+ })
1082
+ ]
1083
+ });
1084
+ }
1085
+ function DiffHeader({
1086
+ decision,
1087
+ file,
1088
+ title
1089
+ }) {
1090
+ return /* @__PURE__ */ jsxs("div", {
1091
+ className: "pragma-row pragma-row--between",
1092
+ children: [
1093
+ /* @__PURE__ */ jsxs("div", {
1094
+ className: "pragma-row",
1095
+ children: [
1096
+ /* @__PURE__ */ jsx2("span", {
1097
+ className: "pragma-title",
1098
+ children: title
1099
+ }),
1100
+ file ? /* @__PURE__ */ jsx2("code", {
1101
+ className: "pragma-chip",
1102
+ children: file
1103
+ }) : null
1104
+ ]
1105
+ }),
1106
+ decision === null ? null : /* @__PURE__ */ jsx2(Badge, {
1107
+ tone: decision ? "success" : "danger",
1108
+ children: verdictLabel(decision)
1109
+ })
1110
+ ]
1111
+ });
1112
+ }
1113
+ function DiffActions({
1114
+ decide,
1115
+ pending
1116
+ }) {
1117
+ return /* @__PURE__ */ jsxs("div", {
1118
+ className: "pragma-row pragma-row--end",
1119
+ children: [
1120
+ /* @__PURE__ */ jsx2(Button, {
1121
+ disabled: pending,
1122
+ onClick: () => void decide(false),
1123
+ variant: "outline",
1124
+ children: "Reject"
1125
+ }),
1126
+ /* @__PURE__ */ jsx2(Button, {
1127
+ disabled: pending,
1128
+ onClick: () => void decide(true),
1129
+ variant: "primary",
1130
+ children: "Accept"
1131
+ })
1132
+ ]
1133
+ });
1134
+ }
1135
+ function verdict(accepted) {
1136
+ return accepted ? "accepted" : "rejected";
1137
+ }
1138
+ function verdictLabel(accepted) {
1139
+ return accepted ? "Accepted" : "Rejected";
1140
+ }
1141
+ function DiffPanes({ after, before }) {
1142
+ const rows = useMemo(() => alignDiffLines(before, after), [before, after]);
1143
+ return /* @__PURE__ */ jsxs("div", {
1144
+ className: "pragma-diff",
1145
+ children: [
1146
+ /* @__PURE__ */ jsx2(DiffPane, {
1147
+ label: "Before",
1148
+ lines: rows.map((row) => row.before),
1149
+ side: "before"
1150
+ }),
1151
+ /* @__PURE__ */ jsx2(DiffPane, {
1152
+ label: "After",
1153
+ lines: rows.map((row) => row.after),
1154
+ side: "after"
1155
+ })
1156
+ ]
1157
+ });
1158
+ }
1159
+ function segmentNodes(line) {
1160
+ let column = 0;
1161
+ return line.segments.map((segment) => {
1162
+ const key = `${line.key}:${column}`;
1163
+ column += segment.text.length;
1164
+ return /* @__PURE__ */ jsx2("span", {
1165
+ className: segment.changed ? "pragma-diff__changed" : undefined,
1166
+ children: segment.text
1167
+ }, key);
1168
+ });
1169
+ }
1170
+ function DiffPane({
1171
+ label,
1172
+ lines,
1173
+ side
1174
+ }) {
1175
+ return /* @__PURE__ */ jsxs("div", {
1176
+ className: `pragma-diff__pane pragma-diff__pane--${side}`,
1177
+ children: [
1178
+ /* @__PURE__ */ jsxs("div", {
1179
+ className: "pragma-diff__label",
1180
+ children: [
1181
+ /* @__PURE__ */ jsx2("span", {
1182
+ children: label
1183
+ }),
1184
+ /* @__PURE__ */ jsx2("span", {
1185
+ children: side === "before" ? "−" : "+"
1186
+ })
1187
+ ]
1188
+ }),
1189
+ /* @__PURE__ */ jsx2("div", {
1190
+ className: "pragma-diff__lines",
1191
+ children: lines.map((line) => /* @__PURE__ */ jsxs("div", {
1192
+ className: `pragma-diff__line pragma-diff__line--${line.kind}`,
1193
+ children: [
1194
+ /* @__PURE__ */ jsx2("span", {
1195
+ "aria-hidden": line.number === null,
1196
+ className: "pragma-diff__gutter",
1197
+ children: line.number ?? ""
1198
+ }),
1199
+ /* @__PURE__ */ jsx2("code", {
1200
+ className: "pragma-diff__text",
1201
+ children: segmentNodes(line)
1202
+ })
1203
+ ]
1204
+ }, line.key))
1205
+ })
1206
+ ]
1207
+ });
1208
+ }
1209
+ function SettledNotice({
1210
+ detail,
1211
+ label,
1212
+ onReset
1213
+ }) {
1214
+ return /* @__PURE__ */ jsxs("div", {
1215
+ className: "pragma-row pragma-row--between",
1216
+ children: [
1217
+ /* @__PURE__ */ jsx2("span", {
1218
+ className: "pragma-hint",
1219
+ children: detail
1220
+ }),
1221
+ /* @__PURE__ */ jsx2(Button, {
1222
+ onClick: onReset,
1223
+ size: "sm",
1224
+ variant: "ghost",
1225
+ children: label
1226
+ })
1227
+ ]
1228
+ });
1229
+ }
1230
+ function AgentProgress({
1231
+ tabIds,
1232
+ title = "Agent progress"
1233
+ }) {
1234
+ const [entries, setEntries] = useState([]);
1235
+ const key = tabIds.join(" ");
1236
+ const tabIdsRef = useRef(tabIds);
1237
+ tabIdsRef.current = tabIds;
1238
+ useEffect(() => scratchpadBridge().subscribeAgentProgress(tabIdsRef.current, setEntries), [key]);
1239
+ return /* @__PURE__ */ jsxs(Card, {
1240
+ children: [
1241
+ /* @__PURE__ */ jsxs("div", {
1242
+ className: "pragma-row pragma-row--between",
1243
+ children: [
1244
+ /* @__PURE__ */ jsx2("span", {
1245
+ className: "pragma-title",
1246
+ children: title
1247
+ }),
1248
+ /* @__PURE__ */ jsxs("span", {
1249
+ className: "pragma-hint",
1250
+ children: [
1251
+ entries.length,
1252
+ " ",
1253
+ entries.length === 1 ? "tab" : "tabs"
1254
+ ]
1255
+ })
1256
+ ]
1257
+ }),
1258
+ entries.length === 0 ? /* @__PURE__ */ jsx2("p", {
1259
+ className: "pragma-empty",
1260
+ children: "No agent tabs are reporting yet."
1261
+ }) : /* @__PURE__ */ jsx2("div", {
1262
+ className: "pragma-stack",
1263
+ children: entries.map((entry) => /* @__PURE__ */ jsx2(AgentProgressRow, {
1264
+ entry
1265
+ }, entry.tabId))
1266
+ })
1267
+ ]
1268
+ });
1269
+ }
1270
+ function AgentProgressRow({ entry }) {
1271
+ const meta = STATUS_META[entry.status];
1272
+ return /* @__PURE__ */ jsxs("div", {
1273
+ className: "pragma-progress-row",
1274
+ children: [
1275
+ /* @__PURE__ */ jsxs("div", {
1276
+ className: "pragma-row pragma-row--between",
1277
+ children: [
1278
+ /* @__PURE__ */ jsx2("span", {
1279
+ className: "pragma-progress-row__name",
1280
+ children: entry.title ?? entry.agent ?? entry.tabId
1281
+ }),
1282
+ /* @__PURE__ */ jsx2(Badge, {
1283
+ tone: meta.tone,
1284
+ children: meta.label
1285
+ })
1286
+ ]
1287
+ }),
1288
+ /* @__PURE__ */ jsx2(Progress, {
1289
+ max: 100,
1290
+ tone: meta.tone,
1291
+ value: meta.value
1292
+ })
1293
+ ]
1294
+ });
1295
+ }
1296
+ var STATUS_META = {
1297
+ running: { label: "Running", tone: "primary", value: 40 },
1298
+ attention: { label: "Needs attention", tone: "warning", value: 70 },
1299
+ done: { label: "Done", tone: "success", value: 100 },
1300
+ cleared: { label: "Idle", tone: "neutral", value: 100 }
1301
+ };
1302
+ export {
1303
+ Whiteboard,
1304
+ Textarea,
1305
+ Progress,
1306
+ Input,
1307
+ DiffReview,
1308
+ Card,
1309
+ Button,
1310
+ Badge,
1311
+ AskQuestion,
1312
+ AgentProgress
1313
+ };