@symbo.ls/mcp 1.0.11 → 1.0.14

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.
@@ -1,23 +1,28 @@
1
- # Migration Guide DOMQL v2v3 & React/Angular/Vue→Symbols
1
+ # Migration Rules: DOMQL v2 to v3 & Framework to Symbols
2
+
3
+ Apply these transformation rules when migrating code. Each rule is a BEFORE/AFTER pair.
2
4
 
3
5
  ---
4
6
 
5
- ## Part 1: DOMQL v2 v3 Migration
7
+ ## Part 1: DOMQL v2 to v3
8
+
9
+ ### Rule Summary
6
10
 
7
- ### Summary of changes
11
+ | What changed | v2 (REMOVE) | v3 (USE INSTEAD) |
12
+ | ----------------------- | ----------------------------- | ----------------------------- |
13
+ | CSS props wrapper | `props: { padding: 'A' }` | `padding: 'A'` (at root) |
14
+ | Events wrapper | `on: { click: fn }` | `onClick: fn` (at root) |
15
+ | Inheritance | `extend: 'Component'` | `extends: 'Component'` |
16
+ | Child extend | `childExtend: 'Item'` | `childExtends: 'Item'` |
17
+ | Child element detection | any key including lowercase | PascalCase keys ONLY |
18
+ | Base components | `extends: 'Text'` / `'Box'` | Remove — implicit defaults |
8
19
 
9
- | Change | v2 ❌ | v3 ✅ |
10
- | ------------------------- | ----------------------------- | ----------------------------- |
11
- | CSS props wrapper | `props: { padding: 'A' }` | `padding: 'A'` (at root) |
12
- | Events wrapper | `on: { click: fn }` | `onClick: fn` (at root) |
13
- | Inheritance | `extend: 'Component'` | `extends: 'Component'` |
14
- | Child extend | `childExtend: 'Item'` | `childExtends: 'Item'` |
15
- | Child element detection | any key including lowercase | PascalCase keys ONLY |
20
+ ### Rule 1: Flatten `props` and `on`
16
21
 
17
- ### Flatten `props` and `on`
22
+ Move all `props` entries to root. Remove `on` wrapper; prefix each event with `on` + CapitalizedEventName. Apply at root AND all nested elements.
18
23
 
24
+ BEFORE:
19
25
  ```js
20
- // v2
21
26
  {
22
27
  props: { position: 'absolute', gap: 'A' },
23
28
  on: {
@@ -26,8 +31,10 @@
26
31
  wheel: (e, t) => {},
27
32
  },
28
33
  }
34
+ ```
29
35
 
30
- // v3
36
+ AFTER:
37
+ ```js
31
38
  {
32
39
  position: 'absolute',
33
40
  gap: 'A',
@@ -37,38 +44,39 @@
37
44
  }
38
45
  ```
39
46
 
40
- Rules:
41
- - Move all `props` entries directly to the component object root
42
- - Remove the `on` wrapper; prefix each event with `on` + `CapitalizedEventName`
43
- - Applies to root and all nested elements
44
-
45
- ### Rename properties
47
+ ### Rule 2: Rename `extend` to `extends`, `childExtend` to `childExtends`
46
48
 
49
+ BEFORE:
47
50
  ```js
48
- // v2
49
51
  { extend: SomeComponent, childExtend: AnotherComponent }
52
+ ```
50
53
 
51
- // v3
54
+ AFTER:
55
+ ```js
52
56
  { extends: SomeComponent, childExtends: AnotherComponent }
53
57
  ```
54
58
 
55
- ### Replace `$stateCollection` with `children` + `childrenAs: 'state'`
59
+ ### Rule 3: Replace `$stateCollection` with `children` + `childrenAs: 'state'`
56
60
 
61
+ BEFORE:
57
62
  ```js
58
- // v2
59
63
  {
60
64
  childExtend: 'TeamItem',
61
65
  $stateCollection: (el, s) => s.data
62
66
  }
67
+ ```
63
68
 
64
- // v3 — Option A: childrenAs: 'state' (explicit)
69
+ AFTER (Option A -- explicit):
70
+ ```js
65
71
  {
66
72
  childExtends: 'TeamItem',
67
73
  childrenAs: 'state',
68
74
  children: (el, s) => s.data
69
75
  }
76
+ ```
70
77
 
71
- // v3 — Option B: state: 'dataKey' shorthand (preferred for nested state)
78
+ AFTER (Option B -- preferred for nested state):
79
+ ```js
72
80
  {
73
81
  state: 'data',
74
82
  childExtends: 'TeamItem',
@@ -76,10 +84,9 @@ Rules:
76
84
  }
77
85
  ```
78
86
 
79
- #### `state: true` on child components
80
-
81
- Components used with `childExtends` that read individual item state (e.g. `({ state }) => state.title`) need `state: true` at their root so each child receives its own state from the parent's children array:
87
+ **`state: true` requirement for child components:** Components used with `childExtends` that read individual item state need `state: true` at their root so each child receives its own state from the parent's children array.
82
88
 
89
+ BEFORE (parent + child):
83
90
  ```js
84
91
  // Parent container
85
92
  export const TeamList = {
@@ -88,7 +95,7 @@ export const TeamList = {
88
95
  children: ({ state }) => state
89
96
  }
90
97
 
91
- // Child component state: true is REQUIRED
98
+ // Child component -- state: true is REQUIRED
92
99
  export const TeamItem = {
93
100
  state: true,
94
101
  Title: { text: ({ state }) => state.name },
@@ -96,102 +103,111 @@ export const TeamItem = {
96
103
  }
97
104
  ```
98
105
 
99
- #### Common pitfall: `children: ({ state }) => state.data`
100
-
101
- **WRONG**: `children: ({ state }) => state.data` does NOT properly pass individual state to child components — all children receive the parent's full state instead of their own item.
102
-
103
- **CORRECT**: Use `state: 'data'` on the container first, then `children: ({ state }) => state`:
106
+ **CRITICAL PITFALL -- `children: ({ state }) => state.data`:**
104
107
 
108
+ WRONG -- children don't get individual state:
105
109
  ```js
106
- // ❌ WRONG — children don't get individual state
107
110
  { children: ({ state }) => state.data, childExtends: 'Item' }
111
+ ```
108
112
 
109
- // ✅ CORRECT state: 'data' narrows scope, children get individual items
113
+ CORRECT -- `state: 'data'` narrows scope, children get individual items:
114
+ ```js
110
115
  { state: 'data', children: ({ state }) => state, childExtends: 'Item' }
111
116
  ```
112
117
 
113
- ### Replace `$propsCollection` with `children`
118
+ ### Rule 4: Replace `$propsCollection` with `children`
119
+
120
+ `$propsCollection` is fully removed in v3. Replace 1:1 with `children`.
114
121
 
122
+ BEFORE:
115
123
  ```js
116
- // v2
117
124
  {
118
125
  childExtend: 'Paragraph',
119
126
  $propsCollection: ({ state }) => state.parse()
120
127
  }
128
+ ```
121
129
 
122
- // v3
130
+ AFTER:
131
+ ```js
123
132
  {
124
133
  childExtends: 'Paragraph',
125
134
  children: ({ state }) => state.parse()
126
135
  }
127
136
  ```
128
137
 
129
- `$propsCollection` is fully removed in v3. Replace 1:1 with `children`. The same pattern applies to array data:
138
+ Also applies to array data:
130
139
 
131
- ```js
132
- // v2
133
- $propsCollection: ({ state }) => state
140
+ BEFORE: `$propsCollection: ({ state }) => state`
141
+ AFTER: `children: ({ state }) => state`
134
142
 
135
- // v3
136
- children: ({ state }) => state
137
- ```
143
+ ### Rule 5: Replace `props: (fn)` function with individual prop functions
138
144
 
139
- ### Replace `props: (fn)` function with individual prop functions
145
+ Dynamic props functions are REMOVED in v3. Split into per-property functions.
140
146
 
147
+ BEFORE:
141
148
  ```js
142
- // v2 — dynamic props function (REMOVED in v3)
143
149
  {
144
150
  props: ({ state }) => ({
145
151
  color: state.active ? 'red' : 'blue',
146
152
  opacity: state.loading ? 0.5 : 1
147
153
  })
148
154
  }
155
+ ```
149
156
 
150
- // v3 — each property is its own function
157
+ AFTER:
158
+ ```js
151
159
  {
152
160
  color: ({ state }) => state.active ? 'red' : 'blue',
153
161
  opacity: ({ state }) => state.loading ? 0.5 : 1
154
162
  }
155
163
  ```
156
164
 
157
- ### Child element detection
165
+ ### Rule 6: PascalCase-only child elements
158
166
 
167
+ In v3, only PascalCase keys create child elements. Lowercase keys are treated as CSS properties.
168
+
169
+ BEFORE (v2):
159
170
  ```js
160
- // v2 — lowercase keys could create elements
161
171
  { div: {} } // creates <div>
172
+ ```
162
173
 
163
- // v3 — only PascalCase keys create elements
174
+ AFTER (v3):
175
+ ```js
164
176
  { div: {} } // treated as a plain prop, NOT rendered
165
177
  { Div: {} } // creates a child element (equivalent to { extends: 'Div' })
166
178
  ```
167
179
 
168
- ### Replace array spread `...[items]` with `children: [items]`
180
+ ### Rule 7: Replace array spread with `children` array
169
181
 
182
+ Array spread creates numeric keys (`0`, `1`, `2`...) which v3 treats as CSS properties. Always use `children` array.
183
+
184
+ BEFORE:
170
185
  ```js
171
- // v2 — numeric keys from spread are lowercase, treated as CSS in v3
172
186
  {
173
187
  childExtend: 'NavLink',
174
188
  ...[{ text: 'About us' }, { text: 'Hiring' }]
175
189
  }
190
+ ```
176
191
 
177
- // v3 — use children array
192
+ AFTER:
193
+ ```js
178
194
  {
179
195
  childExtends: 'NavLink',
180
196
  children: [{ text: 'About us' }, { text: 'Hiring' }]
181
197
  }
182
198
  ```
183
199
 
184
- Array spread creates numeric keys (`0`, `1`, `2`…) which v3 treats as CSS properties (lowercase). Always use `children` array instead.
185
-
186
- ### Picture `src` must go on Img child
200
+ ### Rule 8: Picture `src` must go on Img child
187
201
 
188
202
  The HTML `<picture>` tag does NOT support `src` as an attribute. In v3, lowercase props move to `element.props`, so `element.parent.src` returns `undefined`.
189
203
 
204
+ WRONG:
190
205
  ```js
191
- // ❌ WRONG — src on Picture is silently ignored
192
206
  Picture: { src: '/files/photo.jpg', width: '100%' }
207
+ ```
193
208
 
194
- // ✅ CORRECT — src goes on the Img child inside Picture
209
+ CORRECT:
210
+ ```js
195
211
  Picture: {
196
212
  Img: { src: '/files/photo.jpg' },
197
213
  width: '100%',
@@ -201,37 +217,42 @@ Picture: {
201
217
 
202
218
  For theme-aware sources, use `@dark`/`@light` with `srcset` on Picture, but always put the default `src` on the Img child.
203
219
 
204
- ### `<map>` tag auto-detection
220
+ ### Rule 9: `<map>` tag auto-detection
205
221
 
206
222
  Component keys named `Map` auto-detect as the HTML `<map>` tag (for image maps), which defaults to `display: inline` and has height 0. If using `Map` as a component name for geographic maps or similar, add `tag: 'div'`:
207
223
 
208
224
  ```js
209
225
  export const Map = {
210
- extends: 'Flex',
226
+ flow: 'y',
211
227
  tag: 'div', // prevents <map> auto-detection
212
228
  // ...
213
229
  }
214
230
  ```
215
231
 
216
- ### Non-existent base components
232
+ ### Rule 10: Non-existent base components
217
233
 
218
234
  These v2 component names don't exist in v3 uikit:
219
- - `extends: 'Page'` → use `extends: 'Flex', flexFlow: 'column'`
220
- - `extends: 'Overflow'` → use `extends: 'Flex'`
221
235
 
222
- ### `state: true` vs `state: 'fieldName'` for children
236
+ | v2 (REMOVE) | v3 (USE INSTEAD) |
237
+ | ---------------------- | ----------------------------------------- |
238
+ | `extends: 'Page'` | `flow: 'column'` |
239
+ | `extends: 'Overflow'` | `flow: 'x'` |
223
240
 
224
- - `state: true` for `childExtends`-created children; maps individual array items as each child's state
225
- - `state: 'fieldName'` — for direct PascalCase children that need a specific field from parent state
241
+ ### Rule 11: `state: true` vs `state: 'fieldName'` for children
242
+
243
+ | Context | Use | Purpose |
244
+ | ---------------------------------- | ---------------- | --------------------------------------------------------- |
245
+ | `childExtends`-created children | `state: true` | Maps individual array items as each child's state |
246
+ | Direct PascalCase children | `state: 'field'` | Picks a specific field from parent state |
226
247
 
227
248
  ```js
228
- // childExtends children use state: true on the child component
249
+ // childExtends children -- use state: true on the child component
229
250
  export const ListItem = {
230
251
  state: true,
231
252
  Title: { text: ({ state }) => state.name }
232
253
  }
233
254
 
234
- // Direct PascalCase child use state: 'fieldName'
255
+ // Direct PascalCase child -- use state: 'fieldName'
235
256
  Description: {
236
257
  state: 'description',
237
258
  childExtends: 'Paragraph',
@@ -239,10 +260,28 @@ Description: {
239
260
  }
240
261
  ```
241
262
 
242
- ### Full example
263
+ ### Rule 12: Remove `extends: 'Text'` and `extends: 'Box'`
264
+
265
+ `Text` and `Box` are built-in implicit defaults. Extending them causes unnecessary merge at runtime.
243
266
 
267
+ BEFORE:
268
+ ```js
269
+ Tag: { extends: 'Text', text: 'NEW', padding: 'X A' }
270
+ Card: { extends: 'Box', padding: 'B', background: 'white' }
271
+ ```
272
+
273
+ AFTER:
274
+ ```js
275
+ Tag: { tag: 'span', text: 'NEW', padding: 'X A' }
276
+ Card: { tag: 'div', padding: 'B', background: 'white' }
277
+ ```
278
+
279
+ Use semantic or functional components instead (`Flex`, `Link`, `Button`, `Header`, `Section`, etc.).
280
+
281
+ ### Full v2 to v3 transformation example
282
+
283
+ BEFORE:
244
284
  ```js
245
- // v2
246
285
  {
247
286
  extend: 'Flex',
248
287
  childExtend: 'ListItem',
@@ -262,10 +301,12 @@ Description: {
262
301
  },
263
302
  },
264
303
  }
304
+ ```
265
305
 
266
- // v3
306
+ AFTER:
307
+ ```js
267
308
  {
268
- extends: 'Flex',
309
+ flow: 'x',
269
310
  childExtends: 'ListItem',
270
311
  position: 'absolute',
271
312
  attr: { 'gs-w': 1, 'gs-h': 1 },
@@ -283,13 +324,13 @@ Description: {
283
324
 
284
325
  ---
285
326
 
286
- ## Part 2: React / Angular / Vue Symbols
327
+ ## Part 2: React / Angular / Vue to Symbols
287
328
 
288
- ### From React
329
+ ### React to Symbols
289
330
 
290
331
  | React Pattern | Symbols Equivalent |
291
332
  | -------------------------------------- | ------------------------------------------------------------------------- |
292
- | `function Component()` / `class` | `export const Component = { extends: 'Flex', ... }` |
333
+ | `function Component()` / `class` | `export const Component = { flow: 'y', ... }` |
293
334
  | `import Component from './Component'` | Reference by key: `{ Component: {} }` |
294
335
  | `useState(val)` | `state: { key: val }` + `s.update({ key: newVal })` |
295
336
  | `useEffect(() => {}, [])` | `onRender: (el, s) => {}` |
@@ -310,7 +351,7 @@ Description: {
310
351
  | `<form onSubmit>` | `tag: 'form', onSubmit: (ev, el, s) => { ev.preventDefault(); ... }` |
311
352
  | `fetch()` in components | `functions/fetch.js` + `el.call('fetch', method, path, data)` |
312
353
 
313
- ### From Angular
354
+ ### Angular to Symbols
314
355
 
315
356
  | Angular Pattern | Symbols Equivalent |
316
357
  | ---------------------------------- | ---------------------------------------------------------------------- |
@@ -334,7 +375,7 @@ Description: {
334
375
  | SCSS / component styles | Flatten to props with design tokens |
335
376
  | Reactive Forms | `tag: 'form'`, `Input` children with `name`, `onSubmit` handler |
336
377
 
337
- ### From Vue
378
+ ### Vue to Symbols
338
379
 
339
380
  | Vue Pattern | Symbols Equivalent |
340
381
  | --------------------------------- | --------------------------------------------------------------------------------- |
@@ -361,7 +402,7 @@ Description: {
361
402
 
362
403
  ---
363
404
 
364
- ## Part 3: CSS Design Tokens
405
+ ## Part 3: CSS to Design Tokens
365
406
 
366
407
  | CSS | Symbols |
367
408
  | ----------------------------------------------------- | -------------------------------------------- |
@@ -373,7 +414,7 @@ Description: {
373
414
  | `width: 42px; height: 42px` | `boxSize: 'C'` |
374
415
  | `display: flex; flex-direction: column` | `flow: 'y'` |
375
416
  | `display: flex; flex-direction: row` | `flow: 'x'` |
376
- | `align-items: center; justify-content: center` | `flexAlign: 'center center'` |
417
+ | `align-items: center; justify-content: center` | `align: 'center center'` |
377
418
  | `display: grid; grid-template-columns: repeat(3,1fr)` | `extends: 'Grid', columns: 'repeat(3, 1fr)'` |
378
419
  | `font-size: 20px` | `fontSize: 'A1'` |
379
420
  | `font-weight: 500` | `fontWeight: '500'` |
@@ -411,9 +452,8 @@ Use this as the base template for every new component:
411
452
  ```js
412
453
  // components/ComponentName.js
413
454
  export const ComponentName = {
414
- extends: 'Flex',
415
-
416
455
  // Props flattened at root (design tokens)
456
+ flow: 'y',
417
457
  padding: 'A B',
418
458
  background: 'surface',
419
459
  borderRadius: 'B',
@@ -434,7 +474,7 @@ export const ComponentName = {
434
474
  '@mobileL': { padding: 'A' },
435
475
  '@tabletS': { padding: 'B' },
436
476
 
437
- // Children PascalCase keys, no imports
477
+ // Children -- PascalCase keys, no imports
438
478
  Header: {},
439
479
  Content: {
440
480
  Article: { text: 'Hello' },
@@ -447,15 +487,16 @@ export const ComponentName = {
447
487
 
448
488
  ## Part 5: State Management Migration
449
489
 
450
- | Framework pattern | Symbols equivalent |
451
- | ------------------------- | ---------------------------------------------------- |
452
- | Global store (Redux, Pinia, NgRx) | `state/index.js` with initial flat state. Access via `s.root` in any component |
453
- | Local component state | `state: { key: val }` on the component |
454
- | Derived/computed state | Dynamic prop function: `text: (el, s) => derived(s)` |
455
- | Async data fetch | `onRender: async (el, s) => { ... s.update({data}) }` |
456
- | State persistence | Functions that read/write to localStorage/cookie |
490
+ | Framework pattern | Symbols equivalent |
491
+ | --------------------------------------- | ----------------------------------------------------------------------------- |
492
+ | Global store (Redux, Pinia, NgRx) | `state/index.js` with initial flat state. Access via `s.root` in any component |
493
+ | Local component state | `state: { key: val }` on the component |
494
+ | Derived/computed state | Dynamic prop function: `text: (el, s) => derived(s)` |
495
+ | Async data fetch | `onRender: async (el, s) => { ... s.update({data}) }` |
496
+ | State persistence | Functions that read/write to localStorage/cookie |
497
+
498
+ Async state pattern:
457
499
 
458
- Example async state pattern:
459
500
  ```js
460
501
  export const DataView = {
461
502
  state: { data: null, loading: true, error: null },
@@ -477,43 +518,43 @@ export const DataView = {
477
518
 
478
519
  ### Color tokens: space-separated to dot-notation
479
520
 
480
- | Old | New | Notes |
481
- |---|---|---|
482
- | `'white .1'` | `'white.1'` | Opacity 0.1 |
483
- | `'gray 0.85'` | `'gray.85'` | Opacity 0.85 |
484
- | `'gray .92 +8'` | `'gray.92+8'` | Opacity + relative tone |
485
- | `'gray 1 +16'` | `'gray+16'` | Alpha 1 = default, omit |
486
- | `'gray 1 -68'` | `'gray-68'` | Relative tone |
487
- | `'gray 1 90'` | `'gray=90'` | Absolute lightness (=prefix) |
488
- | `'white 1 -78'` | `'white-78'` | Tone only |
521
+ | BEFORE | AFTER | Notes |
522
+ | ----------------- | ---------------- | ----------------------------- |
523
+ | `'white .1'` | `'white.1'` | Opacity 0.1 |
524
+ | `'gray 0.85'` | `'gray.85'` | Opacity 0.85 |
525
+ | `'gray .92 +8'` | `'gray.92+8'` | Opacity + relative tone |
526
+ | `'gray 1 +16'` | `'gray+16'` | Alpha 1 = default, omit |
527
+ | `'gray 1 -68'` | `'gray-68'` | Relative tone |
528
+ | `'gray 1 90'` | `'gray=90'` | Absolute lightness (=prefix) |
529
+ | `'white 1 -78'` | `'white-78'` | Tone only |
489
530
 
490
531
  ### border: comma-separated to space-separated (CSS order)
491
532
 
492
- | Old | New |
493
- |---|---|
494
- | `'solid, gray, 1px'` | `'1px solid gray'` |
495
- | `'gray6 .1, solid, 1px'` | `'1px solid gray6.1'` |
496
- | `'solid, mediumGrey'` | `'solid mediumGrey'` |
497
- | `'1px, solid'` | `'1px solid'` |
533
+ | BEFORE | AFTER |
534
+ | --------------------------- | ----------------------- |
535
+ | `'solid, gray, 1px'` | `'1px solid gray'` |
536
+ | `'gray6 .1, solid, 1px'` | `'1px solid gray6.1'` |
537
+ | `'solid, mediumGrey'` | `'solid mediumGrey'` |
538
+ | `'1px, solid'` | `'1px solid'` |
498
539
 
499
540
  ### boxShadow: commas to spaces within shadow, pipe to comma between shadows
500
541
 
501
- | Old | New |
502
- |---|---|
503
- | `'white .1, 0, A, C, C'` | `'white.1 0 A C C'` |
504
- | `'black .10, 0px, 2px, 8px, 0px'` | `'black.1 0px 2px 8px 0px'` |
505
- | `'a, b \| c, d'` | `'a b, c d'` |
542
+ | BEFORE | AFTER |
543
+ | ----------------------------------- | --------------------------- |
544
+ | `'white .1, 0, A, C, C'` | `'white.1 0 A C C'` |
545
+ | `'black .10, 0px, 2px, 8px, 0px'` | `'black.1 0px 2px 8px 0px'` |
546
+ | `'a, b \| c, d'` | `'a b, c d'` |
506
547
 
507
548
  ### textStroke/textShadow: comma-separated to space-separated
508
549
 
509
- | Old | New |
510
- |---|---|
511
- | `'1px, gray6'` | `'1px gray6'` |
512
- | `'gray1, 6px, 6px'` | `'gray1 6px 6px'` |
550
+ | BEFORE | AFTER |
551
+ | --------------------- | ------------------ |
552
+ | `'1px, gray6'` | `'1px gray6'` |
553
+ | `'gray1, 6px, 6px'` | `'gray1 6px 6px'` |
513
554
 
514
555
  ### CSS fallback
515
556
 
516
- Raw CSS values now pass through unchanged:
557
+ Raw CSS values pass through unchanged:
517
558
  ```js
518
559
  boxShadow: '0 2px 8px rgba(0,0,0,0.1)' // passes through as-is
519
560
  border: '1px solid #333' // passes through as-is