@mhmo91/schmancy 0.10.10 → 0.10.11
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/dist/agent/schmancy.agent.js.map +1 -1
- package/dist/handover/agent-runtime-followups.md +1 -1
- package/dist/handover/agent-runtime-v1.md +3 -3
- package/dist/handover/claude-design-brief.md +86 -46
- package/dist/handover/claude-design-setup.md +11 -7
- package/dist/handover/schmancy-token-reference.md +12 -6
- package/dist/skills/INDEX.md +1 -1
- package/dist/skills/SKILL.md +7 -6
- package/dist/skills/audio.md +1 -1
- package/dist/skills/discovery.md +3 -3
- package/dist/skills/menu.md +1 -1
- package/dist/skills/overlay.md +1 -1
- package/dist/skills/schmancy/INDEX.md +1 -1
- package/dist/skills/schmancy/SKILL.md +7 -6
- package/dist/skills/schmancy/audio.md +1 -1
- package/dist/skills/schmancy/discovery.md +3 -3
- package/dist/skills/schmancy/menu.md +1 -1
- package/dist/skills/schmancy/overlay.md +1 -1
- package/dist/skills/schmancy/state.md +42 -22
- package/dist/skills/state.md +42 -22
- package/dist/state-BusMG6sM.js.map +1 -1
- package/dist/state-DNdCPITt.cjs.map +1 -1
- package/package.json +1 -1
- package/skills/schmancy/INDEX.md +1 -1
- package/skills/schmancy/SKILL.md +7 -6
- package/skills/schmancy/audio.md +1 -1
- package/skills/schmancy/discovery.md +3 -3
- package/skills/schmancy/menu.md +1 -1
- package/skills/schmancy/overlay.md +1 -1
- package/skills/schmancy/state.md +42 -22
- package/src/CLAUDE.md +112 -354
- package/src/state/CLAUDE.md +26 -18
- package/src/state/SCOPING.md +23 -9
|
@@ -29,8 +29,8 @@ cart.replace({ items: [], total: 0 })
|
|
|
29
29
|
cart.update(d => { d.items.push(item) }) // immer
|
|
30
30
|
cart.delete('total')
|
|
31
31
|
|
|
32
|
-
// Subscribe in a
|
|
33
|
-
class CartView extends
|
|
32
|
+
// Subscribe in a SchmancyElement — direct read auto-tracks via SignalWatcher
|
|
33
|
+
class CartView extends SchmancyElement {
|
|
34
34
|
render() { return html`Items: ${cart.value.items.length}` }
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -207,18 +207,18 @@ with zero ceremony.
|
|
|
207
207
|
|
|
208
208
|
### (1) Default — direct read inside `render()`
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
`SchmancyElement` composes `SignalWatcher` from `@lit-labs/signals`.
|
|
211
211
|
Every signal read inside `render()` auto-tracks; the host re-renders
|
|
212
212
|
on change. No decorator, no field, no binding code.
|
|
213
213
|
|
|
214
214
|
```ts
|
|
215
|
-
import {
|
|
215
|
+
import { html } from 'lit'
|
|
216
216
|
import { customElement } from 'lit/decorators.js'
|
|
217
|
-
import {
|
|
218
|
-
import { cart } from './cart.
|
|
217
|
+
import { SchmancyElement } from '@mhmo91/schmancy/mixins'
|
|
218
|
+
import { cart } from './cart.state'
|
|
219
219
|
|
|
220
220
|
@customElement('cart-view')
|
|
221
|
-
export class CartView extends
|
|
221
|
+
export class CartView extends SchmancyElement {
|
|
222
222
|
render() {
|
|
223
223
|
return html`<span>Items: ${cart.value.items.length}</span>`
|
|
224
224
|
}
|
|
@@ -243,7 +243,7 @@ derived methods, DevTools inspection, or readability:
|
|
|
243
243
|
import { observe } from '@mhmo91/schmancy/state'
|
|
244
244
|
|
|
245
245
|
@customElement('cart-view')
|
|
246
|
-
export class CartView extends
|
|
246
|
+
export class CartView extends SchmancyElement {
|
|
247
247
|
@observe(cart) cart!: CartState
|
|
248
248
|
|
|
249
249
|
onClick() {
|
|
@@ -268,12 +268,12 @@ works under the existing tsconfig with no migration.
|
|
|
268
268
|
|
|
269
269
|
### (3) `bindState(host, source)` — imperative form
|
|
270
270
|
|
|
271
|
-
For hosts that aren't
|
|
271
|
+
For hosts that aren't `SchmancyElement` subclasses (rare):
|
|
272
272
|
|
|
273
273
|
```ts
|
|
274
274
|
import { bindState } from '@mhmo91/schmancy/state'
|
|
275
275
|
|
|
276
|
-
class CustomHost extends LitElement { // not a
|
|
276
|
+
class CustomHost extends LitElement { // not a SchmancyElement subclass
|
|
277
277
|
cart = bindState(this, cart)
|
|
278
278
|
render() {
|
|
279
279
|
return html`<span>Items: ${this.cart.value.items.length}</span>`
|
|
@@ -376,7 +376,7 @@ two side-by-side checkout flows, an `<iframe>`-like wizard, an
|
|
|
376
376
|
embedded preview — wrap the subtree in `<schmancy-context>`.
|
|
377
377
|
|
|
378
378
|
```ts
|
|
379
|
-
class App extends
|
|
379
|
+
class App extends SchmancyElement {
|
|
380
380
|
render() {
|
|
381
381
|
return html`
|
|
382
382
|
<schmancy-context .provides=${[cart]}>
|
|
@@ -402,7 +402,7 @@ after an `await fetch(...)` — all of it auto-resolves to the right
|
|
|
402
402
|
instance based on tree position.
|
|
403
403
|
|
|
404
404
|
```ts
|
|
405
|
-
class CartView extends
|
|
405
|
+
class CartView extends SchmancyElement {
|
|
406
406
|
render() {
|
|
407
407
|
return html`
|
|
408
408
|
<button @click=${() => cart.set({ total: 0 })}>Clear</button>
|
|
@@ -418,22 +418,42 @@ class CartView extends $LitElement() {
|
|
|
418
418
|
}
|
|
419
419
|
```
|
|
420
420
|
|
|
421
|
-
Coverage of the call paths is provided by `SchmancyElement
|
|
421
|
+
Coverage of the call paths is provided by `SchmancyElement` and
|
|
422
|
+
`<schmancy-context>`:
|
|
422
423
|
|
|
423
|
-
- `render()` and every Lit lifecycle hook — wrapped at construction
|
|
424
|
+
- `render()` and every Lit lifecycle hook — wrapped at construction
|
|
425
|
+
via the `_activeHost.run(host, fn)` stack.
|
|
424
426
|
- Class methods (`handleAdd`, `handleSubmit`) — wrapped at construction.
|
|
425
|
-
- `await` continuations inside class methods — propagated via the
|
|
426
|
-
`Promise.then` patch in `state/active-host.ts`.
|
|
427
427
|
- `addEventListener(type, fn)` on the host — wrapped (and unwrapped on
|
|
428
428
|
`removeEventListener`).
|
|
429
|
-
-
|
|
430
|
-
via the `
|
|
431
|
-
`
|
|
429
|
+
- Explicit `.then(...)` continuations off a class-method Promise —
|
|
430
|
+
propagated via the `Promise.prototype.then` patch in
|
|
431
|
+
`state/active-host.ts`, which captures the active host at chain time
|
|
432
|
+
and restores it inside the callback.
|
|
433
|
+
- Inline arrow handlers in templates (`@click=${() => …}`) and any
|
|
434
|
+
other DOM event handler attached inside a `<schmancy-context>`
|
|
435
|
+
subtree — resolved via the capture-phase event listener that
|
|
436
|
+
`<schmancy-context>` installs on itself for ~18 common event types.
|
|
437
|
+
The listener publishes the event's target as the active host through
|
|
438
|
+
the `_publishEventHost(node)` slot for the duration of the
|
|
439
|
+
synchronous handler chain (slot self-clears in the next microtask).
|
|
440
|
+
|
|
441
|
+
**Known limitation — native `await` on a native Promise.** V8's await
|
|
442
|
+
optimization (since 7.x) skips the spec-prescribed
|
|
443
|
+
`Promise.resolve(x).then(continuation)` step, so the `Promise.then`
|
|
444
|
+
patch does not see the resumption. Class methods that mutate state
|
|
445
|
+
across an `await` boundary fall back to the module-scoped global, not
|
|
446
|
+
the active-host's isolated copy. To preserve the host across awaits,
|
|
447
|
+
either keep the mutation in the synchronous prelude before the first
|
|
448
|
+
`await`, or chain explicitly with `.then(...)` (which still routes
|
|
449
|
+
through the patched method). A real fix requires either a build-time
|
|
450
|
+
async-function transform or native `AsyncContext.Variable` in the
|
|
451
|
+
runtime.
|
|
432
452
|
|
|
433
453
|
Pure async callbacks with no DOM origin — a websocket `onmessage`,
|
|
434
|
-
`setInterval` with no triggering user event — fall through to
|
|
435
|
-
module-scoped global. That is the correct semantic: those
|
|
436
|
-
have no tree position to resolve to.
|
|
454
|
+
`setInterval` with no triggering user event — also fall through to
|
|
455
|
+
the module-scoped global. That is the correct semantic: those
|
|
456
|
+
callbacks have no tree position to resolve to.
|
|
437
457
|
|
|
438
458
|
### Lifecycle
|
|
439
459
|
|
package/dist/skills/state.md
CHANGED
|
@@ -29,8 +29,8 @@ cart.replace({ items: [], total: 0 })
|
|
|
29
29
|
cart.update(d => { d.items.push(item) }) // immer
|
|
30
30
|
cart.delete('total')
|
|
31
31
|
|
|
32
|
-
// Subscribe in a
|
|
33
|
-
class CartView extends
|
|
32
|
+
// Subscribe in a SchmancyElement — direct read auto-tracks via SignalWatcher
|
|
33
|
+
class CartView extends SchmancyElement {
|
|
34
34
|
render() { return html`Items: ${cart.value.items.length}` }
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -207,18 +207,18 @@ with zero ceremony.
|
|
|
207
207
|
|
|
208
208
|
### (1) Default — direct read inside `render()`
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
`SchmancyElement` composes `SignalWatcher` from `@lit-labs/signals`.
|
|
211
211
|
Every signal read inside `render()` auto-tracks; the host re-renders
|
|
212
212
|
on change. No decorator, no field, no binding code.
|
|
213
213
|
|
|
214
214
|
```ts
|
|
215
|
-
import {
|
|
215
|
+
import { html } from 'lit'
|
|
216
216
|
import { customElement } from 'lit/decorators.js'
|
|
217
|
-
import {
|
|
218
|
-
import { cart } from './cart.
|
|
217
|
+
import { SchmancyElement } from '@mhmo91/schmancy/mixins'
|
|
218
|
+
import { cart } from './cart.state'
|
|
219
219
|
|
|
220
220
|
@customElement('cart-view')
|
|
221
|
-
export class CartView extends
|
|
221
|
+
export class CartView extends SchmancyElement {
|
|
222
222
|
render() {
|
|
223
223
|
return html`<span>Items: ${cart.value.items.length}</span>`
|
|
224
224
|
}
|
|
@@ -243,7 +243,7 @@ derived methods, DevTools inspection, or readability:
|
|
|
243
243
|
import { observe } from '@mhmo91/schmancy/state'
|
|
244
244
|
|
|
245
245
|
@customElement('cart-view')
|
|
246
|
-
export class CartView extends
|
|
246
|
+
export class CartView extends SchmancyElement {
|
|
247
247
|
@observe(cart) cart!: CartState
|
|
248
248
|
|
|
249
249
|
onClick() {
|
|
@@ -268,12 +268,12 @@ works under the existing tsconfig with no migration.
|
|
|
268
268
|
|
|
269
269
|
### (3) `bindState(host, source)` — imperative form
|
|
270
270
|
|
|
271
|
-
For hosts that aren't
|
|
271
|
+
For hosts that aren't `SchmancyElement` subclasses (rare):
|
|
272
272
|
|
|
273
273
|
```ts
|
|
274
274
|
import { bindState } from '@mhmo91/schmancy/state'
|
|
275
275
|
|
|
276
|
-
class CustomHost extends LitElement { // not a
|
|
276
|
+
class CustomHost extends LitElement { // not a SchmancyElement subclass
|
|
277
277
|
cart = bindState(this, cart)
|
|
278
278
|
render() {
|
|
279
279
|
return html`<span>Items: ${this.cart.value.items.length}</span>`
|
|
@@ -376,7 +376,7 @@ two side-by-side checkout flows, an `<iframe>`-like wizard, an
|
|
|
376
376
|
embedded preview — wrap the subtree in `<schmancy-context>`.
|
|
377
377
|
|
|
378
378
|
```ts
|
|
379
|
-
class App extends
|
|
379
|
+
class App extends SchmancyElement {
|
|
380
380
|
render() {
|
|
381
381
|
return html`
|
|
382
382
|
<schmancy-context .provides=${[cart]}>
|
|
@@ -402,7 +402,7 @@ after an `await fetch(...)` — all of it auto-resolves to the right
|
|
|
402
402
|
instance based on tree position.
|
|
403
403
|
|
|
404
404
|
```ts
|
|
405
|
-
class CartView extends
|
|
405
|
+
class CartView extends SchmancyElement {
|
|
406
406
|
render() {
|
|
407
407
|
return html`
|
|
408
408
|
<button @click=${() => cart.set({ total: 0 })}>Clear</button>
|
|
@@ -418,22 +418,42 @@ class CartView extends $LitElement() {
|
|
|
418
418
|
}
|
|
419
419
|
```
|
|
420
420
|
|
|
421
|
-
Coverage of the call paths is provided by `SchmancyElement
|
|
421
|
+
Coverage of the call paths is provided by `SchmancyElement` and
|
|
422
|
+
`<schmancy-context>`:
|
|
422
423
|
|
|
423
|
-
- `render()` and every Lit lifecycle hook — wrapped at construction
|
|
424
|
+
- `render()` and every Lit lifecycle hook — wrapped at construction
|
|
425
|
+
via the `_activeHost.run(host, fn)` stack.
|
|
424
426
|
- Class methods (`handleAdd`, `handleSubmit`) — wrapped at construction.
|
|
425
|
-
- `await` continuations inside class methods — propagated via the
|
|
426
|
-
`Promise.then` patch in `state/active-host.ts`.
|
|
427
427
|
- `addEventListener(type, fn)` on the host — wrapped (and unwrapped on
|
|
428
428
|
`removeEventListener`).
|
|
429
|
-
-
|
|
430
|
-
via the `
|
|
431
|
-
`
|
|
429
|
+
- Explicit `.then(...)` continuations off a class-method Promise —
|
|
430
|
+
propagated via the `Promise.prototype.then` patch in
|
|
431
|
+
`state/active-host.ts`, which captures the active host at chain time
|
|
432
|
+
and restores it inside the callback.
|
|
433
|
+
- Inline arrow handlers in templates (`@click=${() => …}`) and any
|
|
434
|
+
other DOM event handler attached inside a `<schmancy-context>`
|
|
435
|
+
subtree — resolved via the capture-phase event listener that
|
|
436
|
+
`<schmancy-context>` installs on itself for ~18 common event types.
|
|
437
|
+
The listener publishes the event's target as the active host through
|
|
438
|
+
the `_publishEventHost(node)` slot for the duration of the
|
|
439
|
+
synchronous handler chain (slot self-clears in the next microtask).
|
|
440
|
+
|
|
441
|
+
**Known limitation — native `await` on a native Promise.** V8's await
|
|
442
|
+
optimization (since 7.x) skips the spec-prescribed
|
|
443
|
+
`Promise.resolve(x).then(continuation)` step, so the `Promise.then`
|
|
444
|
+
patch does not see the resumption. Class methods that mutate state
|
|
445
|
+
across an `await` boundary fall back to the module-scoped global, not
|
|
446
|
+
the active-host's isolated copy. To preserve the host across awaits,
|
|
447
|
+
either keep the mutation in the synchronous prelude before the first
|
|
448
|
+
`await`, or chain explicitly with `.then(...)` (which still routes
|
|
449
|
+
through the patched method). A real fix requires either a build-time
|
|
450
|
+
async-function transform or native `AsyncContext.Variable` in the
|
|
451
|
+
runtime.
|
|
432
452
|
|
|
433
453
|
Pure async callbacks with no DOM origin — a websocket `onmessage`,
|
|
434
|
-
`setInterval` with no triggering user event — fall through to
|
|
435
|
-
module-scoped global. That is the correct semantic: those
|
|
436
|
-
have no tree position to resolve to.
|
|
454
|
+
`setInterval` with no triggering user event — also fall through to
|
|
455
|
+
the module-scoped global. That is the correct semantic: those
|
|
456
|
+
callbacks have no tree position to resolve to.
|
|
437
457
|
|
|
438
458
|
### Lifecycle
|
|
439
459
|
|