@nsnanocat/preference-panes 0.6.0 → 0.7.1

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.
Files changed (41) hide show
  1. package/README.md +60 -116
  2. package/dist/module/app.mjs +1170 -0
  3. package/dist/module/index.html +14 -0
  4. package/dist/module/navigation.mjs +160 -0
  5. package/dist/preference-panes.config.js +839 -813
  6. package/dist/preference-panes.mjs +1052 -825
  7. package/dist/preference-panes.proxy.js +1751 -2123
  8. package/package.json +66 -67
  9. package/src/BoxJS.mjs +86 -0
  10. package/src/Store.mjs +127 -0
  11. package/src/browser/Navigation.d.mts +43 -0
  12. package/src/browser/Navigation.mjs +158 -0
  13. package/src/browser/app.mjs +30 -148
  14. package/src/browser/client.d.mts +150 -0
  15. package/src/browser/client.mjs +191 -212
  16. package/src/browser/components.mjs +59 -0
  17. package/src/browser/index.d.ts +19 -152
  18. package/src/browser/index.mjs +60 -5
  19. package/src/browser/module.html +14 -0
  20. package/src/browser/panel.css +245 -223
  21. package/src/browser/panel.mjs +414 -514
  22. package/src/build.mjs +33 -0
  23. package/src/index.d.ts +227 -133
  24. package/src/index.mjs +3 -6
  25. package/src/lib/boxjs.mjs +77 -99
  26. package/src/lib/page-inputs.mjs +17 -0
  27. package/src/lib/response.mjs +16 -0
  28. package/src/lib/settings-path.mjs +10 -23
  29. package/src/proxy/config.mjs +6 -11
  30. package/src/proxy/handler.mjs +48 -27
  31. package/src/proxy/response.mjs +16 -0
  32. package/dist/preference-panes.request.js +0 -2126
  33. package/dist/settings/app.mjs +0 -1044
  34. package/dist/settings/home.css +0 -134
  35. package/dist/settings/index.html +0 -15
  36. package/dist/settings/panel.css +0 -325
  37. package/src/PreferencesHandler.mjs +0 -50
  38. package/src/SettingsHandler.mjs +0 -144
  39. package/src/browser/home.css +0 -134
  40. package/src/browser/site.html +0 -15
  41. package/src/proxy/request.mjs +0 -5
package/README.md CHANGED
@@ -1,149 +1,93 @@
1
1
  # @nsnanocat/preference-panes
2
2
 
3
- 通用 WebView 设置面板与代理持久化存储桥接。0.3.0 起,BoxJS 完全由前端解析;API 只按安装配置中的根和模块读写数据,不下载配置、不重复校验字段或枚举。
4
-
5
- ## 目录
6
-
7
- | 目录 | 内容 |
8
- | --- | --- |
9
- | src/SettingsHandler.mjs | 模块存储桥接 class |
10
- | src/PreferencesHandler.mjs | 存储桥接与声明式静态资源响应 |
11
- | src/browser/ | WebView 控件、内存会话和样式 |
12
- | src/lib/ | 前端 BoxJS 与通用路径解析 |
13
- | src/proxy/ | 代理宿主打包入口 |
14
- | test/ | 类型与行为回归测试 |
15
- | examples/ | BoxJS、HTML 和 Surge 集成示例 |
16
- | apifox/ | 接口说明、原生 JSON 与生成器 |
17
- | .github/ | CI、双平台发布工作流 |
18
- | dist/ | 构建产物,不提交 Git |
19
-
20
- Biome 与 NSNanoCat Util/FlatBufferRoot 对齐:tab、LF、320 列,保留统一 lint 规则。类型声明位于 src/index.d.ts 和 src/browser/index.d.ts,JSDoc 使用中英双语。
21
-
22
- ## 代理接口
23
-
24
- ~~~js
25
- import { SettingsHandler } from "@nsnanocat/preference-panes";
26
-
27
- const handler = new SettingsHandler({
28
- origin: "https://example.org",
29
- storageKey: "Root",
30
- module: "Module"
31
- });
32
- const response = await handler.handle($request);
33
- // 使用现有代理宿主的 done 适配。
34
- // Adapt the response with the existing proxy host's done function.
35
- ~~~
36
-
37
- 安装配置固定 Root.Module,浏览器不能通过 header 指定其它根。API 不接受 configURL、loadConfig 或 resolveSettings,也不依赖 BoxJS 是否可用。
38
-
39
- | 请求 | 行为 |
40
- | --- | --- |
41
- | HEAD /api/Module/… | 确认路由可达,不读取存储 |
42
- | GET /api/Module/Settings/key | 返回原值,缺失返回 404 |
43
- | POST /api/Module/Settings/key | 以任意 JSON 值替换该位置,成功 200 |
44
- | DELETE /api/Module/Settings/key | 删除键或子树,不存在也成功 |
45
- | GET /api/Module/Caches | 返回所有 Caches |
46
- | DELETE /api/Module/Caches | 清空缓存,保留 Settings |
47
- | DELETE /api/Module/ | 重置模块全部数据,保留 Root 下其它模块 |
3
+ PreferencePanes **只负责具体模块的设置页**。外部输入为这个模块的 BoxJS JSON 和可选 CSS;省略 CSS 使用默认样式。
48
4
 
49
- POST 正文就是值本身,允许对象、数组、null、字符串、数字或布尔值。不存在于 BoxJS 中的键也允许读写。使用 util Storage/Lodash 做根对象读改写,保留同级数据;每次 GET 读一次根,POST/DELETE 读一次再写一次,不发网络请求。仍检查模块归属、路径格式、请求来源、JSON 语法和正文大小;不做 BoxJS 业务校验。
5
+ 项目定制主页由 github.io 等调用方独立维护。主页只探测各模块的 JSON 是否可访问,并提供入口;它的布局、品牌、按钮目录不属于 PreferencePanes。本包不生成主页、模块选择目录或安装选择器。
50
6
 
51
- ## WebView
7
+ ## 导入一个模块
52
8
 
53
- ~~~js
54
- import { mountPreferencePanes } from "@nsnanocat/preference-panes/browser";
55
- import "@nsnanocat/preference-panes/browser/panel.css";
9
+ ```js
10
+ import { mount } from "@nsnanocat/preference-panes/browser";
11
+ import boxjs from "./Module.boxjs.json" with { type: "json" };
56
12
 
57
- const panel = mountPreferencePanes({ element: document.querySelector("#preferences") });
58
- // 卸载时调用 panel.destroy()。
59
- // Call panel.destroy() when unmounting.
60
- ~~~
13
+ const page = mount(boxjs, ".pp-panel { --pp-accent: #16866a; }");
14
+ // 离开模块页时释放视图、样式、监听器和会话。
15
+ // Release the view, styles, listeners and session when leaving the module page.
16
+ page.destroy();
17
+ ```
61
18
 
62
- 同一份 HTML /settings/{module} 读取模块名,再 GET /configs/{module} 取得 BoxJS 并生成控件。配置源地址写在模块的 Mock 规则中,不写入页面 query 参数或 API。
19
+ 调用后直接显示该 JSON 对应的模块设置,不先显示入口页,也不按当前 URL 选择其它模块。一次输入必须恰好包含一个可推导模块;多模块文件会报错,不会生成菜单。支持字段数组、单 app 和只包含该模块的 apps 订阅。
63
20
 
64
- 每次进入主菜单仅并发 HEAD 各配置 Mock。打开、再次进入或刷新模块页,各 GET 一次 BoxJS 与设置子树;404 的设置子树按无覆盖值处理。保存/删除根据 HTTP 200 更新页面缓存并显示通知,不追加 GET。
21
+ `@Root.Module.Settings.key` 推导根、模块和字段路径;app id/name 不替代存储映射。模块名、标题、说明、图标和选项来自 BoxJS。CSS 是正文字符串,作用于该模块文档;嵌入项目主页时应使用独立模块页面或 iframe,避免样式作用到宿主。
65
22
 
66
- 设置页使用分组行布局:单选为下拉框,开关为即时切换,多选显示摘要并进入可前进/后退的二级选项页。文本输入、下拉选择及勾选变化均立即串行 POST,无逐项保存或删除按钮;失败恢复当前项已保存值,较新的输入不会被较早请求覆盖。多选页返回时保留主列表滚动位置,不重新 GET 配置。单键 DELETE 能力保留在 API/客户端方法中,不作为逐项页面按钮展示。
23
+ ## 构建模块产物
67
24
 
68
- 模块页底部提供查看/刷新 Caches、清空 Caches 和重置模块。查看缓存按需 GET;清空和重置经确认后 DELETE,成功只更新本页状态。重置后控件显示当前 BoxJS 默认值,再次进入页面才重新读取。模块选择、设置值校验和默认值处理都在前端完成。
25
+ ```js
26
+ import { build } from "@nsnanocat/preference-panes";
69
27
 
70
- 主菜单、导航、控件和样式均由本包实现。业务插件只提供 BoxJS JSON;品牌、菜单、图标与安装映射属于托管站点。插件不导入本包、不维护页面代码,也不把设置请求接入自己的业务 Request。未提供对应配置 Mock 的插件入口保持禁用。
28
+ const files = await build(boxjs, css);
29
+ ```
71
30
 
72
- ## 零前端代码接入
31
+ 返回相对路径到正文的映射,调用方写出并托管即可。每次只生成该模块的 HTML、CSS、BoxJS、读写脚本、配置 Mock 和公共启动 JS。模块文件名独立,可以合并不同模块的产物;不会输出或覆盖项目的 `settings/index.html`。
73
32
 
74
- 托管站点安装本包,部署 `dist/settings/` `/settings/assets/`,将其中 index.html 同时用于 `/settings/` `/settings/{module}`。根菜单读取由站点维护的 `site.boxjs.json`,这份主菜单不放在业务插件仓库:
33
+ 直接访问 `/settings/{module}` 时,启动器先导入 `/configs/{module}` JSON 与该模块 CSS,再调用 mount。JSON 缺失或与 URL 不符时不生成表单。项目主页可自行 HEAD `/configs/{module}` 判断入口可用性;PreferencePanes 不接管主页探测逻辑。
75
34
 
76
- ~~~json
77
- {
78
- "name": "Example",
79
- "icon": "/assets/logo.png",
80
- "sectionTitle": "模块",
81
- "apps": [{ "module": "Module", "name": "Example Module", "icon": "/assets/module.png" }]
82
- }
83
- ~~~
35
+ 也支持为模块页传入 JSON/CSS **资源 URL**:
84
36
 
85
- `apps[].module` 明确对应 `/settings/{module}`、`/configs/{module}` 和 `/api/{module}/`,不从名称推断。这个菜单 JSON 只声明入口;实际字段仍从配置 Mock 返回的 BoxJS 生成。菜单可选 `desc`、`iconDark` 和 `stylesheets`;`iconDark` 是显式暗色图标扩展,不能把 BoxJS 的透明/彩色 icons 当成亮暗版本。stylesheets 仅加载接入方指定的 HTTP(S) 样式,不加载业务 JS。
37
+ ```http
38
+ GET /settings/Module?json=%2Fconfigs%2FModule&css=%2Ftheme.css
39
+ ```
86
40
 
87
- 根页面每次进入重新 HEAD 探测,菜单 JSON 在当前文档只读取一次;模块页直接打开时无需先读菜单。图片与额外样式由托管站点提供。
41
+ ```http
42
+ GET /settings/Module
43
+ X-PreferencePanes-JSON: /configs/Module
44
+ X-PreferencePanes-CSS: /theme.css
45
+ ```
88
46
 
89
- 代理优先使用原生 Mock 提供配置和页面。存储 API 和没有原生 Mock 的资源请求,由独立代理脚本处理。托管站点维护 installation JSON(origin/storageKey/module/resources),并用包内已经构建好的代理运行时生成安装文件:
47
+ 每个 Header 分别优先于对应查询参数,未提供时使用上述模块约定;CSS 传空字符串时仅使用内置默认样式。资源必须为 HTTP(S) 或相对 URL,跨域资源需要允许浏览器 CORS。模块身份仍需与 BoxJS 一致,不改变存储根绑定。
90
48
 
91
- ~~~js
92
- import { readFile, writeFile } from "node:fs/promises";
49
+ Header 方式需要独立代理脚本处理页面请求,将输入写入 HTML;静态 HTML Mock 无法读取请求头。普通网页用 fetch 携带 Header,并把返回的 HTML 赋给同源 iframe.srcdoc;不要用 innerHTML。iframe 隔离模块 CSS,模块二级页使用自身 fragment 历史;主页负责建立进入模块的历史记录,返回时销毁 iframe。重新打开或刷新由主页重新 fetch,不用 localStorage/sessionStorage 缓存输入或设置。
93
50
 
94
- const runtime = await readFile(new URL(import.meta.resolve("@nsnanocat/preference-panes/dist/preference-panes.proxy.js")), "utf8");
95
- const installation = JSON.parse(await readFile("installation.json", "utf8"));
96
- await writeFile("PreferencePanes.request.js", `${runtime}\nPreferencePanes.runPreferences(${JSON.stringify(installation)});\n`);
97
- ~~~
51
+ ## 模块页行为
98
52
 
99
- 通用运行时由独立 PreferencePanes 代理模块安装一次,统一匹配 /api/ 与页面资源;业务插件不再安装读写或页面规则,只提供 /configs/{module} 的配置 Mock。安装映射的 module 可以是模块名数组,例如 ["Module", "Other"],统一模块只能访问声明范围。生成文件不依赖 $argument,Quantumult X 也使用同一文件。API 不下载安装 JSON 或 BoxJS。
53
+ ### 共用导航组件
100
54
 
101
- 独立模块不得匹配 /configs/,资源映射也不得为配置 Mock 提供兜底。主菜单每次进入只 HEAD 配置 Mock;HTTP 200 启用入口,打开后 GET 并解析 JSON。配置缺失、无有效字段或解析失败时不读取设置 API、不生成设置表单。API 可达不代表某个业务模块提供了设置界面。
55
+ 模块二级页与外部定制主页复用 `Navigation`,通过独立导出使用,不引入表单、BoxJS 或品牌目录:
102
56
 
103
- Surge/Loon 的业务插件使用原生 JSON Mock。需要脚本响应配置的平台,由托管站点将包内 dist/preference-panes.config.js 与 `PreferencePanes.mockConfiguration(BoxJS_JSON)` 拼接生成配置响应文件。该文件只支持 GET/HEAD,不包含存储、网络下载或页面处理;它只安装在业务插件的 /configs/{module} 规则中。关闭业务插件后 Mock 消失,独立通用模块继续启用也不会误判该设置入口可用。
57
+ ```js
58
+ import { Navigation } from "@nsnanocat/preference-panes/navigation";
104
59
 
105
- 以下是包内部处理器接受的安装映射形状;也可供需要手动集成的宿主使用:
106
-
107
- ~~~js
108
- import { PreferencesHandler } from "@nsnanocat/preference-panes";
109
-
110
- const handler = new PreferencesHandler({
111
- origin: "https://example.org",
112
- storageKey: "Root",
113
- module: "Module",
114
- resources: [
115
- { pattern: "^/settings/(?:[a-zA-Z0-9_-]+/?)?$", source: "https://example.org/settings/assets/index.html", contentType: "text/html" }
116
- ]
60
+ const navigation = new Navigation(container, home, (key, signal) => {
61
+ // 返回由项目创建的子页节点;异步加载监听 signal,在退出时取消。
62
+ // Return the project's detail node; async loads observe signal for cancellation on departure.
63
+ return detailViews.get(key);
117
64
  });
118
- const response = await handler.handle($request);
119
- ~~~
65
+ navigation.open("detail");
66
+ navigation.addEventListener("change", () => { back.disabled = !navigation.canGoBack; });
67
+ back.onclick = () => navigation.back();
68
+ // 卸载整个组件时释放监听器和页面。
69
+ // Release listeners and views when unmounting the entire component.
70
+ navigation.destroy();
71
+ ```
72
+
73
+ 工厂返回 HTMLElement,未知键返回 undefined;布局、背景与内容由调用方 CSS/DOM 定义。容器内只放导航管理的节点,组件接管其子节点。根页一直保留,子页 280ms 滑入/滑出,返回动画结束后移除。模块内部返回缓存节点保留控件值与滚动;外部主页每次创建新 iframe,重新读取设置。快速切换会取消旧加载与动画,减少动态效果时立即切换。历史记录、书签初始根页和 srcdoc fragment 只在组件中处理;实例之间依靠浏览器联合历史,不跨 iframe 操作 DOM。
120
74
 
121
- 资源 pattern 匹配 pathname,下载源必须避开拦截路径。只有命中静态资源的 GET/HEAD 才下载文件;API 由 SettingsHandler 直接处理,读写不会下载 BoxJS。业务插件只保留配置 Mock;页面与 API 安装规则全部属于独立 PreferencePanes 模块。
75
+ `build` 额外输出公共 `settings/assets/navigation.mjs`,静态主页可直接导入该地址;它不生成或接管主页内容。
122
76
 
123
- ## BoxJS 兼容
77
+ 渲染器使用已导入的 JSON 创建控件,只 GET 一次设置子树;不会再次请求配置或探测其它模块。打开/刷新模块文档重新导入,再读取设置。单选为下拉框,多选为二级选项页;二级返回不刷新设置值。
124
78
 
125
- 前端接受字段数组、单 app apps 订阅。字段 ID @根.模块.子路径.键;模块归属来自字段 ID,不能用 app 名称推断。
79
+ 修改立即串行 POST,200 后更新当前内存并通知;失败恢复已保存值。保留 Caches 查看/清空和模块重置,不展示逐字段保存或删除按钮。API BoxJS 根和模块用 util 读写任意 JSON 路径,不重复校验控件或枚举。
126
80
 
127
- - name/val/type/desc/items:控件标题、默认值、类型、说明和选项。
128
- - boolean/selects/checkboxes/text/textarea/number:支持的控件类型。
129
- - placeholder/rows/autoGrow:输入提示、多行基础行数和自动高度。
130
- - app name/author/desc/descs/repo:纯文本标题、作者、说明和项目链接。
131
- - icon/icons:显式图标优先,原版 icons 为透明/彩色顺序,不是亮暗顺序。
132
- - script:仅保留元数据,不下载或执行。
81
+ ## 文件导入测试台
133
82
 
134
- 不执行 BoxJS HTML、脚本、动态字符串 items,不通过 keys 推导额外字段。WebView 使用原生网络与对象访问,不打入 util 的网络、存储或 Lodash polyfill。代理安装的 storageKey/module 应由接入方与 BoxJS 路径保持一致。
83
+ ```sh
84
+ npm run preview
85
+ ```
135
86
 
136
- ## 构建与发布
87
+ 浏览器中选择一个模块的 JSON、可选 CSS,点击“生成”,在独立 iframe 查看模块页。不会自动装入示例,不会生成项目主页。CSS 隔离在预览文档内;测试数据只保存在内存,不访问用户代理存储。
137
88
 
138
- ~~~sh
139
- npm ci --registry=https://registry.npmjs.org/ --@nsnanocat:registry=https://registry.npmjs.org/
140
- npm run build
141
- npm run check
142
- npm run apifox:generate
143
- npm run apifox:check
144
- npm pack --dry-run
145
- ~~~
89
+ ## 维护
146
90
 
147
- 构建生成 dist/preference-panes.mjs、读取宿主参数的 dist/preference-panes.request.js、供托管站点配置的 dist/preference-panes.proxy.js、仅返回配置的 dist/preference-panes.config.js,以及可直接部署的 dist/settings/{index.html,app.mjs,panel.css,home.css}。0.6.0 支持独立模块统一处理多个业务模块,并将配置 Mock 与通用读写安装彻底分开。
91
+ 遵循 AGENTS.md 与通用 Biome 配置。运行 `npm run check` 和 `npm run apifox:check` 验证代码、类型、行为与文档。0.7.1 采用单模块双输入接口,并将页面与主页导航收敛到共用组件;运行时无额外 npm 依赖。
148
92
 
149
- [完整接口说明](apifox/guide.md) · [Apifox JSON](apifox/preference-panes.apifox.json) · [同步方式](apifox/README.md) · [发布工作流](.github/RELEASING.md)
93
+ [接口规范](apifox/Specification.md) · [Apifox JSON](apifox/preference-panes.apifox.json)