pith-ui 0.1.0 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/AI.md +20 -1
  2. package/API.md +834 -272
  3. package/README.md +3 -1
  4. package/dist/composer.d.ts.map +1 -1
  5. package/dist/composer.js +48 -29
  6. package/dist/composer.js.map +1 -1
  7. package/dist/content/conversation-reader.d.ts.map +1 -1
  8. package/dist/content/conversation-reader.js +8 -1
  9. package/dist/content/conversation-reader.js.map +1 -1
  10. package/dist/content/conversation-view.d.ts +21 -4
  11. package/dist/content/conversation-view.d.ts.map +1 -1
  12. package/dist/content/conversation-view.js +72 -13
  13. package/dist/content/conversation-view.js.map +1 -1
  14. package/dist/content/image-viewer.d.ts +4 -2
  15. package/dist/content/image-viewer.d.ts.map +1 -1
  16. package/dist/content/image-viewer.js +36 -15
  17. package/dist/content/image-viewer.js.map +1 -1
  18. package/dist/content/paged-preview.d.ts +1 -1
  19. package/dist/content/paged-preview.d.ts.map +1 -1
  20. package/dist/content/paged-preview.js +10 -5
  21. package/dist/content/paged-preview.js.map +1 -1
  22. package/dist/content/views.d.ts +3 -0
  23. package/dist/content/views.d.ts.map +1 -1
  24. package/dist/content/views.js +110 -41
  25. package/dist/content/views.js.map +1 -1
  26. package/dist/content/welcome.d.ts.map +1 -1
  27. package/dist/content/welcome.js +19 -7
  28. package/dist/content/welcome.js.map +1 -1
  29. package/dist/dom.js +1 -1
  30. package/dist/dom.js.map +1 -1
  31. package/dist/files/panels.d.ts +8 -1
  32. package/dist/files/panels.d.ts.map +1 -1
  33. package/dist/files/panels.js +235 -120
  34. package/dist/files/panels.js.map +1 -1
  35. package/dist/files/systems.d.ts.map +1 -1
  36. package/dist/files/systems.js +81 -91
  37. package/dist/files/systems.js.map +1 -1
  38. package/dist/forms/actions.d.ts +28 -7
  39. package/dist/forms/actions.d.ts.map +1 -1
  40. package/dist/forms/actions.js +59 -11
  41. package/dist/forms/actions.js.map +1 -1
  42. package/dist/forms/custom-connection.d.ts +2 -1
  43. package/dist/forms/custom-connection.d.ts.map +1 -1
  44. package/dist/forms/custom-connection.js +36 -28
  45. package/dist/forms/custom-connection.js.map +1 -1
  46. package/dist/forms/dialog-frame.d.ts +2 -0
  47. package/dist/forms/dialog-frame.d.ts.map +1 -1
  48. package/dist/forms/dialog-frame.js +46 -26
  49. package/dist/forms/dialog-frame.js.map +1 -1
  50. package/dist/forms/history.d.ts +1 -1
  51. package/dist/forms/history.d.ts.map +1 -1
  52. package/dist/forms/history.js +78 -15
  53. package/dist/forms/history.js.map +1 -1
  54. package/dist/forms/identity.d.ts +12 -0
  55. package/dist/forms/identity.d.ts.map +1 -1
  56. package/dist/forms/identity.js +16 -0
  57. package/dist/forms/identity.js.map +1 -1
  58. package/dist/forms/model-editor.d.ts +8 -3
  59. package/dist/forms/model-editor.d.ts.map +1 -1
  60. package/dist/forms/model-editor.js +73 -62
  61. package/dist/forms/model-editor.js.map +1 -1
  62. package/dist/forms/panels.d.ts +103 -7
  63. package/dist/forms/panels.d.ts.map +1 -1
  64. package/dist/forms/panels.js +183 -77
  65. package/dist/forms/panels.js.map +1 -1
  66. package/dist/forms/provider-login.d.ts.map +1 -1
  67. package/dist/forms/provider-login.js +25 -39
  68. package/dist/forms/provider-login.js.map +1 -1
  69. package/dist/forms/resource-editor.d.ts +9 -5
  70. package/dist/forms/resource-editor.d.ts.map +1 -1
  71. package/dist/forms/resource-editor.js +20 -9
  72. package/dist/forms/resource-editor.js.map +1 -1
  73. package/dist/foundations.d.ts +95 -0
  74. package/dist/foundations.d.ts.map +1 -0
  75. package/dist/foundations.js +299 -0
  76. package/dist/foundations.js.map +1 -0
  77. package/dist/navigation/context-menu.d.ts +8 -1
  78. package/dist/navigation/context-menu.d.ts.map +1 -1
  79. package/dist/navigation/context-menu.js +67 -50
  80. package/dist/navigation/context-menu.js.map +1 -1
  81. package/dist/navigation/navigation.d.ts +55 -7
  82. package/dist/navigation/navigation.d.ts.map +1 -1
  83. package/dist/navigation/navigation.js +167 -18
  84. package/dist/navigation/navigation.js.map +1 -1
  85. package/dist/operation.d.ts +12 -0
  86. package/dist/operation.d.ts.map +1 -0
  87. package/dist/operation.js +49 -0
  88. package/dist/operation.js.map +1 -0
  89. package/dist/primitives.d.ts +11 -1
  90. package/dist/primitives.d.ts.map +1 -1
  91. package/dist/primitives.js +164 -301
  92. package/dist/primitives.js.map +1 -1
  93. package/dist/reconcile.d.ts +11 -0
  94. package/dist/reconcile.d.ts.map +1 -0
  95. package/dist/reconcile.js +36 -0
  96. package/dist/reconcile.js.map +1 -0
  97. package/dist/select.d.ts +14 -0
  98. package/dist/select.d.ts.map +1 -0
  99. package/dist/select.js +304 -0
  100. package/dist/select.js.map +1 -0
  101. package/dist/styles.css +452 -382
  102. package/dist/surface.d.ts +60 -0
  103. package/dist/surface.d.ts.map +1 -0
  104. package/dist/surface.js +344 -0
  105. package/dist/surface.js.map +1 -0
  106. package/dist/tasks/approval.d.ts.map +1 -1
  107. package/dist/tasks/approval.js +86 -45
  108. package/dist/tasks/approval.js.map +1 -1
  109. package/dist/tasks/permission-element.d.ts +3 -0
  110. package/dist/tasks/permission-element.d.ts.map +1 -0
  111. package/dist/tasks/permission-element.js +10 -0
  112. package/dist/tasks/permission-element.js.map +1 -0
  113. package/dist/tasks/permission.d.ts +5 -3
  114. package/dist/tasks/permission.d.ts.map +1 -1
  115. package/dist/tasks/permission.js +47 -22
  116. package/dist/tasks/permission.js.map +1 -1
  117. package/dist/tasks/runtime.d.ts.map +1 -1
  118. package/dist/tasks/runtime.js +30 -13
  119. package/dist/tasks/runtime.js.map +1 -1
  120. package/dist/workspace.d.ts +58 -4
  121. package/dist/workspace.d.ts.map +1 -1
  122. package/dist/workspace.js +202 -44
  123. package/dist/workspace.js.map +1 -1
  124. package/package.json +1 -1
@@ -1,10 +1,11 @@
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;
5
5
  signedIn: boolean;
6
6
  custom: boolean;
7
7
  }
8
+ export type ResourceKind = 'instructions' | 'skill' | 'template';
8
9
  export interface ResourceActionsState {
9
10
  kind: string;
10
11
  resource: {
@@ -13,15 +14,35 @@ export interface ResourceActionsState {
13
14
  };
14
15
  workspaceId: string;
15
16
  }
17
+ export type ProviderAction = 'login' | 'logout' | 'custom' | 'remove-connection';
18
+ export type ResourceAction = 'use-resource' | 'resource';
19
+ export interface ProviderActionTarget {
20
+ readonly id: string;
21
+ }
22
+ export interface ResourceActionTarget {
23
+ readonly workspaceId: string;
24
+ readonly kind: string;
25
+ readonly resource: {
26
+ readonly name: string;
27
+ readonly path: string;
28
+ };
29
+ readonly command?: string;
30
+ }
16
31
  export declare function providerActionsMarkup(provider: ProviderActionsState): string;
17
32
  export declare function resourceActionsMarkup(kind: string, resource: {
18
33
  name: string;
19
34
  path: string;
20
35
  }, 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>;
36
+ export interface FeatureActionOptions {
37
+ onAction?: (action: string, target: DOMStringMap) => ReturnType<UIAction>;
38
+ onError?: (message: string) => void;
39
+ }
40
+ export interface ProviderActionOptions extends FeatureActionOptions {
41
+ onProviderAction?: (action: ProviderAction, target: ProviderActionTarget) => ReturnType<UIAction>;
42
+ }
43
+ export interface ResourceActionOptions extends FeatureActionOptions {
44
+ onResourceAction?: (action: ResourceAction, target: ResourceActionTarget) => ReturnType<UIAction>;
45
+ }
46
+ export declare function createProviderActions(state: ProviderActionsState, options?: ProviderActionOptions): UIBlock<ProviderActionsState>;
47
+ export declare function createResourceActions(state: ResourceActionsState, options?: ResourceActionOptions): UIBlock<ResourceActionsState>;
27
48
  //# 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;AAIpD,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,MAAM,YAAY,GAAG,cAAc,GAAG,OAAO,GAAG,UAAU,CAAC;AACjE,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,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,mBAAmB,CAAC;AACjF,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,UAAU,CAAC;AACzD,MAAM,WAAW,oBAAoB;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAC;AAC3D,MAAM,WAAW,oBAAoB;IAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC;AACzL,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,CAA+lB;AAE5qB,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,CAEvH;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;AACtJ,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IAAE,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,oBAAoB,KAAK,UAAU,CAAC,QAAQ,CAAC,CAAA;CAAC;AACvK,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;IAAE,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,oBAAoB,KAAK,UAAU,CAAC,QAAQ,CAAC,CAAA;CAAC;AAmBvK,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAOrI;AACD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,oBAAoB,EAAE,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAOrI"}
@@ -1,19 +1,67 @@
1
- import { escape } from '../dom.js';
1
+ import { escape, setText } from '../dom.js';
2
+ import { adoptAction } from '../foundations.js';
3
+ import { reconcileKeyed } from '../reconcile.js';
2
4
  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>`; }
5
+ const resourceCommand = (kind, name) => kind === 'skill' ? `/skill:${name} ` : `/${name} `;
3
6
  export function resourceActionsMarkup(kind, resource, workspaceId) {
4
- const command = kind === 'skill' ? `/skill:${resource.name} ` : `/${resource.name} `;
5
- return `<div class="pui-file-actions">${kind !== 'instructions' ? `<button class="pui-secondary-button" data-sdk="use-resource" data-command="${escape(command)}">Use</button>` : ''}<button class="pui-secondary-button" data-sdk="resource" data-resource-workspace="${escape(workspaceId)}" data-kind="${kind}" data-path="${escape(resource.path)}">View / edit</button></div>`;
7
+ return `<div class="pui-file-actions">${kind !== 'instructions' ? `<button type="button" class="pui-secondary-button" data-sdk="use-resource" data-command="${escape(resourceCommand(kind, resource.name))}">Use</button>` : ''}<button type="button" class="pui-secondary-button" data-sdk="resource" data-resource-workspace="${escape(workspaceId)}" data-kind="${escape(kind)}" data-path="${escape(resource.path)}">View / edit</button></div>`;
6
8
  }
7
- function actions(initial, markup, onAction) {
9
+ function actions(initial, className, describe, options, onAction) {
8
10
  const element = document.createElement('div');
9
11
  element.className = 'pui-feature-actions';
10
- const abort = new AbortController();
11
- const update = (state) => { element.innerHTML = markup(state); };
12
+ const toolbar = document.createElement('div');
13
+ toolbar.className = className;
14
+ element.append(toolbar);
15
+ const rows = new Map();
16
+ let destroyed = false;
17
+ const release = (row) => { if (row.stop)
18
+ row.element.removeEventListener('click', row.stop); row.handle.destroy(); };
19
+ const update = (state) => {
20
+ if (destroyed)
21
+ return;
22
+ reconcileKeyed(toolbar, rows, describe(state), { key: view => view.key, element: row => row.element, create(view) {
23
+ const button = document.createElement('button');
24
+ button.type = 'button';
25
+ const stop = onAction || options.onAction ? (event) => event.stopPropagation() : undefined;
26
+ // Stop before the callback: it can synchronously destroy this owner.
27
+ if (stop)
28
+ button.addEventListener('click', stop);
29
+ const row = { element: button, view, handle: adoptAction(button, { onAction: stop ? () => onAction ? onAction(row.view.action, row.view.target) : options.onAction(row.view.action, button.dataset) : undefined, onError: options.onError }), stop };
30
+ return row;
31
+ }, update(row, view) { row.view = view; row.element.className = `pui-secondary-button${view.destructive ? ' pui-destructive-button' : ''}`; setText(row.element, view.label); for (const [name, value] of Object.entries(view.dataset))
32
+ row.element.dataset[name] = value; }, destroy: release });
33
+ };
12
34
  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(); } };
35
+ return { element, update, destroy() { if (destroyed)
36
+ return; destroyed = true; for (const row of rows.values())
37
+ release(row); rows.clear(); element.remove(); } };
38
+ }
39
+ export function createProviderActions(state, options = {}) {
40
+ return actions(state, 'pui-feature-toolbar', value => {
41
+ const target = Object.freeze({ id: value.id });
42
+ const result = [];
43
+ const add = (action, label, destructive = false) => result.push({ key: JSON.stringify([value.id, action]), action, label, target, dataset: { sdk: action, id: value.id }, destructive });
44
+ if (value.oauth)
45
+ add(value.signedIn ? 'logout' : 'login', value.signedIn ? 'Sign out' : 'Sign in with provider');
46
+ if (value.custom) {
47
+ add('custom', 'Edit models and prices');
48
+ add('remove-connection', 'Remove connection', true);
49
+ }
50
+ return result;
51
+ }, options, options.onProviderAction);
52
+ }
53
+ export function createResourceActions(state, options = {}) {
54
+ return actions(state, 'pui-file-actions', value => {
55
+ const resource = Object.freeze({ ...value.resource });
56
+ const identity = JSON.stringify([value.workspaceId, value.kind, resource.name, resource.path]);
57
+ const target = Object.freeze({ workspaceId: value.workspaceId, kind: value.kind, resource });
58
+ const result = [];
59
+ if (value.kind !== 'instructions') {
60
+ const command = resourceCommand(value.kind, resource.name);
61
+ result.push({ key: identity + 'use-resource', action: 'use-resource', label: 'Use', target: Object.freeze({ ...target, command }), dataset: { sdk: 'use-resource', command } });
62
+ }
63
+ result.push({ key: identity + 'resource', action: 'resource', label: 'View / edit', target, dataset: { sdk: 'resource', resourceWorkspace: value.workspaceId, kind: value.kind, path: resource.path } });
64
+ return result;
65
+ }, options, options.onResourceAction);
16
66
  }
17
- export function createProviderActions(state, options = {}) { return actions(state, providerActionsMarkup, options.onAction); }
18
- export function createResourceActions(state, options = {}) { return actions(state, (state) => resourceActionsMarkup(state.kind, state.resource, state.workspaceId), options.onAction); }
19
67
  //# 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, setText } from '../dom.js';\nimport {adoptAction, type ActionController} from '../foundations.js';\nimport {reconcileKeyed} from '../reconcile.js';\nexport interface ProviderActionsState {id: string; oauth: boolean; signedIn: boolean; custom: boolean}\nexport type ResourceKind = 'instructions' | 'skill' | 'template';\nexport interface ResourceActionsState {kind: string; resource: {name: string; path: string}; workspaceId: string}\nexport type ProviderAction = 'login' | 'logout' | 'custom' | 'remove-connection';\nexport type ResourceAction = 'use-resource' | 'resource';\nexport interface ProviderActionTarget {readonly id: string}\nexport interface ResourceActionTarget {readonly workspaceId: string; readonly kind: string; readonly resource: {readonly name: string; readonly path: string}; readonly command?: 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>`;}\nconst resourceCommand = (kind: string, name: string) => kind === 'skill' ? `/skill:${name} ` : `/${name} `;\nexport function resourceActionsMarkup(kind: string, resource: {name: string; path: string}, workspaceId: string): string {\n return `<div class=\"pui-file-actions\">${kind !== 'instructions' ? `<button type=\"button\" class=\"pui-secondary-button\" data-sdk=\"use-resource\" data-command=\"${escape(resourceCommand(kind,resource.name))}\">Use</button>` : ''}<button type=\"button\" class=\"pui-secondary-button\" data-sdk=\"resource\" data-resource-workspace=\"${escape(workspaceId)}\" data-kind=\"${escape(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}\nexport interface ProviderActionOptions extends FeatureActionOptions {onProviderAction?: (action: ProviderAction, target: ProviderActionTarget) => ReturnType<UIAction>}\nexport interface ResourceActionOptions extends FeatureActionOptions {onResourceAction?: (action: ResourceAction, target: ResourceActionTarget) => ReturnType<UIAction>}\ntype ActionView<Action, Target> = {key: string; action: Action; label: string; target: Target; dataset: Record<string,string>; destructive?: boolean};\nfunction actions<State, Action extends string, Target>(initial: State, className: string, describe: (state: Readonly<State>) => readonly ActionView<Action,Target>[], options: FeatureActionOptions, onAction?: (action: Action,target: Target) => ReturnType<UIAction>): UIBlock<State> {\n const element = document.createElement('div'); element.className = 'pui-feature-actions'; const toolbar = document.createElement('div'); toolbar.className = className; element.append(toolbar);\n type Row = {element: HTMLButtonElement; view: ActionView<Action,Target>; handle: ActionController; stop?: (event: Event) => void};\n const rows = new Map<string,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<State>) => {\n if (destroyed) return;\n reconcileKeyed(toolbar,rows,describe(state),{key:view=>view.key,element:row=>row.element,create(view) {\n const button = document.createElement('button'); button.type = 'button';\n const stop = onAction || options.onAction ? (event: Event) => event.stopPropagation() : undefined;\n // Stop before the callback: it can synchronously destroy this owner.\n if (stop) button.addEventListener('click',stop);\n const row: Row = {element:button,view,handle:adoptAction(button,{onAction:stop ? () => onAction ? onAction(row.view.action,row.view.target) : options.onAction!(row.view.action,button.dataset) : undefined,onError:options.onError}),stop}; return row;\n },update(row,view) {row.view = view; row.element.className = `pui-secondary-button${view.destructive ? ' pui-destructive-button' : ''}`; setText(row.element,view.label); for (const [name,value] of Object.entries(view.dataset)) row.element.dataset[name] = value;},destroy:release});\n }; update(initial);\n return {element, update, destroy() {if (destroyed) return; destroyed = true; for (const row of rows.values()) release(row); rows.clear(); element.remove();}};\n}\nexport function createProviderActions(state: ProviderActionsState, options: ProviderActionOptions = {}): UIBlock<ProviderActionsState> {\n return actions(state,'pui-feature-toolbar',value => {\n const target = Object.freeze({id:value.id}); const result: ActionView<ProviderAction,ProviderActionTarget>[] = [];\n const add = (action: ProviderAction,label: string,destructive = false) => result.push({key:JSON.stringify([value.id,action]),action,label,target,dataset:{sdk:action,id:value.id},destructive});\n if (value.oauth) add(value.signedIn ? 'logout' : 'login',value.signedIn ? 'Sign out' : 'Sign in with provider');\n if (value.custom) {add('custom','Edit models and prices'); add('remove-connection','Remove connection',true);} return result;\n },options,options.onProviderAction);\n}\nexport function createResourceActions(state: ResourceActionsState, options: ResourceActionOptions = {}): UIBlock<ResourceActionsState> {\n return actions(state,'pui-file-actions',value => {\n const resource = Object.freeze({...value.resource}); const identity = JSON.stringify([value.workspaceId,value.kind,resource.name,resource.path]);\n const target = Object.freeze({workspaceId:value.workspaceId,kind:value.kind,resource}); const result: ActionView<ResourceAction,ResourceActionTarget>[] = [];\n if (value.kind !== 'instructions') {const command = resourceCommand(value.kind,resource.name); result.push({key:identity+'use-resource',action:'use-resource',label:'Use',target:Object.freeze({...target,command}),dataset:{sdk:'use-resource',command}});}\n result.push({key:identity+'resource',action:'resource',label:'View / edit',target,dataset:{sdk:'resource',resourceWorkspace:value.workspaceId,kind:value.kind,path:resource.path}}); return result;\n },options,options.onResourceAction);\n}\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,CAejS"}
@@ -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,55 +12,62 @@ 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');
39
42
  element.className = 'pui-custom-connection-editor';
40
- element.innerHTML = `<p class="pui-modal-description">Give this endpoint its own name. It can coexist with official providers and other compatible endpoints.</p><form id="custom-form"><label class="pui-field-label">Connection name<input id="custom-name" required value="${escape(state.name)}" /></label><label class="pui-field-label">API base URL<input id="custom-url" type="url" required value="${escape(state.baseUrl)}" placeholder="https://example.com/v1" /></label><label class="pui-field-label">API protocol<select id="custom-api" class="pui-feature-select">${protocols.map((p) => `<option ${state.api === p ? 'selected' : ''}>${p}</option>`).join('')}</select></label><label class="pui-field-label">API key<input id="custom-key" type="password" autocomplete="new-password" placeholder="${state.editing ? 'Leave blank to keep the key at the same endpoint' : 'API key (optional for local endpoints)'}" /></label><div id="custom-models"></div><button type="button" class="pui-secondary-button" id="add-custom-model">Add model</button><p class="pui-field-hint">Limits and capabilities are provided by you. Prices are USD per million tokens. Fill all four rates (0 for free/unused), or leave all four blank for unknown prices.</p><p class="pui-form-error" data-custom-error role="alert"></p><div class="pui-modal-footer"><button type="button" class="pui-secondary-button" data-close="sdk-dialog">Cancel</button><button class="pui-primary-button" type="submit">Save connection</button></div></form>`;
43
+ element.innerHTML = `<p class="pui-modal-description">Give this endpoint its own name. It can coexist with official providers and other compatible endpoints.</p><form id="custom-form"><label class="pui-field-label">Connection name<input id="custom-name" required value="${escape(state.name)}" /></label><label class="pui-field-label">API base URL<input id="custom-url" type="url" required value="${escape(state.baseUrl)}" placeholder="https://example.com/v1" /></label><label class="pui-field-label">API protocol<select id="custom-api" class="pui-feature-select">${protocols.map((p) => `<option ${state.api === p ? 'selected' : ''}>${p}</option>`).join('')}</select></label><label class="pui-field-label">API key<input id="custom-key" type="password" autocomplete="new-password" placeholder="${state.editing ? 'Leave blank to keep the key at the same endpoint' : 'API key (optional for local endpoints)'}" /></label><div id="custom-models"></div><button type="button" class="pui-secondary-button" id="add-custom-model">Add model</button><p class="pui-field-hint">Limits and capabilities are provided by you. Prices are USD per million tokens. Fill all four rates (0 for free/unused), or leave all four blank for unknown prices.</p><p class="pui-form-error" data-custom-error role="alert"></p><div class="pui-modal-footer"><button type="button" class="pui-secondary-button" data-pui-close data-close="sdk-dialog">Cancel</button><button class="pui-primary-button" type="submit">Save connection</button></div></form>`;
41
44
  const prefix = options.idPrefix ?? uniqueId('pui-custom') + '-';
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
+ parts.control($('custom-api'));
53
+ const add = (model) => { const index = nextIndex++; const row = createCustomModelRow(model, index); rows.set(index, row); $('custom-models').append(row.element); if (current.disabled || pending)
54
+ for (const button of row.element.querySelectorAll('button'))
55
+ button.disabled = true; };
56
+ state.models.forEach(add);
57
+ $('add-custom-model').addEventListener('click', () => { if (!current.disabled && !pending)
58
+ add({ id: '', contextWindow: 128000, maxTokens: 8192 }); }, { signal: abort.signal });
59
+ $('custom-models').addEventListener('click', event => { const target = event.target.closest('[data-remove-model]'); if (!target || target.disabled || current.disabled || pending)
60
+ return; const index = Number(target.closest('[data-model-index]')?.dataset.modelIndex); rows.get(index)?.destroy(); rows.delete(index); }, { signal: abort.signal });
61
+ 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()) });
62
+ const update = (next) => { if (destroyed)
63
+ return; current = next; if (next.error !== undefined)
64
+ parts.feedback(element.querySelector('[data-custom-error]')).update({ message: next.error, tone: 'error' }); parts.update(); for (const button of form.querySelectorAll('button:not([role="combobox"]):not([role="option"])'))
65
+ parts.action(button).update({ disabled: next.disabled, pending: button.hasAttribute('data-close') ? false : pending }); };
66
+ const submission = bindFormSubmission(form, { read, unavailable: () => !!current.disabled, onSubmit: options.onSubmit, onPending(value) { pending = value; update(current); }, onError(error) { update({ ...current, error }); } });
60
67
  update(state);
61
68
  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(); } };
69
+ return; destroyed = true; submission.destroy(); abort.abort(); for (const row of rows.values())
70
+ row.destroy(); rows.clear(); for (const input of element.querySelectorAll('input[type="password"]'))
71
+ parts.control(input).reset(''); parts.destroy(); element.remove(); } };
64
72
  }
65
73
  //# sourceMappingURL=custom-connection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"custom-connection.js","sourceRoot":"","sources":["../../src/forms/custom-connection.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds, localField} from './identity.js';\nimport type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nexport interface CustomModelValue {id: string; name?: string; contextWindow?: number; maxTokens?: number; reasoning?: boolean; input?: readonly string[]; cost?: {input: number; output: number; cacheRead: number; cacheWrite: number}}\nexport interface CustomConnectionState {name: string; baseUrl: string; api: string; editing: boolean; models: readonly CustomModelValue[]; error?: string; disabled?: boolean}\nexport interface CustomModelDraft {sourceIndex: number; id: string; name: string; contextWindow: number; maxTokens: number; reasoning: boolean; images: boolean; prices: {input: string; output: string; cacheRead: string; cacheWrite: string}}\nexport interface CustomConnectionDraft {name: string; baseUrl: string; api: string; apiKey: string; models: CustomModelDraft[]}\nconst protocols = ['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai', 'mistral-conversations'];\nfunction modelRowMarkup(model: CustomModelValue, index: number): string {return `<fieldset class=\"pui-custom-model\" data-model-index=\"${index}\"><legend>Model ${index + 1}</legend><label>Model ID<input name=\"id\" required value=\"${escape(model.id)}\" /></label><label>Display name<input name=\"name\" value=\"${escape(model.name || '')}\" /></label><div class=\"pui-sdk-grid\"><label>Context tokens<input name=\"contextWindow\" type=\"number\" min=\"1\" required value=\"${escape(model.contextWindow)}\" /></label><label>Max output tokens<input name=\"maxTokens\" type=\"number\" min=\"1\" required value=\"${escape(model.maxTokens)}\" /></label></div><div class=\"pui-sdk-grid\"><label class=\"pui-checkbox-field\"><input name=\"reasoning\" type=\"checkbox\" ${model.reasoning ? 'checked' : ''} />Thinking effort</label><label class=\"pui-checkbox-field\"><input name=\"images\" type=\"checkbox\" ${model.input?.includes('image') ? 'checked' : ''} />Images</label></div><details class=\"pui-provider-advanced\"><summary>Price estimate (optional)</summary><div class=\"pui-sdk-grid\">${(['input', 'output', 'cacheRead', 'cacheWrite'] as const).map((rate) => `<label>${({ input: \"Input\", output: \"Output\", cacheRead: \"Cache read\", cacheWrite: \"Cache write\" } as Record<string, string>)[rate]}<input name=\"price-${rate}\" type=\"number\" min=\"0\" step=\"any\" value=\"${escape(model.cost?.[rate] ?? '')}\" /></label>`).join('')}</div></details><button type=\"button\" class=\"pui-secondary-button pui-destructive-button\" data-remove-model>Remove model</button></fieldset>`;}\nexport interface CustomModelRowState extends CustomModelValue {disabled?: boolean; error?: string}\nexport function createCustomModelRow(model: CustomModelRowState, index = 0): UIBlock<CustomModelRowState> & {read(): CustomModelDraft; reset(model: CustomModelValue): void} {\n const element = document.createElement('div'); element.className = 'pui-custom-model-row'; element.innerHTML = modelRowMarkup(model,index);\n const field = element.querySelector<HTMLFieldSetElement>('fieldset')!; const error = document.createElement('p'); error.className = 'pui-form-error'; error.setAttribute('role','alert'); element.append(error); let destroyed = false;\n const input = (name: string) => field.querySelector<HTMLInputElement>(`[name=\"${name}\"]`)!;\n const reset = (value: CustomModelValue) => {\n if (destroyed) return;\n input('id').value = value.id; input('name').value = value.name || '';\n input('contextWindow').value = value.contextWindow === undefined ? '' : String(value.contextWindow); input('maxTokens').value = value.maxTokens === undefined ? '' : String(value.maxTokens);\n input('reasoning').checked = !!value.reasoning; input('images').checked = !!value.input?.includes('image');\n for (const rate of ['input','output','cacheRead','cacheWrite'] as const) input('price-'+rate).value = value.cost?.[rate] === undefined ? '' : String(value.cost[rate]);\n };\n const read = (): CustomModelDraft => ({sourceIndex:index,id:input('id').value.trim(),name:input('name').value.trim(),contextWindow:Number(input('contextWindow').value),maxTokens:Number(input('maxTokens').value),reasoning:input('reasoning').checked,images:input('images').checked,prices:{input:input('price-input').value,output:input('price-output').value,cacheRead:input('price-cacheRead').value,cacheWrite:input('price-cacheWrite').value}});\n const update = (state: Readonly<CustomModelRowState>) => {if (destroyed) return; if (state.disabled !== undefined) field.disabled = state.disabled; if (state.error !== undefined) error.textContent = state.error;};\n update(model); return {element,read,reset,update,destroy() {if (destroyed) return; destroyed = true; element.remove();}};\n}\nexport function createCustomConnectionEditor(state: CustomConnectionState, options: {idPrefix?: string} = {}): UIBlock<CustomConnectionState> & {read(): CustomConnectionDraft; readonly form: HTMLFormElement} {\n const element = document.createElement('div'); element.className = 'pui-custom-connection-editor'; element.innerHTML = `<p class=\"pui-modal-description\">Give this endpoint its own name. It can coexist with official providers and other compatible endpoints.</p><form id=\"custom-form\"><label class=\"pui-field-label\">Connection name<input id=\"custom-name\" required value=\"${escape(state.name)}\" /></label><label class=\"pui-field-label\">API base URL<input id=\"custom-url\" type=\"url\" required value=\"${escape(state.baseUrl)}\" placeholder=\"https://example.com/v1\" /></label><label class=\"pui-field-label\">API protocol<select id=\"custom-api\" class=\"pui-feature-select\">${protocols.map((p) => `<option ${state.api === p ? 'selected' : ''}>${p}</option>`).join('')}</select></label><label class=\"pui-field-label\">API key<input id=\"custom-key\" type=\"password\" autocomplete=\"new-password\" placeholder=\"${state.editing ? 'Leave blank to keep the key at the same endpoint' : 'API key (optional for local endpoints)'}\" /></label><div id=\"custom-models\"></div><button type=\"button\" class=\"pui-secondary-button\" id=\"add-custom-model\">Add model</button><p class=\"pui-field-hint\">Limits and capabilities are provided by you. Prices are USD per million tokens. Fill all four rates (0 for free/unused), or leave all four blank for unknown prices.</p><p class=\"pui-form-error\" data-custom-error role=\"alert\"></p><div class=\"pui-modal-footer\"><button type=\"button\" class=\"pui-secondary-button\" data-close=\"sdk-dialog\">Cancel</button><button class=\"pui-primary-button\" type=\"submit\">Save connection</button></div></form>`;\n const prefix = options.idPrefix ?? uniqueId('pui-custom') + '-'; scopeIds(element, prefix);\n const $ = <T extends HTMLElement = HTMLElement>(id: string) => localField<T>(element, prefix + id);\n const form = $<HTMLFormElement>('custom-form'); const models = [...state.models]; let destroyed = false; const abort = new AbortController();\n $('custom-models').innerHTML = models.map(modelRowMarkup).join('');\n $('add-custom-model').addEventListener('click', () => {const model = {id: '', contextWindow: 128000, maxTokens: 8192}; const index = models.length; models.push(model); $('custom-models').insertAdjacentHTML('beforeend', modelRowMarkup(model, index));}, {signal: abort.signal});\n $('custom-models').addEventListener('click', (event) => {(event.target as HTMLElement).closest('[data-remove-model]')?.closest('fieldset')?.remove();}, {signal: abort.signal});\n const read = (): CustomConnectionDraft => ({name: $<HTMLInputElement>('custom-name').value, baseUrl: $<HTMLInputElement>('custom-url').value, api: $<HTMLSelectElement>('custom-api').value, apiKey: $<HTMLInputElement>('custom-key').value, models: Array.from($('custom-models').querySelectorAll<HTMLFieldSetElement>('fieldset')).map((field) => {\n const input = (name: string) => field.querySelector<HTMLInputElement>(`[name=\"${name}\"]`)!;\n return {sourceIndex: Number(field.dataset.modelIndex), id: input('id').value.trim(), name: input('name').value.trim(), contextWindow: Number(input('contextWindow').value), maxTokens: Number(input('maxTokens').value), reasoning: input('reasoning').checked, images: input('images').checked, prices: {input: input('price-input').value, output: input('price-output').value, cacheRead: input('price-cacheRead').value, cacheWrite: input('price-cacheWrite').value}};\n })});\n const update = (state: Readonly<CustomConnectionState>) => {if (destroyed) return; if (state.error !== undefined) element.querySelector<HTMLElement>('[data-custom-error]')!.textContent = state.error; if (state.disabled !== undefined) for (const button of form.querySelectorAll<HTMLButtonElement>('button')) button.disabled = state.disabled;}; update(state);\n return {element, form, read, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); for (const input of element.querySelectorAll<HTMLInputElement>('input[type=\"password\"]')) input.value = ''; element.remove();}};\n}\n"]}
1
+ {"version":3,"file":"custom-connection.js","sourceRoot":"","sources":["../../src/forms/custom-connection.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds,localField,bindFormSubmission} from './identity.js';\nimport type {ActionResult,UIBlock} from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nimport {adoptProductParts} from '../foundations.js';\nexport interface CustomModelValue {id: string; name?: string; contextWindow?: number; maxTokens?: number; reasoning?: boolean; input?: readonly string[]; cost?: {input: number; output: number; cacheRead: number; cacheWrite: number}}\nexport interface CustomConnectionState {name: string; baseUrl: string; api: string; editing: boolean; models: readonly CustomModelValue[]; error?: string; disabled?: boolean}\nexport interface CustomModelDraft {sourceIndex: number; id: string; name: string; contextWindow: number; maxTokens: number; reasoning: boolean; images: boolean; prices: {input: string; output: string; cacheRead: string; cacheWrite: string}}\nexport interface CustomConnectionDraft {name: string; baseUrl: string; api: string; apiKey: string; models: CustomModelDraft[]}\nconst protocols = ['openai-completions', 'openai-responses', 'anthropic-messages', 'google-generative-ai', 'mistral-conversations'];\nfunction modelRowMarkup(model: CustomModelValue, index: number): string {return `<fieldset class=\"pui-custom-model\" data-model-index=\"${index}\"><legend>Model ${index + 1}</legend><label>Model ID<input name=\"id\" required value=\"${escape(model.id)}\" /></label><label>Display name<input name=\"name\" value=\"${escape(model.name || '')}\" /></label><div class=\"pui-sdk-grid\"><label>Context tokens<input name=\"contextWindow\" type=\"number\" min=\"1\" required value=\"${escape(model.contextWindow)}\" /></label><label>Max output tokens<input name=\"maxTokens\" type=\"number\" min=\"1\" required value=\"${escape(model.maxTokens)}\" /></label></div><div class=\"pui-sdk-grid\"><label class=\"pui-checkbox-field\"><input name=\"reasoning\" type=\"checkbox\" ${model.reasoning ? 'checked' : ''} />Thinking effort</label><label class=\"pui-checkbox-field\"><input name=\"images\" type=\"checkbox\" ${model.input?.includes('image') ? 'checked' : ''} />Images</label></div><details class=\"pui-provider-advanced\"><summary>Price estimate (optional)</summary><div class=\"pui-sdk-grid\">${(['input', 'output', 'cacheRead', 'cacheWrite'] as const).map((rate) => `<label>${({ input: \"Input\", output: \"Output\", cacheRead: \"Cache read\", cacheWrite: \"Cache write\" } as Record<string, string>)[rate]}<input name=\"price-${rate}\" type=\"number\" min=\"0\" step=\"any\" value=\"${escape(model.cost?.[rate] ?? '')}\" /></label>`).join('')}</div></details><button type=\"button\" class=\"pui-secondary-button pui-destructive-button\" data-remove-model>Remove model</button></fieldset>`;}\nexport interface CustomModelRowState extends CustomModelValue {disabled?: boolean; error?: string}\nexport function createCustomModelRow(model: CustomModelRowState, index = 0): UIBlock<CustomModelRowState> & {read(): CustomModelDraft; reset(model: CustomModelValue): void} {\n const element = document.createElement('div'); element.className = 'pui-custom-model-row'; element.innerHTML = modelRowMarkup(model,index);\n const field = element.querySelector<HTMLFieldSetElement>('fieldset')!; const error = document.createElement('p'); error.className = 'pui-form-error'; error.setAttribute('role','alert'); element.append(error); const parts = adoptProductParts(element); let destroyed = false;\n const input = (name: string) => field.querySelector<HTMLInputElement>(`[name=\"${name}\"]`)!;\n const reset = (value: CustomModelValue) => {\n if (destroyed) return;\n parts.control(input('id')).reset(value.id); parts.control(input('name')).reset(value.name || '');\n parts.control(input('contextWindow')).reset(value.contextWindow === undefined ? '' : String(value.contextWindow)); parts.control(input('maxTokens')).reset(value.maxTokens === undefined ? '' : String(value.maxTokens));\n parts.control(input('reasoning')).reset(!!value.reasoning); parts.control(input('images')).reset(!!value.input?.includes('image'));\n for (const rate of ['input','output','cacheRead','cacheWrite'] as const) parts.control(input('price-'+rate)).reset(value.cost?.[rate] === undefined ? '' : String(value.cost[rate]));\n };\n const value = (name:string) => parts.control(input(name)).getValue();\n const read = (): CustomModelDraft => ({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')}});\n const update = (state: Readonly<CustomModelRowState>) => {if (destroyed) return; if (state.disabled !== undefined) field.disabled = state.disabled; if (state.error !== undefined) parts.feedback(error).update({message:state.error,tone:'error'});};\n update(model); return {element,read,reset,update,destroy() {if (destroyed) return; destroyed = true; parts.destroy(); element.remove();}};\n}\nexport function createCustomConnectionEditor(state: CustomConnectionState, options: {idPrefix?: string; onSubmit?: (draft: CustomConnectionDraft) => ActionResult | Promise<ActionResult>} = {}): UIBlock<CustomConnectionState> & {read(): CustomConnectionDraft; readonly form: HTMLFormElement} {\n const element = document.createElement('div'); element.className = 'pui-custom-connection-editor'; element.innerHTML = `<p class=\"pui-modal-description\">Give this endpoint its own name. It can coexist with official providers and other compatible endpoints.</p><form id=\"custom-form\"><label class=\"pui-field-label\">Connection name<input id=\"custom-name\" required value=\"${escape(state.name)}\" /></label><label class=\"pui-field-label\">API base URL<input id=\"custom-url\" type=\"url\" required value=\"${escape(state.baseUrl)}\" placeholder=\"https://example.com/v1\" /></label><label class=\"pui-field-label\">API protocol<select id=\"custom-api\" class=\"pui-feature-select\">${protocols.map((p) => `<option ${state.api === p ? 'selected' : ''}>${p}</option>`).join('')}</select></label><label class=\"pui-field-label\">API key<input id=\"custom-key\" type=\"password\" autocomplete=\"new-password\" placeholder=\"${state.editing ? 'Leave blank to keep the key at the same endpoint' : 'API key (optional for local endpoints)'}\" /></label><div id=\"custom-models\"></div><button type=\"button\" class=\"pui-secondary-button\" id=\"add-custom-model\">Add model</button><p class=\"pui-field-hint\">Limits and capabilities are provided by you. Prices are USD per million tokens. Fill all four rates (0 for free/unused), or leave all four blank for unknown prices.</p><p class=\"pui-form-error\" data-custom-error role=\"alert\"></p><div class=\"pui-modal-footer\"><button type=\"button\" class=\"pui-secondary-button\" data-pui-close data-close=\"sdk-dialog\">Cancel</button><button class=\"pui-primary-button\" type=\"submit\">Save connection</button></div></form>`;\n const prefix = options.idPrefix ?? uniqueId('pui-custom') + '-'; scopeIds(element, prefix);\n const $ = <T extends HTMLElement = HTMLElement>(id: string) => localField<T>(element, prefix + id);\n const form = $<HTMLFormElement>('custom-form'); const parts = adoptProductParts(element), rows = new Map<number, ReturnType<typeof createCustomModelRow>>(); let nextIndex = 0, destroyed = false, pending = false; const abort = new AbortController(); let current = state;\n parts.control($<HTMLSelectElement>('custom-api'));\n const add = (model: CustomModelValue) => {const index = nextIndex++; const row = createCustomModelRow(model,index); rows.set(index,row); $('custom-models').append(row.element); if (current.disabled || pending) for (const button of row.element.querySelectorAll<HTMLButtonElement>('button')) button.disabled = true;};\n state.models.forEach(add);\n $('add-custom-model').addEventListener('click', () => {if (!current.disabled && !pending) add({id:'',contextWindow:128000,maxTokens:8192});}, {signal:abort.signal});\n $('custom-models').addEventListener('click', event => {const target = (event.target as HTMLElement).closest<HTMLButtonElement>('[data-remove-model]'); if (!target || target.disabled || current.disabled || pending) return; const index = Number(target.closest<HTMLElement>('[data-model-index]')?.dataset.modelIndex); rows.get(index)?.destroy(); rows.delete(index);}, {signal:abort.signal});\n const read = (): CustomConnectionDraft => ({name:parts.control($<HTMLInputElement>('custom-name')).getValue(),baseUrl:parts.control($<HTMLInputElement>('custom-url')).getValue(),api:parts.control($<HTMLSelectElement>('custom-api')).getValue(),apiKey:parts.control($<HTMLInputElement>('custom-key')).getValue(),models:[...rows.values()].map(row => row.read())});\n const update = (next: Readonly<CustomConnectionState>) => {if (destroyed) return; current = next; if (next.error !== undefined) parts.feedback(element.querySelector<HTMLElement>('[data-custom-error]')!).update({message:next.error,tone:'error'}); parts.update(); for (const button of form.querySelectorAll<HTMLButtonElement>('button:not([role=\"combobox\"]):not([role=\"option\"])')) parts.action(button).update({disabled:next.disabled,pending:button.hasAttribute('data-close') ? false : pending});};\n const submission = bindFormSubmission(form,{read,unavailable:()=>!!current.disabled,onSubmit:options.onSubmit,onPending(value) {pending = value; update(current);},onError(error) {update({...current,error});}});\n update(state);\n return {element,form,read,update,destroy() {if (destroyed) return; destroyed = true; submission.destroy(); abort.abort(); for (const row of rows.values()) row.destroy(); rows.clear(); for (const input of element.querySelectorAll<HTMLInputElement>('input[type=\"password\"]')) parts.control(input).reset(''); parts.destroy(); element.remove();}};\n}\n"]}
@@ -14,11 +14,13 @@ export interface FeatureDialogOptions {
14
14
  titleId?: string;
15
15
  errorId?: string;
16
16
  eyebrow?: string;
17
+ onClose?: () => void;
17
18
  }
18
19
  export declare function createFeatureDialogFrame(options?: FeatureDialogOptions): UIBlock<FeatureDialogState> & {
19
20
  open(state: FeatureDialogState): void;
20
21
  close(): void;
21
22
  fail(message: string): void;
23
+ isOpen(): boolean;
22
24
  };
23
25
  export declare function createFeatureStatus(state: {
24
26
  message: string;
@@ -1 +1 @@
1
- {"version":3,"file":"dialog-frame.d.ts","sourceRoot":"","sources":["../../src/forms/dialog-frame.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,aAAa;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAC;AACtE,MAAM,WAAW,kBAAkB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAA;CAAC;AAC/E,MAAM,WAAW,oBAAoB;IAAE,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC;AAClK,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG;IAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAC;IAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAC,CA6B9L;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,CAAC,CAI1H;AACD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,WAAW,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAC,CAOxM"}
1
+ {"version":3,"file":"dialog-frame.d.ts","sourceRoot":"","sources":["../../src/forms/dialog-frame.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAK1C,MAAM,WAAW,aAAa;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAC;AACtE,MAAM,WAAW,kBAAkB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,aAAa,CAAA;CAAC;AAC/E,MAAM,WAAW,oBAAoB;IAAE,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAC;AACxL,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,oBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC,GAAG;IAAC,IAAI,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAC;IAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,IAAI,OAAO,CAAA;CAAC,CA8CjN;AACD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAC,CAAC,CAK1H;AACD,wBAAgB,uBAAuB,CAAC,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,iBAAiB,CAAC;IAAC,YAAY,EAAE,WAAW,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAC,CAOxM"}
@@ -1,56 +1,76 @@
1
1
  import { scopeIds } from './identity.js';
2
- import { dialogHeading, escape, showDialogError, uniqueId } from '../dom.js';
2
+ import { dialogHeading, uniqueId } from '../dom.js';
3
+ import { installDialogLifecycle } from '../surface.js';
4
+ import { adoptFeedback } from '../foundations.js';
3
5
  export function createFeatureDialogFrame(options = {}) {
4
6
  const dialog = options.dialog || document.createElement('dialog');
5
7
  const content = options.content || document.createElement('div');
6
- const dialogId = options.dialogId || uniqueId('pui-feature-dialog');
8
+ const dialogId = options.dialogId || dialog.id || uniqueId('pui-feature-dialog');
7
9
  const titleId = options.titleId || `${dialogId}-title`;
8
10
  const errorId = options.errorId || `${dialogId}-error`;
9
- if (!options.dialog) {
10
- dialog.id = dialogId;
11
+ dialog.id = dialogId;
12
+ if (!options.dialog)
11
13
  dialog.className = 'pui-modal pui-feature-modal pui-feature-dialog';
12
- dialog.setAttribute('aria-labelledby', titleId);
14
+ dialog.setAttribute('aria-labelledby', titleId);
15
+ if (!dialog.contains(content))
13
16
  dialog.append(content);
14
- }
17
+ const template = document.createElement('template');
18
+ template.innerHTML = dialogHeading('', titleId, dialogId, options.eyebrow || '');
19
+ const heading = template.content.firstElementChild;
20
+ const title = heading.querySelector('h2');
21
+ const close = heading.querySelector('[data-close]');
22
+ const error = document.createElement('p');
23
+ error.id = errorId;
24
+ error.dataset.dialogError = '';
25
+ error.className = 'pui-form-error';
26
+ error.setAttribute('role', 'alert');
27
+ const feedback = adoptFeedback(error, { presentation: 'inline', autoHide: false });
15
28
  let current = null;
29
+ let currentBody = null;
16
30
  let destroyed = false;
17
- const clear = (keep) => { if (current !== keep)
18
- current?.destroy(); current = null; content.replaceChildren(); content.onclick = null; };
31
+ const clear = () => { current?.destroy(); current = null; currentBody = null; content.replaceChildren(); content.onclick = null; };
32
+ const lifecycle = installDialogLifecycle(dialog, { onClose() { clear(); options.onClose?.(); } });
19
33
  const update = (state) => {
20
34
  if (destroyed)
21
35
  return;
22
36
  const active = document.activeElement;
23
37
  const restoreFocus = dialog.open && !!active && (content.contains(active) || active === dialog || active === document.body);
24
38
  const activeId = restoreFocus ? active?.id : '';
25
- clear(state.body instanceof Node ? undefined : state.body);
26
- content.innerHTML = dialogHeading(state.title, titleId, dialogId, options.eyebrow || '') + `<p id="${escape(errorId)}" data-dialog-error class="pui-form-error" role="alert"></p>`;
27
39
  const body = state.body instanceof Node ? state.body : state.body.element;
28
- content.insertBefore(body, content.querySelector('[data-dialog-error]'));
29
- if (!(state.body instanceof Node))
30
- current = state.body;
31
- if (restoreFocus) {
40
+ const next = state.body instanceof Node ? null : state.body;
41
+ const changed = body !== currentBody;
42
+ if (current !== next)
43
+ current?.destroy();
44
+ current = next;
45
+ if (changed) {
46
+ currentBody?.parentNode?.removeChild(currentBody);
47
+ currentBody = body;
48
+ content.replaceChildren(heading, body, error);
49
+ content.onclick = null;
50
+ }
51
+ else if (heading.parentNode !== content)
52
+ content.replaceChildren(heading, body, error);
53
+ title.textContent = state.title;
54
+ close.setAttribute('aria-label', `Close ${state.title.toLowerCase()}`);
55
+ feedback.update({ message: '', tone: 'error' });
56
+ if (restoreFocus && changed) {
32
57
  const previous = activeId ? Array.from(content.querySelectorAll('[id]')).find((node) => node.getAttribute('id') === activeId) : null;
33
58
  const target = previous || content.querySelector('[data-close]');
34
59
  target?.focus({ preventScroll: true });
35
60
  }
36
61
  };
37
- const onClose = () => { if (!dialog.open)
38
- clear(); };
39
- const onClick = (event) => { const button = event.target.closest('[data-close]'); if (button?.dataset.close === dialogId && dialog.open)
40
- dialog.close(); };
41
- dialog.addEventListener('close', onClose);
42
- dialog.addEventListener('click', onClick);
43
- return { element: dialog, update, open(state) { update(state); if (!destroyed && !dialog.open)
44
- dialog.showModal(); }, close() { if (dialog.open)
45
- dialog.close(); }, fail(message) { showDialogError(content, message); }, destroy() { if (destroyed)
46
- return; destroyed = true; dialog.removeEventListener('close', onClose); dialog.removeEventListener('click', onClick); clear(); if (!options.dialog)
62
+ return { element: dialog, update, open(state) { update(state); if (!destroyed)
63
+ lifecycle.open(); }, close: lifecycle.close, isOpen: lifecycle.isOpen, fail(message) { if (!destroyed)
64
+ feedback.update({ message, tone: 'error' }); }, destroy() { if (destroyed)
65
+ return; destroyed = true; lifecycle.close(); lifecycle.destroy(); clear(); feedback.destroy(); if (!options.dialog)
47
66
  dialog.remove(); } };
48
67
  }
49
68
  export function createFeatureStatus(state) {
50
69
  const element = document.createElement('p');
51
- const update = (state) => { element.textContent = state.message; element.className = state.error ? 'pui-form-error' : 'pui-feature-hint'; element.setAttribute('role', state.error ? 'alert' : 'status'); };
70
+ const feedback = adoptFeedback(element, { presentation: 'inline', autoHide: false });
71
+ const update = (state) => { element.className = state.error ? 'pui-form-error' : 'pui-feature-hint'; feedback.update({ message: state.message, tone: state.error ? 'error' : 'info' }); };
52
72
  update(state);
53
- return { element, update, destroy() { element.remove(); } };
73
+ return { element, update, destroy() { feedback.destroy(); element.remove(); } };
54
74
  }
55
75
  export function createSDKDialogElements(options = {}) {
56
76
  const dialog = document.createElement('dialog');