@scenar/cli 0.3.0 → 0.5.0

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.
Files changed (107) hide show
  1. package/example-bundle/assets/index-BU7trbqC.js +147 -0
  2. package/example-bundle/assets/style-COSwl6WP.css +1 -0
  3. package/example-bundle/index.html +6 -2
  4. package/example-bundle/pack-manifest.json +8 -8
  5. package/package.json +4 -9
  6. package/src/__tests__/embed-snippet.test.ts +1 -1
  7. package/src/__tests__/install-command.test.ts +97 -0
  8. package/src/__tests__/local-url.test.ts +17 -0
  9. package/src/__tests__/pack-generate-embed-entry.test.ts +22 -0
  10. package/src/__tests__/run-install.test.ts +222 -0
  11. package/src/__tests__/scenario-validator.test.ts +0 -1
  12. package/src/api.d.ts +3 -1
  13. package/src/api.d.ts.map +1 -1
  14. package/src/api.js +3 -1
  15. package/src/api.js.map +1 -1
  16. package/src/api.ts +10 -1
  17. package/src/commands/install.d.ts +3 -0
  18. package/src/commands/install.d.ts.map +1 -0
  19. package/src/commands/install.js +67 -0
  20. package/src/commands/install.js.map +1 -0
  21. package/src/commands/install.ts +89 -0
  22. package/src/commands/pack.js +2 -2
  23. package/src/commands/pack.js.map +1 -1
  24. package/src/commands/pack.ts +2 -2
  25. package/src/commands/serve.d.ts.map +1 -1
  26. package/src/commands/serve.js +1 -2
  27. package/src/commands/serve.js.map +1 -1
  28. package/src/commands/serve.ts +1 -2
  29. package/src/commands/try.js +1 -1
  30. package/src/commands/try.ts +1 -1
  31. package/src/embed/embed-snippet.d.ts.map +1 -0
  32. package/src/embed/embed-snippet.js.map +1 -0
  33. package/src/index.d.ts.map +1 -1
  34. package/src/index.js +3 -5
  35. package/src/index.js.map +1 -1
  36. package/src/index.ts +3 -5
  37. package/src/install/run-install.d.ts +72 -0
  38. package/src/install/run-install.d.ts.map +1 -0
  39. package/src/install/run-install.js +378 -0
  40. package/src/install/run-install.js.map +1 -0
  41. package/src/install/run-install.ts +461 -0
  42. package/src/install/scaffold.d.ts +29 -0
  43. package/src/install/scaffold.d.ts.map +1 -0
  44. package/src/install/scaffold.js +231 -0
  45. package/src/install/scaffold.js.map +1 -0
  46. package/src/install/scaffold.ts +253 -0
  47. package/src/pack/generate-embed-entry.d.ts +11 -0
  48. package/src/pack/generate-embed-entry.d.ts.map +1 -1
  49. package/src/pack/generate-embed-entry.js +34 -0
  50. package/src/pack/generate-embed-entry.js.map +1 -1
  51. package/src/pack/generate-embed-entry.ts +35 -0
  52. package/src/pack/pack-manifest.d.ts +1 -1
  53. package/src/pack/pack-manifest.d.ts.map +1 -1
  54. package/src/pack/pack-manifest.ts +1 -1
  55. package/src/publish/publish-flow.d.ts +3 -3
  56. package/src/publish/publish-flow.js +3 -3
  57. package/src/publish/publish-flow.ts +3 -3
  58. package/src/publish/run-publish.js +1 -1
  59. package/src/publish/run-publish.js.map +1 -1
  60. package/src/publish/run-publish.ts +1 -1
  61. package/src/render/detect-render-export.d.ts +2 -2
  62. package/src/render/detect-render-export.js +2 -2
  63. package/src/render/detect-render-export.ts +2 -2
  64. package/src/serve/local-url.d.ts +8 -0
  65. package/src/serve/local-url.d.ts.map +1 -0
  66. package/src/serve/local-url.js +21 -0
  67. package/src/serve/local-url.js.map +1 -0
  68. package/src/serve/local-url.ts +20 -0
  69. package/src/serve/run-serve.js +2 -2
  70. package/src/serve/run-serve.js.map +1 -1
  71. package/src/serve/run-serve.ts +2 -2
  72. package/tsconfig.tsbuildinfo +1 -1
  73. package/example-bundle/assets/index-BLQ14SEo.js +0 -147
  74. package/example-bundle/assets/style-Dnzx_jUO.css +0 -1
  75. package/src/__tests__/deploy-command.test.ts +0 -45
  76. package/src/__tests__/deploy-flow.test.ts +0 -125
  77. package/src/__tests__/deploy-upload.test.ts +0 -39
  78. package/src/commands/deploy.d.ts +0 -3
  79. package/src/commands/deploy.d.ts.map +0 -1
  80. package/src/commands/deploy.js +0 -150
  81. package/src/commands/deploy.js.map +0 -1
  82. package/src/commands/deploy.ts +0 -186
  83. package/src/commands/preview.d.ts +0 -3
  84. package/src/commands/preview.d.ts.map +0 -1
  85. package/src/commands/preview.js +0 -93
  86. package/src/commands/preview.js.map +0 -1
  87. package/src/commands/preview.ts +0 -138
  88. package/src/deploy/client.d.ts +0 -20
  89. package/src/deploy/client.d.ts.map +0 -1
  90. package/src/deploy/client.js +0 -22
  91. package/src/deploy/client.js.map +0 -1
  92. package/src/deploy/client.ts +0 -28
  93. package/src/deploy/deploy-flow.d.ts +0 -78
  94. package/src/deploy/deploy-flow.d.ts.map +0 -1
  95. package/src/deploy/deploy-flow.js +0 -53
  96. package/src/deploy/deploy-flow.js.map +0 -1
  97. package/src/deploy/deploy-flow.ts +0 -115
  98. package/src/deploy/embed-snippet.d.ts.map +0 -1
  99. package/src/deploy/embed-snippet.js.map +0 -1
  100. package/src/deploy/upload.d.ts +0 -10
  101. package/src/deploy/upload.d.ts.map +0 -1
  102. package/src/deploy/upload.js +0 -23
  103. package/src/deploy/upload.js.map +0 -1
  104. package/src/deploy/upload.ts +0 -29
  105. /package/src/{deploy → embed}/embed-snippet.d.ts +0 -0
  106. /package/src/{deploy → embed}/embed-snippet.js +0 -0
  107. /package/src/{deploy → embed}/embed-snippet.ts +0 -0
@@ -0,0 +1,231 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ /**
4
+ * Base dependencies every Scenar demos project needs to author tours. The
5
+ * starter tour is built from `@scenar/react` shells, so `@scenar/react` is
6
+ * recorded as `"*"` (resolves to the latest published — pin it once you know
7
+ * the version you want). React is constrained to the major the `@scenar/*`
8
+ * packages target so the embed and the user's real components (added later)
9
+ * share a single React.
10
+ */
11
+ export const BASE_DEPENDENCIES = {
12
+ "@scenar/react": "*",
13
+ react: "^19.0.0",
14
+ "react-dom": "^19.0.0",
15
+ };
16
+ /**
17
+ * Scaffold a fresh Scenar demos project in `dir`.
18
+ *
19
+ * Creates a minimal `package.json`, a `tsconfig.json` tuned for JSX, a
20
+ * `.gitignore` that ignores build output, and a runnable **starter tour** under
21
+ * `tours/example-tour/` (a `steps.ts` timeline, an `index.tsx` that exports
22
+ * `renderStep`, and a `.scenar/providers.tsx`). The starter is built from
23
+ * `@scenar/react` shells so `scenar pack` works immediately; the author then
24
+ * swaps in their real components and wires data in `providers.tsx`.
25
+ *
26
+ * Existing files are never overwritten — this only fills in what is missing, so
27
+ * it is safe to re-run.
28
+ */
29
+ export function scaffoldProject(dir) {
30
+ const created = [];
31
+ writeIfMissing(dir, "package.json", renderPackageJson(path.basename(dir)), created);
32
+ writeIfMissing(dir, "tsconfig.json", TSCONFIG, created);
33
+ writeIfMissing(dir, ".gitignore", GITIGNORE, created);
34
+ writeIfMissing(dir, path.join(TOUR_DIR, "steps.ts"), TOUR_STEPS, created);
35
+ writeIfMissing(dir, path.join(TOUR_DIR, "index.tsx"), TOUR_INDEX, created);
36
+ writeIfMissing(dir, path.join(TOUR_DIR, ".scenar", "providers.tsx"), TOUR_PROVIDERS, created);
37
+ return { created };
38
+ }
39
+ function writeIfMissing(dir, relPath, content, created) {
40
+ const target = path.join(dir, relPath);
41
+ if (fs.existsSync(target))
42
+ return;
43
+ fs.mkdirSync(path.dirname(target), { recursive: true });
44
+ fs.writeFileSync(target, content, "utf-8");
45
+ created.push(relPath);
46
+ }
47
+ function renderPackageJson(name) {
48
+ const pkg = {
49
+ name: toPackageName(name),
50
+ version: "0.0.0",
51
+ private: true,
52
+ type: "module",
53
+ scripts: {
54
+ pack: `scenar pack ${TOUR_DIR}`,
55
+ },
56
+ dependencies: { ...BASE_DEPENDENCIES },
57
+ };
58
+ return JSON.stringify(pkg, null, 2) + "\n";
59
+ }
60
+ /** Coerce a directory name into a valid npm package name. */
61
+ function toPackageName(dirName) {
62
+ const slug = dirName
63
+ .trim()
64
+ .toLowerCase()
65
+ .replace(/[^a-z0-9._-]+/g, "-")
66
+ .replace(/^-+|-+$/g, "");
67
+ return slug.length > 0 ? slug : "scenar-demos";
68
+ }
69
+ /** Where the starter tour is scaffolded (also the default `pack` target). */
70
+ const TOUR_DIR = "tours/example-tour";
71
+ const TSCONFIG = `{
72
+ "compilerOptions": {
73
+ "target": "ES2022",
74
+ "module": "ESNext",
75
+ "moduleResolution": "Bundler",
76
+ "jsx": "react-jsx",
77
+ "strict": true,
78
+ "skipLibCheck": true,
79
+ "esModuleInterop": true,
80
+ "lib": ["ES2022", "DOM", "DOM.Iterable"]
81
+ },
82
+ "include": ["tours"]
83
+ }
84
+ `;
85
+ const GITIGNORE = `node_modules/
86
+
87
+ # Packed embeds — regenerate with \`scenar pack\`
88
+ *-bundle/
89
+ `;
90
+ const TOUR_STEPS = `import type { ScenarioStep } from "@scenar/core";
91
+
92
+ /** The screen shown at a given step (maps to a view in index.tsx). */
93
+ export type ExampleScreen = "login" | "dashboard";
94
+
95
+ /** Data snapshot for one step of the tour. */
96
+ export interface ExampleStepData {
97
+ /** Address-bar URL shown in the browser chrome (no scheme). */
98
+ readonly url: string;
99
+ /** Which screen is shown at this step. */
100
+ readonly screen: ExampleScreen;
101
+ }
102
+
103
+ /**
104
+ * The tour timeline. \`delayMs\` is the dwell before each step (the first is
105
+ * shown immediately); \`narration\` is the spoken script synthesized by
106
+ * \`scenar narrate\`.
107
+ */
108
+ export const steps: ScenarioStep<ExampleStepData>[] = [
109
+ {
110
+ delayMs: 0,
111
+ narration: "Sign in to your workspace.",
112
+ data: { url: "app.example.com/login", screen: "login" },
113
+ },
114
+ {
115
+ delayMs: 2400,
116
+ narration: "And you land on your dashboard.",
117
+ data: { url: "app.example.com/home", screen: "dashboard" },
118
+ },
119
+ ];
120
+ `;
121
+ const TOUR_INDEX = `import type { ReactNode } from "react";
122
+ import {
123
+ BrowserView,
124
+ DashboardPage,
125
+ LoginCardPage,
126
+ type SideNavItem,
127
+ } from "@scenar/react";
128
+ import type { ExampleStepData } from "./steps";
129
+
130
+ /**
131
+ * renderStep: a pure (data, stepIndex) -> ReactNode. Both \`scenar pack\` (embed)
132
+ * and \`scenar render\` (video) consume this contract.
133
+ *
134
+ * This starter draws CSS-only @scenar/react shells so it packs with no extra
135
+ * setup. To show your REAL product UI, install your components
136
+ * (\`scenar install <your-pkg>\`), import and render them here, and wire any data
137
+ * they fetch in .scenar/providers.tsx.
138
+ */
139
+ export function renderStep(data: ExampleStepData, stepIndex: number): ReactNode {
140
+ return (
141
+ <BrowserView url={"https://" + data.url} contentKey={String(stepIndex)}>
142
+ {data.screen === "login" ? <LoginScreen /> : <DashboardScreen />}
143
+ </BrowserView>
144
+ );
145
+ }
146
+
147
+ function LoginScreen(): ReactNode {
148
+ return (
149
+ <LoginCardPage
150
+ appName="Example"
151
+ subtitle="Sign in to your workspace"
152
+ fields={[
153
+ { label: "Email", value: "you@example.com", type: "email" },
154
+ { label: "Password", type: "password" },
155
+ ]}
156
+ submitLabel="Sign in"
157
+ />
158
+ );
159
+ }
160
+
161
+ const SIDEBAR: SideNavItem[] = [
162
+ { label: "Workspace", isSection: true },
163
+ { label: "Overview", active: true },
164
+ { label: "Projects" },
165
+ ];
166
+
167
+ function DashboardScreen(): ReactNode {
168
+ return (
169
+ <DashboardPage
170
+ appName="Example"
171
+ userName="You"
172
+ navLinks={[{ label: "Home", active: true }]}
173
+ sidebarItems={SIDEBAR}
174
+ >
175
+ <div style={{ padding: 20 }}>
176
+ <h2
177
+ style={{
178
+ margin: 0,
179
+ fontSize: 15,
180
+ fontWeight: 600,
181
+ color: "var(--scenar-foreground)",
182
+ }}
183
+ >
184
+ Welcome back
185
+ </h2>
186
+ </div>
187
+ </DashboardPage>
188
+ );
189
+ }
190
+ `;
191
+ const TOUR_PROVIDERS = `/**
192
+ * .scenar/providers.tsx — wraps every step of THIS tour in \`scenar pack\` (embed)
193
+ * and \`scenar render\` (video).
194
+ *
195
+ * You only need this when your tour renders real, data-fetching components that
196
+ * need a client/provider to render in isolation (no backend). A tour built only
197
+ * from @scenar/react shells (like the starter) can leave this as a pass-through
198
+ * or delete it.
199
+ *
200
+ * Recommended pattern for Connect-RPC SDKs (e.g. @stigmer/react): mock data with
201
+ * an in-process router transport — no network, no service worker — and resolve
202
+ * the embed's light/dark mode from \`getEmbedColorMode()\`. Fill in your client,
203
+ * provider, and fixtures, then return the provider instead of the fragment:
204
+ *
205
+ * import "@your-org/react/styles.css";
206
+ * import { createRouterTransport } from "@connectrpc/connect";
207
+ * import { getEmbedColorMode } from "@scenar/react";
208
+ * import { YourClient } from "@your-org/sdk";
209
+ * import { YourProvider } from "@your-org/react";
210
+ * import { YourController } from "@your-org/protos/...";
211
+ *
212
+ * const transport = createRouterTransport((router) => {
213
+ * router.service(YourController, { get: () => yourFixtureObject });
214
+ * });
215
+ * const client = new YourClient({ baseUrl: "/", customTransport: transport });
216
+ *
217
+ * export function PreviewProviders({ children }: { readonly children: ReactNode }) {
218
+ * return (
219
+ * <YourProvider client={client} colorMode={getEmbedColorMode()}>
220
+ * {children}
221
+ * </YourProvider>
222
+ * );
223
+ * }
224
+ */
225
+ import type { ReactNode } from "react";
226
+
227
+ export function PreviewProviders({ children }: { readonly children: ReactNode }) {
228
+ return <>{children}</>;
229
+ }
230
+ `;
231
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../../../src/install/scaffold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,253 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+
4
+ /**
5
+ * Base dependencies every Scenar demos project needs to author tours. The
6
+ * starter tour is built from `@scenar/react` shells, so `@scenar/react` is
7
+ * recorded as `"*"` (resolves to the latest published — pin it once you know
8
+ * the version you want). React is constrained to the major the `@scenar/*`
9
+ * packages target so the embed and the user's real components (added later)
10
+ * share a single React.
11
+ */
12
+ export const BASE_DEPENDENCIES: Readonly<Record<string, string>> = {
13
+ "@scenar/react": "*",
14
+ react: "^19.0.0",
15
+ "react-dom": "^19.0.0",
16
+ };
17
+
18
+ /** Result of scaffolding a fresh demos project. */
19
+ export interface ScaffoldResult {
20
+ /** Files created, relative to the project directory. */
21
+ readonly created: readonly string[];
22
+ }
23
+
24
+ /**
25
+ * Scaffold a fresh Scenar demos project in `dir`.
26
+ *
27
+ * Creates a minimal `package.json`, a `tsconfig.json` tuned for JSX, a
28
+ * `.gitignore` that ignores build output, and a runnable **starter tour** under
29
+ * `tours/example-tour/` (a `steps.ts` timeline, an `index.tsx` that exports
30
+ * `renderStep`, and a `.scenar/providers.tsx`). The starter is built from
31
+ * `@scenar/react` shells so `scenar pack` works immediately; the author then
32
+ * swaps in their real components and wires data in `providers.tsx`.
33
+ *
34
+ * Existing files are never overwritten — this only fills in what is missing, so
35
+ * it is safe to re-run.
36
+ */
37
+ export function scaffoldProject(dir: string): ScaffoldResult {
38
+ const created: string[] = [];
39
+
40
+ writeIfMissing(dir, "package.json", renderPackageJson(path.basename(dir)), created);
41
+ writeIfMissing(dir, "tsconfig.json", TSCONFIG, created);
42
+ writeIfMissing(dir, ".gitignore", GITIGNORE, created);
43
+ writeIfMissing(dir, path.join(TOUR_DIR, "steps.ts"), TOUR_STEPS, created);
44
+ writeIfMissing(dir, path.join(TOUR_DIR, "index.tsx"), TOUR_INDEX, created);
45
+ writeIfMissing(dir, path.join(TOUR_DIR, ".scenar", "providers.tsx"), TOUR_PROVIDERS, created);
46
+
47
+ return { created };
48
+ }
49
+
50
+ function writeIfMissing(
51
+ dir: string,
52
+ relPath: string,
53
+ content: string,
54
+ created: string[],
55
+ ): void {
56
+ const target = path.join(dir, relPath);
57
+ if (fs.existsSync(target)) return;
58
+ fs.mkdirSync(path.dirname(target), { recursive: true });
59
+ fs.writeFileSync(target, content, "utf-8");
60
+ created.push(relPath);
61
+ }
62
+
63
+ function renderPackageJson(name: string): string {
64
+ const pkg = {
65
+ name: toPackageName(name),
66
+ version: "0.0.0",
67
+ private: true,
68
+ type: "module",
69
+ scripts: {
70
+ pack: `scenar pack ${TOUR_DIR}`,
71
+ },
72
+ dependencies: { ...BASE_DEPENDENCIES },
73
+ };
74
+ return JSON.stringify(pkg, null, 2) + "\n";
75
+ }
76
+
77
+ /** Coerce a directory name into a valid npm package name. */
78
+ function toPackageName(dirName: string): string {
79
+ const slug = dirName
80
+ .trim()
81
+ .toLowerCase()
82
+ .replace(/[^a-z0-9._-]+/g, "-")
83
+ .replace(/^-+|-+$/g, "");
84
+ return slug.length > 0 ? slug : "scenar-demos";
85
+ }
86
+
87
+ /** Where the starter tour is scaffolded (also the default `pack` target). */
88
+ const TOUR_DIR = "tours/example-tour";
89
+
90
+ const TSCONFIG = `{
91
+ "compilerOptions": {
92
+ "target": "ES2022",
93
+ "module": "ESNext",
94
+ "moduleResolution": "Bundler",
95
+ "jsx": "react-jsx",
96
+ "strict": true,
97
+ "skipLibCheck": true,
98
+ "esModuleInterop": true,
99
+ "lib": ["ES2022", "DOM", "DOM.Iterable"]
100
+ },
101
+ "include": ["tours"]
102
+ }
103
+ `;
104
+
105
+ const GITIGNORE = `node_modules/
106
+
107
+ # Packed embeds — regenerate with \`scenar pack\`
108
+ *-bundle/
109
+ `;
110
+
111
+ const TOUR_STEPS = `import type { ScenarioStep } from "@scenar/core";
112
+
113
+ /** The screen shown at a given step (maps to a view in index.tsx). */
114
+ export type ExampleScreen = "login" | "dashboard";
115
+
116
+ /** Data snapshot for one step of the tour. */
117
+ export interface ExampleStepData {
118
+ /** Address-bar URL shown in the browser chrome (no scheme). */
119
+ readonly url: string;
120
+ /** Which screen is shown at this step. */
121
+ readonly screen: ExampleScreen;
122
+ }
123
+
124
+ /**
125
+ * The tour timeline. \`delayMs\` is the dwell before each step (the first is
126
+ * shown immediately); \`narration\` is the spoken script synthesized by
127
+ * \`scenar narrate\`.
128
+ */
129
+ export const steps: ScenarioStep<ExampleStepData>[] = [
130
+ {
131
+ delayMs: 0,
132
+ narration: "Sign in to your workspace.",
133
+ data: { url: "app.example.com/login", screen: "login" },
134
+ },
135
+ {
136
+ delayMs: 2400,
137
+ narration: "And you land on your dashboard.",
138
+ data: { url: "app.example.com/home", screen: "dashboard" },
139
+ },
140
+ ];
141
+ `;
142
+
143
+ const TOUR_INDEX = `import type { ReactNode } from "react";
144
+ import {
145
+ BrowserView,
146
+ DashboardPage,
147
+ LoginCardPage,
148
+ type SideNavItem,
149
+ } from "@scenar/react";
150
+ import type { ExampleStepData } from "./steps";
151
+
152
+ /**
153
+ * renderStep: a pure (data, stepIndex) -> ReactNode. Both \`scenar pack\` (embed)
154
+ * and \`scenar render\` (video) consume this contract.
155
+ *
156
+ * This starter draws CSS-only @scenar/react shells so it packs with no extra
157
+ * setup. To show your REAL product UI, install your components
158
+ * (\`scenar install <your-pkg>\`), import and render them here, and wire any data
159
+ * they fetch in .scenar/providers.tsx.
160
+ */
161
+ export function renderStep(data: ExampleStepData, stepIndex: number): ReactNode {
162
+ return (
163
+ <BrowserView url={"https://" + data.url} contentKey={String(stepIndex)}>
164
+ {data.screen === "login" ? <LoginScreen /> : <DashboardScreen />}
165
+ </BrowserView>
166
+ );
167
+ }
168
+
169
+ function LoginScreen(): ReactNode {
170
+ return (
171
+ <LoginCardPage
172
+ appName="Example"
173
+ subtitle="Sign in to your workspace"
174
+ fields={[
175
+ { label: "Email", value: "you@example.com", type: "email" },
176
+ { label: "Password", type: "password" },
177
+ ]}
178
+ submitLabel="Sign in"
179
+ />
180
+ );
181
+ }
182
+
183
+ const SIDEBAR: SideNavItem[] = [
184
+ { label: "Workspace", isSection: true },
185
+ { label: "Overview", active: true },
186
+ { label: "Projects" },
187
+ ];
188
+
189
+ function DashboardScreen(): ReactNode {
190
+ return (
191
+ <DashboardPage
192
+ appName="Example"
193
+ userName="You"
194
+ navLinks={[{ label: "Home", active: true }]}
195
+ sidebarItems={SIDEBAR}
196
+ >
197
+ <div style={{ padding: 20 }}>
198
+ <h2
199
+ style={{
200
+ margin: 0,
201
+ fontSize: 15,
202
+ fontWeight: 600,
203
+ color: "var(--scenar-foreground)",
204
+ }}
205
+ >
206
+ Welcome back
207
+ </h2>
208
+ </div>
209
+ </DashboardPage>
210
+ );
211
+ }
212
+ `;
213
+
214
+ const TOUR_PROVIDERS = `/**
215
+ * .scenar/providers.tsx — wraps every step of THIS tour in \`scenar pack\` (embed)
216
+ * and \`scenar render\` (video).
217
+ *
218
+ * You only need this when your tour renders real, data-fetching components that
219
+ * need a client/provider to render in isolation (no backend). A tour built only
220
+ * from @scenar/react shells (like the starter) can leave this as a pass-through
221
+ * or delete it.
222
+ *
223
+ * Recommended pattern for Connect-RPC SDKs (e.g. @stigmer/react): mock data with
224
+ * an in-process router transport — no network, no service worker — and resolve
225
+ * the embed's light/dark mode from \`getEmbedColorMode()\`. Fill in your client,
226
+ * provider, and fixtures, then return the provider instead of the fragment:
227
+ *
228
+ * import "@your-org/react/styles.css";
229
+ * import { createRouterTransport } from "@connectrpc/connect";
230
+ * import { getEmbedColorMode } from "@scenar/react";
231
+ * import { YourClient } from "@your-org/sdk";
232
+ * import { YourProvider } from "@your-org/react";
233
+ * import { YourController } from "@your-org/protos/...";
234
+ *
235
+ * const transport = createRouterTransport((router) => {
236
+ * router.service(YourController, { get: () => yourFixtureObject });
237
+ * });
238
+ * const client = new YourClient({ baseUrl: "/", customTransport: transport });
239
+ *
240
+ * export function PreviewProviders({ children }: { readonly children: ReactNode }) {
241
+ * return (
242
+ * <YourProvider client={client} colorMode={getEmbedColorMode()}>
243
+ * {children}
244
+ * </YourProvider>
245
+ * );
246
+ * }
247
+ */
248
+ import type { ReactNode } from "react";
249
+
250
+ export function PreviewProviders({ children }: { readonly children: ReactNode }) {
251
+ return <>{children}</>;
252
+ }
253
+ `;
@@ -48,6 +48,17 @@ export declare function generateEmbedEntry(input: EmbedEntryInput): string;
48
48
  * Produce the index.html that boots the embed. The entry is referenced as an
49
49
  * external module script (`<script type="module" src>`) — never inline — so the
50
50
  * server CSP (`script-src 'self'`) is satisfied without loosening.
51
+ *
52
+ * Canvas contract: the page declares `color-scheme: light dark` and keeps
53
+ * html/body transparent. When the embed is framed by a same-origin host (the
54
+ * shipped stigmer.ai layout), the browser propagates the host's color scheme
55
+ * into the frame; because the embed supports both schemes they always match,
56
+ * so the iframe canvas stays *transparent* — every pixel the tour doesn't
57
+ * paint shows the host page's own background, in both themes, even before the
58
+ * entry script loads. (A scheme mismatch is what makes browsers force an
59
+ * opaque white canvas — the historical "white band under the embed".) The
60
+ * margin reset guards against a host stylesheet-less load; the entry paints a
61
+ * theme surface only when the page is top-level (see generateEmbedEntry).
51
62
  */
52
63
  export declare function generateEmbedHtml(scenarioId: string, entryFileName: string): string;
53
64
  //# sourceMappingURL=generate-embed-entry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"generate-embed-entry.d.ts","sourceRoot":"","sources":["../../../src/pack/generate-embed-entry.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAiJjE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAgBnF"}
1
+ {"version":3,"file":"generate-embed-entry.d.ts","sourceRoot":"","sources":["../../../src/pack/generate-embed-entry.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,YAAY,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,CAqKjE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAoBnF"}
@@ -88,6 +88,25 @@ export function generateEmbedEntry(input) {
88
88
  lines.push(` catch { return null; }`);
89
89
  lines.push(`})();`);
90
90
  lines.push(`const _rootClass = _theme === "dark" ? SCENAR_CLASS + " dark" : SCENAR_CLASS;`);
91
+ // --- Page canvas: transparent when framed, theme surface when top-level ---
92
+ // The static HTML keeps html/body transparent (see generateEmbedHtml): a
93
+ // framed embed's canvas then shows the *host page's* background through any
94
+ // pixel the tour doesn't paint (aspect-ratio rounding slivers, load-in), so
95
+ // an embed can never flash a white band on a dark docs page. Do NOT pin
96
+ // `document.documentElement.style.colorScheme` to the resolved theme here —
97
+ // the `light dark` meta must stay authoritative, because a scheme mismatch
98
+ // with the framing document is exactly what makes browsers force an opaque
99
+ // white canvas. Top-level (scenar serve verification, direct bundle links)
100
+ // has no host background to show through, so paint the theme surface token
101
+ // for an intentional-looking page instead of the browser's default white.
102
+ // The body gets the scenar root class so `--scenar-surface` resolves there —
103
+ // referencing the token keeps this in lockstep with the palette instead of
104
+ // duplicating hex values that would drift.
105
+ lines.push(``);
106
+ lines.push(`if (window.parent === window) {`);
107
+ lines.push(` document.body.classList.add(..._rootClass.split(" "));`);
108
+ lines.push(` document.body.style.background = "var(--scenar-surface)";`);
109
+ lines.push(`}`);
91
110
  // --- App tree ---
92
111
  // The structure mirrors the in-app demo wiring exactly (see DemoViewport's
93
112
  // contract and ViewportTransformLayer's "Cursor must be a sibling" invariant):
@@ -162,6 +181,17 @@ export function generateEmbedEntry(input) {
162
181
  * Produce the index.html that boots the embed. The entry is referenced as an
163
182
  * external module script (`<script type="module" src>`) — never inline — so the
164
183
  * server CSP (`script-src 'self'`) is satisfied without loosening.
184
+ *
185
+ * Canvas contract: the page declares `color-scheme: light dark` and keeps
186
+ * html/body transparent. When the embed is framed by a same-origin host (the
187
+ * shipped stigmer.ai layout), the browser propagates the host's color scheme
188
+ * into the frame; because the embed supports both schemes they always match,
189
+ * so the iframe canvas stays *transparent* — every pixel the tour doesn't
190
+ * paint shows the host page's own background, in both themes, even before the
191
+ * entry script loads. (A scheme mismatch is what makes browsers force an
192
+ * opaque white canvas — the historical "white band under the embed".) The
193
+ * margin reset guards against a host stylesheet-less load; the entry paints a
194
+ * theme surface only when the page is top-level (see generateEmbedEntry).
165
195
  */
166
196
  export function generateEmbedHtml(scenarioId, entryFileName) {
167
197
  return [
@@ -170,7 +200,11 @@ export function generateEmbedHtml(scenarioId, entryFileName) {
170
200
  ` <head>`,
171
201
  ` <meta charset="utf-8" />`,
172
202
  ` <meta name="viewport" content="width=device-width, initial-scale=1" />`,
203
+ ` <meta name="color-scheme" content="light dark" />`,
173
204
  ` <title>${escapeHtml(scenarioId)}</title>`,
205
+ ` <style>`,
206
+ ` html, body { margin: 0; background: transparent; }`,
207
+ ` </style>`,
174
208
  ` </head>`,
175
209
  ` <body>`,
176
210
  ` <div id="root"></div>`,
@@ -1 +1 @@
1
- {"version":3,"file":"generate-embed-entry.js","sourceRoot":"","sources":["../../../src/pack/generate-embed-entry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generate-embed-entry.js","sourceRoot":"","sources":["../../../src/pack/generate-embed-entry.ts"],"names":[],"mappings":"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"}
@@ -120,6 +120,26 @@ export function generateEmbedEntry(input: EmbedEntryInput): string {
120
120
  lines.push(`})();`);
121
121
  lines.push(`const _rootClass = _theme === "dark" ? SCENAR_CLASS + " dark" : SCENAR_CLASS;`);
122
122
 
123
+ // --- Page canvas: transparent when framed, theme surface when top-level ---
124
+ // The static HTML keeps html/body transparent (see generateEmbedHtml): a
125
+ // framed embed's canvas then shows the *host page's* background through any
126
+ // pixel the tour doesn't paint (aspect-ratio rounding slivers, load-in), so
127
+ // an embed can never flash a white band on a dark docs page. Do NOT pin
128
+ // `document.documentElement.style.colorScheme` to the resolved theme here —
129
+ // the `light dark` meta must stay authoritative, because a scheme mismatch
130
+ // with the framing document is exactly what makes browsers force an opaque
131
+ // white canvas. Top-level (scenar serve verification, direct bundle links)
132
+ // has no host background to show through, so paint the theme surface token
133
+ // for an intentional-looking page instead of the browser's default white.
134
+ // The body gets the scenar root class so `--scenar-surface` resolves there —
135
+ // referencing the token keeps this in lockstep with the palette instead of
136
+ // duplicating hex values that would drift.
137
+ lines.push(``);
138
+ lines.push(`if (window.parent === window) {`);
139
+ lines.push(` document.body.classList.add(..._rootClass.split(" "));`);
140
+ lines.push(` document.body.style.background = "var(--scenar-surface)";`);
141
+ lines.push(`}`);
142
+
123
143
  // --- App tree ---
124
144
  // The structure mirrors the in-app demo wiring exactly (see DemoViewport's
125
145
  // contract and ViewportTransformLayer's "Cursor must be a sibling" invariant):
@@ -197,6 +217,17 @@ export function generateEmbedEntry(input: EmbedEntryInput): string {
197
217
  * Produce the index.html that boots the embed. The entry is referenced as an
198
218
  * external module script (`<script type="module" src>`) — never inline — so the
199
219
  * server CSP (`script-src 'self'`) is satisfied without loosening.
220
+ *
221
+ * Canvas contract: the page declares `color-scheme: light dark` and keeps
222
+ * html/body transparent. When the embed is framed by a same-origin host (the
223
+ * shipped stigmer.ai layout), the browser propagates the host's color scheme
224
+ * into the frame; because the embed supports both schemes they always match,
225
+ * so the iframe canvas stays *transparent* — every pixel the tour doesn't
226
+ * paint shows the host page's own background, in both themes, even before the
227
+ * entry script loads. (A scheme mismatch is what makes browsers force an
228
+ * opaque white canvas — the historical "white band under the embed".) The
229
+ * margin reset guards against a host stylesheet-less load; the entry paints a
230
+ * theme surface only when the page is top-level (see generateEmbedEntry).
200
231
  */
201
232
  export function generateEmbedHtml(scenarioId: string, entryFileName: string): string {
202
233
  return [
@@ -205,7 +236,11 @@ export function generateEmbedHtml(scenarioId: string, entryFileName: string): st
205
236
  ` <head>`,
206
237
  ` <meta charset="utf-8" />`,
207
238
  ` <meta name="viewport" content="width=device-width, initial-scale=1" />`,
239
+ ` <meta name="color-scheme" content="light dark" />`,
208
240
  ` <title>${escapeHtml(scenarioId)}</title>`,
241
+ ` <style>`,
242
+ ` html, body { margin: 0; background: transparent; }`,
243
+ ` </style>`,
209
244
  ` </head>`,
210
245
  ` <body>`,
211
246
  ` <div id="root"></div>`,
@@ -10,7 +10,7 @@ export interface PackFile {
10
10
  /** Content type the server binds into the presign and the edge serves. */
11
11
  readonly contentType: string;
12
12
  }
13
- /** The deploy instruction file `scenar deploy` consumes. */
13
+ /** The deploy instruction file the upload backend consumes. */
14
14
  export interface PackManifest {
15
15
  readonly schemaVersion: number;
16
16
  readonly scenarioId: string;