@hanzo/ui 8.27.14 → 8.27.16
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/NOTICE +19 -0
- package/dist/agents/Attachments.cjs +29 -0
- package/dist/agents/Attachments.d.ts +31 -0
- package/dist/agents/Attachments.d.ts.map +1 -0
- package/dist/agents/Attachments.js +27 -0
- package/dist/agents/Attachments.js.map +1 -0
- package/dist/agents/Choice.cjs +47 -0
- package/dist/agents/Choice.d.ts +34 -0
- package/dist/agents/Choice.d.ts.map +1 -0
- package/dist/agents/Choice.js +44 -0
- package/dist/agents/Choice.js.map +1 -0
- package/dist/agents/Console.cjs +106 -0
- package/dist/agents/Console.d.ts +54 -0
- package/dist/agents/Console.d.ts.map +1 -0
- package/dist/agents/Console.js +104 -0
- package/dist/agents/Console.js.map +1 -0
- package/dist/agents/Feedback.cjs +50 -0
- package/dist/agents/Feedback.d.ts +25 -0
- package/dist/agents/Feedback.d.ts.map +1 -0
- package/dist/agents/Feedback.js +48 -0
- package/dist/agents/Feedback.js.map +1 -0
- package/dist/agents/FileTabs.cjs +63 -0
- package/dist/agents/FileTabs.d.ts +45 -0
- package/dist/agents/FileTabs.d.ts.map +1 -0
- package/dist/agents/FileTabs.js +61 -0
- package/dist/agents/FileTabs.js.map +1 -0
- package/dist/agents/FileTree.cjs +160 -0
- package/dist/agents/FileTree.d.ts +41 -0
- package/dist/agents/FileTree.d.ts.map +1 -0
- package/dist/agents/FileTree.js +157 -0
- package/dist/agents/FileTree.js.map +1 -0
- package/dist/agents/PreviewFrame.cjs +108 -0
- package/dist/agents/PreviewFrame.d.ts +78 -0
- package/dist/agents/PreviewFrame.d.ts.map +1 -0
- package/dist/agents/PreviewFrame.js +105 -0
- package/dist/agents/PreviewFrame.js.map +1 -0
- package/dist/agents/Suggestions.cjs +29 -0
- package/dist/agents/Suggestions.d.ts +28 -0
- package/dist/agents/Suggestions.d.ts.map +1 -0
- package/dist/agents/Suggestions.js +26 -0
- package/dist/agents/Suggestions.js.map +1 -0
- package/dist/agents/Workspace.cjs +103 -0
- package/dist/agents/Workspace.d.ts +104 -0
- package/dist/agents/Workspace.d.ts.map +1 -0
- package/dist/agents/Workspace.js +98 -0
- package/dist/agents/Workspace.js.map +1 -0
- package/dist/agents/bridge.cjs +201 -0
- package/dist/agents/bridge.d.ts +102 -0
- package/dist/agents/bridge.d.ts.map +1 -0
- package/dist/agents/bridge.js +197 -0
- package/dist/agents/bridge.js.map +1 -0
- package/dist/agents/index.cjs +64 -1
- package/dist/agents/index.d.ts +23 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +23 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/log.cjs +75 -0
- package/dist/agents/log.d.ts +58 -0
- package/dist/agents/log.d.ts.map +1 -0
- package/dist/agents/log.js +65 -0
- package/dist/agents/log.js.map +1 -0
- package/dist/agents/tree.cjs +238 -0
- package/dist/agents/tree.d.ts +99 -0
- package/dist/agents/tree.d.ts.map +1 -0
- package/dist/agents/tree.js +226 -0
- package/dist/agents/tree.js.map +1 -0
- package/dist/backends/gui/3d-card.d.ts +6 -6
- package/dist/backends/gui/3d-marquee.d.ts +7 -8
- package/dist/backends/gui/3d-marquee.d.ts.map +1 -1
- package/dist/backends/gui/3d-pin.d.ts +1 -1
- package/dist/backends/gui/alert.d.ts +3 -3
- package/dist/backends/gui/android.d.ts +1 -1
- package/dist/backends/gui/animated-testimonials.d.ts +1 -1
- package/dist/backends/gui/animated-tooltip.d.ts +1 -1
- package/dist/backends/gui/announcement.d.ts +1 -1
- package/dist/backends/gui/banner.d.ts +1 -1
- package/dist/backends/gui/button.d.ts +1 -1
- package/dist/backends/gui/card.d.ts +7 -7
- package/dist/backends/gui/code-editor.cjs +2 -2
- package/dist/backends/gui/code-editor.d.ts +7 -1
- package/dist/backends/gui/code-editor.d.ts.map +1 -1
- package/dist/backends/gui/code-editor.js +2 -2
- package/dist/backends/gui/code-editor.js.map +1 -1
- package/dist/backends/gui/command.d.ts +5 -5
- package/dist/backends/gui/command.d.ts.map +1 -1
- package/dist/backends/gui/context-menu.d.ts +14 -14
- package/dist/backends/gui/context-menu.d.ts.map +1 -1
- package/dist/backends/gui/credit-card.d.ts +1 -1
- package/dist/backends/gui/desktop.d.ts +1 -1
- package/dist/backends/gui/dock.d.ts +3 -3
- package/dist/backends/gui/dropdown-menu.d.ts +16 -16
- package/dist/backends/gui/dropdown-menu.d.ts.map +1 -1
- package/dist/backends/gui/empty.d.ts +6 -6
- package/dist/backends/gui/field.d.ts +3 -3
- package/dist/backends/gui/gantt.d.ts +1 -1
- package/dist/backends/gui/grid-pattern.d.ts +1 -2
- package/dist/backends/gui/grid-pattern.d.ts.map +1 -1
- package/dist/backends/gui/image-zoom.d.ts +1 -1
- package/dist/backends/gui/iphone-15-pro.d.ts +1 -1
- package/dist/backends/gui/item.d.ts +9 -9
- package/dist/backends/gui/list.d.ts +2 -2
- package/dist/backends/gui/macos-dock.d.ts +2 -2
- package/dist/backends/gui/magnetic.d.ts +1 -1
- package/dist/backends/gui/marquee.d.ts +1 -15
- package/dist/backends/gui/marquee.d.ts.map +1 -1
- package/dist/backends/gui/menu-dock.d.ts +1 -1
- package/dist/backends/gui/menubar.d.ts +15 -15
- package/dist/backends/gui/menubar.d.ts.map +1 -1
- package/dist/backends/gui/message-dock.d.ts +1 -1
- package/dist/backends/gui/motion-highlight.d.ts +1 -12
- package/dist/backends/gui/motion-highlight.d.ts.map +1 -1
- package/dist/backends/gui/pagination.d.ts +4 -4
- package/dist/backends/gui/qr-code.d.ts +1 -2
- package/dist/backends/gui/qr-code.d.ts.map +1 -1
- package/dist/backends/gui/scroll-area.d.ts +3 -14
- package/dist/backends/gui/scroll-area.d.ts.map +1 -1
- package/dist/backends/gui/sheet.d.ts +3 -3
- package/dist/backends/gui/sheet.d.ts.map +1 -1
- package/dist/backends/gui/status.d.ts +1 -1
- package/dist/backends/gui/table.d.ts +8 -8
- package/dist/backends/gui/toaster.d.ts +1 -15
- package/dist/backends/gui/toaster.d.ts.map +1 -1
- package/dist/backends/gui/toggle.d.ts +1 -1
- package/dist/backends/gui/video-player.d.ts +1 -1
- package/dist/chat/Composer.cjs +60 -9
- package/dist/chat/Composer.d.ts +42 -2
- package/dist/chat/Composer.d.ts.map +1 -1
- package/dist/chat/Composer.js +61 -11
- package/dist/chat/Composer.js.map +1 -1
- package/dist/chat/EmptyPrompt.cjs +27 -0
- package/dist/chat/EmptyPrompt.d.ts +30 -0
- package/dist/chat/EmptyPrompt.d.ts.map +1 -0
- package/dist/chat/EmptyPrompt.js +24 -0
- package/dist/chat/EmptyPrompt.js.map +1 -0
- package/dist/chat/SessionRail.cjs +97 -0
- package/dist/chat/SessionRail.d.ts +97 -0
- package/dist/chat/SessionRail.d.ts.map +1 -0
- package/dist/chat/SessionRail.js +94 -0
- package/dist/chat/SessionRail.js.map +1 -0
- package/dist/chat/Thread.d.ts +2 -8
- package/dist/chat/Thread.d.ts.map +1 -1
- package/dist/chat/index.cjs +8 -1
- package/dist/chat/index.d.ts +3 -1
- package/dist/chat/index.d.ts.map +1 -1
- package/dist/chat/index.js +3 -1
- package/dist/chat/index.js.map +1 -1
- package/dist/gallery.cjs +28 -1
- package/dist/gallery.d.ts.map +1 -1
- package/dist/gallery.html +71 -3
- package/dist/gallery.js +30 -3
- package/dist/gallery.js.map +1 -1
- package/dist/product/BranchSelect.cjs +27 -0
- package/dist/product/BranchSelect.d.ts +27 -0
- package/dist/product/BranchSelect.d.ts.map +1 -0
- package/dist/product/BranchSelect.js +23 -0
- package/dist/product/BranchSelect.js.map +1 -0
- package/dist/product/ChipSelect.cjs +267 -0
- package/dist/product/ChipSelect.d.ts +71 -0
- package/dist/product/ChipSelect.d.ts.map +1 -0
- package/dist/product/ChipSelect.js +265 -0
- package/dist/product/ChipSelect.js.map +1 -0
- package/dist/product/RepoSelect.cjs +75 -0
- package/dist/product/RepoSelect.d.ts +63 -0
- package/dist/product/RepoSelect.d.ts.map +1 -0
- package/dist/product/RepoSelect.js +69 -0
- package/dist/product/RepoSelect.js.map +1 -0
- package/dist/product/chipSelect.logic.cjs +147 -0
- package/dist/product/chipSelect.logic.d.ts +92 -0
- package/dist/product/chipSelect.logic.d.ts.map +1 -0
- package/dist/product/chipSelect.logic.js +138 -0
- package/dist/product/chipSelect.logic.js.map +1 -0
- package/dist/product/index.cjs +23 -1
- package/dist/product/index.d.ts +4 -0
- package/dist/product/index.d.ts.map +1 -1
- package/dist/product/index.js +7 -0
- package/dist/product/index.js.map +1 -1
- package/dist/styles.css +68 -0
- package/package.json +2 -4
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.DEVICES = exports.CHAT = exports.VIEWS = exports.CONTROL = void 0;
|
|
5
|
+
exports.Workspace = Workspace;
|
|
6
|
+
exports.Views = Views;
|
|
7
|
+
exports.ProjectChip = ProjectChip;
|
|
8
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
9
|
+
/**
|
|
10
|
+
* Workspace — the builder's frame: one bar, the chat beside the work, the work,
|
|
11
|
+
* and a dock under it.
|
|
12
|
+
*
|
|
13
|
+
* Ported from build-v2 `components/editor/{index,header}.tsx` (MIT, derived from
|
|
14
|
+
* OSW Studio and DeepSite — see NOTICE). v2's editor owned its project, its
|
|
15
|
+
* pages, its preview frames and its fetches in one 875-line component; this is
|
|
16
|
+
* the SHAPE of that screen with every one of those handed back to the host.
|
|
17
|
+
* The bar is three slots, the chat column is a slot, the view is `children`,
|
|
18
|
+
* and the dock is a slot. Nothing here knows what a project is.
|
|
19
|
+
*
|
|
20
|
+
* ┌ start ──────────────┬──────── middle ─────────┬──────── end ┐
|
|
21
|
+
* │ chat (collapsible) │ children — the chosen view │
|
|
22
|
+
* │ │ │
|
|
23
|
+
* │ ├────────────────────────────────────────┤
|
|
24
|
+
* │ │ dock │
|
|
25
|
+
* └─────────────────────┴────────────────────────────────────────┘
|
|
26
|
+
*
|
|
27
|
+
* THE CHAT COLUMN COLLAPSES ON AN EXPLICIT TOGGLE. `collapsed` is controlled
|
|
28
|
+
* and the host persists it — the sidebar canon for every Hanzo surface: a
|
|
29
|
+
* column you ask to hide, never one that slides away when the pointer leaves.
|
|
30
|
+
*
|
|
31
|
+
* ONE PANE AT A TIME ON A PHONE. Below `md` there is room for the chat or the
|
|
32
|
+
* work, not both side by side, so `pane` chooses which shows. The host's view
|
|
33
|
+
* tabs carry a Chat entry that is only drawn there (`VIEWS` + `CHAT`), so the
|
|
34
|
+
* same control that switches views switches panes and there is no second
|
|
35
|
+
* switcher to find. Nothing in the frame is wider than the viewport at 390px:
|
|
36
|
+
* the middle of the bar yields and scrolls, and the ends never shrink.
|
|
37
|
+
*/
|
|
38
|
+
const gui_1 = require("@hanzo/gui");
|
|
39
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
40
|
+
const react_1 = require("react");
|
|
41
|
+
const button_1 = require("../backends/gui/button.cjs");
|
|
42
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
43
|
+
/** The bar's one control height. Every square in the bar measures this. */
|
|
44
|
+
exports.CONTROL = 32;
|
|
45
|
+
/** The chat column's width on a wide screen, px — v2's measured 360. */
|
|
46
|
+
const WIDTH = 360;
|
|
47
|
+
function Workspace({ start, middle, end, chat, children, dock, collapsed = false, pane = 'view', width = WIDTH, ...rest }) {
|
|
48
|
+
const chatShown = pane === 'chat';
|
|
49
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('workspace'), flex: 1, minW: 0, minH: 0, width: "100%", bg: "$background", overflow: "hidden", ...rest, children: [(0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('workspace-bar'), render: "header", items: "center", gap: "$2", px: "$3", minH: 52, shrink: 0, minW: 0, $md: { px: '$4', gap: '$3' }, children: [(0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", gap: "$1.5", minW: 0, shrink: 1, children: start }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { flex: 1, minW: 0, items: "center", justify: "center", gap: "$2", overflow: "hidden", children: (0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", gap: "$2", minW: 0, maxW: "100%", overflow: "scroll", children: middle }) }), (0, jsx_runtime_1.jsx)(gui_1.XStack, { items: "center", gap: "$2", shrink: 0, children: end })] }), (0, jsx_runtime_1.jsxs)(gui_1.XStack, { flex: 1, minH: 0, minW: 0, gap: 0, $md: { gap: '$3', pr: '$3', pb: '$3' }, children: [(0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('workspace-chat'), render: "section", "aria-label": "Chat", display: chatShown ? 'flex' : 'none', width: "100%", minH: 0, minW: 0, $md: { display: collapsed ? 'none' : 'flex', width, shrink: 0 }, children: chat }), (0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('workspace-main'), render: "main", display: chatShown ? 'none' : 'flex', flex: 1, minW: 0, minH: 0, $md: { display: 'flex' }, children: [(0, jsx_runtime_1.jsx)(gui_1.YStack, { flex: 1, minH: 0, minW: 0, children: children }), dock] })] })] }));
|
|
50
|
+
}
|
|
51
|
+
/** The builder's four views, as v2's pane list named and drew them. */
|
|
52
|
+
exports.VIEWS = [
|
|
53
|
+
{ id: 'preview', label: 'Preview', icon: lucide_icons_2_1.Globe },
|
|
54
|
+
{ id: 'files', label: 'Files', icon: lucide_icons_2_1.FileText },
|
|
55
|
+
{ id: 'code', label: 'Code', icon: lucide_icons_2_1.Code2 },
|
|
56
|
+
{ id: 'layers', label: 'Layers', icon: lucide_icons_2_1.Layers },
|
|
57
|
+
];
|
|
58
|
+
/** The phone's extra view: the chat itself, one pane at a time. */
|
|
59
|
+
exports.CHAT = { id: 'chat', label: 'Chat', icon: lucide_icons_2_1.MessageSquare, narrow: true };
|
|
60
|
+
/** The preview's two widths. */
|
|
61
|
+
exports.DEVICES = [
|
|
62
|
+
{ id: 'desktop', label: 'Desktop', icon: lucide_icons_2_1.Monitor },
|
|
63
|
+
{ id: 'mobile', label: 'Mobile', icon: lucide_icons_2_1.Smartphone },
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* A segmented control, as a WAI-ARIA tablist: one tab stop, arrows move the
|
|
67
|
+
* choice, Home/End jump. The chosen segment is the raised one and, by default,
|
|
68
|
+
* the only one wearing its label — v2's bar, where the place you are is the
|
|
69
|
+
* loud thing and every other place is a glyph.
|
|
70
|
+
*/
|
|
71
|
+
function Views({ views, value, onChange, label, labels = 'active', ...rest }) {
|
|
72
|
+
const refs = (0, react_1.useRef)([]);
|
|
73
|
+
const move = (e, index) => {
|
|
74
|
+
const last = views.length - 1;
|
|
75
|
+
const to = e.key === 'ArrowRight' ? (index === last ? 0 : index + 1)
|
|
76
|
+
: e.key === 'ArrowLeft' ? (index === 0 ? last : index - 1)
|
|
77
|
+
: e.key === 'Home' ? 0
|
|
78
|
+
: e.key === 'End' ? last
|
|
79
|
+
: -1;
|
|
80
|
+
if (to < 0)
|
|
81
|
+
return;
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
onChange(views[to].id);
|
|
84
|
+
refs.current[to]?.focus();
|
|
85
|
+
};
|
|
86
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('views'), role: "tablist", "aria-label": label, items: "center", gap: 2, p: 2, rounded: "$3", bg: "$hover", shrink: 0, ...rest, children: views.map((view, index) => {
|
|
87
|
+
const on = view.id === value;
|
|
88
|
+
const Icon = view.icon;
|
|
89
|
+
const words = labels === 'all' || (labels === 'active' && on) || !Icon;
|
|
90
|
+
return ((0, jsx_runtime_1.jsxs)(button_1.Button, { ref: (el) => {
|
|
91
|
+
refs.current[index] = el;
|
|
92
|
+
}, type: "button", variant: "ghost", size: words ? 'sm' : 'icon-sm', role: "tab", "aria-selected": on, "aria-label": view.label, title: view.label, tabIndex: on ? 0 : -1, onClick: () => onChange(view.id), onKeyDown: (e) => move(e, index), minHeight: exports.CONTROL - 4, minWidth: exports.CONTROL + 8, px: words ? '$2.5' : 0, bg: on ? '$raised' : 'transparent', color: on ? '$ink' : '$soft', hoverStyle: { bg: on ? '$raised' : '$hover' }, ...(view.narrow ? { $md: { display: 'none' } } : null), ...{ 'data-view': view.id }, children: [Icon ? (0, jsx_runtime_1.jsx)(Icon, { size: 15 }) : null, words ? ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: on ? '$ink' : '$soft', children: view.label })) : null] }, view.id));
|
|
93
|
+
}) }));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The project switcher's trigger: a square, the name, and the up-down chevron
|
|
97
|
+
* that says "there are others". A real button with a ref, so it can be the
|
|
98
|
+
* trigger of whatever menu or popover the host lists projects in.
|
|
99
|
+
*/
|
|
100
|
+
function ProjectChip({ name, mark, onPress, ref, ...rest }) {
|
|
101
|
+
const initial = name.trim().charAt(0).toUpperCase() || '·';
|
|
102
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ref: ref, ...(0, slot_1.slot)('project-chip'), render: "button", ...{ type: 'button', 'aria-haspopup': 'menu' }, "aria-label": `Project: ${name}`, onPress: onPress, items: "center", gap: "$2", height: exports.CONTROL + 4, minW: 0, shrink: 1, pl: "$1.5", pr: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$panel", cursor: "pointer", hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$rim' }, ...rest, children: [mark ?? ((0, jsx_runtime_1.jsx)(gui_1.YStack, { width: 22, height: 22, rounded: "$2", bg: "$raised", items: "center", justify: "center", shrink: 0, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", fontWeight: "600", color: "$ink", children: initial }) })), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, minW: 0, shrink: 1, children: name }), (0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronsUpDown, { size: 14, opacity: 0.6 })] }));
|
|
103
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace — the builder's frame: one bar, the chat beside the work, the work,
|
|
3
|
+
* and a dock under it.
|
|
4
|
+
*
|
|
5
|
+
* Ported from build-v2 `components/editor/{index,header}.tsx` (MIT, derived from
|
|
6
|
+
* OSW Studio and DeepSite — see NOTICE). v2's editor owned its project, its
|
|
7
|
+
* pages, its preview frames and its fetches in one 875-line component; this is
|
|
8
|
+
* the SHAPE of that screen with every one of those handed back to the host.
|
|
9
|
+
* The bar is three slots, the chat column is a slot, the view is `children`,
|
|
10
|
+
* and the dock is a slot. Nothing here knows what a project is.
|
|
11
|
+
*
|
|
12
|
+
* ┌ start ──────────────┬──────── middle ─────────┬──────── end ┐
|
|
13
|
+
* │ chat (collapsible) │ children — the chosen view │
|
|
14
|
+
* │ │ │
|
|
15
|
+
* │ ├────────────────────────────────────────┤
|
|
16
|
+
* │ │ dock │
|
|
17
|
+
* └─────────────────────┴────────────────────────────────────────┘
|
|
18
|
+
*
|
|
19
|
+
* THE CHAT COLUMN COLLAPSES ON AN EXPLICIT TOGGLE. `collapsed` is controlled
|
|
20
|
+
* and the host persists it — the sidebar canon for every Hanzo surface: a
|
|
21
|
+
* column you ask to hide, never one that slides away when the pointer leaves.
|
|
22
|
+
*
|
|
23
|
+
* ONE PANE AT A TIME ON A PHONE. Below `md` there is room for the chat or the
|
|
24
|
+
* work, not both side by side, so `pane` chooses which shows. The host's view
|
|
25
|
+
* tabs carry a Chat entry that is only drawn there (`VIEWS` + `CHAT`), so the
|
|
26
|
+
* same control that switches views switches panes and there is no second
|
|
27
|
+
* switcher to find. Nothing in the frame is wider than the viewport at 390px:
|
|
28
|
+
* the middle of the bar yields and scrolls, and the ends never shrink.
|
|
29
|
+
*/
|
|
30
|
+
import { XStack, YStack } from '@hanzo/gui';
|
|
31
|
+
import { type ComponentProps, type ComponentType, type ReactNode, type Ref } from 'react';
|
|
32
|
+
type Col = Omit<ComponentProps<typeof YStack>, 'children'>;
|
|
33
|
+
/** The bar's one control height. Every square in the bar measures this. */
|
|
34
|
+
export declare const CONTROL = 32;
|
|
35
|
+
export interface WorkspaceProps extends Col {
|
|
36
|
+
/** The bar's left cluster: the mark, the project, history, the chat toggle. */
|
|
37
|
+
start?: ReactNode;
|
|
38
|
+
/** The bar's middle: view tabs, device, reload, page, open. Yields first. */
|
|
39
|
+
middle?: ReactNode;
|
|
40
|
+
/** The bar's right cluster: Share, Publish. Never shrinks. */
|
|
41
|
+
end?: ReactNode;
|
|
42
|
+
/** The chat column — thread, suggestions, composer. */
|
|
43
|
+
chat?: ReactNode;
|
|
44
|
+
/** The chosen view: preview, files, code, layers. */
|
|
45
|
+
children?: ReactNode;
|
|
46
|
+
/** Under the view: the console. */
|
|
47
|
+
dock?: ReactNode;
|
|
48
|
+
/** The chat column is hidden on a wide screen. Controlled; the host persists it. */
|
|
49
|
+
collapsed?: boolean;
|
|
50
|
+
/** Below `md`, which pane shows. */
|
|
51
|
+
pane?: 'chat' | 'view';
|
|
52
|
+
/** The chat column's width on a wide screen, px. */
|
|
53
|
+
width?: number;
|
|
54
|
+
}
|
|
55
|
+
export declare function Workspace({ start, middle, end, chat, children, dock, collapsed, pane, width, ...rest }: WorkspaceProps): import("react").JSX.Element;
|
|
56
|
+
/** One entry of a segmented control. */
|
|
57
|
+
export interface View {
|
|
58
|
+
id: string;
|
|
59
|
+
label: string;
|
|
60
|
+
icon?: ComponentType<{
|
|
61
|
+
size?: number;
|
|
62
|
+
}>;
|
|
63
|
+
/** Drawn only below `md` — the phone's Chat entry. */
|
|
64
|
+
narrow?: boolean;
|
|
65
|
+
}
|
|
66
|
+
/** The builder's four views, as v2's pane list named and drew them. */
|
|
67
|
+
export declare const VIEWS: readonly View[];
|
|
68
|
+
/** The phone's extra view: the chat itself, one pane at a time. */
|
|
69
|
+
export declare const CHAT: View;
|
|
70
|
+
/** The preview's two widths. */
|
|
71
|
+
export declare const DEVICES: readonly View[];
|
|
72
|
+
export interface ViewsProps extends Omit<ComponentProps<typeof XStack>, 'children' | 'onChange'> {
|
|
73
|
+
views: readonly View[];
|
|
74
|
+
value: string;
|
|
75
|
+
onChange: (id: string) => void;
|
|
76
|
+
/** The group's accessible name: "Editor view", "Device". */
|
|
77
|
+
label: string;
|
|
78
|
+
/** Which segments carry their words: only the chosen one (v2), all, or none. */
|
|
79
|
+
labels?: 'active' | 'all' | 'none';
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* A segmented control, as a WAI-ARIA tablist: one tab stop, arrows move the
|
|
83
|
+
* choice, Home/End jump. The chosen segment is the raised one and, by default,
|
|
84
|
+
* the only one wearing its label — v2's bar, where the place you are is the
|
|
85
|
+
* loud thing and every other place is a glyph.
|
|
86
|
+
*/
|
|
87
|
+
export declare function Views({ views, value, onChange, label, labels, ...rest }: ViewsProps): import("react").JSX.Element;
|
|
88
|
+
export interface ProjectChipProps extends Omit<ComponentProps<typeof XStack>, 'children'> {
|
|
89
|
+
/** The project's name. */
|
|
90
|
+
name: string;
|
|
91
|
+
/** The square before the name; the name's initial when absent. */
|
|
92
|
+
mark?: ReactNode;
|
|
93
|
+
onPress?: () => void;
|
|
94
|
+
/** The button, for a menu or popover to anchor to. A plain prop (React 19). */
|
|
95
|
+
ref?: Ref<HTMLElement>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The project switcher's trigger: a square, the name, and the up-down chevron
|
|
99
|
+
* that says "there are others". A real button with a ref, so it can be the
|
|
100
|
+
* trigger of whatever menu or popover the host lists projects in.
|
|
101
|
+
*/
|
|
102
|
+
export declare function ProjectChip({ name, mark, onPress, ref, ...rest }: ProjectChipProps): import("react").JSX.Element;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=Workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Workspace.d.ts","sourceRoot":"","sources":["../../src/agents/Workspace.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Workspace — the builder's frame: one bar, the chat beside the work, the work,
|
|
5
|
+
* and a dock under it.
|
|
6
|
+
*
|
|
7
|
+
* Ported from build-v2 `components/editor/{index,header}.tsx` (MIT, derived from
|
|
8
|
+
* OSW Studio and DeepSite — see NOTICE). v2's editor owned its project, its
|
|
9
|
+
* pages, its preview frames and its fetches in one 875-line component; this is
|
|
10
|
+
* the SHAPE of that screen with every one of those handed back to the host.
|
|
11
|
+
* The bar is three slots, the chat column is a slot, the view is `children`,
|
|
12
|
+
* and the dock is a slot. Nothing here knows what a project is.
|
|
13
|
+
*
|
|
14
|
+
* ┌ start ──────────────┬──────── middle ─────────┬──────── end ┐
|
|
15
|
+
* │ chat (collapsible) │ children — the chosen view │
|
|
16
|
+
* │ │ │
|
|
17
|
+
* │ ├────────────────────────────────────────┤
|
|
18
|
+
* │ │ dock │
|
|
19
|
+
* └─────────────────────┴────────────────────────────────────────┘
|
|
20
|
+
*
|
|
21
|
+
* THE CHAT COLUMN COLLAPSES ON AN EXPLICIT TOGGLE. `collapsed` is controlled
|
|
22
|
+
* and the host persists it — the sidebar canon for every Hanzo surface: a
|
|
23
|
+
* column you ask to hide, never one that slides away when the pointer leaves.
|
|
24
|
+
*
|
|
25
|
+
* ONE PANE AT A TIME ON A PHONE. Below `md` there is room for the chat or the
|
|
26
|
+
* work, not both side by side, so `pane` chooses which shows. The host's view
|
|
27
|
+
* tabs carry a Chat entry that is only drawn there (`VIEWS` + `CHAT`), so the
|
|
28
|
+
* same control that switches views switches panes and there is no second
|
|
29
|
+
* switcher to find. Nothing in the frame is wider than the viewport at 390px:
|
|
30
|
+
* the middle of the bar yields and scrolls, and the ends never shrink.
|
|
31
|
+
*/
|
|
32
|
+
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
33
|
+
import { ChevronsUpDown, Code2, FileText, Globe, Layers, MessageSquare, Monitor, Smartphone, } from '@hanzogui/lucide-icons-2';
|
|
34
|
+
import { useRef, } from 'react';
|
|
35
|
+
import { Button } from '../backends/gui/button.js';
|
|
36
|
+
import { slot } from '../backends/gui/slot.js';
|
|
37
|
+
/** The bar's one control height. Every square in the bar measures this. */
|
|
38
|
+
export const CONTROL = 32;
|
|
39
|
+
/** The chat column's width on a wide screen, px — v2's measured 360. */
|
|
40
|
+
const WIDTH = 360;
|
|
41
|
+
export function Workspace({ start, middle, end, chat, children, dock, collapsed = false, pane = 'view', width = WIDTH, ...rest }) {
|
|
42
|
+
const chatShown = pane === 'chat';
|
|
43
|
+
return (_jsxs(YStack, { ...slot('workspace'), flex: 1, minW: 0, minH: 0, width: "100%", bg: "$background", overflow: "hidden", ...rest, children: [_jsxs(XStack, { ...slot('workspace-bar'), render: "header", items: "center", gap: "$2", px: "$3", minH: 52, shrink: 0, minW: 0, $md: { px: '$4', gap: '$3' }, children: [_jsx(XStack, { items: "center", gap: "$1.5", minW: 0, shrink: 1, children: start }), _jsx(XStack, { flex: 1, minW: 0, items: "center", justify: "center", gap: "$2", overflow: "hidden", children: _jsx(XStack, { items: "center", gap: "$2", minW: 0, maxW: "100%", overflow: "scroll", children: middle }) }), _jsx(XStack, { items: "center", gap: "$2", shrink: 0, children: end })] }), _jsxs(XStack, { flex: 1, minH: 0, minW: 0, gap: 0, $md: { gap: '$3', pr: '$3', pb: '$3' }, children: [_jsx(YStack, { ...slot('workspace-chat'), render: "section", "aria-label": "Chat", display: chatShown ? 'flex' : 'none', width: "100%", minH: 0, minW: 0, $md: { display: collapsed ? 'none' : 'flex', width, shrink: 0 }, children: chat }), _jsxs(YStack, { ...slot('workspace-main'), render: "main", display: chatShown ? 'none' : 'flex', flex: 1, minW: 0, minH: 0, $md: { display: 'flex' }, children: [_jsx(YStack, { flex: 1, minH: 0, minW: 0, children: children }), dock] })] })] }));
|
|
44
|
+
}
|
|
45
|
+
/** The builder's four views, as v2's pane list named and drew them. */
|
|
46
|
+
export const VIEWS = [
|
|
47
|
+
{ id: 'preview', label: 'Preview', icon: Globe },
|
|
48
|
+
{ id: 'files', label: 'Files', icon: FileText },
|
|
49
|
+
{ id: 'code', label: 'Code', icon: Code2 },
|
|
50
|
+
{ id: 'layers', label: 'Layers', icon: Layers },
|
|
51
|
+
];
|
|
52
|
+
/** The phone's extra view: the chat itself, one pane at a time. */
|
|
53
|
+
export const CHAT = { id: 'chat', label: 'Chat', icon: MessageSquare, narrow: true };
|
|
54
|
+
/** The preview's two widths. */
|
|
55
|
+
export const DEVICES = [
|
|
56
|
+
{ id: 'desktop', label: 'Desktop', icon: Monitor },
|
|
57
|
+
{ id: 'mobile', label: 'Mobile', icon: Smartphone },
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* A segmented control, as a WAI-ARIA tablist: one tab stop, arrows move the
|
|
61
|
+
* choice, Home/End jump. The chosen segment is the raised one and, by default,
|
|
62
|
+
* the only one wearing its label — v2's bar, where the place you are is the
|
|
63
|
+
* loud thing and every other place is a glyph.
|
|
64
|
+
*/
|
|
65
|
+
export function Views({ views, value, onChange, label, labels = 'active', ...rest }) {
|
|
66
|
+
const refs = useRef([]);
|
|
67
|
+
const move = (e, index) => {
|
|
68
|
+
const last = views.length - 1;
|
|
69
|
+
const to = e.key === 'ArrowRight' ? (index === last ? 0 : index + 1)
|
|
70
|
+
: e.key === 'ArrowLeft' ? (index === 0 ? last : index - 1)
|
|
71
|
+
: e.key === 'Home' ? 0
|
|
72
|
+
: e.key === 'End' ? last
|
|
73
|
+
: -1;
|
|
74
|
+
if (to < 0)
|
|
75
|
+
return;
|
|
76
|
+
e.preventDefault();
|
|
77
|
+
onChange(views[to].id);
|
|
78
|
+
refs.current[to]?.focus();
|
|
79
|
+
};
|
|
80
|
+
return (_jsx(XStack, { ...slot('views'), role: "tablist", "aria-label": label, items: "center", gap: 2, p: 2, rounded: "$3", bg: "$hover", shrink: 0, ...rest, children: views.map((view, index) => {
|
|
81
|
+
const on = view.id === value;
|
|
82
|
+
const Icon = view.icon;
|
|
83
|
+
const words = labels === 'all' || (labels === 'active' && on) || !Icon;
|
|
84
|
+
return (_jsxs(Button, { ref: (el) => {
|
|
85
|
+
refs.current[index] = el;
|
|
86
|
+
}, type: "button", variant: "ghost", size: words ? 'sm' : 'icon-sm', role: "tab", "aria-selected": on, "aria-label": view.label, title: view.label, tabIndex: on ? 0 : -1, onClick: () => onChange(view.id), onKeyDown: (e) => move(e, index), minHeight: CONTROL - 4, minWidth: CONTROL + 8, px: words ? '$2.5' : 0, bg: on ? '$raised' : 'transparent', color: on ? '$ink' : '$soft', hoverStyle: { bg: on ? '$raised' : '$hover' }, ...(view.narrow ? { $md: { display: 'none' } } : null), ...{ 'data-view': view.id }, children: [Icon ? _jsx(Icon, { size: 15 }) : null, words ? (_jsx(SizableText, { size: "$2", color: on ? '$ink' : '$soft', children: view.label })) : null] }, view.id));
|
|
87
|
+
}) }));
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The project switcher's trigger: a square, the name, and the up-down chevron
|
|
91
|
+
* that says "there are others". A real button with a ref, so it can be the
|
|
92
|
+
* trigger of whatever menu or popover the host lists projects in.
|
|
93
|
+
*/
|
|
94
|
+
export function ProjectChip({ name, mark, onPress, ref, ...rest }) {
|
|
95
|
+
const initial = name.trim().charAt(0).toUpperCase() || '·';
|
|
96
|
+
return (_jsxs(XStack, { ref: ref, ...slot('project-chip'), render: "button", ...{ type: 'button', 'aria-haspopup': 'menu' }, "aria-label": `Project: ${name}`, onPress: onPress, items: "center", gap: "$2", height: CONTROL + 4, minW: 0, shrink: 1, pl: "$1.5", pr: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$panel", cursor: "pointer", hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$rim' }, ...rest, children: [mark ?? (_jsx(YStack, { width: 22, height: 22, rounded: "$2", bg: "$raised", items: "center", justify: "center", shrink: 0, "aria-hidden": true, children: _jsx(SizableText, { size: "$1", fontWeight: "600", color: "$ink", children: initial }) })), _jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, minW: 0, shrink: 1, children: name }), _jsx(ChevronsUpDown, { size: 14, opacity: 0.6 })] }));
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=Workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Workspace.js","sourceRoot":"","sources":["../../src/agents/Workspace.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,EACL,cAAc,EACd,KAAK,EACL,QAAQ,EACR,KAAK,EACL,MAAM,EACN,aAAa,EACb,OAAO,EACP,UAAU,GACX,MAAM,0BAA0B,CAAA;AACjC,OAAO,EAML,MAAM,GACP,MAAM,OAAO,CAAA;AAEd,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAI3C,2EAA2E;AAC3E,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,CAAA;AAEzB,wEAAwE;AACxE,MAAM,KAAK,GAAG,GAAG,CAAA;AAuBjB,MAAM,UAAU,SAAS,CAAC,EACxB,KAAK,EACL,MAAM,EACN,GAAG,EACH,IAAI,EACJ,QAAQ,EACR,IAAI,EACJ,SAAS,GAAG,KAAK,EACjB,IAAI,GAAG,MAAM,EACb,KAAK,GAAG,KAAK,EACb,GAAG,IAAI,EACQ;IACf,MAAM,SAAS,GAAG,IAAI,KAAK,MAAM,CAAA;IACjC,OAAO,CACL,MAAC,MAAM,OACD,IAAI,CAAC,WAAW,CAAC,EACrB,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,KAAK,EAAC,MAAM,EACZ,EAAE,EAAC,aAAa,EAChB,QAAQ,EAAC,QAAQ,KACb,IAAI,aAER,MAAC,MAAM,OACD,IAAI,CAAC,eAAe,CAAC,EACzB,MAAM,EAAC,QAAQ,EACf,KAAK,EAAC,QAAQ,EACd,GAAG,EAAC,IAAI,EACR,EAAE,EAAC,IAAI,EACP,IAAI,EAAE,EAAE,EACR,MAAM,EAAE,CAAC,EACT,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,aAE5B,KAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,MAAM,EAAC,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,YACjD,KAAK,GACC,EACT,KAAC,MAAM,IACL,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,KAAK,EAAC,QAAQ,EACd,OAAO,EAAC,QAAQ,EAChB,GAAG,EAAC,IAAI,EACR,QAAQ,EAAC,QAAQ,YAKjB,KAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAC,MAAM,EAAC,QAAQ,EAAC,QAAQ,YACnE,MAAM,GACA,GACF,EACT,KAAC,MAAM,IAAC,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,IAAI,EAAC,MAAM,EAAE,CAAC,YACtC,GAAG,GACG,IACF,EAET,MAAC,MAAM,IAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,aAC/E,KAAC,MAAM,OACD,IAAI,CAAC,gBAAgB,CAAC,EAC1B,MAAM,EAAC,SAAS,gBACL,MAAM,EACjB,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EACpC,KAAK,EAAC,MAAM,EACZ,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,EAAE,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,YAE9D,IAAI,GACE,EACT,MAAC,MAAM,OACD,IAAI,CAAC,gBAAgB,CAAC,EAC1B,MAAM,EAAC,MAAM,EACb,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EACpC,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,EACP,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,aAExB,KAAC,MAAM,IAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,YAC9B,QAAQ,GACF,EACR,IAAI,IACE,IACF,IACF,CACV,CAAA;AACH,CAAC;AAWD,uEAAuE;AACvE,MAAM,CAAC,MAAM,KAAK,GAAoB;IACpC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;IAChD,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE;IAC/C,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE;IAC1C,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE;CAChD,CAAA;AAED,mEAAmE;AACnE,MAAM,CAAC,MAAM,IAAI,GAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;AAE1F,gCAAgC;AAChC,MAAM,CAAC,MAAM,OAAO,GAAoB;IACtC,EAAE,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE;IAClD,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE;CACpD,CAAA;AAYD;;;;;GAKG;AACH,MAAM,UAAU,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,EAAE,GAAG,IAAI,EAAc;IAC7F,MAAM,IAAI,GAAG,MAAM,CAAyB,EAAE,CAAC,CAAA;IAC/C,MAAM,IAAI,GAAG,CAAC,CAAgB,EAAE,KAAa,EAAE,EAAE;QAC/C,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;QAC7B,MAAM,EAAE,GACN,CAAC,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;gBAC1D,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;oBACtB,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI;wBACxB,CAAC,CAAC,CAAC,CAAC,CAAA;QACN,IAAI,EAAE,GAAG,CAAC;YAAE,OAAM;QAClB,CAAC,CAAC,cAAc,EAAE,CAAA;QAClB,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAE,CAAC,EAAE,CAAC,CAAA;QACvB,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAA;IAC3B,CAAC,CAAA;IACD,OAAO,CACL,KAAC,MAAM,OACD,IAAI,CAAC,OAAO,CAAC,EACjB,IAAI,EAAC,SAAS,gBACF,KAAK,EACjB,KAAK,EAAC,QAAQ,EACd,GAAG,EAAE,CAAC,EACN,CAAC,EAAE,CAAC,EACJ,OAAO,EAAC,IAAI,EACZ,EAAE,EAAC,QAAQ,EACX,MAAM,EAAE,CAAC,KACL,IAAI,YAEP,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACzB,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,KAAK,KAAK,CAAA;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAA;YACtB,MAAM,KAAK,GAAG,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,CAAA;YACtE,OAAO,CACL,MAAC,MAAM,IAEL,GAAG,EAAE,CAAC,EAAsB,EAAE,EAAE;oBAC9B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;gBAC1B,CAAC,EACD,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,OAAO,EACf,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAC9B,IAAI,EAAC,KAAK,mBACK,EAAE,gBACL,IAAI,CAAC,KAAK,EACtB,KAAK,EAAE,IAAI,CAAC,KAAK,EACjB,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EACrB,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,EAChC,SAAS,EAAE,CAAC,CAAgB,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,CAAC,EAC/C,SAAS,EAAE,OAAO,GAAG,CAAC,EACtB,QAAQ,EAAE,OAAO,GAAG,CAAC,EACrB,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EACtB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,EAClC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,EAC5B,UAAU,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,KACzC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAClD,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,EAAa,aAEvC,IAAI,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,IAAI,EAAE,EAAE,GAAI,CAAC,CAAC,CAAC,IAAI,EAChC,KAAK,CAAC,CAAC,CAAC,CACP,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,YAChD,IAAI,CAAC,KAAK,GACC,CACf,CAAC,CAAC,CAAC,IAAI,KA5BH,IAAI,CAAC,EAAE,CA6BL,CACV,CAAA;QACH,CAAC,CAAC,GACK,CACV,CAAA;AACH,CAAC;AAYD;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI,EAAoB;IACjF,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,GAAG,CAAA;IAC1D,OAAO,CACL,MAAC,MAAM,IACL,GAAG,EAAE,GAAY,KACb,IAAI,CAAC,cAAc,CAAC,EACxB,MAAM,EAAC,QAAQ,KACV,EAAE,IAAI,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAa,gBAC/C,YAAY,IAAI,EAAE,EAC9B,OAAO,EAAE,OAAO,EAChB,KAAK,EAAC,QAAQ,EACd,GAAG,EAAC,IAAI,EACR,MAAM,EAAE,OAAO,GAAG,CAAC,EACnB,IAAI,EAAE,CAAC,EACP,MAAM,EAAE,CAAC,EACT,EAAE,EAAC,MAAM,EACT,EAAE,EAAC,IAAI,EACP,OAAO,EAAC,IAAI,EACZ,WAAW,EAAE,CAAC,EACd,WAAW,EAAC,cAAc,EAC1B,EAAE,EAAC,QAAQ,EACX,MAAM,EAAC,SAAS,EAChB,UAAU,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAC5B,iBAAiB,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,KAC/E,IAAI,aAEP,IAAI,IAAI,CACP,KAAC,MAAM,IACL,KAAK,EAAE,EAAE,EACT,MAAM,EAAE,EAAE,EACV,OAAO,EAAC,IAAI,EACZ,EAAE,EAAC,SAAS,EACZ,KAAK,EAAC,QAAQ,EACd,OAAO,EAAC,QAAQ,EAChB,MAAM,EAAE,CAAC,iCAGT,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,KAAK,EAAC,MAAM,YACjD,OAAO,GACI,GACP,CACV,EACD,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,UAAU,EAAC,KAAK,EAAC,KAAK,EAAC,MAAM,EAAC,aAAa,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,YACtF,IAAI,GACO,EACd,KAAC,cAAc,IAAC,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,GAAI,IACnC,CACV,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.web = web;
|
|
4
|
+
exports.accept = accept;
|
|
5
|
+
exports.script = script;
|
|
6
|
+
/**
|
|
7
|
+
* bridge — the message protocol between a builder and the page it previews.
|
|
8
|
+
*
|
|
9
|
+
* Ported from build-v2 `components/editor/preview/bridge.ts` (MIT, derived from
|
|
10
|
+
* OSW Studio and DeepSite — see NOTICE). v2 wrote the preview document itself
|
|
11
|
+
* (`srcdoc`) and injected this script into it. A repository's deployed site is
|
|
12
|
+
* a page on ITS OWN origin that this package does not write, so nothing is
|
|
13
|
+
* injected: the bridge is OPT-IN. A page that wants element picking and
|
|
14
|
+
* console forwarding includes the script `script(parent)` returns — pinned to
|
|
15
|
+
* the builder's origin — and the frame speaks to it only across that pair of
|
|
16
|
+
* origins.
|
|
17
|
+
*
|
|
18
|
+
* The protocol, both directions, every message a plain object with `type`:
|
|
19
|
+
*
|
|
20
|
+
* page → builder preview:ready the bridge is listening
|
|
21
|
+
* preview:hover {selector, rect, tag} | {selector: null}
|
|
22
|
+
* preview:select {info} an element was picked
|
|
23
|
+
* preview:navigate {path} an in-site link was followed
|
|
24
|
+
* preview:console {level, text} the page's console, as text
|
|
25
|
+
* builder → page preview:editable {active} picking on/off
|
|
26
|
+
* preview:highlight {selector} outline one element
|
|
27
|
+
*
|
|
28
|
+
* What v2 also sent — live style and text edits — is not here. A change made
|
|
29
|
+
* inside the frame is gone on the next load; the builder's edits are runs that
|
|
30
|
+
* commit, and a control that edits something it cannot keep is a lie.
|
|
31
|
+
*
|
|
32
|
+
* Both sides check ORIGIN, never `'*'`: the page posts only to the builder's
|
|
33
|
+
* origin and reads only messages whose source is its parent and whose origin
|
|
34
|
+
* is that origin; the builder reads only messages whose source is the frame's
|
|
35
|
+
* window and whose origin is the frame's. `accept` is that check, in one place.
|
|
36
|
+
*/
|
|
37
|
+
const log_1 = require("./log.cjs");
|
|
38
|
+
const TYPES = new Set(['preview:ready', 'preview:hover', 'preview:select', 'preview:navigate', 'preview:console']);
|
|
39
|
+
const str = (v) => typeof v === 'string';
|
|
40
|
+
const num = (v) => typeof v === 'number' && Number.isFinite(v);
|
|
41
|
+
/** A control character or a line break: nothing a selector, a tag or a path carries. */
|
|
42
|
+
const CONTROL = /[\u0000-\u001f\u007f\u2028\u2029]/;
|
|
43
|
+
/**
|
|
44
|
+
* A selector in the one shape `script` writes — `body`, a plain `#id`, or a
|
|
45
|
+
* `body > tag:nth-of-type(n) > …` path — and nothing else. A page is free to
|
|
46
|
+
* post anything, and a free-text "selector" is the person's next ask written
|
|
47
|
+
* by the page; this shape carries no words.
|
|
48
|
+
*/
|
|
49
|
+
const SELECTOR = /^(?:body|#[A-Za-z][A-Za-z0-9_-]{0,63}|body(?: > [a-z][a-z0-9-]{0,31}(?::nth-of-type\([1-9][0-9]{0,3}\))?){1,24})$/;
|
|
50
|
+
const selector = (v) => str(v) && v.length <= 256 && SELECTOR.test(v);
|
|
51
|
+
/** An element's tag name, as the DOM spells one. */
|
|
52
|
+
const tag = (v) => str(v) && /^[a-z][a-z0-9-]{0,31}$/i.test(v);
|
|
53
|
+
/** A path on the framed page's own origin: one leading slash, never two, no backslash, no whitespace, short. */
|
|
54
|
+
const path = (v) => str(v) && v.length <= 128 && /^\/(?![\/\\])[^\s\\]*$/.test(v) && !CONTROL.test(v);
|
|
55
|
+
/**
|
|
56
|
+
* The address a frame may load: an absolute http(s) URL, resolved against
|
|
57
|
+
* `base`. Anything else — `javascript:`, `data:`, `blob:`, a malformed string —
|
|
58
|
+
* is `null`, so a frame never runs an address a string could smuggle in.
|
|
59
|
+
*/
|
|
60
|
+
function web(url, base) {
|
|
61
|
+
if (!url)
|
|
62
|
+
return null;
|
|
63
|
+
try {
|
|
64
|
+
const u = base ? new URL(url, base) : new URL(url);
|
|
65
|
+
return u.protocol === 'https:' || u.protocol === 'http:' ? u : null;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A message from the frame, or `null`. It must come FROM the frame's window,
|
|
73
|
+
* FROM the frame's origin, and be one of the five shapes above with every
|
|
74
|
+
* field the type it claims. A message that fails any of that is dropped
|
|
75
|
+
* without a word — anything can post to a window.
|
|
76
|
+
*/
|
|
77
|
+
function accept(event, frame, origin) {
|
|
78
|
+
if (!frame || event.source !== frame.contentWindow || event.origin !== origin)
|
|
79
|
+
return null;
|
|
80
|
+
const m = event.data;
|
|
81
|
+
if (!m || typeof m !== 'object' || !str(m.type) || !TYPES.has(m.type))
|
|
82
|
+
return null;
|
|
83
|
+
switch (m.type) {
|
|
84
|
+
case 'preview:ready':
|
|
85
|
+
return { type: 'preview:ready' };
|
|
86
|
+
case 'preview:hover': {
|
|
87
|
+
if (m.selector === null)
|
|
88
|
+
return { type: 'preview:hover', selector: null };
|
|
89
|
+
const r = m.rect;
|
|
90
|
+
if (!selector(m.selector) || !r || !num(r.top) || !num(r.left) || !num(r.width) || !num(r.height))
|
|
91
|
+
return null;
|
|
92
|
+
return {
|
|
93
|
+
type: 'preview:hover',
|
|
94
|
+
selector: m.selector,
|
|
95
|
+
rect: { top: r.top, left: r.left, width: r.width, height: r.height },
|
|
96
|
+
tag: tag(m.tag) ? m.tag : undefined,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
case 'preview:select': {
|
|
100
|
+
const i = m.info;
|
|
101
|
+
if (!i || !selector(i.selector) || !tag(i.tag) || !str(i.html))
|
|
102
|
+
return null;
|
|
103
|
+
return {
|
|
104
|
+
type: 'preview:select',
|
|
105
|
+
info: {
|
|
106
|
+
selector: i.selector,
|
|
107
|
+
tag: i.tag,
|
|
108
|
+
id: str(i.id) && i.id.length <= 128 && !CONTROL.test(i.id) ? i.id : undefined,
|
|
109
|
+
text: str(i.text) ? i.text.slice(0, 200) : undefined,
|
|
110
|
+
html: i.html.slice(0, 8000),
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
case 'preview:navigate':
|
|
115
|
+
return path(m.path) ? { type: 'preview:navigate', path: m.path } : null;
|
|
116
|
+
case 'preview:console': {
|
|
117
|
+
return str(m.text) ? { type: 'preview:console', level: (0, log_1.level)(m.level), text: m.text.slice(0, 4000) } : null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The script a previewed page includes to take part, pinned to the builder at
|
|
124
|
+
* `parent` (an origin: `https://platform.hanzo.ai`). It posts only there, and
|
|
125
|
+
* obeys only its parent window speaking from there.
|
|
126
|
+
*
|
|
127
|
+
* Serve it from the page itself (a `<script>` the site ships), never from the
|
|
128
|
+
* builder: the page decides whether it can be picked apart, not the frame
|
|
129
|
+
* around it.
|
|
130
|
+
*/
|
|
131
|
+
function script(parent) {
|
|
132
|
+
const origin = new URL(parent).origin;
|
|
133
|
+
return `(function () {
|
|
134
|
+
if (window.__hanzoBridge || window.parent === window) return;
|
|
135
|
+
window.__hanzoBridge = true;
|
|
136
|
+
var PARENT = ${JSON.stringify(origin)};
|
|
137
|
+
var editable = false, hovered = null, marked = null;
|
|
138
|
+
function send(m) { try { window.parent.postMessage(m, PARENT); } catch (e) {} }
|
|
139
|
+
function sel(el) {
|
|
140
|
+
if (!el || el === document.body || el === document.documentElement) return 'body';
|
|
141
|
+
if (el.id && /^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(el.id) && document.querySelectorAll('#' + el.id).length === 1) return '#' + el.id;
|
|
142
|
+
var parts = [], node = el;
|
|
143
|
+
while (node && node !== document.body && node.nodeType === 1) {
|
|
144
|
+
var p = node.parentNode; if (!p) break;
|
|
145
|
+
var same = [], i;
|
|
146
|
+
for (i = 0; i < p.children.length; i++) if (p.children[i].tagName === node.tagName) same.push(p.children[i]);
|
|
147
|
+
var tag = node.tagName.toLowerCase();
|
|
148
|
+
parts.unshift(same.length > 1 ? tag + ':nth-of-type(' + (same.indexOf(node) + 1) + ')' : tag);
|
|
149
|
+
node = p;
|
|
150
|
+
}
|
|
151
|
+
return 'body' + (parts.length ? ' > ' + parts.join(' > ') : '');
|
|
152
|
+
}
|
|
153
|
+
function find(s) { if (!s) return null; try { return document.querySelector(s); } catch (e) { return null; } }
|
|
154
|
+
function outline(el, on) { if (el) el.style.outline = on ? '2px solid rgba(120,160,255,.9)' : ''; }
|
|
155
|
+
['log', 'info', 'warn', 'error', 'debug'].forEach(function (level) {
|
|
156
|
+
var original = console[level]; if (typeof original !== 'function') return;
|
|
157
|
+
console[level] = function () {
|
|
158
|
+
var args = Array.prototype.slice.call(arguments), out = [];
|
|
159
|
+
for (var i = 0; i < args.length; i++) {
|
|
160
|
+
var a = args[i];
|
|
161
|
+
if (typeof a === 'string') out.push(a);
|
|
162
|
+
else if (a instanceof Error) out.push(a.stack || a.name + ': ' + a.message);
|
|
163
|
+
else { try { out.push(JSON.stringify(a)); } catch (e) { out.push(String(a)); } }
|
|
164
|
+
}
|
|
165
|
+
send({ type: 'preview:console', level: level, text: out.join(' ') });
|
|
166
|
+
original.apply(console, args);
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
window.addEventListener('error', function (e) { send({ type: 'preview:console', level: 'error', text: String(e.message || 'Script error') }); });
|
|
170
|
+
document.addEventListener('mouseover', function (e) {
|
|
171
|
+
if (!editable) return;
|
|
172
|
+
var t = e.target; if (!t || t === document.body || t === hovered) return;
|
|
173
|
+
outline(hovered, false); hovered = t; outline(t, true);
|
|
174
|
+
var r = t.getBoundingClientRect();
|
|
175
|
+
send({ type: 'preview:hover', selector: sel(t), tag: t.tagName.toLowerCase(), rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
|
|
176
|
+
}, true);
|
|
177
|
+
document.addEventListener('mouseout', function () {
|
|
178
|
+
if (!editable) return; outline(hovered, false); hovered = null; send({ type: 'preview:hover', selector: null });
|
|
179
|
+
}, true);
|
|
180
|
+
document.addEventListener('click', function (e) {
|
|
181
|
+
if (editable) {
|
|
182
|
+
var t = e.target; if (!t || t === document.body) return;
|
|
183
|
+
e.preventDefault(); e.stopPropagation();
|
|
184
|
+
send({ type: 'preview:select', info: { selector: sel(t), tag: t.tagName.toLowerCase(), id: t.id || undefined, text: (t.textContent || '').slice(0, 200), html: (t.outerHTML || '').slice(0, 8000) } });
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
var a = e.target && e.target.closest ? e.target.closest('a[href]') : null;
|
|
188
|
+
if (!a) return;
|
|
189
|
+
var href = a.getAttribute('href') || '';
|
|
190
|
+
if (!href || href.charAt(0) === '#' || /^[a-z][a-z0-9+.-]*:/i.test(href) || href.indexOf('//') === 0) return;
|
|
191
|
+
send({ type: 'preview:navigate', path: href });
|
|
192
|
+
}, true);
|
|
193
|
+
window.addEventListener('message', function (e) {
|
|
194
|
+
if (e.source !== window.parent || e.origin !== PARENT) return;
|
|
195
|
+
var m = e.data; if (!m || typeof m !== 'object') return;
|
|
196
|
+
if (m.type === 'preview:editable') { editable = !!m.active; if (!editable) { outline(hovered, false); hovered = null; } }
|
|
197
|
+
else if (m.type === 'preview:highlight') { outline(marked, false); marked = find(m.selector); outline(marked, true); }
|
|
198
|
+
});
|
|
199
|
+
send({ type: 'preview:ready' });
|
|
200
|
+
})();`;
|
|
201
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bridge — the message protocol between a builder and the page it previews.
|
|
3
|
+
*
|
|
4
|
+
* Ported from build-v2 `components/editor/preview/bridge.ts` (MIT, derived from
|
|
5
|
+
* OSW Studio and DeepSite — see NOTICE). v2 wrote the preview document itself
|
|
6
|
+
* (`srcdoc`) and injected this script into it. A repository's deployed site is
|
|
7
|
+
* a page on ITS OWN origin that this package does not write, so nothing is
|
|
8
|
+
* injected: the bridge is OPT-IN. A page that wants element picking and
|
|
9
|
+
* console forwarding includes the script `script(parent)` returns — pinned to
|
|
10
|
+
* the builder's origin — and the frame speaks to it only across that pair of
|
|
11
|
+
* origins.
|
|
12
|
+
*
|
|
13
|
+
* The protocol, both directions, every message a plain object with `type`:
|
|
14
|
+
*
|
|
15
|
+
* page → builder preview:ready the bridge is listening
|
|
16
|
+
* preview:hover {selector, rect, tag} | {selector: null}
|
|
17
|
+
* preview:select {info} an element was picked
|
|
18
|
+
* preview:navigate {path} an in-site link was followed
|
|
19
|
+
* preview:console {level, text} the page's console, as text
|
|
20
|
+
* builder → page preview:editable {active} picking on/off
|
|
21
|
+
* preview:highlight {selector} outline one element
|
|
22
|
+
*
|
|
23
|
+
* What v2 also sent — live style and text edits — is not here. A change made
|
|
24
|
+
* inside the frame is gone on the next load; the builder's edits are runs that
|
|
25
|
+
* commit, and a control that edits something it cannot keep is a lie.
|
|
26
|
+
*
|
|
27
|
+
* Both sides check ORIGIN, never `'*'`: the page posts only to the builder's
|
|
28
|
+
* origin and reads only messages whose source is its parent and whose origin
|
|
29
|
+
* is that origin; the builder reads only messages whose source is the frame's
|
|
30
|
+
* window and whose origin is the frame's. `accept` is that check, in one place.
|
|
31
|
+
*/
|
|
32
|
+
import { type Level } from './log.js';
|
|
33
|
+
export interface Rect {
|
|
34
|
+
top: number;
|
|
35
|
+
left: number;
|
|
36
|
+
width: number;
|
|
37
|
+
height: number;
|
|
38
|
+
}
|
|
39
|
+
/** What a picked element says about itself. Every field is text. */
|
|
40
|
+
export interface Picked {
|
|
41
|
+
selector: string;
|
|
42
|
+
tag: string;
|
|
43
|
+
id?: string;
|
|
44
|
+
text?: string;
|
|
45
|
+
/** The element's outer HTML, capped. Shown as text, never rendered. */
|
|
46
|
+
html: string;
|
|
47
|
+
}
|
|
48
|
+
export type FrameEvent = {
|
|
49
|
+
type: 'preview:ready';
|
|
50
|
+
} | {
|
|
51
|
+
type: 'preview:hover';
|
|
52
|
+
selector: string | null;
|
|
53
|
+
rect?: Rect;
|
|
54
|
+
tag?: string;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'preview:select';
|
|
57
|
+
info: Picked;
|
|
58
|
+
} | {
|
|
59
|
+
type: 'preview:navigate';
|
|
60
|
+
path: string;
|
|
61
|
+
} | {
|
|
62
|
+
type: 'preview:console';
|
|
63
|
+
level: Level;
|
|
64
|
+
text: string;
|
|
65
|
+
};
|
|
66
|
+
export type FrameCommand = {
|
|
67
|
+
type: 'preview:editable';
|
|
68
|
+
active: boolean;
|
|
69
|
+
} | {
|
|
70
|
+
type: 'preview:highlight';
|
|
71
|
+
selector: string | null;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* The address a frame may load: an absolute http(s) URL, resolved against
|
|
75
|
+
* `base`. Anything else — `javascript:`, `data:`, `blob:`, a malformed string —
|
|
76
|
+
* is `null`, so a frame never runs an address a string could smuggle in.
|
|
77
|
+
*/
|
|
78
|
+
export declare function web(url: string | null | undefined, base?: string): URL | null;
|
|
79
|
+
/**
|
|
80
|
+
* A message from the frame, or `null`. It must come FROM the frame's window,
|
|
81
|
+
* FROM the frame's origin, and be one of the five shapes above with every
|
|
82
|
+
* field the type it claims. A message that fails any of that is dropped
|
|
83
|
+
* without a word — anything can post to a window.
|
|
84
|
+
*/
|
|
85
|
+
export declare function accept(event: {
|
|
86
|
+
source: unknown;
|
|
87
|
+
origin: string;
|
|
88
|
+
data: unknown;
|
|
89
|
+
}, frame: {
|
|
90
|
+
contentWindow: unknown;
|
|
91
|
+
} | null, origin: string): FrameEvent | null;
|
|
92
|
+
/**
|
|
93
|
+
* The script a previewed page includes to take part, pinned to the builder at
|
|
94
|
+
* `parent` (an origin: `https://platform.hanzo.ai`). It posts only there, and
|
|
95
|
+
* obeys only its parent window speaking from there.
|
|
96
|
+
*
|
|
97
|
+
* Serve it from the page itself (a `<script>` the site ships), never from the
|
|
98
|
+
* builder: the page decides whether it can be picked apart, not the frame
|
|
99
|
+
* around it.
|
|
100
|
+
*/
|
|
101
|
+
export declare function script(parent: string): string;
|
|
102
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/agents/bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAS,KAAK,KAAK,EAAE,MAAM,OAAO,CAAA;AAEzC,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,oEAAoE;AACpE,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,KAAK,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3D,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAA;AAyB1D;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,IAAI,CAQ7E;AAED;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EACzD,KAAK,EAAE;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,EACxC,MAAM,EAAE,MAAM,GACb,UAAU,GAAG,IAAI,CAuCnB;AAED;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAsE7C"}
|