@21stware/rpui 0.4.3 → 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.
@@ -0,0 +1,6 @@
1
+ export declare 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[data-rpml-theme=\"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[data-rpml-theme=\"dark\"] body { background:#0b1120; }\n/* Invert the rendered prototype as a whole; hue-rotate keeps colors recognizable. */\nhtml[data-rpml-theme=\"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";
2
+ export declare function injectThemeStyle(): void;
3
+ export declare function currentTheme(): 'light' | 'dark';
4
+ export declare function setTheme(theme: 'light' | 'dark'): void;
5
+ export declare function initTheme(): void;
6
+ export declare function mountThemeFab(host?: HTMLElement): void;
package/dist/gallery.js CHANGED
@@ -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>";
@@ -99581,29 +99585,93 @@ function registerAll() {
99581
99585
  }
99582
99586
  }
99583
99587
  registerAll();
99588
+ const THEME_STYLE_ID = "rpml-theme-style";
99589
+ const ATTR = "data-rpml-theme";
99590
+ const THEME_CSS = `
99591
+ :root {
99592
+ --rpml-gx-border:#e5e7eb; --rpml-gx-side-bg:#fff; --rpml-gx-main-bg:#f4f6f8;
99593
+ --rpml-gx-fg:#111827; --rpml-gx-muted:#6b7280; --rpml-gx-group:#9ca3af;
99594
+ --rpml-gx-hover:#f3f4f6; --rpml-gx-item:#374151; --rpml-gx-active-bg:#eff6ff;
99595
+ --rpml-gx-active-fg:#1d4ed8; --rpml-gx-copy-hover:#e5e7eb; --rpml-gx-ok:#059669;
99596
+ }
99597
+ html[${ATTR}="dark"] {
99598
+ --rpml-gx-border:#2a3344; --rpml-gx-side-bg:#0f172a; --rpml-gx-main-bg:#0b1120;
99599
+ --rpml-gx-fg:#e2e8f0; --rpml-gx-muted:#94a3b8; --rpml-gx-group:#64748b;
99600
+ --rpml-gx-hover:#1e293b; --rpml-gx-item:#cbd5e1; --rpml-gx-active-bg:#1e3a5f;
99601
+ --rpml-gx-active-fg:#93c5fd; --rpml-gx-copy-hover:#334155; --rpml-gx-ok:#34d399;
99602
+ }
99603
+ html[${ATTR}="dark"] body { background:#0b1120; }
99604
+ /* Invert the rendered prototype as a whole; hue-rotate keeps colors recognizable. */
99605
+ html[${ATTR}="dark"] page-el { filter:invert(0.92) hue-rotate(180deg); }
99606
+
99607
+ .rpml-theme-fab {
99608
+ position:fixed; top:12px; right:12px; z-index:50; display:flex; align-items:center;
99609
+ justify-content:center; width:34px; height:34px; padding:0; border:1px solid var(--rpml-gx-border);
99610
+ border-radius:9px; background:var(--rpml-gx-side-bg); color:var(--rpml-gx-fg); font-size:16px;
99611
+ line-height:1; cursor:pointer; box-shadow:0 1px 3px rgba(0,0,0,.12);
99612
+ }
99613
+ .rpml-theme-fab:hover { background:var(--rpml-gx-hover); }
99614
+ `;
99615
+ function injectThemeStyle() {
99616
+ if (document.getElementById(THEME_STYLE_ID)) return;
99617
+ const s = document.createElement("style");
99618
+ s.id = THEME_STYLE_ID;
99619
+ s.textContent = THEME_CSS;
99620
+ document.head.appendChild(s);
99621
+ }
99622
+ function themeFromUrl() {
99623
+ const t = new URLSearchParams(location.search).get("theme");
99624
+ return t === "dark" || t === "light" ? t : null;
99625
+ }
99626
+ function currentTheme() {
99627
+ return document.documentElement.getAttribute(ATTR) === "dark" ? "dark" : "light";
99628
+ }
99629
+ function setTheme(theme) {
99630
+ document.documentElement.setAttribute(ATTR, theme);
99631
+ const url = new URL(location.href);
99632
+ url.searchParams.set("theme", theme);
99633
+ history.replaceState(history.state, "", url);
99634
+ }
99635
+ function initTheme() {
99636
+ const fromUrl = themeFromUrl();
99637
+ if (fromUrl) {
99638
+ document.documentElement.setAttribute(ATTR, fromUrl);
99639
+ return;
99640
+ }
99641
+ const prefersDark = typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: dark)").matches;
99642
+ document.documentElement.setAttribute(ATTR, prefersDark ? "dark" : "light");
99643
+ }
99584
99644
  registerAll();
99585
99645
  const CHROME_CSS = `
99586
99646
  .rpml-gallery { display:grid; grid-template-columns:260px 1fr; height:100vh; font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
99587
99647
  .rpml-gallery.collapsed { grid-template-columns:1fr; }
99588
99648
  .rpml-gallery.collapsed .rpml-gx-side { display:none; }
99589
- .rpml-gx-side { display:flex; flex-direction:column; border-right:1px solid #e5e7eb; background:#fff; min-height:0; }
99590
- .rpml-gx-head { display:flex; align-items:flex-start; justify-content:space-between; gap:8px; padding:16px; border-bottom:1px solid #e5e7eb; font-size:14px; font-weight:700; color:#111827; }
99591
- .rpml-gx-head small { display:block; margin-top:3px; font-size:11px; font-weight:400; color:#6b7280; }
99592
- .rpml-gx-toggle { flex:none; display:flex; align-items:center; justify-content:center; width:26px; height:26px; margin:-3px -3px 0 0; padding:0; border:1px solid #e5e7eb; border-radius:7px; background:#fff; color:#6b7280; font-size:15px; line-height:1; cursor:pointer; }
99593
- .rpml-gx-toggle:hover { background:#f3f4f6; color:#111827; }
99594
- .rpml-gx-fab { position:fixed; top:12px; left:12px; z-index:50; display:none; align-items:center; justify-content:center; width:34px; height:34px; padding:0; border:1px solid #e5e7eb; border-radius:9px; background:#fff; color:#374151; font-size:17px; line-height:1; cursor:pointer; box-shadow:0 1px 3px rgba(0,0,0,.12); }
99595
- .rpml-gx-fab:hover { background:#f3f4f6; color:#111827; }
99649
+ .rpml-gx-side { display:flex; flex-direction:column; border-right:1px solid var(--rpml-gx-border,#e5e7eb); background:var(--rpml-gx-side-bg,#fff); min-height:0; }
99650
+ .rpml-gx-head { display:flex; align-items:flex-start; justify-content:space-between; gap:8px; padding:16px; border-bottom:1px solid var(--rpml-gx-border,#e5e7eb); font-size:14px; font-weight:700; color:var(--rpml-gx-fg,#111827); }
99651
+ .rpml-gx-head small { display:block; margin-top:3px; font-size:11px; font-weight:400; color:var(--rpml-gx-muted,#6b7280); }
99652
+ .rpml-gx-head-actions { flex:none; display:flex; gap:6px; margin:-3px -3px 0 0; }
99653
+ .rpml-gx-btn { display:flex; align-items:center; justify-content:center; width:26px; height:26px; padding:0; border:1px solid var(--rpml-gx-border,#e5e7eb); border-radius:7px; background:var(--rpml-gx-side-bg,#fff); color:var(--rpml-gx-muted,#6b7280); font-size:15px; line-height:1; cursor:pointer; }
99654
+ .rpml-gx-btn:hover { background:var(--rpml-gx-hover,#f3f4f6); color:var(--rpml-gx-fg,#111827); }
99655
+ .rpml-gx-fab { position:fixed; top:12px; left:12px; z-index:50; display:none; align-items:center; justify-content:center; width:34px; height:34px; padding:0; border:1px solid var(--rpml-gx-border,#e5e7eb); border-radius:9px; background:var(--rpml-gx-side-bg,#fff); color:var(--rpml-gx-fg,#374151); font-size:17px; line-height:1; cursor:pointer; box-shadow:0 1px 3px rgba(0,0,0,.12); }
99656
+ .rpml-gx-fab:hover { background:var(--rpml-gx-hover,#f3f4f6); }
99596
99657
  .rpml-gallery.collapsed .rpml-gx-fab { display:flex; }
99597
99658
  .rpml-gx-nav { flex:1; overflow-y:auto; padding:8px; }
99598
- .rpml-gx-group { padding:10px 8px 3px; font-size:11px; font-weight:700; color:#9ca3af; text-transform:uppercase; letter-spacing:.04em; }
99599
- .rpml-gx-item { display:block; padding:6px 10px; border-radius:7px; font-size:13px; color:#374151; text-decoration:none; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
99600
- .rpml-gx-item:hover { background:#f3f4f6; }
99601
- .rpml-gx-item.active { background:#eff6ff; color:#1d4ed8; font-weight:650; }
99602
- .rpml-gx-indent { padding-left:22px; }
99603
- .rpml-gx-main { overflow:auto; min-height:0; background:#f4f6f8; }
99659
+ .rpml-gx-group { padding:10px 8px 3px; font-size:11px; font-weight:700; color:var(--rpml-gx-group,#9ca3af); text-transform:uppercase; letter-spacing:.04em; }
99660
+ .rpml-gx-row { display:flex; align-items:center; border-radius:7px; }
99661
+ .rpml-gx-row:hover { background:var(--rpml-gx-hover,#f3f4f6); }
99662
+ .rpml-gx-row.active { background:var(--rpml-gx-active-bg,#eff6ff); }
99663
+ .rpml-gx-item { flex:1; min-width:0; display:block; padding:6px 10px; font-size:13px; color:var(--rpml-gx-item,#374151); text-decoration:none; cursor:pointer; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
99664
+ .rpml-gx-row.active .rpml-gx-item { color:var(--rpml-gx-active-fg,#1d4ed8); font-weight:650; }
99665
+ .rpml-gx-indent .rpml-gx-item { padding-left:22px; }
99666
+ .rpml-gx-copy { flex:none; margin-right:6px; padding:3px 7px; border:1px solid var(--rpml-gx-border,#e5e7eb); border-radius:6px; background:transparent; color:var(--rpml-gx-muted,#9ca3af); font-size:11px; line-height:1.4; cursor:pointer; opacity:0; transition:opacity .12s; }
99667
+ .rpml-gx-row:hover .rpml-gx-copy { opacity:1; }
99668
+ .rpml-gx-copy:hover { background:var(--rpml-gx-copy-hover,#e5e7eb); color:var(--rpml-gx-fg,#111827); }
99669
+ .rpml-gx-copy.copied { opacity:1; color:var(--rpml-gx-ok,#059669); border-color:var(--rpml-gx-ok,#059669); }
99670
+ .rpml-gx-main { overflow:auto; min-height:0; background:var(--rpml-gx-main-bg,#f4f6f8); }
99604
99671
  .rpml-gx-err { padding:40px; color:#dc2626; font-family:ui-monospace,Menlo,monospace; }
99605
99672
  `;
99606
99673
  function injectChrome() {
99674
+ injectThemeStyle();
99607
99675
  if (document.getElementById("rpml-gallery-style")) return;
99608
99676
  const s = document.createElement("style");
99609
99677
  s.id = "rpml-gallery-style";
@@ -99660,7 +99728,7 @@ function mountGallery(docs, host = document.body) {
99660
99728
  const side = document.createElement("aside");
99661
99729
  side.className = "rpml-gx-side";
99662
99730
  const count = docs.length;
99663
- side.innerHTML = `<div class="rpml-gx-head"><span>RPML 文档<small>${count} 个文件</small></span><button class="rpml-gx-toggle" type="button" title="收起侧边栏" aria-label="收起侧边栏">«</button></div>`;
99731
+ side.innerHTML = `<div class="rpml-gx-head"><span>RPML 文档<small>${count} 个文件</small></span><div class="rpml-gx-head-actions"><button class="rpml-gx-btn rpml-gx-theme" type="button" title="切换亮色/暗色" aria-label="切换亮色/暗色">◑</button><button class="rpml-gx-btn rpml-gx-toggle" type="button" title="收起侧边栏" aria-label="收起侧边栏">«</button></div></div>`;
99664
99732
  const nav = document.createElement("nav");
99665
99733
  nav.className = "rpml-gx-nav";
99666
99734
  side.appendChild(nav);
@@ -99677,18 +99745,32 @@ function mountGallery(docs, host = document.body) {
99677
99745
  host.appendChild(root);
99678
99746
  side.querySelector(".rpml-gx-toggle").addEventListener("click", () => root.classList.add("collapsed"));
99679
99747
  fab.addEventListener("click", () => root.classList.remove("collapsed"));
99748
+ initTheme();
99749
+ side.querySelector(".rpml-gx-theme").addEventListener("click", () => {
99750
+ setTheme(currentTheme() === "dark" ? "light" : "dark");
99751
+ });
99680
99752
  const links = /* @__PURE__ */ new Map();
99681
99753
  function renderNav(node, depth) {
99682
99754
  const entries = [...node.children.values()];
99683
99755
  const leaves = entries.filter((e) => e.path).sort((a, b) => a.name.localeCompare(b.name));
99684
99756
  const folders = entries.filter((e) => !e.path).sort((a, b) => a.name.localeCompare(b.name));
99685
99757
  for (const leaf of leaves) {
99758
+ const row = document.createElement("div");
99759
+ row.className = depth > 0 ? "rpml-gx-row rpml-gx-indent" : "rpml-gx-row";
99686
99760
  const a = document.createElement("a");
99687
- a.className = depth > 0 ? "rpml-gx-item rpml-gx-indent" : "rpml-gx-item";
99761
+ a.className = "rpml-gx-item";
99688
99762
  a.textContent = leaf.title || leaf.name;
99689
99763
  a.href = `#${leaf.path}`;
99690
- links.set(leaf.path, a);
99691
- nav.appendChild(a);
99764
+ const copy = document.createElement("button");
99765
+ copy.className = "rpml-gx-copy";
99766
+ copy.type = "button";
99767
+ copy.title = "复制此页全部内容";
99768
+ copy.setAttribute("aria-label", "复制此页全部内容");
99769
+ copy.dataset.copyPath = leaf.path;
99770
+ copy.textContent = "copy";
99771
+ row.append(a, copy);
99772
+ links.set(leaf.path, row);
99773
+ nav.appendChild(row);
99692
99774
  }
99693
99775
  for (const folder of folders) {
99694
99776
  const g = document.createElement("div");
@@ -99735,7 +99817,24 @@ function mountGallery(docs, host = document.body) {
99735
99817
  show(target, section);
99736
99818
  });
99737
99819
  nav.addEventListener("click", (e) => {
99738
- const a = e.target.closest("a.rpml-gx-item");
99820
+ var _a2;
99821
+ const el = e.target;
99822
+ const copyBtn = el.closest(".rpml-gx-copy");
99823
+ if (copyBtn) {
99824
+ e.preventDefault();
99825
+ const doc = byPath.get(copyBtn.dataset.copyPath);
99826
+ if (!doc) return;
99827
+ void ((_a2 = navigator.clipboard) == null ? void 0 : _a2.writeText(doc.source).then(() => {
99828
+ copyBtn.classList.add("copied");
99829
+ copyBtn.textContent = "已复制";
99830
+ setTimeout(() => {
99831
+ copyBtn.classList.remove("copied");
99832
+ copyBtn.textContent = "copy";
99833
+ }, 1500);
99834
+ }));
99835
+ return;
99836
+ }
99837
+ const a = el.closest("a.rpml-gx-item");
99739
99838
  if (!a) return;
99740
99839
  e.preventDefault();
99741
99840
  const path = decodeURIComponent(a.hash.slice(1));