nextworks 0.2.0-alpha.17 → 0.2.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli_manifests/blocks_manifest.json +10 -2
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_QUICKSTART.md +3 -0
- package/dist/kits/blocks/.nextworks/docs/BLOCKS_README.md +11 -1
- package/dist/kits/blocks/components/sections/CommandShowcase.tsx +517 -0
- package/dist/kits/blocks/components/sections/FeaturedProjectShowcase.tsx +687 -0
- package/dist/kits/blocks/components/sections/HeroWithVideo.tsx +495 -0
- package/dist/kits/blocks/components/sections/ProjectDeepDive.tsx +805 -0
- package/dist/kits/blocks/components/sections/SelectedWorkRail.tsx +485 -0
- package/dist/kits/blocks/package-deps.json +3 -3
- package/dist/kits/blocks/tsconfig.json +0 -2
- package/dist/kits/blocks/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
|
@@ -48,18 +48,22 @@
|
|
|
48
48
|
"components/sections/About.tsx",
|
|
49
49
|
"components/sections/Contact.tsx",
|
|
50
50
|
"components/sections/CTA.tsx",
|
|
51
|
+
"components/sections/CommandShowcase.tsx",
|
|
51
52
|
"components/sections/FAQ.tsx",
|
|
52
53
|
"components/sections/Features.tsx",
|
|
53
54
|
"components/sections/Footer.tsx",
|
|
55
|
+
"components/sections/FeaturedProjectShowcase.tsx",
|
|
54
56
|
"components/sections/HeroMotion.tsx",
|
|
55
57
|
"components/sections/HeroOverlay.tsx",
|
|
56
58
|
"components/sections/HeroProductDemo.tsx",
|
|
57
59
|
"components/sections/HeroSplit.tsx",
|
|
60
|
+
"components/sections/HeroWithVideo.tsx",
|
|
58
61
|
"components/sections/Navbar.tsx",
|
|
59
62
|
"components/sections/product-demo/ApprovalInboxPanel.tsx",
|
|
60
|
-
|
|
63
|
+
"components/sections/product-demo/DemoStage.tsx",
|
|
61
64
|
"components/sections/product-demo/DemoWindow.tsx",
|
|
62
65
|
"components/sections/product-demo/KnowledgePanel.tsx",
|
|
66
|
+
|
|
63
67
|
"components/sections/product-demo/RunConsolePanel.tsx",
|
|
64
68
|
"components/sections/product-demo/types.ts",
|
|
65
69
|
"components/sections/product-demo/TaskListPanel.tsx",
|
|
@@ -68,12 +72,16 @@
|
|
|
68
72
|
"components/sections/PortfolioSimple.tsx",
|
|
69
73
|
"components/sections/Pricing.tsx",
|
|
70
74
|
"components/sections/ProcessTimeline.tsx",
|
|
75
|
+
"components/sections/ProjectDeepDive.tsx",
|
|
76
|
+
"components/sections/SelectedWorkRail.tsx",
|
|
71
77
|
"components/sections/ServicesGrid.tsx",
|
|
72
|
-
|
|
78
|
+
"components/sections/Team.tsx",
|
|
73
79
|
"components/sections/Testimonials.tsx",
|
|
74
80
|
"components/sections/TrustBadges.tsx"
|
|
75
81
|
]
|
|
76
82
|
},
|
|
83
|
+
|
|
84
|
+
|
|
77
85
|
"templates": {
|
|
78
86
|
"description": "Full page templates (Product Launch, SaaS Dashboard, Digital Agency, Gallery)",
|
|
79
87
|
"files": [
|
|
@@ -89,6 +89,9 @@ Supporting template components live alongside the template:
|
|
|
89
89
|
|
|
90
90
|
- Customize a reusable section (Navbar/Hero/etc.):
|
|
91
91
|
- edit `components/sections/<Section>.tsx`
|
|
92
|
+
- use `components/sections/HeroWithVideo.tsx` for a centered dark hero with CTAs, command pill, and video preview
|
|
93
|
+
- use `components/sections/FeaturedProjectShowcase.tsx` for a flagship project block with tags, feature bullets, CTAs, and optional media
|
|
94
|
+
- use `components/sections/CommandShowcase.tsx` for a terminal-style command/setup flow with optional output and notes
|
|
92
95
|
|
|
93
96
|
- Customize theme/palette:
|
|
94
97
|
- see `.nextworks/docs/THEME_GUIDE.md`
|
|
@@ -14,7 +14,8 @@ If you’re reading this inside your app repo, you’ll also find companion docs
|
|
|
14
14
|
Depending on install flags, Blocks adds:
|
|
15
15
|
|
|
16
16
|
- **UI primitives** under `components/ui/**` (Button, Input, Card, Select, Checkbox, Switch, Toaster, etc.)
|
|
17
|
-
- **Reusable marketing sections** under `components/sections/**` (Navbar, Hero variants
|
|
17
|
+
- **Reusable marketing sections** under `components/sections/**` (Navbar, Hero variants including `HeroWithVideo`, `FeaturedProjectShowcase`, `CommandShowcase`, `ProjectDeepDive`, `SelectedWorkRail`, Features, Pricing, Testimonials, FAQ, Contact, Footer, …)
|
|
18
|
+
|
|
18
19
|
- **Full page templates** under a router-native path (see below)
|
|
19
20
|
- **Theme + provider utilities** under `components/**` and `lib/**`
|
|
20
21
|
- **Global styles**: `app/globals.css` and `app/tw-animate.css`
|
|
@@ -22,6 +23,7 @@ Depending on install flags, Blocks adds:
|
|
|
22
23
|
|
|
23
24
|
---
|
|
24
25
|
|
|
26
|
+
|
|
25
27
|
## Install commands / options
|
|
26
28
|
|
|
27
29
|
> ```bash
|
|
@@ -102,6 +104,14 @@ Notes:
|
|
|
102
104
|
|
|
103
105
|
- Want to customize a section (Navbar/Hero/etc.)?
|
|
104
106
|
- edit files directly under `components/sections/**`
|
|
107
|
+
- for a centered cinematic hero with a video preview, start with `components/sections/HeroWithVideo.tsx`
|
|
108
|
+
- for a deeper flagship project block with tags, feature bullets, CTAs, and media, use `components/sections/FeaturedProjectShowcase.tsx`
|
|
109
|
+
- for a terminal-style command/setup flow, use `components/sections/CommandShowcase.tsx`
|
|
110
|
+
- for a premium visual showcase of templates, launches, or selected projects, use `components/sections/SelectedWorkRail.tsx`
|
|
111
|
+
- for a reusable case-study block covering problem, solution, technical highlights, and result, use `components/sections/ProjectDeepDive.tsx`
|
|
112
|
+
|
|
105
113
|
|
|
106
114
|
- Want to customize a template page?
|
|
115
|
+
|
|
107
116
|
- edit the template route file (see paths above) and its local components.
|
|
117
|
+
|
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
|
|
8
|
+
type ButtonVariant =
|
|
9
|
+
| "default"
|
|
10
|
+
| "destructive"
|
|
11
|
+
| "outline"
|
|
12
|
+
| "secondary"
|
|
13
|
+
| "ghost"
|
|
14
|
+
| "link";
|
|
15
|
+
|
|
16
|
+
type ButtonSize = "default" | "sm" | "lg" | "icon";
|
|
17
|
+
|
|
18
|
+
export interface CommandShowcaseCta {
|
|
19
|
+
label?: string;
|
|
20
|
+
href?: string;
|
|
21
|
+
ariaLabel?: string;
|
|
22
|
+
variant?: ButtonVariant;
|
|
23
|
+
size?: ButtonSize;
|
|
24
|
+
className?: string;
|
|
25
|
+
unstyled?: boolean;
|
|
26
|
+
style?: React.CSSProperties;
|
|
27
|
+
target?: string;
|
|
28
|
+
rel?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CommandShowcaseLine {
|
|
32
|
+
text: React.ReactNode;
|
|
33
|
+
className?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface CommandShowcaseCommandLine extends CommandShowcaseLine {
|
|
37
|
+
command?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface CommandShowcaseNote {
|
|
41
|
+
title?: React.ReactNode;
|
|
42
|
+
description: React.ReactNode;
|
|
43
|
+
className?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CommandShowcaseClassNames {
|
|
47
|
+
section?: string;
|
|
48
|
+
backgroundGlow?: string;
|
|
49
|
+
container?: string;
|
|
50
|
+
header?: string;
|
|
51
|
+
eyebrow?: string;
|
|
52
|
+
title?: string;
|
|
53
|
+
description?: string;
|
|
54
|
+
contentGrid?: string;
|
|
55
|
+
terminalOuter?: string;
|
|
56
|
+
terminalCard?: string;
|
|
57
|
+
terminalChrome?: string;
|
|
58
|
+
terminalChromeDot?: string;
|
|
59
|
+
terminalTitle?: string;
|
|
60
|
+
copyButton?: string;
|
|
61
|
+
commandList?: string;
|
|
62
|
+
commandLine?: string;
|
|
63
|
+
prompt?: string;
|
|
64
|
+
commandText?: string;
|
|
65
|
+
outputList?: string;
|
|
66
|
+
outputLine?: string;
|
|
67
|
+
notes?: string;
|
|
68
|
+
note?: string;
|
|
69
|
+
noteMarker?: string;
|
|
70
|
+
noteTitle?: string;
|
|
71
|
+
noteDescription?: string;
|
|
72
|
+
buttons?: string;
|
|
73
|
+
cta?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface CommandShowcaseProps {
|
|
77
|
+
id?: string;
|
|
78
|
+
className?: string;
|
|
79
|
+
eyebrow?: React.ReactNode;
|
|
80
|
+
title?: React.ReactNode;
|
|
81
|
+
description?: React.ReactNode;
|
|
82
|
+
commands?: Array<string | CommandShowcaseCommandLine>;
|
|
83
|
+
output?: Array<string | CommandShowcaseLine> | false;
|
|
84
|
+
notes?: Array<string | CommandShowcaseNote> | false;
|
|
85
|
+
bullets?: Array<string | CommandShowcaseNote> | false;
|
|
86
|
+
cta?: CommandShowcaseCta;
|
|
87
|
+
showCopyButton?: boolean;
|
|
88
|
+
copyLabel?: string;
|
|
89
|
+
copiedLabel?: string;
|
|
90
|
+
copyValue?: string;
|
|
91
|
+
terminalLabel?: string;
|
|
92
|
+
terminalTitle?: React.ReactNode;
|
|
93
|
+
ariaLabel?: string;
|
|
94
|
+
titleId?: string;
|
|
95
|
+
enableMotion?: boolean;
|
|
96
|
+
classNames?: CommandShowcaseClassNames;
|
|
97
|
+
section?: { className?: string };
|
|
98
|
+
container?: { className?: string };
|
|
99
|
+
header?: { className?: string };
|
|
100
|
+
terminal?: { className?: string };
|
|
101
|
+
notesSlot?: { className?: string };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const defaultCommands = [
|
|
105
|
+
"npm create example-app@latest",
|
|
106
|
+
"cd example-app",
|
|
107
|
+
"npm install",
|
|
108
|
+
"npm run dev",
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
const defaultOutput = [
|
|
112
|
+
"✓ Project created",
|
|
113
|
+
"✓ Dependencies installed",
|
|
114
|
+
"✓ Configuration checked",
|
|
115
|
+
"✓ Development server ready",
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const defaultNotes: CommandShowcaseNote[] = [
|
|
119
|
+
{
|
|
120
|
+
title: "Step-by-step",
|
|
121
|
+
description: "Show the exact commands someone can follow from start to finish.",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
title: "Copy-friendly",
|
|
125
|
+
description: "Keep setup details close to the product story without running anything live.",
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
title: "Developer-focused",
|
|
129
|
+
description: "Useful for CLIs, SDKs, starter kits, integration guides, and internal tools.",
|
|
130
|
+
},
|
|
131
|
+
];
|
|
132
|
+
|
|
133
|
+
function normalizeCommandLine(
|
|
134
|
+
line: string | CommandShowcaseCommandLine,
|
|
135
|
+
): CommandShowcaseCommandLine {
|
|
136
|
+
return typeof line === "string" ? { text: line, command: line } : line;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function normalizeLine(line: string | CommandShowcaseLine): CommandShowcaseLine {
|
|
140
|
+
return typeof line === "string" ? { text: line } : line;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function normalizeNote(note: string | CommandShowcaseNote): CommandShowcaseNote {
|
|
144
|
+
return typeof note === "string" ? { description: note } : note;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function getLineText(line: string | CommandShowcaseCommandLine) {
|
|
148
|
+
if (typeof line === "string") {
|
|
149
|
+
return line;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (line.command) {
|
|
153
|
+
return line.command;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return typeof line.text === "string" ? line.text : "";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function renderCta(cta: CommandShowcaseCta | undefined) {
|
|
160
|
+
if (!cta?.label) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<Button
|
|
166
|
+
asChild
|
|
167
|
+
variant={cta.variant}
|
|
168
|
+
size={cta.size}
|
|
169
|
+
className={cta.className}
|
|
170
|
+
unstyled={cta.unstyled}
|
|
171
|
+
style={cta.style}
|
|
172
|
+
>
|
|
173
|
+
<Link
|
|
174
|
+
href={cta.href || "#"}
|
|
175
|
+
aria-label={cta.ariaLabel ?? cta.label}
|
|
176
|
+
target={cta.target}
|
|
177
|
+
rel={cta.rel ?? (cta.target === "_blank" ? "noreferrer" : undefined)}
|
|
178
|
+
>
|
|
179
|
+
{cta.label}
|
|
180
|
+
</Link>
|
|
181
|
+
</Button>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export function CommandShowcase({
|
|
186
|
+
id,
|
|
187
|
+
className,
|
|
188
|
+
eyebrow = "Command showcase",
|
|
189
|
+
title = "Show a setup flow without leaving the page.",
|
|
190
|
+
description =
|
|
191
|
+
"Use a terminal-style section to explain commands, install steps, or developer workflow output.",
|
|
192
|
+
commands = defaultCommands,
|
|
193
|
+
output = defaultOutput,
|
|
194
|
+
notes,
|
|
195
|
+
bullets,
|
|
196
|
+
cta,
|
|
197
|
+
showCopyButton = false,
|
|
198
|
+
copyLabel = "Copy commands",
|
|
199
|
+
copiedLabel = "Copied",
|
|
200
|
+
copyValue,
|
|
201
|
+
terminalLabel = "Terminal preview showing setup commands and output",
|
|
202
|
+
terminalTitle = "setup.sh",
|
|
203
|
+
ariaLabel = "Command showcase section",
|
|
204
|
+
titleId,
|
|
205
|
+
enableMotion = true,
|
|
206
|
+
classNames,
|
|
207
|
+
section,
|
|
208
|
+
container,
|
|
209
|
+
header,
|
|
210
|
+
terminal,
|
|
211
|
+
notesSlot,
|
|
212
|
+
}: CommandShowcaseProps) {
|
|
213
|
+
const [copyStatus, setCopyStatus] = React.useState<"idle" | "copied">("idle");
|
|
214
|
+
|
|
215
|
+
const normalizedCommands = commands.map(normalizeCommandLine);
|
|
216
|
+
const normalizedOutput = output === false ? [] : output.map(normalizeLine);
|
|
217
|
+
const resolvedNotes = notes ?? bullets ?? defaultNotes;
|
|
218
|
+
const normalizedNotes =
|
|
219
|
+
resolvedNotes === false ? [] : resolvedNotes.map(normalizeNote);
|
|
220
|
+
|
|
221
|
+
const resolvedCopyValue =
|
|
222
|
+
copyValue ?? commands.map(getLineText).filter(Boolean).join("\n");
|
|
223
|
+
|
|
224
|
+
const motionClasses = enableMotion
|
|
225
|
+
? "transition-all duration-200 hover:-translate-y-0.5"
|
|
226
|
+
: "transition-none hover:!translate-y-0";
|
|
227
|
+
|
|
228
|
+
const handleCopy = async () => {
|
|
229
|
+
if (!resolvedCopyValue || typeof navigator === "undefined") {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
try {
|
|
234
|
+
await navigator.clipboard.writeText(resolvedCopyValue);
|
|
235
|
+
setCopyStatus("copied");
|
|
236
|
+
window.setTimeout(() => setCopyStatus("idle"), 1600);
|
|
237
|
+
} catch {
|
|
238
|
+
setCopyStatus("idle");
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const resolvedCta: CommandShowcaseCta | undefined = cta?.label
|
|
243
|
+
? {
|
|
244
|
+
href: "#",
|
|
245
|
+
variant: "outline",
|
|
246
|
+
size: "lg",
|
|
247
|
+
unstyled: true,
|
|
248
|
+
...cta,
|
|
249
|
+
className: cn(
|
|
250
|
+
"h-11 rounded-full border border-border bg-background/80 px-6 text-sm font-medium text-foreground hover:bg-muted",
|
|
251
|
+
|
|
252
|
+
motionClasses,
|
|
253
|
+
classNames?.cta,
|
|
254
|
+
cta.className,
|
|
255
|
+
),
|
|
256
|
+
}
|
|
257
|
+
: undefined;
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<section
|
|
261
|
+
id={id}
|
|
262
|
+
className={cn(
|
|
263
|
+
"relative overflow-hidden bg-background px-4 py-20 text-foreground sm:px-6 lg:px-8 lg:py-24",
|
|
264
|
+
|
|
265
|
+
section?.className,
|
|
266
|
+
classNames?.section,
|
|
267
|
+
className,
|
|
268
|
+
)}
|
|
269
|
+
aria-label={ariaLabel}
|
|
270
|
+
aria-labelledby={titleId}
|
|
271
|
+
>
|
|
272
|
+
<div
|
|
273
|
+
className={cn(
|
|
274
|
+
"pointer-events-none absolute left-1/2 top-28 h-[34rem] w-[48rem] -translate-x-1/2 rounded-full bg-[radial-gradient(circle_at_center,rgba(0,0,0,0.08),rgba(0,0,0,0.03)_38%,transparent_72%)] blur-3xl dark:bg-[radial-gradient(circle_at_center,rgba(255,255,255,0.11),rgba(255,255,255,0.04)_38%,transparent_72%)]",
|
|
275
|
+
|
|
276
|
+
classNames?.backgroundGlow,
|
|
277
|
+
)}
|
|
278
|
+
aria-hidden="true"
|
|
279
|
+
/>
|
|
280
|
+
|
|
281
|
+
<div
|
|
282
|
+
className={cn(
|
|
283
|
+
"relative z-10 mx-auto max-w-7xl",
|
|
284
|
+
container?.className,
|
|
285
|
+
classNames?.container,
|
|
286
|
+
)}
|
|
287
|
+
>
|
|
288
|
+
<div
|
|
289
|
+
className={cn(
|
|
290
|
+
"mx-auto max-w-3xl text-center",
|
|
291
|
+
header?.className,
|
|
292
|
+
classNames?.header,
|
|
293
|
+
)}
|
|
294
|
+
>
|
|
295
|
+
{eyebrow ? (
|
|
296
|
+
<p
|
|
297
|
+
className={cn(
|
|
298
|
+
"text-xs font-medium uppercase tracking-[0.28em] text-muted-foreground",
|
|
299
|
+
|
|
300
|
+
classNames?.eyebrow,
|
|
301
|
+
)}
|
|
302
|
+
>
|
|
303
|
+
{eyebrow}
|
|
304
|
+
</p>
|
|
305
|
+
) : null}
|
|
306
|
+
|
|
307
|
+
<h2
|
|
308
|
+
id={titleId}
|
|
309
|
+
className={cn(
|
|
310
|
+
"mt-5 text-balance text-4xl font-semibold tracking-[-0.055em] text-foreground sm:text-5xl lg:text-6xl",
|
|
311
|
+
|
|
312
|
+
classNames?.title,
|
|
313
|
+
)}
|
|
314
|
+
>
|
|
315
|
+
{title}
|
|
316
|
+
</h2>
|
|
317
|
+
|
|
318
|
+
{description ? (
|
|
319
|
+
<p
|
|
320
|
+
className={cn(
|
|
321
|
+
"mx-auto mt-5 max-w-2xl text-pretty text-base leading-7 text-muted-foreground sm:text-lg",
|
|
322
|
+
|
|
323
|
+
classNames?.description,
|
|
324
|
+
)}
|
|
325
|
+
>
|
|
326
|
+
{description}
|
|
327
|
+
</p>
|
|
328
|
+
) : null}
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<div
|
|
332
|
+
className={cn(
|
|
333
|
+
"mt-12 grid gap-6 lg:grid-cols-[minmax(0,1.1fr)_minmax(20rem,0.9fr)] lg:items-stretch",
|
|
334
|
+
classNames?.contentGrid,
|
|
335
|
+
)}
|
|
336
|
+
>
|
|
337
|
+
<div
|
|
338
|
+
className={cn(
|
|
339
|
+
"relative overflow-hidden rounded-[2rem] border border-border bg-card/80 p-2 shadow-[0_24px_90px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_24px_90px_rgba(0,0,0,0.62)]",
|
|
340
|
+
|
|
341
|
+
enableMotion && "transition-transform duration-300 hover:-translate-y-1",
|
|
342
|
+
!enableMotion && "transition-none hover:!translate-y-0",
|
|
343
|
+
terminal?.className,
|
|
344
|
+
classNames?.terminalOuter,
|
|
345
|
+
)}
|
|
346
|
+
>
|
|
347
|
+
<div
|
|
348
|
+
className={cn(
|
|
349
|
+
"overflow-hidden rounded-[1.55rem] border border-border bg-card",
|
|
350
|
+
|
|
351
|
+
classNames?.terminalCard,
|
|
352
|
+
)}
|
|
353
|
+
role="region"
|
|
354
|
+
aria-label={terminalLabel}
|
|
355
|
+
>
|
|
356
|
+
<div
|
|
357
|
+
className={cn(
|
|
358
|
+
"flex h-11 items-center gap-2 border-b border-border px-4",
|
|
359
|
+
|
|
360
|
+
classNames?.terminalChrome,
|
|
361
|
+
)}
|
|
362
|
+
>
|
|
363
|
+
<span
|
|
364
|
+
className={cn("size-2.5 rounded-full bg-foreground/24", classNames?.terminalChromeDot)}
|
|
365
|
+
|
|
366
|
+
aria-hidden="true"
|
|
367
|
+
/>
|
|
368
|
+
<span
|
|
369
|
+
className={cn("size-2.5 rounded-full bg-foreground/16", classNames?.terminalChromeDot)}
|
|
370
|
+
|
|
371
|
+
aria-hidden="true"
|
|
372
|
+
/>
|
|
373
|
+
<span
|
|
374
|
+
className={cn("size-2.5 rounded-full bg-foreground/10", classNames?.terminalChromeDot)}
|
|
375
|
+
|
|
376
|
+
aria-hidden="true"
|
|
377
|
+
/>
|
|
378
|
+
<span
|
|
379
|
+
className={cn(
|
|
380
|
+
"ml-3 min-w-0 flex-1 truncate font-mono text-xs text-muted-foreground",
|
|
381
|
+
|
|
382
|
+
classNames?.terminalTitle,
|
|
383
|
+
)}
|
|
384
|
+
>
|
|
385
|
+
{terminalTitle}
|
|
386
|
+
</span>
|
|
387
|
+
{showCopyButton ? (
|
|
388
|
+
<button
|
|
389
|
+
type="button"
|
|
390
|
+
onClick={handleCopy}
|
|
391
|
+
className={cn(
|
|
392
|
+
"rounded-full border border-border bg-muted/60 px-3 py-1 font-sans text-xs font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
|
|
393
|
+
|
|
394
|
+
classNames?.copyButton,
|
|
395
|
+
)}
|
|
396
|
+
aria-label={copyStatus === "copied" ? copiedLabel : copyLabel}
|
|
397
|
+
>
|
|
398
|
+
{copyStatus === "copied" ? copiedLabel : copyLabel}
|
|
399
|
+
</button>
|
|
400
|
+
) : null}
|
|
401
|
+
</div>
|
|
402
|
+
|
|
403
|
+
<div className="p-5 font-mono text-sm sm:p-6">
|
|
404
|
+
<div className={cn("space-y-3", classNames?.commandList)}>
|
|
405
|
+
{normalizedCommands.map((line, index) => (
|
|
406
|
+
<p
|
|
407
|
+
key={`${getLineText(commands[index])}-${index}`}
|
|
408
|
+
className={cn(
|
|
409
|
+
"flex gap-3 break-all leading-6 text-foreground/80",
|
|
410
|
+
|
|
411
|
+
classNames?.commandLine,
|
|
412
|
+
line.className,
|
|
413
|
+
)}
|
|
414
|
+
>
|
|
415
|
+
<span
|
|
416
|
+
className={cn("shrink-0 text-muted-foreground/60", classNames?.prompt)}
|
|
417
|
+
|
|
418
|
+
aria-hidden="true"
|
|
419
|
+
>
|
|
420
|
+
$
|
|
421
|
+
</span>
|
|
422
|
+
<span className={classNames?.commandText}>{line.text}</span>
|
|
423
|
+
</p>
|
|
424
|
+
))}
|
|
425
|
+
</div>
|
|
426
|
+
|
|
427
|
+
{normalizedOutput.length > 0 ? (
|
|
428
|
+
<div
|
|
429
|
+
className={cn(
|
|
430
|
+
"mt-7 space-y-2 border-t border-border pt-5",
|
|
431
|
+
|
|
432
|
+
classNames?.outputList,
|
|
433
|
+
)}
|
|
434
|
+
aria-label="Command output"
|
|
435
|
+
>
|
|
436
|
+
{normalizedOutput.map((line, index) => (
|
|
437
|
+
<p
|
|
438
|
+
key={`${String(line.text)}-${index}`}
|
|
439
|
+
className={cn(
|
|
440
|
+
"break-all leading-6 text-muted-foreground",
|
|
441
|
+
|
|
442
|
+
classNames?.outputLine,
|
|
443
|
+
line.className,
|
|
444
|
+
)}
|
|
445
|
+
>
|
|
446
|
+
{line.text}
|
|
447
|
+
</p>
|
|
448
|
+
))}
|
|
449
|
+
</div>
|
|
450
|
+
) : null}
|
|
451
|
+
</div>
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{normalizedNotes.length > 0 || resolvedCta ? (
|
|
456
|
+
<div
|
|
457
|
+
className={cn(
|
|
458
|
+
"flex flex-col justify-between rounded-[2rem] border border-border bg-card/80 p-6 shadow-[0_24px_90px_rgba(0,0,0,0.12)] backdrop-blur dark:shadow-[0_24px_90px_rgba(0,0,0,0.48)] sm:p-8",
|
|
459
|
+
|
|
460
|
+
notesSlot?.className,
|
|
461
|
+
classNames?.notes,
|
|
462
|
+
)}
|
|
463
|
+
>
|
|
464
|
+
{normalizedNotes.length > 0 ? (
|
|
465
|
+
<ul className="space-y-5">
|
|
466
|
+
{normalizedNotes.map((note, index) => (
|
|
467
|
+
<li
|
|
468
|
+
key={`${String(note.title ?? note.description)}-${index}`}
|
|
469
|
+
className={cn("flex gap-4", classNames?.note, note.className)}
|
|
470
|
+
>
|
|
471
|
+
<span
|
|
472
|
+
className={cn(
|
|
473
|
+
"mt-2 size-1.5 shrink-0 rounded-full bg-foreground/50",
|
|
474
|
+
|
|
475
|
+
classNames?.noteMarker,
|
|
476
|
+
)}
|
|
477
|
+
aria-hidden="true"
|
|
478
|
+
/>
|
|
479
|
+
<span>
|
|
480
|
+
{note.title ? (
|
|
481
|
+
<span
|
|
482
|
+
className={cn(
|
|
483
|
+
"block text-sm font-medium text-foreground",
|
|
484
|
+
|
|
485
|
+
classNames?.noteTitle,
|
|
486
|
+
)}
|
|
487
|
+
>
|
|
488
|
+
{note.title}
|
|
489
|
+
</span>
|
|
490
|
+
) : null}
|
|
491
|
+
<span
|
|
492
|
+
className={cn(
|
|
493
|
+
"mt-1 block text-sm leading-6 text-muted-foreground",
|
|
494
|
+
|
|
495
|
+
classNames?.noteDescription,
|
|
496
|
+
)}
|
|
497
|
+
>
|
|
498
|
+
{note.description}
|
|
499
|
+
</span>
|
|
500
|
+
</span>
|
|
501
|
+
</li>
|
|
502
|
+
))}
|
|
503
|
+
</ul>
|
|
504
|
+
) : null}
|
|
505
|
+
|
|
506
|
+
{resolvedCta ? (
|
|
507
|
+
<div className={cn("mt-8 flex", classNames?.buttons)}>
|
|
508
|
+
{renderCta(resolvedCta)}
|
|
509
|
+
</div>
|
|
510
|
+
) : null}
|
|
511
|
+
</div>
|
|
512
|
+
) : null}
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
</section>
|
|
516
|
+
);
|
|
517
|
+
}
|