@nsnanocat/preference-panes 0.9.6 → 0.9.7

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.
@@ -1,142 +0,0 @@
1
- /**
2
- * Bilibili common WebView 的官方 JSBridge 适配器。
3
- * Official JSBridge adapter for Bilibili common WebViews.
4
- */
5
- export class BilibiliHost {
6
- #bridge;
7
- #select;
8
- #state = { title: "", actions: [], busy: false };
9
- #revision = 0;
10
- #queue = Promise.resolve();
11
- #destroyed = false;
12
- #document;
13
- #navigation = result => {
14
- if (result.code !== 0 || result.data?.id !== "preference-panes.more" || this.#state.busy || this.#state.actions.length === 0) return;
15
- const revision = this.#revision;
16
- const actions = this.#state.actions;
17
- this.#bridge
18
- .useNative("liveUI.selectPanel", {
19
- title: "更多操作",
20
- options: actions.map(action => ({ text: action.label, value: action.id })),
21
- })
22
- .then(result => {
23
- if (this.#destroyed || revision !== this.#revision || this.#state.busy) return;
24
- const action = actions.find(item => item.id === result.data.text);
25
- if (action) this.#select(action.id);
26
- });
27
- };
28
- #theme = result => {
29
- if (result.code === 0 && result.data?.theme) this.#applyTheme(result.data.theme);
30
- };
31
- #keyboard = result => {
32
- if (result.code === 0 && typeof result.data?.status === "boolean") this.#document.documentElement.style.setProperty("--pp-keyboard-height", `${result.data.status ? result.data.height : 0}px`);
33
- };
34
-
35
- /**
36
- * 连接页面已加载的官方 SDK。
37
- * Connect to the official SDK already loaded by the document.
38
- * @param {(id: string) => void} select 模块菜单回调 / Module action callback.
39
- * @param {Window} [host] Bilibili WebView 窗口 / Bilibili WebView window.
40
- */
41
- constructor(select, host = window) {
42
- this.#bridge = host.biliBridge;
43
- this.#select = select;
44
- this.#document = host.document;
45
- this.#applyTheme(host.navigator.userAgent.includes("themeId/2") ? 2 : 1);
46
- this.ready = this.#connect();
47
- }
48
-
49
- /**
50
- * 初始化主题、键盘和原生导航通道。
51
- * Initialize theme, keyboard and native navigation channels.
52
- * @returns {Promise<void>} 初始化完成 / Initialization completion.
53
- */
54
- async #connect() {
55
- await this.#bridge.initPromise;
56
- if (!this.#bridge.isWbTypeCommon) throw new Error("PreferencePanes requires a Bilibili common WebView");
57
- this.#bridge.addChannel("ui.observeThemeChange", this.#theme, { immediately: true });
58
- this.#bridge.addChannel("ui.observeKeyboardStatus", this.#keyboard);
59
- this.#bridge.addChannel("ui.observeNavigationClick", this.#navigation);
60
- await this.#bridge.useNative("ui.setNavigationHide", { hide: false });
61
- }
62
-
63
- /**
64
- * 同步官方导航标题和更多按钮。
65
- * Synchronize the official navigation title and overflow button.
66
- * @param {{title: string, actions: Array<{id: string, label: string}>, busy: boolean}} state 页面状态 / Page state.
67
- * @returns {Promise<void>} 更新完成 / Update completion.
68
- */
69
- update(state) {
70
- this.#state = state;
71
- const revision = ++this.#revision;
72
- const render = async () => {
73
- await this.ready;
74
- if (this.#destroyed || revision !== this.#revision) return;
75
- await this.#bridge.useNative("ui.setTitle", { title: state.title });
76
- if (this.#destroyed || revision !== this.#revision) return;
77
- await this.#bridge.useNative("ui.setNavigationButton", {
78
- buttons: !state.busy && state.actions.length ? [{ id: "preference-panes.more", type: 3, visible: true }] : [],
79
- });
80
- };
81
- this.#queue = this.#queue.then(render, render);
82
- return this.#queue;
83
- }
84
-
85
- /**
86
- * 显示官方确认面板。
87
- * Show the official confirmation panel.
88
- * @param {string} message 确认内容 / Confirmation message.
89
- * @returns {Promise<boolean>} 用户是否确认 / Whether the user confirmed.
90
- */
91
- async confirm(message) {
92
- await this.ready;
93
- return new Promise((resolve, reject) =>
94
- this.#bridge.callNative({
95
- method: "ability.alert",
96
- data: { type: "confirm", title: this.#state.title, message, confirmButton: "确定", cancelButton: "取消" },
97
- onConfirm: () => resolve(true),
98
- onCancel: () => resolve(false),
99
- onNeutral: () => resolve(false),
100
- callback: result => {
101
- if (result instanceof Error || result === "error") reject(new Error("Native confirmation failed"));
102
- },
103
- }),
104
- );
105
- }
106
-
107
- /**
108
- * 显示官方短提示。
109
- * Show an official short notice.
110
- * @param {string} message 提示内容 / Notice message.
111
- * @returns {Promise<void>} 提示已提交 / Notice dispatched.
112
- */
113
- async notice(message) {
114
- await this.ready;
115
- await this.#bridge.useNative("liveUI.toast", { type: "short", msg: message });
116
- }
117
-
118
- /**
119
- * 释放官方事件通道。
120
- * Release official event channels.
121
- * @returns {void} 无返回值 / No return value.
122
- */
123
- destroy() {
124
- this.#destroyed = true;
125
- this.#revision++;
126
- this.#bridge.removeChannel("ui.observeThemeChange", this.#theme);
127
- this.#bridge.removeChannel("ui.observeKeyboardStatus", this.#keyboard);
128
- this.#bridge.removeChannel("ui.observeNavigationClick", this.#navigation);
129
- }
130
-
131
- /**
132
- * 将官方主题值映射到页面主题标记。
133
- * Map the official theme value to document theme markers.
134
- * @param {number | string} value 官方主题值 / Official theme value.
135
- * @returns {void} 无返回值 / No return value.
136
- */
137
- #applyTheme(value) {
138
- const dark = value === 2 || value === "dark";
139
- this.#document.documentElement.dataset.theme = dark ? "dark" : "light";
140
- this.#document.documentElement.classList.toggle("bili_dark", dark);
141
- }
142
- }
@@ -1,82 +0,0 @@
1
- import { BilibiliHost } from "./BilibiliHost.mjs";
2
- import { ModuleFrame } from "./ModuleFrame.mjs";
3
- import { ModuleStatus } from "./ModuleStatus.mjs";
4
- import { Navigation } from "./Navigation.mjs";
5
-
6
- const container = document.querySelector("[data-preference-panes-pages]");
7
- const home = document.querySelector("[data-preference-panes-home]");
8
- const template = document.querySelector("template[data-preference-panes-module]");
9
- const buttons = [...home.querySelectorAll("[data-module][data-page][data-json]")];
10
- let frame;
11
-
12
- const native = new BilibiliHost(id => frame.perform(id));
13
- const navigation = new Navigation(container, home, (module, signal) => {
14
- const button = buttons.find(button => button.dataset.module === module);
15
- if (!button) return;
16
- const host = template.content.firstElementChild.cloneNode(true);
17
- const message = host.querySelector("[data-module-message]");
18
- frame = new ModuleFrame(button.dataset.page, {
19
- signal,
20
- headers: {
21
- "X-PreferencePanes-JSON": button.dataset.json,
22
- ...(button.dataset.css ? { "X-PreferencePanes-CSS": button.dataset.css } : {}),
23
- },
24
- });
25
- frame.addEventListener("confirm", event => {
26
- event.preventDefault();
27
- native.confirm(event.detail.message).then(event.detail.resolve, event.detail.reject);
28
- });
29
- frame.addEventListener("notice", event => {
30
- event.preventDefault();
31
- native.notice(event.detail.message);
32
- });
33
- frame.addEventListener("change", updateNavigation);
34
- frame.element.onload = () => {
35
- message.hidden = true;
36
- };
37
- host.append(frame.element);
38
- frame.load().catch(error => {
39
- if (!signal.aborted) message.textContent = error.message;
40
- });
41
- return host;
42
- });
43
-
44
- const statuses = buttons.map(button => {
45
- const status = new ModuleStatus(button.querySelector("[data-module-status]"));
46
- status.addEventListener("change", () => {
47
- button.disabled = status.state.status !== "installed";
48
- });
49
- button.addEventListener("click", () => navigation.open(button.dataset.module));
50
- return { button, status };
51
- });
52
-
53
- /**
54
- * 同步当前页面的官方导航状态。
55
- * Synchronize the official navigation state for the current page.
56
- * @returns {void} 无返回值 / No return value.
57
- */
58
- function updateNavigation() {
59
- const state = navigation.current ? frame.state : { title: document.title, actions: [], busy: false };
60
- native.update(state);
61
- }
62
-
63
- /**
64
- * 每次返回主页并发探测全部配置 Mock。
65
- * Probe all configuration Mocks concurrently whenever the home page is entered.
66
- * @returns {void} 无返回值 / No return value.
67
- */
68
- function probe() {
69
- updateNavigation();
70
- if (navigation.current) return;
71
- for (const { button, status } of statuses) status.check(button.dataset.json);
72
- }
73
-
74
- navigation.addEventListener("change", probe);
75
- window.addEventListener("pagehide", event => {
76
- if (!event.persisted) {
77
- for (const { status } of statuses) status.destroy();
78
- navigation.destroy();
79
- native.destroy();
80
- }
81
- });
82
- probe();