@21stware/rpui 0.4.2 → 0.4.4

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.
@@ -1,4 +1,73 @@
1
1
  import { registerAll, parseToPage } from "./rpui.js";
2
+ const THEME_STYLE_ID = "rpml-theme-style";
3
+ const ATTR = "data-rpml-theme";
4
+ const THEME_CSS = `
5
+ :root {
6
+ --rpml-gx-border:#e5e7eb; --rpml-gx-side-bg:#fff; --rpml-gx-main-bg:#f4f6f8;
7
+ --rpml-gx-fg:#111827; --rpml-gx-muted:#6b7280; --rpml-gx-group:#9ca3af;
8
+ --rpml-gx-hover:#f3f4f6; --rpml-gx-item:#374151; --rpml-gx-active-bg:#eff6ff;
9
+ --rpml-gx-active-fg:#1d4ed8; --rpml-gx-copy-hover:#e5e7eb; --rpml-gx-ok:#059669;
10
+ }
11
+ html[${ATTR}="dark"] {
12
+ --rpml-gx-border:#2a3344; --rpml-gx-side-bg:#0f172a; --rpml-gx-main-bg:#0b1120;
13
+ --rpml-gx-fg:#e2e8f0; --rpml-gx-muted:#94a3b8; --rpml-gx-group:#64748b;
14
+ --rpml-gx-hover:#1e293b; --rpml-gx-item:#cbd5e1; --rpml-gx-active-bg:#1e3a5f;
15
+ --rpml-gx-active-fg:#93c5fd; --rpml-gx-copy-hover:#334155; --rpml-gx-ok:#34d399;
16
+ }
17
+ html[${ATTR}="dark"] body { background:#0b1120; }
18
+ /* Invert the rendered prototype as a whole; hue-rotate keeps colors recognizable. */
19
+ html[${ATTR}="dark"] page-el { filter:invert(0.92) hue-rotate(180deg); }
20
+
21
+ .rpml-theme-fab {
22
+ position:fixed; top:12px; right:12px; z-index:50; display:flex; align-items:center;
23
+ justify-content:center; width:34px; height:34px; padding:0; border:1px solid var(--rpml-gx-border);
24
+ border-radius:9px; background:var(--rpml-gx-side-bg); color:var(--rpml-gx-fg); font-size:16px;
25
+ line-height:1; cursor:pointer; box-shadow:0 1px 3px rgba(0,0,0,.12);
26
+ }
27
+ .rpml-theme-fab:hover { background:var(--rpml-gx-hover); }
28
+ `;
29
+ function injectThemeStyle() {
30
+ if (document.getElementById(THEME_STYLE_ID)) return;
31
+ const s = document.createElement("style");
32
+ s.id = THEME_STYLE_ID;
33
+ s.textContent = THEME_CSS;
34
+ document.head.appendChild(s);
35
+ }
36
+ function themeFromUrl() {
37
+ const t = new URLSearchParams(location.search).get("theme");
38
+ return t === "dark" || t === "light" ? t : null;
39
+ }
40
+ function currentTheme() {
41
+ return document.documentElement.getAttribute(ATTR) === "dark" ? "dark" : "light";
42
+ }
43
+ function setTheme(theme) {
44
+ document.documentElement.setAttribute(ATTR, theme);
45
+ const url = new URL(location.href);
46
+ url.searchParams.set("theme", theme);
47
+ history.replaceState(history.state, "", url);
48
+ }
49
+ function initTheme() {
50
+ const fromUrl = themeFromUrl();
51
+ if (fromUrl) {
52
+ document.documentElement.setAttribute(ATTR, fromUrl);
53
+ return;
54
+ }
55
+ const prefersDark = typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches;
56
+ document.documentElement.setAttribute(ATTR, prefersDark ? "dark" : "light");
57
+ }
58
+ function mountThemeFab(host = document.body) {
59
+ injectThemeStyle();
60
+ initTheme();
61
+ if (document.querySelector(".rpml-theme-fab")) return;
62
+ const btn = document.createElement("button");
63
+ btn.className = "rpml-theme-fab";
64
+ btn.type = "button";
65
+ btn.title = "切换亮色/暗色";
66
+ btn.setAttribute("aria-label", "切换亮色/暗色");
67
+ btn.textContent = "◑";
68
+ btn.addEventListener("click", () => setTheme(currentTheme() === "dark" ? "light" : "dark"));
69
+ host.appendChild(btn);
70
+ }
2
71
  registerAll();
3
72
  class RpmlApp extends HTMLElement {
4
73
  async connectedCallback() {
@@ -8,6 +77,7 @@ class RpmlApp extends HTMLElement {
8
77
  const res = await fetch(src);
9
78
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
10
79
  this.replaceWith(parseToPage(await res.text()));
80
+ mountThemeFab();
11
81
  const section = new URLSearchParams(location.search).get("section");
12
82
  if (section) requestAnimationFrame(() => requestAnimationFrame(() => window.dispatchEvent(new CustomEvent("rp-section", { detail: section }))));
13
83
  } catch (e) {
@@ -1 +1 @@
1
- {"version":3,"file":"rpml-loader.js","sources":["../src/rpml-loader.ts"],"sourcesContent":["import { registerAll, parseToPage } from './rpui';\n\nexport { parseToPage };\nregisterAll();\n\nclass RpmlApp extends HTMLElement {\n async connectedCallback() {\n const src = this.getAttribute('src');\n if (!src) return;\n try {\n const res = await fetch(src);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n this.replaceWith(parseToPage(await res.text()));\n // Honor a ?section= deep-link (e.g. arriving from an <anchor> fallback).\n const section = new URLSearchParams(location.search).get('section');\n if (section) requestAnimationFrame(() => requestAnimationFrame(() =>\n window.dispatchEvent(new CustomEvent('rp-section', { detail: section }))));\n } catch (e) {\n this.textContent = `RPML load error: ${e}`;\n console.error(e);\n }\n }\n}\n\ncustomElements.define('rpml-app', RpmlApp);\n\nconst rpmlSrc = new URLSearchParams(location.search).get('rpml');\nif (rpmlSrc && !document.querySelector('rpml-app')) {\n const app = document.createElement('rpml-app') as HTMLElement;\n app.setAttribute('src', rpmlSrc);\n document.body.appendChild(app);\n}\n"],"names":[],"mappings":";AAGA,YAAA;AAEA,MAAM,gBAAgB,YAAY;AAAA,EAChC,MAAM,oBAAoB;AACxB,UAAM,MAAM,KAAK,aAAa,KAAK;AACnC,QAAI,CAAC,IAAK;AACV,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AACjD,WAAK,YAAY,YAAY,MAAM,IAAI,KAAA,CAAM,CAAC;AAE9C,YAAM,UAAU,IAAI,gBAAgB,SAAS,MAAM,EAAE,IAAI,SAAS;AAClE,UAAI,QAAS,uBAAsB,MAAM,sBAAsB,MAC7D,OAAO,cAAc,IAAI,YAAY,cAAc,EAAE,QAAQ,QAAA,CAAS,CAAC,CAAC,CAAC;AAAA,IAC7E,SAAS,GAAG;AACV,WAAK,cAAc,oBAAoB,CAAC;AACxC,cAAQ,MAAM,CAAC;AAAA,IACjB;AAAA,EACF;AACF;AAEA,eAAe,OAAO,YAAY,OAAO;AAEzC,MAAM,UAAU,IAAI,gBAAgB,SAAS,MAAM,EAAE,IAAI,MAAM;AAC/D,IAAI,WAAW,CAAC,SAAS,cAAc,UAAU,GAAG;AAClD,QAAM,MAAM,SAAS,cAAc,UAAU;AAC7C,MAAI,aAAa,OAAO,OAAO;AAC/B,WAAS,KAAK,YAAY,GAAG;AAC/B;"}
1
+ {"version":3,"file":"rpml-loader.js","sources":["../src/core/theme.ts","../src/rpml-loader.ts"],"sourcesContent":["/**\n * Light/dark theme, shared by the gallery (sidebar present) and the single-file\n * loader (no sidebar). The chosen theme lives in the `theme` URL param so it\n * survives reloads and can be deep-linked; on first load with no param we fall\n * back to the OS `prefers-color-scheme`.\n *\n * Strategy:\n * - Chrome (sidebar/header/main bg) is themed with CSS variables (--rpml-gx-*).\n * - The rendered prototype (page-el) carries 256+ hardcoded colors, so instead\n * of remapping each one we invert it as a whole with `invert + hue-rotate`,\n * which flips light/dark while keeping hues (blue stays blue) — guaranteed\n * readable without touching the runtime stylesheet.\n */\n\nconst THEME_STYLE_ID = 'rpml-theme-style';\nconst ATTR = 'data-rpml-theme';\n\nexport const THEME_CSS = `\n:root {\n --rpml-gx-border:#e5e7eb; --rpml-gx-side-bg:#fff; --rpml-gx-main-bg:#f4f6f8;\n --rpml-gx-fg:#111827; --rpml-gx-muted:#6b7280; --rpml-gx-group:#9ca3af;\n --rpml-gx-hover:#f3f4f6; --rpml-gx-item:#374151; --rpml-gx-active-bg:#eff6ff;\n --rpml-gx-active-fg:#1d4ed8; --rpml-gx-copy-hover:#e5e7eb; --rpml-gx-ok:#059669;\n}\nhtml[${ATTR}=\"dark\"] {\n --rpml-gx-border:#2a3344; --rpml-gx-side-bg:#0f172a; --rpml-gx-main-bg:#0b1120;\n --rpml-gx-fg:#e2e8f0; --rpml-gx-muted:#94a3b8; --rpml-gx-group:#64748b;\n --rpml-gx-hover:#1e293b; --rpml-gx-item:#cbd5e1; --rpml-gx-active-bg:#1e3a5f;\n --rpml-gx-active-fg:#93c5fd; --rpml-gx-copy-hover:#334155; --rpml-gx-ok:#34d399;\n}\nhtml[${ATTR}=\"dark\"] body { background:#0b1120; }\n/* Invert the rendered prototype as a whole; hue-rotate keeps colors recognizable. */\nhtml[${ATTR}=\"dark\"] page-el { filter:invert(0.92) hue-rotate(180deg); }\n\n.rpml-theme-fab {\n position:fixed; top:12px; right:12px; z-index:50; display:flex; align-items:center;\n justify-content:center; width:34px; height:34px; padding:0; border:1px solid var(--rpml-gx-border);\n border-radius:9px; background:var(--rpml-gx-side-bg); color:var(--rpml-gx-fg); font-size:16px;\n line-height:1; cursor:pointer; box-shadow:0 1px 3px rgba(0,0,0,.12);\n}\n.rpml-theme-fab:hover { background:var(--rpml-gx-hover); }\n`;\n\nexport function injectThemeStyle(): void {\n if (document.getElementById(THEME_STYLE_ID)) return;\n const s = document.createElement('style');\n s.id = THEME_STYLE_ID;\n s.textContent = THEME_CSS;\n document.head.appendChild(s);\n}\n\nfunction themeFromUrl(): 'light' | 'dark' | null {\n const t = new URLSearchParams(location.search).get('theme');\n return t === 'dark' || t === 'light' ? t : null;\n}\n\nexport function currentTheme(): 'light' | 'dark' {\n return document.documentElement.getAttribute(ATTR) === 'dark' ? 'dark' : 'light';\n}\n\n/** Apply a theme to <html> and reflect it in the `theme` URL param. */\nexport function setTheme(theme: 'light' | 'dark'): void {\n document.documentElement.setAttribute(ATTR, theme);\n const url = new URL(location.href);\n url.searchParams.set('theme', theme);\n history.replaceState(history.state, '', url);\n}\n\n/** Seed the theme from the URL param, falling back to the OS preference.\n * Does not write the URL on the OS-preference path, keeping links clean until\n * the user explicitly toggles. */\nexport function initTheme(): void {\n const fromUrl = themeFromUrl();\n if (fromUrl) { document.documentElement.setAttribute(ATTR, fromUrl); return; }\n const prefersDark = typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches;\n document.documentElement.setAttribute(ATTR, prefersDark ? 'dark' : 'light');\n}\n\n/** A floating top-right toggle, mirroring the sidebar-collapse FAB, so the theme\n * switch is reachable even when there is no sidebar (single-file preview) or it\n * is collapsed. Idempotent. */\nexport function mountThemeFab(host: HTMLElement = document.body): void {\n injectThemeStyle();\n initTheme();\n if (document.querySelector('.rpml-theme-fab')) return;\n const btn = document.createElement('button');\n btn.className = 'rpml-theme-fab';\n btn.type = 'button';\n btn.title = '切换亮色/暗色';\n btn.setAttribute('aria-label', '切换亮色/暗色');\n btn.textContent = '◑';\n btn.addEventListener('click', () => setTheme(currentTheme() === 'dark' ? 'light' : 'dark'));\n host.appendChild(btn);\n}\n","import { registerAll, parseToPage } from './rpui';\nimport { mountThemeFab } from './core/theme';\n\nexport { parseToPage };\nregisterAll();\n\nclass RpmlApp extends HTMLElement {\n async connectedCallback() {\n const src = this.getAttribute('src');\n if (!src) return;\n try {\n const res = await fetch(src);\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n this.replaceWith(parseToPage(await res.text()));\n // A single .rpml preview has no sidebar, so surface the theme toggle as a\n // floating button (mirrors the gallery's collapse FAB).\n mountThemeFab();\n // Honor a ?section= deep-link (e.g. arriving from an <anchor> fallback).\n const section = new URLSearchParams(location.search).get('section');\n if (section) requestAnimationFrame(() => requestAnimationFrame(() =>\n window.dispatchEvent(new CustomEvent('rp-section', { detail: section }))));\n } catch (e) {\n this.textContent = `RPML load error: ${e}`;\n console.error(e);\n }\n }\n}\n\ncustomElements.define('rpml-app', RpmlApp);\n\nconst rpmlSrc = new URLSearchParams(location.search).get('rpml');\nif (rpmlSrc && !document.querySelector('rpml-app')) {\n const app = document.createElement('rpml-app') as HTMLElement;\n app.setAttribute('src', rpmlSrc);\n document.body.appendChild(app);\n}\n"],"names":[],"mappings":";AAcA,MAAM,iBAAiB;AACvB,MAAM,OAAO;AAEN,MAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAOlB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAMJ,IAAI;AAAA;AAAA,OAEJ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAWJ,SAAS,mBAAyB;AACvC,MAAI,SAAS,eAAe,cAAc,EAAG;AAC7C,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEA,SAAS,eAAwC;AAC/C,QAAM,IAAI,IAAI,gBAAgB,SAAS,MAAM,EAAE,IAAI,OAAO;AAC1D,SAAO,MAAM,UAAU,MAAM,UAAU,IAAI;AAC7C;AAEO,SAAS,eAAiC;AAC/C,SAAO,SAAS,gBAAgB,aAAa,IAAI,MAAM,SAAS,SAAS;AAC3E;AAGO,SAAS,SAAS,OAA+B;AACtD,WAAS,gBAAgB,aAAa,MAAM,KAAK;AACjD,QAAM,MAAM,IAAI,IAAI,SAAS,IAAI;AACjC,MAAI,aAAa,IAAI,SAAS,KAAK;AACnC,UAAQ,aAAa,QAAQ,OAAO,IAAI,GAAG;AAC7C;AAKO,SAAS,YAAkB;AAChC,QAAM,UAAU,aAAA;AAChB,MAAI,SAAS;AAAE,aAAS,gBAAgB,aAAa,MAAM,OAAO;AAAG;AAAA,EAAQ;AAC7E,QAAM,cAAc,OAAO,eAAe,cAAc,WAAW,8BAA8B,EAAE;AACnG,WAAS,gBAAgB,aAAa,MAAM,cAAc,SAAS,OAAO;AAC5E;AAKO,SAAS,cAAc,OAAoB,SAAS,MAAY;AACrE,mBAAA;AACA,YAAA;AACA,MAAI,SAAS,cAAc,iBAAiB,EAAG;AAC/C,QAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,MAAI,YAAY;AAChB,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,aAAa,cAAc,SAAS;AACxC,MAAI,cAAc;AAClB,MAAI,iBAAiB,SAAS,MAAM,SAAS,mBAAmB,SAAS,UAAU,MAAM,CAAC;AAC1F,OAAK,YAAY,GAAG;AACtB;ACzFA,YAAA;AAEA,MAAM,gBAAgB,YAAY;AAAA,EAChC,MAAM,oBAAoB;AACxB,UAAM,MAAM,KAAK,aAAa,KAAK;AACnC,QAAI,CAAC,IAAK;AACV,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AACjD,WAAK,YAAY,YAAY,MAAM,IAAI,KAAA,CAAM,CAAC;AAG9C,oBAAA;AAEA,YAAM,UAAU,IAAI,gBAAgB,SAAS,MAAM,EAAE,IAAI,SAAS;AAClE,UAAI,QAAS,uBAAsB,MAAM,sBAAsB,MAC7D,OAAO,cAAc,IAAI,YAAY,cAAc,EAAE,QAAQ,QAAA,CAAS,CAAC,CAAC,CAAC;AAAA,IAC7E,SAAS,GAAG;AACV,WAAK,cAAc,oBAAoB,CAAC;AACxC,cAAQ,MAAM,CAAC;AAAA,IACjB;AAAA,EACF;AACF;AAEA,eAAe,OAAO,YAAY,OAAO;AAEzC,MAAM,UAAU,IAAI,gBAAgB,SAAS,MAAM,EAAE,IAAI,MAAM;AAC/D,IAAI,WAAW,CAAC,SAAS,cAAc,UAAU,GAAG;AAClD,QAAM,MAAM,SAAS,cAAc,UAAU;AAC7C,MAAI,aAAa,OAAO,OAAO;AAC/B,WAAS,KAAK,YAAY,GAAG;AAC/B;"}
package/dist/rpui.js CHANGED
@@ -246,8 +246,8 @@ const style = `
246
246
  * { box-sizing:border-box; }
247
247
  body { margin:0; font-family:var(--rp-font); color:var(--rp-text); background:var(--rp-bg); }
248
248
  .rp-icon { display:inline-block; flex:0 0 auto; vertical-align:-0.16em; }
249
- page-el, page-el { display:block; min-height:100vh; padding:32px 40px; overflow:auto; }
250
- .page-el-shell { display:grid; grid-template-columns:max-content max-content; gap:24px; min-height:100vh; align-items:start; }
249
+ page-el, page-el { display:block; padding:32px 40px; overflow:visible; }
250
+ .page-el-shell { display:grid; grid-template-columns:max-content max-content; gap:24px; min-height:calc(100vh - 64px); align-items:start; }
251
251
  .page-el-main { display:flex; flex-direction:column; min-width:0; overflow:visible; }
252
252
  .page-el-header { flex:0 0 auto; width:fit-content; max-width:none; margin:0 0 22px; }
253
253
  .page-el-title-row { display:flex; align-items:baseline; gap:12px; flex-wrap:wrap; }
@@ -255,7 +255,7 @@ page-el, page-el { display:block; min-height:100vh; padding:32px 40px; overflow:
255
255
  .page-el-route { font-size:13px; color:var(--rp-muted); font-family:ui-monospace,SFMono-Regular,Menlo,monospace; background:rgba(255,255,255,.7); border:1px solid var(--rp-border); border-radius:999px; padding:3px 9px; }
256
256
  .page-el-description { margin:10px 0 0; color:#374151; line-height:1.6; font-size:14px; }
257
257
  .page-el-body { flex:0 1 auto; display:block; width:fit-content; max-width:100%; min-height:0; overflow:visible; }
258
- .annotation-el-pane { min-width:380px; max-width:680px; position:sticky; top:0; height:100vh; overflow-y:auto; overflow-x:auto; padding:0 0 48px 0; align-self:start; }
258
+ .annotation-el-pane { min-width:380px; max-width:680px; position:sticky; top:32px; height:auto; min-height:calc(100vh - 64px); max-height:calc(100vh - 64px); overflow-y:auto; overflow-x:auto; padding:0 0 48px 0; align-self:start; }
259
259
  .annotation-el-pane-inner { padding:4px 12px 24px 6px; }
260
260
  main-view, main-view { display:block; width:fit-content; margin:0 0 28px; position:relative; }
261
261
  .rp-main-shell { position:relative; overflow:visible; border:1px solid var(--rp-border-strong); border-radius:var(--rp-radius-md); background:var(--rp-surface); box-shadow:var(--rp-shadow); }
@@ -275,7 +275,7 @@ annotation-el annotation-el, annotation-el annotation-el, annotation-el annotati
275
275
  .annotation-el-marker.triangle { width:18px; height:16px; background:var(--rp-success); clip-path:polygon(50% 0, 100% 100%, 0 100%); }
276
276
  .annotation-el-marker.triangle > span { transform:translateY(2px); font-size:9px; }
277
277
  .annotation-el-marker.global { width:20px; height:20px; background:#0f172a; border-radius:6px; font-size:11px; }
278
- annotation-global-el, annotation-global-el { display:block; width:fit-content; max-width:980px; margin:0 0 18px; padding:10px 12px 12px; line-height:1.65; color:#1f2937; font-size:14px; background:linear-gradient(180deg,#f8fafc,#fff); border:1px solid var(--rp-border); border-left:3px solid #0f172a; border-radius:var(--rp-radius-md); }
278
+ annotation-global-el, annotation-global-el { display:block; width:fit-content; max-width:980px; margin:0 0 18px; padding:10px 12px 12px; line-height:1.65; color:#1f2937; font-size:14px; background:var(--rp-surface-soft); border:1px solid var(--rp-border); border-radius:var(--rp-radius-md); }
279
279
  .annotation-el-pane annotation-global-el, .annotation-el-pane annotation-global-el { max-width:none; }
280
280
  .annotation-el-pane annotation-global-el .annotation-el-body { max-width:none; }
281
281
  .annotation-el-body { display:block; position:relative; width:fit-content; max-width:920px; }
@@ -302,8 +302,8 @@ layout-el > *, layout-el > * { min-width:0; }
302
302
  viewport-el layout-el, viewport-el layout-el { width:100%; }
303
303
  viewport-el > layout-el, viewport-el > layout-el { flex:1 1 auto; min-height:0; }
304
304
  viewport-el > navbar-el, viewport-el > navbar-el { flex:0 0 auto; }
305
- panel-el, panel-el { display:block; width:fit-content; max-width:100%; background:#fff; border:1px solid var(--rp-border); border-radius:var(--rp-radius-md); padding:var(--snap-padding,16px); }
306
- viewport-el panel-el, viewport-el panel-el { width:auto; min-width:0; }
305
+ panel-el, panel-el { margin:8px; display:block; width:fit-content; max-width:100%; background:#fff; border:1px solid var(--rp-border); border-radius:var(--rp-radius-md); padding:var(--snap-padding,16px); }
306
+ viewport-el panel-el, viewport-el panel-el { margin:8px; width:auto; min-width:0; }
307
307
  panel-el[elevation="1"], panel-el[elevation="1"] { box-shadow:0 4px 16px rgba(15,23,42,.06); }
308
308
  panel-el[elevation="2"], panel-el[elevation="2"] { box-shadow:var(--rp-shadow); }
309
309
  navbar-el, navbar-el { display:flex; align-items:center; gap:14px; height:var(--snap-height,64px); padding:0 24px; background:#fff; border-bottom:1px solid var(--rp-border); }
@@ -868,6 +868,10 @@ class RpAnnotationGlobal extends HTMLElement {
868
868
  this.dataset.rpReady = "true";
869
869
  const existing = Array.from(this.childNodes);
870
870
  const label = attr(this, "label", "全局说明");
871
+ const globalSiblings = this.parentElement ? Array.from(this.parentElement.children).filter(
872
+ (el) => el.tagName.toLowerCase() === "annotation-global-el"
873
+ ) : [];
874
+ this.dataset.rpSection = `global-${globalSiblings.indexOf(this) + 1}`;
871
875
  const marker = document.createElement("span");
872
876
  marker.className = "annotation-el-marker global";
873
877
  marker.innerHTML = "<span>★</span>";
@@ -943,6 +947,7 @@ class RpMainView extends HTMLElement {
943
947
  stage2.style.transform = `scale(${scale})`;
944
948
  const clip = document.createElement("div");
945
949
  clip.className = "rp-main-stage-clip";
950
+ if (!autoHeight) clip.style.height = `${height * scale}px`;
946
951
  children.forEach((n) => {
947
952
  if (isViewportNode(n)) {
948
953
  if (!n.hasAttribute("width") && !n.hasAttribute("device")) n.style.setProperty("--snap-width", `${width}px`);
@@ -980,10 +985,12 @@ class RpMainView extends HTMLElement {
980
985
  if (!usesAutoHeight(this)) return;
981
986
  const shell = this.querySelector(".rp-main-shell");
982
987
  const stage = this.querySelector(".rp-main-stage");
988
+ const clip = this.querySelector(".rp-main-stage-clip");
983
989
  if (!shell || !stage) return;
984
990
  const scale = Number(attr(this, "scale", "0.7")) || 0.7;
985
991
  const next = `${Math.ceil(stage.scrollHeight * scale)}px`;
986
992
  if (shell.style.height !== next) shell.style.height = next;
993
+ if (clip && clip.style.height !== next) clip.style.height = next;
987
994
  }
988
995
  renderPins() {
989
996
  const shell = this.querySelector(".rp-main-shell");