@hanzo/build 0.2.61 → 0.2.62
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/gear.d.ts +2 -0
- package/lib/gear.js +20 -2
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/mic.js +1 -1
- package/lib/prompt.js +5 -3
- package/package.json +2 -2
package/lib/gear.d.ts
CHANGED
|
@@ -49,3 +49,5 @@ export interface GearState {
|
|
|
49
49
|
* writes the default until it has an id). Signed out, it lives for the page.
|
|
50
50
|
*/
|
|
51
51
|
export declare function useGear(t: Target, key?: string): GearState;
|
|
52
|
+
/** The dictation language the person keeps, null for Auto, and how to change it. */
|
|
53
|
+
export declare function useLanguage(t: Target): readonly [string | null, (id: string | null) => void];
|
package/lib/gear.js
CHANGED
|
@@ -62,7 +62,7 @@ export function shelve(gears, key, g) {
|
|
|
62
62
|
}
|
|
63
63
|
return out;
|
|
64
64
|
}
|
|
65
|
-
let doc = { gear: BARE, gears: {} };
|
|
65
|
+
let doc = { gear: BARE, gears: {}, language: null };
|
|
66
66
|
let loaded = null;
|
|
67
67
|
const listeners = new Set();
|
|
68
68
|
const emit = () => listeners.forEach((f) => f());
|
|
@@ -80,7 +80,8 @@ function load(t) {
|
|
|
80
80
|
const raw = r?.prefs ?? {};
|
|
81
81
|
const gear = gearOf(raw.gear);
|
|
82
82
|
const all = raw.gears && typeof raw.gears === 'object' ? raw.gears : {};
|
|
83
|
-
|
|
83
|
+
const language = typeof raw.language === 'string' && raw.language ? raw.language : null;
|
|
84
|
+
doc = { gear, gears: Object.fromEntries(Object.entries(all).map(([k, v]) => [k, gearOf(v, gear)])), language };
|
|
84
85
|
emit();
|
|
85
86
|
})
|
|
86
87
|
.catch(() => {
|
|
@@ -121,3 +122,20 @@ export function useGear(t, key = '') {
|
|
|
121
122
|
},
|
|
122
123
|
}), [gear, error, write, t]);
|
|
123
124
|
}
|
|
125
|
+
/** The dictation language the person keeps, null for Auto, and how to change it. */
|
|
126
|
+
export function useLanguage(t) {
|
|
127
|
+
useEffect(() => load(t), [t]);
|
|
128
|
+
const now = useSyncExternalStore(subscribe, () => doc, () => doc);
|
|
129
|
+
const set = useCallback((id) => {
|
|
130
|
+
const was = doc.language;
|
|
131
|
+
doc = { ...doc, language: id };
|
|
132
|
+
emit();
|
|
133
|
+
// A refused save takes the choice back, so what is shown is what is kept.
|
|
134
|
+
if (t.token())
|
|
135
|
+
call(t, 'PATCH', '/v1/pref', { language: id }).catch(() => {
|
|
136
|
+
doc = { ...doc, language: was };
|
|
137
|
+
emit();
|
|
138
|
+
});
|
|
139
|
+
}, [t]);
|
|
140
|
+
return [now.language, set];
|
|
141
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export { href, parse, path, PLACES, route, SESSION, SLUG, type Route, type Scree
|
|
|
51
51
|
export { catalog, EFFORTS, FIRST, FREE, grants, isFree, keep, kept, nameOf, reasons, useCatalog, useChoice, useMind, type Choice, type Effort, type Mind } from './mind.ts';
|
|
52
52
|
export { ASK, cut, GAP, MEASURE, measure, Prompt, prompt, ROUND, trigger, Tune, useEar, useMention, useUpload, type Chip, type PromptOptions } from './prompt.tsx';
|
|
53
53
|
export { Plus, type Link, type PlusProps, type Row as ShelfRow, type Shelf, type Switch } from './plus.tsx';
|
|
54
|
-
export { BARE, gearOf, MODES, useGear, type Gear, type GearState, type Mode as GearMode } from './gear.ts';
|
|
54
|
+
export { BARE, gearOf, MODES, useGear, useLanguage, type Gear, type GearState, type Mode as GearMode } from './gear.ts';
|
|
55
55
|
export { useCarry, type Carry } from './carry.tsx';
|
|
56
56
|
export { Dictation } from './mic.tsx';
|
|
57
57
|
export type { Composer as PluginComposer, Plugin, Prep, Turn } from './plugin.ts';
|
package/lib/index.js
CHANGED
|
@@ -46,7 +46,7 @@ export { href, parse, path, PLACES, route, SESSION, SLUG } from './route.js';
|
|
|
46
46
|
export { catalog, EFFORTS, FIRST, FREE, grants, isFree, keep, kept, nameOf, reasons, useCatalog, useChoice, useMind } from './mind.js';
|
|
47
47
|
export { ASK, cut, GAP, MEASURE, measure, Prompt, prompt, ROUND, trigger, Tune, useEar, useMention, useUpload } from './prompt.js';
|
|
48
48
|
export { Plus } from './plus.js';
|
|
49
|
-
export { BARE, gearOf, MODES, useGear } from './gear.js';
|
|
49
|
+
export { BARE, gearOf, MODES, useGear, useLanguage } from './gear.js';
|
|
50
50
|
export { useCarry } from './carry.js';
|
|
51
51
|
export { Dictation } from './mic.js';
|
|
52
52
|
export { artifacts, blob, lead, renders, Reply, Served, speakable, speaker } from './reply.js';
|
package/lib/mic.js
CHANGED
|
@@ -91,5 +91,5 @@ export function Dictation({ voice, disabled = false, language = null, onLanguage
|
|
|
91
91
|
const [open, setOpen] = useState(false);
|
|
92
92
|
const { inputs, allowed, using } = useInputs(open);
|
|
93
93
|
const spoken = LANGUAGES.find((l) => l.id === language);
|
|
94
|
-
return (_jsxs(XStack, { "data-slot": "dictation", items: "center", rounded: 8, borderWidth: 1, borderColor: open ? '$borderColor' : 'transparent', hoverStyle: { borderColor: '$borderColor' }, shrink: 0, children: [_jsx(Control, { asChild: true, size: 30, ...PLAIN, children: _jsx(Voice, { voice: voice, disabled: disabled, says: { idle: 'Dictate', listening: 'Dictating — click to stop' }, children: (state) => _jsx(MicIcon, { size: 16, fill: state === 'idle' ? 'none' : '
|
|
94
|
+
return (_jsxs(XStack, { "data-slot": "dictation", items: "center", rounded: 8, borderWidth: 1, borderColor: open ? '$borderColor' : 'transparent', hoverStyle: { borderColor: '$borderColor' }, shrink: 0, children: [_jsx(Control, { asChild: true, size: 30, ...PLAIN, children: _jsx(Voice, { voice: voice, disabled: disabled, says: { idle: 'Dictate', listening: 'Dictating — click to stop' }, children: (state) => _jsx(MicIcon, { size: 16, color: "var(--foreground)", fill: state === 'idle' ? 'none' : 'var(--foreground)' }) }) }), _jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, disabled: disabled, children: _jsx(XStack, { render: "button", "aria-label": "Dictation settings", "data-slot": "dictation-menu", width: 16, height: 30, items: "center", justify: "center", rounded: 6, cursor: "pointer", bg: open ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, "$max-sm": { width: 28, height: 44 }, children: _jsx(ChevronDown, { size: 14, color: "$aside" }) }) }), _jsxs(DropdownMenuContent, { align: "end", minW: 200, "data-slot": "dictation-panel", children: [_jsx(Meter, { on: open && allowed }), inputs.length === 0 ? (_jsx(DropdownMenuLabel, { children: _jsx(SizableText, { fontSize: 14, color: "$hint", children: "No devices found" }) })) : (inputs.map((d, i) => (_jsx(DropdownMenuLabel, { children: _jsxs(XStack, { items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", numberOfLines: 1, flex: 1, children: d.label || `Microphone ${i + 1}` }), d.groupId === using ? _jsx(Check, { size: 14, color: "var(--primary)", "aria-label": "In use" }) : null] }) }, d.deviceId || i)))), onLanguage ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { children: _jsx(SizableText, { fontSize: 13, color: "$hint", children: "Dictation settings" }) }), _jsxs(DropdownMenuSub, { children: [_jsx(DropdownMenuSubTrigger, { "data-slot": "dictation-language", children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", flex: 1, children: "Language" }), _jsx(SizableText, { fontSize: 14, color: "$hint", children: spoken?.label ?? 'Auto' })] }) }), _jsx(DropdownMenuSubContent, { minW: 160, children: [{ id: '', label: 'Auto' }, ...LANGUAGES].map((l) => (_jsx(DropdownMenuItem, { onSelect: () => onLanguage(l.id || null), children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", flex: 1, children: l.label }), (language ?? '') === l.id ? _jsx(Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) }, l.id || 'auto'))) })] })] })) : null] })] })] }));
|
|
95
95
|
}
|
package/lib/prompt.js
CHANGED
|
@@ -42,6 +42,8 @@ export const ROUND = 32;
|
|
|
42
42
|
/** The frame's corner, px: 12 on a laptop, 22 on a phone. */
|
|
43
43
|
const CORNER = 12;
|
|
44
44
|
const CORNER_PHONE = 22;
|
|
45
|
+
/** The ink on a filled round: the accent's own foreground (gui's icons default to the theme's text). */
|
|
46
|
+
const ON = 'var(--primary-foreground)';
|
|
45
47
|
/** A round control with no ground of its own until it is pointed at. */
|
|
46
48
|
const QUIET = { bg: 'transparent', color: 'var(--foreground)', hoverStyle: { bg: '$hover' } };
|
|
47
49
|
/** What every Composer placeholder says, unless a surface names who is listening. */
|
|
@@ -127,7 +129,7 @@ export function prompt(o, state) {
|
|
|
127
129
|
};
|
|
128
130
|
const mention = listed && shelf && typed ? (_jsx(Mention, { shelf: shelf, rows: listed, index: index, onPick: choose, tag: typed.char === '/' ? 'Skill' : 'Plugin', name: typed.char === '/' ? 'Skills' : 'Plugins' })) : null;
|
|
129
131
|
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;
|
|
130
|
-
const foot = o.mind || o.mode || o.where || (!narrow && o.below) ? (_jsxs(YStack, {
|
|
132
|
+
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 }), narrow ? null : o.below] })) : undefined;
|
|
131
133
|
const round = (_jsx(Control, { type: "button", size: ROUND, fill: true, rounded: 999, "data-slot": "composer-send", "aria-label": busy ? 'Stop' : 'Send', "aria-disabled": !live || undefined, opacity: live ? 1 : 0.4, onClick: () => {
|
|
132
134
|
if (!live)
|
|
133
135
|
return;
|
|
@@ -135,7 +137,7 @@ export function prompt(o, state) {
|
|
|
135
137
|
o.onStop?.();
|
|
136
138
|
else
|
|
137
139
|
o.onSend();
|
|
138
|
-
}, children: busy ? _jsx(Square, { size: 13, fill:
|
|
140
|
+
}, children: busy ? _jsx(Square, { size: 13, color: ON, fill: ON }) : _jsx(ArrowUp, { size: 16, strokeWidth: 2.5, color: ON }) }));
|
|
139
141
|
return {
|
|
140
142
|
value: o.value,
|
|
141
143
|
onChange: o.onChange,
|
|
@@ -171,7 +173,7 @@ export function prompt(o, state) {
|
|
|
171
173
|
'$max-sm': { rounded: CORNER_PHONE },
|
|
172
174
|
...(o.framed ? { boxShadow: 'var(--shadow-sheet-1)' } : null),
|
|
173
175
|
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, pl: 8, pr: 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": `Remove ${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)))] })),
|
|
174
|
-
send: (_jsxs(XStack, { items: "center", gap: 6, "data-slot": "prompt-end", shrink: 0, children: [o.voice ? _jsx(Dictation, { voice: o.voice, disabled: disabled, language: o.language ?? null, ...(o.onLanguage ? { onLanguage: o.onLanguage } : {}) }) : null, !busy && !lit && o.talk ? (_jsx(Control, { asChild: true, size: 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 }) }) })) : (round)] })),
|
|
176
|
+
send: (_jsxs(XStack, { items: "center", gap: 6, "data-slot": "prompt-end", shrink: 0, children: [o.voice ? _jsx(Dictation, { voice: o.voice, disabled: disabled, language: o.language ?? null, ...(o.onLanguage ? { onLanguage: o.onLanguage } : {}) }) : null, !busy && !lit && o.talk ? (_jsx(Control, { asChild: true, size: 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)] })),
|
|
175
177
|
};
|
|
176
178
|
}
|
|
177
179
|
/** Ctrl+U (⌘U) uploads, while a composer with an upload is on the page. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/build",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.62",
|
|
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 component, mounted by the Hanzo app at hanzo.ai.",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
},
|
|
73
73
|
"peerDependencies": {
|
|
74
74
|
"@hanzo/gui": ">=8.3.5",
|
|
75
|
-
"@hanzo/ui": ">=8.
|
|
75
|
+
"@hanzo/ui": ">=8.29.0",
|
|
76
76
|
"@hanzogui/lucide-icons-2": ">=8.3.5",
|
|
77
77
|
"react": ">=19.2.8",
|
|
78
78
|
"@hanzo/voice": ">=0.1.16",
|