@harborclient/sdk 1.4.5 → 1.5.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/CHANGELOG.md +10 -0
- package/dist/components/PageHeader/index.d.ts +3 -0
- package/dist/components/PageHeader/index.d.ts.map +1 -1
- package/dist/components/PageHeader/index.js +4 -1
- package/dist/components/Resizable/ResizeHandle.d.ts +3 -0
- package/dist/components/Resizable/ResizeHandle.d.ts.map +1 -1
- package/dist/components/Resizable/ResizeHandle.js +8 -5
- package/dist/components/Sidebar/index.js +1 -1
- package/dist/components/SidebarSection/SectionItem.js +1 -1
- package/dist/components/TabBar/index.js +1 -1
- package/dist/components/VariableInput/index.d.ts +3 -0
- package/dist/components/VariableInput/index.d.ts.map +1 -1
- package/dist/components/VariableInput/index.js +29 -21
- package/dist/runtime/createBridgedPluginContext.js +76 -19
- package/dist/runtime/livePageHandle.d.ts +50 -0
- package/dist/runtime/{webpageHandle.js → livePageHandle.js} +125 -47
- package/dist/snippets.d.ts +34 -12
- package/dist/types.d.ts +463 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/runtime/webpageHandle.d.ts +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 1.5.1 - 2026-08-02
|
|
8
|
+
|
|
9
|
+
- feat(theme): add page header color variables and update related styles. (`577ab426`)
|
|
10
|
+
- feat(theme): introduce header color variable and update related styles. (`6b6637e3`)
|
|
11
|
+
- feat(VariableInput): portal tooltip to document body for improved positioning. (`65430293`)
|
|
12
|
+
|
|
13
|
+
## 1.5.0 - 2026-08-01
|
|
14
|
+
|
|
15
|
+
- feat(live-page): introduce live page functionality and refactor related components. (`16f1f304`)
|
|
16
|
+
|
|
7
17
|
## 1.4.5 - 2026-08-01
|
|
8
18
|
|
|
9
19
|
- feat(websites): implement Add Live Page modal and import functionality. (`1bdbf8a4`)
|
|
@@ -22,6 +22,9 @@ interface Props extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
|
|
|
22
22
|
* Full-bleed page header with a bordered bottom edge, title block on the left,
|
|
23
23
|
* and optional action controls on the right. Sticks to the top of its nearest
|
|
24
24
|
* scroll ancestor so title and actions stay visible while page content scrolls.
|
|
25
|
+
*
|
|
26
|
+
* Colors come from the `page-header`, `page-header-text`, and `page-header-muted`
|
|
27
|
+
* theme tokens (`--mac-page-header*`).
|
|
25
28
|
*/
|
|
26
29
|
export declare function PageHeader({ title, description, icon, children, className, ...props }: Props): JSX.Element;
|
|
27
30
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/PageHeader/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,KAAM,SAAQ,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACvE;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/PageHeader/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAItE,UAAU,KAAM,SAAQ,IAAI,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACvE;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,IAAI,CAAC,EAAE,cAAc,CAAC;IAEtB;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,IAAI,EACJ,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CA+BrB"}
|
|
@@ -5,7 +5,10 @@ import { cn } from '../utils.js';
|
|
|
5
5
|
* Full-bleed page header with a bordered bottom edge, title block on the left,
|
|
6
6
|
* and optional action controls on the right. Sticks to the top of its nearest
|
|
7
7
|
* scroll ancestor so title and actions stay visible while page content scrolls.
|
|
8
|
+
*
|
|
9
|
+
* Colors come from the `page-header`, `page-header-text`, and `page-header-muted`
|
|
10
|
+
* theme tokens (`--mac-page-header*`).
|
|
8
11
|
*/
|
|
9
12
|
export function PageHeader({ title, description, icon, children, className, ...props }) {
|
|
10
|
-
return (_jsxs("div", { ...props, className: cn('hc-page-header sticky top-0 z-10 -mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator bg-
|
|
13
|
+
return (_jsxs("div", { ...props, className: cn('hc-page-header sticky top-0 z-10 -mx-6 mb-4 flex flex-wrap items-center gap-2 border-b border-separator bg-page-header px-6 py-4', className), children: [_jsxs("div", { className: "hc-page-header-content min-w-0 flex-1", children: [_jsxs("h2", { className: "hc-page-header-title m-0 flex items-center gap-2 text-[22px] leading-[1.15] font-bold tracking-[-0.01em] text-page-header-text", children: [icon ? (_jsx(FaIcon, { icon: icon, className: "hc-page-header-title-icon h-4 w-4 shrink-0 text-page-header-muted", "aria-hidden": true })) : null, title] }), description ? (_jsx("p", { className: "hc-page-header-description m-0 mt-1 leading-6 text-page-header-muted", children: description })) : null] }), children ? (_jsx("div", { className: "hc-page-header-actions flex flex-wrap items-center gap-2", children: children })) : null] }));
|
|
11
14
|
}
|
|
@@ -36,6 +36,9 @@ interface Props extends Omit<ComponentPropsWithoutRef<'div'>, 'children' | 'aria
|
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Draggable separator handle for resizable panels.
|
|
39
|
+
*
|
|
40
|
+
* Track and edge colors use the `resize-separator` theme token; the inner grip
|
|
41
|
+
* uses `resize-handle` (`--mac-resize-separator` / `--mac-resize-handle`).
|
|
39
42
|
*/
|
|
40
43
|
export declare function ResizeHandle({ orientation, value, min, max, onResizeStart, onKeyboardResize, ariaLabel, formatValueText, className, ...props }: Props): JSX.Element;
|
|
41
44
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResizeHandle.d.ts","sourceRoot":"","sources":["../../../src/components/Resizable/ResizeHandle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,GAAG,EACH,aAAa,IAAI,kBAAkB,EACnC,UAAU,IAAI,eAAe,EAC9B,MAAM,OAAO,CAAC;AAGf,UAAU,KAAM,SAAQ,IAAI,CAC1B,wBAAwB,CAAC,KAAK,CAAC,EAC/B,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,CACnG;IACC;;OAEG;IACH,WAAW,EAAE,YAAY,GAAG,UAAU,CAAC;IAEvC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAEhD;;OAEG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAEtD;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,GAAG,UAAU,KAAK,MAAM,CAAC;CACrF;AAED
|
|
1
|
+
{"version":3,"file":"ResizeHandle.d.ts","sourceRoot":"","sources":["../../../src/components/Resizable/ResizeHandle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,GAAG,EACH,aAAa,IAAI,kBAAkB,EACnC,UAAU,IAAI,eAAe,EAC9B,MAAM,OAAO,CAAC;AAGf,UAAU,KAAM,SAAQ,IAAI,CAC1B,wBAAwB,CAAC,KAAK,CAAC,EAC/B,UAAU,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,CACnG;IACC;;OAEG;IACH,WAAW,EAAE,YAAY,GAAG,UAAU,CAAC;IAEvC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;IAEhD;;OAEG;IACH,gBAAgB,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAEtD;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,GAAG,UAAU,KAAK,MAAM,CAAC;CACrF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,KAAK,EACL,GAAG,EACH,GAAG,EACH,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,eAAe,EACf,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAsCrB"}
|
|
@@ -2,6 +2,9 @@ import { jsx as _jsx } from "@harborclient/sdk/jsx-runtime";
|
|
|
2
2
|
import { cn } from '../utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* Draggable separator handle for resizable panels.
|
|
5
|
+
*
|
|
6
|
+
* Track and edge colors use the `resize-separator` theme token; the inner grip
|
|
7
|
+
* uses `resize-handle` (`--mac-resize-separator` / `--mac-resize-handle`).
|
|
5
8
|
*/
|
|
6
9
|
export function ResizeHandle({ orientation, value, min, max, onResizeStart, onKeyboardResize, ariaLabel, formatValueText, className, ...props }) {
|
|
7
10
|
const isHorizontal = orientation === 'horizontal';
|
|
@@ -9,9 +12,9 @@ export function ResizeHandle({ orientation, value, min, max, onResizeStart, onKe
|
|
|
9
12
|
const valueText = formatValueText
|
|
10
13
|
? formatValueText(value, orientation)
|
|
11
14
|
: `${dimension} ${Math.round(value)} pixels`;
|
|
12
|
-
return (_jsx("div", { ...props, role: "separator", tabIndex: 0, className: cn('hc-resize-handle font-inherit app-no-drag m-0 flex shrink-0 items-center justify-center bg-
|
|
13
|
-
? 'h-1.5 w-full cursor-row-resize border-b border-separator'
|
|
14
|
-
: 'h-full w-1.5 cursor-col-resize border-r border-separator', className), onMouseDown: onResizeStart, onKeyDown: onKeyboardResize, "aria-orientation": orientation, "aria-label": ariaLabel, "aria-valuenow": Math.round(value), "aria-valuemin": min, "aria-valuemax": Math.round(max), "aria-valuetext": valueText, children: _jsx("div", { className: isHorizontal
|
|
15
|
-
? 'hc-resize-handle-grip h-0.5 w-8 rounded-full bg-
|
|
16
|
-
: 'hc-resize-handle-grip h-8 w-0.5 rounded-full bg-
|
|
15
|
+
return (_jsx("div", { ...props, role: "separator", tabIndex: 0, className: cn('hc-resize-handle font-inherit app-no-drag m-0 flex shrink-0 items-center justify-center bg-resize-separator p-0 text-inherit hover:bg-selection/60', isHorizontal
|
|
16
|
+
? 'h-1.5 w-full cursor-row-resize border-b border-resize-separator'
|
|
17
|
+
: 'h-full w-1.5 cursor-col-resize border-r border-resize-separator', className), onMouseDown: onResizeStart, onKeyDown: onKeyboardResize, "aria-orientation": orientation, "aria-label": ariaLabel, "aria-valuenow": Math.round(value), "aria-valuemin": min, "aria-valuemax": Math.round(max), "aria-valuetext": valueText, children: _jsx("div", { className: isHorizontal
|
|
18
|
+
? 'hc-resize-handle-grip h-0.5 w-8 rounded-full bg-resize-handle'
|
|
19
|
+
: 'hc-resize-handle-grip h-8 w-0.5 rounded-full bg-resize-handle', "aria-hidden": true }) }));
|
|
17
20
|
}
|
|
@@ -17,7 +17,7 @@ export function Sidebar({ side, ariaLabel, header, children, scroll = true, scro
|
|
|
17
17
|
storageKey
|
|
18
18
|
};
|
|
19
19
|
const { size: width, minSize: sidebarMinSize, maxSize: sidebarMaxSize, onResizeStart, onKeyboardResize } = useResizable(resizableOptions);
|
|
20
|
-
const handle = (_jsx(ResizeHandle, { orientation: "vertical", value: width, min: sidebarMinSize, max: sidebarMaxSize, onResizeStart: onResizeStart, onKeyboardResize: onKeyboardResize, ariaLabel: resizeAriaLabel ?? 'Resize sidebar', className: cn(side === 'right' && 'border-r-0 border-l border-separator', resizeHandleClassName) }));
|
|
20
|
+
const handle = (_jsx(ResizeHandle, { orientation: "vertical", value: width, min: sidebarMinSize, max: sidebarMaxSize, onResizeStart: onResizeStart, onKeyboardResize: onKeyboardResize, ariaLabel: resizeAriaLabel ?? 'Resize sidebar', className: cn(side === 'right' && 'border-r-0 border-l border-resize-separator', resizeHandleClassName) }));
|
|
21
21
|
/**
|
|
22
22
|
* Stretch wrapper so short body content (e.g. centered empty states) fills the
|
|
23
23
|
* scroll viewport height while still allowing overflow scroll when content grows.
|
|
@@ -15,7 +15,7 @@ const SectionItem = memo(function SectionItem({ forwardedRef, itemRef, state, to
|
|
|
15
15
|
const [transitionStyle, panelRef] = useHeightTransition(state);
|
|
16
16
|
const itemElementRef = useMergeRef(forwardedRef, itemRef);
|
|
17
17
|
const { status, isMounted, isEnter } = state;
|
|
18
|
-
return (_jsxs("div", { ref: itemElementRef, className: "mb-1", children: [_jsxs("div", { className: `hc-sidebar-section-header flex min-h-8 items-center justify-between gap-2 border-
|
|
18
|
+
return (_jsxs("div", { ref: itemElementRef, className: "mb-1", children: [_jsxs("div", { className: `hc-sidebar-section-header flex min-h-8 items-center justify-between gap-2 border-b border-sidebar-rail-separator bg-sidebar-section py-1 pl-0.5 ${flushBody ? 'mb-0' : 'mb-1'}`, children: [_jsxs("button", { ...buttonProps, type: "button", className: "app-no-drag inline-flex min-w-0 flex-1 cursor-pointer items-center gap-2.5 border-none bg-transparent p-0 text-left", children: [_jsx("span", { className: SIDEBAR_CHEVRON_SLOT_CLASS, children: _jsx(FaIcon, { icon: isEnter ? faChevronDown : faChevronRight, className: `${SIDEBAR_CHEVRON_ICON_CLASS} text-sidebar-section-text` }) }), _jsx("h2", { className: "m-0 text-[15px] leading-none font-medium tracking-wide text-sidebar-section-text uppercase", children: title })] }), (headerActions || onAdd) && (_jsxs("div", { className: "hc-sidebar-section-header-actions flex shrink-0 items-center gap-1", children: [headerActions, onAdd ? (_jsx("button", { type: "button", className: "hc-sidebar-add-button app-no-drag inline-flex shrink-0 cursor-pointer items-center justify-center border-none bg-transparent text-muted hover:bg-selection hover:text-text focus-visible:bg-selection focus-visible:text-text", title: addLabel ?? 'Add', "aria-label": addLabel ?? 'Add', onClick: onAdd, children: _jsx(FaIcon, { icon: faPlus, className: "h-3.5 w-3.5" }) })) : null] }))] }), isMounted ? (_jsx("div", { style: {
|
|
19
19
|
display: status === 'exited' ? 'none' : undefined,
|
|
20
20
|
...transitionStyle
|
|
21
21
|
}, className: "transition-[height] duration-200 ease-out motion-reduce:transition-none", children: _jsx("div", { ...panelProps, ref: panelRef, children: children }) })) : null] }));
|
|
@@ -257,7 +257,7 @@ export function TabBar({ tabs, activeId, wrap, ariaLabel, tabIdPrefix, panelIdPr
|
|
|
257
257
|
}) }, exitingTab.exitKey)))] }), wrap ? newTabButton : null] }), _jsx(DragOverlay, { children: activeDragTab ? (_jsx("div", { className: "rounded-t-lg border border-separator bg-surface px-3 py-2 text-[14px] font-medium shadow-md", children: activeDragTab.dragLabel })) : null })] }), wrap ? null : newTabButton] }));
|
|
258
258
|
const defaultScrollContainer = (row) => (_jsx("div", { className: "min-w-0 flex-1 overflow-x-auto", children: row }));
|
|
259
259
|
const scrollContainer = renderScrollContainer ?? defaultScrollContainer;
|
|
260
|
-
const containerClassName = cn('app-no-drag flex shrink-0 items-end bg-
|
|
260
|
+
const containerClassName = cn('app-no-drag flex shrink-0 items-end bg-header pr-2 pl-0', className);
|
|
261
261
|
return (_jsxs("div", { className: containerClassName, children: [wrap ? _jsx("div", { className: "min-w-0 flex-1", children: tabRow }) : scrollContainer(tabRow), contextMenu != null && buildContextMenuGroups != null && (_jsx(TabContextMenu, { groups: contextMenuGroups, position: { x: contextMenu.x, y: contextMenu.y }, onClose: closeContextMenu }))] }));
|
|
262
262
|
}
|
|
263
263
|
export { TabNewButton } from './TabNewButton.js';
|
|
@@ -78,6 +78,9 @@ export interface Props extends Omit<ComponentPropsWithoutRef<'div'>, 'children'
|
|
|
78
78
|
/**
|
|
79
79
|
* Text input that highlights {{variable}} tokens and shows resolved values on hover.
|
|
80
80
|
*
|
|
81
|
+
* The variable tooltip is portaled to `document.body` so fixed positioning is not
|
|
82
|
+
* trapped under host chrome (for example request tabs above a scroll container).
|
|
83
|
+
*
|
|
81
84
|
* Token highlight color (`text-[#32D2E2]`) and the tooltip `app-no-drag` class rely on
|
|
82
85
|
* host styling in HarborClient `styles.css`.
|
|
83
86
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AACtF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/VariableInput/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AACtF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAS/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAyBnE,MAAM,WAAW,KAAM,SAAQ,IAAI,CACjC,wBAAwB,CAAC,KAAK,CAAC,EAC/B,UAAU,GAAG,WAAW,GAAG,IAAI,GAAG,YAAY,GAAG,iBAAiB,GAAG,WAAW,GAAG,UAAU,CAC9F;IACC;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC;;OAEG;IACH,SAAS,EAAE,QAAQ,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,KAAK,IAAI,CAAC;IAEzD;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAEvC;;OAEG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAE5B;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IAElD;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,SAAS,EACT,WAAW,EACX,SAAS,EACT,SAAc,EACd,gBAAgB,EAChB,cAAc,EACd,EAAE,EACF,YAAY,EAAE,SAAS,EACvB,iBAAiB,EAAE,cAAc,EACjC,MAAM,EACN,qBAAqB,EACrB,uBAAuB,EACvB,kBAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,GAAG,GAAG,CAAC,OAAO,CAsnBrB"}
|
|
@@ -6,6 +6,7 @@ import { useAutocomplete } from '../Autocomplete/useAutocomplete.js';
|
|
|
6
6
|
import { Button } from '../Button/index.js';
|
|
7
7
|
import { VariableTooltipValue } from '../VariableTooltip/index.js';
|
|
8
8
|
import { Input } from '../forms/index.js';
|
|
9
|
+
import { portalToBody } from '../portalToBody.js';
|
|
9
10
|
import { getFocusableElements } from '../useDialogFocus.js';
|
|
10
11
|
import { cn } from '../utils.js';
|
|
11
12
|
/** Delay after the pointer stops moving before a hover tooltip is shown. */
|
|
@@ -15,6 +16,9 @@ const TOOLTIP_HIDE_DELAY_MS = 400;
|
|
|
15
16
|
/**
|
|
16
17
|
* Text input that highlights {{variable}} tokens and shows resolved values on hover.
|
|
17
18
|
*
|
|
19
|
+
* The variable tooltip is portaled to `document.body` so fixed positioning is not
|
|
20
|
+
* trapped under host chrome (for example request tabs above a scroll container).
|
|
21
|
+
*
|
|
18
22
|
* Token highlight color (`text-[#32D2E2]`) and the tooltip `app-no-drag` class rely on
|
|
19
23
|
* host styling in HarborClient `styles.css`.
|
|
20
24
|
*/
|
|
@@ -368,7 +372,7 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
368
372
|
scheduleHide();
|
|
369
373
|
};
|
|
370
374
|
/**
|
|
371
|
-
* Closes any open tooltip when focus leaves the field.
|
|
375
|
+
* Closes any open tooltip when focus leaves the field and its portaled tooltip.
|
|
372
376
|
*/
|
|
373
377
|
const handleWrapperBlur = () => {
|
|
374
378
|
const wrapper = wrapperRef.current;
|
|
@@ -376,11 +380,13 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
376
380
|
return;
|
|
377
381
|
}
|
|
378
382
|
queueMicrotask(() => {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
dismissTooltip();
|
|
383
|
+
const active = document.activeElement;
|
|
384
|
+
if (wrapper.contains(active) || tooltipRef.current?.contains(active)) {
|
|
385
|
+
return;
|
|
383
386
|
}
|
|
387
|
+
cancelHide();
|
|
388
|
+
cancelShow();
|
|
389
|
+
dismissTooltip();
|
|
384
390
|
});
|
|
385
391
|
};
|
|
386
392
|
/**
|
|
@@ -427,20 +433,22 @@ export function VariableInput({ value, onChange, variables, placeholder, onKeyDo
|
|
|
427
433
|
dismissTooltip();
|
|
428
434
|
}
|
|
429
435
|
openAutocomplete();
|
|
430
|
-
}, onBlur: closeAutocomplete, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onSelect: updateTooltipFromCaret, onClick: updateTooltipFromCaret, onScroll: syncScroll, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave }), source && (_jsx(SuggestionList, { open: autocompleteOpen, items: autocompleteItems, activeIndex: autocompleteActiveIndex, anchorRef: inputRef, listboxId: listboxId, onSelect: selectItem, onActiveIndexChange: setActiveIndex, onClose: closeSuggestions })), tooltip && tooltipContent
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
onEditVariable(tooltip.key)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
436
|
+
}, onBlur: closeAutocomplete, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp, onSelect: updateTooltipFromCaret, onClick: updateTooltipFromCaret, onScroll: syncScroll, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave }), source && (_jsx(SuggestionList, { open: autocompleteOpen, items: autocompleteItems, activeIndex: autocompleteActiveIndex, anchorRef: inputRef, listboxId: listboxId, onSelect: selectItem, onActiveIndexChange: setActiveIndex, onClose: closeSuggestions })), tooltip && tooltipContent
|
|
437
|
+
? portalToBody(_jsxs("div", { ref: tooltipRef, id: tooltipId, role: "tooltip", className: "hc-variable-input-tooltip pointer-events-auto fixed z-50 flex max-w-sm -translate-x-1/2 -translate-y-full flex-col gap-2 rounded-lg border border-separator bg-surface px-4 py-3 text-text shadow-md after:pointer-events-auto after:absolute after:right-0 after:-bottom-2 after:left-0 after:h-2 after:content-['']", style: { position: 'fixed', top: tooltip.top - 4, left: tooltip.left }, onMouseEnter: cancelHide, onMouseLeave: scheduleHide, children: [_jsx(VariableTooltipValue, { value: tooltipContent.text, variableKey: tooltip.key, muted: tooltipContent.muted, onClose: () => {
|
|
438
|
+
if (tooltipSource === 'focus') {
|
|
439
|
+
dismissFocusTooltip(true);
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
dismissTooltip();
|
|
443
|
+
}
|
|
444
|
+
} }), onEditVariable && (_jsx(Button, { variant: "secondary", className: "hc-variable-input-tooltip-edit self-start", "aria-label": `Edit value for ${tooltip.key}`, onClick: () => {
|
|
445
|
+
onEditVariable(tooltip.key);
|
|
446
|
+
if (tooltipSource === 'focus') {
|
|
447
|
+
dismissFocusTooltip(true);
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
dismissTooltip();
|
|
451
|
+
}
|
|
452
|
+
}, children: "Edit value" }))] }))
|
|
453
|
+
: null] }));
|
|
446
454
|
}
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
registerContributionHeaderActions
|
|
6
6
|
} from './contributionRegistry.js';
|
|
7
7
|
import { bridgeInvoke, bridgeOn } from './hcBridge.js';
|
|
8
|
+
import { openLivePage } from './livePageHandle.js';
|
|
8
9
|
import { createPluginDatabaseApi } from './pluginDatabaseApi.js';
|
|
9
10
|
import { setHostReact } from './reactHost.js';
|
|
10
|
-
import { openWebpage } from './webpageHandle.js';
|
|
11
11
|
|
|
12
12
|
/** @type {Map<string, Set<(...args: unknown[]) => void | Promise<void>>>} */
|
|
13
13
|
const commandHandlers = new Map();
|
|
@@ -354,7 +354,7 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
354
354
|
const assertAi = () => assertPermission('ai');
|
|
355
355
|
|
|
356
356
|
/**
|
|
357
|
-
* Asserts browser permission for embedded
|
|
357
|
+
* Asserts browser permission for embedded live page control.
|
|
358
358
|
*/
|
|
359
359
|
const assertBrowser = () => assertPermission('browser');
|
|
360
360
|
|
|
@@ -364,43 +364,56 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
364
364
|
const assertLiveServer = () => assertPermission('live-server');
|
|
365
365
|
|
|
366
366
|
/**
|
|
367
|
-
*
|
|
367
|
+
* Asserts live-pages permission for saved live page (website) APIs.
|
|
368
|
+
*/
|
|
369
|
+
const assertLivePages = () => assertPermission('live-pages');
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Invokes a livePage session op on the host renderer via the plugin bridge.
|
|
368
373
|
*
|
|
369
|
-
* @param {Record<string, unknown>} req -
|
|
374
|
+
* @param {Record<string, unknown>} req - ScriptLivePageRequest-shaped payload.
|
|
370
375
|
* @returns {Promise<unknown>} Host session result.
|
|
371
376
|
*/
|
|
372
|
-
const
|
|
377
|
+
const callLivePage = async (req) => {
|
|
373
378
|
const op = String(req.op ?? '');
|
|
374
379
|
switch (op) {
|
|
375
380
|
case 'open':
|
|
376
|
-
return bridgeInvoke('
|
|
381
|
+
return bridgeInvoke('livePage.open', { url: req.url, reuse: req.reuse });
|
|
377
382
|
case 'focus':
|
|
378
|
-
return bridgeInvoke('
|
|
383
|
+
return bridgeInvoke('livePage.focus', { tabId: req.tabId });
|
|
379
384
|
case 'close':
|
|
380
|
-
return bridgeInvoke('
|
|
385
|
+
return bridgeInvoke('livePage.close', { tabId: req.tabId });
|
|
381
386
|
case 'query':
|
|
382
|
-
return bridgeInvoke('
|
|
387
|
+
return bridgeInvoke('livePage.query', {
|
|
383
388
|
tabId: req.tabId,
|
|
384
389
|
selector: req.selector,
|
|
385
390
|
all: req.all,
|
|
386
391
|
maxElements: req.maxElements
|
|
387
392
|
});
|
|
388
393
|
case 'evaluate':
|
|
389
|
-
return bridgeInvoke('
|
|
394
|
+
return bridgeInvoke('livePage.evaluate', {
|
|
390
395
|
tabId: req.tabId,
|
|
391
396
|
expression: req.expression
|
|
392
397
|
});
|
|
393
398
|
case 'injectScript':
|
|
394
|
-
return bridgeInvoke('
|
|
399
|
+
return bridgeInvoke('livePage.injectScript', { tabId: req.tabId, source: req.source });
|
|
395
400
|
case 'injectStylesheet':
|
|
396
|
-
return bridgeInvoke('
|
|
401
|
+
return bridgeInvoke('livePage.injectStylesheet', { tabId: req.tabId, css: req.css });
|
|
397
402
|
case 'screenshot':
|
|
398
|
-
return bridgeInvoke('
|
|
403
|
+
return bridgeInvoke('livePage.screenshot', {
|
|
399
404
|
tabId: req.tabId,
|
|
400
405
|
fullPage: req.fullPage === true
|
|
401
406
|
});
|
|
407
|
+
case 'goBack':
|
|
408
|
+
return bridgeInvoke('livePage.goBack', { tabId: req.tabId });
|
|
409
|
+
case 'goForward':
|
|
410
|
+
return bridgeInvoke('livePage.goForward', { tabId: req.tabId });
|
|
411
|
+
case 'reload':
|
|
412
|
+
return bridgeInvoke('livePage.reload', { tabId: req.tabId });
|
|
413
|
+
case 'navigate':
|
|
414
|
+
return bridgeInvoke('livePage.navigate', { tabId: req.tabId, url: req.url });
|
|
402
415
|
default:
|
|
403
|
-
throw new Error(`Unsupported
|
|
416
|
+
throw new Error(`Unsupported livePage bridge op: ${op}`);
|
|
404
417
|
}
|
|
405
418
|
};
|
|
406
419
|
|
|
@@ -860,6 +873,28 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
860
873
|
});
|
|
861
874
|
});
|
|
862
875
|
},
|
|
876
|
+
registerLivePageChromeAction: (action) => {
|
|
877
|
+
assertManifestContribution('livePageChromeActions', action.id);
|
|
878
|
+
if (!isAgent) {
|
|
879
|
+
return noopDisposable();
|
|
880
|
+
}
|
|
881
|
+
void bridgeInvoke('registerContribution', {
|
|
882
|
+
kind: 'livePageChromeActions',
|
|
883
|
+
contribution: {
|
|
884
|
+
pluginId,
|
|
885
|
+
id: action.id,
|
|
886
|
+
title: action.title,
|
|
887
|
+
command: action.command,
|
|
888
|
+
icon: action.icon
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
return track(() => {
|
|
892
|
+
void bridgeInvoke('unregisterContribution', {
|
|
893
|
+
kind: 'livePageChromeActions',
|
|
894
|
+
contributionId: action.id
|
|
895
|
+
});
|
|
896
|
+
});
|
|
897
|
+
},
|
|
863
898
|
registerScriptEditorAction: (action) => {
|
|
864
899
|
assertManifestContribution('scriptEditorActions', action.id);
|
|
865
900
|
if (!isAgent) {
|
|
@@ -1341,6 +1376,28 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
1341
1376
|
});
|
|
1342
1377
|
}
|
|
1343
1378
|
},
|
|
1379
|
+
livePages: {
|
|
1380
|
+
list: async () => {
|
|
1381
|
+
assertLivePages();
|
|
1382
|
+
return bridgeInvoke('livePages.list');
|
|
1383
|
+
},
|
|
1384
|
+
get: async (idOrUuid) => {
|
|
1385
|
+
assertLivePages();
|
|
1386
|
+
return bridgeInvoke('livePages.get', { idOrUuid });
|
|
1387
|
+
},
|
|
1388
|
+
create: async (input) => {
|
|
1389
|
+
assertLivePages();
|
|
1390
|
+
return bridgeInvoke('livePages.create', { input });
|
|
1391
|
+
},
|
|
1392
|
+
update: async (input) => {
|
|
1393
|
+
assertLivePages();
|
|
1394
|
+
return bridgeInvoke('livePages.update', { input });
|
|
1395
|
+
},
|
|
1396
|
+
delete: async (id) => {
|
|
1397
|
+
assertLivePages();
|
|
1398
|
+
await bridgeInvoke('livePages.delete', { id });
|
|
1399
|
+
}
|
|
1400
|
+
},
|
|
1344
1401
|
ai: {
|
|
1345
1402
|
registerChatPointer: (config) => {
|
|
1346
1403
|
assertAi();
|
|
@@ -1375,13 +1432,13 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
1375
1432
|
/**
|
|
1376
1433
|
* Opens or reuses an embedded browser tab and returns a control handle.
|
|
1377
1434
|
*
|
|
1378
|
-
* Requires the `browser` permission. Same semantics as request-script `hc.
|
|
1435
|
+
* Requires the `browser` permission. Same semantics as request-script `hc.livePage`.
|
|
1379
1436
|
*
|
|
1380
1437
|
* @param {string} [url] - Optional URL; omit to bind the active browser tab.
|
|
1381
1438
|
* @param {{ reuse?: boolean }} [options] - Optional `{ reuse }` (default true).
|
|
1382
|
-
* @returns {Promise<import('../types').
|
|
1439
|
+
* @returns {Promise<import('../types').PluginLivePageHandle>} Webpage handle.
|
|
1383
1440
|
*/
|
|
1384
|
-
|
|
1441
|
+
livePage: async (url, options) => {
|
|
1385
1442
|
assertBrowser();
|
|
1386
1443
|
/**
|
|
1387
1444
|
* Writes screenshot PNG bytes via the plugin filesystem bridge.
|
|
@@ -1394,11 +1451,11 @@ export function createBridgedPluginContext({ pluginId, mode, contributionId, rea
|
|
|
1394
1451
|
assertPermission('filesystem:write');
|
|
1395
1452
|
const result = await bridgeInvoke('fs.writeBytes', { path, base64: pngBase64 });
|
|
1396
1453
|
if (typeof result !== 'string' || !result.trim()) {
|
|
1397
|
-
throw new Error('hc.
|
|
1454
|
+
throw new Error('hc.livePage().screenshot failed to resolve write path');
|
|
1398
1455
|
}
|
|
1399
1456
|
return result;
|
|
1400
1457
|
};
|
|
1401
|
-
return
|
|
1458
|
+
return openLivePage(callLivePage, url, options, writeScreenshotBytes);
|
|
1402
1459
|
}
|
|
1403
1460
|
};
|
|
1404
1461
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PluginLivePageHandle } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Throws when a live-page bridge result is an `{ error }` object.
|
|
5
|
+
*
|
|
6
|
+
* @param result - Raw bridge result.
|
|
7
|
+
* @returns The result when it is not an error.
|
|
8
|
+
* @throws When the bridge returned `{ error: string }`.
|
|
9
|
+
*/
|
|
10
|
+
export function unwrapLivePageBridgeResult(result: unknown): unknown;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalizes the optional second argument to `hc.livePage(url, options)`.
|
|
14
|
+
*
|
|
15
|
+
* @param options - User-provided options.
|
|
16
|
+
* @returns Normalized open options.
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeLivePageOpenOptions(options?: unknown): { reuse?: boolean };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builds a live-page handle whose methods call the host live-page bridge.
|
|
22
|
+
*
|
|
23
|
+
* @param tab - Opened tab metadata from the bridge.
|
|
24
|
+
* @param callLivePage - Bridge transport.
|
|
25
|
+
* @returns Plain-object handle for the plugin world.
|
|
26
|
+
*/
|
|
27
|
+
export function createLivePageHandle(
|
|
28
|
+
tab: {
|
|
29
|
+
tabId: string;
|
|
30
|
+
url: string;
|
|
31
|
+
title: string;
|
|
32
|
+
canGoBack?: boolean;
|
|
33
|
+
canGoForward?: boolean;
|
|
34
|
+
},
|
|
35
|
+
callLivePage: (req: Record<string, unknown>) => Promise<unknown>
|
|
36
|
+
): PluginLivePageHandle;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Opens or reuses an embedded browser tab and returns a control handle.
|
|
40
|
+
*
|
|
41
|
+
* @param callLivePage - Bridge transport that accepts ScriptLivePageRequest-shaped payloads.
|
|
42
|
+
* @param url - Optional URL; omit to bind the active browser tab.
|
|
43
|
+
* @param openOptions - Optional `{ reuse }` (default true).
|
|
44
|
+
* @returns Live-page handle.
|
|
45
|
+
*/
|
|
46
|
+
export function openLivePage(
|
|
47
|
+
callLivePage: (req: Record<string, unknown>) => Promise<unknown>,
|
|
48
|
+
url?: unknown,
|
|
49
|
+
openOptions?: unknown
|
|
50
|
+
): Promise<PluginLivePageHandle>;
|