nextworks 0.2.0-alpha.13 → 0.2.0-alpha.14

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 (42) hide show
  1. package/README.md +3 -1
  2. package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +2 -0
  3. package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +2 -0
  4. package/dist/kits/blocks/app/templates/aiworkflow/PresetThemeVars.tsx +1 -58
  5. package/dist/kits/blocks/app/templates/aiworkflow/README.md +2 -0
  6. package/dist/kits/blocks/app/templates/aiworkflow/components/CTA.tsx +9 -9
  7. package/dist/kits/blocks/app/templates/aiworkflow/components/Contact.tsx +12 -13
  8. package/dist/kits/blocks/app/templates/aiworkflow/components/FAQ.tsx +22 -19
  9. package/dist/kits/blocks/app/templates/aiworkflow/components/FeatureMockups.tsx +562 -0
  10. package/dist/kits/blocks/app/templates/aiworkflow/components/Features.tsx +18 -16
  11. package/dist/kits/blocks/app/templates/aiworkflow/components/Footer.tsx +13 -9
  12. package/dist/kits/blocks/app/templates/aiworkflow/components/Hero.tsx +883 -636
  13. package/dist/kits/blocks/app/templates/aiworkflow/components/Navbar.tsx +14 -15
  14. package/dist/kits/blocks/app/templates/aiworkflow/components/Pricing.tsx +27 -22
  15. package/dist/kits/blocks/app/templates/aiworkflow/components/ProcessTimeline.tsx +20 -21
  16. package/dist/kits/blocks/app/templates/aiworkflow/components/Testimonials.tsx +17 -13
  17. package/dist/kits/blocks/app/templates/aiworkflow/components/TrustBadges.tsx +15 -12
  18. package/dist/kits/blocks/app/templates/aiworkflow/themes/animation.tsx +151 -0
  19. package/dist/kits/blocks/app/templates/aiworkflow/themes/default.tsx +158 -0
  20. package/dist/kits/blocks/app/templates/aiworkflow/themes/test.tsx +163 -0
  21. package/dist/kits/blocks/app/templates/gallery/PresetThemeVars.tsx +46 -0
  22. package/dist/kits/blocks/app/templates/gallery/page.tsx +550 -161
  23. package/dist/kits/blocks/components/sections/HeroProductDemo.tsx +74 -64
  24. package/dist/kits/blocks/components/sections/Navbar.tsx +2 -0
  25. package/dist/kits/blocks/components/sections/product-demo/ApprovalInboxPanel.tsx +16 -13
  26. package/dist/kits/blocks/components/sections/product-demo/DemoStage.tsx +283 -162
  27. package/dist/kits/blocks/components/sections/product-demo/DemoWindow.tsx +65 -53
  28. package/dist/kits/blocks/components/sections/product-demo/KnowledgePanel.tsx +20 -17
  29. package/dist/kits/blocks/components/sections/product-demo/RunConsolePanel.tsx +208 -127
  30. package/dist/kits/blocks/components/sections/product-demo/TaskListPanel.tsx +95 -0
  31. package/dist/kits/blocks/components/sections/product-demo/WorkflowStudioPanel.tsx +714 -161
  32. package/dist/kits/blocks/components/sections/product-demo/types.ts +69 -0
  33. package/dist/kits/blocks/components/ui/theme-selector.tsx +1 -1
  34. package/dist/kits/blocks/package-deps.json +3 -3
  35. package/dist/kits/blocks/public/placeholders/aiworkflow/live.svg +92 -0
  36. package/dist/kits/blocks/public/placeholders/aiworkflow/review.svg +80 -0
  37. package/dist/kits/blocks/public/placeholders/aiworkflow/task.svg +71 -0
  38. package/dist/kits/blocks/tsconfig.json +13 -0
  39. package/dist/utils/file-operations.d.ts.map +1 -1
  40. package/dist/utils/file-operations.js +6 -1
  41. package/dist/utils/file-operations.js.map +1 -1
  42. package/package.json +1 -1
@@ -4,17 +4,18 @@ import React from "react";
4
4
  import { motion } from "motion/react";
5
5
  import { cn } from "@/lib/utils";
6
6
  import { DemoWindow } from "./DemoWindow";
7
- import { ApprovalInboxPanel } from "./ApprovalInboxPanel";
8
7
  import { KnowledgePanel } from "./KnowledgePanel";
9
8
  import { RunConsolePanel } from "./RunConsolePanel";
9
+ import { TaskListPanel } from "./TaskListPanel";
10
10
  import { WorkflowStudioPanel } from "./WorkflowStudioPanel";
11
11
  import type {
12
12
  ProductDemoHighlightTarget,
13
13
  ProductDemoHighlightTone,
14
+ ProductDemoRunConsolePlaybackConfig,
14
15
  ProductDemoScenario,
15
- ProductDemoStatusTone,
16
16
  ProductDemoWindowKey,
17
17
  ProductDemoWindowMeta,
18
+ ProductDemoWorkflowPlaybackConfig,
18
19
  } from "./types";
19
20
 
20
21
  export interface DemoStageProps {
@@ -35,10 +36,10 @@ type WindowRenderData = {
35
36
  };
36
37
 
37
38
  const WINDOW_ORDER: ProductDemoWindowKey[] = [
39
+ "taskList",
38
40
  "workflowStudio",
39
- "knowledgePanel",
40
41
  "runConsole",
41
- "approvalInbox",
42
+ "knowledgePanel",
42
43
  ];
43
44
 
44
45
  const HIGHLIGHT_TONE_CLASSES: Record<ProductDemoHighlightTone, string> = {
@@ -53,16 +54,6 @@ const HIGHLIGHT_TONE_CLASSES: Record<ProductDemoHighlightTone, string> = {
53
54
  muted: "border-border/60 bg-background/70 text-muted-foreground",
54
55
  };
55
56
 
56
- const STATUS_TONE_CLASSES: Record<ProductDemoStatusTone, string> = {
57
- neutral: "border-border/60 bg-muted/55 text-muted-foreground",
58
- info: "border-sky-500/30 bg-sky-500/10 text-sky-600 dark:text-sky-300",
59
- success:
60
- "border-emerald-500/30 bg-emerald-500/10 text-emerald-600 dark:text-emerald-300",
61
- warning:
62
- "border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300",
63
- danger: "border-rose-500/30 bg-rose-500/10 text-rose-600 dark:text-rose-300",
64
- };
65
-
66
57
  function clampInitialScenarioIndex(index: number | undefined, count: number) {
67
58
  if (count <= 0) {
68
59
  return 0;
@@ -75,22 +66,14 @@ function getHighlightClass(tone: ProductDemoHighlightTone = "accent") {
75
66
  return HIGHLIGHT_TONE_CLASSES[tone];
76
67
  }
77
68
 
78
- function getStatusClass(tone: ProductDemoStatusTone = "neutral") {
79
- return STATUS_TONE_CLASSES[tone];
80
- }
81
-
82
69
  function useActiveScenario({
83
70
  scenarios,
84
71
  initialScenarioIndex,
85
72
  activeScenarioKey,
86
- autoCycle,
87
- cycleIntervalMs,
88
73
  }: {
89
74
  scenarios: ProductDemoScenario[];
90
75
  initialScenarioIndex?: number;
91
76
  activeScenarioKey?: string;
92
- autoCycle?: boolean;
93
- cycleIntervalMs?: number;
94
77
  }) {
95
78
  const fallbackIndex = React.useMemo(
96
79
  () => clampInitialScenarioIndex(initialScenarioIndex, scenarios.length),
@@ -117,20 +100,122 @@ function useActiveScenario({
117
100
  }
118
101
  }, [activeScenarioKey, controlledIndex]);
119
102
 
103
+ return {
104
+ activeScenario: scenarios[activeIndex] ?? scenarios[0],
105
+ activeIndex,
106
+ setActiveIndex: setInternalIndex,
107
+ };
108
+ }
109
+
110
+ type PlaybackTimelineConfig = {
111
+ stepCount: number;
112
+ playbackMs?: number;
113
+ playbackStepDurationsMs?: number[];
114
+ playbackResetDelayMs?: number;
115
+ scenarioKey: string;
116
+ };
117
+
118
+ function getWorkflowPlaybackConfig(
119
+ scenario: ProductDemoScenario,
120
+ ): ProductDemoWorkflowPlaybackConfig {
121
+ return {
122
+ playbackMs:
123
+ scenario.playback?.workflowStudio?.playbackMs ??
124
+ scenario.workflowStudio.playbackMs,
125
+ playbackStepDurationsMs:
126
+ scenario.playback?.workflowStudio?.playbackStepDurationsMs ??
127
+ scenario.workflowStudio.playbackStepDurationsMs,
128
+ playbackResetDelayMs:
129
+ scenario.playback?.workflowStudio?.playbackResetDelayMs ??
130
+ scenario.workflowStudio.playbackResetDelayMs,
131
+ };
132
+ }
133
+
134
+ function getRunConsolePlaybackConfig(
135
+ scenario: ProductDemoScenario,
136
+ ): ProductDemoRunConsolePlaybackConfig {
137
+ return {
138
+ playbackMs:
139
+ scenario.playback?.runConsole?.playbackMs ??
140
+ scenario.runConsole.playbackMs,
141
+ playbackStepDurationsMs:
142
+ scenario.playback?.runConsole?.playbackStepDurationsMs ??
143
+ scenario.runConsole.playbackStepDurationsMs,
144
+ playbackResetDelayMs:
145
+ scenario.playback?.runConsole?.playbackResetDelayMs ??
146
+ scenario.runConsole.playbackResetDelayMs,
147
+ playbackStepEntryIndices:
148
+ scenario.playback?.runConsole?.playbackStepEntryIndices ??
149
+ scenario.runConsole.playbackStepEntryIndices,
150
+ playbackStepVisibleLineCounts:
151
+ scenario.playback?.runConsole?.playbackStepVisibleLineCounts ??
152
+ scenario.runConsole.playbackStepVisibleLineCounts,
153
+ };
154
+ }
155
+
156
+ function useDeterministicPlaybackStep({
157
+ stepCount,
158
+ playbackMs,
159
+ playbackStepDurationsMs,
160
+ playbackResetDelayMs,
161
+ scenarioKey,
162
+ }: PlaybackTimelineConfig) {
163
+ const basePlaybackMs = playbackMs ?? 1800;
164
+ const stepDurations = playbackStepDurationsMs ?? [];
165
+ const resetDelayMs =
166
+ playbackResetDelayMs ?? Math.max(basePlaybackMs + 240, 1600);
167
+ const [playbackStep, setPlaybackStep] = React.useState(1);
168
+
120
169
  React.useEffect(() => {
121
- if (activeScenarioKey || !autoCycle || scenarios.length <= 1) {
170
+ const initialStep = Math.max(1, Math.min(2, stepCount));
171
+ setPlaybackStep(initialStep);
172
+
173
+ if (stepCount <= 2) {
122
174
  return;
123
175
  }
124
176
 
125
- const intervalMs = Math.max(cycleIntervalMs ?? 4500, 1200);
126
- const timer = window.setInterval(() => {
127
- setInternalIndex((currentIndex) => (currentIndex + 1) % scenarios.length);
128
- }, intervalMs);
177
+ let timeoutId = 0;
178
+ let cancelled = false;
179
+
180
+ const scheduleStep = (step: number) => {
181
+ const delay = Math.max(260, stepDurations[step - 3] ?? basePlaybackMs);
182
+
183
+ timeoutId = window.setTimeout(() => {
184
+ if (cancelled) {
185
+ return;
186
+ }
187
+
188
+ setPlaybackStep(step);
129
189
 
130
- return () => window.clearInterval(timer);
131
- }, [activeScenarioKey, autoCycle, cycleIntervalMs, scenarios.length]);
190
+ if (step >= stepCount) {
191
+ timeoutId = window.setTimeout(
192
+ () => {
193
+ if (cancelled) {
194
+ return;
195
+ }
132
196
 
133
- return scenarios[activeIndex] ?? scenarios[0];
197
+ setPlaybackStep(initialStep);
198
+ scheduleStep(initialStep + 1);
199
+ },
200
+ Math.max(600, resetDelayMs),
201
+ );
202
+
203
+ return;
204
+ }
205
+
206
+ scheduleStep(step + 1);
207
+ }, delay);
208
+ };
209
+
210
+ scheduleStep(initialStep + 1);
211
+
212
+ return () => {
213
+ cancelled = true;
214
+ window.clearTimeout(timeoutId);
215
+ };
216
+ }, [basePlaybackMs, stepCount, resetDelayMs, stepDurations, scenarioKey]);
217
+
218
+ return playbackStep;
134
219
  }
135
220
 
136
221
  function HighlightPills({
@@ -161,45 +246,33 @@ function HighlightPills({
161
246
 
162
247
  function getWindowRenderData(
163
248
  scenario: ProductDemoScenario,
249
+ onSelectTask: (taskId: string) => void,
164
250
  ): WindowRenderData[] {
165
251
  return [
166
252
  {
167
- key: "workflowStudio",
168
- meta: scenario.workflowStudio.window,
253
+ key: "taskList",
254
+ meta: scenario.taskList.window,
169
255
  content: (
170
- <>
171
- <HighlightPills highlights={scenario.workflowStudio.highlights} />
172
- <WorkflowStudioPanel state={scenario.workflowStudio} />
173
- </>
256
+ <TaskListPanel state={scenario.taskList} onSelect={onSelectTask} />
174
257
  ),
175
258
  },
176
259
  {
177
- key: "knowledgePanel",
178
- meta: scenario.knowledgePanel.window,
179
- content: (
180
- <>
181
- <HighlightPills highlights={scenario.knowledgePanel.highlights} />
182
- <KnowledgePanel state={scenario.knowledgePanel} />
183
- </>
184
- ),
260
+ key: "workflowStudio",
261
+ meta: scenario.workflowStudio.window,
262
+ content: <WorkflowStudioPanel state={scenario.workflowStudio} />,
185
263
  },
186
264
  {
187
265
  key: "runConsole",
188
266
  meta: scenario.runConsole.window,
189
- content: (
190
- <>
191
- <HighlightPills highlights={scenario.runConsole.highlights} />
192
- <RunConsolePanel state={scenario.runConsole} />
193
- </>
194
- ),
267
+ content: <RunConsolePanel state={scenario.runConsole} />,
195
268
  },
196
269
  {
197
- key: "approvalInbox",
198
- meta: scenario.approvalInbox.window,
270
+ key: "knowledgePanel",
271
+ meta: scenario.knowledgePanel.window,
199
272
  content: (
200
273
  <>
201
- <HighlightPills highlights={scenario.approvalInbox.highlights} />
202
- <ApprovalInboxPanel state={scenario.approvalInbox} />
274
+ <HighlightPills highlights={scenario.knowledgePanel.highlights} />
275
+ <KnowledgePanel state={scenario.knowledgePanel} />
203
276
  </>
204
277
  ),
205
278
  },
@@ -208,66 +281,34 @@ function getWindowRenderData(
208
281
 
209
282
  function getWindowShellClass(key: ProductDemoWindowKey) {
210
283
  switch (key) {
284
+ case "taskList":
285
+ return "lg:col-span-2";
211
286
  case "workflowStudio":
212
- return "lg:absolute lg:left-[2%] lg:top-[9%] lg:h-[66%] lg:w-[46%]";
213
- case "knowledgePanel":
214
- return "lg:absolute lg:left-[50%] lg:top-[10%] lg:h-[34%] lg:w-[30%] xl:left-[49%] xl:w-[29%]";
287
+ return "lg:col-span-3";
215
288
  case "runConsole":
216
- return "lg:absolute lg:left-[50%] lg:bottom-[10%] lg:h-[33%] lg:w-[32%] xl:left-[49%] xl:w-[31%]";
289
+ return "lg:col-span-5";
290
+ case "knowledgePanel":
217
291
  case "approvalInbox":
218
- return "lg:absolute lg:right-[1%] lg:top-[17%] lg:h-[45%] lg:w-[24%] xl:right-[2%] xl:h-[44%] xl:w-[23%]";
292
+ return "hidden";
219
293
  default:
220
294
  return "";
221
295
  }
222
296
  }
223
297
 
224
- function getTransformStyle(
225
- meta: ProductDemoWindowMeta,
226
- ): React.CSSProperties | undefined {
227
- const layoutHint = meta.layoutHint;
228
-
229
- if (!layoutHint) {
230
- return undefined;
231
- }
232
-
233
- const translateX = layoutHint.x ?? 0;
234
- const translateY = layoutHint.y ?? 0;
235
- const rotate = layoutHint.rotateDeg ?? 0;
236
-
237
- return {
238
- zIndex: layoutHint.zIndex,
239
- transform: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg)`,
240
- };
241
- }
242
-
243
298
  export function DemoStage({
244
299
  scenarios = [],
245
300
  initialScenarioIndex,
246
301
  activeScenarioKey,
247
- autoCycle = false,
248
- cycleIntervalMs = 4500,
249
302
  className,
250
303
  enableMotion = true,
251
304
  ariaLabel = "Product demo stage",
252
305
  }: DemoStageProps) {
253
- const activeScenario = useActiveScenario({
306
+ const { activeScenario, activeIndex, setActiveIndex } = useActiveScenario({
254
307
  scenarios,
255
308
  initialScenarioIndex,
256
309
  activeScenarioKey,
257
- autoCycle,
258
- cycleIntervalMs,
259
310
  });
260
311
 
261
- const activeIndex = React.useMemo(() => {
262
- if (!activeScenario) {
263
- return 0;
264
- }
265
-
266
- return scenarios.findIndex(
267
- (scenario) => scenario.key === activeScenario.key,
268
- );
269
- }, [activeScenario, scenarios]);
270
-
271
312
  if (!activeScenario) {
272
313
  return (
273
314
  <div
@@ -286,48 +327,77 @@ export function DemoStage({
286
327
  );
287
328
  }
288
329
 
289
- const windows = getWindowRenderData(activeScenario).sort(
290
- (a, b) => WINDOW_ORDER.indexOf(a.key) - WINDOW_ORDER.indexOf(b.key),
291
- );
330
+ const workflowPlayback = getWorkflowPlaybackConfig(activeScenario);
331
+ const runConsolePlayback = getRunConsolePlaybackConfig(activeScenario);
332
+
333
+ const workflowPlaybackStep = useDeterministicPlaybackStep({
334
+ stepCount: Math.max(
335
+ 1,
336
+ activeScenario.workflowStudio.transcript?.length ?? 1,
337
+ ),
338
+ playbackMs: workflowPlayback.playbackMs,
339
+ playbackStepDurationsMs: workflowPlayback.playbackStepDurationsMs,
340
+ playbackResetDelayMs: workflowPlayback.playbackResetDelayMs,
341
+ scenarioKey: `${activeScenario.key}-workflowStudio`,
342
+ });
343
+
344
+ const runConsolePlaybackStep = useDeterministicPlaybackStep({
345
+ stepCount: Math.max(
346
+ 1,
347
+ runConsolePlayback.playbackStepEntryIndices?.length ?? 0,
348
+ runConsolePlayback.playbackStepVisibleLineCounts?.length ?? 0,
349
+ activeScenario.runConsole.entries.length,
350
+ ),
351
+ playbackMs: runConsolePlayback.playbackMs,
352
+ playbackStepDurationsMs: runConsolePlayback.playbackStepDurationsMs,
353
+ playbackResetDelayMs: runConsolePlayback.playbackResetDelayMs,
354
+ scenarioKey: `${activeScenario.key}-runConsole`,
355
+ });
356
+
357
+ const scenarioWithPlayback: ProductDemoScenario = {
358
+ ...activeScenario,
359
+ workflowStudio: {
360
+ ...activeScenario.workflowStudio,
361
+ ...workflowPlayback,
362
+ playbackStep: workflowPlaybackStep,
363
+ },
364
+ runConsole: {
365
+ ...activeScenario.runConsole,
366
+ ...runConsolePlayback,
367
+ playbackStep: runConsolePlaybackStep,
368
+ },
369
+ };
370
+
371
+ const windows = getWindowRenderData(scenarioWithPlayback, (taskId) => {
372
+ const nextIndex = scenarios.findIndex(
373
+ (scenario) => scenario.key === taskId,
374
+ );
375
+
376
+ if (nextIndex >= 0) {
377
+ setActiveIndex(nextIndex);
378
+ }
379
+ }).sort((a, b) => WINDOW_ORDER.indexOf(a.key) - WINDOW_ORDER.indexOf(b.key));
292
380
 
293
381
  return (
294
382
  <div
295
383
  data-product-demo-stage
296
- data-auto-cycle={autoCycle ? "true" : "false"}
297
384
  data-enable-motion={enableMotion ? "true" : "false"}
298
385
  data-scenario-count={scenarios.length}
299
386
  data-active-scenario-key={activeScenario.key}
300
387
  data-active-scenario-index={activeIndex}
301
- data-cycle-interval-ms={cycleIntervalMs}
302
388
  className={cn(
303
- "relative isolate min-h-[30rem] w-full overflow-hidden rounded-[2rem] border border-border/60 bg-[radial-gradient(circle_at_top,rgba(59,130,246,0.13),transparent_35%),linear-gradient(135deg,rgba(15,23,42,0.04),transparent_55%)] shadow-2xl",
389
+ "relative isolate min-h-[36rem] w-full overflow-visible rounded-none border-0 bg-transparent shadow-none lg:h-full lg:min-h-0",
304
390
  className,
305
391
  )}
306
392
  aria-label={ariaLabel}
307
393
  >
308
- <div className="absolute inset-0 bg-gradient-to-br from-background via-background/96 to-muted/45" />
309
- <div className="absolute inset-x-0 top-0 h-24 bg-gradient-to-b from-primary/8 to-transparent" />
310
-
311
- <div className="relative z-10 flex min-h-[30rem] flex-col gap-4 p-4 sm:p-5 lg:block lg:p-6">
312
- <div className="flex flex-wrap items-center justify-between gap-3 pb-1">
313
- <div>
314
- {activeScenario.label && (
315
- <div className="text-[11px] font-medium uppercase tracking-[0.18em] text-primary/80">
316
- {activeScenario.label}
317
- </div>
318
- )}
319
- {activeScenario.description && (
320
- <p className="mt-1 max-w-xl text-xs leading-relaxed text-muted-foreground">
321
- {activeScenario.description}
322
- </p>
323
- )}
324
- </div>
325
-
326
- <HighlightPills highlights={activeScenario.highlights} />
327
- </div>
328
-
394
+ <div className="relative z-10 flex h-[36rem] min-h-[36rem] flex-col gap-0 lg:h-full lg:min-h-0">
329
395
  <div className="grid gap-4 lg:hidden">
330
396
  {windows.map((windowData) => {
397
+ if (getWindowShellClass(windowData.key) === "hidden") {
398
+ return null;
399
+ }
400
+
331
401
  const activeWindow =
332
402
  activeScenario.activeWindow === windowData.key ||
333
403
  (!activeScenario.activeWindow &&
@@ -339,6 +409,9 @@ export function DemoStage({
339
409
  window={windowData.meta}
340
410
  active={activeWindow}
341
411
  enableMotion={enableMotion}
412
+ showControls={false}
413
+ showResizeHandle={false}
414
+ showHeader={false}
342
415
  >
343
416
  {windowData.content}
344
417
  </DemoWindow>
@@ -346,50 +419,98 @@ export function DemoStage({
346
419
  })}
347
420
  </div>
348
421
 
349
- <div className="hidden lg:block lg:h-[34rem] xl:h-[36rem]">
350
- {windows.map((windowData) => {
351
- const activeWindow =
352
- activeScenario.activeWindow === windowData.key ||
353
- (!activeScenario.activeWindow &&
354
- windowData.key === "workflowStudio");
422
+ <div className="hidden overflow-hidden rounded-[0.4rem] border border-[var(--demo-border)] bg-[var(--demo-shell-bg)] shadow-[var(--demo-shell-shadow)] ring-1 ring-[var(--demo-shell-ring)] lg:flex lg:h-full lg:min-h-0 lg:flex-col">
423
+ <div className="relative flex h-[3.2rem] items-center justify-between border-b border-[var(--demo-border)] bg-[var(--demo-shell-muted-bg)] px-4 py-2.5 shadow-[inset_0_1px_0_rgba(255,255,255,0.72)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.05)] sm:px-5">
424
+ <div className="flex min-w-0 items-center gap-3.5">
425
+ <div className="flex h-8 w-8 items-center justify-center rounded-[0.4rem] border border-[var(--demo-border)] bg-[var(--demo-shell-strong-bg)] text-[var(--demo-fg)] shadow-[inset_0_1px_0_rgba(255,255,255,0.72)] dark:shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]">
426
+ <span className="grid grid-cols-2 gap-[2px]">
427
+ <span className="h-[3px] w-[3px] rounded-[1px] bg-[var(--demo-fg)]" />
428
+ <span className="h-[3px] w-[3px] rounded-[1px] bg-[var(--demo-muted-fg)]" />
429
+ <span className="h-[3px] w-[3px] rounded-[1px] bg-[var(--demo-muted-fg)]" />
430
+ <span className="h-[3px] w-[3px] rounded-[1px] bg-[var(--demo-fg)]" />
431
+ </span>
432
+ </div>
433
+ <div className="min-w-0 space-y-0.5">
434
+ <div className="flex min-w-0 items-center gap-2">
435
+ <div className="truncate text-[12px] font-semibold tracking-[-0.02em] text-[var(--demo-fg)]">
436
+ Agent workspace
437
+ </div>
438
+ <span className="hidden rounded-full border border-[var(--demo-border)] bg-[var(--demo-panel-bg)] px-2 py-0.5 text-[9px] font-medium uppercase tracking-[0.14em] text-[var(--demo-subtle-fg)] xl:inline-flex">
439
+ session 03
440
+ </span>
441
+ </div>
442
+ <div className="text-[10px] uppercase tracking-[0.18em] text-[var(--demo-subtle-fg)]">
443
+ Session · live repo
444
+ </div>
445
+ </div>
446
+ </div>
447
+ <div className="flex items-center gap-2.5 text-[10px] uppercase tracking-[0.16em] text-[var(--demo-subtle-fg)]">
448
+ <div className="hidden items-center gap-2 text-[10px] text-[var(--demo-subtle-fg)] xl:flex">
449
+ <span>repo</span>
450
+ <span className="font-mono text-[var(--demo-muted-fg)]">
451
+ apps/web
452
+ </span>
453
+ </div>
454
+ <span className="inline-flex items-center gap-1.5 rounded-full border border-[var(--demo-border)] bg-[var(--demo-panel-bg)] px-2.5 py-1 text-[var(--demo-muted-fg)]">
455
+ <span className="h-1.5 w-1.5 rounded-full bg-[var(--demo-success)]" />
456
+ Active
457
+ </span>
458
+ </div>
459
+ </div>
355
460
 
356
- return (
357
- <motion.div
358
- key={windowData.key}
359
- initial={
360
- enableMotion ? { opacity: 0, y: 18, scale: 0.98 } : false
361
- }
362
- animate={{
363
- opacity: 1,
364
- y: 0,
365
- scale: activeWindow ? 1 : 0.985,
366
- }}
367
- transition={
368
- enableMotion
369
- ? {
370
- type: "tween",
371
- duration: 0.45,
372
- }
373
- : { duration: 0 }
374
- }
375
- className={cn(
376
- "will-change-transform",
377
- getWindowShellClass(windowData.key),
378
- )}
379
- style={getTransformStyle(windowData.meta)}
380
- >
381
- <DemoWindow
382
- window={windowData.meta}
383
- active={activeWindow}
384
- dimmed={!activeWindow}
385
- enableMotion={enableMotion}
386
- className="h-full"
461
+ <div className="grid min-h-0 flex-1 lg:grid-cols-10 lg:gap-0">
462
+ {windows.map((windowData) => {
463
+ const shellClass = getWindowShellClass(windowData.key);
464
+
465
+ if (shellClass === "hidden") {
466
+ return null;
467
+ }
468
+
469
+ const activeWindow =
470
+ activeScenario.activeWindow === windowData.key ||
471
+ (!activeScenario.activeWindow &&
472
+ windowData.key === "workflowStudio");
473
+
474
+ return (
475
+ <motion.div
476
+ key={windowData.key}
477
+ initial={enableMotion ? { opacity: 0 } : false}
478
+ animate={{ opacity: 1 }}
479
+ transition={
480
+ enableMotion
481
+ ? {
482
+ type: "tween",
483
+ duration: 0.24,
484
+ }
485
+ : { duration: 0 }
486
+ }
487
+ className={cn("min-h-0", shellClass)}
387
488
  >
388
- {windowData.content}
389
- </DemoWindow>
390
- </motion.div>
391
- );
392
- })}
489
+ <DemoWindow
490
+ window={windowData.meta}
491
+ active={activeWindow}
492
+ dimmed={false}
493
+ enableMotion={enableMotion}
494
+ showControls={false}
495
+ showResizeHandle={false}
496
+ showHeader={false}
497
+ className={cn(
498
+ "h-full min-h-0 border-0 shadow-none",
499
+ windowData.key === "taskList" &&
500
+ "rounded-none border-r border-[var(--demo-border)]",
501
+ windowData.key === "workflowStudio" &&
502
+ "rounded-none border-r border-[var(--demo-border)]",
503
+
504
+ windowData.key === "runConsole" && "rounded-none",
505
+ )}
506
+ bodyClassName="px-0 py-0 sm:px-0 sm:py-0"
507
+ >
508
+ {windowData.content}
509
+ </DemoWindow>
510
+ </motion.div>
511
+ );
512
+ })}
513
+ </div>
393
514
  </div>
394
515
  </div>
395
516
  </div>