Skip to content

feat: add shadow modes, implicit slots, and :_wc per-host state to dmWc - #145

Merged
dadhi merged 10 commits into
mainfrom
copilot/improve-m-wc
Aug 4, 2026
Merged

feat: add shadow modes, implicit slots, and :_wc per-host state to dmWc#145
dadhi merged 10 commits into
mainfrom
copilot/improve-m-wc

Conversation

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

WC instances need per-host state, shadow DOM opt-in, and slot projection. Today, WC internal state must live in a parallel Map<host, state> or pollute the page signal store — both wrong for instance-scoped data.

Shadow modes

Mods on data-m-wc control shadow attachment. Light DOM remains the default.

<template data-m-wc^dom.open="my-card">...</template>
<template data-m-wc^dom.closed="my-panel">...</template>

wireItClone and prop dispatch updated for shadow-root correctness.

Implicit slots (light DOM)

If the template contains <slot>, host children are projected on connect — no opt-in attribute needed. Named slots, default slot, and fallback content all work.

<template data-m-wc="my-card">
  <header><slot name="title">fallback</slot></header>
  <main><slot></slot></main>
</template>
<my-card><span slot="title">Hello</span><p>Body</p></my-card>

:_wc per-host signal path

A special signal root that resolves to host._wc instead of _dm. Per-instance state is GC'd with the element. All existing mods (^inc, ^rw, ^merge, etc.) compose unchanged.

<my-counter data-m-si:_wc='{"count":0}'>
  <button data-m-ex:_wc.count^inc@.click>+</button>
  <span data-m-ex:.@_wc.count></span>
</my-counter>
  • getSiVal, setSiAndNotifySubs, addTrSub, removeSiSub branch on root === '_wc'
  • Per-host subscriptions stored in a WeakMap (_wcSubs)
  • Public helpers: dmGetHost(host, path) / dmSetHost(host, path, val)

Copilot AI linked an issue Jul 1, 2026 that may be closed by this pull request
- Shadow modes: ^dom.open / ^dom.closed mods on data-m-wc
- Slots: implicit projection in light DOM when template has <slot>
- :_wc signal path root: per-host state lives on host._wc
- Public helpers: dmGetHost(host, path) / dmSetHost(host, path, val)
- wireItClone now handles ShadowRoot nodes
- WC prop dispatch works into shadow root children

Closes #142
Copilot AI changed the title [WIP] Improve m-wc by adding shadow modes, slots, and per-host state feat: add shadow modes, implicit slots, and :_wc per-host state to dmWc Jul 1, 2026
Copilot finished work on behalf of dadhi July 1, 2026 12:51
Copilot AI requested a review from dadhi July 1, 2026 12:51

@dadhi dadhi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot What about the examples, uPlot and Style in index.html and style. Can we use the new functionality to simplify and make those examples more declararive, compact and less js-scripty?

Also I did a similar work + support for the explicit --css-var handling in the separate session and attaching the result patch here. Please analyze it and get the best and more complete version of your changes and the attached changes in this pr.

[
dmax-issue142-patch.tar.gz
](url)

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot What about the examples, uPlot and Style in index.html and style. Can we use the new functionality to simplify and make those examples more declararive, compact and less js-scripty?

Also I d...

Merged your patch (commit feat: merge patch - unified WC host resolution, shadow panel CSS, better tests). Key changes adopted from your session:

  • resolveWcHost walks through shadow boundaries (.host) for correct ancestor lookup
  • getWcSubsFor walks both up and down the host subtree for complete subscription notification
  • Unified setSiAndNotifySubs handles both _wc and normal signals in one code path (removed my duplicated WC branch)
  • dm-style.js panel uses ^dom.open shadow mode with panelShadowCss() (~600 chars saved vs per-host interpolation)
  • dmHostScope.e2e.js replaces dmWcV2.e2e.js with comprehensive tests (shadow open/closed, slots, :_wc isolation, ^merge/^rw composition, CSS custom props)
  • dmStyle.e2e.js added for style panel coverage
  • Fixed m-ex-cel.e2e.js to query through .shadowRoot now that the panel uses shadow DOM

All tests pass (92 headless + all e2e suites).

Copilot finished work on behalf of dadhi July 1, 2026 13:45
Copilot AI requested a review from dadhi July 1, 2026 13:45

@dadhi dadhi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot let do a code review pass one more. Let ensure we are reusing the code for both internal and external purposes. We do not have an unecessary fallbacks. We are dogfooding and evaluating feedback from our examples. We do nog have ubbecessary checks and we inlining code of the small helpers if it make it shorter. We are to the explicit, declarative, concise, orthogobal, glavally applicable principles of the dmax, we have covered combinations with tests and fuzzing.

Copilot AI commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author

@copilot let do a code review pass one more. Let ensure we are reusing the code for both internal and external purposes. We do not have an unecessary fallbacks. We are dogfooding and evaluating feedba...

Done in refactor: inline small helpers, remove redundant up-walk, simplify _wc API, add fuzz coverage.

Changes:

  • Inlined small helpers: getHostEl (was just resolveWcHost), getHostStore (used once), resolveWcMods (used once), parseWcPath (over-engineered for dmGetHost/dmSetHost)
  • Removed redundant up-walk in getWcSubsFor — subscriptions are keyed on the resolved host, so walking ancestors collected nothing useful; now only walks the host + descendants
  • Unified removeSiSub — both _wc and normal branches had identical splice logic; collapsed into one expression
  • Added :_wc fuzz coverage — 8 new deterministic cases exercising ^inc, ^dec, ^merge, ^rw, read, write, style, and deep-path combos on per-host signals

Net: 2056→2006 lines (−50), all 386 fuzz + 92 headless + all e2e suites pass.

Copilot finished work on behalf of dadhi July 1, 2026 17:03
Copilot AI requested a review from dadhi July 1, 2026 17:03

@dadhi dadhi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot Let do a last pass for perf and memory. Let run benchmarks to find any regressions a d fix them and try to win both ds and fixy.

Copilot finished work on behalf of dadhi July 1, 2026 18:08
Copilot AI requested a review from dadhi July 1, 2026 18:08

@dadhi dadhi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot critically assess the pr changes, critique the code size inflation, point to the consistency gaps and missing opportunities

…-piercing dmSel (-55 lines, -2.3KB)

Co-authored-by: dadhi <39516+dadhi@users.noreply.github.com>

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

@copilot critically assess the pr changes, critique the code size inflation, point to the consistency gaps and missing opportunities

Critical assessment and fixes applied in e7aa6e8.

Code size inflation (was 1818→2007, +189 lines):

  • setSiRaw (24 lines) duplicated the deep-path walk already in setSiAndNotifySubs — replaced with 6-line initWcStore that only handles init (no notification needed)
  • dmSel/dmSelAll shadow-piercing + splitCompound (24 lines) — recursive querySelectorAll('*') on every call is an O(n) perf regression with zero test coverage; reverted to original one-liners
  • M_DOM_OPEN/M_DOM_CLOSED constants used once each — inlined
  • projectSlots reverse loop — replaced with Array.from(host.children)
  • dmGetHost/dmSetHost had 2 unnecessary regexes stripping _wc. prefix

Perf issues fixed:

  • IIFE (() => {...})() on every normal signal write in setSiAndNotifySubs — allocated a closure just to null-check _subs.get(), replaced with || NIL
  • getWcSubsFor created a new Set() on every _wc write for dedup that can't happen (subs are keyed per-element, each array is unique)

Consistency gaps:

  • dmSi used setSiRaw for _wc but _dm.set for normal — both are "write without notification" but implemented differently; now both bypass notification consistently

Remaining concerns:

  • resolveWcHost DOM walk on every getSiVal call (hot render path) — could cache host on the subscription itself
  • getWcSubsFor still walks the full host subtree on every write — proportional to WC tree size
  • isWcRoot scattered across 9 call sites adds branching to every signal read/write even when _wc is unused

Net: 2007→1952 lines (−55), 102207→99872 bytes (−2335). All tests pass.

Copilot AI requested a review from dadhi August 4, 2026 21:30
@dadhi
dadhi marked this pull request as ready for review August 4, 2026 21:49
@dadhi
dadhi merged commit a374f29 into main Aug 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve m-wc

2 participants