@aiworkbench/vibe-cli 0.0.4 → 0.0.5
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/index.js +811 -2
- package/package.json +6 -4
- package/templates/react/.env.example +21 -0
- package/templates/react/index.html +82 -0
- package/templates/react/manifest.json +7 -0
- package/templates/react/package.json +24 -0
- package/templates/react/src/App.tsx +15 -0
- package/templates/react/src/main.tsx +35 -0
- package/templates/react/tsconfig.json +13 -0
- package/templates/react/vite.config.ts +23 -0
- package/templates/vanilla/.env.example +21 -0
- package/templates/vanilla/index.html +82 -0
- package/templates/vanilla/manifest.json +7 -0
- package/templates/vanilla/package.json +19 -0
- package/templates/vanilla/src/main.ts +61 -0
- package/templates/vanilla/tsconfig.json +12 -0
- package/templates/vanilla/vite.config.ts +12 -0
- package/templates/vue/.env.example +21 -0
- package/templates/vue/index.html +82 -0
- package/templates/vue/manifest.json +7 -0
- package/templates/vue/package.json +21 -0
- package/templates/vue/src/App.vue +15 -0
- package/templates/vue/src/main.ts +10 -0
- package/templates/vue/tsconfig.json +12 -0
- package/templates/vue/vite.config.ts +22 -0
package/dist/index.js
CHANGED
|
@@ -2789,6 +2789,9 @@ function isKebabCase(name) {
|
|
|
2789
2789
|
function toKebabCase(name) {
|
|
2790
2790
|
return name.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").replace(/[^a-z0-9-]/gi, "").toLowerCase().replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2791
2791
|
}
|
|
2792
|
+
function isValidCustomElementName(name) {
|
|
2793
|
+
return /^[a-z][a-z0-9]*-[a-z0-9-]*$/.test(name);
|
|
2794
|
+
}
|
|
2792
2795
|
function toPascalCase(kebab) {
|
|
2793
2796
|
return kebab.split("-").map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)).join("");
|
|
2794
2797
|
}
|
|
@@ -2806,6 +2809,7 @@ function buildTemplateContext(projectName, framework) {
|
|
|
2806
2809
|
var __dirname2 = dirname2(fileURLToPath(import.meta.url));
|
|
2807
2810
|
async function templateDir(framework) {
|
|
2808
2811
|
const candidates = [
|
|
2812
|
+
resolve(__dirname2, "..", "templates", framework),
|
|
2809
2813
|
resolve(__dirname2, "..", "..", "..", "..", "templates", framework),
|
|
2810
2814
|
resolve(__dirname2, "..", "..", "..", "templates", framework)
|
|
2811
2815
|
];
|
|
@@ -6300,6 +6304,793 @@ this._shadow.appendChild(img);
|
|
|
6300
6304
|
}
|
|
6301
6305
|
}
|
|
6302
6306
|
|
|
6307
|
+
// src/skills/host-design-system.ts
|
|
6308
|
+
function generateHostDesignSystem(config) {
|
|
6309
|
+
const { framework } = config;
|
|
6310
|
+
const stylingApproach = getStylingApproach(framework);
|
|
6311
|
+
return `# Host Design System — USECASE-EDITOR
|
|
6312
|
+
|
|
6313
|
+
Your mini-app renders inside the host's content area via \`<VibeHost>\`. The host provides
|
|
6314
|
+
the header, sidebar, and scroll container. Your job is to fill the content region with UI
|
|
6315
|
+
that looks native to the platform. This guide contains every design token and pattern you
|
|
6316
|
+
need.
|
|
6317
|
+
|
|
6318
|
+
## Important: Shadow DOM Isolation
|
|
6319
|
+
|
|
6320
|
+
Your mini-app lives inside a Shadow DOM boundary. This means:
|
|
6321
|
+
- Host Tailwind classes are **NOT available** inside your component
|
|
6322
|
+
- CSS custom properties (variables) **DO inherit** through Shadow DOM
|
|
6323
|
+
- You must write your own CSS using the values below, not Tailwind utility classes
|
|
6324
|
+
- Use \`adoptedStyleSheets\` or \`?inline\` CSS imports to inject styles into the shadow root
|
|
6325
|
+
|
|
6326
|
+
## Color System (OKLCH)
|
|
6327
|
+
|
|
6328
|
+
The host uses oklch color space exclusively. Use these hardcoded values in your CSS.
|
|
6329
|
+
CSS custom properties like \`var(--foreground)\` will inherit from the host through the
|
|
6330
|
+
shadow boundary, but having the literal values ensures correct rendering in dev sandbox.
|
|
6331
|
+
|
|
6332
|
+
### Light Mode
|
|
6333
|
+
|
|
6334
|
+
| Token | Value | Usage |
|
|
6335
|
+
|-------|-------|-------|
|
|
6336
|
+
| background | \`oklch(1 0 0)\` | Page background |
|
|
6337
|
+
| foreground | \`oklch(0.145 0 0)\` | Primary text |
|
|
6338
|
+
| card | \`oklch(1 0 0)\` | Card backgrounds |
|
|
6339
|
+
| card-foreground | \`oklch(0.145 0 0)\` | Card text |
|
|
6340
|
+
| primary | \`oklch(0.205 0 0)\` | Buttons, emphasis |
|
|
6341
|
+
| primary-foreground | \`oklch(0.985 0 0)\` | Text on primary |
|
|
6342
|
+
| secondary | \`oklch(0.97 0 0)\` | Secondary backgrounds |
|
|
6343
|
+
| secondary-foreground | \`oklch(0.205 0 0)\` | Text on secondary |
|
|
6344
|
+
| tertiary | \`oklch(0.8544 0.1746 90.88)\` | Accent highlight (yellow-green) |
|
|
6345
|
+
| tertiary-foreground | \`oklch(0.145 0 0)\` | Text on tertiary |
|
|
6346
|
+
| muted | \`oklch(0.97 0 0)\` | Subdued backgrounds |
|
|
6347
|
+
| muted-foreground | \`oklch(0.556 0 0)\` | Subdued text, descriptions |
|
|
6348
|
+
| destructive | \`oklch(0.577 0.245 27.325)\` | Error, danger |
|
|
6349
|
+
| border | \`oklch(0.922 0 0)\` | Borders, dividers |
|
|
6350
|
+
| input | \`oklch(0.922 0 0)\` | Input borders |
|
|
6351
|
+
| ring | \`oklch(0.708 0 0)\` | Focus rings |
|
|
6352
|
+
|
|
6353
|
+
### Dark Mode
|
|
6354
|
+
|
|
6355
|
+
| Token | Value | Usage |
|
|
6356
|
+
|-------|-------|-------|
|
|
6357
|
+
| background | \`oklch(0.145 0 0)\` | Page background |
|
|
6358
|
+
| foreground | \`oklch(0.985 0 0)\` | Primary text |
|
|
6359
|
+
| card | \`oklch(0.205 0 0)\` | Card backgrounds |
|
|
6360
|
+
| card-foreground | \`oklch(0.985 0 0)\` | Card text |
|
|
6361
|
+
| primary | \`oklch(0.922 0 0)\` | Buttons, emphasis |
|
|
6362
|
+
| primary-foreground | \`oklch(0.205 0 0)\` | Text on primary |
|
|
6363
|
+
| secondary | \`oklch(0.269 0 0)\` | Secondary backgrounds |
|
|
6364
|
+
| secondary-foreground | \`oklch(0.985 0 0)\` | Text on secondary |
|
|
6365
|
+
| tertiary | \`oklch(0.8544 0.1746 90.88)\` | Same accent (unchanged) |
|
|
6366
|
+
| tertiary-foreground | \`oklch(0.145 0 0)\` | Text on tertiary |
|
|
6367
|
+
| muted | \`oklch(0.269 0 0)\` | Subdued backgrounds |
|
|
6368
|
+
| muted-foreground | \`oklch(0.708 0 0)\` | Subdued text |
|
|
6369
|
+
| destructive | \`oklch(0.704 0.191 22.216)\` | Error (lighter in dark) |
|
|
6370
|
+
| border | \`oklch(1 0 0 / 10%)\` | Borders (semi-transparent) |
|
|
6371
|
+
| input | \`oklch(1 0 0 / 15%)\` | Input borders |
|
|
6372
|
+
| ring | \`oklch(0.556 0 0)\` | Focus rings |
|
|
6373
|
+
|
|
6374
|
+
### Using Host CSS Variables
|
|
6375
|
+
|
|
6376
|
+
CSS custom properties inherit through Shadow DOM. Prefer variables when possible:
|
|
6377
|
+
|
|
6378
|
+
\`\`\`css
|
|
6379
|
+
.my-card {
|
|
6380
|
+
background: var(--card, oklch(1 0 0));
|
|
6381
|
+
color: var(--card-foreground, oklch(0.145 0 0));
|
|
6382
|
+
border: 1px solid var(--border, oklch(0.922 0 0));
|
|
6383
|
+
}
|
|
6384
|
+
\`\`\`
|
|
6385
|
+
|
|
6386
|
+
The fallback values ensure correct rendering in the dev sandbox where host variables
|
|
6387
|
+
are not present.
|
|
6388
|
+
|
|
6389
|
+
## Typography
|
|
6390
|
+
|
|
6391
|
+
### Fonts
|
|
6392
|
+
|
|
6393
|
+
The host loads **Geist** (sans) and **Geist Mono** via Google Fonts, exposed as CSS variables:
|
|
6394
|
+
|
|
6395
|
+
\`\`\`css
|
|
6396
|
+
font-family: var(--font-geist-sans, system-ui, -apple-system, sans-serif);
|
|
6397
|
+
font-family: var(--font-geist-mono, ui-monospace, monospace);
|
|
6398
|
+
\`\`\`
|
|
6399
|
+
|
|
6400
|
+
These variables inherit through Shadow DOM. Always include system font fallbacks.
|
|
6401
|
+
|
|
6402
|
+
### Type Scale
|
|
6403
|
+
|
|
6404
|
+
| Element | Size | Weight | Extra |
|
|
6405
|
+
|---------|------|--------|-------|
|
|
6406
|
+
| Page heading | \`1.125rem\` (text-lg) | \`700\` (bold) | — |
|
|
6407
|
+
| Card title | \`1.125rem\` (text-lg) | \`600\` (semibold) | — |
|
|
6408
|
+
| Section label | \`0.6875rem\` | \`600\` (semibold) | \`text-transform: uppercase; letter-spacing: 0.1em\` |
|
|
6409
|
+
| Body text | \`0.875rem\` (text-sm) | \`400\` (regular) | — |
|
|
6410
|
+
| Description | \`0.875rem\` (text-sm) | \`400\` | Color: muted-foreground |
|
|
6411
|
+
| Small label | \`0.75rem\` (text-xs) | \`500\` (medium) | — |
|
|
6412
|
+
| Badge / pill | \`0.625rem\` (10px) | \`600\` | — |
|
|
6413
|
+
|
|
6414
|
+
### Heading Pattern (Section Labels)
|
|
6415
|
+
|
|
6416
|
+
The host uses uppercase, letter-spaced, muted headings to label sections:
|
|
6417
|
+
|
|
6418
|
+
\`\`\`css
|
|
6419
|
+
.section-label {
|
|
6420
|
+
font-size: 0.6875rem;
|
|
6421
|
+
font-weight: 600;
|
|
6422
|
+
text-transform: uppercase;
|
|
6423
|
+
letter-spacing: 0.1em;
|
|
6424
|
+
color: oklch(0.556 0 0); /* muted-foreground */
|
|
6425
|
+
}
|
|
6426
|
+
\`\`\`
|
|
6427
|
+
|
|
6428
|
+
## Border Radius
|
|
6429
|
+
|
|
6430
|
+
| Token | Value |
|
|
6431
|
+
|-------|-------|
|
|
6432
|
+
| radius-sm | \`4px\` |
|
|
6433
|
+
| radius-md | \`6px\` |
|
|
6434
|
+
| radius-lg | \`8px\` (base) |
|
|
6435
|
+
| radius-xl | \`12px\` |
|
|
6436
|
+
|
|
6437
|
+
- **Cards**: \`border-radius: 12px\` (rounded-xl) for marketplace-style cards, \`8px\` (rounded-lg) for standard cards
|
|
6438
|
+
- **Buttons**: \`6px\` (rounded-md)
|
|
6439
|
+
- **Badges**: \`6px\` (rounded-md)
|
|
6440
|
+
- **Inputs**: \`6px\` (rounded-md)
|
|
6441
|
+
|
|
6442
|
+
## Card Patterns
|
|
6443
|
+
|
|
6444
|
+
### Standard Card
|
|
6445
|
+
|
|
6446
|
+
\`\`\`css
|
|
6447
|
+
.card {
|
|
6448
|
+
background: oklch(1 0 0);
|
|
6449
|
+
border: 1px solid oklch(0.922 0 0);
|
|
6450
|
+
border-radius: 8px;
|
|
6451
|
+
padding: 1.5rem;
|
|
6452
|
+
box-shadow: 0 1px 2px oklch(0.145 0 0 / 0.04);
|
|
6453
|
+
}
|
|
6454
|
+
\`\`\`
|
|
6455
|
+
|
|
6456
|
+
### Marketplace Agent Card (Interactive)
|
|
6457
|
+
|
|
6458
|
+
\`\`\`css
|
|
6459
|
+
.agent-card {
|
|
6460
|
+
display: flex;
|
|
6461
|
+
flex-direction: column;
|
|
6462
|
+
background: oklch(1 0 0);
|
|
6463
|
+
border: 1px solid oklch(0.922 0 0);
|
|
6464
|
+
border-radius: 12px;
|
|
6465
|
+
padding: 1rem;
|
|
6466
|
+
cursor: pointer;
|
|
6467
|
+
transition: box-shadow 180ms ease;
|
|
6468
|
+
overflow: hidden;
|
|
6469
|
+
}
|
|
6470
|
+
|
|
6471
|
+
.agent-card:hover {
|
|
6472
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
6473
|
+
}
|
|
6474
|
+
\`\`\`
|
|
6475
|
+
|
|
6476
|
+
### Builder Card
|
|
6477
|
+
|
|
6478
|
+
\`\`\`css
|
|
6479
|
+
.builder-card {
|
|
6480
|
+
padding: 1rem;
|
|
6481
|
+
border-radius: 8px;
|
|
6482
|
+
border: 1px solid oklch(0.75 0 0); /* gray-300 equivalent */
|
|
6483
|
+
cursor: pointer;
|
|
6484
|
+
transition: background-color 180ms ease;
|
|
6485
|
+
}
|
|
6486
|
+
|
|
6487
|
+
.builder-card:hover {
|
|
6488
|
+
background: oklch(0.97 0 0); /* gray-50 equivalent */
|
|
6489
|
+
}
|
|
6490
|
+
\`\`\`
|
|
6491
|
+
|
|
6492
|
+
### Accent-Highlighted Card
|
|
6493
|
+
|
|
6494
|
+
Use the tertiary color for feature emphasis:
|
|
6495
|
+
|
|
6496
|
+
\`\`\`css
|
|
6497
|
+
.card--accent {
|
|
6498
|
+
background: linear-gradient(
|
|
6499
|
+
135deg,
|
|
6500
|
+
oklch(0.8544 0.1746 90.88 / 0.06),
|
|
6501
|
+
oklch(0.8544 0.1746 90.88 / 0.02)
|
|
6502
|
+
);
|
|
6503
|
+
border-color: oklch(0.8544 0.1746 90.88 / 0.3);
|
|
6504
|
+
}
|
|
6505
|
+
\`\`\`
|
|
6506
|
+
|
|
6507
|
+
## Button Patterns
|
|
6508
|
+
|
|
6509
|
+
### Primary Button
|
|
6510
|
+
|
|
6511
|
+
\`\`\`css
|
|
6512
|
+
.btn-primary {
|
|
6513
|
+
display: inline-flex;
|
|
6514
|
+
align-items: center;
|
|
6515
|
+
justify-content: center;
|
|
6516
|
+
gap: 0.5rem;
|
|
6517
|
+
height: 2.25rem;
|
|
6518
|
+
padding: 0 1rem;
|
|
6519
|
+
border-radius: 6px;
|
|
6520
|
+
font-size: 0.875rem;
|
|
6521
|
+
font-weight: 500;
|
|
6522
|
+
background: oklch(0.205 0 0);
|
|
6523
|
+
color: oklch(0.985 0 0);
|
|
6524
|
+
border: none;
|
|
6525
|
+
cursor: pointer;
|
|
6526
|
+
transition: opacity 150ms ease;
|
|
6527
|
+
}
|
|
6528
|
+
|
|
6529
|
+
.btn-primary:hover {
|
|
6530
|
+
opacity: 0.9;
|
|
6531
|
+
}
|
|
6532
|
+
\`\`\`
|
|
6533
|
+
|
|
6534
|
+
### Button Sizes
|
|
6535
|
+
|
|
6536
|
+
| Size | Height | Padding | Font |
|
|
6537
|
+
|------|--------|---------|------|
|
|
6538
|
+
| xs | \`1.5rem\` | \`0 0.75rem\` | \`0.75rem\` |
|
|
6539
|
+
| sm | \`2rem\` | \`0 0.75rem\` | \`0.875rem\` |
|
|
6540
|
+
| default | \`2.25rem\` | \`0 1rem\` | \`0.875rem\` |
|
|
6541
|
+
| lg | \`2.5rem\` | \`0 1.5rem\` | \`0.875rem\` |
|
|
6542
|
+
|
|
6543
|
+
### Button Variants
|
|
6544
|
+
|
|
6545
|
+
| Variant | Background | Text | Border |
|
|
6546
|
+
|---------|-----------|------|--------|
|
|
6547
|
+
| default | \`oklch(0.205 0 0)\` | \`oklch(0.985 0 0)\` | none |
|
|
6548
|
+
| outline | \`oklch(1 0 0)\` | \`oklch(0.205 0 0)\` | \`1px solid oklch(0.922 0 0)\` |
|
|
6549
|
+
| secondary | \`oklch(0.97 0 0)\` | \`oklch(0.205 0 0)\` | none |
|
|
6550
|
+
| ghost | \`transparent\` | inherit | none (hover: \`oklch(0.97 0 0)\`) |
|
|
6551
|
+
| destructive | \`oklch(0.577 0.245 27.325)\` | \`white\` | none |
|
|
6552
|
+
| highlight | \`oklch(0.85 0.18 95)\` | \`black\` | none |
|
|
6553
|
+
|
|
6554
|
+
### Disabled State
|
|
6555
|
+
|
|
6556
|
+
\`\`\`css
|
|
6557
|
+
.btn:disabled {
|
|
6558
|
+
pointer-events: none;
|
|
6559
|
+
background: oklch(0.9 0 0);
|
|
6560
|
+
color: oklch(0.556 0 0);
|
|
6561
|
+
}
|
|
6562
|
+
\`\`\`
|
|
6563
|
+
|
|
6564
|
+
## Input Patterns
|
|
6565
|
+
|
|
6566
|
+
\`\`\`css
|
|
6567
|
+
.input {
|
|
6568
|
+
height: 2.25rem;
|
|
6569
|
+
width: 100%;
|
|
6570
|
+
border-radius: 6px;
|
|
6571
|
+
border: 1px solid oklch(0.922 0 0);
|
|
6572
|
+
background: transparent;
|
|
6573
|
+
padding: 0.25rem 0.75rem;
|
|
6574
|
+
font-size: 0.875rem;
|
|
6575
|
+
transition: box-shadow 150ms ease, border-color 150ms ease;
|
|
6576
|
+
}
|
|
6577
|
+
|
|
6578
|
+
.input::placeholder {
|
|
6579
|
+
color: oklch(0.556 0 0);
|
|
6580
|
+
}
|
|
6581
|
+
|
|
6582
|
+
.input:focus-visible {
|
|
6583
|
+
border-color: oklch(0.708 0 0);
|
|
6584
|
+
outline: none;
|
|
6585
|
+
box-shadow: 0 0 0 3px oklch(0.708 0 0 / 0.5);
|
|
6586
|
+
}
|
|
6587
|
+
\`\`\`
|
|
6588
|
+
|
|
6589
|
+
## Badge Patterns
|
|
6590
|
+
|
|
6591
|
+
\`\`\`css
|
|
6592
|
+
.badge {
|
|
6593
|
+
display: inline-flex;
|
|
6594
|
+
align-items: center;
|
|
6595
|
+
border-radius: 6px;
|
|
6596
|
+
padding: 0.125rem 0.625rem;
|
|
6597
|
+
font-size: 0.75rem;
|
|
6598
|
+
font-weight: 600;
|
|
6599
|
+
border: 1px solid transparent;
|
|
6600
|
+
}
|
|
6601
|
+
|
|
6602
|
+
.badge--default {
|
|
6603
|
+
background: oklch(0.205 0 0);
|
|
6604
|
+
color: oklch(0.985 0 0);
|
|
6605
|
+
}
|
|
6606
|
+
|
|
6607
|
+
.badge--secondary {
|
|
6608
|
+
background: oklch(0.97 0 0);
|
|
6609
|
+
color: oklch(0.205 0 0);
|
|
6610
|
+
}
|
|
6611
|
+
|
|
6612
|
+
.badge--outline {
|
|
6613
|
+
background: transparent;
|
|
6614
|
+
border-color: oklch(0.922 0 0);
|
|
6615
|
+
color: oklch(0.145 0 0);
|
|
6616
|
+
}
|
|
6617
|
+
\`\`\`
|
|
6618
|
+
|
|
6619
|
+
### Version Badge (Small)
|
|
6620
|
+
|
|
6621
|
+
Used on marketplace cards for version labels:
|
|
6622
|
+
|
|
6623
|
+
\`\`\`css
|
|
6624
|
+
.version-badge {
|
|
6625
|
+
font-size: 0.625rem;
|
|
6626
|
+
line-height: 1;
|
|
6627
|
+
padding: 0.125rem 0.375rem;
|
|
6628
|
+
border-radius: 4px;
|
|
6629
|
+
background: oklch(0.97 0 0);
|
|
6630
|
+
color: oklch(0.45 0 0);
|
|
6631
|
+
border: 1px solid oklch(0.922 0 0);
|
|
6632
|
+
}
|
|
6633
|
+
\`\`\`
|
|
6634
|
+
|
|
6635
|
+
## Layout
|
|
6636
|
+
|
|
6637
|
+
### Content Container
|
|
6638
|
+
|
|
6639
|
+
The host's content area uses responsive horizontal padding and a max-width:
|
|
6640
|
+
|
|
6641
|
+
\`\`\`css
|
|
6642
|
+
.container {
|
|
6643
|
+
margin: 0 auto;
|
|
6644
|
+
width: 100%;
|
|
6645
|
+
padding-left: 1rem;
|
|
6646
|
+
padding-right: 1rem;
|
|
6647
|
+
}
|
|
6648
|
+
|
|
6649
|
+
@media (min-width: 1024px) {
|
|
6650
|
+
.container { padding-left: 1.5rem; padding-right: 1.5rem; }
|
|
6651
|
+
}
|
|
6652
|
+
|
|
6653
|
+
@media (min-width: 1280px) {
|
|
6654
|
+
.container { padding-left: 2rem; padding-right: 2rem; }
|
|
6655
|
+
}
|
|
6656
|
+
\`\`\`
|
|
6657
|
+
|
|
6658
|
+
| Container Size | Max Width |
|
|
6659
|
+
|---------------|-----------|
|
|
6660
|
+
| sm | \`768px\` |
|
|
6661
|
+
| md | \`1440px\` |
|
|
6662
|
+
| lg | \`1600px\` |
|
|
6663
|
+
| xl | none (full width) |
|
|
6664
|
+
|
|
6665
|
+
### Spacing Scale
|
|
6666
|
+
|
|
6667
|
+
| Name | Value | Common Usage |
|
|
6668
|
+
|------|-------|------|
|
|
6669
|
+
| xs | \`0.25rem\` (4px) | Tight gaps |
|
|
6670
|
+
| sm | \`0.5rem\` (8px) | Icon gaps, badge padding |
|
|
6671
|
+
| md | \`0.75rem\` (12px) | Grid gaps, small card gaps |
|
|
6672
|
+
| base | \`1rem\` (16px) | Card padding, component spacing |
|
|
6673
|
+
| lg | \`1.5rem\` (24px) | Card content padding, section gaps |
|
|
6674
|
+
| xl | \`2rem\` (32px) | Page padding, section margin |
|
|
6675
|
+
| 2xl | \`3rem\` (48px) | Large vertical sections |
|
|
6676
|
+
|
|
6677
|
+
### Common Layouts
|
|
6678
|
+
|
|
6679
|
+
**Card Grid (3 columns)**
|
|
6680
|
+
\`\`\`css
|
|
6681
|
+
.card-grid {
|
|
6682
|
+
display: flex;
|
|
6683
|
+
flex-wrap: wrap;
|
|
6684
|
+
gap: 1.5rem;
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6687
|
+
.card-grid > * {
|
|
6688
|
+
flex: 0 0 calc(33.333% - 1rem);
|
|
6689
|
+
}
|
|
6690
|
+
\`\`\`
|
|
6691
|
+
|
|
6692
|
+
**Sidebar + Content**
|
|
6693
|
+
\`\`\`css
|
|
6694
|
+
.layout-with-sidebar {
|
|
6695
|
+
display: flex;
|
|
6696
|
+
gap: 2rem;
|
|
6697
|
+
}
|
|
6698
|
+
|
|
6699
|
+
.sidebar {
|
|
6700
|
+
width: 200px;
|
|
6701
|
+
flex-shrink: 0;
|
|
6702
|
+
}
|
|
6703
|
+
|
|
6704
|
+
.content {
|
|
6705
|
+
flex: 1;
|
|
6706
|
+
min-width: 0;
|
|
6707
|
+
}
|
|
6708
|
+
\`\`\`
|
|
6709
|
+
|
|
6710
|
+
## Shadows
|
|
6711
|
+
|
|
6712
|
+
| Level | Value | Usage |
|
|
6713
|
+
|-------|-------|-------|
|
|
6714
|
+
| xs | \`0 1px 2px oklch(0.145 0 0 / 0.04)\` | Buttons, inputs |
|
|
6715
|
+
| sm | \`0 1px 3px oklch(0.145 0 0 / 0.06)\` | Static cards |
|
|
6716
|
+
| card-hover | \`0 2px 6px rgba(0, 0, 0, 0.15)\` | Interactive card hover |
|
|
6717
|
+
| lg | \`0 10px 15px oklch(0.145 0 0 / 0.1)\` | Dialogs, popovers |
|
|
6718
|
+
|
|
6719
|
+
## Focus States
|
|
6720
|
+
|
|
6721
|
+
All interactive elements must show a visible focus ring for accessibility:
|
|
6722
|
+
|
|
6723
|
+
\`\`\`css
|
|
6724
|
+
.interactive:focus-visible {
|
|
6725
|
+
border-color: oklch(0.708 0 0);
|
|
6726
|
+
outline: none;
|
|
6727
|
+
box-shadow: 0 0 0 3px oklch(0.708 0 0 / 0.5);
|
|
6728
|
+
}
|
|
6729
|
+
\`\`\`
|
|
6730
|
+
|
|
6731
|
+
## Transitions
|
|
6732
|
+
|
|
6733
|
+
Consistent timing across all interactive elements:
|
|
6734
|
+
|
|
6735
|
+
\`\`\`css
|
|
6736
|
+
transition: 180ms ease; /* default for hover/shadow/transform */
|
|
6737
|
+
transition: 150ms ease; /* faster for opacity/color */
|
|
6738
|
+
\`\`\`
|
|
6739
|
+
|
|
6740
|
+
## Navigation Routes
|
|
6741
|
+
|
|
6742
|
+
The host application has these routes. Use \`bridge.navigation.navigate(path)\` to link:
|
|
6743
|
+
|
|
6744
|
+
| Route | Page |
|
|
6745
|
+
|-------|------|
|
|
6746
|
+
| \`/chat\` | Chat with agents |
|
|
6747
|
+
| \`/marketplace\` | Marketplace landing |
|
|
6748
|
+
| \`/marketplace/agents?state=PUBLISHED\` | Published agents |
|
|
6749
|
+
| \`/playground\` | Prompt playground |
|
|
6750
|
+
| \`/builder\` | Agent builder |
|
|
6751
|
+
| \`/documents\` | Document library |
|
|
6752
|
+
| \`/administration\` | Admin panel (admins only) |
|
|
6753
|
+
|
|
6754
|
+
## Dark Mode Support
|
|
6755
|
+
|
|
6756
|
+
Dark mode is toggled via the \`.dark\` class on the \`<html>\` element (class-based, not media query).
|
|
6757
|
+
Use \`bridge.theme.current()\` to detect and \`bridge.events.on("theme-changed", ...)\` to react.
|
|
6758
|
+
|
|
6759
|
+
For CSS-only dark mode in your shadow DOM, use the host's CSS variables with fallbacks:
|
|
6760
|
+
|
|
6761
|
+
\`\`\`css
|
|
6762
|
+
.card {
|
|
6763
|
+
background: var(--card, oklch(1 0 0));
|
|
6764
|
+
color: var(--card-foreground, oklch(0.145 0 0));
|
|
6765
|
+
border-color: var(--border, oklch(0.922 0 0));
|
|
6766
|
+
}
|
|
6767
|
+
\`\`\`
|
|
6768
|
+
|
|
6769
|
+
When the host toggles dark mode, its CSS variables change automatically and your component
|
|
6770
|
+
inherits the new values through the Shadow DOM. No JavaScript needed if you use variables.
|
|
6771
|
+
|
|
6772
|
+
For bridge-driven dark mode with separate value sets:
|
|
6773
|
+
|
|
6774
|
+
${getDarkModeExample(framework)}
|
|
6775
|
+
|
|
6776
|
+
## Icons
|
|
6777
|
+
|
|
6778
|
+
The host uses **Lucide React** icons. Mini-apps should NOT depend on Lucide — it adds
|
|
6779
|
+
unnecessary bundle size. Instead:
|
|
6780
|
+
|
|
6781
|
+
1. **Inline SVG** (preferred) — Copy the SVG paths from [lucide.dev](https://lucide.dev).
|
|
6782
|
+
Use \`viewBox="0 0 24 24"\`, \`fill="none"\`, \`stroke="currentColor"\`, \`stroke-width="2"\`.
|
|
6783
|
+
2. **Single icon import** — If you must use Lucide, import individual icons:
|
|
6784
|
+
\`import { Search } from "lucide-react"\` (NOT the barrel \`lucide-react\` import).
|
|
6785
|
+
|
|
6786
|
+
## Anti-Patterns
|
|
6787
|
+
|
|
6788
|
+
These break visual consistency with the host:
|
|
6789
|
+
|
|
6790
|
+
- **Don't use oklch hue for primary UI.** The host is grayscale-first. Color is reserved for the
|
|
6791
|
+
tertiary accent (\`oklch(0.8544 0.1746 90.88)\`), destructive actions, and chart data.
|
|
6792
|
+
- **Don't use colored backgrounds for sections.** The host uses white/near-white backgrounds with
|
|
6793
|
+
subtle borders to separate content. Colored section backgrounds look foreign.
|
|
6794
|
+
- **Don't use rounded-full on cards.** Cards use \`8px\` or \`12px\` radius, never pill shapes.
|
|
6795
|
+
- **Don't add drop shadows to static elements.** The host uses flat design with \`shadow-sm\` at most.
|
|
6796
|
+
Shadows are interactive cues (hover state), not decoration.
|
|
6797
|
+
- **Don't use custom fonts.** Stick to the Geist font family inherited from the host.
|
|
6798
|
+
- **Don't use thick borders.** The host uses \`1px\` borders exclusively.
|
|
6799
|
+
- **Don't use bright/saturated colors for UI chrome.** The grayscale palette is intentional.
|
|
6800
|
+
|
|
6801
|
+
${stylingApproach}
|
|
6802
|
+
|
|
6803
|
+
## Complete Example: Minimal Card Component
|
|
6804
|
+
|
|
6805
|
+
${getCardExample(framework)}
|
|
6806
|
+
`;
|
|
6807
|
+
}
|
|
6808
|
+
function getStylingApproach(framework) {
|
|
6809
|
+
switch (framework) {
|
|
6810
|
+
case "react":
|
|
6811
|
+
return `## Styling Approach (React)
|
|
6812
|
+
|
|
6813
|
+
Use the \`?inline\` import pattern with \`adoptedStyleSheets\`:
|
|
6814
|
+
|
|
6815
|
+
\`\`\`tsx
|
|
6816
|
+
// src/main.tsx
|
|
6817
|
+
import styles from "./styles.css?inline";
|
|
6818
|
+
|
|
6819
|
+
connectedCallback() {
|
|
6820
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
6821
|
+
const sheet = new CSSStyleSheet();
|
|
6822
|
+
sheet.replaceSync(styles);
|
|
6823
|
+
shadow.adoptedStyleSheets = [sheet];
|
|
6824
|
+
// ... create container and React root
|
|
6825
|
+
}
|
|
6826
|
+
\`\`\`
|
|
6827
|
+
|
|
6828
|
+
Add a TypeScript declaration for the \`?inline\` import:
|
|
6829
|
+
|
|
6830
|
+
\`\`\`ts
|
|
6831
|
+
// src/vite-env.d.ts
|
|
6832
|
+
declare module "*.css?inline" {
|
|
6833
|
+
const css: string;
|
|
6834
|
+
export default css;
|
|
6835
|
+
}
|
|
6836
|
+
\`\`\`
|
|
6837
|
+
|
|
6838
|
+
Then write plain CSS in \`src/styles.css\` using the token values from this guide.`;
|
|
6839
|
+
case "vue":
|
|
6840
|
+
return `## Styling Approach (Vue)
|
|
6841
|
+
|
|
6842
|
+
Vue SFC \`<style>\` blocks are automatically injected into the shadow DOM when using
|
|
6843
|
+
\`defineCustomElement\`:
|
|
6844
|
+
|
|
6845
|
+
\`\`\`vue
|
|
6846
|
+
<style scoped>
|
|
6847
|
+
.card {
|
|
6848
|
+
background: var(--card, oklch(1 0 0));
|
|
6849
|
+
border: 1px solid var(--border, oklch(0.922 0 0));
|
|
6850
|
+
border-radius: 8px;
|
|
6851
|
+
padding: 1.5rem;
|
|
6852
|
+
}
|
|
6853
|
+
</style>
|
|
6854
|
+
\`\`\`
|
|
6855
|
+
|
|
6856
|
+
Use the oklch token values from this guide in your scoped styles.`;
|
|
6857
|
+
case "vanilla":
|
|
6858
|
+
return `## Styling Approach (Vanilla)
|
|
6859
|
+
|
|
6860
|
+
Use adopted stylesheets for best performance:
|
|
6861
|
+
|
|
6862
|
+
\`\`\`ts
|
|
6863
|
+
const sheet = new CSSStyleSheet();
|
|
6864
|
+
sheet.replaceSync(\`
|
|
6865
|
+
.card {
|
|
6866
|
+
background: oklch(1 0 0);
|
|
6867
|
+
border: 1px solid oklch(0.922 0 0);
|
|
6868
|
+
border-radius: 8px;
|
|
6869
|
+
padding: 1.5rem;
|
|
6870
|
+
}
|
|
6871
|
+
\`);
|
|
6872
|
+
this._shadow.adoptedStyleSheets = [sheet];
|
|
6873
|
+
\`\`\`
|
|
6874
|
+
|
|
6875
|
+
Or use a separate CSS file with Vite's \`?inline\` import.`;
|
|
6876
|
+
default:
|
|
6877
|
+
return "";
|
|
6878
|
+
}
|
|
6879
|
+
}
|
|
6880
|
+
function getDarkModeExample(framework) {
|
|
6881
|
+
switch (framework) {
|
|
6882
|
+
case "react":
|
|
6883
|
+
return `\`\`\`tsx
|
|
6884
|
+
function App({ bridge }: VibeProps) {
|
|
6885
|
+
const [theme, setTheme] = useState(bridge.theme?.current() ?? "light");
|
|
6886
|
+
|
|
6887
|
+
useEffect(() => {
|
|
6888
|
+
const unsub = bridge.events?.on("theme-changed", (payload) => {
|
|
6889
|
+
setTheme((payload as { theme: string }).theme);
|
|
6890
|
+
});
|
|
6891
|
+
return unsub;
|
|
6892
|
+
}, [bridge]);
|
|
6893
|
+
|
|
6894
|
+
const isDark = theme === "dark";
|
|
6895
|
+
// Use isDark to toggle class names or inline styles
|
|
6896
|
+
}
|
|
6897
|
+
\`\`\``;
|
|
6898
|
+
case "vue":
|
|
6899
|
+
return `\`\`\`vue
|
|
6900
|
+
<script setup lang="ts">
|
|
6901
|
+
import { ref, onMounted, onUnmounted } from "vue";
|
|
6902
|
+
import type { Bridge } from "@aiworkbench/vibe-types";
|
|
6903
|
+
|
|
6904
|
+
const props = defineProps<{ bridge: Bridge }>();
|
|
6905
|
+
const theme = ref(props.bridge.theme?.current() ?? "light");
|
|
6906
|
+
let unsub: (() => void) | undefined;
|
|
6907
|
+
|
|
6908
|
+
onMounted(() => {
|
|
6909
|
+
unsub = props.bridge.events?.on("theme-changed", (payload) => {
|
|
6910
|
+
theme.value = (payload as { theme: string }).theme;
|
|
6911
|
+
});
|
|
6912
|
+
});
|
|
6913
|
+
onUnmounted(() => unsub?.());
|
|
6914
|
+
</script>
|
|
6915
|
+
\`\`\``;
|
|
6916
|
+
case "vanilla":
|
|
6917
|
+
return `\`\`\`ts
|
|
6918
|
+
set bridge(b: Bridge) {
|
|
6919
|
+
this._bridge = b;
|
|
6920
|
+
this._theme = b.theme?.current() ?? "light";
|
|
6921
|
+
|
|
6922
|
+
this._cleanups.push(
|
|
6923
|
+
b.events?.on("theme-changed", (payload) => {
|
|
6924
|
+
this._theme = (payload as { theme: string }).theme;
|
|
6925
|
+
this.updateTheme();
|
|
6926
|
+
}) ?? (() => {})
|
|
6927
|
+
);
|
|
6928
|
+
|
|
6929
|
+
this.render();
|
|
6930
|
+
}
|
|
6931
|
+
\`\`\``;
|
|
6932
|
+
default:
|
|
6933
|
+
return "";
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6936
|
+
function getCardExample(framework) {
|
|
6937
|
+
switch (framework) {
|
|
6938
|
+
case "react":
|
|
6939
|
+
return `\`\`\`tsx
|
|
6940
|
+
interface CardProps {
|
|
6941
|
+
title: string;
|
|
6942
|
+
description: string;
|
|
6943
|
+
onClick?: () => void;
|
|
6944
|
+
}
|
|
6945
|
+
|
|
6946
|
+
function Card({ title, description, onClick }: CardProps) {
|
|
6947
|
+
return (
|
|
6948
|
+
<div
|
|
6949
|
+
className="card"
|
|
6950
|
+
role={onClick ? "button" : undefined}
|
|
6951
|
+
tabIndex={onClick ? 0 : undefined}
|
|
6952
|
+
onClick={onClick}
|
|
6953
|
+
onKeyDown={(e) => {
|
|
6954
|
+
if (onClick && (e.key === "Enter" || e.key === " ")) {
|
|
6955
|
+
e.preventDefault();
|
|
6956
|
+
onClick();
|
|
6957
|
+
}
|
|
6958
|
+
}}
|
|
6959
|
+
>
|
|
6960
|
+
<div className="card-title">{title}</div>
|
|
6961
|
+
<div className="card-desc">{description}</div>
|
|
6962
|
+
</div>
|
|
6963
|
+
);
|
|
6964
|
+
}
|
|
6965
|
+
\`\`\`
|
|
6966
|
+
|
|
6967
|
+
\`\`\`css
|
|
6968
|
+
/* styles.css */
|
|
6969
|
+
.card {
|
|
6970
|
+
background: var(--card, oklch(1 0 0));
|
|
6971
|
+
color: var(--card-foreground, oklch(0.145 0 0));
|
|
6972
|
+
border: 1px solid var(--border, oklch(0.922 0 0));
|
|
6973
|
+
border-radius: 12px;
|
|
6974
|
+
padding: 1rem;
|
|
6975
|
+
cursor: pointer;
|
|
6976
|
+
transition: box-shadow 180ms ease;
|
|
6977
|
+
user-select: none;
|
|
6978
|
+
}
|
|
6979
|
+
|
|
6980
|
+
.card:hover {
|
|
6981
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
6982
|
+
}
|
|
6983
|
+
|
|
6984
|
+
.card-title {
|
|
6985
|
+
font-size: 1.125rem;
|
|
6986
|
+
font-weight: 600;
|
|
6987
|
+
color: var(--foreground, oklch(0.205 0 0));
|
|
6988
|
+
}
|
|
6989
|
+
|
|
6990
|
+
.card-desc {
|
|
6991
|
+
margin-top: 0.5rem;
|
|
6992
|
+
font-size: 0.875rem;
|
|
6993
|
+
color: var(--muted-foreground, oklch(0.556 0 0));
|
|
6994
|
+
line-height: 1.5;
|
|
6995
|
+
}
|
|
6996
|
+
\`\`\``;
|
|
6997
|
+
case "vue":
|
|
6998
|
+
return `\`\`\`vue
|
|
6999
|
+
<template>
|
|
7000
|
+
<div
|
|
7001
|
+
class="card"
|
|
7002
|
+
:role="onClick ? 'button' : undefined"
|
|
7003
|
+
:tabindex="onClick ? 0 : undefined"
|
|
7004
|
+
@click="onClick?.()"
|
|
7005
|
+
@keydown.enter.space.prevent="onClick?.()"
|
|
7006
|
+
>
|
|
7007
|
+
<div class="card-title">{{ title }}</div>
|
|
7008
|
+
<div class="card-desc">{{ description }}</div>
|
|
7009
|
+
</div>
|
|
7010
|
+
</template>
|
|
7011
|
+
|
|
7012
|
+
<script setup lang="ts">
|
|
7013
|
+
defineProps<{
|
|
7014
|
+
title: string;
|
|
7015
|
+
description: string;
|
|
7016
|
+
onClick?: () => void;
|
|
7017
|
+
}>();
|
|
7018
|
+
</script>
|
|
7019
|
+
|
|
7020
|
+
<style scoped>
|
|
7021
|
+
.card {
|
|
7022
|
+
background: var(--card, oklch(1 0 0));
|
|
7023
|
+
color: var(--card-foreground, oklch(0.145 0 0));
|
|
7024
|
+
border: 1px solid var(--border, oklch(0.922 0 0));
|
|
7025
|
+
border-radius: 12px;
|
|
7026
|
+
padding: 1rem;
|
|
7027
|
+
cursor: pointer;
|
|
7028
|
+
transition: box-shadow 180ms ease;
|
|
7029
|
+
user-select: none;
|
|
7030
|
+
}
|
|
7031
|
+
|
|
7032
|
+
.card:hover {
|
|
7033
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
7034
|
+
}
|
|
7035
|
+
|
|
7036
|
+
.card-title {
|
|
7037
|
+
font-size: 1.125rem;
|
|
7038
|
+
font-weight: 600;
|
|
7039
|
+
}
|
|
7040
|
+
|
|
7041
|
+
.card-desc {
|
|
7042
|
+
margin-top: 0.5rem;
|
|
7043
|
+
font-size: 0.875rem;
|
|
7044
|
+
color: var(--muted-foreground, oklch(0.556 0 0));
|
|
7045
|
+
line-height: 1.5;
|
|
7046
|
+
}
|
|
7047
|
+
</style>
|
|
7048
|
+
\`\`\``;
|
|
7049
|
+
case "vanilla":
|
|
7050
|
+
return `\`\`\`ts
|
|
7051
|
+
private render() {
|
|
7052
|
+
const sheet = new CSSStyleSheet();
|
|
7053
|
+
sheet.replaceSync(\`
|
|
7054
|
+
.card {
|
|
7055
|
+
background: var(--card, oklch(1 0 0));
|
|
7056
|
+
color: var(--card-foreground, oklch(0.145 0 0));
|
|
7057
|
+
border: 1px solid var(--border, oklch(0.922 0 0));
|
|
7058
|
+
border-radius: 12px;
|
|
7059
|
+
padding: 1rem;
|
|
7060
|
+
cursor: pointer;
|
|
7061
|
+
transition: box-shadow 180ms ease;
|
|
7062
|
+
user-select: none;
|
|
7063
|
+
}
|
|
7064
|
+
.card:hover {
|
|
7065
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
|
|
7066
|
+
}
|
|
7067
|
+
.card-title {
|
|
7068
|
+
font-size: 1.125rem;
|
|
7069
|
+
font-weight: 600;
|
|
7070
|
+
}
|
|
7071
|
+
.card-desc {
|
|
7072
|
+
margin-top: 0.5rem;
|
|
7073
|
+
font-size: 0.875rem;
|
|
7074
|
+
color: var(--muted-foreground, oklch(0.556 0 0));
|
|
7075
|
+
line-height: 1.5;
|
|
7076
|
+
}
|
|
7077
|
+
\`);
|
|
7078
|
+
this._shadow.adoptedStyleSheets = [sheet];
|
|
7079
|
+
|
|
7080
|
+
const card = document.createElement("div");
|
|
7081
|
+
card.className = "card";
|
|
7082
|
+
card.innerHTML = \`
|
|
7083
|
+
<div class="card-title">\${this.escapeHtml(this.title)}</div>
|
|
7084
|
+
<div class="card-desc">\${this.escapeHtml(this.description)}</div>
|
|
7085
|
+
\`;
|
|
7086
|
+
this._shadow.appendChild(card);
|
|
7087
|
+
}
|
|
7088
|
+
\`\`\``;
|
|
7089
|
+
default:
|
|
7090
|
+
return "";
|
|
7091
|
+
}
|
|
7092
|
+
}
|
|
7093
|
+
|
|
6303
7094
|
// src/scaffold/write-agent-docs.ts
|
|
6304
7095
|
var PERMISSION_DOCS = {
|
|
6305
7096
|
auth: `### auth
|
|
@@ -6530,7 +7321,8 @@ function generateEmbeddedSkills(config) {
|
|
|
6530
7321
|
{ name: "Performance", content: generatePerformance(config) },
|
|
6531
7322
|
{ name: "Debugging", content: generateDebugging(config) },
|
|
6532
7323
|
{ name: "Navigation Patterns", content: generateNavigationPatterns(config) },
|
|
6533
|
-
{ name: "Asset Handling", content: generateAssetHandling(config) }
|
|
7324
|
+
{ name: "Asset Handling", content: generateAssetHandling(config) },
|
|
7325
|
+
{ name: "Host Design System", content: generateHostDesignSystem(config) }
|
|
6534
7326
|
];
|
|
6535
7327
|
if (config.testing) {
|
|
6536
7328
|
skills.push({ name: "Testing Guide", content: generateTestingGuide(config) });
|
|
@@ -6920,12 +7712,16 @@ function ciWorkflow(registryRepo, githubHost) {
|
|
|
6920
7712
|
` : "";
|
|
6921
7713
|
return `# Auto-generated by vibe-kit — CI/CD for this mini-app.
|
|
6922
7714
|
#
|
|
6923
|
-
# Required repository secrets:
|
|
7715
|
+
# Required repository secrets (dev):
|
|
6924
7716
|
# VIBE_STORAGE_ACCOUNT_NAME — Azure Storage account name
|
|
6925
7717
|
# VIBE_STORAGE_ACCOUNT_KEY — Azure Storage account key
|
|
6926
7718
|
# VIBE_STORAGE_CONTAINER_NAME — Azure container for bundles
|
|
6927
7719
|
# VIBE_REGISTRY_REPO — e.g. "${registryRepo}"
|
|
6928
7720
|
# VIBE_REGISTRY_TOKEN — PAT with repo write access to the registry
|
|
7721
|
+
#
|
|
7722
|
+
# For staging/prod, use environment-scoped secrets (e.g. VIBE_STORAGE_ACCOUNT_KEY_STAGING).
|
|
7723
|
+
# Each environment should use separate Azure Storage accounts so that dev credentials
|
|
7724
|
+
# cannot write to staging/prod. See vibe-publish docs for the full scoping model.
|
|
6929
7725
|
${gheComment}
|
|
6930
7726
|
name: CI
|
|
6931
7727
|
|
|
@@ -7122,6 +7918,19 @@ async function createCommand(rawName) {
|
|
|
7122
7918
|
}
|
|
7123
7919
|
projectName = converted;
|
|
7124
7920
|
}
|
|
7921
|
+
if (!isValidCustomElementName(projectName)) {
|
|
7922
|
+
const suggested = `vibe-${projectName}`;
|
|
7923
|
+
warn(`"${projectName}" is not a valid custom element name (must contain a hyphen).`);
|
|
7924
|
+
const ok = await dist_default3({
|
|
7925
|
+
message: `Use "${suggested}" instead?`,
|
|
7926
|
+
default: true
|
|
7927
|
+
});
|
|
7928
|
+
if (!ok) {
|
|
7929
|
+
error("Aborted.");
|
|
7930
|
+
process.exit(1);
|
|
7931
|
+
}
|
|
7932
|
+
projectName = suggested;
|
|
7933
|
+
}
|
|
7125
7934
|
const outputDir = resolve2(process.cwd(), projectName);
|
|
7126
7935
|
if (await dirExists(outputDir)) {
|
|
7127
7936
|
const overwrite = await dist_default3({
|