@nsnanocat/preference-panes 0.9.8 → 0.9.10
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 +81 -108
- package/dist/module/index.html +1 -1
- package/dist/module/navigation.mjs +67 -36
- package/dist/preference-panes.mjs +78 -105
- package/package.json +1 -1
- package/src/BoxJS.mjs +4 -18
- package/src/browser/ActionMenu.mjs +67 -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 +9 -30
- package/src/browser/panel.mjs +2 -31
- 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
|
@@ -18,7 +18,11 @@
|
|
|
18
18
|
position: relative;
|
|
19
19
|
display: flex;
|
|
20
20
|
flex-direction: column;
|
|
21
|
+
width: 100%;
|
|
22
|
+
max-width: 100vw;
|
|
23
|
+
min-width: 0;
|
|
21
24
|
height: 100vh;
|
|
25
|
+
overflow: hidden;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
:root[data-theme="dark"] .pp-panel {
|
|
@@ -53,23 +57,6 @@
|
|
|
53
57
|
min-width: 0;
|
|
54
58
|
overflow-wrap: anywhere;
|
|
55
59
|
}
|
|
56
|
-
/* 模块图标在正文内;嵌入宿主可使用纯文字原生标题栏。
|
|
57
|
-
* Keep module icons in content so embedded hosts can use text-only native titles. */
|
|
58
|
-
.pp-module-logo {
|
|
59
|
-
display: none;
|
|
60
|
-
width: 64px;
|
|
61
|
-
height: 64px;
|
|
62
|
-
margin: 8px auto 20px;
|
|
63
|
-
}
|
|
64
|
-
.pp-module-logo:not(:empty) {
|
|
65
|
-
display: block;
|
|
66
|
-
}
|
|
67
|
-
.pp-module-logo img {
|
|
68
|
-
display: block;
|
|
69
|
-
width: 100%;
|
|
70
|
-
height: 100%;
|
|
71
|
-
object-fit: contain;
|
|
72
|
-
}
|
|
73
60
|
.pp-nav-spacer {
|
|
74
61
|
width: 44px;
|
|
75
62
|
flex: none;
|
|
@@ -93,17 +80,10 @@
|
|
|
93
80
|
opacity: 0.5;
|
|
94
81
|
cursor: wait;
|
|
95
82
|
}
|
|
96
|
-
/* 固定工具区与滚动视口共用弹性布局,不按工具栏高度拼接页面偏移。
|
|
97
|
-
* Fixed tools and the scrolling viewport share a flex layout without toolbar-height offsets. */
|
|
98
|
-
.pp-toolbar {
|
|
99
|
-
flex: none;
|
|
100
|
-
padding: 12px max(16px, calc((100% - 688px) / 2));
|
|
101
|
-
background: var(--pp-surface);
|
|
102
|
-
border-bottom: 1px solid var(--pp-border);
|
|
103
|
-
}
|
|
104
83
|
.pp-viewport {
|
|
105
84
|
position: relative;
|
|
106
85
|
flex: 1;
|
|
86
|
+
min-width: 0;
|
|
107
87
|
min-height: 0;
|
|
108
88
|
overflow: hidden;
|
|
109
89
|
}
|
|
@@ -120,7 +100,9 @@
|
|
|
120
100
|
.pp-cache-page {
|
|
121
101
|
position: absolute;
|
|
122
102
|
inset: 0;
|
|
123
|
-
|
|
103
|
+
min-width: 0;
|
|
104
|
+
overflow-x: hidden;
|
|
105
|
+
overflow-y: auto;
|
|
124
106
|
padding: 12px max(16px, calc((100% - 688px) / 2)) calc(28px + env(safe-area-inset-bottom) + var(--pp-keyboard-height, 0px));
|
|
125
107
|
scroll-padding-bottom: var(--pp-keyboard-height, 0px);
|
|
126
108
|
background: var(--pp-background);
|
|
@@ -164,10 +146,6 @@
|
|
|
164
146
|
border: 0;
|
|
165
147
|
border-radius: 6px;
|
|
166
148
|
}
|
|
167
|
-
.pp-search {
|
|
168
|
-
width: 100%;
|
|
169
|
-
margin: 0;
|
|
170
|
-
}
|
|
171
149
|
.pp-panel .pp-multiline {
|
|
172
150
|
display: block;
|
|
173
151
|
}
|
|
@@ -205,6 +183,7 @@
|
|
|
205
183
|
font-weight: 400;
|
|
206
184
|
}
|
|
207
185
|
.pp-row {
|
|
186
|
+
min-width: 0;
|
|
208
187
|
min-height: 48px;
|
|
209
188
|
padding: 16px;
|
|
210
189
|
display: flex;
|
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,15 +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
|
-
view.append(logo);
|
|
158
|
-
const search = node("input", "");
|
|
159
|
-
search.type = "search";
|
|
160
|
-
search.placeholder = "搜索设置项";
|
|
161
|
-
search.setAttribute("aria-label", "搜索设置");
|
|
162
|
-
const searchField = fieldControl(search);
|
|
163
|
-
searchField.classList.add("pp-search");
|
|
164
|
-
toolbar.replaceChildren(searchField);
|
|
165
|
-
const searchRows = [];
|
|
166
149
|
/**
|
|
167
150
|
* 挂载后执行的多行高度更新
|
|
168
151
|
* Textarea sizing callbacks run after mounting.
|
|
@@ -181,7 +164,6 @@ export function mountPanel(root, catalog) {
|
|
|
181
164
|
*/
|
|
182
165
|
const updateNavigation = () => {
|
|
183
166
|
const editor = editors.get(navigation.current);
|
|
184
|
-
toolbar.hidden = Boolean(navigation.current);
|
|
185
167
|
heading.textContent = editor?.title ?? definition.metadata?.name ?? active;
|
|
186
168
|
back.disabled = saving || !navigation.canGoBack;
|
|
187
169
|
publishNavigation();
|
|
@@ -406,18 +388,7 @@ export function mountPanel(root, catalog) {
|
|
|
406
388
|
});
|
|
407
389
|
if (eventName === "input") inputContainer.addEventListener("compositionend", event => event.target.dispatchEvent(new window.Event("input", { bubbles: true })));
|
|
408
390
|
groups.get(group).append(row);
|
|
409
|
-
searchRows.push({ row, text: [field.name, field.key, field.description, ...(field.options ?? []).map(option => option.label)].join(" ").toLocaleLowerCase() });
|
|
410
391
|
}
|
|
411
|
-
const empty = node("p", "pp-description", "没有匹配的设置项");
|
|
412
|
-
empty.hidden = true;
|
|
413
|
-
empty.setAttribute("role", "status");
|
|
414
|
-
view.append(empty);
|
|
415
|
-
search.oninput = () => {
|
|
416
|
-
const words = search.value.trim().toLocaleLowerCase().split(/\s+/);
|
|
417
|
-
for (const { row, text } of searchRows) row.hidden = !words.every(word => text.includes(word));
|
|
418
|
-
for (const rows of groups.values()) rows.parentElement.hidden = [...rows.children].every(row => row.hidden);
|
|
419
|
-
empty.hidden = searchRows.some(({ row }) => !row.hidden);
|
|
420
|
-
};
|
|
421
392
|
const cachePage = node("section", "pp-cache-page");
|
|
422
393
|
const output = node("pre", "pp-cache");
|
|
423
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 = [];
|