pith-ui 0.0.0-stage → 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.
- package/AI.md +75 -0
- package/API.md +3688 -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 +123 -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 +65 -0
- package/dist/content/conversation-view.d.ts.map +1 -0
- package/dist/content/conversation-view.js +140 -0
- package/dist/content/conversation-view.js.map +1 -0
- package/dist/content/image-viewer.d.ts +25 -0
- package/dist/content/image-viewer.d.ts.map +1 -0
- package/dist/content/image-viewer.js +114 -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 +108 -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 +54 -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 +100 -0
- package/dist/files/panels.d.ts.map +1 -0
- package/dist/files/panels.js +286 -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 +273 -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 +39 -0
- package/dist/forms/actions.js.map +1 -0
- package/dist/forms/custom-connection.d.ts +62 -0
- package/dist/forms/custom-connection.d.ts.map +1 -0
- package/dist/forms/custom-connection.js +72 -0
- package/dist/forms/custom-connection.js.map +1 -0
- package/dist/forms/dialog-frame.d.ts +41 -0
- package/dist/forms/dialog-frame.d.ts.map +1 -0
- package/dist/forms/dialog-frame.js +95 -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 +16 -0
- package/dist/forms/identity.d.ts.map +1 -0
- package/dist/forms/identity.js +53 -0
- package/dist/forms/identity.js.map +1 -0
- package/dist/forms/model-editor.d.ts +40 -0
- package/dist/forms/model-editor.d.ts.map +1 -0
- package/dist/forms/model-editor.js +89 -0
- package/dist/forms/model-editor.js.map +1 -0
- package/dist/forms/panels.d.ts +146 -0
- package/dist/forms/panels.d.ts.map +1 -0
- package/dist/forms/panels.js +339 -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 +116 -0
- package/dist/forms/provider-login.js.map +1 -0
- package/dist/forms/resource-editor.d.ts +27 -0
- package/dist/forms/resource-editor.d.ts.map +1 -0
- package/dist/forms/resource-editor.js +34 -0
- package/dist/forms/resource-editor.js.map +1 -0
- package/dist/foundations.d.ts +95 -0
- package/dist/foundations.d.ts.map +1 -0
- package/dist/foundations.js +315 -0
- package/dist/foundations.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 +26 -0
- package/dist/navigation/context-menu.d.ts.map +1 -0
- package/dist/navigation/context-menu.js +81 -0
- package/dist/navigation/context-menu.js.map +1 -0
- package/dist/navigation/navigation.d.ts +92 -0
- package/dist/navigation/navigation.d.ts.map +1 -0
- package/dist/navigation/navigation.js +368 -0
- package/dist/navigation/navigation.js.map +1 -0
- package/dist/primitives.d.ts +310 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +633 -0
- package/dist/primitives.js.map +1 -0
- package/dist/styles.css +853 -0
- package/dist/surface.d.ts +29 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +154 -0
- package/dist/surface.js.map +1 -0
- package/dist/tasks/approval.d.ts +25 -0
- package/dist/tasks/approval.d.ts.map +1 -0
- package/dist/tasks/approval.js +64 -0
- package/dist/tasks/approval.js.map +1 -0
- package/dist/tasks/permission.d.ts +37 -0
- package/dist/tasks/permission.d.ts.map +1 -0
- package/dist/tasks/permission.js +73 -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 +92 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +288 -0
- package/dist/workspace.js.map +1 -0
- package/package.json +27 -4
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface DialogLifecycleOptions {
|
|
2
|
+
closeOnBackdrop?: boolean;
|
|
3
|
+
canClose?: () => boolean;
|
|
4
|
+
onOpen?: () => void;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
returnFocus?: () => HTMLElement | null;
|
|
7
|
+
closeButtonSelector?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DialogLifecycle {
|
|
10
|
+
open(): void;
|
|
11
|
+
close(): void;
|
|
12
|
+
isOpen(): boolean;
|
|
13
|
+
destroy(): void;
|
|
14
|
+
}
|
|
15
|
+
/** One native lifecycle per dialog, including dialogs supplied by a host shell. */
|
|
16
|
+
export declare function installDialogLifecycle(dialog: HTMLDialogElement, options?: DialogLifecycleOptions): DialogLifecycle;
|
|
17
|
+
export interface MenuNavigationOptions {
|
|
18
|
+
trigger?: HTMLElement;
|
|
19
|
+
onDismiss: (restoreFocus: boolean) => void;
|
|
20
|
+
isOpen?: () => boolean;
|
|
21
|
+
closeOnOutside?: boolean;
|
|
22
|
+
contains?: (target: Node) => boolean;
|
|
23
|
+
}
|
|
24
|
+
/** Shared keyboard behavior; each menu retains its own presentation and actions. */
|
|
25
|
+
export declare function installMenuNavigation(menu: HTMLElement, options: MenuNavigationOptions): {
|
|
26
|
+
focus(edge?: 'first' | 'last'): void;
|
|
27
|
+
destroy(): void;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=surface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../src/surface.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,sBAAsB;IACrC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AACD,MAAM,WAAW,eAAe;IAC9B,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,OAAO,CAAC;IAClB,OAAO,IAAI,IAAI,CAAC;CACjB;AA2BD,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,sBAA2B,GAAG,eAAe,CA0EvH;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,OAAO,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,KAAK,OAAO,CAAC;CACtC;AAED,oFAAoF;AACpF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,qBAAqB,GAAG;IAAE,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAE,CAsBlJ"}
|
package/dist/surface.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Lifetime } from './core.js';
|
|
2
|
+
const dialogSessions = new WeakMap();
|
|
3
|
+
const activations = new WeakMap();
|
|
4
|
+
function retainActivation(document) {
|
|
5
|
+
let activation = activations.get(document);
|
|
6
|
+
if (!activation) {
|
|
7
|
+
const current = { owners: 0, target: null, listener: () => { } };
|
|
8
|
+
current.listener = event => {
|
|
9
|
+
current.target = event.target instanceof Element ? event.target.closest('button, a[href], input, textarea, select, summary, [tabindex]') : null;
|
|
10
|
+
if (current.timer !== undefined)
|
|
11
|
+
clearTimeout(current.timer);
|
|
12
|
+
current.timer = setTimeout(() => { current.target = null; current.timer = undefined; }, 0);
|
|
13
|
+
};
|
|
14
|
+
document.addEventListener('click', current.listener, true);
|
|
15
|
+
activations.set(document, current);
|
|
16
|
+
activation = current;
|
|
17
|
+
}
|
|
18
|
+
activation.owners++;
|
|
19
|
+
const current = activation;
|
|
20
|
+
return { destroy() { if (--current.owners === 0) {
|
|
21
|
+
document.removeEventListener('click', current.listener, true);
|
|
22
|
+
if (current.timer !== undefined)
|
|
23
|
+
clearTimeout(current.timer);
|
|
24
|
+
activations.delete(document);
|
|
25
|
+
} } };
|
|
26
|
+
}
|
|
27
|
+
/** One native lifecycle per dialog, including dialogs supplied by a host shell. */
|
|
28
|
+
export function installDialogLifecycle(dialog, options = {}) {
|
|
29
|
+
const subscription = { ...options };
|
|
30
|
+
let session = dialogSessions.get(dialog);
|
|
31
|
+
if (!session) {
|
|
32
|
+
session = { listeners: new Lifetime(), subscribers: new Set(), active: dialog.open, previousFocus: null };
|
|
33
|
+
dialogSessions.set(dialog, session);
|
|
34
|
+
const current = session;
|
|
35
|
+
const activation = retainActivation(dialog.ownerDocument);
|
|
36
|
+
current.listeners.add(activation.destroy);
|
|
37
|
+
const canClose = () => [...current.subscribers].every(subscriber => subscriber.canClose?.() !== false);
|
|
38
|
+
current.listeners.listen(dialog, 'cancel', (event => { if (!canClose())
|
|
39
|
+
event.preventDefault(); }));
|
|
40
|
+
current.listeners.listen(dialog, 'focusin', (() => { if (dialog.open)
|
|
41
|
+
current.active = true; }));
|
|
42
|
+
current.listeners.listen(dialog, 'click', (event => {
|
|
43
|
+
if (!dialog.open || !canClose())
|
|
44
|
+
return;
|
|
45
|
+
const target = event.target;
|
|
46
|
+
if (target instanceof Element) {
|
|
47
|
+
for (const subscriber of current.subscribers) {
|
|
48
|
+
const close = target.closest(subscriber.closeButtonSelector ?? '[data-close]');
|
|
49
|
+
if (close && dialog.contains(close) && (!close.dataset.close || close.dataset.close === dialog.id)) {
|
|
50
|
+
dialog.close();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (target !== dialog || ![...current.subscribers].every(subscriber => subscriber.closeOnBackdrop !== false))
|
|
56
|
+
return;
|
|
57
|
+
const pointer = event;
|
|
58
|
+
const rect = dialog.getBoundingClientRect();
|
|
59
|
+
if (pointer.clientX < rect.left || pointer.clientX > rect.right || pointer.clientY < rect.top || pointer.clientY > rect.bottom)
|
|
60
|
+
dialog.close();
|
|
61
|
+
}));
|
|
62
|
+
current.listeners.listen(dialog, 'close', (() => {
|
|
63
|
+
// A native close event may arrive after the same element has reopened.
|
|
64
|
+
if (dialog.open || !current.active)
|
|
65
|
+
return;
|
|
66
|
+
current.active = false;
|
|
67
|
+
const returnFocus = [...current.subscribers].reverse().find(subscriber => subscriber.returnFocus)?.returnFocus;
|
|
68
|
+
const previous = returnFocus ? returnFocus() : current.previousFocus;
|
|
69
|
+
current.previousFocus = null;
|
|
70
|
+
const beforeCallbacks = dialog.ownerDocument.activeElement;
|
|
71
|
+
for (const subscriber of [...current.subscribers])
|
|
72
|
+
subscriber.onClose?.();
|
|
73
|
+
const afterCallbacks = dialog.ownerDocument.activeElement;
|
|
74
|
+
const deliberatelyFocused = afterCallbacks !== beforeCallbacks && afterCallbacks instanceof HTMLElement && afterCallbacks !== dialog.ownerDocument.body && !dialog.contains(afterCallbacks);
|
|
75
|
+
if (!dialog.open && !deliberatelyFocused && previous?.isConnected)
|
|
76
|
+
previous.focus({ preventScroll: true });
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
const current = session;
|
|
80
|
+
current.subscribers.add(subscription);
|
|
81
|
+
let destroyed = false;
|
|
82
|
+
return {
|
|
83
|
+
open() {
|
|
84
|
+
if (destroyed || dialog.open)
|
|
85
|
+
return;
|
|
86
|
+
// Safari does not focus pointer-clicked buttons. Remember this event's
|
|
87
|
+
// actual opener instead of relying solely on activeElement.
|
|
88
|
+
const active = activations.get(dialog.ownerDocument)?.target ?? dialog.ownerDocument.activeElement;
|
|
89
|
+
current.previousFocus = active instanceof HTMLElement && !dialog.contains(active) ? active : null;
|
|
90
|
+
current.active = true;
|
|
91
|
+
if (!dialog.isConnected)
|
|
92
|
+
document.body.append(dialog);
|
|
93
|
+
dialog.showModal();
|
|
94
|
+
for (const subscriber of [...current.subscribers])
|
|
95
|
+
subscriber.onOpen?.();
|
|
96
|
+
},
|
|
97
|
+
close() { if (!destroyed && dialog.open)
|
|
98
|
+
dialog.close(); },
|
|
99
|
+
isOpen: () => !destroyed && dialog.open,
|
|
100
|
+
destroy() {
|
|
101
|
+
if (destroyed)
|
|
102
|
+
return;
|
|
103
|
+
destroyed = true;
|
|
104
|
+
const returnFocus = [...current.subscribers].reverse().find(subscriber => subscriber.returnFocus)?.returnFocus;
|
|
105
|
+
const previous = returnFocus ? returnFocus() : current.previousFocus;
|
|
106
|
+
current.subscribers.delete(subscription);
|
|
107
|
+
if (!current.subscribers.size) {
|
|
108
|
+
current.listeners.destroy();
|
|
109
|
+
if (!dialog.open && previous?.isConnected)
|
|
110
|
+
previous.focus({ preventScroll: true });
|
|
111
|
+
current.previousFocus = null;
|
|
112
|
+
dialogSessions.delete(dialog);
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** Shared keyboard behavior; each menu retains its own presentation and actions. */
|
|
118
|
+
export function installMenuNavigation(menu, options) {
|
|
119
|
+
const life = new Lifetime();
|
|
120
|
+
const open = () => options.isOpen?.() ?? !menu.hidden;
|
|
121
|
+
const targets = () => [...menu.querySelectorAll('[role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], button')]
|
|
122
|
+
.filter(target => !(target instanceof HTMLButtonElement && target.disabled) && target.getAttribute('aria-disabled') !== 'true' && !target.closest('[hidden]'));
|
|
123
|
+
life.listen(menu, 'keydown', (event => {
|
|
124
|
+
if (!open())
|
|
125
|
+
return;
|
|
126
|
+
const keyboard = event;
|
|
127
|
+
if (keyboard.key === 'Escape') {
|
|
128
|
+
keyboard.preventDefault();
|
|
129
|
+
keyboard.stopPropagation();
|
|
130
|
+
options.onDismiss(true);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (keyboard.key === 'Tab') {
|
|
134
|
+
options.onDismiss(false);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(keyboard.key))
|
|
138
|
+
return;
|
|
139
|
+
keyboard.preventDefault();
|
|
140
|
+
keyboard.stopPropagation();
|
|
141
|
+
const items = targets();
|
|
142
|
+
const index = items.indexOf(document.activeElement);
|
|
143
|
+
const next = keyboard.key === 'Home' ? 0 : keyboard.key === 'End' ? items.length - 1 : index < 0 ? (keyboard.key === 'ArrowUp' ? items.length - 1 : 0) : (index + (keyboard.key === 'ArrowUp' ? -1 : 1) + items.length) % items.length;
|
|
144
|
+
items[next]?.focus();
|
|
145
|
+
}));
|
|
146
|
+
if (options.closeOnOutside)
|
|
147
|
+
life.listen(document, 'pointerdown', (event => {
|
|
148
|
+
const target = event.target;
|
|
149
|
+
if (open() && target instanceof Node && !menu.contains(target) && !options.trigger?.contains(target) && !options.contains?.(target))
|
|
150
|
+
options.onDismiss(false);
|
|
151
|
+
}));
|
|
152
|
+
return { focus(edge = 'first') { const items = targets(); (edge === 'last' ? items.at(-1) : items[0])?.focus(); }, destroy() { life.destroy(); } };
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=surface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surface.js","sourceRoot":"","sources":["../src/surface.ts"],"names":[],"mappings":"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","sourcesContent":["import { Lifetime } from './core.js';\n\nexport interface DialogLifecycleOptions {\n closeOnBackdrop?: boolean;\n canClose?: () => boolean;\n onOpen?: () => void;\n onClose?: () => void;\n returnFocus?: () => HTMLElement | null;\n closeButtonSelector?: string;\n}\nexport interface DialogLifecycle {\n open(): void;\n close(): void;\n isOpen(): boolean;\n destroy(): void;\n}\ntype DialogSession = {\n listeners: Lifetime;\n subscribers: Set<DialogLifecycleOptions>;\n active: boolean;\n previousFocus: HTMLElement | null;\n};\nconst dialogSessions = new WeakMap<HTMLDialogElement, DialogSession>();\ntype Activation = {owners: number; target: HTMLElement | null; listener: EventListener; timer?: ReturnType<typeof setTimeout>};\nconst activations = new WeakMap<Document, Activation>();\nfunction retainActivation(document: Document): {destroy(): void} {\n let activation = activations.get(document);\n if (!activation) {\n const current: Activation = {owners: 0, target: null, listener: () => {}};\n current.listener = event => {\n current.target = event.target instanceof Element ? event.target.closest<HTMLElement>('button, a[href], input, textarea, select, summary, [tabindex]') : null;\n if (current.timer !== undefined) clearTimeout(current.timer);\n current.timer = setTimeout(() => {current.target = null; current.timer = undefined;}, 0);\n };\n document.addEventListener('click', current.listener, true);\n activations.set(document, current); activation = current;\n }\n activation.owners++;\n const current = activation;\n return {destroy() {if (--current.owners === 0) {document.removeEventListener('click', current.listener, true); if (current.timer !== undefined) clearTimeout(current.timer); activations.delete(document);}}};\n}\n\n/** One native lifecycle per dialog, including dialogs supplied by a host shell. */\nexport function installDialogLifecycle(dialog: HTMLDialogElement, options: DialogLifecycleOptions = {}): DialogLifecycle {\n const subscription = {...options};\n let session = dialogSessions.get(dialog);\n if (!session) {\n session = { listeners: new Lifetime(), subscribers: new Set(), active: dialog.open, previousFocus: null };\n dialogSessions.set(dialog, session);\n const current = session;\n const activation = retainActivation(dialog.ownerDocument);\n current.listeners.add(activation.destroy);\n const canClose = () => [...current.subscribers].every(subscriber => subscriber.canClose?.() !== false);\n current.listeners.listen(dialog, 'cancel', (event => { if (!canClose()) event.preventDefault(); }) as EventListener);\n current.listeners.listen(dialog, 'focusin', (() => { if (dialog.open) current.active = true; }) as EventListener);\n current.listeners.listen(dialog, 'click', (event => {\n if (!dialog.open || !canClose()) return;\n const target = event.target;\n if (target instanceof Element) {\n for (const subscriber of current.subscribers) {\n const close = target.closest<HTMLElement>(subscriber.closeButtonSelector ?? '[data-close]');\n if (close && dialog.contains(close) && (!close.dataset.close || close.dataset.close === dialog.id)) {\n dialog.close();\n return;\n }\n }\n }\n if (target !== dialog || ![...current.subscribers].every(subscriber => subscriber.closeOnBackdrop !== false)) return;\n const pointer = event as MouseEvent;\n const rect = dialog.getBoundingClientRect();\n if (pointer.clientX < rect.left || pointer.clientX > rect.right || pointer.clientY < rect.top || pointer.clientY > rect.bottom) dialog.close();\n }) as EventListener);\n current.listeners.listen(dialog, 'close', (() => {\n // A native close event may arrive after the same element has reopened.\n if (dialog.open || !current.active) return;\n current.active = false;\n const returnFocus = [...current.subscribers].reverse().find(subscriber => subscriber.returnFocus)?.returnFocus;\n const previous = returnFocus ? returnFocus() : current.previousFocus;\n current.previousFocus = null;\n const beforeCallbacks = dialog.ownerDocument.activeElement;\n for (const subscriber of [...current.subscribers]) subscriber.onClose?.();\n const afterCallbacks = dialog.ownerDocument.activeElement;\n const deliberatelyFocused = afterCallbacks !== beforeCallbacks && afterCallbacks instanceof HTMLElement && afterCallbacks !== dialog.ownerDocument.body && !dialog.contains(afterCallbacks);\n if (!dialog.open && !deliberatelyFocused && previous?.isConnected) previous.focus({ preventScroll: true });\n }) as EventListener);\n }\n const current = session;\n current.subscribers.add(subscription);\n let destroyed = false;\n return {\n open() {\n if (destroyed || dialog.open) return;\n // Safari does not focus pointer-clicked buttons. Remember this event's\n // actual opener instead of relying solely on activeElement.\n const active = activations.get(dialog.ownerDocument)?.target ?? dialog.ownerDocument.activeElement;\n current.previousFocus = active instanceof HTMLElement && !dialog.contains(active) ? active : null;\n current.active = true;\n if (!dialog.isConnected) document.body.append(dialog);\n dialog.showModal();\n for (const subscriber of [...current.subscribers]) subscriber.onOpen?.();\n },\n close() { if (!destroyed && dialog.open) dialog.close(); },\n isOpen: () => !destroyed && dialog.open,\n destroy() {\n if (destroyed) return;\n destroyed = true;\n const returnFocus = [...current.subscribers].reverse().find(subscriber => subscriber.returnFocus)?.returnFocus;\n const previous = returnFocus ? returnFocus() : current.previousFocus;\n current.subscribers.delete(subscription);\n if (!current.subscribers.size) {\n current.listeners.destroy();\n if (!dialog.open && previous?.isConnected) previous.focus({ preventScroll: true });\n current.previousFocus = null;\n dialogSessions.delete(dialog);\n }\n },\n };\n}\n\nexport interface MenuNavigationOptions {\n trigger?: HTMLElement;\n onDismiss: (restoreFocus: boolean) => void;\n isOpen?: () => boolean;\n closeOnOutside?: boolean;\n contains?: (target: Node) => boolean;\n}\n\n/** Shared keyboard behavior; each menu retains its own presentation and actions. */\nexport function installMenuNavigation(menu: HTMLElement, options: MenuNavigationOptions): { focus(edge?: 'first' | 'last'): void; destroy(): void } {\n const life = new Lifetime();\n const open = () => options.isOpen?.() ?? !menu.hidden;\n const targets = () => [...menu.querySelectorAll<HTMLElement>('[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"], button')]\n .filter(target => !(target instanceof HTMLButtonElement && target.disabled) && target.getAttribute('aria-disabled') !== 'true' && !target.closest('[hidden]'));\n life.listen(menu, 'keydown', (event => {\n if (!open()) return;\n const keyboard = event as KeyboardEvent;\n if (keyboard.key === 'Escape') { keyboard.preventDefault(); keyboard.stopPropagation(); options.onDismiss(true); return; }\n if (keyboard.key === 'Tab') { options.onDismiss(false); return; }\n if (!['ArrowDown', 'ArrowUp', 'Home', 'End'].includes(keyboard.key)) return;\n keyboard.preventDefault(); keyboard.stopPropagation();\n const items = targets();\n const index = items.indexOf(document.activeElement as HTMLElement);\n const next = keyboard.key === 'Home' ? 0 : keyboard.key === 'End' ? items.length - 1 : index < 0 ? (keyboard.key === 'ArrowUp' ? items.length - 1 : 0) : (index + (keyboard.key === 'ArrowUp' ? -1 : 1) + items.length) % items.length;\n items[next]?.focus();\n }) as EventListener);\n if (options.closeOnOutside) life.listen(document, 'pointerdown', (event => {\n const target = event.target;\n if (open() && target instanceof Node && !menu.contains(target) && !options.trigger?.contains(target) && !options.contains?.(target)) options.onDismiss(false);\n }) as EventListener);\n return { focus(edge = 'first') { const items = targets(); (edge === 'last' ? items.at(-1) : items[0])?.focus(); }, destroy() { life.destroy(); } };\n}\n"]}
|
|
@@ -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;AAI1C,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,CAwB5N"}
|
|
@@ -0,0 +1,64 @@
|
|
|
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
|
+
import { adoptProductParts } from '../foundations.js';
|
|
5
|
+
export function createApprovalPanel(initial, options = {}) {
|
|
6
|
+
const element = options.element || document.createElement('div');
|
|
7
|
+
if (!options.element)
|
|
8
|
+
element.className = 'pui-approval-panel';
|
|
9
|
+
const prefix = options.idPrefix ?? uniqueId('pui-approval') + '-';
|
|
10
|
+
const parts = adoptProductParts(element);
|
|
11
|
+
const $ = (id) => localField(element, prefix + id);
|
|
12
|
+
const abort = new AbortController();
|
|
13
|
+
let contentAbort = new AbortController();
|
|
14
|
+
let signature = '';
|
|
15
|
+
let destroyed = false;
|
|
16
|
+
let previews = [];
|
|
17
|
+
const update = (state) => {
|
|
18
|
+
if (destroyed)
|
|
19
|
+
return;
|
|
20
|
+
const approval = state.approval;
|
|
21
|
+
const next = JSON.stringify(approval);
|
|
22
|
+
if (next !== signature) {
|
|
23
|
+
signature = next;
|
|
24
|
+
contentAbort.abort();
|
|
25
|
+
contentAbort = new AbortController();
|
|
26
|
+
for (const preview of previews)
|
|
27
|
+
preview.destroy();
|
|
28
|
+
previews = [];
|
|
29
|
+
const warning = approval?.warning || '';
|
|
30
|
+
const canAlwaysAllow = !!approval?.canAlwaysAllow;
|
|
31
|
+
const preview = approval?.preview;
|
|
32
|
+
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>' : ''}` : '';
|
|
33
|
+
const previewMarkup = preview ? `<div class="pui-approval-file"><strong>${preview.kind === 'create' ? 'Create file' : 'Modify file'}</strong><code>${escape(preview.path)}</code></div>${previewBody}` : '';
|
|
34
|
+
const args = `<div id="approval-args-preview"></div>`;
|
|
35
|
+
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>` : '';
|
|
36
|
+
for (const child of element.children)
|
|
37
|
+
scopeIds(child, prefix);
|
|
38
|
+
for (const button of element.querySelectorAll('[data-approval]'))
|
|
39
|
+
button.dataset.approvalId = approval?.id || '';
|
|
40
|
+
if (preview && !preview.error)
|
|
41
|
+
previews.push(createPagedPreview({ container: $('approval-diff-preview'), text: preview.diff || 'No content changes.', kind: 'diff' }));
|
|
42
|
+
if (approval) {
|
|
43
|
+
const argsContainer = $('approval-args-preview');
|
|
44
|
+
const details = argsContainer.closest('details');
|
|
45
|
+
let loaded = false;
|
|
46
|
+
const showArgs = () => { if (destroyed || loaded || (details && !details.open))
|
|
47
|
+
return; loaded = true; previews.push(createPagedPreview({ container: argsContainer, text: approval.argsText, kind: 'text' })); };
|
|
48
|
+
details?.addEventListener('toggle', showArgs, { signal: contentAbort.signal });
|
|
49
|
+
showArgs();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
for (const button of element.querySelectorAll('[data-approval]'))
|
|
53
|
+
parts.action(button).update({ disabled: !!state.disabled });
|
|
54
|
+
parts.update();
|
|
55
|
+
};
|
|
56
|
+
element.addEventListener('click', (event) => { const button = event.target.closest('[data-approval]'); if (button && !button.disabled)
|
|
57
|
+
options.onAction?.(button.dataset.approvalId, button.dataset.approval); }, { signal: abort.signal });
|
|
58
|
+
update(initial);
|
|
59
|
+
return { element, update, destroy() { if (destroyed)
|
|
60
|
+
return; destroyed = true; parts.destroy(); abort.abort(); contentAbort.abort(); for (const preview of previews)
|
|
61
|
+
preview.destroy(); if (!options.element)
|
|
62
|
+
element.remove(); } };
|
|
63
|
+
}
|
|
64
|
+
//# 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';\nimport {adoptProductParts} from '../foundations.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 parts = adoptProductParts(element);\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]')) parts.action(button).update({disabled: !!state.disabled});\n parts.update();\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; parts.destroy(); abort.abort(); contentAbort.abort(); for (const preview of previews) preview.destroy(); if (!options.element) element.remove();}};\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ActionResult, UIBlock } from '../core.js';
|
|
2
|
+
export interface FullAccessState {
|
|
3
|
+
title?: string;
|
|
4
|
+
error?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface FullAccessOptions {
|
|
8
|
+
idPrefix?: string;
|
|
9
|
+
onConfirm?: () => ActionResult | Promise<ActionResult>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createFullAccessConfirmation(initial: FullAccessState, options?: FullAccessOptions): UIBlock<FullAccessState> & {
|
|
12
|
+
readonly confirmButton: HTMLButtonElement;
|
|
13
|
+
};
|
|
14
|
+
export interface TaskFailureState {
|
|
15
|
+
visible: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
advice: string;
|
|
18
|
+
canContinue: boolean;
|
|
19
|
+
workspaceId?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function createTaskFailure(initial: TaskFailureState, options?: {
|
|
22
|
+
element?: HTMLElement;
|
|
23
|
+
onContinue?: () => void;
|
|
24
|
+
onSettings?: () => void;
|
|
25
|
+
onDiagnostics?: () => void;
|
|
26
|
+
}): UIBlock<TaskFailureState>;
|
|
27
|
+
export interface PermissionControlState {
|
|
28
|
+
mode: string;
|
|
29
|
+
description: string;
|
|
30
|
+
disabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare function createPermissionControl(initial: PermissionControlState, options?: {
|
|
33
|
+
element?: HTMLElement;
|
|
34
|
+
descriptionElement?: HTMLElement;
|
|
35
|
+
onChange?: (value: string) => void;
|
|
36
|
+
}): UIBlock<PermissionControlState>;
|
|
37
|
+
//# 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,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAGxD,MAAM,WAAW,eAAe;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AACrF,MAAM,WAAW,iBAAiB;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAAC;AAC9G,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC,GAAG;IAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAA;CAAC,CAQ9K;AACD,MAAM,WAAW,gBAAgB;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAC;AACjI,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,gBAAgB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAK3M;AACD,MAAM,WAAW,sBAAsB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAC;AAC/F,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,EAAE,OAAO,GAAE;IAAC,OAAO,CAAC,EAAE,WAAW,CAAC;IAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAQrN"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { scopeIds } from '../forms/identity.js';
|
|
2
|
+
import { escape, icon, uniqueId } from '../dom.js';
|
|
3
|
+
import { adoptAction, adoptFeedback, adoptProductParts } from '../foundations.js';
|
|
4
|
+
export function createFullAccessConfirmation(initial, options = {}) {
|
|
5
|
+
const state = initial;
|
|
6
|
+
const element = document.createElement('div');
|
|
7
|
+
element.className = 'pui-full-access-confirmation';
|
|
8
|
+
element.innerHTML = `<div class="pui-modal-heading"><div><span class="pui-eyebrow">CONVERSATION PERMISSIONS</span><h2 id="full-access-title">Enable full access?</h2></div><button class="pui-quiet-icon" data-close="permissions-dialog" aria-label="Cancel full access">${icon('close')}</button></div><p class="pui-modal-description">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong>${escape(state.title || 'this conversation')}</strong>.</p><div class="pui-full-access-explanation">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class="pui-permission-revoke-note">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id="permissions-error" class="pui-form-error" role="alert"></div><div class="pui-modal-footer"><button class="pui-secondary-button" type="button" data-close="permissions-dialog">Cancel</button><button class="pui-primary-button pui-full-access-confirm" id="enable-full-access" type="button">Enable full access</button></div>`;
|
|
9
|
+
let destroyed = false;
|
|
10
|
+
const confirmButton = element.querySelector('#enable-full-access');
|
|
11
|
+
const error = element.querySelector('#permissions-error');
|
|
12
|
+
let current = initial;
|
|
13
|
+
const feedback = adoptFeedback(error, { presentation: 'inline', autoHide: false });
|
|
14
|
+
const action = adoptAction(confirmButton, { onAction: options.onConfirm ? () => { update({ ...current, error: '' }); return options.onConfirm(); } : undefined, onError: message => { update({ ...current, error: message }); } });
|
|
15
|
+
const update = (state) => { if (destroyed)
|
|
16
|
+
return; current = state; action.update({ disabled: !!state.disabled }); if (state.error !== undefined)
|
|
17
|
+
feedback.update({ message: state.error, tone: 'error' }); };
|
|
18
|
+
update(initial);
|
|
19
|
+
scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');
|
|
20
|
+
return { element, confirmButton, update, destroy() { if (destroyed)
|
|
21
|
+
return; destroyed = true; action.destroy(); feedback.destroy(); element.remove(); } };
|
|
22
|
+
}
|
|
23
|
+
export function createTaskFailure(initial, options = {}) {
|
|
24
|
+
const element = options.element || document.createElement('section');
|
|
25
|
+
if (!options.element)
|
|
26
|
+
element.className = 'pui-task-failure';
|
|
27
|
+
const abort = new AbortController();
|
|
28
|
+
let signature = '';
|
|
29
|
+
let destroyed = false;
|
|
30
|
+
const update = (state) => { if (destroyed)
|
|
31
|
+
return; const next = JSON.stringify(state); if (next === signature)
|
|
32
|
+
return; signature = next; element.hidden = !state.visible; element.innerHTML = state.visible ? `<strong>${escape(state.message)}</strong><p>${escape(state.advice)}</p><div>${state.canContinue ? `<button class="pui-secondary-button" type="button" data-action="continue" data-idle-action data-idle-workspace="${escape(state.workspaceId)}">Review and continue</button>` : '<button class="pui-secondary-button" type="button" data-action="settings">Check model settings</button>'}<button class="pui-secondary-button" type="button" data-action="diagnostics">Save diagnostics</button></div>` : ''; };
|
|
33
|
+
element.addEventListener('click', (event) => { const action = event.target.closest('[data-action]')?.dataset.action; if (action === 'continue')
|
|
34
|
+
options.onContinue?.(); if (action === 'settings')
|
|
35
|
+
options.onSettings?.(); if (action === 'diagnostics')
|
|
36
|
+
options.onDiagnostics?.(); }, { signal: abort.signal });
|
|
37
|
+
update(initial);
|
|
38
|
+
return { element, update, destroy() { if (destroyed)
|
|
39
|
+
return; destroyed = true; abort.abort(); if (!options.element)
|
|
40
|
+
element.remove(); } };
|
|
41
|
+
}
|
|
42
|
+
export function createPermissionControl(initial, options = {}) {
|
|
43
|
+
const element = options.element || document.createElement('label');
|
|
44
|
+
if (!options.element) {
|
|
45
|
+
element.className = 'pui-permission-control';
|
|
46
|
+
element.innerHTML = `${icon('shield')}<span class="pui-sr-only">Conversation permissions</span><select aria-describedby="pui-permission-description"><option value="ask">Ask before changes</option><option value="workspace-write">Allow workspace changes</option><option value="full-access">Full access</option></select>${icon('down')}`;
|
|
47
|
+
}
|
|
48
|
+
const selector = element.querySelector('select');
|
|
49
|
+
const abort = new AbortController();
|
|
50
|
+
let destroyed = false;
|
|
51
|
+
if (options.descriptionElement) {
|
|
52
|
+
if (!options.descriptionElement.id)
|
|
53
|
+
options.descriptionElement.id = uniqueId('pui-permission-description');
|
|
54
|
+
selector.setAttribute('aria-describedby', options.descriptionElement.id);
|
|
55
|
+
}
|
|
56
|
+
else
|
|
57
|
+
selector.removeAttribute('aria-describedby');
|
|
58
|
+
const parts = adoptProductParts(element);
|
|
59
|
+
const control = parts.control(selector);
|
|
60
|
+
const update = (state) => { if (destroyed)
|
|
61
|
+
return; control.reset(state.mode); control.update({ disabled: !!state.disabled }); element.classList.toggle('pui-full-access', state.mode === 'full-access'); if (options.descriptionElement) {
|
|
62
|
+
options.descriptionElement.textContent = state.description;
|
|
63
|
+
options.descriptionElement.classList.toggle('pui-full-access-note', state.mode === 'full-access');
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
selector.title = state.description; };
|
|
67
|
+
selector.addEventListener('change', () => options.onChange?.(selector.value), { signal: abort.signal });
|
|
68
|
+
update(initial);
|
|
69
|
+
return { element, update, destroy() { if (destroyed)
|
|
70
|
+
return; destroyed = true; parts.destroy(); abort.abort(); if (!options.element)
|
|
71
|
+
element.remove(); } };
|
|
72
|
+
}
|
|
73
|
+
//# 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 { ActionResult, UIBlock } from '../core.js';\nimport { escape, icon, uniqueId } from '../dom.js';\nimport {adoptAction,adoptFeedback,adoptProductParts} from '../foundations.js';\nexport interface FullAccessState {title?: string; error?: string; disabled?: boolean}\nexport interface FullAccessOptions {idPrefix?: string; onConfirm?: () => ActionResult | Promise<ActionResult>}\nexport function createFullAccessConfirmation(initial: FullAccessState, options: FullAccessOptions = {}): UIBlock<FullAccessState> & {readonly confirmButton: HTMLButtonElement} {\n const state = initial; const element = document.createElement('div'); element.className = 'pui-full-access-confirmation'; element.innerHTML = `<div class=\"pui-modal-heading\"><div><span class=\"pui-eyebrow\">CONVERSATION PERMISSIONS</span><h2 id=\"full-access-title\">Enable full access?</h2></div><button class=\"pui-quiet-icon\" data-close=\"permissions-dialog\" aria-label=\"Cancel full access\">${icon('close')}</button></div><p class=\"pui-modal-description\">Give Pith permission to run <strong>all enabled tools, including external MCP connections,</strong> without asking in <strong>${escape(state.title || 'this conversation')}</strong>.</p><div class=\"pui-full-access-explanation\">${icon('shield')}<div><strong>This is access to your computer account and connected services.</strong><p>Commands can read, change, or delete files outside the workspace and access the network. External tools can act on their connected services. There is no OS sandbox.</p></div></div><p class=\"pui-permission-revoke-note\">This applies only to the current conversation and stays enabled across app restarts until you revoke it. To revoke it, choose “Ask before changes” or “Allow workspace changes” in the composer. Workspace permission alone never grants external MCP tools access.</p><div id=\"permissions-error\" class=\"pui-form-error\" role=\"alert\"></div><div class=\"pui-modal-footer\"><button class=\"pui-secondary-button\" type=\"button\" data-close=\"permissions-dialog\">Cancel</button><button class=\"pui-primary-button pui-full-access-confirm\" id=\"enable-full-access\" type=\"button\">Enable full access</button></div>`;\n let destroyed = false; const confirmButton = element.querySelector<HTMLButtonElement>('#enable-full-access')!; const error = element.querySelector<HTMLElement>('#permissions-error')!;\n let current = initial;\n const feedback = adoptFeedback(error,{presentation:'inline',autoHide:false});\n const action = adoptAction(confirmButton,{onAction:options.onConfirm ? () => {update({...current,error:''}); return options.onConfirm!();} : undefined,onError:message => {update({...current,error:message});}});\n const update = (state: Readonly<FullAccessState>) => {if (destroyed) return; current = state; action.update({disabled: !!state.disabled}); if (state.error !== undefined) feedback.update({message:state.error,tone:'error'});}; update(initial); scopeIds(element, options.idPrefix ?? uniqueId('pui-full-access') + '-');\n return {element, confirmButton, update, destroy() {if (destroyed) return; destroyed = true; action.destroy(); feedback.destroy(); element.remove();}};\n}\nexport interface TaskFailureState {visible: boolean; message: string; advice: string; canContinue: boolean; workspaceId?: string}\nexport function createTaskFailure(initial: TaskFailureState, options: {element?: HTMLElement; onContinue?: () => void; onSettings?: () => void; onDiagnostics?: () => void} = {}): UIBlock<TaskFailureState> {\n const element = options.element || document.createElement('section'); if (!options.element) element.className = 'pui-task-failure'; const abort = new AbortController(); let signature = ''; let destroyed = false;\n const update = (state: Readonly<TaskFailureState>) => {if (destroyed) return; const next = JSON.stringify(state); if (next === signature) return; signature = next; element.hidden = !state.visible; element.innerHTML = state.visible ? `<strong>${escape(state.message)}</strong><p>${escape(state.advice)}</p><div>${state.canContinue ? `<button class=\"pui-secondary-button\" type=\"button\" data-action=\"continue\" data-idle-action data-idle-workspace=\"${escape(state.workspaceId)}\">Review and continue</button>` : '<button class=\"pui-secondary-button\" type=\"button\" data-action=\"settings\">Check model settings</button>'}<button class=\"pui-secondary-button\" type=\"button\" data-action=\"diagnostics\">Save diagnostics</button></div>` : '';};\n element.addEventListener('click', (event) => {const action = (event.target as HTMLElement).closest<HTMLElement>('[data-action]')?.dataset.action; if (action === 'continue') options.onContinue?.(); if (action === 'settings') options.onSettings?.(); if (action === 'diagnostics') options.onDiagnostics?.();}, {signal:abort.signal});\n update(initial); return {element, update, destroy() {if (destroyed) return; destroyed = true; abort.abort(); if (!options.element) element.remove();}};\n}\nexport interface PermissionControlState {mode: string; description: string; disabled?: boolean}\nexport function createPermissionControl(initial: PermissionControlState, options: {element?: HTMLElement; descriptionElement?: HTMLElement; onChange?: (value: string) => void} = {}): UIBlock<PermissionControlState> {\n const element = options.element || document.createElement('label'); if (!options.element) {element.className = 'pui-permission-control'; element.innerHTML = `${icon('shield')}<span class=\"pui-sr-only\">Conversation permissions</span><select aria-describedby=\"pui-permission-description\"><option value=\"ask\">Ask before changes</option><option value=\"workspace-write\">Allow workspace changes</option><option value=\"full-access\">Full access</option></select>${icon('down')}`;}\n const selector = element.querySelector<HTMLSelectElement>('select')!; const abort = new AbortController(); let destroyed = false;\n if (options.descriptionElement) {if (!options.descriptionElement.id) options.descriptionElement.id = uniqueId('pui-permission-description'); selector.setAttribute('aria-describedby', options.descriptionElement.id);} else selector.removeAttribute('aria-describedby');\n const parts = adoptProductParts(element); const control = parts.control(selector);\n const update = (state: Readonly<PermissionControlState>) => {if (destroyed) return; control.reset(state.mode); control.update({disabled:!!state.disabled}); element.classList.toggle('pui-full-access', state.mode === 'full-access'); if (options.descriptionElement) {options.descriptionElement.textContent = state.description; options.descriptionElement.classList.toggle('pui-full-access-note', state.mode === 'full-access');} else selector.title = state.description;};\n selector.addEventListener('change', () => options.onChange?.(selector.value), {signal:abort.signal}); update(initial);\n return {element, update, destroy() {if (destroyed) return; destroyed = true; parts.destroy(); abort.abort(); if (!options.element) element.remove();}};\n}\n"]}
|
|
@@ -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"]}
|