@lark-apaas/coding-registry-buildless 0.1.10 → 0.1.11-alpha.20260729134521
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 +0 -21
- package/components/design-canvas.jsx +0 -10
- package/components/ios-frame.jsx +2 -6
- package/components/tweaks-panel.jsx +77 -87
- package/package.json +1 -1
- package/scripts/deck-stage.js +41 -65
|
@@ -14,9 +14,6 @@
|
|
|
14
14
|
// ←/→ seek, space, and 0-to-reset controls, and persists the playhead.
|
|
15
15
|
// Set the optional `poster` prop (seconds) to the moment your opening scene is
|
|
16
16
|
// fully composed — the product thumbnail freezes on that frame (default ~1s).
|
|
17
|
-
// Stage also exposes window.__stage — seek(t) / pause() / play() / time() /
|
|
18
|
-
// playing() / duration — the deterministic way to drive the playhead from
|
|
19
|
-
// outside (tooling, self-checks); never synthesize key/scrubber events for that.
|
|
20
17
|
// Inside <Stage>, any child can call useTime() to read the current
|
|
21
18
|
// playhead (seconds). Or wrap content in <Sprite start={1} end={4}>...</Sprite>
|
|
22
19
|
// to only render during that window (outside it, children are unmounted from
|
|
@@ -439,24 +436,6 @@ function Stage({
|
|
|
439
436
|
return () => window.removeEventListener('keydown', onKey);
|
|
440
437
|
}, [duration]);
|
|
441
438
|
|
|
442
|
-
// Programmatic driver for tooling (preflight / screenshot-at-t): freezes the
|
|
443
|
-
// playhead on an exact frame, unlike synthesized key/scrubber events.
|
|
444
|
-
const timeRef = React.useRef(time);
|
|
445
|
-
timeRef.current = time;
|
|
446
|
-
const playingRef = React.useRef(playing);
|
|
447
|
-
playingRef.current = playing;
|
|
448
|
-
React.useEffect(() => {
|
|
449
|
-
window.__stage = {
|
|
450
|
-
duration,
|
|
451
|
-
seek: (t) => { setPlaying(false); setTime(clamp(t, 0, duration)); },
|
|
452
|
-
pause: () => setPlaying(false),
|
|
453
|
-
play: () => setPlaying(true),
|
|
454
|
-
time: () => timeRef.current,
|
|
455
|
-
playing: () => playingRef.current,
|
|
456
|
-
};
|
|
457
|
-
return () => { delete window.__stage; };
|
|
458
|
-
}, [duration]);
|
|
459
|
-
|
|
460
439
|
const ctxValue = React.useMemo(
|
|
461
440
|
() => ({ time, duration, playing, setTime, setPlaying }),
|
|
462
441
|
[time, duration, playing]
|
|
@@ -847,10 +847,6 @@ 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' }, '*');
|
|
854
850
|
lastPostedScale.current = undefined;
|
|
855
851
|
apply();
|
|
856
852
|
}
|
|
@@ -864,12 +860,6 @@ function DCViewport({ children, minScale = 0.1, maxScale = 8, style = {} }) {
|
|
|
864
860
|
// effect's restore-path apply() may already have posted the restored
|
|
865
861
|
// scale (before miaoda:canvas:present), so clear the guard to re-post it in order.
|
|
866
862
|
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' }, '*');
|
|
873
863
|
lastPostedScale.current = undefined;
|
|
874
864
|
apply();
|
|
875
865
|
|
package/components/ios-frame.jsx
CHANGED
|
@@ -141,12 +141,8 @@ 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
|
-
|
|
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' }}>
|
|
144
|
+
{/* content */}
|
|
145
|
+
<div style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
|
|
150
146
|
<div style={{ flex: 1, overflow: 'auto' }}>{children}</div>
|
|
151
147
|
{keyboard && <IOSKeyboard dark={dark} />}
|
|
152
148
|
</div>
|
|
@@ -62,113 +62,107 @@
|
|
|
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
|
|
70
65
|
const __TWEAKS_STYLE = `
|
|
71
|
-
.twk-panel{position:fixed;right:16px;
|
|
66
|
+
.twk-panel{position:fixed;right:16px;bottom:16px;z-index:2147483646;width:280px;
|
|
72
67
|
max-height:calc(100vh - 32px);display:flex;flex-direction:column;
|
|
73
|
-
transform:scale(var(--dc-inv-zoom,1));transform-origin:
|
|
74
|
-
background
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
68
|
+
transform:scale(var(--dc-inv-zoom,1));transform-origin:bottom right;
|
|
69
|
+
background:rgba(250,249,247,.78);color:#29261b;
|
|
70
|
+
-webkit-backdrop-filter:blur(24px) saturate(160%);backdrop-filter:blur(24px) saturate(160%);
|
|
71
|
+
border:.5px solid rgba(255,255,255,.6);border-radius:14px;
|
|
72
|
+
box-shadow:0 1px 0 rgba(255,255,255,.5) inset,0 12px 40px rgba(0,0,0,.18);
|
|
73
|
+
font:11.5px/1.4 ui-sans-serif,system-ui,-apple-system,sans-serif;overflow:hidden}
|
|
78
74
|
.twk-hd{display:flex;align-items:center;justify-content:space-between;
|
|
79
|
-
padding:
|
|
80
|
-
|
|
81
|
-
.twk-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
.twk-
|
|
85
|
-
.twk-body{padding:14px 16px 16px;display:flex;flex-direction:column;gap:14px;
|
|
75
|
+
padding:10px 8px 10px 14px;cursor:move;user-select:none}
|
|
76
|
+
.twk-hd b{font-size:12px;font-weight:600;letter-spacing:.01em}
|
|
77
|
+
.twk-x{appearance:none;border:0;background:transparent;color:rgba(41,38,27,.55);
|
|
78
|
+
width:22px;height:22px;border-radius:6px;cursor:default;font-size:13px;line-height:1}
|
|
79
|
+
.twk-x:hover{background:rgba(0,0,0,.06);color:#29261b}
|
|
80
|
+
.twk-body{padding:2px 14px 14px;display:flex;flex-direction:column;gap:10px;
|
|
86
81
|
overflow-y:auto;overflow-x:hidden;min-height:0;
|
|
87
|
-
scrollbar-width:thin;scrollbar-color
|
|
82
|
+
scrollbar-width:thin;scrollbar-color:rgba(0,0,0,.15) transparent}
|
|
88
83
|
.twk-body::-webkit-scrollbar{width:8px}
|
|
89
84
|
.twk-body::-webkit-scrollbar-track{background:transparent;margin:2px}
|
|
90
|
-
.twk-body::-webkit-scrollbar-thumb{background
|
|
85
|
+
.twk-body::-webkit-scrollbar-thumb{background:rgba(0,0,0,.15);border-radius:4px;
|
|
91
86
|
border:2px solid transparent;background-clip:content-box}
|
|
92
|
-
.twk-body::-webkit-scrollbar-thumb:hover{background
|
|
87
|
+
.twk-body::-webkit-scrollbar-thumb:hover{background:rgba(0,0,0,.25);
|
|
93
88
|
border:2px solid transparent;background-clip:content-box}
|
|
94
|
-
.twk-row{display:flex;flex-direction:column;gap:
|
|
89
|
+
.twk-row{display:flex;flex-direction:column;gap:5px}
|
|
95
90
|
.twk-row-h{flex-direction:row;align-items:center;justify-content:space-between;gap:10px}
|
|
96
91
|
.twk-lbl{display:flex;justify-content:space-between;align-items:baseline;
|
|
97
|
-
color
|
|
92
|
+
color:rgba(41,38,27,.72)}
|
|
98
93
|
.twk-lbl>span:first-child{font-weight:500}
|
|
99
|
-
.twk-val{color
|
|
94
|
+
.twk-val{color:rgba(41,38,27,.5);font-variant-numeric:tabular-nums}
|
|
100
95
|
|
|
101
|
-
.twk-sect{font-size:
|
|
102
|
-
color
|
|
96
|
+
.twk-sect{font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;
|
|
97
|
+
color:rgba(41,38,27,.45);padding:10px 0 0}
|
|
103
98
|
.twk-sect:first-child{padding-top:0}
|
|
104
99
|
|
|
105
|
-
.twk-field{appearance:none;box-sizing:border-box;width:100%;min-width:0;height:
|
|
106
|
-
border:.5px solid
|
|
107
|
-
background
|
|
108
|
-
.twk-field:focus{border-color
|
|
109
|
-
select.twk-field{padding-right:
|
|
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='
|
|
111
|
-
background-repeat:no-repeat;background-position:right
|
|
100
|
+
.twk-field{appearance:none;box-sizing:border-box;width:100%;min-width:0;height:26px;padding:0 8px;
|
|
101
|
+
border:.5px solid rgba(0,0,0,.1);border-radius:7px;
|
|
102
|
+
background:rgba(255,255,255,.6);color:inherit;font:inherit;outline:none}
|
|
103
|
+
.twk-field:focus{border-color:rgba(0,0,0,.25);background:rgba(255,255,255,.85)}
|
|
104
|
+
select.twk-field{padding-right:22px;
|
|
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='rgba(0,0,0,.5)' d='M0 0h10L5 6z'/></svg>");
|
|
106
|
+
background-repeat:no-repeat;background-position:right 8px center}
|
|
112
107
|
|
|
113
108
|
.twk-slider{appearance:none;-webkit-appearance:none;width:100%;height:4px;margin:6px 0;
|
|
114
|
-
border-radius:999px;background
|
|
109
|
+
border-radius:999px;background:rgba(0,0,0,.12);outline:none}
|
|
115
110
|
.twk-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;
|
|
116
|
-
width:
|
|
117
|
-
border:.5px solid
|
|
118
|
-
.twk-slider::-moz-range-thumb{width:
|
|
119
|
-
background:#fff;border:.5px solid
|
|
111
|
+
width:14px;height:14px;border-radius:50%;background:#fff;
|
|
112
|
+
border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
|
|
113
|
+
.twk-slider::-moz-range-thumb{width:14px;height:14px;border-radius:50%;
|
|
114
|
+
background:#fff;border:.5px solid rgba(0,0,0,.12);box-shadow:0 1px 3px rgba(0,0,0,.2);cursor:default}
|
|
120
115
|
|
|
121
116
|
.twk-seg{position:relative;display:flex;padding:2px;border-radius:8px;
|
|
122
|
-
background
|
|
117
|
+
background:rgba(0,0,0,.06);user-select:none}
|
|
123
118
|
.twk-seg-thumb{position:absolute;top:2px;bottom:2px;border-radius:6px;
|
|
124
|
-
background
|
|
119
|
+
background:rgba(255,255,255,.9);box-shadow:0 1px 2px rgba(0,0,0,.12);
|
|
125
120
|
transition:left .15s cubic-bezier(.3,.7,.4,1),width .15s}
|
|
126
121
|
.twk-seg.dragging .twk-seg-thumb{transition:none}
|
|
127
122
|
.twk-seg button{appearance:none;position:relative;z-index:1;flex:1;border:0;
|
|
128
|
-
background:transparent;color
|
|
123
|
+
background:transparent;color:inherit;font:inherit;font-weight:500;min-height:22px;
|
|
129
124
|
border-radius:6px;cursor:default;padding:4px 6px;line-height:1.2;
|
|
130
|
-
overflow-wrap:anywhere
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
.twk-toggle
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
.twk-num-lbl{font-weight:500;color:#646A73;cursor:ew-resize;
|
|
125
|
+
overflow-wrap:anywhere}
|
|
126
|
+
|
|
127
|
+
.twk-toggle{position:relative;width:32px;height:18px;border:0;border-radius:999px;
|
|
128
|
+
background:rgba(0,0,0,.15);transition:background .15s;cursor:default;padding:0}
|
|
129
|
+
.twk-toggle[data-on="1"]{background:#34c759}
|
|
130
|
+
.twk-toggle i{position:absolute;top:2px;left:2px;width:14px;height:14px;border-radius:50%;
|
|
131
|
+
background:#fff;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:transform .15s}
|
|
132
|
+
.twk-toggle[data-on="1"] i{transform:translateX(14px)}
|
|
133
|
+
|
|
134
|
+
.twk-num{display:flex;align-items:center;box-sizing:border-box;min-width:0;height:26px;padding:0 0 0 8px;
|
|
135
|
+
border:.5px solid rgba(0,0,0,.1);border-radius:7px;background:rgba(255,255,255,.6)}
|
|
136
|
+
.twk-num-lbl{font-weight:500;color:rgba(41,38,27,.6);cursor:ew-resize;
|
|
143
137
|
user-select:none;padding-right:8px}
|
|
144
138
|
.twk-num input{flex:1;min-width:0;height:100%;border:0;background:transparent;
|
|
145
|
-
font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0
|
|
139
|
+
font:inherit;font-variant-numeric:tabular-nums;text-align:right;padding:0 8px 0 0;
|
|
146
140
|
outline:none;color:inherit;-moz-appearance:textfield}
|
|
147
141
|
.twk-num input::-webkit-inner-spin-button,.twk-num input::-webkit-outer-spin-button{
|
|
148
142
|
-webkit-appearance:none;margin:0}
|
|
149
|
-
.twk-num-unit{padding-right:
|
|
143
|
+
.twk-num-unit{padding-right:8px;color:rgba(41,38,27,.45)}
|
|
150
144
|
|
|
151
|
-
.twk-btn{appearance:none;height:
|
|
152
|
-
background
|
|
153
|
-
.twk-btn:hover{background
|
|
154
|
-
.twk-btn.secondary{background
|
|
155
|
-
.twk-btn.secondary:hover{background
|
|
145
|
+
.twk-btn{appearance:none;height:26px;padding:0 12px;border:0;border-radius:7px;
|
|
146
|
+
background:rgba(0,0,0,.78);color:#fff;font:inherit;font-weight:500;cursor:default}
|
|
147
|
+
.twk-btn:hover{background:rgba(0,0,0,.88)}
|
|
148
|
+
.twk-btn.secondary{background:rgba(0,0,0,.06);color:inherit}
|
|
149
|
+
.twk-btn.secondary:hover{background:rgba(0,0,0,.1)}
|
|
156
150
|
|
|
157
|
-
.twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:
|
|
158
|
-
border:.5px solid
|
|
151
|
+
.twk-swatch{appearance:none;-webkit-appearance:none;width:56px;height:22px;
|
|
152
|
+
border:.5px solid rgba(0,0,0,.1);border-radius:6px;padding:0;cursor:default;
|
|
159
153
|
background:transparent;flex-shrink:0}
|
|
160
154
|
.twk-swatch::-webkit-color-swatch-wrapper{padding:0}
|
|
161
155
|
.twk-swatch::-webkit-color-swatch{border:0;border-radius:5.5px}
|
|
162
156
|
.twk-swatch::-moz-color-swatch{border:0;border-radius:5.5px}
|
|
163
157
|
|
|
164
|
-
.twk-chips{display:flex;gap:
|
|
165
|
-
.twk-chip{position:relative;appearance:none;flex:1;min-width:0;height:
|
|
158
|
+
.twk-chips{display:flex;gap:6px}
|
|
159
|
+
.twk-chip{position:relative;appearance:none;flex:1;min-width:0;height:46px;
|
|
166
160
|
padding:0;border:0;border-radius:6px;overflow:hidden;cursor:default;
|
|
167
|
-
box-shadow:0 0 0 .5px rgba(0,0,0,.
|
|
161
|
+
box-shadow:0 0 0 .5px rgba(0,0,0,.12),0 1px 2px rgba(0,0,0,.06);
|
|
168
162
|
transition:transform .12s cubic-bezier(.3,.7,.4,1),box-shadow .12s}
|
|
169
163
|
.twk-chip:hover{transform:translateY(-1px);
|
|
170
|
-
box-shadow:0 0 0 .5px rgba(0,0,0,.
|
|
171
|
-
.twk-chip[data-on="1"]{box-shadow:0 0 0
|
|
164
|
+
box-shadow:0 0 0 .5px rgba(0,0,0,.18),0 4px 10px rgba(0,0,0,.12)}
|
|
165
|
+
.twk-chip[data-on="1"]{box-shadow:0 0 0 1.5px rgba(0,0,0,.85),
|
|
172
166
|
0 2px 6px rgba(0,0,0,.15)}
|
|
173
167
|
.twk-chip>span{position:absolute;top:0;bottom:0;right:0;width:34%;
|
|
174
168
|
display:flex;flex-direction:column;box-shadow:-1px 0 0 rgba(0,0,0,.1)}
|
|
@@ -208,10 +202,6 @@ function useTweaks(defaults) {
|
|
|
208
202
|
function TweaksPanel({ title = 'Tweaks', children }) {
|
|
209
203
|
const [open, setOpen] = React.useState(false);
|
|
210
204
|
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.
|
|
215
205
|
const offsetRef = React.useRef({ x: 16, y: 16 });
|
|
216
206
|
const PAD = 16;
|
|
217
207
|
|
|
@@ -220,13 +210,13 @@ function TweaksPanel({ title = 'Tweaks', children }) {
|
|
|
220
210
|
if (!panel) return;
|
|
221
211
|
const w = panel.offsetWidth, h = panel.offsetHeight;
|
|
222
212
|
const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
|
|
223
|
-
const
|
|
213
|
+
const maxBottom = Math.max(PAD, window.innerHeight - h - PAD);
|
|
224
214
|
offsetRef.current = {
|
|
225
215
|
x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
|
|
226
|
-
y: Math.min(
|
|
216
|
+
y: Math.min(maxBottom, Math.max(PAD, offsetRef.current.y)),
|
|
227
217
|
};
|
|
228
218
|
panel.style.right = offsetRef.current.x + 'px';
|
|
229
|
-
panel.style.
|
|
219
|
+
panel.style.bottom = offsetRef.current.y + 'px';
|
|
230
220
|
}, []);
|
|
231
221
|
|
|
232
222
|
React.useEffect(() => {
|
|
@@ -263,11 +253,11 @@ function TweaksPanel({ title = 'Tweaks', children }) {
|
|
|
263
253
|
const r = panel.getBoundingClientRect();
|
|
264
254
|
const sx = e.clientX, sy = e.clientY;
|
|
265
255
|
const startRight = window.innerWidth - r.right;
|
|
266
|
-
const
|
|
256
|
+
const startBottom = window.innerHeight - r.bottom;
|
|
267
257
|
const move = (ev) => {
|
|
268
258
|
offsetRef.current = {
|
|
269
259
|
x: startRight - (ev.clientX - sx),
|
|
270
|
-
y:
|
|
260
|
+
y: startBottom - (ev.clientY - sy),
|
|
271
261
|
};
|
|
272
262
|
clampToViewport();
|
|
273
263
|
};
|
|
@@ -284,7 +274,7 @@ function TweaksPanel({ title = 'Tweaks', children }) {
|
|
|
284
274
|
<>
|
|
285
275
|
<style>{__TWEAKS_STYLE}</style>
|
|
286
276
|
<div ref={dragRef} className="twk-panel" data-miaoda-chrome=""
|
|
287
|
-
style={{ right: offsetRef.current.x,
|
|
277
|
+
style={{ right: offsetRef.current.x, bottom: offsetRef.current.y }}>
|
|
288
278
|
<div className="twk-hd" onMouseDown={onDragStart}>
|
|
289
279
|
<b>{title}</b>
|
|
290
280
|
<button className="twk-x" aria-label="Close tweaks"
|
|
@@ -353,8 +343,8 @@ function TweakRadio({ label, value, options, onChange }) {
|
|
|
353
343
|
valueRef.current = value;
|
|
354
344
|
|
|
355
345
|
// Segments wrap mid-word once per-segment width runs out. The track is
|
|
356
|
-
// ~
|
|
357
|
-
// to its own padding, and
|
|
346
|
+
// ~248px (280 panel − 28 body pad − 4 seg pad), each button loses 12px
|
|
347
|
+
// to its own padding, and 11.5px system-ui averages ~6.3px/char — so 2
|
|
358
348
|
// options fit ~16 chars each, 3 fit ~10. Past that (or >3 options), fall
|
|
359
349
|
// back to a dropdown rather than wrap.
|
|
360
350
|
const labelLen = (o) => String(typeof o === 'object' ? o.label : o).length;
|
|
@@ -565,23 +555,23 @@ Object.assign(window, {
|
|
|
565
555
|
@keyframes twk-blink{50%{opacity:0}}
|
|
566
556
|
@keyframes twk-fadein{from{opacity:0;transform:translateX(4px)}to{opacity:1;transform:none}}
|
|
567
557
|
.twk-sugg{display:flex;align-items:center;gap:6px;padding:5px 8px;border-radius:8px;
|
|
568
|
-
background
|
|
569
|
-
.twk-sugg:focus-within{background
|
|
558
|
+
background:rgba(0,0,0,.04);border:.5px solid rgba(0,0,0,.06);transition:all .15s}
|
|
559
|
+
.twk-sugg:focus-within{background:rgba(0,0,0,.06);border-color:rgba(0,0,0,.12)}
|
|
570
560
|
.twk-sugg-field{position:relative;flex:1;min-width:0}
|
|
571
561
|
.twk-sugg-field input{width:100%;height:20px;border:0;background:transparent;
|
|
572
562
|
font:inherit;outline:none;color:inherit}
|
|
573
563
|
.twk-sugg-ghost{position:absolute;inset:0;display:flex;align-items:center;
|
|
574
|
-
color
|
|
575
|
-
.twk-sugg-ghost.hint{color
|
|
564
|
+
color:rgba(41,38,27,.42);pointer-events:none;white-space:nowrap;overflow:hidden}
|
|
565
|
+
.twk-sugg-ghost.hint{color:rgba(41,38,27,.28)}
|
|
576
566
|
.twk-sugg-caret{display:inline-block;width:1px;height:13px;margin-left:1px;
|
|
577
567
|
border-right:1.5px solid currentColor;opacity:.5;animation:twk-blink 1s step-end infinite}
|
|
578
568
|
.twk-sugg-ideas{appearance:none;border:0;background:transparent;font:inherit;
|
|
579
|
-
font-size:10.5px;font-weight:600;color
|
|
569
|
+
font-size:10.5px;font-weight:600;color:rgba(41,38,27,.6);cursor:default;padding:0 2px;
|
|
580
570
|
white-space:nowrap;animation:twk-fadein .25s ease}
|
|
581
|
-
.twk-sugg-ideas:hover{color
|
|
571
|
+
.twk-sugg-ideas:hover{color:rgba(41,38,27,.85)}
|
|
582
572
|
.twk-sugg-ideas svg{color:#D97757}
|
|
583
573
|
.twk-sugg-send{appearance:none;border:0;height:20px;padding:0 8px;border-radius:5px;
|
|
584
|
-
background:#
|
|
574
|
+
background:#29261b;color:#fff;font:inherit;font-size:10px;font-weight:600;cursor:default}
|
|
585
575
|
`;
|
|
586
576
|
document.head.appendChild(s);
|
|
587
577
|
})();
|
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.11-alpha.20260729134521",
|
|
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,11 +38,15 @@
|
|
|
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
|
-
*
|
|
45
|
-
*
|
|
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.
|
|
45
|
+
* (h) preflight mode — add `?preflight=1` to the URL to enter
|
|
46
|
+
* mobile-list layout on any device (including desktop). Identical to
|
|
47
|
+
* mobile browse except the outer frame background is transparent,
|
|
48
|
+
* preventing the model from confusing the frame background with slide
|
|
49
|
+
* content during automated visual inspection.
|
|
46
50
|
*
|
|
47
51
|
* Slides are HIDDEN, not unmounted. Non-active slides stay in the DOM with
|
|
48
52
|
* `visibility: hidden` + `opacity: 0`, so their state (videos, iframes,
|
|
@@ -140,7 +144,6 @@
|
|
|
140
144
|
const MOB_RADIUS = 8; // card corner radius
|
|
141
145
|
const MOB_BADGE_IDLE_MS = 2000; // scroll idle before the page badge fades
|
|
142
146
|
const MOB_UI_HIDE_MS = 3000; // presenting controls auto-hide
|
|
143
|
-
const CANVAS_RECAP_MS = 1200; // re-snapshot canvas thumbs once chart entrance animations settle
|
|
144
147
|
|
|
145
148
|
const pad2 = (n) => String(n).padStart(2, '0');
|
|
146
149
|
|
|
@@ -433,6 +436,12 @@
|
|
|
433
436
|
(The equivalent rule further down lives inside @media print only.) */
|
|
434
437
|
:host([data-mobile-list]) ::slotted([data-deck-skip]) { display: none !important; }
|
|
435
438
|
|
|
439
|
+
/* ── Preflight (质检) mode: mobile-list layout with transparent outer
|
|
440
|
+
background so the model doesn't confuse the frame with slide content.
|
|
441
|
+
Activated by ?preflight=1 in the URL. ── */
|
|
442
|
+
:host([data-preflight]) { background: transparent; position: static; overflow: visible; }
|
|
443
|
+
:host([data-preflight]) .stage { background: transparent; position: static; overflow: visible; }
|
|
444
|
+
|
|
436
445
|
/* ── Mobile presenting: CSS pseudo-landscape (no orientation.lock) ──
|
|
437
446
|
Rotate only while the device is physically portrait. Once the user
|
|
438
447
|
turns the phone (system auto-rotate) the viewport itself is landscape,
|
|
@@ -703,6 +712,7 @@
|
|
|
703
712
|
// windowed margin/shadow, see _fit. A bare load with no #N hash starts at
|
|
704
713
|
// slide 1, so ?thumbnail=1 alone yields a clean first-slide frame.
|
|
705
714
|
this._snthumb = /[?&](_snthumb|thumbnail)=/.test(location.search);
|
|
715
|
+
this._preflight = /[?&]preflight=/.test(location.search);
|
|
706
716
|
this._mobile = isMobileUA();
|
|
707
717
|
// Layout-driven size signal. window.resize is unreliable inside
|
|
708
718
|
// prerendered/reframed iOS WebViews and host-managed iframes — the
|
|
@@ -715,7 +725,14 @@
|
|
|
715
725
|
this._mobSizeRo.observe(document.documentElement);
|
|
716
726
|
}
|
|
717
727
|
this._syncMobileMode();
|
|
718
|
-
if (this._mobile) this._syncMobFreezeRule();
|
|
728
|
+
if (this._mobile || this._preflight) this._syncMobFreezeRule();
|
|
729
|
+
if (this._preflight) {
|
|
730
|
+
this.setAttribute('data-preflight', '');
|
|
731
|
+
// Override page-level background set by LLM-authored CSS so the
|
|
732
|
+
// preflight screenshot doesn't pick up the theme's body color.
|
|
733
|
+
document.documentElement.style.background = '#fff';
|
|
734
|
+
document.body.style.background = '#fff';
|
|
735
|
+
}
|
|
719
736
|
if (this._snthumb) this.setAttribute('no-rail', '');
|
|
720
737
|
this._render();
|
|
721
738
|
this._loadNotes();
|
|
@@ -1039,7 +1056,6 @@
|
|
|
1039
1056
|
if (this._scaleRaf) cancelAnimationFrame(this._scaleRaf);
|
|
1040
1057
|
if (this._liveObserver) this._liveObserver.disconnect();
|
|
1041
1058
|
if (this._railObserver) this._railObserver.disconnect();
|
|
1042
|
-
(this._thumbs || []).forEach((t) => clearTimeout(t._canvasRecapTimer));
|
|
1043
1059
|
if (this._onTweakChange) window.removeEventListener('tweakchange', this._onTweakChange);
|
|
1044
1060
|
}
|
|
1045
1061
|
|
|
@@ -1367,38 +1383,26 @@
|
|
|
1367
1383
|
'animation-play-state: running !important; transition-duration: 0s !important; } }';
|
|
1368
1384
|
}
|
|
1369
1385
|
|
|
1370
|
-
/** Mobile browse (data-mobile-list)
|
|
1371
|
-
*
|
|
1372
|
-
*
|
|
1373
|
-
*
|
|
1374
|
-
*
|
|
1375
|
-
*
|
|
1376
|
-
*
|
|
1377
|
-
*
|
|
1378
|
-
*
|
|
1379
|
-
*
|
|
1380
|
-
* `animation … !important` that out-cascades our name:none still
|
|
1381
|
-
* collapses to an instant run. The attribute selector is repeated to
|
|
1382
|
-
* out-rank authored !important rules — important-vs-important resolves
|
|
1383
|
-
* by specificity, and the common `[data-deck-active] .x` gate is only
|
|
1384
|
-
* (0,2,0). Document-level like the print rule: author animations style
|
|
1385
|
-
* light-DOM slide content that shadow CSS can't reach, and <deck-stage>
|
|
1386
|
-
* is a light-DOM element document CSS can key on. Presenting/desktop
|
|
1387
|
-
* keep their animations — the rule is keyed on the host attribute, no
|
|
1388
|
-
* JS toggling needed. */
|
|
1386
|
+
/** Mobile browse (data-mobile-list) force-shows every card at once, so
|
|
1387
|
+
* authored CSS animations all fire together on load — and the ones gated
|
|
1388
|
+
* on [data-deck-active] re-fire as scrolling moves that flag between
|
|
1389
|
+
* cards. Freeze them at their end state while browsing. Same jump-to-end
|
|
1390
|
+
* recipe as the print rule above (NOT animation:none, so a deck that
|
|
1391
|
+
* keeps a hidden base state still renders its content), keyed on the
|
|
1392
|
+
* host attribute so presenting/desktop keep their animations and no JS
|
|
1393
|
+
* toggling is needed. Document-level like the print rule: author
|
|
1394
|
+
* animations style light-DOM slide content that shadow CSS can't reach,
|
|
1395
|
+
* and <deck-stage> itself is a light-DOM element document CSS can key on. */
|
|
1389
1396
|
_syncMobFreezeRule() {
|
|
1390
1397
|
const id = 'deck-stage-mob-freeze';
|
|
1391
1398
|
if (document.getElementById(id)) return;
|
|
1392
|
-
const sel = 'deck-stage[data-mobile-list][data-mobile-list][data-mobile-list]';
|
|
1393
1399
|
const tag = document.createElement('style');
|
|
1394
1400
|
tag.id = id;
|
|
1395
1401
|
tag.textContent =
|
|
1396
|
-
|
|
1397
|
-
' animation-name: none !important;' +
|
|
1402
|
+
'deck-stage[data-mobile-list] *, deck-stage[data-mobile-list] *::before, deck-stage[data-mobile-list] *::after {' +
|
|
1398
1403
|
' animation-delay: -99s !important; animation-duration: .001s !important;' +
|
|
1399
1404
|
' animation-iteration-count: 1 !important; animation-fill-mode: both !important;' +
|
|
1400
|
-
' animation-play-state: running !important;'
|
|
1401
|
-
' transition-duration: 0s !important; transition-delay: 0s !important; }';
|
|
1405
|
+
' animation-play-state: running !important; transition-duration: 0s !important; }';
|
|
1402
1406
|
document.head.appendChild(tag);
|
|
1403
1407
|
}
|
|
1404
1408
|
|
|
@@ -1546,11 +1550,11 @@
|
|
|
1546
1550
|
|
|
1547
1551
|
// The two mobile presentations are mutually exclusive. noscale and
|
|
1548
1552
|
// _snthumb are capture modes whose output must keep desktop geometry,
|
|
1549
|
-
// so they opt out of both.
|
|
1553
|
+
// so they opt out of both. ?preflight=1 forces mobile-list on desktop.
|
|
1550
1554
|
_syncMobileMode() {
|
|
1551
|
-
const eligible = this._mobile && !this.hasAttribute('noscale') && !this._snthumb;
|
|
1555
|
+
const eligible = (this._mobile || this._preflight) && !this.hasAttribute('noscale') && !this._snthumb;
|
|
1552
1556
|
this.toggleAttribute('data-mobile-list', eligible && !this._presenting);
|
|
1553
|
-
this.toggleAttribute('data-mobile-present', eligible && !!this._presenting);
|
|
1557
|
+
this.toggleAttribute('data-mobile-present', eligible && !!this._presenting && !this._preflight);
|
|
1554
1558
|
}
|
|
1555
1559
|
|
|
1556
1560
|
_fitMobileList() {
|
|
@@ -1956,7 +1960,7 @@
|
|
|
1956
1960
|
// it scales clones by the rail's _thumbScale, so rewrite the
|
|
1957
1961
|
// transform for this strip's fixed 132px frames.
|
|
1958
1962
|
const entry = { slide: e2.slide, frame, host: null, clone: null };
|
|
1959
|
-
this._materialize(entry
|
|
1963
|
+
this._materialize(entry);
|
|
1960
1964
|
if (entry.clone) entry.clone.style.transform = 'scale(' + (132 / this.designWidth) + ')';
|
|
1961
1965
|
return { el: t, i: e2.i };
|
|
1962
1966
|
});
|
|
@@ -2078,7 +2082,6 @@
|
|
|
2078
2082
|
// Orphans — slides removed since last render.
|
|
2079
2083
|
bySlide.forEach((t) => {
|
|
2080
2084
|
if (this._railObserver) this._railObserver.unobserve(t.frame);
|
|
2081
|
-
clearTimeout(t._canvasRecapTimer);
|
|
2082
2085
|
t.thumb.remove();
|
|
2083
2086
|
});
|
|
2084
2087
|
// Put thumbs into document order to match _slides. insertBefore on
|
|
@@ -2260,7 +2263,7 @@
|
|
|
2260
2263
|
}
|
|
2261
2264
|
|
|
2262
2265
|
/** Lazily build the clone for a thumb that has scrolled into view. */
|
|
2263
|
-
_materialize(entry
|
|
2266
|
+
_materialize(entry) {
|
|
2264
2267
|
if (entry.host) return;
|
|
2265
2268
|
const dw = this.designWidth, dh = this.designHeight;
|
|
2266
2269
|
let clone = entry.slide.cloneNode(true);
|
|
@@ -2292,7 +2295,6 @@
|
|
|
2292
2295
|
// drawings appear in the thumbnail instead of a blank rectangle.
|
|
2293
2296
|
const origCanvases = entry.slide.querySelectorAll('canvas');
|
|
2294
2297
|
const cloneCanvases = clone.querySelectorAll('canvas');
|
|
2295
|
-
const canvasImgs = []; // {orig, img} pairs kept for the delayed re-snapshot below
|
|
2296
2298
|
cloneCanvases.forEach((el, ci) => {
|
|
2297
2299
|
const orig = origCanvases[ci];
|
|
2298
2300
|
if (!orig || !orig.width || !orig.height) return;
|
|
@@ -2304,7 +2306,6 @@
|
|
|
2304
2306
|
';width:' + orig.offsetWidth + 'px;height:' + orig.offsetHeight + 'px;';
|
|
2305
2307
|
img.className = el.className;
|
|
2306
2308
|
el.replaceWith(img);
|
|
2307
|
-
canvasImgs.push({ orig, img });
|
|
2308
2309
|
} catch (e) {}
|
|
2309
2310
|
});
|
|
2310
2311
|
// Images: defer decode and let the browser pick the smallest
|
|
@@ -2369,30 +2370,6 @@
|
|
|
2369
2370
|
// Once materialized the IO callback is a no-op early-return —
|
|
2370
2371
|
// unobserve so scroll doesn't keep firing it.
|
|
2371
2372
|
if (this._railObserver) this._railObserver.unobserve(entry.frame);
|
|
2372
|
-
// Chart libraries (ECharts/Chart.js) animate their entrance over ~1s and
|
|
2373
|
-
// draw straight to the canvas bitmap — no DOM mutation, so _liveObserver
|
|
2374
|
-
// never sees the finished frame and the toDataURL snapshot above may have
|
|
2375
|
-
// caught a blank or mid-animation canvas. Re-snapshot once after the
|
|
2376
|
-
// animation settles and overwrite each <img>. Guarded so a thumb that was
|
|
2377
|
-
// refreshed (host swapped) or removed (img detached) since is left alone.
|
|
2378
|
-
// scheduleRecap=false for callers whose entry isn't tracked in _thumbs
|
|
2379
|
-
// (the mobile filmstrip builds throwaway entries the cleanup paths can't
|
|
2380
|
-
// reach) — otherwise the timer leaks and, since that path clones the
|
|
2381
|
-
// whole deck eagerly, N recaps would fire together as a toDataURL storm.
|
|
2382
|
-
if (scheduleRecap && canvasImgs.length) {
|
|
2383
|
-
clearTimeout(entry._canvasRecapTimer);
|
|
2384
|
-
entry._canvasRecapTimer = setTimeout(() => {
|
|
2385
|
-
entry._canvasRecapTimer = null;
|
|
2386
|
-
if (entry.host !== host) return;
|
|
2387
|
-
canvasImgs.forEach(({ orig, img }) => {
|
|
2388
|
-
// orig.isConnected: if the app swapped the chart's <canvas> since,
|
|
2389
|
-
// the detached node still reports nonzero width/height — skip it so
|
|
2390
|
-
// we don't paint stale pixels (the swap already re-cloned the thumb).
|
|
2391
|
-
if (!img.isConnected || !orig.isConnected || !orig.width || !orig.height) return;
|
|
2392
|
-
try { img.src = orig.toDataURL(); } catch (e) {}
|
|
2393
|
-
});
|
|
2394
|
-
}, CANVAS_RECAP_MS);
|
|
2395
|
-
}
|
|
2396
2373
|
}
|
|
2397
2374
|
|
|
2398
2375
|
/** Re-clone a single thumb (live-update path). No-op if the thumb
|
|
@@ -2401,7 +2378,6 @@
|
|
|
2401
2378
|
_refreshThumb(slide) {
|
|
2402
2379
|
const entry = (this._thumbs || []).find((t) => t.slide === slide);
|
|
2403
2380
|
if (!entry || !entry.host) return;
|
|
2404
|
-
clearTimeout(entry._canvasRecapTimer);
|
|
2405
2381
|
entry.host.remove();
|
|
2406
2382
|
entry.host = entry.clone = null;
|
|
2407
2383
|
this._materialize(entry);
|