@lark-apaas/coding-registry-buildless 0.1.11-alpha.20260729134521 → 0.1.11-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/components/design-canvas.jsx +10 -0
- package/components/ios-frame.jsx +6 -2
- package/components/tweaks-panel.jsx +87 -77
- package/package.json +1 -1
- package/scripts/deck-stage.js +61 -18
|
@@ -847,6 +847,10 @@ function DCViewport({ children, minScale = 0.1, maxScale = 8, style = {} }) {
|
|
|
847
847
|
// Clear the pan-tick guard so apply() re-posts the current scale
|
|
848
848
|
// even if it's unchanged — the host just reset dcScale to 1.
|
|
849
849
|
window.parent.postMessage({ type: 'miaoda:canvas:present' }, '*');
|
|
850
|
+
// Re-announce the upgrade capability too — the host's readyGen reset
|
|
851
|
+
// (fires on iframe 'load', which for image/font-heavy canvases lands
|
|
852
|
+
// after the mount-time announce) drops prior state, so re-declare here.
|
|
853
|
+
window.parent.postMessage({ type: 'miaoda:upgrade:available', kind: 'design-canvas' }, '*');
|
|
850
854
|
lastPostedScale.current = undefined;
|
|
851
855
|
apply();
|
|
852
856
|
}
|
|
@@ -860,6 +864,12 @@ function DCViewport({ children, minScale = 0.1, maxScale = 8, style = {} }) {
|
|
|
860
864
|
// effect's restore-path apply() may already have posted the restored
|
|
861
865
|
// scale (before miaoda:canvas:present), so clear the guard to re-post it in order.
|
|
862
866
|
window.parent.postMessage({ type: 'miaoda:canvas:present' }, '*');
|
|
867
|
+
// Generic "upgrade" capability signal — announce that this artifact offers
|
|
868
|
+
// an upgrade entry so the host can show/hide its upgrade button. Decoupled
|
|
869
|
+
// from the canvas zoom-proxy protocol above; any product may post the same
|
|
870
|
+
// message. Listener (onHostMsg) is already registered above → announce-after-
|
|
871
|
+
// register order holds. No client-side behavior here.
|
|
872
|
+
window.parent.postMessage({ type: 'miaoda:upgrade:available', kind: 'design-canvas' }, '*');
|
|
863
873
|
lastPostedScale.current = undefined;
|
|
864
874
|
apply();
|
|
865
875
|
|
package/components/ios-frame.jsx
CHANGED
|
@@ -141,8 +141,12 @@ function IOSDevice({
|
|
|
141
141
|
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, zIndex: 10 }}>
|
|
142
142
|
<IOSStatusBar dark={dark} />
|
|
143
143
|
</div>
|
|
144
|
-
{/* content
|
|
145
|
-
|
|
144
|
+
{/* content — isolate:isolate keeps the screen in its own stacking
|
|
145
|
+
context so any z-index authored inside it stays *below* the frame
|
|
146
|
+
chrome (status bar z:10, island z:50, home indicator z:60); a
|
|
147
|
+
sticky/opaque header with a high z-index can no longer paint over
|
|
148
|
+
the status bar. */}
|
|
149
|
+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column', isolation: 'isolate' }}>
|
|
146
150
|
<div style={{ flex: 1, overflow: 'auto' }}>{children}</div>
|
|
147
151
|
{keyboard && <IOSKeyboard dark={dark} />}
|
|
148
152
|
</div>
|
|
@@ -62,107 +62,113 @@
|
|
|
62
62
|
/* END USAGE */
|
|
63
63
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
64
64
|
|
|
65
|
+
// Palette follows the Arco/Semi neutral token system the 创意设计模式 "设计风格"
|
|
66
|
+
// panel is designed in (Figma node 2008-22724): a solid white card on neutral
|
|
67
|
+
// grays instead of the previous warm-glass look.
|
|
68
|
+
// text-1 #1F2329 text-2 #646A73 text-3 #86909C
|
|
69
|
+
// border #DEE0E3 fill #EBECEE / #F2F3F5 accent(selected/on) #1F2329
|
|
65
70
|
const __TWEAKS_STYLE = `
|
|
66
|
-
.twk-panel{position:fixed;right:16px;
|
|
71
|
+
.twk-panel{position:fixed;right:16px;top:16px;z-index:2147483646;width:280px;
|
|
67
72
|
max-height:calc(100vh - 32px);display:flex;flex-direction:column;
|
|
68
|
-
transform:scale(var(--dc-inv-zoom,1));transform-origin:
|
|
69
|
-
background
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
font:11.5px/1.4 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
|
|
73
|
+
transform:scale(var(--dc-inv-zoom,1));transform-origin:top right;
|
|
74
|
+
background:#fff;color:#1F2329;
|
|
75
|
+
border:.5px solid #DEE0E3;border-radius:12px;
|
|
76
|
+
box-shadow:0 8px 24px 8px rgba(0,0,0,.04),0 6px 12px rgba(0,0,0,.04),0 2px 6px rgba(0,0,0,.04);
|
|
77
|
+
font:12px/1.5 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
|
|
74
78
|
.twk-hd{display:flex;align-items:center;justify-content:space-between;
|
|
75
|
-
padding:
|
|
76
|
-
|
|
77
|
-
.twk-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.twk-
|
|
79
|
+
padding:12px 12px 12px 16px;cursor:move;user-select:none;
|
|
80
|
+
border-bottom:.5px solid #EBECEE}
|
|
81
|
+
.twk-hd b{font-size:14px;font-weight:600;letter-spacing:.01em;color:#1F2329}
|
|
82
|
+
.twk-x{appearance:none;border:0;background:transparent;color:#86909C;
|
|
83
|
+
width:24px;height:24px;border-radius:6px;cursor:default;font-size:13px;line-height:1}
|
|
84
|
+
.twk-x:hover{background:#F2F3F5;color:#1F2329}
|
|
85
|
+
.twk-body{padding:14px 16px 16px;display:flex;flex-direction:column;gap:14px;
|
|
81
86
|
overflow-y:auto;overflow-x:hidden;min-height:0;
|
|
82
|
-
scrollbar-width:thin;scrollbar-color
|
|
87
|
+
scrollbar-width:thin;scrollbar-color:#DEE0E3 transparent}
|
|
83
88
|
.twk-body::-webkit-scrollbar{width:8px}
|
|
84
89
|
.twk-body::-webkit-scrollbar-track{background:transparent;margin:2px}
|
|
85
|
-
.twk-body::-webkit-scrollbar-thumb{background
|
|
90
|
+
.twk-body::-webkit-scrollbar-thumb{background:#DEE0E3;border-radius:4px;
|
|
86
91
|
border:2px solid transparent;background-clip:content-box}
|
|
87
|
-
.twk-body::-webkit-scrollbar-thumb:hover{background
|
|
92
|
+
.twk-body::-webkit-scrollbar-thumb:hover{background:#C9CDD4;
|
|
88
93
|
border:2px solid transparent;background-clip:content-box}
|
|
89
|
-
.twk-row{display:flex;flex-direction:column;gap:
|
|
94
|
+
.twk-row{display:flex;flex-direction:column;gap:8px}
|
|
90
95
|
.twk-row-h{flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
|
|
91
96
|
.twk-lbl{display:flex;justify-content:space-between;align-items:baseline;
|
|
92
|
-
color
|
|
97
|
+
color:#1F2329}
|
|
93
98
|
.twk-lbl>span:first-child{font-weight:500}
|
|
94
|
-
.twk-val{color
|
|
99
|
+
.twk-val{color:#646A73;font-variant-numeric:tabular-nums}
|
|
95
100
|
|
|
96
|
-
.twk-sect{font-size:
|
|
97
|
-
color
|
|
101
|
+
.twk-sect{font-size:11px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
|
|
102
|
+
color:#86909C;padding:10px 0 0}
|
|
98
103
|
.twk-sect:first-child{padding-top:0}
|
|
99
104
|
|
|
100
|
-
.twk-field{appearance:none;box-sizing:border-box;width:100%;min-width:0;height:
|
|
101
|
-
border:.5px solid
|
|
102
|
-
background
|
|
103
|
-
.twk-field:focus{border-color:
|
|
104
|
-
select.twk-field{padding-right:
|
|
105
|
-
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='
|
|
106
|
-
background-repeat:no-repeat;background-position:right
|
|
105
|
+
.twk-field{appearance:none;box-sizing:border-box;width:100%;min-width:0;height:32px;padding:0 10px;
|
|
106
|
+
border:.5px solid #DEE0E3;border-radius:8px;
|
|
107
|
+
background:#fff;color:inherit;font:inherit;outline:none}
|
|
108
|
+
.twk-field:focus{border-color:#1F2329;box-shadow:0 0 0 2px rgba(31,35,41,.1)}
|
|
109
|
+
select.twk-field{padding-right:24px;
|
|
110
|
+
background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'><path fill='%234E5969' d='M0 0h10L5 6z'/></svg>");
|
|
111
|
+
background-repeat:no-repeat;background-position:right 10px center}
|
|
107
112
|
|
|
108
113
|
.twk-slider{appearance:none;-webkit-appearance:none;width:100%;height:4px;margin:6px 0;
|
|
109
|
-
border-radius:999px;background
|
|
114
|
+
border-radius:999px;background:#EBECEE;outline:none}
|
|
110
115
|
.twk-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
|
|
111
|
-
width:
|
|
112
|
-
border:.5px solid
|
|
113
|
-
.twk-slider::-moz-range-thumb{width:
|
|
114
|
-
background:#fff;border:.5px solid
|
|
116
|
+
width:16px;height:16px;border-radius:50%;background:#fff;
|
|
117
|
+
border:.5px solid #DEE0E3;box-shadow:0 1px 3px rgba(0,0,0,.16);cursor:default}
|
|
118
|
+
.twk-slider::-moz-range-thumb{width:16px;height:16px;border-radius:50%;
|
|
119
|
+
background:#fff;border:.5px solid #DEE0E3;box-shadow:0 1px 3px rgba(0,0,0,.16);cursor:default}
|
|
115
120
|
|
|
116
121
|
.twk-seg{position:relative;display:flex;padding:2px;border-radius:8px;
|
|
117
|
-
background
|
|
122
|
+
background:#EBECEE;user-select:none}
|
|
118
123
|
.twk-seg-thumb{position:absolute;top:2px;bottom:2px;border-radius:6px;
|
|
119
|
-
background
|
|
124
|
+
background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.1);
|
|
120
125
|
transition:left .15s cubic-bezier(.3,.7,.4,1),width .15s}
|
|
121
126
|
.twk-seg.dragging .twk-seg-thumb{transition:none}
|
|
122
127
|
.twk-seg button{appearance:none;position:relative;z-index:1;flex:1;border:0;
|
|
123
|
-
background:transparent;color
|
|
128
|
+
background:transparent;color:#646A73;font:inherit;font-weight:500;min-height:28px;
|
|
124
129
|
border-radius:6px;cursor:default;padding:4px 6px;line-height:1.2;
|
|
125
|
-
overflow-wrap:anywhere}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.twk-toggle
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
130
|
+
overflow-wrap:anywhere;transition:color .15s}
|
|
131
|
+
.twk-seg button[aria-checked="true"]{color:#1F2329}
|
|
132
|
+
|
|
133
|
+
.twk-toggle{position:relative;width:36px;height:20px;border:0;border-radius:999px;
|
|
134
|
+
background:#E4E6E8;transition:background .15s;cursor:default;padding:0}
|
|
135
|
+
.twk-toggle[data-on="1"]{background:#1F2329}
|
|
136
|
+
.twk-toggle i{position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;
|
|
137
|
+
background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.2);transition:transform .15s}
|
|
138
|
+
.twk-toggle[data-on="1"] i{transform:translateX(16px)}
|
|
139
|
+
|
|
140
|
+
.twk-num{display:flex;align-items:center;box-sizing:border-box;min-width:0;height:32px;padding:0 0 0 10px;
|
|
141
|
+
border:.5px solid #DEE0E3;border-radius:8px;background:#fff}
|
|
142
|
+
.twk-num-lbl{font-weight:500;color:#646A73;cursor:ew-resize;
|
|
137
143
|
user-select:none;padding-right:8px}
|
|
138
144
|
.twk-num input{flex:1;min-width:0;height:100%;border:0;background:transparent;
|
|
139
|
-
font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0
|
|
145
|
+
font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0 10px 0 0;
|
|
140
146
|
outline:none;color:inherit;-moz-appearance:textfield}
|
|
141
147
|
.twk-num input::-webkit-inner-spin-button,.twk-num input::-webkit-outer-spin-button{
|
|
142
148
|
-webkit-appearance:none;margin:0}
|
|
143
|
-
.twk-num-unit{padding-right:
|
|
149
|
+
.twk-num-unit{padding-right:10px;color:#86909C}
|
|
144
150
|
|
|
145
|
-
.twk-btn{appearance:none;height:
|
|
146
|
-
background
|
|
147
|
-
.twk-btn:hover{background
|
|
148
|
-
.twk-btn.secondary{background
|
|
149
|
-
.twk-btn.secondary:hover{background
|
|
151
|
+
.twk-btn{appearance:none;height:32px;padding:0 14px;border:0;border-radius:8px;
|
|
152
|
+
background:#1F2329;color:#fff;font:inherit;font-weight:500;cursor:default}
|
|
153
|
+
.twk-btn:hover{background:#333A44}
|
|
154
|
+
.twk-btn.secondary{background:#F2F3F5;color:#1F2329}
|
|
155
|
+
.twk-btn.secondary:hover{background:#E5E6EB}
|
|
150
156
|
|
|
151
|
-
.twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:
|
|
152
|
-
border:.5px solid
|
|
157
|
+
.twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:24px;
|
|
158
|
+
border:.5px solid #DEE0E3;border-radius:6px;padding:0;cursor:default;
|
|
153
159
|
background:transparent;flex-shrink:0}
|
|
154
160
|
.twk-swatch::-webkit-color-swatch-wrapper{padding:0}
|
|
155
161
|
.twk-swatch::-webkit-color-swatch{border:0;border-radius:5.5px}
|
|
156
162
|
.twk-swatch::-moz-color-swatch{border:0;border-radius:5.5px}
|
|
157
163
|
|
|
158
|
-
.twk-chips{display:flex;gap:
|
|
159
|
-
.twk-chip{position:relative;appearance:none;flex:1;min-width:0;height:
|
|
164
|
+
.twk-chips{display:flex;gap:8px}
|
|
165
|
+
.twk-chip{position:relative;appearance:none;flex:1;min-width:0;height:40px;
|
|
160
166
|
padding:0;border:0;border-radius:6px;overflow:hidden;cursor:default;
|
|
161
|
-
box-shadow:0 0 0 .5px rgba(0,0,0,.
|
|
167
|
+
box-shadow:0 0 0 .5px rgba(0,0,0,.08),0 1px 2px rgba(0,0,0,.05);
|
|
162
168
|
transition:transform .12s cubic-bezier(.3,.7,.4,1),box-shadow .12s}
|
|
163
169
|
.twk-chip:hover{transform:translateY(-1px);
|
|
164
|
-
box-shadow:0 0 0 .5px rgba(0,0,0,.
|
|
165
|
-
.twk-chip[data-on="1"]{box-shadow:0 0 0
|
|
170
|
+
box-shadow:0 0 0 .5px rgba(0,0,0,.14),0 4px 10px rgba(0,0,0,.1)}
|
|
171
|
+
.twk-chip[data-on="1"]{box-shadow:0 0 0 2px #fff,0 0 0 3.5px rgba(0,0,0,.85),
|
|
166
172
|
0 2px 6px rgba(0,0,0,.15)}
|
|
167
173
|
.twk-chip>span{position:absolute;top:0;bottom:0;right:0;width:34%;
|
|
168
174
|
display:flex;flex-direction:column;box-shadow:-1px 0 0 rgba(0,0,0,.1)}
|
|
@@ -202,6 +208,10 @@ function useTweaks(defaults) {
|
|
|
202
208
|
function TweaksPanel({ title = 'Tweaks', children }) {
|
|
203
209
|
const [open, setOpen] = React.useState(false);
|
|
204
210
|
const dragRef = React.useRef(null);
|
|
211
|
+
// x = distance from the viewport's right edge, y = distance from its TOP.
|
|
212
|
+
// The panel anchors top-right (right/top in CSS) so it clears the host's
|
|
213
|
+
// bottom toolbar; both axes still grow inward, so clamp/drag math is
|
|
214
|
+
// symmetric with the old bottom-right anchor.
|
|
205
215
|
const offsetRef = React.useRef({ x: 16, y: 16 });
|
|
206
216
|
const PAD = 16;
|
|
207
217
|
|
|
@@ -210,13 +220,13 @@ function TweaksPanel({ title = 'Tweaks', children }) {
|
|
|
210
220
|
if (!panel) return;
|
|
211
221
|
const w = panel.offsetWidth, h = panel.offsetHeight;
|
|
212
222
|
const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
|
|
213
|
-
const
|
|
223
|
+
const maxTop = Math.max(PAD, window.innerHeight - h - PAD);
|
|
214
224
|
offsetRef.current = {
|
|
215
225
|
x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
|
|
216
|
-
y: Math.min(
|
|
226
|
+
y: Math.min(maxTop, Math.max(PAD, offsetRef.current.y)),
|
|
217
227
|
};
|
|
218
228
|
panel.style.right = offsetRef.current.x + 'px';
|
|
219
|
-
panel.style.
|
|
229
|
+
panel.style.top = offsetRef.current.y + 'px';
|
|
220
230
|
}, []);
|
|
221
231
|
|
|
222
232
|
React.useEffect(() => {
|
|
@@ -253,11 +263,11 @@ function TweaksPanel({ title = 'Tweaks', children }) {
|
|
|
253
263
|
const r = panel.getBoundingClientRect();
|
|
254
264
|
const sx = e.clientX, sy = e.clientY;
|
|
255
265
|
const startRight = window.innerWidth - r.right;
|
|
256
|
-
const
|
|
266
|
+
const startTop = r.top;
|
|
257
267
|
const move = (ev) => {
|
|
258
268
|
offsetRef.current = {
|
|
259
269
|
x: startRight - (ev.clientX - sx),
|
|
260
|
-
y:
|
|
270
|
+
y: startTop + (ev.clientY - sy),
|
|
261
271
|
};
|
|
262
272
|
clampToViewport();
|
|
263
273
|
};
|
|
@@ -274,7 +284,7 @@ function TweaksPanel({ title = 'Tweaks', children }) {
|
|
|
274
284
|
<>
|
|
275
285
|
<style>{__TWEAKS_STYLE}</style>
|
|
276
286
|
<div ref={dragRef} className="twk-panel" data-miaoda-chrome=""
|
|
277
|
-
style={{ right: offsetRef.current.x,
|
|
287
|
+
style={{ right: offsetRef.current.x, top: offsetRef.current.y }}>
|
|
278
288
|
<div className="twk-hd" onMouseDown={onDragStart}>
|
|
279
289
|
<b>{title}</b>
|
|
280
290
|
<button className="twk-x" aria-label="Close tweaks"
|
|
@@ -343,8 +353,8 @@ function TweakRadio({ label, value, options, onChange }) {
|
|
|
343
353
|
valueRef.current = value;
|
|
344
354
|
|
|
345
355
|
// Segments wrap mid-word once per-segment width runs out. The track is
|
|
346
|
-
// ~
|
|
347
|
-
// to its own padding, and
|
|
356
|
+
// ~244px (280 panel − 32 body pad − 4 seg pad), each button loses 12px
|
|
357
|
+
// to its own padding, and 12px system-ui averages ~6.6px/char — so 2
|
|
348
358
|
// options fit ~16 chars each, 3 fit ~10. Past that (or >3 options), fall
|
|
349
359
|
// back to a dropdown rather than wrap.
|
|
350
360
|
const labelLen = (o) => String(typeof o === 'object' ? o.label : o).length;
|
|
@@ -555,23 +565,23 @@ Object.assign(window, {
|
|
|
555
565
|
@keyframes twk-blink{50%{opacity:0}}
|
|
556
566
|
@keyframes twk-fadein{from{opacity:0;transform:translateX(4px)}to{opacity:1;transform:none}}
|
|
557
567
|
.twk-sugg{display:flex;align-items:center;gap:6px;padding:5px 8px;border-radius:8px;
|
|
558
|
-
background
|
|
559
|
-
.twk-sugg:focus-within{background
|
|
568
|
+
background:#F7F8FA;border:.5px solid #EBECEE;transition:all .15s}
|
|
569
|
+
.twk-sugg:focus-within{background:#F2F3F5;border-color:#DEE0E3}
|
|
560
570
|
.twk-sugg-field{position:relative;flex:1;min-width:0}
|
|
561
571
|
.twk-sugg-field input{width:100%;height:20px;border:0;background:transparent;
|
|
562
572
|
font:inherit;outline:none;color:inherit}
|
|
563
573
|
.twk-sugg-ghost{position:absolute;inset:0;display:flex;align-items:center;
|
|
564
|
-
color
|
|
565
|
-
.twk-sugg-ghost.hint{color
|
|
574
|
+
color:#86909C;pointer-events:none;white-space:nowrap;overflow:hidden}
|
|
575
|
+
.twk-sugg-ghost.hint{color:#A9AEB8}
|
|
566
576
|
.twk-sugg-caret{display:inline-block;width:1px;height:13px;margin-left:1px;
|
|
567
577
|
border-right:1.5px solid currentColor;opacity:.5;animation:twk-blink 1s step-end infinite}
|
|
568
578
|
.twk-sugg-ideas{appearance:none;border:0;background:transparent;font:inherit;
|
|
569
|
-
font-size:10.5px;font-weight:600;color
|
|
579
|
+
font-size:10.5px;font-weight:600;color:#646A73;cursor:default;padding:0 2px;
|
|
570
580
|
white-space:nowrap;animation:twk-fadein .25s ease}
|
|
571
|
-
.twk-sugg-ideas:hover{color
|
|
581
|
+
.twk-sugg-ideas:hover{color:#1F2329}
|
|
572
582
|
.twk-sugg-ideas svg{color:#D97757}
|
|
573
583
|
.twk-sugg-send{appearance:none;border:0;height:20px;padding:0 8px;border-radius:5px;
|
|
574
|
-
background:#
|
|
584
|
+
background:#1F2329;color:#fff;font:inherit;font-size:10px;font-weight:600;cursor:default}
|
|
575
585
|
`;
|
|
576
586
|
document.head.appendChild(s);
|
|
577
587
|
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/coding-registry-buildless",
|
|
3
|
-
"version": "0.1.11-
|
|
3
|
+
"version": "0.1.11-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": [
|
package/scripts/deck-stage.js
CHANGED
|
@@ -38,10 +38,11 @@
|
|
|
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
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
41
|
+
* Browse mode renders each slide's static base state and never fires
|
|
42
|
+
* entrance animations — exactly like the thumbnail rail and print
|
|
43
|
+
* (presenting keeps animations). One more surface that requires the
|
|
44
|
+
* visible end-state to be the base style (see Entrance animations
|
|
45
|
+
* below).
|
|
45
46
|
* (h) preflight mode — add `?preflight=1` to the URL to enter
|
|
46
47
|
* mobile-list layout on any device (including desktop). Identical to
|
|
47
48
|
* mobile browse except the outer frame background is transparent,
|
|
@@ -144,6 +145,7 @@
|
|
|
144
145
|
const MOB_RADIUS = 8; // card corner radius
|
|
145
146
|
const MOB_BADGE_IDLE_MS = 2000; // scroll idle before the page badge fades
|
|
146
147
|
const MOB_UI_HIDE_MS = 3000; // presenting controls auto-hide
|
|
148
|
+
const CANVAS_RECAP_MS = 1200; // re-snapshot canvas thumbs once chart entrance animations settle
|
|
147
149
|
|
|
148
150
|
const pad2 = (n) => String(n).padStart(2, '0');
|
|
149
151
|
|
|
@@ -1056,6 +1058,7 @@
|
|
|
1056
1058
|
if (this._scaleRaf) cancelAnimationFrame(this._scaleRaf);
|
|
1057
1059
|
if (this._liveObserver) this._liveObserver.disconnect();
|
|
1058
1060
|
if (this._railObserver) this._railObserver.disconnect();
|
|
1061
|
+
(this._thumbs || []).forEach((t) => clearTimeout(t._canvasRecapTimer));
|
|
1059
1062
|
if (this._onTweakChange) window.removeEventListener('tweakchange', this._onTweakChange);
|
|
1060
1063
|
}
|
|
1061
1064
|
|
|
@@ -1383,26 +1386,38 @@
|
|
|
1383
1386
|
'animation-play-state: running !important; transition-duration: 0s !important; } }';
|
|
1384
1387
|
}
|
|
1385
1388
|
|
|
1386
|
-
/** Mobile browse (data-mobile-list)
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1389
|
-
*
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
*
|
|
1389
|
+
/** Mobile browse (data-mobile-list) renders each card's static BASE
|
|
1390
|
+
* state — the same semantic as the thumbnail rail, print and
|
|
1391
|
+
* reduced-motion. The USAGE authoring contract makes the base style the
|
|
1392
|
+
* visible end-state (hidden lives only inside keyframes' `from`, gated
|
|
1393
|
+
* on [data-deck-active]), so animation-name:none shows exactly the
|
|
1394
|
+
* intended slide. Freezing at the END state instead would key visible
|
|
1395
|
+
* styling to a flag that MOVES during scroll: keyframe properties
|
|
1396
|
+
* (transform especially — it replaces the base value outright) snap on
|
|
1397
|
+
* and off as [data-deck-active] arrives at and leaves each card,
|
|
1398
|
+
* jolting layout. The timing longhands stay as a backstop: an authored
|
|
1399
|
+
* `animation … !important` that out-cascades our name:none still
|
|
1400
|
+
* collapses to an instant run. The attribute selector is repeated to
|
|
1401
|
+
* out-rank authored !important rules — important-vs-important resolves
|
|
1402
|
+
* by specificity, and the common `[data-deck-active] .x` gate is only
|
|
1403
|
+
* (0,2,0). Document-level like the print rule: author animations style
|
|
1404
|
+
* light-DOM slide content that shadow CSS can't reach, and <deck-stage>
|
|
1405
|
+
* is a light-DOM element document CSS can key on. Presenting/desktop
|
|
1406
|
+
* keep their animations — the rule is keyed on the host attribute, no
|
|
1407
|
+
* JS toggling needed. */
|
|
1396
1408
|
_syncMobFreezeRule() {
|
|
1397
1409
|
const id = 'deck-stage-mob-freeze';
|
|
1398
1410
|
if (document.getElementById(id)) return;
|
|
1411
|
+
const sel = 'deck-stage[data-mobile-list][data-mobile-list][data-mobile-list]';
|
|
1399
1412
|
const tag = document.createElement('style');
|
|
1400
1413
|
tag.id = id;
|
|
1401
1414
|
tag.textContent =
|
|
1402
|
-
'
|
|
1415
|
+
sel + ' *, ' + sel + ' *::before, ' + sel + ' *::after {' +
|
|
1416
|
+
' animation-name: none !important;' +
|
|
1403
1417
|
' animation-delay: -99s !important; animation-duration: .001s !important;' +
|
|
1404
1418
|
' animation-iteration-count: 1 !important; animation-fill-mode: both !important;' +
|
|
1405
|
-
' animation-play-state: running !important;
|
|
1419
|
+
' animation-play-state: running !important;' +
|
|
1420
|
+
' transition-duration: 0s !important; transition-delay: 0s !important; }';
|
|
1406
1421
|
document.head.appendChild(tag);
|
|
1407
1422
|
}
|
|
1408
1423
|
|
|
@@ -1960,7 +1975,7 @@
|
|
|
1960
1975
|
// it scales clones by the rail's _thumbScale, so rewrite the
|
|
1961
1976
|
// transform for this strip's fixed 132px frames.
|
|
1962
1977
|
const entry = { slide: e2.slide, frame, host: null, clone: null };
|
|
1963
|
-
this._materialize(entry);
|
|
1978
|
+
this._materialize(entry, false); // throwaway entry (not in _thumbs) — skip the recap timer
|
|
1964
1979
|
if (entry.clone) entry.clone.style.transform = 'scale(' + (132 / this.designWidth) + ')';
|
|
1965
1980
|
return { el: t, i: e2.i };
|
|
1966
1981
|
});
|
|
@@ -2082,6 +2097,7 @@
|
|
|
2082
2097
|
// Orphans — slides removed since last render.
|
|
2083
2098
|
bySlide.forEach((t) => {
|
|
2084
2099
|
if (this._railObserver) this._railObserver.unobserve(t.frame);
|
|
2100
|
+
clearTimeout(t._canvasRecapTimer);
|
|
2085
2101
|
t.thumb.remove();
|
|
2086
2102
|
});
|
|
2087
2103
|
// Put thumbs into document order to match _slides. insertBefore on
|
|
@@ -2263,7 +2279,7 @@
|
|
|
2263
2279
|
}
|
|
2264
2280
|
|
|
2265
2281
|
/** Lazily build the clone for a thumb that has scrolled into view. */
|
|
2266
|
-
_materialize(entry) {
|
|
2282
|
+
_materialize(entry, scheduleRecap = true) {
|
|
2267
2283
|
if (entry.host) return;
|
|
2268
2284
|
const dw = this.designWidth, dh = this.designHeight;
|
|
2269
2285
|
let clone = entry.slide.cloneNode(true);
|
|
@@ -2295,6 +2311,7 @@
|
|
|
2295
2311
|
// drawings appear in the thumbnail instead of a blank rectangle.
|
|
2296
2312
|
const origCanvases = entry.slide.querySelectorAll('canvas');
|
|
2297
2313
|
const cloneCanvases = clone.querySelectorAll('canvas');
|
|
2314
|
+
const canvasImgs = []; // {orig, img} pairs kept for the delayed re-snapshot below
|
|
2298
2315
|
cloneCanvases.forEach((el, ci) => {
|
|
2299
2316
|
const orig = origCanvases[ci];
|
|
2300
2317
|
if (!orig || !orig.width || !orig.height) return;
|
|
@@ -2306,6 +2323,7 @@
|
|
|
2306
2323
|
';width:' + orig.offsetWidth + 'px;height:' + orig.offsetHeight + 'px;';
|
|
2307
2324
|
img.className = el.className;
|
|
2308
2325
|
el.replaceWith(img);
|
|
2326
|
+
canvasImgs.push({ orig, img });
|
|
2309
2327
|
} catch (e) {}
|
|
2310
2328
|
});
|
|
2311
2329
|
// Images: defer decode and let the browser pick the smallest
|
|
@@ -2370,6 +2388,30 @@
|
|
|
2370
2388
|
// Once materialized the IO callback is a no-op early-return —
|
|
2371
2389
|
// unobserve so scroll doesn't keep firing it.
|
|
2372
2390
|
if (this._railObserver) this._railObserver.unobserve(entry.frame);
|
|
2391
|
+
// Chart libraries (ECharts/Chart.js) animate their entrance over ~1s and
|
|
2392
|
+
// draw straight to the canvas bitmap — no DOM mutation, so _liveObserver
|
|
2393
|
+
// never sees the finished frame and the toDataURL snapshot above may have
|
|
2394
|
+
// caught a blank or mid-animation canvas. Re-snapshot once after the
|
|
2395
|
+
// animation settles and overwrite each <img>. Guarded so a thumb that was
|
|
2396
|
+
// refreshed (host swapped) or removed (img detached) since is left alone.
|
|
2397
|
+
// scheduleRecap=false for callers whose entry isn't tracked in _thumbs
|
|
2398
|
+
// (the mobile filmstrip builds throwaway entries the cleanup paths can't
|
|
2399
|
+
// reach) — otherwise the timer leaks and, since that path clones the
|
|
2400
|
+
// whole deck eagerly, N recaps would fire together as a toDataURL storm.
|
|
2401
|
+
if (scheduleRecap && canvasImgs.length) {
|
|
2402
|
+
clearTimeout(entry._canvasRecapTimer);
|
|
2403
|
+
entry._canvasRecapTimer = setTimeout(() => {
|
|
2404
|
+
entry._canvasRecapTimer = null;
|
|
2405
|
+
if (entry.host !== host) return;
|
|
2406
|
+
canvasImgs.forEach(({ orig, img }) => {
|
|
2407
|
+
// orig.isConnected: if the app swapped the chart's <canvas> since,
|
|
2408
|
+
// the detached node still reports nonzero width/height — skip it so
|
|
2409
|
+
// we don't paint stale pixels (the swap already re-cloned the thumb).
|
|
2410
|
+
if (!img.isConnected || !orig.isConnected || !orig.width || !orig.height) return;
|
|
2411
|
+
try { img.src = orig.toDataURL(); } catch (e) {}
|
|
2412
|
+
});
|
|
2413
|
+
}, CANVAS_RECAP_MS);
|
|
2414
|
+
}
|
|
2373
2415
|
}
|
|
2374
2416
|
|
|
2375
2417
|
/** Re-clone a single thumb (live-update path). No-op if the thumb
|
|
@@ -2378,6 +2420,7 @@
|
|
|
2378
2420
|
_refreshThumb(slide) {
|
|
2379
2421
|
const entry = (this._thumbs || []).find((t) => t.slide === slide);
|
|
2380
2422
|
if (!entry || !entry.host) return;
|
|
2423
|
+
clearTimeout(entry._canvasRecapTimer);
|
|
2381
2424
|
entry.host.remove();
|
|
2382
2425
|
entry.host = entry.clone = null;
|
|
2383
2426
|
this._materialize(entry);
|