@hanzo/build 0.2.80 → 0.2.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/lib/activity.d.ts +3 -7
- package/lib/activity.js +18 -5
- package/lib/api/agents.d.ts +1 -20
- package/lib/api/agents.js +16 -8
- package/lib/api/auto.d.ts +3 -23
- package/lib/api/auto.js +39 -22
- package/lib/api/billing.d.ts +0 -35
- package/lib/api/billing.js +6 -5
- package/lib/api/call.d.ts +16 -8
- package/lib/api/call.js +32 -1
- package/lib/api/capabilities.d.ts +0 -11
- package/lib/api/capabilities.js +2 -1
- package/lib/api/codebases.d.ts +0 -10
- package/lib/api/codebases.js +3 -2
- package/lib/api/coding.d.ts +3 -32
- package/lib/api/coding.js +13 -7
- package/lib/api/connectors.d.ts +1 -17
- package/lib/api/connectors.js +5 -4
- package/lib/api/environment.d.ts +6 -20
- package/lib/api/environment.js +22 -19
- package/lib/api/github.d.ts +0 -22
- package/lib/api/github.js +3 -2
- package/lib/api/keys.d.ts +0 -14
- package/lib/api/keys.js +4 -3
- package/lib/api/library.d.ts +3 -17
- package/lib/api/library.js +23 -22
- package/lib/api/links.d.ts +1 -1
- package/lib/api/links.js +1 -1
- package/lib/api/machines.d.ts +0 -15
- package/lib/api/machines.js +6 -5
- package/lib/api/mcp.d.ts +0 -9
- package/lib/api/mcp.js +3 -2
- package/lib/api/members.d.ts +0 -15
- package/lib/api/members.js +4 -3
- package/lib/api/memory.d.ts +0 -11
- package/lib/api/memory.js +2 -1
- package/lib/api/places.d.ts +1 -9
- package/lib/api/places.js +12 -1
- package/lib/api/plugins.d.ts +1 -16
- package/lib/api/plugins.js +4 -3
- package/lib/api/profile.d.ts +0 -10
- package/lib/api/profile.js +5 -4
- package/lib/api/projects.d.ts +0 -14
- package/lib/api/projects.js +11 -10
- package/lib/api/provider.d.ts +2 -14
- package/lib/api/provider.js +12 -10
- package/lib/api/sandbox.d.ts +0 -17
- package/lib/api/sandbox.js +5 -4
- package/lib/api/sessions.d.ts +0 -17
- package/lib/api/sessions.js +5 -4
- package/lib/api/skills.d.ts +1 -15
- package/lib/api/skills.js +5 -4
- package/lib/api/studio.d.ts +0 -14
- package/lib/api/studio.js +6 -5
- package/lib/api/turn.d.ts +6 -28
- package/lib/api/turn.js +105 -49
- package/lib/api/verdict.d.ts +0 -9
- package/lib/api/verdict.js +2 -1
- package/lib/api/webhooks.d.ts +1 -14
- package/lib/api/webhooks.js +5 -4
- package/lib/ask.js +7 -4
- package/lib/automations.d.ts +5 -4
- package/lib/automations.js +95 -74
- package/lib/back.js +10 -4
- package/lib/builder.js +7 -5
- package/lib/carry.js +20 -16
- package/lib/customize/agents.js +78 -31
- package/lib/customize/connectors.js +62 -48
- package/lib/customize/index.js +43 -10
- package/lib/customize/plugins.js +34 -22
- package/lib/customize/say.d.ts +13 -1
- package/lib/customize/say.js +15 -21
- package/lib/customize/skills.js +31 -22
- package/lib/customize/ui.d.ts +5 -9
- package/lib/customize/ui.js +26 -16
- package/lib/data.d.ts +1 -1
- package/lib/data.js +3 -2
- package/lib/desk.js +45 -33
- package/lib/door.js +35 -10
- package/lib/environment.js +25 -19
- package/lib/files.js +43 -34
- package/lib/find.js +12 -9
- package/lib/foot.js +7 -4
- package/lib/forge.js +61 -40
- package/lib/gear.d.ts +2 -1
- package/lib/gear.js +6 -4
- package/lib/git.js +29 -19
- package/lib/landing.d.ts +5 -5
- package/lib/landing.js +52 -44
- package/lib/library.js +73 -51
- package/lib/merge.d.ts +2 -2
- package/lib/meter.d.ts +1 -1
- package/lib/meter.js +14 -5
- package/lib/mic.js +12 -5
- package/lib/mind.d.ts +1 -0
- package/lib/mind.js +5 -3
- package/lib/nav.d.ts +6 -5
- package/lib/nav.js +37 -30
- package/lib/panel.d.ts +2 -1
- package/lib/panel.js +52 -36
- package/lib/plan.d.ts +4 -20
- package/lib/plan.js +26 -10
- package/lib/plans.js +49 -30
- package/lib/plugins/design.js +3 -2
- package/lib/plugins/index.d.ts +23 -4
- package/lib/plugins/index.js +11 -26
- package/lib/plugins/pdf.js +2 -1
- package/lib/plugins/project.js +7 -4
- package/lib/plugins/research.js +3 -2
- package/lib/plugins/sketch.js +6 -3
- package/lib/plugins/slides.js +3 -2
- package/lib/plugins/template.js +6 -3
- package/lib/plus.js +25 -13
- package/lib/prefs.d.ts +0 -14
- package/lib/prefs.js +2 -1
- package/lib/project.js +69 -62
- package/lib/prompt.d.ts +12 -11
- package/lib/prompt.js +38 -20
- package/lib/publish.js +17 -12
- package/lib/reply.js +6 -3
- package/lib/run.js +72 -54
- package/lib/say.d.ts +2 -0
- package/lib/say.js +7 -0
- package/lib/scheduled.js +54 -43
- package/lib/section.d.ts +1 -0
- package/lib/section.js +8 -2
- package/lib/settings/account.d.ts +1 -0
- package/lib/settings/account.js +31 -21
- package/lib/settings/billing.js +55 -28
- package/lib/settings/capabilities.js +26 -15
- package/lib/settings/card.js +19 -12
- package/lib/settings/code.js +22 -11
- package/lib/settings/credits.d.ts +4 -4
- package/lib/settings/credits.js +34 -21
- package/lib/settings/environments.js +21 -14
- package/lib/settings/general.js +26 -14
- package/lib/settings/index.js +6 -3
- package/lib/settings/integrations.js +42 -28
- package/lib/settings/keys.js +37 -19
- package/lib/settings/list.d.ts +0 -5
- package/lib/settings/list.js +24 -15
- package/lib/settings/machines.d.ts +3 -0
- package/lib/settings/machines.js +63 -38
- package/lib/settings/members.js +23 -14
- package/lib/settings/memory.js +12 -6
- package/lib/settings/notifications.js +32 -19
- package/lib/settings/plan.d.ts +1 -1
- package/lib/settings/plan.js +40 -17
- package/lib/settings/privacy.js +26 -13
- package/lib/settings/ui.d.ts +3 -2
- package/lib/settings/ui.js +9 -3
- package/lib/settings/usage.js +7 -3
- package/lib/shelf.js +20 -17
- package/lib/side.d.ts +1 -26
- package/lib/side.js +11 -5
- package/lib/standing.d.ts +2 -10
- package/lib/standing.js +5 -3
- package/lib/suggest.d.ts +8 -28
- package/lib/suggest.js +39 -4
- package/lib/suggestions.js +14 -5
- package/lib/summary.d.ts +1 -1
- package/lib/summary.js +4 -1
- package/lib/sync.js +22 -12
- package/lib/tools.d.ts +4 -3
- package/lib/tools.js +10 -7
- package/lib/transcript.js +10 -8
- package/package.json +4 -2
package/lib/project.js
CHANGED
|
@@ -37,8 +37,13 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
37
37
|
*
|
|
38
38
|
* Derived from the Hanzo App v2 editor (github.com/hanzoai/build-v2), itself
|
|
39
39
|
* derived from OSW Studio and DeepSite (MIT). See NOTICE.
|
|
40
|
+
*
|
|
41
|
+
* Its words are the reader's language (@hanzo/i18n `t`, the target is
|
|
42
|
+
* `target`); a project's and a run's names, branches, paths and the
|
|
43
|
+
* platform's reasons are drawn as given.
|
|
40
44
|
*/
|
|
41
45
|
import { SizableText, useMedia, XStack, YStack } from '@hanzo/gui';
|
|
46
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
42
47
|
import { Clock, ExternalLink, MousePointerClick, PanelLeft, RefreshCcw, Share2, Upload, } from '@hanzogui/lucide-icons-2';
|
|
43
48
|
import { Attachments, CHAT, Console, DEVICES, Feedback, FileTabs, FileTree, PageSelect, PreviewFrame, ProjectChip, VIEWS, Views, Workspace, fold, } from '@hanzo/ui/agents';
|
|
44
49
|
import { Message } from '@hanzo/ui/chat';
|
|
@@ -95,25 +100,27 @@ export function slugOf(name) {
|
|
|
95
100
|
}
|
|
96
101
|
/** One run in the conversation: what was asked, and what it came to. */
|
|
97
102
|
function Turn({ run, open, onOpen, project }) {
|
|
98
|
-
const
|
|
103
|
+
const target = useTarget();
|
|
104
|
+
const { t } = useI18n();
|
|
99
105
|
const [v, setV] = useState(null);
|
|
100
106
|
const [note, setNote] = useState('');
|
|
101
|
-
return (_jsxs(YStack, { gap: "$2", children: [_jsx(Message, { role: "user", children: _jsx(SizableText, { size: "$2", color: "$ink", children: run.title || 'Untitled run' }) }), _jsx(Message, { role: "assistant", busy: LIVE.has(run.status), actions: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Feedback, { text: run.title, verdict: v, onVerdict: (next) => {
|
|
107
|
+
return (_jsxs(YStack, { gap: "$2", children: [_jsx(Message, { role: "user", children: _jsx(SizableText, { size: "$2", color: "$ink", children: run.title || t('Untitled run') }) }), _jsx(Message, { role: "assistant", busy: LIVE.has(run.status), actions: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Feedback, { text: run.title, verdict: v, onVerdict: (next) => {
|
|
102
108
|
setV(next);
|
|
103
|
-
record(
|
|
109
|
+
record(target, run.id, project, next).catch((e) => {
|
|
104
110
|
setV(null);
|
|
105
111
|
setNote(e.message);
|
|
106
112
|
});
|
|
107
|
-
} }), note ? (_jsx(SizableText, { size: "$1", color: "$soft", children: note })) : null, !open ? (_jsx(XStack, { render: "button", onPress: onOpen, hitSlop: 8, children: _jsx(SizableText, { size: "$1", color: "$soft", textDecorationLine: "underline", children:
|
|
113
|
+
} }), note ? (_jsx(SizableText, { size: "$1", color: "$soft", children: note })) : null, !open ? (_jsx(XStack, { render: "button", onPress: onOpen, hitSlop: 8, children: _jsx(SizableText, { size: "$1", color: "$soft", textDecorationLine: "underline", children: t('Show steps') }) })) : null] }), children: _jsx(SizableText, { size: "$2", color: "$ink", children: run.status === 'done' ? t('Done.') : run.status === 'error' ? t('This run hit an error.') : run.status === 'stopped' ? t('Stopped.') : t('Working…') }) })] }));
|
|
108
114
|
}
|
|
109
115
|
export function Project({ at }) {
|
|
110
116
|
const host = useHost();
|
|
111
|
-
const
|
|
117
|
+
const target = useTarget();
|
|
118
|
+
const { t } = useI18n();
|
|
112
119
|
const signed = Boolean(host.person);
|
|
113
|
-
const all = useProjects(
|
|
120
|
+
const all = useProjects(target, signed);
|
|
114
121
|
// The repository the address names, and the forge's record of it.
|
|
115
122
|
const named = 'slug' in at ? null : at;
|
|
116
|
-
const forge = useRead(named && signed ? () => one(
|
|
123
|
+
const forge = useRead(named && signed ? () => one(target, named.name) : null, null, [target, signed, named?.name]);
|
|
117
124
|
const project = all.value.find((p) => (named ? siteOf(p, named) : 'slug' in at && p.slug === at.slug)) ?? null;
|
|
118
125
|
// The site this workspace publishes to: the project's, or the repository's name as one.
|
|
119
126
|
const slug = project?.slug ?? ('slug' in at ? at.slug : slugOf(named?.name ?? ''));
|
|
@@ -135,7 +142,7 @@ export function Project({ at }) {
|
|
|
135
142
|
return () => clearInterval(every);
|
|
136
143
|
}, [building, again]);
|
|
137
144
|
// When the build began, and why it failed, are its latest deployment's to say.
|
|
138
|
-
const failure = useRead(building || failed ? () => deployments(
|
|
145
|
+
const failure = useRead(building || failed ? () => deployments(target, slug) : null, [], [target, slug, building, failed, project?.updated]);
|
|
139
146
|
const latest = failure.value[0];
|
|
140
147
|
const began = latest?.status === 'building' ? latest.created : 0;
|
|
141
148
|
const ran = began ? Math.max(0, Math.floor(now / 1000) - began) : 0;
|
|
@@ -150,7 +157,7 @@ export function Project({ at }) {
|
|
|
150
157
|
return;
|
|
151
158
|
setRedo({ busy: true, error: '' });
|
|
152
159
|
try {
|
|
153
|
-
await declare(
|
|
160
|
+
await declare(target, { repo: clone, ref: home, name: slug, project: slug, mode: 'branch' });
|
|
154
161
|
setNow(Date.now());
|
|
155
162
|
again();
|
|
156
163
|
setRedo({ busy: false, error: '' });
|
|
@@ -161,7 +168,7 @@ export function Project({ at }) {
|
|
|
161
168
|
};
|
|
162
169
|
// The site's runs, or the org's when the repository has no site yet.
|
|
163
170
|
const sited = !named || Boolean(project);
|
|
164
|
-
const listed = useRead(signed ? () => list(
|
|
171
|
+
const listed = useRead(signed ? () => list(target, sited ? { kind: 'coding', project: slug, limit: 50 } : { kind: 'coding', limit: 50 }) : null, [], [target, signed, slug, sited]);
|
|
165
172
|
// Only the runs on this project's own repository: a record can be moved into
|
|
166
173
|
// any project, and a run's branch is what Files, Code and Publish read.
|
|
167
174
|
const mine = (r) => (named ? ours(r.repo, `${named.org}/${named.name}`) && Boolean(r.repo) : ours(r.repo, project?.repo ?? ''));
|
|
@@ -169,7 +176,7 @@ export function Project({ at }) {
|
|
|
169
176
|
const ordered = useMemo(() => [...runs.value].reverse(), [listed.value, project?.repo, named?.org, named?.name]);
|
|
170
177
|
const [chosen, setChosen] = useState(null);
|
|
171
178
|
const current = chosen ?? runs.value[0]?.id ?? null;
|
|
172
|
-
const run = useRun(
|
|
179
|
+
const run = useRun(target, current);
|
|
173
180
|
const end = outcome(run.events);
|
|
174
181
|
const pr = pull(run.record?.pr ?? '', run.record?.repo ?? '');
|
|
175
182
|
const running = LIVE.has(run.status || end.status);
|
|
@@ -199,15 +206,15 @@ export function Project({ at }) {
|
|
|
199
206
|
const [picked, setPicked] = useState([]);
|
|
200
207
|
const [attached, setAttached] = useState([]);
|
|
201
208
|
// The model and effort Chat and New share, and dictation into the ask.
|
|
202
|
-
const mind = useMind(
|
|
203
|
-
const voice = useVoice({ speech: useEar(
|
|
209
|
+
const mind = useMind(target);
|
|
210
|
+
const voice = useVoice({ speech: useEar(target), language: useSpoken(), ...useDictation(draft, setDraft) });
|
|
204
211
|
const [bridge, setBridge] = useState(false);
|
|
205
212
|
const [editing, setEditing] = useState(false);
|
|
206
213
|
const [pageLines, setPageLines] = useState([]);
|
|
207
214
|
const [history, setHistory] = useState(false);
|
|
208
215
|
const [publish, setPublish] = useState(null);
|
|
209
216
|
const [switcher, setSwitcher] = useState(false);
|
|
210
|
-
const repos = useRead(switcher && signed ? () => codebases(
|
|
217
|
+
const repos = useRead(switcher && signed ? () => codebases(target) : null, [], [target, switcher, signed]);
|
|
211
218
|
const frame = useRef(null);
|
|
212
219
|
// What was published may be new files at the same address: the project is read
|
|
213
220
|
// for its address, and the frame loads again.
|
|
@@ -235,7 +242,7 @@ export function Project({ at }) {
|
|
|
235
242
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
236
243
|
}, [current, run.record, end.published]);
|
|
237
244
|
// Where the open run's pull request stands on the forge: Merge is offered while it is open.
|
|
238
|
-
const proposal = useRead(signed && current && pr.href ? () => pushed(
|
|
245
|
+
const proposal = useRead(signed && current && pr.href ? () => pushed(target, current).then((c) => ({ run: current, pull: c.pull })) : null, null, [target, signed, current, pr.href]);
|
|
239
246
|
const proposing = proposal.value?.run === current && proposal.value.pull?.state === 'open';
|
|
240
247
|
// Files is drawn again after a merge, from what the forge holds now.
|
|
241
248
|
const [round, setRound] = useState(0);
|
|
@@ -255,9 +262,9 @@ export function Project({ at }) {
|
|
|
255
262
|
return;
|
|
256
263
|
setFiles((was) => [...was, { path, readOnly: true }]);
|
|
257
264
|
try {
|
|
258
|
-
const b = await blob(
|
|
265
|
+
const b = await blob(target, repo, ref, path);
|
|
259
266
|
const content = b.binary ? '' : b.truncated ? '' : b.text;
|
|
260
|
-
const error = b.binary ? 'A binary file — nothing to show as text.' : b.truncated ? 'Past the 1 MiB view cap — clone the repository to read it.' : undefined;
|
|
267
|
+
const error = b.binary ? t('A binary file — nothing to show as text.') : b.truncated ? t('Past the 1 MiB view cap — clone the repository to read it.') : undefined;
|
|
261
268
|
setFiles((was) => was.map((f) => (f.path === path ? { ...f, content, error } : f)));
|
|
262
269
|
}
|
|
263
270
|
catch (e) {
|
|
@@ -265,21 +272,21 @@ export function Project({ at }) {
|
|
|
265
272
|
}
|
|
266
273
|
};
|
|
267
274
|
// Pages: the HTML files at the repository's root, as the page picker's list.
|
|
268
|
-
const root = useRead(repo ? () => tree(
|
|
275
|
+
const root = useRead(repo ? () => tree(target, repo, ref, '') : null, [], [target, repo, ref]);
|
|
269
276
|
const pages = useMemo(() => [
|
|
270
|
-
{ id: '/', label: 'Homepage' },
|
|
277
|
+
{ id: '/', label: t('Homepage') },
|
|
271
278
|
...root.value
|
|
272
279
|
.filter((e) => !e.dir && /\.html?$/i.test(e.name) && !/^index\.html?$/i.test(e.name))
|
|
273
280
|
.map((e) => ({ id: `/${e.path}`, label: e.name.replace(/\.html?$/i, '') })),
|
|
274
|
-
], [root.value]);
|
|
281
|
+
], [root.value, t]);
|
|
275
282
|
// A project taken from a starter, not published and not being, shows the
|
|
276
283
|
// starter's own live page, which is what the copy is until its first publish.
|
|
277
284
|
const forked = project && !project.live && !building && !failed ? project.forked : '';
|
|
278
|
-
const starters = useRead(forked ? () => templates(
|
|
285
|
+
const starters = useRead(forked ? () => templates(target) : null, [], [target.api, forked]);
|
|
279
286
|
const starter = forked ? starters.value.find((s) => s.slug === forked && s.demo) : undefined;
|
|
280
287
|
const shown = project?.live || starter?.demo || '';
|
|
281
288
|
const src = shown ? new URL(page, shown).toString() : null;
|
|
282
|
-
const why = failure.value[0]?.message || failure.error?.message || (failure.loading ? '' : 'Its build ended without saying why.');
|
|
289
|
+
const why = failure.value[0]?.message || failure.error?.message || (failure.loading ? '' : t('Its build ended without saying why.'));
|
|
283
290
|
const merged = () => {
|
|
284
291
|
proposal.reload();
|
|
285
292
|
root.reload();
|
|
@@ -291,11 +298,11 @@ export function Project({ at }) {
|
|
|
291
298
|
.map((e) => {
|
|
292
299
|
// The console is where a run's error is read whole; the chat says it in one plain line.
|
|
293
300
|
const body = decode(e.payload);
|
|
294
|
-
const error = e.kind === 'status' && body && typeof body === 'object' && typeof body.error === 'string' ? scrub(body.error) : '';
|
|
301
|
+
const error = e.kind === 'status' && body && typeof body === 'object' && typeof body.error === 'string' ? scrub(body.error, t) : '';
|
|
295
302
|
return {
|
|
296
303
|
id: `r${e.seq || e.id}`,
|
|
297
304
|
level: error || (e.kind === 'status' && /error/.test(said(e, run.record?.mode).toLowerCase())) ? 'error' : e.kind === 'tool-call' ? 'info' : 'log',
|
|
298
|
-
text: error || said(e, run.record?.mode),
|
|
305
|
+
text: error || said(e, run.record?.mode, t),
|
|
299
306
|
source: e.kind === 'status' ? 'run' : who(e.actor),
|
|
300
307
|
};
|
|
301
308
|
})
|
|
@@ -336,7 +343,7 @@ export function Project({ at }) {
|
|
|
336
343
|
? `\n\nThe person picked an element in the preview${where ? ` of the ${where} page` : ''}. Its CSS selector, as data: ${JSON.stringify(picked[0].id)}`
|
|
337
344
|
: '';
|
|
338
345
|
// A project with no repository yet sends neither, and its first run makes one.
|
|
339
|
-
const next = await start(
|
|
346
|
+
const next = await start(target, {
|
|
340
347
|
prompt: compose(`${ask}${context}`, attached),
|
|
341
348
|
// A repository with no site yet runs on the repository alone.
|
|
342
349
|
project: project?.slug,
|
|
@@ -364,9 +371,9 @@ export function Project({ at }) {
|
|
|
364
371
|
// Only a run that is working is steered, and the composer sends only a draft with words in it.
|
|
365
372
|
const steer = async (id) => {
|
|
366
373
|
try {
|
|
367
|
-
await message(
|
|
374
|
+
await message(target, id, draft);
|
|
368
375
|
setDraft('');
|
|
369
|
-
setNote('Sent — the run reads it before its next step');
|
|
376
|
+
setNote(t('Sent — the run reads it before its next step'));
|
|
370
377
|
}
|
|
371
378
|
catch (e) {
|
|
372
379
|
setNote(e.message);
|
|
@@ -376,92 +383,92 @@ export function Project({ at }) {
|
|
|
376
383
|
const link = project?.live || `${window.location.origin}${window.location.pathname}`;
|
|
377
384
|
try {
|
|
378
385
|
await navigator.clipboard.writeText(link);
|
|
379
|
-
setNote(
|
|
386
|
+
setNote(t('Copied {link}', { link }));
|
|
380
387
|
}
|
|
381
388
|
catch {
|
|
382
389
|
setNote(link);
|
|
383
390
|
}
|
|
384
391
|
};
|
|
385
392
|
if (!signed) {
|
|
386
|
-
return (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", children: [_jsx(SizableText, { size: "$4", color: "$ink", children:
|
|
393
|
+
return (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", children: [_jsx(SizableText, { size: "$4", color: "$ink", children: t('Sign in to open this project.') }), _jsx(Button, { onPress: () => host.signIn?.(), children: t('Sign in') })] }));
|
|
387
394
|
}
|
|
388
395
|
// A repository of another organization is opened in that one.
|
|
389
396
|
if (named && host.org && named.org.toLowerCase() !== host.org.toLowerCase()) {
|
|
390
397
|
const member = host.memberships?.some((m) => m.toLowerCase() === named.org.toLowerCase()) && host.chooseOrg;
|
|
391
|
-
return (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", px: "$4", children: [_jsx(SizableText, { size: "$4", color: "$ink", text: "center", children:
|
|
398
|
+
return (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", px: "$4", children: [_jsx(SizableText, { size: "$4", color: "$ink", text: "center", children: t('{repo} is in {org}, and you are working in {current}.', { repo: `${named.org}/${named.name}`, org: named.org, current: host.org }) }), member ? (_jsx(Button, { variant: "outline", onPress: () => host.chooseOrg?.(named.org), children: t('Switch to {org}', { org: named.org }) })) : (_jsx(Button, { variant: "outline", onPress: () => host.go(path({ kind: 'screen', screen: 'projects' })), children: t('See this organization’s projects') }))] }));
|
|
392
399
|
}
|
|
393
400
|
if (named ? !forge.loading && !forge.value : !all.loading && !project && !all.error) {
|
|
394
401
|
return (_jsxs(YStack, { flex: 1, items: "center", justify: "center", gap: "$3", px: "$4", children: [_jsx(SizableText, { size: "$4", color: "$ink", text: "center", children: named
|
|
395
402
|
? forge.error && !/not found|404/i.test(forge.error.message)
|
|
396
403
|
? forge.error.message
|
|
397
|
-
:
|
|
404
|
+
: t('{org} has no repository named {name}.', { org: named.org, name: named.name })
|
|
398
405
|
: host.org
|
|
399
|
-
?
|
|
400
|
-
:
|
|
406
|
+
? t('{org} has no project named {name}.', { org: host.org, name: slug })
|
|
407
|
+
: t('There is no project named {name}.', { name: slug }) }), _jsx(Button, { variant: "outline", onPress: () => host.go(path({ kind: 'screen', screen: named ? 'projects' : 'artifacts' })), children: named ? t('See this organization’s projects') : t('See what this organization has built') })] }));
|
|
401
408
|
}
|
|
402
409
|
const chat = (_jsxs(YStack, { "data-slot": "project-chat", flex: 1, minH: 0, position: "relative", children: [_jsx(YStack, { flex: 1, minH: 0, overflow: "scroll", px: "$3", pt: "$2", pb: "$4", gap: "$4", $md: { pr: 0 }, children: ordered.length === 0 ? (_jsx(SizableText, { size: "$2", color: "$ink", children: runs.loading
|
|
403
|
-
? 'Reading this project’s runs…'
|
|
410
|
+
? t('Reading this project’s runs…')
|
|
404
411
|
: project?.live
|
|
405
|
-
?
|
|
412
|
+
? t('{name} is loaded — it is in the preview, and every run on it is under the clock above. Say what to change and it gets built.', { name: project.name })
|
|
406
413
|
: stalled
|
|
407
|
-
?
|
|
414
|
+
? t('{name} has been publishing for {minutes} minutes without finishing. Publish it again from the preview, or say what to change and it gets built.', { name: project?.name ?? '', minutes: Math.floor(ran / 60) })
|
|
408
415
|
: failed
|
|
409
|
-
?
|
|
416
|
+
? t('{name} could not be published. Publish it again from the preview, or say what to change and it gets built.', { name: project?.name ?? '' })
|
|
410
417
|
: building
|
|
411
|
-
?
|
|
418
|
+
? t('{name} is being published — it appears in the preview when its build finishes. Say what to change and it gets built.', { name: project?.name ?? '' })
|
|
412
419
|
: starter
|
|
413
|
-
?
|
|
420
|
+
? t('{name} is loaded — the preview shows the {starter} starter until your copy is published. Say what to change and it gets built.', { name: project?.name ?? '', starter: starter.title })
|
|
414
421
|
: project
|
|
415
|
-
?
|
|
422
|
+
? t('{name} is loaded and nothing is published yet. Say what to change and it gets built.', { name: project.name })
|
|
416
423
|
: named
|
|
417
|
-
?
|
|
418
|
-
:
|
|
419
|
-
? () => void stop(
|
|
420
|
-
: undefined, busy: busy || (running && !draft.trim()), placeholder: running ? 'Steer this run' : 'Ask Hanzo for edits', label:
|
|
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, attached.length ? (_jsx(XStack, { gap: 6, flexWrap: "wrap", children: _jsx(Files, { files: attached, onFiles: setAttached }) })) : null, _jsx(Prompt, { framed: true, value: draft, onChange: setDraft, onSend: () => void (current && running ? steer(current) : send()), onStop: current
|
|
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: ['auto', 'plan'] }, mind: running ? null : 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] }));
|
|
421
428
|
// The preview stays loaded while another view is open, so coming back to it
|
|
422
429
|
// is the page as it was left.
|
|
423
|
-
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":
|
|
424
|
-
? 'Publishing is taking too long'
|
|
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
|
|
431
|
+
? t('Publishing is taking too long')
|
|
425
432
|
: building
|
|
426
433
|
? project?.forked
|
|
427
|
-
? 'Publishing your copy…'
|
|
428
|
-
: 'Publishing…'
|
|
434
|
+
? t('Publishing your copy…')
|
|
435
|
+
: t('Publishing…')
|
|
429
436
|
: failed
|
|
430
|
-
? 'Publishing failed'
|
|
431
|
-
: 'Nothing deployed yet' }), _jsx(SizableText, { size: "$2", color: "$soft", text: "center", children: stalled
|
|
432
|
-
?
|
|
437
|
+
? t('Publishing failed')
|
|
438
|
+
: t('Nothing deployed yet') }), _jsx(SizableText, { size: "$2", color: "$soft", text: "center", children: stalled
|
|
439
|
+
? t('Its build started {minutes} minutes ago and has not finished.', { minutes: Math.floor(ran / 60) })
|
|
433
440
|
: building
|
|
434
|
-
? 'Its page appears here when the build finishes.'
|
|
441
|
+
? t('Its page appears here when the build finishes.')
|
|
435
442
|
: failed
|
|
436
443
|
? why
|
|
437
|
-
: 'Publish this project and its page appears here.' }), (stalled || failed) && clone ? (_jsx(Button, { variant: "outline", disabled: redo.busy, onPress: () => void retry(), children:
|
|
444
|
+
: t('Publish this project and its page appears here.') }), (stalled || failed) && clone ? (_jsx(Button, { variant: "outline", disabled: redo.busy, onPress: () => void retry(), children: t('Publish again') })) : null, redo.error ? (_jsx(SizableText, { size: "$2", color: "$soft", text: "center", role: "alert", children: redo.error })) : null] }), toolbar: bridge ? (_jsx(XStack, { render: "button", "aria-pressed": editing, "aria-label": editing ? t('Stop picking elements') : t('Pick an element to edit'), onPress: () => {
|
|
438
445
|
const next = !editing;
|
|
439
446
|
setEditing(next);
|
|
440
447
|
frame.current?.post({ type: 'preview:editable', active: next });
|
|
441
448
|
}, p: "$2", rounded: "$10", children: _jsx(MousePointerClick, { size: 16 }) })) : undefined })] }));
|
|
442
|
-
const body = view === 'preview' ? null : view === 'files' ? (repo && root.error ? (_jsx(SizableText, { size: "$2", color: "$soft", p: "$4", role: "status", children:
|
|
449
|
+
const body = view === 'preview' ? null : view === 'files' ? (repo && root.error ? (_jsx(SizableText, { size: "$2", color: "$soft", p: "$4", role: "status", children: t('Nothing to show for {repo} at {ref}: the forge answered “{reason}”.', { repo, ref, reason: root.error.message }) })) : repo ? (_jsx(FileTree, { load: async (dir) => (await tree(target, repo, ref, dir)).map((e) => ({ path: e.path, kind: e.dir ? 'dir' : 'file' })), value: file, onSelect: (p) => void openFile(p), label: t('{repo} at {ref}', { repo, ref }) }, round)) : (_jsx(SizableText, { size: "$2", color: "$soft", p: "$4", children: t('This project has no repository yet. Its first run creates one.') }))) : view === 'code' ? (_jsx(FileTabs, { files: files, value: file, onSelect: setFile, onClose: (p) => {
|
|
443
450
|
setFiles((was) => was.filter((f) => f.path !== p));
|
|
444
451
|
if (file === p)
|
|
445
452
|
setFile(null);
|
|
446
|
-
}, empty:
|
|
447
|
-
? 'Pick an element in the preview to see it here and attach it to your next ask.'
|
|
448
|
-
: 'Layers are read through the preview bridge, and this page does not run it. Its elements cannot be listed from here.' })) }));
|
|
449
|
-
return (_jsxs(_Fragment, { children: [_jsxs(Workspace, { start: _jsxs(XStack, { items: "center", gap: "$2", minW: 0, shrink: 1, children: [_jsx(XStack, { render: "button", "aria-label":
|
|
453
|
+
}, empty: _jsx(SizableText, { size: "$2", color: "$soft", p: "$4", children: t('Open a file from Files. Code here is read at {ref}; ask in the chat to change it.', { ref }) }) })) : (_jsx(YStack, { p: "$4", gap: "$2", children: picked.length ? (picked.map((p) => (_jsxs(SizableText, { size: "$2", color: "$ink", children: [p.label, " \u2014 ", p.id] }, p.id)))) : (_jsx(SizableText, { size: "$2", color: "$soft", children: bridge
|
|
454
|
+
? t('Pick an element in the preview to see it here and attach it to your next ask.')
|
|
455
|
+
: t('Layers are read through the preview bridge, and this page does not run it. Its elements cannot be listed from here.') })) }));
|
|
456
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Workspace, { start: _jsxs(XStack, { items: "center", gap: "$2", minW: 0, shrink: 1, children: [_jsx(XStack, { render: "button", "aria-label": t('All runs'), onPress: () => host.go(''), p: "$1", shrink: 0, children: _jsx(HanzoMark, { size: 20 }) }), _jsx(ProjectChip, { name: title, onPress: () => setSwitcher(true) }), _jsx(XStack, { render: "button", "aria-label": t('History'), onPress: () => setHistory(true), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, children: _jsx(Clock, { size: 16 }) }), _jsx(XStack, { render: "button", "aria-label": shut ? t('Show the chat') : t('Hide the chat'), "aria-pressed": !shut, onPress: () => (cramped ? setAsked(!asked) : setCollapsed(!collapsed)), p: "$1.5", rounded: "$3", shrink: 0, hoverStyle: { bg: '$hover' }, "$max-md": { display: 'none' }, children: _jsx(PanelLeft, { size: 16 }) })] }), middle: _jsx(Views, { views: [CHAT, ...VIEWS], value: pane === 'chat' && !wide ? 'chat' : view, onChange: (v) => {
|
|
450
457
|
if (v === 'chat')
|
|
451
458
|
return setPane('chat');
|
|
452
459
|
setView(v);
|
|
453
460
|
setPane('view');
|
|
454
|
-
}, label:
|
|
461
|
+
}, label: t('View') }), end: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(XStack, { display: "none", $md: { display: 'flex' }, children: _jsxs(Button, { variant: "outline", size: "sm", onPress: () => void share(), children: [_jsx(Share2, { size: 14 }), " ", t('Share')] }) }), _jsxs(Button, { size: "sm", "aria-label": t('Publish'), disabled: !clone || !slug, onPress: () => setPublish({ repo: clone, title, ref, name: slug, project: slug }), children: [_jsx(Upload, { size: 14 }), _jsx(SizableText, { size: "$2", color: "inherit", "$max-md": { display: 'none' }, children: t('Publish') })] })] }), chat: chat, dock: _jsx(Console, { lines: lines, height: dock, onHeight: setDock, onClear: () => setPageLines([]), status: run.status || end.status || undefined, label: t('Console') }), collapsed: shut, pane: pane, width: across, onLayout: (e) => setRoom(e.nativeEvent.layout.width), children: [preview, body] }), _jsx(Dialog, { open: history, onOpenChange: setHistory, children: _jsxs(DialogContent, { maxW: 480, children: [_jsx(DialogTitle, { children: t('Runs on {title}', { title }) }), _jsx(YStack, { gap: "$1", children: runs.value.length === 0 ? (_jsx(SizableText, { size: "$2", color: "$soft", children: t('No runs yet.') })) : (runs.value.map((r) => (_jsxs(XStack, { render: "button", onPress: () => {
|
|
455
462
|
setChosen(r.id);
|
|
456
463
|
setHistory(false);
|
|
457
|
-
}, justify: "space-between", px: "$2", py: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: [_jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, flex: 1, children: r.title || 'Untitled run' }), _jsx(SizableText, { size: "$1", color: "$soft", children: r.status })] }, r.id)))) })] }) }), _jsx(Dialog, { open: switcher, onOpenChange: setSwitcher, children: _jsxs(DialogContent, { maxW: 420, children: [_jsx(DialogTitle, { children:
|
|
464
|
+
}, justify: "space-between", px: "$2", py: "$1.5", rounded: "$3", hoverStyle: { bg: '$hover' }, children: [_jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, flex: 1, children: r.title || t('Untitled run') }), _jsx(SizableText, { size: "$1", color: "$soft", children: r.status })] }, r.id)))) })] }) }), _jsx(Dialog, { open: switcher, onOpenChange: setSwitcher, children: _jsxs(DialogContent, { maxW: 420, children: [_jsx(DialogTitle, { children: t('Projects') }), _jsx(YStack, { gap: "$1", maxH: 420, overflow: "scroll", children: repos.error ? (_jsx(SizableText, { size: "$2", color: "$soft", children: repos.error.message })) : repos.loading && repos.value.length === 0 ? (_jsx(SizableText, { size: "$2", color: "$soft", children: t('Reading the forge…') })) : (repos.value.map((c) => {
|
|
458
465
|
const here = Boolean(named) && c.name === named?.name;
|
|
459
|
-
return (_jsx(XStack, { render: "button", "aria-label":
|
|
466
|
+
return (_jsx(XStack, { render: "button", "aria-label": t('Open {repo}', { repo: `${c.org}/${c.name}` }), "aria-current": here ? 'page' : undefined, onPress: () => {
|
|
460
467
|
setSwitcher(false);
|
|
461
468
|
host.go(path({ kind: 'repo', org: c.org || host.org || '', name: c.name }));
|
|
462
469
|
}, px: "$2", py: "$1.5", rounded: "$3", bg: here ? '$hover' : undefined, hoverStyle: { bg: '$hover' }, children: _jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, children: c.name }) }, `${c.org}/${c.name}`));
|
|
463
470
|
})) }), _jsx(Button, { variant: "outline", size: "sm", onPress: () => {
|
|
464
471
|
setSwitcher(false);
|
|
465
472
|
host.go(path({ kind: 'screen', screen: 'projects' }));
|
|
466
|
-
}, children:
|
|
473
|
+
}, children: t('All projects') })] }) }), _jsx(Publish, { source: publish, onClose: () => setPublish(null), onLive: fresh })] }));
|
|
467
474
|
}
|
package/lib/prompt.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type Say } from '@hanzo/i18n/react';
|
|
1
2
|
import { type ComposerProps } from '@hanzo/ui/chat';
|
|
2
3
|
import { type PauseSource } from '@hanzo/ui/models';
|
|
3
4
|
import { type Machine, type Speech } from '@hanzo/voice';
|
|
@@ -19,7 +20,7 @@ export declare const measure: {
|
|
|
19
20
|
/** The round controls' box, px, and a phone's, a finger's width. */
|
|
20
21
|
export declare const ROUND = 32;
|
|
21
22
|
/** What every Composer placeholder says, unless a surface names who is listening. */
|
|
22
|
-
export declare const ASK
|
|
23
|
+
export declare const ASK: string;
|
|
23
24
|
/**
|
|
24
25
|
* The frame as a pane of its own, floating on the pane it is drawn in: the
|
|
25
26
|
* `--pane-*` cut on design's first paper rung.
|
|
@@ -107,23 +108,23 @@ export declare function trigger(value: string): {
|
|
|
107
108
|
query: string;
|
|
108
109
|
at: number;
|
|
109
110
|
} | null;
|
|
110
|
-
/**
|
|
111
|
-
export
|
|
111
|
+
/** What `useMention` hands `prompt`: the lit row of a `/` or `@` list, and the reader's `t`. */
|
|
112
|
+
export interface Mentioned {
|
|
112
113
|
index: number;
|
|
113
114
|
closed: string | null;
|
|
114
|
-
|
|
115
|
+
t?: Say;
|
|
116
|
+
}
|
|
117
|
+
/** @hanzo/ui `Composer` props for the one composer. */
|
|
118
|
+
export declare function prompt(o: PromptOptions, state?: Mentioned): ComposerProps;
|
|
115
119
|
/** Ctrl+U (⌘U) uploads, while a composer with an upload is on the page. */
|
|
116
120
|
export declare function useUpload(upload: (() => void) | undefined): void;
|
|
117
121
|
/**
|
|
118
|
-
* The composer's own state — which row of a `/` or `@` list is lit
|
|
119
|
-
* keys that move it. A surface that hands `prompt` to
|
|
120
|
-
* passes its answer through; `<Prompt>` does it for itself.
|
|
122
|
+
* The composer's own state — which row of a `/` or `@` list is lit, and the
|
|
123
|
+
* reader's `t` — and the keys that move it. A surface that hands `prompt` to
|
|
124
|
+
* `Chat` calls this and passes its answer through; `<Prompt>` does it for itself.
|
|
121
125
|
*/
|
|
122
126
|
export declare function useMention(o: PromptOptions): {
|
|
123
|
-
state:
|
|
124
|
-
index: number;
|
|
125
|
-
closed: string | null;
|
|
126
|
-
};
|
|
127
|
+
state: Mentioned;
|
|
127
128
|
field: ComposerProps['field'];
|
|
128
129
|
};
|
|
129
130
|
/** The composer drawn on its own. */
|