@legioncodeinc/hive 0.2.1 → 0.3.1
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 +2 -2
- package/assets/brand/activeloop-full-mark-logo-on-dark.svg +209 -0
- package/assets/brand/activeloop-full-mark-logo.svg +208 -0
- package/assets/brand/divider-major.svg +1 -0
- package/assets/brand/divider-minor.svg +1 -0
- package/assets/brand/doctor-mark.svg +1 -0
- package/assets/brand/hive-mark.svg +1 -0
- package/assets/brand/hive-wordmark-black.svg +1 -0
- package/assets/brand/hive-wordmark-on-dark.svg +1 -0
- package/assets/brand/legion-logo-dark.svg +16 -0
- package/assets/brand/legion-logo-light.svg +16 -0
- package/assets/brand/nectar-mark.svg +1 -0
- package/assets/logos/fonts/Inter-Italic-VariableFont_opsz_wght.ttf +0 -0
- package/assets/logos/fonts/Inter-VariableFont_opsz_wght.ttf +0 -0
- package/assets/logos/fonts/JetBrainsMono-Bold.woff2 +0 -0
- package/assets/logos/fonts/JetBrainsMono-Medium.woff2 +0 -0
- package/assets/logos/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/assets/logos/fonts/JetBrainsMono-SemiBold.woff2 +0 -0
- package/assets/logos/honeycomb-memory-cluster.svg +17 -0
- package/assets/styles.css +11 -0
- package/assets/tokens/base.css +76 -0
- package/assets/tokens/colors.css +111 -0
- package/assets/tokens/fonts.css +32 -0
- package/assets/tokens/spacing.css +48 -0
- package/assets/tokens/typography.css +38 -0
- package/dist/daemon/dashboard/app.js +26 -25
- package/dist/daemon/dashboard/host.d.ts +7 -0
- package/dist/daemon/dashboard/host.js +16 -0
- package/dist/daemon/dashboard/host.js.map +1 -1
- package/dist/daemon/dashboard/web-assets.d.ts +2 -0
- package/dist/daemon/dashboard/web-assets.js +19 -0
- package/dist/daemon/dashboard/web-assets.js.map +1 -1
- package/dist/daemon/gate.d.ts +2 -2
- package/dist/daemon/gate.js +15 -4
- package/dist/daemon/gate.js.map +1 -1
- package/dist/daemon/installer/bin-resolver.d.ts +34 -0
- package/dist/daemon/installer/bin-resolver.js +107 -0
- package/dist/daemon/installer/bin-resolver.js.map +1 -0
- package/dist/daemon/installer/config.d.ts +63 -0
- package/dist/daemon/installer/config.js +74 -0
- package/dist/daemon/installer/config.js.map +1 -0
- package/dist/daemon/installer/detection.d.ts +20 -0
- package/dist/daemon/installer/detection.js +73 -0
- package/dist/daemon/installer/detection.js.map +1 -0
- package/dist/daemon/installer/funnel-telemetry.d.ts +54 -0
- package/dist/daemon/installer/funnel-telemetry.js +134 -0
- package/dist/daemon/installer/funnel-telemetry.js.map +1 -0
- package/dist/daemon/installer/index.d.ts +12 -0
- package/dist/daemon/installer/index.js +10 -0
- package/dist/daemon/installer/index.js.map +1 -0
- package/dist/daemon/installer/install-state.d.ts +56 -0
- package/dist/daemon/installer/install-state.js +159 -0
- package/dist/daemon/installer/install-state.js.map +1 -0
- package/dist/daemon/installer/manifest-snapshot.json +25 -0
- package/dist/daemon/installer/manifest.d.ts +47 -0
- package/dist/daemon/installer/manifest.js +103 -0
- package/dist/daemon/installer/manifest.js.map +1 -0
- package/dist/daemon/installer/products.d.ts +33 -0
- package/dist/daemon/installer/products.js +42 -0
- package/dist/daemon/installer/products.js.map +1 -0
- package/dist/daemon/installer/routes.d.ts +43 -0
- package/dist/daemon/installer/routes.js +201 -0
- package/dist/daemon/installer/routes.js.map +1 -0
- package/dist/daemon/installer/security.d.ts +33 -0
- package/dist/daemon/installer/security.js +80 -0
- package/dist/daemon/installer/security.js.map +1 -0
- package/dist/daemon/installer/spawn.d.ts +49 -0
- package/dist/daemon/installer/spawn.js +63 -0
- package/dist/daemon/installer/spawn.js.map +1 -0
- package/dist/daemon/installer/token.d.ts +23 -0
- package/dist/daemon/installer/token.js +56 -0
- package/dist/daemon/installer/token.js.map +1 -0
- package/dist/daemon/server.d.ts +6 -0
- package/dist/daemon/server.js +7 -0
- package/dist/daemon/server.js.map +1 -1
- package/dist/dashboard/web/app.js +42 -20
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/boot-route.d.ts +11 -7
- package/dist/dashboard/web/boot-route.js +12 -6
- package/dist/dashboard/web/boot-route.js.map +1 -1
- package/dist/dashboard/web/main.js +2 -1
- package/dist/dashboard/web/main.js.map +1 -1
- package/dist/dashboard/web/onboarding/advanced-picker.d.ts +16 -0
- package/dist/dashboard/web/onboarding/advanced-picker.js +59 -0
- package/dist/dashboard/web/onboarding/advanced-picker.js.map +1 -0
- package/dist/dashboard/web/onboarding/contracts.d.ts +188 -0
- package/dist/dashboard/web/onboarding/contracts.js +161 -0
- package/dist/dashboard/web/onboarding/contracts.js.map +1 -0
- package/dist/dashboard/web/onboarding/health-view.d.ts +17 -0
- package/dist/dashboard/web/onboarding/health-view.js +79 -0
- package/dist/dashboard/web/onboarding/health-view.js.map +1 -0
- package/dist/dashboard/web/onboarding/install-card.d.ts +28 -0
- package/dist/dashboard/web/onboarding/install-card.js +171 -0
- package/dist/dashboard/web/onboarding/install-card.js.map +1 -0
- package/dist/dashboard/web/onboarding/login-step.d.ts +29 -0
- package/dist/dashboard/web/onboarding/login-step.js +104 -0
- package/dist/dashboard/web/onboarding/login-step.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-client.d.ts +52 -0
- package/dist/dashboard/web/onboarding/onboarding-client.js +133 -0
- package/dist/dashboard/web/onboarding/onboarding-client.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-hero.d.ts +24 -0
- package/dist/dashboard/web/onboarding/onboarding-hero.js +70 -0
- package/dist/dashboard/web/onboarding/onboarding-hero.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-screen.d.ts +43 -0
- package/dist/dashboard/web/onboarding/onboarding-screen.js +193 -0
- package/dist/dashboard/web/onboarding/onboarding-screen.js.map +1 -0
- package/dist/dashboard/web/onboarding/onboarding-selection-store.d.ts +20 -0
- package/dist/dashboard/web/onboarding/onboarding-selection-store.js +76 -0
- package/dist/dashboard/web/onboarding/onboarding-selection-store.js.map +1 -0
- package/dist/dashboard/web/onboarding/product-copy.d.ts +40 -0
- package/dist/dashboard/web/onboarding/product-copy.js +70 -0
- package/dist/dashboard/web/onboarding/product-copy.js.map +1 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.d.ts +27 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.js +42 -0
- package/dist/dashboard/web/onboarding/use-install-dwell.js.map +1 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.d.ts +14 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.js +41 -0
- package/dist/dashboard/web/onboarding/use-onboarding-token.js.map +1 -0
- package/dist/dashboard/web/route-daemon-owner.d.ts +7 -0
- package/dist/dashboard/web/route-daemon-owner.js +15 -0
- package/dist/dashboard/web/route-daemon-owner.js.map +1 -0
- package/dist/dashboard/web/wire.d.ts +1 -1
- package/dist/shared/onboarding-types.d.ts +79 -0
- package/dist/shared/onboarding-types.js +12 -0
- package/dist/shared/onboarding-types.js.map +1 -0
- package/dist/telemetry/emit.d.ts +30 -3
- package/dist/telemetry/emit.js +25 -2
- package/dist/telemetry/emit.js.map +1 -1
- package/dist/telemetry/onboarding-session-ledger.d.ts +31 -0
- package/dist/telemetry/onboarding-session-ledger.js +78 -0
- package/dist/telemetry/onboarding-session-ledger.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The per-product GUIDED INSTALL CARD, PRD-009b ob-AC-8/ob-AC-9/ob-AC-10/ob-AC-11/ob-AC-12. A
|
|
4
|
+
* full-screen card carrying the product's logo, title, benefit copy, staged (never percent)
|
|
5
|
+
* progress, and the npm-safety reassurance. Honors a brief minimum dwell on success (ob-AC-11,
|
|
6
|
+
* revised: the real install time drives the pace, the dwell only prevents a flash-through) and
|
|
7
|
+
* NEVER masks a failure behind that dwell (ob-AC-12), a `failed` stage renders its truthful error
|
|
8
|
+
* plus a retry affordance the instant it arrives, regardless of elapsed dwell time.
|
|
9
|
+
*
|
|
10
|
+
* Re-entry (ob-AC-16/ob-AC-17): `initialDetection` (the daemon's own truth from `/api/onboarding/
|
|
11
|
+
* detect`) decides the card's opening move, `install_in_progress` RE-ATTACHES the SSE stream
|
|
12
|
+
* without a new install POST; `install_failed` renders the failure immediately and waits for an
|
|
13
|
+
* explicit retry; anything else starts a fresh install.
|
|
14
|
+
*/
|
|
15
|
+
import React from "react";
|
|
16
|
+
import { IN_FLIGHT_STAGES, installRefusalMessage } from "./contracts.js";
|
|
17
|
+
import { INSTALL_STAGE_LABEL, NPM_SAFETY_COPY, PRODUCT_COPY, productLogoUrl } from "./product-copy.js";
|
|
18
|
+
import { useInstallDwell } from "./use-install-dwell.js";
|
|
19
|
+
function stageStepState(stepIndex, doneCount, terminal) {
|
|
20
|
+
if (stepIndex < doneCount)
|
|
21
|
+
return "done";
|
|
22
|
+
if (stepIndex === doneCount && terminal !== "completed")
|
|
23
|
+
return "active";
|
|
24
|
+
return "pending";
|
|
25
|
+
}
|
|
26
|
+
/** The per-stage stepper (ob-AC-9): a labeled list, never a fabricated percentage. */
|
|
27
|
+
function StageStepper({ product, stage }) {
|
|
28
|
+
const currentIndex = IN_FLIGHT_STAGES.indexOf(stage);
|
|
29
|
+
const doneCount = stage === "completed" ? IN_FLIGHT_STAGES.length : Math.max(0, currentIndex);
|
|
30
|
+
return (_jsx("ul", { "data-testid": `onboarding-install-stage-${product}`, "data-current-stage": stage, style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 8, width: "100%", textAlign: "left" }, children: IN_FLIGHT_STAGES.map((step, i) => {
|
|
31
|
+
const state = stageStepState(i, doneCount, stage);
|
|
32
|
+
return (_jsxs("li", { "data-state": state, style: {
|
|
33
|
+
display: "flex",
|
|
34
|
+
alignItems: "center",
|
|
35
|
+
gap: 10,
|
|
36
|
+
fontFamily: "var(--font-sans)",
|
|
37
|
+
fontSize: "var(--text-sm)",
|
|
38
|
+
color: state === "pending" ? "var(--text-tertiary)" : "var(--text-primary)",
|
|
39
|
+
}, children: [_jsx("span", { "aria-hidden": "true", style: {
|
|
40
|
+
width: 8,
|
|
41
|
+
height: 8,
|
|
42
|
+
flex: "none",
|
|
43
|
+
borderRadius: "50%",
|
|
44
|
+
background: state === "done" ? "var(--verified)" : state === "active" ? "var(--honey)" : "var(--border-strong)",
|
|
45
|
+
animation: state === "active" ? "hc-onboarding-stage-pulse var(--dur-pollinate) var(--ease-in-out) infinite alternate" : "none",
|
|
46
|
+
} }), INSTALL_STAGE_LABEL[step]] }, step));
|
|
47
|
+
}) }));
|
|
48
|
+
}
|
|
49
|
+
/** ob-AC-10, the npm-safety reassurance, verbatim, on every card. */
|
|
50
|
+
function NpmSafetyNote({ product }) {
|
|
51
|
+
return (_jsx("p", { "data-testid": `onboarding-npm-safety-${product}`, style: {
|
|
52
|
+
fontFamily: "var(--font-sans)",
|
|
53
|
+
fontSize: "var(--text-xs)",
|
|
54
|
+
color: "var(--text-tertiary)",
|
|
55
|
+
margin: 0,
|
|
56
|
+
lineHeight: 1.5,
|
|
57
|
+
padding: "10px 14px",
|
|
58
|
+
background: "var(--bg-inset)",
|
|
59
|
+
border: "1px solid var(--border-subtle)",
|
|
60
|
+
borderRadius: "var(--radius-md)",
|
|
61
|
+
}, children: NPM_SAFETY_COPY }));
|
|
62
|
+
}
|
|
63
|
+
export function InstallCard({ product, initialDetection, client, assetBase, onAdvance, minDwellMs }) {
|
|
64
|
+
// Captured ONCE at mount, later prop identity changes (a parent re-render passing an
|
|
65
|
+
// equivalent-but-new object) must never re-trigger the opening-move decision below.
|
|
66
|
+
const openingRef = React.useRef(initialDetection);
|
|
67
|
+
const wasInitiallyInstalled = openingRef.current.state === "installed";
|
|
68
|
+
const wasInitiallyFailed = openingRef.current.state === "install_failed";
|
|
69
|
+
const [stage, setStage] = React.useState(() => {
|
|
70
|
+
if (wasInitiallyInstalled)
|
|
71
|
+
return "completed";
|
|
72
|
+
if (wasInitiallyFailed)
|
|
73
|
+
return "failed";
|
|
74
|
+
return "resolving";
|
|
75
|
+
});
|
|
76
|
+
const [error, setError] = React.useState(() => openingRef.current.error ?? null);
|
|
77
|
+
// Bumped by the retry button; also doubles as "has the operator asked us to try again" so a
|
|
78
|
+
// card that OPENED failed runs no network activity until this leaves 0 (ob-AC-12).
|
|
79
|
+
const [attempt, setAttempt] = React.useState(0);
|
|
80
|
+
React.useEffect(() => {
|
|
81
|
+
if (wasInitiallyInstalled)
|
|
82
|
+
return;
|
|
83
|
+
if (wasInitiallyFailed && attempt === 0)
|
|
84
|
+
return;
|
|
85
|
+
let cancelled = false;
|
|
86
|
+
let unsubscribe = () => { };
|
|
87
|
+
void (async () => {
|
|
88
|
+
// ob-AC-17: re-attach ONLY on the very first attempt of a card that opened mid-flight ,
|
|
89
|
+
// every retry (attempt > 0) always re-POSTs a fresh install, even for that same product.
|
|
90
|
+
const reattachOnly = attempt === 0 && openingRef.current.state === "install_in_progress";
|
|
91
|
+
if (!reattachOnly) {
|
|
92
|
+
const started = await client.startInstall(product);
|
|
93
|
+
if (cancelled)
|
|
94
|
+
return;
|
|
95
|
+
if (started === null) {
|
|
96
|
+
setError({ stage: "resolving", summary: "Could not reach the daemon to start the install. Check your connection and retry." });
|
|
97
|
+
setStage("failed");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if ("error" in started) {
|
|
101
|
+
setError({ stage: "resolving", summary: installRefusalMessage(started.error) });
|
|
102
|
+
setStage("failed");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (started.state === "installed") {
|
|
106
|
+
// A race the daemon itself resolved before this card's POST landed, still honors
|
|
107
|
+
// the minimum dwell below, just skips the SSE stream entirely (nothing to stream).
|
|
108
|
+
setStage("completed");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
setError(null);
|
|
113
|
+
unsubscribe = client.subscribeInstallEvents(product, (event) => {
|
|
114
|
+
if (cancelled)
|
|
115
|
+
return;
|
|
116
|
+
setStage(event.stage);
|
|
117
|
+
if (event.stage === "failed") {
|
|
118
|
+
setError({ stage: "unknown", summary: event.detail ?? "The install failed. Retry below, or check the daemon logs." });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
})();
|
|
122
|
+
return () => {
|
|
123
|
+
cancelled = true;
|
|
124
|
+
unsubscribe();
|
|
125
|
+
};
|
|
126
|
+
// `product`/`client` are stable for a card's lifetime; `attempt` is the sole re-run trigger.
|
|
127
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
128
|
+
}, [attempt, wasInitiallyFailed, wasInitiallyInstalled]);
|
|
129
|
+
const handleRetry = React.useCallback(() => {
|
|
130
|
+
setError(null);
|
|
131
|
+
setStage("resolving");
|
|
132
|
+
setAttempt((n) => n + 1);
|
|
133
|
+
}, []);
|
|
134
|
+
// ob-AC-11: gates ONLY the success-advance path. A `failed` stage is rendered above regardless
|
|
135
|
+
// of dwell, this hook never sees `ready:true` for a failed card, so it can never mask one.
|
|
136
|
+
useInstallDwell(stage === "completed", { onDwellSatisfied: onAdvance, minDwellMs });
|
|
137
|
+
const copy = PRODUCT_COPY[product];
|
|
138
|
+
const failed = stage === "failed";
|
|
139
|
+
return (_jsxs("div", { "data-testid": `onboarding-install-card-${product}`, "data-stage": stage, style: {
|
|
140
|
+
display: "flex",
|
|
141
|
+
alignItems: "center",
|
|
142
|
+
justifyContent: "center",
|
|
143
|
+
minHeight: "100vh",
|
|
144
|
+
padding: 32,
|
|
145
|
+
background: "var(--bg-canvas)",
|
|
146
|
+
textAlign: "center",
|
|
147
|
+
}, children: [_jsxs("div", { style: {
|
|
148
|
+
display: "flex",
|
|
149
|
+
flexDirection: "column",
|
|
150
|
+
alignItems: "center",
|
|
151
|
+
gap: 22,
|
|
152
|
+
width: "100%",
|
|
153
|
+
maxWidth: 540,
|
|
154
|
+
padding: "44px 40px",
|
|
155
|
+
background: "var(--bg-surface)",
|
|
156
|
+
border: "1px solid var(--border-default)",
|
|
157
|
+
borderRadius: "var(--radius-xl)",
|
|
158
|
+
}, children: [_jsx("img", { src: productLogoUrl(product, assetBase), width: 64, height: 64, alt: "" }), _jsx("h1", { style: { fontSize: "var(--text-2xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: copy.title }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 6, maxWidth: 440 }, children: [_jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", fontWeight: 600, color: "var(--text-primary)", margin: 0 }, children: copy.headline }), _jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: copy.lines[0] }), _jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: copy.lines[1] })] }), failed ? (_jsxs("div", { "data-testid": `onboarding-install-error-${product}`, style: { display: "flex", flexDirection: "column", gap: 12, alignItems: "center", width: "100%" }, children: [_jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--severity-critical)", margin: 0 }, children: error?.summary ?? "The install failed." }), _jsx("button", { type: "button", "data-testid": `onboarding-retry-${product}`, onClick: handleRetry, style: {
|
|
159
|
+
height: 40,
|
|
160
|
+
padding: "0 18px",
|
|
161
|
+
borderRadius: "var(--radius-md)",
|
|
162
|
+
border: "1px solid var(--severity-critical)",
|
|
163
|
+
background: "var(--severity-critical-bg)",
|
|
164
|
+
color: "var(--severity-critical)",
|
|
165
|
+
fontFamily: "var(--font-sans)",
|
|
166
|
+
fontSize: "var(--text-sm)",
|
|
167
|
+
fontWeight: 600,
|
|
168
|
+
cursor: "pointer",
|
|
169
|
+
}, children: "Retry" })] })) : (_jsx(StageStepper, { product: product, stage: stage })), _jsx(NpmSafetyNote, { product: product })] }), _jsx("style", { children: "@keyframes hc-onboarding-stage-pulse { from { opacity: .4 } to { opacity: 1 } }" })] }));
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=install-card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install-card.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/install-card.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The onboarding LOGIN STEP, PRD-009b ob-AC-14/ob-AC-15. Closes the known device-code display gap
|
|
3
|
+
* by reusing the EXACT wire contract `GuidedSetup` (`src/dashboard/web/setup-gate.tsx`) already
|
|
4
|
+
* defines (`POST /setup/login`, polled `GET /setup/state`) WITHOUT modifying that module: this is
|
|
5
|
+
* the sibling component the task brief calls for, sharing `wire.ts`'s types/client rather than
|
|
6
|
+
* duplicating the device-flow request shapes.
|
|
7
|
+
*
|
|
8
|
+
* Unlike `/login`'s `GuidedSetup` (which waits for an explicit "First time setup" click), the
|
|
9
|
+
* onboarding flow has already walked the operator through installs and a health check, so this
|
|
10
|
+
* step begins the device flow automatically on mount: one fewer click at the end of a long guided
|
|
11
|
+
* sequence. Once `/setup/state.authenticated` flips true it fires `dashboard_reached`, best-effort
|
|
12
|
+
* POSTs `/api/onboarding/complete`, then hard-navigates to `/` (ob-AC-15) so the server gate
|
|
13
|
+
* revalidates and serves the authoritative dashboard, the same discipline `LoginScreen` follows.
|
|
14
|
+
*/
|
|
15
|
+
import React from "react";
|
|
16
|
+
import { type WireClient } from "../wire.js";
|
|
17
|
+
import type { OnboardingClient } from "./onboarding-client.js";
|
|
18
|
+
/** Mirrors `setup-gate.tsx`'s `SETUP_POLL_MS`, the live-transition poll cadence. */
|
|
19
|
+
export declare const LOGIN_STEP_POLL_MS: 2500;
|
|
20
|
+
export interface LoginStepProps {
|
|
21
|
+
readonly onboardingClient: OnboardingClient;
|
|
22
|
+
/** The proxied setup wire client (defaults to the live one; a test injects a mock). */
|
|
23
|
+
readonly wire?: WireClient;
|
|
24
|
+
/** Test seam: called instead of the real hard navigation once authenticated. */
|
|
25
|
+
readonly onAuthenticated?: () => void;
|
|
26
|
+
/** Overrides {@link LOGIN_STEP_POLL_MS} (a test injects a short window). */
|
|
27
|
+
readonly pollMs?: number;
|
|
28
|
+
}
|
|
29
|
+
export declare function LoginStep({ onboardingClient, wire: wireOverride, onAuthenticated, pollMs }: LoginStepProps): React.JSX.Element;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* The onboarding LOGIN STEP, PRD-009b ob-AC-14/ob-AC-15. Closes the known device-code display gap
|
|
4
|
+
* by reusing the EXACT wire contract `GuidedSetup` (`src/dashboard/web/setup-gate.tsx`) already
|
|
5
|
+
* defines (`POST /setup/login`, polled `GET /setup/state`) WITHOUT modifying that module: this is
|
|
6
|
+
* the sibling component the task brief calls for, sharing `wire.ts`'s types/client rather than
|
|
7
|
+
* duplicating the device-flow request shapes.
|
|
8
|
+
*
|
|
9
|
+
* Unlike `/login`'s `GuidedSetup` (which waits for an explicit "First time setup" click), the
|
|
10
|
+
* onboarding flow has already walked the operator through installs and a health check, so this
|
|
11
|
+
* step begins the device flow automatically on mount: one fewer click at the end of a long guided
|
|
12
|
+
* sequence. Once `/setup/state.authenticated` flips true it fires `dashboard_reached`, best-effort
|
|
13
|
+
* POSTs `/api/onboarding/complete`, then hard-navigates to `/` (ob-AC-15) so the server gate
|
|
14
|
+
* revalidates and serves the authoritative dashboard, the same discipline `LoginScreen` follows.
|
|
15
|
+
*/
|
|
16
|
+
import React from "react";
|
|
17
|
+
import { createWireClient, FRESH_SETUP_STATE } from "../wire.js";
|
|
18
|
+
/** Mirrors `setup-gate.tsx`'s `SETUP_POLL_MS`, the live-transition poll cadence. */
|
|
19
|
+
export const LOGIN_STEP_POLL_MS = 2500;
|
|
20
|
+
/**
|
|
21
|
+
* The grant display (ob-AC-14): the `user_code` prominently, plus `verification_uri_complete`
|
|
22
|
+
* falling back to `verification_uri`, byte-identical fallback rule to `GuidedSetup`'s.
|
|
23
|
+
*/
|
|
24
|
+
function LoginGrant({ grant }) {
|
|
25
|
+
return (_jsxs("div", { "data-testid": "onboarding-login-grant", style: { display: "flex", flexDirection: "column", gap: 10, alignItems: "center" }, children: [_jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0 }, children: "Enter this code to finish linking your Deeplake account:" }), _jsx("code", { "data-testid": "onboarding-login-code", style: { fontFamily: "var(--font-mono)", fontSize: "var(--text-lg)", color: "var(--honey)", letterSpacing: "0.08em" }, children: grant.user_code }), _jsx("a", { href: grant.verification_uri_complete ?? grant.verification_uri, target: "_blank", rel: "noreferrer", "data-testid": "onboarding-login-verification-link", style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)" }, children: "Open the verification page" }), _jsx("span", { style: { fontSize: "var(--text-xs)", color: "var(--text-tertiary)" }, children: "Waiting for you to finish in the browser\u2026" })] }));
|
|
26
|
+
}
|
|
27
|
+
export function LoginStep({ onboardingClient, wire: wireOverride, onAuthenticated, pollMs = LOGIN_STEP_POLL_MS }) {
|
|
28
|
+
const wire = React.useMemo(() => wireOverride ?? createWireClient(), [wireOverride]);
|
|
29
|
+
const [state, setState] = React.useState(FRESH_SETUP_STATE);
|
|
30
|
+
const [grant, setGrant] = React.useState(null);
|
|
31
|
+
const [error, setError] = React.useState(false);
|
|
32
|
+
const beginRef = React.useRef(false);
|
|
33
|
+
const grantShownEventRef = React.useRef(false);
|
|
34
|
+
const navigatedRef = React.useRef(false);
|
|
35
|
+
// Auto-begin the device flow on mount (see module doc for why onboarding skips the button
|
|
36
|
+
// `GuidedSetup` shows on the standalone `/login` route).
|
|
37
|
+
React.useEffect(() => {
|
|
38
|
+
if (beginRef.current)
|
|
39
|
+
return;
|
|
40
|
+
beginRef.current = true;
|
|
41
|
+
void (async () => {
|
|
42
|
+
const result = await wire.setupLogin();
|
|
43
|
+
if (result === null) {
|
|
44
|
+
setError(true);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
setGrant(result);
|
|
48
|
+
})();
|
|
49
|
+
}, [wire]);
|
|
50
|
+
// ob-AC-14, `login_shown` fires the moment the grant (the code the operator must see) renders.
|
|
51
|
+
React.useEffect(() => {
|
|
52
|
+
if (grant === null || grantShownEventRef.current)
|
|
53
|
+
return;
|
|
54
|
+
grantShownEventRef.current = true;
|
|
55
|
+
onboardingClient.sendEvent("login_shown");
|
|
56
|
+
}, [grant, onboardingClient]);
|
|
57
|
+
// Poll `/setup/state` for the live authenticated transition, exactly like `LoginScreen` does.
|
|
58
|
+
React.useEffect(() => {
|
|
59
|
+
if (state.authenticated)
|
|
60
|
+
return;
|
|
61
|
+
let alive = true;
|
|
62
|
+
const tick = async () => {
|
|
63
|
+
const next = await wire.setupState();
|
|
64
|
+
if (alive)
|
|
65
|
+
setState(next);
|
|
66
|
+
};
|
|
67
|
+
void tick();
|
|
68
|
+
const id = setInterval(() => void tick(), pollMs);
|
|
69
|
+
return () => {
|
|
70
|
+
alive = false;
|
|
71
|
+
clearInterval(id);
|
|
72
|
+
};
|
|
73
|
+
}, [wire, state.authenticated, pollMs]);
|
|
74
|
+
// ob-AC-15, authenticated: fire `dashboard_reached`, best-effort POST complete, then hard-nav.
|
|
75
|
+
React.useEffect(() => {
|
|
76
|
+
if (!state.authenticated || navigatedRef.current)
|
|
77
|
+
return;
|
|
78
|
+
navigatedRef.current = true;
|
|
79
|
+
onboardingClient.sendEvent("dashboard_reached");
|
|
80
|
+
void (async () => {
|
|
81
|
+
await onboardingClient.complete();
|
|
82
|
+
if (onAuthenticated !== undefined) {
|
|
83
|
+
onAuthenticated();
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (typeof window !== "undefined")
|
|
87
|
+
window.location.assign("/");
|
|
88
|
+
})();
|
|
89
|
+
}, [state.authenticated, onboardingClient, onAuthenticated]);
|
|
90
|
+
if (state.authenticated)
|
|
91
|
+
return _jsx(_Fragment, {});
|
|
92
|
+
return (_jsxs("div", { "data-testid": "onboarding-login-step", style: {
|
|
93
|
+
display: "flex",
|
|
94
|
+
flexDirection: "column",
|
|
95
|
+
alignItems: "center",
|
|
96
|
+
justifyContent: "center",
|
|
97
|
+
gap: 18,
|
|
98
|
+
minHeight: "100vh",
|
|
99
|
+
padding: 28,
|
|
100
|
+
background: "var(--bg-canvas)",
|
|
101
|
+
textAlign: "center",
|
|
102
|
+
}, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8, maxWidth: 460 }, children: [_jsx("h1", { style: { fontSize: "var(--text-xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "One last step: link Deeplake" }), _jsx("p", { style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0, lineHeight: 1.5 }, children: "Your fleet is up. Link your Deeplake account and you are on your dashboard." })] }), grant !== null ? (_jsx(LoginGrant, { grant: grant })) : error ? (_jsxs("p", { "data-testid": "onboarding-login-error", style: { fontSize: "var(--text-sm)", color: "var(--severity-critical)", margin: 0 }, children: ["Could not start the login. Retry, or run ", _jsx("code", { children: "honeycomb login" }), " in your terminal."] })) : (_jsx("p", { style: { fontSize: "var(--text-sm)", color: "var(--text-secondary)", margin: 0 }, children: "Starting login\u2026" }))] }));
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=login-step.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-step.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/login-step.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/onboarding` route's WIRE CLIENT, PRD-009b, talking to the PRD-009a installer service.
|
|
3
|
+
* Mirrors `wire.ts`'s fetch+zod discipline (fail-soft, never throws into React) but is kept
|
|
4
|
+
* LOCAL to the onboarding feature folder per the file-ownership split with the daemon-side agent
|
|
5
|
+
* building `src/daemon/installer/**` in parallel.
|
|
6
|
+
*
|
|
7
|
+
* Every call carries the one-time onboarding token as the `x-onboarding-token` header (the PRD's
|
|
8
|
+
* "carried on every installer call"), EXCEPT the SSE subscription: `EventSource` cannot set request
|
|
9
|
+
* headers, so the token rides as a `?t=` query param there instead (the implementation note both
|
|
10
|
+
* the parent brief and PRD-009a's SSE contract call for).
|
|
11
|
+
*/
|
|
12
|
+
import { type DetectResponse, type HealthResponse, type InstallableProduct, type InstallProgressEvent, type InstallStartResult } from "./contracts.js";
|
|
13
|
+
/** The header carrying the one-time onboarding token on every installer call (never the SSE query). */
|
|
14
|
+
export declare const ONBOARDING_TOKEN_HEADER: "x-onboarding-token";
|
|
15
|
+
/** A fetch-like function, injectable so tests never hit the network (mirrors `wire.ts`'s `FetchLike`). */
|
|
16
|
+
type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
17
|
+
/** An `EventSource`-constructor-like injectable, so tests never open a real connection. */
|
|
18
|
+
type EventSourceCtorLike = new (url: string | URL) => EventSource;
|
|
19
|
+
export interface OnboardingClientOptions {
|
|
20
|
+
/** Prefixed onto every request path (defaults to same-origin, `""`). A test injects a base. */
|
|
21
|
+
readonly origin?: string;
|
|
22
|
+
/** Defaults to the global `fetch`; a test injects a mock. */
|
|
23
|
+
readonly fetchImpl?: FetchLike;
|
|
24
|
+
/** Defaults to the global `EventSource`; a test injects a fake (jsdom has none, see module doc below). */
|
|
25
|
+
readonly eventSourceCtor?: EventSourceCtorLike;
|
|
26
|
+
}
|
|
27
|
+
/** The onboarding wire surface every onboarding component reads/writes through. */
|
|
28
|
+
export interface OnboardingClient {
|
|
29
|
+
/** `GET /api/onboarding/detect`, never assumes a product set; a failure degrades to {@link EMPTY_DETECTION}. */
|
|
30
|
+
detect(): Promise<DetectResponse>;
|
|
31
|
+
/** `POST /api/onboarding/install`, starts (or short-circuits) one product's install. `null` on failure. */
|
|
32
|
+
startInstall(product: InstallableProduct): Promise<InstallStartResult | null>;
|
|
33
|
+
/**
|
|
34
|
+
* `GET /api/onboarding/install/:product/events` (SSE). Returns an unsubscribe function. In an
|
|
35
|
+
* `EventSource`-less environment (jsdom, mirroring `use-fleet-telemetry.ts`'s documented gap)
|
|
36
|
+
* this is a no-op subscription, the caller's install-card falls back to its optimistic local
|
|
37
|
+
* stage tracking rather than throwing.
|
|
38
|
+
*/
|
|
39
|
+
subscribeInstallEvents(product: InstallableProduct, onEvent: (event: InstallProgressEvent) => void): () => void;
|
|
40
|
+
/** `GET /api/onboarding/health`, a failure degrades to {@link UNREACHABLE_HEALTH} (never `ready:true`). */
|
|
41
|
+
health(): Promise<HealthResponse>;
|
|
42
|
+
/** `POST /api/onboarding/complete` (204). Best-effort: never throws, never blocks the caller's navigation. */
|
|
43
|
+
complete(): Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* `POST /api/onboarding/event` (202), the UI funnel chokepoint. FIRE-AND-FORGET by design: the
|
|
46
|
+
* caller never awaits this (a slow/broken telemetry endpoint must never stall the guided flow).
|
|
47
|
+
*/
|
|
48
|
+
sendEvent(event: string, properties?: Record<string, string>): void;
|
|
49
|
+
}
|
|
50
|
+
/** Build the onboarding wire client bound to one onboarding token (read once from `?t=`, kept in memory). */
|
|
51
|
+
export declare function createOnboardingClient(token: string, options?: OnboardingClientOptions): OnboardingClient;
|
|
52
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `/onboarding` route's WIRE CLIENT, PRD-009b, talking to the PRD-009a installer service.
|
|
3
|
+
* Mirrors `wire.ts`'s fetch+zod discipline (fail-soft, never throws into React) but is kept
|
|
4
|
+
* LOCAL to the onboarding feature folder per the file-ownership split with the daemon-side agent
|
|
5
|
+
* building `src/daemon/installer/**` in parallel.
|
|
6
|
+
*
|
|
7
|
+
* Every call carries the one-time onboarding token as the `x-onboarding-token` header (the PRD's
|
|
8
|
+
* "carried on every installer call"), EXCEPT the SSE subscription: `EventSource` cannot set request
|
|
9
|
+
* headers, so the token rides as a `?t=` query param there instead (the implementation note both
|
|
10
|
+
* the parent brief and PRD-009a's SSE contract call for).
|
|
11
|
+
*/
|
|
12
|
+
import { DetectResponseSchema, EMPTY_DETECTION, InstallProgressEventSchema, InstallRefusalResponseSchema, InstallStartResponseSchema, parseHealthResponse, UNREACHABLE_HEALTH, } from "./contracts.js";
|
|
13
|
+
/** The header carrying the one-time onboarding token on every installer call (never the SSE query). */
|
|
14
|
+
export const ONBOARDING_TOKEN_HEADER = "x-onboarding-token";
|
|
15
|
+
function tokenHeaders(token) {
|
|
16
|
+
return token !== "" ? { [ONBOARDING_TOKEN_HEADER]: token } : {};
|
|
17
|
+
}
|
|
18
|
+
/** Build the onboarding wire client bound to one onboarding token (read once from `?t=`, kept in memory). */
|
|
19
|
+
export function createOnboardingClient(token, options = {}) {
|
|
20
|
+
const origin = options.origin ?? "";
|
|
21
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
22
|
+
const url = (path) => `${origin}${path}`;
|
|
23
|
+
return {
|
|
24
|
+
async detect() {
|
|
25
|
+
try {
|
|
26
|
+
const res = await fetchImpl(url("/api/onboarding/detect"), {
|
|
27
|
+
headers: { accept: "application/json", ...tokenHeaders(token) },
|
|
28
|
+
});
|
|
29
|
+
if (!res.ok)
|
|
30
|
+
return EMPTY_DETECTION;
|
|
31
|
+
const parsed = DetectResponseSchema.safeParse(await res.json());
|
|
32
|
+
return parsed.success ? parsed.data : EMPTY_DETECTION;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return EMPTY_DETECTION;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
async startInstall(product) {
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetchImpl(url("/api/onboarding/install"), {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "content-type": "application/json", accept: "application/json", ...tokenHeaders(token) },
|
|
43
|
+
body: JSON.stringify({ product }),
|
|
44
|
+
});
|
|
45
|
+
if (res.status === 409) {
|
|
46
|
+
const parsed = InstallRefusalResponseSchema.safeParse(await res.json());
|
|
47
|
+
return parsed.success ? parsed.data : null;
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok)
|
|
50
|
+
return null;
|
|
51
|
+
const parsed = InstallStartResponseSchema.safeParse(await res.json());
|
|
52
|
+
return parsed.success ? parsed.data : null;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
subscribeInstallEvents(product, onEvent) {
|
|
59
|
+
const EventSourceCtor = options.eventSourceCtor ?? globalThis.EventSource;
|
|
60
|
+
if (EventSourceCtor === undefined) {
|
|
61
|
+
// jsdom (this repo's test environment) has no EventSource, mirroring the exact gap
|
|
62
|
+
// `use-fleet-telemetry.ts` documents. Tests inject a fake constructor to exercise this path.
|
|
63
|
+
return () => { };
|
|
64
|
+
}
|
|
65
|
+
const qs = token !== "" ? `?t=${encodeURIComponent(token)}` : "";
|
|
66
|
+
let source = null;
|
|
67
|
+
try {
|
|
68
|
+
source = new EventSourceCtor(url(`/api/onboarding/install/${encodeURIComponent(product)}/events${qs}`));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return () => { };
|
|
72
|
+
}
|
|
73
|
+
// The contract names no custom SSE event type, so this subscribes to the default unnamed
|
|
74
|
+
// `message` frame (see the onboarding-client research note in the final report: if the
|
|
75
|
+
// daemon's implementation ends up naming the event, this is a one-line integration fix).
|
|
76
|
+
const handler = (event) => {
|
|
77
|
+
const raw = typeof event.data === "string" ? event.data : String(event.data);
|
|
78
|
+
try {
|
|
79
|
+
const parsed = InstallProgressEventSchema.safeParse(JSON.parse(raw));
|
|
80
|
+
if (parsed.success)
|
|
81
|
+
onEvent(parsed.data);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// A malformed frame is dropped; the next frame (or the server's on-subscribe
|
|
85
|
+
// current-stage resend, ob-AC-17) carries the truth forward.
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
source.addEventListener("message", handler);
|
|
89
|
+
return () => {
|
|
90
|
+
try {
|
|
91
|
+
source?.removeEventListener("message", handler);
|
|
92
|
+
source?.close();
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Closing an already-closed/errored source must never throw into the caller's cleanup.
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
},
|
|
99
|
+
async health() {
|
|
100
|
+
try {
|
|
101
|
+
const res = await fetchImpl(url("/api/onboarding/health"), {
|
|
102
|
+
headers: { accept: "application/json", ...tokenHeaders(token) },
|
|
103
|
+
});
|
|
104
|
+
if (!res.ok)
|
|
105
|
+
return UNREACHABLE_HEALTH;
|
|
106
|
+
return parseHealthResponse(await res.json());
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return UNREACHABLE_HEALTH;
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
async complete() {
|
|
113
|
+
try {
|
|
114
|
+
await fetchImpl(url("/api/onboarding/complete"), { method: "POST", headers: tokenHeaders(token) });
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Best-effort: the login step navigates to the dashboard regardless (never gets the
|
|
118
|
+
// operator stuck because a completion beacon failed to land).
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
sendEvent(event, properties) {
|
|
122
|
+
const body = properties !== undefined ? { event, properties } : { event };
|
|
123
|
+
// Fire-and-forget: the caller never awaits this, so a slow/broken telemetry endpoint can
|
|
124
|
+
// never stall the guided flow. Swallow every failure silently (fail-soft telemetry posture).
|
|
125
|
+
void fetchImpl(url("/api/onboarding/event"), {
|
|
126
|
+
method: "POST",
|
|
127
|
+
headers: { "content-type": "application/json", ...tokenHeaders(token) },
|
|
128
|
+
body: JSON.stringify(body),
|
|
129
|
+
}).catch(() => { });
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=onboarding-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-client.js","sourceRoot":"","sources":["../../../../src/dashboard/web/onboarding/onboarding-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The first-run HERO, PRD-009b ob-AC-4/ob-AC-5. A staggered entrance anchored by the Hive mark:
|
|
3
|
+
* the product logos rise and fade in one by one on a spring-like curve, the Hive mark settles
|
|
4
|
+
* center LAST, then the two choice buttons fade in. Pure CSS animation/transition (no new
|
|
5
|
+
* dependency); `prefers-reduced-motion` collapses every entrance to a plain opacity fade with no
|
|
6
|
+
* transform (see the `hc-onboarding-anim` rule below: the SAFE default IS the reduced-motion form,
|
|
7
|
+
* and a `(prefers-reduced-motion: no-preference)` media query is what OPTS IN to the richer motion,
|
|
8
|
+
* so an environment that cannot evaluate the media query at all still gets the safe fade).
|
|
9
|
+
*/
|
|
10
|
+
import React from "react";
|
|
11
|
+
/** ob-AC-5, the exact two choices, verbatim copy. */
|
|
12
|
+
export type OnboardingMode = "standard" | "advanced";
|
|
13
|
+
export interface OnboardingHeroProps {
|
|
14
|
+
readonly assetBase: string;
|
|
15
|
+
readonly onChooseStandard: () => void;
|
|
16
|
+
readonly onChooseAdvanced: () => void;
|
|
17
|
+
/** The per-entry stagger delay (ms) between each product logo. Overridable for tests. */
|
|
18
|
+
readonly staggerMs?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The first-run hero + the two-choice entry point. Renders unconditionally once the caller has
|
|
22
|
+
* already decided this IS a first-run (no detection re-derivation happens here, per ob-AC-2).
|
|
23
|
+
*/
|
|
24
|
+
export declare function OnboardingHero({ assetBase, onChooseStandard, onChooseAdvanced, staggerMs }: OnboardingHeroProps): React.JSX.Element;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { productLogoUrl } from "./product-copy.js";
|
|
3
|
+
/** The hero's hardcoded hero mark, in the order they animate in BEFORE the Hive mark settles last. */
|
|
4
|
+
const HERO_PRODUCT_MARKS = ["honeycomb", "doctor", "nectar"];
|
|
5
|
+
/** One staggered hero entry (a product logo, or the Hive mark itself). */
|
|
6
|
+
function HeroEntry({ src, delayMs, size, isAnchor, testId, }) {
|
|
7
|
+
return (_jsx("span", { "data-testid": testId, className: `hc-onboarding-anim${isAnchor ? " hc-onboarding-hero-anchor" : " hc-onboarding-hero-logo"}`, style: { display: "inline-flex", animationDelay: `${delayMs}ms` }, children: _jsx("img", { src: src, width: size, height: size, alt: "" }) }));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The first-run hero + the two-choice entry point. Renders unconditionally once the caller has
|
|
11
|
+
* already decided this IS a first-run (no detection re-derivation happens here, per ob-AC-2).
|
|
12
|
+
*/
|
|
13
|
+
export function OnboardingHero({ assetBase, onChooseStandard, onChooseAdvanced, staggerMs = 140 }) {
|
|
14
|
+
return (_jsxs("div", { "data-testid": "onboarding-hero", style: {
|
|
15
|
+
display: "flex",
|
|
16
|
+
alignItems: "center",
|
|
17
|
+
justifyContent: "center",
|
|
18
|
+
minHeight: "100vh",
|
|
19
|
+
padding: 32,
|
|
20
|
+
background: "var(--bg-canvas)",
|
|
21
|
+
textAlign: "center",
|
|
22
|
+
}, children: [_jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", gap: 36, width: "100%", maxWidth: 620 }, children: [_jsxs("div", { "data-testid": "onboarding-hero-marks", style: { display: "flex", alignItems: "center", justifyContent: "center", gap: 20 }, children: [HERO_PRODUCT_MARKS.map((product, i) => (_jsx(HeroEntry, { testId: `onboarding-hero-logo-${product}`, src: productLogoUrl(product, assetBase), delayMs: i * staggerMs, size: 44, isAnchor: false }, product))), _jsx(HeroEntry, { testId: "onboarding-hero-mark", src: productLogoUrl("hive", assetBase), delayMs: HERO_PRODUCT_MARKS.length * staggerMs, size: 56, isAnchor: true })] }), _jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [_jsx("h1", { style: { fontSize: "var(--text-3xl)", fontWeight: 700, color: "var(--text-primary)", margin: 0, letterSpacing: "-0.02em" }, children: "Welcome to the hive" }), _jsx("p", { style: { fontFamily: "var(--font-sans)", fontSize: "var(--text-base)", color: "var(--text-secondary)", margin: 0 }, children: "Let\u2019s get the rest of your fleet running." })] }), _jsxs("div", { "data-testid": "onboarding-choices", className: "hc-onboarding-anim", style: {
|
|
23
|
+
animationDelay: `${(HERO_PRODUCT_MARKS.length + 1) * staggerMs}ms`,
|
|
24
|
+
display: "flex",
|
|
25
|
+
gap: 16,
|
|
26
|
+
flexWrap: "wrap",
|
|
27
|
+
justifyContent: "center",
|
|
28
|
+
}, children: [_jsxs("button", { type: "button", "data-testid": "onboarding-standard-button", onClick: onChooseStandard, style: choiceButtonStyle("primary"), children: [_jsx("span", { style: { fontWeight: 700 }, children: "Standard User" }), _jsx("span", { style: choiceSubtextStyle("primary"), children: "Install the fleet (recommended)" })] }), _jsxs("button", { type: "button", "data-testid": "onboarding-advanced-button", onClick: onChooseAdvanced, style: choiceButtonStyle("secondary"), children: [_jsx("span", { style: { fontWeight: 700 }, children: "Advanced User" }), _jsx("span", { style: choiceSubtextStyle("secondary"), children: "Custom installation" })] })] })] }), _jsx("style", { children: [
|
|
29
|
+
// SAFE default: a plain opacity fade, no transform, this IS the reduced-motion form.
|
|
30
|
+
".hc-onboarding-anim { opacity: 0; animation: hc-onboarding-fade var(--dur-slow) var(--ease-out) both; }",
|
|
31
|
+
"@keyframes hc-onboarding-fade { from { opacity: 0 } to { opacity: 1 } }",
|
|
32
|
+
// Motion allowed: swap in the richer rise-and-settle keyframes, staggered by the
|
|
33
|
+
// inline `animationDelay` each entry already carries.
|
|
34
|
+
"@media (prefers-reduced-motion: no-preference) {",
|
|
35
|
+
" .hc-onboarding-hero-logo { animation-name: hc-onboarding-rise; animation-duration: 620ms; }",
|
|
36
|
+
// The Hive mark settling last gets a spring-like overshoot curve, distinct from the
|
|
37
|
+
// plain ease-out the product logos use, so it visually "settles" rather than just arriving.
|
|
38
|
+
" .hc-onboarding-hero-anchor { animation-name: hc-onboarding-settle; animation-duration: 760ms; animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1); }",
|
|
39
|
+
"}",
|
|
40
|
+
"@keyframes hc-onboarding-rise { from { opacity: 0; transform: translateY(18px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } }",
|
|
41
|
+
"@keyframes hc-onboarding-settle { from { opacity: 0; transform: translateY(24px) scale(0.7); } to { opacity: 1; transform: translateY(0) scale(1); } }",
|
|
42
|
+
].join("\n") })] }));
|
|
43
|
+
}
|
|
44
|
+
function choiceButtonStyle(variant) {
|
|
45
|
+
return {
|
|
46
|
+
display: "flex",
|
|
47
|
+
flexDirection: "column",
|
|
48
|
+
alignItems: "flex-start",
|
|
49
|
+
gap: 4,
|
|
50
|
+
minWidth: 220,
|
|
51
|
+
padding: "16px 22px",
|
|
52
|
+
borderRadius: "var(--radius-lg)",
|
|
53
|
+
border: variant === "primary" ? "1px solid transparent" : "1px solid var(--border-strong)",
|
|
54
|
+
background: variant === "primary" ? "var(--honey)" : "var(--bg-elevated)",
|
|
55
|
+
color: variant === "primary" ? "var(--honey-on)" : "var(--text-primary)",
|
|
56
|
+
cursor: "pointer",
|
|
57
|
+
fontFamily: "var(--font-sans)",
|
|
58
|
+
fontSize: "var(--text-base)",
|
|
59
|
+
textAlign: "left",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function choiceSubtextStyle(variant) {
|
|
63
|
+
return {
|
|
64
|
+
fontSize: "var(--text-xs)",
|
|
65
|
+
fontWeight: 500,
|
|
66
|
+
color: variant === "primary" ? "var(--honey-on)" : "var(--text-secondary)",
|
|
67
|
+
opacity: variant === "primary" ? 0.85 : 1,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=onboarding-hero.js.map
|