pith-ui 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI.md +67 -0
- package/API.md +3281 -0
- package/LICENSE +661 -0
- package/README.md +80 -2
- package/dist/composer.d.ts +38 -0
- package/dist/composer.d.ts.map +1 -0
- package/dist/composer.js +114 -0
- package/dist/composer.js.map +1 -0
- package/dist/content/conversation-reader.d.ts +19 -0
- package/dist/content/conversation-reader.d.ts.map +1 -0
- package/dist/content/conversation-reader.js +280 -0
- package/dist/content/conversation-reader.js.map +1 -0
- package/dist/content/conversation-view.d.ts +48 -0
- package/dist/content/conversation-view.d.ts.map +1 -0
- package/dist/content/conversation-view.js +107 -0
- package/dist/content/conversation-view.js.map +1 -0
- package/dist/content/image-viewer.d.ts +23 -0
- package/dist/content/image-viewer.d.ts.map +1 -0
- package/dist/content/image-viewer.js +101 -0
- package/dist/content/image-viewer.js.map +1 -0
- package/dist/content/markdown.d.ts +12 -0
- package/dist/content/markdown.d.ts.map +1 -0
- package/dist/content/markdown.js +68 -0
- package/dist/content/markdown.js.map +1 -0
- package/dist/content/paged-preview.d.ts +15 -0
- package/dist/content/paged-preview.d.ts.map +1 -0
- package/dist/content/paged-preview.js +105 -0
- package/dist/content/paged-preview.js.map +1 -0
- package/dist/content/views.d.ts +9 -0
- package/dist/content/views.d.ts.map +1 -0
- package/dist/content/views.js +53 -0
- package/dist/content/views.js.map +1 -0
- package/dist/content/welcome.d.ts +18 -0
- package/dist/content/welcome.d.ts.map +1 -0
- package/dist/content/welcome.js +45 -0
- package/dist/content/welcome.js.map +1 -0
- package/dist/core.d.ts +36 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +44 -0
- package/dist/core.js.map +1 -0
- package/dist/dom.d.ts +14 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +69 -0
- package/dist/dom.js.map +1 -0
- package/dist/files/panels.d.ts +95 -0
- package/dist/files/panels.d.ts.map +1 -0
- package/dist/files/panels.js +245 -0
- package/dist/files/panels.js.map +1 -0
- package/dist/files/systems.d.ts +91 -0
- package/dist/files/systems.d.ts.map +1 -0
- package/dist/files/systems.js +256 -0
- package/dist/files/systems.js.map +1 -0
- package/dist/forms/actions.d.ts +27 -0
- package/dist/forms/actions.d.ts.map +1 -0
- package/dist/forms/actions.js +19 -0
- package/dist/forms/actions.js.map +1 -0
- package/dist/forms/custom-connection.d.ts +61 -0
- package/dist/forms/custom-connection.d.ts.map +1 -0
- package/dist/forms/custom-connection.js +65 -0
- package/dist/forms/custom-connection.js.map +1 -0
- package/dist/forms/dialog-frame.d.ts +39 -0
- package/dist/forms/dialog-frame.d.ts.map +1 -0
- package/dist/forms/dialog-frame.js +75 -0
- package/dist/forms/dialog-frame.js.map +1 -0
- package/dist/forms/history.d.ts +16 -0
- package/dist/forms/history.d.ts.map +1 -0
- package/dist/forms/history.js +21 -0
- package/dist/forms/history.js.map +1 -0
- package/dist/forms/identity.d.ts +4 -0
- package/dist/forms/identity.d.ts.map +1 -0
- package/dist/forms/identity.js +21 -0
- package/dist/forms/identity.js.map +1 -0
- package/dist/forms/model-editor.d.ts +35 -0
- package/dist/forms/model-editor.d.ts.map +1 -0
- package/dist/forms/model-editor.js +78 -0
- package/dist/forms/model-editor.js.map +1 -0
- package/dist/forms/panels.d.ts +57 -0
- package/dist/forms/panels.d.ts.map +1 -0
- package/dist/forms/panels.js +224 -0
- package/dist/forms/panels.js.map +1 -0
- package/dist/forms/provider-login.d.ts +22 -0
- package/dist/forms/provider-login.d.ts.map +1 -0
- package/dist/forms/provider-login.js +114 -0
- package/dist/forms/provider-login.js.map +1 -0
- package/dist/forms/resource-editor.d.ts +23 -0
- package/dist/forms/resource-editor.d.ts.map +1 -0
- package/dist/forms/resource-editor.js +24 -0
- package/dist/forms/resource-editor.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation/context-menu.d.ts +25 -0
- package/dist/navigation/context-menu.d.ts.map +1 -0
- package/dist/navigation/context-menu.js +67 -0
- package/dist/navigation/context-menu.js.map +1 -0
- package/dist/navigation/navigation.d.ts +57 -0
- package/dist/navigation/navigation.d.ts.map +1 -0
- package/dist/navigation/navigation.js +185 -0
- package/dist/navigation/navigation.js.map +1 -0
- package/dist/primitives.d.ts +307 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +741 -0
- package/dist/primitives.js.map +1 -0
- package/dist/styles.css +849 -0
- package/dist/tasks/approval.d.ts +25 -0
- package/dist/tasks/approval.d.ts.map +1 -0
- package/dist/tasks/approval.js +61 -0
- package/dist/tasks/approval.js.map +1 -0
- package/dist/tasks/permission.d.ts +35 -0
- package/dist/tasks/permission.d.ts.map +1 -0
- package/dist/tasks/permission.js +67 -0
- package/dist/tasks/permission.js.map +1 -0
- package/dist/tasks/runtime.d.ts +31 -0
- package/dist/tasks/runtime.d.ts.map +1 -0
- package/dist/tasks/runtime.js +34 -0
- package/dist/tasks/runtime.js.map +1 -0
- package/dist/theme.d.ts +4 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +6 -0
- package/dist/theme.js.map +1 -0
- package/dist/workspace.d.ts +38 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +183 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +27 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { UIBlock } from '../core.js';
|
|
2
|
+
export interface ApprovalView {
|
|
3
|
+
id: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
warning?: string;
|
|
6
|
+
canAlwaysAllow: boolean;
|
|
7
|
+
argsText: string;
|
|
8
|
+
preview?: {
|
|
9
|
+
kind: string;
|
|
10
|
+
path: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
diff?: string;
|
|
13
|
+
truncated?: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface ApprovalPanelState {
|
|
17
|
+
approval: ApprovalView | null;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare function createApprovalPanel(initial: ApprovalPanelState, options?: {
|
|
21
|
+
element?: HTMLElement;
|
|
22
|
+
idPrefix?: string;
|
|
23
|
+
onAction?: (id: string, action: 'deny' | 'always' | 'allow') => void;
|
|
24
|
+
}): UIBlock<ApprovalPanelState>;
|
|
25
|
+
//# sourceMappingURL=approval.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../src/tasks/approval.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,MAAM,WAAW,YAAY;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,OAAO,CAAA;KAAC,CAAA;CAAC;AACrN,MAAM,WAAW,kBAAkB;IAAE,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AACvF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAsB5N"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { scopeIds, localField } from '../forms/identity.js';
|
|
2
|
+
import { escape, icon, uniqueId } from '../dom.js';
|
|
3
|
+
import { createPagedPreview } from '../content/paged-preview.js';
|
|
4
|
+
export function createApprovalPanel(initial, options = {}) {
|
|
5
|
+
const element = options.element || document.createElement('div');
|
|
6
|
+
if (!options.element)
|
|
7
|
+
element.className = 'pui-approval-panel';
|
|
8
|
+
const prefix = options.idPrefix ?? uniqueId('pui-approval') + '-';
|
|
9
|
+
const $ = (id) => localField(element, prefix + id);
|
|
10
|
+
const abort = new AbortController();
|
|
11
|
+
let contentAbort = new AbortController();
|
|
12
|
+
let signature = '';
|
|
13
|
+
let destroyed = false;
|
|
14
|
+
let previews = [];
|
|
15
|
+
const update = (state) => {
|
|
16
|
+
if (destroyed)
|
|
17
|
+
return;
|
|
18
|
+
const approval = state.approval;
|
|
19
|
+
const next = JSON.stringify(approval);
|
|
20
|
+
if (next !== signature) {
|
|
21
|
+
signature = next;
|
|
22
|
+
contentAbort.abort();
|
|
23
|
+
contentAbort = new AbortController();
|
|
24
|
+
for (const preview of previews)
|
|
25
|
+
preview.destroy();
|
|
26
|
+
previews = [];
|
|
27
|
+
const warning = approval?.warning || '';
|
|
28
|
+
const canAlwaysAllow = !!approval?.canAlwaysAllow;
|
|
29
|
+
const preview = approval?.preview;
|
|
30
|
+
const previewBody = preview?.error ? `<p class="pui-preview-note" role="status">${escape(preview.error)}</p>` : preview ? `<div id="approval-diff-preview"></div>${preview.truncated ? '<p class="pui-preview-note">Preview shortened. Expand Tool arguments for the complete change.</p>' : ''}` : '';
|
|
31
|
+
const previewMarkup = preview ? `<div class="pui-approval-file"><strong>${preview.kind === 'create' ? 'Create file' : 'Modify file'}</strong><code>${escape(preview.path)}</code></div>${previewBody}` : '';
|
|
32
|
+
const args = `<div id="approval-args-preview"></div>`;
|
|
33
|
+
element.innerHTML = approval ? `<section class="pui-approval-card" aria-label="Tool approval required"><div class="pui-approval-heading">${icon('shield')}<div><strong>Pith needs your permission</strong><span>Review this action before it runs.</span></div><span class="pui-approval-badge">${escape(approval.toolName)}</span></div>${warning ? `<p class="pui-approval-warning">${escape(warning)}</p>` : ''}${previewMarkup}${preview ? `<details class="pui-approval-arguments" ${preview.error ? 'open' : ''}><summary>Tool arguments</summary>${args}</details>` : args}${canAlwaysAllow ? '<p class="pui-approval-scope">Workspace permission applies to this conversation. Commands and external connection tools still need approval.</p>' : ''}<div class="pui-approval-actions"><button class="pui-secondary-button" data-approval="deny" ${state.disabled ? 'disabled' : ''}>Deny</button>${canAlwaysAllow ? `<button class="pui-secondary-button pui-always-allow-button" data-approval="always" ${state.disabled ? 'disabled' : ''}>Always allow workspace changes</button>` : ''}<button class="pui-primary-button" data-approval="allow" ${state.disabled ? 'disabled' : ''}>${icon('check')}Allow this action</button></div></section>` : '';
|
|
34
|
+
for (const child of element.children)
|
|
35
|
+
scopeIds(child, prefix);
|
|
36
|
+
for (const button of element.querySelectorAll('[data-approval]'))
|
|
37
|
+
button.dataset.approvalId = approval?.id || '';
|
|
38
|
+
if (preview && !preview.error)
|
|
39
|
+
previews.push(createPagedPreview({ container: $('approval-diff-preview'), text: preview.diff || 'No content changes.', kind: 'diff' }));
|
|
40
|
+
if (approval) {
|
|
41
|
+
const argsContainer = $('approval-args-preview');
|
|
42
|
+
const details = argsContainer.closest('details');
|
|
43
|
+
let loaded = false;
|
|
44
|
+
const showArgs = () => { if (destroyed || loaded || (details && !details.open))
|
|
45
|
+
return; loaded = true; previews.push(createPagedPreview({ container: argsContainer, text: approval.argsText, kind: 'text' })); };
|
|
46
|
+
details?.addEventListener('toggle', showArgs, { signal: contentAbort.signal });
|
|
47
|
+
showArgs();
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
for (const button of element.querySelectorAll('[data-approval]'))
|
|
51
|
+
button.disabled = !!state.disabled;
|
|
52
|
+
};
|
|
53
|
+
element.addEventListener('click', (event) => { const button = event.target.closest('[data-approval]'); if (button && !button.disabled)
|
|
54
|
+
options.onAction?.(button.dataset.approvalId, button.dataset.approval); }, { signal: abort.signal });
|
|
55
|
+
update(initial);
|
|
56
|
+
return { element, update, destroy() { if (destroyed)
|
|
57
|
+
return; destroyed = true; abort.abort(); contentAbort.abort(); for (const preview of previews)
|
|
58
|
+
preview.destroy(); if (!options.element)
|
|
59
|
+
element.remove(); } };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=approval.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../src/tasks/approval.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds, localField} from '../forms/identity.js';\nimport type { UIBlock } from '../core.js';\nimport {escape, icon, uniqueId} from '../dom.js';\nimport {createPagedPreview, type PagedPreviewState} from '../content/paged-preview.js';\nexport interface ApprovalView {id: string; toolName: string; warning?: string; canAlwaysAllow: boolean; argsText: string; preview?: {kind: string; path: string; error?: string; diff?: string; truncated?: boolean}}\nexport interface ApprovalPanelState {approval: ApprovalView | null; disabled?: boolean}\nexport function createApprovalPanel(initial: ApprovalPanelState, options: {element?: HTMLElement; idPrefix?: string; onAction?: (id: string, action: 'deny' | 'always' | 'allow') => void} = {}): UIBlock<ApprovalPanelState> {\n const element = options.element || document.createElement('div'); if (!options.element) element.className = 'pui-approval-panel';\n const prefix = options.idPrefix ?? uniqueId('pui-approval') + '-';\n const $ = <T extends HTMLElement = HTMLElement>(id: string) => localField<T>(element, prefix + id);\n const abort = new AbortController(); let contentAbort = new AbortController(); let signature = ''; let destroyed = false; let previews: UIBlock<PagedPreviewState>[] = [];\n const update = (state: Readonly<ApprovalPanelState>) => {\n if (destroyed) return; const approval = state.approval; const next = JSON.stringify(approval);\n if (next !== signature) {signature = next; contentAbort.abort(); contentAbort = new AbortController(); for (const preview of previews) preview.destroy(); previews = [];\n const warning = approval?.warning || ''; const canAlwaysAllow = !!approval?.canAlwaysAllow; const preview = approval?.preview;\n const previewBody = preview?.error ? `<p class=\"pui-preview-note\" role=\"status\">${escape(preview.error)}</p>` : preview ? `<div id=\"approval-diff-preview\"></div>${preview.truncated ? '<p class=\"pui-preview-note\">Preview shortened. Expand Tool arguments for the complete change.</p>' : ''}` : '';\n const previewMarkup = preview ? `<div class=\"pui-approval-file\"><strong>${preview.kind === 'create' ? 'Create file' : 'Modify file'}</strong><code>${escape(preview.path)}</code></div>${previewBody}` : '';\n const args = `<div id=\"approval-args-preview\"></div>`;\n\n element.innerHTML = approval ? `<section class=\"pui-approval-card\" aria-label=\"Tool approval required\"><div class=\"pui-approval-heading\">${icon('shield')}<div><strong>Pith needs your permission</strong><span>Review this action before it runs.</span></div><span class=\"pui-approval-badge\">${escape(approval.toolName)}</span></div>${warning ? `<p class=\"pui-approval-warning\">${escape(warning)}</p>` : ''}${previewMarkup}${preview ? `<details class=\"pui-approval-arguments\" ${preview.error ? 'open' : ''}><summary>Tool arguments</summary>${args}</details>` : args}${canAlwaysAllow ? '<p class=\"pui-approval-scope\">Workspace permission applies to this conversation. Commands and external connection tools still need approval.</p>' : ''}<div class=\"pui-approval-actions\"><button class=\"pui-secondary-button\" data-approval=\"deny\" ${state.disabled ? 'disabled' : ''}>Deny</button>${canAlwaysAllow ? `<button class=\"pui-secondary-button pui-always-allow-button\" data-approval=\"always\" ${state.disabled ? 'disabled' : ''}>Always allow workspace changes</button>` : ''}<button class=\"pui-primary-button\" data-approval=\"allow\" ${state.disabled ? 'disabled' : ''}>${icon('check')}Allow this action</button></div></section>` : ''; for (const child of element.children) scopeIds(child as HTMLElement, prefix);\n for (const button of element.querySelectorAll<HTMLButtonElement>('[data-approval]')) button.dataset.approvalId = approval?.id || '';\n if (preview && !preview.error) previews.push(createPagedPreview({container: $<HTMLElement>('approval-diff-preview'), text: preview.diff || 'No content changes.', kind: 'diff'}));\n if (approval) {const argsContainer = $<HTMLElement>('approval-args-preview'); const details = argsContainer.closest('details'); let loaded = false; const showArgs = () => {if (destroyed || loaded || (details && !details.open)) return; loaded = true; previews.push(createPagedPreview({container: argsContainer, text: approval.argsText, kind: 'text'}));}; details?.addEventListener('toggle', showArgs, {signal:contentAbort.signal}); showArgs();}\n }\n for (const button of element.querySelectorAll<HTMLButtonElement>('[data-approval]')) button.disabled = !!state.disabled;\n };\n element.addEventListener('click', (event) => {const button = (event.target as HTMLElement).closest<HTMLButtonElement>('[data-approval]'); if (button && !button.disabled) options.onAction?.(button.dataset.approvalId!, button.dataset.approval as 'deny' | 'always' | 'allow');}, {signal:abort.signal});\n update(initial); return {element, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); contentAbort.abort(); for (const preview of previews) preview.destroy(); if (!options.element) element.remove();}};\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { UIBlock } from '../core.js';
|
|
2
|
+
export interface FullAccessState {
|
|
3
|
+
title?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function createFullAccessConfirmation(initial: FullAccessState, options?: {
|
|
8
|
+
idPrefix?: string;
|
|
9
|
+
}): UIBlock<FullAccessState> & {
|
|
10
|
+
readonly confirmButton: HTMLButtonElement;
|
|
11
|
+
};
|
|
12
|
+
export interface TaskFailureState {
|
|
13
|
+
visible: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
advice: string;
|
|
16
|
+
canContinue: boolean;
|
|
17
|
+
workspaceId?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function createTaskFailure(initial: TaskFailureState, options?: {
|
|
20
|
+
element?: HTMLElement;
|
|
21
|
+
onContinue?: () => void;
|
|
22
|
+
onSettings?: () => void;
|
|
23
|
+
onDiagnostics?: () => void;
|
|
24
|
+
}): UIBlock<TaskFailureState>;
|
|
25
|
+
export interface PermissionControlState {
|
|
26
|
+
mode: string;
|
|
27
|
+
description: string;
|
|
28
|
+
disabled?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare function createPermissionControl(initial: PermissionControlState, options?: {
|
|
31
|
+
element?: HTMLElement;
|
|
32
|
+
descriptionElement?: HTMLElement;
|
|
33
|
+
onChange?: (value: string) => void;
|
|
34
|
+
}): UIBlock<PermissionControlState>;
|
|
35
|
+
//# sourceMappingURL=permission.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.d.ts","sourceRoot":"","sources":["../../src/tasks/permission.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,eAAe;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AACrF,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG;IAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAA;CAAC,CAKhL;AACD,MAAM,WAAW,gBAAgB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAC;AACjI,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAK3M;AACD,MAAM,WAAW,sBAAsB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AAC/F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAOrN"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { scopeIds } from '../forms/identity.js';
|
|
2
|
+
import { escape, icon, uniqueId } from '../dom.js';
|
|
3
|
+
export function createFullAccessConfirmation(initial, options = {}) {
|
|
4
|
+
const state = initial;
|
|
5
|
+
const element = document.createElement('div');
|
|
6
|
+
element.className = 'pui-full-access-confirmation';
|
|
7
|
+
element.innerHTML = `<div class="pui-modal-heading"><div><span class="pui-eyebrow">CONVERSATION PERMISSIONS</span><h2 id="full-access-title">Enable full access?</h2></div><button class="pui-quiet-icon" data-close="permissions-dialog" aria-label="Cancel full access">${icon('close')}</button></div><p class="pui-modal-description">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong>${escape(state.title || 'this conversation')}</strong>.</p><div class="pui-full-access-explanation">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class="pui-permission-revoke-note">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id="permissions-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button class="pui-secondary-button" type="button" data-close="permissions-dialog">Cancel</button><button class="pui-primary-button pui-full-access-confirm" id="enable-full-access" type="button">Enable full access</button></div>`;
|
|
8
|
+
let destroyed = false;
|
|
9
|
+
const confirmButton = element.querySelector('#enable-full-access');
|
|
10
|
+
const error = element.querySelector('#permissions-error');
|
|
11
|
+
const update = (state) => { if (destroyed)
|
|
12
|
+
return; confirmButton.disabled = !!state.disabled; if (state.error !== undefined)
|
|
13
|
+
error.textContent = state.error; };
|
|
14
|
+
update(initial);
|
|
15
|
+
scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');
|
|
16
|
+
return { element, confirmButton, update, destroy() { if (destroyed)
|
|
17
|
+
return; destroyed = true; element.remove(); } };
|
|
18
|
+
}
|
|
19
|
+
export function createTaskFailure(initial, options = {}) {
|
|
20
|
+
const element = options.element || document.createElement('section');
|
|
21
|
+
if (!options.element)
|
|
22
|
+
element.className = 'pui-task-failure';
|
|
23
|
+
const abort = new AbortController();
|
|
24
|
+
let signature = '';
|
|
25
|
+
let destroyed = false;
|
|
26
|
+
const update = (state) => { if (destroyed)
|
|
27
|
+
return; const next = JSON.stringify(state); if (next === signature)
|
|
28
|
+
return; signature = next; element.hidden = !state.visible; element.innerHTML = state.visible ? `<strong>${escape(state.message)}</strong><p>${escape(state.advice)}</p><div>${state.canContinue ? `<button class="pui-secondary-button" type="button" data-action="continue" data-idle-action data-idle-workspace="${escape(state.workspaceId)}">Review and continue</button>` : '<button class="pui-secondary-button" type="button" data-action="settings">Check model settings</button>'}<button class="pui-secondary-button" type="button" data-action="diagnostics">Save diagnostics</button></div>` : ''; };
|
|
29
|
+
element.addEventListener('click', (event) => { const action = event.target.closest('[data-action]')?.dataset.action; if (action === 'continue')
|
|
30
|
+
options.onContinue?.(); if (action === 'settings')
|
|
31
|
+
options.onSettings?.(); if (action === 'diagnostics')
|
|
32
|
+
options.onDiagnostics?.(); }, { signal: abort.signal });
|
|
33
|
+
update(initial);
|
|
34
|
+
return { element, update, destroy() { if (destroyed)
|
|
35
|
+
return; destroyed = true; abort.abort(); if (!options.element)
|
|
36
|
+
element.remove(); } };
|
|
37
|
+
}
|
|
38
|
+
export function createPermissionControl(initial, options = {}) {
|
|
39
|
+
const element = options.element || document.createElement('label');
|
|
40
|
+
if (!options.element) {
|
|
41
|
+
element.className = 'pui-permission-control';
|
|
42
|
+
element.innerHTML = `${icon('shield')}<span class="pui-sr-only">Conversation permissions</span><select aria-describedby="pui-permission-description"><option value="ask">Ask before changes</option><option value="workspace-write">Allow workspace changes</option><option value="full-access">Full access</option></select>${icon('down')}`;
|
|
43
|
+
}
|
|
44
|
+
const selector = element.querySelector('select');
|
|
45
|
+
const abort = new AbortController();
|
|
46
|
+
let destroyed = false;
|
|
47
|
+
if (options.descriptionElement) {
|
|
48
|
+
if (!options.descriptionElement.id)
|
|
49
|
+
options.descriptionElement.id = uniqueId('pui-permission-description');
|
|
50
|
+
selector.setAttribute('aria-describedby', options.descriptionElement.id);
|
|
51
|
+
}
|
|
52
|
+
else
|
|
53
|
+
selector.removeAttribute('aria-describedby');
|
|
54
|
+
const update = (state) => { if (destroyed)
|
|
55
|
+
return; selector.value = state.mode; selector.disabled = !!state.disabled; element.classList.toggle('pui-full-access', state.mode === 'full-access'); if (options.descriptionElement) {
|
|
56
|
+
options.descriptionElement.textContent = state.description;
|
|
57
|
+
options.descriptionElement.classList.toggle('pui-full-access-note', state.mode === 'full-access');
|
|
58
|
+
}
|
|
59
|
+
else
|
|
60
|
+
selector.title = state.description; };
|
|
61
|
+
selector.addEventListener('change', () => options.onChange?.(selector.value), { signal: abort.signal });
|
|
62
|
+
update(initial);
|
|
63
|
+
return { element, update, destroy() { if (destroyed)
|
|
64
|
+
return; destroyed = true; abort.abort(); if (!options.element)
|
|
65
|
+
element.remove(); } };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=permission.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/tasks/permission.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { UIBlock } from '../core.js';\nimport { escape, icon, uniqueId } from '../dom.js';\nexport interface FullAccessState {title?: string; error?: string; disabled?: boolean}\nexport function createFullAccessConfirmation(initial: FullAccessState, options: {idPrefix?: string} = {}): UIBlock<FullAccessState> & {readonly confirmButton: HTMLButtonElement} {\n const state = initial; const element = document.createElement('div'); element.className = 'pui-full-access-confirmation'; element.innerHTML = `<div class=\"pui-modal-heading\"><div><span class=\"pui-eyebrow\">CONVERSATION PERMISSIONS</span><h2 id=\"full-access-title\">Enable full access?</h2></div><button class=\"pui-quiet-icon\" data-close=\"permissions-dialog\" aria-label=\"Cancel full access\">${icon('close')}</button></div><p class=\"pui-modal-description\">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong>${escape(state.title || 'this conversation')}</strong>.</p><div class=\"pui-full-access-explanation\">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class=\"pui-permission-revoke-note\">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id=\"permissions-error\" class=\"pui-form-error\" role=\"alert\"></div><div class=\"pui-modal-footer\"><button class=\"pui-secondary-button\" type=\"button\" data-close=\"permissions-dialog\">Cancel</button><button class=\"pui-primary-button pui-full-access-confirm\" id=\"enable-full-access\" type=\"button\">Enable full access</button></div>`;\n let destroyed = false; const confirmButton = element.querySelector<HTMLButtonElement>('#enable-full-access')!; const error = element.querySelector<HTMLElement>('#permissions-error')!;\n const update = (state: Readonly<FullAccessState>) => {if (destroyed) return; confirmButton.disabled = !!state.disabled; if (state.error !== undefined) error.textContent = state.error;}; update(initial); scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');\n return {element, confirmButton, update, destroy() {if (destroyed) return; destroyed = true; element.remove();}};\n}\nexport interface TaskFailureState {visible: boolean; message: string; advice: string; canContinue: boolean; workspaceId?: string}\nexport function createTaskFailure(initial: TaskFailureState, options: {element?: HTMLElement; onContinue?: () => void; onSettings?: () => void; onDiagnostics?: () => void} = {}): UIBlock<TaskFailureState> {\n const element = options.element || document.createElement('section'); if (!options.element) element.className = 'pui-task-failure'; const abort = new AbortController(); let signature = ''; let destroyed = false;\n const update = (state: Readonly<TaskFailureState>) => {if (destroyed) return; const next = JSON.stringify(state); if (next === signature) return; signature = next; element.hidden = !state.visible; element.innerHTML = state.visible ? `<strong>${escape(state.message)}</strong><p>${escape(state.advice)}</p><div>${state.canContinue ? `<button class=\"pui-secondary-button\" type=\"button\" data-action=\"continue\" data-idle-action data-idle-workspace=\"${escape(state.workspaceId)}\">Review and continue</button>` : '<button class=\"pui-secondary-button\" type=\"button\" data-action=\"settings\">Check model settings</button>'}<button class=\"pui-secondary-button\" type=\"button\" data-action=\"diagnostics\">Save diagnostics</button></div>` : '';};\n element.addEventListener('click', (event) => {const action = (event.target as HTMLElement).closest<HTMLElement>('[data-action]')?.dataset.action; if (action === 'continue') options.onContinue?.(); if (action === 'settings') options.onSettings?.(); if (action === 'diagnostics') options.onDiagnostics?.();}, {signal:abort.signal});\n update(initial); return {element, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); if (!options.element) element.remove();}};\n}\nexport interface PermissionControlState {mode: string; description: string; disabled?: boolean}\nexport function createPermissionControl(initial: PermissionControlState, options: {element?: HTMLElement; descriptionElement?: HTMLElement; onChange?: (value: string) => void} = {}): UIBlock<PermissionControlState> {\n const element = options.element || document.createElement('label'); if (!options.element) {element.className = 'pui-permission-control'; element.innerHTML = `${icon('shield')}<span class=\"pui-sr-only\">Conversation permissions</span><select aria-describedby=\"pui-permission-description\"><option value=\"ask\">Ask before changes</option><option value=\"workspace-write\">Allow workspace changes</option><option value=\"full-access\">Full access</option></select>${icon('down')}`;}\n const selector = element.querySelector<HTMLSelectElement>('select')!; const abort = new AbortController(); let destroyed = false;\n if (options.descriptionElement) {if (!options.descriptionElement.id) options.descriptionElement.id = uniqueId('pui-permission-description'); selector.setAttribute('aria-describedby', options.descriptionElement.id);} else selector.removeAttribute('aria-describedby');\n const update = (state: Readonly<PermissionControlState>) => {if (destroyed) return; selector.value = state.mode; selector.disabled = !!state.disabled; element.classList.toggle('pui-full-access', state.mode === 'full-access'); if (options.descriptionElement) {options.descriptionElement.textContent = state.description; options.descriptionElement.classList.toggle('pui-full-access-note', state.mode === 'full-access');} else selector.title = state.description;};\n selector.addEventListener('change', () => options.onChange?.(selector.value), {signal:abort.signal}); update(initial);\n return {element, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); if (!options.element) element.remove();}};\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { UIBlock } from '../core.js';
|
|
2
|
+
export interface RuntimePanelState {
|
|
3
|
+
visible: boolean;
|
|
4
|
+
summary: string;
|
|
5
|
+
duration: string;
|
|
6
|
+
speed: string;
|
|
7
|
+
provider?: string;
|
|
8
|
+
thinkingLevel?: string;
|
|
9
|
+
inputTokens: string;
|
|
10
|
+
outputTokens: string;
|
|
11
|
+
cacheRead: string;
|
|
12
|
+
cacheWrite: string;
|
|
13
|
+
contextTokens: string;
|
|
14
|
+
contextWindow: string;
|
|
15
|
+
compactions: string;
|
|
16
|
+
toolFailures: string;
|
|
17
|
+
recordedCost: string;
|
|
18
|
+
latestCost: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function createRuntimePanel(initial: RuntimePanelState, options?: {
|
|
21
|
+
element?: HTMLElement;
|
|
22
|
+
idPrefix?: string;
|
|
23
|
+
costs?: Node;
|
|
24
|
+
}): UIBlock<RuntimePanelState> & {
|
|
25
|
+
updateTiming(value: {
|
|
26
|
+
summary: string;
|
|
27
|
+
duration: string;
|
|
28
|
+
speed: string;
|
|
29
|
+
}): void;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/tasks/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,iBAAiB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC;AACvW,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,GAAG;IAAC,YAAY,CAAC,KAAK,EAAE;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI,CAAA;CAAC,CAOnP"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { scopeIds } from '../forms/identity.js';
|
|
2
|
+
import { escape, uniqueId } from '../dom.js';
|
|
3
|
+
export function createRuntimePanel(initial, options = {}) {
|
|
4
|
+
const element = options.element || document.createElement('details');
|
|
5
|
+
if (!options.element) {
|
|
6
|
+
element.className = 'pui-run-status pui-runtime-panel';
|
|
7
|
+
element.innerHTML = '<summary id="run-status-summary"></summary><div id="run-status-details"><div id="runtime-values"></div></div>';
|
|
8
|
+
if (options.costs)
|
|
9
|
+
element.querySelector('#run-status-details').append(options.costs);
|
|
10
|
+
}
|
|
11
|
+
const summary = element.querySelector('#run-status-summary');
|
|
12
|
+
const values = element.querySelector('#runtime-values');
|
|
13
|
+
const prefix = options.idPrefix ?? uniqueId('pui-runtime') + '-';
|
|
14
|
+
for (const child of element.children)
|
|
15
|
+
scopeIds(child, prefix);
|
|
16
|
+
let signature = '';
|
|
17
|
+
let destroyed = false;
|
|
18
|
+
const updateTiming = (state) => { if (destroyed)
|
|
19
|
+
return; summary.textContent = state.summary; const duration = values.querySelector('[data-runtime-duration]'); const speed = values.querySelector('[data-runtime-speed]'); if (duration)
|
|
20
|
+
duration.textContent = state.duration; if (speed)
|
|
21
|
+
speed.textContent = state.speed; };
|
|
22
|
+
const update = (state) => { if (destroyed)
|
|
23
|
+
return; element.hidden = !state.visible; const next = JSON.stringify({ ...state, summary: undefined, duration: undefined, speed: undefined }); if (next !== signature) {
|
|
24
|
+
signature = next;
|
|
25
|
+
values.innerHTML = state.visible ? `<dl>${state.provider ? `<dt>Provider</dt><dd>${escape(state.provider)}</dd>` : ''}${state.thinkingLevel ? `<dt>Thinking effort</dt><dd>${escape(state.thinkingLevel)}</dd>` : ''}<dt>Latest task duration</dt><dd id="runtime-duration" data-runtime-duration></dd><dt>Avg. output tokens/s</dt><dd id="runtime-speed" data-runtime-speed></dd><dt>Session input / output</dt><dd>${escape(state.inputTokens)} / ${escape(state.outputTokens)}</dd><dt>Cache read / write</dt><dd>${escape(state.cacheRead)} / ${escape(state.cacheWrite)}</dd><dt>Estimated conversation context</dt><dd>~${escape(state.contextTokens)} / ${escape(state.contextWindow)} tokens</dd><dt>Context summaries</dt><dd>${escape(state.compactions)}</dd><dt>Tool failures recorded</dt><dd>${escape(state.toolFailures)}</dd><dt>Recorded cost (USD)</dt><dd>${escape(state.recordedCost)}</dd><dt>Latest task (USD)</dt><dd>${escape(state.latestCost)}</dd><dt>Execution</dt><dd>Codemode enabled · durable local task journal</dd></dl><p>Duration and average speed cover the latest task, including tools and waits. Speed uses reported output tokens, including reasoning, retries and summaries; input, cache and earlier tasks are excluded. An interrupted checkpoint shows only a lower bound for duration. Session token figures come from Pith records. Context excludes system instructions and tool schemas. Costs estimate recorded requests; unknown prices and older unrecorded requests are excluded. This is not a provider bill.</p>` : '';
|
|
26
|
+
for (const child of values.children)
|
|
27
|
+
scopeIds(child, prefix);
|
|
28
|
+
} updateTiming(state); };
|
|
29
|
+
update(initial);
|
|
30
|
+
return { element, update, updateTiming, destroy() { if (destroyed)
|
|
31
|
+
return; destroyed = true; if (!options.element)
|
|
32
|
+
element.remove(); } };
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/tasks/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { UIBlock } from '../core.js';\nimport { escape, uniqueId } from '../dom.js';\nexport interface RuntimePanelState {visible: boolean; summary: string; duration: string; speed: string; provider?: string; thinkingLevel?: string; inputTokens: string; outputTokens: string; cacheRead: string; cacheWrite: string; contextTokens: string; contextWindow: string; compactions: string; toolFailures: string; recordedCost: string; latestCost: string}\nexport function createRuntimePanel(initial: RuntimePanelState, options: {element?: HTMLElement; idPrefix?: string; costs?: Node} = {}): UIBlock<RuntimePanelState> & {updateTiming(value: {summary: string; duration: string; speed: string}): void} {\n const element = options.element || document.createElement('details');\n if (!options.element) {element.className = 'pui-run-status pui-runtime-panel'; element.innerHTML = '<summary id=\"run-status-summary\"></summary><div id=\"run-status-details\"><div id=\"runtime-values\"></div></div>'; if (options.costs) element.querySelector('#run-status-details')!.append(options.costs);}\n const summary = element.querySelector<HTMLElement>('#run-status-summary')!; const values = element.querySelector<HTMLElement>('#runtime-values')!; const prefix = options.idPrefix ?? uniqueId('pui-runtime') + '-'; for (const child of element.children) scopeIds(child as HTMLElement, prefix); let signature = ''; let destroyed = false;\n const updateTiming = (state: {summary: string; duration: string; speed: string}) => {if (destroyed) return; summary.textContent = state.summary; const duration = values.querySelector<HTMLElement>('[data-runtime-duration]'); const speed = values.querySelector<HTMLElement>('[data-runtime-speed]'); if (duration) duration.textContent = state.duration; if (speed) speed.textContent = state.speed;};\n const update = (state: Readonly<RuntimePanelState>) => {if (destroyed) return; element.hidden = !state.visible; const next = JSON.stringify({...state, summary: undefined, duration: undefined, speed: undefined}); if (next !== signature) {signature = next; values.innerHTML = state.visible ? `<dl>${state.provider ? `<dt>Provider</dt><dd>${escape(state.provider)}</dd>` : ''}${state.thinkingLevel ? `<dt>Thinking effort</dt><dd>${escape(state.thinkingLevel)}</dd>` : ''}<dt>Latest task duration</dt><dd id=\"runtime-duration\" data-runtime-duration></dd><dt>Avg. output tokens/s</dt><dd id=\"runtime-speed\" data-runtime-speed></dd><dt>Session input / output</dt><dd>${escape(state.inputTokens)} / ${escape(state.outputTokens)}</dd><dt>Cache read / write</dt><dd>${escape(state.cacheRead)} / ${escape(state.cacheWrite)}</dd><dt>Estimated conversation context</dt><dd>~${escape(state.contextTokens)} / ${escape(state.contextWindow)} tokens</dd><dt>Context summaries</dt><dd>${escape(state.compactions)}</dd><dt>Tool failures recorded</dt><dd>${escape(state.toolFailures)}</dd><dt>Recorded cost (USD)</dt><dd>${escape(state.recordedCost)}</dd><dt>Latest task (USD)</dt><dd>${escape(state.latestCost)}</dd><dt>Execution</dt><dd>Codemode enabled · durable local task journal</dd></dl><p>Duration and average speed cover the latest task, including tools and waits. Speed uses reported output tokens, including reasoning, retries and summaries; input, cache and earlier tasks are excluded. An interrupted checkpoint shows only a lower bound for duration. Session token figures come from Pith records. Context excludes system instructions and tool schemas. Costs estimate recorded requests; unknown prices and older unrecorded requests are excluded. This is not a provider bill.</p>` : ''; for (const child of values.children) scopeIds(child as HTMLElement, prefix);} updateTiming(state);};\n update(initial); return {element, update, updateTiming, destroy() {if (destroyed) return; destroyed = true; if (!options.element) element.remove();}};\n}\n"]}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AACrD,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,GAAE,UAAqB,GAAG,IAAI,CAGxF"}
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AACA,0EAA0E;AAC1E,MAAM,UAAU,aAAa,CAAC,IAAiB,EAAE,aAAyB,QAAQ;IAChF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1B,IAAI,CAAC,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC;AACvC,CAAC","sourcesContent":["export type Appearance = 'light' | 'dark' | 'system';\n/** Scope tokens and styles to an explicit application or preview root. */\nexport function setAppearance(root: HTMLElement, appearance: Appearance = 'system'): void {\n root.classList.add('pui');\n root.dataset.appearance = appearance;\n}\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type UIBlock, type ActionResult } from './core.js';
|
|
2
|
+
export interface ComposerViewState {
|
|
3
|
+
placeholder?: string;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
readOnly?: boolean;
|
|
6
|
+
action?: 'send' | 'queue' | 'stop';
|
|
7
|
+
actionDisabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface ComposerViewOptions {
|
|
10
|
+
prefix?: string;
|
|
11
|
+
onSubmit?: (text: string) => void | ActionResult | Promise<void | ActionResult>;
|
|
12
|
+
onStop?: () => void | ActionResult | Promise<void | ActionResult>;
|
|
13
|
+
onInput?: (text: string) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function createComposerView(options?: ComposerViewOptions): {
|
|
16
|
+
element: HTMLFormElement;
|
|
17
|
+
input: HTMLTextAreaElement;
|
|
18
|
+
update: (state: ComposerViewState) => void;
|
|
19
|
+
getValue: () => string;
|
|
20
|
+
reset(value: string): void;
|
|
21
|
+
focus: () => void;
|
|
22
|
+
resize: () => void;
|
|
23
|
+
destroy(): void;
|
|
24
|
+
};
|
|
25
|
+
export interface WorkspaceShellOptions {
|
|
26
|
+
brand: Node;
|
|
27
|
+
prefix?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface WorkspaceShellState {
|
|
30
|
+
workspace?: string;
|
|
31
|
+
conversation?: string;
|
|
32
|
+
reconnecting?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare function createWorkspaceShell(options: WorkspaceShellOptions): UIBlock<WorkspaceShellState> & {
|
|
35
|
+
composer: ReturnType<typeof createComposerView>;
|
|
36
|
+
part<T extends HTMLElement = HTMLElement>(name: string): T;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;AAGxE,MAAM,WAAW,iBAAiB;IAAG,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CAAE;AAClK,MAAM,WAAW,mBAAmB;IAAG,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAAE;AAS/O,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,mBAAwB;;;oBAqB3C,iBAAiB;;iBAyBzB,MAAM;;;;EAGtB;AAED,MAAM,WAAW,qBAAqB;IAAG,KAAK,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;CAAE;AACxE,MAAM,WAAW,mBAAmB;IAAG,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,OAAO,CAAC;CAAE;AAC3G,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,mBAAmB,CAAC,GAAG;IAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IAAC,IAAI,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAAA;CAAE,CAwEnN"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { Generation } from './core.js';
|
|
2
|
+
import { icon } from './dom.js';
|
|
3
|
+
let sequence = 0;
|
|
4
|
+
function identify(root, prefix) {
|
|
5
|
+
if (!prefix)
|
|
6
|
+
return;
|
|
7
|
+
const attributes = ['id', 'for', 'aria-labelledby', 'aria-describedby', 'aria-controls', 'data-close'];
|
|
8
|
+
for (const node of [root, ...root.querySelectorAll('*')])
|
|
9
|
+
for (const attribute of attributes) {
|
|
10
|
+
const value = node.getAttribute(attribute);
|
|
11
|
+
if (value)
|
|
12
|
+
node.setAttribute(attribute, value.split(' ').map(part => prefix + part).join(' '));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function createComposerView(options = {}) {
|
|
16
|
+
const holder = document.createElement('div');
|
|
17
|
+
holder.innerHTML = `<form id="composer-form" class="pui-composer">
|
|
18
|
+
<div id="draft-images" class="pui-draft-images" aria-label="Image attachments" hidden></div>
|
|
19
|
+
<input id="image-picker" type="file" accept="image/png,image/jpeg,image/gif,image/webp" multiple hidden />
|
|
20
|
+
<textarea id="composer-input" rows="1" placeholder="Ask Pith to help with your work…" aria-label="Message Pith"></textarea>
|
|
21
|
+
<div class="pui-composer-toolbar">
|
|
22
|
+
<div class="pui-composer-context">
|
|
23
|
+
<button id="attach-images" type="button" class="pui-quiet-icon pui-attach-images" data-action="attach-images" aria-label="Attach images" title="Attach images">${icon('image')}</button>
|
|
24
|
+
<label class="pui-permission-control" id="permission-control">${icon('shield')}<span class="pui-sr-only">Conversation permissions</span><select id="permission-mode" aria-describedby="permission-description"><option value="ask">Ask before changes</option><option value="workspace-write">Allow workspace changes</option><option value="full-access">Full access</option></select>${icon('down')}</label>
|
|
25
|
+
</div>
|
|
26
|
+
<div class="pui-composer-actions"><button id="composer-model" type="button" class="pui-composer-model" data-action="model" data-idle-action data-global-idle aria-label="Choose model">Choose model</button><label class="pui-composer-thinking"><span class="pui-sr-only">Thinking effort</span><select id="composer-thinking" aria-label="Thinking effort"></select></label><button id="send-button" class="pui-send-button" type="submit" aria-label="Send message">${icon('arrow')}</button></div>
|
|
27
|
+
</div>
|
|
28
|
+
</form>`;
|
|
29
|
+
const element = holder.firstElementChild;
|
|
30
|
+
const input = element.querySelector('textarea');
|
|
31
|
+
const action = element.querySelector('[type="submit"]');
|
|
32
|
+
identify(element, options.prefix ?? 'pui-composer-' + ++sequence + '-');
|
|
33
|
+
const generation = new Generation();
|
|
34
|
+
let pending = false, last = {}, lastAction = 'send';
|
|
35
|
+
const error = document.createElement('div');
|
|
36
|
+
error.className = 'pui-form-error';
|
|
37
|
+
error.setAttribute('role', 'alert');
|
|
38
|
+
error.hidden = true;
|
|
39
|
+
element.insertBefore(error, element.firstChild);
|
|
40
|
+
const resize = () => { input.style.height = 'auto'; input.style.height = Math.min(input.scrollHeight, 180) + 'px'; };
|
|
41
|
+
const update = (state) => {
|
|
42
|
+
last = state;
|
|
43
|
+
if (state.placeholder !== undefined)
|
|
44
|
+
input.placeholder = state.placeholder;
|
|
45
|
+
input.disabled = !!state.disabled;
|
|
46
|
+
input.readOnly = !!state.readOnly;
|
|
47
|
+
if (state.action && state.action !== lastAction) {
|
|
48
|
+
lastAction = state.action;
|
|
49
|
+
action.type = state.action === 'stop' ? 'button' : 'submit';
|
|
50
|
+
action.innerHTML = icon(state.action === 'stop' ? 'stop' : 'arrow');
|
|
51
|
+
action.classList.toggle('pui-is-stop', state.action === 'stop');
|
|
52
|
+
action.setAttribute('aria-label', state.action === 'stop' ? 'Stop agent' : state.action === 'queue' ? 'Queue message' : 'Send message');
|
|
53
|
+
}
|
|
54
|
+
action.disabled = !!(state.actionDisabled || state.disabled || pending);
|
|
55
|
+
};
|
|
56
|
+
const onInput = () => { resize(); options.onInput?.(input.value); };
|
|
57
|
+
const run = async (callback) => {
|
|
58
|
+
if (!callback || pending || action.disabled || input.disabled || input.readOnly)
|
|
59
|
+
return;
|
|
60
|
+
const seq = generation.next();
|
|
61
|
+
pending = true;
|
|
62
|
+
error.hidden = true;
|
|
63
|
+
error.textContent = '';
|
|
64
|
+
update(last);
|
|
65
|
+
try {
|
|
66
|
+
const result = await callback();
|
|
67
|
+
if (generation.current(seq) && result && !result.ok) {
|
|
68
|
+
error.textContent = result.error;
|
|
69
|
+
error.hidden = false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (reason) {
|
|
73
|
+
if (generation.current(seq)) {
|
|
74
|
+
error.textContent = reason instanceof Error ? reason.message : String(reason);
|
|
75
|
+
error.hidden = false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
if (generation.current(seq)) {
|
|
80
|
+
pending = false;
|
|
81
|
+
update(last);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const submit = (event) => { if (options.onSubmit) {
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
const text = input.value;
|
|
88
|
+
void run(() => options.onSubmit(text));
|
|
89
|
+
} };
|
|
90
|
+
const stop = () => { if (action.type === 'button')
|
|
91
|
+
void run(options.onStop); };
|
|
92
|
+
const keydown = (event) => {
|
|
93
|
+
if (options.onSubmit && event.key === 'Enter' && !event.shiftKey && !event.isComposing && action.type === 'submit') {
|
|
94
|
+
event.preventDefault();
|
|
95
|
+
element.requestSubmit();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
input.addEventListener('input', onInput);
|
|
99
|
+
input.addEventListener('keydown', keydown);
|
|
100
|
+
element.addEventListener('submit', submit);
|
|
101
|
+
action.addEventListener('click', stop);
|
|
102
|
+
return { element, input, update, getValue: () => input.value,
|
|
103
|
+
reset(value) { generation.invalidate(); pending = false; error.hidden = true; error.textContent = ''; input.value = value; update(last); resize(); }, focus: () => input.focus(), resize,
|
|
104
|
+
destroy() { generation.destroy(); input.removeEventListener('input', onInput); input.removeEventListener('keydown', keydown); element.removeEventListener('submit', submit); action.removeEventListener('click', stop); element.remove(); } };
|
|
105
|
+
}
|
|
106
|
+
export function createWorkspaceShell(options) {
|
|
107
|
+
const element = document.createElement('div');
|
|
108
|
+
element.className = 'pui-app';
|
|
109
|
+
element.innerHTML = `
|
|
110
|
+
<aside class="pui-sidebar" id="sidebar">
|
|
111
|
+
<div class="pui-brand"><span data-brand-slot></span></div>
|
|
112
|
+
<div class="pui-sidebar-section-label">WORKSPACES <button class="pui-quiet-icon" data-action="workspace" aria-label="Manage workspaces">${icon('plus')}</button></div>
|
|
113
|
+
<label class="pui-history-search">${icon('search')}<span class="pui-sr-only">Search conversation titles</span><input id="history-search" type="search" placeholder="Search conversations" autocomplete="off" /></label>
|
|
114
|
+
<nav id="history-list" class="pui-history-list" aria-label="Workspaces and conversations"></nav>
|
|
115
|
+
<div class="pui-sidebar-bottom">
|
|
116
|
+
<button class="pui-settings-button" data-action="resources">${icon('file')}<span>Workspace resources</span></button>
|
|
117
|
+
<button class="pui-settings-button" data-action="connections">${icon('plug')}<span>Connections</span></button>
|
|
118
|
+
<button class="pui-settings-button" data-action="settings">${icon('settings')}<span>Settings</span></button>
|
|
119
|
+
<div id="connection-status" class="pui-local-status" role="status" hidden><span class="pui-status-dot pui-reconnecting" aria-hidden="true"></span><span>Reconnecting…</span></div>
|
|
120
|
+
</div>
|
|
121
|
+
</aside>
|
|
122
|
+
<div id="conversation-menu" class="pui-sidebar-conversation-menu" role="menu" hidden></div>
|
|
123
|
+
<div class="pui-sidebar-scrim" id="sidebar-scrim"></div>
|
|
124
|
+
<main class="pui-main">
|
|
125
|
+
<header class="pui-topbar">
|
|
126
|
+
<button class="pui-quiet-icon pui-mobile-menu" data-action="menu" aria-label="Open sidebar">${icon('menu')}</button>
|
|
127
|
+
<div class="pui-breadcrumb">${icon('folder')}<span id="workspace-label">No workspace</span>${icon('chevron', 'pui-breadcrumb-chevron')}<span class="pui-breadcrumb-current" id="conversation-label">New conversation</span></div>
|
|
128
|
+
<div class="pui-conversation-actions"><button class="pui-quiet-icon pui-compact-action" data-sdk="compact" data-sdk-idle title="Compress context by summarizing older messages" aria-label="Compress context">${icon('compress')}<span>Compact</span></button><div id="artifacts-control" class="pui-artifacts-control" hidden><button id="artifacts-toggle" type="button" class="pui-quiet-icon pui-artifacts-toggle" aria-label="Conversation files" title="Conversation files" aria-expanded="false" aria-controls="artifacts">${icon('file')}<span id="artifacts-count" class="pui-artifacts-count" aria-hidden="true"></span></button><section id="artifacts" class="pui-artifacts-panel" aria-labelledby="artifacts-title" tabindex="-1" hidden></section></div></div>
|
|
129
|
+
</header>
|
|
130
|
+
<section id="conversation-find" class="pui-conversation-find" aria-label="Find in conversation" hidden>
|
|
131
|
+
<input type="search" aria-label="Find in this conversation" placeholder="Find in this conversation" autocomplete="off" />
|
|
132
|
+
<span data-find-count role="status" aria-live="polite"></span>
|
|
133
|
+
<button type="button" class="pui-quiet-icon pui-find-previous" data-find-previous aria-label="Previous match" title="Previous match (Shift+Enter)">${icon('down')}</button>
|
|
134
|
+
<button type="button" class="pui-quiet-icon" data-find-next aria-label="Next match" title="Next match (Enter)">${icon('down')}</button>
|
|
135
|
+
<button type="button" class="pui-quiet-icon" data-find-close aria-label="Close search" title="Close (Esc)">${icon('close')}</button>
|
|
136
|
+
</section>
|
|
137
|
+
<div class="pui-conversation-region"><section id="chat-scroll" class="pui-chat-scroll" aria-label="Conversation" tabindex="-1">
|
|
138
|
+
<div id="welcome" class="pui-welcome"></div>
|
|
139
|
+
<div id="messages" class="pui-messages" aria-live="polite" aria-relevant="additions text"></div>
|
|
140
|
+
</section><button id="back-to-latest" type="button" class="pui-secondary-button pui-back-to-latest" hidden>${icon('down')}<span>Back to latest</span></button></div>
|
|
141
|
+
<div class="pui-composer-region">
|
|
142
|
+
<div class="pui-composer-panels">
|
|
143
|
+
<div id="approval" class="pui-approval-region"></div>
|
|
144
|
+
<div id="workspace-busy" class="pui-workspace-busy" hidden></div><div id="inline-error" class="pui-inline-error" role="alert" hidden></div>
|
|
145
|
+
<section id="task-failure" class="pui-task-failure" role="status" hidden></section>
|
|
146
|
+
<details id="run-status" class="pui-run-status" hidden><summary id="run-status-summary"></summary><div id="run-status-details"><div id="runtime-values"></div><details id="cost-requests" class="pui-cost-requests" hidden><summary>Request breakdown</summary><div id="cost-ledger"></div></details></div></details>
|
|
147
|
+
<section id="queued-messages" class="pui-queued-messages" aria-label="Pending messages" aria-live="polite" hidden></section>
|
|
148
|
+
</div>
|
|
149
|
+
<div data-composer-slot></div>
|
|
150
|
+
<p id="image-guidance" class="pui-composer-note pui-image-guidance" hidden></p>
|
|
151
|
+
<p class="pui-composer-note" id="permission-description">Pith can read workspace files. Changes and commands require your approval.</p>
|
|
152
|
+
</div>
|
|
153
|
+
</main>
|
|
154
|
+
<section id="completion-notices" class="pui-completion-notices" aria-label="Completed tasks" aria-live="polite"></section>
|
|
155
|
+
<span id="copy-status" class="pui-sr-only" role="status" aria-live="polite"></span>
|
|
156
|
+
<dialog id="model-dialog" class="pui-modal pui-model-modal" aria-labelledby="model-title"><div id="model-content"></div></dialog>
|
|
157
|
+
<dialog id="settings-dialog" class="pui-modal" aria-labelledby="settings-title"><div id="settings-content"></div></dialog>
|
|
158
|
+
<dialog id="workspace-dialog" class="pui-modal" aria-labelledby="workspace-title"><div id="workspace-content"></div></dialog>
|
|
159
|
+
<dialog id="permissions-dialog" class="pui-modal pui-permission-modal" aria-labelledby="full-access-title"><div id="permissions-content"></div></dialog>
|
|
160
|
+
<dialog id="delete-dialog" class="pui-modal" aria-labelledby="delete-title"><div id="delete-content"></div></dialog>
|
|
161
|
+
<dialog id="rename-dialog" class="pui-modal" aria-labelledby="rename-title"><div id="rename-content"></div></dialog>
|
|
162
|
+
<dialog id="queue-edit-dialog" class="pui-modal" aria-labelledby="queue-edit-title"><div id="queue-edit-content"></div></dialog>
|
|
163
|
+
<dialog id="message-edit-dialog" class="pui-modal" aria-labelledby="message-edit-title"><div id="message-edit-content"></div></dialog>
|
|
164
|
+
<dialog id="resources-dialog" class="pui-modal pui-feature-modal" aria-labelledby="resources-title"><div id="resources-content"></div></dialog>
|
|
165
|
+
<dialog id="connections-dialog" class="pui-modal pui-feature-modal" aria-labelledby="connections-title"><div id="connections-content"></div></dialog>
|
|
166
|
+
<dialog id="preview-dialog" class="pui-modal pui-file-preview-modal" aria-labelledby="preview-title"><div id="preview-content"></div></dialog>
|
|
167
|
+
<dialog id="image-dialog"></dialog>
|
|
168
|
+
`;
|
|
169
|
+
element.querySelector('[data-brand-slot]').replaceWith(options.brand);
|
|
170
|
+
const prefix = options.prefix ?? 'pui-workspace-' + ++sequence + '-';
|
|
171
|
+
const composer = createComposerView({ prefix: '' });
|
|
172
|
+
element.querySelector('[data-composer-slot]').replaceWith(composer.element);
|
|
173
|
+
identify(element, prefix);
|
|
174
|
+
const part = (name) => element.querySelector('[id="' + prefix + name + '"]');
|
|
175
|
+
return { element, composer, part, update(state) {
|
|
176
|
+
if (state.workspace !== undefined)
|
|
177
|
+
part('workspace-label').textContent = state.workspace;
|
|
178
|
+
if (state.conversation !== undefined)
|
|
179
|
+
part('conversation-label').textContent = state.conversation;
|
|
180
|
+
part('connection-status').hidden = !state.reconnecting;
|
|
181
|
+
}, destroy() { composer.destroy(); element.remove(); } };
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=workspace.js.map
|