@symbo.ls/mcp 1.0.21 → 1.0.22

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.
@@ -662,7 +662,7 @@ async function handleTool(name, args) {
662
662
  // generate_page
663
663
  if (name === 'generate_page') {
664
664
  const pageName = args.page_name || 'home'
665
- const context = readSkills('RULES.md', 'COMMON_MISTAKES.md', 'PROJECT_STRUCTURE.md', 'PATTERNS.md', 'SNIPPETS.md', 'DEFAULT_LIBRARY.md', 'COMPONENTS.md')
665
+ const context = readSkills('RULES.md', 'COMMON_MISTAKES.md', 'PROJECT_STRUCTURE.md', 'SHARED_LIBRARIES.md', 'PATTERNS.md', 'SNIPPETS.md', 'DEFAULT_LIBRARY.md', 'COMPONENTS.md')
666
666
  return `# Generate Page: ${pageName}\n\n## Description\n${args.description}\n\n## Requirements\n- Export as: \`export const ${pageName} = { ... }\`\n- Page is a plain object composing components\n- Add to pages/index.js route map: \`'/${pageName}': ${pageName}\`\n- Use components by PascalCase key (Header, Footer, Hero, etc.)\n- **MANDATORY: ALL values MUST use design system tokens** — spacing (A, B, C, D), colors (primary, surface, white, gray.5), typography (fontSize: 'B'). ZERO px values, ZERO hex colors, ZERO rgb/hsl.\n- Use Icon component for SVGs — store icons in designSystem/icons\n- NO direct DOM manipulation — all structure via DOMQL declarative syntax\n- Include responsive layout adjustments\n\n## Context — Rules, Structure, Patterns & Snippets\n\n${context}`
667
667
  }
668
668
 
@@ -692,7 +692,8 @@ async function handleTool(name, args) {
692
692
  if (files.includes('index.html') && !files.includes('package.json') && !files.includes('symbols.json')) { envType = 'cdn'; confidence = 'medium' }
693
693
  }
694
694
  const guide = readSkill('RUNNING_APPS.md')
695
- return `# Environment Detection\n\n**Detected: ${envType}** (confidence: ${confidence})\n\n${guide}`
695
+ const sharedLibsGuide = envType === 'local_project' ? '\n\n## Shared Libraries\n\n' + readSkill('SHARED_LIBRARIES.md') : ''
696
+ return `# Environment Detection\n\n**Detected: ${envType}** (confidence: ${confidence})\n\n${guide}${sharedLibsGuide}`
696
697
  }
697
698
 
698
699
  // audit_component (sync)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbo.ls/mcp",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "Symbols.app MCP — docs, code generation, publishing, CLI/SDK reference",
5
5
  "mcpName": "io.github.symbo-ls/symbols-mcp",
6
6
  "files": [
@@ -202,6 +202,7 @@ Generate or update two files. Keep their scopes strictly separated.
202
202
  Include only framework-level findings:
203
203
 
204
204
  - DOMQL v3 violations
205
+ - UPPERCASE design system keys (COLOR, THEME, etc. — must be lowercase)
205
206
  - Event handler misuse
206
207
  - Atom/state mispatterns
207
208
  - Shorthand inconsistencies
@@ -223,3 +223,19 @@ SpreadsheetCell: { fontSize: 'Z1', width: 'E', ... }
223
223
  RowNumberCell: { fontSize: 'Z1', width: 'C', ... }
224
224
  CornerCell: { fontSize: 'Z1', width: 'C', ... }
225
225
  ```
226
+
227
+ ---
228
+
229
+ ## 13. Design system keys — ALWAYS lowercase, never UPPERCASE
230
+
231
+ ```js
232
+ // ❌ WRONG — UPPERCASE keys are deprecated and banned
233
+ import { TYPOGRAPHY, SPACING } from '@symbo.ls/scratch'
234
+ const { COLOR, THEME } = context.designSystem
235
+ set({ COLOR: { blue: '#00f' }, TYPOGRAPHY: { base: 16 } })
236
+
237
+ // ✅ CORRECT — lowercase keys only
238
+ import { typography, spacing } from '@symbo.ls/scratch'
239
+ const { color, theme } = context.designSystem
240
+ set({ color: { blue: '#00f' }, typography: { base: 16 } })
241
+ ```
@@ -65,7 +65,8 @@ All atoms support these additional features:
65
65
  |---------|--------|
66
66
  | Media queries | `@mobile`, `@tablet`, `@tabletSm`, `@dark`, `@light` |
67
67
  | Pseudo selectors | `:hover`, `:active`, `:focus-visible` |
68
- | Conditional cases | `.isActive`, `.disabled`, `.hidden`, `!isActive` |
68
+ | Conditional cases | `.isActive`, `!isActive`, `$isSafari` (global from `context.cases`) |
69
+ | ARIA attributes | `ariaLabel`, `aria: { expanded: true }`, `'aria-label': 'Close'` |
69
70
  | Child overrides | `childProps` — one-level child overrides |
70
71
  | Children | `children` — arrays or nested object trees |
71
72
  | Lifecycle events | `onInit`, `onRender`, `onUpdate`, `onStateUpdate` |
@@ -42,6 +42,7 @@ import pages from './pages/index.js'
42
42
  import * as functions from './functions/index.js'
43
43
  import * as methods from './methods/index.js'
44
44
  import designSystem from './designSystem/index.js'
45
+ import cases from './cases.js'
45
46
  import files from './files/index.js'
46
47
 
47
48
  export default {
@@ -53,6 +54,7 @@ export default {
53
54
  functions,
54
55
  methods,
55
56
  designSystem,
57
+ cases,
56
58
  files
57
59
  }
58
60
  ```
@@ -17,8 +17,8 @@ Design system config lives in `designSystem/`. All tokens resolve to CSS via DOM
17
17
  | `class.js` | Utility CSS class overrides |
18
18
  | `animation.js` | Named keyframe animations |
19
19
  | `media.js` | Custom media query breakpoints |
20
- | `cases.js` | Conditional environment flags |
21
20
  | `reset.js` | Global CSS reset overrides |
21
+ | `vars.js` | Custom CSS properties (custom vars) |
22
22
 
23
23
  ## How Tokens Are Used in Props
24
24
 
@@ -488,16 +488,56 @@ Box: {
488
488
 
489
489
  ---
490
490
 
491
- ## CASES
491
+ ## Cases
492
492
 
493
- | Case | Condition |
494
- |---|---|
495
- | `isSafari` | `true` when browser is Safari |
493
+ Cases are defined in `symbols/cases.js` (not in designSystem) and added to `context.cases`. They are functions that evaluate conditions globally or per-element.
494
+
495
+ ### Defining cases
496
+
497
+ ```js
498
+ // symbols/cases.js
499
+ export default {
500
+ isSafari: () => /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent),
501
+ isGeorgian () { return this?.state?.root?.language === 'ka' },
502
+ isMobile: () => window.innerWidth < 768
503
+ }
504
+
505
+ // symbols/context.js
506
+ import cases from './cases.js'
507
+ export default { cases, /* ...other context */ }
508
+ ```
509
+
510
+ Case functions receive `element` as `this` (and first arg), but must work without it (for global detection like `isSafari`).
511
+
512
+ ### Using cases in components
496
513
 
497
514
  ```js
515
+ // $ prefix — global case from context.cases
498
516
  Element: { $isSafari: { top: 'Z2', right: 'Z2' } }
517
+
518
+ // . prefix — props/state first, then context.cases
519
+ Button: { '.isActive': { background: 'blue', aria: { expanded: true } } }
520
+
521
+ // ! prefix — inverted
522
+ Card: { '!isMobile': { maxWidth: '1200px' } }
523
+ ```
524
+
525
+ Cases work in both CSS props (css-in-props) and HTML attributes (attrs-in-props).
526
+
527
+ ## CSS Custom Properties (vars)
528
+
529
+ Define initial CSS custom properties in designSystem:
530
+
531
+ ```js
532
+ vars: {
533
+ '--header-height': '60px',
534
+ 'sidebar-width': '280px', // auto-prefixed to --sidebar-width
535
+ gap: '1rem' // becomes --gap
536
+ }
499
537
  ```
500
538
 
539
+ Reference in props: `padding: '--gap'` → resolves to `var(--gap)`. Any `--` prefixed value is auto-wrapped in `var()`.
540
+
501
541
  ---
502
542
 
503
543
  ## Design System Configuration
@@ -566,7 +606,7 @@ const designSystemConfig = {
566
606
  ```
567
607
  color, gradient, theme, typography, spacing, timing,
568
608
  font, font_family, icons, semantic_icons, svg, svg_data,
569
- shadow, media, grid, class, reset, unit, animation, cases
609
+ shadow, media, grid, class, reset, unit, animation, vars
570
610
  ```
571
611
 
572
612
  Do NOT wrap these under `props` or other wrappers.
@@ -599,13 +639,30 @@ font: {
599
639
 
600
640
  ```js
601
641
  font: {
602
- inter: {
603
- 400: { url: '/fonts/Inter-Regular.woff2', fontWeight: 400 },
604
- 700: { url: '/fonts/Inter-Bold.woff2', fontWeight: 700 }
605
- }
642
+ inter: [
643
+ { url: '/fonts/Inter-Regular.woff2', fontWeight: 400 },
644
+ { url: '/fonts/Inter-Bold.woff2', fontWeight: 700 }
645
+ ]
646
+ }
647
+ ```
648
+
649
+ ### Multiple format fallbacks (array URL)
650
+
651
+ ```js
652
+ font: {
653
+ Exo2: [
654
+ {
655
+ url: ['Exo2-Medium.woff2', 'Exo2-Medium.woff'],
656
+ fontWeight: '500',
657
+ fontStyle: 'normal',
658
+ fontDisplay: 'swap'
659
+ }
660
+ ]
606
661
  }
607
662
  ```
608
663
 
664
+ Generates comma-separated `src` with auto-detected formats per URL.
665
+
609
666
  ---
610
667
 
611
668
  ## Icons & SVG
@@ -709,6 +766,7 @@ updateVars({ color: { primary: '#ff0000' } }) // Update CSS variables only
709
766
 
710
767
  ## Common Mistakes
711
768
 
769
+ - **NEVER use UPPERCASE keys** (`COLOR`, `THEME`, `TYPOGRAPHY`, etc.) — always use lowercase (`color`, `theme`, `typography`). UPPERCASE is deprecated and banned.
712
770
  - Do NOT nest config under `props` or other wrappers
713
771
  - Use `font_family` not `fontFamily` in config
714
772
  - Define `typography` and `spacing` if you use tokens like `A`, `B2`, or `C+Z`
@@ -23,7 +23,7 @@ Provide one or more of: design description, wireframe, screenshot, or component
23
23
 
24
24
  - Complete, copy-paste ready Symbols component objects
25
25
  - Responsive implementation using Symbols breakpoint syntax (`@tabletS`, `@mobileL`, etc.)
26
- - Accessibility: semantic `tag` values, ARIA attributes where needed
26
+ - Accessibility: semantic `tag` values, ARIA attributes via `aria: {}` shorthand / camelCase (`ariaLabel`) / kebab-case (`aria-label`)
27
27
  - Loading states via `if` conditionals and state flags
28
28
  - Animation via CSS transition properties on the component object
29
29
 
@@ -251,18 +251,25 @@ Box: { tag: 'div', text: 'Submit', onClick: fn } // div is not a button
251
251
  ```js
252
252
  // Landmark roles
253
253
  Box: { role: 'alert', text: 'Error occurred' }
254
- Box: { role: 'status', attr: { 'aria-live': 'polite' }, text: '3 results found' }
254
+ Box: { role: 'status', aria: { live: 'polite' }, text: '3 results found' }
255
255
 
256
- // Labels
257
- Input: { attr: { 'aria-label': 'Search networks' } }
258
- Button: { icon: 'x', attr: { 'aria-label': 'Close dialog' } }
259
- Icon: { name: 'settings', attr: { 'aria-hidden': 'true' } }
256
+ // Labels — three equivalent forms:
257
+ Input: { ariaLabel: 'Search networks' } // camelCase
258
+ Button: { icon: 'x', aria: { label: 'Close dialog' } } // object shorthand
259
+ Icon: { name: 'settings', 'aria-hidden': 'true' } // kebab-case
260
260
 
261
- // Dynamic state
261
+ // Dynamic state via attr block (functions)
262
262
  Button: {
263
263
  attr: (el, s) => ({ 'aria-expanded': s.isOpen, 'aria-controls': 'dropdown-menu' }),
264
264
  onClick: (e, el, s) => s.update({ isOpen: !s.isOpen })
265
265
  }
266
+
267
+ // Dynamic state via conditional cases
268
+ Button: {
269
+ '.isOpen': { aria: { expanded: true } },
270
+ '!isOpen': { ariaHidden: true },
271
+ onClick: (e, el, s) => s.update({ isOpen: !s.isOpen })
272
+ }
266
273
  ```
267
274
 
268
275
  ### Keyboard Navigation
@@ -26,6 +26,7 @@ project-root/
26
26
  └── symbols/ # Symbols frontend (client-side)
27
27
  ├── index.js # Root entry: exports components, pages, state, designSystem, functions
28
28
  ├── state.js # export default { key: initialValue, ... }
29
+ ├── cases.js # export default { isSafari: () => {}, ... } — conditional cases
29
30
  ├── lang.js # Translations — root level, NOT in designSystem
30
31
  ├── dependencies.js # export default { 'pkg': 'exact-version' }
31
32
  ├── config.js # export default { useReset: true, fetch: { adapter: 'supabase', ... }, ... }
@@ -228,9 +229,9 @@ import theme from './theme.js'
228
229
  export default { color, theme }
229
230
  ```
230
231
 
231
- **What belongs here:** color, gradient, theme, font, typography, spacing, timing, grid, icons, shape, reset, animation, media, cases.
232
+ **What belongs here:** color, gradient, theme, font, typography, spacing, timing, grid, icons, shape, reset, animation, media, vars.
232
233
 
233
- **What does NOT belong here:** translations/lang (use root-level `lang.js`), application state, API config, business logic.
234
+ **What does NOT belong here:** translations/lang (use root-level `lang.js`), cases (use root-level `cases.js`), application state, API config, business logic.
234
235
 
235
236
  See `DESIGN_SYSTEM.md` for the full token reference.
236
237
 
@@ -249,7 +250,7 @@ export default {
249
250
 
250
251
  // symbols/context.js
251
252
  import lang from './lang.js'
252
- export default { lang, state, components, designSystem, ...config }
253
+ export default { lang, cases, state, components, designSystem, ...config }
253
254
  ```
254
255
 
255
256
  For Supabase-backed translations, configure the `polyglot` key in `config.js`:
@@ -18,6 +18,27 @@ These rules are absolute. Violations cause silent failures (black page, nothing
18
18
  | `children` + `childExtends` | ~~`$collection`, `$propsCollection`~~ |
19
19
  | `children` + `childrenAs: 'state'` | ~~`$stateCollection`~~ |
20
20
  | No `extends` needed for Text/Box/Flex; replace `extends: 'Flex'` with `flow: 'x'` or `flow: 'y'` | ~~`extends: 'Text'`~~, ~~`extends: 'Box'`~~, ~~`extends: 'Flex'`~~ |
21
+ | `color: {}`, `theme: {}`, `typography: {}` (lowercase) | ~~`COLOR: {}`, `THEME: {}`, `TYPOGRAPHY: {}`~~ (UPPERCASE) |
22
+ | `context.designSystem.color` | ~~`context.designSystem.COLOR`~~ |
23
+ | `import { typography } from '@symbo.ls/scratch'` | ~~`import { TYPOGRAPHY } from '@symbo.ls/scratch'`~~ |
24
+
25
+ ---
26
+
27
+ ## Rule 0 — Design system keys are ALWAYS lowercase
28
+
29
+ UPPERCASE design system keys (`COLOR`, `THEME`, `TYPOGRAPHY`, `SPACING`, `TIMING`, `FONT`, `FONT_FAMILY`, `ICONS`, `SHADOW`, `MEDIA`, `GRID`, `ANIMATION`, `RESET`, `SVG`, `GRADIENT`, `SEMANTIC_ICONS`, `CASES`) are **deprecated and strictly banned**.
30
+
31
+ Always use lowercase: `color`, `theme`, `typography`, `spacing`, `timing`, `font`, `font_family`, `icons`, `shadow`, `media`, `grid`, `animation`, `reset`, `svg`, `gradient`, `vars`.
32
+
33
+ ```js
34
+ // ❌ BANNED
35
+ import { TYPOGRAPHY } from '@symbo.ls/scratch'
36
+ const { COLOR } = context.designSystem
37
+
38
+ // ✅ CORRECT
39
+ import { typography } from '@symbo.ls/scratch'
40
+ const { color } = context.designSystem
41
+ ```
21
42
 
22
43
  ---
23
44
 
@@ -55,6 +55,7 @@ my-app/
55
55
  ├── pages/
56
56
  │ ├── index.js # import-based route map (only file with imports)
57
57
  │ └── main.js # export const main = { extends: 'Page', ... }
58
+ ├── cases.js # export default { isSafari: () => {}, ... }
58
59
  ├── functions/
59
60
  │ ├── index.js # export * from './myFn.js'
60
61
  │ └── myFn.js # export const myFn = function() {}
@@ -0,0 +1,313 @@
1
+ # sharedLibraries
2
+
3
+ sharedLibraries is a mechanism in the Symbols framework that allows projects to inherit components, functions, methods, state, designSystem, pages, files, snippets, and dependencies from external library projects. Libraries are merged into the consuming app's context at runtime — no manual re-exports needed.
4
+
5
+ ---
6
+
7
+ ## Configuration
8
+
9
+ ### symbols.json
10
+
11
+ Three supported formats:
12
+
13
+ ```json
14
+ // Array of strings (library keys)
15
+ "sharedLibraries": ["brand", "shared"]
16
+
17
+ // Object with key:version
18
+ "sharedLibraries": { "brand": "1.0.0", "shared": "latest" }
19
+
20
+ // Object with key:config (used in editor)
21
+ "sharedLibraries": {
22
+ "brand": { "version": "1.0.0", "destDir": "../brand" },
23
+ "shared": { "destDir": "../shared" }
24
+ }
25
+ ```
26
+
27
+ All formats are normalized by the CLI (`cli/helpers/symbolsConfig.js:246-268`) to: `[{ key, version, destDir }]`
28
+
29
+ ### sharedLibraries.js
30
+
31
+ Each package has a `sharedLibraries.js` that imports other packages' `context.js` and exports them as an array:
32
+
33
+ ```js
34
+ import brand from '../brand/context.js'
35
+ import shared from '../shared/context.js'
36
+
37
+ export default [brand, shared]
38
+ ```
39
+
40
+ For remote libraries fetched from the platform:
41
+
42
+ ```js
43
+ import platformSymboLs from '../.symbols_local/libs/platform.symbo.ls/context.js'
44
+ import docsSymboLs from '../.symbols_local/libs/docs.symbo.ls/context.js'
45
+
46
+ export default [platformSymboLs, docsSymboLs]
47
+ ```
48
+
49
+ ### context.js
50
+
51
+ The `sharedLibraries` array is included in the context export:
52
+
53
+ ```js
54
+ import sharedLibraries from './sharedLibraries.js'
55
+ import * as components from './components/index.js'
56
+ import * as functions from './functions/index.js'
57
+ // ...
58
+
59
+ export default {
60
+ sharedLibraries,
61
+ components,
62
+ functions,
63
+ // ...
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## Runtime Merge
70
+
71
+ ### Entry Point
72
+
73
+ `smbls/src/createDomql.js:42-44`:
74
+
75
+ ```js
76
+ if (context.sharedLibraries && context.sharedLibraries.length) {
77
+ prepareSharedLibs(context)
78
+ }
79
+ ```
80
+
81
+ This runs early in context initialization, **before** designSystem, state, components, and pages are prepared.
82
+
83
+ ### Merge Logic
84
+
85
+ `smbls/src/prepare.js:240-251`:
86
+
87
+ ```js
88
+ export const prepareSharedLibs = (context) => {
89
+ const sharedLibraries = context.sharedLibraries
90
+ for (let i = 0; i < sharedLibraries.length; i++) {
91
+ const sharedLib = sharedLibraries[i]
92
+ if (context.type === 'template') {
93
+ overwriteShallow(context.designSystem, sharedLib.designSystem)
94
+ deepMerge(context, sharedLib, ['designSystem'], 1)
95
+ } else {
96
+ deepMerge(context, sharedLib, [], 1)
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ **Two strategies:**
103
+
104
+ | App Type | designSystem | Everything Else |
105
+ |----------|-------------|-----------------|
106
+ | **template** | `overwriteShallow` — library completely replaces template's designSystem | `deepMerge` with designSystem excluded |
107
+ | **regular** | `deepMerge` (app wins) | `deepMerge` (app wins) |
108
+
109
+ ### deepMerge Behavior
110
+
111
+ `smbls/packages/utils/object.js:61-76`:
112
+
113
+ ```js
114
+ export const deepMerge = (element, extend, excludeFrom = METHODS_EXL) => {
115
+ for (const e in extend) {
116
+ if (_startsWithDunder(e)) continue // skip __private
117
+ if (excludeFrom.includes(e)) continue // skip excluded keys
118
+ const elementProp = element[e]
119
+ const extendProp = extend[e]
120
+ if (isObjectLike(elementProp) && isObjectLike(extendProp)) {
121
+ deepMerge(elementProp, extendProp, excludeFrom) // recursive
122
+ } else if (elementProp === undefined) {
123
+ element[e] = extendProp // only fill undefined slots
124
+ }
125
+ }
126
+ return element
127
+ }
128
+ ```
129
+
130
+ Key rules:
131
+ - **App always wins** — only merges when the app property is `undefined`
132
+ - Skips `__` prefixed properties
133
+ - Recursively merges nested objects
134
+ - Skips METHODS_EXL keys: `node`, `context`, `extends`, `__element`, `__ref`, and all element/state/props methods
135
+
136
+ ---
137
+
138
+ ## Order of Precedence
139
+
140
+ Libraries are processed sequentially. First library fills undefined slots, second fills remaining, etc.
141
+
142
+ ```
143
+ App's own context (highest priority — never overwritten)
144
+
145
+ sharedLibraries[0] (fills undefined slots)
146
+
147
+ sharedLibraries[1] (fills remaining undefined slots)
148
+
149
+ UIKit atoms (lowest priority, applied in prepareComponents)
150
+ ```
151
+
152
+ After all merges, `prepareComponents` applies:
153
+
154
+ ```js
155
+ // smbls/src/prepare.js:51-55
156
+ export const prepareComponents = (context) => {
157
+ return context.components
158
+ ? { ...UIkitWithPrefix(), ...context.components }
159
+ : UIkitWithPrefix()
160
+ }
161
+ ```
162
+
163
+ So the final component resolution is: **App > Shared Libraries > UIKit**
164
+
165
+ ---
166
+
167
+ ## What Gets Merged
168
+
169
+ | Merged | Not Merged (METHODS_EXL) |
170
+ |--------|--------------------------|
171
+ | components | node |
172
+ | functions | context |
173
+ | methods | extends |
174
+ | snippets | __element, __ref |
175
+ | pages / routes | Element methods (set, reset, update, remove, lookup...) |
176
+ | state | State methods (parse, create, destroy, toggle...) |
177
+ | designSystem | Props methods |
178
+ | files | Properties starting with `__` |
179
+ | dependencies | |
180
+ | dependenciesOnDemand | |
181
+ | utils, cases, plugins | |
182
+
183
+ ---
184
+
185
+ ## CLI Integration
186
+
187
+ ### Scaffolding (`cli/bin/fs.js:231-345`)
188
+
189
+ `scaffoldSharedLibraries()`:
190
+ 1. Reads `sharedLibraries` from project JSON
191
+ 2. Creates each library as a full project folder via frank's `toFS()`
192
+ 3. Default location: `.symbols_local/libs/`
193
+ 4. Generates `sharedLibraries.js` with import statements
194
+
195
+ ### Fetch (`cli/bin/fetch.js`)
196
+
197
+ When fetching from the platform:
198
+ - Pulls full project data including `sharedLibraries` array
199
+ - Each library is a complete Symbols project object
200
+ - Extracts version info and stores in lock file
201
+
202
+ ### toJSON (`frank/toJSON.js:170-183`)
203
+
204
+ Context modules list includes sharedLibraries:
205
+
206
+ ```js
207
+ const CONTEXT_MODULES = [
208
+ { name: 'state', path: './state.js', style: 'default' },
209
+ { name: 'dependencies', path: './dependencies.js', style: 'default' },
210
+ { name: 'sharedLibraries', path: './sharedLibraries.js', style: 'default' },
211
+ { name: 'components', path: './components/index.js', style: 'namespace' },
212
+ { name: 'snippets', path: './snippets/index.js', style: 'namespace' },
213
+ { name: 'pages', path: './pages/index.js', style: 'default' },
214
+ { name: 'functions', path: './functions/index.js', style: 'namespace' },
215
+ { name: 'methods', path: './methods/index.js', style: 'namespace' },
216
+ { name: 'designSystem', path: './designSystem/index.js', style: 'default' },
217
+ { name: 'files', path: './files/index.js', style: 'default' },
218
+ { name: 'config', path: './config.js', style: 'default' }
219
+ ]
220
+ ```
221
+
222
+ ### Validation (`cli/bin/validate-domql-runner.js:323-342`)
223
+
224
+ `mergeSharedLibraries()` merges all library exports for DOMQL validation:
225
+
226
+ ```js
227
+ function mergeSharedLibraries(sharedLibraries) {
228
+ const merged = { pages: {}, components: {}, functions: {}, methods: {}, snippets: {} }
229
+ for (const lib of libs) {
230
+ if (isPlainObject(lib?.pages)) Object.assign(merged.pages, lib.pages)
231
+ if (isPlainObject(lib?.components)) Object.assign(merged.components, lib.components)
232
+ // ...
233
+ }
234
+ return merged
235
+ }
236
+ ```
237
+
238
+ ---
239
+
240
+ ## Context Preparation Sequence
241
+
242
+ ```
243
+ prepareContext() {
244
+ 1. Set key, define, cssPropsRegistry, window
245
+
246
+ 2. prepareSharedLibs(context)
247
+ → deepMerge each library into context
248
+
249
+ 3. prepareDesignSystem(context)
250
+ → uses context.designSystem (now includes shared lib data)
251
+
252
+ 4. prepareState(app, context)
253
+ → merges context.state + app.state
254
+
255
+ 5. preparePages(app, context)
256
+ → merges app.routes + context.pages
257
+
258
+ 6. prepareComponents(context)
259
+ → { ...UIkit, ...context.components }
260
+
261
+ 7. prepareUtils(context)
262
+ → spreads utils, router, snippets, functions
263
+
264
+ 8. prepareDependencies()
265
+
266
+ 9. prepareMethods(context)
267
+ → spreads context.methods + require/router
268
+ }
269
+ ```
270
+
271
+ ---
272
+
273
+ ## Editor Workspace Map
274
+
275
+ | Package | sharedLibraries | Role |
276
+ |---------|----------------|------|
277
+ | **brand** | `[]` | Provider — colors, typography, design system |
278
+ | **shared** | none | Provider — common components, functions |
279
+ | **preview** | `[brand, shared]` | Consumer — standalone preview app |
280
+ | **convert** | `[brand, shared]` | Consumer — code converter app |
281
+ | **inspect** | `[brand, cms, shared]` | Consumer — inspector editor |
282
+ | **cms** | `[brand, shared]` | Consumer — CMS editor |
283
+ | **docs** | `[shared]` | Consumer — documentation |
284
+ | **assistant** | `[platform, docs, brand, shared]` | Consumer — AI assistant (remote + local libs) |
285
+ | **no-code** | `[brand, cms]` | Consumer — no-code editor |
286
+ | **canvas** | `[]` | Consumer — canvas editor (no shared libs) |
287
+
288
+ ---
289
+
290
+ ## Key Takeaways
291
+
292
+ 1. **No re-exports needed** — components/functions from a shared library are automatically available in the consuming app's context
293
+ 2. **App always wins** — app's own definitions take precedence over shared libraries
294
+ 3. **Order matters** — first library in the array has priority over later ones for filling undefined slots
295
+ 4. **Templates are special** — designSystem is fully overwritten (shallow) rather than deep-merged
296
+ 5. **Methods are protected** — METHODS_EXL prevents shared libraries from overwriting element methods and internal references
297
+ 6. **No circular detection** — JavaScript's ESM module system prevents infinite loops naturally; circular deps resolve as undefined values
298
+
299
+ ---
300
+
301
+ ## Source Files
302
+
303
+ | Purpose | File | Lines |
304
+ |---------|------|-------|
305
+ | Runtime merge entry | `smbls/packages/smbls/src/createDomql.js` | 42-44 |
306
+ | prepareSharedLibs | `smbls/packages/smbls/src/prepare.js` | 240-251 |
307
+ | deepMerge | `smbls/packages/utils/object.js` | 61-76 |
308
+ | overwriteShallow | `smbls/packages/utils/object.js` | 431-439 |
309
+ | METHODS_EXL | `smbls/packages/utils/keys.js` | 147-152 |
310
+ | Config normalization | `smbls/packages/cli/helpers/symbolsConfig.js` | 246-268 |
311
+ | FS scaffolding | `smbls/packages/cli/bin/fs.js` | 231-345 |
312
+ | Context modules | `smbls/packages/frank/toJSON.js` | 170-183 |
313
+ | Validation merge | `smbls/packages/cli/bin/validate-domql-runner.js` | 323-342 |
@@ -19,9 +19,11 @@ export const MyCard = {
19
19
  theme: 'dialog',
20
20
  round: 'C',
21
21
 
22
- // HTML attributes (standard attrs are auto-detected by attrs-in-props)
22
+ // HTML attributes (auto-detected by attrs-in-props)
23
23
  role: 'region',
24
- attr: { 'aria-label': ({ props }) => props.label },
24
+ ariaLabel: 'My card', // camelCase aria-label
25
+ aria: { describedby: 'desc' }, // object shorthand → aria-describedby
26
+ attr: { 'aria-label': ({ props }) => props.label }, // explicit attr block
25
27
 
26
28
  // State
27
29
  state: { open: false },
@@ -150,14 +152,25 @@ export const Hoverable = {
150
152
  }
151
153
  ```
152
154
 
153
- ### CSS Class State Modifiers (Emotion `.className`)
155
+ ### Conditional Props (Cases)
156
+
157
+ Three prefix types for conditional CSS and attributes:
158
+
159
+ | Prefix | Resolution | Example |
160
+ |---|---|---|
161
+ | `$` | Global case from `context.cases` | `$isSafari: { padding: 'B' }` |
162
+ | `.` | Props/state first, then `context.cases` | `.isActive: { opacity: 1 }` |
163
+ | `!` | Inverted — applies when falsy | `!isActive: { opacity: 0 }` |
164
+
165
+ Cases are defined in `symbols/cases.js` and added to `context.cases`. Both CSS props and HTML attributes inside conditional blocks are applied.
154
166
 
155
167
  ```js
156
168
  export const Item = {
157
169
  opacity: 0.6,
158
- '.active': { opacity: 1, fontWeight: '600' },
159
- '.disabled': { opacity: 0.3, pointerEvents: 'none' },
160
- '.hidden': { transform: 'translate3d(0,10%,0)', opacity: 0, visibility: 'hidden' }
170
+ '.active': { opacity: 1, fontWeight: '600', aria: { selected: true } },
171
+ '.disabled': { opacity: 0.3, pointerEvents: 'none', disabled: true },
172
+ '!active': { ariaHidden: true },
173
+ '$isSafari': { padding: 'B' }
161
174
  }
162
175
  ```
163
176