@hanzo/build 0.2.14 → 0.2.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/lib/builder.js +9 -8
- package/lib/foot.js +10 -3
- package/lib/landing.js +2 -2
- package/package.json +1 -1
- package/lib/switch.d.ts +0 -1
- package/lib/switch.js +0 -39
package/lib/builder.js
CHANGED
|
@@ -28,7 +28,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
28
28
|
* left column, never two.
|
|
29
29
|
*/
|
|
30
30
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
31
|
-
import { Blocks, BookOpen, CircleDot, Cpu, FolderGit2, Kanban, LayoutTemplate, Menu, SlidersHorizontal, Workflow } from '@hanzogui/lucide-icons-2';
|
|
31
|
+
import { Blocks, BookOpen, CircleDot, Cpu, FolderGit2, Kanban, LayoutTemplate, Menu, Search, SlidersHorizontal, Workflow } from '@hanzogui/lucide-icons-2';
|
|
32
32
|
import { Button } from '@hanzo/ui';
|
|
33
33
|
import { SessionRail } from '@hanzo/ui/chat';
|
|
34
34
|
import { HanzoMark } from '@hanzo/ui/product';
|
|
@@ -49,7 +49,6 @@ import { Artifacts, Templates } from './shelf.js';
|
|
|
49
49
|
import { Find } from './find.js';
|
|
50
50
|
import { DOCS, Slack, Who } from './foot.js';
|
|
51
51
|
import { Settings } from './settings/index.js';
|
|
52
|
-
import { Where } from './switch.js';
|
|
53
52
|
function Shell() {
|
|
54
53
|
const host = useHost();
|
|
55
54
|
const t = useTarget();
|
|
@@ -72,10 +71,11 @@ function Shell() {
|
|
|
72
71
|
return list;
|
|
73
72
|
}, [recents.value, order]);
|
|
74
73
|
const screen = r.kind === 'screen' ? r.screen : '';
|
|
75
|
-
// The top left
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
// The top left, as on claude.ai: the builder's name, which leads to New, and under
|
|
75
|
+
// it the search that finds a run. The organization is the account's, so it is
|
|
76
|
+
// switched from the account menu at the foot. Beside the rail from md up, beside
|
|
77
|
+
// the drawer's button below it — one place at a time.
|
|
78
|
+
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
79
|
const links = [
|
|
80
80
|
{ id: 'projects', label: 'Projects', icon: _jsx(Kanban, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'projects' })), active: screen === 'projects' },
|
|
81
81
|
{ id: 'artifacts', label: 'Artifacts', icon: _jsx(Blocks, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'artifacts' })), active: screen === 'artifacts' },
|
|
@@ -106,13 +106,14 @@ function Shell() {
|
|
|
106
106
|
];
|
|
107
107
|
return (_jsxs(XStack, { flex: 1, minH: 0, minW: 0, bg: "$background", children: [_jsxs(YStack, { position: "relative", shrink: 0, children: [_jsx(SessionRail, { onNew: () => go(''), fresh: r.kind === 'new', links: links, more: more, recents: rows, active: r.kind === 'run' ? r.id : null, onOpen: (id) => go(id), onSort: () => setOrder(order === 'newest' ? 'running' : 'newest'), sortLabel: order === 'newest' ? 'Show running first' : 'Show newest first', empty: _jsx(SizableText, { size: "$1", color: "$soft", px: "$2", children: !host.person ? 'Sign in to see your runs.' : recents.error ? recents.error.message : recents.loading ? 'Reading…' : 'No runs yet.' }), notice: host.person && !slack ? (_jsx(Slack, { onOpen: () => go(path({ kind: 'settings', section: 'integrations' })), onDismiss: () => setSlack(true) })) : undefined, account: host.person
|
|
108
108
|
? {
|
|
109
|
-
|
|
109
|
+
// Who, and in which organization, as claude.ai says who and on which plan.
|
|
110
|
+
name: [host.person.name || host.person.email, host.org].filter(Boolean).join(' · '),
|
|
110
111
|
onPress: () => {
|
|
111
112
|
setDrawer(false);
|
|
112
113
|
setMenu(true);
|
|
113
114
|
},
|
|
114
115
|
}
|
|
115
|
-
: { name: 'Sign in', onPress: () => host.signIn?.() },
|
|
116
|
+
: { name: 'Sign in', onPress: () => host.signIn?.() }, collapsed: collapsed, onCollapse: setCollapsed, mark: _jsx(Brand, { org: host.org }), pt: collapsed ? undefined : 92, open: drawer, onOpenChange: setDrawer, label: "Runs" }), collapsed ? null : (_jsxs(YStack, { position: "absolute", t: 10, l: 8, r: 8, z: 2, gap: "$2.5", "$max-md": { display: 'none' }, children: [_jsx(XStack, { px: "$2.5", children: name }), _jsxs(XStack, { render: "button", "aria-label": "Search runs", onPress: () => setFinding(true), items: "center", gap: "$2", px: "$2.5", height: 34, rounded: "$3", borderWidth: 1, borderColor: "$borderColor", bg: "$panel", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 15, color: "$soft" }), _jsx(SizableText, { size: "$2", color: "$soft", children: "Search" })] })] })), 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 }), _jsx(Button, { variant: "ghost", size: "icon-sm", onPress: () => setFinding(true), "aria-label": "Search runs", children: _jsx(Search, { size: 18 }) })] }), _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/foot.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* The rail's foot: the card that offers Hanzo in Slack, and the menu the account
|
|
4
|
-
* row opens — who is signed in,
|
|
4
|
+
* row opens — who is signed in, the organization they act in, Settings, Usage,
|
|
5
|
+
* the plans, help and logging out.
|
|
5
6
|
* Everything it opens is an address of this page, but help, which is the
|
|
6
7
|
* documentation.
|
|
7
8
|
*/
|
|
8
9
|
import { Popover, SizableText, XStack, YStack } from '@hanzo/gui';
|
|
9
10
|
import { Gauge, LifeBuoy, LogOut, Settings, Slack as Mark, Sparkles, X } from '@hanzogui/lucide-icons-2';
|
|
10
|
-
import { MenuRow, MenuRule } from '@hanzo/ui/product';
|
|
11
|
+
import { MenuLabel, MenuRow, MenuRule } from '@hanzo/ui/product';
|
|
11
12
|
import { useHost } from './host.js';
|
|
12
13
|
import { path } from './route.js';
|
|
13
14
|
export const DOCS = 'https://docs.hanzo.ai/docs/dev';
|
|
@@ -22,11 +23,17 @@ export function Slack({ onOpen, onDismiss }) {
|
|
|
22
23
|
*/
|
|
23
24
|
export function Who({ open, onOpenChange, narrow }) {
|
|
24
25
|
const host = useHost();
|
|
26
|
+
// The organizations this person acts in; choosing one reloads the page scoped to it.
|
|
27
|
+
const orgs = host.memberships ?? (host.org ? [host.org] : []);
|
|
25
28
|
const go = (p) => {
|
|
26
29
|
onOpenChange(false);
|
|
27
30
|
host.go(p);
|
|
28
31
|
};
|
|
29
|
-
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 6, children: [_jsx(Popover.Anchor, { position: "absolute", l: 8, b: 8, width: narrow ? 48 : 256, height: 36, pointerEvents: "none" }), _jsx(Popover.Content, { role: "menu", "aria-label": "Account", items: "stretch", borderWidth: 1, elevation: "$2", px: "$1", py: "$1", width: 260, bg: "$panel", borderColor: "$borderColor", children: _jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person?.email || host.person?.name || '' }),
|
|
32
|
+
return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, placement: narrow ? 'right-end' : 'top-start', allowFlip: true, stayInFrame: true, offset: 6, children: [_jsx(Popover.Anchor, { position: "absolute", l: 8, b: 8, width: narrow ? 48 : 256, height: 36, pointerEvents: "none" }), _jsx(Popover.Content, { role: "menu", "aria-label": "Account", items: "stretch", borderWidth: 1, elevation: "$2", px: "$1", py: "$1", width: 260, bg: "$panel", borderColor: "$borderColor", children: _jsxs(YStack, { gap: "$1", children: [_jsx(SizableText, { size: "$2", color: "$soft", px: "$2", py: "$1.5", numberOfLines: 1, children: host.person?.email || host.person?.name || '' }), orgs.length ? (_jsxs(_Fragment, { children: [_jsx(MenuRule, {}), _jsx(MenuLabel, { children: "Organization" }), _jsx(YStack, { role: "radiogroup", "aria-label": "Organizations", gap: "$1", children: orgs.map((o) => (_jsx(MenuRow, { label: o, active: o === host.org, onPress: () => {
|
|
33
|
+
onOpenChange(false);
|
|
34
|
+
if (o !== host.org)
|
|
35
|
+
host.chooseOrg?.(o);
|
|
36
|
+
} }, o))) })] })) : null, _jsx(MenuRule, {}), _jsx(MenuRow, { label: "Settings", icon: _jsx(Settings, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'general' })) }), _jsx(MenuRow, { label: "Usage", icon: _jsx(Gauge, { size: 16 }), onPress: () => go(path({ kind: 'settings', section: 'usage' })) }), _jsx(MenuRow, { label: "View all plans", icon: _jsx(Sparkles, { size: 16 }), onPress: () => go(path({ kind: 'screen', screen: 'plans' })) }), _jsx(MenuRow, { label: "Get help", icon: _jsx(LifeBuoy, { size: 16 }), onPress: () => {
|
|
30
37
|
onOpenChange(false);
|
|
31
38
|
window.open(DOCS, '_blank', 'noopener,noreferrer');
|
|
32
39
|
} }), host.signOut ? (_jsxs(_Fragment, { children: [_jsx(MenuRule, {}), _jsx(MenuRow, { label: "Log out", icon: _jsx(LogOut, { size: 16 }), onPress: () => {
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/build",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.16",
|
|
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).",
|
package/lib/switch.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function Where(): import("react").JSX.Element;
|
package/lib/switch.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* The rail's top left: the organization, and the projects under it.
|
|
4
|
-
*
|
|
5
|
-
* The account stays at the foot of the rail. Settings opens that account on
|
|
6
|
-
* this page. Choosing an organization reloads this origin so every read is
|
|
7
|
-
* scoped to it.
|
|
8
|
-
*/
|
|
9
|
-
import { YStack } from '@hanzo/gui';
|
|
10
|
-
import { MenuLabel, MenuRow, MenuRule, OrgSwitcher } from '@hanzo/ui/product';
|
|
11
|
-
import { projects } from './api/projects.js';
|
|
12
|
-
import { useRead } from './data.js';
|
|
13
|
-
import { useHost, useTarget } from './host.js';
|
|
14
|
-
import { route } from './route.js';
|
|
15
|
-
export function Where() {
|
|
16
|
-
const host = useHost();
|
|
17
|
-
const t = useTarget();
|
|
18
|
-
const signed = Boolean(host.person);
|
|
19
|
-
const list = useRead(signed ? () => projects(t) : null, [], [t, signed]);
|
|
20
|
-
const here = route(host.path);
|
|
21
|
-
const current = host.org || '';
|
|
22
|
-
const scope = {
|
|
23
|
-
currentOrg: () => current,
|
|
24
|
-
setCurrentOrg: (next) => host.chooseOrg?.(next),
|
|
25
|
-
isScopedAway: () => false,
|
|
26
|
-
hasSelectedOrg: () => Boolean(current),
|
|
27
|
-
enterOrg: (next) => host.chooseOrg?.(next),
|
|
28
|
-
leaveOrg: () => undefined,
|
|
29
|
-
switchOrg: (next) => host.chooseOrg?.(next),
|
|
30
|
-
};
|
|
31
|
-
const rows = () => Promise.resolve((host.memberships ?? (current ? [current] : [])).map((name) => ({ name, displayName: name })));
|
|
32
|
-
return (_jsx(OrgSwitcher, { scope: scope, orgs: (page) => (page === 0 ? rows() : Promise.resolve([])), pageSize: 50, search: false, heading: "Organization", lead: true, sub: here.kind === 'project' ? here.slug : undefined, current: current ? { name: current, displayName: current } : undefined, aria: current ? `Organization ${current}` : 'Organization', footer: (close) => (_jsxs(YStack, { gap: "$1", children: [list.value.length > 0 ? (_jsxs(_Fragment, { children: [_jsx(MenuRule, {}), _jsx(MenuLabel, { children: "Project" }), _jsx(YStack, { role: "radiogroup", "aria-label": "Projects", gap: "$1", children: list.value.slice(0, 12).map((p) => (_jsx(MenuRow, { label: p.name || p.slug, active: here.kind === 'project' && here.slug === p.slug, onPress: () => {
|
|
33
|
-
close();
|
|
34
|
-
host.go(p.slug);
|
|
35
|
-
} }, p.slug))) })] })) : null, _jsx(MenuRule, {}), _jsx(MenuRow, { label: "Settings", onPress: () => {
|
|
36
|
-
close();
|
|
37
|
-
host.go('-/settings');
|
|
38
|
-
} })] })) }));
|
|
39
|
-
}
|