@lark-apaas/coding-registry-buildless 0.1.5 → 0.1.6
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 +1 -1
- package/scripts/deck-stage.js +78 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-registry-buildless",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
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": [
|
package/scripts/deck-stage.js
CHANGED
|
@@ -38,6 +38,10 @@
|
|
|
38
38
|
* a scrollable vertical card list (browse) and a rotated fullscreen
|
|
39
39
|
* presenting mode. This needs NO authoring changes: never hand-roll
|
|
40
40
|
* mobile/responsive slide layouts. Desktop behaviour is unchanged.
|
|
41
|
+
* Browse mode renders authored CSS animations/transitions at their
|
|
42
|
+
* end state (presenting keeps them) — design animations so the end
|
|
43
|
+
* state is the complete slide; never gate content visibility on an
|
|
44
|
+
* animation having played.
|
|
41
45
|
*
|
|
42
46
|
* Slides are HIDDEN, not unmounted. Non-active slides stay in the DOM with
|
|
43
47
|
* `visibility: hidden` + `opacity: 0`, so their state (videos, iframes,
|
|
@@ -698,7 +702,18 @@
|
|
|
698
702
|
// slide 1, so ?thumbnail=1 alone yields a clean first-slide frame.
|
|
699
703
|
this._snthumb = /[?&](_snthumb|thumbnail)=/.test(location.search);
|
|
700
704
|
this._mobile = isMobileUA();
|
|
705
|
+
// Layout-driven size signal. window.resize is unreliable inside
|
|
706
|
+
// prerendered/reframed iOS WebViews and host-managed iframes — the
|
|
707
|
+
// frame can change size without any resize event, leaving a layout
|
|
708
|
+
// computed at a bogus early width in place forever. ResizeObserver
|
|
709
|
+
// fires from layout itself, so it catches every real size change.
|
|
710
|
+
// Mobile-only: desktop windows deliver resize dependably.
|
|
711
|
+
if (this._mobile && typeof ResizeObserver !== 'undefined') {
|
|
712
|
+
this._mobSizeRo = new ResizeObserver(() => this._onResize());
|
|
713
|
+
this._mobSizeRo.observe(document.documentElement);
|
|
714
|
+
}
|
|
701
715
|
this._syncMobileMode();
|
|
716
|
+
if (this._mobile) this._syncMobFreezeRule();
|
|
702
717
|
if (this._snthumb) this.setAttribute('no-rail', '');
|
|
703
718
|
this._render();
|
|
704
719
|
this._loadNotes();
|
|
@@ -875,6 +890,20 @@
|
|
|
875
890
|
* pendingOut (when provided) and leave a placeholder until the fetch lands. */
|
|
876
891
|
_collectAuthorCss(pendingOut) {
|
|
877
892
|
return Array.from(document.styleSheets).map((sh) => {
|
|
893
|
+
// Inline <style>: quote the author's source text instead of
|
|
894
|
+
// re-serializing the CSSOM. Chromium serializes a var()-carrying
|
|
895
|
+
// `background` shorthand into EMPTY longhands whenever the same
|
|
896
|
+
// rule also sets any background-* longhand — exactly the
|
|
897
|
+
// gradient-text idiom (background: linear-gradient(var(--accent)…);
|
|
898
|
+
// background-clip: text), whose headline then renders invisible
|
|
899
|
+
// (text-fill stays transparent, gradient gone). Empty textContent
|
|
900
|
+
// falls through to the CSSOM path: CSS-in-JS "speedy" injectors
|
|
901
|
+
// (emotion, styled-components) insertRule() into a blank <style>,
|
|
902
|
+
// so raw text would snapshot them as nothing.
|
|
903
|
+
const node = sh.ownerNode;
|
|
904
|
+
if (node && node.tagName === 'STYLE' && node.textContent.trim()) {
|
|
905
|
+
return node.textContent;
|
|
906
|
+
}
|
|
878
907
|
try {
|
|
879
908
|
return Array.from(sh.cssRules).map((r) => r.cssText).join('\n');
|
|
880
909
|
} catch (e) {
|
|
@@ -906,19 +935,29 @@
|
|
|
906
935
|
const authorCss = rawCss
|
|
907
936
|
// The shadow host is featureless outside the functional :host(...)
|
|
908
937
|
// form, so any compound on :root — [attr], .class, #id, :pseudo —
|
|
909
|
-
// must become :host(<compound>) not :host<compound>.
|
|
910
|
-
//
|
|
911
|
-
//
|
|
912
|
-
// match inside the thumb's shadow tree.
|
|
938
|
+
// must become :host(<compound>) not :host<compound>. :root and
|
|
939
|
+
// :host are both pseudo-classes, so this pair is also
|
|
940
|
+
// specificity-neutral as written.
|
|
913
941
|
.replace(/:root((?:\[[^\]]*\]|[.#][-\w]+|:[-\w]+(?:\([^)]*\))?)+)/g, ':host($1)')
|
|
914
942
|
.replace(/:root\b/g, ':host')
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
//
|
|
943
|
+
// html / deck-stage are TYPE selectors (0,0,1) while :host is a
|
|
944
|
+
// pseudo-class (0,1,0): aliasing to bare :host would silently
|
|
945
|
+
// promote every such rule a whole specificity level and flip the
|
|
946
|
+
// author's cascade — `deck-stage section { background }` written
|
|
947
|
+
// as a low-priority default would beat the per-slide `.slide-xxx`
|
|
948
|
+
// class that overrides it, washing thumbnails out while the stage
|
|
949
|
+
// renders correctly. :where(:host) zeroes the alias and
|
|
950
|
+
// :host(:where(…)) pins compounds at plain pseudo-class level, so
|
|
951
|
+
// a rewritten rule stays within one type-unit of its document
|
|
952
|
+
// specificity and can never jump past a class-level override.
|
|
953
|
+
// (html compounds appear in the wild: Tailwind class-strategy dark
|
|
954
|
+
// mode emits html.dark; Pico uses html[data-theme].)
|
|
955
|
+
.replace(/(^|[\s,>~+(}])html((?:\[[^\]]*\]|[.#][-\w]+|:[-\w]+(?:\([^)]*\))?)+)(?![-\w])/g, '$1:host(:where($2))')
|
|
956
|
+
.replace(/(^|[\s,>~+(}])html(?![-\w])/g, '$1:where(:host)')
|
|
918
957
|
// The negative-lookahead (?![-\w]) keeps `.deck-stage-title` and the
|
|
919
958
|
// `--deck-stage-*` custom-property names untouched.
|
|
920
|
-
.replace(/(^|[\s,>~+(}])deck-stage((?:\[[^\]]*\]|[.#][-\w]+|:[-\w]+(?:\([^)]*\))?)+)(?![-\w])/g, '$1:host($2)')
|
|
921
|
-
.replace(/(^|[\s,>~+(}])deck-stage(?![-\w])/g, '$1:host');
|
|
959
|
+
.replace(/(^|[\s,>~+(}])deck-stage((?:\[[^\]]*\]|[.#][-\w]+|:[-\w]+(?:\([^)]*\))?)+)(?![-\w])/g, '$1:host(:where($2))')
|
|
960
|
+
.replace(/(^|[\s,>~+(}])deck-stage(?![-\w])/g, '$1:where(:host)');
|
|
922
961
|
// Every custom property the author references. _syncThumbHostAttrs
|
|
923
962
|
// mirrors each one's *computed* value at <deck-stage> onto the
|
|
924
963
|
// thumb host so the live value wins over the :host default above
|
|
@@ -977,6 +1016,7 @@
|
|
|
977
1016
|
}
|
|
978
1017
|
|
|
979
1018
|
disconnectedCallback() {
|
|
1019
|
+
if (this._mobSizeRo) { this._mobSizeRo.disconnect(); this._mobSizeRo = null; }
|
|
980
1020
|
window.removeEventListener('keydown', this._onKey);
|
|
981
1021
|
window.removeEventListener('resize', this._onResize);
|
|
982
1022
|
window.removeEventListener('message', this._onMessage);
|
|
@@ -1324,6 +1364,29 @@
|
|
|
1324
1364
|
'animation-play-state: running !important; transition-duration: 0s !important; } }';
|
|
1325
1365
|
}
|
|
1326
1366
|
|
|
1367
|
+
/** Mobile browse (data-mobile-list) force-shows every card at once, so
|
|
1368
|
+
* authored CSS animations all fire together on load — and the ones gated
|
|
1369
|
+
* on [data-deck-active] re-fire as scrolling moves that flag between
|
|
1370
|
+
* cards. Freeze them at their end state while browsing. Same jump-to-end
|
|
1371
|
+
* recipe as the print rule above (NOT animation:none, so a deck that
|
|
1372
|
+
* keeps a hidden base state still renders its content), keyed on the
|
|
1373
|
+
* host attribute so presenting/desktop keep their animations and no JS
|
|
1374
|
+
* toggling is needed. Document-level like the print rule: author
|
|
1375
|
+
* animations style light-DOM slide content that shadow CSS can't reach,
|
|
1376
|
+
* and <deck-stage> itself is a light-DOM element document CSS can key on. */
|
|
1377
|
+
_syncMobFreezeRule() {
|
|
1378
|
+
const id = 'deck-stage-mob-freeze';
|
|
1379
|
+
if (document.getElementById(id)) return;
|
|
1380
|
+
const tag = document.createElement('style');
|
|
1381
|
+
tag.id = id;
|
|
1382
|
+
tag.textContent =
|
|
1383
|
+
'deck-stage[data-mobile-list] *, deck-stage[data-mobile-list] *::before, deck-stage[data-mobile-list] *::after {' +
|
|
1384
|
+
' animation-delay: -99s !important; animation-duration: .001s !important;' +
|
|
1385
|
+
' animation-iteration-count: 1 !important; animation-fill-mode: both !important;' +
|
|
1386
|
+
' animation-play-state: running !important; transition-duration: 0s !important; }';
|
|
1387
|
+
document.head.appendChild(tag);
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1327
1390
|
_onSlotChange() {
|
|
1328
1391
|
// Rail mutations (delete/move) already reconcile synchronously and
|
|
1329
1392
|
// emit slidechange with reason 'api'; skip the async slotchange that
|
|
@@ -1478,6 +1541,12 @@
|
|
|
1478
1541
|
_fitMobileList() {
|
|
1479
1542
|
const dw = this.designWidth, dh = this.designHeight;
|
|
1480
1543
|
const vw = window.innerWidth;
|
|
1544
|
+
// Hosts (in-app WebViews, portal iframes) can run us before our frame
|
|
1545
|
+
// has a real size — committing a layout at a bogus width shrinks every
|
|
1546
|
+
// card to near-zero, and the correcting resize event is NOT guaranteed
|
|
1547
|
+
// to follow on iOS. Bail and let the documentElement ResizeObserver
|
|
1548
|
+
// (registered in connectedCallback) re-run us when layout gets a size.
|
|
1549
|
+
if (!vw || vw < 40) { this._mobLastVw = -1; return; }
|
|
1481
1550
|
this._mobLastVw = vw; // baseline for _onResize's height-only skip
|
|
1482
1551
|
const stage = this._canvas.parentElement;
|
|
1483
1552
|
// Clear the stage inset the desktop _fit() wrote inline.
|