@hanzo/build 0.2.13 → 0.2.15
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/builder.js +5 -4
- package/lib/desk.js +1 -1
- package/lib/files.js +1 -1
- package/lib/landing.js +2 -2
- 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/builder.js
CHANGED
|
@@ -72,10 +72,11 @@ function Shell() {
|
|
|
72
72
|
return list;
|
|
73
73
|
}, [recents.value, order]);
|
|
74
74
|
const screen = r.kind === 'screen' ? r.screen : '';
|
|
75
|
-
// The top left: the
|
|
76
|
-
//
|
|
75
|
+
// The top left: the builder's name, which leads to New, and under it the
|
|
76
|
+
// organization switcher once there is an org to switch. Beside the rail from md
|
|
77
|
+
// up, beside the drawer's button below it — one place at a time.
|
|
77
78
|
const switcher = Boolean(host.person && (host.org || (host.memberships?.length ?? 0) > 0));
|
|
78
|
-
const
|
|
79
|
+
const name = (_jsx(XStack, { render: "button", "aria-label": "Hanzo Build", items: "center", onPress: () => go(''), children: _jsx(SizableText, { size: "$5", fontWeight: "600", color: "$ink", numberOfLines: 1, children: "Hanzo Build" }) }));
|
|
79
80
|
const links = [
|
|
80
81
|
{ id: 'projects', label: 'Projects', icon: _jsx(Kanban, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'projects' })), active: screen === 'projects' },
|
|
81
82
|
{ id: 'artifacts', label: 'Artifacts', icon: _jsx(Blocks, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'artifacts' })), active: screen === 'artifacts' },
|
|
@@ -112,7 +113,7 @@ function Shell() {
|
|
|
112
113
|
setMenu(true);
|
|
113
114
|
},
|
|
114
115
|
}
|
|
115
|
-
: { name: 'Sign in', onPress: () => host.signIn?.() }, onSettings: () => go(path({ kind: 'settings', section: 'general' })), onSearch: () => setFinding(true), collapsed: collapsed, onCollapse: setCollapsed, mark: _jsx(Brand, { org: host.org }), pt: collapsed ? undefined : switcher ?
|
|
116
|
+
: { name: 'Sign in', onPress: () => host.signIn?.() }, onSettings: () => go(path({ kind: 'settings', section: 'general' })), onSearch: () => setFinding(true), collapsed: collapsed, onCollapse: setCollapsed, mark: _jsx(Brand, { org: host.org }), pt: collapsed ? undefined : switcher ? 92 : 48, open: drawer, onOpenChange: setDrawer, label: "Runs" }), collapsed ? null : (_jsxs(YStack, { position: "absolute", t: 10, l: 12, r: 8, z: 2, gap: "$2", "$max-md": { display: 'none' }, children: [name, switcher ? _jsx(Where, {}) : null] })), host.person ? _jsx(Who, { open: menu, onOpenChange: setMenu, narrow: collapsed }) : null] }), _jsxs(YStack, { flex: 1, minW: 0, minH: 0, position: "relative", children: [_jsxs(XStack, { height: 44, px: "$2", gap: "$2", items: "center", shrink: 0, $md: { display: 'none' }, children: [_jsx(Button, { variant: "ghost", size: "icon-sm", onPress: () => setDrawer(true), "aria-label": "Open runs", children: _jsx(Menu, { size: 18 }) }), name, _jsx(XStack, { flex: 1 }), switcher ? _jsx(Where, {}) : null] }), _jsx(Pane, { onStarted: () => recents.reload() })] }), _jsx(Find, { open: finding, onOpenChange: setFinding, recents: rows, onOpen: (id) => { setFinding(false); go(id); } })] }));
|
|
116
117
|
}
|
|
117
118
|
/** Whichever pane the address names, beside a rail — the builder's or the host's. */
|
|
118
119
|
function Pane({ onStarted }) {
|
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/landing.js
CHANGED
|
@@ -13,7 +13,7 @@ import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
|
13
13
|
import { Cloud, Monitor } from '@hanzogui/lucide-icons-2';
|
|
14
14
|
import { Button } from '@hanzo/ui';
|
|
15
15
|
import { ModeSelect } from '@hanzo/ui/agents';
|
|
16
|
-
import { Composer
|
|
16
|
+
import { Composer } from '@hanzo/ui/chat';
|
|
17
17
|
import { BranchSelect, ChipSelect, FooterLink, HanzoMark, RepoSelect } from '@hanzo/ui/product';
|
|
18
18
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
19
19
|
import { start, unhonoured } from './api/coding.js';
|
|
@@ -165,7 +165,7 @@ export function Landing({ onStarted }) {
|
|
|
165
165
|
},
|
|
166
166
|
}, placeholder: "Codebase", disabled: !signed }, round), isForge(kept.repo) ? _jsx(BranchSelect, { value: branch, onChange: (b) => set({ branch: b }), load: loadBranches }) : null, _jsx(Files, { files: files, onFiles: setFiles })] }));
|
|
167
167
|
const foot = (_jsxs(XStack, { flex: 1, items: "center", gap: "$2", flexWrap: "wrap", rowGap: "$1", children: [_jsx(Attach, { files: files, onFiles: setFiles, onNote: setNote }), _jsx(Dictate, { onText: (said) => setDraft((d) => (d ? `${d} ${said}` : said)), onNote: setNote }), _jsx(ModeSelect, { modes: MODES, value: kept.mode, onChange: (m) => set({ mode: m }), bg: "transparent", minH: 24, px: "$1.5", self: "center" }), _jsx(XStack, { flex: 1 }), _jsx(ChipSelect, { quiet: true, name: "Model", label: catalog.value.find((m) => m.id === kept.model)?.label ?? 'Enso', chosen: catalog.value.find((m) => m.id === kept.model) ?? null, items: catalog.value, onChange: (m) => set({ model: m.id }), placeholder: "Search models\u2026", placement: "top-end", loading: catalog.loading, error: catalog.error?.message ?? null }), _jsx(ChipSelect, { quiet: true, name: "Effort", label: EFFORTS.find((e) => e.id === kept.effort)?.label ?? 'Medium', chosen: EFFORTS.find((e) => e.id === kept.effort) ?? null, items: EFFORTS, onChange: (e) => set({ effort: e.id }), placement: "top-end", width: 180 })] }));
|
|
168
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, minW: 0, items: "center", px: "$4", children: [_jsxs(YStack, { width: "100%", maxW: COLUMN,
|
|
168
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, minW: 0, items: "center", justify: "center", px: "$4", pb: "$10", overflow: "scroll", children: [_jsxs(YStack, { width: "100%", maxW: COLUMN, gap: "$5", children: [_jsxs(XStack, { role: "heading", "aria-level": 1, justify: "center", items: "center", gap: "$3", flexWrap: "wrap", children: [_jsx(HanzoMark, { size: 26 }), _jsx(SizableText, { size: "$8", color: "$ink", style: { textAlign: 'center' }, children: prefs.callName ? `What’s up next, ${prefs.callName}?` : 'What’s up next?' })] }), _jsxs(YStack, { gap: "$2", children: [env.value && env.value.state !== 'ready' && !place.id ? (_jsxs(XStack, { items: "center", justify: "space-between", gap: "$3", px: "$3", py: "$2", rounded: "$10", borderWidth: 1, borderColor: "$borderColor", children: [_jsx(SizableText, { size: "$2", color: "$ink", flex: 1, minW: 0, children: env.value.state === 'proposed'
|
|
169
169
|
? `A proposed environment for ${codebase} is waiting for review.`
|
|
170
170
|
: `${codebase} has no environment yet, so every run starts it bare.` }), env.value.state === 'proposed' && env.value.session ? (_jsx(Button, { size: "sm", variant: "outline", onPress: () => host.go(env.value.session), children: "Review" })) : (_jsx(Button, { size: "sm", onPress: () => setOffering(true), children: "Set up environment" }))] })) : null, note || unhonoured(kept.mode, place.id) ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note || unhonoured(kept.mode, place.id) })) : null, _jsx(Composer, { inline: true, value: draft, onChange: setDraft, onSend: () => void send(), busy: busy, placeholder: "Describe a task or ask a question", label: "Describe a task or ask a question", head: head, foot: foot })] })] }), _jsx(Publish, { source: adding, onClose: () => setAdding(null) }), codebase ? (_jsx(SetupDialog, { repo: codebase, open: offering, onOpenChange: setOffering, onStart: setup, onSaved: () => {
|
|
171
171
|
setOffering(false);
|
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.15",
|
|
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
|
}
|