@hanzo/ui 8.27.15 → 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 -1
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* FileTree — a repository's files, folders first, one level read at a time.
|
|
5
|
+
*
|
|
6
|
+
* Ported from build-v2 `components/editor/file-tree/{index,glyph}.tsx` (MIT,
|
|
7
|
+
* derived from OSW Studio and DeepSite — see NOTICE). v2 listed a site's pages
|
|
8
|
+
* and let you add, rename and delete them; a repository's files change by runs
|
|
9
|
+
* that commit, so this tree reads and selects and does nothing else.
|
|
10
|
+
*
|
|
11
|
+
* Two ways in, one renderer (`tree.ts`):
|
|
12
|
+
* files every path at once — a flat list the tree groups itself;
|
|
13
|
+
* load one directory per call, the way a repository answers (`''` is the
|
|
14
|
+
* root), read the first time a folder opens and kept after.
|
|
15
|
+
*
|
|
16
|
+
* Names are TEXT. A path comes from a repository somebody else wrote, so it is
|
|
17
|
+
* drawn as a string and never interpreted as markup.
|
|
18
|
+
*
|
|
19
|
+
* Keyboard and roles are the WAI-ARIA tree: one tab stop, arrows move and open,
|
|
20
|
+
* Enter picks. The decisions are `step()` in `tree.ts`; this only applies them.
|
|
21
|
+
*/
|
|
22
|
+
import { ScrollView, SizableText, XStack, YStack } from '@hanzo/gui';
|
|
23
|
+
import { Braces, ChevronDown, ChevronRight, FileCode2, FileJson, FileText, FileType, Folder, FolderOpen, Hash, Image, Settings2, } from '@hanzogui/lucide-icons-2';
|
|
24
|
+
import { useEffect, useMemo, useRef, useState, } from 'react';
|
|
25
|
+
import { slot } from '../backends/gui/slot.js';
|
|
26
|
+
import { ancestors, group, put, rows, step } from './tree.js';
|
|
27
|
+
/**
|
|
28
|
+
* A file's icon, from its extension. The glyph varies and the colour does not:
|
|
29
|
+
* the chrome is monochrome and colour means state (live, failed), so a green
|
|
30
|
+
* `.env` would say "healthy" in a vocabulary that already means something.
|
|
31
|
+
*/
|
|
32
|
+
const BY_EXTENSION = {
|
|
33
|
+
html: FileCode2,
|
|
34
|
+
htm: FileCode2,
|
|
35
|
+
css: Hash,
|
|
36
|
+
scss: Hash,
|
|
37
|
+
js: Braces,
|
|
38
|
+
mjs: Braces,
|
|
39
|
+
cjs: Braces,
|
|
40
|
+
jsx: Braces,
|
|
41
|
+
ts: FileType,
|
|
42
|
+
tsx: FileType,
|
|
43
|
+
json: FileJson,
|
|
44
|
+
md: FileText,
|
|
45
|
+
mdx: FileText,
|
|
46
|
+
txt: FileText,
|
|
47
|
+
svg: Image,
|
|
48
|
+
png: Image,
|
|
49
|
+
jpg: Image,
|
|
50
|
+
jpeg: Image,
|
|
51
|
+
gif: Image,
|
|
52
|
+
webp: Image,
|
|
53
|
+
ico: Image,
|
|
54
|
+
toml: Settings2,
|
|
55
|
+
yml: Settings2,
|
|
56
|
+
yaml: Settings2,
|
|
57
|
+
env: Settings2,
|
|
58
|
+
};
|
|
59
|
+
/** A file's icon, drawn. `.env` splits to `['', 'env']`, so the last segment is the extension. */
|
|
60
|
+
export function glyph(name, size = 14) {
|
|
61
|
+
const ext = name.split('.').pop()?.toLowerCase() ?? '';
|
|
62
|
+
const Icon = BY_EXTENSION[ext] ?? FileText;
|
|
63
|
+
return _jsx(Icon, { size: size });
|
|
64
|
+
}
|
|
65
|
+
export function FileTree({ files, load, value = null, onSelect, label = 'Files', empty, ...rest }) {
|
|
66
|
+
const [loaded, setLoaded] = useState(() => new Map());
|
|
67
|
+
const [reads, setReads] = useState(() => new Map());
|
|
68
|
+
const [open, setOpen] = useState(() => new Set(value ? ancestors(value) : []));
|
|
69
|
+
const [focus, setFocus] = useState(0);
|
|
70
|
+
const items = useRef([]);
|
|
71
|
+
const asked = useRef(new Set());
|
|
72
|
+
const listing = useMemo(() => (files ? group([...files]) : loaded), [files, loaded]);
|
|
73
|
+
const read = (dir) => {
|
|
74
|
+
if (!load || asked.current.has(dir))
|
|
75
|
+
return;
|
|
76
|
+
asked.current.add(dir);
|
|
77
|
+
setReads((m) => new Map(m).set(dir, 'loading'));
|
|
78
|
+
load(dir).then((entries) => {
|
|
79
|
+
setLoaded((l) => put(l, dir, entries));
|
|
80
|
+
setReads((m) => {
|
|
81
|
+
const next = new Map(m);
|
|
82
|
+
next.delete(dir);
|
|
83
|
+
return next;
|
|
84
|
+
});
|
|
85
|
+
}, (e) => {
|
|
86
|
+
asked.current.delete(dir);
|
|
87
|
+
setReads((m) => new Map(m).set(dir, { error: e instanceof Error ? e.message : 'Could not read this folder' }));
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
// The root, and every folder above the chosen file, as soon as there is a
|
|
91
|
+
// way to read them. Revealing the selection is the tree's job: a file opened
|
|
92
|
+
// from elsewhere must not be "selected" inside a folder nobody opened.
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
if (!load)
|
|
95
|
+
return;
|
|
96
|
+
read('');
|
|
97
|
+
for (const dir of value ? ancestors(value) : [])
|
|
98
|
+
read(dir);
|
|
99
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
100
|
+
}, [load, value]);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!value)
|
|
103
|
+
return;
|
|
104
|
+
const reveal = ancestors(value);
|
|
105
|
+
if (reveal.length)
|
|
106
|
+
setOpen((o) => (reveal.every((d) => o.has(d)) ? o : new Set([...o, ...reveal])));
|
|
107
|
+
}, [value]);
|
|
108
|
+
const list = useMemo(() => rows(listing, open), [listing, open]);
|
|
109
|
+
const at = Math.min(focus, Math.max(list.length - 1, 0));
|
|
110
|
+
const toggle = (dir) => {
|
|
111
|
+
const opening = !open.has(dir);
|
|
112
|
+
setOpen((o) => {
|
|
113
|
+
const next = new Set(o);
|
|
114
|
+
if (opening)
|
|
115
|
+
next.add(dir);
|
|
116
|
+
else
|
|
117
|
+
next.delete(dir);
|
|
118
|
+
return next;
|
|
119
|
+
});
|
|
120
|
+
if (opening)
|
|
121
|
+
read(dir);
|
|
122
|
+
};
|
|
123
|
+
const keyed = (e) => {
|
|
124
|
+
const move = step(list, at, e.key);
|
|
125
|
+
if (!move)
|
|
126
|
+
return;
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
if (move.toggle)
|
|
129
|
+
toggle(move.toggle);
|
|
130
|
+
if (move.pick)
|
|
131
|
+
onSelect?.(move.pick);
|
|
132
|
+
setFocus(move.focus);
|
|
133
|
+
items.current[move.focus]?.focus();
|
|
134
|
+
};
|
|
135
|
+
const root = reads.get('');
|
|
136
|
+
if (list.length === 0) {
|
|
137
|
+
return (_jsx(YStack, { ...slot('file-tree'), flex: 1, minH: 0, items: "center", justify: "center", p: "$4", ...rest, children: _jsx(SizableText, { size: "$2", color: "$soft", text: "center", children: root === 'loading'
|
|
138
|
+
? 'Reading files…'
|
|
139
|
+
: root && typeof root === 'object'
|
|
140
|
+
? root.error
|
|
141
|
+
: (empty ?? 'No files yet.') }) }));
|
|
142
|
+
}
|
|
143
|
+
return (_jsx(ScrollView, { ...slot('file-tree'), flex: 1, minH: 0, ...rest, children: _jsx(YStack, { role: "tree", "aria-label": label, py: "$1.5", px: "$1.5", onKeyDown: keyed, children: list.map((row, index) => (_jsx(Item, { row: row, index: index, size: list.length, focused: index === at, chosen: row.kind === 'file' && row.path === value, state: row.kind === 'dir' && row.open ? reads.get(row.path) : undefined, onRef: (el) => {
|
|
144
|
+
items.current[index] = el;
|
|
145
|
+
}, onPress: () => {
|
|
146
|
+
setFocus(index);
|
|
147
|
+
if (row.kind === 'dir')
|
|
148
|
+
toggle(row.path);
|
|
149
|
+
else
|
|
150
|
+
onSelect?.(row.path);
|
|
151
|
+
} }, `${row.kind}:${row.path}`))) }) }));
|
|
152
|
+
}
|
|
153
|
+
function Item({ row, index, size, focused, chosen, state, onRef, onPress, }) {
|
|
154
|
+
const dir = row.kind === 'dir';
|
|
155
|
+
return (_jsxs(_Fragment, { children: [_jsxs(XStack, { ref: onRef, role: "treeitem", "aria-level": row.depth + 1, "aria-setsize": size, "aria-posinset": index + 1, "aria-selected": chosen, ...(dir ? { 'aria-expanded': row.open } : null), tabIndex: focused ? 0 : -1, onPress: onPress, items: "center", gap: "$1.5", minH: 28, pr: "$2", rounded: "$3", cursor: "pointer", bg: chosen ? '$raised' : 'transparent', hoverStyle: { bg: chosen ? '$raised' : '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$rim', outlineOffset: -2 }, style: { paddingLeft: 8 + row.depth * 12 }, ...{ 'data-path': row.path }, children: [dir ? (_jsxs(_Fragment, { children: [row.open ? _jsx(ChevronDown, { size: 13, opacity: 0.6 }) : _jsx(ChevronRight, { size: 13, opacity: 0.6 }), row.open ? _jsx(FolderOpen, { size: 14 }) : _jsx(Folder, { size: 14 })] })) : (_jsx(XStack, { pl: 17, items: "center", children: glyph(row.name) })), _jsx(SizableText, { size: "$2", color: chosen || dir ? '$ink' : '$quiet', numberOfLines: 1, flex: 1, minW: 0, children: row.name })] }), state ? (_jsx(SizableText, { size: "$1", color: state === 'loading' ? '$soft' : '$bad', style: { paddingLeft: 8 + (row.depth + 1) * 12 + 17 }, py: "$1", "aria-live": "polite", children: state === 'loading' ? 'Reading…' : state.error })) : null] }));
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=FileTree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileTree.js","sourceRoot":"","sources":["../../src/agents/FileTree.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PHONE = void 0;
|
|
5
|
+
exports.PreviewFrame = PreviewFrame;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
/**
|
|
8
|
+
* PreviewFrame — the running app, framed, beside the thing that builds it.
|
|
9
|
+
*
|
|
10
|
+
* Ported from build-v2 `components/editor/preview/index.tsx` (MIT, derived from
|
|
11
|
+
* OSW Studio and DeepSite — see NOTICE). v2 streamed a document it wrote into
|
|
12
|
+
* two `srcdoc` frames and swapped them as a build arrived. What a repository
|
|
13
|
+
* builds is a DEPLOYED page at an address — a preview URL, a sandbox's dev
|
|
14
|
+
* server, the live site — so this frames an address, and the builder's own
|
|
15
|
+
* bytes never enter it.
|
|
16
|
+
*
|
|
17
|
+
* THE SANDBOX, token by token. The framed page is somebody's app: it has to
|
|
18
|
+
* run to be previewed, and it must not be able to act on the page around it.
|
|
19
|
+
*
|
|
20
|
+
* allow-scripts it is an app; without script it is a picture.
|
|
21
|
+
* allow-forms a sign-up form is part of what you are checking.
|
|
22
|
+
* allow-popups its own links that open a tab still open one…
|
|
23
|
+
* allow-popups-to-escape-sandbox …as an ordinary tab, not a crippled one.
|
|
24
|
+
* allow-same-origin ONLY when the address is on ANOTHER origin than
|
|
25
|
+
* this page. There it grants the app its OWN
|
|
26
|
+
* origin — its cookies, its storage, its API —
|
|
27
|
+
* and nothing of ours. On OUR origin the pair
|
|
28
|
+
* `allow-scripts allow-same-origin` lets the
|
|
29
|
+
* framed page reach up and remove its own sandbox,
|
|
30
|
+
* so a same-origin address runs with no origin at
|
|
31
|
+
* all (opaque) instead.
|
|
32
|
+
*
|
|
33
|
+
* Deliberately absent: `allow-top-navigation` (a preview that can move the
|
|
34
|
+
* window it sits in is not a preview), `allow-modals`, `allow-downloads`,
|
|
35
|
+
* `allow-pointer-lock`, and every `allow=` feature — no camera, microphone or
|
|
36
|
+
* clipboard is granted to a page because it is being looked at.
|
|
37
|
+
*
|
|
38
|
+
* Only http(s) is framed. `javascript:`, `data:` and `blob:` addresses are
|
|
39
|
+
* refused before they reach the attribute, and so is anything that fails to
|
|
40
|
+
* parse — `web()` in `bridge.ts` is that check.
|
|
41
|
+
*
|
|
42
|
+
* THE BRIDGE IS OPT-IN. Element picking and the page's console arrive only if
|
|
43
|
+
* the page ships the `script()` from `bridge.ts`, pinned to this origin; this
|
|
44
|
+
* listens only to the frame's own window speaking from the frame's own origin,
|
|
45
|
+
* and posts only to that origin. See `bridge.ts` for the protocol.
|
|
46
|
+
*/
|
|
47
|
+
const gui_1 = require("@hanzo/gui");
|
|
48
|
+
const react_1 = require("react");
|
|
49
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
50
|
+
const bridge_1 = require("./bridge.cjs");
|
|
51
|
+
/** A phone's width, px — the narrow preview. */
|
|
52
|
+
exports.PHONE = 390;
|
|
53
|
+
function PreviewFrame({ src, title = 'Preview', device = 'desktop', empty, toolbar, onBridge, onLoad, ref, ...rest }) {
|
|
54
|
+
const frame = (0, react_1.useRef)(null);
|
|
55
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
56
|
+
const [nonce, setNonce] = (0, react_1.useState)(0);
|
|
57
|
+
const here = typeof window === 'undefined' ? undefined : window.location.href;
|
|
58
|
+
const url = (0, react_1.useMemo)(() => (0, bridge_1.web)(src, here), [src, here]);
|
|
59
|
+
const foreign = Boolean(url && typeof window !== 'undefined' && url.origin !== window.location.origin);
|
|
60
|
+
// No `allow-popups-to-escape-sandbox`: an escaped popup keeps a handle on this
|
|
61
|
+
// window and can move it — a framed page could send the builder's own tab to
|
|
62
|
+
// a look-alike sign-in.
|
|
63
|
+
const sandbox = ['allow-scripts', 'allow-forms', 'allow-popups']
|
|
64
|
+
.concat(foreign ? ['allow-same-origin'] : [])
|
|
65
|
+
.join(' ');
|
|
66
|
+
// Whether the host asked the page to let a person pick: until it has, a
|
|
67
|
+
// pick or a hover the page reports on its own is not a person's.
|
|
68
|
+
const picking = (0, react_1.useRef)(false);
|
|
69
|
+
// A new document has not been asked to let anyone pick: the gate closes on
|
|
70
|
+
// every load, and the host opens it again once the page says it is ready.
|
|
71
|
+
(0, react_1.useEffect)(() => {
|
|
72
|
+
setLoading(true);
|
|
73
|
+
picking.current = false;
|
|
74
|
+
}, [url?.href, nonce]);
|
|
75
|
+
(0, react_1.useImperativeHandle)(ref, () => ({
|
|
76
|
+
reload: () => setNonce((n) => n + 1),
|
|
77
|
+
post: (command) => {
|
|
78
|
+
if (command.type === 'preview:editable')
|
|
79
|
+
picking.current = command.active;
|
|
80
|
+
if (!url || !foreign)
|
|
81
|
+
return;
|
|
82
|
+
frame.current?.contentWindow?.postMessage(command, url.origin);
|
|
83
|
+
},
|
|
84
|
+
}), [url, foreign]);
|
|
85
|
+
(0, react_1.useEffect)(() => {
|
|
86
|
+
if (!onBridge || !url || !foreign)
|
|
87
|
+
return;
|
|
88
|
+
const origin = url.origin;
|
|
89
|
+
const listen = (event) => {
|
|
90
|
+
const said = (0, bridge_1.accept)(event, frame.current, origin);
|
|
91
|
+
if (!said)
|
|
92
|
+
return;
|
|
93
|
+
if ((said.type === 'preview:select' || said.type === 'preview:hover') && !picking.current)
|
|
94
|
+
return;
|
|
95
|
+
onBridge(said);
|
|
96
|
+
};
|
|
97
|
+
window.addEventListener('message', listen);
|
|
98
|
+
return () => window.removeEventListener('message', listen);
|
|
99
|
+
}, [onBridge, url, foreign]);
|
|
100
|
+
if (!url) {
|
|
101
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.YStack, { ...(0, slot_1.slot)('preview-frame'), flex: 1, minH: 0, items: "center", justify: "center", p: "$6", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$sunken", ...rest, children: empty ?? ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { items: "center", gap: "$1.5", maxW: 360, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$4", fontWeight: "600", color: "$ink", text: "center", children: src ? 'This address cannot be previewed.' : 'Nothing deployed yet.' }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", text: "center", children: src ? 'Only http and https pages open here.' : 'Describe what to build and the running app appears here.' })] })) }));
|
|
102
|
+
}
|
|
103
|
+
const phone = device === 'mobile';
|
|
104
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.YStack, { ...(0, slot_1.slot)('preview-frame'), "data-device": device, position: "relative", flex: 1, minH: 0, minW: 0, rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$sunken", overflow: "hidden", items: "center", ...rest, children: [(0, jsx_runtime_1.jsxs)(gui_1.YStack, { position: "relative", flex: 1, minH: 0, width: "100%", maxW: phone ? exports.PHONE : '100%', ...(phone ? { borderLeftWidth: 1, borderRightWidth: 1, borderColor: '$borderColor' } : null), children: [(0, jsx_runtime_1.jsx)("iframe", { ref: frame, src: url.href, title: title, sandbox: sandbox, referrerPolicy: "strict-origin-when-cross-origin", onLoad: () => {
|
|
105
|
+
setLoading(false);
|
|
106
|
+
onLoad?.();
|
|
107
|
+
}, style: { display: 'block', width: '100%', height: '100%', border: 0, background: 'transparent' } }, `${url.href}#${nonce}`), loading ? ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { position: "absolute", t: 0, r: 0, b: 0, l: 0, items: "center", justify: "center", gap: "$2", bg: "$sunken", pointerEvents: "none", "aria-hidden": true, children: [(0, jsx_runtime_1.jsx)(gui_1.Spinner, { size: "small", maxW: 16, maxH: 16 }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$2", color: "$soft", children: "Loading preview\u2026" })] })) : null] }), toolbar ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { ...(0, slot_1.slot)('preview-toolbar'), position: "absolute", b: "$4", self: "center", items: "center", gap: "$1", p: "$1.5", rounded: 999, borderWidth: 1, borderColor: "$borderColor", bg: "$background", shadowColor: "rgba(0,0,0,0.4)", shadowRadius: 16, shadowOffset: { width: 0, height: 6 }, children: toolbar })) : null] }));
|
|
108
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PreviewFrame — the running app, framed, beside the thing that builds it.
|
|
3
|
+
*
|
|
4
|
+
* Ported from build-v2 `components/editor/preview/index.tsx` (MIT, derived from
|
|
5
|
+
* OSW Studio and DeepSite — see NOTICE). v2 streamed a document it wrote into
|
|
6
|
+
* two `srcdoc` frames and swapped them as a build arrived. What a repository
|
|
7
|
+
* builds is a DEPLOYED page at an address — a preview URL, a sandbox's dev
|
|
8
|
+
* server, the live site — so this frames an address, and the builder's own
|
|
9
|
+
* bytes never enter it.
|
|
10
|
+
*
|
|
11
|
+
* THE SANDBOX, token by token. The framed page is somebody's app: it has to
|
|
12
|
+
* run to be previewed, and it must not be able to act on the page around it.
|
|
13
|
+
*
|
|
14
|
+
* allow-scripts it is an app; without script it is a picture.
|
|
15
|
+
* allow-forms a sign-up form is part of what you are checking.
|
|
16
|
+
* allow-popups its own links that open a tab still open one…
|
|
17
|
+
* allow-popups-to-escape-sandbox …as an ordinary tab, not a crippled one.
|
|
18
|
+
* allow-same-origin ONLY when the address is on ANOTHER origin than
|
|
19
|
+
* this page. There it grants the app its OWN
|
|
20
|
+
* origin — its cookies, its storage, its API —
|
|
21
|
+
* and nothing of ours. On OUR origin the pair
|
|
22
|
+
* `allow-scripts allow-same-origin` lets the
|
|
23
|
+
* framed page reach up and remove its own sandbox,
|
|
24
|
+
* so a same-origin address runs with no origin at
|
|
25
|
+
* all (opaque) instead.
|
|
26
|
+
*
|
|
27
|
+
* Deliberately absent: `allow-top-navigation` (a preview that can move the
|
|
28
|
+
* window it sits in is not a preview), `allow-modals`, `allow-downloads`,
|
|
29
|
+
* `allow-pointer-lock`, and every `allow=` feature — no camera, microphone or
|
|
30
|
+
* clipboard is granted to a page because it is being looked at.
|
|
31
|
+
*
|
|
32
|
+
* Only http(s) is framed. `javascript:`, `data:` and `blob:` addresses are
|
|
33
|
+
* refused before they reach the attribute, and so is anything that fails to
|
|
34
|
+
* parse — `web()` in `bridge.ts` is that check.
|
|
35
|
+
*
|
|
36
|
+
* THE BRIDGE IS OPT-IN. Element picking and the page's console arrive only if
|
|
37
|
+
* the page ships the `script()` from `bridge.ts`, pinned to this origin; this
|
|
38
|
+
* listens only to the frame's own window speaking from the frame's own origin,
|
|
39
|
+
* and posts only to that origin. See `bridge.ts` for the protocol.
|
|
40
|
+
*/
|
|
41
|
+
import { YStack } from '@hanzo/gui';
|
|
42
|
+
import { type ComponentProps, type ReactNode, type Ref } from 'react';
|
|
43
|
+
import { type FrameCommand, type FrameEvent } from './bridge.js';
|
|
44
|
+
type Col = Omit<ComponentProps<typeof YStack>, 'children'>;
|
|
45
|
+
/** A phone's width, px — the narrow preview. */
|
|
46
|
+
export declare const PHONE = 390;
|
|
47
|
+
/** What the host can do to a mounted frame. */
|
|
48
|
+
export interface PreviewHandle {
|
|
49
|
+
/** Load the address again. */
|
|
50
|
+
reload(): void;
|
|
51
|
+
/** Say something to a page running the bridge. Dropped when there is none. */
|
|
52
|
+
post(command: FrameCommand): void;
|
|
53
|
+
}
|
|
54
|
+
export interface PreviewFrameProps extends Col {
|
|
55
|
+
/** The address to frame. Anything but http(s) is refused. */
|
|
56
|
+
src?: string | null;
|
|
57
|
+
/** The frame's accessible name. */
|
|
58
|
+
title?: string;
|
|
59
|
+
/** Desktop fills the pane; mobile is a phone-wide column. */
|
|
60
|
+
device?: 'desktop' | 'mobile';
|
|
61
|
+
/** Drawn when there is no address — nothing deployed yet. */
|
|
62
|
+
empty?: ReactNode;
|
|
63
|
+
/** A floating bar over the bottom of the frame — v2's edit toolbar. */
|
|
64
|
+
toolbar?: ReactNode;
|
|
65
|
+
/** Messages from a page running the bridge. */
|
|
66
|
+
onBridge?: (event: FrameEvent) => void;
|
|
67
|
+
/** Called once the frame has loaded. */
|
|
68
|
+
onLoad?: () => void;
|
|
69
|
+
/**
|
|
70
|
+
* The handle — `reload()` and `post()`. A plain prop (React 19), not
|
|
71
|
+
* `forwardRef`: the wrapper's `Omit<Props, 'ref'>` collapses these props to
|
|
72
|
+
* `any` against gui's index signature, and every caller loses their types.
|
|
73
|
+
*/
|
|
74
|
+
ref?: Ref<PreviewHandle>;
|
|
75
|
+
}
|
|
76
|
+
export declare function PreviewFrame({ src, title, device, empty, toolbar, onBridge, onLoad, ref, ...rest }: PreviewFrameProps): import("react").JSX.Element;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=PreviewFrame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreviewFrame.d.ts","sourceRoot":"","sources":["../../src/agents/PreviewFrame.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,OAAO,EAAgC,MAAM,EAAE,MAAM,YAAY,CAAA;AACjE,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,GAAG,EAMT,MAAM,OAAO,CAAA;AAGd,OAAO,EAAU,KAAK,YAAY,EAAE,KAAK,UAAU,EAAO,MAAM,UAAU,CAAA;AAE1E,KAAK,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;AAE1D,gDAAgD;AAChD,eAAO,MAAM,KAAK,MAAM,CAAA;AAExB,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,MAAM,IAAI,IAAI,CAAA;IACd,8EAA8E;IAC9E,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAA;CAClC;AAED,MAAM,WAAW,iBAAkB,SAAQ,GAAG;IAC5C,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6DAA6D;IAC7D,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;IAC7B,6DAA6D;IAC7D,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,uEAAuE;IACvE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAA;IACtC,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB;;;;OAIG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC,aAAa,CAAC,CAAA;CACzB;AAED,wBAAgB,YAAY,CAAC,EAC3B,GAAG,EACH,KAAiB,EACjB,MAAkB,EAClB,KAAK,EACL,OAAO,EACP,QAAQ,EACR,MAAM,EACN,GAAG,EACH,GAAG,IAAI,EACR,EAAE,iBAAiB,+BAiKnB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* PreviewFrame — the running app, framed, beside the thing that builds it.
|
|
5
|
+
*
|
|
6
|
+
* Ported from build-v2 `components/editor/preview/index.tsx` (MIT, derived from
|
|
7
|
+
* OSW Studio and DeepSite — see NOTICE). v2 streamed a document it wrote into
|
|
8
|
+
* two `srcdoc` frames and swapped them as a build arrived. What a repository
|
|
9
|
+
* builds is a DEPLOYED page at an address — a preview URL, a sandbox's dev
|
|
10
|
+
* server, the live site — so this frames an address, and the builder's own
|
|
11
|
+
* bytes never enter it.
|
|
12
|
+
*
|
|
13
|
+
* THE SANDBOX, token by token. The framed page is somebody's app: it has to
|
|
14
|
+
* run to be previewed, and it must not be able to act on the page around it.
|
|
15
|
+
*
|
|
16
|
+
* allow-scripts it is an app; without script it is a picture.
|
|
17
|
+
* allow-forms a sign-up form is part of what you are checking.
|
|
18
|
+
* allow-popups its own links that open a tab still open one…
|
|
19
|
+
* allow-popups-to-escape-sandbox …as an ordinary tab, not a crippled one.
|
|
20
|
+
* allow-same-origin ONLY when the address is on ANOTHER origin than
|
|
21
|
+
* this page. There it grants the app its OWN
|
|
22
|
+
* origin — its cookies, its storage, its API —
|
|
23
|
+
* and nothing of ours. On OUR origin the pair
|
|
24
|
+
* `allow-scripts allow-same-origin` lets the
|
|
25
|
+
* framed page reach up and remove its own sandbox,
|
|
26
|
+
* so a same-origin address runs with no origin at
|
|
27
|
+
* all (opaque) instead.
|
|
28
|
+
*
|
|
29
|
+
* Deliberately absent: `allow-top-navigation` (a preview that can move the
|
|
30
|
+
* window it sits in is not a preview), `allow-modals`, `allow-downloads`,
|
|
31
|
+
* `allow-pointer-lock`, and every `allow=` feature — no camera, microphone or
|
|
32
|
+
* clipboard is granted to a page because it is being looked at.
|
|
33
|
+
*
|
|
34
|
+
* Only http(s) is framed. `javascript:`, `data:` and `blob:` addresses are
|
|
35
|
+
* refused before they reach the attribute, and so is anything that fails to
|
|
36
|
+
* parse — `web()` in `bridge.ts` is that check.
|
|
37
|
+
*
|
|
38
|
+
* THE BRIDGE IS OPT-IN. Element picking and the page's console arrive only if
|
|
39
|
+
* the page ships the `script()` from `bridge.ts`, pinned to this origin; this
|
|
40
|
+
* listens only to the frame's own window speaking from the frame's own origin,
|
|
41
|
+
* and posts only to that origin. See `bridge.ts` for the protocol.
|
|
42
|
+
*/
|
|
43
|
+
import { SizableText, Spinner, XStack, YStack } from '@hanzo/gui';
|
|
44
|
+
import { useEffect, useImperativeHandle, useMemo, useRef, useState, } from 'react';
|
|
45
|
+
import { slot } from '../backends/gui/slot.js';
|
|
46
|
+
import { accept, web } from './bridge.js';
|
|
47
|
+
/** A phone's width, px — the narrow preview. */
|
|
48
|
+
export const PHONE = 390;
|
|
49
|
+
export function PreviewFrame({ src, title = 'Preview', device = 'desktop', empty, toolbar, onBridge, onLoad, ref, ...rest }) {
|
|
50
|
+
const frame = useRef(null);
|
|
51
|
+
const [loading, setLoading] = useState(true);
|
|
52
|
+
const [nonce, setNonce] = useState(0);
|
|
53
|
+
const here = typeof window === 'undefined' ? undefined : window.location.href;
|
|
54
|
+
const url = useMemo(() => web(src, here), [src, here]);
|
|
55
|
+
const foreign = Boolean(url && typeof window !== 'undefined' && url.origin !== window.location.origin);
|
|
56
|
+
// No `allow-popups-to-escape-sandbox`: an escaped popup keeps a handle on this
|
|
57
|
+
// window and can move it — a framed page could send the builder's own tab to
|
|
58
|
+
// a look-alike sign-in.
|
|
59
|
+
const sandbox = ['allow-scripts', 'allow-forms', 'allow-popups']
|
|
60
|
+
.concat(foreign ? ['allow-same-origin'] : [])
|
|
61
|
+
.join(' ');
|
|
62
|
+
// Whether the host asked the page to let a person pick: until it has, a
|
|
63
|
+
// pick or a hover the page reports on its own is not a person's.
|
|
64
|
+
const picking = useRef(false);
|
|
65
|
+
// A new document has not been asked to let anyone pick: the gate closes on
|
|
66
|
+
// every load, and the host opens it again once the page says it is ready.
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
setLoading(true);
|
|
69
|
+
picking.current = false;
|
|
70
|
+
}, [url?.href, nonce]);
|
|
71
|
+
useImperativeHandle(ref, () => ({
|
|
72
|
+
reload: () => setNonce((n) => n + 1),
|
|
73
|
+
post: (command) => {
|
|
74
|
+
if (command.type === 'preview:editable')
|
|
75
|
+
picking.current = command.active;
|
|
76
|
+
if (!url || !foreign)
|
|
77
|
+
return;
|
|
78
|
+
frame.current?.contentWindow?.postMessage(command, url.origin);
|
|
79
|
+
},
|
|
80
|
+
}), [url, foreign]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!onBridge || !url || !foreign)
|
|
83
|
+
return;
|
|
84
|
+
const origin = url.origin;
|
|
85
|
+
const listen = (event) => {
|
|
86
|
+
const said = accept(event, frame.current, origin);
|
|
87
|
+
if (!said)
|
|
88
|
+
return;
|
|
89
|
+
if ((said.type === 'preview:select' || said.type === 'preview:hover') && !picking.current)
|
|
90
|
+
return;
|
|
91
|
+
onBridge(said);
|
|
92
|
+
};
|
|
93
|
+
window.addEventListener('message', listen);
|
|
94
|
+
return () => window.removeEventListener('message', listen);
|
|
95
|
+
}, [onBridge, url, foreign]);
|
|
96
|
+
if (!url) {
|
|
97
|
+
return (_jsx(YStack, { ...slot('preview-frame'), flex: 1, minH: 0, items: "center", justify: "center", p: "$6", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$sunken", ...rest, children: empty ?? (_jsxs(YStack, { items: "center", gap: "$1.5", maxW: 360, children: [_jsx(SizableText, { size: "$4", fontWeight: "600", color: "$ink", text: "center", children: src ? 'This address cannot be previewed.' : 'Nothing deployed yet.' }), _jsx(SizableText, { size: "$2", color: "$soft", text: "center", children: src ? 'Only http and https pages open here.' : 'Describe what to build and the running app appears here.' })] })) }));
|
|
98
|
+
}
|
|
99
|
+
const phone = device === 'mobile';
|
|
100
|
+
return (_jsxs(YStack, { ...slot('preview-frame'), "data-device": device, position: "relative", flex: 1, minH: 0, minW: 0, rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$sunken", overflow: "hidden", items: "center", ...rest, children: [_jsxs(YStack, { position: "relative", flex: 1, minH: 0, width: "100%", maxW: phone ? PHONE : '100%', ...(phone ? { borderLeftWidth: 1, borderRightWidth: 1, borderColor: '$borderColor' } : null), children: [_jsx("iframe", { ref: frame, src: url.href, title: title, sandbox: sandbox, referrerPolicy: "strict-origin-when-cross-origin", onLoad: () => {
|
|
101
|
+
setLoading(false);
|
|
102
|
+
onLoad?.();
|
|
103
|
+
}, style: { display: 'block', width: '100%', height: '100%', border: 0, background: 'transparent' } }, `${url.href}#${nonce}`), loading ? (_jsxs(XStack, { position: "absolute", t: 0, r: 0, b: 0, l: 0, items: "center", justify: "center", gap: "$2", bg: "$sunken", pointerEvents: "none", "aria-hidden": true, children: [_jsx(Spinner, { size: "small", maxW: 16, maxH: 16 }), _jsx(SizableText, { size: "$2", color: "$soft", children: "Loading preview\u2026" })] })) : null] }), toolbar ? (_jsx(XStack, { ...slot('preview-toolbar'), position: "absolute", b: "$4", self: "center", items: "center", gap: "$1", p: "$1.5", rounded: 999, borderWidth: 1, borderColor: "$borderColor", bg: "$background", shadowColor: "rgba(0,0,0,0.4)", shadowRadius: 16, shadowOffset: { width: 0, height: 6 }, children: toolbar })) : null] }));
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=PreviewFrame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PreviewFrame.js","sourceRoot":"","sources":["../../src/agents/PreviewFrame.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SUGGESTIONS = void 0;
|
|
5
|
+
exports.Suggestions = Suggestions;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
/**
|
|
8
|
+
* Suggestions — the row of asks above the composer, and a way to put it away.
|
|
9
|
+
*
|
|
10
|
+
* Ported from build-v2 `components/editor/ask-ai` (the "Review security ·
|
|
11
|
+
* Review SEO · Improve accessibility" chips) (MIT, derived from OSW Studio and
|
|
12
|
+
* DeepSite — see NOTICE). A chip SENDS its words as the next turn in whatever
|
|
13
|
+
* mode the composer is in — it does not fill the box and wait — which is what
|
|
14
|
+
* v2 settled on, so `onPick` hands the words out and the host sends them.
|
|
15
|
+
*
|
|
16
|
+
* The row scrolls sideways rather than wrapping or widening its column: three
|
|
17
|
+
* chips must not push a phone's composer off the bottom or its page sideways.
|
|
18
|
+
*/
|
|
19
|
+
const gui_1 = require("@hanzo/gui");
|
|
20
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
21
|
+
const button_1 = require("../backends/gui/button.cjs");
|
|
22
|
+
const slot_1 = require("../backends/gui/slot.cjs");
|
|
23
|
+
/** v2's three, for a host that has nothing more specific to offer. */
|
|
24
|
+
exports.SUGGESTIONS = ['Review security', 'Review SEO', 'Improve accessibility'];
|
|
25
|
+
function Suggestions({ items, onPick, onDismiss, label = 'Suggestions', ...rest }) {
|
|
26
|
+
if (items.length === 0)
|
|
27
|
+
return null;
|
|
28
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { ...(0, slot_1.slot)('suggestions'), role: "group", "aria-label": label, items: "center", gap: "$1.5", minW: 0, ...rest, children: [(0, jsx_runtime_1.jsx)(gui_1.ScrollView, { horizontal: true, flex: 1, minW: 0, showsHorizontalScrollIndicator: false, children: (0, jsx_runtime_1.jsx)(gui_1.XStack, { gap: "$1.5", items: "center", children: items.map((item) => ((0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "outline", size: "sm", minHeight: 24, px: "$2.5", rounded: 999, bg: "transparent", onClick: () => onPick(item), children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { size: "$1", color: "$quiet", numberOfLines: 1, children: item }) }, item))) }) }), onDismiss ? ((0, jsx_runtime_1.jsx)(button_1.Button, { type: "button", variant: "ghost", size: "icon-sm", minHeight: 24, minWidth: 24, "aria-label": `Hide ${label.toLowerCase()}`, title: `Hide ${label.toLowerCase()}`, onClick: onDismiss, children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.X, { size: 13 }) })) : null] }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Suggestions — the row of asks above the composer, and a way to put it away.
|
|
3
|
+
*
|
|
4
|
+
* Ported from build-v2 `components/editor/ask-ai` (the "Review security ·
|
|
5
|
+
* Review SEO · Improve accessibility" chips) (MIT, derived from OSW Studio and
|
|
6
|
+
* DeepSite — see NOTICE). A chip SENDS its words as the next turn in whatever
|
|
7
|
+
* mode the composer is in — it does not fill the box and wait — which is what
|
|
8
|
+
* v2 settled on, so `onPick` hands the words out and the host sends them.
|
|
9
|
+
*
|
|
10
|
+
* The row scrolls sideways rather than wrapping or widening its column: three
|
|
11
|
+
* chips must not push a phone's composer off the bottom or its page sideways.
|
|
12
|
+
*/
|
|
13
|
+
import { XStack } from '@hanzo/gui';
|
|
14
|
+
import type { ComponentProps } from 'react';
|
|
15
|
+
type Row = Omit<ComponentProps<typeof XStack>, 'children'>;
|
|
16
|
+
/** v2's three, for a host that has nothing more specific to offer. */
|
|
17
|
+
export declare const SUGGESTIONS: readonly string[];
|
|
18
|
+
export interface SuggestionsProps extends Row {
|
|
19
|
+
items: readonly string[];
|
|
20
|
+
onPick: (item: string) => void;
|
|
21
|
+
/** Omit and the row cannot be dismissed. */
|
|
22
|
+
onDismiss?: () => void;
|
|
23
|
+
/** The group's accessible name. */
|
|
24
|
+
label?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function Suggestions({ items, onPick, onDismiss, label, ...rest }: SuggestionsProps): import("react").JSX.Element | null;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=Suggestions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Suggestions.d.ts","sourceRoot":"","sources":["../../src/agents/Suggestions.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,OAAO,EAA2B,MAAM,EAAE,MAAM,YAAY,CAAA;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAK3C,KAAK,GAAG,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,EAAE,UAAU,CAAC,CAAA;AAE1D,sEAAsE;AACtE,eAAO,MAAM,WAAW,EAAE,SAAS,MAAM,EAA+D,CAAA;AAExG,MAAM,WAAW,gBAAiB,SAAQ,GAAG;IAC3C,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAqB,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,sCAyCzG"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Suggestions — the row of asks above the composer, and a way to put it away.
|
|
5
|
+
*
|
|
6
|
+
* Ported from build-v2 `components/editor/ask-ai` (the "Review security ·
|
|
7
|
+
* Review SEO · Improve accessibility" chips) (MIT, derived from OSW Studio and
|
|
8
|
+
* DeepSite — see NOTICE). A chip SENDS its words as the next turn in whatever
|
|
9
|
+
* mode the composer is in — it does not fill the box and wait — which is what
|
|
10
|
+
* v2 settled on, so `onPick` hands the words out and the host sends them.
|
|
11
|
+
*
|
|
12
|
+
* The row scrolls sideways rather than wrapping or widening its column: three
|
|
13
|
+
* chips must not push a phone's composer off the bottom or its page sideways.
|
|
14
|
+
*/
|
|
15
|
+
import { ScrollView, SizableText, XStack } from '@hanzo/gui';
|
|
16
|
+
import { X } from '@hanzogui/lucide-icons-2';
|
|
17
|
+
import { Button } from '../backends/gui/button.js';
|
|
18
|
+
import { slot } from '../backends/gui/slot.js';
|
|
19
|
+
/** v2's three, for a host that has nothing more specific to offer. */
|
|
20
|
+
export const SUGGESTIONS = ['Review security', 'Review SEO', 'Improve accessibility'];
|
|
21
|
+
export function Suggestions({ items, onPick, onDismiss, label = 'Suggestions', ...rest }) {
|
|
22
|
+
if (items.length === 0)
|
|
23
|
+
return null;
|
|
24
|
+
return (_jsxs(XStack, { ...slot('suggestions'), role: "group", "aria-label": label, items: "center", gap: "$1.5", minW: 0, ...rest, children: [_jsx(ScrollView, { horizontal: true, flex: 1, minW: 0, showsHorizontalScrollIndicator: false, children: _jsx(XStack, { gap: "$1.5", items: "center", children: items.map((item) => (_jsx(Button, { type: "button", variant: "outline", size: "sm", minHeight: 24, px: "$2.5", rounded: 999, bg: "transparent", onClick: () => onPick(item), children: _jsx(SizableText, { size: "$1", color: "$quiet", numberOfLines: 1, children: item }) }, item))) }) }), onDismiss ? (_jsx(Button, { type: "button", variant: "ghost", size: "icon-sm", minHeight: 24, minWidth: 24, "aria-label": `Hide ${label.toLowerCase()}`, title: `Hide ${label.toLowerCase()}`, onClick: onDismiss, children: _jsx(X, { size: 13 }) })) : null] }));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=Suggestions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Suggestions.js","sourceRoot":"","sources":["../../src/agents/Suggestions.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAE,CAAC,EAAE,MAAM,0BAA0B,CAAA;AAG5C,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAA;AAI3C,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAsB,CAAC,iBAAiB,EAAE,YAAY,EAAE,uBAAuB,CAAC,CAAA;AAWxG,MAAM,UAAU,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,GAAG,aAAa,EAAE,GAAG,IAAI,EAAoB;IACxG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACnC,OAAO,CACL,MAAC,MAAM,OAAK,IAAI,CAAC,aAAa,CAAC,EAAE,IAAI,EAAC,OAAO,gBAAa,KAAK,EAAE,KAAK,EAAC,QAAQ,EAAC,GAAG,EAAC,MAAM,EAAC,IAAI,EAAE,CAAC,KAAM,IAAI,aAC1G,KAAC,UAAU,IAAC,UAAU,QAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,8BAA8B,EAAE,KAAK,YAC5E,KAAC,MAAM,IAAC,GAAG,EAAC,MAAM,EAAC,KAAK,EAAC,QAAQ,YAC9B,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,KAAC,MAAM,IAEL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,SAAS,EAAE,EAAE,EACb,EAAE,EAAC,MAAM,EACT,OAAO,EAAE,GAAG,EACZ,EAAE,EAAC,aAAa,EAChB,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,YAE3B,KAAC,WAAW,IAAC,IAAI,EAAC,IAAI,EAAC,KAAK,EAAC,QAAQ,EAAC,aAAa,EAAE,CAAC,YACnD,IAAI,GACO,IAZT,IAAI,CAaF,CACV,CAAC,GACK,GACE,EACZ,SAAS,CAAC,CAAC,CAAC,CACX,KAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,OAAO,EACf,IAAI,EAAC,SAAS,EACd,SAAS,EAAE,EAAE,EACb,QAAQ,EAAE,EAAE,gBACA,QAAQ,KAAK,CAAC,WAAW,EAAE,EAAE,EACzC,KAAK,EAAE,QAAQ,KAAK,CAAC,WAAW,EAAE,EAAE,EACpC,OAAO,EAAE,SAAS,YAElB,KAAC,CAAC,IAAC,IAAI,EAAE,EAAE,GAAI,GACR,CACV,CAAC,CAAC,CAAC,IAAI,IACD,CACV,CAAA;AACH,CAAC"}
|