@birdapi/velinstyle 0.6.1 → 0.8.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.
- package/README.de.md +337 -316
- package/README.md +33 -12
- package/cli/blueprint.js +8 -0
- package/cli/blueprints/bottom-nav-mobile.html +17 -0
- package/cli/blueprints/cookie-consent.html +9 -0
- package/cli/blueprints/empty-state.html +5 -0
- package/cli/blueprints/filter-bar.html +15 -0
- package/cli/blueprints/notification-center.html +13 -0
- package/cli/blueprints/onboarding.html +23 -0
- package/cli/blueprints/pricing-table.html +20 -0
- package/cli/blueprints/settings-panel.html +20 -0
- package/cli/index.js +119 -3
- package/cli/layout-audit.js +325 -0
- package/cli/scaffold-recipes.json +70 -0
- package/cli/scaffold.js +155 -0
- package/cli/scanner.js +114 -0
- package/components/focus-manager.js +106 -80
- package/components/index.js +20 -1
- package/components/sanitize.js +29 -3
- package/components/shadow-a11y-styles.js +18 -0
- package/components/velin-accordion.js +112 -98
- package/components/velin-announcer.js +35 -0
- package/components/velin-bottom-nav.js +89 -0
- package/components/velin-carousel.js +40 -5
- package/components/velin-collapse.js +95 -65
- package/components/velin-combobox.js +149 -0
- package/components/velin-command.js +127 -0
- package/components/velin-counter.js +152 -0
- package/components/velin-drawer.js +6 -3
- package/components/velin-dropdown.js +33 -2
- package/components/velin-flip.js +220 -0
- package/components/velin-icon.js +43 -9
- package/components/velin-live-dot.js +85 -0
- package/components/velin-menubar.js +83 -0
- package/components/velin-modal.js +3 -1
- package/components/velin-popover.js +61 -21
- package/components/velin-rating.js +91 -0
- package/components/velin-reveal.js +80 -0
- package/components/velin-segmented-control.js +108 -0
- package/components/velin-sheet.js +107 -0
- package/components/velin-sparkline.js +207 -0
- package/components/velin-theme-toggle.js +277 -60
- package/components/velin-tooltip-wc.js +26 -2
- package/dist/velinstyle-components.iife.js +1849 -120
- package/dist/velinstyle-components.js +1871 -120
- package/dist/velinstyle-components.min.js +434 -91
- package/dist/velinstyle.css +640 -44
- package/dist/velinstyle.min.css +1 -1
- package/package.json +7 -3
- package/src/a11y/focus-not-obscured.css +21 -0
- package/src/a11y/forced-colors.css +86 -38
- package/src/a11y/high-contrast-aaa.css +37 -0
- package/src/a11y/preferences.css +106 -85
- package/src/a11y/security.css +119 -104
- package/src/a11y/target-size.css +32 -0
- package/src/base/reset.css +12 -1
- package/src/base/root.css +4 -4
- package/src/components/nav.css +152 -151
- package/src/tokens/motion.css +7 -0
- package/src/utilities/animation.css +97 -1
- package/src/utilities/chart-animation.css +101 -0
- package/src/utilities/filter-effects.css +103 -0
- package/src/utilities/safe-area.css +39 -0
- package/src/velinstyle.css +9 -1
package/README.md
CHANGED
|
@@ -8,15 +8,14 @@
|
|
|
8
8
|
<br>
|
|
9
9
|
|
|
10
10
|
[](LICENSE)
|
|
11
|
-
[](CHANGELOG.md)
|
|
12
12
|
[](docs/a11y.html)
|
|
13
13
|
[]()
|
|
14
14
|
[]()
|
|
15
|
-
[](https://www.npmjs.com/package/@birdapi/velinstyle)
|
|
16
|
+
[](https://skylitedesign.github.io/velinstyle/)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
[Website](https://velinstyle.info) · [Docs](docs/index.html) · [Samples](samples/) · [Playground](tools/playground/index.html) · [Theme Builder](tools/theme-builder/index.html) · [Issues](https://github.com/SkyliteDesign/velinstyle/issues)
|
|
18
|
+
[Documentation](https://skylitedesign.github.io/velinstyle/) · [npm](https://www.npmjs.com/package/@birdapi/velinstyle) · [Samples](samples/) · [Playground](tools/playground/index.html) · [Theme Builder](tools/theme-builder/index.html) · [Issues](https://github.com/SkyliteDesign/velinstyle/issues)
|
|
20
19
|
|
|
21
20
|
**English** · **[Deutsch](README.de.md)**
|
|
22
21
|
|
|
@@ -74,8 +73,8 @@ Teams ship UI under pressure. **Accessibility** and **consistent theming** are o
|
|
|
74
73
|
<tr><td align="center">🎨</td><td><strong>OKLCH + 13 theme presets</strong></td><td>Perceptually uniform colors; dark mode via token swap</td></tr>
|
|
75
74
|
<tr><td align="center">📦</td><td><strong>~46 KB CSS + ~66 KB JS (min)</strong></td><td>Lean defaults vs. heavier all-in-one stacks</td></tr>
|
|
76
75
|
<tr><td align="center">📐</td><td><strong>Container Queries + utilities</strong></td><td>Components adapt to <em>their</em> container, not only the viewport</td></tr>
|
|
77
|
-
<tr><td align="center">🧩</td><td><strong>25 CSS modules ·
|
|
78
|
-
<tr><td align="center">🛠️</td><td><strong>Optional CLI</strong></td><td><code>init</code>, <code>build</code>, <code>icons</code>, <code>scan</code>, <code>prefix</code>, <code>blueprint</code>, <code>tokens build</code></td></tr>
|
|
76
|
+
<tr><td align="center">🧩</td><td><strong>25 CSS modules · 29 Web Components</strong></td><td>Modals, tabs, drawers, toasts, icons—documented APIs in <a href="docs/css-components.html">CSS</a> & <a href="docs/components.html">WC docs</a></td></tr>
|
|
77
|
+
<tr><td align="center">🛠️</td><td><strong>Optional CLI</strong></td><td><code>init</code>, <code>build</code>, <code>icons</code>, <code>scan</code>, <code>prefix</code>, <code>blueprint</code>, <code>scaffold</code>, <code>layout</code>, <code>tokens build</code></td></tr>
|
|
79
78
|
<tr><td align="center">🌍</td><td><strong>RTL-ready</strong></td><td>Logical properties and layout-minded defaults · <a href="samples/rtl.html">RTL sample</a></td></tr>
|
|
80
79
|
</tbody>
|
|
81
80
|
</table>
|
|
@@ -114,16 +113,16 @@ npm install @birdapi/velinstyle
|
|
|
114
113
|
### <span>②</span> Link CSS + components (ES modules)
|
|
115
114
|
|
|
116
115
|
```html
|
|
117
|
-
<link rel="stylesheet" href="node_modules/velinstyle/dist/velinstyle.min.css">
|
|
118
|
-
<script type="module" src="node_modules/velinstyle/dist/velinstyle-components.min.js"></script>
|
|
116
|
+
<link rel="stylesheet" href="node_modules/@birdapi/velinstyle/dist/velinstyle.min.css">
|
|
117
|
+
<script type="module" src="node_modules/@birdapi/velinstyle/dist/velinstyle-components.min.js"></script>
|
|
119
118
|
```
|
|
120
119
|
|
|
121
120
|
<details>
|
|
122
121
|
<summary><strong>CDN alternative</strong> (skip npm install)</summary>
|
|
123
122
|
|
|
124
123
|
```html
|
|
125
|
-
<link rel="stylesheet" href="https://unpkg.com/velinstyle@latest/dist/velinstyle.min.css">
|
|
126
|
-
<script type="module" src="https://unpkg.com/velinstyle@latest/dist/velinstyle-components.min.js"></script>
|
|
124
|
+
<link rel="stylesheet" href="https://unpkg.com/@birdapi/velinstyle@latest/dist/velinstyle.min.css">
|
|
125
|
+
<script type="module" src="https://unpkg.com/@birdapi/velinstyle@latest/dist/velinstyle-components.min.js"></script>
|
|
127
126
|
```
|
|
128
127
|
|
|
129
128
|
</details>
|
|
@@ -226,7 +225,29 @@ All commands: `npx velinstyle <command>` · `npx velinstyle --help`
|
|
|
226
225
|
</details>
|
|
227
226
|
|
|
228
227
|
<details>
|
|
229
|
-
<summary><strong>
|
|
228
|
+
<summary><strong>scaffold</strong> — prompt → HTML (0.8.0)</summary>
|
|
229
|
+
|
|
230
|
+
- **`npx velinstyle scaffold list-intents`**
|
|
231
|
+
- **`npx velinstyle scaffold "Navbar with search" -o nav.html`**
|
|
232
|
+
- **`npx velinstyle scaffold "…" --json`** — for agents/CI
|
|
233
|
+
|
|
234
|
+
See [docs/guides/prompt-scaffolding.html](docs/guides/prompt-scaffolding.html).
|
|
235
|
+
|
|
236
|
+
</details>
|
|
237
|
+
|
|
238
|
+
<details>
|
|
239
|
+
<summary><strong>layout</strong> — responsive audit (0.8.0)</summary>
|
|
240
|
+
|
|
241
|
+
- **`npx velinstyle layout audit [path]`**
|
|
242
|
+
- **`npx velinstyle layout suggest [path]`**
|
|
243
|
+
- **`npx velinstyle layout fix [path] --write`**
|
|
244
|
+
|
|
245
|
+
See [docs/guides/responsive-layout.html](docs/guides/responsive-layout.html).
|
|
246
|
+
|
|
247
|
+
</details>
|
|
248
|
+
|
|
249
|
+
<details>
|
|
250
|
+
<summary><strong>blueprint</strong> — 22 HTML snippets</summary>
|
|
230
251
|
|
|
231
252
|
- **`npx velinstyle blueprint list`**
|
|
232
253
|
- **`npx velinstyle blueprint <name> -o snippet.html`**
|
package/cli/blueprint.js
CHANGED
|
@@ -18,6 +18,14 @@ const BLUEPRINTS = {
|
|
|
18
18
|
'search-field': 'search-field.html',
|
|
19
19
|
'sidebar-layout': 'sidebar-layout.html',
|
|
20
20
|
'table-responsive': 'table-responsive.html',
|
|
21
|
+
'bottom-nav-mobile': 'bottom-nav-mobile.html',
|
|
22
|
+
'empty-state': 'empty-state.html',
|
|
23
|
+
'cookie-consent': 'cookie-consent.html',
|
|
24
|
+
'filter-bar': 'filter-bar.html',
|
|
25
|
+
'notification-center': 'notification-center.html',
|
|
26
|
+
'settings-panel': 'settings-panel.html',
|
|
27
|
+
onboarding: 'onboarding.html',
|
|
28
|
+
'pricing-table': 'pricing-table.html',
|
|
21
29
|
};
|
|
22
30
|
|
|
23
31
|
export function listBlueprints() {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<nav class="velin-pb-safe" aria-label="Primary">
|
|
2
|
+
<velin-bottom-nav current="home">
|
|
3
|
+
<a href="#home" data-nav="home" current>Home</a>
|
|
4
|
+
<a href="#search" data-nav="search">Search</a>
|
|
5
|
+
<a href="#profile" data-nav="profile">Profile</a>
|
|
6
|
+
</velin-bottom-nav>
|
|
7
|
+
</nav>
|
|
8
|
+
<script type="module">
|
|
9
|
+
import '../../dist/velinstyle-components.min.js';
|
|
10
|
+
const nav = document.querySelector('velin-bottom-nav');
|
|
11
|
+
nav?.querySelectorAll('a').forEach((link) => {
|
|
12
|
+
link.addEventListener('click', (e) => {
|
|
13
|
+
e.preventDefault();
|
|
14
|
+
nav.setAttribute('current', link.dataset.nav || '');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
</script>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<aside class="velin-p-4 velin-border velin-rounded-lg velin-pb-safe" role="region" aria-label="Cookie consent">
|
|
2
|
+
<div class="velin-user-content">
|
|
3
|
+
<p class="velin-mbe-3">We use essential cookies only. No third-party trackers in this demo.</p>
|
|
4
|
+
</div>
|
|
5
|
+
<div class="velin-flex velin-gap-3">
|
|
6
|
+
<button type="button" class="velin-btn velin-btn--primary">Accept</button>
|
|
7
|
+
<button type="button" class="velin-btn velin-btn--outline">Decline</button>
|
|
8
|
+
</div>
|
|
9
|
+
</aside>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<section class="velin-p-8 velin-text-center" aria-labelledby="empty-title">
|
|
2
|
+
<h2 id="empty-title" class="velin-text-xl velin-font-bold velin-mbe-2">No items yet</h2>
|
|
3
|
+
<p class="velin-text-muted velin-mbe-4">Create your first entry to see it listed here.</p>
|
|
4
|
+
<button type="button" class="velin-btn velin-btn--primary">Create item</button>
|
|
5
|
+
</section>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<form class="velin-flex velin-flex-wrap velin-gap-3 velin-p-4 velin-border-b" role="search" aria-label="Filter list">
|
|
2
|
+
<label class="velin-flex velin-flex-col velin-gap-1">
|
|
3
|
+
<span class="velin-text-sm">Query</span>
|
|
4
|
+
<input type="search" class="velin-input" name="q" placeholder="Search…" />
|
|
5
|
+
</label>
|
|
6
|
+
<label class="velin-flex velin-flex-col velin-gap-1">
|
|
7
|
+
<span class="velin-text-sm">Status</span>
|
|
8
|
+
<select class="velin-select" name="status">
|
|
9
|
+
<option value="">All</option>
|
|
10
|
+
<option value="open">Open</option>
|
|
11
|
+
<option value="done">Done</option>
|
|
12
|
+
</select>
|
|
13
|
+
</label>
|
|
14
|
+
<button type="submit" class="velin-btn velin-btn--primary velin-self-end">Apply</button>
|
|
15
|
+
</form>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<section class="velin-p-4" aria-labelledby="notif-title">
|
|
2
|
+
<h2 id="notif-title" class="velin-text-lg velin-font-bold velin-mbe-4">Notifications</h2>
|
|
3
|
+
<ul class="velin-flex velin-flex-col velin-gap-3" role="list">
|
|
4
|
+
<li class="velin-p-3 velin-border velin-rounded-lg">
|
|
5
|
+
<p class="velin-font-medium">Backup complete</p>
|
|
6
|
+
<p class="velin-text-sm velin-text-muted">2 minutes ago</p>
|
|
7
|
+
</li>
|
|
8
|
+
<li class="velin-p-3 velin-border velin-rounded-lg">
|
|
9
|
+
<p class="velin-font-medium">New comment</p>
|
|
10
|
+
<p class="velin-text-sm velin-text-muted">1 hour ago</p>
|
|
11
|
+
</li>
|
|
12
|
+
</ul>
|
|
13
|
+
</section>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<velin-stepper-wc id="onboard" labels="Welcome,Profile,Done" active="0">
|
|
2
|
+
<section class="velin-p-6">
|
|
3
|
+
<h2 class="velin-text-xl velin-mbe-2">Welcome</h2>
|
|
4
|
+
<p class="velin-mbe-4">Set up your workspace in a few steps.</p>
|
|
5
|
+
<button type="button" class="velin-btn velin-btn--primary" data-next>Continue</button>
|
|
6
|
+
</section>
|
|
7
|
+
<section class="velin-p-6">
|
|
8
|
+
<h2 class="velin-text-xl velin-mbe-2">Profile</h2>
|
|
9
|
+
<input type="text" class="velin-input velin-mbe-4" placeholder="Your name" aria-label="Name" />
|
|
10
|
+
<button type="button" class="velin-btn velin-btn--primary" data-next>Continue</button>
|
|
11
|
+
</section>
|
|
12
|
+
<section class="velin-p-6">
|
|
13
|
+
<h2 class="velin-text-xl velin-mbe-2">Done</h2>
|
|
14
|
+
<p>You are ready to go.</p>
|
|
15
|
+
</section>
|
|
16
|
+
</velin-stepper-wc>
|
|
17
|
+
<script type="module">
|
|
18
|
+
import '../../dist/velinstyle-components.min.js';
|
|
19
|
+
const stepper = document.getElementById('onboard');
|
|
20
|
+
document.querySelectorAll('[data-next]').forEach((btn) => {
|
|
21
|
+
btn.addEventListener('click', () => stepper?.next());
|
|
22
|
+
});
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<div class="velin-grid velin-gap-4" style="grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));">
|
|
2
|
+
<article class="velin-p-6 velin-border velin-rounded-lg velin-text-center">
|
|
3
|
+
<h3 class="velin-text-lg velin-font-bold">Starter</h3>
|
|
4
|
+
<p class="velin-text-3xl velin-font-bold velin-mbs-2 velin-mbe-4">$0</p>
|
|
5
|
+
<ul class="velin-text-sm velin-mbe-4" role="list">
|
|
6
|
+
<li>1 project</li>
|
|
7
|
+
<li>Community support</li>
|
|
8
|
+
</ul>
|
|
9
|
+
<button type="button" class="velin-btn velin-btn--outline velin-w-full">Choose</button>
|
|
10
|
+
</article>
|
|
11
|
+
<article class="velin-p-6 velin-border velin-rounded-lg velin-text-center velin-border-primary">
|
|
12
|
+
<h3 class="velin-text-lg velin-font-bold">Pro</h3>
|
|
13
|
+
<p class="velin-text-3xl velin-font-bold velin-mbs-2 velin-mbe-4">$12</p>
|
|
14
|
+
<ul class="velin-text-sm velin-mbe-4" role="list">
|
|
15
|
+
<li>Unlimited projects</li>
|
|
16
|
+
<li>Priority support</li>
|
|
17
|
+
</ul>
|
|
18
|
+
<button type="button" class="velin-btn velin-btn--primary velin-w-full">Choose</button>
|
|
19
|
+
</article>
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<velin-sheet id="settings-sheet" title="Settings">
|
|
2
|
+
<form class="velin-flex velin-flex-col velin-gap-4 velin-p-4">
|
|
3
|
+
<label class="velin-flex velin-flex-col velin-gap-1">
|
|
4
|
+
<span>Display name</span>
|
|
5
|
+
<input type="text" class="velin-input" name="name" autocomplete="name" />
|
|
6
|
+
</label>
|
|
7
|
+
<label class="velin-flex velin-items-center velin-gap-2">
|
|
8
|
+
<input type="checkbox" class="velin-checkbox" name="notify" />
|
|
9
|
+
<span>Email notifications</span>
|
|
10
|
+
</label>
|
|
11
|
+
<button type="submit" class="velin-btn velin-btn--primary">Save</button>
|
|
12
|
+
</form>
|
|
13
|
+
</velin-sheet>
|
|
14
|
+
<button type="button" class="velin-btn velin-btn--outline" id="open-settings">Open settings</button>
|
|
15
|
+
<script type="module">
|
|
16
|
+
import '../../dist/velinstyle-components.min.js';
|
|
17
|
+
document.getElementById('open-settings')?.addEventListener('click', () => {
|
|
18
|
+
document.getElementById('settings-sheet')?.open?.();
|
|
19
|
+
});
|
|
20
|
+
</script>
|
package/cli/index.js
CHANGED
|
@@ -11,7 +11,7 @@ const PKG_ROOT = join(__dirname, '..');
|
|
|
11
11
|
const args = process.argv.slice(2);
|
|
12
12
|
const command = args[0];
|
|
13
13
|
|
|
14
|
-
const LAYERS = ['tokens', 'reset', 'base', 'a11y', 'layout', 'components', 'utilities', 'helpers'];
|
|
14
|
+
const LAYERS = ['tokens', 'reset', 'base', 'a11y', 'layout', 'components', 'utilities', 'security', 'helpers'];
|
|
15
15
|
|
|
16
16
|
const LAYER_FILES = {
|
|
17
17
|
tokens: [
|
|
@@ -24,8 +24,9 @@ const LAYER_FILES = {
|
|
|
24
24
|
a11y: [
|
|
25
25
|
'a11y/sr-only.css', 'a11y/skip-link.css', 'a11y/reduced-motion.css',
|
|
26
26
|
'a11y/forced-colors.css', 'a11y/skeleton.css', 'a11y/preferences.css',
|
|
27
|
-
'a11y/
|
|
27
|
+
'a11y/focus-not-obscured.css', 'a11y/target-size.css', 'a11y/high-contrast-aaa.css',
|
|
28
28
|
],
|
|
29
|
+
security: ['a11y/security.css'],
|
|
29
30
|
layout: [
|
|
30
31
|
'layout/breakpoints.css', 'layout/container.css', 'layout/grid.css',
|
|
31
32
|
'layout/flex.css', 'layout/patterns.css',
|
|
@@ -48,6 +49,7 @@ const LAYER_FILES = {
|
|
|
48
49
|
'utilities/scroll.css', 'utilities/color-mix.css', 'utilities/scroll-animation.css',
|
|
49
50
|
'utilities/view-transition.css', 'utilities/scope.css', 'utilities/anchor.css',
|
|
50
51
|
'utilities/filter.css', 'utilities/container-style.css', 'utilities/state.css',
|
|
52
|
+
'utilities/safe-area.css',
|
|
51
53
|
],
|
|
52
54
|
helpers: ['helpers/helpers.css'],
|
|
53
55
|
};
|
|
@@ -78,7 +80,7 @@ function hasFlag(flag, alias) {
|
|
|
78
80
|
|
|
79
81
|
function help() {
|
|
80
82
|
console.log(`
|
|
81
|
-
${C.bold('VelinStyle CLI')} v0.
|
|
83
|
+
${C.bold('VelinStyle CLI')} v0.8.0
|
|
82
84
|
|
|
83
85
|
${C.bold('Usage:')}
|
|
84
86
|
velinstyle init Create velinstyle.config.js
|
|
@@ -89,6 +91,8 @@ function help() {
|
|
|
89
91
|
velinstyle scan [path] Security & accessibility scanner
|
|
90
92
|
velinstyle prefix [path] Add missing velin- prefix (dry-run; use --write)
|
|
91
93
|
velinstyle blueprint [name] Print HTML blueprint (run: velinstyle blueprint list)
|
|
94
|
+
velinstyle scaffold "<prompt>" Generate layout HTML from a text description
|
|
95
|
+
velinstyle layout <sub> Responsive layout audit and fixes
|
|
92
96
|
velinstyle tokens build Generate CSS variables from tokens.json
|
|
93
97
|
|
|
94
98
|
${C.bold('Icons subcommands:')}
|
|
@@ -106,6 +110,16 @@ function help() {
|
|
|
106
110
|
velinstyle blueprint list Print all blueprint ids
|
|
107
111
|
velinstyle blueprint <name> [--output, -o <file>]
|
|
108
112
|
|
|
113
|
+
${C.bold('Scaffold (0.8.0):')}
|
|
114
|
+
velinstyle scaffold "<prompt>" Compose blueprints from natural language
|
|
115
|
+
velinstyle scaffold list-intents Show supported intent keywords
|
|
116
|
+
velinstyle scaffold "<prompt>" -o out.html [--json]
|
|
117
|
+
|
|
118
|
+
${C.bold('Layout (0.8.0):')}
|
|
119
|
+
velinstyle layout audit [path] Report flex/grid/responsive issues
|
|
120
|
+
velinstyle layout suggest [path] Audit with fix suggestions
|
|
121
|
+
velinstyle layout fix [path] Apply safe fixes (--dry-run default, --write)
|
|
122
|
+
|
|
109
123
|
${C.bold('Tokens:')}
|
|
110
124
|
velinstyle tokens build [--input <path>] [--output, -o <file>]
|
|
111
125
|
|
|
@@ -496,6 +510,104 @@ async function prefixCmd() {
|
|
|
496
510
|
}
|
|
497
511
|
}
|
|
498
512
|
|
|
513
|
+
async function scaffoldCmd() {
|
|
514
|
+
const sub = args[1];
|
|
515
|
+
const { scaffoldFromPrompt, listIntents } = await import('./scaffold.js');
|
|
516
|
+
if (sub === 'list-intents') {
|
|
517
|
+
console.log(`\n ${C.bold('Scaffold intents:')}\n`);
|
|
518
|
+
for (const i of listIntents()) {
|
|
519
|
+
console.log(` ${C.cyan(i.id)} — ${i.blueprints.join(' + ')}`);
|
|
520
|
+
console.log(C.dim(` keywords: ${i.keywords.slice(0, 5).join(', ')}…`));
|
|
521
|
+
}
|
|
522
|
+
console.log('');
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
const promptParts = [];
|
|
526
|
+
let i = 1;
|
|
527
|
+
while (i < args.length) {
|
|
528
|
+
const a = args[i];
|
|
529
|
+
if (a.startsWith('-')) break;
|
|
530
|
+
if (a !== 'scaffold') promptParts.push(a);
|
|
531
|
+
i += 1;
|
|
532
|
+
}
|
|
533
|
+
const prompt = promptParts.join(' ').trim() || sub;
|
|
534
|
+
if (!prompt || prompt === 'list-intents') {
|
|
535
|
+
console.log('Usage: velinstyle scaffold "<description>" [-o file.html] [--json]');
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const r = scaffoldFromPrompt(prompt);
|
|
539
|
+
if (!r.ok) {
|
|
540
|
+
console.log(C.red(r.error));
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
const out = getArg('--output', '-o');
|
|
544
|
+
const asJson = hasFlag('--json');
|
|
545
|
+
if (asJson) {
|
|
546
|
+
console.log(JSON.stringify(r, null, 2));
|
|
547
|
+
} else if (out) {
|
|
548
|
+
writeFileSync(resolve(out), r.html, 'utf-8');
|
|
549
|
+
console.log(C.green(`Wrote ${resolve(out)} (intent: ${r.intent}, ${r.confidence})`));
|
|
550
|
+
if (r.responsiveHints?.length) {
|
|
551
|
+
console.log(C.yellow(` ${r.responsiveHints.length} layout hint(s) — run: velinstyle layout suggest ${out}`));
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
console.log(r.html);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async function layoutCmd() {
|
|
559
|
+
const sub = args[1] || 'audit';
|
|
560
|
+
const rawTarget = args[2] && !args[2].startsWith('-') ? args[2] : '.';
|
|
561
|
+
const targetPath = resolve(rawTarget);
|
|
562
|
+
const asJson = hasFlag('--json');
|
|
563
|
+
const write = hasFlag('--write');
|
|
564
|
+
const dryRun = hasFlag('--dry-run') || !write;
|
|
565
|
+
|
|
566
|
+
const { auditPath, suggestFromIssues, formatTextReport, fixPath } = await import('./layout-audit.js');
|
|
567
|
+
|
|
568
|
+
if (sub === 'fix') {
|
|
569
|
+
const result = fixPath(targetPath, { write, dryRun });
|
|
570
|
+
if (!result.ok) {
|
|
571
|
+
console.log(C.red(result.error || 'Fix failed'));
|
|
572
|
+
process.exit(1);
|
|
573
|
+
}
|
|
574
|
+
if (asJson) {
|
|
575
|
+
console.log(JSON.stringify(result, null, 2));
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
if (result.changes.length === 0) {
|
|
579
|
+
console.log(C.green('No safe fixes to apply.'));
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
for (const c of result.changes) {
|
|
583
|
+
console.log(`${dryRun ? '[dry-run] ' : ''}${c.file}`);
|
|
584
|
+
c.changes.forEach((ch) => console.log(C.dim(` - ${ch}`)));
|
|
585
|
+
}
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
const { issues, files } = auditPath(targetPath);
|
|
590
|
+
const outIssues = sub === 'suggest' ? suggestFromIssues(issues) : issues;
|
|
591
|
+
|
|
592
|
+
if (asJson) {
|
|
593
|
+
console.log(JSON.stringify({ files, issues: outIssues }, null, 2));
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
console.log(formatTextReport(outIssues, files));
|
|
598
|
+
if (sub === 'suggest' && issues.length) {
|
|
599
|
+
console.log(C.bold('Suggested fixes:'));
|
|
600
|
+
for (const i of outIssues) {
|
|
601
|
+
console.log(` [${i.id}] ${i.fix}`);
|
|
602
|
+
if (i.responsive?.mobile) {
|
|
603
|
+
console.log(C.dim(` mobile: ${i.responsive.mobile}`));
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
console.log('');
|
|
607
|
+
}
|
|
608
|
+
process.exit(issues.some((x) => x.severity === 'error') ? 1 : 0);
|
|
609
|
+
}
|
|
610
|
+
|
|
499
611
|
async function scanCmd() {
|
|
500
612
|
const targetPath = args[1] && !args[1].startsWith('-') ? resolve(args[1]) : resolve('.');
|
|
501
613
|
const format = getArg('--format') || 'text';
|
|
@@ -507,6 +619,7 @@ async function scanCmd() {
|
|
|
507
619
|
fixLang = 'de';
|
|
508
620
|
}
|
|
509
621
|
const severity = getArg('--severity') || 'warning';
|
|
622
|
+
const only = getArg('--only');
|
|
510
623
|
|
|
511
624
|
const { scan } = await import('./scanner.js');
|
|
512
625
|
const exitCode = scan(targetPath, {
|
|
@@ -515,6 +628,7 @@ async function scanCmd() {
|
|
|
515
628
|
fixDryRun,
|
|
516
629
|
fixLang: fixLang || undefined,
|
|
517
630
|
format,
|
|
631
|
+
only,
|
|
518
632
|
});
|
|
519
633
|
process.exit(exitCode);
|
|
520
634
|
}
|
|
@@ -531,6 +645,8 @@ switch (command) {
|
|
|
531
645
|
case 'tokens': await tokensCmd(); break;
|
|
532
646
|
case 'scan': scanCmd(); break;
|
|
533
647
|
case 'prefix': await prefixCmd(); break;
|
|
648
|
+
case 'scaffold': await scaffoldCmd(); break;
|
|
649
|
+
case 'layout': await layoutCmd(); break;
|
|
534
650
|
case '--help': case '-h': help(); break;
|
|
535
651
|
default: help(); break;
|
|
536
652
|
}
|