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":"dialog-frame.js","sourceRoot":"","sources":["../../src/forms/dialog-frame.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from './identity.js';\nimport type { UIBlock } from '../core.js';\nimport { dialogHeading, escape, showDialogError, uniqueId } from '../dom.js';\n\nexport interface DialogContent {element: HTMLElement; destroy(): void}\nexport interface FeatureDialogState {title: string; body: Node | DialogContent}\nexport interface FeatureDialogOptions {dialog?: HTMLDialogElement; content?: HTMLElement; dialogId?: string; titleId?: string; errorId?: string; eyebrow?: string}\nexport function createFeatureDialogFrame(options: FeatureDialogOptions = {}): UIBlock<FeatureDialogState> & {open(state: FeatureDialogState): void; close(): void; fail(message: string): void} {\n const dialog = options.dialog || document.createElement('dialog');\n const content = options.content || document.createElement('div');\n const dialogId = options.dialogId || uniqueId('pui-feature-dialog');\n const titleId = options.titleId || `${dialogId}-title`;\n const errorId = options.errorId || `${dialogId}-error`;\n if (!options.dialog) {dialog.id = dialogId; dialog.className = 'pui-modal pui-feature-modal pui-feature-dialog'; dialog.setAttribute('aria-labelledby', titleId); dialog.append(content);}\n let current: DialogContent | null = null; let destroyed = false;\n const clear = (keep?: DialogContent) => {if (current !== keep) current?.destroy(); current = null; content.replaceChildren(); content.onclick = null;};\n const update = (state: Readonly<FeatureDialogState>) => {\n if (destroyed) return;\n const active = document.activeElement;\n const restoreFocus = dialog.open && !!active && (content.contains(active) || active === dialog || active === document.body);\n const activeId = restoreFocus ? active?.id : '';\n clear(state.body instanceof Node ? undefined : state.body);\n content.innerHTML = dialogHeading(state.title, titleId, dialogId, options.eyebrow || '') + `<p id=\"${escape(errorId)}\" data-dialog-error class=\"pui-form-error\" role=\"alert\"></p>`;\n const body = state.body instanceof Node ? state.body : state.body.element;\n content.insertBefore(body, content.querySelector('[data-dialog-error]'));\n if (!(state.body instanceof Node)) current = state.body;\n if (restoreFocus) {\n const previous = activeId ? Array.from(content.querySelectorAll<HTMLElement>('[id]')).find((node) => node.getAttribute('id') === activeId) : null;\n const target = previous || content.querySelector<HTMLElement>('[data-close]');\n target?.focus({preventScroll: true});\n }\n };\n const onClose = () => {if (!dialog.open) clear();};\n const onClick = (event: Event) => {const button = (event.target as HTMLElement).closest<HTMLElement>('[data-close]'); if (button?.dataset.close === dialogId && dialog.open) dialog.close();};\n dialog.addEventListener('close', onClose); dialog.addEventListener('click', onClick);\n return {element: dialog, update, open(state) {update(state); if (!destroyed && !dialog.open) dialog.showModal();}, close() {if (dialog.open) dialog.close();}, fail(message) {showDialogError(content, message);}, destroy() {if (destroyed) return; destroyed = true; dialog.removeEventListener('close', onClose); dialog.removeEventListener('click', onClick); clear(); if (!options.dialog) dialog.remove();}};\n}\nexport function createFeatureStatus(state: {message: string; error?: boolean}): UIBlock<{message: string; error?: boolean}> {\n const element = document.createElement('p');\n const update = (state: {message: string; error?: boolean}) => {element.textContent = state.message; element.className = state.error ? 'pui-form-error' : 'pui-feature-hint'; element.setAttribute('role', state.error ? 'alert' : 'status');};\n update(state); return {element, update, destroy() {element.remove();}};\n}\nexport function createSDKDialogElements(options: {idPrefix?: string} = {}): {dialog: HTMLDialogElement; content: HTMLElement; oauthDialog: HTMLDialogElement; oauthContent: HTMLElement; destroy(): void} {\n const dialog = document.createElement('dialog'); dialog.id = 'sdk-dialog'; dialog.className = 'pui-modal pui-feature-modal'; dialog.setAttribute('aria-labelledby', 'sdk-title');\n const content = document.createElement('div'); content.id = 'sdk-content'; dialog.append(content);\n const oauthDialog = document.createElement('dialog'); oauthDialog.id = 'oauth-dialog'; oauthDialog.className = 'pui-modal'; oauthDialog.setAttribute('aria-labelledby', 'oauth-title');\n const oauthContent = document.createElement('div'); oauthContent.id = 'oauth-content'; oauthDialog.append(oauthContent);\n const prefix = options.idPrefix ?? uniqueId('pui-sdk') + '-'; scopeIds(dialog, prefix); scopeIds(oauthDialog, prefix);\n return {dialog, content, oauthDialog, oauthContent, destroy() {dialog.remove(); oauthDialog.remove();}};\n}\n"]}
1
+ {"version":3,"file":"dialog-frame.js","sourceRoot":"","sources":["../../src/forms/dialog-frame.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from './identity.js';\nimport type { UIBlock } from '../core.js';\nimport { dialogHeading, uniqueId } from '../dom.js';\nimport { installDialogLifecycle } from '../surface.js';\nimport { adoptFeedback } from '../foundations.js';\n\nexport interface DialogContent {element: HTMLElement; destroy(): void}\nexport interface FeatureDialogState {title: string; body: Node | DialogContent}\nexport interface FeatureDialogOptions {dialog?: HTMLDialogElement; content?: HTMLElement; dialogId?: string; titleId?: string; errorId?: string; eyebrow?: string; onClose?: () => void}\nexport function createFeatureDialogFrame(options: FeatureDialogOptions = {}): UIBlock<FeatureDialogState> & {open(state: FeatureDialogState): void; close(): void; fail(message: string): void; isOpen(): boolean} {\n const dialog = options.dialog || document.createElement('dialog');\n const content = options.content || document.createElement('div');\n const dialogId = options.dialogId || dialog.id || uniqueId('pui-feature-dialog');\n const titleId = options.titleId || `${dialogId}-title`;\n const errorId = options.errorId || `${dialogId}-error`;\n dialog.id = dialogId;\n if (!options.dialog) dialog.className = 'pui-modal pui-feature-modal pui-feature-dialog';\n dialog.setAttribute('aria-labelledby', titleId);\n if (!dialog.contains(content)) dialog.append(content);\n const template = document.createElement('template');\n template.innerHTML = dialogHeading('', titleId, dialogId, options.eyebrow || '');\n const heading = template.content.firstElementChild as HTMLElement;\n const title = heading.querySelector('h2')!;\n const close = heading.querySelector<HTMLButtonElement>('[data-close]')!;\n const error = document.createElement('p'); error.id = errorId; error.dataset.dialogError = ''; error.className = 'pui-form-error'; error.setAttribute('role', 'alert');\n const feedback = adoptFeedback(error, {presentation: 'inline', autoHide: false});\n let current: DialogContent | null = null; let currentBody: Node | null = null; let destroyed = false;\n const clear = () => {current?.destroy(); current = null; currentBody = null; content.replaceChildren(); content.onclick = null;};\n const lifecycle = installDialogLifecycle(dialog, {onClose() { clear(); options.onClose?.(); }});\n const update = (state: Readonly<FeatureDialogState>) => {\n if (destroyed) return;\n const active = document.activeElement;\n const restoreFocus = dialog.open && !!active && (content.contains(active) || active === dialog || active === document.body);\n const activeId = restoreFocus ? active?.id : '';\n const body = state.body instanceof Node ? state.body : state.body.element;\n const next = state.body instanceof Node ? null : state.body;\n const changed = body !== currentBody;\n if (current !== next) current?.destroy();\n current = next;\n if (changed) {\n currentBody?.parentNode?.removeChild(currentBody);\n currentBody = body;\n content.replaceChildren(heading, body, error);\n content.onclick = null;\n } else if (heading.parentNode !== content) content.replaceChildren(heading, body, error);\n title.textContent = state.title;\n close.setAttribute('aria-label', `Close ${state.title.toLowerCase()}`);\n feedback.update({message: '', tone: 'error'});\n if (restoreFocus && changed) {\n const previous = activeId ? Array.from(content.querySelectorAll<HTMLElement>('[id]')).find((node) => node.getAttribute('id') === activeId) : null;\n const target = previous || content.querySelector<HTMLElement>('[data-close]');\n target?.focus({preventScroll: true});\n }\n };\n return {element: dialog, update, open(state) {update(state); if (!destroyed) lifecycle.open();}, close: lifecycle.close, isOpen: lifecycle.isOpen, fail(message) {if (!destroyed) feedback.update({message, tone: 'error'});}, destroy() {if (destroyed) return; destroyed = true; lifecycle.close(); lifecycle.destroy(); clear(); feedback.destroy(); if (!options.dialog) dialog.remove();}};\n}\nexport function createFeatureStatus(state: {message: string; error?: boolean}): UIBlock<{message: string; error?: boolean}> {\n const element = document.createElement('p');\n const feedback = adoptFeedback(element, {presentation: 'inline', autoHide: false});\n const update = (state: {message: string; error?: boolean}) => {element.className = state.error ? 'pui-form-error' : 'pui-feature-hint'; feedback.update({message: state.message, tone: state.error ? 'error' : 'info'});};\n update(state); return {element, update, destroy() {feedback.destroy(); element.remove();}};\n}\nexport function createSDKDialogElements(options: {idPrefix?: string} = {}): {dialog: HTMLDialogElement; content: HTMLElement; oauthDialog: HTMLDialogElement; oauthContent: HTMLElement; destroy(): void} {\n const dialog = document.createElement('dialog'); dialog.id = 'sdk-dialog'; dialog.className = 'pui-modal pui-feature-modal'; dialog.setAttribute('aria-labelledby', 'sdk-title');\n const content = document.createElement('div'); content.id = 'sdk-content'; dialog.append(content);\n const oauthDialog = document.createElement('dialog'); oauthDialog.id = 'oauth-dialog'; oauthDialog.className = 'pui-modal'; oauthDialog.setAttribute('aria-labelledby', 'oauth-title');\n const oauthContent = document.createElement('div'); oauthContent.id = 'oauth-content'; oauthDialog.append(oauthContent);\n const prefix = options.idPrefix ?? uniqueId('pui-sdk') + '-'; scopeIds(dialog, prefix); scopeIds(oauthDialog, prefix);\n return {dialog, content, oauthDialog, oauthContent, destroy() {dialog.remove(); oauthDialog.remove();}};\n}\n"]}
@@ -10,7 +10,7 @@ export interface BranchHistoryState {
10
10
  error?: string;
11
11
  loading?: boolean;
12
12
  }
13
- export declare function createBranchHistory(state: BranchHistoryState, options?: {
13
+ export declare function createBranchHistory(initial: BranchHistoryState, options?: {
14
14
  onContinue?: (id: string) => void | Promise<void>;
15
15
  }): UIBlock<BranchHistoryState>;
16
16
  //# sourceMappingURL=history.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/forms/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC;AAC5F,MAAM,WAAW,kBAAkB;IAAE,KAAK,EAAE,SAAS,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAC;AACnH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,GAAE;IAAC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAK7J"}
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/forms/history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,MAAM,WAAW,iBAAiB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC;AAC5F,MAAM,WAAW,kBAAkB;IAAE,KAAK,EAAE,SAAS,iBAAiB,EAAE,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAC;AAEnH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,GAAE;IAAC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAsD/J"}
@@ -1,21 +1,84 @@
1
- import { escape } from '../dom.js';
2
- export function createBranchHistory(state, options = {}) {
1
+ import { setText } from '../dom.js';
2
+ import { adoptAction, adoptFeedback } from '../foundations.js';
3
+ export function createBranchHistory(initial, options = {}) {
3
4
  const element = document.createElement('div');
4
5
  element.className = 'pui-branch-history';
5
- const abort = new AbortController();
6
+ const description = document.createElement('p');
7
+ description.className = 'pui-modal-description';
8
+ description.textContent = 'Continue from any saved message. Other branches remain available here; files and external actions are not undone.';
9
+ const list = document.createElement('div');
10
+ list.className = 'pui-branch-list';
11
+ const empty = document.createElement('p');
12
+ empty.textContent = 'No history yet.';
13
+ const status = document.createElement('div');
14
+ const error = document.createElement('p');
15
+ error.className = 'pui-form-error';
16
+ const feedback = adoptFeedback(status, { presentation: 'inline' });
17
+ const actionFeedback = adoptFeedback(error, { presentation: 'inline' });
18
+ const rows = new Map();
6
19
  let destroyed = false;
7
- const update = (state) => { if (destroyed)
8
- return; if (state.loading) {
9
- element.textContent = 'Loading history…';
10
- return;
11
- } if (state.error) {
12
- element.textContent = state.error;
13
- return;
14
- } element.innerHTML = `<p class="pui-modal-description">Continue from any saved message. Other branches remain available here; files and external actions are not undone.</p><div class="pui-branch-list">${(state.nodes || []).map((node) => `<article class="pui-branch-node ${node.active ? 'pui-current-branch' : ''}"><div class="pui-connection-card-heading"><strong>${escape(node.role)}${node.active ? ' · current branch' : ''}</strong><button class="pui-secondary-button" data-branch="${escape(node.id)}">Continue here</button></div><p>${escape(node.text.slice(0, 360) || '(Image or empty reply)')}</p></article>`).join('') || '<p>No history yet.</p>'}</div>`; };
15
- element.addEventListener('click', (event) => { const target = event.target.closest('[data-branch]'); if (target && options.onContinue)
16
- void options.onContinue(target.dataset.branch); }, { signal: abort.signal });
17
- update(state);
20
+ const clear = () => { for (const row of rows.values())
21
+ row.action.destroy(); rows.clear(); list.replaceChildren(); };
22
+ const update = (state) => {
23
+ if (destroyed)
24
+ return;
25
+ if (state.loading || state.error) {
26
+ clear();
27
+ const message = state.loading ? 'Loading history…' : state.error;
28
+ feedback.update({ message, tone: state.loading ? 'info' : 'error' });
29
+ actionFeedback.update({ message: '', tone: 'error' });
30
+ element.replaceChildren(status);
31
+ return;
32
+ }
33
+ feedback.update({ message: '', tone: 'info' });
34
+ if (description.parentNode !== element)
35
+ element.replaceChildren(description, list, error);
36
+ const nodes = state.nodes ?? [];
37
+ const keep = new Set(nodes.map(node => node.id));
38
+ for (const [id, row] of rows)
39
+ if (!keep.has(id)) {
40
+ row.action.destroy();
41
+ row.element.remove();
42
+ rows.delete(id);
43
+ }
44
+ let cursor = list.firstElementChild;
45
+ for (const node of nodes) {
46
+ let row = rows.get(node.id);
47
+ if (!row) {
48
+ const article = document.createElement('article');
49
+ article.className = 'pui-branch-node';
50
+ const heading = document.createElement('div');
51
+ heading.className = 'pui-connection-card-heading';
52
+ const role = document.createElement('strong'), button = document.createElement('button'), text = document.createElement('p');
53
+ button.type = 'button';
54
+ button.className = 'pui-secondary-button';
55
+ button.textContent = 'Continue here';
56
+ button.dataset.branch = node.id;
57
+ heading.append(role, button);
58
+ article.append(heading, text);
59
+ const action = adoptAction(button, {
60
+ onAction: options.onContinue ? () => { actionFeedback.update({ message: '', tone: 'error' }); return options.onContinue(node.id); } : undefined,
61
+ onError: message => actionFeedback.update({ message, tone: 'error' }),
62
+ });
63
+ row = { element: article, role, text, action };
64
+ rows.set(node.id, row);
65
+ }
66
+ row.element.classList.toggle('pui-current-branch', node.active);
67
+ setText(row.role, `${node.role}${node.active ? ' · current branch' : ''}`);
68
+ setText(row.text, node.text.slice(0, 360) || '(Image or empty reply)');
69
+ if (row.element !== cursor)
70
+ list.insertBefore(row.element, cursor);
71
+ cursor = row.element.nextElementSibling;
72
+ }
73
+ if (!nodes.length) {
74
+ if (empty.parentNode !== list)
75
+ list.append(empty);
76
+ }
77
+ else
78
+ empty.remove();
79
+ };
80
+ update(initial);
18
81
  return { element, update, destroy() { if (destroyed)
19
- return; destroyed = true; abort.abort(); element.remove(); } };
82
+ return; destroyed = true; clear(); feedback.destroy(); actionFeedback.destroy(); element.remove(); } };
20
83
  }
21
84
  //# sourceMappingURL=history.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/forms/history.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGnC,MAAM,UAAU,mBAAmB,CAAC,KAAyB,EAAE,UAA+D,EAAE;IAC/H,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAAC,OAAO,CAAC,SAAS,GAAG,oBAAoB,CAAC;IAAC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IAAC,IAAI,SAAS,GAAG,KAAK,CAAC;IACpJ,MAAM,MAAM,GAAG,CAAC,KAAmC,EAAE,EAAE,GAAE,IAAI,SAAS;QAAE,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAAA,OAAO,CAAC,WAAW,GAAG,kBAAkB,CAAC;QAAC,OAAO;IAAA,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAAA,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC,KAAK,CAAC;QAAC,OAAO;IAAA,CAAC,CAAC,OAAO,CAAC,SAAS,GAAG,sLAAsL,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,mCAAmC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,sDAAsD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE,8DAA8D,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,oCAAoC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,wBAAwB,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,wBAAwB,QAAQ,CAAC,CAAA,CAAC,CAAC;IACx2B,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,GAAE,MAAM,MAAM,GAAI,KAAK,CAAC,MAAsB,CAAC,OAAO,CAAoB,eAAe,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI,OAAO,CAAC,UAAU;QAAE,KAAK,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,MAAO,CAAC,CAAC,CAAA,CAAC,EAAE,EAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAC,CAAC,CAAC;IACrP,MAAM,CAAC,KAAK,CAAC,CAAC;IAAC,OAAO,EAAC,OAAO,EAAE,MAAM,EAAE,OAAO,KAAI,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA,CAAC,EAAC,CAAC;AAChI,CAAC","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { escape } from '../dom.js';\nexport interface BranchHistoryNode {id: string; role: string; text: string; active: boolean}\nexport interface BranchHistoryState {nodes: readonly BranchHistoryNode[] | null; error?: string; loading?: boolean}\nexport function createBranchHistory(state: BranchHistoryState, options: {onContinue?: (id: string) => void | Promise<void>} = {}): UIBlock<BranchHistoryState> {\n const element = document.createElement('div'); element.className = 'pui-branch-history'; const abort = new AbortController(); let destroyed = false;\n const update = (state: Readonly<BranchHistoryState>) => {if (destroyed) return; if (state.loading) {element.textContent = 'Loading history…'; return;} if (state.error) {element.textContent = state.error; return;} element.innerHTML = `<p class=\"pui-modal-description\">Continue from any saved message. Other branches remain available here; files and external actions are not undone.</p><div class=\"pui-branch-list\">${(state.nodes || []).map((node) => `<article class=\"pui-branch-node ${node.active ? 'pui-current-branch' : ''}\"><div class=\"pui-connection-card-heading\"><strong>${escape(node.role)}${node.active ? ' · current branch' : ''}</strong><button class=\"pui-secondary-button\" data-branch=\"${escape(node.id)}\">Continue here</button></div><p>${escape(node.text.slice(0, 360) || '(Image or empty reply)')}</p></article>`).join('') || '<p>No history yet.</p>'}</div>`;};\n element.addEventListener('click', (event) => {const target = (event.target as HTMLElement).closest<HTMLButtonElement>('[data-branch]'); if (target && options.onContinue) void options.onContinue(target.dataset.branch!);}, {signal: abort.signal});\n update(state); return {element, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); element.remove();}};\n}\n"]}
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/forms/history.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { setText } from '../dom.js';\nimport { adoptAction, adoptFeedback, type ActionController } from '../foundations.js';\n\nexport interface BranchHistoryNode {id: string; role: string; text: string; active: boolean}\nexport interface BranchHistoryState {nodes: readonly BranchHistoryNode[] | null; error?: string; loading?: boolean}\n\nexport function createBranchHistory(initial: BranchHistoryState, options: {onContinue?: (id: string) => void | Promise<void>} = {}): UIBlock<BranchHistoryState> {\n const element = document.createElement('div'); element.className = 'pui-branch-history';\n const description = document.createElement('p'); description.className = 'pui-modal-description';\n description.textContent = 'Continue from any saved message. Other branches remain available here; files and external actions are not undone.';\n const list = document.createElement('div'); list.className = 'pui-branch-list';\n const empty = document.createElement('p'); empty.textContent = 'No history yet.';\n const status = document.createElement('div');\n const error = document.createElement('p'); error.className = 'pui-form-error';\n const feedback = adoptFeedback(status, { presentation: 'inline' });\n const actionFeedback = adoptFeedback(error, { presentation: 'inline' });\n type Row = { element: HTMLElement; role: HTMLElement; text: HTMLElement; action: ActionController };\n const rows = new Map<string, Row>();\n let destroyed = false;\n const clear = (): void => { for (const row of rows.values()) row.action.destroy(); rows.clear(); list.replaceChildren(); };\n const update = (state: Readonly<BranchHistoryState>): void => {\n if (destroyed) return;\n if (state.loading || state.error) {\n clear();\n const message = state.loading ? 'Loading history…' : state.error!;\n feedback.update({ message, tone: state.loading ? 'info' : 'error' });\n actionFeedback.update({ message: '', tone: 'error' });\n element.replaceChildren(status);\n return;\n }\n feedback.update({ message: '', tone: 'info' });\n if (description.parentNode !== element) element.replaceChildren(description, list, error);\n const nodes = state.nodes ?? [];\n const keep = new Set(nodes.map(node => node.id));\n for (const [id, row] of rows) if (!keep.has(id)) { row.action.destroy(); row.element.remove(); rows.delete(id); }\n let cursor = list.firstElementChild;\n for (const node of nodes) {\n let row = rows.get(node.id);\n if (!row) {\n const article = document.createElement('article'); article.className = 'pui-branch-node';\n const heading = document.createElement('div'); heading.className = 'pui-connection-card-heading';\n const role = document.createElement('strong'), button = document.createElement('button'), text = document.createElement('p');\n button.type = 'button'; button.className = 'pui-secondary-button'; button.textContent = 'Continue here'; button.dataset.branch = node.id;\n heading.append(role, button); article.append(heading, text);\n const action = adoptAction(button, {\n onAction: options.onContinue ? () => { actionFeedback.update({ message: '', tone: 'error' }); return options.onContinue!(node.id); } : undefined,\n onError: message => actionFeedback.update({ message, tone: 'error' }),\n });\n row = { element: article, role, text, action }; rows.set(node.id, row);\n }\n row.element.classList.toggle('pui-current-branch', node.active);\n setText(row.role, `${node.role}${node.active ? ' · current branch' : ''}`);\n setText(row.text, node.text.slice(0, 360) || '(Image or empty reply)');\n if (row.element !== cursor) list.insertBefore(row.element, cursor);\n cursor = row.element.nextElementSibling;\n }\n if (!nodes.length) { if (empty.parentNode !== list) list.append(empty); } else empty.remove();\n };\n update(initial);\n return { element, update, destroy() { if (destroyed) return; destroyed = true; clear(); feedback.destroy(); actionFeedback.destroy(); element.remove(); } };\n}\n"]}
@@ -1,4 +1,16 @@
1
1
  /** Scope DOM IDs and their references without relying on document-level lookup. */
2
2
  export declare function scopeIds(root: HTMLElement, prefix: string): void;
3
3
  export declare function localField<T extends HTMLElement = HTMLElement>(root: HTMLElement, id: string): T;
4
+ import { type ActionResult } from '../core.js';
5
+ /** Shared form lifetime; business writes and target identity remain with the caller. */
6
+ export declare function bindFormSubmission<Value>(form: HTMLFormElement, options: {
7
+ read: () => Value;
8
+ unavailable: () => boolean;
9
+ onSubmit?: (value: Value) => ActionResult | Promise<ActionResult>;
10
+ onPending: (pending: boolean) => void;
11
+ onError: (error: string) => void;
12
+ }): {
13
+ invalidate(): void;
14
+ destroy(): void;
15
+ };
4
16
  //# sourceMappingURL=identity.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/forms/identity.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAQhE;AACD,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAIhG"}
1
+ {"version":3,"file":"identity.d.ts","sourceRoot":"","sources":["../../src/forms/identity.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAQhE;AACD,wBAAgB,UAAU,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAIhG;AACD,OAAO,EAAC,KAAK,YAAY,EAAC,MAAM,YAAY,CAAC;AAI7C,wFAAwF;AACxF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,EAAC,eAAe,EAAC,OAAO,EAAC;IACrE,IAAI,EAAC,MAAI,KAAK,CAAC;IACf,WAAW,EAAC,MAAI,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAC,KAAK,KAAG,YAAY,GAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7D,SAAS,EAAC,CAAC,OAAO,EAAC,OAAO,KAAG,IAAI,CAAC;IAClC,OAAO,EAAC,CAAC,KAAK,EAAC,MAAM,KAAG,IAAI,CAAC;CAC9B,GAAE;IAAC,UAAU,IAAG,IAAI,CAAC;IAAA,OAAO,IAAG,IAAI,CAAA;CAAC,CAQpC"}
@@ -18,4 +18,20 @@ export function localField(root, id) {
18
18
  throw new Error(`Unknown component field: ${id}`);
19
19
  return found;
20
20
  }
21
+ import {} from '../core.js';
22
+ import { createOperation } from '../operation.js';
23
+ /** Shared form lifetime; business writes and target identity remain with the caller. */
24
+ export function bindFormSubmission(form, options) {
25
+ const abort = new AbortController();
26
+ const operation = createOperation({ unavailable: options.unavailable, onPending: options.onPending, onError: options.onError });
27
+ if (options.onSubmit)
28
+ form.addEventListener('submit', event => {
29
+ event.preventDefault();
30
+ if (operation.pending || options.unavailable() || !form.reportValidity())
31
+ return;
32
+ operation.run(() => { options.onError(''); return options.onSubmit(options.read()); }, result => { if (!result.ok)
33
+ options.onError(result.error); });
34
+ }, { signal: abort.signal });
35
+ return { invalidate() { operation.invalidate(); }, destroy() { operation.destroy(); abort.abort(); } };
36
+ }
21
37
  //# sourceMappingURL=identity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/forms/identity.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAC,IAAiB,EAAE,MAAc;IACxD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,gBAAgB,CAAc,mEAAmE,CAAC;QAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACpK,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC;IACvG,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAc,GAAG,CAAC,CAAC;QAAE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YAC1G,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;YAC3C,IAAI,KAAK;gBAAE,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7F,CAAC;AACH,CAAC;AACD,MAAM,UAAU,UAAU,CAAsC,IAAiB,EAAE,EAAU;IAC3F,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAc,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC3G,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;IAC9D,OAAO,KAAU,CAAC;AACpB,CAAC","sourcesContent":["/** Scope DOM IDs and their references without relying on document-level lookup. */\nexport function scopeIds(root: HTMLElement, prefix: string): void {\n for (const control of root.querySelectorAll<HTMLElement>('input:not([type=\"checkbox\"]):not([type=\"file\"]), textarea, select')) control.classList.add('pui-control');\n if (!prefix) return;\n const attributes = ['id', 'for', 'aria-labelledby', 'aria-describedby', 'aria-controls', 'data-close'];\n for (const node of [root, ...root.querySelectorAll<HTMLElement>('*')]) for (const attribute of attributes) {\n const value = node.getAttribute(attribute);\n if (value) node.setAttribute(attribute, value.split(' ').map(id => prefix + id).join(' '));\n }\n}\nexport function localField<T extends HTMLElement = HTMLElement>(root: HTMLElement, id: string): T {\n const found = [...root.querySelectorAll<HTMLElement>('[id]')].find(node => node.getAttribute('id') === id);\n if (!found) throw new Error(`Unknown component field: ${id}`);\n return found as T;\n}\n"]}
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/forms/identity.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAC,IAAiB,EAAE,MAAc;IACxD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,gBAAgB,CAAc,mEAAmE,CAAC;QAAE,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACpK,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC;IACvG,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAc,GAAG,CAAC,CAAC;QAAE,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YAC1G,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;YAC3C,IAAI,KAAK;gBAAE,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7F,CAAC;AACH,CAAC;AACD,MAAM,UAAU,UAAU,CAAsC,IAAiB,EAAE,EAAU;IAC3F,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAc,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC3G,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC;IAC9D,OAAO,KAAU,CAAC;AACpB,CAAC;AACD,OAAO,EAAmB,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAGhD,wFAAwF;AACxF,MAAM,UAAU,kBAAkB,CAAQ,IAAoB,EAAC,OAM9D;IACC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,eAAe,CAAC,EAAC,WAAW,EAAC,OAAO,CAAC,WAAW,EAAC,SAAS,EAAC,OAAO,CAAC,SAAS,EAAC,OAAO,EAAC,OAAO,CAAC,OAAO,EAAC,CAAC,CAAC;IACzH,IAAI,OAAO,CAAC,QAAQ;QAAE,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAC,KAAK,CAAA,EAAE;YAC1D,KAAK,CAAC,cAAc,EAAE,CAAC;YAAC,IAAI,SAAS,CAAC,OAAO,IAAI,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;gBAAE,OAAO;YACzG,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,GAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,QAAS,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA,CAAC,EAAE,MAAM,CAAC,EAAE,GAAE,IAAI,CAAC,MAAM,CAAC,EAAE;gBAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC,CAAC;QACpJ,CAAC,EAAC,EAAC,MAAM,EAAC,KAAK,CAAC,MAAM,EAAC,CAAC,CAAC;IACzB,OAAO,EAAC,UAAU,KAAI,SAAS,CAAC,UAAU,EAAE,CAAC,CAAA,CAAC,EAAC,OAAO,KAAI,SAAS,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAA,CAAC,EAAC,CAAC;AAClG,CAAC","sourcesContent":["/** Scope DOM IDs and their references without relying on document-level lookup. */\nexport function scopeIds(root: HTMLElement, prefix: string): void {\n for (const control of root.querySelectorAll<HTMLElement>('input:not([type=\"checkbox\"]):not([type=\"file\"]), textarea, select')) control.classList.add('pui-control');\n if (!prefix) return;\n const attributes = ['id', 'for', 'aria-labelledby', 'aria-describedby', 'aria-controls', 'data-close'];\n for (const node of [root, ...root.querySelectorAll<HTMLElement>('*')]) for (const attribute of attributes) {\n const value = node.getAttribute(attribute);\n if (value) node.setAttribute(attribute, value.split(' ').map(id => prefix + id).join(' '));\n }\n}\nexport function localField<T extends HTMLElement = HTMLElement>(root: HTMLElement, id: string): T {\n const found = [...root.querySelectorAll<HTMLElement>('[id]')].find(node => node.getAttribute('id') === id);\n if (!found) throw new Error(`Unknown component field: ${id}`);\n return found as T;\n}\nimport {type ActionResult} from '../core.js';\nimport {createOperation} from '../operation.js';\n\n\n/** Shared form lifetime; business writes and target identity remain with the caller. */\nexport function bindFormSubmission<Value>(form:HTMLFormElement,options:{\n read:()=>Value;\n unavailable:()=>boolean;\n onSubmit?: (value:Value)=>ActionResult|Promise<ActionResult>;\n onPending:(pending:boolean)=>void;\n onError:(error:string)=>void;\n}):{invalidate():void;destroy():void} {\n const abort = new AbortController();\n const operation = createOperation({unavailable:options.unavailable,onPending:options.onPending,onError:options.onError});\n if (options.onSubmit) form.addEventListener('submit',event=>{\n event.preventDefault(); if (operation.pending || options.unavailable() || !form.reportValidity()) return;\n operation.run(() => {options.onError(''); return options.onSubmit!(options.read());}, result => {if (!result.ok) options.onError(result.error);});\n },{signal:abort.signal});\n return {invalidate() {operation.invalidate();},destroy() {operation.destroy(); abort.abort();}};\n}\n"]}
@@ -18,18 +18,23 @@ export interface ModelEditorState {
18
18
  providers: readonly ModelProviderOption[] | null;
19
19
  models: readonly ModelOption[] | null;
20
20
  selection: ModelSelection;
21
+ disabled?: boolean;
21
22
  }
22
23
  export interface ModelSelection {
23
24
  provider: string;
24
25
  model: string;
25
26
  thinkingLevel: string;
26
27
  }
28
+ export interface ModelEditorBlock extends UIBlock<ModelEditorState> {
29
+ read(): ModelSelection;
30
+ reset(selection: ModelSelection): void;
31
+ focus(): void;
32
+ }
27
33
  export declare function thinkingLabel(level: string): string;
34
+ export declare function modelEditorMarkup(prefix: string): string;
28
35
  export declare function createModelEditor(initial: ModelEditorState, options?: {
29
36
  prefix?: string;
30
37
  element?: HTMLElement;
31
38
  onChange?: (selection: ModelSelection) => void;
32
- }): UIBlock<ModelEditorState> & {
33
- read(): ModelSelection;
34
- };
39
+ }): ModelEditorBlock;
35
40
  //# sourceMappingURL=model-editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-editor.d.ts","sourceRoot":"","sources":["../../src/forms/model-editor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,mBAAmB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAC;AACvG,MAAM,WAAW,WAAW;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC;AACvL,MAAM,WAAW,gBAAgB;IAAE,SAAS,EAAE,SAAS,mBAAmB,EAAE,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAC;AACtJ,MAAM,WAAW,cAAc;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAC;AACxF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAuG;AAC3J,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,GAAG;IAAC,IAAI,IAAI,cAAc,CAAA;CAAC,CA2DzN"}
1
+ {"version":3,"file":"model-editor.d.ts","sourceRoot":"","sources":["../../src/forms/model-editor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,mBAAmB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AACD,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1C,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACrB;AACD,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,SAAS,mBAAmB,EAAE,GAAG,IAAI,CAAC;IACjD,MAAM,EAAE,SAAS,WAAW,EAAE,GAAG,IAAI,CAAC;IACtC,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AACD,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,gBAAiB,SAAQ,OAAO,CAAC,gBAAgB,CAAC;IAC/D,IAAI,IAAI,cAAc,CAAC;IACvB,KAAK,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,KAAK,IAAI,IAAI,CAAC;CACjB;AACD,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAuG;AAC3J,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAshC;AAC/kC,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CAC7C,GAAG,gBAAgB,CAqExB"}
@@ -1,6 +1,7 @@
1
1
  import { escape, uniqueId } from '../dom.js';
2
- import { modelEditorMarkup } from './panels.js';
2
+ import { adoptProductParts, reconcileSelectOptions as optionsInto } from '../foundations.js';
3
3
  export function thinkingLabel(level) { return level === 'off' ? 'Thinking off' : `Thinking: ${level === 'xhigh' ? 'extra high' : level}`; }
4
+ export function modelEditorMarkup(prefix) { prefix = escape(prefix); return `<label class="pui-field-label" for="${prefix}-provider">Provider</label><select class="pui-feature-select" id="${prefix}-provider" required disabled><option value="">Loading providers…</option></select><label class="pui-field-label" for="${prefix}-search">Model</label><input id="${prefix}-search" type="search" placeholder="Search by model name or ID" autocomplete="off" /><label class="pui-sr-only" for="${prefix}-model">Choose model</label><select id="${prefix}-model" class="pui-model-list" size="6" required disabled></select><p id="${prefix}-capabilities" class="pui-field-hint" aria-live="polite"></p><label class="pui-field-label" id="${prefix}-thinking-label" for="${prefix}-thinking">Thinking effort</label><select id="${prefix}-thinking" class="pui-feature-select" aria-describedby="${prefix}-capabilities" required></select><p class="pui-field-hint">Built-in models come from the Pith SDK catalog. Custom models use your saved definitions. Availability depends on your endpoint and account.</p>`; }
4
5
  export function createModelEditor(initial, options = {}) {
5
6
  const prefix = options.prefix || uniqueId('pui-model');
6
7
  const element = options.element || document.createElement('div');
@@ -8,71 +9,81 @@ export function createModelEditor(initial, options = {}) {
8
9
  element.className = 'pui-model-editor';
9
10
  element.innerHTML = modelEditorMarkup(prefix);
10
11
  }
11
- const $ = (id) => { const field = Array.from(element.querySelectorAll('[id]')).find((node) => node.getAttribute('id') === id); if (!field)
12
- throw new Error(`Unknown model field: ${id}`); return field; };
13
- let binding = new AbortController();
14
- let destroyed = false;
15
- let signature = '';
16
- const read = () => ({ provider: $(`${prefix}-provider`).value, model: $(`${prefix}-model`).value, thinkingLevel: $(`${prefix}-thinking`).value });
17
- const update = (state) => {
12
+ const field = (id) => {
13
+ const result = Array.from(element.querySelectorAll('[id]')).find(node => node.id === prefix + '-' + id);
14
+ if (!result)
15
+ throw new Error(`Unknown model field: ${id}`);
16
+ return result;
17
+ };
18
+ const provider = field('provider'), model = field('model'), search = field('search'), effort = field('thinking');
19
+ const parts = adoptProductParts(element), abort = new AbortController();
20
+ let destroyed = false, initialized = false, signature = '', selected = '';
21
+ let state = initial;
22
+ const read = () => ({ provider: parts.control(provider).getValue(), model: parts.control(model).getValue(), thinkingLevel: parts.control(effort).getValue() });
23
+ const disabled = () => { parts.control(provider).update({ disabled: !!state.disabled || !state.providers?.length }); parts.control(model).update({ disabled: !!state.disabled || !model.options.length }); parts.control(effort).update({ disabled: !!state.disabled || effort.options.length < 2 }); parts.control(search).update({ disabled: !!state.disabled }); };
24
+ const capabilities = (preferred = effort.value) => {
25
+ selected = model.value;
26
+ const entry = (state.models || []).find(entry => entry.provider === provider.value && entry.id === selected);
27
+ const levels = entry?.thinkingLevels?.length ? entry.thinkingLevels : ['off'];
28
+ optionsInto(effort, levels.map(level => ({ value: level, label: thinkingLabel(level) })));
29
+ parts.control(effort).reset(levels.includes(preferred) ? preferred : levels.includes('high') ? 'high' : levels.includes('medium') ? 'medium' : levels[0]);
30
+ effort.hidden = levels.length < 2;
31
+ field('thinking-label').hidden = effort.hidden;
32
+ field('capabilities').textContent = entry ? `${entry.supportsImages ? 'Text + images' : 'Text only'} · ${new Intl.NumberFormat('en').format(entry.contextWindow)} context tokens · ${new Intl.NumberFormat('en').format(entry.maxTokens)} max output tokens` : 'No matching models.';
33
+ disabled();
34
+ };
35
+ const renderModels = (preferred = effort.value) => {
36
+ const query = search.value.trim().toLowerCase();
37
+ const entries = (state.models || []).filter(entry => entry.provider === provider.value && (entry.id === selected || `${entry.name} ${entry.id}`.toLowerCase().includes(query)));
38
+ optionsInto(model, entries.map(entry => ({ value: entry.id, label: `${entry.name} — ${entry.id}` })));
39
+ parts.control(model).reset(entries.some(entry => entry.id === selected) ? selected : entries[0]?.id || '');
40
+ capabilities(preferred);
41
+ };
42
+ const chooseProvider = (selection) => {
43
+ const entry = state.providers?.find(entry => entry.id === provider.value);
44
+ search.value = '';
45
+ selected = selection && selection.provider === provider.value ? selection.model : entry?.model || '';
46
+ renderModels(selection && selection.provider === provider.value ? selection.thinkingLevel : entry?.thinkingLevel || 'high');
47
+ };
48
+ const reset = (selection) => {
18
49
  if (destroyed)
19
50
  return;
20
- const next = JSON.stringify(state);
21
- if (next === signature)
51
+ parts.control(provider).reset(state.providers?.some(entry => entry.id === selection.provider) ? selection.provider : state.providers?.[0]?.id || '');
52
+ chooseProvider(selection);
53
+ };
54
+ const update = (next) => {
55
+ if (destroyed)
22
56
  return;
23
- signature = next;
24
- binding.abort();
25
- binding = new AbortController();
26
- const catalog = state;
27
- const selection = state.selection;
28
- const providers = catalog.providers || [];
29
- const providerSelect = $(`${prefix}-provider`);
30
- const modelSelect = $(`${prefix}-model`);
31
- const search = $(`${prefix}-search`);
32
- const effort = $(`${prefix}-thinking`);
33
- providerSelect.innerHTML = providers.map((entry) => `<option value="${escape(entry.id)}">${escape(entry.name)}</option>`).join('');
34
- providerSelect.disabled = providers.length === 0;
35
- providerSelect.value = providers.some((entry) => entry.id === selection.provider) ? selection.provider : providers[0]?.id || '';
36
- let selected = '';
37
- const capabilities = () => {
38
- selected = modelSelect.value;
39
- const model = (catalog.models || []).find((entry) => entry.provider === providerSelect.value && entry.id === selected);
40
- const previous = effort.value;
41
- const levels = model?.thinkingLevels || ['off'];
42
- effort.innerHTML = levels.map((level) => `<option value="${escape(level)}">${escape(thinkingLabel(level))}</option>`).join('');
43
- effort.value = levels.includes(previous) ? previous : levels.includes('high') ? 'high' : levels.includes('medium') ? 'medium' : levels[0];
44
- effort.disabled = levels.length < 2;
45
- effort.hidden = levels.length < 2;
46
- $(`${prefix}-thinking-label`).hidden = levels.length < 2;
47
- $(`${prefix}-capabilities`).textContent = model ? `${model.supportsImages ? 'Text + images' : 'Text only'} · ${new Intl.NumberFormat('en').format(model.contextWindow)} context tokens · ${new Intl.NumberFormat('en').format(model.maxTokens)} max output tokens` : 'No matching models.';
48
- };
49
- const renderModels = () => {
50
- const query = search.value.trim().toLowerCase();
51
- const models = (catalog.models || []).filter((entry) => entry.provider === providerSelect.value && (entry.id === selected || `${entry.name} ${entry.id}`.toLowerCase().includes(query)));
52
- modelSelect.innerHTML = models.map((entry) => `<option value="${escape(entry.id)}">${escape(entry.name)} — ${escape(entry.id)}</option>`).join('');
53
- modelSelect.disabled = models.length === 0;
54
- modelSelect.value = models.some((entry) => entry.id === selected) ? selected : models[0]?.id || '';
55
- capabilities();
56
- };
57
- const chooseProvider = () => {
58
- const provider = providers.find((entry) => entry.id === providerSelect.value);
59
- if (!provider)
60
- return;
61
- search.value = '';
62
- selected = provider.model || (provider.id === selection.provider ? selection.model : '');
63
- effort.innerHTML = `<option value="${escape(provider.thinkingLevel || selection.thinkingLevel || 'high')}"></option>`;
64
- renderModels();
65
- };
66
- providerSelect.addEventListener('change', chooseProvider, { signal: binding.signal });
67
- modelSelect.addEventListener('change', capabilities, { signal: binding.signal });
68
- search.addEventListener('input', renderModels, { signal: binding.signal });
69
- chooseProvider();
70
- if (options.onChange)
71
- element.addEventListener('change', () => options.onChange(read()), { signal: binding.signal });
57
+ const draft = read();
58
+ state = next;
59
+ const nextSignature = JSON.stringify([next.providers, next.models]);
60
+ if (nextSignature !== signature) {
61
+ signature = nextSignature;
62
+ optionsInto(provider, (state.providers || []).map(entry => ({ value: entry.id, label: entry.name })));
63
+ if (!initialized || !draft.provider)
64
+ reset(next.selection);
65
+ else {
66
+ parts.control(provider).reset(state.providers?.some(entry => entry.id === draft.provider) ? draft.provider : state.providers?.[0]?.id || '');
67
+ selected = draft.model;
68
+ renderModels(draft.thinkingLevel);
69
+ }
70
+ initialized = true;
71
+ }
72
+ disabled();
72
73
  };
74
+ provider.addEventListener('change', () => chooseProvider(), { signal: abort.signal });
75
+ model.addEventListener('change', () => capabilities(), { signal: abort.signal });
76
+ search.addEventListener('input', () => renderModels(), { signal: abort.signal });
77
+ element.addEventListener('change', () => options.onChange?.(read()), { signal: abort.signal });
73
78
  update(initial);
74
- return { element, update, read, destroy() { if (destroyed)
75
- return; destroyed = true; binding.abort(); if (!options.element)
76
- element.remove(); } };
79
+ return { element, update, read, reset, focus() { parts.control(search).focus(); }, destroy() {
80
+ if (destroyed)
81
+ return;
82
+ destroyed = true;
83
+ abort.abort();
84
+ parts.destroy();
85
+ if (!options.element)
86
+ element.remove();
87
+ } };
77
88
  }
78
89
  //# sourceMappingURL=model-editor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"model-editor.js","sourceRoot":"","sources":["../../src/forms/model-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nimport { modelEditorMarkup } from './panels.js';\nexport interface ModelProviderOption {id: string; name: string; model?: string; thinkingLevel?: string}\nexport interface ModelOption {id: string; name: string; provider: string; thinkingLevels?: readonly string[] | null; supportsImages: boolean; contextWindow: number; maxTokens: number}\nexport interface ModelEditorState {providers: readonly ModelProviderOption[] | null; models: readonly ModelOption[] | null; selection: ModelSelection}\nexport interface ModelSelection {provider: string; model: string; thinkingLevel: string}\nexport function thinkingLabel(level: string): string { return level === 'off' ? 'Thinking off' : `Thinking: ${level === 'xhigh' ? 'extra high' : level}`; }\nexport function createModelEditor(initial: ModelEditorState, options: {prefix?: string; element?: HTMLElement; onChange?: (selection: ModelSelection) => void} = {}): UIBlock<ModelEditorState> & {read(): ModelSelection} {\n const prefix = options.prefix || uniqueId('pui-model');\n const element = options.element || document.createElement('div');\n if (!options.element) {element.className = 'pui-model-editor'; element.innerHTML = modelEditorMarkup(prefix);}\n const $ = <T extends HTMLElement = HTMLElement>(id: string) => {const field = Array.from(element.querySelectorAll<HTMLElement>('[id]')).find((node) => node.getAttribute('id') === id); if (!field) throw new Error(`Unknown model field: ${id}`); return field as T;};\n let binding = new AbortController(); let destroyed = false; let signature = '';\n const read = (): ModelSelection => ({provider: $<HTMLSelectElement>(`${prefix}-provider`).value, model: $<HTMLSelectElement>(`${prefix}-model`).value, thinkingLevel: $<HTMLSelectElement>(`${prefix}-thinking`).value});\n const update = (state: Readonly<ModelEditorState>) => {\n if (destroyed) return;\n const next = JSON.stringify(state); if (next === signature) return; signature = next;\n binding.abort(); binding = new AbortController();\n const catalog = state; const selection = state.selection;\n const providers = catalog.providers || [];\n const providerSelect = $<HTMLSelectElement>(`${prefix}-provider`);\n const modelSelect = $<HTMLSelectElement>(`${prefix}-model`);\n const search = $<HTMLInputElement>(`${prefix}-search`);\n const effort = $<HTMLSelectElement>(`${prefix}-thinking`);\n providerSelect.innerHTML = providers.map((entry) => `<option value=\"${escape(entry.id)}\">${escape(entry.name)}</option>`).join('');\n providerSelect.disabled = providers.length === 0;\n providerSelect.value = providers.some((entry) => entry.id === selection.provider) ? selection.provider! : providers[0]?.id || '';\n let selected = '';\n const capabilities = () => {\n selected = modelSelect.value;\n const model = (catalog.models || []).find((entry) => entry.provider === providerSelect.value && entry.id === selected);\n const previous = effort.value;\n const levels = model?.thinkingLevels || ['off'];\n effort.innerHTML = levels.map((level) => `<option value=\"${escape(level)}\">${escape(thinkingLabel(level))}</option>`).join('');\n effort.value = levels.includes(previous) ? previous : levels.includes('high') ? 'high' : levels.includes('medium') ? 'medium' : levels[0];\n effort.disabled = levels.length < 2;\n effort.hidden = levels.length < 2;\n $(`${prefix}-thinking-label`).hidden = levels.length < 2;\n $(`${prefix}-capabilities`).textContent = model ? `${model.supportsImages ? 'Text + images' : 'Text only'} · ${new Intl.NumberFormat('en').format(model.contextWindow)} context tokens · ${new Intl.NumberFormat('en').format(model.maxTokens)} max output tokens` : 'No matching models.';\n };\n const renderModels = () => {\n const query = search.value.trim().toLowerCase();\n const models = (catalog.models || []).filter((entry) => entry.provider === providerSelect.value && (entry.id === selected || `${entry.name} ${entry.id}`.toLowerCase().includes(query)));\n modelSelect.innerHTML = models.map((entry) => `<option value=\"${escape(entry.id)}\">${escape(entry.name)} — ${escape(entry.id)}</option>`).join('');\n modelSelect.disabled = models.length === 0;\n modelSelect.value = models.some((entry) => entry.id === selected) ? selected : models[0]?.id || '';\n capabilities();\n };\n const chooseProvider = () => {\n const provider = providers.find((entry) => entry.id === providerSelect.value);\n if (!provider)\n return;\n search.value = '';\n selected = provider.model || (provider.id === selection.provider ? selection.model : '');\n effort.innerHTML = `<option value=\"${escape(provider.thinkingLevel || selection.thinkingLevel || 'high')}\"></option>`;\n renderModels();\n };\n providerSelect.addEventListener('change', chooseProvider, {signal: binding.signal});\n modelSelect.addEventListener('change', capabilities, {signal: binding.signal});\n search.addEventListener('input', renderModels, {signal: binding.signal});\n chooseProvider();\n\n if (options.onChange) element.addEventListener('change', () => options.onChange!(read()), {signal: binding.signal});\n };\n update(initial);\n return {element, update, read, destroy() {if (destroyed) return; destroyed = true; binding.abort(); if (!options.element) element.remove();}};\n}\n"]}
1
+ {"version":3,"file":"model-editor.js","sourceRoot":"","sources":["../../src/forms/model-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nimport {adoptProductParts,reconcileSelectOptions as optionsInto} from '../foundations.js';\nexport interface ModelProviderOption {\n id: string;\n name: string;\n model?: string;\n thinkingLevel?: string;\n}\nexport interface ModelOption {\n id: string;\n name: string;\n provider: string;\n thinkingLevels?: readonly string[] | null;\n supportsImages: boolean;\n contextWindow: number;\n maxTokens: number;\n}\nexport interface ModelEditorState {\n providers: readonly ModelProviderOption[] | null;\n models: readonly ModelOption[] | null;\n selection: ModelSelection;\n disabled?: boolean;\n}\nexport interface ModelSelection {\n provider: string;\n model: string;\n thinkingLevel: string;\n}\nexport interface ModelEditorBlock extends UIBlock<ModelEditorState> {\n read(): ModelSelection;\n reset(selection: ModelSelection): void;\n focus(): void;\n}\nexport function thinkingLabel(level: string): string { return level === 'off' ? 'Thinking off' : `Thinking: ${level === 'xhigh' ? 'extra high' : level}`; }\nexport function modelEditorMarkup(prefix: string): string { prefix = escape(prefix); return `<label class=\"pui-field-label\" for=\"${prefix}-provider\">Provider</label><select class=\"pui-feature-select\" id=\"${prefix}-provider\" required disabled><option value=\"\">Loading providers…</option></select><label class=\"pui-field-label\" for=\"${prefix}-search\">Model</label><input id=\"${prefix}-search\" type=\"search\" placeholder=\"Search by model name or ID\" autocomplete=\"off\" /><label class=\"pui-sr-only\" for=\"${prefix}-model\">Choose model</label><select id=\"${prefix}-model\" class=\"pui-model-list\" size=\"6\" required disabled></select><p id=\"${prefix}-capabilities\" class=\"pui-field-hint\" aria-live=\"polite\"></p><label class=\"pui-field-label\" id=\"${prefix}-thinking-label\" for=\"${prefix}-thinking\">Thinking effort</label><select id=\"${prefix}-thinking\" class=\"pui-feature-select\" aria-describedby=\"${prefix}-capabilities\" required></select><p class=\"pui-field-hint\">Built-in models come from the Pith SDK catalog. Custom models use your saved definitions. Availability depends on your endpoint and account.</p>`; }\nexport function createModelEditor(initial: ModelEditorState, options: {\n prefix?: string;\n element?: HTMLElement;\n onChange?: (selection: ModelSelection) => void;\n} = {}): ModelEditorBlock {\n const prefix = options.prefix || uniqueId('pui-model');\n const element = options.element || document.createElement('div');\n if (!options.element) {\n element.className = 'pui-model-editor';\n element.innerHTML = modelEditorMarkup(prefix);\n }\n const field = <T extends HTMLElement = HTMLElement>(id: string) => { const result = Array.from(element.querySelectorAll<HTMLElement>('[id]')).find(node => node.id === prefix + '-' + id); if (!result)\n throw new Error(`Unknown model field: ${id}`); return result as T; };\n const provider = field<HTMLSelectElement>('provider'), model = field<HTMLSelectElement>('model'), search = field<HTMLInputElement>('search'), effort = field<HTMLSelectElement>('thinking');\n const parts = adoptProductParts(element), abort = new AbortController();\n let destroyed = false, initialized = false, signature = '', selected = '';\n let state: Readonly<ModelEditorState> = initial;\n const read = (): ModelSelection => ({ provider: parts.control(provider).getValue(), model: parts.control(model).getValue(), thinkingLevel: parts.control(effort).getValue() });\n const disabled = () => { parts.control(provider).update({ disabled: !!state.disabled || !state.providers?.length }); parts.control(model).update({ disabled: !!state.disabled || !model.options.length }); parts.control(effort).update({ disabled: !!state.disabled || effort.options.length < 2 }); parts.control(search).update({disabled:!!state.disabled}); };\n const capabilities = (preferred = effort.value) => {\n selected = model.value;\n const entry = (state.models || []).find(entry => entry.provider === provider.value && entry.id === selected);\n const levels = entry?.thinkingLevels?.length ? entry.thinkingLevels : ['off'];\n optionsInto(effort, levels.map(level => ({ value: level, label: thinkingLabel(level) })));\n parts.control(effort).reset(levels.includes(preferred) ? preferred : levels.includes('high') ? 'high' : levels.includes('medium') ? 'medium' : levels[0]);\n effort.hidden = levels.length < 2;\n field('thinking-label').hidden = effort.hidden;\n field('capabilities').textContent = entry ? `${entry.supportsImages ? 'Text + images' : 'Text only'} · ${new Intl.NumberFormat('en').format(entry.contextWindow)} context tokens · ${new Intl.NumberFormat('en').format(entry.maxTokens)} max output tokens` : 'No matching models.';\n disabled();\n };\n const renderModels = (preferred = effort.value) => {\n const query = search.value.trim().toLowerCase();\n const entries = (state.models || []).filter(entry => entry.provider === provider.value && (entry.id === selected || `${entry.name} ${entry.id}`.toLowerCase().includes(query)));\n optionsInto(model, entries.map(entry => ({ value: entry.id, label: `${entry.name} — ${entry.id}` })));\n parts.control(model).reset(entries.some(entry => entry.id === selected) ? selected : entries[0]?.id || '');\n capabilities(preferred);\n };\n const chooseProvider = (selection?: ModelSelection) => {\n const entry = state.providers?.find(entry => entry.id === provider.value);\n search.value = '';\n selected = selection && selection.provider === provider.value ? selection.model : entry?.model || '';\n renderModels(selection && selection.provider === provider.value ? selection.thinkingLevel : entry?.thinkingLevel || 'high');\n };\n const reset = (selection: ModelSelection) => { if (destroyed)\n return; parts.control(provider).reset(state.providers?.some(entry => entry.id === selection.provider) ? selection.provider : state.providers?.[0]?.id || ''); chooseProvider(selection); };\n const update = (next: Readonly<ModelEditorState>) => {\n if (destroyed)\n return;\n const draft = read();\n state = next;\n const nextSignature = JSON.stringify([next.providers, next.models]);\n if (nextSignature !== signature) {\n signature = nextSignature;\n optionsInto(provider, (state.providers || []).map(entry => ({ value: entry.id, label: entry.name })));\n if (!initialized || !draft.provider)\n reset(next.selection);\n else {\n parts.control(provider).reset(state.providers?.some(entry => entry.id === draft.provider) ? draft.provider : state.providers?.[0]?.id || '');\n selected = draft.model;\n renderModels(draft.thinkingLevel);\n }\n initialized = true;\n }\n disabled();\n };\n provider.addEventListener('change', () => chooseProvider(), { signal: abort.signal });\n model.addEventListener('change', () => capabilities(), { signal: abort.signal });\n search.addEventListener('input', () => renderModels(), { signal: abort.signal });\n element.addEventListener('change', () => options.onChange?.(read()), { signal: abort.signal });\n update(initial);\n return { element, update, read, reset, focus() { parts.control(search).focus(); }, destroy() { if (destroyed)\n return; destroyed = true; abort.abort(); parts.destroy(); if (!options.element)\n element.remove(); } };\n}\n"]}