@lark-apaas/coding-registry-buildless 0.1.20 → 0.1.22
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/components/animations.jsx +173 -6
- package/components/design-canvas.jsx +260 -14
- package/components/tweaks-panel.jsx +8 -5
- package/package.json +1 -1
- package/scripts/android-frame.js +253 -0
- package/scripts/browser-window.js +246 -0
- package/scripts/deck-stage.js +176 -6
- package/scripts/design-canvas.js +2189 -0
- package/scripts/ios-frame.js +338 -0
- package/scripts/macos-window.js +260 -0
package/scripts/deck-stage.js
CHANGED
|
@@ -111,11 +111,86 @@
|
|
|
111
111
|
* @media (prefers-reduced-motion:no-preference){ [data-deck-active] .fade-up{ animation: fadeUp .5s both } }
|
|
112
112
|
* @keyframes fadeUp { from{ opacity:0; transform:translateY(24px) } to{ opacity:1; transform:none } }
|
|
113
113
|
* Avoid infinite decorative loops on slide content.
|
|
114
|
+
*
|
|
115
|
+
* window.__miaodaMirror (defined below) is Magic-Share sync plumbing between
|
|
116
|
+
* this component and the miaoda host — internal; do not call it from slide
|
|
117
|
+
* code or remove it.
|
|
114
118
|
*/
|
|
115
119
|
/* END USAGE */
|
|
116
120
|
|
|
117
121
|
(() => {
|
|
118
122
|
if (typeof window === 'undefined') return;
|
|
123
|
+
|
|
124
|
+
// ── Mirror sync singleton ──────────────────────────────────────────────────
|
|
125
|
+
// Shared by every buildless script on the page (first one to load defines
|
|
126
|
+
// it). Inlined per script rather than shipped as its own registry file:
|
|
127
|
+
// call sites use the global bare, so inlining is what guarantees
|
|
128
|
+
// definition-before-use — a separate copy-in file would add a whole "SDK
|
|
129
|
+
// copied but <script> tag missing" failure mode. Keep this block identical
|
|
130
|
+
// across deck-stage.js / design-canvas.jsx / animations.jsx — same
|
|
131
|
+
// convention as the mobile-UA regex. Two halves of
|
|
132
|
+
// the 妙享 (Magic Share) mirror protocol:
|
|
133
|
+
// emit(msg, opts) — stamps msg.__mirror so the host relay treats the
|
|
134
|
+
// message as a broadcastable UI-state snapshot;
|
|
135
|
+
// unstamped messages are never mirrored. opts.key is
|
|
136
|
+
// the sync channel (maps to the share SDK's
|
|
137
|
+
// actionType): late joiners receive only the LATEST
|
|
138
|
+
// snapshot per key, so states that overwrite each
|
|
139
|
+
// other must share one key. opts.throttle is the
|
|
140
|
+
// suggested host-side coalescing interval in ms —
|
|
141
|
+
// snapshots make dropped intermediate frames
|
|
142
|
+
// lossless, so hint it on any continuous gesture.
|
|
143
|
+
// onApply(type, fn) — replay entry for a message type this script itself
|
|
144
|
+
// emits. The host sends {type:'miaoda:mirror:apply',
|
|
145
|
+
// uuid, message, latencyMs} and needs
|
|
146
|
+
// {type:'miaoda:mirror:apply-result', uuid, result}
|
|
147
|
+
// back to drive the SDK's retry: result.type
|
|
148
|
+
// 'failed' means retry (use it for transient
|
|
149
|
+
// not-ready states), 'ignored' is terminal (not
|
|
150
|
+
// applicable / bad payload), 'applied' is success.
|
|
151
|
+
// Unregistered types stay silent on purpose —
|
|
152
|
+
// another script on the page may own them, and the
|
|
153
|
+
// host's timeout is the backstop.
|
|
154
|
+
if (!window.__miaodaMirror) {
|
|
155
|
+
const appliers = new Map(); // message.type -> Set<fn>
|
|
156
|
+
window.__miaodaMirror = {
|
|
157
|
+
emit(msg, opts) {
|
|
158
|
+
try { window.parent.postMessage(Object.assign({}, msg, { __mirror: Object.assign({ v: 1 }, opts) }), '*'); } catch (e) {}
|
|
159
|
+
},
|
|
160
|
+
onApply(type, fn) {
|
|
161
|
+
if (!appliers.has(type)) appliers.set(type, new Set());
|
|
162
|
+
appliers.get(type).add(fn);
|
|
163
|
+
return () => { const s = appliers.get(type); if (s) s.delete(fn); };
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
window.addEventListener('message', async (e) => {
|
|
167
|
+
// Replays are accepted from the host frame only — generated pages embed
|
|
168
|
+
// arbitrary third-party iframes, any of which could otherwise forge an
|
|
169
|
+
// apply and drive state (plus its persisted side effects).
|
|
170
|
+
if (e.source !== window.parent) return;
|
|
171
|
+
const d = e.data;
|
|
172
|
+
if (!d || d.type !== 'miaoda:mirror:apply' || !d.message || !d.message.type) return;
|
|
173
|
+
const fns = appliers.get(d.message.type);
|
|
174
|
+
if (!fns || !fns.size) return;
|
|
175
|
+
// Merge across handlers (multi-instance case): one success is enough.
|
|
176
|
+
// Instances share a channel, so a sibling that can't apply yet (a lazily
|
|
177
|
+
// slotted second deck returning 'failed') must not poison an applied
|
|
178
|
+
// result into an SDK retry storm of no-ops — 'failed' only stands when
|
|
179
|
+
// nobody applied. Every handler still runs (no short-circuit) so each
|
|
180
|
+
// sibling instance gets the state.
|
|
181
|
+
let result = { type: 'ignored', reason: 'no applier result' };
|
|
182
|
+
for (const fn of Array.from(fns)) {
|
|
183
|
+
let r;
|
|
184
|
+
try { r = await fn(d.message, { latencyMs: d.latencyMs, uuid: d.uuid }); }
|
|
185
|
+
catch (err) { r = { type: 'failed', reason: String((err && err.message) || err) }; }
|
|
186
|
+
if (!r) continue;
|
|
187
|
+
if (r.type === 'applied') result = r;
|
|
188
|
+
else if (result.type !== 'applied' && (r.type === 'failed' || result.type !== 'failed')) result = r;
|
|
189
|
+
}
|
|
190
|
+
try { window.parent.postMessage({ type: 'miaoda:mirror:apply-result', uuid: d.uuid, result }, '*'); } catch (e2) {}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
119
194
|
const DESIGN_W_DEFAULT = 1920;
|
|
120
195
|
const DESIGN_H_DEFAULT = 1080;
|
|
121
196
|
// Safe margin around the scaled slide in edit/preview (non-presenting) mode,
|
|
@@ -149,7 +224,15 @@
|
|
|
149
224
|
* reuses. It costs one thing — render() also resets that img's `sizes`,
|
|
150
225
|
* discarding the rail-width hint _materialize sets; the URL still matches the
|
|
151
226
|
* slide's, so the thumb reuses the slide's decoded bitmap. */
|
|
152
|
-
|
|
227
|
+
/* The device frames (ios-/android-/macos-/browser-) get in on the *other* half
|
|
228
|
+
* of the rule. Their content lives in a shadow root, so cloneNode captures none
|
|
229
|
+
* of it — for them re-mounting is not "adds nothing", it is the only way to get
|
|
230
|
+
* the content at all. They do satisfy clause 2: purely presentational, read
|
|
231
|
+
* attributes and paint the shadow, no storage and no messages.
|
|
232
|
+
* design-canvas is deliberately NOT on this list — it reads localStorage,
|
|
233
|
+
* fetches its sidecar and posts to the host on mount, so running it once per
|
|
234
|
+
* thumbnail would corrupt the real state. */
|
|
235
|
+
const KEEP_LIVE_PREFIXES = ['cd-', 'ios-', 'android-', 'macos-', 'browser-'];
|
|
153
236
|
const keepsCloneLive = (el) => KEEP_LIVE_PREFIXES.some((p) => el.localName.startsWith(p));
|
|
154
237
|
|
|
155
238
|
/* Why a clone needs a substrate rule of its own: _collectAuthorCss samples
|
|
@@ -880,6 +963,57 @@
|
|
|
880
963
|
});
|
|
881
964
|
// Announce to the host that this product IS a deck scene.
|
|
882
965
|
try { window.parent.postMessage({ type: 'miaoda:deck:available' }, '*'); } catch (e) {}
|
|
966
|
+
// Magic Share replay entries — each consumes the very message type this
|
|
967
|
+
// component emits, so the emit↔apply contract lives in one file and is
|
|
968
|
+
// version-consistent on both ends of a share by construction. Replays
|
|
969
|
+
// route through the same internal paths as local interaction (_go, the
|
|
970
|
+
// miaoda:deck:* inbound handlers); the re-emit those paths produce is
|
|
971
|
+
// the host role gate's to drop (the protocol's first anti-echo layer).
|
|
972
|
+
// Synthetic replays through _onMessage run under _applyingMirror so the
|
|
973
|
+
// handlers can tell "presenter state to render" from "this viewer's own
|
|
974
|
+
// choice to persist" (see the rail localStorage guard).
|
|
975
|
+
const applyViaOnMessage = (data) => {
|
|
976
|
+
this._applyingMirror = true;
|
|
977
|
+
try { this._onMessage({ data }); } finally { this._applyingMirror = false; }
|
|
978
|
+
};
|
|
979
|
+
// Capture iframes navigate on their own — the emit side stays unstamped
|
|
980
|
+
// there for exactly that reason, and the replay entries stay
|
|
981
|
+
// unregistered symmetrically so a (mis)forwarded apply can't steer a
|
|
982
|
+
// pinned thumbnail either.
|
|
983
|
+
this._mirrorOff = this._snthumb ? [] : [
|
|
984
|
+
window.__miaodaMirror.onApply('miaoda:deck:slide-changed', (m) => {
|
|
985
|
+
// isFinite, not typeof: NaN|0 would silently land on slide 1.
|
|
986
|
+
if (!Number.isFinite(m.index) || m.index < 0) return { type: 'ignored', reason: 'bad index' };
|
|
987
|
+
// Slides are collected by slotchange AFTER connectedCallback, so a
|
|
988
|
+
// late joiner's replay can beat them — that's transient, and only
|
|
989
|
+
// 'failed' makes the SDK retry ('ignored' would strand the joiner
|
|
990
|
+
// on slide 1).
|
|
991
|
+
if (!this._slides.length) return { type: 'failed', reason: 'no slides yet' };
|
|
992
|
+
const requested = m.index | 0;
|
|
993
|
+
const clamped = Math.max(0, Math.min(this._slides.length - 1, requested));
|
|
994
|
+
if (clamped !== requested) {
|
|
995
|
+
// Fewer slides than the presenter (reload lag after an agent
|
|
996
|
+
// edit): show the nearest page as best effort, but answer
|
|
997
|
+
// 'failed' so the SDK keeps retrying until the slide exists —
|
|
998
|
+
// 'applied' here would strand this end on the clamped page.
|
|
999
|
+
if (clamped !== this._index) this._go(clamped, 'api');
|
|
1000
|
+
return { type: 'failed', reason: 'requested slide not present yet' };
|
|
1001
|
+
}
|
|
1002
|
+
if (clamped === this._index) return { type: 'applied' };
|
|
1003
|
+
this._go(clamped, 'api');
|
|
1004
|
+
return this._index === clamped ? { type: 'applied' } : { type: 'failed', reason: 'goto did not land' };
|
|
1005
|
+
}),
|
|
1006
|
+
window.__miaodaMirror.onApply('miaoda:deck:presenting-changed', (m) => {
|
|
1007
|
+
if (typeof m.on !== 'boolean') return { type: 'ignored', reason: 'no on' };
|
|
1008
|
+
applyViaOnMessage({ type: 'miaoda:deck:presenting', on: m.on });
|
|
1009
|
+
return { type: 'applied' };
|
|
1010
|
+
}),
|
|
1011
|
+
window.__miaodaMirror.onApply('miaoda:deck:rail-visible-changed', (m) => {
|
|
1012
|
+
if (typeof m.on !== 'boolean') return { type: 'ignored', reason: 'no on' };
|
|
1013
|
+
applyViaOnMessage({ type: 'miaoda:deck:rail-visible', on: m.on });
|
|
1014
|
+
return { type: 'applied' };
|
|
1015
|
+
}),
|
|
1016
|
+
];
|
|
883
1017
|
}
|
|
884
1018
|
|
|
885
1019
|
_enableRail() {
|
|
@@ -1235,6 +1369,8 @@
|
|
|
1235
1369
|
if (this._headObserver) this._headObserver.disconnect();
|
|
1236
1370
|
(this._thumbs || []).forEach((t) => clearTimeout(t._canvasRecapTimer));
|
|
1237
1371
|
if (this._onTweakChange) window.removeEventListener('tweakchange', this._onTweakChange);
|
|
1372
|
+
(this._mirrorOff || []).forEach((off) => off());
|
|
1373
|
+
this._mirrorOff = null;
|
|
1238
1374
|
}
|
|
1239
1375
|
|
|
1240
1376
|
attributeChangedCallback() {
|
|
@@ -1689,6 +1825,16 @@
|
|
|
1689
1825
|
if (n >= 0 && n !== this._index) this._go(n, 'api');
|
|
1690
1826
|
}
|
|
1691
1827
|
|
|
1828
|
+
// Single home for the capture-iframe rule: presenter-popup thumbnails
|
|
1829
|
+
// (_snthumb) navigate on their own, so nothing they post may carry the
|
|
1830
|
+
// Magic Share stamp — a stamped emit from a pinned N±1 thumbnail would
|
|
1831
|
+
// broadcast conflicting state. Route every stamp through here so a new
|
|
1832
|
+
// emit site can't forget the rule.
|
|
1833
|
+
_stampMirror(msg, key) {
|
|
1834
|
+
if (!this._snthumb) msg.__mirror = { v: 1, key: key };
|
|
1835
|
+
return msg;
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1692
1838
|
_applyIndex({ broadcast = true, reason = 'init' } = {}) {
|
|
1693
1839
|
if (!this._slides.length) return;
|
|
1694
1840
|
const prev = this._prevIndex == null ? -1 : this._prevIndex;
|
|
@@ -1718,8 +1864,14 @@
|
|
|
1718
1864
|
this._syncNotesBody();
|
|
1719
1865
|
|
|
1720
1866
|
if (broadcast) {
|
|
1721
|
-
// (1) Parent-window postMessage for speaker-notes renderers
|
|
1722
|
-
|
|
1867
|
+
// (1) Parent-window postMessage for speaker-notes renderers, stamped
|
|
1868
|
+
// __mirror for Magic Share. Capture iframes (presenter-popup
|
|
1869
|
+
// thumbnails pinned to N±1) navigate on their own — stamping them
|
|
1870
|
+
// would broadcast conflicting indices, so they stay unstamped.
|
|
1871
|
+
try {
|
|
1872
|
+
const msg = { type: 'miaoda:deck:slide-changed', index: curr, total: this._slides.length, skipped: this._skippedIndices() };
|
|
1873
|
+
window.parent.postMessage(this._stampMirror(msg, 'deck:slide'), '*');
|
|
1874
|
+
} catch (e) {}
|
|
1723
1875
|
|
|
1724
1876
|
// (2) In-page CustomEvent on the <deck-stage> element itself.
|
|
1725
1877
|
// Bubbles and composes out of shadow DOM so slide code can listen:
|
|
@@ -1977,6 +2129,10 @@
|
|
|
1977
2129
|
_onMessage(e) {
|
|
1978
2130
|
const d = e.data;
|
|
1979
2131
|
if (d && d.type === 'miaoda:deck:presenting' && typeof d.on === 'boolean') {
|
|
2132
|
+
// Same-value messages (incl. mirror replays routed back through here)
|
|
2133
|
+
// must not re-run the layout work or re-emit. _presenting starts
|
|
2134
|
+
// undefined, so the first message always lands.
|
|
2135
|
+
if (d.on === this._presenting) return;
|
|
1980
2136
|
this._presenting = d.on;
|
|
1981
2137
|
this._syncMobileMode();
|
|
1982
2138
|
if (!d.on) this._toggleMobUi(false);
|
|
@@ -1985,6 +2141,9 @@
|
|
|
1985
2141
|
this._closeMenu();
|
|
1986
2142
|
this._fit();
|
|
1987
2143
|
this._scaleThumbs();
|
|
2144
|
+
// Presenting is host-driven, so the inbound handler is the one place
|
|
2145
|
+
// the state settles — snapshot it out for Magic Share from here.
|
|
2146
|
+
try { window.parent.postMessage(this._stampMirror({ type: 'miaoda:deck:presenting-changed', on: d.on }, 'deck:presenting'), '*'); } catch (e) {}
|
|
1988
2147
|
}
|
|
1989
2148
|
// Host's Preview segment (ViewerMode='none'): the rail's drag-reorder /
|
|
1990
2149
|
// right-click skip-delete affordances are editing chrome, so hide it
|
|
@@ -2007,9 +2166,14 @@
|
|
|
2007
2166
|
if (d && d.type === 'miaoda:deck:rail-visible' && typeof d.on === 'boolean') {
|
|
2008
2167
|
if (d.on === this._railVisible) return;
|
|
2009
2168
|
this._railVisible = d.on;
|
|
2010
|
-
|
|
2169
|
+
// Rail visibility is a per-viewer preference — a mirror replay drives
|
|
2170
|
+
// the visual state but must not overwrite what this viewer chose for
|
|
2171
|
+
// themselves after the share ends.
|
|
2172
|
+
if (!this._applyingMirror) {
|
|
2173
|
+
try { localStorage.setItem('deck-stage.railVisible', d.on ? '1' : '0'); } catch (e) {}
|
|
2174
|
+
}
|
|
2011
2175
|
// Notify the parent so its toolbar state stays in sync.
|
|
2012
|
-
try { window.parent.postMessage({ type: 'miaoda:deck:rail-visible-changed', on: d.on }, '*'); } catch (e) {}
|
|
2176
|
+
try { window.parent.postMessage(this._stampMirror({ type: 'miaoda:deck:rail-visible-changed', on: d.on }, 'deck:rail-visible'), '*'); } catch (e) {}
|
|
2013
2177
|
// Arm the transition, commit it, then flip state — otherwise the
|
|
2014
2178
|
// browser coalesces both writes and nothing animates on show.
|
|
2015
2179
|
this.setAttribute('data-rail-anim', '');
|
|
@@ -2132,6 +2296,9 @@
|
|
|
2132
2296
|
this._fit();
|
|
2133
2297
|
this._toggleMobUi(false);
|
|
2134
2298
|
try { window.parent.postMessage({ type: 'miaoda:deck:presenting-dismissed' }, '*'); } catch (err) {}
|
|
2299
|
+
// The mobile back button flips presenting locally (no host round-trip),
|
|
2300
|
+
// so mirror the settled state from here too.
|
|
2301
|
+
try { window.parent.postMessage(this._stampMirror({ type: 'miaoda:deck:presenting-changed', on: false }, 'deck:presenting'), '*'); } catch (e) {}
|
|
2135
2302
|
});
|
|
2136
2303
|
const strip = document.createElement('div');
|
|
2137
2304
|
strip.className = 'mob-strip';
|
|
@@ -2742,7 +2909,10 @@
|
|
|
2742
2909
|
this._emitDeckChange({ action: on ? 'skip' : 'unskip', from: i, slide });
|
|
2743
2910
|
// Re-broadcast so the presenter popup's prev/next thumbnails re-pick
|
|
2744
2911
|
// the nearest non-skipped slide without waiting for a nav event.
|
|
2745
|
-
try {
|
|
2912
|
+
try {
|
|
2913
|
+
const msg = { type: 'miaoda:deck:slide-changed', index: this._index, total: this._slides.length, skipped: this._skippedIndices() };
|
|
2914
|
+
window.parent.postMessage(this._stampMirror(msg, 'deck:slide'), '*');
|
|
2915
|
+
} catch (e) {}
|
|
2746
2916
|
}
|
|
2747
2917
|
|
|
2748
2918
|
_skippedIndices() {
|