@hanzo/build 0.2.12 → 0.2.14
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/README.md +10 -0
- package/lib/api/connectors.d.ts +2 -0
- package/lib/api/connectors.js +1 -0
- package/lib/api/skills.d.ts +2 -0
- package/lib/api/skills.js +9 -1
- package/lib/customize/connectors.js +1 -1
- package/lib/customize/skills.js +1 -1
- package/lib/desk.js +1 -1
- package/lib/files.js +1 -1
- package/lib/project.js +10 -3
- package/lib/run.js +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ pnpm dev # http://localhost:3200
|
|
|
15
15
|
pnpm test # the transport contract and the address grammar
|
|
16
16
|
pnpm site # every screen of https://hanzo.build in a browser (SITE= for another origin)
|
|
17
17
|
pnpm e2e # one live agent turn, projected the way a run draws it
|
|
18
|
+
pnpm cover # unit tests and every browser spec against a dev server, one coverage report in coverage/
|
|
18
19
|
pnpm build:lib # lib/ — what npm ships
|
|
19
20
|
```
|
|
20
21
|
|
|
@@ -27,6 +28,15 @@ refused.
|
|
|
27
28
|
a build: the gateway admits an origin by allowlist and a localhost port is not
|
|
28
29
|
on it.
|
|
29
30
|
|
|
31
|
+
`pnpm cover` is one number for the unit tests and the browser specs together:
|
|
32
|
+
both keep raw V8 coverage with source maps (`cover/unit.ts`, `src/e2e/fixture.ts`)
|
|
33
|
+
and `cover/run.ts` merges them by original source into `coverage/html/index.html`,
|
|
34
|
+
`lcov.info` and `coverage-summary.json`, and lists every file short of 100% with
|
|
35
|
+
the lines no test reached in `coverage/gaps.txt`. Arguments go to Playwright
|
|
36
|
+
(`pnpm cover src/e2e/run.spec.ts`). `src/e2e/screens.spec.ts` draws every screen
|
|
37
|
+
and state at a phone, a tablet, a laptop and a desktop — checked to fit, and
|
|
38
|
+
saved to `screens/<size>/<name>.png`.
|
|
39
|
+
|
|
30
40
|
## Mounting it
|
|
31
41
|
|
|
32
42
|
```tsx
|
package/lib/api/connectors.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export interface Server {
|
|
|
27
27
|
/** The catalog listing it was added from, or '' for a URL typed in. */
|
|
28
28
|
listing: string;
|
|
29
29
|
created: number;
|
|
30
|
+
/** False when no admin put it in place, so no run carries it; true, or unsaid by an older platform. */
|
|
31
|
+
admitted: boolean;
|
|
30
32
|
}
|
|
31
33
|
export interface Remote {
|
|
32
34
|
transport: string;
|
package/lib/api/connectors.js
CHANGED
package/lib/api/skills.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ export interface Skill {
|
|
|
26
26
|
created: number;
|
|
27
27
|
/** The repository it was read from, or '' for one written here. */
|
|
28
28
|
source: string;
|
|
29
|
+
/** False when no admin wrote it, so no run carries it; true, or unsaid by an older platform. */
|
|
30
|
+
admitted: boolean;
|
|
29
31
|
}
|
|
30
32
|
/** One skill of the brand's catalogue. */
|
|
31
33
|
export interface Entry {
|
package/lib/api/skills.js
CHANGED
|
@@ -29,7 +29,15 @@ const rows = (v) => (Array.isArray(v) ? v : []);
|
|
|
29
29
|
export function skill(raw) {
|
|
30
30
|
const o = obj(raw);
|
|
31
31
|
const name = str(o.name);
|
|
32
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
id: str(o.id) || name,
|
|
34
|
+
name,
|
|
35
|
+
description: str(o.description),
|
|
36
|
+
content: str(o.content),
|
|
37
|
+
created: num(o.createdAt),
|
|
38
|
+
source: str(o.source),
|
|
39
|
+
admitted: o.admitted !== false,
|
|
40
|
+
};
|
|
33
41
|
}
|
|
34
42
|
export function catalogue(raw) {
|
|
35
43
|
const o = obj(raw);
|
|
@@ -56,7 +56,7 @@ export function Connectors({ view, q, adding, onAdding, onView }) {
|
|
|
56
56
|
return _jsx(Soft, { action: host.signIn ? _jsx(Button, { size: "sm", onPress: () => host.signIn?.(), children: "Sign in" }) : undefined, children: "Sign in to see your connectors." });
|
|
57
57
|
}
|
|
58
58
|
const shown = mine.value.filter((s) => matches(q, s.name, s.url, s.listing));
|
|
59
|
-
return (_jsxs(YStack, { gap: "$3", children: [_jsx(Line, { children: note }), mine.error && !mine.value.length ? (_jsx(Soft, { children: mine.error.message })) : mine.loading && !mine.value.length ? (_jsx(Soft, { children: "Reading your connectors\u2026" })) : !mine.value.length ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover connectors" }), children: may ? 'No connectors yet. Add an MCP server by its URL, or pick one off the shelf.' : 'No connectors yet. An org admin adds them.' })) : !shown.length ? (_jsx(Soft, { children: "No connector matches." })) : (_jsx(Grid, { label: "Your connectors", children: shown.map((s) => (_jsx(Tile, { title: s.name, detail: where(s.url), meta: [s.listing ? 'From the shelf' : 'Added by URL', s.secret ? 'secret sealed in KMS' : '', day(s.created)].filter(Boolean).join(' · '), mark: _jsx(Mark, { name: s.name }), onOpen: () => setOpened(s) }, s.id))) })), opened ? (_jsx(Detail, { server: opened, may: may, onClose: () => setOpened(null), onDeleted: () => {
|
|
59
|
+
return (_jsxs(YStack, { gap: "$3", children: [_jsx(Line, { children: note }), mine.error && !mine.value.length ? (_jsx(Soft, { children: mine.error.message })) : mine.loading && !mine.value.length ? (_jsx(Soft, { children: "Reading your connectors\u2026" })) : !mine.value.length ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover connectors" }), children: may ? 'No connectors yet. Add an MCP server by its URL, or pick one off the shelf.' : 'No connectors yet. An org admin adds them.' })) : !shown.length ? (_jsx(Soft, { children: "No connector matches." })) : (_jsx(Grid, { label: "Your connectors", children: shown.map((s) => (_jsx(Tile, { title: s.name, detail: where(s.url), meta: [s.listing ? 'From the shelf' : 'Added by URL', s.secret ? 'secret sealed in KMS' : '', day(s.created), s.admitted ? '' : 'not in runs until an admin adds it again'].filter(Boolean).join(' · '), mark: _jsx(Mark, { name: s.name }), onOpen: () => setOpened(s) }, s.id))) })), opened ? (_jsx(Detail, { server: opened, may: may, onClose: () => setOpened(null), onDeleted: () => {
|
|
60
60
|
setNote(`${opened.name} is removed`);
|
|
61
61
|
setOpened(null);
|
|
62
62
|
mine.reload();
|
package/lib/customize/skills.js
CHANGED
|
@@ -79,7 +79,7 @@ export function Skills({ view, q, adding, onAdding, onView }) {
|
|
|
79
79
|
const ownShown = own.value.filter((s) => matches(q, s.name, s.description));
|
|
80
80
|
const added = on.value.filter((x) => nameOf(x.name) && !mine.has(nameOf(x.name)) && matches(q, nameOf(x.name), x.description));
|
|
81
81
|
const error = own.error ?? on.error;
|
|
82
|
-
return (_jsxs(YStack, { gap: "$5", children: [_jsx(Line, { children: note }), error && !own.value.length && !on.value.length ? (_jsx(Soft, { children: error.message })) : (own.loading || on.loading) && !own.value.length && !on.value.length ? (_jsx(Soft, { children: "Reading your skills\u2026" })) : !own.value.length && !added.length && !q ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover skills" }), children: "No skills yet. Write one, or add one from the catalogue." })) : (_jsxs(_Fragment, { children: [_jsx(Part, { title: "Written here", detail: "Your organization\u2019s own. Off keeps it, and keeps it out of every agent\u2019s prompt.", children: ownShown.length ? (_jsx(Grid, { label: "Your skills", children: ownShown.map((s) => (_jsx(Tile, { title: s.name, detail: s.description || firstLine(s.content), meta: s.source ? `From ${s.source}` : day(s.created) ? `Saved ${day(s.created)}` : undefined, mark: _jsx(Mark, { name: s.name }), onOpen: () => setEditing(s), action: may ? (_jsx(Switch, { checked: lit.has(tool(s.name)), disabled: busy === s.name, onCheckedChange: (v) => void flip(s.name, v), "aria-label": `${s.name} on` })) : (_jsx(SizableText, { size: "$1", color: "$soft", children: lit.has(tool(s.name)) ? 'On' : 'Off' })) }, s.id))) })) : (_jsx(SizableText, { size: "$2", color: "$soft", children: own.value.length ? 'None of yours matches.' : 'None yet.' })) }), added.length ? (_jsx(Part, { title: "Added from the catalogue", detail: "On for every agent that names them.", children: _jsx(Grid, { label: "Added skills", children: added.map((x) => {
|
|
82
|
+
return (_jsxs(YStack, { gap: "$5", children: [_jsx(Line, { children: note }), error && !own.value.length && !on.value.length ? (_jsx(Soft, { children: error.message })) : (own.loading || on.loading) && !own.value.length && !on.value.length ? (_jsx(Soft, { children: "Reading your skills\u2026" })) : !own.value.length && !added.length && !q ? (_jsx(Soft, { action: _jsx(Button, { size: "sm", variant: "outline", onPress: () => onView('discover'), children: "Discover skills" }), children: "No skills yet. Write one, or add one from the catalogue." })) : (_jsxs(_Fragment, { children: [_jsx(Part, { title: "Written here", detail: "Your organization\u2019s own. Off keeps it, and keeps it out of every agent\u2019s prompt.", children: ownShown.length ? (_jsx(Grid, { label: "Your skills", children: ownShown.map((s) => (_jsx(Tile, { title: s.name, detail: s.description || firstLine(s.content), meta: [s.source ? `From ${s.source}` : day(s.created) ? `Saved ${day(s.created)}` : '', s.admitted ? '' : 'Not in runs until an admin saves it'].filter(Boolean).join(' · ') || undefined, mark: _jsx(Mark, { name: s.name }), onOpen: () => setEditing(s), action: may ? (_jsx(Switch, { checked: lit.has(tool(s.name)), disabled: busy === s.name, onCheckedChange: (v) => void flip(s.name, v), "aria-label": `${s.name} on` })) : (_jsx(SizableText, { size: "$1", color: "$soft", children: lit.has(tool(s.name)) ? 'On' : 'Off' })) }, s.id))) })) : (_jsx(SizableText, { size: "$2", color: "$soft", children: own.value.length ? 'None of yours matches.' : 'None yet.' })) }), added.length ? (_jsx(Part, { title: "Added from the catalogue", detail: "On for every agent that names them.", children: _jsx(Grid, { label: "Added skills", children: added.map((x) => {
|
|
83
83
|
const name = nameOf(x.name);
|
|
84
84
|
return (_jsx(Tile, { title: name, detail: x.description, mark: _jsx(Mark, { name: name, icon: _jsx(BookOpen, { size: 15 }) }), onOpen: () => setReading({ name, description: x.description, product: '' }), action: may ? (_jsx(Button, { size: "sm", variant: "ghost", disabled: busy === name, onPress: () => void flip(name, false), "aria-label": `Remove ${name}`, children: "Remove" })) : undefined }, x.name));
|
|
85
85
|
}) }) })) : null] })), reader, editor] }));
|
package/lib/desk.js
CHANGED
|
@@ -20,7 +20,7 @@ export function Desk({ id, repo, branch, base, environment, mode, pr, title, pro
|
|
|
20
20
|
const name = repo.split('/').filter(Boolean).pop() || repo;
|
|
21
21
|
const lines = shell(events);
|
|
22
22
|
const bleed = BLEED.includes(tab);
|
|
23
|
-
return (_jsxs(YStack, { role: "complementary", "aria-label": "Run details", flex: 1, minW: 0, minH: 0, borderLeftWidth: 1, borderColor: "$borderColor", display: "none", $md: { display: 'flex' }, children: [_jsxs(XStack, { px: "$2", pt: "$2", gap: "$1", borderBottomWidth: 1, borderColor: "$borderColor", items: "center", children: [_jsx(TabButton, { id: "environment", tab: tab, onPick: setTab, children: "Environment" }), _jsx(TabButton, { id: "git", tab: tab, onPick: setTab, children: "Git" }), _jsx(TabButton, { id: "desktop", tab: tab, onPick: setTab, children: "Desktop" }), _jsx(TabButton, { id: "terminal", tab: tab, onPick: setTab, children: "Terminal" }), _jsx(TabButton, { id: "files", tab: tab, onPick: setTab, children: "Files" }), _jsx(TabButton, { id: "subscriptions", tab: tab, onPick: setTab, children: "Subscriptions" })
|
|
23
|
+
return (_jsxs(YStack, { role: "complementary", "aria-label": "Run details", flex: 1, minW: 0, minH: 0, borderLeftWidth: 1, borderColor: "$borderColor", display: "none", $md: { display: 'flex' }, children: [_jsxs(XStack, { px: "$2", pt: "$2", gap: "$1", borderBottomWidth: 1, borderColor: "$borderColor", items: "center", children: [_jsxs(XStack, { flex: 1, minW: 0, gap: "$1", overflow: "scroll", children: [_jsx(TabButton, { id: "environment", tab: tab, onPick: setTab, children: "Environment" }), _jsx(TabButton, { id: "git", tab: tab, onPick: setTab, children: "Git" }), _jsx(TabButton, { id: "desktop", tab: tab, onPick: setTab, children: "Desktop" }), _jsx(TabButton, { id: "terminal", tab: tab, onPick: setTab, children: "Terminal" }), _jsx(TabButton, { id: "files", tab: tab, onPick: setTab, children: "Files" }), _jsx(TabButton, { id: "subscriptions", tab: tab, onPick: setTab, children: "Subscriptions" })] }), onHide ? (_jsx(XStack, { render: "button", "aria-label": "Hide the side pane", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, onPress: onHide, children: _jsx(PanelRightClose, { size: 16 }) })) : null, _jsx(DropdownMenu, { trigger: _jsx(XStack, { render: "button", "aria-label": "Run actions", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(MoreHorizontal, { size: 16 }) }), items: [...menu, { key: 'details', label: 'Details', icon: _jsx(Info, { size: 16 }), onSelect: () => setTab('environment') }] })] }), _jsxs(YStack, { flex: 1, minH: 0, overflow: bleed ? 'hidden' : 'scroll', px: bleed ? 0 : '$3', py: bleed ? 0 : '$3', children: [tab === 'environment' ? (_jsxs(YStack, { gap: "$2", children: [_jsx(Environment, { repo: name, busy: live && mode === 'setup' }), _jsx(SizableText, { size: "$2", color: "$ink", pt: "$4", children: "This run" }), _jsx(Fact, { label: "Run", value: id }), _jsx(Fact, { label: "Repository", value: name || '—' }), _jsx(Fact, { label: "Branch", value: branch || '—' }), _jsx(Fact, { label: "Base", value: base || '—' }), _jsx(Fact, { label: "Runs on", value: environment || 'sandbox' }), _jsx(Fact, { label: "Mode", value: mode || 'build' })] })) : null, tab === 'git' ? _jsx(Git, { session: id, title: title, live: live }) : null, tab === 'desktop' ? _jsx(Door, { which: "screen", sandbox: sandbox, live: live, session: id }) : null, tab === 'terminal' ? (_jsx(Terminal, { id: id, sandbox: sandbox, lines: lines, live: live, refused: refused, retry: retry, onRetry: onRetry })) : null, tab === 'files' ? (_jsx(Files, { session: id, repo: repo, branch: branch, sandbox: sandbox, live: live, mode: mode, project: project, pr: pr, onEnvironment: () => setTab('environment') })) : null, tab === 'subscriptions' ? (_jsx(YStack, { flex: 1, items: "center", justify: "center", px: "$6", children: _jsx(SizableText, { size: "$2", color: "$soft", style: { textAlign: 'center', maxWidth: 320 }, children: "Ask an agent to subscribe to a Slack channel or thread, a pull request, or a timer." }) })) : null] })] }));
|
|
24
24
|
}
|
|
25
25
|
function TabButton({ id, tab, onPick, children }) {
|
|
26
26
|
const on = tab === id;
|
package/lib/files.js
CHANGED
|
@@ -19,7 +19,7 @@ export function Files({ session, repo, branch, sandbox, live, mode, project, pr,
|
|
|
19
19
|
const reachable = live && Boolean(sandbox);
|
|
20
20
|
const [source, setSource] = useState(reachable ? 'live' : 'branch');
|
|
21
21
|
const from = reachable ? source : 'branch';
|
|
22
|
-
return (_jsxs(YStack, { gap: "$3", children: [_jsxs(XStack, { gap: "$1", items: "center", children: [_jsx(Pick, { on: pane === 'files', onPress: () => setPane('files'), children: "Files" }), _jsx(Pick, { on: pane === 'artifacts', onPress: () => setPane('artifacts'), children: "Artifacts" }), _jsx(XStack, { flex: 1 }), pane === 'files' && reachable ? (_jsxs(_Fragment, { children: [_jsx(Pick, { on: from === 'live', onPress: () => setSource('live'), children: "Live" }), _jsx(Pick, { on: from === 'branch', onPress: () => setSource('branch'), children: "Branch" })] })) : null] }), pane === 'artifacts' ? (_jsx(Artifacts, { repo: repo, branch: branch, mode: mode, project: project, pr: pr, onEnvironment: onEnvironment })) : from === 'live' ? (_jsx(Live, { sandbox: sandbox })) : (_jsx(Branch, { session: session, label: `${repo}${branch ? `@${branch}` : ''}` }))] }));
|
|
22
|
+
return (_jsxs(YStack, { gap: "$3", children: [_jsxs(XStack, { gap: "$1", items: "center", flexWrap: "wrap", children: [_jsx(Pick, { on: pane === 'files', onPress: () => setPane('files'), children: "Files" }), _jsx(Pick, { on: pane === 'artifacts', onPress: () => setPane('artifacts'), children: "Artifacts" }), _jsx(XStack, { flex: 1 }), pane === 'files' && reachable ? (_jsxs(_Fragment, { children: [_jsx(Pick, { on: from === 'live', onPress: () => setSource('live'), children: "Live" }), _jsx(Pick, { on: from === 'branch', onPress: () => setSource('branch'), children: "Branch" })] })) : null] }), pane === 'artifacts' ? (_jsx(Artifacts, { repo: repo, branch: branch, mode: mode, project: project, pr: pr, onEnvironment: onEnvironment })) : from === 'live' ? (_jsx(Live, { sandbox: sandbox })) : (_jsx(Branch, { session: session, label: `${repo}${branch ? `@${branch}` : ''}` }))] }));
|
|
23
23
|
}
|
|
24
24
|
/** The sandbox's working tree, as it is right now. */
|
|
25
25
|
function Live({ sandbox }) {
|
package/lib/project.js
CHANGED
|
@@ -19,9 +19,9 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
19
19
|
* Derived from the Hanzo App v2 editor (github.com/hanzoai/build-v2), itself
|
|
20
20
|
* derived from OSW Studio and DeepSite (MIT). See NOTICE.
|
|
21
21
|
*/
|
|
22
|
-
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
22
|
+
import { SizableText, useMedia, XStack, YStack } from '@hanzo/gui';
|
|
23
23
|
import { Clock, ExternalLink, MousePointerClick, PanelLeft, RefreshCcw, Share2, Upload, } from '@hanzogui/lucide-icons-2';
|
|
24
|
-
import { Attachments, Console, DEVICES, Feedback, FileTabs, FileTree, ModeSelect, PageSelect, PreviewFrame, ProjectChip, Suggestions, SUGGESTIONS, VIEWS, Views, Workspace, fold, } from '@hanzo/ui/agents';
|
|
24
|
+
import { Attachments, CHAT, Console, DEVICES, Feedback, FileTabs, FileTree, ModeSelect, PageSelect, PreviewFrame, ProjectChip, Suggestions, SUGGESTIONS, VIEWS, Views, Workspace, fold, } from '@hanzo/ui/agents';
|
|
25
25
|
import { Composer, Message } from '@hanzo/ui/chat';
|
|
26
26
|
import { Button, Dialog, DialogContent, DialogTitle } from '@hanzo/ui';
|
|
27
27
|
import { HanzoMark } from '@hanzo/ui/product';
|
|
@@ -72,7 +72,9 @@ export function Project({ slug }) {
|
|
|
72
72
|
const [view, setView] = useKept(`hanzo.build.view.${slug}`, 'preview');
|
|
73
73
|
const [device, setDevice] = useState('desktop');
|
|
74
74
|
const [collapsed, setCollapsed] = useKept('hanzo.build.chat', false);
|
|
75
|
+
// Below md the chat and the work take turns, and Chat is one more view to switch back to.
|
|
75
76
|
const [pane, setPane] = useState('chat');
|
|
77
|
+
const wide = useMedia().md;
|
|
76
78
|
const [dock, setDock] = useKept('hanzo.build.dock', 36);
|
|
77
79
|
const [dismissed, setDismissed] = useKept(`hanzo.build.hints.${slug}`, false);
|
|
78
80
|
const [mode, setMode] = useKept('hanzo.build.mode', 'build');
|
|
@@ -234,7 +236,12 @@ export function Project({ slug }) {
|
|
|
234
236
|
}, empty: _jsxs(SizableText, { size: "$2", color: "$soft", p: "$4", children: ["Open a file from Files. Code here is read at ", ref, "; ask in the chat to change it."] }) })) : (_jsx(YStack, { p: "$4", gap: "$2", children: picked.length ? (picked.map((p) => (_jsxs(SizableText, { size: "$2", color: "$ink", children: [p.label, " \u2014 ", p.id] }, p.id)))) : (_jsx(SizableText, { size: "$2", color: "$soft", children: bridge
|
|
235
237
|
? 'Pick an element in the preview to see it here and attach it to your next ask.'
|
|
236
238
|
: 'Layers are read through the preview bridge, and this page does not run it. Its elements cannot be listed from here.' })) }));
|
|
237
|
-
return (_jsxs(_Fragment, { children: [_jsx(Workspace, { start: _jsxs(XStack, { items: "center", gap: "$2", minW: 0, shrink: 1, children: [_jsx(XStack, { render: "button", "aria-label": "All runs", onPress: () => host.go(''), p: "$1", shrink: 0, children: _jsx(HanzoMark, { size: 20 }) }), _jsx(ProjectChip, { name: project?.name ?? slug, onPress: () => setSwitcher(true) }), _jsx(XStack, { render: "button", "aria-label": "History", onPress: () => setHistory(true), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(Clock, { size: 16 }) }), _jsx(XStack, { render: "button", "aria-label": collapsed ? 'Show the chat' : 'Hide the chat', "aria-pressed": !collapsed, onPress: () => setCollapsed(!collapsed), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' },
|
|
239
|
+
return (_jsxs(_Fragment, { children: [_jsx(Workspace, { start: _jsxs(XStack, { items: "center", gap: "$2", minW: 0, shrink: 1, children: [_jsx(XStack, { render: "button", "aria-label": "All runs", onPress: () => host.go(''), p: "$1", shrink: 0, children: _jsx(HanzoMark, { size: 20 }) }), _jsx(ProjectChip, { name: project?.name ?? slug, onPress: () => setSwitcher(true) }), _jsx(XStack, { render: "button", "aria-label": "History", onPress: () => setHistory(true), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(Clock, { size: 16 }) }), _jsx(XStack, { render: "button", "aria-label": collapsed ? 'Show the chat' : 'Hide the chat', "aria-pressed": !collapsed, onPress: () => setCollapsed(!collapsed), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, "$max-md": { display: 'none' }, children: _jsx(PanelLeft, { size: 16 }) })] }), middle: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Views, { views: [...VIEWS, CHAT], value: pane === 'chat' && !wide ? 'chat' : view, onChange: (v) => {
|
|
240
|
+
if (v === 'chat')
|
|
241
|
+
return setPane('chat');
|
|
242
|
+
setView(v);
|
|
243
|
+
setPane('view');
|
|
244
|
+
}, label: "View" }), _jsx(Views, { views: DEVICES, value: device, onChange: (d) => setDevice(d), label: "Device", labels: "none" }), _jsx(XStack, { render: "button", "aria-label": "Reload the preview", onPress: () => frame.current?.reload(), p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(RefreshCcw, { size: 16 }) }), _jsx(PageSelect, { pages: pages, value: page, onChange: setPage }), src ? (_jsx(Out, { href: src, label: "Open in a new tab", children: _jsx(XStack, { p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(ExternalLink, { size: 16 }) }) })) : null] }), end: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(XStack, { display: "none", $md: { display: 'flex' }, children: _jsxs(Button, { variant: "outline", size: "sm", onPress: () => void share(), children: [_jsx(Share2, { size: 14 }), " Share"] }) }), _jsxs(Button, { size: "sm", disabled: !project?.repo, onPress: () => project &&
|
|
238
245
|
setPublish({ repo: project.repo, title: project.name, ref, name: project.slug, project: project.slug }), children: [_jsx(Upload, { size: 14 }), " Publish"] })] }), chat: chat, dock: _jsx(Console, { lines: lines, height: dock, onHeight: setDock, onClear: () => setPageLines([]), status: run.status || end.status || undefined, label: "Console" }), collapsed: collapsed, pane: pane, children: body }), _jsx(Dialog, { open: history, onOpenChange: setHistory, children: _jsxs(DialogContent, { maxW: 480, children: [_jsxs(DialogTitle, { children: ["Runs on ", project?.name ?? slug] }), _jsx(YStack, { gap: "$1", children: runs.value.length === 0 ? (_jsx(SizableText, { size: "$2", color: "$soft", children: "No runs yet." })) : (runs.value.map((r) => (_jsxs(XStack, { render: "button", onPress: () => {
|
|
239
246
|
setChosen(r.id);
|
|
240
247
|
setHistory(false);
|
package/lib/run.js
CHANGED
|
@@ -238,7 +238,7 @@ export function Run({ id }) {
|
|
|
238
238
|
: finished
|
|
239
239
|
? 'Follow up — continues in a new run'
|
|
240
240
|
: 'Reading this run…';
|
|
241
|
-
return (_jsxs(XStack, { flex: 1, minH: 0, minW: 0, width: "100%", children: [_jsxs(YStack, { flex: 1, minH: 0, minW: 0, width: "100%", px: "$6", "$max-md": { px: '$4' }, children: [_jsxs(XStack, { pt: "$3", pb: "$2", gap: "$3", items: "center", minH: 44, children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { render: "h1", size: "$5", color: "$ink", numberOfLines: 1, children: title }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: [state || (detail.loading ? 'reading' : ''), record?.repo, record?.branch, record?.published ? 'shared' : ''].filter(Boolean).join(' · ') })] }), desk ? null : (_jsx(XStack, { render: "button", "aria-label": "Show the side pane", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, onPress: () => setDesk(true), children: _jsx(PanelRight, { size: 16 }) })), pr.href ? (_jsx(Out, { href: pr.href, label: `Open pull request ${pr.label}`, children: _jsxs(XStack, { items: "center", gap: "$1.5", px: "$2.5", py: "$1.5", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: [_jsx(GitPullRequest, { size: 14 }), _jsx(SizableText, { size: "$2", color: "$ink", children: pr.label }), _jsx(ExternalLink, { size: 12, opacity: 0.6 })] }) })) : null, menu.length ? (_jsx(DropdownMenu, { trigger: _jsx(XStack, { render: "button", "aria-label": "Manage this run", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(MoreHorizontal, { size: 16 }) }), items: menu })) : null] }), _jsx(Transcript, { cards: shown,
|
|
241
|
+
return (_jsxs(XStack, { flex: 1, minH: 0, minW: 0, width: "100%", children: [_jsxs(YStack, { flex: 1, minH: 0, minW: 0, width: "100%", px: "$6", "$max-md": { px: '$4' }, children: [_jsxs(XStack, { pt: "$3", pb: "$2", gap: "$3", items: "center", minH: 44, children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { render: "h1", size: "$5", color: "$ink", numberOfLines: 1, children: title }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: [state || (detail.loading ? 'reading' : ''), record?.repo, record?.branch, record?.published ? 'shared' : ''].filter(Boolean).join(' · ') })] }), desk ? null : (_jsx(XStack, { render: "button", "aria-label": "Show the side pane", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, onPress: () => setDesk(true), "$max-md": { display: 'none' }, children: _jsx(PanelRight, { size: 16 }) })), pr.href ? (_jsx(Out, { href: pr.href, label: `Open pull request ${pr.label}`, children: _jsxs(XStack, { items: "center", gap: "$1.5", px: "$2.5", py: "$1.5", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: [_jsx(GitPullRequest, { size: 14 }), _jsx(SizableText, { size: "$2", color: "$ink", children: pr.label }), _jsx(ExternalLink, { size: 12, opacity: 0.6 })] }) })) : null, menu.length ? (_jsx(DropdownMenu, { trigger: _jsx(XStack, { render: "button", "aria-label": "Manage this run", px: "$2", py: "$1", rounded: "$2", hoverStyle: { bg: '$hover' }, children: _jsx(MoreHorizontal, { size: 16 }) }), items: menu })) : null] }), _jsx(Transcript, { cards: shown,
|
|
242
242
|
// A paused run's agent was stopped where it stood; what it was running is cut off.
|
|
243
243
|
live: running && !paused, header: hidden ? (_jsx(SizableText, { size: "$1", color: "$soft", children: `${hidden} earlier ${hidden === 1 ? 'turn is' : 'turns are'} not shown. The run’s Git tab has everything it pushed.` })) : null, onApprove: planned && earlier ? (p) => void act(() => carry(p, 'approve'), 'The build could not start') : undefined, approving: busy, empty: signed ? (_jsx(SizableText, { size: "$2", color: "$soft", children: detail.error
|
|
244
244
|
? detail.error.message
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/build",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0 OR MIT",
|
|
6
6
|
"description": "Hanzo Build — say what you want, watch it built, deployed and running. The builder as a page (hanzo.build) and as a component (hanzo.ai/dev).",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@types/react": "19.2.18",
|
|
22
22
|
"@types/react-dom": "19.2.7",
|
|
23
23
|
"@vitejs/plugin-react": "^6.1.1",
|
|
24
|
+
"monocart-coverage-reports": "2.13.0",
|
|
24
25
|
"next-themes": "^0.4.6",
|
|
25
26
|
"react": "19.2.8",
|
|
26
27
|
"react-dom": "19.2.8",
|
|
@@ -68,6 +69,7 @@
|
|
|
68
69
|
"preview": "vite preview",
|
|
69
70
|
"test": "vitest run",
|
|
70
71
|
"e2e": "HANZO_E2E=1 vitest run src/e2e",
|
|
71
|
-
"site": "playwright test"
|
|
72
|
+
"site": "playwright test",
|
|
73
|
+
"cover": "node cover/run.ts"
|
|
72
74
|
}
|
|
73
75
|
}
|