@lark-apaas/coding-registry-buildless 0.1.7 → 0.1.8-alpha.20260720141429

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.
@@ -63,9 +63,9 @@
63
63
  // ─────────────────────────────────────────────────────────────────────────────
64
64
 
65
65
  const __TWEAKS_STYLE = `
66
- .twk-panel{position:fixed;right:16px;bottom:16px;z-index:2147483646;width:280px;
66
+ .twk-panel{position:fixed;right:16px;top:16px;z-index:2147483646;width:280px;
67
67
  max-height:calc(100vh - 32px);display:flex;flex-direction:column;
68
- transform:scale(var(--dc-inv-zoom,1));transform-origin:bottom right;
68
+ transform:scale(var(--dc-inv-zoom,1));transform-origin:top right;
69
69
  background:rgba(250,249,247,.78);color:#29261b;
70
70
  -webkit-backdrop-filter:blur(24px) saturate(160%);backdrop-filter:blur(24px) saturate(160%);
71
71
  border:.5px solid rgba(255,255,255,.6);border-radius:14px;
@@ -202,6 +202,10 @@ function useTweaks(defaults) {
202
202
  function TweaksPanel({ title = 'Tweaks', children }) {
203
203
  const [open, setOpen] = React.useState(false);
204
204
  const dragRef = React.useRef(null);
205
+ // x = distance from the viewport's right edge, y = distance from its TOP.
206
+ // The panel anchors top-right (right/top in CSS) so it clears the host's
207
+ // bottom toolbar; both axes still grow inward, so clamp/drag math is
208
+ // symmetric with the old bottom-right anchor.
205
209
  const offsetRef = React.useRef({ x: 16, y: 16 });
206
210
  const PAD = 16;
207
211
 
@@ -210,13 +214,13 @@ function TweaksPanel({ title = 'Tweaks', children }) {
210
214
  if (!panel) return;
211
215
  const w = panel.offsetWidth, h = panel.offsetHeight;
212
216
  const maxRight = Math.max(PAD, window.innerWidth - w - PAD);
213
- const maxBottom = Math.max(PAD, window.innerHeight - h - PAD);
217
+ const maxTop = Math.max(PAD, window.innerHeight - h - PAD);
214
218
  offsetRef.current = {
215
219
  x: Math.min(maxRight, Math.max(PAD, offsetRef.current.x)),
216
- y: Math.min(maxBottom, Math.max(PAD, offsetRef.current.y)),
220
+ y: Math.min(maxTop, Math.max(PAD, offsetRef.current.y)),
217
221
  };
218
222
  panel.style.right = offsetRef.current.x + 'px';
219
- panel.style.bottom = offsetRef.current.y + 'px';
223
+ panel.style.top = offsetRef.current.y + 'px';
220
224
  }, []);
221
225
 
222
226
  React.useEffect(() => {
@@ -253,11 +257,11 @@ function TweaksPanel({ title = 'Tweaks', children }) {
253
257
  const r = panel.getBoundingClientRect();
254
258
  const sx = e.clientX, sy = e.clientY;
255
259
  const startRight = window.innerWidth - r.right;
256
- const startBottom = window.innerHeight - r.bottom;
260
+ const startTop = r.top;
257
261
  const move = (ev) => {
258
262
  offsetRef.current = {
259
263
  x: startRight - (ev.clientX - sx),
260
- y: startBottom - (ev.clientY - sy),
264
+ y: startTop + (ev.clientY - sy),
261
265
  };
262
266
  clampToViewport();
263
267
  };
@@ -274,7 +278,7 @@ function TweaksPanel({ title = 'Tweaks', children }) {
274
278
  <>
275
279
  <style>{__TWEAKS_STYLE}</style>
276
280
  <div ref={dragRef} className="twk-panel" data-miaoda-chrome=""
277
- style={{ right: offsetRef.current.x, bottom: offsetRef.current.y }}>
281
+ style={{ right: offsetRef.current.x, top: offsetRef.current.y }}>
278
282
  <div className="twk-hd" onMouseDown={onDragStart}>
279
283
  <b>{title}</b>
280
284
  <button className="twk-x" aria-label="Close tweaks"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/coding-registry-buildless",
3
- "version": "0.1.7",
3
+ "version": "0.1.8-alpha.20260720141429",
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": [
@@ -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 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
+ * 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
  *
46
47
  * Slides are HIDDEN, not unmounted. Non-active slides stay in the DOM with
47
48
  * `visibility: hidden` + `opacity: 0`, so their state (videos, iframes,
@@ -139,6 +140,7 @@
139
140
  const MOB_RADIUS = 8; // card corner radius
140
141
  const MOB_BADGE_IDLE_MS = 2000; // scroll idle before the page badge fades
141
142
  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
142
144
 
143
145
  const pad2 = (n) => String(n).padStart(2, '0');
144
146
 
@@ -1037,6 +1039,7 @@
1037
1039
  if (this._scaleRaf) cancelAnimationFrame(this._scaleRaf);
1038
1040
  if (this._liveObserver) this._liveObserver.disconnect();
1039
1041
  if (this._railObserver) this._railObserver.disconnect();
1042
+ (this._thumbs || []).forEach((t) => clearTimeout(t._canvasRecapTimer));
1040
1043
  if (this._onTweakChange) window.removeEventListener('tweakchange', this._onTweakChange);
1041
1044
  }
1042
1045
 
@@ -1364,26 +1367,38 @@
1364
1367
  'animation-play-state: running !important; transition-duration: 0s !important; } }';
1365
1368
  }
1366
1369
 
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. */
1370
+ /** Mobile browse (data-mobile-list) renders each card's static BASE
1371
+ * state — the same semantic as the thumbnail rail, print and
1372
+ * reduced-motion. The USAGE authoring contract makes the base style the
1373
+ * visible end-state (hidden lives only inside keyframes' `from`, gated
1374
+ * on [data-deck-active]), so animation-name:none shows exactly the
1375
+ * intended slide. Freezing at the END state instead would key visible
1376
+ * styling to a flag that MOVES during scroll: keyframe properties
1377
+ * (transform especially — it replaces the base value outright) snap on
1378
+ * and off as [data-deck-active] arrives at and leaves each card,
1379
+ * jolting layout. The timing longhands stay as a backstop: an authored
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. */
1377
1389
  _syncMobFreezeRule() {
1378
1390
  const id = 'deck-stage-mob-freeze';
1379
1391
  if (document.getElementById(id)) return;
1392
+ const sel = 'deck-stage[data-mobile-list][data-mobile-list][data-mobile-list]';
1380
1393
  const tag = document.createElement('style');
1381
1394
  tag.id = id;
1382
1395
  tag.textContent =
1383
- 'deck-stage[data-mobile-list] *, deck-stage[data-mobile-list] *::before, deck-stage[data-mobile-list] *::after {' +
1396
+ sel + ' *, ' + sel + ' *::before, ' + sel + ' *::after {' +
1397
+ ' animation-name: none !important;' +
1384
1398
  ' animation-delay: -99s !important; animation-duration: .001s !important;' +
1385
1399
  ' animation-iteration-count: 1 !important; animation-fill-mode: both !important;' +
1386
- ' animation-play-state: running !important; transition-duration: 0s !important; }';
1400
+ ' animation-play-state: running !important;' +
1401
+ ' transition-duration: 0s !important; transition-delay: 0s !important; }';
1387
1402
  document.head.appendChild(tag);
1388
1403
  }
1389
1404
 
@@ -2063,6 +2078,7 @@
2063
2078
  // Orphans — slides removed since last render.
2064
2079
  bySlide.forEach((t) => {
2065
2080
  if (this._railObserver) this._railObserver.unobserve(t.frame);
2081
+ clearTimeout(t._canvasRecapTimer);
2066
2082
  t.thumb.remove();
2067
2083
  });
2068
2084
  // Put thumbs into document order to match _slides. insertBefore on
@@ -2276,6 +2292,7 @@
2276
2292
  // drawings appear in the thumbnail instead of a blank rectangle.
2277
2293
  const origCanvases = entry.slide.querySelectorAll('canvas');
2278
2294
  const cloneCanvases = clone.querySelectorAll('canvas');
2295
+ const canvasImgs = []; // {orig, img} pairs kept for the delayed re-snapshot below
2279
2296
  cloneCanvases.forEach((el, ci) => {
2280
2297
  const orig = origCanvases[ci];
2281
2298
  if (!orig || !orig.width || !orig.height) return;
@@ -2287,6 +2304,7 @@
2287
2304
  ';width:' + orig.offsetWidth + 'px;height:' + orig.offsetHeight + 'px;';
2288
2305
  img.className = el.className;
2289
2306
  el.replaceWith(img);
2307
+ canvasImgs.push({ orig, img });
2290
2308
  } catch (e) {}
2291
2309
  });
2292
2310
  // Images: defer decode and let the browser pick the smallest
@@ -2351,6 +2369,23 @@
2351
2369
  // Once materialized the IO callback is a no-op early-return —
2352
2370
  // unobserve so scroll doesn't keep firing it.
2353
2371
  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
+ if (canvasImgs.length) {
2379
+ clearTimeout(entry._canvasRecapTimer);
2380
+ entry._canvasRecapTimer = setTimeout(() => {
2381
+ entry._canvasRecapTimer = null;
2382
+ if (entry.host !== host) return;
2383
+ canvasImgs.forEach(({ orig, img }) => {
2384
+ if (!img.isConnected || !orig.width || !orig.height) return;
2385
+ try { img.src = orig.toDataURL(); } catch (e) {}
2386
+ });
2387
+ }, CANVAS_RECAP_MS);
2388
+ }
2354
2389
  }
2355
2390
 
2356
2391
  /** Re-clone a single thumb (live-update path). No-op if the thumb
@@ -2359,6 +2394,7 @@
2359
2394
  _refreshThumb(slide) {
2360
2395
  const entry = (this._thumbs || []).find((t) => t.slide === slide);
2361
2396
  if (!entry || !entry.host) return;
2397
+ clearTimeout(entry._canvasRecapTimer);
2362
2398
  entry.host.remove();
2363
2399
  entry.host = entry.clone = null;
2364
2400
  this._materialize(entry);