pith-ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/AI.md +8 -0
  2. package/API.md +656 -249
  3. package/README.md +1 -1
  4. package/dist/composer.d.ts.map +1 -1
  5. package/dist/composer.js +13 -4
  6. package/dist/composer.js.map +1 -1
  7. package/dist/content/conversation-view.d.ts +21 -4
  8. package/dist/content/conversation-view.d.ts.map +1 -1
  9. package/dist/content/conversation-view.js +38 -5
  10. package/dist/content/conversation-view.js.map +1 -1
  11. package/dist/content/image-viewer.d.ts +4 -2
  12. package/dist/content/image-viewer.d.ts.map +1 -1
  13. package/dist/content/image-viewer.js +25 -12
  14. package/dist/content/image-viewer.js.map +1 -1
  15. package/dist/content/paged-preview.d.ts.map +1 -1
  16. package/dist/content/paged-preview.js +6 -3
  17. package/dist/content/paged-preview.js.map +1 -1
  18. package/dist/content/welcome.d.ts.map +1 -1
  19. package/dist/content/welcome.js +14 -5
  20. package/dist/content/welcome.js.map +1 -1
  21. package/dist/files/panels.d.ts +6 -1
  22. package/dist/files/panels.d.ts.map +1 -1
  23. package/dist/files/panels.js +48 -7
  24. package/dist/files/panels.js.map +1 -1
  25. package/dist/files/systems.d.ts.map +1 -1
  26. package/dist/files/systems.js +47 -30
  27. package/dist/files/systems.js.map +1 -1
  28. package/dist/forms/actions.d.ts +7 -7
  29. package/dist/forms/actions.d.ts.map +1 -1
  30. package/dist/forms/actions.js +28 -8
  31. package/dist/forms/actions.js.map +1 -1
  32. package/dist/forms/custom-connection.d.ts +2 -1
  33. package/dist/forms/custom-connection.d.ts.map +1 -1
  34. package/dist/forms/custom-connection.js +34 -27
  35. package/dist/forms/custom-connection.js.map +1 -1
  36. package/dist/forms/dialog-frame.d.ts +2 -0
  37. package/dist/forms/dialog-frame.d.ts.map +1 -1
  38. package/dist/forms/dialog-frame.js +46 -26
  39. package/dist/forms/dialog-frame.js.map +1 -1
  40. package/dist/forms/identity.d.ts +12 -0
  41. package/dist/forms/identity.d.ts.map +1 -1
  42. package/dist/forms/identity.js +32 -0
  43. package/dist/forms/identity.js.map +1 -1
  44. package/dist/forms/model-editor.d.ts +8 -3
  45. package/dist/forms/model-editor.d.ts.map +1 -1
  46. package/dist/forms/model-editor.js +73 -62
  47. package/dist/forms/model-editor.js.map +1 -1
  48. package/dist/forms/panels.d.ts +95 -6
  49. package/dist/forms/panels.d.ts.map +1 -1
  50. package/dist/forms/panels.js +142 -27
  51. package/dist/forms/panels.js.map +1 -1
  52. package/dist/forms/provider-login.d.ts.map +1 -1
  53. package/dist/forms/provider-login.js +9 -7
  54. package/dist/forms/provider-login.js.map +1 -1
  55. package/dist/forms/resource-editor.d.ts +9 -5
  56. package/dist/forms/resource-editor.d.ts.map +1 -1
  57. package/dist/forms/resource-editor.js +18 -8
  58. package/dist/forms/resource-editor.js.map +1 -1
  59. package/dist/foundations.d.ts +95 -0
  60. package/dist/foundations.d.ts.map +1 -0
  61. package/dist/foundations.js +315 -0
  62. package/dist/foundations.js.map +1 -0
  63. package/dist/navigation/context-menu.d.ts +1 -0
  64. package/dist/navigation/context-menu.d.ts.map +1 -1
  65. package/dist/navigation/context-menu.js +48 -34
  66. package/dist/navigation/context-menu.js.map +1 -1
  67. package/dist/navigation/navigation.d.ts +42 -7
  68. package/dist/navigation/navigation.d.ts.map +1 -1
  69. package/dist/navigation/navigation.js +197 -14
  70. package/dist/navigation/navigation.js.map +1 -1
  71. package/dist/primitives.d.ts +3 -0
  72. package/dist/primitives.d.ts.map +1 -1
  73. package/dist/primitives.js +60 -168
  74. package/dist/primitives.js.map +1 -1
  75. package/dist/styles.css +32 -28
  76. package/dist/surface.d.ts +29 -0
  77. package/dist/surface.d.ts.map +1 -0
  78. package/dist/surface.js +154 -0
  79. package/dist/surface.js.map +1 -0
  80. package/dist/tasks/approval.d.ts.map +1 -1
  81. package/dist/tasks/approval.js +5 -2
  82. package/dist/tasks/approval.js.map +1 -1
  83. package/dist/tasks/permission.d.ts +5 -3
  84. package/dist/tasks/permission.d.ts.map +1 -1
  85. package/dist/tasks/permission.js +11 -5
  86. package/dist/tasks/permission.js.map +1 -1
  87. package/dist/workspace.d.ts +58 -4
  88. package/dist/workspace.d.ts.map +1 -1
  89. package/dist/workspace.js +120 -15
  90. package/dist/workspace.js.map +1 -1
  91. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"model-editor.js","sourceRoot":"","sources":["../../src/forms/model-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nimport { modelEditorMarkup } from './panels.js';\nexport interface ModelProviderOption {id: string; name: string; model?: string; thinkingLevel?: string}\nexport interface ModelOption {id: string; name: string; provider: string; thinkingLevels?: readonly string[] | null; supportsImages: boolean; contextWindow: number; maxTokens: number}\nexport interface ModelEditorState {providers: readonly ModelProviderOption[] | null; models: readonly ModelOption[] | null; selection: ModelSelection}\nexport interface ModelSelection {provider: string; model: string; thinkingLevel: string}\nexport function thinkingLabel(level: string): string { return level === 'off' ? 'Thinking off' : `Thinking: ${level === 'xhigh' ? 'extra high' : level}`; }\nexport function createModelEditor(initial: ModelEditorState, options: {prefix?: string; element?: HTMLElement; onChange?: (selection: ModelSelection) => void} = {}): UIBlock<ModelEditorState> & {read(): ModelSelection} {\n const prefix = options.prefix || uniqueId('pui-model');\n const element = options.element || document.createElement('div');\n if (!options.element) {element.className = 'pui-model-editor'; element.innerHTML = modelEditorMarkup(prefix);}\n const $ = <T extends HTMLElement = HTMLElement>(id: string) => {const field = Array.from(element.querySelectorAll<HTMLElement>('[id]')).find((node) => node.getAttribute('id') === id); if (!field) throw new Error(`Unknown model field: ${id}`); return field as T;};\n let binding = new AbortController(); let destroyed = false; let signature = '';\n const read = (): ModelSelection => ({provider: $<HTMLSelectElement>(`${prefix}-provider`).value, model: $<HTMLSelectElement>(`${prefix}-model`).value, thinkingLevel: $<HTMLSelectElement>(`${prefix}-thinking`).value});\n const update = (state: Readonly<ModelEditorState>) => {\n if (destroyed) return;\n const next = JSON.stringify(state); if (next === signature) return; signature = next;\n binding.abort(); binding = new AbortController();\n const catalog = state; const selection = state.selection;\n const providers = catalog.providers || [];\n const providerSelect = $<HTMLSelectElement>(`${prefix}-provider`);\n const modelSelect = $<HTMLSelectElement>(`${prefix}-model`);\n const search = $<HTMLInputElement>(`${prefix}-search`);\n const effort = $<HTMLSelectElement>(`${prefix}-thinking`);\n providerSelect.innerHTML = providers.map((entry) => `<option value=\"${escape(entry.id)}\">${escape(entry.name)}</option>`).join('');\n providerSelect.disabled = providers.length === 0;\n providerSelect.value = providers.some((entry) => entry.id === selection.provider) ? selection.provider! : providers[0]?.id || '';\n let selected = '';\n const capabilities = () => {\n selected = modelSelect.value;\n const model = (catalog.models || []).find((entry) => entry.provider === providerSelect.value && entry.id === selected);\n const previous = effort.value;\n const levels = model?.thinkingLevels || ['off'];\n effort.innerHTML = levels.map((level) => `<option value=\"${escape(level)}\">${escape(thinkingLabel(level))}</option>`).join('');\n effort.value = levels.includes(previous) ? previous : levels.includes('high') ? 'high' : levels.includes('medium') ? 'medium' : levels[0];\n effort.disabled = levels.length < 2;\n effort.hidden = levels.length < 2;\n $(`${prefix}-thinking-label`).hidden = levels.length < 2;\n $(`${prefix}-capabilities`).textContent = model ? `${model.supportsImages ? 'Text + images' : 'Text only'} · ${new Intl.NumberFormat('en').format(model.contextWindow)} context tokens · ${new Intl.NumberFormat('en').format(model.maxTokens)} max output tokens` : 'No matching models.';\n };\n const renderModels = () => {\n const query = search.value.trim().toLowerCase();\n const models = (catalog.models || []).filter((entry) => entry.provider === providerSelect.value && (entry.id === selected || `${entry.name} ${entry.id}`.toLowerCase().includes(query)));\n modelSelect.innerHTML = models.map((entry) => `<option value=\"${escape(entry.id)}\">${escape(entry.name)} — ${escape(entry.id)}</option>`).join('');\n modelSelect.disabled = models.length === 0;\n modelSelect.value = models.some((entry) => entry.id === selected) ? selected : models[0]?.id || '';\n capabilities();\n };\n const chooseProvider = () => {\n const provider = providers.find((entry) => entry.id === providerSelect.value);\n if (!provider)\n return;\n search.value = '';\n selected = provider.model || (provider.id === selection.provider ? selection.model : '');\n effort.innerHTML = `<option value=\"${escape(provider.thinkingLevel || selection.thinkingLevel || 'high')}\"></option>`;\n renderModels();\n };\n providerSelect.addEventListener('change', chooseProvider, {signal: binding.signal});\n modelSelect.addEventListener('change', capabilities, {signal: binding.signal});\n search.addEventListener('input', renderModels, {signal: binding.signal});\n chooseProvider();\n\n if (options.onChange) element.addEventListener('change', () => options.onChange!(read()), {signal: binding.signal});\n };\n update(initial);\n return {element, update, read, destroy() {if (destroyed) return; destroyed = true; binding.abort(); if (!options.element) element.remove();}};\n}\n"]}
1
+ {"version":3,"file":"model-editor.js","sourceRoot":"","sources":["../../src/forms/model-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nimport {adoptProductParts,reconcileSelectOptions as optionsInto} from '../foundations.js';\nexport interface ModelProviderOption {\n id: string;\n name: string;\n model?: string;\n thinkingLevel?: string;\n}\nexport interface ModelOption {\n id: string;\n name: string;\n provider: string;\n thinkingLevels?: readonly string[] | null;\n supportsImages: boolean;\n contextWindow: number;\n maxTokens: number;\n}\nexport interface ModelEditorState {\n providers: readonly ModelProviderOption[] | null;\n models: readonly ModelOption[] | null;\n selection: ModelSelection;\n disabled?: boolean;\n}\nexport interface ModelSelection {\n provider: string;\n model: string;\n thinkingLevel: string;\n}\nexport interface ModelEditorBlock extends UIBlock<ModelEditorState> {\n read(): ModelSelection;\n reset(selection: ModelSelection): void;\n focus(): void;\n}\nexport function thinkingLabel(level: string): string { return level === 'off' ? 'Thinking off' : `Thinking: ${level === 'xhigh' ? 'extra high' : level}`; }\nexport function modelEditorMarkup(prefix: string): string { prefix = escape(prefix); return `<label class=\"pui-field-label\" for=\"${prefix}-provider\">Provider</label><select class=\"pui-feature-select\" id=\"${prefix}-provider\" required disabled><option value=\"\">Loading providers…</option></select><label class=\"pui-field-label\" for=\"${prefix}-search\">Model</label><input id=\"${prefix}-search\" type=\"search\" placeholder=\"Search by model name or ID\" autocomplete=\"off\" /><label class=\"pui-sr-only\" for=\"${prefix}-model\">Choose model</label><select id=\"${prefix}-model\" class=\"pui-model-list\" size=\"6\" required disabled></select><p id=\"${prefix}-capabilities\" class=\"pui-field-hint\" aria-live=\"polite\"></p><label class=\"pui-field-label\" id=\"${prefix}-thinking-label\" for=\"${prefix}-thinking\">Thinking effort</label><select id=\"${prefix}-thinking\" class=\"pui-feature-select\" aria-describedby=\"${prefix}-capabilities\" required></select><p class=\"pui-field-hint\">Built-in models come from the Pith SDK catalog. Custom models use your saved definitions. Availability depends on your endpoint and account.</p>`; }\nexport function createModelEditor(initial: ModelEditorState, options: {\n prefix?: string;\n element?: HTMLElement;\n onChange?: (selection: ModelSelection) => void;\n} = {}): ModelEditorBlock {\n const prefix = options.prefix || uniqueId('pui-model');\n const element = options.element || document.createElement('div');\n if (!options.element) {\n element.className = 'pui-model-editor';\n element.innerHTML = modelEditorMarkup(prefix);\n }\n const field = <T extends HTMLElement = HTMLElement>(id: string) => { const result = Array.from(element.querySelectorAll<HTMLElement>('[id]')).find(node => node.id === prefix + '-' + id); if (!result)\n throw new Error(`Unknown model field: ${id}`); return result as T; };\n const provider = field<HTMLSelectElement>('provider'), model = field<HTMLSelectElement>('model'), search = field<HTMLInputElement>('search'), effort = field<HTMLSelectElement>('thinking');\n const parts = adoptProductParts(element), abort = new AbortController();\n let destroyed = false, initialized = false, signature = '', selected = '';\n let state: Readonly<ModelEditorState> = initial;\n const read = (): ModelSelection => ({ provider: parts.control(provider).getValue(), model: parts.control(model).getValue(), thinkingLevel: parts.control(effort).getValue() });\n const disabled = () => { parts.control(provider).update({ disabled: !!state.disabled || !state.providers?.length }); parts.control(model).update({ disabled: !!state.disabled || !model.options.length }); parts.control(effort).update({ disabled: !!state.disabled || effort.options.length < 2 }); parts.control(search).update({disabled:!!state.disabled}); };\n const capabilities = (preferred = effort.value) => {\n selected = model.value;\n const entry = (state.models || []).find(entry => entry.provider === provider.value && entry.id === selected);\n const levels = entry?.thinkingLevels?.length ? entry.thinkingLevels : ['off'];\n optionsInto(effort, levels.map(level => ({ value: level, label: thinkingLabel(level) })));\n effort.value = levels.includes(preferred) ? preferred : levels.includes('high') ? 'high' : levels.includes('medium') ? 'medium' : levels[0];\n effort.hidden = levels.length < 2;\n field('thinking-label').hidden = effort.hidden;\n field('capabilities').textContent = entry ? `${entry.supportsImages ? 'Text + images' : 'Text only'} · ${new Intl.NumberFormat('en').format(entry.contextWindow)} context tokens · ${new Intl.NumberFormat('en').format(entry.maxTokens)} max output tokens` : 'No matching models.';\n disabled();\n };\n const renderModels = (preferred = effort.value) => {\n const query = search.value.trim().toLowerCase();\n const entries = (state.models || []).filter(entry => entry.provider === provider.value && (entry.id === selected || `${entry.name} ${entry.id}`.toLowerCase().includes(query)));\n optionsInto(model, entries.map(entry => ({ value: entry.id, label: `${entry.name} — ${entry.id}` })));\n model.value = entries.some(entry => entry.id === selected) ? selected : entries[0]?.id || '';\n capabilities(preferred);\n };\n const chooseProvider = (selection?: ModelSelection) => {\n const entry = state.providers?.find(entry => entry.id === provider.value);\n search.value = '';\n selected = selection && selection.provider === provider.value ? selection.model : entry?.model || '';\n renderModels(selection && selection.provider === provider.value ? selection.thinkingLevel : entry?.thinkingLevel || 'high');\n };\n const reset = (selection: ModelSelection) => { if (destroyed)\n return; parts.control(provider).reset(state.providers?.some(entry => entry.id === selection.provider) ? selection.provider : state.providers?.[0]?.id || ''); chooseProvider(selection); };\n const update = (next: Readonly<ModelEditorState>) => {\n if (destroyed)\n return;\n const draft = read();\n state = next;\n const nextSignature = JSON.stringify([next.providers, next.models]);\n if (nextSignature !== signature) {\n signature = nextSignature;\n optionsInto(provider, (state.providers || []).map(entry => ({ value: entry.id, label: entry.name })));\n if (!initialized || !draft.provider)\n reset(next.selection);\n else {\n provider.value = state.providers?.some(entry => entry.id === draft.provider) ? draft.provider : state.providers?.[0]?.id || '';\n selected = draft.model;\n renderModels(draft.thinkingLevel);\n }\n initialized = true;\n }\n disabled();\n };\n provider.addEventListener('change', () => chooseProvider(), { signal: abort.signal });\n model.addEventListener('change', () => capabilities(), { signal: abort.signal });\n search.addEventListener('input', () => renderModels(), { signal: abort.signal });\n element.addEventListener('change', () => options.onChange?.(read()), { signal: abort.signal });\n update(initial);\n return { element, update, read, reset, focus() { parts.control(search).focus(); }, destroy() { if (destroyed)\n return; destroyed = true; abort.abort(); parts.destroy(); if (!options.element)\n element.remove(); } };\n}\n"]}
@@ -1,4 +1,6 @@
1
- import type { UIBlock } from '../core.js';
1
+ import { type ActionResult, type UIBlock } from '../core.js';
2
+ import { type ModelEditorState, type ModelSelection } from './model-editor.js';
3
+ export { modelEditorMarkup } from './model-editor.js';
2
4
  export interface FormFeedback {
3
5
  error?: string;
4
6
  disabled?: boolean;
@@ -10,11 +12,63 @@ export interface FormDOMBlock<State extends FormFeedback> extends UIBlock<State>
10
12
  export interface SettingsPanelState extends FormFeedback {
11
13
  baseUrl: string;
12
14
  hasApiKey: boolean;
15
+ ready?: boolean;
16
+ testing?: boolean;
17
+ appearance?: {
18
+ mode: 'light' | 'dark' | 'system';
19
+ disabled?: boolean;
20
+ error?: string;
21
+ };
22
+ testResult?: {
23
+ message: string;
24
+ ok?: boolean;
25
+ };
13
26
  }
14
27
  export interface FormOptions {
15
28
  idPrefix?: string;
16
29
  }
17
- export declare function createSettingsPanel(state: SettingsPanelState, options?: FormOptions): FormDOMBlock<SettingsPanelState>;
30
+ export interface SettingsDraft {
31
+ provider: string;
32
+ baseUrl: string;
33
+ apiKey: string;
34
+ }
35
+ export interface SettingsProviderOption {
36
+ id: string;
37
+ name: string;
38
+ configured?: boolean;
39
+ signedIn?: boolean;
40
+ }
41
+ export interface SettingsCredentialView {
42
+ status: string;
43
+ hidden: boolean;
44
+ required: boolean;
45
+ placeholder: string;
46
+ removeVisible: boolean;
47
+ hint?: string;
48
+ actions?: Node;
49
+ }
50
+ export interface SettingsPanel extends FormDOMBlock<SettingsPanelState> {
51
+ read(): SettingsDraft;
52
+ setProviders(providers: readonly SettingsProviderOption[], selected?: string): void;
53
+ resetProvider(value: {
54
+ provider: string;
55
+ baseUrl: string;
56
+ clearKey?: boolean;
57
+ collapseAdvanced?: boolean;
58
+ }): void;
59
+ setCredential(view: SettingsCredentialView): void;
60
+ clearSecrets(): void;
61
+ readonly updates: HTMLElement;
62
+ }
63
+ export interface SettingsPanelOptions extends FormOptions {
64
+ onAppearanceChange?: (mode: string) => void;
65
+ onProviderChange?: (provider: string) => void;
66
+ onDraftChange?: (draft: SettingsDraft) => void;
67
+ onSubmit?: (draft: SettingsDraft) => ActionResult | Promise<ActionResult>;
68
+ onTest?: (draft: SettingsDraft) => void;
69
+ onRemoveKey?: (draft: SettingsDraft) => ActionResult | Promise<ActionResult>;
70
+ }
71
+ export declare function createSettingsPanel(state: SettingsPanelState, options?: SettingsPanelOptions): SettingsPanel;
18
72
  export interface MCPEditorValue {
19
73
  name: string;
20
74
  type: string;
@@ -28,13 +82,48 @@ export interface MCPEditorValue {
28
82
  hasBearerToken?: boolean;
29
83
  envKeys?: readonly string[];
30
84
  }
31
- export interface MCPConnectionsPanel extends FormDOMBlock<FormFeedback> {
85
+ export interface MCPConnectionDraft {
86
+ name: string;
87
+ type: 'http' | 'stdio';
88
+ url: string;
89
+ oauth: boolean;
90
+ oauthClientId: string;
91
+ oauthScope: string;
92
+ command: string;
93
+ args: string;
94
+ enabled: boolean;
95
+ bearerToken: string;
96
+ clearBearerToken: boolean;
97
+ env: string;
98
+ clearEnv: boolean;
99
+ }
100
+ export interface MCPConnectionsState extends FormFeedback {
101
+ formError?: string;
102
+ }
103
+ export interface MCPConnectionsPanel extends FormDOMBlock<MCPConnectionsState> {
32
104
  renderType(): void;
33
105
  edit(value?: MCPEditorValue): void;
106
+ read(): MCPConnectionDraft;
107
+ clearBearerToken(): void;
108
+ clearSecrets(): void;
109
+ toggleEnvironment(): void;
110
+ readonly list: HTMLElement;
111
+ }
112
+ export declare function createMCPConnectionsPanel(state?: MCPConnectionsState, options?: FormOptions & {
113
+ onSubmit?: (draft: MCPConnectionDraft) => ActionResult | Promise<ActionResult>;
114
+ }): MCPConnectionsPanel;
115
+ export interface ModelPickerState extends FormFeedback {
116
+ editor?: ModelEditorState;
117
+ loading?: boolean;
118
+ }
119
+ export interface ModelPicker extends FormDOMBlock<ModelPickerState> {
120
+ read(): ModelSelection;
121
+ reset(selection: ModelSelection): void;
122
+ focus(): void;
34
123
  }
35
- export declare function createMCPConnectionsPanel(state?: FormFeedback, options?: FormOptions): MCPConnectionsPanel;
36
- export declare function modelEditorMarkup(prefix: string): string;
37
- export declare function createModelPicker(state?: FormFeedback, options?: FormOptions): FormDOMBlock<FormFeedback>;
124
+ export declare function createModelPicker(state?: ModelPickerState, options?: FormOptions & {
125
+ onSubmit?: (selection: ModelSelection) => ActionResult | Promise<ActionResult>;
126
+ }): ModelPicker;
38
127
  export interface MCPConnectionItem {
39
128
  name: string;
40
129
  type: string;
@@ -1 +1 @@
1
- {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../src/forms/panels.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,MAAM,WAAW,YAAY;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AACpE,MAAM,WAAW,YAAY,CAAC,KAAK,SAAS,YAAY,CAAE,SAAQ,OAAO,CAAC,KAAK,CAAC;IAC9E,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;IAC1D,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AA0BD,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE;AAChG,MAAM,WAAW,WAAW;IAAG,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;AAClD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,GAAE,WAAgB,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAuD1H;AACD,MAAM,WAAW,cAAc;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAC;AAC9P,MAAM,WAAW,mBAAoB,SAAQ,YAAY,CAAC,YAAY,CAAC;IAAE,UAAU,IAAI,IAAI,CAAC;IAAC,IAAI,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,IAAI,CAAA;CAAC;AAChI,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,YAAiB,EAAE,OAAO,GAAE,WAAgB,GAAG,mBAAmB,CAwBlH;AACD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAshC;AAC/kC,wBAAgB,iBAAiB,CAAC,KAAK,GAAE,YAAiB,EAAE,OAAO,GAAE,WAAgB,GAAG,YAAY,CAAC,YAAY,CAAC,CAEjH;AACD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IACvG,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACjJ;AACD,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAAG,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE;AAEtG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAmCxG"}
1
+ {"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../../src/forms/panels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,KAAK,YAAY,EAAE,KAAK,OAAO,EAAC,MAAM,YAAY,CAAC;AAGvE,OAAO,EAAuC,KAAK,gBAAgB,EAAE,KAAK,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAEnH,OAAO,EAAC,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,YAAY;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE;AACpE,MAAM,WAAW,YAAY,CAAC,KAAK,SAAS,YAAY,CAAE,SAAQ,OAAO,CAAC,KAAK,CAAC;IAC9E,KAAK,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;IAC1D,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7B;AA0BD,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE;QAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC;IAAC,UAAU,CAAC,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAC,CAAA;CAAE;AACxQ,MAAM,WAAW,WAAW;IAAG,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;AAClD,MAAM,WAAW,aAAa;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC;AAClF,MAAM,WAAW,sBAAsB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AAC5G,MAAM,WAAW,sBAAsB;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,IAAI,CAAA;CAAC;AACxK,MAAM,WAAW,aAAc,SAAQ,YAAY,CAAC,kBAAkB,CAAC;IAAE,IAAI,IAAI,aAAa,CAAC;IAAC,YAAY,CAAC,SAAS,EAAE,SAAS,sBAAsB,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,KAAK,EAAE;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAC,GAAG,IAAI,CAAC;IAAC,aAAa,CAAC,IAAI,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAAC,YAAY,IAAI,IAAI,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;CAAC;AAC7Y,MAAM,WAAW,oBAAqB,SAAQ,WAAW;IAAE,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAC;AACxY,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,OAAO,GAAE,oBAAyB,GAAG,aAAa,CAwFhH;AACD,MAAM,WAAW,cAAc;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAC;AAC9P,MAAM,WAAW,kBAAkB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAC;AACnR,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAC;AAC9E,MAAM,WAAW,mBAAoB,SAAQ,YAAY,CAAC,mBAAmB,CAAC;IAAE,UAAU,IAAI,IAAI,CAAC;IAAC,IAAI,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAAC,IAAI,IAAI,kBAAkB,CAAC;IAAC,gBAAgB,IAAI,IAAI,CAAC;IAAC,YAAY,IAAI,IAAI,CAAC;IAAC,iBAAiB,IAAI,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAC;AAC1Q,wBAAgB,yBAAyB,CAAC,KAAK,GAAE,mBAAwB,EAAE,OAAO,GAAE,WAAW,GAAG;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAM,GAAG,mBAAmB,CAiC5M;AACD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IAAE,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAC;AACrG,MAAM,WAAW,WAAY,SAAQ,YAAY,CAAC,gBAAgB,CAAC;IAAE,IAAI,IAAI,cAAc,CAAC;IAAC,KAAK,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IAAC,KAAK,IAAI,IAAI,CAAA;CAAC;AACnJ,wBAAgB,iBAAiB,CAAC,KAAK,GAAE,gBAAqB,EAAE,OAAO,GAAE,WAAW,GAAG;IAAC,QAAQ,CAAC,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAM,GAAG,WAAW,CAUzL;AACD,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IACvG,cAAc,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACjJ;AACD,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAAG,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAA;CAAE;AAEtG,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAmCxG"}
@@ -1,10 +1,15 @@
1
- import { scopeIds } from './identity.js';
1
+ import { Generation } from '../core.js';
2
+ import { scopeIds, bindFormSubmission } from './identity.js';
2
3
  import { dialogHeading, escape, icon, uniqueId, setText } from '../dom.js';
4
+ import { createModelEditor, modelEditorMarkup } from './model-editor.js';
5
+ import { adoptProductParts, reconcileSelectOptions } from '../foundations.js';
6
+ export { modelEditorMarkup } from './model-editor.js';
3
7
  function formBlock(markup, className, initial, prefix = '') {
4
8
  const element = document.createElement('div');
5
9
  element.className = className;
6
10
  element.innerHTML = markup;
7
11
  scopeIds(element, prefix);
12
+ const parts = adoptProductParts(element);
8
13
  let destroyed = false;
9
14
  const field = (id) => {
10
15
  const result = Array.from(element.querySelectorAll('[id]')).find((node) => node.getAttribute('id') === prefix + id);
@@ -18,20 +23,20 @@ function formBlock(markup, className, initial, prefix = '') {
18
23
  if (state.error !== undefined) {
19
24
  const error = element.querySelector('[data-dialog-error]') || element.querySelector('.pui-form-error[role="alert"]');
20
25
  if (error)
21
- error.textContent = state.error;
26
+ parts.feedback(error).update({ message: state.error, tone: 'error' });
22
27
  }
23
28
  if (state.disabled !== undefined) {
24
29
  for (const control of element.querySelectorAll('button[type="submit"]')) {
25
- control.disabled = state.disabled || control.dataset.controlPending === 'true';
30
+ parts.action(control).update({ disabled: state.disabled, pending: control.dataset.controlPending === 'true' });
26
31
  }
27
32
  }
28
33
  };
29
34
  update(initial);
30
- return { element, field, id: (id) => prefix + id, update, destroy() { if (destroyed)
31
- return; destroyed = true; element.remove(); } };
35
+ return { element, field, parts, id: (id) => prefix + id, update, destroy() { if (destroyed)
36
+ return; destroyed = true; parts.destroy(); element.remove(); } };
32
37
  }
33
38
  export function createSettingsPanel(state, options = {}) {
34
- return formBlock(`
39
+ const block = formBlock(`
35
40
  ${dialogHeading('Settings', 'settings-title', 'settings-dialog', 'MAKE IT YOURS')}
36
41
  <section class="pui-settings-section pui-appearance-settings" aria-labelledby="appearance-title">
37
42
  <h3 id="appearance-title">Appearance</h3>
@@ -85,55 +90,164 @@ export function createSettingsPanel(state, options = {}) {
85
90
  </section>
86
91
  <div class="pui-modal-footer"><button class="pui-secondary-button" type="button" data-close="settings-dialog">Cancel</button><button class="pui-primary-button" id="save-settings" data-control-pending="true" disabled data-global-idle type="submit">Save settings</button></div>
87
92
  </form>`, 'pui-settings-panel', state, options.idPrefix ?? uniqueId('pui-form') + '-');
93
+ const $ = block.field, abort = new AbortController(), generation = new Generation();
94
+ let current = state, pending = false, removing = false, destroyed = false;
95
+ const form = $('settings-form'), submit = $('save-settings'), test = $('test-connection'), remove = $('remove-key');
96
+ const read = () => { const key = $('api-key'); return { provider: block.parts.control($('settings-provider')).getValue(), baseUrl: block.parts.control($('base-url')).getValue().trim(), apiKey: key.disabled ? '' : block.parts.control(key).getValue().trim() }; };
97
+ const update = (next) => {
98
+ if (destroyed)
99
+ return;
100
+ current = next;
101
+ block.update(next);
102
+ block.parts.action(submit).update({ disabled: next.disabled, pending: pending || !!next.testing || next.ready !== true });
103
+ block.parts.action(test).update({ label: next.testing ? 'Cancel test' : 'Test connection', disabled: !!next.disabled || pending, pending: next.ready !== true });
104
+ block.parts.action(remove).update({ disabled: !!next.disabled || pending, pending: removing });
105
+ if (next.appearance) {
106
+ const appearance = block.parts.control($('appearance-mode'));
107
+ appearance.reset(next.appearance.mode);
108
+ appearance.update({ disabled: !!next.appearance.disabled });
109
+ block.parts.feedback($('appearance-error')).update({ message: next.appearance.error || '', tone: 'error' });
110
+ }
111
+ if (next.testResult) {
112
+ const result = $('connection-test-result');
113
+ result.textContent = next.testResult.message;
114
+ result.className = next.testResult.ok === undefined ? '' : next.testResult.ok ? 'pui-test-success' : 'pui-form-error';
115
+ }
116
+ };
117
+ const run = async (action, kind) => {
118
+ if (!action || pending || removing || current.disabled || current.testing || current.ready !== true)
119
+ return;
120
+ const seq = generation.next();
121
+ if (kind === 'submit')
122
+ pending = true;
123
+ else
124
+ removing = true;
125
+ update({ ...current, error: '' });
126
+ try {
127
+ const result = await action(read());
128
+ if (generation.current(seq) && !result.ok)
129
+ update({ ...current, error: result.error });
130
+ }
131
+ catch (error) {
132
+ if (generation.current(seq))
133
+ update({ ...current, error: error instanceof Error ? error.message : String(error) });
134
+ }
135
+ finally {
136
+ if (generation.current(seq)) {
137
+ pending = false;
138
+ removing = false;
139
+ update(current);
140
+ }
141
+ }
142
+ };
143
+ form.addEventListener('submit', event => { if (options.onSubmit) {
144
+ event.preventDefault();
145
+ void run(options.onSubmit, 'submit');
146
+ } }, { signal: abort.signal });
147
+ remove.addEventListener('click', () => { void run(options.onRemoveKey, 'remove'); }, { signal: abort.signal });
148
+ test.addEventListener('click', () => { if (!test.disabled && (current.testing || form.reportValidity()))
149
+ options.onTest?.(read()); }, { signal: abort.signal });
150
+ $('appearance-mode').addEventListener('change', () => options.onAppearanceChange?.($('appearance-mode').value), { signal: abort.signal });
151
+ $('settings-provider').addEventListener('change', () => options.onProviderChange?.(read().provider), { signal: abort.signal });
152
+ form.addEventListener('input', () => options.onDraftChange?.(read()), { signal: abort.signal });
153
+ $('base-url').addEventListener('invalid', () => { $('provider-advanced').open = true; }, { signal: abort.signal });
154
+ update(state);
155
+ return { ...block, read, update, updates: $('update-settings'),
156
+ setProviders(providers, selected) { if (destroyed)
157
+ return; const select = $('settings-provider'), previous = selected ?? select.value; reconcileSelectOptions(select, providers.map(provider => ({ value: provider.id, label: `${provider.name}${provider.signedIn ? ' · Signed in' : provider.configured ? ' · Configured' : ''}` })), providers.some(provider => provider.id === previous) ? previous : providers[0]?.id || ''); block.parts.control(select).update({ disabled: !providers.length }); },
158
+ resetProvider(value) { if (destroyed)
159
+ return; generation.invalidate(); pending = false; removing = false; update({ ...current, error: '' }); block.parts.control($('settings-provider')).reset(value.provider); block.parts.control($('base-url')).reset(value.baseUrl); if (value.clearKey)
160
+ block.parts.control($('api-key')).reset(''); if (value.collapseAdvanced)
161
+ $('provider-advanced').open = false; },
162
+ setCredential(view) { if (destroyed)
163
+ return; $('key-status').textContent = view.status; $('provider-api-key').hidden = view.hidden; block.parts.control($('api-key')).update({ disabled: view.hidden, required: view.required, placeholder: view.placeholder }); remove.hidden = !view.removeVisible; $('provider-auth-hint').hidden = !view.hint; $('provider-auth-hint').textContent = view.hint || ''; const actions = $('provider-actions'); actions.hidden = !view.actions; if (view.actions && actions.firstChild !== view.actions)
164
+ actions.replaceChildren(view.actions);
165
+ else if (!view.actions)
166
+ actions.replaceChildren(); block.parts.update(); },
167
+ clearSecrets() { block.parts.control($('api-key')).reset(''); },
168
+ destroy() { if (destroyed)
169
+ return; destroyed = true; generation.destroy(); abort.abort(); $('api-key').value = ''; block.destroy(); }
170
+ };
88
171
  }
89
172
  export function createMCPConnectionsPanel(state = {}, options = {}) {
90
173
  const block = formBlock(`${dialogHeading('Connections', 'connections-title', 'connections-dialog', 'EXTERNAL TOOLS')}<p class="pui-modal-description">Add an MCP server to give Pith access to its tools. Enabled connections connect automatically when a task starts. Disable a connection to stop using it in tasks. External tools require approval unless this conversation has full access.</p><div class="pui-feature-toolbar"><button class="pui-secondary-button" data-action="refresh-mcp">Refresh</button></div><div id="mcp-list"><p class="pui-feature-hint">Loading connections…</p></div><div id="mcp-error" data-dialog-error class="pui-form-error" role="alert"></div><section class="pui-connection-editor"><div class="pui-section-heading"><span id="mcp-form-title">Add connection</span><button type="button" class="pui-secondary-button" data-action="new-mcp">New connection</button></div><form id="mcp-form"><label class="pui-field-label" for="mcp-name">Name</label><input id="mcp-name" name="name" required autocomplete="off" placeholder="my-tools" /><label class="pui-field-label" for="mcp-type">Connection type</label><select id="mcp-type" name="type" class="pui-feature-select"><option value="http">HTTP</option><option value="stdio">Local command (stdio)</option></select><div id="mcp-http-fields"><label class="pui-field-label" for="mcp-url">Server URL</label><input id="mcp-url" name="url" type="url" placeholder="https://example.com/mcp" autocomplete="off" /><label class="pui-checkbox-field"><input id="mcp-oauth" type="checkbox" />Sign in with OAuth</label><details class="pui-provider-advanced"><summary>OAuth settings (optional)</summary><label class="pui-field-label">Registered client ID<input id="mcp-oauth-client" /></label><label class="pui-field-label">Scopes<input id="mcp-oauth-scope" /></label><p class="pui-field-hint">Uses the server’s OAuth discovery and PKCE. The temporary callback listens on 127.0.0.1:54819.</p></details><label class="pui-field-label" for="mcp-token">Bearer token (optional)</label><input id="mcp-token" name="bearerToken" type="password" autocomplete="new-password" placeholder="Optional token" /><label class="pui-checkbox-field" id="mcp-clear-token-row" hidden><input id="mcp-clear-token" type="checkbox" />Remove saved bearer token</label><p class="pui-field-hint">Saved tokens are never returned to this page. Leave this field blank to keep an existing token.</p></div><div id="mcp-stdio-fields" hidden><label class="pui-field-label" for="mcp-command">Installed executable</label><input id="mcp-command" name="command" placeholder="/path/to/mcp-server" autocomplete="off" /><label class="pui-field-label" for="mcp-args">Arguments (JSON array)</label><textarea id="mcp-args" name="args" rows="3" spellcheck="false">[]</textarea><p class="pui-field-hint">The executable and any runtime it needs must already be installed on your computer. Pith Desk does not bundle external runtimes or a marketplace.</p><div class="pui-secret-label"><label class="pui-field-label" for="mcp-env">Environment overrides (JSON object, optional)</label><button class="pui-secondary-button" type="button" id="mcp-env-visibility" data-action="toggle-env" aria-controls="mcp-env" aria-pressed="false">Show</button></div><textarea id="mcp-env" class="pui-secret-field" name="env" rows="3" spellcheck="false" autocomplete="off" aria-describedby="mcp-env-hint" placeholder='{"API_KEY":"…"}'></textarea><p id="mcp-env-hint" class="pui-field-hint">Values stay private. Leave blank to keep saved overrides. Add a JSON object of string keys and values to set or replace individual overrides.</p><p id="mcp-env-keys" class="pui-field-hint" hidden></p><label class="pui-checkbox-field" id="mcp-clear-env-row" hidden><input id="mcp-clear-env" type="checkbox" />Clear saved environment overrides</label></div><label class="pui-checkbox-field"><input id="mcp-enabled" name="enabled" type="checkbox" checked />Enable this connection</label><div id="mcp-form-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button class="pui-secondary-button" type="button" data-close="connections-dialog">Close</button><button class="pui-primary-button" type="submit" data-idle-action data-global-idle>Save connection</button></div></form></section>`, 'pui-mcp-connections', state, options.idPrefix ?? uniqueId('pui-form') + '-');
91
174
  const $ = block.field;
92
175
  const abort = new AbortController();
93
- let disposed = false;
176
+ let disposed = false, pending = false, current = state;
177
+ const form = $('mcp-form'), submit = form.querySelector('button[type="submit"]');
178
+ const control = (id) => block.parts.control($(id));
179
+ const read = () => ({ name: control('mcp-name').getValue().trim(), type: control('mcp-type').getValue() === 'stdio' ? 'stdio' : 'http', url: control('mcp-url').getValue().trim(), oauth: control('mcp-oauth').getChecked(), oauthClientId: control('mcp-oauth-client').getValue().trim(), oauthScope: control('mcp-oauth-scope').getValue().trim(), command: control('mcp-command').getValue().trim(), args: control('mcp-args').getValue(), enabled: control('mcp-enabled').getChecked(), bearerToken: control('mcp-token').getValue().trim(), clearBearerToken: control('mcp-clear-token').getChecked(), env: control('mcp-env').getValue(), clearEnv: control('mcp-clear-env').getChecked() });
180
+ const update = (next) => { if (disposed)
181
+ return; current = next; block.update(next); if (next.formError !== undefined)
182
+ block.parts.feedback($('mcp-form-error')).update({ message: next.formError, tone: 'error' }); block.parts.action(submit).update({ disabled: next.disabled, pending }); };
94
183
  const renderType = () => { if (disposed)
95
- return; const http = $('mcp-type').value === 'http'; $('mcp-http-fields').hidden = !http; $('mcp-stdio-fields').hidden = http; $('mcp-url').required = http; $('mcp-command').required = !http; };
184
+ return; const http = control('mcp-type').getValue() === 'http'; $('mcp-http-fields').hidden = !http; $('mcp-stdio-fields').hidden = http; control('mcp-url').update({ required: http }); control('mcp-command').update({ required: !http }); };
96
185
  const edit = (entry) => {
97
186
  if (disposed)
98
187
  return;
188
+ submission.invalidate();
99
189
  $('mcp-form').reset();
100
190
  $('mcp-env').classList.remove('pui-revealed');
101
191
  $('mcp-env-visibility').setAttribute('aria-pressed', 'false');
102
192
  $('mcp-env-visibility').textContent = 'Show';
103
193
  $('mcp-form-title').textContent = entry ? `Edit ${entry.name}` : 'Add connection';
104
- $('mcp-name').value = entry?.name || '';
105
- $('mcp-name').readOnly = !!entry;
106
- $('mcp-type').value = entry?.type || 'http';
107
- $('mcp-url').value = entry?.url || '';
108
- $('mcp-oauth').checked = entry?.oauth || false;
109
- $('mcp-oauth-client').value = entry?.oauthClientId || '';
110
- $('mcp-oauth-scope').value = entry?.oauthScope || '';
111
- $('mcp-command').value = entry?.command || '';
112
- $('mcp-args').value = JSON.stringify(entry?.args || [], null, 2);
113
- $('mcp-enabled').checked = entry?.enabled ?? true;
114
- $('mcp-token').placeholder = entry?.hasBearerToken ? 'Leave blank to keep your saved token' : 'Optional token';
194
+ control('mcp-name').reset(entry?.name || '');
195
+ control('mcp-name').update({ readOnly: !!entry });
196
+ control('mcp-type').reset(entry?.type || 'http');
197
+ control('mcp-url').reset(entry?.url || '');
198
+ control('mcp-oauth').reset(entry?.oauth || false);
199
+ control('mcp-oauth-client').reset(entry?.oauthClientId || '');
200
+ control('mcp-oauth-scope').reset(entry?.oauthScope || '');
201
+ control('mcp-command').reset(entry?.command || '');
202
+ control('mcp-args').reset(JSON.stringify(entry?.args || [], null, 2));
203
+ control('mcp-enabled').reset(entry?.enabled ?? true);
204
+ control('mcp-token').update({ placeholder: entry?.hasBearerToken ? 'Leave blank to keep your saved token' : 'Optional token' });
115
205
  $('mcp-clear-token-row').hidden = !entry?.hasBearerToken;
116
206
  const envKeys = entry?.envKeys || [];
117
207
  $('mcp-clear-env-row').hidden = !envKeys.length;
118
208
  $('mcp-env-keys').hidden = !envKeys.length;
119
209
  $('mcp-env-keys').textContent = `Saved environment keys: ${envKeys.join(', ')}`;
120
210
  $('mcp-form-error').textContent = '';
211
+ current = { ...current, formError: '' };
212
+ update(current);
121
213
  renderType();
122
214
  $(entry ? entry.type === 'stdio' ? 'mcp-command' : 'mcp-url' : 'mcp-name').focus();
123
215
  };
124
216
  $('mcp-type').addEventListener('change', renderType, { signal: abort.signal });
125
- $('mcp-env-visibility').addEventListener('click', (event) => { event.stopPropagation(); const field = $('mcp-env'); const revealed = field.classList.toggle('pui-revealed'); const button = $('mcp-env-visibility'); button.setAttribute('aria-pressed', String(revealed)); button.textContent = revealed ? 'Hide' : 'Show'; }, { signal: abort.signal });
217
+ const toggleEnvironment = () => { if (disposed)
218
+ return; const field = $('mcp-env'); const revealed = field.classList.toggle('pui-revealed'); const button = $('mcp-env-visibility'); button.setAttribute('aria-pressed', String(revealed)); block.parts.action(button).update({ label: revealed ? 'Hide' : 'Show' }); };
219
+ $('mcp-env-visibility').addEventListener('click', event => { event.stopPropagation(); toggleEnvironment(); }, { signal: abort.signal });
220
+ const submission = bindFormSubmission(form, { read, unavailable: () => !!current.disabled, onSubmit: options.onSubmit, onPending(value) { pending = value; update(current); }, onError(formError) { update({ ...current, formError }); } });
221
+ const clearBearerToken = () => { control('mcp-token').reset(''); };
222
+ const clearSecrets = () => { clearBearerToken(); control('mcp-env').reset(''); };
126
223
  renderType();
127
- return { ...block, renderType, edit, destroy() { if (disposed)
128
- return; disposed = true; abort.abort(); block.destroy(); } };
224
+ update(state);
225
+ return { ...block, update, renderType, edit, read, clearBearerToken, clearSecrets, toggleEnvironment, list: $('mcp-list'), destroy() { if (disposed)
226
+ return; disposed = true; submission.destroy(); abort.abort(); clearSecrets(); block.destroy(); } };
129
227
  }
130
- export function modelEditorMarkup(prefix) { prefix = escape(prefix); return `<label class="pui-field-label" for="${prefix}-provider">Provider</label><select class="pui-feature-select" id="${prefix}-provider" required disabled><option value="">Loading providers…</option></select><label class="pui-field-label" for="${prefix}-search">Model</label><input id="${prefix}-search" type="search" placeholder="Search by model name or ID" autocomplete="off" /><label class="pui-sr-only" for="${prefix}-model">Choose model</label><select id="${prefix}-model" class="pui-model-list" size="6" required disabled></select><p id="${prefix}-capabilities" class="pui-field-hint" aria-live="polite"></p><label class="pui-field-label" id="${prefix}-thinking-label" for="${prefix}-thinking">Thinking effort</label><select id="${prefix}-thinking" class="pui-feature-select" aria-describedby="${prefix}-capabilities" required></select><p class="pui-field-hint">Built-in models come from the Pith SDK catalog. Custom models use your saved definitions. Availability depends on your endpoint and account.</p>`; }
131
228
  export function createModelPicker(state = {}, options = {}) {
132
- return formBlock(`${dialogHeading('Choose a model', 'model-title', 'model-dialog', 'PITH MODEL CATALOG')}<form id="model-form">${modelEditorMarkup('picker')}<div id="model-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button type="button" class="pui-secondary-button" data-close="model-dialog">Cancel</button><button type="submit" class="pui-primary-button" id="choose-model" data-global-idle data-control-pending="true" disabled>Use model</button></div></form>`, 'pui-model-picker', state, options.idPrefix ?? uniqueId('pui-form') + '-');
229
+ const prefix = options.idPrefix ?? uniqueId('pui-form') + '-';
230
+ const block = formBlock(`${dialogHeading('Choose a model', 'model-title', 'model-dialog', 'PITH MODEL CATALOG')}<form id="model-form">${modelEditorMarkup('picker')}<div id="model-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button type="button" class="pui-secondary-button" data-close="model-dialog">Cancel</button><button type="submit" class="pui-primary-button" id="choose-model" data-global-idle data-control-pending="true" disabled>Use model</button></div></form>`, 'pui-model-picker', state, prefix);
231
+ let editor;
232
+ let destroyed = false, pending = false, current = state;
233
+ const form = block.field('model-form'), submit = block.field('choose-model');
234
+ const update = (next) => { if (destroyed)
235
+ return; current = next; block.update(next); if (next.editor) {
236
+ if (editor)
237
+ editor.update(next.editor);
238
+ else
239
+ editor = createModelEditor(next.editor, { prefix: prefix + 'picker', element: block.element });
240
+ } const loading = next.loading ?? !next.editor; block.parts.action(submit).update({ disabled: next.disabled, pending: pending || loading }); };
241
+ const read = () => editor?.read() || { provider: '', model: '', thinkingLevel: '' };
242
+ const submission = bindFormSubmission(form, { read, unavailable: () => submit.disabled, onSubmit: options.onSubmit, onPending(value) { pending = value; update(current); }, onError(error) { update({ ...current, error }); } });
243
+ update(state);
244
+ return { ...block, update, read, reset(selection) { submission.invalidate(); editor?.reset(selection); }, focus() { editor?.focus(); }, destroy() { if (destroyed)
245
+ return; destroyed = true; submission.destroy(); editor?.destroy(); block.destroy(); } };
133
246
  }
134
247
  export function createMCPConnectionList(initial) {
135
248
  const element = document.createElement('div');
136
249
  element.className = 'pui-mcp-connection-list';
250
+ const parts = adoptProductParts(element);
137
251
  let destroyed = false;
138
252
  const entries = new Map();
139
253
  const empty = document.createElement('p');
@@ -180,7 +294,7 @@ export function createMCPConnectionList(initial) {
180
294
  entry.status.classList.toggle('pui-is-error', item.enabled && item.status === 'error');
181
295
  entry.env.hidden = !item.envKeys?.length;
182
296
  setText(entry.env, item.envKeys?.length ? 'Saved environment keys: ' + item.envKeys.join(', ') : '');
183
- setText(entry.error, item.error || '');
297
+ parts.feedback(entry.error).update({ message: item.error || '', tone: 'error' });
184
298
  setText(entry.signIn, item.signedIn ? 'Sign in again' : 'Sign in');
185
299
  entry.signIn.dataset.id = entry.signOut.dataset.id = item.name;
186
300
  entry.edit.dataset.mcpEdit = item.name;
@@ -200,7 +314,7 @@ export function createMCPConnectionList(initial) {
200
314
  else
201
315
  entry.signOut.remove();
202
316
  for (const button of [entry.signIn, entry.signOut, entry.edit, entry.toggle, entry.remove])
203
- button.disabled = !!state.disabled;
317
+ parts.action(button).update({ disabled: !!state.disabled });
204
318
  if (entry.element !== cursor)
205
319
  element.insertBefore(entry.element, cursor);
206
320
  cursor = entry.element.nextElementSibling;
@@ -216,9 +330,10 @@ export function createMCPConnectionList(initial) {
216
330
  }
217
331
  else
218
332
  empty.remove();
333
+ parts.update();
219
334
  };
220
335
  update(initial);
221
336
  return { element, update, destroy() { if (destroyed)
222
- return; destroyed = true; entries.clear(); element.remove(); } };
337
+ return; destroyed = true; parts.destroy(); entries.clear(); element.remove(); } };
223
338
  }
224
339
  //# sourceMappingURL=panels.js.map