pith-ui 0.1.1 → 0.2.0-app.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 (140) hide show
  1. package/AI.md +33 -8
  2. package/API.md +424 -268
  3. package/APP-API.md +2019 -0
  4. package/APP.md +137 -0
  5. package/README.md +37 -4
  6. package/dist/app/files.d.ts +68 -0
  7. package/dist/app/files.d.ts.map +1 -0
  8. package/dist/app/files.js +177 -0
  9. package/dist/app/files.js.map +1 -0
  10. package/dist/app/index.d.ts +57 -0
  11. package/dist/app/index.d.ts.map +1 -0
  12. package/dist/app/index.js +300 -0
  13. package/dist/app/index.js.map +1 -0
  14. package/dist/app/internal.d.ts +31 -0
  15. package/dist/app/internal.d.ts.map +1 -0
  16. package/dist/app/internal.js +2 -0
  17. package/dist/app/internal.js.map +1 -0
  18. package/dist/app/resources.d.ts +89 -0
  19. package/dist/app/resources.d.ts.map +1 -0
  20. package/dist/app/resources.js +190 -0
  21. package/dist/app/resources.js.map +1 -0
  22. package/dist/app/session.d.ts +14 -0
  23. package/dist/app/session.d.ts.map +1 -0
  24. package/dist/app/session.js +12 -0
  25. package/dist/app/session.js.map +1 -0
  26. package/dist/app/settings.d.ts +109 -0
  27. package/dist/app/settings.d.ts.map +1 -0
  28. package/dist/app/settings.js +279 -0
  29. package/dist/app/settings.js.map +1 -0
  30. package/dist/app/workspace.d.ts +228 -0
  31. package/dist/app/workspace.d.ts.map +1 -0
  32. package/dist/app/workspace.js +402 -0
  33. package/dist/app/workspace.js.map +1 -0
  34. package/dist/composer.d.ts.map +1 -1
  35. package/dist/composer.js +37 -27
  36. package/dist/composer.js.map +1 -1
  37. package/dist/content/conversation-reader.d.ts.map +1 -1
  38. package/dist/content/conversation-reader.js +8 -1
  39. package/dist/content/conversation-reader.js.map +1 -1
  40. package/dist/content/conversation-view.d.ts.map +1 -1
  41. package/dist/content/conversation-view.js +34 -8
  42. package/dist/content/conversation-view.js.map +1 -1
  43. package/dist/content/image-viewer.d.ts.map +1 -1
  44. package/dist/content/image-viewer.js +11 -3
  45. package/dist/content/image-viewer.js.map +1 -1
  46. package/dist/content/paged-preview.d.ts +1 -1
  47. package/dist/content/paged-preview.d.ts.map +1 -1
  48. package/dist/content/paged-preview.js +4 -2
  49. package/dist/content/paged-preview.js.map +1 -1
  50. package/dist/content/views.d.ts +3 -0
  51. package/dist/content/views.d.ts.map +1 -1
  52. package/dist/content/views.js +110 -41
  53. package/dist/content/views.js.map +1 -1
  54. package/dist/content/welcome.d.ts.map +1 -1
  55. package/dist/content/welcome.js +5 -2
  56. package/dist/content/welcome.js.map +1 -1
  57. package/dist/dom.js +1 -1
  58. package/dist/dom.js.map +1 -1
  59. package/dist/files/panels.d.ts +2 -0
  60. package/dist/files/panels.d.ts.map +1 -1
  61. package/dist/files/panels.js +202 -128
  62. package/dist/files/panels.js.map +1 -1
  63. package/dist/files/systems.d.ts.map +1 -1
  64. package/dist/files/systems.js +37 -64
  65. package/dist/files/systems.js.map +1 -1
  66. package/dist/forms/actions.d.ts +24 -2
  67. package/dist/forms/actions.d.ts.map +1 -1
  68. package/dist/forms/actions.js +54 -25
  69. package/dist/forms/actions.js.map +1 -1
  70. package/dist/forms/custom-connection.d.ts.map +1 -1
  71. package/dist/forms/custom-connection.js +3 -2
  72. package/dist/forms/custom-connection.js.map +1 -1
  73. package/dist/forms/history.d.ts +1 -1
  74. package/dist/forms/history.d.ts.map +1 -1
  75. package/dist/forms/history.js +78 -15
  76. package/dist/forms/history.js.map +1 -1
  77. package/dist/forms/identity.d.ts.map +1 -1
  78. package/dist/forms/identity.js +9 -25
  79. package/dist/forms/identity.js.map +1 -1
  80. package/dist/forms/model-editor.js +3 -3
  81. package/dist/forms/model-editor.js.map +1 -1
  82. package/dist/forms/panels.d.ts +9 -2
  83. package/dist/forms/panels.d.ts.map +1 -1
  84. package/dist/forms/panels.js +79 -88
  85. package/dist/forms/panels.js.map +1 -1
  86. package/dist/forms/provider-login.d.ts.map +1 -1
  87. package/dist/forms/provider-login.js +20 -36
  88. package/dist/forms/provider-login.js.map +1 -1
  89. package/dist/forms/resource-editor.js +5 -4
  90. package/dist/forms/resource-editor.js.map +1 -1
  91. package/dist/foundations.d.ts +1 -1
  92. package/dist/foundations.d.ts.map +1 -1
  93. package/dist/foundations.js +22 -38
  94. package/dist/foundations.js.map +1 -1
  95. package/dist/navigation/context-menu.d.ts +7 -1
  96. package/dist/navigation/context-menu.d.ts.map +1 -1
  97. package/dist/navigation/context-menu.js +60 -57
  98. package/dist/navigation/context-menu.js.map +1 -1
  99. package/dist/navigation/navigation.d.ts +13 -0
  100. package/dist/navigation/navigation.d.ts.map +1 -1
  101. package/dist/navigation/navigation.js +96 -130
  102. package/dist/navigation/navigation.js.map +1 -1
  103. package/dist/operation.d.ts +12 -0
  104. package/dist/operation.d.ts.map +1 -0
  105. package/dist/operation.js +49 -0
  106. package/dist/operation.js.map +1 -0
  107. package/dist/primitives.d.ts +8 -1
  108. package/dist/primitives.d.ts.map +1 -1
  109. package/dist/primitives.js +114 -143
  110. package/dist/primitives.js.map +1 -1
  111. package/dist/reconcile.d.ts +11 -0
  112. package/dist/reconcile.d.ts.map +1 -0
  113. package/dist/reconcile.js +36 -0
  114. package/dist/reconcile.js.map +1 -0
  115. package/dist/select.d.ts +14 -0
  116. package/dist/select.d.ts.map +1 -0
  117. package/dist/select.js +304 -0
  118. package/dist/select.js.map +1 -0
  119. package/dist/styles.css +439 -370
  120. package/dist/surface.d.ts +31 -0
  121. package/dist/surface.d.ts.map +1 -1
  122. package/dist/surface.js +193 -3
  123. package/dist/surface.js.map +1 -1
  124. package/dist/tasks/approval.d.ts.map +1 -1
  125. package/dist/tasks/approval.js +83 -45
  126. package/dist/tasks/approval.js.map +1 -1
  127. package/dist/tasks/permission-element.d.ts +3 -0
  128. package/dist/tasks/permission-element.d.ts.map +1 -0
  129. package/dist/tasks/permission-element.js +10 -0
  130. package/dist/tasks/permission-element.js.map +1 -0
  131. package/dist/tasks/permission.d.ts.map +1 -1
  132. package/dist/tasks/permission.js +37 -18
  133. package/dist/tasks/permission.js.map +1 -1
  134. package/dist/tasks/runtime.d.ts.map +1 -1
  135. package/dist/tasks/runtime.js +32 -15
  136. package/dist/tasks/runtime.js.map +1 -1
  137. package/dist/workspace.d.ts.map +1 -1
  138. package/dist/workspace.js +93 -40
  139. package/dist/workspace.js.map +1 -1
  140. package/package.json +42 -8
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission-element.js","sourceRoot":"","sources":["../../src/tasks/permission-element.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAE/B,sEAAsE;AACtE,MAAM,UAAU,wBAAwB;IACtC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IAAC,OAAO,CAAC,SAAS,GAAG,wBAAwB,CAAC;IAAC,OAAO,CAAC,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC;IACpI,OAAO,CAAC,SAAS,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,4OAA4O,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IAChS,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import {icon} from '../dom.js';\n\n/** Internal retained-node template shared with the composer shell. */\nexport function permissionControlElement(): HTMLLabelElement {\n const element = document.createElement('label'); element.className = 'pui-permission-control'; element.dataset.puiSelectAnchor = '';\n element.innerHTML = `${icon('shield')}<span class=\"pui-sr-only\">Conversation permissions</span><select><option value=\"ask\">Ask before changes</option><option value=\"workspace-write\">Allow workspace changes</option><option value=\"full-access\">Full access</option></select>${icon('down')}`;\n return element;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../../src/tasks/permission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGxD,MAAM,WAAW,eAAe;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AACrF,MAAM,WAAW,iBAAiB;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAC;AAC9G,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG;IAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAA;CAAC,CAQ9K;AACD,MAAM,WAAW,gBAAgB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAC;AACjI,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAK3M;AACD,MAAM,WAAW,sBAAsB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AAC/F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAQrN"}
1
+ {"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../../src/tasks/permission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAKxD,MAAM,WAAW,eAAe;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AACrF,MAAM,WAAW,iBAAiB;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAC;AAC9G,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG;IAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAA;CAAC,CAQ9K;AACD,MAAM,WAAW,gBAAgB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAC;AACjI,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAmB3M;AACD,MAAM,WAAW,sBAAsB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AAC/F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAQrN"}
@@ -1,11 +1,13 @@
1
1
  import { scopeIds } from '../forms/identity.js';
2
- import { escape, icon, uniqueId } from '../dom.js';
2
+ import { escape, icon, uniqueId, setText } from '../dom.js';
3
3
  import { adoptAction, adoptFeedback, adoptProductParts } from '../foundations.js';
4
+ import { permissionControlElement } from './permission-element.js';
5
+ import { reconcileKeyed } from '../reconcile.js';
4
6
  export function createFullAccessConfirmation(initial, options = {}) {
5
7
  const state = initial;
6
8
  const element = document.createElement('div');
7
9
  element.className = 'pui-full-access-confirmation';
8
- element.innerHTML = `<div class="pui-modal-heading"><div><span class="pui-eyebrow">CONVERSATION PERMISSIONS</span><h2 id="full-access-title">Enable full access?</h2></div><button class="pui-quiet-icon" data-close="permissions-dialog" aria-label="Cancel full access">${icon('close')}</button></div><p class="pui-modal-description">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong>${escape(state.title || 'this conversation')}</strong>.</p><div class="pui-full-access-explanation">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class="pui-permission-revoke-note">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id="permissions-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button class="pui-secondary-button" type="button" data-close="permissions-dialog">Cancel</button><button class="pui-primary-button pui-full-access-confirm" id="enable-full-access" type="button">Enable full access</button></div>`;
10
+ element.innerHTML = `<div class="pui-modal-heading"><div><span class="pui-eyebrow">CONVERSATION PERMISSIONS</span><h2 id="full-access-title">Enable full access?</h2></div><button type="button" class="pui-quiet-icon" data-pui-close data-close="permissions-dialog" aria-label="Cancel full access">${icon('close')}</button></div><p class="pui-modal-description">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong data-permission-target>${escape(state.title || 'this conversation')}</strong>.</p><div class="pui-full-access-explanation">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class="pui-permission-revoke-note">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id="permissions-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button class="pui-secondary-button" type="button" data-pui-close data-close="permissions-dialog">Cancel</button><button class="pui-primary-button pui-full-access-confirm" id="enable-full-access" type="button">Enable full access</button></div>`;
9
11
  let destroyed = false;
10
12
  const confirmButton = element.querySelector('#enable-full-access');
11
13
  const error = element.querySelector('#permissions-error');
@@ -13,7 +15,7 @@ export function createFullAccessConfirmation(initial, options = {}) {
13
15
  const feedback = adoptFeedback(error, { presentation: 'inline', autoHide: false });
14
16
  const action = adoptAction(confirmButton, { onAction: options.onConfirm ? () => { update({ ...current, error: '' }); return options.onConfirm(); } : undefined, onError: message => { update({ ...current, error: message }); } });
15
17
  const update = (state) => { if (destroyed)
16
- return; current = state; action.update({ disabled: !!state.disabled }); if (state.error !== undefined)
18
+ return; current = state; setText(element.querySelector('[data-permission-target]'), state.title || 'this conversation'); action.update({ disabled: !!state.disabled }); if (state.error !== undefined)
17
19
  feedback.update({ message: state.error, tone: 'error' }); };
18
20
  update(initial);
19
21
  scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');
@@ -24,27 +26,44 @@ export function createTaskFailure(initial, options = {}) {
24
26
  const element = options.element || document.createElement('section');
25
27
  if (!options.element)
26
28
  element.className = 'pui-task-failure';
27
- const abort = new AbortController();
28
- let signature = '';
29
+ const message = document.createElement('strong'), advice = document.createElement('p'), actions = document.createElement('div');
30
+ const rows = new Map();
29
31
  let destroyed = false;
30
- const update = (state) => { if (destroyed)
31
- return; const next = JSON.stringify(state); if (next === signature)
32
- return; signature = next; element.hidden = !state.visible; element.innerHTML = state.visible ? `<strong>${escape(state.message)}</strong><p>${escape(state.advice)}</p><div>${state.canContinue ? `<button class="pui-secondary-button" type="button" data-action="continue" data-idle-action data-idle-workspace="${escape(state.workspaceId)}">Review and continue</button>` : '<button class="pui-secondary-button" type="button" data-action="settings">Check model settings</button>'}<button class="pui-secondary-button" type="button" data-action="diagnostics">Save diagnostics</button></div>` : ''; };
33
- element.addEventListener('click', (event) => { const action = event.target.closest('[data-action]')?.dataset.action; if (action === 'continue')
34
- options.onContinue?.(); if (action === 'settings')
35
- options.onSettings?.(); if (action === 'diagnostics')
36
- options.onDiagnostics?.(); }, { signal: abort.signal });
32
+ const release = (row) => { if (row.stop)
33
+ row.element.removeEventListener('click', row.stop); row.handle.destroy(); };
34
+ const update = (state) => {
35
+ if (destroyed)
36
+ return;
37
+ element.hidden = !state.visible;
38
+ if (!state.visible)
39
+ return;
40
+ if (message.parentElement !== element)
41
+ element.replaceChildren(message, advice, actions);
42
+ setText(message, state.message);
43
+ setText(advice, state.advice);
44
+ const views = [{ action: state.canContinue ? 'continue' : 'settings', label: state.canContinue ? 'Review and continue' : 'Check model settings', callback: state.canContinue ? options.onContinue : options.onSettings }, { action: 'diagnostics', label: 'Save diagnostics', callback: options.onDiagnostics }];
45
+ reconcileKeyed(actions, rows, views, { key: view => view.action, element: row => row.element, create(view) {
46
+ const button = document.createElement('button');
47
+ button.type = 'button';
48
+ button.className = 'pui-secondary-button';
49
+ button.dataset.action = view.action;
50
+ const stop = view.callback ? (event) => event.stopPropagation() : undefined;
51
+ if (stop)
52
+ button.addEventListener('click', stop);
53
+ return { element: button, handle: adoptAction(button, { onAction: view.callback }), stop };
54
+ }, update(row, view) { row.handle.update({ label: view.label }); if (view.action === 'continue') {
55
+ row.element.dataset.idleAction = '';
56
+ row.element.dataset.idleWorkspace = state.workspaceId || '';
57
+ } }, destroy: release });
58
+ };
37
59
  update(initial);
38
60
  return { element, update, destroy() { if (destroyed)
39
- return; destroyed = true; abort.abort(); if (!options.element)
61
+ return; destroyed = true; for (const row of rows.values())
62
+ release(row); rows.clear(); if (!options.element)
40
63
  element.remove(); } };
41
64
  }
42
65
  export function createPermissionControl(initial, options = {}) {
43
- const element = options.element || document.createElement('label');
44
- if (!options.element) {
45
- element.className = 'pui-permission-control';
46
- element.innerHTML = `${icon('shield')}<span class="pui-sr-only">Conversation permissions</span><select aria-describedby="pui-permission-description"><option value="ask">Ask before changes</option><option value="workspace-write">Allow workspace changes</option><option value="full-access">Full access</option></select>${icon('down')}`;
47
- }
66
+ const element = options.element || permissionControlElement();
48
67
  const selector = element.querySelector('select');
49
68
  const abort = new AbortController();
50
69
  let destroyed = false;
@@ -1 +1 @@
1
- {"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/tasks/permission.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { ActionResult, UIBlock } from '../core.js';\nimport { escape, icon, uniqueId } from '../dom.js';\nimport {adoptAction,adoptFeedback,adoptProductParts} from '../foundations.js';\nexport interface FullAccessState {title?: string; error?: string; disabled?: boolean}\nexport interface FullAccessOptions {idPrefix?: string; onConfirm?: () => ActionResult | Promise<ActionResult>}\nexport function createFullAccessConfirmation(initial: FullAccessState, options: FullAccessOptions = {}): UIBlock<FullAccessState> & {readonly confirmButton: HTMLButtonElement} {\n const state = initial; const element = document.createElement('div'); element.className = 'pui-full-access-confirmation'; element.innerHTML = `<div class=\"pui-modal-heading\"><div><span class=\"pui-eyebrow\">CONVERSATION PERMISSIONS</span><h2 id=\"full-access-title\">Enable full access?</h2></div><button class=\"pui-quiet-icon\" data-close=\"permissions-dialog\" aria-label=\"Cancel full access\">${icon('close')}</button></div><p class=\"pui-modal-description\">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong>${escape(state.title || 'this conversation')}</strong>.</p><div class=\"pui-full-access-explanation\">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class=\"pui-permission-revoke-note\">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id=\"permissions-error\" class=\"pui-form-error\" role=\"alert\"></div><div class=\"pui-modal-footer\"><button class=\"pui-secondary-button\" type=\"button\" data-close=\"permissions-dialog\">Cancel</button><button class=\"pui-primary-button pui-full-access-confirm\" id=\"enable-full-access\" type=\"button\">Enable full access</button></div>`;\n let destroyed = false; const confirmButton = element.querySelector<HTMLButtonElement>('#enable-full-access')!; const error = element.querySelector<HTMLElement>('#permissions-error')!;\n let current = initial;\n const feedback = adoptFeedback(error,{presentation:'inline',autoHide:false});\n const action = adoptAction(confirmButton,{onAction:options.onConfirm ? () => {update({...current,error:''}); return options.onConfirm!();} : undefined,onError:message => {update({...current,error:message});}});\n const update = (state: Readonly<FullAccessState>) => {if (destroyed) return; current = state; action.update({disabled: !!state.disabled}); if (state.error !== undefined) feedback.update({message:state.error,tone:'error'});}; update(initial); scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');\n return {element, confirmButton, update, destroy() {if (destroyed) return; destroyed = true; action.destroy(); feedback.destroy(); element.remove();}};\n}\nexport interface TaskFailureState {visible: boolean; message: string; advice: string; canContinue: boolean; workspaceId?: string}\nexport function createTaskFailure(initial: TaskFailureState, options: {element?: HTMLElement; onContinue?: () => void; onSettings?: () => void; onDiagnostics?: () => void} = {}): UIBlock<TaskFailureState> {\n const element = options.element || document.createElement('section'); if (!options.element) element.className = 'pui-task-failure'; const abort = new AbortController(); let signature = ''; let destroyed = false;\n const update = (state: Readonly<TaskFailureState>) => {if (destroyed) return; const next = JSON.stringify(state); if (next === signature) return; signature = next; element.hidden = !state.visible; element.innerHTML = state.visible ? `<strong>${escape(state.message)}</strong><p>${escape(state.advice)}</p><div>${state.canContinue ? `<button class=\"pui-secondary-button\" type=\"button\" data-action=\"continue\" data-idle-action data-idle-workspace=\"${escape(state.workspaceId)}\">Review and continue</button>` : '<button class=\"pui-secondary-button\" type=\"button\" data-action=\"settings\">Check model settings</button>'}<button class=\"pui-secondary-button\" type=\"button\" data-action=\"diagnostics\">Save diagnostics</button></div>` : '';};\n element.addEventListener('click', (event) => {const action = (event.target as HTMLElement).closest<HTMLElement>('[data-action]')?.dataset.action; if (action === 'continue') options.onContinue?.(); if (action === 'settings') options.onSettings?.(); if (action === 'diagnostics') options.onDiagnostics?.();}, {signal:abort.signal});\n update(initial); return {element, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); if (!options.element) element.remove();}};\n}\nexport interface PermissionControlState {mode: string; description: string; disabled?: boolean}\nexport function createPermissionControl(initial: PermissionControlState, options: {element?: HTMLElement; descriptionElement?: HTMLElement; onChange?: (value: string) => void} = {}): UIBlock<PermissionControlState> {\n const element = options.element || document.createElement('label'); if (!options.element) {element.className = 'pui-permission-control'; element.innerHTML = `${icon('shield')}<span class=\"pui-sr-only\">Conversation permissions</span><select aria-describedby=\"pui-permission-description\"><option value=\"ask\">Ask before changes</option><option value=\"workspace-write\">Allow workspace changes</option><option value=\"full-access\">Full access</option></select>${icon('down')}`;}\n const selector = element.querySelector<HTMLSelectElement>('select')!; const abort = new AbortController(); let destroyed = false;\n if (options.descriptionElement) {if (!options.descriptionElement.id) options.descriptionElement.id = uniqueId('pui-permission-description'); selector.setAttribute('aria-describedby', options.descriptionElement.id);} else selector.removeAttribute('aria-describedby');\n const parts = adoptProductParts(element); const control = parts.control(selector);\n const update = (state: Readonly<PermissionControlState>) => {if (destroyed) return; control.reset(state.mode); control.update({disabled:!!state.disabled}); element.classList.toggle('pui-full-access', state.mode === 'full-access'); if (options.descriptionElement) {options.descriptionElement.textContent = state.description; options.descriptionElement.classList.toggle('pui-full-access-note', state.mode === 'full-access');} else selector.title = state.description;};\n selector.addEventListener('change', () => options.onChange?.(selector.value), {signal:abort.signal}); update(initial);\n return {element, update, destroy() {if (destroyed) return; destroyed = true; parts.destroy(); abort.abort(); if (!options.element) element.remove();}};\n}\n"]}
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/tasks/permission.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { ActionResult, UIBlock } from '../core.js';\nimport { escape, icon, uniqueId, setText } from '../dom.js';\nimport {adoptAction,adoptFeedback,adoptProductParts} from '../foundations.js';\nimport {permissionControlElement} from './permission-element.js';\nimport {reconcileKeyed} from '../reconcile.js';\nexport interface FullAccessState {title?: string; error?: string; disabled?: boolean}\nexport interface FullAccessOptions {idPrefix?: string; onConfirm?: () => ActionResult | Promise<ActionResult>}\nexport function createFullAccessConfirmation(initial: FullAccessState, options: FullAccessOptions = {}): UIBlock<FullAccessState> & {readonly confirmButton: HTMLButtonElement} {\n const state = initial; const element = document.createElement('div'); element.className = 'pui-full-access-confirmation'; element.innerHTML = `<div class=\"pui-modal-heading\"><div><span class=\"pui-eyebrow\">CONVERSATION PERMISSIONS</span><h2 id=\"full-access-title\">Enable full access?</h2></div><button type=\"button\" class=\"pui-quiet-icon\" data-pui-close data-close=\"permissions-dialog\" aria-label=\"Cancel full access\">${icon('close')}</button></div><p class=\"pui-modal-description\">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong data-permission-target>${escape(state.title || 'this conversation')}</strong>.</p><div class=\"pui-full-access-explanation\">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class=\"pui-permission-revoke-note\">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id=\"permissions-error\" class=\"pui-form-error\" role=\"alert\"></div><div class=\"pui-modal-footer\"><button class=\"pui-secondary-button\" type=\"button\" data-pui-close data-close=\"permissions-dialog\">Cancel</button><button class=\"pui-primary-button pui-full-access-confirm\" id=\"enable-full-access\" type=\"button\">Enable full access</button></div>`;\n let destroyed = false; const confirmButton = element.querySelector<HTMLButtonElement>('#enable-full-access')!; const error = element.querySelector<HTMLElement>('#permissions-error')!;\n let current = initial;\n const feedback = adoptFeedback(error,{presentation:'inline',autoHide:false});\n const action = adoptAction(confirmButton,{onAction:options.onConfirm ? () => {update({...current,error:''}); return options.onConfirm!();} : undefined,onError:message => {update({...current,error:message});}});\n const update = (state: Readonly<FullAccessState>) => {if (destroyed) return; current = state; setText(element.querySelector<HTMLElement>('[data-permission-target]')!,state.title || 'this conversation'); action.update({disabled: !!state.disabled}); if (state.error !== undefined) feedback.update({message:state.error,tone:'error'});}; update(initial); scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');\n return {element, confirmButton, update, destroy() {if (destroyed) return; destroyed = true; action.destroy(); feedback.destroy(); element.remove();}};\n}\nexport interface TaskFailureState {visible: boolean; message: string; advice: string; canContinue: boolean; workspaceId?: string}\nexport function createTaskFailure(initial: TaskFailureState, options: {element?: HTMLElement; onContinue?: () => void; onSettings?: () => void; onDiagnostics?: () => void} = {}): UIBlock<TaskFailureState> {\n const element = options.element || document.createElement('section'); if (!options.element) element.className = 'pui-task-failure';\n const message = document.createElement('strong'), advice = document.createElement('p'), actions = document.createElement('div');\n type Action = 'continue' | 'settings' | 'diagnostics';\n type Row = {element: HTMLButtonElement; handle: ReturnType<typeof adoptAction>; stop?: (event: Event) => void};\n const rows = new Map<Action,Row>(); let destroyed = false;\n const release = (row: Row) => {if (row.stop) row.element.removeEventListener('click',row.stop); row.handle.destroy();};\n const update = (state: Readonly<TaskFailureState>) => {\n if (destroyed) return; element.hidden = !state.visible; if (!state.visible) return;\n if (message.parentElement !== element) element.replaceChildren(message,advice,actions);\n setText(message,state.message); setText(advice,state.advice);\n const views: Array<{action: Action; label: string; callback?: () => void}> = [{action:state.canContinue ? 'continue' : 'settings',label:state.canContinue ? 'Review and continue' : 'Check model settings',callback:state.canContinue ? options.onContinue : options.onSettings},{action:'diagnostics',label:'Save diagnostics',callback:options.onDiagnostics}];\n reconcileKeyed(actions,rows,views,{key:view=>view.action,element:row=>row.element,create(view) {\n const button = document.createElement('button'); button.type = 'button'; button.className = 'pui-secondary-button'; button.dataset.action = view.action;\n const stop = view.callback ? (event: Event) => event.stopPropagation() : undefined; if (stop) button.addEventListener('click',stop);\n return {element:button,handle:adoptAction(button,{onAction:view.callback}),stop};\n },update(row,view) {row.handle.update({label:view.label}); if (view.action === 'continue') {row.element.dataset.idleAction = ''; row.element.dataset.idleWorkspace = state.workspaceId || '';}},destroy:release});\n };\n update(initial); return {element,update,destroy() {if (destroyed) return; destroyed = true; for (const row of rows.values()) release(row); rows.clear(); if (!options.element) element.remove();}};\n}\nexport interface PermissionControlState {mode: string; description: string; disabled?: boolean}\nexport function createPermissionControl(initial: PermissionControlState, options: {element?: HTMLElement; descriptionElement?: HTMLElement; onChange?: (value: string) => void} = {}): UIBlock<PermissionControlState> {\n const element = options.element || permissionControlElement();\n const selector = element.querySelector<HTMLSelectElement>('select')!; const abort = new AbortController(); let destroyed = false;\n if (options.descriptionElement) {if (!options.descriptionElement.id) options.descriptionElement.id = uniqueId('pui-permission-description'); selector.setAttribute('aria-describedby', options.descriptionElement.id);} else selector.removeAttribute('aria-describedby');\n const parts = adoptProductParts(element); const control = parts.control(selector);\n const update = (state: Readonly<PermissionControlState>) => {if (destroyed) return; control.reset(state.mode); control.update({disabled:!!state.disabled}); element.classList.toggle('pui-full-access', state.mode === 'full-access'); if (options.descriptionElement) {options.descriptionElement.textContent = state.description; options.descriptionElement.classList.toggle('pui-full-access-note', state.mode === 'full-access');} else selector.title = state.description;};\n selector.addEventListener('change', () => options.onChange?.(selector.value), {signal:abort.signal}); update(initial);\n return {element, update, destroy() {if (destroyed) return; destroyed = true; parts.destroy(); abort.abort(); if (!options.element) element.remove();}};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/tasks/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC;AACvW,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG;IAAC,YAAY,CAAC,KAAK,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAA;CAAC,CAOnP"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/tasks/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,iBAAiB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC;AACvW,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG;IAAC,YAAY,CAAC,KAAK,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAA;CAAC,CAsBnP"}
@@ -1,5 +1,6 @@
1
1
  import { scopeIds } from '../forms/identity.js';
2
- import { escape, uniqueId } from '../dom.js';
2
+ import { setText, uniqueId } from '../dom.js';
3
+ import { reconcileKeyed } from '../reconcile.js';
3
4
  export function createRuntimePanel(initial, options = {}) {
4
5
  const element = options.element || document.createElement('details');
5
6
  if (!options.element) {
@@ -8,27 +9,43 @@ export function createRuntimePanel(initial, options = {}) {
8
9
  if (options.costs)
9
10
  element.querySelector('#run-status-details').append(options.costs);
10
11
  }
11
- const summary = element.querySelector('#run-status-summary');
12
- const values = element.querySelector('#runtime-values');
12
+ const summary = element.querySelector('[id$="run-status-summary"]');
13
+ const values = element.querySelector('[id$="runtime-values"]');
13
14
  const prefix = options.idPrefix ?? uniqueId('pui-runtime') + '-';
14
15
  for (const child of element.children)
15
16
  scopeIds(child, prefix);
16
- let signature = '';
17
17
  let destroyed = false;
18
+ const list = document.createElement('dl'), note = document.createElement('p');
19
+ note.textContent = 'Duration and average speed cover the latest task, including tools and waits. Speed uses reported output tokens, including reasoning, retries and summaries; input, cache and earlier tasks are excluded. An interrupted checkpoint shows only a lower bound for duration. Session token figures come from Pith records. Context excludes system instructions and tool schemas. Costs estimate recorded requests; unknown prices and older unrecorded requests are excluded. This is not a provider bill.';
20
+ const rows = new Map();
18
21
  const updateTiming = (state) => { if (destroyed)
19
- return; summary.textContent = state.summary; const duration = values.querySelector('[data-runtime-duration]'); const speed = values.querySelector('[data-runtime-speed]'); if (duration)
20
- duration.textContent = state.duration; if (speed)
21
- speed.textContent = state.speed; };
22
- const update = (state) => { if (destroyed)
23
- return; element.hidden = !state.visible; const next = JSON.stringify({ ...state, summary: undefined, duration: undefined, speed: undefined }); if (next !== signature) {
24
- signature = next;
25
- values.innerHTML = state.visible ? `<dl>${state.provider ? `<dt>Provider</dt><dd>${escape(state.provider)}</dd>` : ''}${state.thinkingLevel ? `<dt>Thinking effort</dt><dd>${escape(state.thinkingLevel)}</dd>` : ''}<dt>Latest task duration</dt><dd id="runtime-duration" data-runtime-duration></dd><dt>Avg. output tokens/s</dt><dd id="runtime-speed" data-runtime-speed></dd><dt>Session input / output</dt><dd>${escape(state.inputTokens)} / ${escape(state.outputTokens)}</dd><dt>Cache read / write</dt><dd>${escape(state.cacheRead)} / ${escape(state.cacheWrite)}</dd><dt>Estimated conversation context</dt><dd>~${escape(state.contextTokens)} / ${escape(state.contextWindow)} tokens</dd><dt>Context summaries</dt><dd>${escape(state.compactions)}</dd><dt>Tool failures recorded</dt><dd>${escape(state.toolFailures)}</dd><dt>Recorded cost (USD)</dt><dd>${escape(state.recordedCost)}</dd><dt>Latest task (USD)</dt><dd>${escape(state.latestCost)}</dd><dt>Execution</dt><dd>Codemode enabled · durable local task journal</dd></dl><p>Duration and average speed cover the latest task, including tools and waits. Speed uses reported output tokens, including reasoning, retries and summaries; input, cache and earlier tasks are excluded. An interrupted checkpoint shows only a lower bound for duration. Session token figures come from Pith records. Context excludes system instructions and tool schemas. Costs estimate recorded requests; unknown prices and older unrecorded requests are excluded. This is not a provider bill.</p>` : '';
26
- for (const child of values.children)
27
- scopeIds(child, prefix);
28
- } updateTiming(state); };
22
+ return; setText(summary, state.summary); const duration = rows.get('duration-value'), speed = rows.get('speed-value'); if (duration)
23
+ setText(duration, state.duration); if (speed)
24
+ setText(speed, state.speed); };
25
+ const update = (state) => {
26
+ if (destroyed)
27
+ return;
28
+ element.hidden = !state.visible;
29
+ if (state.visible) {
30
+ if (list.parentElement !== values)
31
+ values.replaceChildren(list, note);
32
+ const fields = [];
33
+ if (state.provider)
34
+ fields.push(['provider', 'Provider', state.provider]);
35
+ if (state.thinkingLevel)
36
+ fields.push(['thinking', 'Thinking effort', state.thinkingLevel]);
37
+ fields.push(['duration', 'Latest task duration', state.duration], ['speed', 'Avg. output tokens/s', state.speed], ['tokens', 'Session input / output', `${state.inputTokens} / ${state.outputTokens}`], ['cache', 'Cache read / write', `${state.cacheRead} / ${state.cacheWrite}`], ['context', 'Estimated conversation context', `~${state.contextTokens} / ${state.contextWindow} tokens`], ['compactions', 'Context summaries', state.compactions], ['failures', 'Tool failures recorded', state.toolFailures], ['recorded', 'Recorded cost (USD)', state.recordedCost], ['latest', 'Latest task (USD)', state.latestCost], ['execution', 'Execution', 'Codemode enabled · durable local task journal']);
38
+ const entries = fields.flatMap(([id, label, value]) => [{ key: id + '-label', tag: 'dt', text: label, id }, { key: id + '-value', tag: 'dd', text: value, id }]);
39
+ reconcileKeyed(list, rows, entries, { key: row => row.key, element: row => row, create(row) { const node = document.createElement(row.tag); if (row.tag === 'dd' && (row.id === 'duration' || row.id === 'speed')) {
40
+ node.id = prefix + 'runtime-' + row.id;
41
+ node.setAttribute('data-runtime-' + row.id, '');
42
+ } return node; }, update(node, row) { setText(node, row.text); }, destroy() { } });
43
+ }
44
+ updateTiming(state);
45
+ };
29
46
  update(initial);
30
47
  return { element, update, updateTiming, destroy() { if (destroyed)
31
- return; destroyed = true; if (!options.element)
48
+ return; destroyed = true; rows.clear(); if (!options.element)
32
49
  element.remove(); } };
33
50
  }
34
51
  //# sourceMappingURL=runtime.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/tasks/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nexport interface RuntimePanelState {visible: boolean; summary: string; duration: string; speed: string; provider?: string; thinkingLevel?: string; inputTokens: string; outputTokens: string; cacheRead: string; cacheWrite: string; contextTokens: string; contextWindow: string; compactions: string; toolFailures: string; recordedCost: string; latestCost: string}\nexport function createRuntimePanel(initial: RuntimePanelState, options: {element?: HTMLElement; idPrefix?: string; costs?: Node} = {}): UIBlock<RuntimePanelState> & {updateTiming(value: {summary: string; duration: string; speed: string}): void} {\n const element = options.element || document.createElement('details');\n if (!options.element) {element.className = 'pui-run-status pui-runtime-panel'; element.innerHTML = '<summary id=\"run-status-summary\"></summary><div id=\"run-status-details\"><div id=\"runtime-values\"></div></div>'; if (options.costs) element.querySelector('#run-status-details')!.append(options.costs);}\n const summary = element.querySelector<HTMLElement>('#run-status-summary')!; const values = element.querySelector<HTMLElement>('#runtime-values')!; const prefix = options.idPrefix ?? uniqueId('pui-runtime') + '-'; for (const child of element.children) scopeIds(child as HTMLElement, prefix); let signature = ''; let destroyed = false;\n const updateTiming = (state: {summary: string; duration: string; speed: string}) => {if (destroyed) return; summary.textContent = state.summary; const duration = values.querySelector<HTMLElement>('[data-runtime-duration]'); const speed = values.querySelector<HTMLElement>('[data-runtime-speed]'); if (duration) duration.textContent = state.duration; if (speed) speed.textContent = state.speed;};\n const update = (state: Readonly<RuntimePanelState>) => {if (destroyed) return; element.hidden = !state.visible; const next = JSON.stringify({...state, summary: undefined, duration: undefined, speed: undefined}); if (next !== signature) {signature = next; values.innerHTML = state.visible ? `<dl>${state.provider ? `<dt>Provider</dt><dd>${escape(state.provider)}</dd>` : ''}${state.thinkingLevel ? `<dt>Thinking effort</dt><dd>${escape(state.thinkingLevel)}</dd>` : ''}<dt>Latest task duration</dt><dd id=\"runtime-duration\" data-runtime-duration></dd><dt>Avg. output tokens/s</dt><dd id=\"runtime-speed\" data-runtime-speed></dd><dt>Session input / output</dt><dd>${escape(state.inputTokens)} / ${escape(state.outputTokens)}</dd><dt>Cache read / write</dt><dd>${escape(state.cacheRead)} / ${escape(state.cacheWrite)}</dd><dt>Estimated conversation context</dt><dd>~${escape(state.contextTokens)} / ${escape(state.contextWindow)} tokens</dd><dt>Context summaries</dt><dd>${escape(state.compactions)}</dd><dt>Tool failures recorded</dt><dd>${escape(state.toolFailures)}</dd><dt>Recorded cost (USD)</dt><dd>${escape(state.recordedCost)}</dd><dt>Latest task (USD)</dt><dd>${escape(state.latestCost)}</dd><dt>Execution</dt><dd>Codemode enabled · durable local task journal</dd></dl><p>Duration and average speed cover the latest task, including tools and waits. Speed uses reported output tokens, including reasoning, retries and summaries; input, cache and earlier tasks are excluded. An interrupted checkpoint shows only a lower bound for duration. Session token figures come from Pith records. Context excludes system instructions and tool schemas. Costs estimate recorded requests; unknown prices and older unrecorded requests are excluded. This is not a provider bill.</p>` : ''; for (const child of values.children) scopeIds(child as HTMLElement, prefix);} updateTiming(state);};\n update(initial); return {element, update, updateTiming, destroy() {if (destroyed) return; destroyed = true; if (!options.element) element.remove();}};\n}\n"]}
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/tasks/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAC,cAAc,EAAC,MAAM,iBAAiB,CAAC;AAE/C,MAAM,UAAU,kBAAkB,CAAC,OAA0B,EAAE,UAAoE,EAAE;IACpI,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IACrE,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QAAA,OAAO,CAAC,SAAS,GAAG,kCAAkC,CAAC;QAAC,OAAO,CAAC,SAAS,GAAG,+GAA+G,CAAC;QAAC,IAAI,OAAO,CAAC,KAAK;YAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAE,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAAA,CAAC;IAC5S,MAAM,OAAO,GAAG,OAAO,CAAC,aAAa,CAAc,4BAA4B,CAAE,CAAC;IAAC,MAAM,MAAM,GAAG,OAAO,CAAC,aAAa,CAAc,wBAAwB,CAAE,CAAC;IAAC,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,aAAa,CAAC,GAAG,GAAG,CAAC;IAAC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ;QAAE,QAAQ,CAAC,KAAoB,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,SAAS,GAAG,KAAK,CAAC;IACvU,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IAC9E,IAAI,CAAC,WAAW,GAAG,0eAA0e,CAAC;IAC9f,MAAM,IAAI,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC3C,MAAM,YAAY,GAAG,CAAC,KAAyD,EAAE,EAAE,GAAE,IAAI,SAAS;QAAE,OAAO,CAAC,OAAO,CAAC,OAAO,EAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,QAAQ;QAAE,OAAO,CAAC,QAAQ,EAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,KAAK;QAAE,OAAO,CAAC,KAAK,EAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAA,CAAC,CAAC;IAClT,MAAM,MAAM,GAAG,CAAC,KAAkC,EAAE,EAAE;QACpD,IAAI,SAAS;YAAE,OAAO;QAAC,OAAO,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;QACvD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,IAAI,CAAC,aAAa,KAAK,MAAM;gBAAE,MAAM,CAAC,eAAe,CAAC,IAAI,EAAC,IAAI,CAAC,CAAC;YACrE,MAAM,MAAM,GAAkC,EAAE,CAAC;YACjD,IAAI,KAAK,CAAC,QAAQ;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAC,UAAU,EAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxE,IAAI,KAAK,CAAC,aAAa;gBAAE,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAC,iBAAiB,EAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;YACzF,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAC,sBAAsB,EAAC,KAAK,CAAC,QAAQ,CAAC,EAAC,CAAC,OAAO,EAAC,sBAAsB,EAAC,KAAK,CAAC,KAAK,CAAC,EAAC,CAAC,QAAQ,EAAC,wBAAwB,EAAC,GAAG,KAAK,CAAC,WAAW,MAAM,KAAK,CAAC,YAAY,EAAE,CAAC,EAAC,CAAC,OAAO,EAAC,oBAAoB,EAAC,GAAG,KAAK,CAAC,SAAS,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC,EAAC,CAAC,SAAS,EAAC,gCAAgC,EAAC,IAAI,KAAK,CAAC,aAAa,MAAM,KAAK,CAAC,aAAa,SAAS,CAAC,EAAC,CAAC,aAAa,EAAC,mBAAmB,EAAC,KAAK,CAAC,WAAW,CAAC,EAAC,CAAC,UAAU,EAAC,wBAAwB,EAAC,KAAK,CAAC,YAAY,CAAC,EAAC,CAAC,UAAU,EAAC,qBAAqB,EAAC,KAAK,CAAC,YAAY,CAAC,EAAC,CAAC,QAAQ,EAAC,mBAAmB,EAAC,KAAK,CAAC,UAAU,CAAC,EAAC,CAAC,WAAW,EAAC,WAAW,EAAC,+CAA+C,CAAC,CAAC,CAAC;YAChpB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAC,KAAK,EAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAC,GAAG,EAAC,EAAE,GAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,EAAE,EAAC,EAAC,EAAC,GAAG,EAAC,EAAE,GAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,EAAE,EAAC,CAAC,CAAC,CAAC;YAC1I,cAAc,CAAC,IAAI,EAAC,IAAI,EAAC,OAAO,EAAC,EAAC,GAAG,EAAC,GAAG,CAAA,EAAE,CAAA,GAAG,CAAC,GAAG,EAAC,OAAO,EAAC,GAAG,CAAA,EAAE,CAAA,GAAG,EAAC,MAAM,CAAC,GAAG,IAAG,MAAM,IAAI,GAAG,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,KAAK,UAAU,IAAI,GAAG,CAAC,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC;oBAAA,IAAI,CAAC,EAAE,GAAG,MAAM,GAAC,UAAU,GAAC,GAAG,CAAC,EAAE,CAAC;oBAAC,IAAI,CAAC,YAAY,CAAC,eAAe,GAAC,GAAG,CAAC,EAAE,EAAC,EAAE,CAAC,CAAC;gBAAA,CAAC,CAAC,OAAO,IAAI,CAAC,CAAA,CAAC,EAAC,MAAM,CAAC,IAAI,EAAC,GAAG,IAAG,OAAO,CAAC,IAAI,EAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA,CAAC,EAAC,OAAO,KAAI,CAAC,EAAC,CAAC,CAAC;QACpW,CAAC;QACD,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,CAAC;IAAC,OAAO,EAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,KAAI,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA,CAAC,EAAC,CAAC;AACrK,CAAC","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { UIBlock } from '../core.js';\nimport { setText, uniqueId } from '../dom.js';\nimport {reconcileKeyed} from '../reconcile.js';\nexport interface RuntimePanelState {visible: boolean; summary: string; duration: string; speed: string; provider?: string; thinkingLevel?: string; inputTokens: string; outputTokens: string; cacheRead: string; cacheWrite: string; contextTokens: string; contextWindow: string; compactions: string; toolFailures: string; recordedCost: string; latestCost: string}\nexport function createRuntimePanel(initial: RuntimePanelState, options: {element?: HTMLElement; idPrefix?: string; costs?: Node} = {}): UIBlock<RuntimePanelState> & {updateTiming(value: {summary: string; duration: string; speed: string}): void} {\n const element = options.element || document.createElement('details');\n if (!options.element) {element.className = 'pui-run-status pui-runtime-panel'; element.innerHTML = '<summary id=\"run-status-summary\"></summary><div id=\"run-status-details\"><div id=\"runtime-values\"></div></div>'; if (options.costs) element.querySelector('#run-status-details')!.append(options.costs);}\n const summary = element.querySelector<HTMLElement>('[id$=\"run-status-summary\"]')!; const values = element.querySelector<HTMLElement>('[id$=\"runtime-values\"]')!; const prefix = options.idPrefix ?? uniqueId('pui-runtime') + '-'; for (const child of element.children) scopeIds(child as HTMLElement, prefix); let destroyed = false;\n const list = document.createElement('dl'), note = document.createElement('p');\n note.textContent = 'Duration and average speed cover the latest task, including tools and waits. Speed uses reported output tokens, including reasoning, retries and summaries; input, cache and earlier tasks are excluded. An interrupted checkpoint shows only a lower bound for duration. Session token figures come from Pith records. Context excludes system instructions and tool schemas. Costs estimate recorded requests; unknown prices and older unrecorded requests are excluded. This is not a provider bill.';\n const rows = new Map<string,HTMLElement>();\n const updateTiming = (state: {summary: string; duration: string; speed: string}) => {if (destroyed) return; setText(summary,state.summary); const duration = rows.get('duration-value'), speed = rows.get('speed-value'); if (duration) setText(duration,state.duration); if (speed) setText(speed,state.speed);};\n const update = (state: Readonly<RuntimePanelState>) => {\n if (destroyed) return; element.hidden = !state.visible;\n if (state.visible) {\n if (list.parentElement !== values) values.replaceChildren(list,note);\n const fields: Array<[string,string,string]> = [];\n if (state.provider) fields.push(['provider','Provider',state.provider]);\n if (state.thinkingLevel) fields.push(['thinking','Thinking effort',state.thinkingLevel]);\n fields.push(['duration','Latest task duration',state.duration],['speed','Avg. output tokens/s',state.speed],['tokens','Session input / output',`${state.inputTokens} / ${state.outputTokens}`],['cache','Cache read / write',`${state.cacheRead} / ${state.cacheWrite}`],['context','Estimated conversation context',`~${state.contextTokens} / ${state.contextWindow} tokens`],['compactions','Context summaries',state.compactions],['failures','Tool failures recorded',state.toolFailures],['recorded','Recorded cost (USD)',state.recordedCost],['latest','Latest task (USD)',state.latestCost],['execution','Execution','Codemode enabled · durable local task journal']);\n const entries = fields.flatMap(([id,label,value]) => [{key:id+'-label',tag:'dt',text:label,id},{key:id+'-value',tag:'dd',text:value,id}]);\n reconcileKeyed(list,rows,entries,{key:row=>row.key,element:row=>row,create(row) {const node = document.createElement(row.tag); if (row.tag === 'dd' && (row.id === 'duration' || row.id === 'speed')) {node.id = prefix+'runtime-'+row.id; node.setAttribute('data-runtime-'+row.id,'');} return node;},update(node,row) {setText(node,row.text);},destroy() {}});\n }\n updateTiming(state);\n };\n update(initial); return {element, update, updateTiming, destroy() {if (destroyed) return; destroyed = true; rows.clear(); if (!options.element) element.remove();}};\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"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"}
package/dist/workspace.js CHANGED
@@ -1,8 +1,10 @@
1
- import { Generation } from './core.js';
2
- import { icon } from './dom.js';
1
+ import {} from './core.js';
2
+ import { focusableElements, icon } from './dom.js';
3
3
  import { controlDisabled } from './core.js';
4
4
  import { adoptProductParts, reconcileSelectOptions } from './foundations.js';
5
5
  import { installDialogLifecycle } from './surface.js';
6
+ import { permissionControlElement } from './tasks/permission-element.js';
7
+ import { createOperation } from './operation.js';
6
8
  let sequence = 0;
7
9
  function identify(root, prefix) {
8
10
  if (!prefix)
@@ -24,27 +26,37 @@ export function createComposerView(options = {}) {
24
26
  <div class="pui-composer-toolbar">
25
27
  <div class="pui-composer-context">
26
28
  <button id="attach-images" type="button" class="pui-quiet-icon pui-attach-images" data-action="attach-images" aria-label="Attach images" title="Attach images">${icon('image')}</button>
27
- <label class="pui-permission-control" id="permission-control">${icon('shield')}<span class="pui-sr-only">Conversation permissions</span><select id="permission-mode" aria-describedby="permission-description"><option value="ask">Ask before changes</option><option value="workspace-write">Allow workspace changes</option><option value="full-access">Full access</option></select>${icon('down')}</label>
29
+ <span data-permission-slot></span>
28
30
  </div>
29
31
  <div class="pui-composer-actions"><button id="composer-model" type="button" class="pui-composer-model" data-action="model" data-idle-action data-global-idle aria-label="Choose model">Choose model</button><label class="pui-composer-thinking"><span class="pui-sr-only">Thinking effort</span><select id="composer-thinking" aria-label="Thinking effort"></select></label><button id="send-button" class="pui-send-button" type="submit" aria-label="Send message">${icon('arrow')}</button></div>
30
32
  </div>
31
33
  </form>`;
32
34
  const element = holder.firstElementChild;
35
+ const permission = permissionControlElement();
36
+ permission.id = 'permission-control';
37
+ const permissionMode = permission.querySelector('select');
38
+ permissionMode.id = 'permission-mode';
39
+ permissionMode.setAttribute('aria-describedby', 'permission-description');
40
+ element.querySelector('[data-permission-slot]').replaceWith(permission);
33
41
  const input = element.querySelector('textarea');
34
42
  const action = element.querySelector('[type="submit"]');
35
43
  const model = element.querySelector('[data-action="model"]');
36
44
  const thinking = element.querySelector('[aria-label="Thinking effort"]');
45
+ const thinkingLabel = thinking.closest('label');
37
46
  const imagePicker = element.querySelector('[type="file"]');
38
47
  identify(element, options.prefix ?? 'pui-composer-' + ++sequence + '-');
39
- const generation = new Generation();
40
48
  const parts = adoptProductParts(element);
49
+ parts.control(element.querySelector('[id$="permission-mode"]'));
50
+ parts.control(thinking);
41
51
  let destroyed = false;
42
- let pending = false, last = {}, lastAction = 'send';
52
+ let last = {}, lastAction = 'send';
43
53
  const error = document.createElement('div');
44
54
  error.className = 'pui-form-error';
45
55
  error.setAttribute('role', 'alert');
46
56
  error.hidden = true;
47
57
  element.insertBefore(error, element.firstChild);
58
+ const feedback = parts.feedback(error);
59
+ const setError = (message) => { feedback.update({ message, tone: 'error' }); error.hidden = !message; };
48
60
  const resize = () => { input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 180) + 'px'; };
49
61
  const update = (state) => {
50
62
  if (destroyed)
@@ -60,46 +72,27 @@ export function createComposerView(options = {}) {
60
72
  const actionLabel = lastAction === 'stop' ? 'Stop agent' : lastAction === 'queue' ? 'Queue message' : 'Send message';
61
73
  action.setAttribute('aria-label', actionLabel);
62
74
  action.title = actionLabel;
63
- parts.action(action).update({ disabled: !!(state.actionDisabled || state.disabled), pending });
75
+ parts.action(action).update({ disabled: !!(state.actionDisabled || state.disabled), pending: operation.pending });
64
76
  if (state.model) {
65
77
  parts.action(model).update({ label: state.model.label, title: state.model.description, disabled: !!state.model.disabled });
66
78
  }
67
79
  if (state.thinking) {
68
80
  const { options: choices, value, disabled } = state.thinking;
69
81
  reconcileSelectOptions(thinking, choices, value);
82
+ thinkingLabel.hidden = choices.length < 2;
70
83
  parts.control(thinking).update({ disabled: !!disabled });
71
- thinking.parentElement.hidden = choices.length < 2;
72
84
  }
73
85
  parts.update();
74
86
  };
87
+ const operation = createOperation({ unavailable: () => destroyed || action.disabled, onPending: () => update(last), onError: setError });
75
88
  const onInput = () => { resize(); options.onInput?.(input.value); };
76
- const run = async (callback, textSubmission = true) => {
77
- if (!callback || pending || action.disabled || (textSubmission && (input.disabled || input.readOnly)))
89
+ const run = (callback, textSubmission = true) => {
90
+ if (!callback || (textSubmission && (input.disabled || input.readOnly)))
78
91
  return;
79
- const seq = generation.next();
80
- pending = true;
81
- error.hidden = true;
82
- error.textContent = '';
83
- update(last);
84
- try {
85
- const result = await callback();
86
- if (generation.current(seq) && result && !result.ok) {
87
- error.textContent = result.error;
88
- error.hidden = false;
89
- }
90
- }
91
- catch (reason) {
92
- if (generation.current(seq)) {
93
- error.textContent = reason instanceof Error ? reason.message : String(reason);
94
- error.hidden = false;
95
- }
96
- }
97
- finally {
98
- if (generation.current(seq)) {
99
- pending = false;
100
- update(last);
101
- }
102
- }
92
+ operation.run(() => { setError(''); return callback(); }, result => {
93
+ if (result && !result.ok)
94
+ setError(result.error);
95
+ });
103
96
  };
104
97
  const submit = (event) => { if (options.onSubmit) {
105
98
  event.preventDefault();
@@ -136,12 +129,12 @@ export function createComposerView(options = {}) {
136
129
  action.addEventListener('click', stop);
137
130
  return { element, input, update, getValue: () => input.value,
138
131
  reset(value) { if (destroyed)
139
- return; generation.invalidate(); pending = false; error.hidden = true; error.textContent = ''; input.value = value; update(last); resize(); }, focus: () => { if (!destroyed)
132
+ return; operation.invalidate(); setError(''); input.value = value; update(last); resize(); }, focus: () => { if (!destroyed)
140
133
  input.focus(); }, resize,
141
134
  pickImages() { if (!destroyed)
142
135
  imagePicker.click(); },
143
136
  destroy() { if (destroyed)
144
- return; destroyed = true; generation.destroy(); parts.destroy(); input.removeEventListener('input', onInput); input.removeEventListener('keydown', keydown); element.removeEventListener('submit', submit); action.removeEventListener('click', stop); element.removeEventListener('click', click); thinking.removeEventListener('change', chooseThinking); imagePicker.removeEventListener('change', images); element.remove(); } };
137
+ return; destroyed = true; operation.destroy(); parts.destroy(); input.removeEventListener('input', onInput); input.removeEventListener('keydown', keydown); element.removeEventListener('submit', submit); action.removeEventListener('click', stop); element.removeEventListener('click', click); thinking.removeEventListener('change', chooseThinking); imagePicker.removeEventListener('change', images); element.remove(); } };
145
138
  }
146
139
  export function createWorkspaceShell(options) {
147
140
  const element = document.createElement('div');
@@ -218,12 +211,72 @@ export function createWorkspaceShell(options) {
218
211
  const name = dialog.id.slice(prefix.length).replace(/-dialog$/, '');
219
212
  return [name, installDialogLifecycle(dialog, { onClose: () => options.onDialogClose?.(name) })];
220
213
  }));
221
- const setSidebarOpen = (open) => { if (destroyed)
222
- return; part('sidebar').classList.toggle('pui-open', open); part('sidebar-scrim').classList.toggle('pui-visible', open); };
223
- const sidebarClick = (event) => { const target = event.target; if (target === part('sidebar-scrim') || target.closest('[data-action="menu"]')) {
224
- setSidebarOpen(target !== part('sidebar-scrim'));
214
+ const sidebar = part('sidebar'), scrim = part('sidebar-scrim');
215
+ const main = element.querySelector('.pui-main');
216
+ const sidebarTrigger = element.querySelector('.pui-mobile-menu');
217
+ const narrow = matchMedia('(max-width: 720px)');
218
+ let sidebarOpen = false;
219
+ sidebarTrigger.setAttribute('aria-controls', sidebar.id);
220
+ scrim.setAttribute('aria-hidden', 'true');
221
+ const sidebarControls = () => focusableElements(sidebar);
222
+ const syncSidebar = () => {
223
+ if (destroyed)
224
+ return;
225
+ const open = narrow.matches && sidebarOpen;
226
+ main.inert = open;
227
+ sidebar.inert = narrow.matches && !open;
228
+ sidebarTrigger.setAttribute('aria-expanded', String(sidebarOpen));
229
+ sidebar.classList.toggle('pui-open', sidebarOpen);
230
+ scrim.classList.toggle('pui-visible', sidebarOpen);
231
+ };
232
+ const setSidebarOpen = (open) => {
233
+ if (destroyed)
234
+ return;
235
+ const wasOpen = sidebarOpen;
236
+ sidebarOpen = open;
237
+ if (!open && narrow.matches && sidebar.contains(document.activeElement)) {
238
+ main.inert = false;
239
+ sidebarTrigger.focus({ preventScroll: true });
240
+ }
241
+ syncSidebar();
242
+ if (narrow.matches && open && !wasOpen)
243
+ sidebarControls()[0]?.focus({ preventScroll: true });
244
+ };
245
+ const onBreakpoint = () => {
246
+ if (narrow.matches && !sidebarOpen && sidebar.contains(document.activeElement)) {
247
+ main.inert = false;
248
+ sidebarTrigger.focus({ preventScroll: true });
249
+ }
250
+ syncSidebar();
251
+ };
252
+ const sidebarKeydown = (event) => {
253
+ if (!narrow.matches || !sidebarOpen || event.defaultPrevented || element.querySelector('dialog[open]'))
254
+ return;
255
+ if (event.key === 'Escape') {
256
+ event.preventDefault();
257
+ event.stopPropagation();
258
+ setSidebarOpen(false);
259
+ return;
260
+ }
261
+ if (event.key !== 'Tab')
262
+ return;
263
+ const controls = sidebarControls(), first = controls[0], last = controls.at(-1);
264
+ if (event.shiftKey && document.activeElement === first) {
265
+ event.preventDefault();
266
+ last?.focus();
267
+ }
268
+ else if (!event.shiftKey && document.activeElement === last) {
269
+ event.preventDefault();
270
+ first?.focus();
271
+ }
272
+ };
273
+ const sidebarClick = (event) => { const target = event.target; if (target === scrim || target.closest('[data-action="menu"]')) {
274
+ setSidebarOpen(target !== scrim && !sidebarOpen);
225
275
  event.stopPropagation();
226
276
  } };
277
+ narrow.addEventListener('change', onBreakpoint);
278
+ element.addEventListener('keydown', sidebarKeydown);
279
+ syncSidebar();
227
280
  const search = part('history-search');
228
281
  const onSearch = () => options.onSearch?.(search.value);
229
282
  search.addEventListener('input', onSearch);
@@ -282,7 +335,7 @@ export function createWorkspaceShell(options) {
282
335
  link.setAttribute('aria-disabled', String(unavailable({ kind: 'conversation', id: link.dataset.downloadConversation })));
283
336
  },
284
337
  destroy() { if (destroyed)
285
- return; destroyed = true; for (const dialog of dialogs.values())
338
+ return; destroyed = true; narrow.removeEventListener('change', onBreakpoint); element.removeEventListener('keydown', sidebarKeydown); sidebar.inert = false; main.inert = false; for (const dialog of dialogs.values())
286
339
  dialog.destroy(); parts.destroy(); search.removeEventListener('input', onSearch); element.removeEventListener('click', sidebarClick); busy.removeEventListener('click', onBusy); composer.destroy(); element.remove(); } };
287
340
  }
288
341
  //# sourceMappingURL=workspace.js.map