@birdapi/velinstyle 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.de.md +18 -4
  2. package/README.md +42 -7
  3. package/cli/cli-manifest.json +441 -174
  4. package/cli/docgen/extract-cli.js +2 -1
  5. package/cli/docs-generate.js +9 -0
  6. package/cli/experience.js +173 -0
  7. package/cli/index.js +1294 -417
  8. package/cli/motion.js +738 -0
  9. package/cli/production/extract.js +46 -6
  10. package/cli/production/report.js +104 -14
  11. package/cli/production/run.js +44 -1
  12. package/cli/security.js +190 -0
  13. package/cli/skills.js +53 -10
  14. package/cli/transparency.js +140 -41
  15. package/cli/workflow.js +32 -17
  16. package/components/velin-theme-toggle.js +15 -1
  17. package/core/a11y/component-contracts.json +391 -274
  18. package/core/attributes/registry.js +1 -1
  19. package/core/motion/analyze.js +38 -0
  20. package/core/motion/blueprint.js +51 -0
  21. package/core/motion/composer.js +114 -0
  22. package/core/motion/data/registry.json +352 -0
  23. package/core/motion/effects.js +17 -1
  24. package/core/motion/index.js +112 -9
  25. package/core/motion/optimize.js +44 -0
  26. package/core/motion/policy.js +77 -0
  27. package/core/motion/registry.js +131 -0
  28. package/core/motion/review.js +59 -0
  29. package/core/motion/scan.js +150 -0
  30. package/core/motion/timeline.js +68 -0
  31. package/core/motion/transitions.js +49 -0
  32. package/core/motion/triggers.js +168 -0
  33. package/core/security/checks/fs.js +92 -0
  34. package/core/security/detect.js +35 -0
  35. package/core/security/engine.js +116 -0
  36. package/core/security/index.js +18 -0
  37. package/core/security/registry.js +85 -0
  38. package/core/security/report/report.js +113 -0
  39. package/core/security/rules/ci/index.js +146 -0
  40. package/core/security/rules/consumer/index.js +174 -0
  41. package/core/security/rules/deps/index.js +182 -0
  42. package/core/security/rules/index.js +24 -0
  43. package/core/security/rules/publish/index.js +230 -0
  44. package/core/security/rules/repo/index.js +143 -0
  45. package/core/security/rules/secrets/index.js +133 -0
  46. package/core/security/score/dependency-health.js +53 -0
  47. package/core/security/score/release-health.js +66 -0
  48. package/core/security/score/score.js +102 -0
  49. package/core/security/timeline/timeline.js +76 -0
  50. package/dist/chunks/attributes-HK7VIOLA.js +1080 -0
  51. package/dist/chunks/attributes-VRHHVNDO.js +1080 -0
  52. package/dist/chunks/chunk-MYKUI364.js +116 -0
  53. package/dist/chunks/chunk-NEVBPD5T.js +116 -0
  54. package/dist/chunks/runtime-entry.js +1 -1
  55. package/dist/chunks/velin-theme-toggle-J2NHC7IM.js +304 -0
  56. package/dist/llms.txt +2 -2
  57. package/dist/search-index.json +166 -2
  58. package/dist/velin-agent.json +156 -32
  59. package/dist/velinstyle-components.iife.js +862 -17
  60. package/dist/velinstyle-components.js +862 -17
  61. package/dist/velinstyle-components.min.js +126 -126
  62. package/dist/velinstyle.css +42 -7
  63. package/dist/velinstyle.min.css +1 -1
  64. package/package.json +158 -143
  65. package/packages/velinstyle-cli-core/package.json +11 -0
  66. package/packages/velinstyle-cli-core/src/config.js +84 -0
  67. package/packages/velinstyle-cli-core/src/contract.js +56 -0
  68. package/packages/velinstyle-cli-core/src/event-bus.js +50 -0
  69. package/packages/velinstyle-cli-core/src/index.js +18 -0
  70. package/packages/velinstyle-cli-core/src/lifecycle.js +33 -0
  71. package/packages/velinstyle-cli-core/src/runner.js +129 -0
  72. package/packages/velinstyle-cli-registry/data/commands.json +1216 -0
  73. package/packages/velinstyle-cli-registry/data/layouts.json +33 -0
  74. package/packages/velinstyle-cli-registry/data/widgets.json +35 -0
  75. package/packages/velinstyle-cli-registry/package.json +13 -0
  76. package/packages/velinstyle-cli-registry/src/index.js +88 -0
  77. package/packages/velinstyle-cli-renderer/package.json +8 -0
  78. package/packages/velinstyle-cli-renderer/src/index.js +286 -0
  79. package/packages/velinstyle-cli-theme/package.json +8 -0
  80. package/packages/velinstyle-cli-theme/src/index.js +84 -0
  81. package/packages/velinstyle-cli-ui/package.json +8 -0
  82. package/packages/velinstyle-cli-ui/src/index.js +34 -0
  83. package/packages/velinstyle-motion/README.md +81 -0
  84. package/packages/velinstyle-motion/package.json +37 -0
  85. package/packages/velinstyle-motion/src/ai/mini-prompt.js +119 -0
  86. package/packages/velinstyle-motion/src/ai/provider.js +59 -0
  87. package/packages/velinstyle-motion/src/ai/providers/pixverse-setup.js +402 -0
  88. package/packages/velinstyle-motion/src/ai/providers/pixverse.js +245 -0
  89. package/packages/velinstyle-motion/src/config.js +257 -0
  90. package/packages/velinstyle-motion/src/export/html-clip.js +141 -0
  91. package/packages/velinstyle-motion/src/index.js +304 -0
  92. package/packages/velinstyle-motion/src/jobs/cache.js +30 -0
  93. package/packages/velinstyle-motion/src/jobs/queue.js +69 -0
  94. package/packages/velinstyle-motion/src/jobs/usage.js +53 -0
  95. package/packages/velinstyle-motion/src/local/effects.js +139 -0
  96. package/packages/velinstyle-motion/src/local/engine.js +96 -0
  97. package/packages/velinstyle-motion/src/providers-catalog.js +44 -0
  98. package/packages/velinstyle-motion/src/registry/presets.js +25 -0
  99. package/packages/velinstyle-motion/src/registry/presets.json +85 -0
  100. package/packages/velinstyle-motion/src/ux/cost-gate.js +37 -0
  101. package/packages/velinstyle-motion/src/ux/pixverse-gate.js +153 -0
  102. package/packages/velinstyle-motion/src/ux/wizard.js +171 -0
  103. package/packages/velinstyle-motion/src/validate-image.js +91 -0
  104. package/packages/velinstyle-skills/catalog.json +1 -1
  105. package/packages/velinstyle-skills/registry.json +1 -142
  106. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +1 -1
  107. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +1 -1
  108. package/src/tokens/motion.css +7 -0
  109. package/src/utilities/scroll-animation.css +66 -0
@@ -0,0 +1,33 @@
1
+ {
2
+ "version": "1.2.5",
3
+ "layouts": [
4
+ {
5
+ "id": "standard",
6
+ "slots": ["hero", "project", "work", "result", "suggestions", "footer"]
7
+ },
8
+ {
9
+ "id": "build",
10
+ "slots": ["hero", "project", "progress", "breakdown", "diff", "suggestions", "footer"]
11
+ },
12
+ {
13
+ "id": "doctor",
14
+ "slots": ["hero", "environment", "checks", "result", "suggestions", "footer"]
15
+ },
16
+ {
17
+ "id": "review",
18
+ "slots": ["hero", "scores", "issues", "result", "suggestions", "footer"]
19
+ },
20
+ {
21
+ "id": "dashboard",
22
+ "slots": ["hero", "project", "cards", "result", "suggestions", "footer"]
23
+ },
24
+ {
25
+ "id": "audit",
26
+ "slots": ["hero", "counts", "findings", "result", "suggestions", "footer"]
27
+ },
28
+ {
29
+ "id": "wizard",
30
+ "slots": ["hero", "steps", "work", "result", "footer"]
31
+ }
32
+ ]
33
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "version": "1.2.5",
3
+ "widgets": [
4
+ { "id": "hero", "title": "Hero", "category": "layout" },
5
+ { "id": "section", "title": "Section", "category": "layout" },
6
+ { "id": "divider", "title": "Divider", "category": "layout" },
7
+ { "id": "footer", "title": "Footer", "category": "layout" },
8
+ { "id": "badge", "title": "Badge", "category": "status" },
9
+ { "id": "status", "title": "Status", "category": "status" },
10
+ { "id": "alert", "title": "Alert", "category": "status" },
11
+ { "id": "toast", "title": "Toast", "category": "status" },
12
+ { "id": "hint", "title": "Hint", "category": "status" },
13
+ { "id": "suggestion", "title": "Suggestion", "category": "status" },
14
+ { "id": "notification", "title": "Notification", "category": "status" },
15
+ { "id": "table", "title": "Table", "category": "data" },
16
+ { "id": "tree", "title": "Tree", "category": "data" },
17
+ { "id": "timeline", "title": "Timeline", "category": "data" },
18
+ { "id": "progress", "title": "Progress", "category": "data" },
19
+ { "id": "stats", "title": "Stats", "category": "data" },
20
+ { "id": "kpi", "title": "KPI", "category": "data" },
21
+ { "id": "diff", "title": "Diff", "category": "data" },
22
+ { "id": "score", "title": "Score Card", "category": "reports" },
23
+ { "id": "summary", "title": "Summary", "category": "reports" },
24
+ { "id": "recommendation", "title": "Recommendation", "category": "reports" },
25
+ { "id": "health", "title": "Health", "category": "reports" },
26
+ { "id": "bundle", "title": "Bundle", "category": "widgets" },
27
+ { "id": "savings", "title": "Savings", "category": "widgets" },
28
+ { "id": "memory", "title": "Memory", "category": "widgets" },
29
+ { "id": "cpu", "title": "CPU", "category": "widgets" },
30
+ { "id": "duration", "title": "Duration", "category": "widgets" },
31
+ { "id": "files", "title": "Files", "category": "widgets" },
32
+ { "id": "size", "title": "Size", "category": "widgets" },
33
+ { "id": "review", "title": "Review", "category": "widgets" }
34
+ ]
35
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "@birdapi/velinstyle-cli-registry",
3
+ "version": "1.3.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": {
8
+ ".": "./src/index.js",
9
+ "./commands": "./data/commands.json",
10
+ "./widgets": "./data/widgets.json",
11
+ "./layouts": "./data/layouts.json"
12
+ }
13
+ }
@@ -0,0 +1,88 @@
1
+ import { readFileSync } from 'fs';
2
+ import { dirname, join } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
6
+
7
+ function loadJson(rel) {
8
+ return JSON.parse(readFileSync(join(ROOT, rel), 'utf8'));
9
+ }
10
+
11
+ let _commands;
12
+ let _widgets;
13
+ let _layouts;
14
+
15
+ export function getCommandsRegistry() {
16
+ if (!_commands) _commands = loadJson('data/commands.json');
17
+ return _commands;
18
+ }
19
+
20
+ export function getWidgetsRegistry() {
21
+ if (!_widgets) _widgets = loadJson('data/widgets.json');
22
+ return _widgets;
23
+ }
24
+
25
+ export function getLayoutsRegistry() {
26
+ if (!_layouts) _layouts = loadJson('data/layouts.json');
27
+ return _layouts;
28
+ }
29
+
30
+ export function getCommand(id) {
31
+ return getCommandsRegistry().commands.find((c) => c.id === id || (c.aliases || []).includes(id)) || null;
32
+ }
33
+
34
+ export function getLayout(id) {
35
+ return getLayoutsRegistry().layouts.find((l) => l.id === id) || getLayoutsRegistry().layouts.find((l) => l.id === 'standard');
36
+ }
37
+
38
+ export function getWidget(id) {
39
+ return getWidgetsRegistry().widgets.find((w) => w.id === id) || null;
40
+ }
41
+
42
+ export function listCommandsByCategory() {
43
+ const map = new Map();
44
+ for (const cmd of getCommandsRegistry().commands) {
45
+ if (!map.has(cmd.category)) map.set(cmd.category, []);
46
+ map.get(cmd.category).push(cmd);
47
+ }
48
+ return map;
49
+ }
50
+
51
+ /** Bridge: shape compatible with legacy cli-manifest.json consumers */
52
+ export function toCliManifest(version = getCommandsRegistry().version) {
53
+ return {
54
+ version,
55
+ commands: getCommandsRegistry().commands
56
+ .filter((c) => !['dashboard', 'replay'].includes(c.id) || true)
57
+ .map((c) => ({
58
+ name: c.id,
59
+ summary: c.description,
60
+ flags: c.flags || [],
61
+ example: (c.examples && c.examples[0]) || `velinstyle ${c.id}`,
62
+ ...(c.subcommands?.length ? { subcommands: c.subcommands } : {}),
63
+ })),
64
+ };
65
+ }
66
+
67
+ /** Register a plugin command at runtime (skeleton). */
68
+ export function registerPluginCommand(record, { pluginId } = {}) {
69
+ const reg = getCommandsRegistry();
70
+ const existing = reg.commands.findIndex((c) => c.id === record.id);
71
+ const entry = {
72
+ maturity: 'experimental',
73
+ since: '1.2.5',
74
+ aliases: [],
75
+ tags: ['plugin'],
76
+ supports: ['standard', 'json'],
77
+ widgets: [],
78
+ layout: 'standard',
79
+ flags: [],
80
+ examples: [],
81
+ subcommands: [],
82
+ ...record,
83
+ pluginId: pluginId || record.pluginId || null,
84
+ };
85
+ if (existing >= 0) reg.commands[existing] = entry;
86
+ else reg.commands.push(entry);
87
+ return entry;
88
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@birdapi/velinstyle-cli-renderer",
3
+ "version": "1.3.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": { ".": "./src/index.js" }
8
+ }
@@ -0,0 +1,286 @@
1
+ import { resolveTheme } from '../../velinstyle-cli-theme/src/index.js';
2
+ import { getLayout, getCommand } from '../../velinstyle-cli-registry/src/index.js';
3
+ import { createUi } from '../../velinstyle-cli-ui/src/index.js';
4
+
5
+ function pad(s, n) {
6
+ const str = String(s);
7
+ return str.length >= n ? str : str + ' '.repeat(n - str.length);
8
+ }
9
+
10
+ function formatKb(n) {
11
+ if (n == null) return '—';
12
+ if (Math.abs(n) < 1024) return `${n} B`;
13
+ return `${(n / 1024).toFixed(n >= 10240 ? 0 : 1)} KB`;
14
+ }
15
+
16
+ function progressBar(pct, width = 22, theme) {
17
+ const p = Math.max(0, Math.min(100, Math.round(pct || 0)));
18
+ const filled = Math.round((p / 100) * width);
19
+ const bar = '█'.repeat(filled) + '░'.repeat(Math.max(0, width - filled));
20
+ return theme.unicode === false ? `[${'#'.repeat(filled)}${'.'.repeat(width - filled)}] ${p}%` : `${bar} ${p}%`;
21
+ }
22
+
23
+ function boxLine(theme, width, title) {
24
+ const { tl, tr, h } = theme.box;
25
+ const inner = Math.max(4, width - 2);
26
+ const label = title ? ` ${title} ` : '';
27
+ const left = Math.floor((inner - label.length) / 2);
28
+ const right = inner - label.length - left;
29
+ return `${tl}${h.repeat(Math.max(0, left))}${label}${h.repeat(Math.max(0, right))}${tr}`;
30
+ }
31
+
32
+ function boxBottom(theme, width) {
33
+ const { bl, br, h } = theme.box;
34
+ return `${bl}${h.repeat(Math.max(0, width - 2))}${br}`;
35
+ }
36
+
37
+ function renderWidget(w, theme, lines) {
38
+ const p = w.props || {};
39
+ switch (w.id) {
40
+ case 'hero': {
41
+ const width = p.width || 48;
42
+ lines.push(theme.paint('accent', boxLine(theme, width, p.eyebrow || 'Velin Experience')));
43
+ lines.push(`${theme.box.v} ${theme.bold(pad(p.title || '', width - 4))} ${theme.box.v}`);
44
+ if (p.subtitle) lines.push(`${theme.box.v} ${theme.dim(pad(p.subtitle, width - 4))} ${theme.box.v}`);
45
+ lines.push(theme.paint('accent', boxBottom(theme, width)));
46
+ lines.push('');
47
+ break;
48
+ }
49
+ case 'section': {
50
+ lines.push(theme.dim('─'.repeat(46)));
51
+ lines.push(theme.bold(p.title || 'Section'));
52
+ lines.push(theme.dim('─'.repeat(46)));
53
+ break;
54
+ }
55
+ case 'badge':
56
+ case 'status': {
57
+ const tone = p.tone || (p.label === 'PASS' ? 'ok' : p.label === 'FAIL' || p.label === 'ERROR' ? 'err' : 'warn');
58
+ lines.push(theme.paint(tone, `${theme.icons.dot} ${p.label || w.id}`));
59
+ break;
60
+ }
61
+ case 'alert':
62
+ case 'notification': {
63
+ const icon = p.tone === 'ok' ? theme.icons.pass : p.tone === 'err' ? theme.icons.fail : p.tone === 'warn' ? theme.icons.warn : theme.icons.info;
64
+ lines.push(`${theme.paint(p.tone || 'accent', icon)} ${p.message || ''}`);
65
+ break;
66
+ }
67
+ case 'progress': {
68
+ const width = p.width || 28;
69
+ lines.push(theme.paint('accent', boxLine(theme, width + 6, p.label || 'Progress')));
70
+ lines.push(`${theme.box.v} ${progressBar(p.percent ?? 0, width, { unicode: true })} ${theme.box.v}`);
71
+ lines.push(theme.paint('accent', boxBottom(theme, width + 6)));
72
+ break;
73
+ }
74
+ case 'table': {
75
+ const cols = p.columns || [];
76
+ const rows = p.rows || [];
77
+ if (cols.length) lines.push(theme.bold(cols.map((c) => pad(c, 14)).join('')));
78
+ for (const row of rows) {
79
+ lines.push((Array.isArray(row) ? row : cols.map((c) => row[c])).map((c) => pad(c, 14)).join(''));
80
+ }
81
+ break;
82
+ }
83
+ case 'stats':
84
+ case 'kpi': {
85
+ const items = p.items || [];
86
+ for (const it of items) {
87
+ lines.push(`${pad(it.label, 16)}${theme.bold(String(it.value))}`);
88
+ }
89
+ break;
90
+ }
91
+ case 'score': {
92
+ lines.push(theme.bold(p.title || 'Scores'));
93
+ for (const s of p.scores || []) {
94
+ lines.push(`${pad(s.label, 16)}${theme.bold(String(s.value))}`);
95
+ }
96
+ break;
97
+ }
98
+ case 'health': {
99
+ lines.push(theme.bold(p.title || 'Health'));
100
+ for (const row of p.rows || []) {
101
+ const ok = row.status === 'PASS' || row.status === 'ok';
102
+ lines.push(`${pad(row.label, 16)}${theme.paint(ok ? 'ok' : 'warn', row.status)}`);
103
+ }
104
+ break;
105
+ }
106
+ case 'bundle':
107
+ case 'savings': {
108
+ lines.push(theme.bold(p.title || 'Production Report'));
109
+ lines.push('');
110
+ for (const row of p.rows || []) {
111
+ const before = row.beforeCount != null ? row.beforeCount : formatKb(row.before);
112
+ const after = row.afterCount != null ? row.afterCount : formatKb(row.after);
113
+ lines.push(`${pad(row.label, 14)}${pad(before, 10)}→ ${after}`);
114
+ }
115
+ if (p.savingsPct != null) {
116
+ lines.push('');
117
+ lines.push(`${pad('Savings', 14)}${theme.paint('ok', `${p.savingsPct} %`)}`);
118
+ }
119
+ break;
120
+ }
121
+ case 'diff': {
122
+ lines.push(theme.bold(p.title || 'Diff'));
123
+ for (const line of p.removed || []) lines.push(theme.paint('err', `- ${line}`));
124
+ for (const line of p.added || []) lines.push(theme.paint('ok', `+ ${line}`));
125
+ for (const line of p.changed || []) lines.push(theme.paint('warn', `~ ${line}`));
126
+ break;
127
+ }
128
+ case 'tree': {
129
+ lines.push(theme.bold(p.title || 'Tree'));
130
+ for (const node of p.nodes || []) {
131
+ const indent = ' '.repeat(node.depth || 0);
132
+ const branch = node.depth ? '├─ ' : '';
133
+ lines.push(`${indent}${branch}${node.label}`);
134
+ }
135
+ break;
136
+ }
137
+ case 'timeline': {
138
+ for (const step of p.steps || []) {
139
+ lines.push(`${theme.dim(step.at || '')} ${step.label}`);
140
+ }
141
+ break;
142
+ }
143
+ case 'suggestion': {
144
+ lines.push(`${theme.icons.star} ${p.text || ''}`);
145
+ break;
146
+ }
147
+ case 'footer': {
148
+ lines.push('');
149
+ lines.push(theme.dim(p.text || `Done · Velin Experience`));
150
+ break;
151
+ }
152
+ default:
153
+ if (p.message) lines.push(String(p.message));
154
+ }
155
+ }
156
+
157
+ /** Order widgets by layout slots when possible (id or props.slot). */
158
+ function orderWidgetsByLayout(widgets, layout) {
159
+ if (!layout?.slots?.length || !widgets?.length) return widgets || [];
160
+ const used = new Set();
161
+ const ordered = [];
162
+ for (const slot of layout.slots) {
163
+ const match = widgets.find((w, i) => !used.has(i) && ((w.props?.slot || w.slot || w.id) === slot));
164
+ if (match) {
165
+ const idx = widgets.indexOf(match);
166
+ used.add(idx);
167
+ ordered.push(match);
168
+ }
169
+ }
170
+ widgets.forEach((w, i) => {
171
+ if (!used.has(i)) ordered.push(w);
172
+ });
173
+ return ordered;
174
+ }
175
+
176
+ export function renderTerminal(contract, config = {}) {
177
+ const theme = resolveTheme(config.theme || 'modern', { unicode: config.unicode !== false });
178
+ const cmd = getCommand(contract.meta?.commandId);
179
+ const layout = getLayout(contract.meta?.layout || cmd?.layout || 'standard');
180
+ const lines = [];
181
+ const mode = config.output || config.mode || 'standard';
182
+
183
+ if (mode === 'compact') {
184
+ const st = contract.result?.status || 'PASS';
185
+ lines.push(`${st} ${contract.result?.message || contract.meta?.commandId || ''}`.trim());
186
+ return lines.join('\n');
187
+ }
188
+
189
+ const ordered = orderWidgetsByLayout(contract.widgets || [], layout);
190
+ for (const w of ordered) {
191
+ renderWidget(w, theme, lines);
192
+ }
193
+
194
+ if (!contract.widgets?.length) {
195
+ lines.push(theme.bold(cmd?.title || contract.meta?.commandId || 'Velin Experience'));
196
+ }
197
+
198
+ if (contract.result?.message && mode !== 'dashboard') {
199
+ const tone = contract.result.status === 'PASS' ? 'ok' : contract.result.status === 'ERROR' ? 'err' : 'warn';
200
+ lines.push('');
201
+ lines.push(theme.paint(tone, `${contract.result.status} ${contract.result.message}`));
202
+ }
203
+
204
+ for (const s of contract.suggestions || []) {
205
+ lines.push(`${theme.icons.star} ${s.text || s}`);
206
+ }
207
+
208
+ for (const n of contract.notifications || []) {
209
+ lines.push(`${theme.icons.info} ${n.message || n}`);
210
+ }
211
+
212
+ if (mode === 'debug' || mode === 'verbose') {
213
+ lines.push('');
214
+ lines.push(theme.dim(`lifecycle: ${(contract.lifecycle?.stages || []).map((s) => s.stage).join(' → ')}`));
215
+ lines.push(theme.dim(`layout: ${layout.id} · theme: ${theme.id} · ${contract.meta?.durationMs ?? 0} ms`));
216
+ }
217
+
218
+ if (contract.meta?.durationMs != null && mode !== 'compact') {
219
+ lines.push('');
220
+ lines.push(theme.dim(`Done in ${(contract.meta.durationMs / 1000).toFixed(2)} s · Velin Experience`));
221
+ }
222
+
223
+ return lines.join('\n');
224
+ }
225
+
226
+ export function renderJson(contract) {
227
+ return JSON.stringify(contract, null, 2);
228
+ }
229
+
230
+ export function renderMarkdown(contract) {
231
+ const lines = [`# ${contract.meta?.commandId || 'Velin Experience'}`, ''];
232
+ lines.push(`**Status:** ${contract.result?.status || 'PASS'} — ${contract.result?.message || ''}`, '');
233
+ for (const w of contract.widgets || []) {
234
+ lines.push(`## ${w.title || w.id}`, '');
235
+ if (w.id === 'bundle' || w.id === 'savings') {
236
+ lines.push('| Metric | Before | After |', '| --- | --- | --- |');
237
+ for (const row of w.props?.rows || []) {
238
+ const before = row.beforeCount != null ? row.beforeCount : formatKb(row.before);
239
+ const after = row.afterCount != null ? row.afterCount : formatKb(row.after);
240
+ lines.push(`| ${row.label} | ${before} | ${after} |`);
241
+ }
242
+ if (w.props?.savingsPct != null) lines.push('', `**Savings:** ${w.props.savingsPct}%`);
243
+ lines.push('');
244
+ } else if (w.id === 'score' || w.id === 'stats' || w.id === 'kpi') {
245
+ for (const it of w.props?.scores || w.props?.items || []) {
246
+ lines.push(`- **${it.label}:** ${it.value}`);
247
+ }
248
+ lines.push('');
249
+ } else if (w.props?.message) {
250
+ lines.push(w.props.message, '');
251
+ }
252
+ }
253
+ if (contract.suggestions?.length) {
254
+ lines.push('## Suggestions', '');
255
+ for (const s of contract.suggestions) lines.push(`- ${s.text || s}`);
256
+ }
257
+ return lines.join('\n');
258
+ }
259
+
260
+ export function renderSarif(contract) {
261
+ const runs = [{
262
+ tool: { driver: { name: 'Velin Experience', informationUri: 'https://velinstyle.info' } },
263
+ results: (contract.diagnostics || []).map((d) => ({
264
+ level: d.severity === 'error' ? 'error' : d.severity === 'warning' ? 'warning' : 'note',
265
+ message: { text: d.message || String(d) },
266
+ locations: d.path ? [{ physicalLocation: { artifactLocation: { uri: d.path } } }] : [],
267
+ })),
268
+ }];
269
+ return JSON.stringify({ $schema: 'https://json.schemastore.org/sarif-2.1.0.json', version: '2.1.0', runs }, null, 2);
270
+ }
271
+
272
+ export function renderContract(contract, config = {}) {
273
+ const mode = (config.output || config.mode || 'standard').toLowerCase();
274
+ if (mode === 'json') return renderJson(contract);
275
+ if (mode === 'sarif') return renderSarif(contract);
276
+ if (mode === 'markdown' || mode === 'md') return renderMarkdown(contract);
277
+ return renderTerminal(contract, config);
278
+ }
279
+
280
+ export function createExperienceSession(config = {}) {
281
+ const theme = resolveTheme(config.theme || 'modern', { unicode: config.unicode !== false });
282
+ const ui = createUi(theme);
283
+ return { theme, ui, render: (contract) => renderContract(contract, config) };
284
+ }
285
+
286
+ export { formatKb, progressBar };
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@birdapi/velinstyle-cli-theme",
3
+ "version": "1.3.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": { ".": "./src/index.js" }
8
+ }
@@ -0,0 +1,84 @@
1
+ const ANSI = {
2
+ reset: '\x1b[0m',
3
+ bold: '\x1b[1m',
4
+ dim: '\x1b[2m',
5
+ red: '\x1b[31m',
6
+ green: '\x1b[32m',
7
+ yellow: '\x1b[33m',
8
+ blue: '\x1b[34m',
9
+ magenta: '\x1b[35m',
10
+ cyan: '\x1b[36m',
11
+ white: '\x1b[37m',
12
+ gray: '\x1b[90m',
13
+ };
14
+
15
+ const ICONS_UNICODE = {
16
+ pass: '✓',
17
+ fail: '✖',
18
+ warn: '⚠',
19
+ info: 'ℹ',
20
+ star: '★',
21
+ play: '►',
22
+ dot: '●',
23
+ hex: '⬢',
24
+ hexEmpty: '⬡',
25
+ };
26
+
27
+ const ICONS_ASCII = {
28
+ pass: '[OK]',
29
+ fail: '[X]',
30
+ warn: '[!]',
31
+ info: '[i]',
32
+ star: '*',
33
+ play: '>',
34
+ dot: '*',
35
+ hex: '#',
36
+ hexEmpty: 'o',
37
+ };
38
+
39
+ const BOX = {
40
+ modern: { h: '─', v: '│', tl: '╭', tr: '╮', bl: '╰', br: '╯', cross: '┼' },
41
+ minimal: { h: '-', v: '|', tl: '+', tr: '+', bl: '+', br: '+', cross: '+' },
42
+ classic: { h: '=', v: '|', tl: '+', tr: '+', bl: '+', br: '+', cross: '+' },
43
+ matrix: { h: '═', v: '║', tl: '╔', tr: '╗', bl: '╚', br: '╝', cross: '╬' },
44
+ light: { h: '─', v: '│', tl: '┌', tr: '┐', bl: '└', br: '┘', cross: '┼' },
45
+ professional: { h: '─', v: '│', tl: '┌', tr: '┐', bl: '└', br: '┘', cross: '┼' },
46
+ velin: { h: '─', v: '│', tl: '╭', tr: '╮', bl: '╰', br: '╯', cross: '┼' },
47
+ };
48
+
49
+ const COLORS = {
50
+ modern: { accent: 'cyan', ok: 'green', warn: 'yellow', err: 'red', muted: 'gray' },
51
+ minimal: { accent: 'white', ok: 'green', warn: 'yellow', err: 'red', muted: 'gray' },
52
+ classic: { accent: 'blue', ok: 'green', warn: 'yellow', err: 'red', muted: 'gray' },
53
+ matrix: { accent: 'green', ok: 'green', warn: 'yellow', err: 'red', muted: 'dim' },
54
+ light: { accent: 'blue', ok: 'green', warn: 'yellow', err: 'red', muted: 'gray' },
55
+ professional: { accent: 'cyan', ok: 'green', warn: 'yellow', err: 'red', muted: 'gray' },
56
+ velin: { accent: 'magenta', ok: 'green', warn: 'yellow', err: 'red', muted: 'gray' },
57
+ };
58
+
59
+ export const THEME_IDS = Object.keys(COLORS);
60
+
61
+ export function resolveTheme(name = 'modern', { unicode = true } = {}) {
62
+ const id = THEME_IDS.includes(name) ? name : 'modern';
63
+ const colors = COLORS[id];
64
+ const box = BOX[id] || BOX.modern;
65
+ const icons = unicode ? ICONS_UNICODE : ICONS_ASCII;
66
+
67
+ function paint(tone, text) {
68
+ const key = colors[tone] || tone;
69
+ const code = ANSI[key] || '';
70
+ if (!code) return String(text);
71
+ return `${code}${text}${ANSI.reset}`;
72
+ }
73
+
74
+ return {
75
+ id,
76
+ box,
77
+ icons,
78
+ colors,
79
+ paint,
80
+ bold: (s) => `${ANSI.bold}${s}${ANSI.reset}`,
81
+ dim: (s) => `${ANSI.dim}${s}${ANSI.reset}`,
82
+ ansi: ANSI,
83
+ };
84
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@birdapi/velinstyle-cli-ui",
3
+ "version": "1.3.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "exports": { ".": "./src/index.js" }
8
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Velin Experience UI — thin wrappers over widget registry ids.
3
+ */
4
+ import { getWidget } from '../../velinstyle-cli-registry/src/index.js';
5
+
6
+ export function createUi(theme) {
7
+ function widget(id, props = {}) {
8
+ const meta = getWidget(id);
9
+ return { id, title: meta?.title || id, category: meta?.category || 'data', props, theme: theme.id };
10
+ }
11
+
12
+ return {
13
+ theme,
14
+ widget,
15
+ hero: (props) => widget('hero', props),
16
+ section: (props) => widget('section', props),
17
+ badge: (props) => widget('badge', props),
18
+ alert: (props) => widget('alert', props),
19
+ table: (props) => widget('table', props),
20
+ tree: (props) => widget('tree', props),
21
+ timeline: (props) => widget('timeline', props),
22
+ progress: (props) => widget('progress', props),
23
+ stats: (props) => widget('stats', props),
24
+ kpi: (props) => widget('kpi', props),
25
+ diff: (props) => widget('diff', props),
26
+ score: (props) => widget('score', props),
27
+ health: (props) => widget('health', props),
28
+ bundle: (props) => widget('bundle', props),
29
+ savings: (props) => widget('savings', props),
30
+ notification: (props) => widget('notification', props),
31
+ suggestion: (props) => widget('suggestion', props),
32
+ footer: (props) => widget('footer', props),
33
+ };
34
+ }