@nsnanocat/preference-panes 0.9.9 → 0.9.11
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 +2 -4
- package/dist/api.js +1 -1
- package/dist/module/app.mjs +82 -107
- package/dist/module/index.html +1 -1
- package/dist/module/navigation.mjs +68 -36
- package/dist/preference-panes.mjs +79 -104
- package/package.json +1 -1
- package/src/BoxJS.mjs +4 -18
- package/src/browser/ActionMenu.mjs +68 -36
- package/src/browser/Navigation.d.mts +6 -0
- package/src/browser/app.mjs +3 -3
- package/src/browser/client.mjs +1 -1
- package/src/browser/components.mjs +0 -16
- package/src/browser/index.mjs +1 -1
- package/src/browser/panel.css +0 -35
- package/src/browser/panel.mjs +2 -30
- package/src/build.mjs +1 -3
- package/src/lib/boxjs.mjs +3 -2
package/src/browser/index.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { mountPanel } from "./panel.mjs";
|
|
|
12
12
|
*/
|
|
13
13
|
export function mount(boxjs, css = "") {
|
|
14
14
|
if (typeof css !== "string") throw new TypeError("CSS must be a string");
|
|
15
|
-
const catalog = new BoxJS(boxjs);
|
|
15
|
+
const catalog = boxjs instanceof BoxJS ? boxjs : new BoxJS(boxjs);
|
|
16
16
|
const metadata = catalog.module.metadata;
|
|
17
17
|
const image = metadata.icon || metadata.icons?.[1] || metadata.icons?.[0];
|
|
18
18
|
if (image) resourceURL(image);
|
package/src/browser/panel.css
CHANGED
|
@@ -57,23 +57,6 @@
|
|
|
57
57
|
min-width: 0;
|
|
58
58
|
overflow-wrap: anywhere;
|
|
59
59
|
}
|
|
60
|
-
/* 模块图标位于固定搜索工具栏左侧;嵌入宿主仍可使用纯文字原生标题栏。
|
|
61
|
-
* Keep the module icon beside the fixed search field while embedded hosts use text-only native titles. */
|
|
62
|
-
.pp-module-logo {
|
|
63
|
-
display: none;
|
|
64
|
-
width: 36px;
|
|
65
|
-
height: 36px;
|
|
66
|
-
flex: none;
|
|
67
|
-
}
|
|
68
|
-
.pp-module-logo:not(:empty) {
|
|
69
|
-
display: block;
|
|
70
|
-
}
|
|
71
|
-
.pp-module-logo img {
|
|
72
|
-
display: block;
|
|
73
|
-
width: 100%;
|
|
74
|
-
height: 100%;
|
|
75
|
-
object-fit: contain;
|
|
76
|
-
}
|
|
77
60
|
.pp-nav-spacer {
|
|
78
61
|
width: 44px;
|
|
79
62
|
flex: none;
|
|
@@ -97,18 +80,6 @@
|
|
|
97
80
|
opacity: 0.5;
|
|
98
81
|
cursor: wait;
|
|
99
82
|
}
|
|
100
|
-
/* 固定工具区与滚动视口共用弹性布局,不按工具栏高度拼接页面偏移。
|
|
101
|
-
* Fixed tools and the scrolling viewport share a flex layout without toolbar-height offsets. */
|
|
102
|
-
.pp-toolbar {
|
|
103
|
-
flex: none;
|
|
104
|
-
min-width: 0;
|
|
105
|
-
padding: 12px max(16px, calc((100% - 688px) / 2));
|
|
106
|
-
display: flex;
|
|
107
|
-
align-items: center;
|
|
108
|
-
gap: 12px;
|
|
109
|
-
background: var(--pp-surface);
|
|
110
|
-
border-bottom: 1px solid var(--pp-border);
|
|
111
|
-
}
|
|
112
83
|
.pp-viewport {
|
|
113
84
|
position: relative;
|
|
114
85
|
flex: 1;
|
|
@@ -175,12 +146,6 @@
|
|
|
175
146
|
border: 0;
|
|
176
147
|
border-radius: 6px;
|
|
177
148
|
}
|
|
178
|
-
.pp-search {
|
|
179
|
-
flex: 1;
|
|
180
|
-
width: 100%;
|
|
181
|
-
min-width: 0;
|
|
182
|
-
margin: 0;
|
|
183
|
-
}
|
|
184
149
|
.pp-panel .pp-multiline {
|
|
185
150
|
display: block;
|
|
186
151
|
}
|
package/src/browser/panel.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ActionMenu } from "./ActionMenu.mjs";
|
|
2
2
|
import { createPreferencesClient } from "./client.mjs";
|
|
3
|
-
import { errorView, fieldControl,
|
|
3
|
+
import { errorView, fieldControl, element as node, requestConfirmation, resourceURL, settingRow } from "./components.mjs";
|
|
4
4
|
import { Navigation } from "./Navigation.mjs";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -30,17 +30,10 @@ export function mountPanel(root, catalog) {
|
|
|
30
30
|
const menu = new ActionMenu(id => runAction(id));
|
|
31
31
|
const trailing = node("span", "pp-nav-spacer");
|
|
32
32
|
trailing.append(menu.element);
|
|
33
|
-
const logo = node("span", "pp-module-logo");
|
|
34
|
-
logo.setAttribute("aria-hidden", "true");
|
|
35
|
-
const image = icon(catalog.module.metadata, "");
|
|
36
|
-
if (image) logo.append(image);
|
|
37
|
-
const toolbar = node("div", "pp-toolbar");
|
|
38
|
-
toolbar.setAttribute("role", "search");
|
|
39
|
-
toolbar.hidden = true;
|
|
40
33
|
const viewport = node("div", "pp-viewport");
|
|
41
34
|
let toast;
|
|
42
35
|
header.append(back, heading, trailing);
|
|
43
|
-
shell.append(header,
|
|
36
|
+
shell.append(header, viewport);
|
|
44
37
|
root.append(shell);
|
|
45
38
|
// 嵌入模式向宿主发布导航状态,宿主不读取或修改模块内部 DOM。
|
|
46
39
|
// Embedded mode publishes navigation state without host reads or mutations of the module DOM.
|
|
@@ -131,7 +124,6 @@ export function mountPanel(root, catalog) {
|
|
|
131
124
|
async function open(module) {
|
|
132
125
|
const version = ++generation;
|
|
133
126
|
active = module;
|
|
134
|
-
toolbar.hidden = true;
|
|
135
127
|
back.disabled = window.history.length <= 1;
|
|
136
128
|
heading.textContent = module;
|
|
137
129
|
publishNavigation();
|
|
@@ -154,14 +146,6 @@ export function mountPanel(root, catalog) {
|
|
|
154
146
|
const { definition, values } = client.snapshot(active);
|
|
155
147
|
heading.textContent = definition.metadata?.name || active;
|
|
156
148
|
const view = node("section", "pp-fields");
|
|
157
|
-
const search = node("input", "");
|
|
158
|
-
search.type = "search";
|
|
159
|
-
search.placeholder = "搜索设置项";
|
|
160
|
-
search.setAttribute("aria-label", "搜索设置");
|
|
161
|
-
const searchField = fieldControl(search);
|
|
162
|
-
searchField.classList.add("pp-search");
|
|
163
|
-
toolbar.replaceChildren(logo, searchField);
|
|
164
|
-
const searchRows = [];
|
|
165
149
|
/**
|
|
166
150
|
* 挂载后执行的多行高度更新
|
|
167
151
|
* Textarea sizing callbacks run after mounting.
|
|
@@ -180,7 +164,6 @@ export function mountPanel(root, catalog) {
|
|
|
180
164
|
*/
|
|
181
165
|
const updateNavigation = () => {
|
|
182
166
|
const editor = editors.get(navigation.current);
|
|
183
|
-
toolbar.hidden = Boolean(navigation.current);
|
|
184
167
|
heading.textContent = editor?.title ?? definition.metadata?.name ?? active;
|
|
185
168
|
back.disabled = saving || !navigation.canGoBack;
|
|
186
169
|
publishNavigation();
|
|
@@ -405,18 +388,7 @@ export function mountPanel(root, catalog) {
|
|
|
405
388
|
});
|
|
406
389
|
if (eventName === "input") inputContainer.addEventListener("compositionend", event => event.target.dispatchEvent(new window.Event("input", { bubbles: true })));
|
|
407
390
|
groups.get(group).append(row);
|
|
408
|
-
searchRows.push({ row, text: [field.name, field.key, field.description, ...(field.options ?? []).map(option => option.label)].join(" ").toLocaleLowerCase() });
|
|
409
391
|
}
|
|
410
|
-
const empty = node("p", "pp-description", "没有匹配的设置项");
|
|
411
|
-
empty.hidden = true;
|
|
412
|
-
empty.setAttribute("role", "status");
|
|
413
|
-
view.append(empty);
|
|
414
|
-
search.oninput = () => {
|
|
415
|
-
const words = search.value.trim().toLocaleLowerCase().split(/\s+/);
|
|
416
|
-
for (const { row, text } of searchRows) row.hidden = !words.every(word => text.includes(word));
|
|
417
|
-
for (const rows of groups.values()) rows.parentElement.hidden = [...rows.children].every(row => row.hidden);
|
|
418
|
-
empty.hidden = searchRows.some(({ row }) => !row.hidden);
|
|
419
|
-
};
|
|
420
392
|
const cachePage = node("section", "pp-cache-page");
|
|
421
393
|
const output = node("pre", "pp-cache");
|
|
422
394
|
output.textContent = "暂无缓存";
|
package/src/build.mjs
CHANGED
|
@@ -12,12 +12,10 @@ export async function build(boxjs, css = "") {
|
|
|
12
12
|
if (typeof css !== "string") throw new TypeError("CSS must be a string");
|
|
13
13
|
const catalog = new BoxJS(boxjs);
|
|
14
14
|
const module = catalog.module.module;
|
|
15
|
-
const [html, app
|
|
15
|
+
const [html, app] = await Promise.all([readFile(new URL("../dist/module/index.html", import.meta.url), "utf8"), readFile(new URL("../dist/module/app.mjs", import.meta.url), "utf8")]);
|
|
16
16
|
return {
|
|
17
17
|
[`settings/${module}/index.html`]: html,
|
|
18
|
-
[`settings/assets/${module}.html`]: html,
|
|
19
18
|
"settings/assets/app.mjs": app,
|
|
20
|
-
"settings/assets/navigation.mjs": navigation,
|
|
21
19
|
[`settings/assets/${module}.css`]: css,
|
|
22
20
|
};
|
|
23
21
|
}
|
package/src/lib/boxjs.mjs
CHANGED
|
@@ -4,14 +4,15 @@ import { validatePathParts } from "./settings-path.mjs";
|
|
|
4
4
|
/**
|
|
5
5
|
* 将 BoxJS 数组、app 或订阅转换为模块字段,保留原文件为唯一字段来源。
|
|
6
6
|
* Normalize a BoxJS array, app or subscription using the source JSON as the field authority.
|
|
7
|
-
* @param {unknown} config BoxJS JSON / BoxJS document.
|
|
7
|
+
* @param {unknown | BoxJS} config BoxJS JSON 或已解析目录 / BoxJS document or parsed catalog.
|
|
8
8
|
* @param {string} module API 第一段模块名 / First API path segment.
|
|
9
9
|
* @returns {import("../index.js").ModuleDefinition} 存储根和字段 / Storage root and fields.
|
|
10
10
|
* @throws {TypeError} 配置结构、字段路径、默认值或展示属性无效 / Invalid configuration, field path, default or presentation attribute.
|
|
11
11
|
*/
|
|
12
12
|
export function normalizeBoxJs(config, module) {
|
|
13
13
|
validatePathParts([module]);
|
|
14
|
-
const
|
|
14
|
+
const catalog = config instanceof BoxJS ? config : new BoxJS(config);
|
|
15
|
+
const target = catalog.modules.get(module);
|
|
15
16
|
if (!target) throw new TypeError(`No BoxJS settings for module: ${module}`);
|
|
16
17
|
const { entries, storageKey, metadata } = target;
|
|
17
18
|
const fields = [];
|