pith-ui 0.1.1 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AI.md +12 -1
- package/API.md +347 -192
- package/README.md +3 -1
- package/dist/composer.d.ts.map +1 -1
- package/dist/composer.js +37 -27
- package/dist/composer.js.map +1 -1
- package/dist/content/conversation-reader.d.ts.map +1 -1
- package/dist/content/conversation-reader.js +8 -1
- package/dist/content/conversation-reader.js.map +1 -1
- package/dist/content/conversation-view.d.ts.map +1 -1
- package/dist/content/conversation-view.js +34 -8
- package/dist/content/conversation-view.js.map +1 -1
- package/dist/content/image-viewer.d.ts.map +1 -1
- package/dist/content/image-viewer.js +11 -3
- package/dist/content/image-viewer.js.map +1 -1
- package/dist/content/paged-preview.d.ts +1 -1
- package/dist/content/paged-preview.d.ts.map +1 -1
- package/dist/content/paged-preview.js +4 -2
- package/dist/content/paged-preview.js.map +1 -1
- package/dist/content/views.d.ts +3 -0
- package/dist/content/views.d.ts.map +1 -1
- package/dist/content/views.js +110 -41
- package/dist/content/views.js.map +1 -1
- package/dist/content/welcome.d.ts.map +1 -1
- package/dist/content/welcome.js +5 -2
- package/dist/content/welcome.js.map +1 -1
- package/dist/dom.js +1 -1
- package/dist/dom.js.map +1 -1
- package/dist/files/panels.d.ts +2 -0
- package/dist/files/panels.d.ts.map +1 -1
- package/dist/files/panels.js +202 -128
- package/dist/files/panels.js.map +1 -1
- package/dist/files/systems.d.ts.map +1 -1
- package/dist/files/systems.js +37 -64
- package/dist/files/systems.js.map +1 -1
- package/dist/forms/actions.d.ts +23 -2
- package/dist/forms/actions.d.ts.map +1 -1
- package/dist/forms/actions.js +53 -25
- package/dist/forms/actions.js.map +1 -1
- package/dist/forms/custom-connection.d.ts.map +1 -1
- package/dist/forms/custom-connection.js +3 -2
- package/dist/forms/custom-connection.js.map +1 -1
- package/dist/forms/history.d.ts +1 -1
- package/dist/forms/history.d.ts.map +1 -1
- package/dist/forms/history.js +78 -15
- package/dist/forms/history.js.map +1 -1
- package/dist/forms/identity.d.ts.map +1 -1
- package/dist/forms/identity.js +9 -25
- package/dist/forms/identity.js.map +1 -1
- package/dist/forms/model-editor.js +3 -3
- package/dist/forms/model-editor.js.map +1 -1
- package/dist/forms/panels.d.ts +9 -2
- package/dist/forms/panels.d.ts.map +1 -1
- package/dist/forms/panels.js +79 -88
- package/dist/forms/panels.js.map +1 -1
- package/dist/forms/provider-login.d.ts.map +1 -1
- package/dist/forms/provider-login.js +20 -36
- package/dist/forms/provider-login.js.map +1 -1
- package/dist/forms/resource-editor.js +5 -4
- package/dist/forms/resource-editor.js.map +1 -1
- package/dist/foundations.d.ts +1 -1
- package/dist/foundations.d.ts.map +1 -1
- package/dist/foundations.js +22 -38
- package/dist/foundations.js.map +1 -1
- package/dist/navigation/context-menu.d.ts +7 -1
- package/dist/navigation/context-menu.d.ts.map +1 -1
- package/dist/navigation/context-menu.js +60 -57
- package/dist/navigation/context-menu.js.map +1 -1
- package/dist/navigation/navigation.d.ts +13 -0
- package/dist/navigation/navigation.d.ts.map +1 -1
- package/dist/navigation/navigation.js +96 -130
- package/dist/navigation/navigation.js.map +1 -1
- package/dist/operation.d.ts +12 -0
- package/dist/operation.d.ts.map +1 -0
- package/dist/operation.js +49 -0
- package/dist/operation.js.map +1 -0
- package/dist/primitives.d.ts +8 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +114 -143
- package/dist/primitives.js.map +1 -1
- package/dist/reconcile.d.ts +11 -0
- package/dist/reconcile.d.ts.map +1 -0
- package/dist/reconcile.js +36 -0
- package/dist/reconcile.js.map +1 -0
- package/dist/select.d.ts +14 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +304 -0
- package/dist/select.js.map +1 -0
- package/dist/styles.css +435 -369
- package/dist/surface.d.ts +31 -0
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +193 -3
- package/dist/surface.js.map +1 -1
- package/dist/tasks/approval.d.ts.map +1 -1
- package/dist/tasks/approval.js +83 -45
- package/dist/tasks/approval.js.map +1 -1
- package/dist/tasks/permission-element.d.ts +3 -0
- package/dist/tasks/permission-element.d.ts.map +1 -0
- package/dist/tasks/permission-element.js +10 -0
- package/dist/tasks/permission-element.js.map +1 -0
- package/dist/tasks/permission.d.ts.map +1 -1
- package/dist/tasks/permission.js +37 -18
- package/dist/tasks/permission.js.map +1 -1
- package/dist/tasks/runtime.d.ts.map +1 -1
- package/dist/tasks/runtime.js +30 -13
- package/dist/tasks/runtime.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +93 -40
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
package/dist/surface.d.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
interface AnchoredSurfaceOptions {
|
|
2
|
+
trigger: HTMLElement;
|
|
3
|
+
panel: HTMLElement;
|
|
4
|
+
closeOnFocusOut?: boolean;
|
|
5
|
+
exclusions?: readonly Node[];
|
|
6
|
+
onToggle?: (open: boolean, restoreFocus?: boolean) => void;
|
|
7
|
+
position?: boolean;
|
|
8
|
+
canOpen?: () => boolean;
|
|
9
|
+
canEscape?: () => boolean;
|
|
10
|
+
getAnchor?: () => Pick<DOMRect, 'left' | 'right' | 'top' | 'bottom' | 'width' | 'height'>;
|
|
11
|
+
anchorElement?: HTMLElement;
|
|
12
|
+
dismissOnMove?: boolean;
|
|
13
|
+
}
|
|
14
|
+
interface AnchoredSurface {
|
|
15
|
+
open(): void;
|
|
16
|
+
close(restoreFocus?: boolean): void;
|
|
17
|
+
isOpen(): boolean;
|
|
18
|
+
destroy(): void;
|
|
19
|
+
}
|
|
20
|
+
/** Shared nonmodal surface ownership. Retained panels can keep their own layout;
|
|
21
|
+
* anchored panels use the native top layer to escape scroll/overflow containers.
|
|
22
|
+
*/
|
|
23
|
+
export declare function installAnchoredSurface(options: AnchoredSurfaceOptions): AnchoredSurface;
|
|
24
|
+
/** A content block can request closure without guessing its eventual frame ID. */
|
|
25
|
+
export declare function bindContentClose(owner: HTMLElement, options?: {
|
|
26
|
+
dialogId?: string;
|
|
27
|
+
onClose?: () => void;
|
|
28
|
+
}): {
|
|
29
|
+
destroy(): void;
|
|
30
|
+
};
|
|
1
31
|
export interface DialogLifecycleOptions {
|
|
2
32
|
closeOnBackdrop?: boolean;
|
|
3
33
|
canClose?: () => boolean;
|
|
@@ -26,4 +56,5 @@ export declare function installMenuNavigation(menu: HTMLElement, options: MenuNa
|
|
|
26
56
|
focus(edge?: 'first' | 'last'): void;
|
|
27
57
|
destroy(): void;
|
|
28
58
|
};
|
|
59
|
+
export {};
|
|
29
60
|
//# sourceMappingURL=surface.d.ts.map
|
package/dist/surface.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
1
|
+
{"version":3,"file":"surface.d.ts","sourceRoot":"","sources":["../src/surface.ts"],"names":[],"mappings":"AAEA,UAAU,sBAAsB;IAC9B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,SAAS,IAAI,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAC;IAC1F,aAAa,CAAC,EAAE,WAAW,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AACD,UAAU,eAAe;IACvB,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,MAAM,IAAI,OAAO,CAAC;IAClB,OAAO,IAAI,IAAI,CAAC;CACjB;AAGD;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CA2GvF;AASD,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CAAO,GAAG;IAAE,OAAO,IAAI,IAAI,CAAA;CAAE,CAcnI;AAED,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
CHANGED
|
@@ -1,4 +1,194 @@
|
|
|
1
1
|
import { Lifetime } from './core.js';
|
|
2
|
+
const floatingStacks = new WeakMap();
|
|
3
|
+
/** Shared nonmodal surface ownership. Retained panels can keep their own layout;
|
|
4
|
+
* anchored panels use the native top layer to escape scroll/overflow containers.
|
|
5
|
+
*/
|
|
6
|
+
export function installAnchoredSurface(options) {
|
|
7
|
+
const { trigger, panel } = options;
|
|
8
|
+
const doc = trigger.ownerDocument, view = doc.defaultView;
|
|
9
|
+
const position = options.position !== false;
|
|
10
|
+
const originalPopover = panel.getAttribute('popover');
|
|
11
|
+
const originalStyle = panel.getAttribute('style');
|
|
12
|
+
const topLayer = position && typeof panel.showPopover === 'function';
|
|
13
|
+
if (topLayer)
|
|
14
|
+
panel.setAttribute('popover', 'manual');
|
|
15
|
+
if (position)
|
|
16
|
+
panel.dataset.puiFloating = '';
|
|
17
|
+
let opened = false, disposed = false, events;
|
|
18
|
+
const stack = floatingStacks.get(doc) ?? [];
|
|
19
|
+
floatingStacks.set(doc, stack);
|
|
20
|
+
panel.hidden = true;
|
|
21
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
22
|
+
const contains = (target) => target instanceof Node &&
|
|
23
|
+
(trigger.contains(target) || panel.contains(target) || options.exclusions?.some(node => node.contains(target)));
|
|
24
|
+
const style = (name, value) => { if (panel.style.getPropertyValue(name) !== value)
|
|
25
|
+
panel.style.setProperty(name, value); };
|
|
26
|
+
const place = () => {
|
|
27
|
+
if (!opened || !position)
|
|
28
|
+
return;
|
|
29
|
+
if (!trigger.isConnected || !trigger.getClientRects().length) {
|
|
30
|
+
close();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const anchor = options.getAnchor?.() ?? options.anchorElement?.getBoundingClientRect() ?? trigger.getBoundingClientRect();
|
|
34
|
+
const viewport = view.visualViewport;
|
|
35
|
+
const left = viewport?.offsetLeft ?? 0, top = viewport?.offsetTop ?? 0;
|
|
36
|
+
const width = viewport?.width ?? doc.documentElement.clientWidth;
|
|
37
|
+
const height = viewport?.height ?? doc.documentElement.clientHeight;
|
|
38
|
+
const gap = parseFloat(view.getComputedStyle(panel).getPropertyValue('--pui-space-sm')) || 8;
|
|
39
|
+
const margin = gap;
|
|
40
|
+
style('max-width', `${Math.max(0, width - margin * 2)}px`);
|
|
41
|
+
style('min-width', `${Math.min(anchor.width, Math.max(0, width - margin * 2))}px`);
|
|
42
|
+
// scrollHeight measures the content without expanding the viewport first.
|
|
43
|
+
// Expanding a scrolled list would clamp scrollTop and hide its active item.
|
|
44
|
+
const scrollTop = panel.scrollTop, scrollLeft = panel.scrollLeft;
|
|
45
|
+
const desired = Math.min(panel.scrollHeight + 2, 320);
|
|
46
|
+
const below = top + height - anchor.bottom - margin - gap;
|
|
47
|
+
const above = anchor.top - top - margin - gap;
|
|
48
|
+
const upwards = below < desired && above > below;
|
|
49
|
+
const available = Math.max(0, upwards ? above : below);
|
|
50
|
+
style('max-height', `${Math.min(320, available)}px`);
|
|
51
|
+
const bounds = panel.getBoundingClientRect();
|
|
52
|
+
const horizontal = anchor.left + bounds.width > left + width - margin ? anchor.right - bounds.width : anchor.left;
|
|
53
|
+
style('left', `${Math.max(left + margin, Math.min(horizontal, left + width - margin - bounds.width))}px`);
|
|
54
|
+
style('top', `${upwards ? Math.max(top + margin, anchor.top - gap - bounds.height) : anchor.bottom + gap}px`);
|
|
55
|
+
panel.scrollTop = scrollTop;
|
|
56
|
+
panel.scrollLeft = scrollLeft;
|
|
57
|
+
};
|
|
58
|
+
function close(restoreFocus = false) {
|
|
59
|
+
if (!opened)
|
|
60
|
+
return;
|
|
61
|
+
opened = false;
|
|
62
|
+
events?.destroy();
|
|
63
|
+
events = undefined;
|
|
64
|
+
const index = stack.indexOf(handle);
|
|
65
|
+
if (index >= 0)
|
|
66
|
+
stack.splice(index, 1);
|
|
67
|
+
if (topLayer && panel.matches(':popover-open'))
|
|
68
|
+
panel.hidePopover();
|
|
69
|
+
panel.hidden = true;
|
|
70
|
+
trigger.setAttribute('aria-expanded', 'false');
|
|
71
|
+
options.onToggle?.(false, restoreFocus);
|
|
72
|
+
if (restoreFocus && trigger.isConnected && !trigger.matches(':disabled'))
|
|
73
|
+
trigger.focus({ preventScroll: true });
|
|
74
|
+
}
|
|
75
|
+
const handle = {
|
|
76
|
+
open() {
|
|
77
|
+
if (disposed || opened || options.canOpen?.() === false || !trigger.isConnected || !trigger.getClientRects().length)
|
|
78
|
+
return;
|
|
79
|
+
opened = true;
|
|
80
|
+
panel.hidden = false;
|
|
81
|
+
trigger.setAttribute('aria-expanded', 'true');
|
|
82
|
+
stack.push(handle);
|
|
83
|
+
if (topLayer)
|
|
84
|
+
panel.showPopover();
|
|
85
|
+
place();
|
|
86
|
+
if (!opened)
|
|
87
|
+
return;
|
|
88
|
+
events = new Lifetime();
|
|
89
|
+
const outside = (event) => { if (!contains(event.target))
|
|
90
|
+
close(); };
|
|
91
|
+
events.listen(doc, 'pointerdown', outside, { capture: true });
|
|
92
|
+
events.listen(doc, 'click', outside, { capture: true });
|
|
93
|
+
if (options.closeOnFocusOut !== false) {
|
|
94
|
+
events.listen(doc, 'focusin', outside, { capture: true });
|
|
95
|
+
const blur = (event) => {
|
|
96
|
+
// Native pointer focus can briefly expose body between focusout and
|
|
97
|
+
// focusin. Its related target already identifies an internal move.
|
|
98
|
+
if (contains(event.relatedTarget))
|
|
99
|
+
return;
|
|
100
|
+
queueMicrotask(() => { if (opened && !contains(doc.activeElement))
|
|
101
|
+
close(); });
|
|
102
|
+
};
|
|
103
|
+
events.listen(trigger, 'focusout', blur);
|
|
104
|
+
events.listen(panel, 'focusout', blur);
|
|
105
|
+
}
|
|
106
|
+
events.listen(doc, 'keydown', (event => {
|
|
107
|
+
const key = event;
|
|
108
|
+
if (key.key !== 'Escape' || stack.at(-1) !== handle || options.canEscape?.() === false)
|
|
109
|
+
return;
|
|
110
|
+
const modal = [...doc.querySelectorAll('dialog[open]')].at(-1);
|
|
111
|
+
if (modal && !modal.contains(trigger))
|
|
112
|
+
return;
|
|
113
|
+
key.preventDefault();
|
|
114
|
+
key.stopPropagation();
|
|
115
|
+
close(true);
|
|
116
|
+
}), { capture: true });
|
|
117
|
+
if (position) {
|
|
118
|
+
const move = () => { if (options.dismissOnMove)
|
|
119
|
+
close();
|
|
120
|
+
else
|
|
121
|
+
place(); };
|
|
122
|
+
events.listen(view, 'resize', move);
|
|
123
|
+
events.listen(doc, 'scroll', event => { if (!(event.target instanceof Node) || !panel.contains(event.target))
|
|
124
|
+
move(); }, { capture: true });
|
|
125
|
+
if (view.visualViewport) {
|
|
126
|
+
events.listen(view.visualViewport, 'resize', move);
|
|
127
|
+
events.listen(view.visualViewport, 'scroll', move);
|
|
128
|
+
}
|
|
129
|
+
const observer = new ResizeObserver(place);
|
|
130
|
+
observer.observe(trigger);
|
|
131
|
+
observer.observe(panel);
|
|
132
|
+
if (options.anchorElement && options.anchorElement !== trigger)
|
|
133
|
+
observer.observe(options.anchorElement);
|
|
134
|
+
events.add(() => observer.disconnect());
|
|
135
|
+
}
|
|
136
|
+
options.onToggle?.(true);
|
|
137
|
+
},
|
|
138
|
+
close,
|
|
139
|
+
isOpen: () => opened,
|
|
140
|
+
destroy() {
|
|
141
|
+
if (disposed)
|
|
142
|
+
return;
|
|
143
|
+
close();
|
|
144
|
+
disposed = true;
|
|
145
|
+
if (position) {
|
|
146
|
+
delete panel.dataset.puiFloating;
|
|
147
|
+
if (originalStyle === null)
|
|
148
|
+
panel.removeAttribute('style');
|
|
149
|
+
else
|
|
150
|
+
panel.setAttribute('style', originalStyle);
|
|
151
|
+
}
|
|
152
|
+
if (topLayer) {
|
|
153
|
+
if (originalPopover === null)
|
|
154
|
+
panel.removeAttribute('popover');
|
|
155
|
+
else
|
|
156
|
+
panel.setAttribute('popover', originalPopover);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
return handle;
|
|
161
|
+
}
|
|
162
|
+
function belongsToCloseScope(owner, close, dialogId) {
|
|
163
|
+
if (!owner.contains(close) || close.matches(':disabled') || close.getAttribute('aria-disabled') === 'true')
|
|
164
|
+
return false;
|
|
165
|
+
const dialog = owner.closest('dialog');
|
|
166
|
+
if (close.closest('dialog') !== dialog)
|
|
167
|
+
return false;
|
|
168
|
+
return close.hasAttribute('data-pui-close') || !close.dataset.close || close.dataset.close === (dialogId ?? dialog?.id ?? owner.id);
|
|
169
|
+
}
|
|
170
|
+
/** A content block can request closure without guessing its eventual frame ID. */
|
|
171
|
+
export function bindContentClose(owner, options = {}) {
|
|
172
|
+
const life = new Lifetime();
|
|
173
|
+
life.listen(owner, 'click', (event => {
|
|
174
|
+
if (event.defaultPrevented || !(event.target instanceof Element))
|
|
175
|
+
return;
|
|
176
|
+
const close = event.target.closest('[data-pui-close], [data-close]');
|
|
177
|
+
if (!close || !belongsToCloseScope(owner, close, options.dialogId))
|
|
178
|
+
return;
|
|
179
|
+
const dialog = owner.closest('dialog');
|
|
180
|
+
if (dialog && ![...(dialogSessions.get(dialog)?.subscribers ?? [])].every(subscriber => subscriber.canClose?.() !== false))
|
|
181
|
+
return;
|
|
182
|
+
event.preventDefault();
|
|
183
|
+
if (options.onClose)
|
|
184
|
+
options.onClose();
|
|
185
|
+
else if (dialog?.open)
|
|
186
|
+
dialog.close();
|
|
187
|
+
else
|
|
188
|
+
owner.hidden = true;
|
|
189
|
+
}));
|
|
190
|
+
return { destroy: () => life.destroy() };
|
|
191
|
+
}
|
|
2
192
|
const dialogSessions = new WeakMap();
|
|
3
193
|
const activations = new WeakMap();
|
|
4
194
|
function retainActivation(document) {
|
|
@@ -40,13 +230,13 @@ export function installDialogLifecycle(dialog, options = {}) {
|
|
|
40
230
|
current.listeners.listen(dialog, 'focusin', (() => { if (dialog.open)
|
|
41
231
|
current.active = true; }));
|
|
42
232
|
current.listeners.listen(dialog, 'click', (event => {
|
|
43
|
-
if (!dialog.open || !canClose())
|
|
233
|
+
if (!dialog.open || !canClose() || event.defaultPrevented)
|
|
44
234
|
return;
|
|
45
235
|
const target = event.target;
|
|
46
236
|
if (target instanceof Element) {
|
|
47
237
|
for (const subscriber of current.subscribers) {
|
|
48
|
-
const close = target.closest(subscriber.closeButtonSelector ?? '[data-close]');
|
|
49
|
-
if (close && dialog
|
|
238
|
+
const close = target.closest(subscriber.closeButtonSelector ?? '[data-pui-close], [data-close]');
|
|
239
|
+
if (close && belongsToCloseScope(dialog, close, dialog.id)) {
|
|
50
240
|
dialog.close();
|
|
51
241
|
return;
|
|
52
242
|
}
|
package/dist/surface.js.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"file":"surface.js","sourceRoot":"","sources":["../src/surface.ts"],"names":[],"mappings":"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","sourcesContent":["import { Lifetime } from './core.js';\n\ninterface AnchoredSurfaceOptions {\n trigger: HTMLElement;\n panel: HTMLElement;\n closeOnFocusOut?: boolean;\n exclusions?: readonly Node[];\n onToggle?: (open: boolean, restoreFocus?: boolean) => void;\n position?: boolean;\n canOpen?: () => boolean;\n canEscape?: () => boolean;\n getAnchor?: () => Pick<DOMRect, 'left' | 'right' | 'top' | 'bottom' | 'width' | 'height'>;\n anchorElement?: HTMLElement;\n dismissOnMove?: boolean;\n}\ninterface AnchoredSurface {\n open(): void;\n close(restoreFocus?: boolean): void;\n isOpen(): boolean;\n destroy(): void;\n}\nconst floatingStacks = new WeakMap<Document, AnchoredSurface[]>();\n\n/** Shared nonmodal surface ownership. Retained panels can keep their own layout;\n * anchored panels use the native top layer to escape scroll/overflow containers.\n */\nexport function installAnchoredSurface(options: AnchoredSurfaceOptions): AnchoredSurface {\n const { trigger, panel } = options;\n const doc = trigger.ownerDocument, view = doc.defaultView!;\n const position = options.position !== false;\n const originalPopover = panel.getAttribute('popover');\n const originalStyle = panel.getAttribute('style');\n const topLayer = position && typeof panel.showPopover === 'function';\n if (topLayer) panel.setAttribute('popover', 'manual');\n if (position) panel.dataset.puiFloating = '';\n let opened = false, disposed = false, events: Lifetime | undefined;\n const stack = floatingStacks.get(doc) ?? [];\n floatingStacks.set(doc, stack);\n panel.hidden = true;\n trigger.setAttribute('aria-expanded', 'false');\n const contains = (target: EventTarget | null) => target instanceof Node &&\n (trigger.contains(target) || panel.contains(target) || options.exclusions?.some(node => node.contains(target)));\n const style = (name: string, value: string) => { if (panel.style.getPropertyValue(name) !== value) panel.style.setProperty(name, value); };\n const place = () => {\n if (!opened || !position) return;\n if (!trigger.isConnected || !trigger.getClientRects().length) { close(); return; }\n const anchor = options.getAnchor?.() ?? options.anchorElement?.getBoundingClientRect() ?? trigger.getBoundingClientRect();\n const viewport = view.visualViewport;\n const left = viewport?.offsetLeft ?? 0, top = viewport?.offsetTop ?? 0;\n const width = viewport?.width ?? doc.documentElement.clientWidth;\n const height = viewport?.height ?? doc.documentElement.clientHeight;\n const gap = parseFloat(view.getComputedStyle(panel).getPropertyValue('--pui-space-sm')) || 8;\n const margin = gap;\n style('max-width', `${Math.max(0, width - margin * 2)}px`);\n style('min-width', `${Math.min(anchor.width, Math.max(0, width - margin * 2))}px`);\n // scrollHeight measures the content without expanding the viewport first.\n // Expanding a scrolled list would clamp scrollTop and hide its active item.\n const scrollTop = panel.scrollTop, scrollLeft = panel.scrollLeft;\n const desired = Math.min(panel.scrollHeight + 2, 320);\n const below = top + height - anchor.bottom - margin - gap;\n const above = anchor.top - top - margin - gap;\n const upwards = below < desired && above > below;\n const available = Math.max(0, upwards ? above : below);\n style('max-height', `${Math.min(320, available)}px`);\n const bounds = panel.getBoundingClientRect();\n const horizontal = anchor.left + bounds.width > left + width - margin ? anchor.right - bounds.width : anchor.left;\n style('left', `${Math.max(left + margin, Math.min(horizontal, left + width - margin - bounds.width))}px`);\n style('top', `${upwards ? Math.max(top + margin, anchor.top - gap - bounds.height) : anchor.bottom + gap}px`);\n panel.scrollTop = scrollTop; panel.scrollLeft = scrollLeft;\n };\n function close(restoreFocus = false): void {\n if (!opened) return;\n opened = false;\n events?.destroy(); events = undefined;\n const index = stack.indexOf(handle); if (index >= 0) stack.splice(index, 1);\n if (topLayer && panel.matches(':popover-open')) panel.hidePopover();\n panel.hidden = true; trigger.setAttribute('aria-expanded', 'false');\n options.onToggle?.(false, restoreFocus);\n if (restoreFocus && trigger.isConnected && !trigger.matches(':disabled')) trigger.focus({ preventScroll: true });\n }\n const handle: AnchoredSurface = {\n open() {\n if (disposed || opened || options.canOpen?.() === false || !trigger.isConnected || !trigger.getClientRects().length) return;\n opened = true; panel.hidden = false; trigger.setAttribute('aria-expanded', 'true');\n stack.push(handle);\n if (topLayer) panel.showPopover();\n place();\n if (!opened) return;\n events = new Lifetime();\n const outside = (event: Event) => { if (!contains(event.target)) close(); };\n events.listen(doc, 'pointerdown', outside, { capture: true });\n events.listen(doc, 'click', outside, { capture: true });\n if (options.closeOnFocusOut !== false) {\n events.listen(doc, 'focusin', outside, { capture: true });\n const blur = (event: Event) => {\n // Native pointer focus can briefly expose body between focusout and\n // focusin. Its related target already identifies an internal move.\n if (contains((event as FocusEvent).relatedTarget)) return;\n queueMicrotask(() => { if (opened && !contains(doc.activeElement)) close(); });\n };\n events.listen(trigger, 'focusout', blur); events.listen(panel, 'focusout', blur);\n }\n events.listen(doc, 'keydown', (event => {\n const key = event as KeyboardEvent;\n if (key.key !== 'Escape' || stack.at(-1) !== handle || options.canEscape?.() === false) return;\n const modal = [...doc.querySelectorAll<HTMLDialogElement>('dialog[open]')].at(-1);\n if (modal && !modal.contains(trigger)) return;\n key.preventDefault(); key.stopPropagation(); close(true);\n }) as EventListener, { capture: true });\n if (position) {\n const move = () => { if (options.dismissOnMove) close(); else place(); };\n events.listen(view, 'resize', move);\n events.listen(doc, 'scroll', event => { if (!(event.target instanceof Node) || !panel.contains(event.target)) move(); }, { capture: true });\n if (view.visualViewport) { events.listen(view.visualViewport, 'resize', move); events.listen(view.visualViewport, 'scroll', move); }\n const observer = new ResizeObserver(place); observer.observe(trigger); observer.observe(panel);\n if (options.anchorElement && options.anchorElement !== trigger) observer.observe(options.anchorElement);\n events.add(() => observer.disconnect());\n }\n options.onToggle?.(true);\n },\n close,\n isOpen: () => opened,\n destroy() {\n if (disposed) return;\n close(); disposed = true;\n if (position) {\n delete panel.dataset.puiFloating;\n if (originalStyle === null) panel.removeAttribute('style'); else panel.setAttribute('style', originalStyle);\n }\n if (topLayer) { if (originalPopover === null) panel.removeAttribute('popover'); else panel.setAttribute('popover', originalPopover); }\n },\n };\n return handle;\n}\n\nfunction belongsToCloseScope(owner: HTMLElement, close: HTMLElement, dialogId?: string): boolean {\n if (!owner.contains(close) || close.matches(':disabled') || close.getAttribute('aria-disabled') === 'true') return false;\n const dialog = owner.closest('dialog');\n if (close.closest('dialog') !== dialog) return false;\n return close.hasAttribute('data-pui-close') || !close.dataset.close || close.dataset.close === (dialogId ?? dialog?.id ?? owner.id);\n}\n\n/** A content block can request closure without guessing its eventual frame ID. */\nexport function bindContentClose(owner: HTMLElement, options: { dialogId?: string; onClose?: () => void } = {}): { destroy(): void } {\n const life = new Lifetime();\n life.listen(owner, 'click', (event => {\n if (event.defaultPrevented || !(event.target instanceof Element)) return;\n const close = event.target.closest<HTMLElement>('[data-pui-close], [data-close]');\n if (!close || !belongsToCloseScope(owner, close, options.dialogId)) return;\n const dialog = owner.closest('dialog');\n if (dialog && ![...(dialogSessions.get(dialog)?.subscribers ?? [])].every(subscriber => subscriber.canClose?.() !== false)) return;\n event.preventDefault();\n if (options.onClose) options.onClose();\n else if (dialog?.open) dialog.close();\n else owner.hidden = true;\n }) as EventListener);\n return { destroy: () => life.destroy() };\n}\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() || event.defaultPrevented) 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-pui-close], [data-close]');\n if (close && belongsToCloseScope(dialog, 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"]}
|
|
@@ -1 +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,
|
|
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,CAkC5N"}
|
package/dist/tasks/approval.js
CHANGED
|
@@ -1,64 +1,102 @@
|
|
|
1
|
-
import { scopeIds
|
|
2
|
-
import {
|
|
1
|
+
import { scopeIds } from '../forms/identity.js';
|
|
2
|
+
import { icon, uniqueId, setText } from '../dom.js';
|
|
3
3
|
import { createPagedPreview } from '../content/paged-preview.js';
|
|
4
4
|
import { adoptProductParts } from '../foundations.js';
|
|
5
5
|
export function createApprovalPanel(initial, options = {}) {
|
|
6
6
|
const element = options.element || document.createElement('div');
|
|
7
7
|
if (!options.element)
|
|
8
8
|
element.className = 'pui-approval-panel';
|
|
9
|
-
const prefix = options.idPrefix ?? uniqueId('pui-approval') + '-';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
const prefix = options.idPrefix ?? uniqueId('pui-approval') + '-', parts = adoptProductParts(element), abort = new AbortController();
|
|
10
|
+
let destroyed = false, current = null, contentAbort = new AbortController();
|
|
11
|
+
let card, warning, badge, file, fileKind, path, previewError, diff, shortened, details, args, scope, always;
|
|
12
|
+
let diffBlock, argsBlock;
|
|
13
|
+
const clear = () => { contentAbort.abort(); contentAbort = new AbortController(); diffBlock?.destroy(); argsBlock?.destroy(); diffBlock = argsBlock = undefined; card = undefined; element.replaceChildren(); };
|
|
14
|
+
const showArgs = () => { if (destroyed || !current || argsBlock || (current.preview && !details.open))
|
|
15
|
+
return; argsBlock = createPagedPreview({ container: args, text: current.argsText, kind: 'text' }); };
|
|
16
|
+
function create() {
|
|
17
|
+
element.innerHTML = `<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"></span></div><p class="pui-approval-warning" hidden></p><div class="pui-approval-file" hidden><strong></strong><code></code></div><p class="pui-preview-note" role="status" hidden></p><div id="approval-diff-preview" hidden></div><p class="pui-preview-note" data-shortened hidden>Preview shortened. Expand Tool arguments for the complete change.</p><details class="pui-approval-arguments"><summary>Tool arguments</summary><div id="approval-args-preview"></div></details><p class="pui-approval-scope" hidden>Workspace permission applies to this conversation. Commands and external connection tools still need approval.</p><div class="pui-approval-actions"><button type="button" class="pui-secondary-button" data-approval="deny">Deny</button><button type="button" class="pui-secondary-button pui-always-allow-button" data-approval="always">Always allow workspace changes</button><button type="button" class="pui-primary-button" data-approval="allow">${icon('check')}Allow this action</button></div></section>`;
|
|
18
|
+
card = element.firstElementChild;
|
|
19
|
+
scopeIds(card, prefix);
|
|
20
|
+
warning = card.querySelector('.pui-approval-warning');
|
|
21
|
+
badge = card.querySelector('.pui-approval-badge');
|
|
22
|
+
file = card.querySelector('.pui-approval-file');
|
|
23
|
+
fileKind = file.querySelector('strong');
|
|
24
|
+
path = file.querySelector('code');
|
|
25
|
+
previewError = card.querySelector('[role="status"]');
|
|
26
|
+
diff = card.querySelector('[id$="approval-diff-preview"]');
|
|
27
|
+
shortened = card.querySelector('[data-shortened]');
|
|
28
|
+
details = card.querySelector('details');
|
|
29
|
+
args = details.querySelector('div');
|
|
30
|
+
scope = card.querySelector('.pui-approval-scope');
|
|
31
|
+
always = card.querySelector('[data-approval="always"]');
|
|
32
|
+
details.addEventListener('toggle', showArgs, { signal: contentAbort.signal });
|
|
33
|
+
}
|
|
17
34
|
const update = (state) => {
|
|
18
35
|
if (destroyed)
|
|
19
36
|
return;
|
|
20
|
-
const approval = state.approval;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
37
|
+
const approval = state.approval, changed = approval?.id !== current?.id;
|
|
38
|
+
if (changed || !approval)
|
|
39
|
+
clear();
|
|
40
|
+
current = approval;
|
|
41
|
+
if (!approval) {
|
|
42
|
+
parts.update();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const fresh = !card;
|
|
46
|
+
if (fresh)
|
|
47
|
+
create();
|
|
48
|
+
const preview = approval.preview;
|
|
49
|
+
setText(badge, approval.toolName);
|
|
50
|
+
setText(warning, approval.warning || '');
|
|
51
|
+
warning.hidden = !approval.warning;
|
|
52
|
+
file.hidden = !preview;
|
|
53
|
+
if (preview) {
|
|
54
|
+
setText(fileKind, preview.kind === 'create' ? 'Create file' : 'Modify file');
|
|
55
|
+
setText(path, preview.path);
|
|
56
|
+
}
|
|
57
|
+
previewError.hidden = !preview?.error;
|
|
58
|
+
setText(previewError, preview?.error || '');
|
|
59
|
+
diff.hidden = !preview || !!preview.error;
|
|
60
|
+
shortened.hidden = !preview || !!preview.error || !preview.truncated;
|
|
61
|
+
if (preview && !preview.error) {
|
|
62
|
+
const value = { text: preview.diff || 'No content changes.', kind: 'diff' };
|
|
63
|
+
if (diffBlock)
|
|
64
|
+
diffBlock.update(value);
|
|
65
|
+
else
|
|
66
|
+
diffBlock = createPagedPreview({ container: diff, ...value });
|
|
67
|
+
}
|
|
68
|
+
else if (diffBlock) {
|
|
69
|
+
diffBlock.destroy();
|
|
70
|
+
diffBlock = undefined;
|
|
51
71
|
}
|
|
52
|
-
|
|
72
|
+
if (preview) {
|
|
73
|
+
if (args.parentElement !== details)
|
|
74
|
+
details.append(args);
|
|
75
|
+
if (details.parentElement !== card)
|
|
76
|
+
card.insertBefore(details, scope);
|
|
77
|
+
if (fresh)
|
|
78
|
+
details.open = !!preview.error;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
if (args.parentElement !== card)
|
|
82
|
+
card.insertBefore(args, scope);
|
|
83
|
+
details.remove();
|
|
84
|
+
}
|
|
85
|
+
showArgs();
|
|
86
|
+
argsBlock?.update({ text: approval.argsText, kind: 'text' });
|
|
87
|
+
scope.hidden = !approval.canAlwaysAllow;
|
|
88
|
+
always.hidden = !approval.canAlwaysAllow;
|
|
89
|
+
for (const button of element.querySelectorAll('[data-approval]')) {
|
|
90
|
+
button.dataset.approvalId = approval.id;
|
|
53
91
|
parts.action(button).update({ disabled: !!state.disabled });
|
|
92
|
+
}
|
|
54
93
|
parts.update();
|
|
55
94
|
};
|
|
56
|
-
element.addEventListener('click',
|
|
95
|
+
element.addEventListener('click', event => { const button = event.target.closest('[data-approval]'); if (button && !button.disabled && !button.hidden)
|
|
57
96
|
options.onAction?.(button.dataset.approvalId, button.dataset.approval); }, { signal: abort.signal });
|
|
58
97
|
update(initial);
|
|
59
98
|
return { element, update, destroy() { if (destroyed)
|
|
60
|
-
return; destroyed = true; parts.destroy(); abort.abort(); contentAbort.abort();
|
|
61
|
-
preview.destroy(); if (!options.element)
|
|
99
|
+
return; destroyed = true; clear(); parts.destroy(); abort.abort(); contentAbort.abort(); if (!options.element)
|
|
62
100
|
element.remove(); } };
|
|
63
101
|
}
|
|
64
102
|
//# sourceMappingURL=approval.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../src/tasks/approval.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAE,UAAU,EAAC,MAAM,sBAAsB,CAAC;AAE1D,OAAO,EAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAC,MAAM,WAAW,CAAC;AACjD,OAAO,EAAC,kBAAkB,EAAyB,MAAM,6BAA6B,CAAC;AACvF,OAAO,EAAC,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AAGpD,MAAM,UAAU,mBAAmB,CAAC,OAA2B,EAAE,UAA4H,EAAE;IAC9L,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAAC,IAAI,CAAC,OAAO,CAAC,OAAO;QAAE,OAAO,CAAC,SAAS,GAAG,oBAAoB,CAAC;IACjI,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC,cAAc,CAAC,GAAG,GAAG,CAAC;IAClE,MAAM,KAAK,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,CAAC,GAAG,CAAsC,EAAU,EAAE,EAAE,CAAC,UAAU,CAAI,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC;IACnG,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IAAC,IAAI,YAAY,GAAG,IAAI,eAAe,EAAE,CAAC;IAAC,IAAI,SAAS,GAAG,EAAE,CAAC;IAAC,IAAI,SAAS,GAAG,KAAK,CAAC;IAAC,IAAI,QAAQ,GAAiC,EAAE,CAAC;IAC1K,MAAM,MAAM,GAAG,CAAC,KAAmC,EAAE,EAAE;QACrD,IAAI,SAAS;YAAE,OAAO;QAAC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;QAAC,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC9F,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAAA,SAAS,GAAG,IAAI,CAAC;YAAC,YAAY,CAAC,KAAK,EAAE,CAAC;YAAC,YAAY,GAAG,IAAI,eAAe,EAAE,CAAC;YAAC,KAAK,MAAM,OAAO,IAAI,QAAQ;gBAAE,OAAO,CAAC,OAAO,EAAE,CAAC;YAAC,QAAQ,GAAG,EAAE,CAAC;YACxK,MAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC;YAAC,MAAM,cAAc,GAAG,CAAC,CAAC,QAAQ,EAAE,cAAc,CAAC;YAAC,MAAM,OAAO,GAAG,QAAQ,EAAE,OAAO,CAAC;YACzH,MAAM,WAAW,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,6CAA6C,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,yCAAyC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,mGAAmG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvS,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,0CAA0C,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,kBAAkB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,gBAAgB,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5M,MAAM,IAAI,GAAG,wCAAwC,CAAC;YAE3D,OAAO,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,4GAA4G,IAAI,CAAC,QAAQ,CAAC,yIAAyI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,OAAO,CAAC,CAAC,CAAC,mCAAmC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC,2CAA2C,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,qCAAqC,IAAI,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,kJAAkJ,CAAC,CAAC,CAAC,EAAE,+FAA+F,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,iBAAiB,cAAc,CAAC,CAAC,CAAC,uFAAuF,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,0CAA0C,CAAC,CAAC,CAAC,EAAE,4DAA4D,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,IAAI,CAAC,OAAO,CAAC,4CAA4C,CAAC,CAAC,CAAC,EAAE,CAAC;YAAC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ;gBAAE,QAAQ,CAAC,KAAoB,EAAE,MAAM,CAAC,CAAC;YAChxC,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,gBAAgB,CAAoB,iBAAiB,CAAC;gBAAE,MAAM,CAAC,OAAO,CAAC,UAAU,GAAG,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC;YACpI,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK;gBAAE,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAC,SAAS,EAAE,CAAC,CAAc,uBAAuB,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,qBAAqB,EAAE,IAAI,EAAE,MAAM,EAAC,CAAC,CAAC,CAAC;YAClL,IAAI,QAAQ,EAAE,CAAC;gBAAA,MAAM,aAAa,GAAG,CAAC,CAAc,uBAAuB,CAAC,CAAC;gBAAC,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;gBAAC,IAAI,MAAM,GAAG,KAAK,CAAC;gBAAC,MAAM,QAAQ,GAAG,GAAG,EAAE,GAAE,IAAI,SAAS,IAAI,MAAM,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;oBAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,EAAC,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAC,CAAC,CAAC,CAAC,CAAA,CAAC,CAAC;gBAAC,OAAO,EAAE,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAC,MAAM,EAAC,YAAY,CAAC,MAAM,EAAC,CAAC,CAAC;gBAAC,QAAQ,EAAE,CAAC;YAAA,CAAC;QAC3b,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,gBAAgB,CAAoB,iBAAiB,CAAC;YAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,EAAC,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAC,CAAC,CAAC;QAC/I,KAAK,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC,CAAC;IACF,OAAO,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,GAAE,MAAM,MAAM,GAAI,KAAK,CAAC,MAAsB,CAAC,OAAO,CAAoB,iBAAiB,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ;QAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,UAAW,EAAE,MAAM,CAAC,OAAO,CAAC,QAAuC,CAAC,CAAC,CAAA,CAAC,EAAE,EAAC,MAAM,EAAC,KAAK,CAAC,MAAM,EAAC,CAAC,CAAC;IAC3S,MAAM,CAAC,OAAO,CAAC,CAAC;IAAC,OAAO,EAAC,OAAO,EAAE,MAAM,EAAE,OAAO,KAAI,IAAI,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO;YAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA,CAAC,EAAC,CAAC;AAClP,CAAC","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"]}
|
|
1
|
+
{"version":3,"file":"approval.js","sourceRoot":"","sources":["../../src/tasks/approval.ts"],"names":[],"mappings":"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","sourcesContent":["import {scopeIds} from '../forms/identity.js';\nimport type { UIBlock } from '../core.js';\nimport {icon, uniqueId, setText} 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') + '-', parts = adoptProductParts(element), abort = new AbortController();\n let destroyed = false, current: ApprovalView | null = null, contentAbort = new AbortController();\n let card: HTMLElement | undefined, warning: HTMLElement, badge: HTMLElement, file: HTMLElement, fileKind: HTMLElement, path: HTMLElement, previewError: HTMLElement, diff: HTMLElement, shortened: HTMLElement, details: HTMLDetailsElement, args: HTMLElement, scope: HTMLElement, always: HTMLButtonElement;\n let diffBlock: UIBlock<PagedPreviewState> | undefined, argsBlock: UIBlock<PagedPreviewState> | undefined;\n const clear = () => {contentAbort.abort(); contentAbort = new AbortController(); diffBlock?.destroy(); argsBlock?.destroy(); diffBlock = argsBlock = undefined; card = undefined; element.replaceChildren();};\n const showArgs = () => {if (destroyed || !current || argsBlock || (current.preview && !details.open)) return; argsBlock = createPagedPreview({container:args,text:current.argsText,kind:'text'});};\n function create(): void {\n element.innerHTML = `<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\"></span></div><p class=\"pui-approval-warning\" hidden></p><div class=\"pui-approval-file\" hidden><strong></strong><code></code></div><p class=\"pui-preview-note\" role=\"status\" hidden></p><div id=\"approval-diff-preview\" hidden></div><p class=\"pui-preview-note\" data-shortened hidden>Preview shortened. Expand Tool arguments for the complete change.</p><details class=\"pui-approval-arguments\"><summary>Tool arguments</summary><div id=\"approval-args-preview\"></div></details><p class=\"pui-approval-scope\" hidden>Workspace permission applies to this conversation. Commands and external connection tools still need approval.</p><div class=\"pui-approval-actions\"><button type=\"button\" class=\"pui-secondary-button\" data-approval=\"deny\">Deny</button><button type=\"button\" class=\"pui-secondary-button pui-always-allow-button\" data-approval=\"always\">Always allow workspace changes</button><button type=\"button\" class=\"pui-primary-button\" data-approval=\"allow\">${icon('check')}Allow this action</button></div></section>`;\n card = element.firstElementChild as HTMLElement; scopeIds(card,prefix);\n warning = card.querySelector('.pui-approval-warning')!; badge = card.querySelector('.pui-approval-badge')!; file = card.querySelector('.pui-approval-file')!; fileKind = file.querySelector('strong')!; path = file.querySelector('code')!;\n previewError = card.querySelector('[role=\"status\"]')!; diff = card.querySelector('[id$=\"approval-diff-preview\"]')!; shortened = card.querySelector('[data-shortened]')!; details = card.querySelector('details')!; args = details.querySelector('div')!; scope = card.querySelector('.pui-approval-scope')!; always = card.querySelector('[data-approval=\"always\"]')!;\n details.addEventListener('toggle',showArgs,{signal:contentAbort.signal});\n }\n const update = (state: Readonly<ApprovalPanelState>) => {\n if (destroyed) return; const approval = state.approval, changed = approval?.id !== current?.id; if (changed || !approval) clear(); current = approval;\n if (!approval) {parts.update(); return;}\n const fresh = !card; if (fresh) create(); const preview = approval.preview;\n setText(badge,approval.toolName); setText(warning,approval.warning || ''); warning.hidden = !approval.warning;\n file.hidden = !preview; if (preview) {setText(fileKind,preview.kind === 'create' ? 'Create file' : 'Modify file'); setText(path,preview.path);}\n previewError.hidden = !preview?.error; setText(previewError,preview?.error || '');\n diff.hidden = !preview || !!preview.error; shortened.hidden = !preview || !!preview.error || !preview.truncated;\n if (preview && !preview.error) {const value = {text:preview.diff || 'No content changes.',kind:'diff' as const}; if (diffBlock) diffBlock.update(value); else diffBlock = createPagedPreview({container:diff,...value});}\n else if (diffBlock) {diffBlock.destroy(); diffBlock = undefined;}\n if (preview) {if (args.parentElement !== details) details.append(args); if (details.parentElement !== card) card!.insertBefore(details,scope); if (fresh) details.open = !!preview.error;}\n else {if (args.parentElement !== card) card!.insertBefore(args,scope); details.remove();}\n showArgs(); argsBlock?.update({text:approval.argsText,kind:'text'});\n scope.hidden = !approval.canAlwaysAllow; always.hidden = !approval.canAlwaysAllow;\n for (const button of element.querySelectorAll<HTMLButtonElement>('[data-approval]')) {button.dataset.approvalId = approval.id; 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 && !button.hidden) 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; clear(); parts.destroy(); abort.abort(); contentAbort.abort(); if (!options.element) element.remove();}};\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-element.d.ts","sourceRoot":"","sources":["../../src/tasks/permission-element.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,wBAAgB,wBAAwB,IAAI,gBAAgB,CAI3D"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { icon } from '../dom.js';
|
|
2
|
+
/** Internal retained-node template shared with the composer shell. */
|
|
3
|
+
export function permissionControlElement() {
|
|
4
|
+
const element = document.createElement('label');
|
|
5
|
+
element.className = 'pui-permission-control';
|
|
6
|
+
element.dataset.puiSelectAnchor = '';
|
|
7
|
+
element.innerHTML = `${icon('shield')}<span class="pui-sr-only">Conversation permissions</span><select><option value="ask">Ask before changes</option><option value="workspace-write">Allow workspace changes</option><option value="full-access">Full access</option></select>${icon('down')}`;
|
|
8
|
+
return element;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=permission-element.js.map
|