@nsnanocat/preference-panes 1.1.2 → 1.1.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.
- package/README.md +7 -4
- package/dist/api.js +6 -530
- package/dist/module/index.html +1 -1
- package/dist/module/index.mjs +49 -38
- package/dist/module/navigation.mjs +3 -3
- package/dist/preference-panes.mjs +49 -38
- package/dist/web.js +1 -1
- package/package.json +1 -1
- package/src/api.mjs +6 -38
- package/src/browser/ActionMenu.mjs +2 -2
- package/src/browser/ModuleStatus.mjs +1 -1
- package/src/browser/boxjs.mjs +1 -0
- package/src/browser/panel.css +0 -3
- package/src/browser/panel.mjs +45 -35
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* 独立页面的三点按钮和底部操作菜单;嵌入页面由宿主提供对应界面。
|
|
3
|
+
* Overflow trigger and bottom action sheet for standalone pages; embedded pages use host-provided chrome.
|
|
4
4
|
*/
|
|
5
5
|
export class ActionMenu {
|
|
6
6
|
#button;
|
|
@@ -76,7 +76,7 @@ export class ModuleStatus extends EventTarget {
|
|
|
76
76
|
const response = await probeModule(url, { ...options, signal: controller.signal });
|
|
77
77
|
if (controller !== this.#controller) return response;
|
|
78
78
|
const version = response.status === 200 ? response.headers.get("X-PreferencePanes-Version")?.trim() || null : null;
|
|
79
|
-
this.#render(
|
|
79
|
+
this.#render(version ? "installed" : "missing", version);
|
|
80
80
|
return response;
|
|
81
81
|
} catch (error) {
|
|
82
82
|
if (controller !== this.#controller) return;
|
package/src/browser/boxjs.mjs
CHANGED
|
@@ -25,6 +25,7 @@ export function normalizeBoxJs(config, module) {
|
|
|
25
25
|
if (!storageKey || storageKey.startsWith("@") || parts.length < 2) throw new TypeError("A BoxJS setting must be below a literal storage root and module");
|
|
26
26
|
validatePathParts(parts);
|
|
27
27
|
const name = parts[0];
|
|
28
|
+
if (["get", "set", "delete"].includes(name)) throw new TypeError(`Reserved API module name: ${name}`);
|
|
28
29
|
let target = modules.get(name);
|
|
29
30
|
if (!target) {
|
|
30
31
|
target = { module: name, storageKey, entries: [], owners: new Set() };
|
package/src/browser/panel.css
CHANGED
package/src/browser/panel.mjs
CHANGED
|
@@ -32,13 +32,9 @@ export class PreferencesPanel {
|
|
|
32
32
|
const title = definition.metadata?.name ?? definition.module;
|
|
33
33
|
const document = root.ownerDocument;
|
|
34
34
|
const window = document.defaultView;
|
|
35
|
+
const frame = window.frameElement?.dataset.preferencePanes ? window.frameElement : undefined;
|
|
35
36
|
const shell = node("div", "pp-panel");
|
|
36
37
|
shell.dataset.module = definition.module;
|
|
37
|
-
const header = node("header", "pp-header");
|
|
38
|
-
const back = node("button", "pp-back", "‹");
|
|
39
|
-
back.setAttribute("aria-label", "返回");
|
|
40
|
-
back.type = "button";
|
|
41
|
-
const heading = node("h1", "pp-title", title);
|
|
42
38
|
const handlers = new Map();
|
|
43
39
|
const menuItems = [
|
|
44
40
|
{ id: "viewSettings", label: "查看设置" },
|
|
@@ -46,37 +42,51 @@ export class PreferencesPanel {
|
|
|
46
42
|
{ id: "clearCaches", label: "清空缓存", destructive: true },
|
|
47
43
|
{ id: "reset", label: "重置设置", destructive: true },
|
|
48
44
|
];
|
|
49
|
-
const menu = new ActionMenu(id => runAction(id));
|
|
50
|
-
const trailing = node("span", "pp-nav-spacer");
|
|
51
|
-
trailing.append(menu.element);
|
|
52
45
|
const viewport = node("div", "pp-viewport");
|
|
46
|
+
let back, heading, menu;
|
|
47
|
+
if (frame) shell.append(viewport);
|
|
48
|
+
else {
|
|
49
|
+
const header = node("header", "pp-header");
|
|
50
|
+
back = node("button", "pp-back", "‹");
|
|
51
|
+
back.setAttribute("aria-label", "返回");
|
|
52
|
+
back.type = "button";
|
|
53
|
+
heading = node("h1", "pp-title", title);
|
|
54
|
+
menu = new ActionMenu(id => runAction(id));
|
|
55
|
+
const trailing = node("span", "pp-nav-spacer");
|
|
56
|
+
trailing.append(menu.element);
|
|
57
|
+
header.append(back, heading, trailing);
|
|
58
|
+
shell.append(header, viewport);
|
|
59
|
+
}
|
|
53
60
|
let toast;
|
|
54
|
-
header.append(back, heading, trailing);
|
|
55
|
-
shell.append(header, viewport);
|
|
56
61
|
root.append(shell);
|
|
57
62
|
// 嵌入模式向宿主发布导航状态,宿主不读取或修改模块内部 DOM。
|
|
58
63
|
// Embedded mode publishes navigation state without host reads or mutations of the module DOM.
|
|
59
64
|
const publishNavigation = () => {
|
|
60
65
|
const actions = handlers.size ? menuItems : [];
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
if (frame)
|
|
67
|
+
frame.dispatchEvent(
|
|
68
|
+
new frame.ownerDocument.defaultView.CustomEvent("preferencepanes:change", {
|
|
69
|
+
detail: { title: currentTitle, module: definition.module, busy: saving, canGoBack, actions },
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
else {
|
|
73
|
+
heading.textContent = currentTitle;
|
|
74
|
+
back.disabled = !canGoBack;
|
|
75
|
+
menu.update(actions, saving);
|
|
76
|
+
}
|
|
69
77
|
};
|
|
70
78
|
const onAction = event => {
|
|
71
79
|
if (!saving && handlers.has(event.detail)) runAction(event.detail);
|
|
72
80
|
};
|
|
73
|
-
|
|
81
|
+
frame?.addEventListener("preferencepanes:action", onAction);
|
|
74
82
|
let timer,
|
|
75
83
|
navigation,
|
|
76
84
|
generation = 0,
|
|
77
85
|
active = null,
|
|
78
86
|
saving = false,
|
|
79
|
-
destroyed = false
|
|
87
|
+
destroyed = false,
|
|
88
|
+
currentTitle = title,
|
|
89
|
+
canGoBack = window.history.length > 1;
|
|
80
90
|
/**
|
|
81
91
|
* 展示短暂通知,不刷新设置数据。
|
|
82
92
|
* Display a transient notification without refreshing settings.
|
|
@@ -105,7 +115,6 @@ export class PreferencesPanel {
|
|
|
105
115
|
}
|
|
106
116
|
// 宿主接管时不创建网页 Toast,也不运行其计时器。
|
|
107
117
|
// A host-owned notice creates no web Toast and starts no local timer.
|
|
108
|
-
const frame = window.frameElement;
|
|
109
118
|
if (frame && !frame.dispatchEvent(new frame.ownerDocument.defaultView.CustomEvent("preferencepanes:notice", { cancelable: true, detail: { kind: event.kind, message } }))) return;
|
|
110
119
|
if (!toast) {
|
|
111
120
|
toast = node("div", "pp-toast");
|
|
@@ -143,8 +152,8 @@ export class PreferencesPanel {
|
|
|
143
152
|
async function open(module) {
|
|
144
153
|
const version = ++generation;
|
|
145
154
|
active = module;
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
currentTitle = module;
|
|
156
|
+
canGoBack = window.history.length > 1;
|
|
148
157
|
publishNavigation();
|
|
149
158
|
viewport.replaceChildren(statusView("读取设置…"));
|
|
150
159
|
try {
|
|
@@ -163,7 +172,7 @@ export class PreferencesPanel {
|
|
|
163
172
|
*/
|
|
164
173
|
function controls() {
|
|
165
174
|
const { definition, values } = client.snapshot();
|
|
166
|
-
|
|
175
|
+
currentTitle = definition.metadata?.name || active;
|
|
167
176
|
const view = node("section", "pp-fields");
|
|
168
177
|
/**
|
|
169
178
|
* 挂载后执行的多行高度更新
|
|
@@ -183,8 +192,8 @@ export class PreferencesPanel {
|
|
|
183
192
|
*/
|
|
184
193
|
const updateNavigation = () => {
|
|
185
194
|
const editor = editors.get(navigation.current);
|
|
186
|
-
|
|
187
|
-
|
|
195
|
+
currentTitle = editor?.title ?? definition.metadata?.name ?? active;
|
|
196
|
+
canGoBack = !saving && navigation.canGoBack;
|
|
188
197
|
publishNavigation();
|
|
189
198
|
};
|
|
190
199
|
/**
|
|
@@ -198,7 +207,7 @@ export class PreferencesPanel {
|
|
|
198
207
|
function perform(action, success, failure = () => {}) {
|
|
199
208
|
pendingWrites++;
|
|
200
209
|
saving = true;
|
|
201
|
-
|
|
210
|
+
canGoBack = false;
|
|
202
211
|
publishNavigation();
|
|
203
212
|
queue = queue
|
|
204
213
|
.then(action)
|
|
@@ -214,7 +223,7 @@ export class PreferencesPanel {
|
|
|
214
223
|
pendingWrites--;
|
|
215
224
|
saving = pendingWrites > 0;
|
|
216
225
|
if (destroyed && !saving) client.leave();
|
|
217
|
-
|
|
226
|
+
canGoBack = !saving && navigation.canGoBack;
|
|
218
227
|
publishNavigation();
|
|
219
228
|
});
|
|
220
229
|
return queue;
|
|
@@ -489,16 +498,17 @@ export class PreferencesPanel {
|
|
|
489
498
|
* Loaded forms delegate back to navigation; loading views can return to the previous document.
|
|
490
499
|
* @returns {void} 无返回值 / No return value.
|
|
491
500
|
*/
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
501
|
+
if (back)
|
|
502
|
+
back.onclick = () => {
|
|
503
|
+
if (saving) return;
|
|
504
|
+
if (navigation) navigation.back();
|
|
505
|
+
else window.history.back();
|
|
506
|
+
};
|
|
497
507
|
open(definition.module);
|
|
498
508
|
return () => {
|
|
499
509
|
destroyed = true;
|
|
500
|
-
menu
|
|
501
|
-
|
|
510
|
+
menu?.destroy();
|
|
511
|
+
frame?.removeEventListener("preferencepanes:action", onAction);
|
|
502
512
|
navigation?.destroy();
|
|
503
513
|
generation++;
|
|
504
514
|
if (active && !saving) client.leave();
|