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.
- package/AI.md +20 -1
- package/API.md +834 -272
- package/README.md +3 -1
- package/dist/composer.d.ts.map +1 -1
- package/dist/composer.js +48 -29
- package/dist/composer.js.map +1 -1
- package/dist/content/conversation-reader.d.ts.map +1 -1
- package/dist/content/conversation-reader.js +8 -1
- package/dist/content/conversation-reader.js.map +1 -1
- package/dist/content/conversation-view.d.ts +21 -4
- package/dist/content/conversation-view.d.ts.map +1 -1
- package/dist/content/conversation-view.js +72 -13
- package/dist/content/conversation-view.js.map +1 -1
- package/dist/content/image-viewer.d.ts +4 -2
- package/dist/content/image-viewer.d.ts.map +1 -1
- package/dist/content/image-viewer.js +36 -15
- package/dist/content/image-viewer.js.map +1 -1
- package/dist/content/paged-preview.d.ts +1 -1
- package/dist/content/paged-preview.d.ts.map +1 -1
- package/dist/content/paged-preview.js +10 -5
- package/dist/content/paged-preview.js.map +1 -1
- package/dist/content/views.d.ts +3 -0
- package/dist/content/views.d.ts.map +1 -1
- package/dist/content/views.js +110 -41
- package/dist/content/views.js.map +1 -1
- package/dist/content/welcome.d.ts.map +1 -1
- package/dist/content/welcome.js +19 -7
- package/dist/content/welcome.js.map +1 -1
- package/dist/dom.js +1 -1
- package/dist/dom.js.map +1 -1
- package/dist/files/panels.d.ts +8 -1
- package/dist/files/panels.d.ts.map +1 -1
- package/dist/files/panels.js +235 -120
- package/dist/files/panels.js.map +1 -1
- package/dist/files/systems.d.ts.map +1 -1
- package/dist/files/systems.js +81 -91
- package/dist/files/systems.js.map +1 -1
- package/dist/forms/actions.d.ts +28 -7
- package/dist/forms/actions.d.ts.map +1 -1
- package/dist/forms/actions.js +59 -11
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/custom-connection.d.ts +2 -1
- package/dist/forms/custom-connection.d.ts.map +1 -1
- package/dist/forms/custom-connection.js +36 -28
- package/dist/forms/custom-connection.js.map +1 -1
- package/dist/forms/dialog-frame.d.ts +2 -0
- package/dist/forms/dialog-frame.d.ts.map +1 -1
- package/dist/forms/dialog-frame.js +46 -26
- package/dist/forms/dialog-frame.js.map +1 -1
- package/dist/forms/history.d.ts +1 -1
- package/dist/forms/history.d.ts.map +1 -1
- package/dist/forms/history.js +78 -15
- package/dist/forms/history.js.map +1 -1
- package/dist/forms/identity.d.ts +12 -0
- package/dist/forms/identity.d.ts.map +1 -1
- package/dist/forms/identity.js +16 -0
- package/dist/forms/identity.js.map +1 -1
- package/dist/forms/model-editor.d.ts +8 -3
- package/dist/forms/model-editor.d.ts.map +1 -1
- package/dist/forms/model-editor.js +73 -62
- package/dist/forms/model-editor.js.map +1 -1
- package/dist/forms/panels.d.ts +103 -7
- package/dist/forms/panels.d.ts.map +1 -1
- package/dist/forms/panels.js +183 -77
- package/dist/forms/panels.js.map +1 -1
- package/dist/forms/provider-login.d.ts.map +1 -1
- package/dist/forms/provider-login.js +25 -39
- package/dist/forms/provider-login.js.map +1 -1
- package/dist/forms/resource-editor.d.ts +9 -5
- package/dist/forms/resource-editor.d.ts.map +1 -1
- package/dist/forms/resource-editor.js +20 -9
- package/dist/forms/resource-editor.js.map +1 -1
- package/dist/foundations.d.ts +95 -0
- package/dist/foundations.d.ts.map +1 -0
- package/dist/foundations.js +299 -0
- package/dist/foundations.js.map +1 -0
- package/dist/navigation/context-menu.d.ts +8 -1
- package/dist/navigation/context-menu.d.ts.map +1 -1
- package/dist/navigation/context-menu.js +67 -50
- package/dist/navigation/context-menu.js.map +1 -1
- package/dist/navigation/navigation.d.ts +55 -7
- package/dist/navigation/navigation.d.ts.map +1 -1
- package/dist/navigation/navigation.js +167 -18
- package/dist/navigation/navigation.js.map +1 -1
- package/dist/operation.d.ts +12 -0
- package/dist/operation.d.ts.map +1 -0
- package/dist/operation.js +49 -0
- package/dist/operation.js.map +1 -0
- package/dist/primitives.d.ts +11 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +164 -301
- package/dist/primitives.js.map +1 -1
- package/dist/reconcile.d.ts +11 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +36 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/select.d.ts +14 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +304 -0
- package/dist/select.js.map +1 -0
- package/dist/styles.css +452 -382
- package/dist/surface.d.ts +60 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +344 -0
- package/dist/surface.js.map +1 -0
- package/dist/tasks/approval.d.ts.map +1 -1
- package/dist/tasks/approval.js +86 -45
- package/dist/tasks/approval.js.map +1 -1
- package/dist/tasks/permission-element.d.ts +3 -0
- package/dist/tasks/permission-element.d.ts.map +1 -0
- package/dist/tasks/permission-element.js +10 -0
- package/dist/tasks/permission-element.js.map +1 -0
- package/dist/tasks/permission.d.ts +5 -3
- package/dist/tasks/permission.d.ts.map +1 -1
- package/dist/tasks/permission.js +47 -22
- package/dist/tasks/permission.js.map +1 -1
- package/dist/tasks/runtime.d.ts.map +1 -1
- package/dist/tasks/runtime.js +30 -13
- package/dist/tasks/runtime.js.map +1 -1
- package/dist/workspace.d.ts +58 -4
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +202 -44
- package/dist/workspace.js.map +1 -1
- 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.
|
|
23
|
+
npm install --save-exact pith-ui@0.1.4
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
```ts
|
package/dist/composer.d.ts.map
CHANGED
|
@@ -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;
|
|
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 {
|
|
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
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
image
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
package/dist/composer.js.map
CHANGED
|
@@ -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;
|
|
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) {
|
|
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
|
|
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):
|
|
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,
|
|
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
|
|
21
|
+
if (options.onCopyCode) {
|
|
22
|
+
event.stopPropagation();
|
|
23
|
+
options.onCopyCode(text, target);
|
|
24
|
+
}
|
|
15
25
|
return;
|
|
16
26
|
}
|
|
17
|
-
options.onAction
|
|
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
|
|
30
|
-
|
|
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),
|
|
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
|
|
46
|
-
html: () => `<details class="pui-tool-group" data-tool-fold="${key}" ${
|
|
47
|
-
children: tools.map((tool) => { const toolKey = `${key}-tool-${tool.toolCallId || tool.id}`; return messageView(tool, 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
|
-
|
|
58
|
-
|
|
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>` : '';
|