@archetypeai/ds-cli 0.3.9 → 0.3.11
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/bin.js +4 -0
- package/commands/create.js +76 -33
- package/commands/init.js +49 -19
- package/files/AGENTS.md +19 -3
- package/files/CLAUDE.md +21 -3
- package/files/rules/accessibility.md +49 -0
- package/files/rules/frontend-architecture.md +77 -0
- package/files/skills/apply-ds/SKILL.md +86 -82
- package/files/skills/apply-ds/scripts/audit.sh +169 -0
- package/files/skills/apply-ds/scripts/setup.sh +48 -166
- package/files/skills/create-dashboard/SKILL.md +12 -0
- package/files/skills/embedding-from-file/SKILL.md +415 -0
- package/files/skills/embedding-from-sensor/SKILL.md +406 -0
- package/files/skills/embedding-upload/SKILL.md +414 -0
- package/files/skills/fix-accessibility/SKILL.md +57 -9
- package/files/skills/newton-activity-monitor-lens-on-video/SKILL.md +817 -0
- package/files/skills/newton-camera-frame-analysis/SKILL.md +611 -0
- package/files/skills/newton-camera-frame-analysis/scripts/activity-monitor-frame.py +165 -0
- package/files/skills/newton-camera-frame-analysis/scripts/captures/logs/api_responses_20260206_105610.json +62 -0
- package/files/skills/newton-camera-frame-analysis/scripts/continuous_monitor.py +119 -0
- package/files/skills/newton-direct-query/SKILL.md +212 -0
- package/files/skills/newton-direct-query/scripts/direct_query.py +129 -0
- package/files/skills/newton-machine-state-from-file/SKILL.md +545 -0
- package/files/skills/newton-machine-state-from-sensor/SKILL.md +707 -0
- package/files/skills/newton-machine-state-upload/SKILL.md +986 -0
- package/lib/add-ds-config-codeagent.js +5 -1
- package/lib/is-interactive.js +19 -0
- package/lib/scaffold-ds-svelte-project.js +117 -0
- package/package.json +1 -1
package/bin.js
CHANGED
|
@@ -31,12 +31,16 @@ Create flags:
|
|
|
31
31
|
--fonts / --no-fonts Install internal fonts (default: prompt)
|
|
32
32
|
--no-components Skip component installation (default: install all)
|
|
33
33
|
--codeagent <cursor|claude|none> Agent configuration (default: prompt)
|
|
34
|
+
--defaults Skip prompts, use sensible defaults
|
|
34
35
|
|
|
35
36
|
Init flags:
|
|
36
37
|
--pm <npm|pnpm|bun|yarn> Package manager (default: auto-detect)
|
|
37
38
|
--fonts / --no-fonts Install internal fonts (default: prompt)
|
|
38
39
|
--no-components Skip component installation (default: install all)
|
|
39
40
|
--codeagent <cursor|claude|none> Agent configuration (default: prompt)
|
|
41
|
+
--defaults Skip prompts, use sensible defaults
|
|
42
|
+
|
|
43
|
+
Non-interactive shells (no TTY) automatically use defaults.
|
|
40
44
|
|
|
41
45
|
Add targets:
|
|
42
46
|
ds-ui-svelte Install all design system components
|
package/commands/create.js
CHANGED
|
@@ -8,10 +8,12 @@ import {
|
|
|
8
8
|
installTokens,
|
|
9
9
|
initShadcn,
|
|
10
10
|
configureCss,
|
|
11
|
+
installLinting,
|
|
11
12
|
installComponents,
|
|
12
13
|
createDemoPage,
|
|
13
14
|
installAgentConfig
|
|
14
15
|
} from '../lib/scaffold-ds-svelte-project.js';
|
|
16
|
+
import { isInteractive, DEFAULTS, logDefaultsNotice } from '../lib/is-interactive.js';
|
|
15
17
|
|
|
16
18
|
// parse flags
|
|
17
19
|
export function parseFlags(args) {
|
|
@@ -20,7 +22,8 @@ export function parseFlags(args) {
|
|
|
20
22
|
framework: null,
|
|
21
23
|
pm: null,
|
|
22
24
|
components: true,
|
|
23
|
-
agent: null
|
|
25
|
+
agent: null,
|
|
26
|
+
defaults: false
|
|
24
27
|
};
|
|
25
28
|
|
|
26
29
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -33,6 +36,8 @@ export function parseFlags(args) {
|
|
|
33
36
|
flags.components = false;
|
|
34
37
|
} else if (arg === '--codeagent' && args[i + 1]) {
|
|
35
38
|
flags.agent = args[++i];
|
|
39
|
+
} else if (arg === '--defaults') {
|
|
40
|
+
flags.defaults = true;
|
|
36
41
|
} else if (!arg.startsWith('--') && !flags.name) {
|
|
37
42
|
flags.name = arg;
|
|
38
43
|
}
|
|
@@ -44,12 +49,17 @@ export function parseFlags(args) {
|
|
|
44
49
|
// create a new project
|
|
45
50
|
export async function create(args) {
|
|
46
51
|
const flags = parseFlags(args);
|
|
52
|
+
const interactive = isInteractive(flags);
|
|
47
53
|
|
|
48
54
|
p.intro('Create a new project using Archetype AI Design System.');
|
|
49
55
|
|
|
50
56
|
// specify project name
|
|
51
57
|
let name = flags.name;
|
|
52
58
|
if (!name) {
|
|
59
|
+
if (!interactive) {
|
|
60
|
+
p.log.error('Project name is required. Usage: ds create <project-name>');
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
53
63
|
name = await p.text({
|
|
54
64
|
message: 'What is your project name?',
|
|
55
65
|
placeholder: 'my-app',
|
|
@@ -87,26 +97,38 @@ export async function create(args) {
|
|
|
87
97
|
// specify framework
|
|
88
98
|
let framework = flags.framework;
|
|
89
99
|
if (!framework) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
if (!interactive) {
|
|
101
|
+
framework = DEFAULTS.framework;
|
|
102
|
+
} else {
|
|
103
|
+
framework = await p.select({
|
|
104
|
+
message: 'Which framework?',
|
|
105
|
+
options: [{ value: 'svelte', label: 'SvelteKit', hint: 'Svelte 5 + Tailwind v4, default' }]
|
|
106
|
+
});
|
|
107
|
+
if (p.isCancel(framework)) {
|
|
108
|
+
p.cancel('Setup cancelled.');
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
97
111
|
}
|
|
98
112
|
}
|
|
99
113
|
|
|
100
114
|
// specify package manager
|
|
101
115
|
let pmName = flags.pm;
|
|
102
116
|
if (!pmName) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
117
|
+
if (!interactive) {
|
|
118
|
+
pmName = DEFAULTS.pm;
|
|
119
|
+
} else {
|
|
120
|
+
pmName = await p.select({
|
|
121
|
+
message: 'Which package manager?',
|
|
122
|
+
options: pmNames.map((n) => ({
|
|
123
|
+
value: n,
|
|
124
|
+
label: n,
|
|
125
|
+
...(n === DEFAULTS.pm && { hint: 'default' })
|
|
126
|
+
}))
|
|
127
|
+
});
|
|
128
|
+
if (p.isCancel(pmName)) {
|
|
129
|
+
p.cancel('Setup cancelled.');
|
|
130
|
+
process.exit(0);
|
|
131
|
+
}
|
|
110
132
|
}
|
|
111
133
|
}
|
|
112
134
|
const pm = getPm(pmName);
|
|
@@ -122,13 +144,17 @@ export async function create(args) {
|
|
|
122
144
|
const projectPath = resolve(targetDir, name);
|
|
123
145
|
|
|
124
146
|
if (existsSync(projectPath)) {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
147
|
+
if (!interactive) {
|
|
148
|
+
p.log.warn(`Directory "${name}" already exists. Proceeding (non-interactive mode).`);
|
|
149
|
+
} else {
|
|
150
|
+
const overwrite = await p.confirm({
|
|
151
|
+
message: `Directory "${name}" already exists. Continue anyway?`,
|
|
152
|
+
initialValue: false
|
|
153
|
+
});
|
|
154
|
+
if (p.isCancel(overwrite) || !overwrite) {
|
|
155
|
+
p.cancel('Setup cancelled.');
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
132
158
|
}
|
|
133
159
|
}
|
|
134
160
|
|
|
@@ -151,6 +177,9 @@ export async function create(args) {
|
|
|
151
177
|
const includeFonts = false;
|
|
152
178
|
configureCss(projectPath, includeFonts);
|
|
153
179
|
|
|
180
|
+
// set up linting and formatting
|
|
181
|
+
await installLinting(pm, projectPath);
|
|
182
|
+
|
|
154
183
|
// install components
|
|
155
184
|
if (flags.components) {
|
|
156
185
|
try {
|
|
@@ -169,20 +198,33 @@ export async function create(args) {
|
|
|
169
198
|
// specify agent config
|
|
170
199
|
let agent = flags.agent;
|
|
171
200
|
if (!agent) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
201
|
+
if (!interactive) {
|
|
202
|
+
agent = DEFAULTS.agent;
|
|
203
|
+
} else {
|
|
204
|
+
agent = await p.select({
|
|
205
|
+
message: 'Install AI agent configuration?',
|
|
206
|
+
options: [
|
|
207
|
+
{ value: 'none', label: 'None' },
|
|
208
|
+
{ value: 'cursor', label: 'Cursor', hint: 'AGENTS.md + skills + rules' },
|
|
209
|
+
{ value: 'claude', label: 'Claude Code', hint: 'CLAUDE.md + skills + rules, default' }
|
|
210
|
+
]
|
|
211
|
+
});
|
|
212
|
+
if (p.isCancel(agent)) {
|
|
213
|
+
p.cancel('Setup cancelled.');
|
|
214
|
+
process.exit(0);
|
|
215
|
+
}
|
|
183
216
|
}
|
|
184
217
|
}
|
|
185
218
|
|
|
219
|
+
// log defaults notice
|
|
220
|
+
if (!interactive) {
|
|
221
|
+
const applied = {};
|
|
222
|
+
if (!flags.framework) applied.framework = framework;
|
|
223
|
+
if (!flags.pm) applied.pm = pmName;
|
|
224
|
+
if (!flags.agent) applied.codeagent = agent;
|
|
225
|
+
logDefaultsNotice(applied);
|
|
226
|
+
}
|
|
227
|
+
|
|
186
228
|
const validAgents = ['cursor', 'claude', 'none'];
|
|
187
229
|
if (!validAgents.includes(agent)) {
|
|
188
230
|
p.log.error(`Invalid --codeagent value "${agent}". Must be one of: ${validAgents.join(', ')}`);
|
|
@@ -203,6 +245,7 @@ export async function create(args) {
|
|
|
203
245
|
`Framework: ${framework}`,
|
|
204
246
|
`Package Manager: ${pm.name}`,
|
|
205
247
|
`Fonts: ${includeFonts ? 'installed' : 'skipped'}`,
|
|
248
|
+
`Linting: configured`,
|
|
206
249
|
`Components: ${flags.components ? 'all' : 'skipped'}`,
|
|
207
250
|
`Agent Configuration: ${agent}`
|
|
208
251
|
].join('\n');
|
package/commands/init.js
CHANGED
|
@@ -8,16 +8,19 @@ import {
|
|
|
8
8
|
installTokens,
|
|
9
9
|
initShadcn,
|
|
10
10
|
prependCss,
|
|
11
|
+
installLinting,
|
|
11
12
|
installComponents,
|
|
12
13
|
installAgentConfig
|
|
13
14
|
} from '../lib/scaffold-ds-svelte-project.js';
|
|
15
|
+
import { isInteractive, DEFAULTS, logDefaultsNotice } from '../lib/is-interactive.js';
|
|
14
16
|
|
|
15
17
|
// parse flags
|
|
16
18
|
export function parseFlags(args) {
|
|
17
19
|
const flags = {
|
|
18
20
|
pm: null,
|
|
19
21
|
components: true,
|
|
20
|
-
agent: null
|
|
22
|
+
agent: null,
|
|
23
|
+
defaults: false
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -28,6 +31,8 @@ export function parseFlags(args) {
|
|
|
28
31
|
flags.components = false;
|
|
29
32
|
} else if (arg === '--codeagent' && args[i + 1]) {
|
|
30
33
|
flags.agent = args[++i];
|
|
34
|
+
} else if (arg === '--defaults') {
|
|
35
|
+
flags.defaults = true;
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
38
|
|
|
@@ -48,6 +53,7 @@ function hasTailwind(projectPath) {
|
|
|
48
53
|
// initialize the design system in an existing project
|
|
49
54
|
export async function init(args) {
|
|
50
55
|
const flags = parseFlags(args);
|
|
56
|
+
const interactive = isInteractive(flags);
|
|
51
57
|
const projectPath = process.cwd();
|
|
52
58
|
|
|
53
59
|
// validate --pm flag
|
|
@@ -81,17 +87,33 @@ export async function init(args) {
|
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
if (!pmName) {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
if (!interactive) {
|
|
91
|
+
pmName = DEFAULTS.pm;
|
|
92
|
+
} else {
|
|
93
|
+
pmName = await p.select({
|
|
94
|
+
message: 'Which package manager?',
|
|
95
|
+
options: pmNames.map((n) => ({
|
|
96
|
+
value: n,
|
|
97
|
+
label: n,
|
|
98
|
+
...(n === DEFAULTS.pm && { hint: 'default' })
|
|
99
|
+
}))
|
|
100
|
+
});
|
|
101
|
+
if (p.isCancel(pmName)) {
|
|
102
|
+
p.cancel('Setup cancelled.');
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
107
|
const pm = getPm(pmName);
|
|
94
108
|
|
|
109
|
+
// log defaults notice
|
|
110
|
+
if (!interactive) {
|
|
111
|
+
const applied = {};
|
|
112
|
+
if (!flags.pm) applied.pm = pmName;
|
|
113
|
+
if (!flags.agent) applied.codeagent = DEFAULTS.agent;
|
|
114
|
+
logDefaultsNotice(applied);
|
|
115
|
+
}
|
|
116
|
+
|
|
95
117
|
// verify package manager is installed
|
|
96
118
|
if (!isPmInstalled(pmName)) {
|
|
97
119
|
p.log.error(`${pmName} is not installed. Please install it first and try again.`);
|
|
@@ -120,6 +142,9 @@ export async function init(args) {
|
|
|
120
142
|
const includeFonts = false;
|
|
121
143
|
prependCss(projectPath, includeFonts);
|
|
122
144
|
|
|
145
|
+
// set up linting and formatting
|
|
146
|
+
await installLinting(pm, projectPath);
|
|
147
|
+
|
|
123
148
|
// install components
|
|
124
149
|
if (flags.components) {
|
|
125
150
|
try {
|
|
@@ -138,17 +163,21 @@ export async function init(args) {
|
|
|
138
163
|
// specify agent config
|
|
139
164
|
let agent = flags.agent;
|
|
140
165
|
if (!agent) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
166
|
+
if (!interactive) {
|
|
167
|
+
agent = DEFAULTS.agent;
|
|
168
|
+
} else {
|
|
169
|
+
agent = await p.select({
|
|
170
|
+
message: 'Install AI agent configuration?',
|
|
171
|
+
options: [
|
|
172
|
+
{ value: 'none', label: 'None' },
|
|
173
|
+
{ value: 'cursor', label: 'Cursor', hint: 'AGENTS.md + skills + rules' },
|
|
174
|
+
{ value: 'claude', label: 'Claude Code', hint: 'CLAUDE.md + skills + rules, default' }
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
if (p.isCancel(agent)) {
|
|
178
|
+
p.cancel('Setup cancelled.');
|
|
179
|
+
process.exit(0);
|
|
180
|
+
}
|
|
152
181
|
}
|
|
153
182
|
}
|
|
154
183
|
|
|
@@ -167,6 +196,7 @@ export async function init(args) {
|
|
|
167
196
|
`Location: ${projectPath}`,
|
|
168
197
|
`Package Manager: ${pm.name}`,
|
|
169
198
|
`Fonts: ${includeFonts ? 'installed' : 'skipped'}`,
|
|
199
|
+
`Linting: configured`,
|
|
170
200
|
`Components: ${flags.components ? 'all' : 'skipped'}`,
|
|
171
201
|
`Agent Config: ${agent}`
|
|
172
202
|
].join('\n');
|
package/files/AGENTS.md
CHANGED
|
@@ -28,7 +28,6 @@ Standard Tailwind is fine for:
|
|
|
28
28
|
|
|
29
29
|
- Spacing/sizing: `p-4`, `w-full`, `gap-2`, `h-screen`
|
|
30
30
|
- Layout: `flex`, `grid`, `absolute`, `relative`
|
|
31
|
-
- One-off colors: gradients, illustrations, custom accents
|
|
32
31
|
|
|
33
32
|
## CSS Import Order
|
|
34
33
|
|
|
@@ -49,15 +48,32 @@ Standard Tailwind is fine for:
|
|
|
49
48
|
|
|
50
49
|
Read these when relevant to your task:
|
|
51
50
|
|
|
52
|
-
- `@skills/apply-ds` -
|
|
51
|
+
- `@skills/apply-ds` - apply DS tokens, components, and patterns to an existing demo
|
|
53
52
|
- `@skills/build-pattern` - create composite patterns from primitives
|
|
54
53
|
- `@skills/setup-chart` - set up charts with layerchart
|
|
55
54
|
- `@skills/create-dashboard` - scaffold a full-viewport dashboard with menubar and panels
|
|
56
55
|
- `@skills/fix-accessibility` - audit and fix a11y issues
|
|
57
56
|
- `@skills/fix-metadata` - update page titles, favicons, and OG tags
|
|
58
57
|
- `@skills/deploy-worker` - deploy SvelteKit projects to Cloudflare Workers
|
|
59
|
-
- `@skills/
|
|
58
|
+
- `@skills/embedding-from-file` - run an Embedding Lens by streaming sensor data from a CSV file
|
|
59
|
+
- `@skills/embedding-from-sensor` - run an Embedding Lens by streaming real-time data from a physical sensor
|
|
60
|
+
- `@skills/embedding-upload` - run an Embedding Lens by uploading a CSV file for server-side processing
|
|
61
|
+
- `@skills/newton-activity-monitor-lens-on-video` - analyze uploaded video files using Newton's activity monitor lens
|
|
62
|
+
- `@skills/newton-camera-frame-analysis` - live webcam frame analysis using Newton's vision model
|
|
63
|
+
- `@skills/newton-direct-query` - simple direct query to Newton model using the /query API endpoint
|
|
64
|
+
- `@skills/newton-machine-state-from-file` - run a Machine State Lens by streaming sensor data from a CSV file
|
|
65
|
+
- `@skills/newton-machine-state-from-sensor` - run a Machine State Lens by streaming real-time data from a physical sensor
|
|
66
|
+
- `@skills/newton-machine-state-upload` - run a Machine State Lens by uploading a CSV file for server-side processing
|
|
60
67
|
|
|
61
68
|
## Rules
|
|
62
69
|
|
|
63
70
|
See `@rules/` for comprehensive guidance on design principles, components, styling, charts, and linting.
|
|
71
|
+
|
|
72
|
+
- `@rules/accessibility` — a11y guidelines and ARIA patterns
|
|
73
|
+
- `@rules/charts` — chart setup, layerchart conventions, data visualization
|
|
74
|
+
- `@rules/components` — component API patterns, props, variants, slots
|
|
75
|
+
- `@rules/design-principles` — visual design language, spacing, typography
|
|
76
|
+
- `@rules/frontend-architecture` — component decomposition, page composition, API logic extraction
|
|
77
|
+
- `@rules/linting` — linting and formatting rules
|
|
78
|
+
- `@rules/state` — state management with Svelte 5 runes
|
|
79
|
+
- `@rules/styling` — Tailwind v4, semantic tokens, theming
|
package/files/CLAUDE.md
CHANGED
|
@@ -28,7 +28,6 @@ Standard Tailwind is fine for:
|
|
|
28
28
|
|
|
29
29
|
- Spacing/sizing: `p-4`, `w-full`, `gap-2`, `h-screen`
|
|
30
30
|
- Layout: `flex`, `grid`, `absolute`, `relative`
|
|
31
|
-
- One-off colors: gradients, illustrations, custom accents
|
|
32
31
|
|
|
33
32
|
## CSS Import Order
|
|
34
33
|
|
|
@@ -47,17 +46,36 @@ Standard Tailwind is fine for:
|
|
|
47
46
|
|
|
48
47
|
## Skills
|
|
49
48
|
|
|
49
|
+
**Skill composition:** When building a demo that uses a Newton or Embedding API skill, also consult `@skills/create-dashboard` for dashboard layouts and `@skills/build-pattern` for extracting components. Only include chart components (SensorChart, ScatterChart) if the user's request involves time-series or explicitly mentions charts.
|
|
50
|
+
|
|
50
51
|
Read these when relevant to your task:
|
|
51
52
|
|
|
52
|
-
- `@skills/apply-ds` -
|
|
53
|
+
- `@skills/apply-ds` - apply DS tokens, components, and patterns to an existing demo
|
|
53
54
|
- `@skills/build-pattern` - create composite patterns from primitives
|
|
54
55
|
- `@skills/setup-chart` - set up charts with layerchart
|
|
55
56
|
- `@skills/create-dashboard` - scaffold a full-viewport dashboard with menubar and panels
|
|
56
57
|
- `@skills/fix-accessibility` - audit and fix a11y issues
|
|
57
58
|
- `@skills/fix-metadata` - update page titles, favicons, and OG tags
|
|
58
59
|
- `@skills/deploy-worker` - deploy SvelteKit projects to Cloudflare Workers
|
|
59
|
-
- `@skills/
|
|
60
|
+
- `@skills/embedding-from-file` - run an Embedding Lens by streaming sensor data from a CSV file
|
|
61
|
+
- `@skills/embedding-from-sensor` - run an Embedding Lens by streaming real-time data from a physical sensor
|
|
62
|
+
- `@skills/embedding-upload` - run an Embedding Lens by uploading a CSV file for server-side processing
|
|
63
|
+
- `@skills/newton-activity-monitor-lens-on-video` - analyze uploaded video files using Newton's activity monitor lens
|
|
64
|
+
- `@skills/newton-camera-frame-analysis` - live webcam frame analysis using Newton's vision model
|
|
65
|
+
- `@skills/newton-direct-query` - simple direct query to Newton model using the /query API endpoint
|
|
66
|
+
- `@skills/newton-machine-state-from-file` - run a Machine State Lens by streaming sensor data from a CSV file
|
|
67
|
+
- `@skills/newton-machine-state-from-sensor` - run a Machine State Lens by streaming real-time data from a physical sensor
|
|
68
|
+
- `@skills/newton-machine-state-upload` - run a Machine State Lens by uploading a CSV file for server-side processing
|
|
60
69
|
|
|
61
70
|
## Rules
|
|
62
71
|
|
|
63
72
|
See `@rules/` for comprehensive guidance on design principles, components, styling, charts, and linting.
|
|
73
|
+
|
|
74
|
+
- `@rules/accessibility` — a11y guidelines and ARIA patterns
|
|
75
|
+
- `@rules/charts` — chart setup, layerchart conventions, data visualization
|
|
76
|
+
- `@rules/components` — component API patterns, props, variants, slots
|
|
77
|
+
- `@rules/design-principles` — visual design language, spacing, typography
|
|
78
|
+
- `@rules/frontend-architecture` — component decomposition, page composition, API logic extraction
|
|
79
|
+
- `@rules/linting` — linting and formatting rules
|
|
80
|
+
- `@rules/state` — state management with Svelte 5 runes
|
|
81
|
+
- `@rules/styling` — Tailwind v4, semantic tokens, theming
|
|
@@ -205,10 +205,59 @@ For custom interactive elements, ensure:
|
|
|
205
205
|
>
|
|
206
206
|
```
|
|
207
207
|
|
|
208
|
+
## Page Structure
|
|
209
|
+
|
|
210
|
+
### Skip Link
|
|
211
|
+
|
|
212
|
+
Every page should have a skip link as the first focusable element:
|
|
213
|
+
|
|
214
|
+
```svelte
|
|
215
|
+
<a
|
|
216
|
+
href="#main-content"
|
|
217
|
+
class="sr-only focus:not-sr-only focus:fixed focus:top-4 focus:left-4 focus:z-50 focus:rounded-md focus:bg-background focus:px-4 focus:py-2 focus:text-foreground focus:ring-2 focus:ring-ring"
|
|
218
|
+
>
|
|
219
|
+
Skip to content
|
|
220
|
+
</a>
|
|
221
|
+
|
|
222
|
+
<main id="main-content">
|
|
223
|
+
<!-- page content -->
|
|
224
|
+
</main>
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Semantic Landmarks
|
|
228
|
+
|
|
229
|
+
Use semantic HTML elements instead of generic `<div>` wrappers:
|
|
230
|
+
|
|
231
|
+
```svelte
|
|
232
|
+
<!-- Before -->
|
|
233
|
+
<div class="header">...</div>
|
|
234
|
+
<div class="nav">...</div>
|
|
235
|
+
<div class="content">...</div>
|
|
236
|
+
|
|
237
|
+
<!-- After -->
|
|
238
|
+
<header>...</header>
|
|
239
|
+
<nav>...</nav>
|
|
240
|
+
<main id="main-content">...</main>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Heading Hierarchy
|
|
244
|
+
|
|
245
|
+
Every page needs an `<h1>`. If the visual design doesn't include one, add it as screen-reader-only:
|
|
246
|
+
|
|
247
|
+
```svelte
|
|
248
|
+
<h1 class="sr-only">Dashboard</h1>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Never skip heading levels (e.g. `<h1>` → `<h3>`). Use the correct level for the document outline.
|
|
252
|
+
|
|
208
253
|
## Checklist
|
|
209
254
|
|
|
210
255
|
When building components, verify:
|
|
211
256
|
|
|
257
|
+
- [ ] Page has a skip-to-content link as the first focusable element
|
|
258
|
+
- [ ] Page uses semantic landmarks (`<main>`, `<header>`, `<nav>`)
|
|
259
|
+
- [ ] Page has an `<h1>` (visible or `sr-only`)
|
|
260
|
+
- [ ] Heading hierarchy doesn't skip levels
|
|
212
261
|
- [ ] Icon-only buttons have `aria-label`
|
|
213
262
|
- [ ] Decorative icons have `aria-hidden="true"`
|
|
214
263
|
- [ ] Interactive groups have `aria-label`
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
paths:
|
|
3
|
+
- '**/routes/**/*.svelte'
|
|
4
|
+
- '**/+page.svelte'
|
|
5
|
+
- '**/+layout.svelte'
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Frontend Architecture
|
|
9
|
+
|
|
10
|
+
When building a page that involves more than a simple static layout, decompose it into components rather than writing a monolithic `+page.svelte`.
|
|
11
|
+
|
|
12
|
+
## When to extract a component
|
|
13
|
+
|
|
14
|
+
Extract a component when a section of UI has:
|
|
15
|
+
|
|
16
|
+
- 3+ primitives composed together (Card + Badge + Button = a status card)
|
|
17
|
+
- Its own reactive state (`$state`, `$derived`)
|
|
18
|
+
- Potential for reuse across pages or skills
|
|
19
|
+
|
|
20
|
+
Common extraction candidates: media inputs, status displays, result/summary views, streaming logs, file upload flows.
|
|
21
|
+
|
|
22
|
+
## Gold-standard references
|
|
23
|
+
|
|
24
|
+
The project includes pattern components that demonstrate these conventions. Study them before building new ones:
|
|
25
|
+
|
|
26
|
+
- `$lib/components/ui/VideoPlayer.svelte` — media playback with controls, composes Card + AspectRatio + Button + Slider
|
|
27
|
+
- `$lib/components/ui/ExpandableLog.svelte` — streaming log display, composes Collapsible + Item + Badge
|
|
28
|
+
- `$lib/components/ui/StatusBadge.svelte` — health indicator with derived state, composes Badge + Avatar
|
|
29
|
+
- `$lib/components/ui/HealthscoreCard.svelte` — score card with derived state, composes Card sub-components
|
|
30
|
+
- `$lib/components/ui/Menubar.svelte` — branded header with snippet slots
|
|
31
|
+
|
|
32
|
+
Check if an existing pattern fits before building a new one. Use `@skills/build-pattern` to create new patterns that follow these same conventions.
|
|
33
|
+
|
|
34
|
+
## Component conventions
|
|
35
|
+
|
|
36
|
+
Follow `@rules/components` for the full conventions. The essentials:
|
|
37
|
+
|
|
38
|
+
- `let { class: className, ...restProps } = $props();`
|
|
39
|
+
- `cn()` for all class merging — never raw string concatenation
|
|
40
|
+
- `$derived` for computed state
|
|
41
|
+
- Spread `...restProps` on the root element
|
|
42
|
+
- Compose from DS primitives (Card, Badge, Button, etc.) — not raw HTML
|
|
43
|
+
|
|
44
|
+
## Page-level orchestration
|
|
45
|
+
|
|
46
|
+
`+page.svelte` is the orchestrator. It should:
|
|
47
|
+
|
|
48
|
+
- Own flow state (status, session IDs, error messages)
|
|
49
|
+
- Import and compose child components
|
|
50
|
+
- Pass data down via props
|
|
51
|
+
- Handle top-level layout (using `@skills/create-dashboard` for dashboard layouts)
|
|
52
|
+
|
|
53
|
+
Components should be presentational where possible — receive data via props, emit events up.
|
|
54
|
+
|
|
55
|
+
## API and streaming logic extraction
|
|
56
|
+
|
|
57
|
+
SSE consumers, fetch wrappers, polling loops, and data transforms belong in a utility file, not inline in components or pages:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
src/lib/api/activity-monitor.js — SSE + session management
|
|
61
|
+
src/lib/api/machine-state.js — streaming + windowing
|
|
62
|
+
src/lib/api/embeddings.js — upload + embedding extraction
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
This keeps components focused on rendering and makes API logic testable and reusable.
|
|
66
|
+
|
|
67
|
+
## Streaming UI
|
|
68
|
+
|
|
69
|
+
For skills that stream results (SSE, polling), prefer:
|
|
70
|
+
|
|
71
|
+
- Progressive rendering — show results as they arrive, don't wait for completion
|
|
72
|
+
- Live counters or progress indicators
|
|
73
|
+
- Auto-scrolling log views (reference ExpandableLog pattern)
|
|
74
|
+
|
|
75
|
+
## Override clause
|
|
76
|
+
|
|
77
|
+
If the user explicitly requests a single-file prototype, a minimal example, or specifies a different structure, follow their instruction. These guidelines apply to production-quality demos, not quick experiments.
|