@codeyam/codeyam-cli 0.1.23 → 0.1.25
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/analyzer-template/.build-info.json +7 -7
- package/analyzer-template/log.txt +3 -3
- package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +7 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +7 -1
- package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
- package/analyzer-template/project/runMultiScenarioServer.ts +26 -3
- package/background/src/lib/virtualized/project/runMultiScenarioServer.js +23 -3
- package/background/src/lib/virtualized/project/runMultiScenarioServer.js.map +1 -1
- package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js +30 -0
- package/codeyam-cli/src/commands/__tests__/editor.designSystem.test.js.map +1 -0
- package/codeyam-cli/src/commands/editor.js +349 -106
- package/codeyam-cli/src/commands/editor.js.map +1 -1
- package/codeyam-cli/src/data/designSystems.js +27 -0
- package/codeyam-cli/src/data/designSystems.js.map +1 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js +44 -0
- package/codeyam-cli/src/utils/__tests__/editorApi.test.js.map +1 -1
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js +6 -0
- package/codeyam-cli/src/utils/__tests__/webappDetection.test.js.map +1 -1
- package/codeyam-cli/src/utils/editorApi.js +16 -0
- package/codeyam-cli/src/utils/editorApi.js.map +1 -1
- package/codeyam-cli/src/utils/editorScenarios.js +1 -0
- package/codeyam-cli/src/utils/editorScenarios.js.map +1 -1
- package/codeyam-cli/src/utils/entityChangeStatus.server.js +15 -0
- package/codeyam-cli/src/utils/entityChangeStatus.server.js.map +1 -1
- package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js +159 -0
- package/codeyam-cli/src/utils/queue/__tests__/job.interactiveStart.test.js.map +1 -0
- package/codeyam-cli/src/utils/queue/job.js +9 -1
- package/codeyam-cli/src/utils/queue/job.js.map +1 -1
- package/codeyam-cli/src/utils/scenariosManifest.js +8 -2
- package/codeyam-cli/src/utils/scenariosManifest.js.map +1 -1
- package/codeyam-cli/src/utils/testResultCache.js +53 -0
- package/codeyam-cli/src/utils/testResultCache.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.server.js +81 -0
- package/codeyam-cli/src/utils/testResultCache.server.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.server.test.js +187 -0
- package/codeyam-cli/src/utils/testResultCache.server.test.js.map +1 -0
- package/codeyam-cli/src/utils/testResultCache.test.js +230 -0
- package/codeyam-cli/src/utils/testResultCache.test.js.map +1 -0
- package/codeyam-cli/src/utils/webappDetection.js +4 -2
- package/codeyam-cli/src/utils/webappDetection.js.map +1 -1
- package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js +98 -0
- package/codeyam-cli/src/webserver/__tests__/api.interactive-switch-scenario.test.js.map +1 -0
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js +32 -0
- package/codeyam-cli/src/webserver/__tests__/idleDetector.test.js.map +1 -1
- package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js +34 -0
- package/codeyam-cli/src/webserver/app/routes/api.interactive-switch-scenario.js.map +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-CKeQT5Ty.js → InteractivePreview-DtYTSPL2.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DUMfcNVK.js → ScenarioViewer-CefgqbCr.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-Bc8BG-Lw.js +34 -0
- package/codeyam-cli/src/webserver/build/client/assets/{_index-BAWd-Xjf.js → _index-C1YkzTAV.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BOARiB-g.js → activity.(_tab)-yH46LLUz.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/api.editor-verify-routes-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/api.interactive-switch-scenario-l0sNRNKZ.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-C8y4mmyv.js → dev.empty-CRepiabR.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DLM1-ZMt.js +96 -0
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-ByHz6rAQ.js → entity._sha._-DYJRGiDI.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.dev-CmLO432x.js → entity._sha.scenarios._scenarioId.dev-wdiwx5-Z.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-Bz9sCUF_.js → entity._sha.scenarios._scenarioId.fullscreen-BrkN-40Y.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-DQM8E7L4.js → entity._sha_.create-scenario-DxfhekTZ.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CAoXLsQr.js → entity._sha_.edit._scenarioId-CRXJWmpB.js} +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/globals-9EkC9j9I.css +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/manifest-7e749098.js +1 -0
- package/codeyam-cli/src/webserver/build/client/assets/{root-D2_tktnk.js → root-DGtly3mb.js} +2 -2
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-D9QZKaLJ.js +2 -0
- package/codeyam-cli/src/webserver/build/server/assets/{analysisRunner-By5slFjw.js → analysisRunner-CO8xocj3.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{index-DXaOwBnm.js → index-QKPqlUgg.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/{init-CLG1LjQM.js → init-DlspChIk.js} +1 -1
- package/codeyam-cli/src/webserver/build/server/assets/server-build-ChzicV-B.js +689 -0
- package/codeyam-cli/src/webserver/build/server/index.js +1 -1
- package/codeyam-cli/src/webserver/build-info.json +5 -5
- package/codeyam-cli/src/webserver/idleDetector.js +12 -3
- package/codeyam-cli/src/webserver/idleDetector.js.map +1 -1
- package/codeyam-cli/src/webserver/terminalServer.js +4 -3
- package/codeyam-cli/src/webserver/terminalServer.js.map +1 -1
- package/codeyam-cli/templates/design-systems/clean-dashboard-design-system.md +255 -0
- package/codeyam-cli/templates/design-systems/editorial-design-system.md +267 -0
- package/codeyam-cli/templates/design-systems/mono-brutalist-design-system.md +256 -0
- package/codeyam-cli/templates/design-systems/neo-brutalist-design-system.md +294 -0
- package/codeyam-cli/templates/expo-react-native/MOBILE_SETUP.md +115 -0
- package/codeyam-cli/templates/expo-react-native/__tests__/.gitkeep +0 -0
- package/codeyam-cli/templates/expo-react-native/app/_layout.tsx +3 -2
- package/codeyam-cli/templates/expo-react-native/global.css +7 -0
- package/codeyam-cli/templates/expo-react-native/lib/theme.ts +73 -0
- package/codeyam-cli/templates/expo-react-native/package.json +16 -6
- package/codeyam-cli/templates/isolation-route/expo-router.tsx.template +54 -0
- package/codeyam-cli/templates/nextjs-prisma-sqlite/seed-adapter.ts +10 -5
- package/codeyam-cli/templates/seed-adapters/supabase.ts +14 -5
- package/codeyam-cli/templates/skills/codeyam-editor/SKILL.md +15 -0
- package/package.json +1 -1
- package/packages/database/src/lib/loadAnalysis.js +7 -1
- package/packages/database/src/lib/loadAnalysis.js.map +1 -1
- package/codeyam-cli/src/webserver/build/client/assets/Spinner-D0LgAaSa.js +0 -34
- package/codeyam-cli/src/webserver/build/client/assets/editor.entity.(_sha)-DMv5ESGo.js +0 -96
- package/codeyam-cli/src/webserver/build/client/assets/globals-oyPmV37k.css +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/manifest-1a45e154.js +0 -1
- package/codeyam-cli/src/webserver/build/client/assets/useLastLogLine-BNd5hYuW.js +0 -2
- package/codeyam-cli/src/webserver/build/server/assets/server-build-NZmUqQv6.js +0 -688
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# Neo-Brutalist Design System
|
|
2
|
+
|
|
3
|
+
> Hard-edged, warm, editorial UI. Offset shadows, bold borders, hot pink accent. For any web or mobile app.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
Heavy black borders and offset box shadows create a "printed" feel. Warm off-white base (never pure white or grey). Typography is heavy and confident. No gradients. No rounded corners over 18px.
|
|
8
|
+
|
|
9
|
+
**Rules:**
|
|
10
|
+
|
|
11
|
+
1. Cards always have `border: 2px solid` + offset box shadow
|
|
12
|
+
2. Interactive elements "press" on hover/active (`translate(2px, 2px)`, shadow removed)
|
|
13
|
+
3. Dark mode inverts backgrounds but keeps accent colors identical
|
|
14
|
+
4. Dark cards/sidebar stay dark in both modes
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Colors
|
|
19
|
+
|
|
20
|
+
### Light Mode
|
|
21
|
+
|
|
22
|
+
```css
|
|
23
|
+
[data-theme='light'] {
|
|
24
|
+
--bg-base: #f5f0e8; /* warm off-white page bg */
|
|
25
|
+
--bg-surface: #ffffff; /* cards, panels */
|
|
26
|
+
--bg-muted: #ede8de; /* subtle sections, table headers */
|
|
27
|
+
--bg-inverse: #0d0d0d; /* dark surfaces */
|
|
28
|
+
|
|
29
|
+
--text-primary: #0d0d0d;
|
|
30
|
+
--text-secondary: #666666;
|
|
31
|
+
--text-muted: #999999;
|
|
32
|
+
--text-inverse: #ffffff;
|
|
33
|
+
|
|
34
|
+
--border: #0d0d0d;
|
|
35
|
+
--border-light: #e5ddd0; /* internal dividers */
|
|
36
|
+
|
|
37
|
+
--shadow-sm: 2px 2px 0px #0d0d0d;
|
|
38
|
+
--shadow-md: 4px 4px 0px #0d0d0d;
|
|
39
|
+
--shadow-lg: 6px 6px 0px #0d0d0d;
|
|
40
|
+
--shadow-accent: 4px 4px 0px #ff90e8;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Dark Mode
|
|
45
|
+
|
|
46
|
+
```css
|
|
47
|
+
[data-theme='dark'] {
|
|
48
|
+
--bg-base: #111111;
|
|
49
|
+
--bg-surface: #1a1a1a;
|
|
50
|
+
--bg-muted: #222222;
|
|
51
|
+
--text-primary: #f5f0e8;
|
|
52
|
+
--text-secondary: #aaaaaa;
|
|
53
|
+
--text-muted: #666666;
|
|
54
|
+
--text-inverse: #0d0d0d;
|
|
55
|
+
--border: #f5f0e8;
|
|
56
|
+
--border-light: #2a2a2a;
|
|
57
|
+
--shadow-sm: 2px 2px 0px #f5f0e8;
|
|
58
|
+
--shadow-md: 4px 4px 0px #f5f0e8;
|
|
59
|
+
--shadow-lg: 6px 6px 0px #f5f0e8;
|
|
60
|
+
/* Accents unchanged. Dark cards/sidebar unchanged. */
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Respect `@media (prefers-color-scheme: dark)` on `:root:not([data-theme="light"])`.
|
|
65
|
+
|
|
66
|
+
### Accents
|
|
67
|
+
|
|
68
|
+
```css
|
|
69
|
+
/* These don't change between modes */
|
|
70
|
+
--pink: #ff90e8; /* primary: CTAs, active nav, highlights */
|
|
71
|
+
--pink-light: #ffe4f8;
|
|
72
|
+
--teal: #14b8a6; /* secondary: success states, live data */
|
|
73
|
+
--teal-light: #ccfbf1;
|
|
74
|
+
--teal-dark: #0f766e;
|
|
75
|
+
--yellow: #facc15; /* tertiary: alerts, featured labels */
|
|
76
|
+
--yellow-light: #fef9c3;
|
|
77
|
+
--yellow-dark: #ca8a04;
|
|
78
|
+
--orange: #fb923c; /* quaternary: warnings */
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Semantic
|
|
82
|
+
|
|
83
|
+
```css
|
|
84
|
+
--success-bg: #d1fae5;
|
|
85
|
+
--success-text: #065f46;
|
|
86
|
+
--danger-bg: #fee2e2;
|
|
87
|
+
--danger-text: #991b1b;
|
|
88
|
+
--warning-bg: #fef9c3;
|
|
89
|
+
--warning-text: #854d0e;
|
|
90
|
+
/* Dark mode: --success-bg: #052e16; --success-text: #86efac; etc. */
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Ordered Palette
|
|
94
|
+
|
|
95
|
+
```css
|
|
96
|
+
/* For any categorical sequence: tags, categories, avatars, etc. */
|
|
97
|
+
--palette-1: #ff90e8; /* pink */
|
|
98
|
+
--palette-2: #14b8a6; /* teal */
|
|
99
|
+
--palette-3: #facc15; /* yellow */
|
|
100
|
+
--palette-4: #fb923c; /* orange */
|
|
101
|
+
--palette-5: #818cf8; /* purple */
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Typography
|
|
107
|
+
|
|
108
|
+
**Font stack:** `'DM Sans', sans-serif` (body), `'DM Serif Display', serif` (display/h1), `'DM Mono', monospace` (code)
|
|
109
|
+
|
|
110
|
+
| Style | Size | Weight | Notes |
|
|
111
|
+
| ----------- | ------------------------ | ------------------- | ----------------------------------------------------- |
|
|
112
|
+
| Display | `clamp(28px, 4vw, 48px)` | 400 (Serif Display) | `letter-spacing: -1.5px; line-height: 1.1` |
|
|
113
|
+
| H1 | `clamp(22px, 3vw, 36px)` | 900 | `letter-spacing: -1px` |
|
|
114
|
+
| H2 | 20px | 900 | `letter-spacing: -0.5px` |
|
|
115
|
+
| H3 | 15px | 700 | |
|
|
116
|
+
| Label | 11px | 700 | `uppercase; letter-spacing: 1px; color: --text-muted` |
|
|
117
|
+
| Body | 14px | 400 | `line-height: 1.6` |
|
|
118
|
+
| Body SM | 13px | 400 | `line-height: 1.5` |
|
|
119
|
+
| Large Value | `clamp(22px, 3vw, 32px)` | 900 | `letter-spacing: -1px` — for emphasized numbers |
|
|
120
|
+
|
|
121
|
+
**Common pattern — eyebrow + title:** Label above H2 in almost every section.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Spacing & Radius
|
|
126
|
+
|
|
127
|
+
Base unit: 4px. Scale: 4, 8, 12, 16, 20, 24, 28, 32, 40, 48px.
|
|
128
|
+
|
|
129
|
+
| Context | Value |
|
|
130
|
+
| ---------------------- | ----------- |
|
|
131
|
+
| Card padding (compact) | `16px 18px` |
|
|
132
|
+
| Card padding (default) | `22px 24px` |
|
|
133
|
+
| Card padding (large) | `28px 28px` |
|
|
134
|
+
| Grid gap | 18px |
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
--radius-sm: 8px;
|
|
138
|
+
--radius-md: 12px;
|
|
139
|
+
--radius-lg: 18px; /* cards — max radius in the system */
|
|
140
|
+
--radius-pill: 999px; /* badges, avatars */
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Layout
|
|
146
|
+
|
|
147
|
+
### Common Patterns
|
|
148
|
+
|
|
149
|
+
- **Sidebar:** 220px fixed left, `--bg-inverse`, always dark in both modes
|
|
150
|
+
- **Top nav:** Sticky, 64px height, `--bg-base`, bottom border
|
|
151
|
+
- **Bottom nav (mobile):** Fixed, 64px, `--pink-light` active bg. Add `env(safe-area-inset-bottom)` padding
|
|
152
|
+
- **Main content:** flex-1, `--bg-base`, padding `36px 40px`
|
|
153
|
+
|
|
154
|
+
### Navigation States
|
|
155
|
+
|
|
156
|
+
- Logo mark: 36px accent square + bold name
|
|
157
|
+
- Nav items: 14px, `10px 14px` padding, rounded 10px. Hover/active → `--pink` bg + dark text
|
|
158
|
+
- Top nav active link: inverse bg pill
|
|
159
|
+
|
|
160
|
+
### Mobile / Small Viewport
|
|
161
|
+
|
|
162
|
+
- Single column, 16px padding
|
|
163
|
+
- Cards stack vertically, buttons full-width in forms
|
|
164
|
+
- Reduce shadows to `--shadow-sm`
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Components
|
|
169
|
+
|
|
170
|
+
### Card
|
|
171
|
+
|
|
172
|
+
```css
|
|
173
|
+
.card {
|
|
174
|
+
background: var(--bg-surface);
|
|
175
|
+
border: 2px solid var(--border);
|
|
176
|
+
border-radius: var(--radius-lg);
|
|
177
|
+
padding: 22px 24px;
|
|
178
|
+
box-shadow: var(--shadow-md);
|
|
179
|
+
position: relative;
|
|
180
|
+
overflow: hidden;
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Variants:** `default` (white), `dark` (inverse bg + accent shadow), `pink`, `teal`, `yellow`, `muted` (base bg).
|
|
185
|
+
|
|
186
|
+
**Interactive states:** hover → `translateY(-3px)` + `shadow-lg`. Active → `translate(2px, 2px)` + `shadow-sm`.
|
|
187
|
+
|
|
188
|
+
### Value Card
|
|
189
|
+
|
|
190
|
+
Card with: decorative accent circle (top-right, 64px, 20% opacity), icon box (40px, accent bg, 2px border, rounded 10px), label (eyebrow), emphasized value (Large Value style), optional trend badge (pill, success/danger colors).
|
|
191
|
+
|
|
192
|
+
### Button
|
|
193
|
+
|
|
194
|
+
```css
|
|
195
|
+
.btn {
|
|
196
|
+
padding: 11px 22px;
|
|
197
|
+
border-radius: var(--radius-md);
|
|
198
|
+
font-weight: 700;
|
|
199
|
+
font-size: 14px;
|
|
200
|
+
border: 2px solid var(--border);
|
|
201
|
+
}
|
|
202
|
+
.btn:hover {
|
|
203
|
+
transform: translate(-1px, -1px);
|
|
204
|
+
}
|
|
205
|
+
.btn:active {
|
|
206
|
+
transform: translate(2px, 2px);
|
|
207
|
+
box-shadow: none;
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
| Variant | Background | Text | Border/Shadow |
|
|
212
|
+
| --------- | -------------- | ---------------- | ------------------------- |
|
|
213
|
+
| Primary | `--pink` | `--text-primary` | `--shadow-md` |
|
|
214
|
+
| Secondary | `--bg-surface` | `--text-primary` | `--shadow-sm` |
|
|
215
|
+
| Teal | `--teal` | `--text-primary` | `--shadow-md` |
|
|
216
|
+
| Yellow | `--yellow` | `--text-primary` | `--shadow-md` |
|
|
217
|
+
| Dark | `--bg-inverse` | `--text-inverse` | `--shadow-accent` |
|
|
218
|
+
| Ghost | transparent | `--text-primary` | none; hover: `--bg-muted` |
|
|
219
|
+
|
|
220
|
+
| Size | Padding | Font Size |
|
|
221
|
+
| ------------ | ----------- | --------- |
|
|
222
|
+
| SM | `7px 14px` | 12px |
|
|
223
|
+
| MD (default) | `11px 22px` | 14px |
|
|
224
|
+
| LG | `15px 30px` | 16px |
|
|
225
|
+
|
|
226
|
+
Icon-only: 40x40, no padding, `--radius-sm`.
|
|
227
|
+
|
|
228
|
+
### Badge / Pill
|
|
229
|
+
|
|
230
|
+
`padding: 3px 10px; border-radius: 999px; font-size: 11px; font-weight: 700; border: 1.5px solid currentColor`. Semantic variants: success, danger, warning, pink, teal, yellow, neutral. Solid variant: inverse bg+text.
|
|
231
|
+
|
|
232
|
+
### Input
|
|
233
|
+
|
|
234
|
+
`padding: 11px 16px; border: 2px solid var(--border); border-radius: var(--radius-md); font-size: 14px`. Focus: `box-shadow: 0 0 0 3px var(--pink)`. Input-group: input + button joined with zero'd inner radii.
|
|
235
|
+
|
|
236
|
+
### Table
|
|
237
|
+
|
|
238
|
+
Container: card shell (border, radius-lg, shadow-md, overflow hidden). Header: `--bg-muted`, 11px uppercase labels. Rows: `16px 28px` padding, `--border-light` divider, hover → muted bg.
|
|
239
|
+
|
|
240
|
+
### Progress Bar
|
|
241
|
+
|
|
242
|
+
Track: 12px height, `--bg-muted`, pill radius, 2px border. Fill: pill radius, accent color. Animate: `width 0.8s cubic-bezier(0.34, 1.56, 0.64, 1)`.
|
|
243
|
+
|
|
244
|
+
### Avatar
|
|
245
|
+
|
|
246
|
+
Square-ish (`--radius-sm`), 2px border. Sizes: 28/36/48px. Cycle through ordered palette colors.
|
|
247
|
+
|
|
248
|
+
### Tag / Chip
|
|
249
|
+
|
|
250
|
+
`padding: 5px 12px; border-radius: 999px; border: 2px solid var(--border); font-size: 13px; font-weight: 600`. Hover → pink bg. Active → inverse bg+text.
|
|
251
|
+
|
|
252
|
+
### Tooltip
|
|
253
|
+
|
|
254
|
+
`background: --bg-inverse; border: 2px solid --pink; border-radius: --radius-md; padding: 10px 16px; color: --text-inverse; font-size: 13px`. Bold label + bold value.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Motion
|
|
259
|
+
|
|
260
|
+
```css
|
|
261
|
+
--transition-fast: 120ms ease; /* button press */
|
|
262
|
+
--transition-base: 180ms ease; /* card hover, general */
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Bouncy and tactile — elements lift and press. Staggered fade-up on page load:
|
|
266
|
+
|
|
267
|
+
```css
|
|
268
|
+
@keyframes fadeUp {
|
|
269
|
+
from {
|
|
270
|
+
opacity: 0;
|
|
271
|
+
transform: translateY(12px);
|
|
272
|
+
}
|
|
273
|
+
to {
|
|
274
|
+
opacity: 1;
|
|
275
|
+
transform: translateY(0);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
/* animation: fadeUp 0.4s ease forwards; delay: n * 0.05s */
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Checklist
|
|
284
|
+
|
|
285
|
+
- [ ] Background is `#F5F0E8` (light) or `#111111` (dark) — never pure white or grey
|
|
286
|
+
- [ ] Every card has 2px border + offset box shadow
|
|
287
|
+
- [ ] Interactive elements press-shift on :active
|
|
288
|
+
- [ ] DM Sans (body) + DM Serif Display (headlines) + DM Mono (code)
|
|
289
|
+
- [ ] At least one pink element visible on every screen
|
|
290
|
+
- [ ] Labels: 11px uppercase, letter-spacing 1px
|
|
291
|
+
- [ ] Badges are pill-shaped with border
|
|
292
|
+
- [ ] Dark sidebar/cards unchanged in dark mode
|
|
293
|
+
- [ ] No gradients anywhere
|
|
294
|
+
- [ ] Spacing follows 4px grid
|
|
@@ -78,6 +78,121 @@ Use Tailwind classes on React Native components:
|
|
|
78
78
|
</View>
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
+
## CodeYam Scenario Data
|
|
82
|
+
|
|
83
|
+
### AsyncStorage Seeding
|
|
84
|
+
|
|
85
|
+
AsyncStorage uses `localStorage` on web, so CodeYam's existing localStorage injection works automatically. Use the `localStorage` field in scenario JSON to pre-populate storage:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"name": "With Saved Items",
|
|
90
|
+
"url": "/",
|
|
91
|
+
"dimensions": ["iPhone 16"],
|
|
92
|
+
"localStorage": {
|
|
93
|
+
"items": "[{\"id\":\"1\",\"title\":\"Buy groceries\",\"done\":false},{\"id\":\"2\",\"title\":\"Walk the dog\",\"done\":true}]"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Values must be JSON strings (matching how AsyncStorage stores them). Your `storage.get()` calls will read this data normally.
|
|
99
|
+
|
|
100
|
+
### API Mocking
|
|
101
|
+
|
|
102
|
+
For apps that fetch from APIs, use relative URLs so CodeYam's proxy can intercept and mock them:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
// Use relative URLs — these go through the CodeYam proxy
|
|
106
|
+
const response = await fetch('/api/items');
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Then provide mock routes in your scenario data:
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"name": "With API Data",
|
|
114
|
+
"url": "/",
|
|
115
|
+
"dimensions": ["iPhone 16"],
|
|
116
|
+
"routes": {
|
|
117
|
+
"/api/items": {
|
|
118
|
+
"body": [{ "id": 1, "title": "First item" }],
|
|
119
|
+
"status": 200
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Device Presets
|
|
126
|
+
|
|
127
|
+
Mobile projects default to these screen sizes:
|
|
128
|
+
|
|
129
|
+
| Preset | Width | Height |
|
|
130
|
+
| ----------------- | ----- | ------ |
|
|
131
|
+
| iPhone 16 | 393 | 852 |
|
|
132
|
+
| iPhone 16 Pro Max | 430 | 932 |
|
|
133
|
+
| iPhone SE | 375 | 667 |
|
|
134
|
+
| Pixel 8 | 412 | 915 |
|
|
135
|
+
| iPad mini | 744 | 1133 |
|
|
136
|
+
|
|
137
|
+
## Design Tokens (lib/theme.ts)
|
|
138
|
+
|
|
139
|
+
All design tokens live in `lib/theme.ts` — this is the **single source of truth** for colors, spacing, typography, and border radius. Import and use in every component:
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { theme } from '@/lib/theme';
|
|
143
|
+
|
|
144
|
+
<View
|
|
145
|
+
style={{ backgroundColor: theme.colors.bgBase, padding: theme.spacing.lg }}
|
|
146
|
+
>
|
|
147
|
+
<Text
|
|
148
|
+
style={{ fontSize: theme.fontSize.lg, color: theme.colors.textPrimary }}
|
|
149
|
+
>
|
|
150
|
+
Hello
|
|
151
|
+
</Text>
|
|
152
|
+
</View>;
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Do NOT:**
|
|
156
|
+
|
|
157
|
+
- Use CSS custom properties (`var(--token)`) — they don't work in React Native
|
|
158
|
+
- Hardcode color strings or pixel values in components
|
|
159
|
+
- Create a separate `globals.css` token system — `lib/theme.ts` is the only source
|
|
160
|
+
|
|
161
|
+
When a design system is selected, populate `lib/theme.ts` with its tokens.
|
|
162
|
+
|
|
163
|
+
## Testing
|
|
164
|
+
|
|
165
|
+
Tests use Jest with the `jest-expo` preset. Run with:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
npx jest # Run all tests
|
|
169
|
+
npx jest app/hooks/useCounter.ts # Run specific test file
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The Jest config is in `package.json`. The `transformIgnorePatterns` is pre-configured to handle Expo and React Native module transforms — you should not need to modify it.
|
|
173
|
+
|
|
174
|
+
## Web vs Native Differences
|
|
175
|
+
|
|
176
|
+
The CodeYam editor previews your app via **Expo Web** (react-native-web in a browser). Some differences from native iOS/Android devices are expected:
|
|
177
|
+
|
|
178
|
+
| Aspect | Web Preview | Native Device |
|
|
179
|
+
| ---------------- | ------------------------------------------------------ | ------------------------------------------------------- |
|
|
180
|
+
| **Fonts** | System fonts, may differ in weight/metrics/line-height | Loaded custom fonts (if added via expo-font) |
|
|
181
|
+
| **SafeAreaView** | No effect (no notch in browser) | Applies real safe area insets for notch, home indicator |
|
|
182
|
+
| **Platform.OS** | Returns `'web'` | Returns `'ios'` or `'android'` |
|
|
183
|
+
| **Shadows** | Uses CSS `box-shadow` (works well) | Uses RN shadow props (iOS) or `elevation` (Android) |
|
|
184
|
+
| **Gestures** | Mouse drag events | Touch/swipe with inertia |
|
|
185
|
+
| **StatusBar** | No visible effect | Controls device status bar appearance |
|
|
186
|
+
| **Haptics** | No-op | Real haptic feedback via `expo-haptics` |
|
|
187
|
+
|
|
188
|
+
**The web preview is for layout and data verification.** Test final visual polish on a real device or simulator:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm run ios # iOS Simulator
|
|
192
|
+
npm run android # Android Emulator
|
|
193
|
+
npm start # Pick platform from Expo menu
|
|
194
|
+
```
|
|
195
|
+
|
|
81
196
|
## Building for Production
|
|
82
197
|
|
|
83
198
|
```bash
|
|
File without changes
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import '../global.css';
|
|
2
2
|
import { Stack } from 'expo-router';
|
|
3
3
|
import { StatusBar } from 'expo-status-bar';
|
|
4
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
4
5
|
|
|
5
6
|
export default function RootLayout() {
|
|
6
7
|
return (
|
|
7
|
-
|
|
8
|
+
<SafeAreaProvider>
|
|
8
9
|
<Stack screenOptions={{ headerShown: false }} />
|
|
9
10
|
<StatusBar style="auto" />
|
|
10
|
-
|
|
11
|
+
</SafeAreaProvider>
|
|
11
12
|
);
|
|
12
13
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/* NativeWind / Tailwind utility layer.
|
|
2
|
+
*
|
|
3
|
+
* Design tokens are defined in lib/theme.ts — that is the single source
|
|
4
|
+
* of truth for colors, spacing, typography, and border radius.
|
|
5
|
+
* Do NOT define CSS custom properties here for design tokens.
|
|
6
|
+
* React Native components cannot read CSS variables.
|
|
7
|
+
*/
|
|
1
8
|
@tailwind base;
|
|
2
9
|
@tailwind components;
|
|
3
10
|
@tailwind utilities;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design tokens — single source of truth for all styling.
|
|
3
|
+
*
|
|
4
|
+
* When a design system is selected (via .codeyam/design-system.md),
|
|
5
|
+
* populate these values from the design system's color palette,
|
|
6
|
+
* typography scale, and spacing rhythm.
|
|
7
|
+
*
|
|
8
|
+
* Import in every component:
|
|
9
|
+
* import { theme } from '@/lib/theme';
|
|
10
|
+
* <View style={{ backgroundColor: theme.colors.bgBase }}>
|
|
11
|
+
*
|
|
12
|
+
* Do NOT use CSS custom properties (var(--token)) in React Native.
|
|
13
|
+
* Do NOT hardcode color strings or pixel values in components.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const theme = {
|
|
17
|
+
colors: {
|
|
18
|
+
bgBase: '#f8fafc',
|
|
19
|
+
bgSurface: '#ffffff',
|
|
20
|
+
bgSurfaceRaised: '#ffffff',
|
|
21
|
+
bgInverse: '#1e293b',
|
|
22
|
+
|
|
23
|
+
textPrimary: '#0f172a',
|
|
24
|
+
textSecondary: '#475569',
|
|
25
|
+
textMuted: '#94a3b8',
|
|
26
|
+
|
|
27
|
+
border: '#e2e8f0',
|
|
28
|
+
|
|
29
|
+
accent: '#005C75',
|
|
30
|
+
accentLight: '#e0f2fe',
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
spacing: {
|
|
34
|
+
xs: 4,
|
|
35
|
+
sm: 8,
|
|
36
|
+
md: 12,
|
|
37
|
+
lg: 16,
|
|
38
|
+
xl: 24,
|
|
39
|
+
'2xl': 32,
|
|
40
|
+
'3xl': 48,
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
fontSize: {
|
|
44
|
+
xs: 12,
|
|
45
|
+
sm: 14,
|
|
46
|
+
base: 16,
|
|
47
|
+
lg: 18,
|
|
48
|
+
xl: 20,
|
|
49
|
+
'2xl': 24,
|
|
50
|
+
'3xl': 30,
|
|
51
|
+
'4xl': 36,
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
fontWeight: {
|
|
55
|
+
normal: '400' as const,
|
|
56
|
+
medium: '500' as const,
|
|
57
|
+
semibold: '600' as const,
|
|
58
|
+
bold: '700' as const,
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
fontFamily: {
|
|
62
|
+
sans: 'System',
|
|
63
|
+
mono: 'monospace',
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
borderRadius: {
|
|
67
|
+
sm: 4,
|
|
68
|
+
md: 8,
|
|
69
|
+
lg: 12,
|
|
70
|
+
xl: 16,
|
|
71
|
+
full: 9999,
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"start": "expo start",
|
|
10
10
|
"android": "expo start --android",
|
|
11
11
|
"ios": "expo start --ios",
|
|
12
|
-
"build:web": "expo export --platform web"
|
|
12
|
+
"build:web": "expo export --platform web",
|
|
13
|
+
"test": "jest"
|
|
13
14
|
},
|
|
14
15
|
"dependencies": {
|
|
15
16
|
"expo": "~55.0.5",
|
|
@@ -17,8 +18,9 @@
|
|
|
17
18
|
"expo-status-bar": "~55.0.4",
|
|
18
19
|
"expo-linking": "~55.0.7",
|
|
19
20
|
"expo-constants": "~55.0.7",
|
|
20
|
-
"
|
|
21
|
-
"react
|
|
21
|
+
"expo-font": "~55.0.4",
|
|
22
|
+
"react": "19.2.4",
|
|
23
|
+
"react-dom": "19.2.4",
|
|
22
24
|
"react-native": "0.83.2",
|
|
23
25
|
"react-native-web": "^0.21.0",
|
|
24
26
|
"react-native-safe-area-context": "~5.6.2",
|
|
@@ -27,12 +29,20 @@
|
|
|
27
29
|
"@expo/vector-icons": "^15.0.2",
|
|
28
30
|
"@react-native-async-storage/async-storage": "2.2.0",
|
|
29
31
|
"nativewind": "^4.2.2",
|
|
30
|
-
"tailwindcss": "^3.4.19"
|
|
31
|
-
"react-native-reanimated": "4.2.1"
|
|
32
|
+
"tailwindcss": "^3.4.19"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@types/react": "~19.2.2",
|
|
35
36
|
"babel-preset-expo": "^55.0.10",
|
|
36
|
-
"typescript": "~5.9.2"
|
|
37
|
+
"typescript": "~5.9.2",
|
|
38
|
+
"jest": "^29.7.0",
|
|
39
|
+
"jest-expo": "~55.0.5",
|
|
40
|
+
"@testing-library/react-native": "^13.2.0"
|
|
41
|
+
},
|
|
42
|
+
"jest": {
|
|
43
|
+
"preset": "jest-expo",
|
|
44
|
+
"transformIgnorePatterns": [
|
|
45
|
+
"node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|nativewind)"
|
|
46
|
+
]
|
|
37
47
|
}
|
|
38
48
|
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeYam Isolation Route — Expo Router
|
|
3
|
+
*
|
|
4
|
+
* Place this file at: app/isolated-components/ComponentName.tsx
|
|
5
|
+
* URL: /isolated-components/ComponentName?s=ScenarioName
|
|
6
|
+
*
|
|
7
|
+
* This route renders a single component with scenario-specific props,
|
|
8
|
+
* enabling isolated component preview and screenshot capture.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useLocalSearchParams } from 'expo-router';
|
|
12
|
+
import { View, Text } from 'react-native';
|
|
13
|
+
import { theme } from '@/lib/theme';
|
|
14
|
+
// import YourComponent from '@/app/components/YourComponent';
|
|
15
|
+
|
|
16
|
+
// Define scenarios: map scenario names to component props
|
|
17
|
+
const scenarios: Record<string, Record<string, unknown>> = {
|
|
18
|
+
Default: {
|
|
19
|
+
// props for the default scenario
|
|
20
|
+
},
|
|
21
|
+
// Add more scenarios here
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default function ComponentIsolation() {
|
|
25
|
+
const { s } = useLocalSearchParams<{ s: string }>();
|
|
26
|
+
const scenarioName = s || 'Default';
|
|
27
|
+
const props = scenarios[scenarioName];
|
|
28
|
+
|
|
29
|
+
if (!props) {
|
|
30
|
+
return (
|
|
31
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
32
|
+
<Text style={{ fontFamily: theme.fontFamily.mono, color: '#e74c3c' }}>
|
|
33
|
+
Unknown scenario: {scenarioName}
|
|
34
|
+
</Text>
|
|
35
|
+
</View>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<View
|
|
41
|
+
style={{
|
|
42
|
+
flex: 1,
|
|
43
|
+
justifyContent: 'center',
|
|
44
|
+
alignItems: 'center',
|
|
45
|
+
backgroundColor: theme.colors.bgBase,
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<View nativeID="codeyam-capture" style={{ maxWidth: 384 }}>
|
|
49
|
+
{/* <YourComponent {...props} /> */}
|
|
50
|
+
<Text>Replace with your component</Text>
|
|
51
|
+
</View>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -37,7 +37,12 @@ async function main() {
|
|
|
37
37
|
const data = JSON.parse(raw);
|
|
38
38
|
const seed = data.seed || data;
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// Discover ALL models from the Prisma schema — not just the tables in the seed data.
|
|
41
|
+
// This ensures FK-dependent tables (e.g., Passenger → Flight) are cleared even when
|
|
42
|
+
// the seed only contains the parent table's data.
|
|
43
|
+
const allModels = Prisma.dmmf.datamodel.models.map(
|
|
44
|
+
(m) => m.name.charAt(0).toLowerCase() + m.name.slice(1),
|
|
45
|
+
);
|
|
41
46
|
|
|
42
47
|
// Run everything in a single transaction for atomicity and speed.
|
|
43
48
|
// SQLite auto-commits each statement by default — wrapping in a transaction
|
|
@@ -48,8 +53,8 @@ async function main() {
|
|
|
48
53
|
// clearing parent and child tables.
|
|
49
54
|
await tx.$executeRawUnsafe('PRAGMA foreign_keys = OFF');
|
|
50
55
|
|
|
51
|
-
// Wipe
|
|
52
|
-
for (const table of [...
|
|
56
|
+
// Wipe ALL tables in the schema (not just seeded ones)
|
|
57
|
+
for (const table of [...allModels].reverse()) {
|
|
53
58
|
try {
|
|
54
59
|
await (tx as any)[table].deleteMany();
|
|
55
60
|
} catch {
|
|
@@ -57,10 +62,10 @@ async function main() {
|
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
// Batch-reset auto-increment counters
|
|
65
|
+
// Batch-reset auto-increment counters for ALL tables.
|
|
61
66
|
// Without this, SQLite IDs keep climbing across scenario switches,
|
|
62
67
|
// causing hardcoded URLs like /drinks/1 to 404.
|
|
63
|
-
const seqNames =
|
|
68
|
+
const seqNames = allModels
|
|
64
69
|
.flatMap((t) => [`'${t}'`, `'${t.charAt(0).toUpperCase() + t.slice(1)}'`])
|
|
65
70
|
.join(', ');
|
|
66
71
|
try {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
30
|
import { createClient } from '@supabase/supabase-js';
|
|
31
|
+
import { Prisma } from '@prisma/client';
|
|
31
32
|
import * as fs from 'fs';
|
|
32
33
|
import * as path from 'path';
|
|
33
34
|
|
|
@@ -109,13 +110,21 @@ function getProjectRef(): string {
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
async function seedTables(seed: Record<string, unknown[]>) {
|
|
112
|
-
|
|
113
|
-
if (tableNames.length === 0) return;
|
|
113
|
+
if (Object.keys(seed).length === 0) return;
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
// Discover ALL models from the Prisma schema — not just the tables in the seed data.
|
|
116
|
+
// This ensures FK-dependent tables are cleared even when the seed only contains
|
|
117
|
+
// the parent table's data. Every editor project has Prisma installed.
|
|
118
|
+
const allModels = Prisma.dmmf.datamodel.models.map(
|
|
119
|
+
(m) => m.name.charAt(0).toLowerCase() + m.name.slice(1),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
console.log(
|
|
123
|
+
`Clearing ${allModels.length} tables, seeding: ${Object.keys(seed).join(', ')}`,
|
|
124
|
+
);
|
|
116
125
|
|
|
117
|
-
//
|
|
118
|
-
for (const table of [...
|
|
126
|
+
// Clear ALL tables in reverse order (children before parents for FK safety)
|
|
127
|
+
for (const table of [...allModels].reverse()) {
|
|
119
128
|
const { error } = await supabase.from(table).delete().gte('id', 0);
|
|
120
129
|
if (error) {
|
|
121
130
|
const { error: error2 } = await supabase
|