pith-ui 0.1.0 → 0.1.1

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 (91) hide show
  1. package/AI.md +8 -0
  2. package/API.md +656 -249
  3. package/README.md +1 -1
  4. package/dist/composer.d.ts.map +1 -1
  5. package/dist/composer.js +13 -4
  6. package/dist/composer.js.map +1 -1
  7. package/dist/content/conversation-view.d.ts +21 -4
  8. package/dist/content/conversation-view.d.ts.map +1 -1
  9. package/dist/content/conversation-view.js +38 -5
  10. package/dist/content/conversation-view.js.map +1 -1
  11. package/dist/content/image-viewer.d.ts +4 -2
  12. package/dist/content/image-viewer.d.ts.map +1 -1
  13. package/dist/content/image-viewer.js +25 -12
  14. package/dist/content/image-viewer.js.map +1 -1
  15. package/dist/content/paged-preview.d.ts.map +1 -1
  16. package/dist/content/paged-preview.js +6 -3
  17. package/dist/content/paged-preview.js.map +1 -1
  18. package/dist/content/welcome.d.ts.map +1 -1
  19. package/dist/content/welcome.js +14 -5
  20. package/dist/content/welcome.js.map +1 -1
  21. package/dist/files/panels.d.ts +6 -1
  22. package/dist/files/panels.d.ts.map +1 -1
  23. package/dist/files/panels.js +48 -7
  24. package/dist/files/panels.js.map +1 -1
  25. package/dist/files/systems.d.ts.map +1 -1
  26. package/dist/files/systems.js +47 -30
  27. package/dist/files/systems.js.map +1 -1
  28. package/dist/forms/actions.d.ts +7 -7
  29. package/dist/forms/actions.d.ts.map +1 -1
  30. package/dist/forms/actions.js +28 -8
  31. package/dist/forms/actions.js.map +1 -1
  32. package/dist/forms/custom-connection.d.ts +2 -1
  33. package/dist/forms/custom-connection.d.ts.map +1 -1
  34. package/dist/forms/custom-connection.js +34 -27
  35. package/dist/forms/custom-connection.js.map +1 -1
  36. package/dist/forms/dialog-frame.d.ts +2 -0
  37. package/dist/forms/dialog-frame.d.ts.map +1 -1
  38. package/dist/forms/dialog-frame.js +46 -26
  39. package/dist/forms/dialog-frame.js.map +1 -1
  40. package/dist/forms/identity.d.ts +12 -0
  41. package/dist/forms/identity.d.ts.map +1 -1
  42. package/dist/forms/identity.js +32 -0
  43. package/dist/forms/identity.js.map +1 -1
  44. package/dist/forms/model-editor.d.ts +8 -3
  45. package/dist/forms/model-editor.d.ts.map +1 -1
  46. package/dist/forms/model-editor.js +73 -62
  47. package/dist/forms/model-editor.js.map +1 -1
  48. package/dist/forms/panels.d.ts +95 -6
  49. package/dist/forms/panels.d.ts.map +1 -1
  50. package/dist/forms/panels.js +142 -27
  51. package/dist/forms/panels.js.map +1 -1
  52. package/dist/forms/provider-login.d.ts.map +1 -1
  53. package/dist/forms/provider-login.js +9 -7
  54. package/dist/forms/provider-login.js.map +1 -1
  55. package/dist/forms/resource-editor.d.ts +9 -5
  56. package/dist/forms/resource-editor.d.ts.map +1 -1
  57. package/dist/forms/resource-editor.js +18 -8
  58. package/dist/forms/resource-editor.js.map +1 -1
  59. package/dist/foundations.d.ts +95 -0
  60. package/dist/foundations.d.ts.map +1 -0
  61. package/dist/foundations.js +315 -0
  62. package/dist/foundations.js.map +1 -0
  63. package/dist/navigation/context-menu.d.ts +1 -0
  64. package/dist/navigation/context-menu.d.ts.map +1 -1
  65. package/dist/navigation/context-menu.js +48 -34
  66. package/dist/navigation/context-menu.js.map +1 -1
  67. package/dist/navigation/navigation.d.ts +42 -7
  68. package/dist/navigation/navigation.d.ts.map +1 -1
  69. package/dist/navigation/navigation.js +197 -14
  70. package/dist/navigation/navigation.js.map +1 -1
  71. package/dist/primitives.d.ts +3 -0
  72. package/dist/primitives.d.ts.map +1 -1
  73. package/dist/primitives.js +60 -168
  74. package/dist/primitives.js.map +1 -1
  75. package/dist/styles.css +32 -28
  76. package/dist/surface.d.ts +29 -0
  77. package/dist/surface.d.ts.map +1 -0
  78. package/dist/surface.js +154 -0
  79. package/dist/surface.js.map +1 -0
  80. package/dist/tasks/approval.d.ts.map +1 -1
  81. package/dist/tasks/approval.js +5 -2
  82. package/dist/tasks/approval.js.map +1 -1
  83. package/dist/tasks/permission.d.ts +5 -3
  84. package/dist/tasks/permission.d.ts.map +1 -1
  85. package/dist/tasks/permission.js +11 -5
  86. package/dist/tasks/permission.js.map +1 -1
  87. package/dist/workspace.d.ts +58 -4
  88. package/dist/workspace.d.ts.map +1 -1
  89. package/dist/workspace.js +120 -15
  90. package/dist/workspace.js.map +1 -1
  91. package/package.json +1 -1
@@ -1,4 +1,5 @@
1
1
  import { icon, setText } from '../dom.js';
2
+ import { adoptAction, adoptFeedback, adoptProductParts } from '../foundations.js';
2
3
  const currency = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 6 });
3
4
  const numbers = new Intl.NumberFormat();
4
5
  export function formatCost(cost, latestTask = false) {
@@ -107,6 +108,7 @@ export function createUpdatePanel(options = {}) {
107
108
  const check = element.querySelector('[data-update-check]');
108
109
  const install = element.querySelector('[data-update-install]');
109
110
  const progress = element.querySelector('progress');
111
+ const parts = adoptProductParts(element);
110
112
  let state;
111
113
  let pending = false, destroyed = false;
112
114
  let error = '';
@@ -118,17 +120,17 @@ export function createUpdatePanel(options = {}) {
118
120
  if (destroyed)
119
121
  return;
120
122
  if (!state) {
121
- check.disabled = install.disabled = pending;
122
- message.textContent = error;
123
+ parts.action(check).update({ pending });
124
+ parts.action(install).update({ pending });
125
+ parts.feedback(message).update({ message: error, tone: 'info' });
123
126
  return;
124
127
  }
125
128
  version.textContent = `${options.productName || 'Application'} ${state.version}`;
126
- message.textContent = error || state.message;
129
+ parts.feedback(message).update({ message: error || state.message, tone: 'info' });
127
130
  const busy = ['checking', 'installing', 'restarting'].includes(state.phase);
128
- check.disabled = !state.enabled || (busy && !retryCheck) || pending;
131
+ parts.action(check).update({ disabled: !state.enabled || (busy && !retryCheck), pending });
129
132
  install.hidden = !state.availableVersion;
130
- install.disabled = busy || pending;
131
- install.textContent = 'Update and restart';
133
+ parts.action(install).update({ disabled: busy, pending, label: 'Update and restart' });
132
134
  progress.hidden = state.phase !== 'installing';
133
135
  progress.value = state.progress;
134
136
  }
@@ -142,7 +144,7 @@ export function createUpdatePanel(options = {}) {
142
144
  render();
143
145
  } },
144
146
  destroy() { if (destroyed)
145
- return; destroyed = true; check.removeEventListener('click', checkAction); install.removeEventListener('click', installAction); },
147
+ return; destroyed = true; parts.destroy(); check.removeEventListener('click', checkAction); install.removeEventListener('click', installAction); },
146
148
  };
147
149
  }
148
150
  export function historyStatusMarkup(status) {
@@ -156,6 +158,7 @@ export function historyStatusMarkup(status) {
156
158
  }
157
159
  export function createCompletionNotices(options = {}) {
158
160
  const element = options.container || document.createElement('div');
161
+ const parts = adoptProductParts(element);
159
162
  const entries = new Map();
160
163
  let destroyed = false;
161
164
  const click = (event) => {
@@ -192,6 +195,8 @@ export function createCompletionNotices(options = {}) {
192
195
  entries.set(notice.runID, entry);
193
196
  }
194
197
  setText(entry.text, `${notice.workspace} · ${notice.title}`);
198
+ parts.action(entry.open).update({ label: 'Open conversation' });
199
+ parts.action(entry.dismiss).update({ label: 'Dismiss completion notification' });
195
200
  if (entry.open.dataset.conversation !== notice.id)
196
201
  entry.open.dataset.conversation = notice.id;
197
202
  if (entry.dismiss.dataset.dismissCompletion !== notice.runID)
@@ -200,57 +205,69 @@ export function createCompletionNotices(options = {}) {
200
205
  element.insertBefore(entry.element, cursor);
201
206
  cursor = entry.element.nextElementSibling;
202
207
  }
208
+ parts.update();
203
209
  if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused)
204
210
  focused.focus({ preventScroll: true });
205
211
  },
206
212
  destroy() { if (destroyed)
207
- return; destroyed = true; entries.clear(); element.removeEventListener?.('click', click); element.replaceChildren(); },
213
+ return; destroyed = true; parts.destroy(); entries.clear(); element.removeEventListener?.('click', click); element.replaceChildren(); },
208
214
  };
209
215
  }
210
216
  export function createCopyFeedback(options) {
211
217
  let destroyed = false;
212
- const timers = new Map();
218
+ const notice = adoptFeedback(options.status, { presentation: 'inline', autoHide: false });
219
+ const entries = new Map();
220
+ const release = (button, entry) => {
221
+ if (entry.timer)
222
+ clearTimeout(entry.timer);
223
+ if (entry.displayed && button.isConnected) {
224
+ button.replaceChildren(...entry.original);
225
+ if (entry.label === null)
226
+ button.removeAttribute('aria-label');
227
+ else
228
+ button.setAttribute('aria-label', entry.label);
229
+ button.title = entry.title;
230
+ }
231
+ delete button.dataset.copyPending;
232
+ entry.controller.update({ disabled: options.isDisabled(button) });
233
+ entry.controller.destroy();
234
+ entries.delete(button);
235
+ };
213
236
  return {
214
237
  async copy(button, action, announcement) {
215
238
  if (destroyed || button.dataset.copyPending === 'true')
216
239
  return;
217
- const original = button.innerHTML, label = button.getAttribute('aria-label'), title = button.title;
218
- options.status.textContent = '';
240
+ const entry = { controller: adoptAction(button), original: [...button.childNodes], label: button.getAttribute('aria-label'), title: button.title, displayed: false };
241
+ entries.set(button, entry);
242
+ notice.update({ message: '', tone: 'info' });
219
243
  button.dataset.copyPending = 'true';
220
- button.disabled = true;
244
+ entry.controller.update({ disabled: options.isDisabled(button) });
221
245
  try {
222
246
  await action();
223
247
  if (destroyed)
224
248
  return;
225
- options.status.textContent = announcement;
226
- if (!button.isConnected)
249
+ notice.update({ message: announcement, tone: 'info' });
250
+ if (!button.isConnected) {
251
+ release(button, entry);
227
252
  return;
253
+ }
228
254
  button.innerHTML = button.classList.contains('pui-message-action') ? icon('check') : 'Copied';
229
255
  button.setAttribute('aria-label', announcement);
230
256
  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));
257
+ entry.displayed = true;
258
+ entry.timer = setTimeout(() => { if (!destroyed && entries.get(button) === entry)
259
+ release(button, entry); }, 1500);
244
260
  }
245
261
  catch (error) {
246
262
  if (destroyed)
247
263
  return;
248
- delete button.dataset.copyPending;
249
- button.disabled = false;
264
+ release(button, entry);
250
265
  options.onError(error);
251
266
  }
252
267
  },
253
- destroy() { destroyed = true; timers.forEach(clearTimeout); timers.clear(); },
268
+ destroy() { if (destroyed)
269
+ return; destroyed = true; for (const [button, entry] of entries)
270
+ release(button, entry); notice.destroy(); },
254
271
  };
255
272
  }
256
273
  //# sourceMappingURL=systems.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"systems.js","sourceRoot":"","sources":["../../src/files/systems.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAC;AAC5I,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;AAExC,MAAM,UAAU,UAAU,CAAC,IAAoC,EAAE,UAAU,GAAG,KAAK;IACjF,IAAI,IAAI,EAAE,WAAW;QAAE,OAAO,aAAa,CAAC;IAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC;IAClE,MAAM,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;IACrF,IAAI,CAAC,KAAK;QAAE,OAAO,cAAc,CAAC;IAClC,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC/G,CAAC;AAOD,MAAM,UAAU,gBAAgB,CAAC,UAAuC,EAAE;IACxE,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAe,CAAC;IACpC,IAAI,KAA0C,CAAC;IAC/C,IAAI,IAA6B,CAAC;IAClC,IAAI,KAAsC,CAAC;IAC3C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,SAAS,KAAK,KAAW,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC3H,SAAS,MAAM;QACb,IAAI,KAAK;YAAE,OAAO;QAClB,OAAO,CAAC,SAAS,GAAG,gNAAgN,CAAC;QACrO,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAE,CAAC;QAAC,IAAI,GAAG,OAAO,CAAC,aAAa,CAAc,gBAAgB,CAAE,CAAC;QACtG,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QAAC,IAAI,CAAC,WAAW,GAAG,2BAA2B,CAAC;QAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxK,CAAC;IACD,SAAS,OAAO,CAAC,EAAU;QACzB,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACzC,GAAG,CAAC,SAAS,GAAG,kSAAkS,CAAC;QACnT,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,SAAS,CAAE,CAAC;QAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC;QAC3E,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,aAAa,CAAC,iBAAiB,CAAE,EAAE,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,aAAa,CAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,eAAe,CAAE,EAAE,KAAK,EAAE,GAAG,CAAC,aAAa,CAAC,cAAc,CAAE,EAAE,SAAS,EAAE,GAAG,CAAC,aAAa,CAAC,kBAAkB,CAAE,EAAE,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC/V,CAAC;IACD,OAAO;QACL,OAAO;QACP,KAAK,KAAW,IAAI,CAAC,SAAS;YAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1C,OAAO,KAAW,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,iBAAiB;YAAE,OAAO,CAAC,WAAW,GAAG,4BAA4B,CAAC,CAAC,CAAC;QACrH,QAAQ,CAAC,OAAe,IAAU,IAAI,CAAC,SAAS,EAAE,CAAC;YAAC,KAAK,EAAE,CAAC;YAAC,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC;QAAC,CAAC,CAAC,CAAC;QAC/F,MAAM,CAAC,MAAiC;YACtC,IAAI,SAAS;gBAAE,OAAO;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;YACvC,MAAM,EAAE,CAAC;YACT,OAAO,CAAC,IAAK,EAAE,8JAA8J,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,eAAe,6DAA6D,CAAC,CAAC,CAAC,EAAE,mEAAmE,CAAC,CAAC;YACtW,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAAC,CAAC;YAC3F,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,KAAM,CAAC,aAAa;oBAAE,KAAM,CAAC,MAAM,CAAC,KAAM,CAAC,CAAC;gBAAC,OAAO;YAAC,CAAC;YACnF,KAAM,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,MAAM,GAAG,KAAM,CAAC,iBAAiB,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;YACxD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;oBAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAAC,CAAC;gBACnE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC7G,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC;gBACvD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;gBACtI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;gBAChG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBACzF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBAC9F,IAAI,GAAG,CAAC,OAAO,KAAK,MAAM;oBAAE,KAAM,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACrE,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC;YAC1C,CAAC;YACD,IAAI,OAAO,YAAY,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,aAAa,KAAK,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAChJ,CAAC;QACD,OAAO,KAAW,IAAI,CAAC,SAAS;YAAE,KAAK,EAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;KAC/D,CAAC;AACJ,CAAC;AAGD,MAAM,UAAU,iBAAiB,CAAC,UAA2G,EAAE;IAC7I,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,uBAAuB,CAAC;QAAE,OAAO,CAAC,SAAS,GAAG,2VAA2V,CAAC;IACra,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAc,uBAAuB,CAAE,CAAC;IAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAc,uBAAuB,CAAE,CAAC;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAoB,qBAAqB,CAAE,CAAC;IAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAoB,uBAAuB,CAAE,CAAC;IACnF,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAsB,UAAU,CAAE,CAAC;IACzE,IAAI,KAA6C,CAAC;IAClD,IAAI,OAAO,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,CAAC;IACvC,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,aAAa,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;IAC3F,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC/F,SAAS,MAAM;QACb,IAAI,SAAS;YAAE,OAAO;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;YAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC;YAAC,OAAO,CAAC,WAAW,GAAG,KAAK,CAAC;YAAC,OAAO;QAAC,CAAC;QACjG,OAAO,CAAC,WAAW,GAAG,GAAG,OAAO,CAAC,WAAW,IAAI,aAAa,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACjF,OAAO,CAAC,WAAW,GAAG,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC;QAC7C,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5E,KAAK,CAAC,QAAQ,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC;QACpE,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC;QAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,IAAI,OAAO,CAAC;QAAC,OAAO,CAAC,WAAW,GAAG,oBAAoB,CAAC;QACzH,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,KAAK,YAAY,CAAC;QAAC,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC;IAClF,CAAC;IACD,OAAO;QACL,OAAO;QACP,MAAM,CAAC,KAAiC,IAAU,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5G,UAAU,CAAC,KAAc,IAAU,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/D,QAAQ,CAAC,KAAa,EAAE,UAAyC,EAAE,IAAU,IAAI,CAAC,SAAS,EAAE,CAAC;YAAC,KAAK,GAAG,KAAK,CAAC;YAAC,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;YAAC,MAAM,EAAE,CAAC;QAAC,CAAC,CAAC,CAAC;QACnK,OAAO,KAAW,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;KACnK,CAAC;AACJ,CAAC;AAGD,MAAM,UAAU,mBAAmB,CAAC,MAAkD;IACpF,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,+TAA+T,CAAC;IAClW,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,gJAAgJ,CAAC;IAClL,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,mKAAmK,CAAC;IACpM,OAAO,6DAA6D,CAAC;AACvE,CAAC;AACD,MAAM,UAAU,uBAAuB,CAAC,UAA2G,EAAE;IACnJ,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACnE,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4G,CAAC;IACpI,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,KAAK,GAAG,CAAC,KAAY,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAI,KAAK,CAAC,MAAkB,CAAC,OAAO,CAAc,gDAAgD,CAAC,CAAC;QAChH,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO;QACjD,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY;YAAE,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;aAC1E,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB;YAAE,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACnG,CAAC,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS;QAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAClF,OAAO;QACL,OAAO;QACP,MAAM,CAAC,OAAO;YACZ,IAAI,SAAS;gBAAE,OAAO;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,OAAO;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAAC,CAAC;YACrG,IAAI,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACtC,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;oBAAC,OAAO,CAAC,SAAS,GAAG,uBAAuB,CAAC;oBAC/F,OAAO,CAAC,SAAS,GAAG,yDAAyD,IAAI,CAAC,OAAO,CAAC,6TAA6T,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;oBAChb,KAAK,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,CAAE,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAE,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,2BAA2B,CAAE,EAAE,CAAC;oBACnL,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;gBACD,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC7D,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,KAAK,MAAM,CAAC,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC;gBAC/F,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,KAAK,MAAM,CAAC,KAAK;oBAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC;gBACrH,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM;oBAAE,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC1E,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;YAC5C,CAAC;YACD,IAAI,OAAO,YAAY,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,aAAa,KAAK,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAChJ,CAAC;QACD,OAAO,KAAW,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;KACxJ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAuH;IACxJ,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoD,CAAC;IAC3E,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,MAAyB,EAAE,MAA2B,EAAE,YAAoB;YACrF,IAAI,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,MAAM;gBAAE,OAAO;YAC/D,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YACnG,OAAO,CAAC,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC;YAAC,MAAM,CAAC,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC;YAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC;YAC7F,IAAI,CAAC;gBACH,MAAM,MAAM,EAAE,CAAC;gBACf,IAAI,SAAS;oBAAE,OAAO;gBACtB,OAAO,CAAC,MAAM,CAAC,WAAW,GAAG,YAAY,CAAC;gBAC1C,IAAI,CAAC,MAAM,CAAC,WAAW;oBAAE,OAAO;gBAChC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC9F,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;gBAAC,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC;gBAC7E,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,EAAE;oBACjC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACtB,IAAI,SAAS,IAAI,CAAC,MAAM,CAAC,WAAW;wBAAE,OAAO;oBAC7C,MAAM,CAAC,SAAS,GAAG,QAAQ,CAAC;oBAC5B,IAAI,KAAK,KAAK,IAAI;wBAAE,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;;wBAAM,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;oBACxG,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;oBAAC,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;oBAAC,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;gBACxG,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;YACZ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,SAAS;oBAAE,OAAO;gBACtB,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;gBAAC,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC;gBAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;QACD,OAAO,KAAW,SAAS,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;KACpF,CAAC;AACJ,CAAC","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"]}
1
+ {"version":3,"file":"systems.js","sourceRoot":"","sources":["../../src/files/systems.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAGlF,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,EAAE,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAC;AAC5I,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;AAExC,MAAM,UAAU,UAAU,CAAC,IAAoC,EAAE,UAAU,GAAG,KAAK;IACjF,IAAI,IAAI,EAAE,WAAW;QAAE,OAAO,aAAa,CAAC;IAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC;IAClE,MAAM,OAAO,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,IAAI,EAAE,eAAe,CAAC,IAAI,CAAC,CAAC;IACrF,IAAI,CAAC,KAAK;QAAE,OAAO,cAAc,CAAC;IAClC,IAAI,KAAK,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IACxC,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC/G,CAAC;AAOD,MAAM,UAAU,gBAAgB,CAAC,UAAuC,EAAE;IACxE,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAe,CAAC;IACpC,IAAI,KAA0C,CAAC;IAC/C,IAAI,IAA6B,CAAC;IAClC,IAAI,KAAsC,CAAC;IAC3C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,SAAS,KAAK,KAAW,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC3H,SAAS,MAAM;QACb,IAAI,KAAK;YAAE,OAAO;QAClB,OAAO,CAAC,SAAS,GAAG,gNAAgN,CAAC;QACrO,KAAK,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,CAAE,CAAC;QAAC,IAAI,GAAG,OAAO,CAAC,aAAa,CAAc,gBAAgB,CAAE,CAAC;QACtG,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAAC,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;QAAC,IAAI,CAAC,WAAW,GAAG,2BAA2B,CAAC;QAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxK,CAAC;IACD,SAAS,OAAO,CAAC,EAAU;QACzB,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACzC,GAAG,CAAC,SAAS,GAAG,kSAAkS,CAAC;QACnT,MAAM,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,SAAS,CAAE,CAAC;QAAC,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,EAAE,CAAC;QAC3E,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,aAAa,CAAC,gBAAgB,CAAE,EAAE,QAAQ,EAAE,GAAG,CAAC,aAAa,CAAC,iBAAiB,CAAE,EAAE,IAAI,EAAE,GAAG,CAAC,aAAa,CAAC,aAAa,CAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,eAAe,CAAE,EAAE,KAAK,EAAE,GAAG,CAAC,aAAa,CAAC,cAAc,CAAE,EAAE,SAAS,EAAE,GAAG,CAAC,aAAa,CAAC,kBAAkB,CAAE,EAAE,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC/V,CAAC;IACD,OAAO;QACL,OAAO;QACP,KAAK,KAAW,IAAI,CAAC,SAAS;YAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QAC1C,OAAO,KAAW,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,iBAAiB;YAAE,OAAO,CAAC,WAAW,GAAG,4BAA4B,CAAC,CAAC,CAAC;QACrH,QAAQ,CAAC,OAAe,IAAU,IAAI,CAAC,SAAS,EAAE,CAAC;YAAC,KAAK,EAAE,CAAC;YAAC,OAAO,CAAC,WAAW,GAAG,OAAO,CAAC;QAAC,CAAC,CAAC,CAAC;QAC/F,MAAM,CAAC,MAAiC;YACtC,IAAI,SAAS;gBAAE,OAAO;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;YACvC,MAAM,EAAE,CAAC;YACT,OAAO,CAAC,IAAK,EAAE,8JAA8J,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,eAAe,6DAA6D,CAAC,CAAC,CAAC,EAAE,mEAAmE,CAAC,CAAC;YACtW,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,CAAC,EAAE,EAAE,GAAG,CAAC,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAAC,CAAC;YAC3F,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,KAAM,CAAC,aAAa;oBAAE,KAAM,CAAC,MAAM,CAAC,KAAM,CAAC,CAAC;gBAAC,OAAO;YAAC,CAAC;YACnF,KAAM,CAAC,MAAM,EAAE,CAAC;YAChB,IAAI,MAAM,GAAG,KAAM,CAAC,iBAAiB,CAAC;YACtC,MAAM,CAAC,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC;YACxD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;gBAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;oBAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;gBAAC,CAAC;gBACnE,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;gBAC/D,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,OAAO,CAAC,YAAY,IAAI,OAAO,CAAC,QAAQ,MAAM,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC7G,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjI,GAAG,CAAC,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC;gBACvD,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC;gBACtI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;gBAChG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;gBACzF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBAC9F,IAAI,GAAG,CAAC,OAAO,KAAK,MAAM;oBAAE,KAAM,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBACrE,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,kBAAkB,CAAC;YAC1C,CAAC;YACD,IAAI,OAAO,YAAY,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,aAAa,KAAK,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAChJ,CAAC;QACD,OAAO,KAAW,IAAI,CAAC,SAAS;YAAE,KAAK,EAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;KAC/D,CAAC;AACJ,CAAC;AAGD,MAAM,UAAU,iBAAiB,CAAC,UAA2G,EAAE;IAC7I,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACvE,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,uBAAuB,CAAC;QAAE,OAAO,CAAC,SAAS,GAAG,2VAA2V,CAAC;IACra,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAc,uBAAuB,CAAE,CAAC;IAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAc,uBAAuB,CAAE,CAAC;IAC7E,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa,CAAoB,qBAAqB,CAAE,CAAC;IAC/E,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAoB,uBAAuB,CAAE,CAAC;IACnF,MAAM,QAAQ,GAAG,OAAO,CAAC,aAAa,CAAsB,UAAU,CAAE,CAAC;IACzE,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,KAA6C,CAAC;IAClD,IAAI,OAAO,GAAG,KAAK,EAAE,SAAS,GAAG,KAAK,CAAC;IACvC,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,EAAE,aAAa,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;IAC3F,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IAAC,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAC/F,SAAS,MAAM;QACb,IAAI,SAAS;YAAE,OAAO;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;YAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC;YAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC;YAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,KAAK,EAAC,IAAI,EAAC,MAAM,EAAC,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QACpK,OAAO,CAAC,WAAW,GAAG,GAAG,OAAO,CAAC,WAAW,IAAI,aAAa,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QACjF,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,KAAK,IAAI,KAAK,CAAC,OAAO,EAAC,IAAI,EAAC,MAAM,EAAC,CAAC,CAAC;QAC7E,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5E,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAC,QAAQ,EAAC,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,EAAC,OAAO,EAAC,CAAC,CAAC;QACvF,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,gBAAgB,CAAC;QAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAC,QAAQ,EAAC,IAAI,EAAC,OAAO,EAAC,KAAK,EAAC,oBAAoB,EAAC,CAAC,CAAC;QAC3H,QAAQ,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,KAAK,YAAY,CAAC;QAAC,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC;IAClF,CAAC;IACD,OAAO;QACL,OAAO;QACP,MAAM,CAAC,KAAiC,IAAU,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC5G,UAAU,CAAC,KAAc,IAAU,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAC/D,QAAQ,CAAC,KAAa,EAAE,UAAyC,EAAE,IAAU,IAAI,CAAC,SAAS,EAAE,CAAC;YAAC,KAAK,GAAG,KAAK,CAAC;YAAC,UAAU,GAAG,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC;YAAC,MAAM,EAAE,CAAC;QAAC,CAAC,CAAC,CAAC;QACnK,OAAO,KAAW,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;KACpL,CAAC;AACJ,CAAC;AAGD,MAAM,UAAU,mBAAmB,CAAC,MAAkD;IACpF,IAAI,MAAM,KAAK,UAAU;QAAE,OAAO,+TAA+T,CAAC;IAClW,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,gJAAgJ,CAAC;IAClL,IAAI,MAAM,KAAK,QAAQ;QAAE,OAAO,mKAAmK,CAAC;IACpM,OAAO,6DAA6D,CAAC;AACvE,CAAC;AACD,MAAM,UAAU,uBAAuB,CAAC,UAA2G,EAAE;IACnJ,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA4G,CAAC;IACpI,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,KAAK,GAAG,CAAC,KAAY,EAAE,EAAE;QAC7B,MAAM,MAAM,GAAI,KAAK,CAAC,MAAkB,CAAC,OAAO,CAAc,gDAAgD,CAAC,CAAC;QAChH,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAO;QACjD,IAAI,MAAM,CAAC,OAAO,CAAC,YAAY;YAAE,OAAO,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;aAC1E,IAAI,MAAM,CAAC,OAAO,CAAC,iBAAiB;YAAE,OAAO,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACnG,CAAC,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,SAAS;QAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAClF,OAAO;QACL,OAAO;QACP,MAAM,CAAC,OAAO;YACZ,IAAI,SAAS;gBAAE,OAAO;YACtB,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,OAAO;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;oBAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;oBAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAAC,CAAC;YACrG,IAAI,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YACvC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,IAAI,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACtC,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;oBAAC,OAAO,CAAC,SAAS,GAAG,uBAAuB,CAAC;oBAC/F,OAAO,CAAC,SAAS,GAAG,yDAAyD,IAAI,CAAC,OAAO,CAAC,6TAA6T,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC;oBAChb,KAAK,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,CAAE,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAE,EAAE,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,2BAA2B,CAAE,EAAE,CAAC;oBACnL,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;gBACnC,CAAC;gBACD,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,MAAM,CAAC,SAAS,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC7D,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,EAAC,KAAK,EAAC,mBAAmB,EAAC,CAAC,CAAC;gBAC7D,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,EAAC,KAAK,EAAC,iCAAiC,EAAC,CAAC,CAAC;gBAC9E,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,KAAK,MAAM,CAAC,EAAE;oBAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,MAAM,CAAC,EAAE,CAAC;gBAC/F,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,KAAK,MAAM,CAAC,KAAK;oBAAE,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,GAAG,MAAM,CAAC,KAAK,CAAC;gBACrH,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM;oBAAE,OAAO,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;gBAC1E,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,CAAC;YAC5C,CAAC;YACD,KAAK,CAAC,MAAM,EAAE,CAAC;YACf,IAAI,OAAO,YAAY,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,aAAa,KAAK,OAAO;gBAAE,OAAO,CAAC,KAAK,CAAC,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAChJ,CAAC;QACD,OAAO,KAAW,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;KACzK,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAAuH;IACxJ,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,EAAC,YAAY,EAAC,QAAQ,EAAC,QAAQ,EAAC,KAAK,EAAC,CAAC,CAAC;IAErF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgC,CAAC;IACxD,MAAM,OAAO,GAAG,CAAC,MAAyB,EAAE,KAAgB,EAAQ,EAAE;QACpE,IAAI,KAAK,CAAC,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YAC1C,MAAM,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI;gBAAE,MAAM,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;;gBAAM,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YACpH,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QAC7B,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC;QAClC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAC,QAAQ,EAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAC,CAAC,CAAC;QAC/D,KAAK,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,IAAI,CAAC,MAAyB,EAAE,MAA2B,EAAE,YAAoB;YACrF,IAAI,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,MAAM;gBAAE,OAAO;YAC/D,MAAM,KAAK,GAAc,EAAE,UAAU,EAAC,WAAW,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,EAAE,KAAK,EAAC,MAAM,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,KAAK,EAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAC,KAAK,EAAE,CAAC;YAC3K,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC3B,MAAM,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,EAAE,EAAC,IAAI,EAAC,MAAM,EAAC,CAAC,CAAC;YAAC,MAAM,CAAC,OAAO,CAAC,WAAW,GAAG,MAAM,CAAC;YAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAC,QAAQ,EAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAC,CAAC,CAAC;YAC7I,IAAI,CAAC;gBACH,MAAM,MAAM,EAAE,CAAC;gBACf,IAAI,SAAS;oBAAE,OAAO;gBACtB,MAAM,CAAC,MAAM,CAAC,EAAC,OAAO,EAAC,YAAY,EAAC,IAAI,EAAC,MAAM,EAAC,CAAC,CAAC;gBAClD,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;oBAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;oBAAC,OAAO;gBAAC,CAAC;gBAC5D,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC9F,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;gBAAC,MAAM,CAAC,KAAK,GAAG,YAAY,CAAC;gBAC7E,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC;gBACvB,KAAK,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,KAAK;oBAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YACrH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,SAAS;oBAAE,OAAO;gBACtB,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;QACD,OAAO,KAAW,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,OAAO;YAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;KAC9I,CAAC;AACJ,CAAC","sourcesContent":["import { icon, setText } from '../dom.js';\nimport type { UIBlock } from '../core.js';\nimport { adoptAction, adoptFeedback, adoptProductParts } from '../foundations.js';\nimport type { ActionController } from '../foundations.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 const parts = adoptProductParts(element);\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) { parts.action(check).update({pending}); parts.action(install).update({pending}); parts.feedback(message).update({message:error,tone:'info'}); return; }\n version.textContent = `${options.productName || 'Application'} ${state.version}`;\n parts.feedback(message).update({message:error || state.message,tone:'info'});\n const busy = ['checking', 'installing', 'restarting'].includes(state.phase);\n parts.action(check).update({disabled:!state.enabled || (busy && !retryCheck),pending});\n install.hidden = !state.availableVersion; parts.action(install).update({disabled:busy,pending,label:'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; parts.destroy(); 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 parts = adoptProductParts(element);\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 parts.action(entry.open).update({label:'Open conversation'});\n parts.action(entry.dismiss).update({label:'Dismiss completion notification'});\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 parts.update();\n if (focused instanceof HTMLElement && element.contains(focused) && document.activeElement !== focused) focused.focus({ preventScroll: true });\n },\n destroy(): void { if (destroyed) return; destroyed = true; parts.destroy(); 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 notice = adoptFeedback(options.status, {presentation:'inline',autoHide:false});\n type CopyEntry = { controller: ActionController; original: ChildNode[]; label: string | null; title: string; displayed: boolean; timer?: ReturnType<typeof setTimeout> };\n const entries = new Map<HTMLButtonElement, CopyEntry>();\n const release = (button: HTMLButtonElement, entry: CopyEntry): void => {\n if (entry.timer) clearTimeout(entry.timer);\n if (entry.displayed && button.isConnected) {\n button.replaceChildren(...entry.original);\n if (entry.label === null) button.removeAttribute('aria-label'); else button.setAttribute('aria-label', entry.label);\n button.title = entry.title;\n }\n delete button.dataset.copyPending;\n entry.controller.update({disabled:options.isDisabled(button)});\n entry.controller.destroy();\n entries.delete(button);\n };\n return {\n async copy(button: HTMLButtonElement, action: () => Promise<void>, announcement: string): Promise<void> {\n if (destroyed || button.dataset.copyPending === 'true') return;\n const entry: CopyEntry = { controller:adoptAction(button), original:[...button.childNodes], label:button.getAttribute('aria-label'), title:button.title, displayed:false };\n entries.set(button, entry);\n notice.update({message:'',tone:'info'}); button.dataset.copyPending = 'true'; entry.controller.update({disabled:options.isDisabled(button)});\n try {\n await action();\n if (destroyed) return;\n notice.update({message:announcement,tone:'info'});\n if (!button.isConnected) { release(button, entry); return; }\n button.innerHTML = button.classList.contains('pui-message-action') ? icon('check') : 'Copied';\n button.setAttribute('aria-label', announcement); button.title = announcement;\n entry.displayed = true;\n entry.timer = setTimeout(() => { if (!destroyed && entries.get(button) === entry) release(button, entry); }, 1500);\n } catch (error) {\n if (destroyed) return;\n release(button, entry); options.onError(error);\n }\n },\n destroy(): void { if (destroyed) return; destroyed = true; for (const [button, entry] of entries) release(button, entry); notice.destroy(); },\n };\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { UIBlock } from '../core.js';
1
+ import type { UIBlock, UIAction } from '../core.js';
2
2
  export interface ProviderActionsState {
3
3
  id: string;
4
4
  oauth: boolean;
@@ -18,10 +18,10 @@ export declare function resourceActionsMarkup(kind: string, resource: {
18
18
  name: string;
19
19
  path: string;
20
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>;
21
+ export interface FeatureActionOptions {
22
+ onAction?: (action: string, target: DOMStringMap) => ReturnType<UIAction>;
23
+ onError?: (message: string) => void;
24
+ }
25
+ export declare function createProviderActions(state: ProviderActionsState, options?: FeatureActionOptions): UIBlock<ProviderActionsState>;
26
+ export declare function createResourceActions(state: ResourceActionsState, options?: FeatureActionOptions): UIBlock<ResourceActionsState>;
27
27
  //# sourceMappingURL=actions.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/forms/actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGpD,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;AACD,MAAM,WAAW,oBAAoB;IAAE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,KAAK,UAAU,CAAC,QAAQ,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CAAC;AAiBtJ,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAyD;AAC9L,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAmH"}
@@ -1,19 +1,39 @@
1
1
  import { escape } from '../dom.js';
2
+ import { adoptAction } from '../foundations.js';
2
3
  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
4
  export function resourceActionsMarkup(kind, resource, workspaceId) {
4
5
  const command = kind === 'skill' ? `/skill:${resource.name} ` : `/${resource.name} `;
5
6
  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
  }
7
- function actions(initial, markup, onAction) {
8
+ function actions(initial, markup, options) {
8
9
  const element = document.createElement('div');
9
10
  element.className = 'pui-feature-actions';
10
- const abort = new AbortController();
11
- const update = (state) => { element.innerHTML = markup(state); };
11
+ let destroyed = false, signature = '';
12
+ let handles = [];
13
+ const update = (state) => {
14
+ if (destroyed)
15
+ return;
16
+ const next = markup(state);
17
+ if (next === signature)
18
+ return;
19
+ signature = next;
20
+ handles.forEach(handle => handle.destroy());
21
+ handles = [];
22
+ element.innerHTML = next;
23
+ if (options.onAction)
24
+ for (const button of element.querySelectorAll('[data-sdk]')) {
25
+ // Stop before the owned callback runs: it may synchronously replace or
26
+ // destroy this block, removing listeners further up the bubbling path.
27
+ const stopDelegation = (event) => event.stopPropagation();
28
+ button.addEventListener('click', stopDelegation);
29
+ const handle = adoptAction(button, { onAction: () => options.onAction(button.dataset.sdk, button.dataset), onError: options.onError });
30
+ handles.push({ ...handle, destroy() { button.removeEventListener('click', stopDelegation); handle.destroy(); } });
31
+ }
32
+ };
12
33
  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(); } };
34
+ return { element, update, destroy() { if (destroyed)
35
+ return; destroyed = true; handles.forEach(handle => handle.destroy()); handles = []; element.remove(); } };
16
36
  }
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); }
37
+ export function createProviderActions(state, options = {}) { return actions(state, providerActionsMarkup, options); }
38
+ export function createResourceActions(state, options = {}) { return actions(state, (state) => resourceActionsMarkup(state.kind, state.resource, state.workspaceId), options); }
19
39
  //# sourceMappingURL=actions.js.map
@@ -1 +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"]}
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/forms/actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock, UIAction } from '../core.js';\nimport { escape } from '../dom.js';\nimport {adoptAction, type ActionController} from '../foundations.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}\nexport interface FeatureActionOptions {onAction?: (action: string, target: DOMStringMap) => ReturnType<UIAction>; onError?: (message: string) => void}\nfunction actions<State>(initial: State, markup: (state: State) => string, options: FeatureActionOptions): UIBlock<State> {\n const element = document.createElement('div'); element.className = 'pui-feature-actions'; let destroyed = false, signature = ''; let handles: ActionController[] = [];\n const update = (state: Readonly<State>) => {\n if (destroyed) return; const next = markup(state); if (next === signature) return; signature = next;\n handles.forEach(handle => handle.destroy()); handles = []; element.innerHTML = next;\n if (options.onAction) for (const button of element.querySelectorAll<HTMLButtonElement>('[data-sdk]')) {\n // Stop before the owned callback runs: it may synchronously replace or\n // destroy this block, removing listeners further up the bubbling path.\n const stopDelegation = (event: Event) => event.stopPropagation();\n button.addEventListener('click',stopDelegation);\n const handle = adoptAction(button, {onAction: () => options.onAction!(button.dataset.sdk!, button.dataset), onError: options.onError});\n handles.push({...handle,destroy() {button.removeEventListener('click',stopDelegation);handle.destroy();}});\n }\n }; update(initial);\n return {element, update, destroy() {if (destroyed) return; destroyed = true; handles.forEach(handle => handle.destroy()); handles = []; element.remove();}};\n}\nexport function createProviderActions(state: ProviderActionsState, options: FeatureActionOptions = {}): UIBlock<ProviderActionsState> {return actions(state, providerActionsMarkup, options);}\nexport function createResourceActions(state: ResourceActionsState, options: FeatureActionOptions = {}): UIBlock<ResourceActionsState> {return actions(state, (state) => resourceActionsMarkup(state.kind, state.resource, state.workspaceId), options);}\n"]}
@@ -1,4 +1,4 @@
1
- import type { UIBlock } from '../core.js';
1
+ import type { ActionResult, UIBlock } from '../core.js';
2
2
  export interface CustomModelValue {
3
3
  id: string;
4
4
  name?: string;
@@ -54,6 +54,7 @@ export declare function createCustomModelRow(model: CustomModelRowState, index?:
54
54
  };
55
55
  export declare function createCustomConnectionEditor(state: CustomConnectionState, options?: {
56
56
  idPrefix?: string;
57
+ onSubmit?: (draft: CustomConnectionDraft) => ActionResult | Promise<ActionResult>;
57
58
  }): UIBlock<CustomConnectionState> & {
58
59
  read(): CustomConnectionDraft;
59
60
  readonly form: HTMLFormElement;
@@ -1 +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"}
1
+ {"version":3,"file":"custom-connection.d.ts","sourceRoot":"","sources":["../../src/forms/custom-connection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,YAAY,EAAC,OAAO,EAAC,MAAM,YAAY,CAAC;AAGrD,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,CAe3K;AACD,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,qBAAqB,EAAE,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC,GAAG;IAAC,IAAI,IAAI,qBAAqB,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAC,CAcjS"}
@@ -1,5 +1,6 @@
1
- import { scopeIds, localField } from './identity.js';
1
+ import { scopeIds, localField, bindFormSubmission } from './identity.js';
2
2
  import { escape, uniqueId } from '../dom.js';
3
+ import { adoptProductParts } from '../foundations.js';
3
4
  const protocols = ['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai', 'mistral-conversations'];
4
5
  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
6
  export function createCustomModelRow(model, index = 0) {
@@ -11,28 +12,30 @@ export function createCustomModelRow(model, index = 0) {
11
12
  error.className = 'pui-form-error';
12
13
  error.setAttribute('role', 'alert');
13
14
  element.append(error);
15
+ const parts = adoptProductParts(element);
14
16
  let destroyed = false;
15
17
  const input = (name) => field.querySelector(`[name="${name}"]`);
16
18
  const reset = (value) => {
17
19
  if (destroyed)
18
20
  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');
21
+ parts.control(input('id')).reset(value.id);
22
+ parts.control(input('name')).reset(value.name || '');
23
+ parts.control(input('contextWindow')).reset(value.contextWindow === undefined ? '' : String(value.contextWindow));
24
+ parts.control(input('maxTokens')).reset(value.maxTokens === undefined ? '' : String(value.maxTokens));
25
+ parts.control(input('reasoning')).reset(!!value.reasoning);
26
+ parts.control(input('images')).reset(!!value.input?.includes('image'));
25
27
  for (const rate of ['input', 'output', 'cacheRead', 'cacheWrite'])
26
- input('price-' + rate).value = value.cost?.[rate] === undefined ? '' : String(value.cost[rate]);
28
+ parts.control(input('price-' + rate)).reset(value.cost?.[rate] === undefined ? '' : String(value.cost[rate]));
27
29
  };
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 } });
30
+ const value = (name) => parts.control(input(name)).getValue();
31
+ const read = () => ({ sourceIndex: index, id: value('id').trim(), name: value('name').trim(), contextWindow: Number(value('contextWindow')), maxTokens: Number(value('maxTokens')), reasoning: parts.control(input('reasoning')).getChecked(), images: parts.control(input('images')).getChecked(), prices: { input: value('price-input'), output: value('price-output'), cacheRead: value('price-cacheRead'), cacheWrite: value('price-cacheWrite') } });
29
32
  const update = (state) => { if (destroyed)
30
33
  return; if (state.disabled !== undefined)
31
34
  field.disabled = state.disabled; if (state.error !== undefined)
32
- error.textContent = state.error; };
35
+ parts.feedback(error).update({ message: state.error, tone: 'error' }); };
33
36
  update(model);
34
37
  return { element, read, reset, update, destroy() { if (destroyed)
35
- return; destroyed = true; element.remove(); } };
38
+ return; destroyed = true; parts.destroy(); element.remove(); } };
36
39
  }
37
40
  export function createCustomConnectionEditor(state, options = {}) {
38
41
  const element = document.createElement('div');
@@ -42,24 +45,28 @@ export function createCustomConnectionEditor(state, options = {}) {
42
45
  scopeIds(element, prefix);
43
46
  const $ = (id) => localField(element, prefix + id);
44
47
  const form = $('custom-form');
45
- const models = [...state.models];
46
- let destroyed = false;
48
+ const parts = adoptProductParts(element), rows = new Map();
49
+ let nextIndex = 0, destroyed = false, pending = false;
47
50
  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; };
51
+ let current = state;
52
+ const add = (model) => { const index = nextIndex++; const row = createCustomModelRow(model, index); rows.set(index, row); $('custom-models').append(row.element); if (current.disabled || pending)
53
+ for (const button of row.element.querySelectorAll('button'))
54
+ button.disabled = true; };
55
+ state.models.forEach(add);
56
+ $('add-custom-model').addEventListener('click', () => { if (!current.disabled && !pending)
57
+ add({ id: '', contextWindow: 128000, maxTokens: 8192 }); }, { signal: abort.signal });
58
+ $('custom-models').addEventListener('click', event => { const target = event.target.closest('[data-remove-model]'); if (!target || target.disabled || current.disabled || pending)
59
+ return; const index = Number(target.closest('[data-model-index]')?.dataset.modelIndex); rows.get(index)?.destroy(); rows.delete(index); }, { signal: abort.signal });
60
+ const read = () => ({ name: parts.control($('custom-name')).getValue(), baseUrl: parts.control($('custom-url')).getValue(), api: parts.control($('custom-api')).getValue(), apiKey: parts.control($('custom-key')).getValue(), models: [...rows.values()].map(row => row.read()) });
61
+ const update = (next) => { if (destroyed)
62
+ return; current = next; if (next.error !== undefined)
63
+ parts.feedback(element.querySelector('[data-custom-error]')).update({ message: next.error, tone: 'error' }); parts.update(); for (const button of form.querySelectorAll('button'))
64
+ parts.action(button).update({ disabled: next.disabled, pending: button.hasAttribute('data-close') ? false : pending }); };
65
+ const submission = bindFormSubmission(form, { read, unavailable: () => !!current.disabled, onSubmit: options.onSubmit, onPending(value) { pending = value; update(current); }, onError(error) { update({ ...current, error }); } });
60
66
  update(state);
61
67
  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(); } };
68
+ return; destroyed = true; submission.destroy(); abort.abort(); for (const row of rows.values())
69
+ row.destroy(); rows.clear(); for (const input of element.querySelectorAll('input[type="password"]'))
70
+ parts.control(input).reset(''); parts.destroy(); element.remove(); } };
64
71
  }
65
72
  //# sourceMappingURL=custom-connection.js.map