pith-ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/AI.md +67 -0
  2. package/API.md +3281 -0
  3. package/LICENSE +661 -0
  4. package/README.md +80 -2
  5. package/dist/composer.d.ts +38 -0
  6. package/dist/composer.d.ts.map +1 -0
  7. package/dist/composer.js +114 -0
  8. package/dist/composer.js.map +1 -0
  9. package/dist/content/conversation-reader.d.ts +19 -0
  10. package/dist/content/conversation-reader.d.ts.map +1 -0
  11. package/dist/content/conversation-reader.js +280 -0
  12. package/dist/content/conversation-reader.js.map +1 -0
  13. package/dist/content/conversation-view.d.ts +48 -0
  14. package/dist/content/conversation-view.d.ts.map +1 -0
  15. package/dist/content/conversation-view.js +107 -0
  16. package/dist/content/conversation-view.js.map +1 -0
  17. package/dist/content/image-viewer.d.ts +23 -0
  18. package/dist/content/image-viewer.d.ts.map +1 -0
  19. package/dist/content/image-viewer.js +101 -0
  20. package/dist/content/image-viewer.js.map +1 -0
  21. package/dist/content/markdown.d.ts +12 -0
  22. package/dist/content/markdown.d.ts.map +1 -0
  23. package/dist/content/markdown.js +68 -0
  24. package/dist/content/markdown.js.map +1 -0
  25. package/dist/content/paged-preview.d.ts +15 -0
  26. package/dist/content/paged-preview.d.ts.map +1 -0
  27. package/dist/content/paged-preview.js +105 -0
  28. package/dist/content/paged-preview.js.map +1 -0
  29. package/dist/content/views.d.ts +9 -0
  30. package/dist/content/views.d.ts.map +1 -0
  31. package/dist/content/views.js +53 -0
  32. package/dist/content/views.js.map +1 -0
  33. package/dist/content/welcome.d.ts +18 -0
  34. package/dist/content/welcome.d.ts.map +1 -0
  35. package/dist/content/welcome.js +45 -0
  36. package/dist/content/welcome.js.map +1 -0
  37. package/dist/core.d.ts +36 -0
  38. package/dist/core.d.ts.map +1 -0
  39. package/dist/core.js +44 -0
  40. package/dist/core.js.map +1 -0
  41. package/dist/dom.d.ts +14 -0
  42. package/dist/dom.d.ts.map +1 -0
  43. package/dist/dom.js +69 -0
  44. package/dist/dom.js.map +1 -0
  45. package/dist/files/panels.d.ts +95 -0
  46. package/dist/files/panels.d.ts.map +1 -0
  47. package/dist/files/panels.js +245 -0
  48. package/dist/files/panels.js.map +1 -0
  49. package/dist/files/systems.d.ts +91 -0
  50. package/dist/files/systems.d.ts.map +1 -0
  51. package/dist/files/systems.js +256 -0
  52. package/dist/files/systems.js.map +1 -0
  53. package/dist/forms/actions.d.ts +27 -0
  54. package/dist/forms/actions.d.ts.map +1 -0
  55. package/dist/forms/actions.js +19 -0
  56. package/dist/forms/actions.js.map +1 -0
  57. package/dist/forms/custom-connection.d.ts +61 -0
  58. package/dist/forms/custom-connection.d.ts.map +1 -0
  59. package/dist/forms/custom-connection.js +65 -0
  60. package/dist/forms/custom-connection.js.map +1 -0
  61. package/dist/forms/dialog-frame.d.ts +39 -0
  62. package/dist/forms/dialog-frame.d.ts.map +1 -0
  63. package/dist/forms/dialog-frame.js +75 -0
  64. package/dist/forms/dialog-frame.js.map +1 -0
  65. package/dist/forms/history.d.ts +16 -0
  66. package/dist/forms/history.d.ts.map +1 -0
  67. package/dist/forms/history.js +21 -0
  68. package/dist/forms/history.js.map +1 -0
  69. package/dist/forms/identity.d.ts +4 -0
  70. package/dist/forms/identity.d.ts.map +1 -0
  71. package/dist/forms/identity.js +21 -0
  72. package/dist/forms/identity.js.map +1 -0
  73. package/dist/forms/model-editor.d.ts +35 -0
  74. package/dist/forms/model-editor.d.ts.map +1 -0
  75. package/dist/forms/model-editor.js +78 -0
  76. package/dist/forms/model-editor.js.map +1 -0
  77. package/dist/forms/panels.d.ts +57 -0
  78. package/dist/forms/panels.d.ts.map +1 -0
  79. package/dist/forms/panels.js +224 -0
  80. package/dist/forms/panels.js.map +1 -0
  81. package/dist/forms/provider-login.d.ts +22 -0
  82. package/dist/forms/provider-login.d.ts.map +1 -0
  83. package/dist/forms/provider-login.js +114 -0
  84. package/dist/forms/provider-login.js.map +1 -0
  85. package/dist/forms/resource-editor.d.ts +23 -0
  86. package/dist/forms/resource-editor.d.ts.map +1 -0
  87. package/dist/forms/resource-editor.js +24 -0
  88. package/dist/forms/resource-editor.js.map +1 -0
  89. package/dist/index.d.ts +28 -0
  90. package/dist/index.d.ts.map +1 -0
  91. package/dist/index.js +28 -0
  92. package/dist/index.js.map +1 -0
  93. package/dist/navigation/context-menu.d.ts +25 -0
  94. package/dist/navigation/context-menu.d.ts.map +1 -0
  95. package/dist/navigation/context-menu.js +67 -0
  96. package/dist/navigation/context-menu.js.map +1 -0
  97. package/dist/navigation/navigation.d.ts +57 -0
  98. package/dist/navigation/navigation.d.ts.map +1 -0
  99. package/dist/navigation/navigation.js +185 -0
  100. package/dist/navigation/navigation.js.map +1 -0
  101. package/dist/primitives.d.ts +307 -0
  102. package/dist/primitives.d.ts.map +1 -0
  103. package/dist/primitives.js +741 -0
  104. package/dist/primitives.js.map +1 -0
  105. package/dist/styles.css +849 -0
  106. package/dist/tasks/approval.d.ts +25 -0
  107. package/dist/tasks/approval.d.ts.map +1 -0
  108. package/dist/tasks/approval.js +61 -0
  109. package/dist/tasks/approval.js.map +1 -0
  110. package/dist/tasks/permission.d.ts +35 -0
  111. package/dist/tasks/permission.d.ts.map +1 -0
  112. package/dist/tasks/permission.js +67 -0
  113. package/dist/tasks/permission.js.map +1 -0
  114. package/dist/tasks/runtime.d.ts +31 -0
  115. package/dist/tasks/runtime.d.ts.map +1 -0
  116. package/dist/tasks/runtime.js +34 -0
  117. package/dist/tasks/runtime.js.map +1 -0
  118. package/dist/theme.d.ts +4 -0
  119. package/dist/theme.d.ts.map +1 -0
  120. package/dist/theme.js +6 -0
  121. package/dist/theme.js.map +1 -0
  122. package/dist/workspace.d.ts +38 -0
  123. package/dist/workspace.d.ts.map +1 -0
  124. package/dist/workspace.js +183 -0
  125. package/dist/workspace.js.map +1 -0
  126. package/package.json +27 -4
@@ -0,0 +1,256 @@
1
+ import { icon, setText } from '../dom.js';
2
+ const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 6 });
3
+ const numbers = new Intl.NumberFormat();
4
+ export function formatCost(cost, latestTask = false) {
5
+ if (cost?.unavailable)
6
+ return 'Unavailable';
7
+ const count = latestTask ? cost?.runRequests : cost?.requestCount;
8
+ const unknown = (latestTask ? cost?.runUnknownRequests : cost?.unknownRequests) || 0;
9
+ if (!count)
10
+ return 'Not recorded';
11
+ if (count === unknown)
12
+ return 'Unknown';
13
+ return `~${currency.format((latestTask ? cost?.runTotal : cost?.total) || 0)}${unknown ? ' (partial)' : ''}`;
14
+ }
15
+ export function createCostLedger(options = {}) {
16
+ const element = options.container || document.createElement('div');
17
+ const rows = new Map();
18
+ let table;
19
+ let note;
20
+ let empty;
21
+ let destroyed = false;
22
+ function clear() { rows.clear(); table = undefined; note = undefined; empty = undefined; element.replaceChildren(); }
23
+ function ensure() {
24
+ if (table)
25
+ return;
26
+ element.innerHTML = '<p class="pui-cost-note"></p><div class="pui-cost-table"><table><thead><tr><th>Request</th><th>Input</th><th>Output</th><th>Cache read / write</th><th>Estimate</th></tr></thead><tbody></tbody></table></div>';
27
+ table = element.querySelector('tbody');
28
+ note = element.querySelector('.pui-cost-note');
29
+ empty = document.createElement('tr');
30
+ const cell = document.createElement('td');
31
+ cell.colSpan = 5;
32
+ cell.textContent = 'No recorded requests yet.';
33
+ empty.append(cell);
34
+ }
35
+ function makeRow(id) {
36
+ const row = document.createElement('tr');
37
+ row.innerHTML = '<td><strong data-purpose></strong><br><span data-identity></span><br><small data-time></small><details><summary>Price source and rates (USD / million tokens)</summary><p data-source></p><pre data-price></pre><p data-reasoning hidden></p></details></td><td></td><td></td><td></td><td></td>';
38
+ const details = row.querySelector('details');
39
+ details.dataset.costId = id;
40
+ return { element: row, purpose: row.querySelector('[data-purpose]'), identity: row.querySelector('[data-identity]'), time: row.querySelector('[data-time]'), details, source: row.querySelector('[data-source]'), price: row.querySelector('[data-price]'), reasoning: row.querySelector('[data-reasoning]'), cells: [...row.querySelectorAll('td')] };
41
+ }
42
+ return {
43
+ element,
44
+ clear() { if (!destroyed)
45
+ clear(); },
46
+ loading() { if (!destroyed && !element.childElementCount)
47
+ element.textContent = 'Loading request breakdown…'; },
48
+ setError(message) { if (!destroyed) {
49
+ clear();
50
+ element.textContent = message;
51
+ } },
52
+ update(report) {
53
+ if (destroyed)
54
+ return;
55
+ const focused = document.activeElement;
56
+ ensure();
57
+ setText(note, `USD estimates for recorded requests, not a provider bill. Prices are saved per request; retries and context summaries are included when usage is reported. ${report.unknownRequests ? `${report.unknownRequests} request(s) have unknown prices or usage and are excluded. ` : ''}Older requests made before recording began are not reconstructed.`);
58
+ const requests = report.requests || [];
59
+ const keep = new Set(requests.map(request => request.id));
60
+ for (const [id, row] of rows)
61
+ if (!keep.has(id)) {
62
+ row.element.remove();
63
+ rows.delete(id);
64
+ }
65
+ if (!requests.length) {
66
+ if (!empty.parentElement)
67
+ table.append(empty);
68
+ return;
69
+ }
70
+ empty.remove();
71
+ let cursor = table.firstElementChild;
72
+ const n = (value) => numbers.format(value || 0);
73
+ for (const request of requests) {
74
+ let row = rows.get(request.id);
75
+ if (!row) {
76
+ row = makeRow(request.id);
77
+ rows.set(request.id, row);
78
+ }
79
+ setText(row.purpose, `${request.purpose} · ${request.status}`);
80
+ setText(row.identity, `${request.providerName || request.provider} / ${request.modelName || request.model}`);
81
+ setText(row.time, request.time);
82
+ setText(row.source, request.source);
83
+ setText(row.price, JSON.stringify(request.price, null, 2));
84
+ row.reasoning.hidden = request.usage.reasoning == null;
85
+ setText(row.reasoning, request.usage.reasoning == null ? '' : `Reasoning tokens: ${n(request.usage.reasoning)} (included in output)`);
86
+ setText(row.cells[1], n(request.usage.input));
87
+ setText(row.cells[2], n(request.usage.output));
88
+ setText(row.cells[3], `${n(request.usage.cacheRead)} / ${n(request.usage.cacheWrite)}`);
89
+ setText(row.cells[4], request.known ? currency.format(request.usage.cost.total) : 'Unknown');
90
+ if (row.element !== cursor)
91
+ table.insertBefore(row.element, cursor);
92
+ cursor = row.element.nextElementSibling;
93
+ }
94
+ if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused)
95
+ focused.focus({ preventScroll: true });
96
+ },
97
+ destroy() { if (!destroyed)
98
+ clear(); destroyed = true; },
99
+ };
100
+ }
101
+ export function createUpdatePanel(options = {}) {
102
+ const element = options.container || document.createElement('section');
103
+ if (!element.querySelector('[data-update-version]'))
104
+ element.innerHTML = '<p data-update-version></p><p data-update-message role="status"></p><div class="pui-feature-toolbar"><button type="button" class="pui-secondary-button" data-update-check>Check for updates</button><button type="button" class="pui-secondary-button" data-update-install hidden>Update and restart</button></div><progress max="100" hidden></progress>';
105
+ const version = element.querySelector('[data-update-version]');
106
+ const message = element.querySelector('[data-update-message]');
107
+ const check = element.querySelector('[data-update-check]');
108
+ const install = element.querySelector('[data-update-install]');
109
+ const progress = element.querySelector('progress');
110
+ let state;
111
+ let pending = false, destroyed = false;
112
+ let error = '';
113
+ let retryCheck = false;
114
+ const checkAction = () => options.onCheck?.(), installAction = () => options.onInstall?.();
115
+ check.addEventListener('click', checkAction);
116
+ install.addEventListener('click', installAction);
117
+ function render() {
118
+ if (destroyed)
119
+ return;
120
+ if (!state) {
121
+ check.disabled = install.disabled = pending;
122
+ message.textContent = error;
123
+ return;
124
+ }
125
+ version.textContent = `${options.productName || 'Application'} ${state.version}`;
126
+ message.textContent = error || state.message;
127
+ const busy = ['checking', 'installing', 'restarting'].includes(state.phase);
128
+ check.disabled = !state.enabled || (busy && !retryCheck) || pending;
129
+ install.hidden = !state.availableVersion;
130
+ install.disabled = busy || pending;
131
+ install.textContent = 'Update and restart';
132
+ progress.hidden = state.phase !== 'installing';
133
+ progress.value = state.progress;
134
+ }
135
+ return {
136
+ element,
137
+ update(value) { state = value; error = ''; retryCheck = false; render(); },
138
+ setPending(value) { pending = value; render(); },
139
+ setError(value, options = {}) { if (!destroyed) {
140
+ error = value;
141
+ retryCheck = !!options.allowCheckRetry;
142
+ render();
143
+ } },
144
+ destroy() { if (destroyed)
145
+ return; destroyed = true; check.removeEventListener('click', checkAction); install.removeEventListener('click', installAction); },
146
+ };
147
+ }
148
+ export function historyStatusMarkup(status) {
149
+ if (status === 'approval')
150
+ return '<span class="pui-history-status pui-needs-approval" role="img" aria-label="Waiting for approval" title="Waiting for approval"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9"/><path d="M12 7v6m0 4h.01"/></svg></span>';
151
+ if (status === 'running')
152
+ return '<span class="pui-history-status" role="img" aria-label="Running" title="Running"><i class="pui-history-spinner" aria-hidden="true"></i></span>';
153
+ if (status === 'unread')
154
+ return '<span class="pui-history-status" role="img" aria-label="Completed, unread" title="Completed, unread"><i class="pui-history-unread" aria-hidden="true"></i></span>';
155
+ return '<span class="pui-history-status" aria-hidden="true"></span>';
156
+ }
157
+ export function createCompletionNotices(options = {}) {
158
+ const element = options.container || document.createElement('div');
159
+ const entries = new Map();
160
+ let destroyed = false;
161
+ const click = (event) => {
162
+ const target = event.target.closest('[data-conversation], [data-dismiss-completion]');
163
+ if (!target || !element.contains(target))
164
+ return;
165
+ if (target.dataset.conversation)
166
+ options.onOpen?.(target.dataset.conversation);
167
+ else if (target.dataset.dismissCompletion)
168
+ options.onDismiss?.(target.dataset.dismissCompletion);
169
+ };
170
+ if (options.onOpen || options.onDismiss)
171
+ element.addEventListener('click', click);
172
+ return {
173
+ element,
174
+ update(notices) {
175
+ if (destroyed)
176
+ return;
177
+ const focused = document.activeElement;
178
+ const keep = new Set(notices.map(notice => notice.runID));
179
+ for (const [id, entry] of entries)
180
+ if (!keep.has(id)) {
181
+ entry.element.remove();
182
+ entries.delete(id);
183
+ }
184
+ let cursor = element.firstElementChild;
185
+ for (const notice of notices) {
186
+ let entry = entries.get(notice.runID);
187
+ if (!entry) {
188
+ const article = document.createElement('article');
189
+ article.className = 'pui-completion-notice';
190
+ article.innerHTML = `<span class="pui-completion-check" aria-hidden="true">${icon('check')}</span><div class="pui-completion-content"><strong>Task completed</strong><p></p><button class="pui-text-button" type="button" data-conversation>Open conversation</button></div><button class="pui-quiet-icon pui-completion-dismiss" type="button" data-dismiss-completion aria-label="Dismiss completion notification">${icon('close')}</button>`;
191
+ entry = { element: article, text: article.querySelector('p'), open: article.querySelector('[data-conversation]'), dismiss: article.querySelector('[data-dismiss-completion]') };
192
+ entries.set(notice.runID, entry);
193
+ }
194
+ setText(entry.text, `${notice.workspace} · ${notice.title}`);
195
+ if (entry.open.dataset.conversation !== notice.id)
196
+ entry.open.dataset.conversation = notice.id;
197
+ if (entry.dismiss.dataset.dismissCompletion !== notice.runID)
198
+ entry.dismiss.dataset.dismissCompletion = notice.runID;
199
+ if (entry.element !== cursor)
200
+ element.insertBefore(entry.element, cursor);
201
+ cursor = entry.element.nextElementSibling;
202
+ }
203
+ if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused)
204
+ focused.focus({ preventScroll: true });
205
+ },
206
+ destroy() { if (destroyed)
207
+ return; destroyed = true; entries.clear(); element.removeEventListener?.('click', click); element.replaceChildren(); },
208
+ };
209
+ }
210
+ export function createCopyFeedback(options) {
211
+ let destroyed = false;
212
+ const timers = new Map();
213
+ return {
214
+ async copy(button, action, announcement) {
215
+ if (destroyed || button.dataset.copyPending === 'true')
216
+ return;
217
+ const original = button.innerHTML, label = button.getAttribute('aria-label'), title = button.title;
218
+ options.status.textContent = '';
219
+ button.dataset.copyPending = 'true';
220
+ button.disabled = true;
221
+ try {
222
+ await action();
223
+ if (destroyed)
224
+ return;
225
+ options.status.textContent = announcement;
226
+ if (!button.isConnected)
227
+ return;
228
+ button.innerHTML = button.classList.contains('pui-message-action') ? icon('check') : 'Copied';
229
+ button.setAttribute('aria-label', announcement);
230
+ button.title = announcement;
231
+ timers.set(button, setTimeout(() => {
232
+ timers.delete(button);
233
+ if (destroyed || !button.isConnected)
234
+ return;
235
+ button.innerHTML = original;
236
+ if (label === null)
237
+ button.removeAttribute('aria-label');
238
+ else
239
+ button.setAttribute('aria-label', label);
240
+ button.title = title;
241
+ delete button.dataset.copyPending;
242
+ button.disabled = options.isDisabled(button);
243
+ }, 1500));
244
+ }
245
+ catch (error) {
246
+ if (destroyed)
247
+ return;
248
+ delete button.dataset.copyPending;
249
+ button.disabled = false;
250
+ options.onError(error);
251
+ }
252
+ },
253
+ destroy() { destroyed = true; timers.forEach(clearTimeout); timers.clear(); },
254
+ };
255
+ }
256
+ //# sourceMappingURL=systems.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"systems.js","sourceRoot":"","sources":["../../src/files/systems.ts"],"names":[],"mappings":"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","sourcesContent":["import { icon, setText } from '../dom.js';\nimport type { UIBlock } from '../core.js';\n\nconst currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 6 });\nconst numbers = new Intl.NumberFormat();\nexport interface CostSummaryDisplay { unavailable?: boolean; requestCount: number; unknownRequests: number; runRequests: number; runUnknownRequests: number; total: number; runTotal: number }\nexport function formatCost(cost: CostSummaryDisplay | undefined, latestTask = false): string {\n if (cost?.unavailable) return 'Unavailable';\n const count = latestTask ? cost?.runRequests : cost?.requestCount;\n const unknown = (latestTask ? cost?.runUnknownRequests : cost?.unknownRequests) || 0;\n if (!count) return 'Not recorded';\n if (count === unknown) return 'Unknown';\n return `~${currency.format((latestTask ? cost?.runTotal : cost?.total) || 0)}${unknown ? ' (partial)' : ''}`;\n}\nexport interface CostRequestDisplay {\n id: string; purpose: string; status: string; provider: string; providerName?: string; model: string; modelName?: string;\n time: string; source: string; price: unknown; known: boolean;\n usage: { input: number; output: number; cacheRead: number; cacheWrite: number; reasoning?: number; cost: { total: number } };\n}\nexport interface CostLedgerState { unknownRequests: number; requests: readonly CostRequestDisplay[] | null }\nexport function createCostLedger(options: { container?: HTMLElement } = {}) {\n const element = options.container || document.createElement('div');\n type Row = { element: HTMLTableRowElement; purpose: HTMLElement; identity: HTMLElement; time: HTMLElement; details: HTMLDetailsElement; source: HTMLElement; price: HTMLElement; reasoning: HTMLElement; cells: HTMLTableCellElement[] };\n const rows = new Map<string, Row>();\n let table: HTMLTableSectionElement | undefined;\n let note: HTMLElement | undefined;\n let empty: HTMLTableRowElement | undefined;\n let destroyed = false;\n function clear(): void { rows.clear(); table = undefined; note = undefined; empty = undefined; element.replaceChildren(); }\n function ensure(): void {\n if (table) return;\n element.innerHTML = '<p class=\"pui-cost-note\"></p><div class=\"pui-cost-table\"><table><thead><tr><th>Request</th><th>Input</th><th>Output</th><th>Cache read / write</th><th>Estimate</th></tr></thead><tbody></tbody></table></div>';\n table = element.querySelector('tbody')!; note = element.querySelector<HTMLElement>('.pui-cost-note')!;\n empty = document.createElement('tr'); const cell = document.createElement('td'); cell.colSpan = 5; cell.textContent = 'No recorded requests yet.'; empty.append(cell);\n }\n function makeRow(id: string): Row {\n const row = document.createElement('tr');\n row.innerHTML = '<td><strong data-purpose></strong><br><span data-identity></span><br><small data-time></small><details><summary>Price source and rates (USD / million tokens)</summary><p data-source></p><pre data-price></pre><p data-reasoning hidden></p></details></td><td></td><td></td><td></td><td></td>';\n const details = row.querySelector('details')!; details.dataset.costId = id;\n return { element: row, purpose: row.querySelector('[data-purpose]')!, identity: row.querySelector('[data-identity]')!, time: row.querySelector('[data-time]')!, details, source: row.querySelector('[data-source]')!, price: row.querySelector('[data-price]')!, reasoning: row.querySelector('[data-reasoning]')!, cells: [...row.querySelectorAll('td')] };\n }\n return {\n element,\n clear(): void { if (!destroyed) clear(); },\n loading(): void { if (!destroyed && !element.childElementCount) element.textContent = 'Loading request breakdown…'; },\n setError(message: string): void { if (!destroyed) { clear(); element.textContent = message; } },\n update(report: Readonly<CostLedgerState>): void {\n if (destroyed) return;\n const focused = document.activeElement;\n ensure();\n setText(note!, `USD estimates for recorded requests, not a provider bill. Prices are saved per request; retries and context summaries are included when usage is reported. ${report.unknownRequests ? `${report.unknownRequests} request(s) have unknown prices or usage and are excluded. ` : ''}Older requests made before recording began are not reconstructed.`);\n const requests = report.requests || [];\n const keep = new Set(requests.map(request => request.id));\n for (const [id, row] of rows) if (!keep.has(id)) { row.element.remove(); rows.delete(id); }\n if (!requests.length) { if (!empty!.parentElement) table!.append(empty!); return; }\n empty!.remove();\n let cursor = table!.firstElementChild;\n const n = (value: number) => numbers.format(value || 0);\n for (const request of requests) {\n let row = rows.get(request.id);\n if (!row) { row = makeRow(request.id); rows.set(request.id, row); }\n setText(row.purpose, `${request.purpose} · ${request.status}`);\n setText(row.identity, `${request.providerName || request.provider} / ${request.modelName || request.model}`);\n setText(row.time, request.time); setText(row.source, request.source); setText(row.price, JSON.stringify(request.price, null, 2));\n row.reasoning.hidden = request.usage.reasoning == null;\n setText(row.reasoning, request.usage.reasoning == null ? '' : `Reasoning tokens: ${n(request.usage.reasoning)} (included in output)`);\n setText(row.cells[1]!, n(request.usage.input)); setText(row.cells[2]!, n(request.usage.output));\n setText(row.cells[3]!, `${n(request.usage.cacheRead)} / ${n(request.usage.cacheWrite)}`);\n setText(row.cells[4]!, request.known ? currency.format(request.usage.cost.total) : 'Unknown');\n if (row.element !== cursor) table!.insertBefore(row.element, cursor);\n cursor = row.element.nextElementSibling;\n }\n if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused) focused.focus({ preventScroll: true });\n },\n destroy(): void { if (!destroyed) clear(); destroyed = true; },\n };\n}\n\nexport interface UpdatePanelState { version: string; enabled: boolean; phase: string; availableVersion: string; progress: number; message: string }\nexport function createUpdatePanel(options: { container?: HTMLElement; productName?: string; onCheck?: () => void; onInstall?: () => void } = {}) {\n const element = options.container || document.createElement('section');\n if (!element.querySelector('[data-update-version]')) element.innerHTML = '<p data-update-version></p><p data-update-message role=\"status\"></p><div class=\"pui-feature-toolbar\"><button type=\"button\" class=\"pui-secondary-button\" data-update-check>Check for updates</button><button type=\"button\" class=\"pui-secondary-button\" data-update-install hidden>Update and restart</button></div><progress max=\"100\" hidden></progress>';\n const version = element.querySelector<HTMLElement>('[data-update-version]')!;\n const message = element.querySelector<HTMLElement>('[data-update-message]')!;\n const check = element.querySelector<HTMLButtonElement>('[data-update-check]')!;\n const install = element.querySelector<HTMLButtonElement>('[data-update-install]')!;\n const progress = element.querySelector<HTMLProgressElement>('progress')!;\n let state: Readonly<UpdatePanelState> | undefined;\n let pending = false, destroyed = false;\n let error = '';\n let retryCheck = false;\n const checkAction = () => options.onCheck?.(), installAction = () => options.onInstall?.();\n check.addEventListener('click', checkAction); install.addEventListener('click', installAction);\n function render(): void {\n if (destroyed) return;\n if (!state) { check.disabled = install.disabled = pending; message.textContent = error; return; }\n version.textContent = `${options.productName || 'Application'} ${state.version}`;\n message.textContent = error || state.message;\n const busy = ['checking', 'installing', 'restarting'].includes(state.phase);\n check.disabled = !state.enabled || (busy && !retryCheck) || pending;\n install.hidden = !state.availableVersion; install.disabled = busy || pending; install.textContent = 'Update and restart';\n progress.hidden = state.phase !== 'installing'; progress.value = state.progress;\n }\n return {\n element,\n update(value: Readonly<UpdatePanelState>): void { state = value; error = ''; retryCheck = false; render(); },\n setPending(value: boolean): void { pending = value; render(); },\n setError(value: string, options: { allowCheckRetry?: boolean } = {}): void { if (!destroyed) { error = value; retryCheck = !!options.allowCheckRetry; render(); } },\n destroy(): void { if (destroyed) return; destroyed = true; check.removeEventListener('click', checkAction); install.removeEventListener('click', installAction); },\n };\n}\n\nexport interface CompletionNoticeDisplay { id: string; runID: string; title: string; workspace: string }\nexport function historyStatusMarkup(status: 'approval' | 'running' | 'unread' | 'idle'): string {\n if (status === 'approval') return '<span class=\"pui-history-status pui-needs-approval\" role=\"img\" aria-label=\"Waiting for approval\" title=\"Waiting for approval\"><svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" aria-hidden=\"true\"><circle cx=\"12\" cy=\"12\" r=\"9\"/><path d=\"M12 7v6m0 4h.01\"/></svg></span>';\n if (status === 'running') return '<span class=\"pui-history-status\" role=\"img\" aria-label=\"Running\" title=\"Running\"><i class=\"pui-history-spinner\" aria-hidden=\"true\"></i></span>';\n if (status === 'unread') return '<span class=\"pui-history-status\" role=\"img\" aria-label=\"Completed, unread\" title=\"Completed, unread\"><i class=\"pui-history-unread\" aria-hidden=\"true\"></i></span>';\n return '<span class=\"pui-history-status\" aria-hidden=\"true\"></span>';\n}\nexport function createCompletionNotices(options: { container?: HTMLElement; onOpen?: (id: string) => void; onDismiss?: (runID: string) => void } = {}): UIBlock<readonly CompletionNoticeDisplay[]> {\n const element = options.container || document.createElement('div');\n const entries = new Map<string, { element: HTMLElement; text: HTMLElement; open: HTMLButtonElement; dismiss: HTMLButtonElement }>();\n let destroyed = false;\n const click = (event: Event) => {\n const target = (event.target as Element).closest<HTMLElement>('[data-conversation], [data-dismiss-completion]');\n if (!target || !element.contains(target)) return;\n if (target.dataset.conversation) options.onOpen?.(target.dataset.conversation);\n else if (target.dataset.dismissCompletion) options.onDismiss?.(target.dataset.dismissCompletion);\n };\n if (options.onOpen || options.onDismiss) element.addEventListener('click', click);\n return {\n element,\n update(notices): void {\n if (destroyed) return;\n const focused = document.activeElement;\n const keep = new Set(notices.map(notice => notice.runID));\n for (const [id, entry] of entries) if (!keep.has(id)) { entry.element.remove(); entries.delete(id); }\n let cursor = element.firstElementChild;\n for (const notice of notices) {\n let entry = entries.get(notice.runID);\n if (!entry) {\n const article = document.createElement('article'); article.className = 'pui-completion-notice';\n article.innerHTML = `<span class=\"pui-completion-check\" aria-hidden=\"true\">${icon('check')}</span><div class=\"pui-completion-content\"><strong>Task completed</strong><p></p><button class=\"pui-text-button\" type=\"button\" data-conversation>Open conversation</button></div><button class=\"pui-quiet-icon pui-completion-dismiss\" type=\"button\" data-dismiss-completion aria-label=\"Dismiss completion notification\">${icon('close')}</button>`;\n entry = { element: article, text: article.querySelector('p')!, open: article.querySelector('[data-conversation]')!, dismiss: article.querySelector('[data-dismiss-completion]')! };\n entries.set(notice.runID, entry);\n }\n setText(entry.text, `${notice.workspace} · ${notice.title}`);\n if (entry.open.dataset.conversation !== notice.id) entry.open.dataset.conversation = notice.id;\n if (entry.dismiss.dataset.dismissCompletion !== notice.runID) entry.dismiss.dataset.dismissCompletion = notice.runID;\n if (entry.element !== cursor) element.insertBefore(entry.element, cursor);\n cursor = entry.element.nextElementSibling;\n }\n if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused) focused.focus({ preventScroll: true });\n },\n destroy(): void { if (destroyed) return; destroyed = true; entries.clear(); element.removeEventListener?.('click', click); element.replaceChildren(); },\n };\n}\n\nexport function createCopyFeedback(options: { status: HTMLElement; isDisabled: (button: HTMLButtonElement) => boolean; onError: (error: unknown) => void }) {\n let destroyed = false;\n const timers = new Map<HTMLButtonElement, ReturnType<typeof setTimeout>>();\n return {\n async copy(button: HTMLButtonElement, action: () => Promise<void>, announcement: string): Promise<void> {\n if (destroyed || button.dataset.copyPending === 'true') return;\n const original = button.innerHTML, label = button.getAttribute('aria-label'), title = button.title;\n options.status.textContent = ''; button.dataset.copyPending = 'true'; button.disabled = true;\n try {\n await action();\n if (destroyed) return;\n options.status.textContent = announcement;\n if (!button.isConnected) return;\n button.innerHTML = button.classList.contains('pui-message-action') ? icon('check') : 'Copied';\n button.setAttribute('aria-label', announcement); button.title = announcement;\n timers.set(button, setTimeout(() => {\n timers.delete(button);\n if (destroyed || !button.isConnected) return;\n button.innerHTML = original;\n if (label === null) button.removeAttribute('aria-label'); else button.setAttribute('aria-label', label);\n button.title = title; delete button.dataset.copyPending; button.disabled = options.isDisabled(button);\n }, 1500));\n } catch (error) {\n if (destroyed) return;\n delete button.dataset.copyPending; button.disabled = false; options.onError(error);\n }\n },\n destroy(): void { destroyed = true; timers.forEach(clearTimeout); timers.clear(); },\n };\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import type { UIBlock } from '../core.js';
2
+ export interface ProviderActionsState {
3
+ id: string;
4
+ oauth: boolean;
5
+ signedIn: boolean;
6
+ custom: boolean;
7
+ }
8
+ export interface ResourceActionsState {
9
+ kind: string;
10
+ resource: {
11
+ name: string;
12
+ path: string;
13
+ };
14
+ workspaceId: string;
15
+ }
16
+ export declare function providerActionsMarkup(provider: ProviderActionsState): string;
17
+ export declare function resourceActionsMarkup(kind: string, resource: {
18
+ name: string;
19
+ path: string;
20
+ }, workspaceId: string): string;
21
+ export declare function createProviderActions(state: ProviderActionsState, options?: {
22
+ onAction?: (action: string, target: DOMStringMap) => void;
23
+ }): UIBlock<ProviderActionsState>;
24
+ export declare function createResourceActions(state: ResourceActionsState, options?: {
25
+ onAction?: (action: string, target: DOMStringMap) => void;
26
+ }): UIBlock<ResourceActionsState>;
27
+ //# sourceMappingURL=actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/forms/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,oBAAoB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAC;AACtG,MAAM,WAAW,oBAAoB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC;AACjH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,CAA+lB;AAC5qB,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAGvH;AAOD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAkE;AAC9O,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAA4H"}
@@ -0,0 +1,19 @@
1
+ import { escape } from '../dom.js';
2
+ export function providerActionsMarkup(provider) { return `<div class="pui-feature-toolbar">${provider.oauth ? `<button type="button" class="pui-secondary-button" data-sdk="${provider.signedIn ? 'logout' : 'login'}" data-id="${escape(provider.id)}">${provider.signedIn ? 'Sign out' : 'Sign in with provider'}</button>` : ''}${provider.custom ? `<button type="button" class="pui-secondary-button" data-sdk="custom" data-id="${escape(provider.id)}">Edit models and prices</button><button type="button" class="pui-secondary-button pui-destructive-button" data-sdk="remove-connection" data-id="${escape(provider.id)}">Remove connection</button>` : ''}</div>`; }
3
+ export function resourceActionsMarkup(kind, resource, workspaceId) {
4
+ const command = kind === 'skill' ? `/skill:${resource.name} ` : `/${resource.name} `;
5
+ return `<div class="pui-file-actions">${kind !== 'instructions' ? `<button class="pui-secondary-button" data-sdk="use-resource" data-command="${escape(command)}">Use</button>` : ''}<button class="pui-secondary-button" data-sdk="resource" data-resource-workspace="${escape(workspaceId)}" data-kind="${kind}" data-path="${escape(resource.path)}">View / edit</button></div>`;
6
+ }
7
+ function actions(initial, markup, onAction) {
8
+ const element = document.createElement('div');
9
+ element.className = 'pui-feature-actions';
10
+ const abort = new AbortController();
11
+ const update = (state) => { element.innerHTML = markup(state); };
12
+ update(initial);
13
+ element.addEventListener('click', (event) => { const button = event.target.closest('[data-sdk]'); if (button && !button.disabled)
14
+ onAction?.(button.dataset.sdk, button.dataset); }, { signal: abort.signal });
15
+ return { element, update, destroy() { abort.abort(); element.remove(); } };
16
+ }
17
+ export function createProviderActions(state, options = {}) { return actions(state, providerActionsMarkup, options.onAction); }
18
+ export function createResourceActions(state, options = {}) { return actions(state, (state) => resourceActionsMarkup(state.kind, state.resource, state.workspaceId), options.onAction); }
19
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/forms/actions.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGnC,MAAM,UAAU,qBAAqB,CAAC,QAA8B,IAAW,OAAO,oCAAoC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,gEAAgE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,cAAc,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,uBAAuB,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,iFAAiF,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,oJAAoJ,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,8BAA8B,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA,CAAC;AAC5qB,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,QAAsC,EAAE,WAAmB;IAC9G,MAAM,OAAO,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,UAAU,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC;IACrF,OAAO,iCAAiC,IAAI,KAAK,cAAc,CAAC,CAAC,CAAC,8EAA8E,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,qFAAqF,MAAM,CAAC,WAAW,CAAC,gBAAgB,IAAI,gBAAgB,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,8BAA8B,CAAC;AACrX,CAAC;AACD,SAAS,OAAO,CAAQ,OAAc,EAAE,MAAgC,EAAE,QAAyD;IAClI,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAAC,OAAO,CAAC,SAAS,GAAG,qBAAqB,CAAC;IAAC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IAC9H,MAAM,MAAM,GAAG,CAAC,KAAsB,EAAE,EAAE,GAAE,OAAO,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;IAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACjG,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,GAAE,MAAM,MAAM,GAAI,KAAK,CAAC,MAAsB,CAAC,OAAO,CAAoB,YAAY,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ;QAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAA,CAAC,EAAE,EAAC,MAAM,EAAC,KAAK,CAAC,MAAM,EAAC,CAAC,CAAC;IAC/O,OAAO,EAAC,OAAO,EAAE,MAAM,EAAE,OAAO,KAAI,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA,CAAC,EAAC,CAAC;AACxE,CAAC;AACD,MAAM,UAAU,qBAAqB,CAAC,KAA2B,EAAE,UAAuE,EAAE,IAAkC,OAAO,OAAO,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA,CAAC;AAC9O,MAAM,UAAU,qBAAqB,CAAC,KAA2B,EAAE,UAAuE,EAAE,IAAkC,OAAO,OAAO,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA,CAAC","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { escape } from '../dom.js';\nexport interface ProviderActionsState {id: string; oauth: boolean; signedIn: boolean; custom: boolean}\nexport interface ResourceActionsState {kind: string; resource: {name: string; path: string}; workspaceId: string}\nexport function providerActionsMarkup(provider: ProviderActionsState): string {return `<div class=\"pui-feature-toolbar\">${provider.oauth ? `<button type=\"button\" class=\"pui-secondary-button\" data-sdk=\"${provider.signedIn ? 'logout' : 'login'}\" data-id=\"${escape(provider.id)}\">${provider.signedIn ? 'Sign out' : 'Sign in with provider'}</button>` : ''}${provider.custom ? `<button type=\"button\" class=\"pui-secondary-button\" data-sdk=\"custom\" data-id=\"${escape(provider.id)}\">Edit models and prices</button><button type=\"button\" class=\"pui-secondary-button pui-destructive-button\" data-sdk=\"remove-connection\" data-id=\"${escape(provider.id)}\">Remove connection</button>` : ''}</div>`;}\nexport function resourceActionsMarkup(kind: string, resource: {name: string; path: string}, workspaceId: string): string {\n const command = kind === 'skill' ? `/skill:${resource.name} ` : `/${resource.name} `;\n return `<div class=\"pui-file-actions\">${kind !== 'instructions' ? `<button class=\"pui-secondary-button\" data-sdk=\"use-resource\" data-command=\"${escape(command)}\">Use</button>` : ''}<button class=\"pui-secondary-button\" data-sdk=\"resource\" data-resource-workspace=\"${escape(workspaceId)}\" data-kind=\"${kind}\" data-path=\"${escape(resource.path)}\">View / edit</button></div>`;\n}\nfunction actions<State>(initial: State, markup: (state: State) => string, onAction?: (action: string, target: DOMStringMap) => void): UIBlock<State> {\n const element = document.createElement('div'); element.className = 'pui-feature-actions'; const abort = new AbortController();\n const update = (state: Readonly<State>) => {element.innerHTML = markup(state);}; update(initial);\n element.addEventListener('click', (event) => {const button = (event.target as HTMLElement).closest<HTMLButtonElement>('[data-sdk]'); if (button && !button.disabled) onAction?.(button.dataset.sdk!, button.dataset);}, {signal:abort.signal});\n return {element, update, destroy() {abort.abort(); element.remove();}};\n}\nexport function createProviderActions(state: ProviderActionsState, options: {onAction?: (action: string, target: DOMStringMap) => void} = {}): UIBlock<ProviderActionsState> {return actions(state, providerActionsMarkup, options.onAction);}\nexport function createResourceActions(state: ResourceActionsState, options: {onAction?: (action: string, target: DOMStringMap) => void} = {}): UIBlock<ResourceActionsState> {return actions(state, (state) => resourceActionsMarkup(state.kind, state.resource, state.workspaceId), options.onAction);}\n"]}
@@ -0,0 +1,61 @@
1
+ import type { UIBlock } from '../core.js';
2
+ export interface CustomModelValue {
3
+ id: string;
4
+ name?: string;
5
+ contextWindow?: number;
6
+ maxTokens?: number;
7
+ reasoning?: boolean;
8
+ input?: readonly string[];
9
+ cost?: {
10
+ input: number;
11
+ output: number;
12
+ cacheRead: number;
13
+ cacheWrite: number;
14
+ };
15
+ }
16
+ export interface CustomConnectionState {
17
+ name: string;
18
+ baseUrl: string;
19
+ api: string;
20
+ editing: boolean;
21
+ models: readonly CustomModelValue[];
22
+ error?: string;
23
+ disabled?: boolean;
24
+ }
25
+ export interface CustomModelDraft {
26
+ sourceIndex: number;
27
+ id: string;
28
+ name: string;
29
+ contextWindow: number;
30
+ maxTokens: number;
31
+ reasoning: boolean;
32
+ images: boolean;
33
+ prices: {
34
+ input: string;
35
+ output: string;
36
+ cacheRead: string;
37
+ cacheWrite: string;
38
+ };
39
+ }
40
+ export interface CustomConnectionDraft {
41
+ name: string;
42
+ baseUrl: string;
43
+ api: string;
44
+ apiKey: string;
45
+ models: CustomModelDraft[];
46
+ }
47
+ export interface CustomModelRowState extends CustomModelValue {
48
+ disabled?: boolean;
49
+ error?: string;
50
+ }
51
+ export declare function createCustomModelRow(model: CustomModelRowState, index?: number): UIBlock<CustomModelRowState> & {
52
+ read(): CustomModelDraft;
53
+ reset(model: CustomModelValue): void;
54
+ };
55
+ export declare function createCustomConnectionEditor(state: CustomConnectionState, options?: {
56
+ idPrefix?: string;
57
+ }): UIBlock<CustomConnectionState> & {
58
+ read(): CustomConnectionDraft;
59
+ readonly form: HTMLFormElement;
60
+ };
61
+ //# sourceMappingURL=custom-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-connection.d.ts","sourceRoot":"","sources":["../../src/forms/custom-connection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAA;CAAC;AACxO,MAAM,WAAW,qBAAqB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AAC9K,MAAM,WAAW,gBAAgB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAA;CAAC;AAChP,MAAM,WAAW,qBAAqB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,EAAE,CAAA;CAAC;AAG/H,MAAM,WAAW,mBAAoB,SAAQ,gBAAgB;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAC;AAClG,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,mBAAmB,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAAG;IAAC,IAAI,IAAI,gBAAgB,CAAC;IAAC,KAAK,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAAA;CAAC,CAc3K;AACD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,GAAG;IAAC,IAAI,IAAI,qBAAqB,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAC,CAc9M"}
@@ -0,0 +1,65 @@
1
+ import { scopeIds, localField } from './identity.js';
2
+ import { escape, uniqueId } from '../dom.js';
3
+ const protocols = ['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai', 'mistral-conversations'];
4
+ function modelRowMarkup(model, index) { return `<fieldset class="pui-custom-model" data-model-index="${index}"><legend>Model ${index + 1}</legend><label>Model ID<input name="id" required value="${escape(model.id)}" /></label><label>Display name<input name="name" value="${escape(model.name || '')}" /></label><div class="pui-sdk-grid"><label>Context tokens<input name="contextWindow" type="number" min="1" required value="${escape(model.contextWindow)}" /></label><label>Max output tokens<input name="maxTokens" type="number" min="1" required value="${escape(model.maxTokens)}" /></label></div><div class="pui-sdk-grid"><label class="pui-checkbox-field"><input name="reasoning" type="checkbox" ${model.reasoning ? 'checked' : ''} />Thinking effort</label><label class="pui-checkbox-field"><input name="images" type="checkbox" ${model.input?.includes('image') ? 'checked' : ''} />Images</label></div><details class="pui-provider-advanced"><summary>Price estimate (optional)</summary><div class="pui-sdk-grid">${['input', 'output', 'cacheRead', 'cacheWrite'].map((rate) => `<label>${{ input: "Input", output: "Output", cacheRead: "Cache read", cacheWrite: "Cache write" }[rate]}<input name="price-${rate}" type="number" min="0" step="any" value="${escape(model.cost?.[rate] ?? '')}" /></label>`).join('')}</div></details><button type="button" class="pui-secondary-button pui-destructive-button" data-remove-model>Remove model</button></fieldset>`; }
5
+ export function createCustomModelRow(model, index = 0) {
6
+ const element = document.createElement('div');
7
+ element.className = 'pui-custom-model-row';
8
+ element.innerHTML = modelRowMarkup(model, index);
9
+ const field = element.querySelector('fieldset');
10
+ const error = document.createElement('p');
11
+ error.className = 'pui-form-error';
12
+ error.setAttribute('role', 'alert');
13
+ element.append(error);
14
+ let destroyed = false;
15
+ const input = (name) => field.querySelector(`[name="${name}"]`);
16
+ const reset = (value) => {
17
+ if (destroyed)
18
+ return;
19
+ input('id').value = value.id;
20
+ input('name').value = value.name || '';
21
+ input('contextWindow').value = value.contextWindow === undefined ? '' : String(value.contextWindow);
22
+ input('maxTokens').value = value.maxTokens === undefined ? '' : String(value.maxTokens);
23
+ input('reasoning').checked = !!value.reasoning;
24
+ input('images').checked = !!value.input?.includes('image');
25
+ for (const rate of ['input', 'output', 'cacheRead', 'cacheWrite'])
26
+ input('price-' + rate).value = value.cost?.[rate] === undefined ? '' : String(value.cost[rate]);
27
+ };
28
+ const read = () => ({ sourceIndex: index, id: input('id').value.trim(), name: input('name').value.trim(), contextWindow: Number(input('contextWindow').value), maxTokens: Number(input('maxTokens').value), reasoning: input('reasoning').checked, images: input('images').checked, prices: { input: input('price-input').value, output: input('price-output').value, cacheRead: input('price-cacheRead').value, cacheWrite: input('price-cacheWrite').value } });
29
+ const update = (state) => { if (destroyed)
30
+ return; if (state.disabled !== undefined)
31
+ field.disabled = state.disabled; if (state.error !== undefined)
32
+ error.textContent = state.error; };
33
+ update(model);
34
+ return { element, read, reset, update, destroy() { if (destroyed)
35
+ return; destroyed = true; element.remove(); } };
36
+ }
37
+ export function createCustomConnectionEditor(state, options = {}) {
38
+ const element = document.createElement('div');
39
+ element.className = 'pui-custom-connection-editor';
40
+ element.innerHTML = `<p class="pui-modal-description">Give this endpoint its own name. It can coexist with official providers and other compatible endpoints.</p><form id="custom-form"><label class="pui-field-label">Connection name<input id="custom-name" required value="${escape(state.name)}" /></label><label class="pui-field-label">API base URL<input id="custom-url" type="url" required value="${escape(state.baseUrl)}" placeholder="https://example.com/v1" /></label><label class="pui-field-label">API protocol<select id="custom-api" class="pui-feature-select">${protocols.map((p) => `<option ${state.api === p ? 'selected' : ''}>${p}</option>`).join('')}</select></label><label class="pui-field-label">API key<input id="custom-key" type="password" autocomplete="new-password" placeholder="${state.editing ? 'Leave blank to keep the key at the same endpoint' : 'API key (optional for local endpoints)'}" /></label><div id="custom-models"></div><button type="button" class="pui-secondary-button" id="add-custom-model">Add model</button><p class="pui-field-hint">Limits and capabilities are provided by you. Prices are USD per million tokens. Fill all four rates (0 for free/unused), or leave all four blank for unknown prices.</p><p class="pui-form-error" data-custom-error role="alert"></p><div class="pui-modal-footer"><button type="button" class="pui-secondary-button" data-close="sdk-dialog">Cancel</button><button class="pui-primary-button" type="submit">Save connection</button></div></form>`;
41
+ const prefix = options.idPrefix ?? uniqueId('pui-custom') + '-';
42
+ scopeIds(element, prefix);
43
+ const $ = (id) => localField(element, prefix + id);
44
+ const form = $('custom-form');
45
+ const models = [...state.models];
46
+ let destroyed = false;
47
+ const abort = new AbortController();
48
+ $('custom-models').innerHTML = models.map(modelRowMarkup).join('');
49
+ $('add-custom-model').addEventListener('click', () => { const model = { id: '', contextWindow: 128000, maxTokens: 8192 }; const index = models.length; models.push(model); $('custom-models').insertAdjacentHTML('beforeend', modelRowMarkup(model, index)); }, { signal: abort.signal });
50
+ $('custom-models').addEventListener('click', (event) => { event.target.closest('[data-remove-model]')?.closest('fieldset')?.remove(); }, { signal: abort.signal });
51
+ const read = () => ({ name: $('custom-name').value, baseUrl: $('custom-url').value, api: $('custom-api').value, apiKey: $('custom-key').value, models: Array.from($('custom-models').querySelectorAll('fieldset')).map((field) => {
52
+ const input = (name) => field.querySelector(`[name="${name}"]`);
53
+ return { sourceIndex: Number(field.dataset.modelIndex), id: input('id').value.trim(), name: input('name').value.trim(), contextWindow: Number(input('contextWindow').value), maxTokens: Number(input('maxTokens').value), reasoning: input('reasoning').checked, images: input('images').checked, prices: { input: input('price-input').value, output: input('price-output').value, cacheRead: input('price-cacheRead').value, cacheWrite: input('price-cacheWrite').value } };
54
+ }) });
55
+ const update = (state) => { if (destroyed)
56
+ return; if (state.error !== undefined)
57
+ element.querySelector('[data-custom-error]').textContent = state.error; if (state.disabled !== undefined)
58
+ for (const button of form.querySelectorAll('button'))
59
+ button.disabled = state.disabled; };
60
+ update(state);
61
+ return { element, form, read, update, destroy() { if (destroyed)
62
+ return; destroyed = true; abort.abort(); for (const input of element.querySelectorAll('input[type="password"]'))
63
+ input.value = ''; element.remove(); } };
64
+ }
65
+ //# sourceMappingURL=custom-connection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-connection.js","sourceRoot":"","sources":["../../src/forms/custom-connection.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds, localField} from './identity.js';\nimport type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nexport interface CustomModelValue {id: string; name?: string; contextWindow?: number; maxTokens?: number; reasoning?: boolean; input?: readonly string[]; cost?: {input: number; output: number; cacheRead: number; cacheWrite: number}}\nexport interface CustomConnectionState {name: string; baseUrl: string; api: string; editing: boolean; models: readonly CustomModelValue[]; error?: string; disabled?: boolean}\nexport interface CustomModelDraft {sourceIndex: number; id: string; name: string; contextWindow: number; maxTokens: number; reasoning: boolean; images: boolean; prices: {input: string; output: string; cacheRead: string; cacheWrite: string}}\nexport interface CustomConnectionDraft {name: string; baseUrl: string; api: string; apiKey: string; models: CustomModelDraft[]}\nconst protocols = ['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai', 'mistral-conversations'];\nfunction modelRowMarkup(model: CustomModelValue, index: number): string {return `<fieldset class=\"pui-custom-model\" data-model-index=\"${index}\"><legend>Model ${index + 1}</legend><label>Model ID<input name=\"id\" required value=\"${escape(model.id)}\" /></label><label>Display name<input name=\"name\" value=\"${escape(model.name || '')}\" /></label><div class=\"pui-sdk-grid\"><label>Context tokens<input name=\"contextWindow\" type=\"number\" min=\"1\" required value=\"${escape(model.contextWindow)}\" /></label><label>Max output tokens<input name=\"maxTokens\" type=\"number\" min=\"1\" required value=\"${escape(model.maxTokens)}\" /></label></div><div class=\"pui-sdk-grid\"><label class=\"pui-checkbox-field\"><input name=\"reasoning\" type=\"checkbox\" ${model.reasoning ? 'checked' : ''} />Thinking effort</label><label class=\"pui-checkbox-field\"><input name=\"images\" type=\"checkbox\" ${model.input?.includes('image') ? 'checked' : ''} />Images</label></div><details class=\"pui-provider-advanced\"><summary>Price estimate (optional)</summary><div class=\"pui-sdk-grid\">${(['input', 'output', 'cacheRead', 'cacheWrite'] as const).map((rate) => `<label>${({ input: \"Input\", output: \"Output\", cacheRead: \"Cache read\", cacheWrite: \"Cache write\" } as Record<string, string>)[rate]}<input name=\"price-${rate}\" type=\"number\" min=\"0\" step=\"any\" value=\"${escape(model.cost?.[rate] ?? '')}\" /></label>`).join('')}</div></details><button type=\"button\" class=\"pui-secondary-button pui-destructive-button\" data-remove-model>Remove model</button></fieldset>`;}\nexport interface CustomModelRowState extends CustomModelValue {disabled?: boolean; error?: string}\nexport function createCustomModelRow(model: CustomModelRowState, index = 0): UIBlock<CustomModelRowState> & {read(): CustomModelDraft; reset(model: CustomModelValue): void} {\n const element = document.createElement('div'); element.className = 'pui-custom-model-row'; element.innerHTML = modelRowMarkup(model,index);\n const field = element.querySelector<HTMLFieldSetElement>('fieldset')!; const error = document.createElement('p'); error.className = 'pui-form-error'; error.setAttribute('role','alert'); element.append(error); let destroyed = false;\n const input = (name: string) => field.querySelector<HTMLInputElement>(`[name=\"${name}\"]`)!;\n const reset = (value: CustomModelValue) => {\n if (destroyed) return;\n input('id').value = value.id; input('name').value = value.name || '';\n input('contextWindow').value = value.contextWindow === undefined ? '' : String(value.contextWindow); input('maxTokens').value = value.maxTokens === undefined ? '' : String(value.maxTokens);\n input('reasoning').checked = !!value.reasoning; input('images').checked = !!value.input?.includes('image');\n for (const rate of ['input','output','cacheRead','cacheWrite'] as const) input('price-'+rate).value = value.cost?.[rate] === undefined ? '' : String(value.cost[rate]);\n };\n const read = (): CustomModelDraft => ({sourceIndex:index,id:input('id').value.trim(),name:input('name').value.trim(),contextWindow:Number(input('contextWindow').value),maxTokens:Number(input('maxTokens').value),reasoning:input('reasoning').checked,images:input('images').checked,prices:{input:input('price-input').value,output:input('price-output').value,cacheRead:input('price-cacheRead').value,cacheWrite:input('price-cacheWrite').value}});\n const update = (state: Readonly<CustomModelRowState>) => {if (destroyed) return; if (state.disabled !== undefined) field.disabled = state.disabled; if (state.error !== undefined) error.textContent = state.error;};\n update(model); return {element,read,reset,update,destroy() {if (destroyed) return; destroyed = true; element.remove();}};\n}\nexport function createCustomConnectionEditor(state: CustomConnectionState, options: {idPrefix?: string} = {}): UIBlock<CustomConnectionState> & {read(): CustomConnectionDraft; readonly form: HTMLFormElement} {\n const element = document.createElement('div'); element.className = 'pui-custom-connection-editor'; element.innerHTML = `<p class=\"pui-modal-description\">Give this endpoint its own name. It can coexist with official providers and other compatible endpoints.</p><form id=\"custom-form\"><label class=\"pui-field-label\">Connection name<input id=\"custom-name\" required value=\"${escape(state.name)}\" /></label><label class=\"pui-field-label\">API base URL<input id=\"custom-url\" type=\"url\" required value=\"${escape(state.baseUrl)}\" placeholder=\"https://example.com/v1\" /></label><label class=\"pui-field-label\">API protocol<select id=\"custom-api\" class=\"pui-feature-select\">${protocols.map((p) => `<option ${state.api === p ? 'selected' : ''}>${p}</option>`).join('')}</select></label><label class=\"pui-field-label\">API key<input id=\"custom-key\" type=\"password\" autocomplete=\"new-password\" placeholder=\"${state.editing ? 'Leave blank to keep the key at the same endpoint' : 'API key (optional for local endpoints)'}\" /></label><div id=\"custom-models\"></div><button type=\"button\" class=\"pui-secondary-button\" id=\"add-custom-model\">Add model</button><p class=\"pui-field-hint\">Limits and capabilities are provided by you. Prices are USD per million tokens. Fill all four rates (0 for free/unused), or leave all four blank for unknown prices.</p><p class=\"pui-form-error\" data-custom-error role=\"alert\"></p><div class=\"pui-modal-footer\"><button type=\"button\" class=\"pui-secondary-button\" data-close=\"sdk-dialog\">Cancel</button><button class=\"pui-primary-button\" type=\"submit\">Save connection</button></div></form>`;\n const prefix = options.idPrefix ?? uniqueId('pui-custom') + '-'; scopeIds(element, prefix);\n const $ = <T extends HTMLElement = HTMLElement>(id: string) => localField<T>(element, prefix + id);\n const form = $<HTMLFormElement>('custom-form'); const models = [...state.models]; let destroyed = false; const abort = new AbortController();\n $('custom-models').innerHTML = models.map(modelRowMarkup).join('');\n $('add-custom-model').addEventListener('click', () => {const model = {id: '', contextWindow: 128000, maxTokens: 8192}; const index = models.length; models.push(model); $('custom-models').insertAdjacentHTML('beforeend', modelRowMarkup(model, index));}, {signal: abort.signal});\n $('custom-models').addEventListener('click', (event) => {(event.target as HTMLElement).closest('[data-remove-model]')?.closest('fieldset')?.remove();}, {signal: abort.signal});\n const read = (): CustomConnectionDraft => ({name: $<HTMLInputElement>('custom-name').value, baseUrl: $<HTMLInputElement>('custom-url').value, api: $<HTMLSelectElement>('custom-api').value, apiKey: $<HTMLInputElement>('custom-key').value, models: Array.from($('custom-models').querySelectorAll<HTMLFieldSetElement>('fieldset')).map((field) => {\n const input = (name: string) => field.querySelector<HTMLInputElement>(`[name=\"${name}\"]`)!;\n return {sourceIndex: Number(field.dataset.modelIndex), id: input('id').value.trim(), name: input('name').value.trim(), contextWindow: Number(input('contextWindow').value), maxTokens: Number(input('maxTokens').value), reasoning: input('reasoning').checked, images: input('images').checked, prices: {input: input('price-input').value, output: input('price-output').value, cacheRead: input('price-cacheRead').value, cacheWrite: input('price-cacheWrite').value}};\n })});\n const update = (state: Readonly<CustomConnectionState>) => {if (destroyed) return; if (state.error !== undefined) element.querySelector<HTMLElement>('[data-custom-error]')!.textContent = state.error; if (state.disabled !== undefined) for (const button of form.querySelectorAll<HTMLButtonElement>('button')) button.disabled = state.disabled;}; update(state);\n return {element, form, read, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); for (const input of element.querySelectorAll<HTMLInputElement>('input[type=\"password\"]')) input.value = ''; element.remove();}};\n}\n"]}
@@ -0,0 +1,39 @@
1
+ import type { UIBlock } from '../core.js';
2
+ export interface DialogContent {
3
+ element: HTMLElement;
4
+ destroy(): void;
5
+ }
6
+ export interface FeatureDialogState {
7
+ title: string;
8
+ body: Node | DialogContent;
9
+ }
10
+ export interface FeatureDialogOptions {
11
+ dialog?: HTMLDialogElement;
12
+ content?: HTMLElement;
13
+ dialogId?: string;
14
+ titleId?: string;
15
+ errorId?: string;
16
+ eyebrow?: string;
17
+ }
18
+ export declare function createFeatureDialogFrame(options?: FeatureDialogOptions): UIBlock<FeatureDialogState> & {
19
+ open(state: FeatureDialogState): void;
20
+ close(): void;
21
+ fail(message: string): void;
22
+ };
23
+ export declare function createFeatureStatus(state: {
24
+ message: string;
25
+ error?: boolean;
26
+ }): UIBlock<{
27
+ message: string;
28
+ error?: boolean;
29
+ }>;
30
+ export declare function createSDKDialogElements(options?: {
31
+ idPrefix?: string;
32
+ }): {
33
+ dialog: HTMLDialogElement;
34
+ content: HTMLElement;
35
+ oauthDialog: HTMLDialogElement;
36
+ oauthContent: HTMLElement;
37
+ destroy(): void;
38
+ };
39
+ //# sourceMappingURL=dialog-frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dialog-frame.d.ts","sourceRoot":"","sources":["../../src/forms/dialog-frame.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,aAAa;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAC;AACtE,MAAM,WAAW,kBAAkB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAA;CAAC;AAC/E,MAAM,WAAW,oBAAoB;IAAE,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC;AAClK,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG;IAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAC;IAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,CA6B9L;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,CAAC,CAI1H;AACD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,WAAW,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAC,CAOxM"}