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
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-view.js","sourceRoot":"","sources":["../../src/content/conversation-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { escape, icon } from '../dom.js';\nimport type { UIBlock } from '../core.js';\nimport { renderMarkdown, type MarkdownOptions } from './markdown.js';\nimport { reconcileViews, clearViews, type View } from './views.js';\n\n/** UI data only. Hosts decide copy/edit/branch eligibility and resolve images. */\nexport interface ConversationMessage {\n id: string;\n role: string;\n text: string;\n timestamp?: number;\n toolName?: string;\n toolCallId?: string;\n status?: string;\n images?: Array<{ index: number; mimeType?: string }>;\n command?: { text: string; cwd: string; exitCode?: number };\n actions?: { copy?: boolean | string; edit?: boolean; branch?: { key: string; label?: string } };\n}\nexport interface ConversationViewState {\n id: string;\n messages: readonly ConversationMessage[];\n working: boolean;\n folds?: ReadonlyMap<string, boolean>;\n}\nexport interface ConversationAction { action: 'copy' | 'edit' | 'branch'; messageId: string; branchKey?: string }\nexport interface ConversationViewOptions {\n container?: HTMLElement;\n markdown?: (conversationId: string) => MarkdownOptions;\n onAction?: (action: ConversationAction) => void;\n onCopyCode?: (text: string) => void;\n}\n\n\nexport function createConversationView(options: ConversationViewOptions = {}): UIBlock<ConversationViewState> {\n const container = options.container || document.createElement('div');\n container.classList.add('pui-conversation-messages');\n let destroyed = false;\n const click = (event: Event) => {\n const target = (event.target as Element).closest<HTMLElement>('[data-message-action], [data-copy-code]');\n if (!target || !container.contains(target) || (target as HTMLButtonElement).disabled) return;\n if (target.hasAttribute('data-copy-code')) {\n const text = target.closest('.pui-code-block')?.querySelector('pre > code')?.textContent || '';\n options.onCopyCode?.(text);\n return;\n }\n options.onAction?.({ action: target.dataset.messageAction as ConversationAction['action'], messageId: target.dataset.messageActionId!, branchKey: target.dataset.branchKey });\n };\n container.addEventListener('click', click);\n let activeId = '';\n function update(id: string, messages: readonly ConversationMessage[], working: boolean, folds: ReadonlyMap<string, boolean>): void {\n if (destroyed) return;\n if (id !== activeId) { clearViews(container); activeId = id; }\n const views: View[] = [];\n const messageView = (message: ConversationMessage, key = message.id, foldKey = ''): View => ({\n key, signature: JSON.stringify(message), html: () => renderMessage(id, message, folds.get(foldKey), foldKey, (text, conversationId) => renderMarkdown(text, options.markdown?.(conversationId))),\n });\n let groupIndex = 0;\n for (let index = 0; index < messages.length;) {\n if (messages[index].role !== 'tool') { views.push(messageView(messages[index++])); continue; }\n const start = index;\n while (index < messages.length && messages[index].role === 'tool') index++;\n const tools = messages.slice(start, index), key = `group-${groupIndex++}`;\n const running = tools.filter((tool) => tool.status === 'running').length;\n const failed = tools.filter((tool) => tool.status === 'error').length;\n const label = toolGroupLabel(tools);\n views.push({ key, signature: JSON.stringify([label, tools.length, failed, running]),\n 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>`,\n children: tools.map((tool) => { const toolKey = `${key}-tool-${tool.toolCallId || tool.id}`; return messageView(tool, toolKey, toolKey); }),\n });\n }\n if (working) 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>' });\n reconcileViews(container, views);\n }\n return {\n element: container,\n update(state): void {\n const folds = state.folds || new Map(Array.from(container.querySelectorAll<HTMLDetailsElement>('[data-tool-fold]'), (element) => [element.dataset.toolFold!, element.open] as const));\n update(state.id, state.messages, state.working, folds);\n },\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n container.removeEventListener('click', click);\n clearViews(container);\n },\n };\n}\n\nfunction toolGroupLabel(tools: readonly ConversationMessage[]): string {\n if (tools.length === 1) return ({ list_files: 'Listed files', read_file: 'Read a file', run_command: 'Ran a command', write_file: 'Wrote a file', edit_file: 'Edited a file', tool_search: 'Searched tools', codemode: 'Ran code' } as Record<string, string>)[tools[0].toolName || ''] || 'Used a tool';\n const hasCommands = tools.some((tool) => tool.toolName === 'run_command');\n const exploredFiles = tools.every((tool) => ['list_files', 'read_file', 'grep', 'find', 'ls', 'run_command'].includes(tool.toolName || ''));\n if (tools.every((tool) => tool.toolName === 'run_command')) return 'Ran commands';\n if (exploredFiles) return hasCommands ? 'Ran commands and explored files' : 'Explored files';\n return 'Used tools';\n}\n\nfunction renderMessage(conversationId: string, message: ConversationMessage, expanded = false, foldKey = '', markdown: (value: string, conversationId: string) => string = (text) => renderMarkdown(text)): string {\n const text = message.text ?? '';\n 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('');\n const gallery = images ? `<div class=\"pui-message-images\">${images}</div>` : '';\n const copyLabel = typeof message.actions?.copy === 'string' ? message.actions.copy : 'Copy message';\n 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>` : '';\n 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>` : '';\n const branchAction = message.actions?.branch;\n 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>` : '';\n const actions = copy || edit || branch ? `<div class=\"pui-message-actions\">${copy}${edit}${branch}</div>` : '';\n const date = new Date(message.timestamp || NaN);\n 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>` : '';\n const footer = time || actions ? `<div class=\"pui-message-footer\">${time}${actions}</div>` : '';\n if (message.role === 'tool') {\n const command = message.command;\n const details = command ? `<div class=\"pui-command-details\"><span class=\"pui-tool-field-label\">Command</span><pre class=\"pui-command-text\">${escape(command.text)}</pre><div class=\"pui-command-directory\"><span class=\"pui-tool-field-label\">Directory</span><code>${escape(command.cwd)}</code></div></div>` : '';\n const status = command ? ({ running: 'Running', done: 'Completed', error: 'Failed' } as Record<string, string>)[message.status || ''] || message.status || 'Result' : message.status || 'Result';\n const exit = command?.exitCode !== undefined ? ` · Exit ${command.exitCode}` : '';\n const output = command ? `<div class=\"pui-command-output\"><span class=\"pui-tool-field-label\">Output</span><pre>${escape(text)}</pre></div>` : `<pre>${escape(text)}</pre>`;\n return `<details class=\"pui-tool-message\" data-reading-id=\"${escape(message.id)}\" data-tool-fold=\"${escape(foldKey)}\" ${expanded ? 'open' : ''}><summary>${icon('terminal')}<span>${command ? 'Command' : escape(message.toolName || 'Tool result')}</span><span class=\"pui-tool-status${message.status === 'error' ? ' pui-tool-status-error' : ''}\">${escape(status + exit)}</span>${icon('down')}</summary>${details}${output}${gallery}</details>`;\n }\n if (message.role === 'user') return `<article class=\"pui-message pui-user-message\" data-reading-id=\"${escape(message.id)}\" aria-label=\"Your message\"><div class=\"pui-message-content\">${gallery}${text ? `<div class=\"pui-user-text\">${escape(text)}</div>` : ''}${footer}</div></article>`;\n if (message.role === 'system') return `<div class=\"pui-system-message\" data-reading-id=\"${escape(message.id)}\">${escape(text)}</div>`;\n return `<article class=\"pui-message pui-assistant-message\" data-reading-id=\"${escape(message.id)}\" aria-label=\"Agent response\"><div class=\"pui-message-content\"><div class=\"pui-markdown\">${markdown(text, conversationId)}</div>${message.status === 'error' ? '<span class=\"pui-message-error-label\">Response interrupted</span>' : ''}${footer}</div></article>`;\n}\n"]}
1
+ {"version":3,"file":"conversation-view.js","sourceRoot":"","sources":["../../src/content/conversation-view.ts"],"names":[],"mappings":"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","sourcesContent":["import { escape, icon } from '../dom.js';\nimport { controlDisabled, type UIBlock } from '../core.js';\nimport { adoptProductParts } from '../foundations.js';\nimport { renderMarkdown, type MarkdownOptions } from './markdown.js';\nimport { reconcileViews, clearViews, type View } from './views.js';\n\n/** UI data only. Hosts decide copy/edit/branch eligibility and resolve images. */\nexport interface ConversationMessage {\n id: string;\n role: string;\n text: string;\n timestamp?: number;\n toolName?: string;\n toolCallId?: string;\n status?: string;\n images?: Array<{ index: number; mimeType?: string }>;\n command?: { text: string; cwd: string; exitCode?: number };\n actions?: { copy?: boolean | string; edit?: boolean; branch?: { key: string; label?: string } };\n}\nexport interface ConversationViewState {\n id: string;\n messages: readonly ConversationMessage[];\n working: boolean;\n folds?: ReadonlyMap<string, boolean>;\n}\nexport interface ConversationAction { action: 'copy' | 'edit' | 'branch'; messageId: string; branchKey?: string }\nexport interface ConversationActionAvailability {copy?: boolean; edit?: boolean; branch?: boolean}\nexport type ConversationImageState = {status: 'ready'; src: string} | {status: 'unavailable'};\nexport interface ConversationViewBlock extends UIBlock<ConversationViewState> {\n /** Disabled state, separate from whether an action exists for a message. */\n updateActionAvailability(disabled: Readonly<ConversationActionAvailability>): void;\n images(): readonly HTMLImageElement[];\n setImageState(image: HTMLImageElement, state: ConversationImageState): void;\n}\nexport interface ConversationViewOptions {\n container?: HTMLElement;\n markdown?: (conversationId: string) => MarkdownOptions;\n onAction?: (action: ConversationAction, button: HTMLButtonElement) => void;\n onCopyCode?: (text: string, button: HTMLButtonElement) => void;\n}\n\n\nexport function createConversationView(options: ConversationViewOptions = {}): ConversationViewBlock {\n const container = options.container || document.createElement('div');\n container.classList.add('pui-conversation-messages');\n const parts = adoptProductParts(container);\n let actionButtons: HTMLButtonElement[] = [];\n let disabledActions: Readonly<ConversationActionAvailability> = {};\n const syncAvailability = () => {for (const button of actionButtons) parts.action(button).update({disabled: controlDisabled(button, !!disabledActions[button.dataset.messageAction as ConversationAction['action']])});};\n let destroyed = false;\n const click = (event: Event) => {\n const target = (event.target as Element).closest<HTMLButtonElement>('[data-message-action], [data-copy-code]');\n if (!target || !container.contains(target) || target.disabled) return;\n if (target.hasAttribute('data-copy-code')) {\n const text = target.closest('.pui-code-block')?.querySelector('pre > code')?.textContent || '';\n if (options.onCopyCode) {event.stopPropagation(); options.onCopyCode(text, target);}\n return;\n }\n if (options.onAction) {event.stopPropagation(); options.onAction({ action: target.dataset.messageAction as ConversationAction['action'], messageId: target.dataset.messageActionId!, branchKey: target.dataset.branchKey }, target);}\n };\n container.addEventListener('click', click);\n let activeId = '';\n let groups = new Map<string, Set<string>>();\n function update(id: string, messages: readonly ConversationMessage[], working: boolean, folds?: ReadonlyMap<string, boolean>): void {\n if (destroyed) return;\n if (id !== activeId) { clearViews(container); groups.clear(); activeId = id; }\n const views: View[] = [];\n const aliases = new Map<string, string>();\n const expanded = (key: string, legacy = '') => folds?.get(key) ?? folds?.get(legacy) ?? false;\n const messageView = (message: ConversationMessage, key = `message:${message.id}`, foldKey = '', legacyFoldKey = ''): View => ({\n key, signature: JSON.stringify(message), html: () => renderMessage(id, message, expanded(foldKey, legacyFoldKey), foldKey, (text, conversationId) => renderMarkdown(text, options.markdown?.(conversationId))),\n });\n const nextGroups = new Map<string, Set<string>>();\n let groupIndex = 0;\n for (let index = 0; index < messages.length;) {\n if (messages[index].role !== 'tool') { views.push(messageView(messages[index++])); continue; }\n const start = index;\n while (index < messages.length && messages[index].role === 'tool') index++;\n const tools = messages.slice(start, index), members = new Set(tools.map(tool => tool.id));\n // A group can gain results or have earlier history inserted. Reuse its\n // identity through an existing member instead of its current list index.\n const retained = [...groups].find(([key, previous]) => !nextGroups.has(key) && [...members].some(id => previous.has(id)));\n const baseKey = `group-${tools[0].id}`;\n let key = retained?.[0] ?? baseKey;\n // A retained group's first member may no longer be present. A newly\n // inserted group using that member must not take its still-owned key.\n if (!retained) for (let suffix = 2; groups.has(key) || nextGroups.has(key); suffix++) key = `${baseKey}-${suffix}`;\n const legacyKey = `group-${groupIndex++}`;\n nextGroups.set(key, members); aliases.set(key, legacyKey);\n const running = tools.filter((tool) => tool.status === 'running').length;\n const failed = tools.filter((tool) => tool.status === 'error').length;\n const label = toolGroupLabel(tools);\n views.push({ key:`tools:${key}`, signature: JSON.stringify([label, tools.length, failed, running]),\n 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>`,\n 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); }),\n });\n }\n if (working) 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>' });\n reconcileViews(container, views);\n groups = nextGroups;\n if (folds) for (const details of container.querySelectorAll<HTMLDetailsElement>('[data-tool-fold]')) {\n const key = details.dataset.toolFold!, legacy = aliases.get(key);\n const value = folds.get(key) ?? (legacy ? folds.get(legacy) : undefined);\n if (value !== undefined && details.open !== value) details.open = value;\n }\n actionButtons = [...container.querySelectorAll<HTMLButtonElement>('[data-message-action]')];\n parts.update(); syncAvailability();\n }\n return {\n element: container,\n update(state): void {\n update(state.id, state.messages, state.working, state.folds);\n },\n updateActionAvailability(disabled): void {\n if (destroyed || (disabled.copy === disabledActions.copy && disabled.edit === disabledActions.edit && disabled.branch === disabledActions.branch)) return;\n disabledActions = {...disabled}; syncAvailability();\n },\n images: () => destroyed ? [] : [...container.querySelectorAll<HTMLImageElement>('img[data-image-key]')],\n setImageState(image, state): void {\n if (destroyed || !container.contains(image)) return;\n image.classList.toggle('pui-image-unavailable', state.status === 'unavailable');\n image.alt = state.status === 'unavailable' ? 'Image unavailable' : `Image ${Number(image.dataset.imageIndex) + 1}`;\n if (state.status === 'ready') image.src = state.src;\n },\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n container.removeEventListener('click', click);\n clearViews(container);\n parts.destroy(); actionButtons = []; groups.clear();\n },\n };\n}\n\nfunction toolGroupLabel(tools: readonly ConversationMessage[]): string {\n if (tools.length === 1) return ({ list_files: 'Listed files', read_file: 'Read a file', run_command: 'Ran a command', write_file: 'Wrote a file', edit_file: 'Edited a file', tool_search: 'Searched tools', codemode: 'Ran code' } as Record<string, string>)[tools[0].toolName || ''] || 'Used a tool';\n const hasCommands = tools.some((tool) => tool.toolName === 'run_command');\n const exploredFiles = tools.every((tool) => ['list_files', 'read_file', 'grep', 'find', 'ls', 'run_command'].includes(tool.toolName || ''));\n if (tools.every((tool) => tool.toolName === 'run_command')) return 'Ran commands';\n if (exploredFiles) return hasCommands ? 'Ran commands and explored files' : 'Explored files';\n return 'Used tools';\n}\n\nfunction renderMessage(conversationId: string, message: ConversationMessage, expanded = false, foldKey = '', markdown: (value: string, conversationId: string) => string = (text) => renderMarkdown(text)): string {\n const text = message.text ?? '';\n 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('');\n const gallery = images ? `<div class=\"pui-message-images\">${images}</div>` : '';\n const copyLabel = typeof message.actions?.copy === 'string' ? message.actions.copy : 'Copy message';\n 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>` : '';\n 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>` : '';\n const branchAction = message.actions?.branch;\n 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>` : '';\n const actions = copy || edit || branch ? `<div class=\"pui-message-actions\">${copy}${edit}${branch}</div>` : '';\n const date = new Date(message.timestamp || NaN);\n 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>` : '';\n const footer = time || actions ? `<div class=\"pui-message-footer\">${time}${actions}</div>` : '';\n if (message.role === 'tool') {\n const command = message.command;\n const details = command ? `<div class=\"pui-command-details\"><span class=\"pui-tool-field-label\">Command</span><pre class=\"pui-command-text\">${escape(command.text)}</pre><div class=\"pui-command-directory\"><span class=\"pui-tool-field-label\">Directory</span><code>${escape(command.cwd)}</code></div></div>` : '';\n const status = command ? ({ running: 'Running', done: 'Completed', error: 'Failed' } as Record<string, string>)[message.status || ''] || message.status || 'Result' : message.status || 'Result';\n const exit = command?.exitCode !== undefined ? ` · Exit ${command.exitCode}` : '';\n const output = command ? `<div class=\"pui-command-output\"><span class=\"pui-tool-field-label\">Output</span><pre>${escape(text)}</pre></div>` : `<pre>${escape(text)}</pre>`;\n return `<details class=\"pui-tool-message\" data-reading-id=\"${escape(message.id)}\" data-tool-fold=\"${escape(foldKey)}\" ${expanded ? 'open' : ''}><summary>${icon('terminal')}<span>${command ? 'Command' : escape(message.toolName || 'Tool result')}</span><span class=\"pui-tool-status${message.status === 'error' ? ' pui-tool-status-error' : ''}\">${escape(status + exit)}</span>${icon('down')}</summary>${details}${output}${gallery}</details>`;\n }\n if (message.role === 'user') return `<article class=\"pui-message pui-user-message\" data-reading-id=\"${escape(message.id)}\" aria-label=\"Your message\"><div class=\"pui-message-content\">${gallery}${text ? `<div class=\"pui-user-text\">${escape(text)}</div>` : ''}${footer}</div></article>`;\n if (message.role === 'system') return `<div class=\"pui-system-message\" data-reading-id=\"${escape(message.id)}\">${escape(text)}</div>`;\n return `<article class=\"pui-message pui-assistant-message\" data-reading-id=\"${escape(message.id)}\" aria-label=\"Agent response\"><div class=\"pui-message-content\"><div class=\"pui-markdown\">${markdown(text, conversationId)}</div>${message.status === 'error' ? '<span class=\"pui-message-error-label\">Response interrupted</span>' : ''}${footer}</div></article>`;\n}\n"]}
@@ -14,10 +14,12 @@ export declare function createImageViewer(options?: ImageViewerOptions): {
14
14
  close: () => void;
15
15
  destroy(): void;
16
16
  };
17
- export declare function installImageViewer(dialog: HTMLDialogElement): {
17
+ export declare function installImageViewer(dialog: HTMLDialogElement, options?: {
18
+ triggerRoot?: HTMLElement;
19
+ }): {
20
+ destroy(): void;
18
21
  element: HTMLDialogElement;
19
22
  open(target: HTMLImageElement | ImageViewerInput): void;
20
23
  close: () => void;
21
- destroy(): void;
22
24
  };
23
25
  //# sourceMappingURL=image-viewer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../../src/content/image-viewer.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IAAG,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AACtF,MAAM,WAAW,gBAAgB;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,WAAW,CAAA;CAAE;AAG1F,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;;iBA8CjD,gBAAgB,GAAG,gBAAgB,GAAG,IAAI;iBAHvC,IAAI;eAqBT,IAAI;EAalB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB;;iBAjC3C,gBAAgB,GAAG,gBAAgB,GAAG,IAAI;iBAHvC,IAAI;eAqBT,IAAI;EAiBlB"}
1
+ {"version":3,"file":"image-viewer.d.ts","sourceRoot":"","sources":["../../src/content/image-viewer.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IAAG,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AACtF,MAAM,WAAW,gBAAgB;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,WAAW,CAAA;CAAE;AAG1F,kFAAkF;AAClF,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,kBAAuB;;iBAgDjD,gBAAgB,GAAG,gBAAgB,GAAG,IAAI;iBAHvC,IAAI;eAqBT,IAAI;EAgBlB;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,WAAW,CAAA;CAAO;;;iBApCxF,gBAAgB,GAAG,gBAAgB,GAAG,IAAI;iBAHvC,IAAI;EAoDvB"}
@@ -1,4 +1,6 @@
1
1
  import { escape, icon } from '../dom.js';
2
+ import { installDialogLifecycle } from '../surface.js';
3
+ import { adoptFeedback } from '../foundations.js';
2
4
  let sequence = 0;
3
5
  /** A modal above either a generated-file preview or a conversation attachment. */
4
6
  export function createImageViewer(options = {}) {
@@ -13,6 +15,7 @@ export function createImageViewer(options = {}) {
13
15
  const viewport = dialog.querySelector('.pui-image-viewer-viewport');
14
16
  const title = dialog.querySelector('h2');
15
17
  const status = dialog.querySelector('[data-image-scale]');
18
+ const feedback = adoptFeedback(status, { presentation: 'inline', autoHide: false });
16
19
  const listeners = new AbortController();
17
20
  const signal = listeners.signal;
18
21
  let destroyed = false;
@@ -24,7 +27,7 @@ export function createImageViewer(options = {}) {
24
27
  scale = Math.min(1, viewport.clientWidth / image.naturalWidth, viewport.clientHeight / image.naturalHeight);
25
28
  image.style.width = `${image.naturalWidth * scale}px`;
26
29
  image.style.height = `${image.naturalHeight * scale}px`;
27
- status.textContent = `${Math.round(scale * 100)}% · ${image.naturalWidth} × ${image.naturalHeight}`;
30
+ feedback.update({ message: `${Math.round(scale * 100)}% · ${image.naturalWidth} × ${image.naturalHeight}`, tone: 'info' });
28
31
  }
29
32
  function resize(factor) { fitted = false; const next = scale * factor; if (Number.isFinite(next) && next > 0)
30
33
  scale = next; update(); }
@@ -37,15 +40,17 @@ export function createImageViewer(options = {}) {
37
40
  const observer = new ResizeObserver(() => { if (dialog.open && fitted)
38
41
  update(); });
39
42
  observer.observe(viewport);
40
- dialog.addEventListener('close', () => {
41
- image.removeAttribute('src');
42
- image.removeAttribute('style');
43
- source?.focus({ preventScroll: true });
44
- source = null;
45
- }, { signal });
46
- document.addEventListener('keydown', (event) => {
43
+ const lifecycle = installDialogLifecycle(dialog, { returnFocus: () => source, onClose: () => {
44
+ image.removeAttribute('src');
45
+ image.removeAttribute('style');
46
+ source = null;
47
+ } });
48
+ dialog.addEventListener('keydown', (event) => {
47
49
  if (!dialog.open || event.isComposing || event.metaKey || event.ctrlKey || event.altKey)
48
50
  return;
51
+ const target = event.target instanceof Element ? event.target : null;
52
+ if (target?.closest('dialog') !== dialog || target.closest('input, textarea, [contenteditable]:not([contenteditable="false"])'))
53
+ return;
49
54
  if (event.key === '+' || event.key === '=') {
50
55
  event.preventDefault();
51
56
  resize(1.25);
@@ -55,8 +60,7 @@ export function createImageViewer(options = {}) {
55
60
  resize(1 / 1.25);
56
61
  }
57
62
  }, { signal });
58
- function close() { if (dialog.open)
59
- dialog.close(); }
63
+ function close() { lifecycle.close(); }
60
64
  return {
61
65
  element: dialog,
62
66
  open(target) {
@@ -72,10 +76,9 @@ export function createImageViewer(options = {}) {
72
76
  image.alt = input.alt || '';
73
77
  scale = 1;
74
78
  fitted = true;
75
- status.textContent = 'Loading…';
79
+ feedback.update({ message: 'Loading…', tone: 'info' });
76
80
  image.src = input.src;
77
- if (!dialog.open)
78
- dialog.showModal();
81
+ lifecycle.open();
79
82
  viewport.scrollTop = viewport.scrollLeft = 0;
80
83
  update();
81
84
  },
@@ -90,12 +93,30 @@ export function createImageViewer(options = {}) {
90
93
  source = null;
91
94
  destroyed = true;
92
95
  observer.disconnect();
96
+ feedback.destroy();
97
+ lifecycle.destroy();
93
98
  listeners.abort();
94
99
  dialog.replaceChildren();
100
+ if (!options.dialog)
101
+ dialog.remove();
95
102
  },
96
103
  };
97
104
  }
98
- export function installImageViewer(dialog) {
99
- return createImageViewer({ dialog, headingId: dialog.getAttribute('aria-labelledby') || undefined });
105
+ export function installImageViewer(dialog, options = {}) {
106
+ const viewer = createImageViewer({ dialog, headingId: dialog.getAttribute('aria-labelledby') || undefined });
107
+ const abort = new AbortController();
108
+ const trigger = (event) => {
109
+ const image = event.target;
110
+ if (!(image instanceof HTMLImageElement) || !image.matches('.pui-history-image, .pui-draft-image img, .pui-file-preview-image'))
111
+ return;
112
+ if (event instanceof KeyboardEvent && (event.isComposing || !['Enter', ' '].includes(event.key)))
113
+ return;
114
+ if (event instanceof KeyboardEvent)
115
+ event.preventDefault();
116
+ viewer.open(image);
117
+ };
118
+ options.triggerRoot?.addEventListener('click', trigger, { signal: abort.signal });
119
+ options.triggerRoot?.addEventListener('keydown', trigger, { signal: abort.signal });
120
+ return { ...viewer, destroy() { abort.abort(); viewer.destroy(); } };
100
121
  }
101
122
  //# sourceMappingURL=image-viewer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image-viewer.js","sourceRoot":"","sources":["../../src/content/image-viewer.ts"],"names":[],"mappings":"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","sourcesContent":["import { escape, icon } from '../dom.js';\n\nexport interface ImageViewerOptions { dialog?: HTMLDialogElement; headingId?: string }\nexport interface ImageViewerInput { src: string; alt?: string; returnFocus?: HTMLElement }\nlet sequence = 0;\n\n/** A modal above either a generated-file preview or a conversation attachment. */\nexport function createImageViewer(options: ImageViewerOptions = {}) {\n const dialog = options.dialog || document.createElement('dialog');\n const headingId = options.headingId || `pui-image-title-${++sequence}`;\n dialog.classList.add('pui-modal', 'pui-image-viewer');\n dialog.setAttribute('aria-labelledby', headingId);\n dialog.innerHTML = `<div class=\"pui-modal-heading\"><h2 id=\"${escape(headingId)}\">Image</h2><button type=\"button\" class=\"pui-quiet-icon\" data-image-close aria-label=\"Close image\">${icon('close')}</button></div>\n <div class=\"pui-image-viewer-viewport\"><img alt=\"\" /></div>\n <div class=\"pui-image-viewer-toolbar\"><span data-image-scale role=\"status\"></span><div><button type=\"button\" class=\"pui-secondary-button\" data-image-out aria-label=\"Zoom out\">−</button><button type=\"button\" class=\"pui-secondary-button\" data-image-in aria-label=\"Zoom in\">+</button><button type=\"button\" class=\"pui-secondary-button\" data-image-fit>Fit</button><button type=\"button\" class=\"pui-secondary-button\" data-image-original>Original size</button></div></div>`;\n const image = dialog.querySelector<HTMLImageElement>('img')!;\n const viewport = dialog.querySelector<HTMLElement>('.pui-image-viewer-viewport')!;\n const title = dialog.querySelector<HTMLElement>('h2')!;\n const status = dialog.querySelector<HTMLElement>('[data-image-scale]')!;\n const listeners = new AbortController();\n const signal = listeners.signal;\n let destroyed = false;\n let scale = 1, fitted = true, source: HTMLElement | null = null;\n function update(): void {\n if (destroyed || !image.naturalWidth) return;\n if (fitted) scale = Math.min(1, viewport.clientWidth / image.naturalWidth, viewport.clientHeight / image.naturalHeight);\n image.style.width = `${image.naturalWidth * scale}px`;\n image.style.height = `${image.naturalHeight * scale}px`;\n status.textContent = `${Math.round(scale * 100)}% · ${image.naturalWidth} × ${image.naturalHeight}`;\n }\n function resize(factor: number): void { fitted = false; const next = scale * factor; if (Number.isFinite(next) && next > 0) scale = next; update(); }\n dialog.querySelector('[data-image-fit]')!.addEventListener('click', () => { fitted = true; update(); }, { signal });\n dialog.querySelector('[data-image-original]')!.addEventListener('click', () => { fitted = false; scale = 1; update(); }, { signal });\n dialog.querySelector('[data-image-in]')!.addEventListener('click', () => resize(1.25), { signal });\n dialog.querySelector('[data-image-out]')!.addEventListener('click', () => resize(1 / 1.25), { signal });\n dialog.querySelector('[data-image-close]')!.addEventListener('click', () => dialog.close(), { signal });\n image.addEventListener('load', update, { signal });\n const observer = new ResizeObserver(() => { if (dialog.open && fitted) update(); });\n observer.observe(viewport);\n dialog.addEventListener('close', () => {\n image.removeAttribute('src');\n image.removeAttribute('style');\n source?.focus({ preventScroll: true });\n source = null;\n }, { signal });\n document.addEventListener('keydown', (event) => {\n if (!dialog.open || event.isComposing || event.metaKey || event.ctrlKey || event.altKey) return;\n if (event.key === '+' || event.key === '=') { event.preventDefault(); resize(1.25); }\n if (event.key === '-') { event.preventDefault(); resize(1 / 1.25); }\n }, { signal });\n function close(): void { if (dialog.open) dialog.close(); }\n return {\n element: dialog,\n open(target: HTMLImageElement | ImageViewerInput): void {\n if (destroyed) return;\n const input = target instanceof HTMLImageElement\n ? target.getAttribute('src') && !target.classList.contains('pui-image-unavailable') ? { src: target.src, alt: target.alt, returnFocus: target } : null\n : target;\n if (!input?.src) return;\n source = input.returnFocus || null;\n title.textContent = input.alt || 'Image';\n image.alt = input.alt || '';\n scale = 1;\n fitted = true;\n status.textContent = 'Loading…';\n image.src = input.src;\n if (!dialog.open) dialog.showModal();\n viewport.scrollTop = viewport.scrollLeft = 0;\n update();\n },\n close,\n destroy(): void {\n if (destroyed) return;\n close();\n image.removeAttribute('src');\n image.removeAttribute('style');\n source?.focus({ preventScroll: true });\n source = null;\n destroyed = true;\n observer.disconnect();\n listeners.abort();\n dialog.replaceChildren();\n },\n };\n}\n\nexport function installImageViewer(dialog: HTMLDialogElement) {\n return createImageViewer({ dialog, headingId: dialog.getAttribute('aria-labelledby') || undefined });\n}\n"]}
1
+ {"version":3,"file":"image-viewer.js","sourceRoot":"","sources":["../../src/content/image-viewer.ts"],"names":[],"mappings":"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","sourcesContent":["import { escape, icon } from '../dom.js';\nimport { installDialogLifecycle } from '../surface.js';\nimport {adoptFeedback} from '../foundations.js';\n\nexport interface ImageViewerOptions { dialog?: HTMLDialogElement; headingId?: string }\nexport interface ImageViewerInput { src: string; alt?: string; returnFocus?: HTMLElement }\nlet sequence = 0;\n\n/** A modal above either a generated-file preview or a conversation attachment. */\nexport function createImageViewer(options: ImageViewerOptions = {}) {\n const dialog = options.dialog || document.createElement('dialog');\n const headingId = options.headingId || `pui-image-title-${++sequence}`;\n dialog.classList.add('pui-modal', 'pui-image-viewer');\n dialog.setAttribute('aria-labelledby', headingId);\n dialog.innerHTML = `<div class=\"pui-modal-heading\"><h2 id=\"${escape(headingId)}\">Image</h2><button type=\"button\" class=\"pui-quiet-icon\" data-image-close aria-label=\"Close image\">${icon('close')}</button></div>\n <div class=\"pui-image-viewer-viewport\"><img alt=\"\" /></div>\n <div class=\"pui-image-viewer-toolbar\"><span data-image-scale role=\"status\"></span><div><button type=\"button\" class=\"pui-secondary-button\" data-image-out aria-label=\"Zoom out\">−</button><button type=\"button\" class=\"pui-secondary-button\" data-image-in aria-label=\"Zoom in\">+</button><button type=\"button\" class=\"pui-secondary-button\" data-image-fit>Fit</button><button type=\"button\" class=\"pui-secondary-button\" data-image-original>Original size</button></div></div>`;\n const image = dialog.querySelector<HTMLImageElement>('img')!;\n const viewport = dialog.querySelector<HTMLElement>('.pui-image-viewer-viewport')!;\n const title = dialog.querySelector<HTMLElement>('h2')!;\n const status = dialog.querySelector<HTMLElement>('[data-image-scale]')!;\n const feedback = adoptFeedback(status, {presentation:'inline', autoHide:false});\n const listeners = new AbortController();\n const signal = listeners.signal;\n let destroyed = false;\n let scale = 1, fitted = true, source: HTMLElement | null = null;\n function update(): void {\n if (destroyed || !image.naturalWidth) return;\n if (fitted) scale = Math.min(1, viewport.clientWidth / image.naturalWidth, viewport.clientHeight / image.naturalHeight);\n image.style.width = `${image.naturalWidth * scale}px`;\n image.style.height = `${image.naturalHeight * scale}px`;\n feedback.update({message:`${Math.round(scale * 100)}% · ${image.naturalWidth} × ${image.naturalHeight}`, tone:'info'});\n }\n function resize(factor: number): void { fitted = false; const next = scale * factor; if (Number.isFinite(next) && next > 0) scale = next; update(); }\n dialog.querySelector('[data-image-fit]')!.addEventListener('click', () => { fitted = true; update(); }, { signal });\n dialog.querySelector('[data-image-original]')!.addEventListener('click', () => { fitted = false; scale = 1; update(); }, { signal });\n dialog.querySelector('[data-image-in]')!.addEventListener('click', () => resize(1.25), { signal });\n dialog.querySelector('[data-image-out]')!.addEventListener('click', () => resize(1 / 1.25), { signal });\n dialog.querySelector('[data-image-close]')!.addEventListener('click', () => dialog.close(), { signal });\n image.addEventListener('load', update, { signal });\n const observer = new ResizeObserver(() => { if (dialog.open && fitted) update(); });\n observer.observe(viewport);\n const lifecycle = installDialogLifecycle(dialog, { returnFocus: () => source, onClose: () => {\n image.removeAttribute('src');\n image.removeAttribute('style');\n source = null;\n } });\n dialog.addEventListener('keydown', (event) => {\n if (!dialog.open || event.isComposing || event.metaKey || event.ctrlKey || event.altKey) return;\n const target = event.target instanceof Element ? event.target : null;\n if (target?.closest('dialog') !== dialog || target.closest('input, textarea, [contenteditable]:not([contenteditable=\"false\"])')) return;\n if (event.key === '+' || event.key === '=') { event.preventDefault(); resize(1.25); }\n if (event.key === '-') { event.preventDefault(); resize(1 / 1.25); }\n }, { signal });\n function close(): void { lifecycle.close(); }\n return {\n element: dialog,\n open(target: HTMLImageElement | ImageViewerInput): void {\n if (destroyed) return;\n const input = target instanceof HTMLImageElement\n ? target.getAttribute('src') && !target.classList.contains('pui-image-unavailable') ? { src: target.src, alt: target.alt, returnFocus: target } : null\n : target;\n if (!input?.src) return;\n source = input.returnFocus || null;\n title.textContent = input.alt || 'Image';\n image.alt = input.alt || '';\n scale = 1;\n fitted = true;\n feedback.update({message:'Loading…', tone:'info'});\n image.src = input.src;\n lifecycle.open();\n viewport.scrollTop = viewport.scrollLeft = 0;\n update();\n },\n close,\n destroy(): void {\n if (destroyed) return;\n close();\n image.removeAttribute('src');\n image.removeAttribute('style');\n source?.focus({ preventScroll: true });\n source = null;\n destroyed = true;\n observer.disconnect();\n feedback.destroy();\n lifecycle.destroy();\n listeners.abort();\n dialog.replaceChildren();\n if (!options.dialog) dialog.remove();\n },\n };\n}\n\nexport function installImageViewer(dialog: HTMLDialogElement, options: { triggerRoot?: HTMLElement } = {}) {\n const viewer = createImageViewer({ dialog, headingId: dialog.getAttribute('aria-labelledby') || undefined });\n const abort = new AbortController();\n const trigger = (event: Event) => {\n const image = event.target;\n if (!(image instanceof HTMLImageElement) || !image.matches('.pui-history-image, .pui-draft-image img, .pui-file-preview-image')) return;\n if (event instanceof KeyboardEvent && (event.isComposing || !['Enter', ' '].includes(event.key))) return;\n if (event instanceof KeyboardEvent) event.preventDefault();\n viewer.open(image);\n };\n options.triggerRoot?.addEventListener('click', trigger, { signal: abort.signal });\n options.triggerRoot?.addEventListener('keydown', trigger, { signal: abort.signal });\n return { ...viewer, destroy() { abort.abort(); viewer.destroy(); } };\n}\n"]}
@@ -8,7 +8,7 @@ export interface PagedPreviewState {
8
8
  }
9
9
  export interface PagedPreviewOptions extends PagedPreviewState {
10
10
  container?: HTMLElement;
11
- onCopyCode?: (text: string) => void;
11
+ onCopyCode?: (text: string, button: HTMLButtonElement) => void;
12
12
  }
13
13
  export declare function createPagedPreview(options: PagedPreviewOptions): UIBlock<PagedPreviewState>;
14
14
  export declare function mountPagedPreview(container: HTMLElement, text: string, kind: PreviewKind): void;
@@ -1 +1 @@
1
- {"version":3,"file":"paged-preview.d.ts","sourceRoot":"","sources":["../../src/content/paged-preview.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,SAAmB,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBvG;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AACvD,MAAM,WAAW,iBAAiB;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE;AACtE,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAAG,SAAS,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE;AAE/H,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0B3F;AAGD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAG/F"}
1
+ {"version":3,"file":"paged-preview.d.ts","sourceRoot":"","sources":["../../src/content/paged-preview.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK1C,eAAO,MAAM,gBAAgB,QAAa,CAAC;AAE3C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,SAAmB,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAgBvG;AAED,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AACvD,MAAM,WAAW,iBAAiB;IAAG,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,WAAW,CAAA;CAAE;AACtE,MAAM,WAAW,mBAAoB,SAAQ,iBAAiB;IAAG,SAAS,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,KAAK,IAAI,CAAA;CAAE;AAE1J,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0B3F;AAGD,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAG/F"}
@@ -1,4 +1,5 @@
1
1
  import { renderDiff, renderMarkdown } from './markdown.js';
2
+ import { adoptProductParts } from '../foundations.js';
2
3
  // A rendering window, not a file acceptance cap. Large Markdown is shown as
3
4
  // original text so page boundaries cannot change the meaning of fenced code.
4
5
  export const previewPageBytes = 256 * 1024;
@@ -31,8 +32,10 @@ export function createPagedPreview(options) {
31
32
  let dispose = renderPreview(element, current.text, current.kind);
32
33
  const click = (event) => {
33
34
  const button = event.target.closest('[data-copy-code]');
34
- if (button && !button.disabled && element.contains(button))
35
- options.onCopyCode?.(button.closest('.pui-code-block')?.querySelector('code')?.textContent || '');
35
+ if (button && !button.disabled && element.contains(button) && options.onCopyCode) {
36
+ event.stopPropagation();
37
+ void options.onCopyCode(button.closest('.pui-code-block')?.querySelector('code')?.textContent || '', button);
38
+ }
36
39
  };
37
40
  if (options.onCopyCode)
38
41
  element.addEventListener('click', click);
@@ -80,6 +83,7 @@ function renderPreview(container, text, kind) {
80
83
  previous.textContent = 'Previous';
81
84
  next.textContent = 'Next';
82
85
  toolbar.append(previous, status, next);
86
+ const parts = adoptProductParts(container);
83
87
  let page = 0;
84
88
  const decoder = new TextDecoder('utf-8', { fatal: true });
85
89
  const render = () => {
@@ -91,15 +95,16 @@ function renderPreview(container, text, kind) {
91
95
  content.innerHTML = renderDiff(value);
92
96
  else
93
97
  content.textContent = value;
94
- previous.disabled = page === 0;
95
- next.disabled = page === ranges.length - 1;
98
+ parts.action(previous).update({ disabled: page === 0 });
99
+ parts.action(next).update({ disabled: page === ranges.length - 1 });
96
100
  status.textContent = `${page + 1} / ${ranges.length}${kind === 'markdown' && ranges.length > 1 ? ' · Plain text preview' : ''}`;
97
101
  content.scrollTop = 0;
102
+ parts.update();
98
103
  };
99
104
  previous.onclick = () => { page--; render(); };
100
105
  next.onclick = () => { page++; render(); };
101
106
  container.replaceChildren(...(ranges.length > 1 ? [toolbar, content] : [content]));
102
107
  render();
103
- return () => { previous.onclick = null; next.onclick = null; };
108
+ return () => { parts.destroy(); previous.onclick = null; next.onclick = null; };
104
109
  }
105
110
  //# sourceMappingURL=paged-preview.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"paged-preview.js","sourceRoot":"","sources":["../../src/content/paged-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderDiff, renderMarkdown } from './markdown.js';\nimport type { UIBlock } from '../core.js';\n\n// A rendering window, not a file acceptance cap. Large Markdown is shown as\n// original text so page boundaries cannot change the meaning of fenced code.\nexport const previewPageBytes = 256 * 1024;\n\nexport function previewPageRanges(bytes: Uint8Array, window = previewPageBytes): Array<[number, number]> {\n if (window < 4) throw new RangeError('A preview window must fit a UTF-8 character');\n const ranges: Array<[number, number]> = [];\n for (let start = 0; start < bytes.length;) {\n let end = Math.min(start + window, bytes.length);\n while (end < bytes.length && (bytes[end] & 0xc0) === 0x80) end--;\n if (end < bytes.length) {\n // Prefer complete lines without turning a long line into tiny pages.\n for (let i = end - 1; i >= start + window / 2 - 1; i--) {\n if (bytes[i] === 10) { end = i + 1; break; }\n }\n }\n ranges.push([start, end]);\n start = end;\n }\n return ranges.length ? ranges : [[0, 0]];\n}\n\nexport type PreviewKind = 'text' | 'markdown' | 'diff';\nexport interface PagedPreviewState { text: string; kind: PreviewKind }\nexport interface PagedPreviewOptions extends PagedPreviewState { container?: HTMLElement; onCopyCode?: (text: string) => void }\n\nexport function createPagedPreview(options: PagedPreviewOptions): UIBlock<PagedPreviewState> {\n const element = options.container || document.createElement('div');\n let current = { text: options.text, kind: options.kind };\n let disposed = false;\n let dispose = renderPreview(element, current.text, current.kind);\n const click = (event: Event) => {\n const button = (event.target as Element).closest<HTMLButtonElement>('[data-copy-code]');\n if (button && !button.disabled && element.contains(button)) options.onCopyCode?.(button.closest('.pui-code-block')?.querySelector('code')?.textContent || '');\n };\n if (options.onCopyCode) element.addEventListener('click', click);\n return {\n element,\n update(state): void {\n if (disposed || (state.text === current.text && state.kind === current.kind)) return;\n dispose();\n current = { ...state };\n dispose = renderPreview(element, state.text, state.kind);\n },\n destroy(): void {\n if (disposed) return;\n disposed = true;\n dispose();\n element.removeEventListener('click', click);\n element.replaceChildren();\n },\n };\n}\n\nconst mounted = new WeakMap<HTMLElement, UIBlock<PagedPreviewState>>();\nexport function mountPagedPreview(container: HTMLElement, text: string, kind: PreviewKind): void {\n mounted.get(container)?.destroy();\n mounted.set(container, createPagedPreview({ container, text, kind }));\n}\n\nfunction renderPreview(container: HTMLElement, text: string, kind: PreviewKind): () => void {\n const bytes = new TextEncoder().encode(text);\n const ranges = previewPageRanges(bytes);\n const content = document.createElement(kind === 'markdown' && ranges.length === 1 ? 'div' : 'pre');\n content.className = kind === 'diff' ? 'pui-approval-diff' : content.tagName === 'DIV' ? 'pui-markdown' : 'pui-file-preview-text';\n if (kind === 'diff') content.setAttribute('aria-label', 'Proposed file changes');\n const toolbar = document.createElement('div');\n toolbar.className = 'pui-feature-toolbar pui-preview-pagination';\n const previous = document.createElement('button');\n const next = document.createElement('button');\n const status = document.createElement('span');\n status.setAttribute('aria-live', 'polite');\n for (const button of [previous, next]) { button.type = 'button'; button.className = 'pui-secondary-button'; }\n previous.textContent = 'Previous'; next.textContent = 'Next';\n toolbar.append(previous, status, next);\n let page = 0;\n const decoder = new TextDecoder('utf-8', { fatal: true });\n const render = () => {\n const [start, end] = ranges[page];\n const value = decoder.decode(bytes.subarray(start, end));\n if (kind === 'markdown' && ranges.length === 1) content.innerHTML = renderMarkdown(value);\n else if (kind === 'diff') content.innerHTML = renderDiff(value);\n else content.textContent = value;\n previous.disabled = page === 0; next.disabled = page === ranges.length - 1;\n status.textContent = `${page + 1} / ${ranges.length}${kind === 'markdown' && ranges.length > 1 ? ' · Plain text preview' : ''}`;\n content.scrollTop = 0;\n };\n previous.onclick = () => { page--; render(); };\n next.onclick = () => { page++; render(); };\n container.replaceChildren(...(ranges.length > 1 ? [toolbar, content] : [content]));\n render();\n return () => { previous.onclick = null; next.onclick = null; };\n}\n"]}
1
+ {"version":3,"file":"paged-preview.js","sourceRoot":"","sources":["../../src/content/paged-preview.ts"],"names":[],"mappings":"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","sourcesContent":["import { renderDiff, renderMarkdown } from './markdown.js';\nimport type { UIBlock } from '../core.js';\nimport { adoptProductParts } from '../foundations.js';\n\n// A rendering window, not a file acceptance cap. Large Markdown is shown as\n// original text so page boundaries cannot change the meaning of fenced code.\nexport const previewPageBytes = 256 * 1024;\n\nexport function previewPageRanges(bytes: Uint8Array, window = previewPageBytes): Array<[number, number]> {\n if (window < 4) throw new RangeError('A preview window must fit a UTF-8 character');\n const ranges: Array<[number, number]> = [];\n for (let start = 0; start < bytes.length;) {\n let end = Math.min(start + window, bytes.length);\n while (end < bytes.length && (bytes[end] & 0xc0) === 0x80) end--;\n if (end < bytes.length) {\n // Prefer complete lines without turning a long line into tiny pages.\n for (let i = end - 1; i >= start + window / 2 - 1; i--) {\n if (bytes[i] === 10) { end = i + 1; break; }\n }\n }\n ranges.push([start, end]);\n start = end;\n }\n return ranges.length ? ranges : [[0, 0]];\n}\n\nexport type PreviewKind = 'text' | 'markdown' | 'diff';\nexport interface PagedPreviewState { text: string; kind: PreviewKind }\nexport interface PagedPreviewOptions extends PagedPreviewState { container?: HTMLElement; onCopyCode?: (text: string, button: HTMLButtonElement) => void }\n\nexport function createPagedPreview(options: PagedPreviewOptions): UIBlock<PagedPreviewState> {\n const element = options.container || document.createElement('div');\n let current = { text: options.text, kind: options.kind };\n let disposed = false;\n let dispose = renderPreview(element, current.text, current.kind);\n const click = (event: Event) => {\n const button = (event.target as Element).closest<HTMLButtonElement>('[data-copy-code]');\n if (button && !button.disabled && element.contains(button) && options.onCopyCode) {event.stopPropagation(); void options.onCopyCode(button.closest('.pui-code-block')?.querySelector('code')?.textContent || '', button);}\n };\n if (options.onCopyCode) element.addEventListener('click', click);\n return {\n element,\n update(state): void {\n if (disposed || (state.text === current.text && state.kind === current.kind)) return;\n dispose();\n current = { ...state };\n dispose = renderPreview(element, state.text, state.kind);\n },\n destroy(): void {\n if (disposed) return;\n disposed = true;\n dispose();\n element.removeEventListener('click', click);\n element.replaceChildren();\n },\n };\n}\n\nconst mounted = new WeakMap<HTMLElement, UIBlock<PagedPreviewState>>();\nexport function mountPagedPreview(container: HTMLElement, text: string, kind: PreviewKind): void {\n mounted.get(container)?.destroy();\n mounted.set(container, createPagedPreview({ container, text, kind }));\n}\n\nfunction renderPreview(container: HTMLElement, text: string, kind: PreviewKind): () => void {\n const bytes = new TextEncoder().encode(text);\n const ranges = previewPageRanges(bytes);\n const content = document.createElement(kind === 'markdown' && ranges.length === 1 ? 'div' : 'pre');\n content.className = kind === 'diff' ? 'pui-approval-diff' : content.tagName === 'DIV' ? 'pui-markdown' : 'pui-file-preview-text';\n if (kind === 'diff') content.setAttribute('aria-label', 'Proposed file changes');\n const toolbar = document.createElement('div');\n toolbar.className = 'pui-feature-toolbar pui-preview-pagination';\n const previous = document.createElement('button');\n const next = document.createElement('button');\n const status = document.createElement('span');\n status.setAttribute('aria-live', 'polite');\n for (const button of [previous, next]) { button.type = 'button'; button.className = 'pui-secondary-button'; }\n previous.textContent = 'Previous'; next.textContent = 'Next';\n toolbar.append(previous, status, next);\n const parts = adoptProductParts(container);\n let page = 0;\n const decoder = new TextDecoder('utf-8', { fatal: true });\n const render = () => {\n const [start, end] = ranges[page];\n const value = decoder.decode(bytes.subarray(start, end));\n if (kind === 'markdown' && ranges.length === 1) content.innerHTML = renderMarkdown(value);\n else if (kind === 'diff') content.innerHTML = renderDiff(value);\n else content.textContent = value;\n parts.action(previous).update({ disabled: page === 0 }); parts.action(next).update({ disabled: page === ranges.length - 1 });\n status.textContent = `${page + 1} / ${ranges.length}${kind === 'markdown' && ranges.length > 1 ? ' · Plain text preview' : ''}`;\n content.scrollTop = 0;\n parts.update();\n };\n previous.onclick = () => { page--; render(); };\n next.onclick = () => { page++; render(); };\n container.replaceChildren(...(ranges.length > 1 ? [toolbar, content] : [content]));\n render();\n return () => { parts.destroy(); previous.onclick = null; next.onclick = null; };\n}\n"]}
@@ -6,4 +6,7 @@ export type View = {
6
6
  };
7
7
  export declare function reconcileViews(parent: HTMLElement, views: View[]): void;
8
8
  export declare function clearViews(parent: HTMLElement): void;
9
+ /** Private patching of library-produced, sanitized markup. This is not a public
10
+ * HTML renderer: product factories retain their own templates and state. */
11
+ export declare function patchChildren(parent: HTMLElement, source: ParentNode): void;
9
12
  //# sourceMappingURL=views.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/content/views.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC;AAO7F,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAsCvE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAsD"}
1
+ {"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/content/views.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,IAAI,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAA;CAAE,CAAC;AAO7F,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAwBvE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAsD;AAE3G;4EAC4E;AAC5E,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAI3E"}
@@ -1,53 +1,122 @@
1
+ import { reconcileKeyed } from '../reconcile.js';
1
2
  let version = 0;
2
3
  const entries = new WeakMap();
3
4
  // Reuse unchanged nodes: selection, focus, decoded images, feedback and code
4
5
  // scrolling belong to the reader, and must survive another message's updates.
5
6
  export function reconcileViews(parent, views) {
6
7
  const previous = entries.get(parent) || new Map();
7
- const current = new Map();
8
- let cursor = parent.firstElementChild;
9
- for (const view of views) {
10
- let entry = previous.get(view.key);
11
- let scroll;
12
- if (!entry || entry.signature !== view.signature) {
13
- const template = document.createElement('template');
14
- template.innerHTML = view.html();
15
- const element = template.content.firstElementChild;
16
- element.dataset.renderVersion = String(++version);
17
- if (entry && view.children) {
18
- // Group summaries may change while completed tool results stay intact.
19
- entry.element.querySelector('summary').replaceWith(element.querySelector('summary'));
20
- entry.signature = view.signature;
21
- }
22
- else {
23
- if (entry) {
24
- const images = new Map(Array.from(entry.element.querySelectorAll('[data-image-key]'), (image) => [image.dataset.imageKey, image]));
25
- for (const image of element.querySelectorAll('[data-image-key]')) {
26
- const saved = images.get(image.dataset.imageKey);
27
- if (saved)
28
- image.replaceWith(saved);
29
- }
30
- scroll = Array.from(entry.element.querySelectorAll('pre'), (pre) => pre.scrollLeft);
31
- entry.element.replaceWith(element);
32
- if (cursor === entry.element)
33
- cursor = element;
8
+ const render = (view) => {
9
+ const template = document.createElement('template');
10
+ template.innerHTML = view.html();
11
+ const element = template.content.firstElementChild;
12
+ element.dataset.renderVersion = String(++version);
13
+ return element;
14
+ };
15
+ reconcileKeyed(parent, previous, views, {
16
+ key: view => view.key, element: entry => entry.element,
17
+ create: view => ({ element: render(view), signature: view.signature }),
18
+ update(entry, view) {
19
+ if (entry.signature !== view.signature) {
20
+ const next = render(view);
21
+ if (entry.element.tagName === next.tagName)
22
+ patchElement(entry.element, next);
23
+ else {
24
+ entry.element.replaceWith(next);
25
+ entry.element = next;
34
26
  }
35
- entry = { element, signature: view.signature };
27
+ entry.signature = view.signature;
36
28
  }
29
+ if (view.children)
30
+ reconcileViews(entry.element.querySelector('[data-view-children]'), view.children);
31
+ },
32
+ destroy() { },
33
+ });
34
+ entries.set(parent, previous);
35
+ }
36
+ export function clearViews(parent) { parent.replaceChildren(); entries.delete(parent); }
37
+ /** Private patching of library-produced, sanitized markup. This is not a public
38
+ * HTML renderer: product factories retain their own templates and state. */
39
+ export function patchChildren(parent, source) {
40
+ const focused = parent.ownerDocument.activeElement;
41
+ patchNodes(parent, source);
42
+ if (focused instanceof HTMLElement && focused.isConnected && parent.contains(focused) && parent.ownerDocument.activeElement !== focused)
43
+ focused.focus({ preventScroll: true });
44
+ }
45
+ function key(node) {
46
+ if (!(node instanceof Element))
47
+ return;
48
+ for (const attribute of ['data-image-key', 'data-message-action', 'data-welcome-action', 'data-prompt', 'data-view-children', 'data-copy-code', 'id']) {
49
+ if (node.hasAttribute(attribute))
50
+ return `${node.tagName}:${attribute}:${node.getAttribute(attribute)}`;
51
+ }
52
+ }
53
+ function matches(current, next) {
54
+ if (current.nodeType !== next.nodeType)
55
+ return false;
56
+ if (current instanceof Element && next instanceof Element)
57
+ return current.tagName === next.tagName && key(current) === key(next);
58
+ return true;
59
+ }
60
+ function patchNodes(parent, source) {
61
+ let cursor = parent.firstChild;
62
+ for (const next of [...source.childNodes]) {
63
+ let current = cursor;
64
+ if (!current || !matches(current, next))
65
+ current = key(next) ? [...parent.childNodes].find(node => matches(node, next)) ?? null : null;
66
+ if (!current) {
67
+ parent.insertBefore(next, cursor);
68
+ continue;
37
69
  }
38
- if (entry.element !== cursor)
39
- parent.insertBefore(entry.element, cursor);
40
- if (scroll)
41
- entry.element.querySelectorAll('pre').forEach((pre, index) => { pre.scrollLeft = scroll[index] || 0; });
42
- cursor = entry.element.nextElementSibling;
43
- if (view.children)
44
- reconcileViews(entry.element.querySelector('[data-view-children]'), view.children);
45
- current.set(view.key, entry);
70
+ if (current !== cursor)
71
+ parent.insertBefore(current, cursor);
72
+ if (current instanceof HTMLElement && next instanceof HTMLElement)
73
+ patchElement(current, next);
74
+ else if (current instanceof SVGElement && next instanceof SVGElement) {
75
+ patchAttributes(current, next);
76
+ patchNodes(current, next);
77
+ }
78
+ else if (current instanceof Text && next instanceof Text && current.data !== next.data) {
79
+ // Replacing only the changed span retains Range offsets in an unchanged
80
+ // streamed prefix, unlike assigning textContent or replacing the Text.
81
+ let prefix = 0, suffix = 0;
82
+ while (prefix < current.length && prefix < next.length && current.data[prefix] === next.data[prefix])
83
+ prefix++;
84
+ while (suffix < current.length - prefix && suffix < next.length - prefix && current.data[current.length - suffix - 1] === next.data[next.length - suffix - 1])
85
+ suffix++;
86
+ current.replaceData(prefix, current.length - prefix - suffix, next.data.slice(prefix, next.length - suffix));
87
+ }
88
+ cursor = current.nextSibling;
89
+ }
90
+ while (cursor) {
91
+ const next = cursor.nextSibling;
92
+ cursor.remove();
93
+ cursor = next;
46
94
  }
47
- for (const [key, entry] of previous)
48
- if (!current.has(key))
49
- entry.element.remove();
50
- entries.set(parent, current);
51
95
  }
52
- export function clearViews(parent) { parent.replaceChildren(); entries.delete(parent); }
96
+ function patchAttributes(current, next) {
97
+ for (const attribute of [...current.attributes]) {
98
+ if (current instanceof HTMLDetailsElement && attribute.name === 'open')
99
+ continue;
100
+ if (!next.hasAttribute(attribute.name))
101
+ current.removeAttribute(attribute.name);
102
+ }
103
+ for (const attribute of [...next.attributes]) {
104
+ if (current instanceof HTMLDetailsElement && attribute.name === 'open')
105
+ continue;
106
+ if (current.getAttribute(attribute.name) !== attribute.value)
107
+ current.setAttribute(attribute.name, attribute.value);
108
+ }
109
+ }
110
+ function patchElement(current, next) {
111
+ if ((current instanceof HTMLImageElement && current.dataset.imageKey && current.dataset.imageKey === next.dataset.imageKey) || (current instanceof HTMLButtonElement && current.dataset.copyPending === 'true'))
112
+ return;
113
+ const scrollTop = current.scrollTop, scrollLeft = current.scrollLeft;
114
+ patchAttributes(current, next);
115
+ if (!current.hasAttribute('data-view-children'))
116
+ patchChildren(current, next);
117
+ if (current.scrollTop !== scrollTop)
118
+ current.scrollTop = scrollTop;
119
+ if (current.scrollLeft !== scrollLeft)
120
+ current.scrollLeft = scrollLeft;
121
+ }
53
122
  //# sourceMappingURL=views.js.map