@birdapi/velinstyle 1.2.1 → 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.
- package/README.de.md +148 -11
- package/README.md +174 -16
- package/cli/__fixtures__/atelier-library/showcases/01-login/app.css +1 -0
- package/cli/__fixtures__/atelier-library/showcases/01-login/app.js +2 -0
- package/cli/__fixtures__/atelier-library/showcases/01-login/index.html +5 -0
- package/cli/__fixtures__/atelier-library/showcases/04-pricing/app.css +1 -0
- package/cli/__fixtures__/atelier-library/showcases/04-pricing/app.js +2 -0
- package/cli/__fixtures__/atelier-library/showcases/04-pricing/index.html +5 -0
- package/cli/__fixtures__/atelier-library/showcases/36-calendar/app.css +1 -0
- package/cli/__fixtures__/atelier-library/showcases/36-calendar/app.js +2 -0
- package/cli/__fixtures__/atelier-library/showcases/36-calendar/index.html +5 -0
- package/cli/atelier-catalog.json +1267 -0
- package/cli/atelier-formats.js +159 -0
- package/cli/atelier.js +382 -0
- package/cli/blueprints/empty-state.html +3 -5
- package/cli/cli-manifest.json +441 -161
- package/cli/docgen/extract-cli.js +2 -1
- package/cli/docs-generate.js +9 -0
- package/cli/experience.js +173 -0
- package/cli/index.js +1384 -361
- package/cli/motion.js +738 -0
- package/cli/production/component-graph.json +166 -0
- package/cli/production/explain.js +52 -0
- package/cli/production/extract.js +278 -0
- package/cli/production/graph.js +102 -0
- package/cli/production/report.js +168 -0
- package/cli/production/run.js +355 -0
- package/cli/production/trim-css.js +177 -0
- package/cli/production/trim-fonts.js +23 -0
- package/cli/production/trim-icons.js +38 -0
- package/cli/production/trim-js.js +48 -0
- package/cli/production/trim-motion.js +22 -0
- package/cli/production/trim-themes.js +50 -0
- package/cli/production/watch.js +38 -0
- package/cli/review.js +48 -1
- package/cli/scripts/write-atelier-fixtures.mjs +33 -0
- package/cli/security.js +190 -0
- package/cli/skills.js +53 -10
- package/cli/transparency.js +140 -41
- package/cli/workflow.js +32 -17
- package/components/index.js +3 -0
- package/components/runtime/component-loaders.js +3 -0
- package/components/velin-drawer.js +43 -4
- package/components/velin-empty-state.js +83 -0
- package/components/velin-modal.js +76 -15
- package/components/velin-otp-input.js +220 -0
- package/components/velin-password-strength.js +147 -0
- package/components/velin-sheet.js +49 -4
- package/components/velin-theme-toggle.js +15 -1
- package/core/a11y/component-contracts.json +391 -255
- package/core/attributes/registry.js +1 -1
- package/core/meta/knowledge/components.json +75 -3
- package/core/motion/analyze.js +38 -0
- package/core/motion/blueprint.js +51 -0
- package/core/motion/composer.js +114 -0
- package/core/motion/data/registry.json +352 -0
- package/core/motion/effects.js +17 -1
- package/core/motion/index.js +112 -9
- package/core/motion/optimize.js +44 -0
- package/core/motion/policy.js +77 -0
- package/core/motion/registry.js +131 -0
- package/core/motion/review.js +59 -0
- package/core/motion/scan.js +150 -0
- package/core/motion/timeline.js +68 -0
- package/core/motion/transitions.js +49 -0
- package/core/motion/triggers.js +168 -0
- package/core/security/checks/fs.js +92 -0
- package/core/security/detect.js +35 -0
- package/core/security/engine.js +116 -0
- package/core/security/index.js +18 -0
- package/core/security/registry.js +85 -0
- package/core/security/report/report.js +113 -0
- package/core/security/rules/ci/index.js +146 -0
- package/core/security/rules/consumer/index.js +174 -0
- package/core/security/rules/deps/index.js +182 -0
- package/core/security/rules/index.js +24 -0
- package/core/security/rules/publish/index.js +230 -0
- package/core/security/rules/repo/index.js +143 -0
- package/core/security/rules/secrets/index.js +133 -0
- package/core/security/score/dependency-health.js +53 -0
- package/core/security/score/release-health.js +66 -0
- package/core/security/score/score.js +102 -0
- package/core/security/timeline/timeline.js +76 -0
- package/dist/chunks/attributes-2ORR27KA.js +404 -0
- package/dist/chunks/attributes-HK7VIOLA.js +1080 -0
- package/dist/chunks/attributes-VRHHVNDO.js +1080 -0
- package/dist/chunks/chunk-MYKUI364.js +116 -0
- package/dist/chunks/chunk-NEVBPD5T.js +116 -0
- package/dist/chunks/chunk-Y6HN7HWX.js +116 -0
- package/dist/chunks/runtime-entry.js +1 -1
- package/dist/chunks/velin-drawer-A7Q7EBOS.js +162 -0
- package/dist/chunks/velin-empty-state-3NTUH2RF.js +81 -0
- package/dist/chunks/velin-modal-3MV4FYBK.js +231 -0
- package/dist/chunks/velin-otp-input-PSK42MM6.js +207 -0
- package/dist/chunks/velin-password-strength-TAXMLSED.js +136 -0
- package/dist/chunks/velin-sheet-N2VVYXFP.js +157 -0
- package/dist/chunks/velin-theme-toggle-J2NHC7IM.js +304 -0
- package/dist/llms.txt +4 -4
- package/dist/search-index.json +232 -5
- package/dist/velin-agent.json +433 -46
- package/dist/velinstyle-components.iife.js +3695 -2272
- package/dist/velinstyle-components.js +3698 -2271
- package/dist/velinstyle-components.min.js +262 -120
- package/dist/velinstyle.css +96 -13
- package/dist/velinstyle.d.ts +3 -0
- package/dist/velinstyle.min.css +1 -1
- package/package.json +19 -4
- package/packages/velinstyle-cli-core/package.json +11 -0
- package/packages/velinstyle-cli-core/src/config.js +84 -0
- package/packages/velinstyle-cli-core/src/contract.js +56 -0
- package/packages/velinstyle-cli-core/src/event-bus.js +50 -0
- package/packages/velinstyle-cli-core/src/index.js +18 -0
- package/packages/velinstyle-cli-core/src/lifecycle.js +33 -0
- package/packages/velinstyle-cli-core/src/runner.js +129 -0
- package/packages/velinstyle-cli-registry/data/commands.json +1216 -0
- package/packages/velinstyle-cli-registry/data/layouts.json +33 -0
- package/packages/velinstyle-cli-registry/data/widgets.json +35 -0
- package/packages/velinstyle-cli-registry/package.json +13 -0
- package/packages/velinstyle-cli-registry/src/index.js +88 -0
- package/packages/velinstyle-cli-renderer/package.json +8 -0
- package/packages/velinstyle-cli-renderer/src/index.js +286 -0
- package/packages/velinstyle-cli-theme/package.json +8 -0
- package/packages/velinstyle-cli-theme/src/index.js +84 -0
- package/packages/velinstyle-cli-ui/package.json +8 -0
- package/packages/velinstyle-cli-ui/src/index.js +34 -0
- package/packages/velinstyle-motion/README.md +81 -0
- package/packages/velinstyle-motion/package.json +37 -0
- package/packages/velinstyle-motion/src/ai/mini-prompt.js +119 -0
- package/packages/velinstyle-motion/src/ai/provider.js +59 -0
- package/packages/velinstyle-motion/src/ai/providers/pixverse-setup.js +402 -0
- package/packages/velinstyle-motion/src/ai/providers/pixverse.js +245 -0
- package/packages/velinstyle-motion/src/config.js +257 -0
- package/packages/velinstyle-motion/src/export/html-clip.js +141 -0
- package/packages/velinstyle-motion/src/index.js +304 -0
- package/packages/velinstyle-motion/src/jobs/cache.js +30 -0
- package/packages/velinstyle-motion/src/jobs/queue.js +69 -0
- package/packages/velinstyle-motion/src/jobs/usage.js +53 -0
- package/packages/velinstyle-motion/src/local/effects.js +139 -0
- package/packages/velinstyle-motion/src/local/engine.js +96 -0
- package/packages/velinstyle-motion/src/providers-catalog.js +44 -0
- package/packages/velinstyle-motion/src/registry/presets.js +25 -0
- package/packages/velinstyle-motion/src/registry/presets.json +85 -0
- package/packages/velinstyle-motion/src/ux/cost-gate.js +37 -0
- package/packages/velinstyle-motion/src/ux/pixverse-gate.js +153 -0
- package/packages/velinstyle-motion/src/ux/wizard.js +171 -0
- package/packages/velinstyle-motion/src/validate-image.js +91 -0
- package/packages/velinstyle-skills/catalog.json +1 -1
- package/packages/velinstyle-skills/registry.json +1 -142
- package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +1 -1
- package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +1 -1
- package/src/components/data-table.css +19 -0
- package/src/components/empty-auth.css +33 -0
- package/src/components/table.css +16 -6
- package/src/tokens/motion.css +7 -0
- package/src/utilities/scroll-animation.css +66 -0
- package/src/velinstyle.css +1 -0
package/cli/skills.js
CHANGED
|
@@ -28,7 +28,11 @@ export function loadSkillRegistry() {
|
|
|
28
28
|
return loadRegistry(REGISTRY_PATH);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
/**
|
|
32
|
+
* @param {string[]} argv
|
|
33
|
+
* @param {{ silent?: boolean }} [opts]
|
|
34
|
+
*/
|
|
35
|
+
export function skillsList(argv = [], opts = {}) {
|
|
32
36
|
const registry = loadSkillRegistry();
|
|
33
37
|
const items = listSkills(registry, {
|
|
34
38
|
capability: argValue(argv, '--capability') || undefined,
|
|
@@ -37,14 +41,16 @@ export function skillsList(argv = []) {
|
|
|
37
41
|
origin: argValue(argv, '--origin') || undefined,
|
|
38
42
|
category: argValue(argv, '--category') || undefined,
|
|
39
43
|
});
|
|
44
|
+
if (opts.silent) return items;
|
|
40
45
|
if (hasFlag(argv, '--json')) {
|
|
41
46
|
console.log(JSON.stringify(items, null, 2));
|
|
42
|
-
return;
|
|
47
|
+
return items;
|
|
43
48
|
}
|
|
44
49
|
for (const item of items) {
|
|
45
50
|
console.log(`${item.id} [${item.category}] ${item.priority}/${item.status} capabilities=${(item.capabilities || []).join(',')}`);
|
|
46
51
|
}
|
|
47
52
|
console.log(`\n${items.length} skill(s)`);
|
|
53
|
+
return items;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
export function skillsShow(skillId, argv = []) {
|
|
@@ -56,9 +62,10 @@ export function skillsShow(skillId, argv = []) {
|
|
|
56
62
|
}
|
|
57
63
|
if (hasFlag(argv, '--human')) {
|
|
58
64
|
console.log(formatSkillHuman(skill));
|
|
59
|
-
return;
|
|
65
|
+
return skill;
|
|
60
66
|
}
|
|
61
67
|
console.log(JSON.stringify(skill, null, 2));
|
|
68
|
+
return skill;
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
function formatSkillHuman(skill) {
|
|
@@ -103,9 +110,14 @@ function formatSkillHuman(skill) {
|
|
|
103
110
|
return lines.filter(Boolean).join('\n');
|
|
104
111
|
}
|
|
105
112
|
|
|
106
|
-
|
|
113
|
+
/**
|
|
114
|
+
* @param {string[]} argv
|
|
115
|
+
* @param {{ silent?: boolean }} [opts]
|
|
116
|
+
*/
|
|
117
|
+
export function skillsInstall(argv = [], opts = {}) {
|
|
107
118
|
const selection = argv[0];
|
|
108
119
|
if (!selection) {
|
|
120
|
+
if (opts.silent) return null;
|
|
109
121
|
console.error('Usage: velinstyle skills install <skill-id|pack|bundle:id|project:id>');
|
|
110
122
|
process.exit(1);
|
|
111
123
|
}
|
|
@@ -118,18 +130,30 @@ export function skillsInstall(argv = []) {
|
|
|
118
130
|
targetRoot: target,
|
|
119
131
|
includeDependencies: !hasFlag(argv, '--no-deps'),
|
|
120
132
|
});
|
|
133
|
+
if (opts.silent) {
|
|
134
|
+
const markerPath = join(target, 'registry-source.json');
|
|
135
|
+
writeFileSync(markerPath, JSON.stringify({ source: REGISTRY_PATH, installed: result.installed }, null, 2), 'utf-8');
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
121
138
|
for (const installed of result.installed) console.log(`Installed ${installed}`);
|
|
122
139
|
if (result.issues.length) {
|
|
123
140
|
for (const issue of result.issues) console.warn(`Warning: ${issue}`);
|
|
124
141
|
}
|
|
125
142
|
const markerPath = join(target, 'registry-source.json');
|
|
126
143
|
writeFileSync(markerPath, JSON.stringify({ source: REGISTRY_PATH, installed: result.installed }, null, 2), 'utf-8');
|
|
144
|
+
return result;
|
|
127
145
|
}
|
|
128
146
|
|
|
129
|
-
|
|
147
|
+
/**
|
|
148
|
+
* @param {string} skillId
|
|
149
|
+
* @param {string[]} argv
|
|
150
|
+
* @param {{ silent?: boolean }} [opts]
|
|
151
|
+
*/
|
|
152
|
+
export function skillsRun(skillId, argv = [], opts = {}) {
|
|
130
153
|
const registry = loadSkillRegistry();
|
|
131
154
|
const resolved = resolveSkillGraph(registry, skillId);
|
|
132
155
|
if (resolved.issues.length) {
|
|
156
|
+
if (opts.silent) return { ok: false, issues: resolved.issues, steps: [] };
|
|
133
157
|
for (const issue of resolved.issues) console.error(issue);
|
|
134
158
|
process.exit(1);
|
|
135
159
|
}
|
|
@@ -139,20 +163,27 @@ export function skillsRun(skillId, argv = []) {
|
|
|
139
163
|
if (!skill) continue;
|
|
140
164
|
steps.push({ id: skill.id, requiresCli: skill.requiresCli || [], onlyIf: skill.onlyIf || [] });
|
|
141
165
|
}
|
|
166
|
+
const payload = { steps };
|
|
167
|
+
if (opts.silent) return payload;
|
|
142
168
|
if (hasFlag(argv, '--json')) {
|
|
143
|
-
console.log(JSON.stringify(
|
|
144
|
-
return;
|
|
169
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
170
|
+
return payload;
|
|
145
171
|
}
|
|
146
172
|
for (const step of steps) {
|
|
147
173
|
console.log(`- ${step.id}`);
|
|
148
174
|
if (step.requiresCli.length) console.log(` cli: ${step.requiresCli.join(', ')}`);
|
|
149
175
|
if (step.onlyIf.length) console.log(` onlyIf: ${step.onlyIf.join(', ')}`);
|
|
150
176
|
}
|
|
177
|
+
return payload;
|
|
151
178
|
}
|
|
152
179
|
|
|
153
|
-
|
|
180
|
+
/**
|
|
181
|
+
* @param {{ silent?: boolean }} [opts]
|
|
182
|
+
*/
|
|
183
|
+
export function skillsValidate(opts = {}) {
|
|
154
184
|
const registry = loadSkillRegistry();
|
|
155
185
|
const result = validateRegistry(registry);
|
|
186
|
+
if (opts.silent) return result;
|
|
156
187
|
if (result.errors.length) {
|
|
157
188
|
for (const error of result.errors) console.error(`Error: ${error}`);
|
|
158
189
|
}
|
|
@@ -163,9 +194,14 @@ export function skillsValidate() {
|
|
|
163
194
|
process.exit(result.ok ? 0 : 1);
|
|
164
195
|
}
|
|
165
196
|
|
|
166
|
-
|
|
197
|
+
/**
|
|
198
|
+
* @param {string[]} argv
|
|
199
|
+
* @param {{ silent?: boolean }} [opts]
|
|
200
|
+
*/
|
|
201
|
+
export async function skillsDoctor(argv = [], opts = {}) {
|
|
167
202
|
const { doctorSkillPaths } = await import('../scripts/check-skills-paths.mjs');
|
|
168
203
|
const result = doctorSkillPaths(PKG_ROOT);
|
|
204
|
+
if (opts.silent) return result;
|
|
169
205
|
if (hasFlag(argv, '--json')) {
|
|
170
206
|
console.log(JSON.stringify(result, null, 2));
|
|
171
207
|
process.exit(result.ok ? 0 : 1);
|
|
@@ -176,9 +212,14 @@ export async function skillsDoctor(argv = []) {
|
|
|
176
212
|
process.exit(1);
|
|
177
213
|
}
|
|
178
214
|
console.log(`skills doctor OK (${result.checked} paths).`);
|
|
215
|
+
return result;
|
|
179
216
|
}
|
|
180
217
|
|
|
181
|
-
|
|
218
|
+
/**
|
|
219
|
+
* @param {string} kind
|
|
220
|
+
* @param {{ silent?: boolean }} [opts]
|
|
221
|
+
*/
|
|
222
|
+
export function skillsIndex(kind, opts = {}) {
|
|
182
223
|
const registry = loadSkillRegistry();
|
|
183
224
|
const map = {
|
|
184
225
|
packs: registry.packs || [],
|
|
@@ -188,7 +229,9 @@ export function skillsIndex(kind) {
|
|
|
188
229
|
graphs: registry.workflowGraphs || [],
|
|
189
230
|
};
|
|
190
231
|
const out = map[kind] || [];
|
|
232
|
+
if (opts.silent) return out;
|
|
191
233
|
console.log(JSON.stringify(out, null, 2));
|
|
234
|
+
return out;
|
|
192
235
|
}
|
|
193
236
|
|
|
194
237
|
export function skillsCommand(argv = []) {
|
package/cli/transparency.js
CHANGED
|
@@ -44,10 +44,10 @@ function printScores(scores, C) {
|
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
46
|
* @param {string[]} args process.argv slice after "transparency"
|
|
47
|
-
* @param {{ C: object, getArg: Function, hasFlag: Function }} util
|
|
47
|
+
* @param {{ C: object, getArg: Function, hasFlag: Function, emitExperience?: Function }} util
|
|
48
48
|
*/
|
|
49
49
|
export async function transparencyCmd(args, util) {
|
|
50
|
-
const { C, getArg, hasFlag } = util;
|
|
50
|
+
const { C, getArg, hasFlag, emitExperience } = util;
|
|
51
51
|
const sub = args[0] || 'doctor';
|
|
52
52
|
const rest = args.slice(1);
|
|
53
53
|
const target = rest.find((a) => !a.startsWith('-')) || '.';
|
|
@@ -55,32 +55,55 @@ export async function transparencyCmd(args, util) {
|
|
|
55
55
|
const policy = loadPolicy(policyPath);
|
|
56
56
|
const asJson = hasFlag('--json');
|
|
57
57
|
const engine = createTransparencyEngine({ policy });
|
|
58
|
+
const present = emitExperience
|
|
59
|
+
? async (payload) => emitExperience('transparency', payload)
|
|
60
|
+
: null;
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
62
|
+
try {
|
|
63
|
+
if (sub === 'scan') {
|
|
64
|
+
return await runDoctor(target, engine, { asJson, C, present });
|
|
65
|
+
}
|
|
66
|
+
if (sub === 'doctor') {
|
|
67
|
+
return await runDoctor(target, engine, { asJson, C, present });
|
|
68
|
+
}
|
|
69
|
+
if (sub === 'validate') {
|
|
70
|
+
return await runValidate(target, engine, { asJson, C, present });
|
|
71
|
+
}
|
|
72
|
+
if (sub === 'report') {
|
|
73
|
+
return await runReport(target, engine, { C, getArg, present });
|
|
74
|
+
}
|
|
75
|
+
if (sub === 'export') {
|
|
76
|
+
return await runExport(target, engine, { C, getArg, present });
|
|
77
|
+
}
|
|
78
|
+
if (sub === 'migrate') {
|
|
79
|
+
return await runMigrate(target, engine, { C, hasFlag, getArg, present });
|
|
80
|
+
}
|
|
81
|
+
if (sub === 'suggest') {
|
|
82
|
+
return await runMigrate(target, engine, { C, hasFlag, getArg, present, forceDry: true });
|
|
83
|
+
}
|
|
84
|
+
if (sub === 'apply') {
|
|
85
|
+
return await runMigrate(target, engine, { C, hasFlag, getArg, present, forceApply: true });
|
|
86
|
+
}
|
|
87
|
+
} catch (e) {
|
|
88
|
+
const message = /** @type {Error} */ (e).message || String(e);
|
|
89
|
+
if (present) {
|
|
90
|
+
await present({ status: 'ERROR', message, exitCode: 1 });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.log(C.red(message));
|
|
94
|
+
process.exitCode = 1;
|
|
95
|
+
return;
|
|
82
96
|
}
|
|
83
97
|
|
|
98
|
+
if (present) {
|
|
99
|
+
await present({
|
|
100
|
+
status: 'ERROR',
|
|
101
|
+
message: `Unknown transparency subcommand: ${sub}`,
|
|
102
|
+
suggestions: [{ text: 'velinstyle transparency doctor|validate|report|export|migrate|scan [path]' }],
|
|
103
|
+
exitCode: 1,
|
|
104
|
+
});
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
84
107
|
console.log(`Unknown transparency subcommand: ${sub}
|
|
85
108
|
Usage:
|
|
86
109
|
velinstyle transparency doctor|validate|report|export|migrate|scan [path]
|
|
@@ -88,9 +111,13 @@ Usage:
|
|
|
88
111
|
process.exitCode = 1;
|
|
89
112
|
}
|
|
90
113
|
|
|
91
|
-
async function runDoctor(target, engine, { asJson, C }) {
|
|
114
|
+
async function runDoctor(target, engine, { asJson, C, present }) {
|
|
92
115
|
const files = collectHtmlFiles(target);
|
|
93
116
|
if (!files.length) {
|
|
117
|
+
if (present) {
|
|
118
|
+
await present({ status: 'WARNING', message: 'No HTML files found.', exitCode: 1 });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
94
121
|
console.log(C.yellow('No HTML files found.'));
|
|
95
122
|
process.exitCode = 1;
|
|
96
123
|
return;
|
|
@@ -102,7 +129,7 @@ async function runDoctor(target, engine, { asJson, C }) {
|
|
|
102
129
|
const report = await engine.doctor(html, { file });
|
|
103
130
|
reports.push(report);
|
|
104
131
|
if (!report.ok) failed += 1;
|
|
105
|
-
if (!asJson) {
|
|
132
|
+
if (!asJson && !present) {
|
|
106
133
|
console.log(C.bold(`\n── ${basename(file)} ──`));
|
|
107
134
|
console.log(report.ok ? C.green('PASS') : C.red('FAIL'));
|
|
108
135
|
console.log(`disclosures=${report.summary.disclosures} errors=${report.summary.errors} warnings=${report.summary.warnings}`);
|
|
@@ -116,6 +143,30 @@ async function runDoctor(target, engine, { asJson, C }) {
|
|
|
116
143
|
}
|
|
117
144
|
if (asJson) {
|
|
118
145
|
console.log(JSON.stringify(files.length === 1 ? reports[0] : { reports }, null, 2));
|
|
146
|
+
} else if (present) {
|
|
147
|
+
const findings = reports.flatMap((r) =>
|
|
148
|
+
(r.findings || []).slice(0, 10).map((f) => [
|
|
149
|
+
basename(r.file || '') || 'file',
|
|
150
|
+
f.severity,
|
|
151
|
+
f.code,
|
|
152
|
+
String(f.message || '').slice(0, 40),
|
|
153
|
+
]),
|
|
154
|
+
);
|
|
155
|
+
const lastScores = reports[reports.length - 1]?.scores || {};
|
|
156
|
+
await present({
|
|
157
|
+
subtitle: 'doctor',
|
|
158
|
+
status: failed ? 'ERROR' : 'PASS',
|
|
159
|
+
message: failed ? `transparency doctor: ${failed} file(s) failed` : 'transparency doctor: ok',
|
|
160
|
+
items: [
|
|
161
|
+
{ label: 'Files', value: String(files.length) },
|
|
162
|
+
{ label: 'Failed', value: String(failed) },
|
|
163
|
+
...Object.entries(lastScores).map(([k, v]) => ({ label: k, value: `${v}%` })),
|
|
164
|
+
],
|
|
165
|
+
table: findings.length
|
|
166
|
+
? { columns: ['File', 'Sev', 'Code', 'Message'], rows: findings.slice(0, 25) }
|
|
167
|
+
: null,
|
|
168
|
+
exitCode: failed ? 1 : undefined,
|
|
169
|
+
});
|
|
119
170
|
} else {
|
|
120
171
|
console.log(failed ? C.red(`\ntransparency doctor: ${failed} file(s) failed`) : C.green('\ntransparency doctor: ok'));
|
|
121
172
|
}
|
|
@@ -123,7 +174,7 @@ async function runDoctor(target, engine, { asJson, C }) {
|
|
|
123
174
|
return reports;
|
|
124
175
|
}
|
|
125
176
|
|
|
126
|
-
async function runValidate(target, engine, { asJson, C }) {
|
|
177
|
+
async function runValidate(target, engine, { asJson, C, present }) {
|
|
127
178
|
const files = collectHtmlFiles(target);
|
|
128
179
|
const all = [];
|
|
129
180
|
let failed = 0;
|
|
@@ -132,15 +183,28 @@ async function runValidate(target, engine, { asJson, C }) {
|
|
|
132
183
|
const result = await engine.validate(html, { file });
|
|
133
184
|
all.push({ file, ...result });
|
|
134
185
|
if (!result.ok) failed += 1;
|
|
135
|
-
if (!asJson) {
|
|
186
|
+
if (!asJson && !present) {
|
|
136
187
|
console.log(`${basename(file)}: ${result.ok ? C.green('valid') : C.red('invalid')} (${result.findings.length} findings)`);
|
|
137
188
|
}
|
|
138
189
|
}
|
|
139
|
-
if (asJson)
|
|
190
|
+
if (asJson) {
|
|
191
|
+
console.log(JSON.stringify(all, null, 2));
|
|
192
|
+
} else if (present) {
|
|
193
|
+
await present({
|
|
194
|
+
subtitle: 'validate',
|
|
195
|
+
status: failed ? 'ERROR' : 'PASS',
|
|
196
|
+
message: failed ? `${failed} file(s) invalid` : 'All files valid',
|
|
197
|
+
table: {
|
|
198
|
+
columns: ['File', 'Status', 'Findings'],
|
|
199
|
+
rows: all.map((r) => [basename(r.file), r.ok ? 'valid' : 'invalid', String(r.findings?.length || 0)]),
|
|
200
|
+
},
|
|
201
|
+
exitCode: failed ? 1 : undefined,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
140
204
|
if (failed) process.exitCode = 1;
|
|
141
205
|
}
|
|
142
206
|
|
|
143
|
-
async function runReport(target, engine, { C, getArg }) {
|
|
207
|
+
async function runReport(target, engine, { C, getArg, present }) {
|
|
144
208
|
const outDir = resolve(getArg('--out') || 'transparency-report');
|
|
145
209
|
mkdirSync(outDir, { recursive: true });
|
|
146
210
|
const files = collectHtmlFiles(target);
|
|
@@ -155,10 +219,21 @@ async function runReport(target, engine, { C, getArg }) {
|
|
|
155
219
|
combined.files.push({ file, scores: artifacts.json.scores, ok: artifacts.json.ok });
|
|
156
220
|
}
|
|
157
221
|
writeFileSync(join(outDir, 'index.json'), JSON.stringify(combined, null, 2));
|
|
158
|
-
|
|
222
|
+
if (present) {
|
|
223
|
+
await present({
|
|
224
|
+
subtitle: 'report',
|
|
225
|
+
message: `Wrote transparency reports to ${outDir}`,
|
|
226
|
+
items: [
|
|
227
|
+
{ label: 'Files', value: String(files.length) },
|
|
228
|
+
{ label: 'Out', value: outDir },
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
} else {
|
|
232
|
+
console.log(C.green(`Wrote transparency reports to ${outDir}`));
|
|
233
|
+
}
|
|
159
234
|
}
|
|
160
235
|
|
|
161
|
-
async function runExport(target, engine, { C, getArg }) {
|
|
236
|
+
async function runExport(target, engine, { C, getArg, present }) {
|
|
162
237
|
const format = getArg('--format') || 'json';
|
|
163
238
|
const out = getArg('--output') || getArg('-o');
|
|
164
239
|
const files = collectHtmlFiles(target);
|
|
@@ -171,32 +246,56 @@ async function runExport(target, engine, { C, getArg }) {
|
|
|
171
246
|
const body = fresh.export(format);
|
|
172
247
|
if (out) {
|
|
173
248
|
writeFileSync(resolve(out), body);
|
|
174
|
-
|
|
249
|
+
if (present) {
|
|
250
|
+
await present({
|
|
251
|
+
subtitle: 'export',
|
|
252
|
+
message: `Exported ${format} → ${out}`,
|
|
253
|
+
items: [{ label: 'Format', value: format }, { label: 'Out', value: out }],
|
|
254
|
+
});
|
|
255
|
+
} else {
|
|
256
|
+
console.log(C.green(`Exported ${format} → ${out}`));
|
|
257
|
+
}
|
|
175
258
|
} else {
|
|
176
259
|
console.log(body);
|
|
177
260
|
}
|
|
178
261
|
}
|
|
179
262
|
|
|
180
|
-
async function runMigrate(target, engine, { C, hasFlag, getArg, forceDry, forceApply }) {
|
|
263
|
+
async function runMigrate(target, engine, { C, hasFlag, getArg, present, forceDry, forceApply }) {
|
|
181
264
|
const apply = forceApply || hasFlag('--apply') || hasFlag('--write');
|
|
182
265
|
const dryRun = forceDry || (!apply);
|
|
183
266
|
const files = collectHtmlFiles(target);
|
|
184
267
|
let total = 0;
|
|
268
|
+
const rows = [];
|
|
185
269
|
for (const file of files) {
|
|
186
270
|
const html = readFileSync(file, 'utf-8');
|
|
187
271
|
const result = await engine.migrate(html, { file, apply: apply && !dryRun, dryRun });
|
|
188
272
|
total += result.suggestions.length;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
273
|
+
if (!present) {
|
|
274
|
+
console.log(C.bold(`\n── ${basename(file)} ──`));
|
|
275
|
+
console.log(`${result.suggestions.length} suggestion(s)${apply && !dryRun ? `, applied ${result.applied}` : ' (dry-run)'}`);
|
|
276
|
+
for (const s of result.suggestions.slice(0, 30)) {
|
|
277
|
+
console.log(C.dim(` • ${s.id || s.field || s.kind}: ${s.reason}`));
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (const s of result.suggestions.slice(0, 15)) {
|
|
281
|
+
rows.push([basename(file), s.id || s.field || s.kind || '—', String(s.reason || '').slice(0, 40)]);
|
|
193
282
|
}
|
|
194
283
|
if (apply && !dryRun && result.applied) {
|
|
195
284
|
writeFileSync(file, result.html);
|
|
196
|
-
console.log(C.green(` wrote ${file}`));
|
|
285
|
+
if (!present) console.log(C.green(` wrote ${file}`));
|
|
197
286
|
}
|
|
198
287
|
}
|
|
199
|
-
if (
|
|
288
|
+
if (present) {
|
|
289
|
+
await present({
|
|
290
|
+
subtitle: apply && !dryRun ? 'apply' : 'migrate',
|
|
291
|
+
message: total
|
|
292
|
+
? `${total} suggestion(s)${apply && !dryRun ? ' applied' : ' (dry-run)'}`
|
|
293
|
+
: 'No migration suggestions',
|
|
294
|
+
table: rows.length ? { columns: ['File', 'Id', 'Reason'], rows: rows.slice(0, 30) } : null,
|
|
295
|
+
});
|
|
296
|
+
} else if (!total) {
|
|
297
|
+
console.log(C.green('\nNo migration suggestions.'));
|
|
298
|
+
}
|
|
200
299
|
}
|
|
201
300
|
|
|
202
301
|
/**
|
package/cli/workflow.js
CHANGED
|
@@ -5,38 +5,53 @@ function hasFlag(argv, flag) {
|
|
|
5
5
|
return argv.includes(flag);
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
velinstyle workflow landingpage
|
|
15
|
-
velinstyle workflow project:marketing-site --json
|
|
16
|
-
`);
|
|
17
|
-
process.exit(0);
|
|
18
|
-
}
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a workflow graph or project workflow without printing.
|
|
10
|
+
* @param {string} workflowId
|
|
11
|
+
* @returns {{ ok: true, data: object } | { ok: false, error: string, issues?: string[] }}
|
|
12
|
+
*/
|
|
13
|
+
export function workflowResolve(workflowId) {
|
|
19
14
|
const registry = loadRegistry(getRegistryPathForMeta());
|
|
20
15
|
let resolved;
|
|
21
16
|
if (workflowId.startsWith('project:')) {
|
|
22
17
|
const projectId = workflowId.replace('project:', '');
|
|
23
18
|
const project = (registry.projects || []).find((item) => item.id === projectId);
|
|
24
19
|
if (!project) {
|
|
25
|
-
|
|
26
|
-
process.exit(1);
|
|
20
|
+
return { ok: false, error: `Unknown project: ${projectId}` };
|
|
27
21
|
}
|
|
28
22
|
resolved = resolveWorkflowGraph(registry, project.workflowGraph);
|
|
29
23
|
} else {
|
|
30
24
|
resolved = resolveWorkflowGraph(registry, workflowId);
|
|
31
25
|
}
|
|
32
26
|
if (resolved.issues.length) {
|
|
33
|
-
|
|
27
|
+
return { ok: false, error: 'Workflow resolve failed', issues: resolved.issues };
|
|
28
|
+
}
|
|
29
|
+
return { ok: true, data: resolved };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function workflowCommand(argv = []) {
|
|
33
|
+
const workflowId = argv[0];
|
|
34
|
+
if (!workflowId || workflowId === '--help' || workflowId === '-h') {
|
|
35
|
+
console.log(`Usage: velinstyle workflow <graph-id|project:id> [--json]
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
velinstyle workflow landingpage
|
|
39
|
+
velinstyle workflow project:marketing-site --json
|
|
40
|
+
`);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
const resolved = workflowResolve(workflowId);
|
|
44
|
+
if (!resolved.ok) {
|
|
45
|
+
console.error(resolved.error);
|
|
46
|
+
if (resolved.issues) {
|
|
47
|
+
for (const issue of resolved.issues) console.error(issue);
|
|
48
|
+
}
|
|
34
49
|
process.exit(1);
|
|
35
50
|
}
|
|
36
51
|
if (hasFlag(argv, '--json')) {
|
|
37
|
-
console.log(JSON.stringify(resolved, null, 2));
|
|
52
|
+
console.log(JSON.stringify(resolved.data, null, 2));
|
|
38
53
|
return;
|
|
39
54
|
}
|
|
40
|
-
console.log(`Workflow steps (${resolved.orderedSteps.length}):`);
|
|
41
|
-
for (const step of resolved.orderedSteps) console.log(`- ${step}`);
|
|
55
|
+
console.log(`Workflow steps (${resolved.data.orderedSteps.length}):`);
|
|
56
|
+
for (const step of resolved.data.orderedSteps) console.log(`- ${step}`);
|
|
42
57
|
}
|
package/components/index.js
CHANGED
|
@@ -38,6 +38,9 @@ export { default as VelinFormSummary } from './velin-form-summary.js';
|
|
|
38
38
|
export { default as VelinCalendar } from './velin-calendar.js';
|
|
39
39
|
export { default as VelinFileDropzone } from './velin-file-dropzone.js';
|
|
40
40
|
export { default as VelinSearch } from './velin-search.js';
|
|
41
|
+
export { default as VelinOtpInput } from './velin-otp-input.js';
|
|
42
|
+
export { default as VelinPasswordStrength, scorePassword } from './velin-password-strength.js';
|
|
43
|
+
export { default as VelinEmptyState } from './velin-empty-state.js';
|
|
41
44
|
export { bindDeclarativeSearch } from './velin-search.js';
|
|
42
45
|
export { initReveal, initMotion, velinMotion } from './velin-reveal.js';
|
|
43
46
|
export { initA11y, announce, getAnnouncer } from './a11y-entry.js';
|
|
@@ -19,6 +19,7 @@ export const COMPONENT_LOADERS = {
|
|
|
19
19
|
'velin-drawer': () => import('../velin-drawer.js'),
|
|
20
20
|
'velin-dropdown': () => import('../velin-dropdown.js'),
|
|
21
21
|
'velin-email': () => import('../velin-email.js'),
|
|
22
|
+
'velin-empty-state': () => import('../velin-empty-state.js'),
|
|
22
23
|
'velin-file-dropzone': () => import('../velin-file-dropzone.js'),
|
|
23
24
|
'velin-form-summary': () => import('../velin-form-summary.js'),
|
|
24
25
|
'velin-icon': () => import('../velin-icon.js'),
|
|
@@ -26,6 +27,8 @@ export const COMPONENT_LOADERS = {
|
|
|
26
27
|
'velin-live-dot': () => import('../velin-live-dot.js'),
|
|
27
28
|
'velin-menubar': () => import('../velin-menubar.js'),
|
|
28
29
|
'velin-modal': () => import('../velin-modal.js'),
|
|
30
|
+
'velin-otp-input': () => import('../velin-otp-input.js'),
|
|
31
|
+
'velin-password-strength': () => import('../velin-password-strength.js'),
|
|
29
32
|
'velin-persist': () => import('../velin-persist.js'),
|
|
30
33
|
'velin-popover': () => import('../velin-popover.js'),
|
|
31
34
|
'velin-progress-ring': () => import('../velin-progress-ring.js'),
|
|
@@ -49,25 +49,28 @@ const styles = `
|
|
|
49
49
|
`;
|
|
50
50
|
|
|
51
51
|
class VelinDrawer extends HTMLElement {
|
|
52
|
-
static get observedAttributes() { return ['open']; }
|
|
52
|
+
static get observedAttributes() { return ['open', 'title']; }
|
|
53
53
|
|
|
54
54
|
constructor() {
|
|
55
55
|
super();
|
|
56
56
|
this.attachShadow({ mode: 'open', delegatesFocus: true });
|
|
57
57
|
this._prev = null;
|
|
58
58
|
this._onKey = this._onKey.bind(this);
|
|
59
|
+
this._onTitleSlot = this._onTitleSlot.bind(this);
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
connectedCallback() {
|
|
62
|
-
|
|
63
|
-
const safeTitle = escapeHTML(title);
|
|
63
|
+
if (this.shadowRoot.querySelector('.drawer')) return;
|
|
64
64
|
const titleId = 'velin-drawer-title';
|
|
65
65
|
this.shadowRoot.innerHTML = `
|
|
66
66
|
<style>${styles}</style>
|
|
67
67
|
<div class="overlay" part="overlay"></div>
|
|
68
68
|
<div class="drawer" role="dialog" aria-modal="true" aria-labelledby="${titleId}" part="drawer">
|
|
69
69
|
<div class="header" part="header">
|
|
70
|
-
<h2 class="title" id="${titleId}"
|
|
70
|
+
<h2 class="title" id="${titleId}" part="title">
|
|
71
|
+
<slot name="title"></slot>
|
|
72
|
+
<span class="title-fallback"></span>
|
|
73
|
+
</h2>
|
|
71
74
|
<button class="close-btn" aria-label="Close" part="close">×</button>
|
|
72
75
|
</div>
|
|
73
76
|
<div class="body" part="body"><slot></slot></div>
|
|
@@ -75,15 +78,51 @@ class VelinDrawer extends HTMLElement {
|
|
|
75
78
|
`;
|
|
76
79
|
this.shadowRoot.querySelector('.close-btn').addEventListener('click', () => this.close());
|
|
77
80
|
this.shadowRoot.querySelector('.overlay').addEventListener('click', () => this.close());
|
|
81
|
+
this.shadowRoot.querySelector('slot[name="title"]').addEventListener('slotchange', this._onTitleSlot);
|
|
82
|
+
this._syncTitle();
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
attributeChangedCallback(name) {
|
|
81
86
|
if (name === 'open') this.hasAttribute('open') ? this._open() : this._close();
|
|
87
|
+
if (name === 'title') this._syncTitle();
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
open() { this.setAttribute('open', ''); }
|
|
85
91
|
close() { this.removeAttribute('open'); this.dispatchEvent(new CustomEvent('velin-close', { bubbles: true })); }
|
|
86
92
|
|
|
93
|
+
_syncTitle() {
|
|
94
|
+
const fallback = this.shadowRoot?.querySelector('.title-fallback');
|
|
95
|
+
if (!fallback) return;
|
|
96
|
+
fallback.textContent = this.getAttribute('title') || '';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_hasTitleSlot() {
|
|
100
|
+
const slot = this.shadowRoot?.querySelector('slot[name="title"]');
|
|
101
|
+
if (!slot) return false;
|
|
102
|
+
return slot.assignedNodes({ flatten: true }).some((n) => {
|
|
103
|
+
if (n.nodeType === Node.TEXT_NODE) return Boolean(n.textContent.trim());
|
|
104
|
+
return n.nodeType === Node.ELEMENT_NODE;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_onTitleSlot() {
|
|
109
|
+
const dialog = this.shadowRoot?.querySelector('[role="dialog"]');
|
|
110
|
+
const fallback = this.shadowRoot?.querySelector('.title-fallback');
|
|
111
|
+
if (!dialog || !fallback) return;
|
|
112
|
+
if (this._hasTitleSlot()) {
|
|
113
|
+
fallback.hidden = true;
|
|
114
|
+
dialog.removeAttribute('aria-labelledby');
|
|
115
|
+
const slot = this.shadowRoot.querySelector('slot[name="title"]');
|
|
116
|
+
const label = slot.assignedNodes({ flatten: true }).map((n) => n.textContent || '').join(' ').trim();
|
|
117
|
+
if (label) dialog.setAttribute('aria-label', label);
|
|
118
|
+
} else {
|
|
119
|
+
fallback.hidden = false;
|
|
120
|
+
dialog.removeAttribute('aria-label');
|
|
121
|
+
dialog.setAttribute('aria-labelledby', 'velin-drawer-title');
|
|
122
|
+
this._syncTitle();
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
87
126
|
_open() {
|
|
88
127
|
this._prev = saveFocus();
|
|
89
128
|
setBackgroundInert(this);
|