@hanzo/app 0.2.9 → 0.2.10
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/dist/App.js +10 -4
- package/dist/Learning.d.ts +5 -0
- package/dist/Learning.js +93 -0
- package/dist/Me.js +9 -5
- package/dist/Setup.js +2 -2
- package/dist/learning.d.ts +79 -0
- package/dist/learning.js +48 -0
- package/package.json +1 -1
package/dist/App.js
CHANGED
|
@@ -61,14 +61,15 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
61
61
|
// so the builder's own plans are never drawn.
|
|
62
62
|
import { useI18n } from '@hanzo/i18n/react';
|
|
63
63
|
import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
64
|
-
import { ArrowLeft, ArrowRight } from 'lucide-react';
|
|
64
|
+
import { ArrowLeft, ArrowRight, RefreshCw } from 'lucide-react';
|
|
65
65
|
import { AiProvider } from '@hanzo/ai/react';
|
|
66
66
|
import { XStack, YStack } from '@hanzo/gui';
|
|
67
|
-
import { Builder, HostProvider, PrefsProvider, route } from '@hanzo/build';
|
|
67
|
+
import { Builder, HostProvider, PrefsProvider, route, tabs } from '@hanzo/build';
|
|
68
68
|
import { nav as places } from '@hanzo/studio';
|
|
69
69
|
import { useAi } from './lib/ai.js';
|
|
70
70
|
import { Chat } from './Chat.js';
|
|
71
71
|
import { Column } from './Column.js';
|
|
72
|
+
import { Learning } from './Learning.js';
|
|
72
73
|
import { isPlans, leave, useDevHost } from './dev.js';
|
|
73
74
|
import { Orgs } from './Orgs.js';
|
|
74
75
|
import { useRooms } from './host.js';
|
|
@@ -118,6 +119,7 @@ function Window({ bar }) {
|
|
|
118
119
|
const [closed, setClosed] = useKept('hanzo.build.rail', false);
|
|
119
120
|
// The palette is open (the sidebar's search; ⌘K toggles it).
|
|
120
121
|
const [finding, setFinding] = useState(false);
|
|
122
|
+
const [learning, setLearning] = useState(false);
|
|
121
123
|
const found = useCallback(() => setFinding(false), []);
|
|
122
124
|
const dev = useDevHost();
|
|
123
125
|
// The builder's moves put the drawer away, in its place in the history; one to
|
|
@@ -254,7 +256,11 @@ function Window({ bar }) {
|
|
|
254
256
|
pick(o);
|
|
255
257
|
window.location.assign(mode === 'dev' ? app('') : mode === 'studio' ? studio('') : mode === 'tabs' ? TERMINALS : talk());
|
|
256
258
|
};
|
|
257
|
-
const sidebar = mode === 'studio' ? places(maker, (p) => maker.go(p)) :
|
|
259
|
+
const sidebar = mode === 'studio' ? places(maker, (p) => maker.go(p)) : mode === 'dev' && raised(where) === 'customize'
|
|
260
|
+
? tabs(host, (p) => host.go(p), t).map((group) => ({ ...group, links: [...group.links, {
|
|
261
|
+
id: 'learning', label: t('Learning'), active: learning, icon: _jsx(RefreshCw, { size: 16, "aria-hidden": true }),
|
|
262
|
+
onPress: () => { shut(); setLearning(true); },
|
|
263
|
+
}] })) : undefined;
|
|
258
264
|
const flip = () => setClosed(!closed);
|
|
259
265
|
const seek = () => {
|
|
260
266
|
shut();
|
|
@@ -262,5 +268,5 @@ function Window({ bar }) {
|
|
|
262
268
|
};
|
|
263
269
|
return (_jsx(AiProvider, { client: client ?? undefined, children: _jsx(HostProvider, { host: host, children: _jsx(PrefsProvider, { children: _jsxs(YStack, { ref: windowRef, "data-slot": "window", ...(bar ? floor : null), flex: 1, minW: 0, height: "100dvh", overflow: "hidden", "$max-md": { position: 'fixed', t: TOP, l: 0, r: 0, height: HEIGHT, ...{ overflow: 'clip' } }, children: [bar ? (_jsxs(XStack, { "data-slot": "title-bar", height: BAR, shrink: 0, items: "center", gap: 2, pl: (bar.inset ?? 0) + 6, pr: "$2", ...bar.drag, children: [_jsx(XStack, { "$max-md": { display: 'none' }, children: _jsx(Toggle, { open: !closed, onPress: flip }) }), _jsx(XStack, { $md: { display: 'none' }, children: _jsx(Toggle, { open: drawer.state === 'open', onPress: drawer.open, label: t('Open sidebar') }) }), _jsx(Mark, { label: t('Back'), "data-slot": "back", onPress: () => router.back(), children: _jsx(ArrowLeft, { size: 18, "aria-hidden": true, "data-i18n-mirror": true }) }), _jsx(Mark, { label: t('Forward'), "data-slot": "forward", onPress: () => router.forward(), children: _jsx(ArrowRight, { size: 18, "aria-hidden": true, "data-i18n-mirror": true }) }), _jsx(XStack, { flex: 1, self: "stretch", ...bar.drag })] })) : null, _jsxs(XStack, { ref: drawer.pane, "data-room": "", "data-app": mode, "data-slot": "frame", ...(bar ? null : floor), ...(drawer.slide ? RAISED : null),
|
|
264
270
|
// inline-style: the slide, a transform with no logical form whose sign and distance the drawer writes on the element itself, a drag every frame (Drawer.tsx).
|
|
265
|
-
style: drawer.slide ?? undefined, flex: 1, minW: 0, minH: 0, paddingInlineEnd: "var(--pane-gap)", pb: "var(--pane-gap)", pt: 0, "$max-md": { p: 0 }, children: [drawer.slide ? _jsx(Lift, {}) : null, _jsx(YStack, { height: "100%", "$max-md": { display: 'none' }, children: _jsx(Rail, { host: host, mode: mode, at: where, open: !closed, toggle: bar ? undefined : flip, onChat: toChat, onDev: toDev, onStudio: toStudio, onTabs: toTabs, go: move, onOrg: org }) }), closed || mode === 'tabs' ? null : (_jsx(YStack, { "$max-md": { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: () => setClosed(true), onSeek: seek, onBack: back, places: sidebar }) })), _jsxs(YStack, { "data-slot": "stage", flex: 1, minW: 0, minH: 0, marginInlineStart: closed || mode === 'tabs' ? 0 : 'var(--pane-gap)', "$max-md": { marginInlineStart: 0, pb: FOOT }, children: [bar ? null : _jsx(Top, { host: host, mode: mode, places: sidebar, open: drawer.state !== 'shut', go: move, onMenu: drawer.open, onChat: toChat, onDev: toDev }), _jsx(XStack, { "data-slot": "panes", flex: 1, minW: 0, minH: 0, children: mode === 'chat' ? (_jsx(Paned, { framed: true, children: _jsx(Chat, {}) })) : mode === 'tabs' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(Suspense, { fallback: null, children: _jsx(Terminals, {}) }) })) : mode === 'studio' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(YStack, { flex: 1, minH: 0, height: "100%", children: _jsx(Suspense, { fallback: null, children: _jsx(Studio, { host: maker, rail: false }) }) }) })) : (_jsx(YStack, { render: "main", "data-slot": "pane", ...(run ? null : { ...pane(), overflow: 'hidden' }), flex: 1, minW: 0, minH: 0, children: plans ? null : _jsx(Builder, { host: host, rail: false }) })) })] })] }), drawer.state === 'shut' ? null : (_jsx(Layer, { drawer: drawer, top: bar ? BAR : 0, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: shut, onSeek: seek, onBack: back, drawer: true, places: sidebar, rail: _jsx(Rail, { host: host, mode: mode, at: where, open: true, rows: true, onChat: toChat, onDev: toDev, onStudio: toStudio, onTabs: toTabs, go: move, onOrg: org }), foot: _jsx(Foot, { host: host, onChat: fresh, onOrg: org }) }) })), finding ? _jsx(Palette, { go: move, onClose: found, mode: mode, host: host }) : null] }) }) }) }));
|
|
271
|
+
style: drawer.slide ?? undefined, flex: 1, minW: 0, minH: 0, paddingInlineEnd: "var(--pane-gap)", pb: "var(--pane-gap)", pt: 0, "$max-md": { p: 0 }, children: [drawer.slide ? _jsx(Lift, {}) : null, _jsx(YStack, { height: "100%", "$max-md": { display: 'none' }, children: _jsx(Rail, { host: host, mode: mode, at: where, open: !closed, toggle: bar ? undefined : flip, onChat: toChat, onDev: toDev, onStudio: toStudio, onTabs: toTabs, go: move, onOrg: org }) }), closed || mode === 'tabs' ? null : (_jsx(YStack, { "$max-md": { display: 'none' }, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: () => setClosed(true), onSeek: seek, onBack: back, places: sidebar }) })), _jsxs(YStack, { "data-slot": "stage", flex: 1, minW: 0, minH: 0, marginInlineStart: closed || mode === 'tabs' ? 0 : 'var(--pane-gap)', "$max-md": { marginInlineStart: 0, pb: FOOT }, children: [bar ? null : _jsx(Top, { host: host, mode: mode, places: sidebar, open: drawer.state !== 'shut', go: move, onMenu: drawer.open, onChat: toChat, onDev: toDev }), _jsx(XStack, { "data-slot": "panes", flex: 1, minW: 0, minH: 0, children: mode === 'chat' ? (_jsx(Paned, { framed: true, children: _jsx(Chat, {}) })) : mode === 'tabs' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(Suspense, { fallback: null, children: _jsx(Terminals, {}) }) })) : mode === 'studio' ? (_jsx(YStack, { render: "main", "data-slot": "pane", ...pane(), overflow: "hidden", flex: 1, minW: 0, minH: 0, children: _jsx(YStack, { flex: 1, minH: 0, height: "100%", children: _jsx(Suspense, { fallback: null, children: _jsx(Studio, { host: maker, rail: false }) }) }) })) : (_jsx(YStack, { render: "main", "data-slot": "pane", ...(run ? null : { ...pane(), overflow: 'hidden' }), flex: 1, minW: 0, minH: 0, children: plans ? null : _jsx(Builder, { host: host, rail: false }) })) })] })] }), drawer.state === 'shut' ? null : (_jsx(Layer, { drawer: drawer, top: bar ? BAR : 0, children: _jsx(Column, { host: host, mode: mode, thread: thread, go: move, onShut: shut, onSeek: seek, onBack: back, drawer: true, places: sidebar, rail: _jsx(Rail, { host: host, mode: mode, at: where, open: true, rows: true, onChat: toChat, onDev: toDev, onStudio: toStudio, onTabs: toTabs, go: move, onOrg: org }), foot: _jsx(Foot, { host: host, onChat: fresh, onOrg: org }) }) })), finding ? _jsx(Palette, { go: move, onClose: found, mode: mode, host: host }) : null, learning ? _jsx(Learning, { open: true, onClose: () => setLearning(false) }) : null] }) }) }) }));
|
|
266
272
|
}
|
package/dist/Learning.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
4
|
+
import { useIam } from '@hanzo/iam/react';
|
|
5
|
+
import { useEffect, useRef, useState } from 'react';
|
|
6
|
+
import { Button, Dialog, DialogContent, DialogTitle, Input, Text, XStack, YStack } from '@hanzo/ui';
|
|
7
|
+
import { say } from './failure.js';
|
|
8
|
+
import { org } from './lib/session.js';
|
|
9
|
+
import { learningAnalyze, learningCompare, learningCreate, learningRead } from './learning.js';
|
|
10
|
+
/** The user's actual evidence and findings. Closed dialogs make no API requests. */
|
|
11
|
+
export function Learning({ open, onClose }) {
|
|
12
|
+
const { t } = useI18n();
|
|
13
|
+
const { accessToken } = useIam();
|
|
14
|
+
const tenant = org();
|
|
15
|
+
const identity = `${tenant}:${accessToken ?? ''}`;
|
|
16
|
+
const [held, setHeld] = useState({ identity: '', answers: {} });
|
|
17
|
+
const [revision, setRevision] = useState(0);
|
|
18
|
+
const [tab, setTab] = useState('research');
|
|
19
|
+
const [form, setForm] = useState(false);
|
|
20
|
+
const [busy, setBusy] = useState(false);
|
|
21
|
+
const [problem, setProblem] = useState(null);
|
|
22
|
+
const draftId = useRef('');
|
|
23
|
+
const [measuring, setMeasuring] = useState(false);
|
|
24
|
+
const [measured, setMeasured] = useState({ identity: '' });
|
|
25
|
+
const [pair, setPair] = useState({ benchmark: '', a: '', b: '' });
|
|
26
|
+
const results = measured.identity === identity ? measured : null;
|
|
27
|
+
const [proposal, setProposal] = useState({ title: '', question: '', baseline: '', target: '', metric: 'accuracy', category: 'all', direction: 'higher', minQuestions: 20, minGain: 1 });
|
|
28
|
+
const answers = held.identity === identity ? held.answers : {};
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
if (!open || !accessToken)
|
|
31
|
+
return;
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
setHeld({ identity, answers: {} });
|
|
34
|
+
for (const part of ['studies', 'reviews', 'experiments', 'benchmarks']) {
|
|
35
|
+
void learningRead(part, controller.signal).then((rows) => { if (!controller.signal.aborted)
|
|
36
|
+
setHeld((old) => ({ identity, answers: { ...old.answers, [part]: { rows } } })); }, (error) => { if (!controller.signal.aborted)
|
|
37
|
+
setHeld((old) => ({ identity, answers: { ...old.answers, [part]: { error } } })); });
|
|
38
|
+
}
|
|
39
|
+
return () => controller.abort();
|
|
40
|
+
}, [open, accessToken, identity, revision]);
|
|
41
|
+
const create = async () => {
|
|
42
|
+
if (busy)
|
|
43
|
+
return;
|
|
44
|
+
setBusy(true);
|
|
45
|
+
setProblem(null);
|
|
46
|
+
try {
|
|
47
|
+
if (!draftId.current)
|
|
48
|
+
draftId.current = `improvement-${crypto.randomUUID()}`;
|
|
49
|
+
await learningCreate(proposal, draftId.current);
|
|
50
|
+
draftId.current = '';
|
|
51
|
+
setForm(false);
|
|
52
|
+
setRevision((n) => n + 1);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
setProblem(error);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
setBusy(false);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
const analyze = async (id) => {
|
|
62
|
+
setMeasuring(true);
|
|
63
|
+
setMeasured({ identity });
|
|
64
|
+
try {
|
|
65
|
+
setMeasured({ identity, analysis: await learningAnalyze(id) });
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
setMeasured({ identity, error });
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
setMeasuring(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const compare = async () => {
|
|
75
|
+
setMeasuring(true);
|
|
76
|
+
setMeasured({ identity });
|
|
77
|
+
try {
|
|
78
|
+
setMeasured({ identity, comparison: await learningCompare(pair.benchmark, pair.a, pair.b) });
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
setMeasured({ identity, error });
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
setMeasuring(false);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const state = (part) => answers[part]?.error
|
|
88
|
+
? _jsx(Text, { role: "alert", color: "$soft", children: say(answers[part]?.error, t('your learning activity'), 'read', t) })
|
|
89
|
+
: !answers[part]?.rows ? _jsx(Text, { color: "$soft", children: t('Loading…') }) : null;
|
|
90
|
+
const field = (key, label) => (_jsxs(YStack, { gap: "$1", children: [_jsx(Text, { fontSize: "$2", children: label }), _jsx(Input, { "aria-label": label, value: proposal[key], onChangeText: (value) => setProposal((old) => ({ ...old, [key]: value })) })] }));
|
|
91
|
+
return _jsx(Dialog, { open: open, onOpenChange: (next) => { if (!next)
|
|
92
|
+
onClose(); }, children: _jsx(DialogContent, { width: "100%", maxW: 900, p: "$5", children: _jsxs(YStack, { gap: "$4", maxH: "80vh", overflow: "scroll", children: [_jsxs(XStack, { items: "center", justify: "space-between", gap: "$2", children: [_jsx(DialogTitle, { children: _jsx(Text, { fontSize: "$5", children: t('Learning') }) }), _jsx(Button, { size: "sm", onPress: onClose, children: t('Close') })] }), _jsx(Text, { color: "$soft", children: t('Research, test ideas, and review improvements backed by your workspace’s evidence.') }), !accessToken ? _jsx(Text, { role: "status", children: t('Sign in to see your learning activity.') }) : _jsxs(_Fragment, { children: [_jsxs(XStack, { gap: "$2", flexWrap: "wrap", role: "tablist", "aria-label": t('Learning'), children: [_jsx(Button, { role: "tab", "aria-selected": tab === 'research', onPress: () => setTab('research'), children: t('Research') }), _jsx(Button, { role: "tab", "aria-selected": tab === 'experiments', onPress: () => setTab('experiments'), children: t('Experiments') }), _jsx(Button, { role: "tab", "aria-selected": tab === 'benchmarks', onPress: () => setTab('benchmarks'), children: t('Benchmarks') }), _jsx(Button, { size: "sm", onPress: () => setRevision((n) => n + 1), children: t('Refresh') })] }), tab === 'research' ? _jsxs(YStack, { gap: "$3", children: [_jsx(Text, { color: "$soft", children: t('A study with a baseline and acceptance criteria reviews new results as they arrive. Findings remain drafts until reviewed.') }), _jsx(Button, { size: "sm", onPress: () => setForm(!form), children: form ? t('Cancel') : t('New improvement study') }), form ? _jsx("form", { onSubmit: (event) => { event.preventDefault(); void create(); }, children: _jsxs(YStack, { gap: "$2", children: [field('title', t('Title')), field('question', t('Research question')), field('baseline', t('Baseline run ID')), field('target', t('App, harness, skill or connector')), field('category', t('Metric category')), field('metric', t('Primary metric')), _jsx(XStack, { gap: "$2", children: _jsx(Button, { type: "button", size: "sm", onPress: () => setProposal((p) => ({ ...p, direction: p.direction === 'higher' ? 'lower' : 'higher' })), children: proposal.direction === 'higher' ? t('Higher is better') : t('Lower is better') }) }), _jsx(Text, { fontSize: "$2", children: t('Minimum completed evaluation size') }), _jsx(Input, { "aria-label": t('Minimum completed evaluation size'), type: "number", value: String(proposal.minQuestions), onChangeText: (v) => setProposal((p) => ({ ...p, minQuestions: Number(v) })) }), _jsx(Text, { fontSize: "$2", children: t('Minimum gain in metric units') }), _jsx(Input, { "aria-label": t('Minimum gain in metric units'), type: "number", value: String(proposal.minGain), onChangeText: (v) => setProposal((p) => ({ ...p, minGain: Number(v) })) }), _jsx(Text, { color: "$soft", children: t('This enables evaluation of recorded results. It does not start a model call or change a live app.') }), problem ? _jsx(Text, { role: "alert", children: say(problem, t('this study'), 'save', t) }) : null, _jsx(Button, { type: "submit", disabled: busy || !proposal.baseline.trim() || !proposal.target.trim() || !proposal.title.trim() || !proposal.question.trim() || !proposal.metric.trim() || !Number.isInteger(proposal.minQuestions) || proposal.minQuestions < 1 || !Number.isFinite(proposal.minGain) || proposal.minGain < 0, children: busy ? t('Saving…') : t('Enable study') })] }) }) : null, state('studies'), answers.studies?.rows?.map((study) => _jsxs(YStack, { gap: "$1", p: "$3", bg: "$panel", rounded: "$3", children: [_jsx(Text, { children: study.title || study.id }), _jsx(Text, { color: "$soft", children: study.question }), _jsx(Text, { fontSize: "$2", children: study.improvement ? t('Automatic review enabled') : t('Evidence only') })] }, `${study.project}:${study.id}`)), answers.studies?.rows?.length === 0 ? _jsx(Text, { color: "$soft", children: t('No studies yet. Create one to review recorded results continuously.') }) : null, _jsx(Text, { fontSize: "$4", children: t('Improvement findings') }), state('reviews'), answers.reviews?.rows?.map((row) => _jsxs(YStack, { gap: "$1", p: "$3", bg: "$panel", rounded: "$3", children: [_jsx(Text, { children: row.action.review?.target }), _jsxs(Text, { children: [row.action.review?.status, " \u00B7 ", row.status] }), _jsx(Text, { color: "$soft", children: row.action.description }), row.action.review?.blocks?.map((block, i) => _jsx(Text, { fontSize: "$2", color: "$soft", children: block }, i))] }, row.id)), answers.reviews?.rows?.length === 0 ? _jsx(Text, { color: "$soft", children: t('Findings appear when a study has baseline and candidate results. No result is being claimed yet.') }) : null] }) : tab === 'experiments' ? _jsxs(YStack, { gap: "$3", children: [_jsx(Text, { color: "$soft", children: t('Controlled experiments assign variants and measure their outcomes. Choosing a winner changes a live rollout and requires an authorized review.') }), state('experiments'), answers.experiments?.rows?.map((row) => _jsxs(YStack, { gap: "$1", p: "$3", bg: "$panel", rounded: "$3", children: [_jsx(Text, { children: row.name || row.id }), _jsxs(Text, { color: "$soft", children: [row.status, row.winner ? ` · ${row.winner}` : ''] }), _jsx(Button, { size: "sm", disabled: measuring, onPress: () => void analyze(row.id), children: t('Analyze outcomes') })] }, row.id)), results?.error ? _jsx(Text, { role: "alert", children: say(results.error, t('this experiment'), 'read', t) }) : null, results?.analysis?.results?.map((row) => _jsxs(YStack, { gap: "$1", children: [_jsx(Text, { children: row.variant }), _jsx(Text, { color: "$soft", children: t('{converted} of {exposed} converted', { converted: row.converted, exposed: row.exposed }) })] }, row.variant)), answers.experiments?.rows?.length === 0 ? _jsx(Text, { color: "$soft", children: t('No controlled experiments yet.') }) : null] }) : _jsxs(YStack, { gap: "$3", children: [_jsx(Text, { color: "$soft", children: t('Benchmarks compare actual measurements. Requesting a benchmark result does not execute an evaluation harness.') }), state('benchmarks'), _jsx(Input, { "aria-label": t('Benchmark ID'), placeholder: t('Benchmark ID'), value: pair.benchmark, onChangeText: (benchmark) => setPair((p) => ({ ...p, benchmark })) }), _jsx(Input, { "aria-label": t('First model ID'), placeholder: t('First model ID'), value: pair.a, onChangeText: (a) => setPair((p) => ({ ...p, a })) }), _jsx(Input, { "aria-label": t('Second model ID'), placeholder: t('Second model ID'), value: pair.b, onChangeText: (b) => setPair((p) => ({ ...p, b })) }), _jsx(Button, { disabled: measuring || !pair.benchmark.trim() || !pair.a.trim() || !pair.b.trim(), onPress: () => void compare(), children: t('Compare measurements') }), results?.error ? _jsx(Text, { role: "alert", children: say(results.error, t('these benchmark results'), 'read', t) }) : null, results?.comparison ? _jsx(YStack, { gap: "$1", children: results.comparison.n_common === 0 ? _jsx(Text, { children: t('No shared measured items. Record both evaluations before comparing.') }) : _jsxs(_Fragment, { children: [_jsx(Text, { children: t('{count} shared measured items', { count: results.comparison.n_common }) }), _jsxs(Text, { children: [results.comparison.a, ": ", results.comparison.a_correct, " \u00B7 ", results.comparison.b, ": ", results.comparison.b_correct] })] }) }) : null, answers.benchmarks?.rows?.map((row) => _jsxs(YStack, { gap: "$1", p: "$3", bg: "$panel", rounded: "$3", children: [_jsx(Text, { children: row.name || row.id }), _jsx(Text, { color: "$soft", children: row.direction === 'lower' ? t('Lower is better') : t('Higher is better') }), _jsx(Button, { size: "sm", onPress: () => setPair((p) => ({ ...p, benchmark: row.id })), children: t('Use this benchmark') })] }, row.id))] })] })] }) }) });
|
|
93
|
+
}
|
package/dist/Me.js
CHANGED
|
@@ -32,11 +32,11 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
32
32
|
// the plan from billing and the limits. An organization is created by the same
|
|
33
33
|
// `POST /v1/account/orgs` the no-organization gate uses (Orgs.tsx); the next load
|
|
34
34
|
// mints a token that carries it.
|
|
35
|
-
import { phrase } from '@hanzo/i18n';
|
|
35
|
+
import { phrase, locales } from '@hanzo/i18n';
|
|
36
36
|
import { useI18n } from '@hanzo/i18n/react';
|
|
37
37
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
38
38
|
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
39
|
-
import { ArrowUpRight, BookOpen, Building2, Check, ChevronsUpDown, CircleHelp, CircleUserRound, Download, FileText, FlaskConical, Info, Keyboard, LifeBuoy, LogOut, MessagesSquare, Palette, Plus, ScrollText, Settings, ShieldCheck, Sparkles, Users, X, } from 'lucide-react';
|
|
39
|
+
import { ArrowUpRight, BookOpen, Building2, Check, ChevronsUpDown, CircleHelp, CircleUserRound, Download, FileText, FlaskConical, Info, Keyboard, Languages, LifeBuoy, LogOut, MessagesSquare, Palette, Plus, ScrollText, Settings, ShieldCheck, Sparkles, Users, X, } from 'lucide-react';
|
|
40
40
|
import { label, Meter, said as spoken, useStanding } from '@hanzo/build';
|
|
41
41
|
import { useOrganizations } from '@hanzo/iam/react';
|
|
42
42
|
import { Button, Dialog, DialogContent, DialogTitle, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Input, Kbd, } from '@hanzo/ui';
|
|
@@ -130,7 +130,7 @@ export function Face({ host, size = 28 }) {
|
|
|
130
130
|
}
|
|
131
131
|
/** A row's face: its icon in a 16px well, its words, and what trails them. */
|
|
132
132
|
function Line({ icon, children, sub, trail }) {
|
|
133
|
-
return (_jsxs(_Fragment, { children: [_jsx(XStack, { width: 16, height: 16, items: "center", justify: "center", shrink: 0, children: icon }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, children: children }), sub ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: sub })) : null] }), trail] }));
|
|
133
|
+
return (_jsxs(_Fragment, { children: [icon !== undefined ? (_jsx(XStack, { width: 16, height: 16, items: "center", justify: "center", shrink: 0, children: icon })) : null, _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", color: "$ink", numberOfLines: 1, children: children }), sub ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: sub })) : null] }), trail] }));
|
|
134
134
|
}
|
|
135
135
|
/** An organization's mark in the list: its initials on a rounded square. */
|
|
136
136
|
const Mark = ({ org }) => (_jsx(XStack, { width: 16, height: 16, rounded: "$2", items: "center", justify: "center", bg: "$raised", shrink: 0, children: org.personal ? (_jsx(SizableText, { size: "$1", fontWeight: "600", color: "$ink", children: initials(org.display).charAt(0) })) : (_jsx(Building2, { size: 12, "aria-hidden": true })) }));
|
|
@@ -143,10 +143,11 @@ export const upgrade = (family) => family === 'Enterprise' ? phrase('Enterprise
|
|
|
143
143
|
/** The keys a row prints, in this platform's spelling. */
|
|
144
144
|
const mac = () => typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
|
|
145
145
|
export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
146
|
-
const { t, dir } = useI18n();
|
|
146
|
+
const { t, dir, locale, setLocale } = useI18n();
|
|
147
147
|
const rtl = dir === 'rtl';
|
|
148
148
|
const [open, setOpen] = useState(false);
|
|
149
149
|
const [account, setAccount] = useState(false);
|
|
150
|
+
const [lang, setLang] = useState(false);
|
|
150
151
|
const [help, setHelp] = useState(false);
|
|
151
152
|
const [creating, setCreating] = useState(false);
|
|
152
153
|
const [keys, setKeys] = useState(false);
|
|
@@ -206,6 +207,7 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
|
206
207
|
if (next)
|
|
207
208
|
return;
|
|
208
209
|
setAccount(false);
|
|
210
|
+
setLang(false);
|
|
209
211
|
setHelp(false);
|
|
210
212
|
},
|
|
211
213
|
// Toward the content from the rail, and aligned as the row's own edge is: mirrored right to left.
|
|
@@ -218,7 +220,9 @@ export function Me({ host, narrow = false, above = false, onOrg, }) {
|
|
|
218
220
|
$touchable: narrow ? { width: 44, minH: 44 } : undefined, px: narrow ? 0 : '$2', gap: "$2.5", rounded: narrow ? 999 : '$5', items: "center", justify: narrow ? 'center' : 'flex-start', hoverStyle: { bg: '$hover' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, cursor: "pointer", children: [_jsx(Face, { host: host, size: narrow ? 26 : 32 }), narrow ? null : (_jsxs(_Fragment, { children: [_jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$3", fontWeight: "600", color: "$ink", numberOfLines: 1, text: "start", children: who }), working ? (_jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, text: "start", children: working })) : null] }), isFree ? (_jsx(Button, { size: "xs", variant: "outline", "data-slot": "me-upgrade", px: "$2", py: "$0.5", rounded: "$3", onPress: (e) => {
|
|
219
221
|
e?.stopPropagation?.();
|
|
220
222
|
getPro();
|
|
221
|
-
}, cursor: "pointer", children: t('Upgrade') })) : null, _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsxs(DropdownMenuContent, { minW: 260, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": t('Account'), onCloseAutoFocus: back, children: [_jsxs(DropdownMenuSub, { open: account, onOpenChange: setAccount, children: [_jsxs(DropdownMenuSubTrigger, { minH: 48, gap: "$2.5", children: [_jsx(Face, { host: host, size: 28 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", numberOfLines: 1, children: who }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: host.person.email || working })] })] }), account ? (_jsxs(DropdownMenuSubContent, { minW: 260, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: [_jsx(Meter, { read: read, onPlans: () => leave('here'), onBilling: () => (setOpen(false), showSettings('billing')) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { children: t('Organizations') }), all.map((o) => (_jsx(DropdownMenuItem, { onSelect: () => o.name !== current && onOrg(o.name), children: _jsx(Line, { icon: _jsx(Mark, { org: o }), sub: [o.personal ? t('Personal') : said(o.role, t), planFor(o) || (isFree && o.personal ? t('Free') : '')].filter(Boolean).join(' - '), trail: o.name === current ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: o.display }) }, o.name))), _jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Plus, { size: 16, "aria-hidden": true }), children: t('Create organization') }) }), _jsx(DropdownMenuItem, { onSelect: () => out(site(settings('organization'))), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), children: t('Organization settings') }) }), where().team ? null : here && !here.personal ? (_jsx(DropdownMenuItem, { onSelect: () => out(`${ENTRY}&org=${encodeURIComponent(here.name)}`), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), trail: _jsx(ArrowUpRight, { size: 14, "aria-hidden": true, opacity: 0.6 }), children: t('Open in Hanzo Team') }) })) : (_jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), children: t('Create a team') }) })), superAdmin() ? (_jsx(DropdownMenuItem, { onSelect: choose, children: _jsx(Line, { icon: _jsx(Building2, { size: 16, "aria-hidden": true }), children: t('All organizations') }) })) : null] })) : null] }), team ? (_jsx(DropdownMenuItem, { "data-slot": "me-team", onSelect: () => team.name !== current && onOrg(team.name), children: _jsx(Line, { icon: _jsx(Mark, { org: team }), trail: team.name === current ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: t('{org} · {plan}', { org: team.display, plan: teamPlan || t('No plan') }) }) })) : null, personal ? (_jsx(DropdownMenuItem, { "data-slot": "me-personal", onSelect: () => personal.name !== current && onOrg(personal.name), actionsVisibility: "always", actions: ownPlan ? undefined : (_jsx(Button, { size: "xs", variant: "outline", onPress: getPro, children: t('Get Pro') })), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), trail: personal.name === current ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: t('Personal · {plan}', { plan: ownPlan || t('No plan') }) }) })) : null, _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: () => leave('here'), children: _jsx(Line, { icon: _jsx(Sparkles, { size: 16, "aria-hidden": true }), children: t(plans) }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('general'), children: _jsx(Line, { icon: _jsx(Palette, { size: 16, "aria-hidden": true }), children: t('Personalization') }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('account'), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), children: t('Profile') }) }), _jsx(DropdownMenuItem, { "aria-keyshortcuts": "Control+, Meta+,", onSelect: () => showSettings('general'), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), trail: _jsx(DropdownMenuShortcut, { "aria-hidden": true, children: _jsx("bdi", { dir: "ltr", children: apple ? '⌘,' : 'Ctrl+,' }) }), children: t('Settings') }) }), _jsx(DropdownMenuItem, { onSelect: () => window.location.assign(site('/beta/status')), children: _jsx(Line, { icon: _jsx(FlaskConical, { size: 16, "aria-hidden": true }), children: t('Beta') }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuSub, { open:
|
|
223
|
+
}, cursor: "pointer", children: t('Upgrade') })) : null, _jsx(ChevronsUpDown, { size: 14, "aria-hidden": true, opacity: 0.6 })] }))] }) }), _jsxs(DropdownMenuContent, { minW: 260, maxW: "calc(100vw - 16px)", "aria-labelledby": undefined, "aria-label": t('Account'), onCloseAutoFocus: back, children: [_jsxs(DropdownMenuSub, { open: account, onOpenChange: setAccount, children: [_jsxs(DropdownMenuSubTrigger, { minH: 48, gap: "$2.5", children: [_jsx(Face, { host: host, size: 28 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", numberOfLines: 1, children: who }), _jsx(SizableText, { size: "$1", color: "$soft", numberOfLines: 1, children: host.person.email || working })] })] }), account ? (_jsxs(DropdownMenuSubContent, { minW: 260, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: [_jsx(Meter, { read: read, onPlans: () => leave('here'), onBilling: () => (setOpen(false), showSettings('billing')) }), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { children: t('Organizations') }), all.map((o) => (_jsx(DropdownMenuItem, { onSelect: () => o.name !== current && onOrg(o.name), children: _jsx(Line, { icon: _jsx(Mark, { org: o }), sub: [o.personal ? t('Personal') : said(o.role, t), planFor(o) || (isFree && o.personal ? t('Free') : '')].filter(Boolean).join(' - '), trail: o.name === current ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: o.display }) }, o.name))), _jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Plus, { size: 16, "aria-hidden": true }), children: t('Create organization') }) }), _jsx(DropdownMenuItem, { onSelect: () => out(site(settings('organization'))), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), children: t('Organization settings') }) }), where().team ? null : here && !here.personal ? (_jsx(DropdownMenuItem, { onSelect: () => out(`${ENTRY}&org=${encodeURIComponent(here.name)}`), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), trail: _jsx(ArrowUpRight, { size: 14, "aria-hidden": true, opacity: 0.6 }), children: t('Open in Hanzo Team') }) })) : (_jsx(DropdownMenuItem, { onSelect: () => setCreating(true), children: _jsx(Line, { icon: _jsx(Users, { size: 16, "aria-hidden": true }), children: t('Create a team') }) })), superAdmin() ? (_jsx(DropdownMenuItem, { onSelect: choose, children: _jsx(Line, { icon: _jsx(Building2, { size: 16, "aria-hidden": true }), children: t('All organizations') }) })) : null] })) : null] }), team ? (_jsx(DropdownMenuItem, { "data-slot": "me-team", onSelect: () => team.name !== current && onOrg(team.name), children: _jsx(Line, { icon: _jsx(Mark, { org: team }), trail: team.name === current ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: t('{org} · {plan}', { org: team.display, plan: teamPlan || t('No plan') }) }) })) : null, personal ? (_jsx(DropdownMenuItem, { "data-slot": "me-personal", onSelect: () => personal.name !== current && onOrg(personal.name), actionsVisibility: "always", actions: ownPlan ? undefined : (_jsx(Button, { size: "xs", variant: "outline", onPress: getPro, children: t('Get Pro') })), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), trail: personal.name === current ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: t('Personal · {plan}', { plan: ownPlan || t('No plan') }) }) })) : null, _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: () => leave('here'), children: _jsx(Line, { icon: _jsx(Sparkles, { size: 16, "aria-hidden": true }), children: t(plans) }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('general'), children: _jsx(Line, { icon: _jsx(Palette, { size: 16, "aria-hidden": true }), children: t('Personalization') }) }), _jsx(DropdownMenuItem, { onSelect: () => showSettings('account'), children: _jsx(Line, { icon: _jsx(CircleUserRound, { size: 16, "aria-hidden": true }), children: t('Profile') }) }), _jsx(DropdownMenuItem, { "aria-keyshortcuts": "Control+, Meta+,", onSelect: () => showSettings('general'), children: _jsx(Line, { icon: _jsx(Settings, { size: 16, "aria-hidden": true }), trail: _jsx(DropdownMenuShortcut, { "aria-hidden": true, children: _jsx("bdi", { dir: "ltr", children: apple ? '⌘,' : 'Ctrl+,' }) }), children: t('Settings') }) }), _jsx(DropdownMenuItem, { onSelect: () => window.location.assign(site('/beta/status')), children: _jsx(Line, { icon: _jsx(FlaskConical, { size: 16, "aria-hidden": true }), children: t('Beta') }) }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuSub, { open: lang, onOpenChange: setLang, children: [_jsx(DropdownMenuSubTrigger, { children: _jsx(Line, { icon: _jsx(Languages, { size: 16, "aria-hidden": true }), children: t('Language') }) }), lang ? (_jsx(DropdownMenuSubContent, { minW: 220, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: locales
|
|
224
|
+
.filter((l) => !l.review || l.code === locale)
|
|
225
|
+
.map((l) => (_jsx(DropdownMenuItem, { onSelect: () => void setLocale(l.code), children: _jsx(Line, { sub: l.englishName !== l.name ? l.englishName : undefined, trail: l.code === locale ? _jsx(Check, { size: 16, "aria-label": t('Current') }) : null, children: l.name }) }, l.code))) })) : null] }), _jsxs(DropdownMenuSub, { open: help, onOpenChange: setHelp, children: [_jsx(DropdownMenuSubTrigger, { children: _jsx(Line, { icon: _jsx(LifeBuoy, { size: 16, "aria-hidden": true }), children: t('Help') }) }), help ? (_jsxs(DropdownMenuSubContent, { minW: 240, maxW: "calc(100vw - 16px)", pointerEvents: "auto", children: [pages().map((p) => (_jsx(DropdownMenuItem, { onSelect: () => out(p.href), children: _jsx(Line, { icon: ICONS[p.key], trail: p.key === 'help' ? _jsx(ArrowUpRight, { size: 14, "aria-hidden": true, opacity: 0.6 }) : null, children: t(p.label) }) }, p.key))), _jsx(DropdownMenuItem, { "aria-keyshortcuts": "Control+/ Meta+/", onSelect: () => setKeys(true), children: _jsx(Line, { icon: _jsx(Keyboard, { size: 16, "aria-hidden": true }), trail: _jsx(DropdownMenuShortcut, { "aria-hidden": true, children: _jsx("bdi", { dir: "ltr", children: apple ? '⌘/' : 'Ctrl+/' }) }), children: t('Keyboard shortcuts') }) }), _jsx(DropdownMenuSeparator, {}), legal().map((p) => (_jsx(DropdownMenuItem, { onSelect: () => out(p.href), children: _jsx(Line, { icon: ICONS[p.key], children: t(p.label) }) }, p.key)))] })) : null] }), host.signOut ? (_jsx(DropdownMenuItem, { onSelect: () => host.signOut?.(), children: _jsx(Line, { icon: _jsx(LogOut, { size: 16, "aria-hidden": true, "data-i18n-mirror": true }), children: t('Log out') }) })) : null] })] }), _jsx(Create, { open: creating, onOpenChange: setCreating }), _jsx(Shortcuts, { open: keys, onOpenChange: setKeys, apple: apple })] }));
|
|
222
226
|
}
|
|
223
227
|
/** The app's keys (help.ts `KEYS`), one row each, spelled for this platform. */
|
|
224
228
|
function Shortcuts({ open, onOpenChange, apple }) {
|
package/dist/Setup.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
4
4
|
// Runs INSIDE the app as a compact card in the sidebar column and as an interactive
|
|
5
5
|
// popup modal dialog. Never a full-screen redirect away from the user's workspace.
|
|
6
6
|
//
|
|
7
|
-
// 1. GitHub: Sync repositories, PRs, and team
|
|
7
|
+
// 1. GitHub: Sync repositories, PRs, and team repository context
|
|
8
8
|
// 2. Slack: Route notifications, agent conversations, and bot alerts
|
|
9
9
|
// 3. Wallet: Web3 multi-chain wallet (MetaMask/EVM/Base/Solana) for credits & payouts
|
|
10
10
|
import { useEffect, useState } from 'react';
|
|
@@ -203,5 +203,5 @@ export function Setup() {
|
|
|
203
203
|
}
|
|
204
204
|
};
|
|
205
205
|
const truncate = (s) => (s.length > 12 ? `${s.slice(0, 6)}…${s.slice(-4)}` : s);
|
|
206
|
-
return (_jsxs(_Fragment, { children: [_jsxs(YStack, { "data-slot": "setup", role: "region", "aria-label": t('Onboarding (1-2-3)'), gap: "$2.5", p: "$3", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$raised", width: "100%", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Sparkles, { size: 15, color: "var(--primary)", "aria-hidden": true }), _jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", children: allDone ? t('Onboarding complete') : t('Quick setup ({n}/3)', { n: totalDone }) })] }), _jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Box, { render: "button", "aria-label": t('Expand onboarding popup'), onClick: () => setModalOpen(true), p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Maximize2, { size: 13, "aria-hidden": true, color: "$soft" }) }), _jsx(Box, { render: "button", "aria-label": t('Dismiss'), onClick: () => setLater(true), p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(X, { size: 13, "aria-hidden": true, color: "$soft" }) })] })] }), _jsxs(XStack, { gap: "$1", width: "100%", children: [_jsx(Box, { flex: 1, height: 4, rounded: 2, bg: githubDone ? GOOD : step === 1 ? '$ink' : '$edge' }), _jsx(Box, { flex: 1, height: 4, rounded: 2, bg: slackDone ? GOOD : step === 2 ? '$ink' : '$edge' }), _jsx(Box, { flex: 1, height: 4, rounded: 2, bg: walletDone ? GOOD : step === 3 ? '$ink' : '$edge' })] }), allDone ? (_jsxs(YStack, { gap: "$2", pt: "$1", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Check, { size: 16, color: GOOD, "aria-hidden": true }), _jsx(Text, { fontSize: "$2", color: "$ink", fontWeight: "500", children: t('GitHub, Slack & Wallet connected') })] }), _jsx(Box, { render: "button", onClick: () => setLater(true), p: "$1.5", rounded: "$3", bg: "$edge", hoverStyle: { bg: '$hover' }, items: "center", justify: "center", cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "500", children: t('Done') }) })] })) : step === 1 ? (_jsxs(YStack, { gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(ProviderMark, { org: "github", size: 18 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: t('1. Connect GitHub') })] }), githubDone ? (_jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Check, { size: 12, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : null] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Sync repositories, PRs, and team codebase context.') }), _jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", children: [_jsx(Box, { render: "button", onClick: handleConnectGithub, "aria-disabled": busy === 'github', px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", opacity: busy === 'github' ? 0.6 : 1, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'github' ? t('Connecting…') : githubDone ? t('Reconnect') : t('Connect GitHub') }) }), _jsx(Box, { render: "button", onClick: () => setStep(2), px: "$2", py: "$1", rounded: "$3", bg: "transparent", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Skip') }) })] })] })) : step === 2 ? (_jsxs(YStack, { gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(SlackMark, { size: 18 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: t('2. Connect Slack') })] }), slackDone ? (_jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Check, { size: 12, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : null] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Route agent notifications and chat in team channels.') }), _jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", children: [_jsx(Box, { render: "button", onClick: handleConnectSlack, "aria-disabled": busy === 'slack', px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", opacity: busy === 'slack' ? 0.6 : 1, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'slack' ? t('Connecting…') : slackDone ? t('Reconnect') : t('Connect Slack') }) }), _jsx(Box, { render: "button", onClick: () => setStep(3), px: "$2", py: "$1", rounded: "$3", bg: "transparent", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Skip') }) })] })] })) : (_jsxs(YStack, { gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Wallet, { size: 18, color: "var(--primary)" }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: t('3. Connect Wallet') })] }), walletDone ? (_jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Check, { size: 12, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: walletAddr ? truncate(walletAddr) : t('Connected') })] })) : null] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Link Web3 wallet for compute credits and rewards.') }), showWalletInput ? (_jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", children: [_jsx(Input, { value: manualWallet, onChangeText: setManualWallet, placeholder: "0x\u2026 or Solana/Base", height: 30, flex: 1, fontSize: "$1" }), _jsx(Box, { render: "button", onClick: () => void saveWalletAddress(manualWallet), px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: t('Save') }) })] })) : (_jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", flexWrap: "wrap", children: [_jsx(Box, { render: "button", onClick: handleConnectWalletWeb3, "aria-disabled": busy === 'wallet', px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'wallet' ? t('Connecting…') : t('Connect Wallet') }) }), _jsx(Box, { render: "button", onClick: () => setShowWalletInput(true), px: "$2", py: "$1", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", color: "$ink", children: t('Enter address') }) })] }))] })), errorMsg ? (_jsx(Text, { fontSize: "$1", color: "$red10", children: errorMsg })) : null] }), _jsx(Dialog, { open: modalOpen, onOpenChange: setModalOpen, children: _jsxs(DialogContent, { maxW: 520, rounded: "$6", gap: "$4", p: "$5", children: [_jsx(DialogTitle, { children: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Sparkles, { size: 20, color: "var(--primary)", "aria-hidden": true }), _jsx(Text, { fontSize: "$5", fontWeight: "600", color: "$ink", children: t('Set up {brand} in 3 steps', { brand: brand() }) })] }) }), _jsx(Text, { fontSize: "$2", color: "$soft", children: t('Connect your essential developer tools and Web3 wallet directly inside {brand()}.', { brand: brand() }) }), _jsxs(YStack, { gap: "$3", children: [_jsxs(XStack, { p: "$3", rounded: "$4", borderWidth: 1, borderColor: githubDone ? GOOD : '$borderColor', bg: "$raised", items: "center", justify: "space-between", gap: "$3", children: [_jsxs(XStack, { items: "center", gap: "$3", flex: 1, children: [_jsx(ProviderMark, { org: "github", size: 24 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: t('1. GitHub') }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Sync repositories, PRs, and code context') })] })] }), githubDone ? (_jsxs(XStack, { items: "center", gap: "$1", px: "$2", py: "$1", rounded: "$2", bg: "$hover", children: [_jsx(Check, { size: 14, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : (_jsx(Box, { render: "button", onClick: handleConnectGithub, px: "$3", py: "$1.5", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'github' ? t('Connecting…') : t('Connect') }) }))] }), _jsxs(XStack, { p: "$3", rounded: "$4", borderWidth: 1, borderColor: slackDone ? GOOD : '$borderColor', bg: "$raised", items: "center", justify: "space-between", gap: "$3", children: [_jsxs(XStack, { items: "center", gap: "$3", flex: 1, children: [_jsx(SlackMark, { size: 24 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: t('2. Slack') }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Notifications and agent bot chat in channels') })] })] }), slackDone ? (_jsxs(XStack, { items: "center", gap: "$1", px: "$2", py: "$1", rounded: "$2", bg: "$hover", children: [_jsx(Check, { size: 14, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : (_jsx(Box, { render: "button", onClick: handleConnectSlack, px: "$3", py: "$1.5", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'slack' ? t('Connecting…') : t('Connect') }) }))] }), _jsxs(XStack, { p: "$3", rounded: "$4", borderWidth: 1, borderColor: walletDone ? GOOD : '$borderColor', bg: "$raised", items: "center", justify: "space-between", gap: "$3", children: [_jsxs(XStack, { items: "center", gap: "$3", flex: 1, children: [_jsx(Wallet, { size: 24, color: "var(--primary)" }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: t('3. Web3 Wallet') }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('On-chain identity, compute credits, and rewards') })] })] }), walletDone ? (_jsxs(XStack, { items: "center", gap: "$1", px: "$2", py: "$1", rounded: "$2", bg: "$hover", children: [_jsx(Check, { size: 14, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: walletAddr ? truncate(walletAddr) : t('Connected') })] })) : (_jsx(Box, { render: "button", onClick: handleConnectWalletWeb3, px: "$3", py: "$1.5", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'wallet' ? t('Connecting…') : t('Connect') }) }))] })] }), _jsx(XStack, { justify: "flex-end", pt: "$2", children: _jsx(Box, { render: "button", onClick: () => setModalOpen(false), px: "$4", py: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "500", color: "$ink", children: t('Close') }) }) })] }) })] }));
|
|
206
|
+
return (_jsxs(_Fragment, { children: [_jsxs(YStack, { "data-slot": "setup", role: "region", "aria-label": t('Onboarding (1-2-3)'), gap: "$2.5", p: "$3", rounded: "$4", borderWidth: 1, borderColor: "$borderColor", bg: "$raised", width: "100%", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$1.5", children: [_jsx(Sparkles, { size: 15, color: "var(--primary)", "aria-hidden": true }), _jsx(SizableText, { size: "$2", fontWeight: "600", color: "$ink", children: allDone ? t('Onboarding complete') : t('Quick setup ({n}/3)', { n: totalDone }) })] }), _jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Box, { render: "button", "aria-label": t('Expand onboarding popup'), onClick: () => setModalOpen(true), p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Maximize2, { size: 13, "aria-hidden": true, color: "$soft" }) }), _jsx(Box, { render: "button", "aria-label": t('Dismiss'), onClick: () => setLater(true), p: "$1", rounded: 999, hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(X, { size: 13, "aria-hidden": true, color: "$soft" }) })] })] }), _jsxs(XStack, { gap: "$1", width: "100%", children: [_jsx(Box, { flex: 1, height: 4, rounded: 2, bg: githubDone ? GOOD : step === 1 ? '$ink' : '$edge' }), _jsx(Box, { flex: 1, height: 4, rounded: 2, bg: slackDone ? GOOD : step === 2 ? '$ink' : '$edge' }), _jsx(Box, { flex: 1, height: 4, rounded: 2, bg: walletDone ? GOOD : step === 3 ? '$ink' : '$edge' })] }), allDone ? (_jsxs(YStack, { gap: "$2", pt: "$1", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Check, { size: 16, color: GOOD, "aria-hidden": true }), _jsx(Text, { fontSize: "$2", color: "$ink", fontWeight: "500", children: t('GitHub, Slack & Wallet connected') })] }), _jsx(Box, { render: "button", onClick: () => setLater(true), p: "$1.5", rounded: "$3", bg: "$edge", hoverStyle: { bg: '$hover' }, items: "center", justify: "center", cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "500", children: t('Done') }) })] })) : step === 1 ? (_jsxs(YStack, { gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(ProviderMark, { org: "github", size: 18 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: t('1. Connect GitHub') })] }), githubDone ? (_jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Check, { size: 12, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : null] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Sync repositories, PRs, and team repository context.') }), _jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", children: [_jsx(Box, { render: "button", onClick: handleConnectGithub, "aria-disabled": busy === 'github', px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", opacity: busy === 'github' ? 0.6 : 1, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'github' ? t('Connecting…') : githubDone ? t('Reconnect') : t('Connect GitHub') }) }), _jsx(Box, { render: "button", onClick: () => setStep(2), px: "$2", py: "$1", rounded: "$3", bg: "transparent", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Skip') }) })] })] })) : step === 2 ? (_jsxs(YStack, { gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(SlackMark, { size: 18 }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: t('2. Connect Slack') })] }), slackDone ? (_jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Check, { size: 12, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : null] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Route agent notifications and chat in team channels.') }), _jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", children: [_jsx(Box, { render: "button", onClick: handleConnectSlack, "aria-disabled": busy === 'slack', px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", opacity: busy === 'slack' ? 0.6 : 1, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'slack' ? t('Connecting…') : slackDone ? t('Reconnect') : t('Connect Slack') }) }), _jsx(Box, { render: "button", onClick: () => setStep(3), px: "$2", py: "$1", rounded: "$3", bg: "transparent", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Skip') }) })] })] })) : (_jsxs(YStack, { gap: "$2", children: [_jsxs(XStack, { items: "center", justify: "space-between", children: [_jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Wallet, { size: 18, color: "var(--primary)" }), _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "$ink", children: t('3. Connect Wallet') })] }), walletDone ? (_jsxs(XStack, { items: "center", gap: "$1", children: [_jsx(Check, { size: 12, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: walletAddr ? truncate(walletAddr) : t('Connected') })] })) : null] }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Link Web3 wallet for compute credits and rewards.') }), showWalletInput ? (_jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", children: [_jsx(Input, { value: manualWallet, onChangeText: setManualWallet, placeholder: "0x\u2026 or Solana/Base", height: 30, flex: 1, fontSize: "$1" }), _jsx(Box, { render: "button", onClick: () => void saveWalletAddress(manualWallet), px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: t('Save') }) })] })) : (_jsxs(XStack, { gap: "$1.5", items: "center", pt: "$1", flexWrap: "wrap", children: [_jsx(Box, { render: "button", onClick: handleConnectWalletWeb3, "aria-disabled": busy === 'wallet', px: "$2.5", py: "$1", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'wallet' ? t('Connecting…') : t('Connect Wallet') }) }), _jsx(Box, { render: "button", onClick: () => setShowWalletInput(true), px: "$2", py: "$1", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$1", color: "$ink", children: t('Enter address') }) })] }))] })), errorMsg ? (_jsx(Text, { fontSize: "$1", color: "$red10", children: errorMsg })) : null] }), _jsx(Dialog, { open: modalOpen, onOpenChange: setModalOpen, children: _jsxs(DialogContent, { maxW: 520, rounded: "$6", gap: "$4", p: "$5", children: [_jsx(DialogTitle, { children: _jsxs(XStack, { items: "center", gap: "$2", children: [_jsx(Sparkles, { size: 20, color: "var(--primary)", "aria-hidden": true }), _jsx(Text, { fontSize: "$5", fontWeight: "600", color: "$ink", children: t('Set up {brand} in 3 steps', { brand: brand() }) })] }) }), _jsx(Text, { fontSize: "$2", color: "$soft", children: t('Connect your essential developer tools and Web3 wallet directly inside {brand()}.', { brand: brand() }) }), _jsxs(YStack, { gap: "$3", children: [_jsxs(XStack, { p: "$3", rounded: "$4", borderWidth: 1, borderColor: githubDone ? GOOD : '$borderColor', bg: "$raised", items: "center", justify: "space-between", gap: "$3", children: [_jsxs(XStack, { items: "center", gap: "$3", flex: 1, children: [_jsx(ProviderMark, { org: "github", size: 24 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: t('1. GitHub') }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Sync repositories, PRs, and code context') })] })] }), githubDone ? (_jsxs(XStack, { items: "center", gap: "$1", px: "$2", py: "$1", rounded: "$2", bg: "$hover", children: [_jsx(Check, { size: 14, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : (_jsx(Box, { render: "button", onClick: handleConnectGithub, px: "$3", py: "$1.5", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'github' ? t('Connecting…') : t('Connect') }) }))] }), _jsxs(XStack, { p: "$3", rounded: "$4", borderWidth: 1, borderColor: slackDone ? GOOD : '$borderColor', bg: "$raised", items: "center", justify: "space-between", gap: "$3", children: [_jsxs(XStack, { items: "center", gap: "$3", flex: 1, children: [_jsx(SlackMark, { size: 24 }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: t('2. Slack') }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('Notifications and agent bot chat in channels') })] })] }), slackDone ? (_jsxs(XStack, { items: "center", gap: "$1", px: "$2", py: "$1", rounded: "$2", bg: "$hover", children: [_jsx(Check, { size: 14, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: t('Connected') })] })) : (_jsx(Box, { render: "button", onClick: handleConnectSlack, px: "$3", py: "$1.5", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'slack' ? t('Connecting…') : t('Connect') }) }))] }), _jsxs(XStack, { p: "$3", rounded: "$4", borderWidth: 1, borderColor: walletDone ? GOOD : '$borderColor', bg: "$raised", items: "center", justify: "space-between", gap: "$3", children: [_jsxs(XStack, { items: "center", gap: "$3", flex: 1, children: [_jsx(Wallet, { size: 24, color: "var(--primary)" }), _jsxs(YStack, { flex: 1, minW: 0, children: [_jsx(Text, { fontSize: "$3", fontWeight: "600", color: "$ink", children: t('3. Web3 Wallet') }), _jsx(Text, { fontSize: "$1", color: "$soft", children: t('On-chain identity, compute credits, and rewards') })] })] }), walletDone ? (_jsxs(XStack, { items: "center", gap: "$1", px: "$2", py: "$1", rounded: "$2", bg: "$hover", children: [_jsx(Check, { size: 14, color: GOOD }), _jsx(Text, { fontSize: "$1", color: GOOD, fontWeight: "600", children: walletAddr ? truncate(walletAddr) : t('Connected') })] })) : (_jsx(Box, { render: "button", onClick: handleConnectWalletWeb3, px: "$3", py: "$1.5", rounded: "$3", bg: "var(--accent)", hoverStyle: { bg: 'var(--accent-hover)' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "600", color: "var(--accent-foreground)", children: busy === 'wallet' ? t('Connecting…') : t('Connect') }) }))] })] }), _jsx(XStack, { justify: "flex-end", pt: "$2", children: _jsx(Box, { render: "button", onClick: () => setModalOpen(false), px: "$4", py: "$2", rounded: "$3", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, cursor: "pointer", children: _jsx(Text, { fontSize: "$2", fontWeight: "500", color: "$ink", children: t('Close') }) }) })] }) })] }));
|
|
207
207
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface LearningStudy {
|
|
2
|
+
id: string;
|
|
3
|
+
project: string;
|
|
4
|
+
title: string;
|
|
5
|
+
question: string;
|
|
6
|
+
improvement?: {
|
|
7
|
+
baseline: string;
|
|
8
|
+
target: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export interface LearningReview {
|
|
12
|
+
id: string;
|
|
13
|
+
status: string;
|
|
14
|
+
action: {
|
|
15
|
+
rule?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
review?: {
|
|
18
|
+
target: string;
|
|
19
|
+
candidate: string;
|
|
20
|
+
baseline: string;
|
|
21
|
+
status: string;
|
|
22
|
+
blocks: string[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface LearningRows {
|
|
27
|
+
studies: LearningStudy[];
|
|
28
|
+
reviews: LearningReview[];
|
|
29
|
+
experiments: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
status: string;
|
|
33
|
+
winner?: string;
|
|
34
|
+
}[];
|
|
35
|
+
benchmarks: {
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
direction: string;
|
|
39
|
+
}[];
|
|
40
|
+
}
|
|
41
|
+
export type LearningPart = keyof LearningRows;
|
|
42
|
+
export declare const LEARNING_PATHS: Record<LearningPart, string>;
|
|
43
|
+
/** Every read uses the same IAM/org headers as the rest of the workspace. */
|
|
44
|
+
export declare function learningRead<K extends LearningPart>(part: K, signal?: AbortSignal): Promise<LearningRows[K]>;
|
|
45
|
+
export interface LearningProposal {
|
|
46
|
+
title: string;
|
|
47
|
+
question: string;
|
|
48
|
+
baseline: string;
|
|
49
|
+
target: string;
|
|
50
|
+
metric: string;
|
|
51
|
+
category: string;
|
|
52
|
+
direction: 'higher' | 'lower';
|
|
53
|
+
minQuestions: number;
|
|
54
|
+
minGain: number;
|
|
55
|
+
}
|
|
56
|
+
/** Creating a study arms deterministic evaluation; it starts no model or rollout. */
|
|
57
|
+
export declare function learningCreate(proposal: LearningProposal, id: string): Promise<void>;
|
|
58
|
+
export interface LearningAnalysis {
|
|
59
|
+
winner: string;
|
|
60
|
+
results: {
|
|
61
|
+
variant: string;
|
|
62
|
+
exposed: number;
|
|
63
|
+
converted: number;
|
|
64
|
+
rate: number;
|
|
65
|
+
significant: boolean;
|
|
66
|
+
}[];
|
|
67
|
+
}
|
|
68
|
+
export interface LearningComparison {
|
|
69
|
+
a: string;
|
|
70
|
+
b: string;
|
|
71
|
+
n_common: number;
|
|
72
|
+
a_correct: number;
|
|
73
|
+
b_correct: number;
|
|
74
|
+
mcnemar_p: number;
|
|
75
|
+
}
|
|
76
|
+
/** Analyze measured outcomes; this never calls the live-rollout decide route. */
|
|
77
|
+
export declare function learningAnalyze(id: string): Promise<LearningAnalysis>;
|
|
78
|
+
/** Pair actual completed items. Zero common items is displayed as no evidence. */
|
|
79
|
+
export declare function learningCompare(benchmark: string, a: string, b: string): Promise<LearningComparison>;
|
package/dist/learning.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { api } from './lib/api.js';
|
|
2
|
+
import { scope } from './lib/session.js';
|
|
3
|
+
export const LEARNING_PATHS = {
|
|
4
|
+
studies: '/v1/research/studies',
|
|
5
|
+
reviews: '/v1/research/actions',
|
|
6
|
+
experiments: '/v1/experiment',
|
|
7
|
+
benchmarks: '/v1/benchmark/catalog',
|
|
8
|
+
};
|
|
9
|
+
/** Every read uses the same IAM/org headers as the rest of the workspace. */
|
|
10
|
+
export async function learningRead(part, signal) {
|
|
11
|
+
const r = await fetch(`${api()}${LEARNING_PATHS[part]}`, { headers: scope(), signal });
|
|
12
|
+
if (!r.ok)
|
|
13
|
+
throw Object.assign(new Error(`${r.status}`), { status: r.status });
|
|
14
|
+
const body = await r.json();
|
|
15
|
+
if (!Array.isArray(body.data))
|
|
16
|
+
throw new Error('Learning response is missing its data array');
|
|
17
|
+
return (part === 'reviews' ? body.data.filter((row) => row.action?.rule === 'research.improvement.v1') : body.data);
|
|
18
|
+
}
|
|
19
|
+
/** Creating a study arms deterministic evaluation; it starts no model or rollout. */
|
|
20
|
+
export async function learningCreate(proposal, id) {
|
|
21
|
+
const r = await fetch(`${api()}/v1/research/studies`, {
|
|
22
|
+
method: 'POST',
|
|
23
|
+
headers: { 'Content-Type': 'application/json', ...scope() },
|
|
24
|
+
body: JSON.stringify({ studies: [{ id, title: proposal.title, question: proposal.question,
|
|
25
|
+
improvement: { baseline: proposal.baseline, target: proposal.target, min_questions: proposal.minQuestions,
|
|
26
|
+
require_intervals: false, metrics: [{ category: proposal.category, metric: proposal.metric, direction: proposal.direction, min_gain: proposal.minGain, max_regression: 0 }] },
|
|
27
|
+
}] }),
|
|
28
|
+
});
|
|
29
|
+
if (!r.ok)
|
|
30
|
+
throw Object.assign(new Error(`${r.status}`), { status: r.status });
|
|
31
|
+
}
|
|
32
|
+
/** Analyze measured outcomes; this never calls the live-rollout decide route. */
|
|
33
|
+
export async function learningAnalyze(id) {
|
|
34
|
+
const r = await fetch(`${api()}/v1/experiment/${encodeURIComponent(id)}/analyze`, {
|
|
35
|
+
method: 'POST', headers: { 'Content-Type': 'application/json', ...scope() }, body: '{}',
|
|
36
|
+
});
|
|
37
|
+
if (!r.ok)
|
|
38
|
+
throw Object.assign(new Error(`${r.status}`), { status: r.status });
|
|
39
|
+
return await r.json();
|
|
40
|
+
}
|
|
41
|
+
/** Pair actual completed items. Zero common items is displayed as no evidence. */
|
|
42
|
+
export async function learningCompare(benchmark, a, b) {
|
|
43
|
+
const query = new URLSearchParams({ benchmark, a, b });
|
|
44
|
+
const r = await fetch(`${api()}/v1/benchmark/compare?${query}`, { headers: scope() });
|
|
45
|
+
if (!r.ok)
|
|
46
|
+
throw Object.assign(new Error(`${r.status}`), { status: r.status });
|
|
47
|
+
return await r.json();
|
|
48
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hanzo/app",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "Hanzo's app: the shell and its apps (Home, Chat, Inbox, Contacts, Meet, Calendar, Drive, Board, Work, Bots), once, for every Hanzo surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|