pith-ui 0.1.0 → 0.1.4

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 (124) hide show
  1. package/AI.md +20 -1
  2. package/API.md +834 -272
  3. package/README.md +3 -1
  4. package/dist/composer.d.ts.map +1 -1
  5. package/dist/composer.js +48 -29
  6. package/dist/composer.js.map +1 -1
  7. package/dist/content/conversation-reader.d.ts.map +1 -1
  8. package/dist/content/conversation-reader.js +8 -1
  9. package/dist/content/conversation-reader.js.map +1 -1
  10. package/dist/content/conversation-view.d.ts +21 -4
  11. package/dist/content/conversation-view.d.ts.map +1 -1
  12. package/dist/content/conversation-view.js +72 -13
  13. package/dist/content/conversation-view.js.map +1 -1
  14. package/dist/content/image-viewer.d.ts +4 -2
  15. package/dist/content/image-viewer.d.ts.map +1 -1
  16. package/dist/content/image-viewer.js +36 -15
  17. package/dist/content/image-viewer.js.map +1 -1
  18. package/dist/content/paged-preview.d.ts +1 -1
  19. package/dist/content/paged-preview.d.ts.map +1 -1
  20. package/dist/content/paged-preview.js +10 -5
  21. package/dist/content/paged-preview.js.map +1 -1
  22. package/dist/content/views.d.ts +3 -0
  23. package/dist/content/views.d.ts.map +1 -1
  24. package/dist/content/views.js +110 -41
  25. package/dist/content/views.js.map +1 -1
  26. package/dist/content/welcome.d.ts.map +1 -1
  27. package/dist/content/welcome.js +19 -7
  28. package/dist/content/welcome.js.map +1 -1
  29. package/dist/dom.js +1 -1
  30. package/dist/dom.js.map +1 -1
  31. package/dist/files/panels.d.ts +8 -1
  32. package/dist/files/panels.d.ts.map +1 -1
  33. package/dist/files/panels.js +235 -120
  34. package/dist/files/panels.js.map +1 -1
  35. package/dist/files/systems.d.ts.map +1 -1
  36. package/dist/files/systems.js +81 -91
  37. package/dist/files/systems.js.map +1 -1
  38. package/dist/forms/actions.d.ts +28 -7
  39. package/dist/forms/actions.d.ts.map +1 -1
  40. package/dist/forms/actions.js +59 -11
  41. package/dist/forms/actions.js.map +1 -1
  42. package/dist/forms/custom-connection.d.ts +2 -1
  43. package/dist/forms/custom-connection.d.ts.map +1 -1
  44. package/dist/forms/custom-connection.js +36 -28
  45. package/dist/forms/custom-connection.js.map +1 -1
  46. package/dist/forms/dialog-frame.d.ts +2 -0
  47. package/dist/forms/dialog-frame.d.ts.map +1 -1
  48. package/dist/forms/dialog-frame.js +46 -26
  49. package/dist/forms/dialog-frame.js.map +1 -1
  50. package/dist/forms/history.d.ts +1 -1
  51. package/dist/forms/history.d.ts.map +1 -1
  52. package/dist/forms/history.js +78 -15
  53. package/dist/forms/history.js.map +1 -1
  54. package/dist/forms/identity.d.ts +12 -0
  55. package/dist/forms/identity.d.ts.map +1 -1
  56. package/dist/forms/identity.js +16 -0
  57. package/dist/forms/identity.js.map +1 -1
  58. package/dist/forms/model-editor.d.ts +8 -3
  59. package/dist/forms/model-editor.d.ts.map +1 -1
  60. package/dist/forms/model-editor.js +73 -62
  61. package/dist/forms/model-editor.js.map +1 -1
  62. package/dist/forms/panels.d.ts +103 -7
  63. package/dist/forms/panels.d.ts.map +1 -1
  64. package/dist/forms/panels.js +183 -77
  65. package/dist/forms/panels.js.map +1 -1
  66. package/dist/forms/provider-login.d.ts.map +1 -1
  67. package/dist/forms/provider-login.js +25 -39
  68. package/dist/forms/provider-login.js.map +1 -1
  69. package/dist/forms/resource-editor.d.ts +9 -5
  70. package/dist/forms/resource-editor.d.ts.map +1 -1
  71. package/dist/forms/resource-editor.js +20 -9
  72. package/dist/forms/resource-editor.js.map +1 -1
  73. package/dist/foundations.d.ts +95 -0
  74. package/dist/foundations.d.ts.map +1 -0
  75. package/dist/foundations.js +299 -0
  76. package/dist/foundations.js.map +1 -0
  77. package/dist/navigation/context-menu.d.ts +8 -1
  78. package/dist/navigation/context-menu.d.ts.map +1 -1
  79. package/dist/navigation/context-menu.js +67 -50
  80. package/dist/navigation/context-menu.js.map +1 -1
  81. package/dist/navigation/navigation.d.ts +55 -7
  82. package/dist/navigation/navigation.d.ts.map +1 -1
  83. package/dist/navigation/navigation.js +167 -18
  84. package/dist/navigation/navigation.js.map +1 -1
  85. package/dist/operation.d.ts +12 -0
  86. package/dist/operation.d.ts.map +1 -0
  87. package/dist/operation.js +49 -0
  88. package/dist/operation.js.map +1 -0
  89. package/dist/primitives.d.ts +11 -1
  90. package/dist/primitives.d.ts.map +1 -1
  91. package/dist/primitives.js +164 -301
  92. package/dist/primitives.js.map +1 -1
  93. package/dist/reconcile.d.ts +11 -0
  94. package/dist/reconcile.d.ts.map +1 -0
  95. package/dist/reconcile.js +36 -0
  96. package/dist/reconcile.js.map +1 -0
  97. package/dist/select.d.ts +14 -0
  98. package/dist/select.d.ts.map +1 -0
  99. package/dist/select.js +304 -0
  100. package/dist/select.js.map +1 -0
  101. package/dist/styles.css +452 -382
  102. package/dist/surface.d.ts +60 -0
  103. package/dist/surface.d.ts.map +1 -0
  104. package/dist/surface.js +344 -0
  105. package/dist/surface.js.map +1 -0
  106. package/dist/tasks/approval.d.ts.map +1 -1
  107. package/dist/tasks/approval.js +86 -45
  108. package/dist/tasks/approval.js.map +1 -1
  109. package/dist/tasks/permission-element.d.ts +3 -0
  110. package/dist/tasks/permission-element.d.ts.map +1 -0
  111. package/dist/tasks/permission-element.js +10 -0
  112. package/dist/tasks/permission-element.js.map +1 -0
  113. package/dist/tasks/permission.d.ts +5 -3
  114. package/dist/tasks/permission.d.ts.map +1 -1
  115. package/dist/tasks/permission.js +47 -22
  116. package/dist/tasks/permission.js.map +1 -1
  117. package/dist/tasks/runtime.d.ts.map +1 -1
  118. package/dist/tasks/runtime.js +30 -13
  119. package/dist/tasks/runtime.js.map +1 -1
  120. package/dist/workspace.d.ts +58 -4
  121. package/dist/workspace.d.ts.map +1 -1
  122. package/dist/workspace.js +202 -44
  123. package/dist/workspace.js.map +1 -1
  124. package/package.json +1 -1
package/README.md CHANGED
@@ -4,6 +4,8 @@ Native TypeScript UI building blocks for AI-assisted application development. Ch
4
4
 
5
5
  The source is TypeScript and CSS. The package contains compiled ESM, declarations and a separate stylesheet. It uses browser DOM APIs, native controls and system fonts; no UI framework or animation runtime is required.
6
6
 
7
+ Presentation is fixed rather than assembled from style choices: system UI and monospace code fonts, small/medium/large text at 12/14/16 CSS px, regular/strong weights at 400/600, a shared 1.5 line height, and 4/8/12/16/24 CSS px spacing. Blocks choose the appropriate role automatically. Applications supply their own branding.
8
+
7
9
  ## Try the components
8
10
 
9
11
  The [interactive showroom](https://minifish-org.github.io/pith-ui/) groups **design foundations → base components → product blocks → composed pages**. It exposes real controls, theme and state comparisons, CSS-pixel measurements, spacing rulers and motion playback. Showroom data and actions are simulated and labelled accordingly. Automated checks do not substitute for human acceptance.
@@ -18,7 +20,7 @@ npm run dev
18
20
  ## Use the package
19
21
 
20
22
  ```sh
21
- npm install --save-exact pith-ui@0.1.0
23
+ npm install --save-exact pith-ui@0.1.4
22
24
  ```
23
25
 
24
26
  ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGzC,MAAM,WAAW,cAAc;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAAE;AAChG,MAAM,WAAW,oBAAoB;IAAG,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;CAAE;AAC5G,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACtE;AACD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAuCzG;AAED,MAAM,WAAW,cAAc;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAAE;AAE1E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAQxH;AACD,MAAM,WAAW,gBAAgB;IAAG,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAAE;AAC3F,wBAAgB,iBAAiB,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;CAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuBnL"}
1
+ {"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,MAAM,WAAW,cAAc;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAAE;AAChG,MAAM,WAAW,oBAAoB;IAAG,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;CAAE;AAC5G,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CACtE;AACD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAyCzG;AAED,MAAM,WAAW,cAAc;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;CAAE;AAE1E,wBAAgB,kBAAkB,CAAC,OAAO,EAAE;IAAE,SAAS,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CAQxH;AACD,MAAM,WAAW,gBAAgB;IAAG,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAAE;AAC3F,wBAAgB,iBAAiB,CAAC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;CAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgCnL"}
package/dist/composer.js CHANGED
@@ -1,8 +1,11 @@
1
- import { escape, icon } from './dom.js';
1
+ import { icon, setText } from './dom.js';
2
+ import { adoptProductParts } from './foundations.js';
3
+ import { reconcileKeyed } from './reconcile.js';
2
4
  export function createPendingMessages(options = {}) {
3
5
  const element = options.container ?? document.createElement('section');
4
6
  element.classList.add('pui-queued-messages');
5
7
  element.setAttribute('aria-label', 'Pending messages');
8
+ const parts = adoptProductParts(element);
6
9
  let destroyed = false;
7
10
  const rows = new Map();
8
11
  const heading = document.createElement('div');
@@ -47,7 +50,7 @@ export function createPendingMessages(options = {}) {
47
50
  count.hidden = !item.imageCount;
48
51
  for (const button of row.querySelectorAll('[data-queue-action]')) {
49
52
  button.dataset.queuedMessage = item.id;
50
- button.disabled = state.busy;
53
+ parts.action(button).update({ disabled: state.busy });
51
54
  if (button.dataset.queueAction === 'steer')
52
55
  button.hidden = item.mode !== 'follow-up' || !state.running;
53
56
  }
@@ -55,8 +58,9 @@ export function createPendingMessages(options = {}) {
55
58
  element.insertBefore(row, cursor);
56
59
  cursor = row.nextElementSibling;
57
60
  }
61
+ parts.update();
58
62
  }, destroy() { if (destroyed)
59
- return; destroyed = true; element.removeEventListener('click', click); rows.clear(); element.replaceChildren(); } };
63
+ return; destroyed = true; parts.destroy(); element.removeEventListener('click', click); rows.clear(); element.replaceChildren(); } };
60
64
  }
61
65
  export function createDropFeedback(options) {
62
66
  const element = options.container;
@@ -74,41 +78,56 @@ export function createAttachments(options = {}) {
74
78
  const element = options.container ?? document.createElement('div');
75
79
  element.classList.add('pui-draft-images');
76
80
  element.setAttribute('aria-label', 'Image attachments');
81
+ const parts = adoptProductParts(element);
82
+ let destroyed = false;
83
+ const figures = new Map([...element.querySelectorAll('[data-attachment-id]')].map(node => [node.dataset.attachmentId, node]));
77
84
  const click = (event) => {
78
85
  const target = event.target;
79
86
  const button = target.closest('[data-remove-image]');
80
- if (button && !button.disabled)
81
- options.onRemove?.(button.dataset.removeImage);
82
- if (target instanceof HTMLImageElement)
83
- options.onOpen?.(target);
87
+ if (button && !button.disabled && options.onRemove) {
88
+ event.stopPropagation();
89
+ options.onRemove(button.dataset.removeImage);
90
+ }
91
+ if (target instanceof HTMLImageElement && options.onOpen) {
92
+ event.stopPropagation();
93
+ options.onOpen(target);
94
+ }
95
+ };
96
+ const keydown = (event) => {
97
+ if (!options.onOpen || !(event.target instanceof HTMLImageElement) || event.isComposing || !['Enter', ' '].includes(event.key))
98
+ return;
99
+ event.preventDefault();
100
+ event.stopPropagation();
101
+ if (!event.repeat)
102
+ options.onOpen(event.target);
84
103
  };
85
104
  element.addEventListener('click', click);
105
+ element.addEventListener('keydown', keydown);
86
106
  return { element, update(state) {
107
+ if (destroyed)
108
+ return;
87
109
  element.hidden = !state.items.length;
88
- const keys = new Set(state.items.map(item => item.id));
89
- for (const node of element.querySelectorAll('[data-attachment-id]'))
90
- if (!keys.has(node.dataset.attachmentId))
91
- node.remove();
92
- for (const item of state.items) {
93
- let figure = [...element.children].find(node => node.dataset.attachmentId === item.id);
94
- if (!figure) {
95
- figure = document.createElement('figure');
110
+ reconcileKeyed(element, figures, state.items, { key: item => item.id, element: figure => figure,
111
+ create(item) {
112
+ const figure = document.createElement('figure');
96
113
  figure.className = 'pui-draft-image';
97
114
  figure.dataset.attachmentId = item.id;
98
115
  figure.innerHTML = `<img role="button" tabindex="0" title="Click to enlarge" /><figcaption></figcaption><button type="button" class="pui-remove-image">${icon('close')}</button>`;
99
- }
100
- const image = figure.querySelector('img');
101
- if (image.getAttribute('src') !== item.url)
102
- image.src = item.url;
103
- image.alt = item.name;
104
- figure.querySelector('figcaption').textContent = item.name;
105
- const button = figure.querySelector('button');
106
- button.dataset.removeImage = item.id;
107
- button.setAttribute('aria-label', 'Remove ' + item.name);
108
- button.disabled = !!state.disabled;
109
- if (element.children[state.items.indexOf(item)] !== figure)
110
- element.insertBefore(figure, element.children[state.items.indexOf(item)] ?? null);
111
- }
112
- }, destroy() { element.removeEventListener('click', click); element.replaceChildren(); } };
116
+ return figure;
117
+ },
118
+ update(figure, item) {
119
+ const image = figure.querySelector('img');
120
+ if (image.getAttribute('src') !== item.url)
121
+ image.src = item.url;
122
+ image.alt = item.name;
123
+ setText(figure.querySelector('figcaption'), item.name);
124
+ const button = figure.querySelector('button');
125
+ button.dataset.removeImage = item.id;
126
+ button.setAttribute('aria-label', 'Remove ' + item.name);
127
+ parts.action(button).update({ disabled: !!state.disabled });
128
+ }, destroy() { }, });
129
+ parts.update();
130
+ }, destroy() { if (destroyed)
131
+ return; destroyed = true; parts.destroy(); element.removeEventListener('click', click); element.removeEventListener('keydown', keydown); figures.clear(); element.replaceChildren(); } };
113
132
  }
114
133
  //# sourceMappingURL=composer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from './core.js';\nimport { escape, icon } from './dom.js';\n\nexport interface PendingMessage { id: string; text: string; mode: string; imageCount?: number; }\nexport interface PendingMessagesState { items: readonly PendingMessage[]; running: boolean; busy: boolean; }\nexport interface PendingMessagesOptions {\n container?: HTMLElement;\n onAction?: (action: 'edit' | 'delete' | 'steer', id: string) => void;\n}\nexport function createPendingMessages(options: PendingMessagesOptions = {}): UIBlock<PendingMessagesState> {\n const element = options.container ?? document.createElement('section');\n element.classList.add('pui-queued-messages'); element.setAttribute('aria-label', 'Pending messages');\n let destroyed = false;\n const rows = new Map<string, HTMLElement>();\n const heading = document.createElement('div'); heading.className = 'pui-section-heading'; heading.textContent = 'Pending messages';\n element.append(heading);\n const click = (event: Event) => {\n const button = (event.target as Element).closest<HTMLButtonElement>('[data-queue-action]');\n if (button && !button.disabled) options.onAction?.(button.dataset.queueAction as 'edit' | 'delete' | 'steer', button.dataset.queuedMessage!);\n };\n element.addEventListener('click', click);\n return { element, update(state) {\n if (destroyed) return;\n element.hidden = !state.items.length;\n const keep = new Set(state.items.map(item => item.id));\n for (const [key, row] of rows) if (!keep.has(key)) { row.remove(); rows.delete(key); }\n let cursor: Element | null = heading.nextElementSibling;\n for (const item of state.items) {\n let row = rows.get(item.id);\n if (!row) {\n row = document.createElement('div'); row.className = 'pui-queued-message';\n row.innerHTML = `<span class=\"pui-queue-badge\">Instruction</span><div class=\"pui-queued-message-content\"><span class=\"pui-queued-message-text\"></span><small class=\"pui-queue-image-count\"></small></div><div class=\"pui-queued-message-actions\"><button type=\"button\" class=\"pui-quiet-icon pui-queue-steer\" data-queue-action=\"steer\" title=\"Steer the current task at its next turn\">${icon('steer')}<span>Steer</span></button><button type=\"button\" class=\"pui-quiet-icon\" data-queue-action=\"edit\" aria-label=\"Edit pending message\" title=\"Edit message\">${icon('edit')}</button><button type=\"button\" class=\"pui-quiet-icon\" data-queue-action=\"delete\" aria-label=\"Delete pending message\" title=\"Delete message\">${icon('trash')}</button></div>`;\n rows.set(item.id, row);\n }\n row.querySelector<HTMLElement>('.pui-queue-badge')!.hidden = item.mode !== 'steer';\n const text = row.querySelector<HTMLElement>('.pui-queued-message-text')!;\n const value = item.text || 'Image attachment'; if (text.textContent !== value) text.textContent = value; text.title = item.text;\n const count = row.querySelector<HTMLElement>('.pui-queue-image-count')!;\n const label = item.imageCount ? `${item.imageCount} image${item.imageCount === 1 ? '' : 's'}` : '';\n if (count.textContent !== label) count.textContent = label; count.hidden = !item.imageCount;\n for (const button of row.querySelectorAll<HTMLButtonElement>('[data-queue-action]')) {\n button.dataset.queuedMessage = item.id; button.disabled = state.busy;\n if (button.dataset.queueAction === 'steer') button.hidden = item.mode !== 'follow-up' || !state.running;\n }\n if (row !== cursor) element.insertBefore(row, cursor);\n cursor = row.nextElementSibling;\n }\n }, destroy() { if (destroyed) return; destroyed = true; element.removeEventListener('click', click); rows.clear(); element.replaceChildren(); } };\n}\n\nexport interface AttachmentItem { id: string; url: string; name: string; }\n\nexport function createDropFeedback(options: { container: HTMLElement }): UIBlock<{ active: boolean; disabled?: boolean }> {\n const element = options.container;\n let destroyed = false;\n return { element, update(state) {\n if (destroyed) return;\n if (state.active && !state.disabled) element.classList.add('pui-drag-images');\n else element.classList.remove('pui-drag-images');\n }, destroy() { destroyed = true; element.classList.remove('pui-drag-images'); } };\n}\nexport interface AttachmentsState { items: readonly AttachmentItem[]; disabled?: boolean; }\nexport function createAttachments(options: { container?: HTMLElement; onRemove?: (id: string) => void; onOpen?: (image: HTMLImageElement) => void } = {}): UIBlock<AttachmentsState> {\n const element = options.container ?? document.createElement('div'); element.classList.add('pui-draft-images'); element.setAttribute('aria-label', 'Image attachments');\n const click = (event: Event) => {\n const target = event.target as Element;\n const button = target.closest<HTMLButtonElement>('[data-remove-image]');\n if (button && !button.disabled) options.onRemove?.(button.dataset.removeImage!);\n if (target instanceof HTMLImageElement) options.onOpen?.(target);\n };\n element.addEventListener('click', click);\n return { element, update(state) {\n element.hidden = !state.items.length;\n const keys = new Set(state.items.map(item => item.id));\n for (const node of element.querySelectorAll<HTMLElement>('[data-attachment-id]')) if (!keys.has(node.dataset.attachmentId!)) node.remove();\n for (const item of state.items) {\n let figure = [...element.children].find(node => (node as HTMLElement).dataset.attachmentId === item.id) as HTMLElement | undefined;\n if (!figure) { figure = document.createElement('figure'); figure.className = 'pui-draft-image'; figure.dataset.attachmentId = item.id;\n figure.innerHTML = `<img role=\"button\" tabindex=\"0\" title=\"Click to enlarge\" /><figcaption></figcaption><button type=\"button\" class=\"pui-remove-image\">${icon('close')}</button>`; }\n const image = figure.querySelector('img')!; if (image.getAttribute('src') !== item.url) image.src = item.url; image.alt = item.name;\n figure.querySelector('figcaption')!.textContent = item.name;\n const button = figure.querySelector('button')!; button.dataset.removeImage = item.id; button.setAttribute('aria-label', 'Remove ' + item.name); button.disabled = !!state.disabled;\n if (element.children[state.items.indexOf(item)] !== figure) element.insertBefore(figure, element.children[state.items.indexOf(item)] ?? null);\n }\n }, destroy() { element.removeEventListener('click', click); element.replaceChildren(); } };\n}\n"]}
1
+ {"version":3,"file":"composer.js","sourceRoot":"","sources":["../src/composer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from './core.js';\nimport { icon, setText } from './dom.js';\nimport { adoptProductParts } from './foundations.js';\nimport { reconcileKeyed } from './reconcile.js';\n\nexport interface PendingMessage { id: string; text: string; mode: string; imageCount?: number; }\nexport interface PendingMessagesState { items: readonly PendingMessage[]; running: boolean; busy: boolean; }\nexport interface PendingMessagesOptions {\n container?: HTMLElement;\n onAction?: (action: 'edit' | 'delete' | 'steer', id: string) => void;\n}\nexport function createPendingMessages(options: PendingMessagesOptions = {}): UIBlock<PendingMessagesState> {\n const element = options.container ?? document.createElement('section');\n element.classList.add('pui-queued-messages'); element.setAttribute('aria-label', 'Pending messages');\n const parts = adoptProductParts(element);\n let destroyed = false;\n const rows = new Map<string, HTMLElement>();\n const heading = document.createElement('div'); heading.className = 'pui-section-heading'; heading.textContent = 'Pending messages';\n element.append(heading);\n const click = (event: Event) => {\n const button = (event.target as Element).closest<HTMLButtonElement>('[data-queue-action]');\n if (button && !button.disabled) options.onAction?.(button.dataset.queueAction as 'edit' | 'delete' | 'steer', button.dataset.queuedMessage!);\n };\n element.addEventListener('click', click);\n return { element, update(state) {\n if (destroyed) return;\n element.hidden = !state.items.length;\n const keep = new Set(state.items.map(item => item.id));\n for (const [key, row] of rows) if (!keep.has(key)) { row.remove(); rows.delete(key); }\n let cursor: Element | null = heading.nextElementSibling;\n for (const item of state.items) {\n let row = rows.get(item.id);\n if (!row) {\n row = document.createElement('div'); row.className = 'pui-queued-message';\n row.innerHTML = `<span class=\"pui-queue-badge\">Instruction</span><div class=\"pui-queued-message-content\"><span class=\"pui-queued-message-text\"></span><small class=\"pui-queue-image-count\"></small></div><div class=\"pui-queued-message-actions\"><button type=\"button\" class=\"pui-quiet-icon pui-queue-steer\" data-queue-action=\"steer\" title=\"Steer the current task at its next turn\">${icon('steer')}<span>Steer</span></button><button type=\"button\" class=\"pui-quiet-icon\" data-queue-action=\"edit\" aria-label=\"Edit pending message\" title=\"Edit message\">${icon('edit')}</button><button type=\"button\" class=\"pui-quiet-icon\" data-queue-action=\"delete\" aria-label=\"Delete pending message\" title=\"Delete message\">${icon('trash')}</button></div>`;\n rows.set(item.id, row);\n }\n row.querySelector<HTMLElement>('.pui-queue-badge')!.hidden = item.mode !== 'steer';\n const text = row.querySelector<HTMLElement>('.pui-queued-message-text')!;\n const value = item.text || 'Image attachment'; if (text.textContent !== value) text.textContent = value; text.title = item.text;\n const count = row.querySelector<HTMLElement>('.pui-queue-image-count')!;\n const label = item.imageCount ? `${item.imageCount} image${item.imageCount === 1 ? '' : 's'}` : '';\n if (count.textContent !== label) count.textContent = label; count.hidden = !item.imageCount;\n for (const button of row.querySelectorAll<HTMLButtonElement>('[data-queue-action]')) {\n button.dataset.queuedMessage = item.id; parts.action(button).update({ disabled: state.busy });\n if (button.dataset.queueAction === 'steer') button.hidden = item.mode !== 'follow-up' || !state.running;\n }\n if (row !== cursor) element.insertBefore(row, cursor);\n cursor = row.nextElementSibling;\n }\n parts.update();\n }, destroy() { if (destroyed) return; destroyed = true; parts.destroy(); element.removeEventListener('click', click); rows.clear(); element.replaceChildren(); } };\n}\n\nexport interface AttachmentItem { id: string; url: string; name: string; }\n\nexport function createDropFeedback(options: { container: HTMLElement }): UIBlock<{ active: boolean; disabled?: boolean }> {\n const element = options.container;\n let destroyed = false;\n return { element, update(state) {\n if (destroyed) return;\n if (state.active && !state.disabled) element.classList.add('pui-drag-images');\n else element.classList.remove('pui-drag-images');\n }, destroy() { destroyed = true; element.classList.remove('pui-drag-images'); } };\n}\nexport interface AttachmentsState { items: readonly AttachmentItem[]; disabled?: boolean; }\nexport function createAttachments(options: { container?: HTMLElement; onRemove?: (id: string) => void; onOpen?: (image: HTMLImageElement) => void } = {}): UIBlock<AttachmentsState> {\n const element = options.container ?? document.createElement('div'); element.classList.add('pui-draft-images'); element.setAttribute('aria-label', 'Image attachments');\n const parts = adoptProductParts(element);\n let destroyed = false;\n const figures = new Map([...element.querySelectorAll<HTMLElement>('[data-attachment-id]')].map(node => [node.dataset.attachmentId!, node]));\n const click = (event: Event) => {\n const target = event.target as Element;\n const button = target.closest<HTMLButtonElement>('[data-remove-image]');\n if (button && !button.disabled && options.onRemove) {event.stopPropagation(); options.onRemove(button.dataset.removeImage!);}\n if (target instanceof HTMLImageElement && options.onOpen) {event.stopPropagation(); options.onOpen(target);}\n };\n const keydown = (event: KeyboardEvent) => {\n if (!options.onOpen || !(event.target instanceof HTMLImageElement) || event.isComposing || !['Enter',' '].includes(event.key)) return;\n event.preventDefault(); event.stopPropagation();\n if (!event.repeat) options.onOpen(event.target);\n };\n element.addEventListener('click', click);\n element.addEventListener('keydown', keydown);\n return { element, update(state) {\n if (destroyed) return;\n element.hidden = !state.items.length;\n reconcileKeyed(element,figures,state.items,{key:item=>item.id,element:figure=>figure,\n create(item) {const figure = document.createElement('figure'); figure.className = 'pui-draft-image'; figure.dataset.attachmentId = item.id;\n figure.innerHTML = `<img role=\"button\" tabindex=\"0\" title=\"Click to enlarge\" /><figcaption></figcaption><button type=\"button\" class=\"pui-remove-image\">${icon('close')}</button>`;return figure;},\n update(figure,item) {\n const image = figure.querySelector('img')!; if (image.getAttribute('src') !== item.url) image.src = item.url; image.alt = item.name;\n setText(figure.querySelector('figcaption')!,item.name);\n const button = figure.querySelector('button')!; button.dataset.removeImage = item.id; button.setAttribute('aria-label', 'Remove ' + item.name); parts.action(button).update({ disabled: !!state.disabled });\n },destroy() {},\n });\n parts.update();\n }, destroy() { if (destroyed) return; destroyed = true; parts.destroy(); element.removeEventListener('click', click); element.removeEventListener('keydown', keydown); figures.clear(); element.replaceChildren(); } };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-reader.d.ts","sourceRoot":"","sources":["../../src/content/conversation-reader.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAGzD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAInE;AAKD,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,EAAE,WAAW,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;CACnC;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,yBAA8B;qBA2LrJ,MAAM,GAAG,IAAI;mBAQf,IAAI;eAYR,MAAM,EAAE,GAAG,IAAI;wBArJH,IAAI;oBAiFR,IAAI;qBAMH,IAAI;eAkEb,IAAI;EAYlB"}
1
+ {"version":3,"file":"conversation-reader.d.ts","sourceRoot":"","sources":["../../src/content/conversation-reader.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAGzD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAInE;AAKD,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,EAAE,WAAW,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC;CACnC;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,yBAA8B;qBA8LrJ,MAAM,GAAG,IAAI;mBAQf,IAAI;eAYR,MAAM,EAAE,GAAG,IAAI;wBAxJH,IAAI;oBAkFR,IAAI;qBAOH,IAAI;eAmEb,IAAI;EAYlB"}
@@ -66,6 +66,8 @@ export function installConversationReader(scroll, messages, bar, latest, options
66
66
  updateLatest();
67
67
  }
68
68
  function followLatest() {
69
+ if (destroyed)
70
+ return;
69
71
  const position = { top: 0, follow: true };
70
72
  if (pendingPosition)
71
73
  pendingPosition = position;
@@ -161,12 +163,16 @@ export function installConversationReader(scroll, messages, bar, latest, options
161
163
  reveal();
162
164
  }
163
165
  function openFind() {
166
+ if (destroyed)
167
+ return;
164
168
  bar.hidden = false;
165
169
  markMatches();
166
170
  field.focus();
167
171
  field.select();
168
172
  }
169
173
  function closeFind() {
174
+ if (destroyed)
175
+ return;
170
176
  bar.hidden = true;
171
177
  clearMarks();
172
178
  matches = [];
@@ -257,7 +263,8 @@ export function installConversationReader(scroll, messages, bar, latest, options
257
263
  updateLatest();
258
264
  });
259
265
  },
260
- prune(ids) { const keep = new Set(ids); for (const id of positions.keys())
266
+ prune(ids) { if (destroyed)
267
+ return; const keep = new Set(ids); for (const id of positions.keys())
261
268
  if (!keep.has(id))
262
269
  positions.delete(id); },
263
270
  followLatest,
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-reader.js","sourceRoot":"","sources":["../../src/content/conversation-reader.ts"],"names":[],"mappings":"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","sourcesContent":["export interface TextRange { start: number; end: number }\n\n// Literal, case-insensitive matches, with offsets into the original text.\nexport function findRanges(text: string, query: string): TextRange[] {\n if (!query) return [];\n const pattern = new RegExp(query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'giu');\n return [...text.matchAll(pattern)].map((match) => ({ start: match.index!, end: match.index! + match[0].length }));\n}\n\ninterface ReadingPosition { top: number; follow: boolean; anchor?: string; offset?: number }\ninterface Match { key: string; parts: HTMLElement[] }\n\nexport interface ConversationReaderOptions {\n keyboardTarget?: EventTarget;\n isKeyboardBlocked?: () => boolean;\n}\n\nexport function installConversationReader(scroll: HTMLElement, messages: HTMLElement, bar: HTMLElement, latest: HTMLButtonElement, options: ConversationReaderOptions = {}) {\n let destroyed = false;\n const removals: Array<() => void> = [];\n function listen<T extends Event>(target: EventTarget, type: string, listener: (event: T) => void, settings?: AddEventListenerOptions): void {\n target.addEventListener(type, listener as EventListener, settings);\n removals.push(() => target.removeEventListener?.(type, listener as EventListener, settings));\n }\n const field = bar.querySelector<HTMLInputElement>('input')!;\n const count = bar.querySelector<HTMLElement>('[data-find-count]')!;\n const previous = bar.querySelector<HTMLButtonElement>('[data-find-previous]')!;\n const next = bar.querySelector<HTMLButtonElement>('[data-find-next]')!;\n const close = bar.querySelector<HTMLButtonElement>('[data-find-close]')!;\n const positions = new Map<string, ReadingPosition>();\n let activeID = '';\n let matches: Match[] = [];\n let selected = -1;\n let frame = 0;\n let pendingPosition: ReadingPosition | undefined;\n let restoring = false;\n let searchSignature = '';\n let contentHeight = scroll.scrollHeight;\n let viewportHeight = scroll.clientHeight;\n let viewportWidth = scroll.clientWidth;\n\n const nearBottom = () => scroll.scrollHeight - scroll.scrollTop - scroll.clientHeight < 80;\n const layoutChanged = () => contentHeight !== scroll.scrollHeight || viewportHeight !== scroll.clientHeight || viewportWidth !== scroll.clientWidth;\n function recordLayout(): void {\n contentHeight = scroll.scrollHeight;\n viewportHeight = scroll.clientHeight;\n viewportWidth = scroll.clientWidth;\n }\n function capture(follow = nearBottom()): ReadingPosition {\n const top = scroll.getBoundingClientRect().top;\n const anchor = [...messages.querySelectorAll<HTMLElement>('[data-reading-id]')].find((element) => element.getBoundingClientRect().bottom > top);\n return { top: scroll.scrollTop, follow, anchor: anchor?.dataset.readingId, offset: anchor ? anchor.getBoundingClientRect().top - top : undefined };\n }\n function updateLatest(): void { latest.hidden = !messages.childElementCount || nearBottom(); }\n function remember(): void {\n if (activeID && !restoring && !pendingPosition) positions.set(activeID, capture());\n recordLayout();\n updateLatest();\n }\n function restore(position: ReadingPosition): void {\n restoring = true;\n if (position.follow) scroll.scrollTop = scroll.scrollHeight;\n else {\n const anchor = [...messages.querySelectorAll<HTMLElement>('[data-reading-id]')].find((element) => element.dataset.readingId === position.anchor);\n scroll.scrollTop = anchor && position.offset !== undefined\n ? scroll.scrollTop + anchor.getBoundingClientRect().top - scroll.getBoundingClientRect().top - position.offset\n : position.top;\n }\n restoring = false;\n // Following is user intent. Layout changes (including image decoding) must\n // not turn it off, or turn a history reader back into a follower.\n if (activeID) positions.set(activeID, capture(position.follow));\n recordLayout();\n updateLatest();\n }\n function followLatest(): void {\n const position = { top: 0, follow: true };\n if (pendingPosition) pendingPosition = position;\n restore(position);\n }\n\n let cachedMatches = new WeakMap<HTMLElement, { query: string; version?: string; matches: typeof matches }>();\n function clearMarks(root: HTMLElement = messages): void {\n for (const mark of root.querySelectorAll('mark[data-find-match]')) mark.replaceWith(document.createTextNode(mark.textContent || ''));\n root.normalize();\n if (root === messages) cachedMatches = new WeakMap();\n else cachedMatches.delete(root);\n }\n function markMatches(): void {\n const previousKey = matches[selected]?.key;\n matches = [];\n const query = field.value;\n if (!bar.hidden && query) {\n for (const root of messages.querySelectorAll<HTMLElement>('[data-reading-id]')) {\n const cached = cachedMatches.get(root);\n if (cached?.query === query && cached.version === root.dataset.renderVersion) { matches.push(...cached.matches); continue; }\n clearMarks(root);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\n acceptNode: (node) => node.parentElement?.closest('button, summary, script, style, [data-find-ignore]') ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT,\n });\n const nodes: { node: Text; start: number; end: number }[] = [];\n let text = '', block: Element | null = null;\n while (walker.nextNode()) {\n const node = walker.currentNode as Text;\n const parent = node.parentElement?.closest('p, pre, li, h1, h2, h3, h4, h5, h6, .pui-user-text, .pui-system-message') || root;\n if (text && parent !== block) text += '\\n';\n block = parent;\n const start = text.length;\n text += node.data;\n nodes.push({ node, start, end: text.length });\n }\n const ranges = findRanges(text, query);\n const base = matches.length;\n matches.push(...ranges.map((range) => ({ key: `${root.dataset.readingId}:${range.start}`, parts: [] })));\n for (const { node, start, end } of nodes) {\n const intersections = ranges.map((range, index) => ({ start: Math.max(start, range.start) - start, end: Math.min(end, range.end) - start, index: base + index })).filter((range) => range.end > range.start);\n if (!intersections.length) continue;\n const fragment = document.createDocumentFragment();\n let offset = 0;\n for (const range of intersections) {\n fragment.append(document.createTextNode(node.data.slice(offset, range.start)));\n const mark = document.createElement('mark');\n mark.dataset.findMatch = String(range.index);\n mark.textContent = node.data.slice(range.start, range.end);\n matches[range.index].parts.push(mark);\n fragment.append(mark);\n offset = range.end;\n }\n fragment.append(document.createTextNode(node.data.slice(offset)));\n node.replaceWith(fragment);\n }\n cachedMatches.set(root, { query, version: root.dataset.renderVersion, matches: matches.slice(base) });\n }\n } else clearMarks();\n const kept = matches.findIndex((match) => match.key === previousKey);\n selected = matches.length ? kept >= 0 ? kept : Math.max(0, Math.min(selected, matches.length - 1)) : -1;\n updateSelection();\n }\n function updateSelection(): void {\n matches.forEach((match, index) => match.parts.forEach((part) => part.classList.toggle('pui-find-current', index === selected)));\n count.textContent = field.value ? matches.length ? `${selected + 1} / ${matches.length}` : 'No matches' : '';\n previous.disabled = next.disabled = !matches.length;\n }\n function reveal(): void {\n const target = matches[selected]?.parts[0];\n if (!target) return;\n for (let parent = target.parentElement; parent && parent !== messages; parent = parent.parentElement) if (parent instanceof HTMLDetailsElement) parent.open = true;\n scroll.scrollTop += target.getBoundingClientRect().top - scroll.getBoundingClientRect().top - scroll.clientHeight / 3;\n remember();\n }\n function move(direction: number): void {\n if (!matches.length) return;\n selected = (selected + direction + matches.length) % matches.length;\n updateSelection();\n reveal();\n }\n function openFind(): void {\n bar.hidden = false;\n markMatches();\n field.focus();\n field.select();\n }\n function closeFind(): void {\n bar.hidden = true;\n clearMarks();\n matches = [];\n selected = -1;\n scroll.focus({ preventScroll: true });\n updateLatest();\n }\n listen(field, 'input', () => { selected = -1; matches = []; markMatches(); reveal(); });\n listen<KeyboardEvent>(field, 'keydown', (event) => {\n if (event.key === 'Enter' && !event.isComposing) { event.preventDefault(); move(event.shiftKey ? -1 : 1); }\n });\n listen(previous, 'click', () => move(-1));\n listen(next, 'click', () => move(1));\n listen(close, 'click', closeFind);\n listen<KeyboardEvent>(options.keyboardTarget || document, 'keydown', (event) => {\n if (event.isComposing || options.isKeyboardBlocked?.()) return;\n if ((event.metaKey || event.ctrlKey) && !event.altKey && event.key.toLowerCase() === 'f') { event.preventDefault(); openFind(); }\n else if (event.key === 'Escape' && !bar.hidden) { event.preventDefault(); closeFind(); }\n });\n listen(latest, 'click', followLatest);\n listen(scroll, 'scroll', () => {\n if (restoring || pendingPosition) return;\n const saved = positions.get(activeID);\n // A resize can clamp scrollTop and dispatch a scroll before ResizeObserver.\n // Only scrolling within a stable layout changes the following preference.\n if (saved && layoutChanged()) restore(saved);\n else if (!saved || Math.abs(scroll.scrollTop - saved.top) > 1) remember();\n else updateLatest();\n }, { passive: true });\n // Async image decoding and resizing must not displace an existing anchor.\n const observer = new ResizeObserver(() => {\n if (destroyed) return;\n if (!pendingPosition && activeID) {\n const saved = positions.get(activeID);\n if (saved) restore(saved);\n }\n updateLatest();\n });\n observer.observe(messages);\n observer.observe(scroll);\n return {\n beforeRender(id: string): void {\n if (destroyed) return;\n if (activeID && !positions.has(activeID)) positions.set(activeID, capture());\n pendingPosition = id === activeID ? pendingPosition || positions.get(activeID) || capture() : positions.get(id) || { top: 0, follow: true };\n if (id !== activeID) { selected = -1; matches = []; searchSignature = ''; }\n activeID = id;\n cancelAnimationFrame(frame);\n },\n afterRender(): void {\n if (destroyed) return;\n const signature = messages.innerHTML;\n if (!bar.hidden && signature !== searchSignature) { markMatches(); searchSignature = messages.innerHTML; }\n frame = requestAnimationFrame(() => {\n if (destroyed) return;\n const position = pendingPosition;\n pendingPosition = undefined;\n if (position) restore(position);\n else updateLatest();\n });\n },\n prune(ids: string[]): void { const keep = new Set(ids); for (const id of positions.keys()) if (!keep.has(id)) positions.delete(id); },\n followLatest,\n openFind,\n closeFind,\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n cancelAnimationFrame(frame);\n observer.disconnect();\n removals.splice(0).forEach((remove) => remove());\n clearMarks();\n positions.clear();\n matches = [];\n pendingPosition = undefined;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"conversation-reader.js","sourceRoot":"","sources":["../../src/content/conversation-reader.ts"],"names":[],"mappings":"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","sourcesContent":["export interface TextRange { start: number; end: number }\n\n// Literal, case-insensitive matches, with offsets into the original text.\nexport function findRanges(text: string, query: string): TextRange[] {\n if (!query) return [];\n const pattern = new RegExp(query.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'giu');\n return [...text.matchAll(pattern)].map((match) => ({ start: match.index!, end: match.index! + match[0].length }));\n}\n\ninterface ReadingPosition { top: number; follow: boolean; anchor?: string; offset?: number }\ninterface Match { key: string; parts: HTMLElement[] }\n\nexport interface ConversationReaderOptions {\n keyboardTarget?: EventTarget;\n isKeyboardBlocked?: () => boolean;\n}\n\nexport function installConversationReader(scroll: HTMLElement, messages: HTMLElement, bar: HTMLElement, latest: HTMLButtonElement, options: ConversationReaderOptions = {}) {\n let destroyed = false;\n const removals: Array<() => void> = [];\n function listen<T extends Event>(target: EventTarget, type: string, listener: (event: T) => void, settings?: AddEventListenerOptions): void {\n target.addEventListener(type, listener as EventListener, settings);\n removals.push(() => target.removeEventListener?.(type, listener as EventListener, settings));\n }\n const field = bar.querySelector<HTMLInputElement>('input')!;\n const count = bar.querySelector<HTMLElement>('[data-find-count]')!;\n const previous = bar.querySelector<HTMLButtonElement>('[data-find-previous]')!;\n const next = bar.querySelector<HTMLButtonElement>('[data-find-next]')!;\n const close = bar.querySelector<HTMLButtonElement>('[data-find-close]')!;\n const positions = new Map<string, ReadingPosition>();\n let activeID = '';\n let matches: Match[] = [];\n let selected = -1;\n let frame = 0;\n let pendingPosition: ReadingPosition | undefined;\n let restoring = false;\n let searchSignature = '';\n let contentHeight = scroll.scrollHeight;\n let viewportHeight = scroll.clientHeight;\n let viewportWidth = scroll.clientWidth;\n\n const nearBottom = () => scroll.scrollHeight - scroll.scrollTop - scroll.clientHeight < 80;\n const layoutChanged = () => contentHeight !== scroll.scrollHeight || viewportHeight !== scroll.clientHeight || viewportWidth !== scroll.clientWidth;\n function recordLayout(): void {\n contentHeight = scroll.scrollHeight;\n viewportHeight = scroll.clientHeight;\n viewportWidth = scroll.clientWidth;\n }\n function capture(follow = nearBottom()): ReadingPosition {\n const top = scroll.getBoundingClientRect().top;\n const anchor = [...messages.querySelectorAll<HTMLElement>('[data-reading-id]')].find((element) => element.getBoundingClientRect().bottom > top);\n return { top: scroll.scrollTop, follow, anchor: anchor?.dataset.readingId, offset: anchor ? anchor.getBoundingClientRect().top - top : undefined };\n }\n function updateLatest(): void { latest.hidden = !messages.childElementCount || nearBottom(); }\n function remember(): void {\n if (activeID && !restoring && !pendingPosition) positions.set(activeID, capture());\n recordLayout();\n updateLatest();\n }\n function restore(position: ReadingPosition): void {\n restoring = true;\n if (position.follow) scroll.scrollTop = scroll.scrollHeight;\n else {\n const anchor = [...messages.querySelectorAll<HTMLElement>('[data-reading-id]')].find((element) => element.dataset.readingId === position.anchor);\n scroll.scrollTop = anchor && position.offset !== undefined\n ? scroll.scrollTop + anchor.getBoundingClientRect().top - scroll.getBoundingClientRect().top - position.offset\n : position.top;\n }\n restoring = false;\n // Following is user intent. Layout changes (including image decoding) must\n // not turn it off, or turn a history reader back into a follower.\n if (activeID) positions.set(activeID, capture(position.follow));\n recordLayout();\n updateLatest();\n }\n function followLatest(): void {\n if (destroyed) return;\n const position = { top: 0, follow: true };\n if (pendingPosition) pendingPosition = position;\n restore(position);\n }\n\n let cachedMatches = new WeakMap<HTMLElement, { query: string; version?: string; matches: typeof matches }>();\n function clearMarks(root: HTMLElement = messages): void {\n for (const mark of root.querySelectorAll('mark[data-find-match]')) mark.replaceWith(document.createTextNode(mark.textContent || ''));\n root.normalize();\n if (root === messages) cachedMatches = new WeakMap();\n else cachedMatches.delete(root);\n }\n function markMatches(): void {\n const previousKey = matches[selected]?.key;\n matches = [];\n const query = field.value;\n if (!bar.hidden && query) {\n for (const root of messages.querySelectorAll<HTMLElement>('[data-reading-id]')) {\n const cached = cachedMatches.get(root);\n if (cached?.query === query && cached.version === root.dataset.renderVersion) { matches.push(...cached.matches); continue; }\n clearMarks(root);\n const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {\n acceptNode: (node) => node.parentElement?.closest('button, summary, script, style, [data-find-ignore]') ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT,\n });\n const nodes: { node: Text; start: number; end: number }[] = [];\n let text = '', block: Element | null = null;\n while (walker.nextNode()) {\n const node = walker.currentNode as Text;\n const parent = node.parentElement?.closest('p, pre, li, h1, h2, h3, h4, h5, h6, .pui-user-text, .pui-system-message') || root;\n if (text && parent !== block) text += '\\n';\n block = parent;\n const start = text.length;\n text += node.data;\n nodes.push({ node, start, end: text.length });\n }\n const ranges = findRanges(text, query);\n const base = matches.length;\n matches.push(...ranges.map((range) => ({ key: `${root.dataset.readingId}:${range.start}`, parts: [] })));\n for (const { node, start, end } of nodes) {\n const intersections = ranges.map((range, index) => ({ start: Math.max(start, range.start) - start, end: Math.min(end, range.end) - start, index: base + index })).filter((range) => range.end > range.start);\n if (!intersections.length) continue;\n const fragment = document.createDocumentFragment();\n let offset = 0;\n for (const range of intersections) {\n fragment.append(document.createTextNode(node.data.slice(offset, range.start)));\n const mark = document.createElement('mark');\n mark.dataset.findMatch = String(range.index);\n mark.textContent = node.data.slice(range.start, range.end);\n matches[range.index].parts.push(mark);\n fragment.append(mark);\n offset = range.end;\n }\n fragment.append(document.createTextNode(node.data.slice(offset)));\n node.replaceWith(fragment);\n }\n cachedMatches.set(root, { query, version: root.dataset.renderVersion, matches: matches.slice(base) });\n }\n } else clearMarks();\n const kept = matches.findIndex((match) => match.key === previousKey);\n selected = matches.length ? kept >= 0 ? kept : Math.max(0, Math.min(selected, matches.length - 1)) : -1;\n updateSelection();\n }\n function updateSelection(): void {\n matches.forEach((match, index) => match.parts.forEach((part) => part.classList.toggle('pui-find-current', index === selected)));\n count.textContent = field.value ? matches.length ? `${selected + 1} / ${matches.length}` : 'No matches' : '';\n previous.disabled = next.disabled = !matches.length;\n }\n function reveal(): void {\n const target = matches[selected]?.parts[0];\n if (!target) return;\n for (let parent = target.parentElement; parent && parent !== messages; parent = parent.parentElement) if (parent instanceof HTMLDetailsElement) parent.open = true;\n scroll.scrollTop += target.getBoundingClientRect().top - scroll.getBoundingClientRect().top - scroll.clientHeight / 3;\n remember();\n }\n function move(direction: number): void {\n if (!matches.length) return;\n selected = (selected + direction + matches.length) % matches.length;\n updateSelection();\n reveal();\n }\n function openFind(): void {\n if (destroyed) return;\n bar.hidden = false;\n markMatches();\n field.focus();\n field.select();\n }\n function closeFind(): void {\n if (destroyed) return;\n bar.hidden = true;\n clearMarks();\n matches = [];\n selected = -1;\n scroll.focus({ preventScroll: true });\n updateLatest();\n }\n listen(field, 'input', () => { selected = -1; matches = []; markMatches(); reveal(); });\n listen<KeyboardEvent>(field, 'keydown', (event) => {\n if (event.key === 'Enter' && !event.isComposing) { event.preventDefault(); move(event.shiftKey ? -1 : 1); }\n });\n listen(previous, 'click', () => move(-1));\n listen(next, 'click', () => move(1));\n listen(close, 'click', closeFind);\n listen<KeyboardEvent>(options.keyboardTarget || document, 'keydown', (event) => {\n if (event.isComposing || options.isKeyboardBlocked?.()) return;\n if ((event.metaKey || event.ctrlKey) && !event.altKey && event.key.toLowerCase() === 'f') { event.preventDefault(); openFind(); }\n else if (event.key === 'Escape' && !bar.hidden) { event.preventDefault(); closeFind(); }\n });\n listen(latest, 'click', followLatest);\n listen(scroll, 'scroll', () => {\n if (restoring || pendingPosition) return;\n const saved = positions.get(activeID);\n // A resize can clamp scrollTop and dispatch a scroll before ResizeObserver.\n // Only scrolling within a stable layout changes the following preference.\n if (saved && layoutChanged()) restore(saved);\n else if (!saved || Math.abs(scroll.scrollTop - saved.top) > 1) remember();\n else updateLatest();\n }, { passive: true });\n // Async image decoding and resizing must not displace an existing anchor.\n const observer = new ResizeObserver(() => {\n if (destroyed) return;\n if (!pendingPosition && activeID) {\n const saved = positions.get(activeID);\n if (saved) restore(saved);\n }\n updateLatest();\n });\n observer.observe(messages);\n observer.observe(scroll);\n return {\n beforeRender(id: string): void {\n if (destroyed) return;\n if (activeID && !positions.has(activeID)) positions.set(activeID, capture());\n pendingPosition = id === activeID ? pendingPosition || positions.get(activeID) || capture() : positions.get(id) || { top: 0, follow: true };\n if (id !== activeID) { selected = -1; matches = []; searchSignature = ''; }\n activeID = id;\n cancelAnimationFrame(frame);\n },\n afterRender(): void {\n if (destroyed) return;\n const signature = messages.innerHTML;\n if (!bar.hidden && signature !== searchSignature) { markMatches(); searchSignature = messages.innerHTML; }\n frame = requestAnimationFrame(() => {\n if (destroyed) return;\n const position = pendingPosition;\n pendingPosition = undefined;\n if (position) restore(position);\n else updateLatest();\n });\n },\n prune(ids: string[]): void { if (destroyed) return; const keep = new Set(ids); for (const id of positions.keys()) if (!keep.has(id)) positions.delete(id); },\n followLatest,\n openFind,\n closeFind,\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n cancelAnimationFrame(frame);\n observer.disconnect();\n removals.splice(0).forEach((remove) => remove());\n clearMarks();\n positions.clear();\n matches = [];\n pendingPosition = undefined;\n },\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { UIBlock } from '../core.js';
1
+ import { type UIBlock } from '../core.js';
2
2
  import { type MarkdownOptions } from './markdown.js';
3
3
  /** UI data only. Hosts decide copy/edit/branch eligibility and resolve images. */
4
4
  export interface ConversationMessage {
@@ -38,11 +38,28 @@ export interface ConversationAction {
38
38
  messageId: string;
39
39
  branchKey?: string;
40
40
  }
41
+ export interface ConversationActionAvailability {
42
+ copy?: boolean;
43
+ edit?: boolean;
44
+ branch?: boolean;
45
+ }
46
+ export type ConversationImageState = {
47
+ status: 'ready';
48
+ src: string;
49
+ } | {
50
+ status: 'unavailable';
51
+ };
52
+ export interface ConversationViewBlock extends UIBlock<ConversationViewState> {
53
+ /** Disabled state, separate from whether an action exists for a message. */
54
+ updateActionAvailability(disabled: Readonly<ConversationActionAvailability>): void;
55
+ images(): readonly HTMLImageElement[];
56
+ setImageState(image: HTMLImageElement, state: ConversationImageState): void;
57
+ }
41
58
  export interface ConversationViewOptions {
42
59
  container?: HTMLElement;
43
60
  markdown?: (conversationId: string) => MarkdownOptions;
44
- onAction?: (action: ConversationAction) => void;
45
- onCopyCode?: (text: string) => void;
61
+ onAction?: (action: ConversationAction, button: HTMLButtonElement) => void;
62
+ onCopyCode?: (text: string, button: HTMLButtonElement) => void;
46
63
  }
47
- export declare function createConversationView(options?: ConversationViewOptions): UIBlock<ConversationViewState>;
64
+ export declare function createConversationView(options?: ConversationViewOptions): ConversationViewBlock;
48
65
  //# sourceMappingURL=conversation-view.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-view.d.ts","sourceRoot":"","sources":["../../src/content/conversation-view.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrE,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACjG;AACD,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AACD,MAAM,WAAW,kBAAkB;IAAG,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AACjH,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,eAAe,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAChD,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAGD,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAqD5G"}
1
+ {"version":3,"file":"conversation-view.d.ts","sourceRoot":"","sources":["../../src/content/conversation-view.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,eAAe,CAAC;AAGrE,kFAAkF;AAClF,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3D,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACjG;AACD,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AACD,MAAM,WAAW,kBAAkB;IAAG,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AACjH,MAAM,WAAW,8BAA8B;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAC;AAClG,MAAM,MAAM,sBAAsB,GAAG;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAC,GAAG;IAAC,MAAM,EAAE,aAAa,CAAA;CAAC,CAAC;AAC9F,MAAM,WAAW,qBAAsB,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IAC3E,4EAA4E;IAC5E,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,CAAC,8BAA8B,CAAC,GAAG,IAAI,CAAC;IACnF,MAAM,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACtC,aAAa,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,sBAAsB,GAAG,IAAI,CAAC;CAC7E;AACD,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,eAAe,CAAC;IACvD,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC3E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAChE;AAGD,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,uBAA4B,GAAG,qBAAqB,CA0FnG"}
@@ -1,9 +1,16 @@
1
1
  import { escape, icon } from '../dom.js';
2
+ import { controlDisabled } from '../core.js';
3
+ import { adoptProductParts } from '../foundations.js';
2
4
  import { renderMarkdown } from './markdown.js';
3
5
  import { reconcileViews, clearViews } from './views.js';
4
6
  export function createConversationView(options = {}) {
5
7
  const container = options.container || document.createElement('div');
6
8
  container.classList.add('pui-conversation-messages');
9
+ const parts = adoptProductParts(container);
10
+ let actionButtons = [];
11
+ let disabledActions = {};
12
+ const syncAvailability = () => { for (const button of actionButtons)
13
+ parts.action(button).update({ disabled: controlDisabled(button, !!disabledActions[button.dataset.messageAction]) }); };
7
14
  let destroyed = false;
8
15
  const click = (event) => {
9
16
  const target = event.target.closest('[data-message-action], [data-copy-code]');
@@ -11,24 +18,35 @@ export function createConversationView(options = {}) {
11
18
  return;
12
19
  if (target.hasAttribute('data-copy-code')) {
13
20
  const text = target.closest('.pui-code-block')?.querySelector('pre > code')?.textContent || '';
14
- options.onCopyCode?.(text);
21
+ if (options.onCopyCode) {
22
+ event.stopPropagation();
23
+ options.onCopyCode(text, target);
24
+ }
15
25
  return;
16
26
  }
17
- options.onAction?.({ action: target.dataset.messageAction, messageId: target.dataset.messageActionId, branchKey: target.dataset.branchKey });
27
+ if (options.onAction) {
28
+ event.stopPropagation();
29
+ options.onAction({ action: target.dataset.messageAction, messageId: target.dataset.messageActionId, branchKey: target.dataset.branchKey }, target);
30
+ }
18
31
  };
19
32
  container.addEventListener('click', click);
20
33
  let activeId = '';
34
+ let groups = new Map();
21
35
  function update(id, messages, working, folds) {
22
36
  if (destroyed)
23
37
  return;
24
38
  if (id !== activeId) {
25
39
  clearViews(container);
40
+ groups.clear();
26
41
  activeId = id;
27
42
  }
28
43
  const views = [];
29
- const messageView = (message, key = message.id, foldKey = '') => ({
30
- key, signature: JSON.stringify(message), html: () => renderMessage(id, message, folds.get(foldKey), foldKey, (text, conversationId) => renderMarkdown(text, options.markdown?.(conversationId))),
44
+ const aliases = new Map();
45
+ const expanded = (key, legacy = '') => folds?.get(key) ?? folds?.get(legacy) ?? false;
46
+ const messageView = (message, key = `message:${message.id}`, foldKey = '', legacyFoldKey = '') => ({
47
+ key, signature: JSON.stringify(message), html: () => renderMessage(id, message, expanded(foldKey, legacyFoldKey), foldKey, (text, conversationId) => renderMarkdown(text, options.markdown?.(conversationId))),
31
48
  });
49
+ const nextGroups = new Map();
32
50
  let groupIndex = 0;
33
51
  for (let index = 0; index < messages.length;) {
34
52
  if (messages[index].role !== 'tool') {
@@ -38,24 +56,62 @@ export function createConversationView(options = {}) {
38
56
  const start = index;
39
57
  while (index < messages.length && messages[index].role === 'tool')
40
58
  index++;
41
- const tools = messages.slice(start, index), key = `group-${groupIndex++}`;
59
+ const tools = messages.slice(start, index), members = new Set(tools.map(tool => tool.id));
60
+ // A group can gain results or have earlier history inserted. Reuse its
61
+ // identity through an existing member instead of its current list index.
62
+ const retained = [...groups].find(([key, previous]) => !nextGroups.has(key) && [...members].some(id => previous.has(id)));
63
+ const baseKey = `group-${tools[0].id}`;
64
+ let key = retained?.[0] ?? baseKey;
65
+ // A retained group's first member may no longer be present. A newly
66
+ // inserted group using that member must not take its still-owned key.
67
+ if (!retained)
68
+ for (let suffix = 2; groups.has(key) || nextGroups.has(key); suffix++)
69
+ key = `${baseKey}-${suffix}`;
70
+ const legacyKey = `group-${groupIndex++}`;
71
+ nextGroups.set(key, members);
72
+ aliases.set(key, legacyKey);
42
73
  const running = tools.filter((tool) => tool.status === 'running').length;
43
74
  const failed = tools.filter((tool) => tool.status === 'error').length;
44
75
  const label = toolGroupLabel(tools);
45
- views.push({ key, signature: JSON.stringify([label, tools.length, failed, running]),
46
- html: () => `<details class="pui-tool-group" data-tool-fold="${key}" ${folds.get(key) ? 'open' : ''}><summary>${icon('terminal')}<span class="pui-tool-group-label">${label}</span><span class="pui-tool-group-count">${tools.length} ${tools.length === 1 ? 'call' : 'calls'}</span>${failed ? `<span class="pui-tool-group-failed">${failed} failed</span>` : ''}${running ? `<span class="pui-tool-group-running">${running} running</span>` : ''}${icon('down')}</summary><div class="pui-tool-group-content" data-view-children></div></details>`,
47
- children: tools.map((tool) => { const toolKey = `${key}-tool-${tool.toolCallId || tool.id}`; return messageView(tool, toolKey, toolKey); }),
76
+ views.push({ key: `tools:${key}`, signature: JSON.stringify([label, tools.length, failed, running]),
77
+ html: () => `<details class="pui-tool-group" data-tool-fold="${escape(key)}" ${expanded(key, legacyKey) ? 'open' : ''}><summary>${icon('terminal')}<span class="pui-tool-group-label">${label}</span><span class="pui-tool-group-count">${tools.length} ${tools.length === 1 ? 'call' : 'calls'}</span>${failed ? `<span class="pui-tool-group-failed">${failed} failed</span>` : ''}${running ? `<span class="pui-tool-group-running">${running} running</span>` : ''}${icon('down')}</summary><div class="pui-tool-group-content" data-view-children></div></details>`,
78
+ children: tools.map((tool) => { const toolKey = `${key}-tool-${tool.toolCallId || tool.id}`, legacyToolKey = `${legacyKey}-tool-${tool.toolCallId || tool.id}`; aliases.set(toolKey, legacyToolKey); return messageView(tool, `tool:${tool.id}`, toolKey, legacyToolKey); }),
48
79
  });
49
80
  }
50
81
  if (working)
51
82
  views.push({ key: 'working', signature: 'working', html: () => '<div class="pui-agent-working" role="status"><span class="pui-working-dots" aria-hidden="true"><i></i><i></i><i></i></span><span>Working…</span></div>' });
52
83
  reconcileViews(container, views);
84
+ groups = nextGroups;
85
+ if (folds)
86
+ for (const details of container.querySelectorAll('[data-tool-fold]')) {
87
+ const key = details.dataset.toolFold, legacy = aliases.get(key);
88
+ const value = folds.get(key) ?? (legacy ? folds.get(legacy) : undefined);
89
+ if (value !== undefined && details.open !== value)
90
+ details.open = value;
91
+ }
92
+ actionButtons = [...container.querySelectorAll('[data-message-action]')];
93
+ parts.update();
94
+ syncAvailability();
53
95
  }
54
96
  return {
55
97
  element: container,
56
98
  update(state) {
57
- const folds = state.folds || new Map(Array.from(container.querySelectorAll('[data-tool-fold]'), (element) => [element.dataset.toolFold, element.open]));
58
- update(state.id, state.messages, state.working, folds);
99
+ update(state.id, state.messages, state.working, state.folds);
100
+ },
101
+ updateActionAvailability(disabled) {
102
+ if (destroyed || (disabled.copy === disabledActions.copy && disabled.edit === disabledActions.edit && disabled.branch === disabledActions.branch))
103
+ return;
104
+ disabledActions = { ...disabled };
105
+ syncAvailability();
106
+ },
107
+ images: () => destroyed ? [] : [...container.querySelectorAll('img[data-image-key]')],
108
+ setImageState(image, state) {
109
+ if (destroyed || !container.contains(image))
110
+ return;
111
+ image.classList.toggle('pui-image-unavailable', state.status === 'unavailable');
112
+ image.alt = state.status === 'unavailable' ? 'Image unavailable' : `Image ${Number(image.dataset.imageIndex) + 1}`;
113
+ if (state.status === 'ready')
114
+ image.src = state.src;
59
115
  },
60
116
  destroy() {
61
117
  if (destroyed)
@@ -63,6 +119,9 @@ export function createConversationView(options = {}) {
63
119
  destroyed = true;
64
120
  container.removeEventListener('click', click);
65
121
  clearViews(container);
122
+ parts.destroy();
123
+ actionButtons = [];
124
+ groups.clear();
66
125
  },
67
126
  };
68
127
  }
@@ -82,10 +141,10 @@ function renderMessage(conversationId, message, expanded = false, foldKey = '',
82
141
  const images = (message.images || []).map((image) => `<img class="pui-history-image" data-image-key="${escape(`${conversationId}/${message.id}/${image.index}`)}" data-message-id="${escape(message.id)}" data-image-index="${image.index}" alt="Image ${image.index + 1}" role="button" tabindex="0" title="Click to enlarge" loading="lazy" />`).join('');
83
142
  const gallery = images ? `<div class="pui-message-images">${images}</div>` : '';
84
143
  const copyLabel = typeof message.actions?.copy === 'string' ? message.actions.copy : 'Copy message';
85
- const copy = message.actions?.copy ? `<button type="button" class="pui-quiet-icon pui-message-action" data-message-action="copy" data-message-action-id="${escape(message.id)}" title="${escape(copyLabel)}" aria-label="${escape(copyLabel)}">${icon('copy')}</button>` : '';
86
- const edit = message.actions?.edit ? `<button type="button" class="pui-quiet-icon pui-message-action" data-message-action="edit" data-message-action-id="${escape(message.id)}" title="Edit message" aria-label="Edit message">${icon('edit')}</button>` : '';
144
+ const copy = message.actions?.copy ? `<button type="button" class="pui-quiet-icon pui-message-action" data-message-action="copy" data-message-action-id="${escape(message.id)}" data-copy-message="${escape(message.id)}" title="${escape(copyLabel)}" aria-label="${escape(copyLabel)}">${icon('copy')}</button>` : '';
145
+ const edit = message.actions?.edit ? `<button type="button" class="pui-quiet-icon pui-message-action" data-message-action="edit" data-message-action-id="${escape(message.id)}" data-edit-message="${escape(message.id)}" title="Edit message" aria-label="Edit message">${icon('edit')}</button>` : '';
87
146
  const branchAction = message.actions?.branch;
88
- const branch = branchAction ? `<button type="button" class="pui-quiet-icon pui-message-action" data-message-action="branch" data-message-action-id="${escape(message.id)}" data-branch-key="${escape(branchAction.key)}" title="${escape(branchAction.label || 'Branch from this message')}" aria-label="${escape(branchAction.label || 'Branch from this message')}">${icon('branch')}</button>` : '';
147
+ const branch = branchAction ? `<button type="button" class="pui-quiet-icon pui-message-action" data-message-action="branch" data-message-action-id="${escape(message.id)}" data-branch-key="${escape(branchAction.key)}" data-node="${escape(branchAction.key)}" title="${escape(branchAction.label || 'Branch from this message')}" aria-label="${escape(branchAction.label || 'Branch from this message')}">${icon('branch')}</button>` : '';
89
148
  const actions = copy || edit || branch ? `<div class="pui-message-actions">${copy}${edit}${branch}</div>` : '';
90
149
  const date = new Date(message.timestamp || NaN);
91
150
  const time = Number.isFinite(date.getTime()) ? `<time class="pui-message-time" datetime="${date.toISOString()}" title="${escape(date.toLocaleString())}">${escape(date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }))}</time>` : '';