@hanzo/build 0.2.83 → 0.2.85
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 -10
- package/lib/carry.d.ts +3 -4
- package/lib/carry.js +2 -2
- package/lib/host.d.ts +0 -2
- package/lib/index.d.ts +5 -7
- package/lib/index.js +5 -7
- package/lib/landing.js +51 -45
- package/lib/mind.d.ts +28 -5
- package/lib/mind.js +5 -30
- package/lib/plugins/index.d.ts +1 -1
- package/lib/project.js +7 -7
- package/lib/run.js +6 -7
- package/lib/settings/general.js +2 -1
- package/lib/suggestions.js +3 -2
- package/lib/tools.d.ts +9 -6
- package/lib/tools.js +5 -14
- package/lib/transcript.js +1 -2
- package/lib/voice.d.ts +4 -4
- package/lib/voice.js +11 -11
- package/package.json +3 -3
- package/lib/mic.d.ts +0 -12
- package/lib/mic.js +0 -139
- package/lib/plus.d.ts +0 -46
- package/lib/plus.js +0 -72
- package/lib/prompt.d.ts +0 -134
- package/lib/prompt.js +0 -272
package/README.md
CHANGED
|
@@ -119,12 +119,13 @@ One of each, shared with Chat (@hanzo/rooms imports them):
|
|
|
119
119
|
|
|
120
120
|
- `useMind` — the model and effort: `GET /v1/models` read once per platform,
|
|
121
121
|
and the choice kept in this browser (`hanzo.mind`, Enso and Auto until
|
|
122
|
-
changed).
|
|
123
|
-
Max) always, the model chip only where the
|
|
124
|
-
|
|
122
|
+
changed). The composer's row changes it: the effort (Auto, High, XHigh,
|
|
123
|
+
Max) always, the model chip only where the person turned the model selector
|
|
124
|
+
on (Settings → Appearance, `@hanzo/appearance` `composing`). Settings → Code
|
|
125
125
|
writes the same choice. `@hanzo/build/mind` serves the store alone, without
|
|
126
|
-
the builder.
|
|
127
|
-
-
|
|
126
|
+
the builder. `pacing(mind)` is the effort as the composer reads it.
|
|
127
|
+
- The composer is `@hanzo/ui/chat`'s `Composer`, once for Chat, Dev and Studio;
|
|
128
|
+
`useEar` is the platform's ear it dictates through.
|
|
128
129
|
- `Reply` — copy, Listen and Open under an answer.
|
|
129
130
|
- `Panel` over `useDeck` — the side panel and its kept tabs.
|
|
130
131
|
- `useSplit` / `SideProvider` — the column the panel stands in: its edge (the
|
|
@@ -256,10 +257,12 @@ are any member's.
|
|
|
256
257
|
(Cloud is the platform's sandbox with the codebase's environment; the org's
|
|
257
258
|
machines follow under remote control, each linked with `hanzo link`), the
|
|
258
259
|
codebase and the branch — the forge's repositories, filtered as you type, read
|
|
259
|
-
again
|
|
260
|
-
there yet — then the ask, in the composer Chat draws too (`
|
|
261
|
-
and Build or Plan at its start; the effort, the model where the
|
|
262
|
-
|
|
260
|
+
again each time the list opens, and a way to bring a GitHub repository that is
|
|
261
|
+
not there yet — then the ask, in the composer Chat draws too (`@hanzo/ui/chat`): the
|
|
262
|
+
paperclip and Build or Plan at its start; the effort, the model where the person
|
|
263
|
+
turned the selector on, dictation and send at its end. The three pills sit above
|
|
264
|
+
the field (environment, repository with its All / Organization / GitHub tabs, branch),
|
|
265
|
+
and Repos, Branches, Issues and Templates under it, each a press to its list. A codebase can be added to a project. Opening a codebase or an
|
|
263
266
|
issue from its own screen lands here with that choice already made.
|
|
264
267
|
|
|
265
268
|
**A run.** The transcript as it streams, drawn by what each part is: what the
|
|
@@ -373,7 +376,7 @@ src/
|
|
|
373
376
|
prose.tsx markdown, drawn as text (markdown.ts reads it)
|
|
374
377
|
desk.tsx the pane beside a run
|
|
375
378
|
mind.ts the model and effort, one choice with Chat
|
|
376
|
-
|
|
379
|
+
voice.ts the language dictation listens in, and the platform's ear
|
|
377
380
|
reply.tsx the row under an answer
|
|
378
381
|
panel.tsx the side panel; tabs.ts its kept tabs
|
|
379
382
|
side.tsx the column it stands in: width, the middle column, full screen
|
package/lib/carry.d.ts
CHANGED
|
@@ -2,8 +2,7 @@ import { type ReactNode } from 'react';
|
|
|
2
2
|
import { type Target } from './api/call.ts';
|
|
3
3
|
import { type GearState, type Mode } from './gear.ts';
|
|
4
4
|
import type { Composer, Prep, Turn } from './plugin.ts';
|
|
5
|
-
import type { Shelf } from '
|
|
6
|
-
import type { Chip, PromptOptions } from './prompt.tsx';
|
|
5
|
+
import type { Carried, Modes, Shelf } from '@hanzo/ui/chat';
|
|
7
6
|
/** What the surface lends the plugins: its draft, its files, its way about. */
|
|
8
7
|
export type Lent = Omit<Composer, 'target' | 'show'>;
|
|
9
8
|
export interface Carry {
|
|
@@ -20,9 +19,9 @@ export interface Carry {
|
|
|
20
19
|
/** What the plugins that are on add to the message about to go. */
|
|
21
20
|
prep: (turn: Omit<Turn, 'design'>, signal: AbortSignal) => Promise<Prep>;
|
|
22
21
|
/** The chips after `+`: the skills on, Research, and Plan. */
|
|
23
|
-
on:
|
|
22
|
+
on: Carried[];
|
|
24
23
|
/** The mode pill: this surface's modes, the conversation's choice among them. */
|
|
25
|
-
mode:
|
|
24
|
+
mode: Modes<Mode>;
|
|
26
25
|
/** Whether `what` may run: in Manual the person is asked (once, always in this conversation, or not); else yes. */
|
|
27
26
|
allow: (what: Asked) => Promise<boolean>;
|
|
28
27
|
/** The words the gear adds to every message, '' when none. */
|
package/lib/carry.js
CHANGED
|
@@ -26,7 +26,7 @@ import { active, authored, document, nameOf as skillOf } from './api/skills.js';
|
|
|
26
26
|
import { brand, brands } from './api/studio.js';
|
|
27
27
|
import { useRead } from './data.js';
|
|
28
28
|
import { said as designSaid } from './design.js';
|
|
29
|
-
import { DEV_MODES, useGear } from './gear.js';
|
|
29
|
+
import { DEV_MODES, MODES, useGear } from './gear.js';
|
|
30
30
|
import { active as lighted, prep, shelf as pluginShelf, usePlugins } from './plugins/index.js';
|
|
31
31
|
/** The most of each skill a message carries. */
|
|
32
32
|
const SKILL_MAX = 16_000;
|
|
@@ -171,7 +171,7 @@ export function useCarry(t, key, customize, lent, modes = DEV_MODES) {
|
|
|
171
171
|
};
|
|
172
172
|
return {
|
|
173
173
|
gear,
|
|
174
|
-
mode: { value: mode, set: (m) => gear.set({ mode: m }), items: modes },
|
|
174
|
+
mode: { value: mode, set: (m) => gear.set({ mode: m }), items: MODES.filter((x) => modes.includes(x.id)) },
|
|
175
175
|
allow,
|
|
176
176
|
skills: shelf,
|
|
177
177
|
plugins,
|
package/lib/host.d.ts
CHANGED
|
@@ -107,8 +107,6 @@ export interface Host {
|
|
|
107
107
|
browser?: Browser;
|
|
108
108
|
/** The host's sidebar lists Settings' sections and Customize's tabs, so the pages draw neither. */
|
|
109
109
|
sections?: boolean;
|
|
110
|
-
/** The composer names the model and offers its picker beside the effort: the person turned the model selector on in the host's settings. Absent, the effort alone. */
|
|
111
|
-
models?: boolean;
|
|
112
110
|
/** The default agents Customize › Agents offers to start from. Absent, none. */
|
|
113
111
|
cast?: readonly Cast[];
|
|
114
112
|
/** Every persona Customize › Agents offers to start from, read when Browse opens. Absent, none. */
|
package/lib/index.d.ts
CHANGED
|
@@ -45,16 +45,14 @@ export type { Read } from './data.ts';
|
|
|
45
45
|
export { href, parse, path, PLACES, route, SESSION, SLUG, type Route, type Screen } from './route.ts';
|
|
46
46
|
/**
|
|
47
47
|
* What Chat draws the same way (@hanzo/rooms imports these): the model and
|
|
48
|
-
* effort (`useMind`, one choice kept for both,
|
|
49
|
-
* (`
|
|
50
|
-
*
|
|
48
|
+
* effort (`useMind`, one choice kept for both), the row under an answer
|
|
49
|
+
* (`Reply`), and the side panel (`Panel` over a `Deck` of tabs). The composer is
|
|
50
|
+
* @hanzo/ui/chat's, for Chat and Dev alike.
|
|
51
51
|
*/
|
|
52
|
-
export { catalog, EFFORTS, FIRST, FREE, grants, isFree, keep, kept, nameOf, useCatalog, useChoice, useMind, type Choice, type Effort, type Mind } from './mind.ts';
|
|
53
|
-
export {
|
|
54
|
-
export { Plus, type Link, type PlusProps, type Row as ShelfRow, type Shelf, type Switch } from './plus.tsx';
|
|
52
|
+
export { catalog, EFFORTS, FIRST, FREE, grants, isFree, keep, kept, level, nameOf, pacing, useCatalog, useChoice, useMind, type Choice, type Effort, type Mind } from './mind.ts';
|
|
53
|
+
export { useEar } from './voice.ts';
|
|
55
54
|
export { BARE, CHAT_MODES, DEV_MODES, gearOf, MODES, useGear, useLanguage, type Gear, type GearState, type Mode as GearMode } from './gear.ts';
|
|
56
55
|
export { useCarry, type Carry } from './carry.tsx';
|
|
57
|
-
export { Dictation } from './mic.tsx';
|
|
58
56
|
export { artifacts, blob, lead, renders, Reply, Served, speakable, speaker, type Artifact, type Listen } from './reply.tsx';
|
|
59
57
|
export { frames, Page, Panel, PanelToggle, type Kind, type PageProps, type PanelProps, type Suggestion } from './panel.tsx';
|
|
60
58
|
export { SideProvider, useSplit, type Split } from './side.tsx';
|
package/lib/index.js
CHANGED
|
@@ -40,16 +40,14 @@ export { kind, label, said, share, spent, ways, when } from './plan.js';
|
|
|
40
40
|
export { href, parse, path, PLACES, route, SESSION, SLUG } from './route.js';
|
|
41
41
|
/**
|
|
42
42
|
* What Chat draws the same way (@hanzo/rooms imports these): the model and
|
|
43
|
-
* effort (`useMind`, one choice kept for both,
|
|
44
|
-
* (`
|
|
45
|
-
*
|
|
43
|
+
* effort (`useMind`, one choice kept for both), the row under an answer
|
|
44
|
+
* (`Reply`), and the side panel (`Panel` over a `Deck` of tabs). The composer is
|
|
45
|
+
* @hanzo/ui/chat's, for Chat and Dev alike.
|
|
46
46
|
*/
|
|
47
|
-
export { catalog, EFFORTS, FIRST, FREE, grants, isFree, keep, kept, nameOf, useCatalog, useChoice, useMind } from './mind.js';
|
|
48
|
-
export {
|
|
49
|
-
export { Plus } from './plus.js';
|
|
47
|
+
export { catalog, EFFORTS, FIRST, FREE, grants, isFree, keep, kept, level, nameOf, pacing, useCatalog, useChoice, useMind } from './mind.js';
|
|
48
|
+
export { useEar } from './voice.js';
|
|
50
49
|
export { BARE, CHAT_MODES, DEV_MODES, gearOf, MODES, useGear, useLanguage } from './gear.js';
|
|
51
50
|
export { useCarry } from './carry.js';
|
|
52
|
-
export { Dictation } from './mic.js';
|
|
53
51
|
export { artifacts, blob, lead, renders, Reply, Served, speakable, speaker } from './reply.js';
|
|
54
52
|
export { frames, Page, Panel, PanelToggle } from './panel.js';
|
|
55
53
|
export { SideProvider, useSplit } from './side.js';
|
package/lib/landing.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/**
|
|
3
3
|
* New: the empty state. A heading at the top of the column, and at the foot of
|
|
4
4
|
* the pane the composer — where the run goes (Cloud: the sandbox with the
|
|
5
5
|
* codebase's environment; or one of the org's machines, under remote control),
|
|
6
6
|
* which repository and branch it starts from, and the ask itself — the one
|
|
7
|
-
* composer Chat draws too (
|
|
8
|
-
*
|
|
7
|
+
* composer Chat draws too (@hanzo/ui/chat): Dev hands it the three pills, the
|
|
8
|
+
* chips that go to its repositories, issues and templates, and its commands.
|
|
9
9
|
*
|
|
10
10
|
* Sending starts a coding run on a project and opens it. A project is a
|
|
11
11
|
* repository — the org's on the forge, or one its GitHub grants — so a run
|
|
@@ -19,8 +19,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
19
19
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
20
20
|
import { phrase } from '@hanzo/i18n';
|
|
21
21
|
import { useI18n } from '@hanzo/i18n/react';
|
|
22
|
-
import { Cloud, Monitor } from '@hanzogui/lucide-icons-2';
|
|
22
|
+
import { CircleDot, Cloud, Code, GitBranch, LayoutTemplate, Monitor, Play } from '@hanzogui/lucide-icons-2';
|
|
23
23
|
import { Button } from '@hanzo/ui';
|
|
24
|
+
import { Composer, MEASURE } from '@hanzo/ui/chat';
|
|
24
25
|
import { BranchSelect, ChipSelect, FooterLink, RepoSelect } from '@hanzo/ui/product';
|
|
25
26
|
import { useLimits } from '@hanzo/ui/product/useLimits';
|
|
26
27
|
import { useDictation, useTalk, useVoice } from '@hanzo/voice';
|
|
@@ -36,7 +37,7 @@ import { ready, SANDBOX } from './api/places.js';
|
|
|
36
37
|
import { asHub, isForge, isHub, settle } from './choice.js';
|
|
37
38
|
import { useCached, useKept, usePlaces, useRead } from './data.js';
|
|
38
39
|
import { useHost, useTarget } from './host.js';
|
|
39
|
-
import { level, useMind } from './mind.js';
|
|
40
|
+
import { level, pacing, useMind } from './mind.js';
|
|
40
41
|
import { usePrefs } from './prefs.js';
|
|
41
42
|
import { Publish } from './publish.js';
|
|
42
43
|
import { path } from './route.js';
|
|
@@ -44,10 +45,8 @@ import { useCarry } from './carry.js';
|
|
|
44
45
|
import { DEV_MODES } from './gear.js';
|
|
45
46
|
import { book, hand, Suggestions, taken, useSuggestions } from './suggestions.js';
|
|
46
47
|
import { want } from './library.js';
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
import { compose, Files, pick, read as readFiles } from './tools.js';
|
|
50
|
-
import { useSpoken } from './voice.js';
|
|
48
|
+
import { carrying, compose, pick, read as readFiles } from './tools.js';
|
|
49
|
+
import { useEar, useSpoken } from './voice.js';
|
|
51
50
|
/** One vocabulary for the mode, on New and in a workspace: English, marked (`phrase`), drawn through `t`. */
|
|
52
51
|
export const MODES = [
|
|
53
52
|
{ id: 'build', label: phrase('Build'), hint: phrase('Edits, commits and pushes a branch') },
|
|
@@ -104,9 +103,9 @@ export function Landing({ onStarted }) {
|
|
|
104
103
|
const [note, setNote] = useState('');
|
|
105
104
|
const [adding, setAdding] = useState(null);
|
|
106
105
|
const [offering, setOffering] = useState(false);
|
|
107
|
-
// The repository
|
|
106
|
+
// The repository and branch lists are read again each time they open.
|
|
108
107
|
const [picking, setPicking] = useState(false);
|
|
109
|
-
const [
|
|
108
|
+
const [branching, setBranching] = useState(false);
|
|
110
109
|
// A codebase or an issue hands its words over once. Left in the kept choice,
|
|
111
110
|
// every later visit to New would put them back.
|
|
112
111
|
useEffect(() => {
|
|
@@ -171,7 +170,7 @@ export function Landing({ onStarted }) {
|
|
|
171
170
|
const github = (hub?.repos ?? []).map(asHub);
|
|
172
171
|
for (const r of github)
|
|
173
172
|
hubs.current.set(r.full_name.toLowerCase(), r);
|
|
174
|
-
return { repos: [...repos, ...github], next: null };
|
|
173
|
+
return { repos: [...repos.map((r) => ({ ...r, source: 'forge' })), ...github.map((r) => ({ ...r, source: 'github' }))], next: null };
|
|
175
174
|
}, [target]);
|
|
176
175
|
// Drawn only once a forge codebase is chosen; one the forge no longer lists offers its default branch.
|
|
177
176
|
const home = kept.repo?.default_branch || 'main';
|
|
@@ -306,27 +305,20 @@ export function Landing({ onStarted }) {
|
|
|
306
305
|
setIssue(0);
|
|
307
306
|
set({ repo: row, branch: row.default_branch });
|
|
308
307
|
};
|
|
309
|
-
// Cloud is the sandbox, set up by the chosen codebase's environment.
|
|
310
|
-
const setUp = codebase && env.value
|
|
311
|
-
? env.value.state === 'ready'
|
|
312
|
-
? t('{repo} environment', { repo: codebase })
|
|
313
|
-
: env.value.state === 'proposed'
|
|
314
|
-
? t('{repo} environment proposed', { repo: codebase })
|
|
315
|
-
: unset(env.value)
|
|
316
|
-
? t('no {repo} environment yet', { repo: codebase })
|
|
317
|
-
: t('{repo} environment, empty', { repo: codebase })
|
|
318
|
-
: '';
|
|
308
|
+
// Cloud is the sandbox, set up by the chosen codebase's environment; the rest are the org's machines.
|
|
319
309
|
const placeItems = useMemo(() => (places.value.length ? places.value : [SANDBOX]).map((p) => ({
|
|
320
310
|
id: p.id || 'sandbox',
|
|
321
311
|
label: p.id ? p.label : t('Cloud'),
|
|
322
|
-
hint: p.id ? [
|
|
312
|
+
hint: p.id ? [p.status, p.capacity].filter(Boolean).join(' · ') : '',
|
|
313
|
+
icon: p.id ? _jsx(Monitor, { size: 13, color: "$soft" }) : _jsx(Cloud, { size: 13, color: "$soft" }),
|
|
323
314
|
disabled: !ready(p),
|
|
324
315
|
place: p,
|
|
325
|
-
})), [places.value,
|
|
316
|
+
})), [places.value, t]);
|
|
326
317
|
const branch = kept.branch || kept.repo?.default_branch || 'main';
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
318
|
+
const forge = isForge(kept.repo);
|
|
319
|
+
const screen = (to) => () => host.go(path({ kind: 'screen', screen: to }));
|
|
320
|
+
// The composer's pills: where the run goes, which repository, which branch.
|
|
321
|
+
const pills = (_jsxs(_Fragment, { children: [_jsx(ChipSelect, { name: t('Where the run runs'), icon: place.id ? _jsx(Monitor, { size: 13, color: "$soft" }) : _jsx(Cloud, { size: 13, color: "$soft" }), label: place.id ? place.label : t('Cloud'), chosen: placeItems.find((i) => i.place.id === place.id), items: placeItems, onChange: (i) => set({ place: i.place.id }), placeholder: t('Search machines…'), placement: "top-start", loading: places.loading, error: places.error?.message ?? null, footer: _jsx(FooterLink, { label: t('Manage…'), onPress: () => host.go(path({ kind: 'settings', section: 'machines' })) }) }), _jsx(RepoSelect, { open: picking, onOpenChange: setPicking, value: isForge(kept.repo) ? { ...kept.repo, source: 'forge' } : isHub(kept.repo) ? { ...kept.repo, source: 'github' } : null, onChange: (r) => {
|
|
330
322
|
const hub = r.github ? hubs.current.get((r.full_name ?? "").toLowerCase()) : undefined;
|
|
331
323
|
setIssue(0);
|
|
332
324
|
if (hub) {
|
|
@@ -335,10 +327,11 @@ export function Landing({ onStarted }) {
|
|
|
335
327
|
}
|
|
336
328
|
const row = known.current.get(r.name) ?? chosen(r);
|
|
337
329
|
set({ repo: row, branch: row.default_branch });
|
|
338
|
-
}, load: loadRepos,
|
|
339
|
-
label: t('
|
|
340
|
-
|
|
341
|
-
|
|
330
|
+
}, load: loadRepos, tabs: [
|
|
331
|
+
{ id: 'all', label: t('All') },
|
|
332
|
+
{ id: 'forge', label: t('Organization') },
|
|
333
|
+
{ id: 'github', label: 'GitHub' },
|
|
334
|
+
], troubleshoot: { label: t('Bring a repository from GitHub'), onPress: screen('sync') }, action: {
|
|
342
335
|
label: t('Add to project'),
|
|
343
336
|
onPress: (r) => {
|
|
344
337
|
const row = known.current.get(r.name);
|
|
@@ -349,25 +342,38 @@ export function Landing({ onStarted }) {
|
|
|
349
342
|
name: r.name,
|
|
350
343
|
});
|
|
351
344
|
},
|
|
352
|
-
}, placeholder: t('
|
|
353
|
-
|
|
354
|
-
|
|
345
|
+
}, placeholder: t('Repository'), placement: "top-start", disabled: !signed }), forge ? _jsx(BranchSelect, { open: branching, onOpenChange: setBranching, value: branch, onChange: (b) => set({ branch: b }), load: loadBranches, placement: "top-start" }) : null] }));
|
|
346
|
+
// Where a run goes from here: its repositories, branches, issues and templates, each one press away.
|
|
347
|
+
const chips = [
|
|
348
|
+
{ id: 'repos', label: t('Repos'), icon: _jsx(Code, { size: 13, color: "$soft" }), onPress: () => setPicking(true) },
|
|
349
|
+
...(forge ? [{ id: 'branches', label: t('Branches'), icon: _jsx(GitBranch, { size: 13, color: "$soft" }), onPress: () => setBranching(true) }] : []),
|
|
350
|
+
{ id: 'issues', label: t('Issues'), icon: _jsx(CircleDot, { size: 13, color: "$soft" }), onPress: screen('issues') },
|
|
351
|
+
{ id: 'templates', label: t('Templates'), icon: _jsx(LayoutTemplate, { size: 13, color: "$soft" }), onPress: screen('templates') },
|
|
352
|
+
];
|
|
353
|
+
const commands = [
|
|
354
|
+
{ id: 'run', label: t('Run'), keys: 'Ctrl+↵', icon: _jsx(Play, { size: 14, color: "$soft" }), disabled: !draft.trim(), run: () => void send() },
|
|
355
|
+
{ id: 'issues', label: t('View issues'), icon: _jsx(CircleDot, { size: 14, color: "$soft" }), run: screen('issues') },
|
|
356
|
+
{ id: 'templates', label: t('Browse templates'), icon: _jsx(LayoutTemplate, { size: 14, color: "$soft" }), run: screen('templates') },
|
|
357
|
+
{ id: 'projects', label: t('Open projects'), icon: _jsx(Code, { size: 14, color: "$soft" }), run: screen('projects') },
|
|
358
|
+
{ id: 'sync', label: t('Bring a repository from GitHub'), icon: _jsx(GitBranch, { size: 14, color: "$soft" }), run: screen('sync') },
|
|
359
|
+
];
|
|
360
|
+
// The machines a run can go to, as the `+` menu's Devices: the same choice as the pill's.
|
|
355
361
|
const devices = {
|
|
356
|
-
rows: placeItems.map((i) => ({ id: i.place.id || 'sandbox', label: i.label, note: i.hint, icon: _jsx(
|
|
362
|
+
rows: placeItems.map((i) => ({ id: i.place.id || 'sandbox', label: i.label, note: i.hint, icon: _jsx(Monitor, { size: 16 }), on: i.place.id === place.id })),
|
|
357
363
|
pick: (id) => set({ place: id === 'sandbox' ? '' : id }),
|
|
358
364
|
loading: places.loading,
|
|
359
365
|
error: places.error?.message ?? null,
|
|
360
366
|
};
|
|
361
|
-
return (_jsxs(YStack, { flex: 1, minH: 0, minW: 0, items: "center",
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
367
|
+
return (_jsxs(YStack, { flex: 1, minH: 0, minW: 0, items: "center", overflowX: "hidden", "data-slot": "new", children: [_jsx(YStack, { flex: 1, minH: 0, width: "100%", maxW: MEASURE, justify: "flex-end", pb: "$3", overflowY: "auto", children: _jsx(SizableText, { render: "h1", fontSize: "$7", lineHeight: "$7", fontWeight: "700", color: "$ink", style: { textAlign: 'center' }, children: t('Build what’s next.') }) }), _jsx(YStack, { width: "100%", maxW: MEASURE, gap: "$2", pb: 12, shrink: 0, children: _jsxs(YStack, { gap: "$2", children: [stranger ? (_jsxs(XStack, { items: "center", justify: "space-between", gap: "$3", px: "$3", py: "$2", rounded: "$10", borderWidth: 1, borderColor: "$borderColor", role: "alert", children: [_jsx(SizableText, { size: "$2", color: "$ink", flex: 1, minW: 0, children: t('{repo} is not one of this organization’s repositories.', { repo: codebase }) }), _jsx(Button, { size: "sm", variant: "outline", onPress: () => setPicking(true), children: t('Choose a repository') })] })) : env.value && (env.value.state === 'proposed' || unset(env.value)) && !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'
|
|
368
|
+
? t('A proposed environment for {repo} is waiting for review.', { repo: codebase })
|
|
369
|
+
: t('{repo} has no environment yet, so every run starts it bare.', { repo: codebase }) }), env.value.state === 'proposed' && env.value.session ? (_jsx(Button, { size: "sm", variant: "outline", onPress: () => host.go(env.value.session), children: t('Review') })) : (_jsx(Button, { size: "sm", onPress: () => setOffering(true), children: t('Set up environment') }))] })) : null, note || unhonoured(mode, place.id, t) || mind.note ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note || unhonoured(mode, place.id, t) || mind.note })) : null, _jsx(Composer, { column: true, framed: true, value: draft, onChange: setDraft, onSend: () => void send(), busy: busy, placeholder: t('Ask, build, or run anything…'), label: t('Describe a task or ask a question'), plus: {
|
|
370
|
+
upload: () => pick(files, setFiles, setNote, t),
|
|
371
|
+
skills: carry.skills,
|
|
372
|
+
design: carry.design,
|
|
373
|
+
plugins: carry.plugins,
|
|
374
|
+
devices,
|
|
375
|
+
memory: { on: carry.gear.gear.memory, set: (on) => carry.gear.set({ memory: on }) },
|
|
376
|
+
}, carried: [...carry.on, ...carrying(files, setFiles)], slash: carry.skills, at: carry.plugins, pills: pills, chips: chips, commands: commands, starters: _jsx(Suggestions, { rows: hints.rows, onAct: act, onDismiss: hints.dismiss }), mind: mind, effort: pacing(mind), limits: limits, onUpgrade: () => host.go(path({ kind: 'screen', screen: 'plans' })), voice: voice, language: prefs.language ?? null, onLanguage: (id) => void savePrefs({ language: id }), talk: talk })] }) }), _jsx(Publish, { source: adding, onClose: () => setAdding(null) }), carry.dialog, codebase ? (_jsx(SetupDialog, { repo: codebase, homes: homes, open: offering, onOpenChange: setOffering, onStart: setup, onRepo: choose, onSaved: () => {
|
|
371
377
|
setOffering(false);
|
|
372
378
|
env.reload();
|
|
373
379
|
} })) : null] }));
|
package/lib/mind.d.ts
CHANGED
|
@@ -1,12 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which model answers, and how hard it thinks: one store for Chat and Dev, and
|
|
3
|
+
* the catalog both offer. The composer (@hanzo/ui/chat) changes it.
|
|
4
|
+
*
|
|
5
|
+
* GET /v1/models the catalog, read once per platform and shared
|
|
6
|
+
* hanzo.mind the person's choice, in this browser: {model, effort}
|
|
7
|
+
*
|
|
8
|
+
* The effort is always under the composer; the model chip only where the person
|
|
9
|
+
* turned it on (Settings → Appearance › Model in composer). Enso
|
|
10
|
+
* routes every ask — a message in Chat, a step in Dev — so naming a model is an
|
|
11
|
+
* override, and the panel says so before it offers one. A premium model is
|
|
12
|
+
* picked for the screen it was picked on (`usePick`) and never kept, so the next
|
|
13
|
+
* conversation or run opens on what was kept, Enso until something else is.
|
|
14
|
+
*
|
|
15
|
+
* The efforts are Auto, High, XHigh and Max, Chat's words (Hanzo's
|
|
16
|
+
* ThinkingEffort), Auto until another is chosen. A coding run takes low, medium
|
|
17
|
+
* or high (apps/coding `efforts`), so `level` is what a run names: medium for
|
|
18
|
+
* Auto, what a run took before anyone chose, and high for the rest, the most it
|
|
19
|
+
* takes. Auto names one rather than none, because a run that names none takes
|
|
20
|
+
* an effort the person saved to their account once (apps/coding start.go) and
|
|
21
|
+
* no screen shows any more.
|
|
22
|
+
*/
|
|
23
|
+
import type { Modes } from '@hanzo/ui/chat';
|
|
1
24
|
import { type Target } from './api/call.ts';
|
|
2
25
|
import { type Model } from './api/models.ts';
|
|
3
26
|
export type Effort = 'auto' | 'high' | 'xhigh' | 'max';
|
|
4
|
-
/** The efforts, in order
|
|
5
|
-
export declare const EFFORTS:
|
|
27
|
+
/** The efforts a Dev run offers, in order: the composer's own list (Chat offers every one), less Low and Medium. Marked English, drawn through `t`. */
|
|
28
|
+
export declare const EFFORTS: (import("@hanzo/ui/chat").Way<import("@hanzo/ui/chat").Effort> & {
|
|
6
29
|
id: Effort;
|
|
7
|
-
|
|
8
|
-
note: string;
|
|
9
|
-
}[];
|
|
30
|
+
})[];
|
|
10
31
|
/** The effort a coding run names (apps/coding takes low, medium or high): medium for Auto, high for the rest. */
|
|
11
32
|
export declare const level: (effort: Effort) => 'medium' | 'high';
|
|
12
33
|
/** What a person chose: the model (Enso routes) and the effort. */
|
|
@@ -55,6 +76,8 @@ export interface Catalog {
|
|
|
55
76
|
export declare function useCatalog(t: Target): Catalog;
|
|
56
77
|
/** Whether `model` is one this plan may send: on Free, the catalog's free class alone. */
|
|
57
78
|
export declare function grants(free: boolean, model: string, catalog: readonly Model[]): boolean;
|
|
79
|
+
/** The effort as the composer reads it: the one in force, its list, and the way to change it. */
|
|
80
|
+
export declare const pacing: (m: Pick<Mind, 'effort' | 'pace'>) => Modes<Effort>;
|
|
58
81
|
/** The chip's whole state: the catalog, what answers next, and how to change either. */
|
|
59
82
|
export interface Mind {
|
|
60
83
|
model: string;
|
package/lib/mind.js
CHANGED
|
@@ -1,38 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
* Which model answers, and how hard it thinks: one store for Chat and Dev, and
|
|
3
|
-
* the catalog both offer. The chip that changes it is `Tune` (prompt.tsx).
|
|
4
|
-
*
|
|
5
|
-
* GET /v1/models the catalog, read once per platform and shared
|
|
6
|
-
* hanzo.mind the person's choice, in this browser: {model, effort}
|
|
7
|
-
*
|
|
8
|
-
* The effort is always under the composer; the model chip only where the host
|
|
9
|
-
* shows it (Hanzo's Advanced settings › model selector, `Host.models`). Enso
|
|
10
|
-
* routes every ask — a message in Chat, a step in Dev — so naming a model is an
|
|
11
|
-
* override, and the panel says so before it offers one. A premium model is
|
|
12
|
-
* picked for the screen it was picked on (`usePick`) and never kept, so the next
|
|
13
|
-
* conversation or run opens on what was kept, Enso until something else is.
|
|
14
|
-
*
|
|
15
|
-
* The efforts are Auto, High, XHigh and Max, Chat's words (Hanzo's
|
|
16
|
-
* ThinkingEffort), Auto until another is chosen. A coding run takes low, medium
|
|
17
|
-
* or high (apps/coding `efforts`), so `level` is what a run names: medium for
|
|
18
|
-
* Auto, what a run took before anyone chose, and high for the rest, the most it
|
|
19
|
-
* takes. Auto names one rather than none, because a run that names none takes
|
|
20
|
-
* an effort the person saved to their account once (apps/coding start.go) and
|
|
21
|
-
* no screen shows any more.
|
|
22
|
-
*/
|
|
23
|
-
import { phrase } from '@hanzo/i18n';
|
|
1
|
+
import { EFFORTS as EVERY } from '@hanzo/ui/chat/pure';
|
|
24
2
|
import { parseModels } from '@hanzo/ui/models/catalog';
|
|
25
3
|
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
26
4
|
import { Refusal } from './api/call.js';
|
|
27
5
|
import { ENSO, models as read } from './api/models.js';
|
|
28
6
|
import { usePick } from './pick.js';
|
|
29
|
-
/** The efforts, in order
|
|
30
|
-
export const EFFORTS = [
|
|
31
|
-
{ id: 'auto', label: phrase('Auto'), note: phrase('Adaptive reasoning based on task complexity') },
|
|
32
|
-
{ id: 'high', label: phrase('High'), note: phrase('Deep reasoning for complex problems') },
|
|
33
|
-
{ id: 'xhigh', label: phrase('XHigh'), note: phrase('Extended multi-step reasoning for hard tasks') },
|
|
34
|
-
{ id: 'max', label: phrase('Max'), note: phrase('Maximum compute and exhaustive verification') },
|
|
35
|
-
];
|
|
7
|
+
/** The efforts a Dev run offers, in order: the composer's own list (Chat offers every one), less Low and Medium. Marked English, drawn through `t`. */
|
|
8
|
+
export const EFFORTS = EVERY.filter((e) => ['auto', 'high', 'xhigh', 'max'].includes(e.id));
|
|
36
9
|
/** The effort a coding run names (apps/coding takes low, medium or high): medium for Auto, high for the rest. */
|
|
37
10
|
export const level = (effort) => (effort === 'auto' ? 'medium' : 'high');
|
|
38
11
|
export const FIRST = { model: ENSO, effort: 'auto' };
|
|
@@ -205,6 +178,8 @@ export function grants(free, model, catalog) {
|
|
|
205
178
|
return true;
|
|
206
179
|
return catalog.find((m) => m.id === model)?.class === 'free';
|
|
207
180
|
}
|
|
181
|
+
/** The effort as the composer reads it: the one in force, its list, and the way to change it. */
|
|
182
|
+
export const pacing = (m) => ({ value: m.effort, set: m.pace, items: EFFORTS });
|
|
208
183
|
/**
|
|
209
184
|
* The model and effort for the plan `plan` names (`GET /v1/ai/limits` `plan`).
|
|
210
185
|
* On Free the default is `FREE` and a kept pick outside the free lane is set
|
package/lib/plugins/index.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type { Say } from '@hanzo/i18n/react';
|
|
|
19
19
|
import type { Target } from '../api/call.ts';
|
|
20
20
|
import type { Gear, GearState } from '../gear.ts';
|
|
21
21
|
import type { Composer, Plugin, Prep, Turn } from '../plugin.ts';
|
|
22
|
-
import type { Shelf } from '
|
|
22
|
+
import type { Shelf } from '@hanzo/ui/chat';
|
|
23
23
|
export { research } from './research.tsx';
|
|
24
24
|
export interface Plugins {
|
|
25
25
|
plugins: Plugin[];
|
package/lib/project.js
CHANGED
|
@@ -46,7 +46,7 @@ import { SizableText, useMedia, XStack, YStack } from '@hanzo/gui';
|
|
|
46
46
|
import { useI18n } from '@hanzo/i18n/react';
|
|
47
47
|
import { Clock, ExternalLink, MousePointerClick, PanelLeft, RefreshCcw, Share2, Upload, } from '@hanzogui/lucide-icons-2';
|
|
48
48
|
import { Attachments, CHAT, Console, DEVICES, Feedback, FileTabs, FileTree, PageSelect, PreviewFrame, ProjectChip, VIEWS, Views, Workspace, fold, } from '@hanzo/ui/agents';
|
|
49
|
-
import { Message } from '@hanzo/ui/chat';
|
|
49
|
+
import { Composer, Message } from '@hanzo/ui/chat';
|
|
50
50
|
import { Button, Dialog, DialogContent, DialogTitle } from '@hanzo/ui';
|
|
51
51
|
import { HanzoMark } from '@hanzo/ui/product';
|
|
52
52
|
import { useDictation, useVoice } from '@hanzo/voice';
|
|
@@ -64,13 +64,13 @@ import { useKept, useProjects, useRead, useRun } from './data.js';
|
|
|
64
64
|
import { Merge } from './git.js';
|
|
65
65
|
import { Gutter } from './gutter.js';
|
|
66
66
|
import { useHost, useTarget } from './host.js';
|
|
67
|
-
import {
|
|
67
|
+
import { MODES } from './gear.js';
|
|
68
|
+
import { level, pacing, useMind } from './mind.js';
|
|
68
69
|
import { Out } from './out.js';
|
|
69
|
-
import {
|
|
70
|
-
import { useSpoken } from './voice.js';
|
|
70
|
+
import { useEar, useSpoken } from './voice.js';
|
|
71
71
|
import { Publish } from './publish.js';
|
|
72
72
|
import { path } from './route.js';
|
|
73
|
-
import {
|
|
73
|
+
import { carrying, compose, pick } from './tools.js';
|
|
74
74
|
const LIVE = new Set(['running', 'paused']);
|
|
75
75
|
/** How often a project being published is read again, ms. */
|
|
76
76
|
const AGAIN = 5000;
|
|
@@ -422,9 +422,9 @@ export function Project({ at }) {
|
|
|
422
422
|
? t('{name} is loaded and nothing is published yet. Say what to change and it gets built.', { name: project.name })
|
|
423
423
|
: named
|
|
424
424
|
? t('{repo} is open on {branch}. Say what to change and it gets built there.', { repo: `${named.org}/${named.name}`, branch: home })
|
|
425
|
-
: t('{name} is loaded. Say what to change and it gets built.', { name: slug }) })) : (ordered.map((r) => (_jsxs(YStack, { gap: "$2", children: [_jsx(Turn, { run: r, open: r.id === current, onOpen: () => setChosen(r.id), project: slug }), r.id === current ? (_jsxs(YStack, { gap: "$1", paddingInlineStart: "$2", borderInlineStartWidth: 1, borderColor: "$borderColor", children: [fold(run.events.map((e) => ({ kind: e.kind, actor: who(e.actor), seq: e.seq, id: e.id, text: said(e, run.record?.mode, t) })).filter((b) => b.text)).map((b) => (_jsx(SizableText, { size: "$1", color: "$soft", children: b.text }, b.key))), pr.href ? (_jsxs(XStack, { items: "center", gap: "$2", flexWrap: "wrap", children: [_jsxs(Out, { href: pr.href, label: t('Open pull request {pr}', { pr: pr.label }), children: [_jsx(SizableText, { size: "$1", color: "$ink", textDecorationLine: "underline", children: t('Pull request {pr}', { pr: pr.label }) }), _jsx(ExternalLink, { size: 11 })] }), _jsx(Merge, { session: r.id, open: proposing, onMerged: merged })] })) : null] })) : null] }, r.id)))) }), _jsxs(YStack, { "data-slot": "project-ask", px: "$3", pb: "$3", gap: "$2", minW: 0, $md: { pr: 0 }, children: [note ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note })) : null, picked.length ? _jsx(Attachments, { items: picked, onRemove: () => setPicked([]) }) : null,
|
|
425
|
+
: t('{name} is loaded. Say what to change and it gets built.', { name: slug }) })) : (ordered.map((r) => (_jsxs(YStack, { gap: "$2", children: [_jsx(Turn, { run: r, open: r.id === current, onOpen: () => setChosen(r.id), project: slug }), r.id === current ? (_jsxs(YStack, { gap: "$1", paddingInlineStart: "$2", borderInlineStartWidth: 1, borderColor: "$borderColor", children: [fold(run.events.map((e) => ({ kind: e.kind, actor: who(e.actor), seq: e.seq, id: e.id, text: said(e, run.record?.mode, t) })).filter((b) => b.text)).map((b) => (_jsx(SizableText, { size: "$1", color: "$soft", children: b.text }, b.key))), pr.href ? (_jsxs(XStack, { items: "center", gap: "$2", flexWrap: "wrap", children: [_jsxs(Out, { href: pr.href, label: t('Open pull request {pr}', { pr: pr.label }), children: [_jsx(SizableText, { size: "$1", color: "$ink", textDecorationLine: "underline", children: t('Pull request {pr}', { pr: pr.label }) }), _jsx(ExternalLink, { size: 11 })] }), _jsx(Merge, { session: r.id, open: proposing, onMerged: merged })] })) : null] })) : null] }, r.id)))) }), _jsxs(YStack, { "data-slot": "project-ask", px: "$3", pb: "$3", gap: "$2", minW: 0, $md: { pr: 0 }, children: [note ? (_jsx(SizableText, { size: "$1", color: "$soft", role: "status", children: note })) : null, picked.length ? _jsx(Attachments, { items: picked, onRemove: () => setPicked([]) }) : null, _jsx(Composer, { column: true, framed: true, value: draft, onChange: setDraft, onSend: () => void (current && running ? steer(current) : send()), onStop: current
|
|
426
426
|
? () => void stop(target, current).then(() => setNote(t('Stop requested — the run keeps its work on its branch')), (e) => setNote(e.message))
|
|
427
|
-
: undefined, busy: busy || (running && !draft.trim()), placeholder: running ? t('Steer this run') : t('Ask Hanzo for edits'), label: t('Ask Hanzo for edits'), plus: { upload: () => pick(attached, setAttached, setNote, t) }, mode: running ? null : { value: mode === 'plan' ? 'plan' : 'auto', set: (m) => setMode(m === 'plan' ? 'plan' : 'build'), items:
|
|
427
|
+
: undefined, busy: busy || (running && !draft.trim()), placeholder: running ? t('Steer this run') : t('Ask Hanzo for edits'), label: t('Ask Hanzo for edits'), plus: { upload: () => pick(attached, setAttached, setNote, t) }, carried: carrying(attached, setAttached), mode: running ? null : { value: mode === 'plan' ? 'plan' : 'auto', set: (m) => setMode(m === 'plan' ? 'plan' : 'build'), items: MODES.filter((m) => m.id !== 'manual') }, mind: running ? null : mind, effort: running ? null : pacing(mind), voice: voice })] }), wide && !shut ? (_jsx(Gutter, { side: "left", span: across, floor: CHAT_FLOOR, ceil: most, reset: CHAT_WIDTH, onSpan: setSplit, label: t('Resize the chat'), gap: GUTTER })) : null] }));
|
|
428
428
|
// The preview stays loaded while another view is open, so coming back to it
|
|
429
429
|
// is the page as it was left.
|
|
430
430
|
const preview = (_jsxs(YStack, { "data-slot": "preview", flex: 1, minH: 0, minW: 0, gap: "$2", display: view === 'preview' ? 'flex' : 'none', children: [_jsxs(XStack, { "data-slot": "preview-bar", items: "center", gap: "$1.5", minW: 0, shrink: 0, "$max-md": { px: '$2' }, children: [_jsx(XStack, { render: "button", "aria-label": t('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, shrink: 1 }), _jsx(XStack, { flex: 1 }), _jsx(Views, { views: DEVICES, value: device, onChange: (d) => setDevice(d), label: t('Device'), labels: "none" }), src ? (_jsx(Out, { href: src, label: t('Open in a new tab'), children: _jsx(XStack, { p: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: _jsx(ExternalLink, { size: 16 }) }) })) : null] }), _jsx(PreviewFrame, { ref: frame, src: src, title: t('{title} preview', { title }), device: device, onBridge: onBridge, empty: _jsxs(YStack, { items: "center", gap: "$2", p: "$6", children: [_jsx(SizableText, { size: "$3", color: "$ink", children: stalled
|
package/lib/run.js
CHANGED
|
@@ -29,7 +29,7 @@ import { useI18n } from '@hanzo/i18n/react';
|
|
|
29
29
|
import { ChevronDown, ChevronRight, Clock, Copy, ExternalLink, GitPullRequest, Globe, Pencil, Share } from '@hanzogui/lucide-icons-2';
|
|
30
30
|
import { Button, Dialog, DialogContent, DialogTitle, DropdownMenu, Input } from '@hanzo/ui';
|
|
31
31
|
import { Steer } from '@hanzo/ui/agents';
|
|
32
|
-
import { Failure } from '@hanzo/ui/chat';
|
|
32
|
+
import { Composer, Failure, measure } from '@hanzo/ui/chat';
|
|
33
33
|
import { useLimits } from '@hanzo/ui/product/useLimits';
|
|
34
34
|
import { useDictation, useVoice } from '@hanzo/voice';
|
|
35
35
|
import { useEffect, useMemo, useState } from 'react';
|
|
@@ -41,18 +41,17 @@ import { verdict } from './api/verdict.js';
|
|
|
41
41
|
import { useKept, useProjects, useRead, useRun } from './data.js';
|
|
42
42
|
import { Desk, SEED } from './desk.js';
|
|
43
43
|
import { useHost, useTarget } from './host.js';
|
|
44
|
-
import { level, useMind } from './mind.js';
|
|
44
|
+
import { level, pacing, useMind } from './mind.js';
|
|
45
45
|
import { Out } from './out.js';
|
|
46
46
|
import { Gutter } from './gutter.js';
|
|
47
47
|
import { gap, pane } from './pane.js';
|
|
48
|
-
import {
|
|
49
|
-
import { useSpoken } from './voice.js';
|
|
48
|
+
import { useEar, useSpoken } from './voice.js';
|
|
50
49
|
import { speaker } from './reply.js';
|
|
51
50
|
import { PanelToggle } from './panel.js';
|
|
52
51
|
import { schedule } from './scheduled.js';
|
|
53
52
|
import { SideProvider, useSplit } from './side.js';
|
|
54
53
|
import { PAGE, useDeck, useKey, usePanel } from './tabs.js';
|
|
55
|
-
import {
|
|
54
|
+
import { carrying, compose, pick } from './tools.js';
|
|
56
55
|
import { Transcript } from './transcript.js';
|
|
57
56
|
const LIVE = new Set(['running', 'paused', '']);
|
|
58
57
|
/** A run's status as the line under its title says it; one the service adds later is drawn as it comes. */
|
|
@@ -384,9 +383,9 @@ export function Run({ id }) {
|
|
|
384
383
|
: say('This run is still working.') }), _jsx(Steer, { onCommand: command, withhold: [
|
|
385
384
|
...(paused ? ['pause'] : ['resume']),
|
|
386
385
|
...(busy || waiting ? ['pause', 'resume', 'stop'] : []),
|
|
387
|
-
] }), _jsx(Button, { size: "sm", disabled: notify, onPress: () => void ask(), children: notify ? say('You will be notified') : say('Notify me') })] })) : null] }), _jsx(
|
|
386
|
+
] }), _jsx(Button, { size: "sm", disabled: notify, onPress: () => void ask(), children: notify ? say('You will be notified') : say('Notify me') })] })) : null] }), _jsx(Composer, { column: true, framed: true, value: draft, onChange: setDraft, onSend: send, ready: Boolean(draft.trim()), disabled: !signed || busy || waiting || !(running || finished), placeholder: placeholder, label: steering ? say('Steer this run') : say('Follow up on this run'), plus: { upload: () => pick(files, setFiles, setNote, say) }, carried: carrying(files, setFiles),
|
|
388
387
|
// What a new run from here is made with; words that steer this run use neither.
|
|
389
|
-
|
|
388
|
+
mind: signed && !steering ? mind : null, effort: signed && !steering ? pacing(mind) : null, limits: limits, voice: voice })] })] }), desk && !wide ? desk_(true) : null, desk && wide ? (_jsxs(YStack, { "data-slot": "desk", ref: split.aside, ...pane,
|
|
390
389
|
// A pane of its own beside the run's, the gutter between them (useSplit reads it as the inline-start margin).
|
|
391
390
|
marginInlineStart: gap, position: "relative", shrink: 0, minH: 0, style: split.asideStyle, children: [split.grip ? (_jsx(Gutter, { ...split.grip, label: say('Resize the side panel'), inset: pane.borderWidth })) : null, _jsx(SideProvider, { value: split.side, children: desk_(false) })] })) : null, _jsx(Dialog, { open: naming, onOpenChange: setNaming, children: _jsxs(DialogContent, { maxW: 480, children: [_jsx(DialogTitle, { children: say('Rename this run') }), _jsx(Input, { autoFocus: true, value: name, onChangeText: setName, "aria-label": say('The run’s name'), maxLength: 512, onKeyDown: (e) => {
|
|
392
391
|
if (e.key === 'Enter')
|
package/lib/settings/general.js
CHANGED
|
@@ -15,7 +15,8 @@ import { useState } from 'react';
|
|
|
15
15
|
import { told } from '../api/call.js';
|
|
16
16
|
import { useHost } from '../host.js';
|
|
17
17
|
import { usePrefs } from '../prefs.js';
|
|
18
|
-
import { LANGUAGES
|
|
18
|
+
import { LANGUAGES } from '@hanzo/ui/chat/pure';
|
|
19
|
+
import { spoken } from '../voice.js';
|
|
19
20
|
import { Card, Group, Heading, Note, Row, Soft } from './ui.js';
|
|
20
21
|
/** The choices, each word English and marked (`phrase`): drawn through `t`. */
|
|
21
22
|
const THEMES = [
|
package/lib/suggestions.js
CHANGED
|
@@ -206,6 +206,7 @@ function Row({ row: kept, touch, onAct, onDismiss }) {
|
|
|
206
206
|
// A starter in the reader's words, its prompt too: what is sent is what they pressed.
|
|
207
207
|
const row = starter(kept) ? { ...kept, title: t(kept.title), act: 'prompt' in kept.act ? { ...kept.act, prompt: t(kept.act.prompt) } : kept.act } : kept;
|
|
208
208
|
const Mark = MARKS[row.icon];
|
|
209
|
-
const
|
|
210
|
-
|
|
209
|
+
const handleAct = () => onAct(row.act, row);
|
|
210
|
+
const button = (_jsxs(XStack, { render: "button", "data-act": row.act.kind, "aria-label": row.title, onPress: handleAct, onClick: handleAct, flex: 1, minW: 0, self: "stretch", items: "center", gap: "$3", paddingInlineStart: "$3", rounded: "$4", cursor: "pointer", focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: [_jsx(Mark, { size: 18, color: "$soft" }), _jsx(SizableText, { fontSize: 15, color: "$soft", numberOfLines: 1, shrink: 1, minW: 0, children: row.title })] }));
|
|
211
|
+
return (_jsxs(XStack, { role: "listitem", "data-slot": "suggestion", items: "center", gap: "$1", minH: PITCH, paddingInlineEnd: "$2", rounded: "$4", hoverStyle: { bg: '$hover' }, onPointerEnter: () => setNear(true), onPointerLeave: () => setNear(false), children: [row.summary && !touch ? (_jsxs(HoverCard, { openDelay: 300, closeDelay: 150, children: [_jsx(HoverCardTrigger, { flex: 1, minW: 0, self: "stretch", flexDirection: "row", children: button }), _jsx(HoverCardContent, { side: "top", align: "start", sideOffset: 8, width: 470, maxW: "calc(100vw - 32px)", rounded: "$6", p: "$4", children: _jsx(SizableText, { fontSize: 16, lineHeight: 26, color: "$ink", children: row.summary }) })] })) : (button), _jsx(XStack, { render: "button", "aria-label": t('Dismiss {suggestion}', { suggestion: row.title }), onPress: () => onDismiss(row.id), p: "$1.5", rounded: "$2", cursor: "pointer", opacity: touch || near ? 1 : 0, hoverStyle: { bg: '$hover' }, focusVisibleStyle: { opacity: 1, outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, children: _jsx(X, { size: 16, color: "$soft" }) })] }));
|
|
211
212
|
}
|