@lark-apaas/coding-registry-buildless 0.1.17 → 0.1.18-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-registry-buildless",
3
- "version": "0.1.17",
3
+ "version": "0.1.18-beta.0",
4
4
  "description": "Miaoda registry (buildless runtime) — shadcn-style copy-in SDKs (globals + CDN-pinned, browser Babel) for buildless static HTML stacks",
5
5
  "type": "module",
6
6
  "files": [
@@ -130,6 +130,28 @@
130
130
  // Slide-authored controls that should keep a tap instead of it navigating.
131
131
  const INTERACTIVE_SEL = 'a[href], button, input, select, textarea, summary, label, video[controls], audio[controls], [role="button"], [onclick], [tabindex]:not([tabindex^="-"]), [contenteditable]:not([contenteditable="false" i])';
132
132
 
133
+ /* Custom element families whose thumbnail clones keep their tag instead of
134
+ * being neutered (see _materialize).
135
+ *
136
+ * A whitelist rather than repairing the replacement <div>: rewriting the tag
137
+ * drops every selector keyed on it at once — the type selector plus any
138
+ * combinator naming it, and cd-elements styles its elements exactly that way
139
+ * (`cd-image{display:contents}`) — so copying declarations back onto the div
140
+ * is unbounded and only ever partially right. Restoring `display` alone leaves
141
+ * `my-cols{display:grid;grid-template-columns:1fr 1fr}` a one-column grid.
142
+ *
143
+ * Admission needs both, since a whitelisted clone mounts once per thumbnail:
144
+ * 1. rendered content sits in the element's own light DOM, so cloneNode
145
+ * already captured it and re-mounting adds nothing;
146
+ * 2. mounting is idempotent and side-effect-free.
147
+ * cd-* qualifies: <cd-image> inlines a real <img> carrying the computed
148
+ * src/srcset, and render() is an idempotent attribute pass over an <img> it
149
+ * reuses. It costs one thing — render() also resets that img's `sizes`,
150
+ * discarding the rail-width hint _materialize sets; the URL still matches the
151
+ * slide's, so the thumb reuses the slide's decoded bitmap. */
152
+ const KEEP_LIVE_PREFIXES = ['cd-'];
153
+ const keepsCloneLive = (el) => KEEP_LIVE_PREFIXES.some((p) => el.localName.startsWith(p));
154
+
133
155
  // Mirrors the host platform's isMobile() (UA regex + iPad maxTouchPoints);
134
156
  // the product iframe is cross-origin, so the shared helper can't be
135
157
  // imported. Deliberately NOT viewport-based: the desktop workbench renders
@@ -865,16 +887,22 @@
865
887
  }
866
888
  });
867
889
  }, { root: this._rail, rootMargin: '400px 0px' });
868
- // Tweaks typically change CSS vars / attrs OUTSIDE <deck-stage>
869
- // (on <html>, <body>, a wrapper div, or a <style> tag), which
870
- // _liveObserver can't see. Re-snapshot author CSS (constructable
871
- // sheet is shared by reference, so one replaceSync updates every
872
- // thumb shadow root) and re-sync each thumb host's attrs + custom
873
- // properties. In-slide DOM mutations are _liveObserver's job.
874
- // Debounced so slider drags don't thrash.
875
- this._onTweakChange = () => {
876
- clearTimeout(this._tweakTimer);
877
- this._tweakTimer = setTimeout(() => {
890
+ // Whenever author CSS changes anywhere outside the slides, the thumbnail
891
+ // clones need it re-snapshotted: they read author styles through a
892
+ // separate constructable sheet, not through the document. _liveObserver
893
+ // only watches the slides, so it never sees a rule written on <html>,
894
+ // <body>, a wrapper div, or a <style>/<link> in head. Re-snapshot (the
895
+ // sheet is shared by reference, so one replaceSync updates every thumb
896
+ // shadow root), then re-sync each thumb host's attrs + custom properties
897
+ // — a freshly arrived sheet can define vars the mirrored values predate.
898
+ // In-slide DOM mutations stay _liveObserver's job.
899
+ //
900
+ // Every trigger shares this one debounce timer on purpose: two triggers
901
+ // landing together must collapse into ONE resync, or it costs two
902
+ // replaceSyncs plus two full passes of var mirroring across every thumb.
903
+ this._resyncAuthorCss = () => {
904
+ clearTimeout(this._authorCssTimer);
905
+ this._authorCssTimer = setTimeout(() => {
878
906
  this._snapshotAuthorCss();
879
907
  // One getComputedStyle for the whole batch — each
880
908
  // getPropertyValue read below reuses the same computed style
@@ -885,7 +913,42 @@
885
913
  });
886
914
  }, 120);
887
915
  };
916
+ // Trigger: the TweaksPanel's sliders/toggles, which write CSS vars
917
+ // outside <deck-stage>. Debouncing is what keeps a slider drag from
918
+ // thrashing the resync.
919
+ this._onTweakChange = () => this._resyncAuthorCss();
888
920
  window.addEventListener('tweakchange', this._onTweakChange);
921
+ // A <style>/<link> reaching document.head after the snapshot below has to
922
+ // re-enter it. That snapshot runs while <deck-stage> upgrades, which can
923
+ // precede a deferred or CDN-hosted SDK script injecting a rule the clones
924
+ // need — otherwise whichever <script src> won the race decides whether
925
+ // thumbnails are faithful. cd-elements is the live case: the first
926
+ // <cd-image>'s connectedCallback is what puts `cd-image{display:contents}`
927
+ // in head, and a KEEP_LIVE_PREFIXES clone that misses it renders as an
928
+ // unstyled `display:inline` element.
929
+ // Trigger: a sheet joining or leaving head. Same resync as any other
930
+ // author-CSS change, so it goes through the shared path above — no
931
+ // re-clone needed, and a tweak arriving alongside it collapses into one.
932
+ const restyling = (nodes) => {
933
+ for (const n of nodes) {
934
+ // The print-freeze <style> is head churn this component causes itself,
935
+ // carrying nothing a thumbnail needs.
936
+ if (n === this._freezeStyle) continue;
937
+ if (n.tagName === 'STYLE' || n.tagName === 'LINK') return true;
938
+ }
939
+ return false;
940
+ };
941
+ this._headObserver = new MutationObserver((records) => {
942
+ // Removals matter too: a theme switcher swapping one <link> for another
943
+ // would otherwise leave the dropped sheet's rules in the snapshot.
944
+ for (const r of records) {
945
+ if (restyling(r.addedNodes) || restyling(r.removedNodes)) {
946
+ this._resyncAuthorCss();
947
+ return;
948
+ }
949
+ }
950
+ });
951
+ this._headObserver.observe(document.head, { childList: true });
889
952
  this._snapshotAuthorCss();
890
953
  // Build the rail now that it's enabled — slotchange already fired,
891
954
  // so _renderRail's early-return skipped the initial build.
@@ -1081,7 +1144,7 @@
1081
1144
  if (this._dragGhost) { this._dragGhost.remove(); this._dragGhost = null; }
1082
1145
  this.removeEventListener('click', this._onTap);
1083
1146
  if (this._liveTimer) clearTimeout(this._liveTimer);
1084
- if (this._tweakTimer) clearTimeout(this._tweakTimer);
1147
+ if (this._authorCssTimer) clearTimeout(this._authorCssTimer);
1085
1148
  if (this._railAnimTimer) clearTimeout(this._railAnimTimer);
1086
1149
  if (this._notesPersistTimer) clearTimeout(this._notesPersistTimer);
1087
1150
  clearTimeout(this._mobUiTimer);
@@ -1090,6 +1153,7 @@
1090
1153
  if (this._scaleRaf) cancelAnimationFrame(this._scaleRaf);
1091
1154
  if (this._liveObserver) this._liveObserver.disconnect();
1092
1155
  if (this._railObserver) this._railObserver.disconnect();
1156
+ if (this._headObserver) this._headObserver.disconnect();
1093
1157
  (this._thumbs || []).forEach((t) => clearTimeout(t._canvasRecapTimer));
1094
1158
  if (this._onTweakChange) window.removeEventListener('tweakchange', this._onTweakChange);
1095
1159
  }
@@ -2377,7 +2441,8 @@
2377
2441
  });
2378
2442
  // Custom elements inside the slide would have their
2379
2443
  // connectedCallback fire when the clone is appended. Replace them
2380
- // with inert boxes so a component-heavy deck doesn't run N copies
2444
+ // with inert boxes — except the KEEP_LIVE_PREFIXES families — so a
2445
+ // component-heavy deck doesn't run N copies
2381
2446
  // of each component's mount logic in the rail. Children are
2382
2447
  // preserved so layout-wrapper elements (<my-column><h2>…</h2>)
2383
2448
  // still show their authored content; the querySelectorAll NodeList
@@ -2400,12 +2465,13 @@
2400
2465
  while (el.firstChild) box.appendChild(el.firstChild);
2401
2466
  return box;
2402
2467
  };
2468
+ const needsNeuter = (el) => el.localName.includes('-') && !keepsCloneLive(el);
2403
2469
  // querySelectorAll('*') returns descendants only — a custom-element
2404
2470
  // slide root (<my-slide>…</my-slide>) would slip through and upgrade
2405
2471
  // on append. Swap the root first.
2406
- if (clone.tagName.includes('-')) clone = neuter(clone);
2472
+ if (needsNeuter(clone)) clone = neuter(clone);
2407
2473
  clone.querySelectorAll('*').forEach((el) => {
2408
- if (el.tagName.includes('-')) el.replaceWith(neuter(el));
2474
+ if (needsNeuter(el)) el.replaceWith(neuter(el));
2409
2475
  });
2410
2476
  clone.style.cssText += ';position:absolute;top:0;left:0;transform-origin:0 0;' +
2411
2477
  'pointer-events:none;width:' + dw + 'px;height:' + dh + 'px;' +