@21stware/rpui 0.4.4 → 0.5.5
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/dist/canvas/page.d.ts +3 -0
- package/dist/core/live-render.d.ts +11 -0
- package/dist/gallery.d.ts +5 -1
- package/dist/gallery.js +176 -77
- package/dist/gallery.js.map +1 -1
- package/dist/rpui.d.ts +3 -1
- package/dist/rpui.js +63 -3
- package/dist/rpui.js.map +1 -1
- package/dist/serve.js +48 -8
- package/package.json +6 -4
package/dist/canvas/page.d.ts
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface LiveRenderOpts {
|
|
2
|
+
scroller?: Element | null;
|
|
3
|
+
preserve?: boolean;
|
|
4
|
+
onError?: (msg: string | null) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface DocRenderer {
|
|
7
|
+
render(source: string): void;
|
|
8
|
+
destroy(): void;
|
|
9
|
+
}
|
|
10
|
+
export declare function createDocRenderer(host: HTMLElement, opts?: Pick<LiveRenderOpts, 'scroller' | 'onError'>): DocRenderer;
|
|
11
|
+
export declare function liveRender(host: HTMLElement, source: string, opts?: LiveRenderOpts): void;
|
package/dist/gallery.d.ts
CHANGED
|
@@ -2,4 +2,8 @@ export interface RpmlDoc {
|
|
|
2
2
|
path: string;
|
|
3
3
|
source: string;
|
|
4
4
|
}
|
|
5
|
-
export
|
|
5
|
+
export interface GalleryController {
|
|
6
|
+
update(newDocs: RpmlDoc[]): void;
|
|
7
|
+
destroy(): void;
|
|
8
|
+
}
|
|
9
|
+
export declare function mountGallery(docs: RpmlDoc[], host?: HTMLElement): GalleryController;
|
package/dist/gallery.js
CHANGED
|
@@ -1,6 +1,59 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
function attr(el, name, fallback = "") {
|
|
5
|
+
return el.getAttribute(name) ?? fallback;
|
|
6
|
+
}
|
|
7
|
+
function intAttr(el, name, fallback) {
|
|
8
|
+
const raw = el.getAttribute(name);
|
|
9
|
+
const value = raw === null || raw === "" ? NaN : Number(raw);
|
|
10
|
+
return Number.isFinite(value) ? value : fallback;
|
|
11
|
+
}
|
|
12
|
+
function escapeHtml(value) {
|
|
13
|
+
return value.replace(/[&<>'"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ })[c] || c);
|
|
14
|
+
}
|
|
15
|
+
function csv(el, name, fallback) {
|
|
16
|
+
return attr(el, name, fallback).split(",").map((s) => s.trim()).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
const deviceWidths = { web: 1440, ipad: 834, mobile: 390 };
|
|
19
|
+
function resolveWidth(el, fallback) {
|
|
20
|
+
const raw = el.getAttribute("width");
|
|
21
|
+
const width = raw === null || raw === "" ? NaN : Number(raw);
|
|
22
|
+
if (Number.isFinite(width)) return width;
|
|
23
|
+
return deviceWidths[attr(el, "device")] ?? fallback;
|
|
24
|
+
}
|
|
25
|
+
function hasExplicitNumericHeight(el) {
|
|
26
|
+
const raw = el.getAttribute("height");
|
|
27
|
+
return raw !== null && raw !== "" && Number.isFinite(Number(raw));
|
|
28
|
+
}
|
|
29
|
+
function usesAutoHeight(el) {
|
|
30
|
+
const raw = el.getAttribute("height");
|
|
31
|
+
return raw === "auto" || el.hasAttribute("auto-height") || !!el.getAttribute("device") && !hasExplicitNumericHeight(el);
|
|
32
|
+
}
|
|
33
|
+
function resolveHeight(el, fallback) {
|
|
34
|
+
const raw = el.getAttribute("height");
|
|
35
|
+
const height = raw === null || raw === "" ? NaN : Number(raw);
|
|
36
|
+
return Number.isFinite(height) ? height : fallback;
|
|
37
|
+
}
|
|
38
|
+
function isTopAnnotation(node) {
|
|
39
|
+
if (!(node instanceof HTMLElement)) return false;
|
|
40
|
+
const tag = node.tagName.toLowerCase();
|
|
41
|
+
return tag === "annotation-el" || tag === "annotation-el";
|
|
42
|
+
}
|
|
43
|
+
function isGlobalAnnotation(node) {
|
|
44
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === "annotation-global-el";
|
|
45
|
+
}
|
|
46
|
+
function isViewportNode(node) {
|
|
47
|
+
if (!(node instanceof HTMLElement)) return false;
|
|
48
|
+
const tag = node.tagName.toLowerCase();
|
|
49
|
+
return tag === "viewport-el" || tag === "viewport-el";
|
|
50
|
+
}
|
|
51
|
+
function define(name, ctor) {
|
|
52
|
+
if (customElements.get(name)) return;
|
|
53
|
+
const Alias = class extends ctor {
|
|
54
|
+
};
|
|
55
|
+
customElements.define(name, Alias);
|
|
56
|
+
}
|
|
4
57
|
const PRIMITIVES = [
|
|
5
58
|
// layout
|
|
6
59
|
"viewport",
|
|
@@ -187,59 +240,6 @@ function parseToPage(source) {
|
|
|
187
240
|
if (!root) throw new Error("RPML parse error: no <page> root element found");
|
|
188
241
|
return root;
|
|
189
242
|
}
|
|
190
|
-
function attr(el, name, fallback = "") {
|
|
191
|
-
return el.getAttribute(name) ?? fallback;
|
|
192
|
-
}
|
|
193
|
-
function intAttr(el, name, fallback) {
|
|
194
|
-
const raw = el.getAttribute(name);
|
|
195
|
-
const value = raw === null || raw === "" ? NaN : Number(raw);
|
|
196
|
-
return Number.isFinite(value) ? value : fallback;
|
|
197
|
-
}
|
|
198
|
-
function escapeHtml(value) {
|
|
199
|
-
return value.replace(/[&<>'"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", "'": "'", '"': """ })[c] || c);
|
|
200
|
-
}
|
|
201
|
-
function csv(el, name, fallback) {
|
|
202
|
-
return attr(el, name, fallback).split(",").map((s) => s.trim()).filter(Boolean);
|
|
203
|
-
}
|
|
204
|
-
const deviceWidths = { web: 1440, ipad: 834, mobile: 390 };
|
|
205
|
-
function resolveWidth(el, fallback) {
|
|
206
|
-
const raw = el.getAttribute("width");
|
|
207
|
-
const width = raw === null || raw === "" ? NaN : Number(raw);
|
|
208
|
-
if (Number.isFinite(width)) return width;
|
|
209
|
-
return deviceWidths[attr(el, "device")] ?? fallback;
|
|
210
|
-
}
|
|
211
|
-
function hasExplicitNumericHeight(el) {
|
|
212
|
-
const raw = el.getAttribute("height");
|
|
213
|
-
return raw !== null && raw !== "" && Number.isFinite(Number(raw));
|
|
214
|
-
}
|
|
215
|
-
function usesAutoHeight(el) {
|
|
216
|
-
const raw = el.getAttribute("height");
|
|
217
|
-
return raw === "auto" || el.hasAttribute("auto-height") || !!el.getAttribute("device") && !hasExplicitNumericHeight(el);
|
|
218
|
-
}
|
|
219
|
-
function resolveHeight(el, fallback) {
|
|
220
|
-
const raw = el.getAttribute("height");
|
|
221
|
-
const height = raw === null || raw === "" ? NaN : Number(raw);
|
|
222
|
-
return Number.isFinite(height) ? height : fallback;
|
|
223
|
-
}
|
|
224
|
-
function isTopAnnotation(node) {
|
|
225
|
-
if (!(node instanceof HTMLElement)) return false;
|
|
226
|
-
const tag = node.tagName.toLowerCase();
|
|
227
|
-
return tag === "annotation-el" || tag === "annotation-el";
|
|
228
|
-
}
|
|
229
|
-
function isGlobalAnnotation(node) {
|
|
230
|
-
return node instanceof HTMLElement && node.tagName.toLowerCase() === "annotation-global-el";
|
|
231
|
-
}
|
|
232
|
-
function isViewportNode(node) {
|
|
233
|
-
if (!(node instanceof HTMLElement)) return false;
|
|
234
|
-
const tag = node.tagName.toLowerCase();
|
|
235
|
-
return tag === "viewport-el" || tag === "viewport-el";
|
|
236
|
-
}
|
|
237
|
-
function define(name, ctor) {
|
|
238
|
-
if (customElements.get(name)) return;
|
|
239
|
-
const Alias = class extends ctor {
|
|
240
|
-
};
|
|
241
|
-
customElements.define(name, Alias);
|
|
242
|
-
}
|
|
243
243
|
const RPUI_STYLE_ID = "rpui-runtime-style";
|
|
244
244
|
const style = `
|
|
245
245
|
:root { --rp-bg:#f0f2f5; --rp-surface:#fff; --rp-surface-soft:#f9fafb; --rp-text:#111827; --rp-muted:#6b7280; --rp-border:#e5e7eb; --rp-border-strong:#d1d5db; --rp-primary:#2563eb; --rp-success:#059669; --rp-warning:#d97706; --rp-danger:#dc2626; --rp-purple:#7c3aed; --rp-radius-sm:4px; --rp-radius-md:8px; --rp-radius-lg:12px; --rp-shadow:0 8px 28px rgba(15,23,42,.08); --rp-font:-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif; }
|
|
@@ -1031,8 +1031,13 @@ function activateSection(path, pane) {
|
|
|
1031
1031
|
setTimeout(() => target.classList.remove("rp-section-focus"), 3e3);
|
|
1032
1032
|
}
|
|
1033
1033
|
class RpPage extends HTMLElement {
|
|
1034
|
+
constructor() {
|
|
1035
|
+
super(...arguments);
|
|
1036
|
+
__publicField(this, "_handlers");
|
|
1037
|
+
}
|
|
1034
1038
|
connectedCallback() {
|
|
1035
1039
|
injectStyle();
|
|
1040
|
+
this.wireRouting();
|
|
1036
1041
|
if (this.dataset.rpReady) return;
|
|
1037
1042
|
this.dataset.rpReady = "true";
|
|
1038
1043
|
const pageTitle = attr(this, "title", "Untitled");
|
|
@@ -1069,13 +1074,29 @@ class RpPage extends HTMLElement {
|
|
|
1069
1074
|
const mv = body.querySelector("main-view, main-view");
|
|
1070
1075
|
if (mv) header.style.maxWidth = `${mv.offsetWidth}px`;
|
|
1071
1076
|
});
|
|
1077
|
+
this.wireRouting();
|
|
1078
|
+
requestAnimationFrame(this._handlers[0]);
|
|
1079
|
+
}
|
|
1080
|
+
/** Section routing: handle URL and rp-section events. Re-queries the pane
|
|
1081
|
+
* lazily so listeners don't capture build-scope DOM (survives reconnect). */
|
|
1082
|
+
wireRouting() {
|
|
1083
|
+
if (this._handlers) return;
|
|
1084
|
+
const pane = () => this.querySelector(".annotation-el-pane");
|
|
1072
1085
|
const go = () => {
|
|
1073
1086
|
const sec = new URLSearchParams(location.search).get("section");
|
|
1074
|
-
if (sec) activateSection(sec, pane);
|
|
1087
|
+
if (sec) activateSection(sec, pane());
|
|
1075
1088
|
};
|
|
1089
|
+
const onSection = (e) => activateSection(e.detail, pane());
|
|
1090
|
+
this._handlers = [go, onSection];
|
|
1076
1091
|
window.addEventListener("popstate", go);
|
|
1077
|
-
window.addEventListener("rp-section",
|
|
1078
|
-
|
|
1092
|
+
window.addEventListener("rp-section", onSection);
|
|
1093
|
+
}
|
|
1094
|
+
disconnectedCallback() {
|
|
1095
|
+
if (this._handlers) {
|
|
1096
|
+
window.removeEventListener("popstate", this._handlers[0]);
|
|
1097
|
+
window.removeEventListener("rp-section", this._handlers[1]);
|
|
1098
|
+
this._handlers = void 0;
|
|
1099
|
+
}
|
|
1079
1100
|
}
|
|
1080
1101
|
}
|
|
1081
1102
|
class GenericElement extends HTMLElement {
|
|
@@ -99584,7 +99605,30 @@ function registerAll() {
|
|
|
99584
99605
|
define(toComponentTag(suffix), ctor);
|
|
99585
99606
|
}
|
|
99586
99607
|
}
|
|
99587
|
-
|
|
99608
|
+
function liveRender(host, source, opts = {}) {
|
|
99609
|
+
const { scroller, preserve = true, onError } = opts;
|
|
99610
|
+
const sc = scroller ?? document.scrollingElement ?? document.documentElement;
|
|
99611
|
+
const pane = preserve ? host.querySelector(".annotation-el-pane") : null;
|
|
99612
|
+
const pos = preserve ? { x: sc.scrollLeft, y: sc.scrollTop, px: (pane == null ? void 0 : pane.scrollLeft) ?? 0, py: (pane == null ? void 0 : pane.scrollTop) ?? 0 } : null;
|
|
99613
|
+
try {
|
|
99614
|
+
host.replaceChildren(parseToPage(source));
|
|
99615
|
+
onError == null ? void 0 : onError(null);
|
|
99616
|
+
} catch (e) {
|
|
99617
|
+
onError == null ? void 0 : onError(e.message ?? String(e));
|
|
99618
|
+
return;
|
|
99619
|
+
}
|
|
99620
|
+
if (pos) {
|
|
99621
|
+
requestAnimationFrame(() => requestAnimationFrame(() => {
|
|
99622
|
+
sc.scrollLeft = pos.x;
|
|
99623
|
+
sc.scrollTop = pos.y;
|
|
99624
|
+
const newPane = host.querySelector(".annotation-el-pane");
|
|
99625
|
+
if (newPane) {
|
|
99626
|
+
newPane.scrollLeft = pos.px;
|
|
99627
|
+
newPane.scrollTop = pos.py;
|
|
99628
|
+
}
|
|
99629
|
+
}));
|
|
99630
|
+
}
|
|
99631
|
+
}
|
|
99588
99632
|
const THEME_STYLE_ID = "rpml-theme-style";
|
|
99589
99633
|
const ATTR = "data-rpml-theme";
|
|
99590
99634
|
const THEME_CSS = `
|
|
@@ -99721,14 +99765,13 @@ function resolveAnchorTarget(to, fromPath, paths) {
|
|
|
99721
99765
|
}
|
|
99722
99766
|
function mountGallery(docs, host = document.body) {
|
|
99723
99767
|
injectChrome();
|
|
99724
|
-
|
|
99725
|
-
|
|
99768
|
+
let byPath = new Map(docs.map((d) => [d.path, d]));
|
|
99769
|
+
let tree = buildTree(docs);
|
|
99726
99770
|
const root = document.createElement("div");
|
|
99727
99771
|
root.className = "rpml-gallery";
|
|
99728
99772
|
const side = document.createElement("aside");
|
|
99729
99773
|
side.className = "rpml-gx-side";
|
|
99730
|
-
|
|
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>`;
|
|
99774
|
+
side.innerHTML = `<div class="rpml-gx-head"><span>RPML 文档<small>${docs.length} 个文件</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>`;
|
|
99732
99775
|
const nav = document.createElement("nav");
|
|
99733
99776
|
nav.className = "rpml-gx-nav";
|
|
99734
99777
|
side.appendChild(nav);
|
|
@@ -99781,25 +99824,34 @@ function mountGallery(docs, host = document.body) {
|
|
|
99781
99824
|
}
|
|
99782
99825
|
}
|
|
99783
99826
|
renderNav(tree, 0);
|
|
99827
|
+
function rebuildNav(newDocs) {
|
|
99828
|
+
nav.innerHTML = "";
|
|
99829
|
+
links.clear();
|
|
99830
|
+
tree = buildTree(newDocs);
|
|
99831
|
+
renderNav(tree, 0);
|
|
99832
|
+
side.querySelector(".rpml-gx-head small").textContent = `${newDocs.length} 个文件`;
|
|
99833
|
+
}
|
|
99784
99834
|
function pickDefault() {
|
|
99785
|
-
const
|
|
99786
|
-
|
|
99835
|
+
const cur = [...byPath.values()];
|
|
99836
|
+
const idx = cur.find((d) => basename(d.path).replace(/\.rpml$/i, "").toLowerCase() === "index");
|
|
99837
|
+
return (idx ?? cur[0]).path;
|
|
99787
99838
|
}
|
|
99788
99839
|
let currentPath = "";
|
|
99789
|
-
function show(path, section) {
|
|
99840
|
+
function show(path, section, preserve = false) {
|
|
99790
99841
|
const doc = byPath.get(path);
|
|
99791
99842
|
if (!doc) {
|
|
99792
99843
|
main.innerHTML = `<div class="rpml-gx-err">未找到文档:${path}</div>`;
|
|
99793
99844
|
return;
|
|
99794
99845
|
}
|
|
99795
|
-
|
|
99796
|
-
main
|
|
99797
|
-
|
|
99798
|
-
|
|
99799
|
-
|
|
99800
|
-
|
|
99801
|
-
}
|
|
99802
|
-
|
|
99846
|
+
liveRender(main, doc.source, {
|
|
99847
|
+
scroller: main,
|
|
99848
|
+
preserve,
|
|
99849
|
+
onError: (msg) => {
|
|
99850
|
+
if (msg) main.innerHTML = `<div class="rpml-gx-err">RPML 解析错误:${msg}</div>`;
|
|
99851
|
+
}
|
|
99852
|
+
});
|
|
99853
|
+
currentPath = path;
|
|
99854
|
+
links.forEach((row, p) => row.classList.toggle("active", p === path));
|
|
99803
99855
|
if (section) {
|
|
99804
99856
|
requestAnimationFrame(() => requestAnimationFrame(() => window.dispatchEvent(new CustomEvent("rp-section", { detail: section }))));
|
|
99805
99857
|
}
|
|
@@ -99808,14 +99860,17 @@ function mountGallery(docs, host = document.body) {
|
|
|
99808
99860
|
const path = decodeURIComponent(location.hash.slice(1)) || pickDefault();
|
|
99809
99861
|
show(path);
|
|
99810
99862
|
}
|
|
99811
|
-
|
|
99863
|
+
const onAnchor = (e) => {
|
|
99812
99864
|
const { to, section } = e.detail;
|
|
99813
99865
|
const target = resolveAnchorTarget(to, currentPath, new Set(byPath.keys()));
|
|
99814
99866
|
if (!target) return;
|
|
99815
99867
|
e.preventDefault();
|
|
99816
99868
|
history.pushState(null, "", `#${target}`);
|
|
99817
99869
|
show(target, section);
|
|
99818
|
-
}
|
|
99870
|
+
};
|
|
99871
|
+
const onPopstate = () => route();
|
|
99872
|
+
window.addEventListener("rp-anchor", onAnchor);
|
|
99873
|
+
window.addEventListener("popstate", onPopstate);
|
|
99819
99874
|
nav.addEventListener("click", (e) => {
|
|
99820
99875
|
var _a2;
|
|
99821
99876
|
const el = e.target;
|
|
@@ -99841,13 +99896,57 @@ function mountGallery(docs, host = document.body) {
|
|
|
99841
99896
|
history.pushState(null, "", a.hash);
|
|
99842
99897
|
show(path);
|
|
99843
99898
|
});
|
|
99844
|
-
window.addEventListener("popstate", route);
|
|
99845
99899
|
route();
|
|
99900
|
+
const controller = {
|
|
99901
|
+
update(newDocs) {
|
|
99902
|
+
var _a2;
|
|
99903
|
+
const prevSource = (_a2 = byPath.get(currentPath)) == null ? void 0 : _a2.source;
|
|
99904
|
+
byPath = new Map(newDocs.map((d) => [d.path, d]));
|
|
99905
|
+
const newPaths = newDocs.map((d) => d.path).sort().join("\0");
|
|
99906
|
+
const oldPaths = [...links.keys()].sort().join("\0");
|
|
99907
|
+
if (newPaths !== oldPaths) rebuildNav(newDocs);
|
|
99908
|
+
const curr = byPath.get(currentPath);
|
|
99909
|
+
if (curr) {
|
|
99910
|
+
if (curr.source !== prevSource) show(currentPath, void 0, true);
|
|
99911
|
+
else links.forEach((row, p) => row.classList.toggle("active", p === currentPath));
|
|
99912
|
+
} else if (newDocs.length) {
|
|
99913
|
+
const def = pickDefault();
|
|
99914
|
+
history.replaceState(null, "", `#${def}`);
|
|
99915
|
+
show(def);
|
|
99916
|
+
}
|
|
99917
|
+
},
|
|
99918
|
+
destroy() {
|
|
99919
|
+
window.removeEventListener("rp-anchor", onAnchor);
|
|
99920
|
+
window.removeEventListener("popstate", onPopstate);
|
|
99921
|
+
host.innerHTML = "";
|
|
99922
|
+
const g = globalThis;
|
|
99923
|
+
if (g.__RPML_GALLERY__ === controller) delete g.__RPML_GALLERY__;
|
|
99924
|
+
}
|
|
99925
|
+
};
|
|
99926
|
+
globalThis.__RPML_GALLERY__ = controller;
|
|
99927
|
+
return controller;
|
|
99846
99928
|
}
|
|
99847
99929
|
const inlined = globalThis.__RPML_DOCS__;
|
|
99848
99930
|
if (inlined && Array.isArray(inlined) && inlined.length) {
|
|
99849
|
-
|
|
99850
|
-
|
|
99931
|
+
const mount = () => {
|
|
99932
|
+
const gallery = mountGallery(inlined);
|
|
99933
|
+
if (globalThis.__RPML_LIVE__) {
|
|
99934
|
+
const es = new EventSource("/~live");
|
|
99935
|
+
es.onmessage = (ev) => {
|
|
99936
|
+
try {
|
|
99937
|
+
gallery.update(JSON.parse(ev.data));
|
|
99938
|
+
} catch {
|
|
99939
|
+
}
|
|
99940
|
+
};
|
|
99941
|
+
const origDestroy = gallery.destroy.bind(gallery);
|
|
99942
|
+
gallery.destroy = () => {
|
|
99943
|
+
es.close();
|
|
99944
|
+
origDestroy();
|
|
99945
|
+
};
|
|
99946
|
+
}
|
|
99947
|
+
};
|
|
99948
|
+
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", mount);
|
|
99949
|
+
else mount();
|
|
99851
99950
|
}
|
|
99852
99951
|
export {
|
|
99853
99952
|
mountGallery
|