Skip to content

Commit 5f5bb2e

Browse files
authored
Merge branch 'main' into feat/utils-identity
2 parents 8eb9080 + 497c7a3 commit 5f5bb2e

55 files changed

Lines changed: 1146 additions & 223 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

ai/research/data-sync/plan.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,14 @@ Notes:
199199
- `pruneChildPaths` yields the changed path set — value-less and index-addressed (the reactivity review's finding), so the wire write picks values via `get` per path, and array segments translate to id-addressed form for schema-declared keyed arrays before send (see Repeating groups)
200200
- Clone cost is a non-goal: Meteor-era apps cloned liberally and stayed fast where it mattered. The place it would compound — per-inbound-delta apply — is avoided by field swaps incidentally, not as a crusade
201201

202+
### Write capture — `trackWrites`
203+
204+
`trackWrites(doc, fn, opts)` is the layer's write-capture primitive, lifted from its first consumer (`Signal.mutate`). It runs the mutator body and reports `{ changed, result, paths }``paths` only when `returnPaths: true`. The data layer always passes `returnPaths: true`, and `onWrite` for the per-write streaming path (outbox append, optimistic dep fires); a signal passes `returnPaths: false` because a signal's reactive granularity is the whole cell, so a path has nowhere to route — `returnPaths: false` is the floor, the data layer lives above it. The body runs synchronously: the tracked proxy expires at callback return, which is the root of the sync-only mutator constraint, not a separate rule.
205+
206+
The contract the layer relies on is that `paths` is deliberately thin — value-less, kind-less, positional (see the Write Path note above on `pruneChildPaths`, and Repeating groups for id-addressing). Richness is reconstructed by the consumer at the point it is needed, not carried in the capture: new values read via `get(doc, path)` against the post-apply doc, op-kind inferred from value-presence (undefined means cleared) plus channel-membership re-match, base values taken from the rebase shadow — never the proxy trap, whose intermediate value is a partially-rebased replay artifact — and id-addressed array paths translated by schema-aware lookup at commit. The capture stays a position log because every richer datum has a more authoritative downstream source; widening the return would only manufacture a second, staler one.
207+
208+
`trackWrites` is the owned-write engine — a mutation the layer performs and watches, both the client optimistic apply and the server authoritative apply. Its twin is `detectChanges(before, after)`, which diffs two finished images rather than watching a mutation, and is the external-write engine behind `watch()` CDC (External Writers). Owned writes capture, external writes diff — the dual-stream router's two capture engines, not redundant paths.
209+
202210
## Execution Without Fibers
203211

204212
Meteor's server-side sync CRUD was fibers suspending the stack during I/O. Node removed that, Meteor 3 paid with `insertAsync` everywhere. This design splits the problem by what actually blocks:

ai/skills/authoring/component-composition.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type: skill
1111

1212
> **Skill:** `component-composition`
1313
> **Purpose:** How to structure parent-child component relationships — when to use configuration, slots, passed templates, or imperative coordination.
14-
> **Last Updated:** 2026-03-04
14+
> **Last Updated:** 2026-06-16
1515
1616
---
1717

@@ -59,7 +59,7 @@ The core is a single `{#each}...{else}...{/each}` block:
5959
<div class="{uiClasses}menu" part="menu">
6060
{#each item in items}
6161
<menu-item
62-
active={isValueActive value item}
62+
active={isCurrentValue value item}
6363
href={item.href}
6464
value={item.value}
6565
exportparts="item"

ai/skills/authoring/component-css.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type: skill
1111

1212
> **Skill:** `component-css`
1313
> **Purpose:** Canonical patterns for writing CSS inside a component's shadow DOM — nesting, container queries, responsive design, state management, theming, and design token usage.
14-
> **Last Updated:** 2026-03-04
14+
> **Last Updated:** 2026-06-16
1515
1616
---
1717

@@ -46,16 +46,16 @@ Write minimal, maintainable CSS that leverages the design token system and mirro
4646

4747
.header {
4848
font-weight: var(--bold);
49-
margin-bottom: var(--compact-spacing);
49+
margin-bottom: var(--spacing-xs);
5050
}
5151

5252
.items {
5353
display: flex;
5454
flex-direction: column;
55-
gap: var(--compact-spacing);
55+
gap: var(--gap-xs);
5656

5757
.item {
58-
padding: var(--compact-spacing);
58+
padding: var(--padding-xs);
5959
cursor: pointer;
6060

6161
&:hover {
@@ -84,7 +84,7 @@ Write minimal, maintainable CSS that leverages the design token system and mirro
8484

8585
.actions {
8686
display: flex;
87-
gap: var(--compact-spacing);
87+
gap: var(--gap-xs);
8888
}
8989
}
9090
}
@@ -371,7 +371,7 @@ Semantic UI uses a sophisticated theme system where CSS variables automatically
371371
/* Allow external override */
372372
my-component {
373373
--component-max-width: 800px;
374-
--component-spacing: var(--compact-spacing);
374+
--component-spacing: var(--spacing-xs);
375375
}
376376
```
377377

ai/skills/authoring/component-patterns.md

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type: skill
1111

1212
> **Skill:** `component-patterns`
1313
> **Purpose:** Decision trees and production patterns for component communication, DOM querying, race condition prevention, resource cleanup, async reactions, lazy loading, scroll handling, and key anti-patterns.
14-
> **Last Updated:** 2026-03-04
14+
> **Last Updated:** 2026-06-16
1515
1616
---
1717

@@ -46,30 +46,22 @@ How should component A talk to component B?
4646
Use when a child needs to read or mutate parent state. The child knows about the parent by name.
4747

4848
```javascript
49-
// todo-item.js — child component
50-
const createComponent = ({ self, data, findParent }) => ({
51-
getTodos() {
52-
return findParent('todoList').todos;
49+
// panel.js — child accesses parent coordinator, then drives its state
50+
const createComponent = ({ el, self, findParent, settings }) => ({
51+
getPanels() {
52+
return findParent('uiPanels');
5353
},
54-
toggleCompleted() {
55-
const todos = self.getTodos();
56-
todos.toggleItemProperty(data.task._id, 'completed');
54+
minimize() {
55+
const panels = self.getPanels();
56+
settings.minimized = true;
57+
panels.setPanelMinimized(panels.getPanelIndex(el));
5758
},
5859
});
5960
```
60-
*Source: `docs/src/examples/framework/todo-list/todo-item.js`*
61+
*Source: `src/components/panels/panel.js`*
6162

6263
The argument to `findParent` is the **camelCase component name**, not the tag name. `findParent('uiPanels')` finds `<ui-panels>`, not `findParent('ui-panels')`.
6364

64-
```javascript
65-
// panel.js — child accesses parent coordinator
66-
getPanels() {
67-
const panels = findParent('uiPanels');
68-
return panels;
69-
},
70-
```
71-
*Source: `src/components/panels/panel.js`*
72-
7365
### Pattern 2: Parent listens to child via event delegation
7466

7567
Use when the parent needs to react to child lifecycle or user actions without the child needing a reference to the parent.
@@ -120,7 +112,7 @@ $('context-menu.box').settings({
120112
],
121113
});
122114
```
123-
*Source: `docs/src/examples/context-menu/page.js`*
115+
*Source: `docs/src/examples/component/context-menu/page.js`*
124116

125117
Use `initialize` instead of `settings` when the script may run before the component is in the DOM.
126118

ai/skills/authoring/component-specs.md

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type: skill
1111

1212
> **Skill:** `component-specs`
1313
> **Purpose:** Guide to the @semantic-ui/specs package — declarative component metadata, spec file format, SpecReader API, shared terms system, and build pipeline integration for spec-driven web components.
14-
> **Last Updated:** 2026-03-04
14+
> **Last Updated:** 2026-06-16
1515
1616
---
1717

@@ -76,7 +76,7 @@ Property Definitions & Attribute Mapping
7676
**Parallel Pipeline for Documentation**:
7777
```
7878
Component Definition (button.spec.js)
79-
SpecReader.getDefinition()
79+
DocsSpecReader.getDefinition()
8080
Documentation Objects with Examples
8181
↓ Template System
8282
Rendered Documentation Pages
@@ -198,7 +198,7 @@ Tiers are cumulative: `standard` ⊂ `extended` ⊂ `full`. The value is the **l
198198
At build time, these are aggregated into `dist/presets.json` which the CDN upload script reads and publishes to R2. The CDN Worker uses presets to resolve URLs like `/core@canary/standard` into the correct set of component imports.
199199

200200
Tiers:
201-
- **`standard`** (~40-50 components) — General-purpose UI for building typical apps. The "don't think about it" default.
201+
- **`standard`** — General-purpose UI for building typical apps. The "don't think about it" default.
202202
- **`extended`** — Standard + specialized components (rich form inputs, data viz, niche patterns).
203203
- **`full`** — Every user-facing component.
204204

@@ -526,8 +526,12 @@ const componentSpec = reader.getWebComponentSpec();
526526
### Generating Documentation
527527

528528
```javascript
529+
// Doc-generation methods live on DocsSpecReader (also from '@semantic-ui/specs')
530+
import { DocsSpecReader } from '@semantic-ui/specs';
531+
const docsReader = new DocsSpecReader(buttonSpec);
532+
529533
// Get complete definition with examples
530-
const definition = reader.getDefinition();
534+
const definition = docsReader.getDefinition();
531535

532536
// Result structure:
533537
{
@@ -539,28 +543,28 @@ const definition = reader.getDefinition();
539543
}
540544

541545
// Generate navigation menu for documentation
542-
const menu = reader.getDefinitionMenu();
546+
const menu = docsReader.getDefinitionMenu();
543547

544548
// Get ordered examples for display
545-
const examples = reader.getOrderedExamples();
549+
const examples = docsReader.getOrderedExamples();
546550
```
547551

548552
### Attribute Dialect Support
549553

550-
SpecReader supports three attribute dialects for flexibility:
554+
`getCodeFromModifiers` lives on `DocsSpecReader` and renders example code in three attribute dialects:
551555

552556
```javascript
553557
// Standard dialect (modifier-based)
554-
reader.getCodeFromModifiers('large primary');
558+
docsReader.getCodeFromModifiers('large primary');
555559
// Result: <ui-button large primary>Click Me</ui-button>
556560

557561
// Verbose dialect (explicit attributes)
558-
const verboseReader = new SpecReader(spec, { dialect: 'verbose' });
562+
const verboseReader = new DocsSpecReader(spec, { dialect: 'verbose' });
559563
verboseReader.getCodeFromModifiers('large primary');
560564
// Result: <ui-button size="large" emphasis="primary">Click Me</ui-button>
561565

562566
// Classic dialect (class-based)
563-
const classicReader = new SpecReader(spec, { dialect: 'classic' });
567+
const classicReader = new DocsSpecReader(spec, { dialect: 'classic' });
564568
classicReader.getCodeFromModifiers('large primary');
565569
// Result: <ui-button class="large primary">Click Me</ui-button>
566570
```

ai/skills/authoring/component-theming.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type: skill
1111

1212
> **Skill:** `component-theming`
1313
> **Purpose:** Build custom components whose styles automatically adapt to light/dark mode and respond to runtime theme changes
14-
> **Last Updated:** 2026-03-04
14+
> **Last Updated:** 2026-06-16
1515
1616
---
1717

@@ -109,7 +109,7 @@ Some visual effects genuinely need different parameters per theme (e.g., shadows
109109

110110
@container style(--dark-mode: true) {
111111
backdrop-filter: blur(8px) brightness(1.1);
112-
box-shadow: 0 0 20px var(--primary-color-20);
112+
box-shadow: 0 0 20px var(--primary-20);
113113
}
114114

115115
@container style(--light-mode: true) {

ai/skills/authoring/example-curriculum.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ The first 7 carry the core argument. The rest fill pattern gaps. Each entry list
2424

2525
### 1. `minimal` — The Floor
2626

27-
A complete component in 6 lines. Inline template and CSS as strings. This is the absolute minimum — proof that the simplest case is trivially simple before any complexity is introduced.
27+
A complete component in a few lines. Inline template and CSS as strings. This is the absolute minimum — proof that the simplest case is trivially simple before any complexity is introduced.
2828

2929
**New patterns:** Inline template/CSS strings (no `getText`), `onCreated` lifecycle hook, `formatDate` helper.
3030

ai/skills/authoring/reactive-state.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type: skill
1111

1212
> **Skill:** `reactive-state`
1313
> **Purpose:** Comprehensive guide to the @semantic-ui/reactivity package — a standalone signals-based reactive system with automatic dependency tracking for state management.
14-
> **Last Updated:** 2026-03-04
14+
> **Last Updated:** 2026-06-16
1515
1616
---
1717

@@ -158,7 +158,8 @@ const users = signal([
158158
]);
159159

160160
// Find by ID (supports id, _id, hash, key properties)
161-
const index = users.getItem(1); // Returns index of item with id=1
161+
const user = users.getItem(1); // Returns the item with id=1
162+
const userIndex = users.getItemIndex(1); // Returns its index (-1 if absent)
162163
users.setItemProperty(1, 'name', 'Alice2'); // Set property on item with id=1
163164
users.replaceItem(1, newUserObject); // Replace entire item with id=1
164165
users.removeItem(1); // Remove item with id=1

ai/skills/authoring/render-pipeline.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Template String
2323
|
2424
v
2525
+-----------------+
26-
| TemplateCompiler | packages/templating/src/compiler/
26+
| TemplateCompiler | packages/compiler/src/
2727
| | string -> AST (array of node objects)
2828
+-----------------+
2929
|
@@ -125,7 +125,7 @@ The engines are hot-swappable — two components on the same page can use differ
125125

126126
## Stage 1: TemplateCompiler
127127

128-
`packages/templating/src/compiler/template-compiler.js`
128+
`packages/compiler/src/template-compiler.js`
129129

130130
The compiler transforms a template string into an AST — a flat array of node objects. It has no knowledge of rendering, reactivity, or components.
131131

@@ -567,8 +567,8 @@ lookupExpressionValue (multi-token):
567567
### Key files
568568

569569
```
570-
packages/templating/src/compiler/string-scanner.js StringScanner (char-by-char parsing)
571-
packages/templating/src/compiler/template-compiler.js TemplateCompiler (string -> AST)
570+
packages/compiler/src/string-scanner.js StringScanner (char-by-char parsing)
571+
packages/compiler/src/template-compiler.js TemplateCompiler (string -> AST)
572572
packages/templating/src/template.js Template (lifecycle, state, events)
573573
packages/templating/src/template-helpers.js Built-in template helpers
574574

ai/skills/authoring/ssr-hydration.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ For each marker:
391391
392392
### The skipFirstWrite contract
393393
394-
When debugging "my signal mutation doesn't update the DOM after hydration," name `skipFirstWrite: true` explicitly — it's the grep-able load-bearing mechanism in `packages/renderer/src/engines/native/reactive-data.js` and surfacing it lets the reader navigate the code path directly.
394+
When debugging "my signal mutation doesn't update the DOM after hydration," name `skipFirstWrite: true` explicitly — it's the grep-able load-bearing mechanism in `packages/renderer/src/engines/native/attribute-binding.js` and surfacing it lets the reader navigate the code path directly.
395395
396396
Per-binding Reactions wired during hydration use `skipFirstWrite: true`:
397397
@@ -592,7 +592,8 @@ packages/renderer/src/
592592
├── expression-evaluator.js Shared expression evaluation
593593
├── engines/native/server.js ServerRenderer — AST -> HTML string
594594
├── engines/native/renderer.js Renderer — hydrateMarkers(), hydrateAttributes()
595-
├── engines/native/reactive-data.js bindAttribute / bindTextExpression with skipFirstWrite
595+
├── engines/native/attribute-binding.js bindAttribute (attribute-position skipFirstWrite)
596+
│ text-position hydrate lives in blocks/expression.js
596597
└── engines/native/blocks/
597598
└── each.js each.hydrate (eager adoptServerItems), item proxy
598599

0 commit comments

Comments
 (0)