@hanzo/build 0.2.82 → 0.2.84
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 +17 -10
- package/lib/carry.d.ts +3 -4
- package/lib/carry.js +2 -2
- package/lib/index.d.ts +5 -7
- package/lib/index.js +5 -7
- package/lib/landing.js +53 -47
- package/lib/mind.d.ts +32 -9
- package/lib/mind.js +11 -35
- package/lib/plugins/index.d.ts +1 -1
- package/lib/project.js +8 -8
- package/lib/run.js +7 -8
- package/lib/settings/code.js +1 -1
- package/lib/settings/general.js +2 -1
- 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 -131
- package/lib/prompt.js +0 -273
package/README.md
CHANGED
|
@@ -118,10 +118,14 @@ French and right to left in Arabic.
|
|
|
118
118
|
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
|
-
and the choice kept in this browser (`hanzo.mind`, Enso and
|
|
122
|
-
changed).
|
|
123
|
-
|
|
124
|
-
|
|
121
|
+
and the choice kept in this browser (`hanzo.mind`, Enso and Auto until
|
|
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
|
+
writes the same choice. `@hanzo/build/mind` serves the store alone, without
|
|
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.
|
|
125
129
|
- `Reply` — copy, Listen and Open under an answer.
|
|
126
130
|
- `Panel` over `useDeck` — the side panel and its kept tabs.
|
|
127
131
|
- `useSplit` / `SideProvider` — the column the panel stands in: its edge (the
|
|
@@ -253,10 +257,12 @@ are any member's.
|
|
|
253
257
|
(Cloud is the platform's sandbox with the codebase's environment; the org's
|
|
254
258
|
machines follow under remote control, each linked with `hanzo link`), the
|
|
255
259
|
codebase and the branch — the forge's repositories, filtered as you type, read
|
|
256
|
-
again
|
|
257
|
-
there yet — then the ask, in the composer Chat draws too (`
|
|
258
|
-
and Build or Plan at its start; the
|
|
259
|
-
|
|
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
|
|
260
266
|
issue from its own screen lands here with that choice already made.
|
|
261
267
|
|
|
262
268
|
**A run.** The transcript as it streams, drawn by what each part is: what the
|
|
@@ -351,7 +357,8 @@ picker and open-in-tab; Share and Publish; the console dock under it.
|
|
|
351
357
|
`mode`, `model` and `effort` are sent with a run as asked — Enso as
|
|
352
358
|
`enso-auto`, never left out — and `issue`, the number a run started from an
|
|
353
359
|
issue closes; the platform honours them where it does and nothing here
|
|
354
|
-
simulates them.
|
|
360
|
+
simulates them. A run takes `low`, `medium` or `high`, so Auto names `medium`
|
|
361
|
+
and High, XHigh and Max name `high` (`level`).
|
|
355
362
|
|
|
356
363
|
## Layout
|
|
357
364
|
|
|
@@ -369,7 +376,7 @@ src/
|
|
|
369
376
|
prose.tsx markdown, drawn as text (markdown.ts reads it)
|
|
370
377
|
desk.tsx the pane beside a run
|
|
371
378
|
mind.ts the model and effort, one choice with Chat
|
|
372
|
-
|
|
379
|
+
voice.ts the language dictation listens in, and the platform's ear
|
|
373
380
|
reply.tsx the row under an answer
|
|
374
381
|
panel.tsx the side panel; tabs.ts its kept tabs
|
|
375
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/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,
|
|
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,
|
|
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 { 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';
|
|
@@ -231,7 +230,7 @@ export function Landing({ onStarted }) {
|
|
|
231
230
|
targetId: place.id,
|
|
232
231
|
mode,
|
|
233
232
|
model: mind.model,
|
|
234
|
-
effort: mind.effort,
|
|
233
|
+
effort: level(mind.effort),
|
|
235
234
|
issue,
|
|
236
235
|
});
|
|
237
236
|
setDraft('');
|
|
@@ -297,7 +296,7 @@ export function Landing({ onStarted }) {
|
|
|
297
296
|
};
|
|
298
297
|
/** Start the agent that finds a codebase's environment, and open it: its run is the progress. A refusal is the dialog's to say. */
|
|
299
298
|
const setup = async (repo) => {
|
|
300
|
-
const run = await start(target, { prompt: SETUP, repo, mode: 'setup', model: mind.model, effort: mind.effort });
|
|
299
|
+
const run = await start(target, { prompt: SETUP, repo, mode: 'setup', model: mind.model, effort: level(mind.effort) });
|
|
301
300
|
onStarted(run.session);
|
|
302
301
|
};
|
|
303
302
|
/** The codebase a dialog chose, chosen here too. */
|
|
@@ -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,35 @@
|
|
|
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
|
-
export type Effort = '
|
|
4
|
-
/** The efforts, in
|
|
5
|
-
export declare const EFFORTS:
|
|
26
|
+
export type Effort = 'auto' | 'high' | 'xhigh' | 'max';
|
|
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
|
-
|
|
9
|
-
|
|
30
|
+
})[];
|
|
31
|
+
/** The effort a coding run names (apps/coding takes low, medium or high): medium for Auto, high for the rest. */
|
|
32
|
+
export declare const level: (effort: Effort) => 'medium' | 'high';
|
|
10
33
|
/** What a person chose: the model (Enso routes) and the effort. */
|
|
11
34
|
export interface Choice {
|
|
12
35
|
model: string;
|
|
@@ -17,14 +40,12 @@ export declare const FIRST: Choice;
|
|
|
17
40
|
export declare const FREE = "enso-free";
|
|
18
41
|
/** Whether a plan grants only the free lane. An unknown plan is not taken for Free. */
|
|
19
42
|
export declare const isFree: (plan: string | null | undefined) => boolean;
|
|
20
|
-
/** The kept choice, Enso and
|
|
43
|
+
/** The kept choice, Enso and Auto where nothing is kept. Stable while the stored text is. Low and Medium, offered once, read as Auto. */
|
|
21
44
|
export declare function kept(): Choice;
|
|
22
45
|
/** Keep a change, in this browser, for every surface that reads it. */
|
|
23
46
|
export declare function keep(patch: Partial<Choice>): void;
|
|
24
47
|
/** The kept choice and the way to change it. The server and the first render read the default. */
|
|
25
48
|
export declare function useChoice(): readonly [Choice, (patch: Partial<Choice>) => void];
|
|
26
|
-
/** Whether `model` takes an effort: the router and an undescribed model do, a listed one only if it reasons. */
|
|
27
|
-
export declare function reasons(model: string, catalog: readonly Model[]): boolean;
|
|
28
49
|
/** A model's name as the catalog spells it; Enso for the router. */
|
|
29
50
|
export declare function nameOf(model: string, catalog: readonly Model[]): string;
|
|
30
51
|
/** An id said as a name: its last path part, each word capitalised (`enso-flash` → Enso Flash). */
|
|
@@ -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,34 +1,14 @@
|
|
|
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
|
-
* Effort is on the chip and the model is one press in. Enso routes every ask —
|
|
9
|
-
* a message in Chat, a step in Dev — so naming a model is an override, and the
|
|
10
|
-
* panel says so before it offers one. A premium model is picked for the screen
|
|
11
|
-
* it was picked on (`usePick`) and never kept, so the next conversation or run
|
|
12
|
-
* opens on what was kept, Enso until something else is.
|
|
13
|
-
*
|
|
14
|
-
* The efforts are the set both planes take: `reasoning_effort` on a completion
|
|
15
|
-
* and `effort` on a coding run (apps/coding `efforts`). A model the catalog
|
|
16
|
-
* lists as not reasoning offers none; Enso and a model the catalog does not
|
|
17
|
-
* describe offer all three.
|
|
18
|
-
*/
|
|
19
|
-
import { phrase } from '@hanzo/i18n';
|
|
1
|
+
import { EFFORTS as EVERY } from '@hanzo/ui/chat/pure';
|
|
20
2
|
import { parseModels } from '@hanzo/ui/models/catalog';
|
|
21
3
|
import { useEffect, useState, useSyncExternalStore } from 'react';
|
|
22
4
|
import { Refusal } from './api/call.js';
|
|
23
5
|
import { ENSO, models as read } from './api/models.js';
|
|
24
6
|
import { usePick } from './pick.js';
|
|
25
|
-
/** The efforts, in
|
|
26
|
-
export const EFFORTS = [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
];
|
|
31
|
-
export const FIRST = { model: ENSO, effort: 'medium' };
|
|
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));
|
|
9
|
+
/** The effort a coding run names (apps/coding takes low, medium or high): medium for Auto, high for the rest. */
|
|
10
|
+
export const level = (effort) => (effort === 'auto' ? 'medium' : 'high');
|
|
11
|
+
export const FIRST = { model: ENSO, effort: 'auto' };
|
|
32
12
|
/** What Free answers on: the catalog's free lane, Enso's first. `free` is an alias of it. */
|
|
33
13
|
export const FREE = 'enso-free';
|
|
34
14
|
/** Whether a plan grants only the free lane. An unknown plan is not taken for Free. */
|
|
@@ -49,7 +29,7 @@ let raw;
|
|
|
49
29
|
let held = FIRST;
|
|
50
30
|
/** A choice the browser would not store, held for the page. */
|
|
51
31
|
let memo = null;
|
|
52
|
-
/** The kept choice, Enso and
|
|
32
|
+
/** The kept choice, Enso and Auto where nothing is kept. Stable while the stored text is. Low and Medium, offered once, read as Auto. */
|
|
53
33
|
export function kept() {
|
|
54
34
|
if (memo)
|
|
55
35
|
return memo;
|
|
@@ -74,7 +54,8 @@ export function kept() {
|
|
|
74
54
|
}
|
|
75
55
|
/** Keep a change, in this browser, for every surface that reads it. */
|
|
76
56
|
export function keep(patch) {
|
|
77
|
-
const
|
|
57
|
+
const was = kept();
|
|
58
|
+
const next = { ...was, ...patch, effort: patch.effort === undefined ? was.effort : effortOf(patch.effort) };
|
|
78
59
|
try {
|
|
79
60
|
store()?.setItem(KEY, JSON.stringify(next));
|
|
80
61
|
memo = null;
|
|
@@ -98,13 +79,6 @@ function subscribe(change) {
|
|
|
98
79
|
export function useChoice() {
|
|
99
80
|
return [useSyncExternalStore(subscribe, kept, () => FIRST), keep];
|
|
100
81
|
}
|
|
101
|
-
/** Whether `model` takes an effort: the router and an undescribed model do, a listed one only if it reasons. */
|
|
102
|
-
export function reasons(model, catalog) {
|
|
103
|
-
if (model === ENSO)
|
|
104
|
-
return true;
|
|
105
|
-
const row = catalog.find((m) => m.id === model);
|
|
106
|
-
return !row || row.supports_reasoning === true;
|
|
107
|
-
}
|
|
108
82
|
/** A model's name as the catalog spells it; Enso for the router. */
|
|
109
83
|
export function nameOf(model, catalog) {
|
|
110
84
|
if (model === ENSO)
|
|
@@ -204,6 +178,8 @@ export function grants(free, model, catalog) {
|
|
|
204
178
|
return true;
|
|
205
179
|
return catalog.find((m) => m.id === model)?.class === 'free';
|
|
206
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 });
|
|
207
183
|
/**
|
|
208
184
|
* The model and effort for the plan `plan` names (`GET /v1/ai/limits` `plan`).
|
|
209
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;
|
|
@@ -353,7 +353,7 @@ export function Project({ at }) {
|
|
|
353
353
|
mode,
|
|
354
354
|
// Enso named as itself: a run that names none takes the person's saved coding default.
|
|
355
355
|
model: mind.model,
|
|
356
|
-
effort: mind.effort,
|
|
356
|
+
effort: level(mind.effort),
|
|
357
357
|
});
|
|
358
358
|
setDraft('');
|
|
359
359
|
setPicked([]);
|
|
@@ -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
|