@hanzo/build 0.2.80 → 0.2.82
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 +13 -0
- package/lib/activity.d.ts +3 -7
- package/lib/activity.js +18 -5
- package/lib/api/agents.d.ts +1 -20
- package/lib/api/agents.js +16 -8
- package/lib/api/auto.d.ts +3 -23
- package/lib/api/auto.js +39 -22
- package/lib/api/billing.d.ts +0 -35
- package/lib/api/billing.js +6 -5
- package/lib/api/call.d.ts +16 -8
- package/lib/api/call.js +32 -1
- package/lib/api/capabilities.d.ts +0 -11
- package/lib/api/capabilities.js +2 -1
- package/lib/api/codebases.d.ts +0 -10
- package/lib/api/codebases.js +3 -2
- package/lib/api/coding.d.ts +3 -32
- package/lib/api/coding.js +13 -7
- package/lib/api/connectors.d.ts +1 -17
- package/lib/api/connectors.js +5 -4
- package/lib/api/environment.d.ts +6 -20
- package/lib/api/environment.js +22 -19
- package/lib/api/github.d.ts +0 -22
- package/lib/api/github.js +3 -2
- package/lib/api/keys.d.ts +0 -14
- package/lib/api/keys.js +4 -3
- package/lib/api/library.d.ts +3 -17
- package/lib/api/library.js +23 -22
- package/lib/api/links.d.ts +1 -1
- package/lib/api/links.js +1 -1
- package/lib/api/machines.d.ts +0 -15
- package/lib/api/machines.js +6 -5
- package/lib/api/mcp.d.ts +0 -9
- package/lib/api/mcp.js +3 -2
- package/lib/api/members.d.ts +0 -15
- package/lib/api/members.js +4 -3
- package/lib/api/memory.d.ts +0 -11
- package/lib/api/memory.js +2 -1
- package/lib/api/places.d.ts +1 -9
- package/lib/api/places.js +12 -1
- package/lib/api/plugins.d.ts +1 -16
- package/lib/api/plugins.js +4 -3
- package/lib/api/profile.d.ts +0 -10
- package/lib/api/profile.js +5 -4
- package/lib/api/projects.d.ts +0 -14
- package/lib/api/projects.js +11 -10
- package/lib/api/provider.d.ts +2 -14
- package/lib/api/provider.js +12 -10
- package/lib/api/sandbox.d.ts +0 -17
- package/lib/api/sandbox.js +5 -4
- package/lib/api/sessions.d.ts +0 -17
- package/lib/api/sessions.js +5 -4
- package/lib/api/skills.d.ts +1 -15
- package/lib/api/skills.js +5 -4
- package/lib/api/studio.d.ts +0 -14
- package/lib/api/studio.js +6 -5
- package/lib/api/turn.d.ts +6 -28
- package/lib/api/turn.js +105 -49
- package/lib/api/verdict.d.ts +0 -9
- package/lib/api/verdict.js +2 -1
- package/lib/api/webhooks.d.ts +1 -14
- package/lib/api/webhooks.js +5 -4
- package/lib/ask.js +7 -4
- package/lib/automations.d.ts +5 -4
- package/lib/automations.js +95 -74
- package/lib/back.js +10 -4
- package/lib/builder.js +7 -5
- package/lib/carry.js +20 -16
- package/lib/customize/agents.js +78 -31
- package/lib/customize/connectors.js +62 -48
- package/lib/customize/index.js +43 -10
- package/lib/customize/plugins.js +34 -22
- package/lib/customize/say.d.ts +13 -1
- package/lib/customize/say.js +15 -21
- package/lib/customize/skills.js +31 -22
- package/lib/customize/ui.d.ts +5 -9
- package/lib/customize/ui.js +26 -16
- package/lib/data.d.ts +1 -1
- package/lib/data.js +3 -2
- package/lib/desk.js +45 -33
- package/lib/door.js +35 -10
- package/lib/environment.js +25 -19
- package/lib/files.js +43 -34
- package/lib/find.js +12 -9
- package/lib/foot.js +7 -4
- package/lib/forge.js +61 -40
- package/lib/gear.d.ts +2 -1
- package/lib/gear.js +6 -4
- package/lib/git.js +29 -19
- package/lib/landing.d.ts +5 -5
- package/lib/landing.js +52 -44
- package/lib/library.js +73 -51
- package/lib/merge.d.ts +2 -2
- package/lib/meter.d.ts +1 -1
- package/lib/meter.js +14 -5
- package/lib/mic.js +12 -5
- package/lib/mind.d.ts +1 -0
- package/lib/mind.js +5 -3
- package/lib/nav.d.ts +6 -5
- package/lib/nav.js +37 -30
- package/lib/panel.d.ts +2 -1
- package/lib/panel.js +52 -36
- package/lib/plan.d.ts +4 -20
- package/lib/plan.js +26 -10
- package/lib/plans.js +49 -30
- package/lib/plugins/design.js +3 -2
- package/lib/plugins/index.d.ts +23 -4
- package/lib/plugins/index.js +11 -26
- package/lib/plugins/pdf.js +2 -1
- package/lib/plugins/project.js +7 -4
- package/lib/plugins/research.js +3 -2
- package/lib/plugins/sketch.js +6 -3
- package/lib/plugins/slides.js +3 -2
- package/lib/plugins/template.js +6 -3
- package/lib/plus.js +25 -13
- package/lib/prefs.d.ts +0 -14
- package/lib/prefs.js +2 -1
- package/lib/project.js +69 -62
- package/lib/prompt.d.ts +12 -11
- package/lib/prompt.js +38 -20
- package/lib/publish.js +17 -12
- package/lib/reply.js +6 -3
- package/lib/run.js +72 -54
- package/lib/say.d.ts +2 -0
- package/lib/say.js +7 -0
- package/lib/scheduled.js +54 -43
- package/lib/section.d.ts +1 -0
- package/lib/section.js +8 -2
- package/lib/settings/account.d.ts +1 -0
- package/lib/settings/account.js +31 -21
- package/lib/settings/billing.js +55 -28
- package/lib/settings/capabilities.js +26 -15
- package/lib/settings/card.js +19 -12
- package/lib/settings/code.js +22 -11
- package/lib/settings/credits.d.ts +4 -4
- package/lib/settings/credits.js +34 -21
- package/lib/settings/environments.js +21 -14
- package/lib/settings/general.js +26 -14
- package/lib/settings/index.js +6 -3
- package/lib/settings/integrations.js +42 -28
- package/lib/settings/keys.js +37 -19
- package/lib/settings/list.d.ts +0 -5
- package/lib/settings/list.js +24 -15
- package/lib/settings/machines.d.ts +3 -0
- package/lib/settings/machines.js +63 -38
- package/lib/settings/members.js +23 -14
- package/lib/settings/memory.js +12 -6
- package/lib/settings/notifications.js +32 -19
- package/lib/settings/plan.d.ts +1 -1
- package/lib/settings/plan.js +40 -17
- package/lib/settings/privacy.js +26 -13
- package/lib/settings/ui.d.ts +3 -2
- package/lib/settings/ui.js +9 -3
- package/lib/settings/usage.js +7 -3
- package/lib/shelf.js +20 -17
- package/lib/side.d.ts +1 -26
- package/lib/side.js +11 -5
- package/lib/standing.d.ts +2 -10
- package/lib/standing.js +5 -3
- package/lib/suggest.d.ts +8 -28
- package/lib/suggest.js +39 -4
- package/lib/suggestions.js +14 -5
- package/lib/summary.d.ts +1 -1
- package/lib/summary.js +4 -1
- package/lib/sync.js +22 -12
- package/lib/tools.d.ts +4 -3
- package/lib/tools.js +10 -7
- package/lib/transcript.js +10 -8
- package/package.json +4 -2
package/lib/prompt.js
CHANGED
|
@@ -17,8 +17,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
17
17
|
* composer through `Chat` (its `composer` prop) and one that draws it directly
|
|
18
18
|
* (`<Prompt>`) draw the same thing. The frame is as wide as the text above it
|
|
19
19
|
* (`measure`).
|
|
20
|
+
*
|
|
21
|
+
* Its words are the reader's language (@hanzo/i18n): the components here ask
|
|
22
|
+
* `useI18n` themselves, and `prompt`, which is not a component, takes `t` from
|
|
23
|
+
* `useMention`'s state, English without one. Model names, a host's own words
|
|
24
|
+
* (its placeholder, a shelf's rows and links) and what a person typed are
|
|
25
|
+
* drawn as given.
|
|
20
26
|
*/
|
|
21
27
|
import { SizableText, useMedia, XStack, YStack } from '@hanzo/gui';
|
|
28
|
+
import { phrase } from '@hanzo/i18n';
|
|
29
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
22
30
|
import { ArrowUp, AudioLines, Check, Square, X } from '@hanzogui/lucide-icons-2';
|
|
23
31
|
import { Control } from '@hanzo/composer';
|
|
24
32
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSearch, DropdownMenuTrigger, Popover, PopoverContent, PopoverTrigger } from '@hanzo/ui';
|
|
@@ -31,6 +39,7 @@ import { MODES } from './gear.js';
|
|
|
31
39
|
import { Dictation } from './mic.js';
|
|
32
40
|
import { EFFORTS, grants, nameOf, reasons, ROUTER_IDS } from './mind.js';
|
|
33
41
|
import { Plus } from './plus.js';
|
|
42
|
+
import { english } from './say.js';
|
|
34
43
|
/** The reading measure: @hanzo/design's prose width, which Width (appearance) retunes. */
|
|
35
44
|
export const MEASURE = 'var(--container-prose, 48rem)';
|
|
36
45
|
/** The space between turns, px: one rhythm for Chat and Dev. */
|
|
@@ -50,7 +59,7 @@ const QUIET = { bg: 'transparent', color: 'var(--foreground)', hoverStyle: { bg:
|
|
|
50
59
|
/** The model rows the menu draws before anything is typed. */
|
|
51
60
|
const SHOWN = 40;
|
|
52
61
|
/** What every Composer placeholder says, unless a surface names who is listening. */
|
|
53
|
-
export const ASK = 'Type / for skills';
|
|
62
|
+
export const ASK = phrase('Type / for skills');
|
|
54
63
|
/**
|
|
55
64
|
* The frame as a pane of its own, floating on the pane it is drawn in: the
|
|
56
65
|
* `--pane-*` cut on design's first paper rung.
|
|
@@ -71,9 +80,10 @@ export function useEar(t) {
|
|
|
71
80
|
function Word({ children, dim = false, label, open }) {
|
|
72
81
|
return (_jsx(XStack, { render: "button", "aria-label": label, "aria-expanded": open, items: "center", height: 24, px: 6, rounded: 6, cursor: "pointer", bg: open ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineColor: '$outlineColor', outlineWidth: 2, outlineStyle: 'solid' }, "$max-sm": { height: 44, px: 8 }, children: _jsx(SizableText, { fontSize: 14, fontWeight: dim ? '400' : '500', color: dim ? '$hint' : '$ink', numberOfLines: 1, children: children }) }));
|
|
73
82
|
}
|
|
74
|
-
/** A titled list of choices, each with a line saying what it does and a check on the one in force. */
|
|
83
|
+
/** A titled list of choices, each with a line saying what it does and a check on the one in force. The items' words are marked (`phrase`); the title comes translated. */
|
|
75
84
|
function Choices({ title, items, value, onPick, }) {
|
|
76
|
-
|
|
85
|
+
const { t } = useI18n();
|
|
86
|
+
return (_jsxs(YStack, { role: "listbox", "aria-label": title, gap: 2, "data-slot": "choices", children: [_jsx(SizableText, { fontSize: 13, color: "$hint", px: 10, pt: 6, pb: 4, children: title }), items.map((c) => (_jsxs(XStack, { render: "button", role: "option", "aria-selected": c.id === value, "aria-disabled": c.off || undefined, onPress: () => !c.off && onPick(c.id), items: "flex-start", gap: 12, px: 10, py: 8, rounded: 8, width: "100%", cursor: c.off ? 'default' : 'pointer', opacity: c.off ? 0.5 : 1, hoverStyle: c.off ? undefined : { bg: '$hover' }, children: [_jsxs(YStack, { flex: 1, minW: 0, items: "flex-start", gap: 2, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", text: "start", children: t(c.label) }), _jsx(SizableText, { fontSize: 13, color: "$hint", text: "start", children: t(c.note) })] }), c.id === value ? _jsx(Check, { size: 16, color: "var(--primary)", mt: 2, "aria-hidden": true }) : null] }, c.id)))] }));
|
|
77
87
|
}
|
|
78
88
|
/** A word in the row that opens a panel of choices. */
|
|
79
89
|
function Opens({ label, word, dim, children }) {
|
|
@@ -82,6 +92,7 @@ function Opens({ label, word, dim, children }) {
|
|
|
82
92
|
}
|
|
83
93
|
/** The model's name, and the catalog behind it: Free's lane to pick from, the rest an upgrade. */
|
|
84
94
|
function Model({ mind, onUpgrade, limits }) {
|
|
95
|
+
const { t } = useI18n();
|
|
85
96
|
const [open, setOpen] = useState(false);
|
|
86
97
|
const [q, setQ] = useState('');
|
|
87
98
|
// The catalog is hundreds long, and drawing all of it is what made the menu slow
|
|
@@ -101,33 +112,39 @@ function Model({ mind, onUpgrade, limits }) {
|
|
|
101
112
|
return { groups: cut, more: list.length - cut.reduce((n, g) => n + g.models.length, 0) };
|
|
102
113
|
}, [mind.models, q]);
|
|
103
114
|
const routed = mind.model === ENSO;
|
|
104
|
-
return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, placement: "top-end", children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(XStack, { "data-slot": "mind", children: _jsx(Word, { label:
|
|
115
|
+
return (_jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, placement: "top-end", children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(XStack, { "data-slot": "mind", children: _jsx(Word, { label: t('Model: {name}', { name: nameOf(mind.model, mind.models) }), open: open, children: nameOf(mind.model, mind.models) }) }) }), _jsxs(DropdownMenuContent, { align: "end", minW: 280, "data-slot": "model-menu", children: [_jsx(DropdownMenuSearch, { value: q, onValueChange: setQ, placeholder: t('Search models') }), _jsxs(YStack, { maxH: "min(420px, 60vh)", overflowY: "auto", overflowX: "hidden", "data-slot": "model-rows", children: [!mind.free && !q.trim() ? (_jsx(DropdownMenuItem, { onSelect: () => mind.pick(ENSO), children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsxs(YStack, { flex: 1, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", children: "Enso" }), _jsx(SizableText, { fontSize: 12, color: "$hint", children: t('Picks the model for each message and step') })] }), routed ? _jsx(Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) })) : null, groups.map((g) => (_jsxs(YStack, { role: "group", "aria-label": g.label, children: [_jsx(SizableText, { fontSize: 12, color: "$hint", px: 8, pt: 8, pb: 2, children: g.label }), g.models.map((m) => {
|
|
105
116
|
const ok = grants(mind.free, m.id, mind.models);
|
|
106
117
|
// Paused by the plan's usage: still listed and still picked; the gateway answers from Enso.
|
|
107
118
|
const paused = isPaused(m, limits);
|
|
108
|
-
return (_jsx(DropdownMenuItem, { "data-slot": "model-row", "data-model": m.id, "data-paused": paused ? 'true' : undefined, onSelect: () => (ok ? mind.pick(m.id) : onUpgrade?.(m.id)), children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: ok ? '$ink' : '$hint', numberOfLines: 1, flex: 1, children: nameOf(m.id, mind.models) }), ok ? null : (_jsx(SizableText, { fontSize: 12, color: "$hint", px: 6, py: 1, rounded: 6, borderWidth: 1, borderColor: "$borderColor", children:
|
|
109
|
-
})] }, g.key))), more > 0 ? (_jsx(SizableText, { fontSize: 12, color: "$hint", px: 8, py: 8, children:
|
|
119
|
+
return (_jsx(DropdownMenuItem, { "data-slot": "model-row", "data-model": m.id, "data-paused": paused ? 'true' : undefined, onSelect: () => (ok ? mind.pick(m.id) : onUpgrade?.(m.id)), children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: ok ? '$ink' : '$hint', numberOfLines: 1, flex: 1, children: nameOf(m.id, mind.models) }), ok ? null : (_jsx(SizableText, { fontSize: 12, color: "$hint", px: 6, py: 1, rounded: 6, borderWidth: 1, borderColor: "$borderColor", children: t('Upgrade') })), ok && paused ? (_jsx(SizableText, { fontSize: 12, color: "$hint", children: t('Paused') })) : null, m.id === mind.model ? _jsx(Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) }, m.id));
|
|
120
|
+
})] }, g.key))), more > 0 ? (_jsx(SizableText, { fontSize: 12, color: "$hint", px: 8, py: 8, children: t('Type to search {count} more models', { count: more }) })) : null] })] })] }));
|
|
110
121
|
}
|
|
111
122
|
/** The row under the frame: where (Dev), then the model, the effort and the mode. */
|
|
112
123
|
export function Tune({ mind, mode, where, onUpgrade, limits }) {
|
|
124
|
+
const { t } = useI18n();
|
|
113
125
|
const thinks = mind ? reasons(mind.model, mind.models) : false;
|
|
114
126
|
const pace = mind ? EFFORTS.find((e) => e.id === mind.effort) : null;
|
|
115
|
-
|
|
127
|
+
const now = mode ? MODES.find((m) => m.id === mode.value) : undefined;
|
|
128
|
+
const way = now ? t(now.label) : '';
|
|
129
|
+
return (_jsxs(XStack, { "data-slot": "tune", width: "100%", items: "center", gap: 2, flexWrap: "wrap", rowGap: 4, children: [where ? (_jsx(XStack, { items: "center", gap: 2, flexWrap: "wrap", minW: 0, "$max-sm": { width: '100%' }, children: where })) : null, _jsx(XStack, { flex: 1, "$max-sm": { display: 'none' } }), _jsxs(XStack, { items: "center", gap: 2, marginInlineStart: "auto", shrink: 0, children: [mind ? _jsx(Model, { mind: mind, onUpgrade: onUpgrade, limits: limits }) : null, mind && thinks && pace ? (_jsx(Opens, { label: t('Effort: {level}', { level: t(pace.label) }), word: t(pace.label), dim: true, children: (close) => (_jsx(Choices, { title: t('Effort'), items: EFFORTS, value: mind.effort, onPick: (e) => {
|
|
116
130
|
mind.pace(e);
|
|
117
131
|
close();
|
|
118
|
-
} })) })) : null, mode ? (_jsxs(Popover, { allowFlip: true, placement: "top-end", children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(XStack, { render: "button", "data-slot": "mode", "aria-label":
|
|
132
|
+
} })) })) : null, mode ? (_jsxs(Popover, { allowFlip: true, placement: "top-end", children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(XStack, { render: "button", "data-slot": "mode", "aria-label": t('Mode: {mode}', { mode: way }), items: "center", height: 24, px: 8, marginInlineStart: 4, rounded: 6, bg: "$hover", cursor: "pointer", hoverStyle: { bg: '$raised' }, focusVisibleStyle: { outlineColor: '$outlineColor', outlineWidth: 2, outlineStyle: 'solid' }, "$max-sm": { height: 44, px: 12 }, children: _jsx(SizableText, { fontSize: 14, color: "$ink", children: way }) }) }), _jsx(PopoverContent, { solid: true, p: 6, width: 288, maxW: "calc(100vw - 24px)", children: _jsx(Choices, { title: t('Mode'), items: mode.items ? MODES.filter((m) => mode.items.includes(m.id)) : MODES, value: mode.value, onPick: mode.set }) })] })) : null] })] }));
|
|
119
133
|
}
|
|
120
134
|
/** The `/` or `@` the caret ends on, and the words typed after it. */
|
|
121
135
|
export function trigger(value) {
|
|
122
136
|
const m = /(^|\s)([/@])([^\s/@]*)$/.exec(value);
|
|
123
137
|
return m ? { char: m[2], query: m[3].toLowerCase(), at: m.index + m[1].length } : null;
|
|
124
138
|
}
|
|
125
|
-
/** The list `/` or `@` opens above the frame. */
|
|
126
|
-
function Mention({ shelf, rows, index, onPick,
|
|
127
|
-
|
|
139
|
+
/** The list `/` (the skills) or `@` (the plugins) opens above the frame. */
|
|
140
|
+
function Mention({ shelf, rows, index, onPick, skills }) {
|
|
141
|
+
const { t } = useI18n();
|
|
142
|
+
const tag = t(skills ? 'Skill' : 'Plugin');
|
|
143
|
+
return (_jsxs(YStack, { role: "listbox", "aria-label": t(skills ? 'Skills' : 'Plugins'), "data-slot": "mention", width: "100%", maxH: 240, overflowY: "auto", overflowX: "hidden", p: 6, rounded: CORNER, borderWidth: 1, borderColor: "$borderColor", bg: "$background", "$max-sm": { rounded: CORNER_PHONE }, children: [_jsx(SizableText, { fontSize: 13, color: "$hint", px: 8, pt: 4, pb: 6, children: shelf.search ?? t(skills ? 'Type to search skills' : 'Type to search plugins') }), rows.length ? (rows.map((r, i) => (_jsxs(XStack, { render: "button", role: "option", "aria-selected": i === index, onPress: () => onPick(r.id), items: "center", gap: 10, px: 8, height: 36, rounded: 8, bg: i === index ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, cursor: "pointer", "$max-sm": { height: 46 }, children: [r.icon ? (_jsx(XStack, { width: 16, items: "center", justify: "center", "aria-hidden": true, children: r.icon })) : null, _jsx(SizableText, { fontSize: 15, color: "$ink", numberOfLines: 1, flex: 1, text: "start", children: r.label }), _jsx(SizableText, { fontSize: 13, color: "$hint", children: r.on ? t('On') : tag })] }, r.id)))) : (_jsx(SizableText, { fontSize: 14, color: "$hint", px: 8, py: 8, children: t(shelf.loading ? 'Reading…' : 'Nothing matches') }))] }));
|
|
128
144
|
}
|
|
129
145
|
/** @hanzo/ui `Composer` props for the one composer. */
|
|
130
146
|
export function prompt(o, state) {
|
|
147
|
+
const t = state?.t ?? english;
|
|
131
148
|
const lit = o.ready ?? Boolean(o.value.trim());
|
|
132
149
|
const busy = Boolean(o.busy);
|
|
133
150
|
const disabled = Boolean(o.disabled);
|
|
@@ -144,10 +161,10 @@ export function prompt(o, state) {
|
|
|
144
161
|
o.onChange(o.value.slice(0, typed.at).trimEnd() + (typed.at > 0 ? ' ' : ''));
|
|
145
162
|
shelf.pick(id);
|
|
146
163
|
};
|
|
147
|
-
const mention = listed && shelf && typed ? (_jsx(Mention, { shelf: shelf, rows: listed, index: index, onPick: choose,
|
|
164
|
+
const mention = listed && shelf && typed ? (_jsx(Mention, { shelf: shelf, rows: listed, index: index, onPick: choose, skills: typed.char === '/' })) : null;
|
|
148
165
|
const head = o.head || o.note || mention || (narrow && o.below) ? (_jsxs(YStack, { ...measure, gap: "$1.5", "data-slot": "prompt-head", children: [narrow ? o.below : null, o.head, mention, o.note ? (_jsx(SizableText, { "data-slot": "prompt-note", fontSize: 12, color: "$soft", text: "center", self: "center", children: o.note })) : null] })) : undefined;
|
|
149
166
|
const foot = o.mind || o.mode || o.where || (!narrow && o.below) ? (_jsxs(YStack, { ...measure, gap: 10, "data-slot": "prompt-foot", children: [_jsx(Tune, { mind: o.mind, mode: o.mode, where: o.where, onUpgrade: o.onUpgrade, limits: o.limits }), narrow ? null : o.below] })) : undefined;
|
|
150
|
-
const round = (_jsx(Control, { type: "button", size: narrow ? TAP : ROUND, fill: true, rounded: 999, "data-slot": "composer-send", "aria-label": busy ? 'Stop' : 'Send', "aria-disabled": !live || undefined, opacity: live ? 1 : 0.4, onClick: () => {
|
|
167
|
+
const round = (_jsx(Control, { type: "button", size: narrow ? TAP : ROUND, fill: true, rounded: 999, "data-slot": "composer-send", "aria-label": t(busy ? 'Stop' : 'Send'), "aria-disabled": !live || undefined, opacity: live ? 1 : 0.4, onClick: () => {
|
|
151
168
|
if (!live)
|
|
152
169
|
return;
|
|
153
170
|
if (busy)
|
|
@@ -166,7 +183,7 @@ export function prompt(o, state) {
|
|
|
166
183
|
...(o.onStop ? { onStop: o.onStop } : {}),
|
|
167
184
|
busy,
|
|
168
185
|
disabled,
|
|
169
|
-
placeholder: o.placeholder ?? ASK,
|
|
186
|
+
placeholder: o.placeholder ?? t(ASK),
|
|
170
187
|
...(o.label ? { label: o.label } : {}),
|
|
171
188
|
// At rest two rows: one line of text, then the tools. It grows with the text to the kit's ceiling, then scrolls.
|
|
172
189
|
rows: 1,
|
|
@@ -191,8 +208,8 @@ export function prompt(o, state) {
|
|
|
191
208
|
bg: '$edge',
|
|
192
209
|
'$max-sm': { rounded: CORNER_PHONE },
|
|
193
210
|
...(o.framed ? { boxShadow: 'var(--shadow-sheet-1)' } : null),
|
|
194
|
-
children: (_jsxs(XStack, { items: "center", gap: 4, "data-slot": "prompt-start", flex: 1, minW: 0, flexWrap: "wrap", children: [o.plus ? _jsx(Plus, { ...o.plus, disabled: disabled || o.plus.disabled }) : null, (o.on ?? []).map((c) => (_jsxs(XStack, { "data-slot": "prompt-chip", items: "center", gap: 6,
|
|
195
|
-
send: (_jsxs(XStack, { items: "center", gap: 6, "data-slot": "prompt-end", shrink: 0, children: [o.voice ? _jsx(Dictation, { voice: o.voice, disabled: disabled, narrow: narrow, language: o.language ?? null, ...(o.onLanguage ? { onLanguage: o.onLanguage } : {}) }) : null, !busy && !lit && o.talk ? (_jsx(Control, { asChild: true, size: narrow ? TAP : ROUND, fill: narrow, rounded: 999, ...(narrow ? null : QUIET), children: _jsx(Voice, { voice: o.talk, disabled: disabled, "data-slot": "composer-talk", says: { idle: 'Voice mode', listening: 'In conversation — click to hang up', speaking: 'Hanzo is speaking — talk to interrupt' }, children: () => _jsx(AudioLines, { size: 16, color: narrow ? ON : 'var(--foreground)' }) }) })) : (round)] })),
|
|
211
|
+
children: (_jsxs(XStack, { items: "center", gap: 4, "data-slot": "prompt-start", flex: 1, minW: 0, flexWrap: "wrap", children: [o.plus ? _jsx(Plus, { ...o.plus, disabled: disabled || o.plus.disabled }) : null, (o.on ?? []).map((c) => (_jsxs(XStack, { "data-slot": "prompt-chip", items: "center", gap: 6, paddingInlineStart: 8, paddingInlineEnd: 4, height: 28, rounded: 8, bg: "$hover", maxW: 180, children: [c.icon ? _jsx(XStack, { "aria-hidden": true, children: c.icon }) : null, _jsx(SizableText, { fontSize: 13, color: "$ink", numberOfLines: 1, shrink: 1, children: c.label }), _jsx(XStack, { render: "button", "aria-label": t('Remove {name}', { name: c.label }), onPress: c.onRemove, width: 20, height: 20, rounded: 6, items: "center", justify: "center", hoverStyle: { bg: '$raised' }, cursor: "pointer", children: _jsx(X, { size: 12, color: "$hint" }) })] }, c.id)))] })),
|
|
212
|
+
send: (_jsxs(XStack, { items: "center", gap: 6, "data-slot": "prompt-end", shrink: 0, children: [o.voice ? _jsx(Dictation, { voice: o.voice, disabled: disabled, narrow: narrow, language: o.language ?? null, ...(o.onLanguage ? { onLanguage: o.onLanguage } : {}) }) : null, !busy && !lit && o.talk ? (_jsx(Control, { asChild: true, size: narrow ? TAP : ROUND, fill: narrow, rounded: 999, ...(narrow ? null : QUIET), children: _jsx(Voice, { voice: o.talk, disabled: disabled, "data-slot": "composer-talk", says: { idle: t('Voice mode'), listening: t('In conversation — click to hang up'), speaking: t('Hanzo is speaking — talk to interrupt') }, children: () => _jsx(AudioLines, { size: 16, color: narrow ? ON : 'var(--foreground)' }) }) })) : (round)] })),
|
|
196
213
|
};
|
|
197
214
|
}
|
|
198
215
|
/** Ctrl+U (⌘U) uploads, while a composer with an upload is on the page. */
|
|
@@ -213,11 +230,12 @@ export function useUpload(upload) {
|
|
|
213
230
|
}, [Boolean(upload)]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
214
231
|
}
|
|
215
232
|
/**
|
|
216
|
-
* The composer's own state — which row of a `/` or `@` list is lit
|
|
217
|
-
* keys that move it. A surface that hands `prompt` to
|
|
218
|
-
* passes its answer through; `<Prompt>` does it for itself.
|
|
233
|
+
* The composer's own state — which row of a `/` or `@` list is lit, and the
|
|
234
|
+
* reader's `t` — and the keys that move it. A surface that hands `prompt` to
|
|
235
|
+
* `Chat` calls this and passes its answer through; `<Prompt>` does it for itself.
|
|
219
236
|
*/
|
|
220
237
|
export function useMention(o) {
|
|
238
|
+
const { t } = useI18n();
|
|
221
239
|
const [index, setIndex] = useState(0);
|
|
222
240
|
const [closed, setClosed] = useState(null);
|
|
223
241
|
useUpload(o.plus?.upload);
|
|
@@ -245,7 +263,7 @@ export function useMention(o) {
|
|
|
245
263
|
o.field?.onKeyDown?.(e);
|
|
246
264
|
},
|
|
247
265
|
};
|
|
248
|
-
return { state: { index, closed }, field };
|
|
266
|
+
return { state: { index, closed, t }, field };
|
|
249
267
|
}
|
|
250
268
|
/** The composer drawn on its own. */
|
|
251
269
|
export function Prompt(o) {
|
package/lib/publish.js
CHANGED
|
@@ -16,9 +16,11 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
16
16
|
* project's address again.
|
|
17
17
|
*
|
|
18
18
|
* Every state shown is one the platform reported; a refusal shows the
|
|
19
|
-
* platform's own sentence.
|
|
19
|
+
* platform's own sentence. The dialog's own words are the reader's language
|
|
20
|
+
* (@hanzo/i18n `t`).
|
|
20
21
|
*/
|
|
21
22
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
23
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
22
24
|
import { ExternalLink } from '@hanzogui/lucide-icons-2';
|
|
23
25
|
import { Button, Dialog, DialogContent, DialogDescription, DialogTitle, Input } from '@hanzo/ui';
|
|
24
26
|
import { useEffect, useRef, useState } from 'react';
|
|
@@ -26,8 +28,9 @@ import { builds, declare, label } from './api/platform.js';
|
|
|
26
28
|
import { useHost, useTarget } from './host.js';
|
|
27
29
|
import { Out } from './out.js';
|
|
28
30
|
export function Publish({ source, onClose, onLive }) {
|
|
29
|
-
const
|
|
31
|
+
const target = useTarget();
|
|
30
32
|
const host = useHost();
|
|
33
|
+
const { t } = useI18n();
|
|
31
34
|
const [project, setProject] = useState('');
|
|
32
35
|
const [phase, setPhase] = useState('ask');
|
|
33
36
|
const [out, setOut] = useState(null);
|
|
@@ -50,7 +53,7 @@ export function Publish({ source, onClose, onLive }) {
|
|
|
50
53
|
let live = true;
|
|
51
54
|
const tick = async () => {
|
|
52
55
|
try {
|
|
53
|
-
const row = (await builds(
|
|
56
|
+
const row = (await builds(target)).find((b) => b.id === id);
|
|
54
57
|
if (live && row)
|
|
55
58
|
setStatus(row.status);
|
|
56
59
|
}
|
|
@@ -64,7 +67,7 @@ export function Publish({ source, onClose, onLive }) {
|
|
|
64
67
|
live = false;
|
|
65
68
|
clearInterval(every);
|
|
66
69
|
};
|
|
67
|
-
}, [
|
|
70
|
+
}, [target, out, phase]);
|
|
68
71
|
const green = /^(succeeded|success|done|complete|completed|ok)$/i.test(status);
|
|
69
72
|
// A site's build going green is the site going live, at the project's address.
|
|
70
73
|
useEffect(() => {
|
|
@@ -77,7 +80,7 @@ export function Publish({ source, onClose, onLive }) {
|
|
|
77
80
|
setPhase('sending');
|
|
78
81
|
setError('');
|
|
79
82
|
try {
|
|
80
|
-
const d = await declare(
|
|
83
|
+
const d = await declare(target, { repo: source.repo, ref: source.ref, name: source.name, project: project || source.name, mode: 'branch' });
|
|
81
84
|
setOut(d);
|
|
82
85
|
setStatus(d.build?.status ?? '');
|
|
83
86
|
setPhase('declared');
|
|
@@ -94,7 +97,7 @@ export function Publish({ source, onClose, onLive }) {
|
|
|
94
97
|
setPhase('shipping');
|
|
95
98
|
setError('');
|
|
96
99
|
try {
|
|
97
|
-
await declare(
|
|
100
|
+
await declare(target, { repo: source.repo, ref: source.ref, name: source.name, project: project || source.name, mode: 'commit', tag: built });
|
|
98
101
|
setPhase('shipped');
|
|
99
102
|
}
|
|
100
103
|
catch (e) {
|
|
@@ -102,11 +105,13 @@ export function Publish({ source, onClose, onLive }) {
|
|
|
102
105
|
setPhase('declared');
|
|
103
106
|
}
|
|
104
107
|
};
|
|
105
|
-
return (_jsx(Dialog, { open: true, onOpenChange: (o) => !o && onClose(), children: _jsxs(DialogContent, { maxW: 480, children: [_jsx(DialogTitle, { children:
|
|
106
|
-
?
|
|
107
|
-
:
|
|
108
|
-
? 'Declared on main — CD applies it on its next pass.'
|
|
108
|
+
return (_jsx(Dialog, { open: true, onOpenChange: (o) => !o && onClose(), children: _jsxs(DialogContent, { maxW: 480, children: [_jsx(DialogTitle, { children: t('Add to project') }), _jsx(DialogDescription, { children: out?.live
|
|
109
|
+
? t('Build {repo} at {ref} and publish it at its project’s address.', { repo: source.title, ref: source.ref })
|
|
110
|
+
: `${t('Build {repo} at {ref} and declare it in a project.', { repo: source.title, ref: source.ref })} ${host.admin ? t('As an admin you can take a green build to main.') : t('It opens a review; merging it is what deploys.')}` }), _jsxs(YStack, { gap: "$1.5", children: [_jsx(SizableText, { render: "label", size: "$2", color: "$soft", htmlFor: "publish-project", children: t('Project') }), _jsx(Input, { id: "publish-project", value: project, onChangeText: (v) => setProject(v), disabled: phase !== 'ask' && phase !== 'failed', placeholder: label(source.name), "aria-label": t('Project') }), _jsx(SizableText, { size: "$1", color: "$soft", children: t('Saved as {name}', { name: label(project || source.name) }) })] }), out ? (_jsxs(YStack, { gap: "$2", role: "status", children: [out.notice ? (_jsx(SizableText, { size: "$2", color: "$soft", children: out.notice })) : null, _jsx(SizableText, { size: "$2", color: "$ink", children: phase === 'shipped'
|
|
111
|
+
? t('Declared on main — CD applies it on its next pass.')
|
|
109
112
|
: out.live && green
|
|
110
|
-
?
|
|
111
|
-
:
|
|
113
|
+
? t('Build {id} succeeded — it is live at the project’s address.', { id: built })
|
|
114
|
+
: status
|
|
115
|
+
? t('Build {id}: {status}', { id: built, status })
|
|
116
|
+
: t('Build {id}: building', { id: built }) }), out.review ? (_jsxs(Out, { href: out.review, children: [_jsx(SizableText, { size: "$2", color: "$ink", textDecorationLine: "underline", children: t('Open the review') }), _jsx(ExternalLink, { size: 12, opacity: 0.6 })] })) : null] })) : null, error ? (_jsx(SizableText, { size: "$2", color: "$red10", role: "alert", children: error })) : null, _jsxs(XStack, { gap: "$2", justify: "flex-end", children: [_jsx(Button, { variant: "outline", size: "sm", onPress: onClose, children: phase === 'shipped' || phase === 'declared' ? t('Done') : t('Cancel') }), phase === 'ask' || phase === 'failed' || phase === 'sending' ? (_jsx(Button, { size: "sm", disabled: phase === 'sending', onPress: () => void send(), children: phase === 'sending' ? t('Adding…') : t('Add to project') })) : host.admin && !out?.live && (phase === 'declared' || phase === 'shipping') ? (_jsx(Button, { size: "sm", disabled: !green || phase === 'shipping', onPress: () => void ship(), children: phase === 'shipping' ? t('Shipping…') : green ? t('Ship to main') : t('Waiting for the build') })) : null] })] }) }));
|
|
112
117
|
}
|
package/lib/reply.js
CHANGED
|
@@ -9,6 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
9
9
|
* from its own bytes (`blob`).
|
|
10
10
|
*/
|
|
11
11
|
import { SizableText, XStack } from '@hanzo/gui';
|
|
12
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
12
13
|
import { Check, Copy, PanelRight, Square, Volume2 } from '@hanzogui/lucide-icons-2';
|
|
13
14
|
import { mouth, speech } from '@hanzo/voice';
|
|
14
15
|
import { useEffect, useRef, useState } from 'react';
|
|
@@ -97,6 +98,7 @@ function Act({ label, onPress, children, lit = false }) {
|
|
|
97
98
|
}
|
|
98
99
|
/** Copy, Listen and Open under an answer; `children` follow them. */
|
|
99
100
|
export function Reply({ text, listen, onOpen, children }) {
|
|
101
|
+
const { t: say } = useI18n();
|
|
100
102
|
const [took, setTook] = useState(false);
|
|
101
103
|
const [speaking, setSpeaking] = useState(false);
|
|
102
104
|
const stop = useRef(null);
|
|
@@ -107,7 +109,7 @@ export function Reply({ text, listen, onOpen, children }) {
|
|
|
107
109
|
return () => clearTimeout(t);
|
|
108
110
|
}, [took]);
|
|
109
111
|
useEffect(() => () => stop.current?.(), []);
|
|
110
|
-
return (_jsxs(XStack, { "data-slot": "reply", ml: "$-1", items: "center", gap: "$1.5", flexWrap: "wrap", children: [_jsx(Act, { label: took ? 'Copied' : 'Copy this answer', lit: took, onPress: () => void navigator.clipboard?.writeText(text).then(() => setTook(true), () => { }), children: took ? _jsx(Check, { size: 13 }) : _jsx(Copy, { size: 13 }) }), listen ? (_jsxs(Act, { label: speaking ? 'Stop reading' : 'Listen to this answer', lit: speaking, onPress: () => {
|
|
112
|
+
return (_jsxs(XStack, { "data-slot": "reply", ml: "$-1", items: "center", gap: "$1.5", flexWrap: "wrap", children: [_jsx(Act, { label: took ? say('Copied') : say('Copy this answer'), lit: took, onPress: () => void navigator.clipboard?.writeText(text).then(() => setTook(true), () => { }), children: took ? _jsx(Check, { size: 13 }) : _jsx(Copy, { size: 13 }) }), listen ? (_jsxs(Act, { label: speaking ? say('Stop reading') : say('Listen to this answer'), lit: speaking, onPress: () => {
|
|
111
113
|
if (speaking) {
|
|
112
114
|
stop.current?.();
|
|
113
115
|
setSpeaking(false);
|
|
@@ -115,9 +117,10 @@ export function Reply({ text, listen, onOpen, children }) {
|
|
|
115
117
|
}
|
|
116
118
|
setSpeaking(true);
|
|
117
119
|
stop.current = listen(text, () => setSpeaking(false));
|
|
118
|
-
}, children: [speaking ? _jsx(Square, { size: 12 }) : _jsx(Volume2, { size: 13 }), _jsx(SizableText, { size: "$1", color: "$ink", children: speaking ? 'Stop' : 'Listen' })] })) : null, onOpen ? (_jsxs(Act, { label:
|
|
120
|
+
}, children: [speaking ? _jsx(Square, { size: 12 }) : _jsx(Volume2, { size: 13 }), _jsx(SizableText, { size: "$1", color: "$ink", children: speaking ? say('Stop') : say('Listen') })] })) : null, onOpen ? (_jsxs(Act, { label: say('Open in the side panel'), onPress: onOpen, children: [_jsx(PanelRight, { size: 13 }), _jsx(SizableText, { size: "$1", color: "$ink", children: say('Open') })] })) : null, children] }));
|
|
119
121
|
}
|
|
120
122
|
/** Which model answered, where the gateway says it was not the one asked for or a router. */
|
|
121
123
|
export function Served({ served, asked }) {
|
|
122
|
-
|
|
124
|
+
const { t } = useI18n();
|
|
125
|
+
return (_jsx(SizableText, { size: "$1", color: "$soft", "data-slot": "served", children: asked && asked !== served ? t('answered by {model} · {asked} was unavailable', { model: served, asked }) : t('answered by {model}', { model: served }) }));
|
|
123
126
|
}
|