@symbiotejs/symbiote 3.8.0-webmcp.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -359,7 +359,8 @@ All modern browsers: Chrome, Firefox, Safari, Edge, Opera.
359
359
  - [Lit vs Symbiote.js](https://github.com/symbiotejs/symbiote.js/blob/main/docs/lit-vs-symbiote.md) - Side-by-side comparison
360
360
  - [Live Examples](https://rnd-pro.com/symbiote/3x/examples/) - Interactive Code Playground
361
361
  - [JSDA-Kit](https://github.com/rnd-pro/jsda-kit) - All-in-one companion tool: server, SSG, bundling, import maps, and native Symbiote.js SSR integration
362
- - [AI Reference](https://github.com/symbiotejs/symbiote.js/blob/main/AI_REFERENCE.md)
362
+ - [AI / llms.txt](https://rnd-pro.com/symbiote/llms.txt) — index for AI tools
363
+ - [Full docs (single file)](https://rnd-pro.com/symbiote/llms-full.txt) — complete merged reference for AI context
363
364
  - [Changelog](https://github.com/symbiotejs/symbiote.js/blob/main/CHANGELOG.md)
364
365
 
365
366
  ## Related articles
package/docs/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Symbiote.js Documentation
2
+
3
+ > **Version 3.x** — A lightweight, standards-first UI library built on Web Components.
4
+ > Zero dependencies. ~7.3 KB brotli / ~8.1 KB gzip. No virtual DOM, no compiler, no build step required.
5
+
6
+ ## Getting Started
7
+
8
+ - [**Get Started**](./get-started.md) — Installation, first component, platform specs
9
+ - [**Ecosystem**](./ecosystem.md) — IDE setup, build tools, code sharing
10
+
11
+ ## Core Concepts
12
+
13
+ - [**Templates**](./templates.md) — HTML string templates, binding syntax, slots, element references
14
+ - [**Properties**](./properties.md) — State initialization, `$` proxy, subscriptions, computed properties
15
+ - [**Context**](./context.md) — Local, named, pop-up, and shared data contexts
16
+ - [**List Rendering**](./list-rendering.md) — Itemize API for dynamic lists
17
+ - [**Lifecycle**](./lifecycle.md) — Component lifecycle callbacks and destruction
18
+ - [**Flags**](./flags.md) — Component configuration flags
19
+ - [**Attributes**](./attributes.md) — HTML attribute binding and observation
20
+
21
+ ## State Management
22
+
23
+ - [**PubSub**](./pubsub.md) — Standalone state management, named contexts
24
+
25
+ ## Styling
26
+
27
+ - [**Styling**](./styling.md) — rootStyles, shadowStyles, CSS processing
28
+ - [**CSS Data**](./css-data.md) — CSS custom properties as reactive state
29
+
30
+ ## Features
31
+
32
+ - [**WebMCP Experimental**](./webmcp.md) — Expose live UI state and handlers as browser-native tools
33
+ - [**Routing**](./routing.md) — SPA routing with path patterns, guards, lazy loading
34
+ - [**SSR**](./ssr.md) — Server-side rendering, streaming, hydration
35
+ - [**SSR and Your Server Setup**](./ssr-server.md) — Static build, streaming, Express or Fastify
36
+ - [**Animations**](./animations.md) — CSS-driven exit transitions
37
+
38
+ ## Development
39
+
40
+ - [**Dev Mode**](./dev-mode.md) — Verbose warnings for debugging
41
+ - [**TypeScript**](./typescript.md) — JSDoc types, TypeScript usage, and hybrid template checks
42
+ - [**Security**](./security.md) — Trusted Types and CSP compliance
43
+
44
+ ## Migration
45
+
46
+ - [**2.x → 3.x Migration Guide**](./migration-2x-to-3x.md) — Breaking changes and upgrade path
47
+
48
+ ## Additional Resources
49
+
50
+ - [Lit vs Symbiote.js](./lit-vs-symbiote.md) — Side-by-side comparison
51
+ - [Live Examples](https://rnd-pro.com/symbiote/3x/examples/) - Interactive Code Playground
52
+ - [Common Mistakes](./common-mistakes.md) — Patterns that frequently cause bugs
53
+ - [Examples](./examples.md) — 26 complete working examples
54
+ - [llms.txt](https://rnd-pro.com/symbiote/llms.txt) — index for AI tools
55
+ - [llms-full.txt](https://rnd-pro.com/symbiote/llms-full.txt) — complete merged reference for AI context
56
+ - [Changelog](../CHANGELOG.md)
57
+ - [npm](https://www.npmjs.com/package/@symbiotejs/symbiote)
58
+ - [GitHub Discussions](https://github.com/symbiotejs/symbiote.js/discussions)
59
+
60
+ ---
61
+
62
+ © [rnd-pro.com](https://rnd-pro.com) — MIT License
@@ -0,0 +1,61 @@
1
+ # Animations
2
+
3
+ Symbiote.js provides CSS-driven exit transitions with zero JS animation code.
4
+
5
+ ## `animateOut`
6
+
7
+ `animateOut(el)` sets the `[leaving]` attribute on an element, waits for CSS `transitionend`, then removes the element from the DOM. If no CSS transition is defined, removes immediately.
8
+
9
+ ```js
10
+ import { animateOut } from '@symbiotejs/symbiote';
11
+
12
+ // or as a static method:
13
+ Symbiote.animateOut(el);
14
+ ```
15
+
16
+ ## CSS pattern
17
+
18
+ Use `@starting-style` for enter animations and `[leaving]` for exit:
19
+ ```css
20
+ my-item {
21
+ opacity: 1;
22
+ transform: translateY(0);
23
+ transition: opacity 0.3s, transform 0.3s;
24
+
25
+ /* Enter (CSS-native, no JS needed): */
26
+ @starting-style {
27
+ opacity: 0;
28
+ transform: translateY(20px);
29
+ }
30
+
31
+ /* Exit (triggered by animateOut): */
32
+ &[leaving] {
33
+ opacity: 0;
34
+ transform: translateY(-10px);
35
+ }
36
+ }
37
+ ```
38
+
39
+ ## Itemize integration
40
+
41
+ Both itemize processors use `animateOut` automatically for item removal. Items with CSS `transition` + `[leaving]` styles will animate out before being removed from the DOM:
42
+ ```css
43
+ user-card {
44
+ opacity: 1;
45
+ transition: opacity 0.3s;
46
+
47
+ @starting-style {
48
+ opacity: 0;
49
+ }
50
+
51
+ &[leaving] {
52
+ opacity: 0;
53
+ }
54
+ }
55
+ ```
56
+
57
+ No additional JavaScript is required — just define the CSS transitions and Symbiote handles the rest.
58
+
59
+ ---
60
+
61
+ Next: [CSS Data →](./css-data.md)
@@ -0,0 +1,85 @@
1
+ # Attributes
2
+
3
+ Like all regular DOM elements, Symbiote components can have their own HTML attributes. And like standard Custom Elements, they can react to dynamic attribute changes.
4
+
5
+ ## Attribute connection
6
+
7
+ The simplest way to connect a local state property to an attribute value is with the `@` token:
8
+ ```js
9
+ class MyComponent extends Symbiote {
10
+
11
+ init$ = {
12
+ '@attribute-name': 'initial value',
13
+ }
14
+
15
+ }
16
+ ```
17
+
18
+ Or initiate from the component's template directly:
19
+ ```js
20
+ class MyComponent extends Symbiote {}
21
+
22
+ MyComponent.template = html`<h1>{{@attribute-name}}</h1>`;
23
+ ```
24
+
25
+ Then use it as an attribute in markup:
26
+ ```html
27
+ <my-component attribute-name="attribute value"></my-component>
28
+ ```
29
+
30
+ ## Attribute change reaction
31
+
32
+ Using a property accessor:
33
+ ```js
34
+ class MyComponent extends Symbiote {
35
+
36
+ set 'my-attribute'(val) {
37
+ console.log(val);
38
+ }
39
+
40
+ }
41
+
42
+ MyComponent.observedAttributes = [
43
+ 'my-attribute',
44
+ ];
45
+ ```
46
+
47
+ ## `bindAttributes()` static method
48
+
49
+ Bind attributes to property values directly:
50
+ ```js
51
+ class MyComponent extends Symbiote {
52
+
53
+ init$ = {
54
+ myProp: '',
55
+ }
56
+
57
+ }
58
+
59
+ // Map the attribute name to corresponding property key:
60
+ MyComponent.bindAttributes({
61
+ 'my-attribute': 'myProp',
62
+ });
63
+
64
+ // observedAttributes is auto-populated
65
+
66
+ MyComponent.template = html`
67
+ <div>{{myProp}}</div>
68
+ `;
69
+ ```
70
+
71
+ ## Reserved attribute names
72
+
73
+ These attribute names are used internally by Symbiote.js:
74
+
75
+ - `bind`
76
+ - `ctx`
77
+ - `ref`
78
+ - `itemize`
79
+ - `item-tag`
80
+ - `use-template`
81
+ - `skip-text-nodes`
82
+
83
+ ---
84
+
85
+ Next: [PubSub →](./pubsub.md)
@@ -0,0 +1,267 @@
1
+ # Common Mistakes
2
+
3
+ Patterns that frequently trip up new users and AI code generators.
4
+
5
+ ---
6
+
7
+ ## 1. Using `this` inside template strings
8
+
9
+ Templates are context-free strings — they DO NOT execute inside a component instance and do not close over `this`. Binding values by name; the component resolves them at render time.
10
+
11
+ ```js
12
+ // WRONG
13
+ MyComponent.template = html`<div>${this.title}</div>`;
14
+
15
+ // CORRECT — use binding syntax
16
+ MyComponent.template = html`<div>{{title}}</div>`;
17
+ // OR
18
+ MyComponent.template = html`<div ${{textContent: 'title'}}></div>`;
19
+ // OR
20
+ MyComponent.template = `<div bind="textContent: title"></div>`;
21
+ ```
22
+
23
+ ---
24
+
25
+ ## 2. Defining `template`, `rootStyles`, or `shadowStyles` inside the class body
26
+
27
+ `template`, `rootStyles`, and `shadowStyles` are **static property setters** on the `Symbiote` base class — not regular static fields. Assigning them inside the class body with `static template = ...` bypasses the setter and does nothing.
28
+
29
+ ```js
30
+ // WRONG
31
+ class MyComponent extends Symbiote {
32
+ static template = html`<div>{{text}}</div>`; // setter never called
33
+ }
34
+
35
+ // CORRECT — assign outside the class body
36
+ class MyComponent extends Symbiote {}
37
+ MyComponent.template = html`<div>{{text}}</div>`;
38
+ MyComponent.rootStyles = css`my-component { display: block; }`;
39
+ ```
40
+
41
+ ---
42
+
43
+ ## 3. Missing `^` prefix when referencing a parent-defined handler from an item template
44
+
45
+ Itemize items are real Symbiote components with their own reactive state (the data properties mapped from the source array or object). They can have their own handlers too — either passed as functions in the source data objects, or defined on a separate component class registered for the tag name set by `item-tag` attribute.
46
+
47
+ The common mistake is defining a shared handler on the **parent** (e.g. to handle all item clicks in one place) and then binding to it from the item template *without* `^`. Without `^`, the binding looks for the handler on the item itself — where it doesn't exist — and fails.
48
+
49
+ ```js
50
+ class MyList extends Symbiote {
51
+ init$ = {
52
+ items: [{ name: 'Alice' }, { name: 'Bob' }],
53
+ onItemClick: (e) => { console.log('clicked'); },
54
+ }
55
+ }
56
+
57
+ // WRONG — looks for onItemClick on the item, not the parent
58
+ MyList.template = html`
59
+ <ul itemize="items">
60
+ <template>
61
+ <li><button ${{onclick: 'onItemClick'}}>{{name}}</button></li>
62
+ </template>
63
+ </ul>
64
+ `;
65
+
66
+ // CORRECT — ^ walks up the DOM to find onItemClick in the parent's init$
67
+ MyList.template = html`
68
+ <ul itemize="items">
69
+ <template>
70
+ <li><button ${{onclick: '^onItemClick'}}>{{name}}</button></li>
71
+ </template>
72
+ </ul>
73
+ `;
74
+ ```
75
+
76
+ > `^`-targeted properties must be defined in the ancestors's `init$` — the walk does not check plain class properties.
77
+
78
+ When each item needs its own independent handler, you can pass it directly in the data:
79
+ ```js
80
+ this.$.items = [
81
+ { name: 'Alice', onItemClick: () => console.log('Alice clicked') },
82
+ { name: 'Bob', onItemClick: () => console.log('Bob clicked') },
83
+ ];
84
+ ```
85
+ ```html
86
+ <ul itemize="items">
87
+ <template>
88
+ <li><button ${{onclick: 'onItemClick'}}>{{name}}</button></li>
89
+ </template>
90
+ </ul>
91
+ ```
92
+
93
+ Or, as it recommended for the most cases, register a dedicated component class for the tag name set by `item-tag`:
94
+ ```js
95
+ class MyItem extends Symbiote {
96
+ onItemClick() { console.log(this.$.name); }
97
+ }
98
+ MyItem.template = html`<li><button ${{onclick: 'onItemClick'}}>{{name}}</button></li>`;
99
+ MyItem.reg('my-item');
100
+ ```
101
+ ```html
102
+ <ul itemize="items" item-tag="my-item"></ul>
103
+ ```
104
+
105
+ ---
106
+
107
+ ## 4. Using `@` attribute prefix directly in HTML
108
+
109
+ `@` is a binding syntax prefix only — it means "bind to HTML attribute" inside `${{}}` expressions. It is not a valid HTML attribute prefix.
110
+
111
+ ```html
112
+ <!-- WRONG -->
113
+ <div @hidden="isHidden">...</div>
114
+
115
+ <!-- CORRECT — @-prefix is only inside binding objects -->
116
+ <div ${{'@hidden': 'isHidden'}}>...</div>
117
+ <!-- OR, when html template helper is not used: -->
118
+ <div bind="@hidden': isHidden">...</div>
119
+ ```
120
+
121
+ ---
122
+
123
+ ## 5. Expecting dotted `init$` keys to create nested state
124
+
125
+ State is flat by design. A key like `'obj.prop'` in `init$` is a literal flat key named `obj.prop` — it does not create a nested object `{ obj: { prop: ... } }`. `this.$['obj.prop']` reads and writes that same flat key and works fine.
126
+
127
+ ```js
128
+ // MISLEADING — this does NOT create { obj: { prop: 'value' } }
129
+ init$ = { 'obj.prop': 'value' };
130
+ // It creates a flat key: store['obj.prop'] = 'value'
131
+ // Reading/writing it works: this.$['obj.prop'] = 'new' ✓
132
+
133
+ // If you need a nested object in state, store it as a flat key with an object value:
134
+ init$ = { obj: { prop: 'value' } };
135
+ this.$.obj = { ...this.$.obj, prop: 'new' }; // replace the whole object to trigger reactivity
136
+ ```
137
+
138
+ Dot notation in binding **targets** is fully supported — for both standard DOM properties and child component state:
139
+ ```js
140
+ // DOM property path:
141
+ html`<div ${{'style.color': 'colorProp'}}>Text</div>`
142
+
143
+ // Child component's $ state proxy:
144
+ html`<child-el ${{'$.childProp': 'parentProp'}}></child-el>`
145
+ ```
146
+
147
+ ---
148
+
149
+ ## 6. Using `*prop` without a `ctx` attribute or `--ctx` CSS variable
150
+
151
+ Shared context properties (`*`-prefix) require a context name to be set on the component — either via the `ctx` HTML attribute or the `--ctx` CSS custom property. Without one, `*` props silently have no effect (dev mode warns).
152
+
153
+ ```html
154
+ <!-- WRONG — no context name, *files is never shared -->
155
+ <upload-btn></upload-btn>
156
+ <file-list></file-list>
157
+
158
+ <!-- CORRECT -->
159
+ <upload-btn ctx="gallery"></upload-btn>
160
+ <file-list ctx="gallery"></file-list>
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 7. Expecting Shadow DOM by default
166
+
167
+ Shadow DOM is opt-in. By default, templates render into the component's light DOM. Use `renderShadow = true` or assign `shadowStyles` to opt in.
168
+
169
+ ```js
170
+ // Light DOM (default)
171
+ class MyComponent extends Symbiote {}
172
+
173
+ // Shadow DOM — either flag:
174
+ class MyComponent extends Symbiote {
175
+ renderShadow = true;
176
+ }
177
+ // or via shadowStyles (auto-creates shadow root):
178
+ MyComponent.shadowStyles = css`:host { display: block; }`;
179
+ ```
180
+
181
+ ---
182
+
183
+ ## 8. Adding a wrapper div inside the template
184
+
185
+ The custom element itself is the container. Adding a wrapping `<div>` as the single root inside the template is unnecessary — it creates extra DOM nesting and duplicates the element's own box. Style the component tag directly instead.
186
+
187
+ ```js
188
+ // WRONG — the <div class="wrapper"> is redundant, my-widget is already the root
189
+ MyWidget.template = html`
190
+ <div class="wrapper">
191
+ <h1>{{title}}</h1>
192
+ <button ${{onclick: 'onAction'}}>Go</button>
193
+ </div>
194
+ `;
195
+
196
+ // CORRECT — template content renders directly inside the custom element
197
+ MyWidget.template = html`
198
+ <h1>{{title}}</h1>
199
+ <button ${{onclick: 'onAction'}}>Go</button>
200
+ `;
201
+ ```
202
+
203
+ ```css
204
+ /* Style the element tag directly */
205
+ my-widget {
206
+ display: block;
207
+ padding: 20px;
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 9. Relying on class property fallbacks for prefixed bindings
214
+
215
+ Class property fallbacks (resolving unregistered keys from own instance properties or prototype methods) only apply to **local, unprefixed** bindings. Any prefixed binding — `^prop`, `*prop` — resolves exclusively through the data context (`init$` or `add$()`). Plain class properties are never checked for these.
216
+
217
+ ```js
218
+ // WRONG — onItemClick is a class property, not in init$
219
+ // ^ will not find it
220
+ class TaskList extends Symbiote {
221
+ onItemClick() { console.log('clicked'); }
222
+ }
223
+
224
+ TaskList.template = html`
225
+ <ul itemize="tasks">
226
+ <template>
227
+ <li ${{onclick: '^onItemClick'}}>{{name}}</li>
228
+ </template>
229
+ </ul>
230
+ `;
231
+
232
+ // CORRECT — register in init$ so any prefixed binding can resolve it
233
+ class TaskList extends Symbiote {
234
+ init$ = {
235
+ onItemClick: () => { console.log('clicked'); },
236
+ }
237
+ }
238
+ ```
239
+
240
+ The same applies to shared (`*`) bindings — the property must exist in the shared context's registered data, not merely as a class property.
241
+
242
+ Named external contexts (`CTX/prop`) are different: they are registered globally via `PubSub.registerCtx()` and are fully independent of any component's `init$` — components reference them freely without declaring anything locally.
243
+
244
+ ---
245
+
246
+ ## 10. Treating `init$` as a plain object
247
+
248
+ `init$` is processed once at connection time to populate the component's reactive context. Mutating it after the fact has no effect. Use `this.$` or `add$()` for runtime changes.
249
+
250
+ ```js
251
+ // WRONG — modifying init$ after construction does nothing
252
+ class MyComponent extends Symbiote {
253
+ init$ = { count: 0 };
254
+ initCallback() {
255
+ this.init$.count = 10; // too late, already processed
256
+ }
257
+ }
258
+
259
+ // CORRECT
260
+ class MyComponent extends Symbiote {
261
+ init$ = { count: 0 };
262
+ initCallback() {
263
+ this.$.count = 10; // write through the $ proxy
264
+ }
265
+ }
266
+ ```
267
+