@menghuan1918/dsh-apollo 0.6.4 → 0.6.5
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 +6 -6
- package/docs/DEVELOPMENT.md +6 -6
- package/package.json +2 -2
- package/src/client.js +373 -51
package/README.md
CHANGED
|
@@ -90,13 +90,13 @@ flowchart TB
|
|
|
90
90
|
|
|
91
91
|
```sh
|
|
92
92
|
# npm 安装(发布后)
|
|
93
|
-
dsh plugin --profile <profile> add @menghuan1918/dsh-apollo@0.6.
|
|
93
|
+
dsh plugin --profile <profile> add @menghuan1918/dsh-apollo@0.6.5
|
|
94
94
|
|
|
95
95
|
# 或从 GitHub main 源码安装(备选)
|
|
96
96
|
dsh plugin --profile <profile> add github:Menghuan1918/dsh-apollo
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
> **发布状态**:Apollo `0.6.
|
|
99
|
+
> **发布状态**:Apollo `0.6.5` 与 `dsh-better-ask-user@0.1.7` 的本地修复与隔离安装验证已完成,npm 发布待确认;`dsh-better-ptc@0.1.0` 已发布。提交的 manifest 使用精确 npm 版本,**没有** `file:` / `link:` / Git 说明符。发布顺序见[发布顺序](#发布顺序)。
|
|
100
100
|
|
|
101
101
|
安装、升级或换包都会改变运行的代码,必须重启对应 profile 的 DSH 进程;只刷新网页不够。目标行、子代理行、压缩行、共享 PTC 行与 ask-user 行由 Apollo 的 bundle patch 插入,不需要再单独 `add` 五个子包。
|
|
102
102
|
|
|
@@ -106,7 +106,7 @@ dsh plugin --profile <profile> add github:Menghuan1918/dsh-apollo
|
|
|
106
106
|
|
|
107
107
|
```sh
|
|
108
108
|
dsh plugin --profile <profile> remove dsh-apollo
|
|
109
|
-
dsh plugin --profile <profile> add @menghuan1918/dsh-apollo@0.6.
|
|
109
|
+
dsh plugin --profile <profile> add @menghuan1918/dsh-apollo@0.6.5
|
|
110
110
|
```
|
|
111
111
|
|
|
112
112
|
> `dsh-apollo` 只是旧入口名;unscoped 的 `dsh-apollo` 已被其他项目占用,不要从 npm 安装它。
|
|
@@ -180,7 +180,7 @@ dsh plugin --profile <profile> add @menghuan1918/dsh-better-subagent@0.1.0
|
|
|
180
180
|
dsh plugin --profile <profile> add dsh-better-compact@0.1.1
|
|
181
181
|
|
|
182
182
|
# 只装提问增强(同时需要共享 PTC 行)
|
|
183
|
-
dash plugin --profile <profile> add dsh-better-ask-user@0.1.
|
|
183
|
+
dash plugin --profile <profile> add dsh-better-ask-user@0.1.7 dsh-better-ptc@0.1.0
|
|
184
184
|
```
|
|
185
185
|
|
|
186
186
|
独立安装时的行、页面和提示词与 Apollo 集成完全相同;行 id/name 也是同一组稳定值。区别只在**由谁插入这些行**:Apollo 的 bundle patch 自己携带同样的行,因为 DSH 不会自动应用传递依赖的 bundle patch。
|
|
@@ -235,7 +235,7 @@ dsh plugin --profile <profile> remove dsh-better-compact
|
|
|
235
235
|
- `package.json` 的 `files` 只列入 `docs/DEVELOPMENT.md`,并额外用 [.npmignore](.npmignore) 排除 `docs/specs/`;
|
|
236
236
|
- `tests/packaging.test.mjs` 直接运行 `npm pack --dry-run --json`,断言发布清单没有 Specs、测试、脚本、快照或 tarball。
|
|
237
237
|
|
|
238
|
-
发布顺序:**先发布并验证五个独立包(`dsh-better-ask-user@0.1.
|
|
238
|
+
发布顺序:**先发布并验证五个独立包(`dsh-better-ask-user@0.1.7`、`dsh-better-ptc@0.1.0`、`@menghuan1918/dsh-better-subagent@0.2.0`、`dsh-better-goal@0.2.1`、`dsh-better-compact@0.1.2`),再发布 Apollo 0.6.5**。Apollo 的精确依赖指向尚不存在的版本时,`npm install` 会失败;不要用开发 `file:`/`link:` 依赖发布。各子仓各自有 trusted publishing workflow;Apollo 的 [release workflow](.github/workflows/release.yml) 只发布本包。
|
|
239
239
|
|
|
240
240
|
## 验证状态(诚实记录)
|
|
241
241
|
|
|
@@ -248,7 +248,7 @@ dsh plugin --profile <profile> remove dsh-better-compact
|
|
|
248
248
|
|
|
249
249
|
**未验证 / 明确限制**:
|
|
250
250
|
|
|
251
|
-
-
|
|
251
|
+
- 0.6.5 与 ask-user 0.1.7 的本地修复及隔离安装验证通过;实际 npm 发布待确认。
|
|
252
252
|
- 未修改或重启用户当前运行的 profile,未验证当前 GUI(`http://127.0.0.1:3384`)的设置页/卡片;S3 视觉设置页的 GUI 验收归 S3/最终宿主验收。
|
|
253
253
|
- 未运行真实 Codex/Claude CLI 的真实模型回合(那属于两个子包自己的验证;Apollo 侧本轮只用脚本化适配器)。
|
|
254
254
|
- 真实模型摘要保真、缓存命中率与跨版本预设漂移仍需按实际环境复验。
|
package/docs/DEVELOPMENT.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
面向维护者、集成者和需要排查装配问题的用户。用户安装与配置请看 [README](../README.md);本文保留 DSH 插件的实现细节、旧配置迁移、组合安装边界与验证方法。
|
|
4
4
|
|
|
5
|
-
当前实现版本:`0.6.
|
|
5
|
+
当前实现版本:`0.6.5`;npm 包名是 `@menghuan1918/dsh-apollo`。纯 ESM,无构建步骤;宿主浏览器半边是直接加载的闭包工厂。
|
|
6
6
|
|
|
7
7
|
## 目录
|
|
8
8
|
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
| `dsh-better-goal` | `0.2.1` | 目标域(`questionTimeoutSeconds` 已弃用、仅保留读取) |
|
|
37
37
|
| `@menghuan1918/dsh-better-subagent` | `0.2.0` | 子代理 CLI/等待/child guard/分档/设置页;注册 `wait_subagent` PTC 豁免 |
|
|
38
38
|
| `dsh-better-compact` | `0.1.2` | 压缩 companion + engine + 设置页/诊断 |
|
|
39
|
-
| `dsh-better-ask-user` | `0.1.
|
|
39
|
+
| `dsh-better-ask-user` | `0.1.7` | 官方 `ask_user_question` 入参/返回 + 插件超时等待 + 历史预览行;注册 PTC 豁免;旧 goal 等待迁移 |
|
|
40
40
|
| `dsh-better-ptc` | `0.1.0` | 单例 PTC deadline 豁免服务 |
|
|
41
41
|
| `@deepseek-ai/schemastery` | `~3.18.4` | Apollo 行 Config |
|
|
42
42
|
| `react` | peer | 宿主提供 |
|
|
@@ -318,10 +318,10 @@ DSH_PACKAGE_ROOT="/…/node_modules/@deepseek-ai/dsh" npm run prompts
|
|
|
318
318
|
### npm 安装
|
|
319
319
|
|
|
320
320
|
```sh
|
|
321
|
-
dsh plugin --profile <profile> add @menghuan1918/dsh-apollo@0.6.
|
|
321
|
+
dsh plugin --profile <profile> add @menghuan1918/dsh-apollo@0.6.5
|
|
322
322
|
```
|
|
323
323
|
|
|
324
|
-
包发布后即可用此命令安装,并带入五个精确依赖、应用三份 bundle patch。安装后需要重启对应 profile 的 DSH 进程。0.6.
|
|
324
|
+
包发布后即可用此命令安装,并带入五个精确依赖、应用三份 bundle patch。安装后需要重启对应 profile 的 DSH 进程。0.6.5 与 ask-user 0.1.7 的本地修复及隔离安装验证通过;实际 npm 发布待确认。
|
|
325
325
|
|
|
326
326
|
### GitHub 源码安装
|
|
327
327
|
|
|
@@ -376,9 +376,9 @@ dsh plugin --profile <profile> remove @menghuan1918/dsh-apollo
|
|
|
376
376
|
|
|
377
377
|
两个子仓和 Apollo 都用 GitHub Release 触发 `.github/workflows/release.yml`,通过 npm trusted publishing(OIDC)发布,仓库里不存 npm token。tag 必须是 `v<package.json 的 version>`,CI 会先校验再跑测试和 `npm publish --provenance`。
|
|
378
378
|
|
|
379
|
-
1. 先发布 `dsh-better-ptc@0.1.0`、`dsh-better-ask-user@0.1.
|
|
379
|
+
1. 先发布 `dsh-better-ptc@0.1.0`、`dsh-better-ask-user@0.1.7`、`@menghuan1918/dsh-better-subagent@0.2.0`、`dsh-better-goal@0.2.1`、`dsh-better-compact@0.1.2`(各自仓库的 Release);
|
|
380
380
|
2. 在官方 registry(`https://registry.npmjs.org/`)确认五个版本可取得、可安装;
|
|
381
|
-
3. 再把 Apollo 的精确依赖指向已发布版本,推 main 并发布 Apollo Release(本轮为 `v0.6.
|
|
381
|
+
3. 再把 Apollo 的精确依赖指向已发布版本,推 main 并发布 Apollo Release(本轮为 `v0.6.5`);
|
|
382
382
|
4. 子包版本可取得前,不要把指向它们的精确依赖推到 Apollo main,否则 `npm install` / GitHub 源码安装会 404。
|
|
383
383
|
|
|
384
384
|
## 测试与冒烟
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@menghuan1918/dsh-apollo",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"description": "DSH plugin bundle: workflow skills, engineering presets, independent better-goal / better-subagent / better-compact / better-ask-user integration, and a minimal job exit guard. Pure ESM, no build step.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"repository": {
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"dependencies": {
|
|
56
56
|
"@deepseek-ai/schemastery": "~3.18.4",
|
|
57
57
|
"@menghuan1918/dsh-better-subagent": "0.2.0",
|
|
58
|
-
"dsh-better-ask-user": "0.1.
|
|
58
|
+
"dsh-better-ask-user": "0.1.7",
|
|
59
59
|
"dsh-better-compact": "0.1.2",
|
|
60
60
|
"dsh-better-goal": "0.2.1",
|
|
61
61
|
"dsh-better-ptc": "0.1.0"
|
package/src/client.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* dsh-apollo (npm: @menghuan1918/dsh-apollo) —
|
|
2
|
+
* dsh-apollo (npm: @menghuan1918/dsh-apollo) — 浏览器半边。
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* - `exitGuard`:运行中**后台 job** 的退出守卫开关(volatile,保存即生效);
|
|
6
|
-
* - 迁移/弃用说明:CLI 子代理、模型分档、运行中子代理守卫与压缩设置分别由
|
|
7
|
-
* `@menghuan1918/dsh-better-subagent` 与 `dsh-better-compact` 的行拥有。
|
|
8
|
-
* Apollo 的旧字段仍能装载(供一次性迁移读取),但不再作为可编辑的子代理设置。
|
|
4
|
+
* 两块 UI:
|
|
9
5
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
6
|
+
* 1. **行配置页**(`plugins.row.config`,键 `<包名>#apollo-skills`):后台任务
|
|
7
|
+
* `exitGuard` 开关 + 迁移/弃用说明。保存即生效(volatile),写入当前
|
|
8
|
+
* profile 的行配置(cordis.patch.yml)。
|
|
9
|
+
*
|
|
10
|
+
* 2. **集成设置弹窗**(`plugins.detail.actions` 的 "Apollo 集成设置" 按钮,
|
|
11
|
+
* 只在 subject 为 Apollo bundle 详情页时渲染):弹窗左 nav + 右内容,聚合
|
|
12
|
+
* 渲染各子插件自己注册到 `plugins.row.config` 的设置组件——entry 组件经
|
|
13
|
+
* `ctx.slots.entriesOfSlot` 按 `#<rowId>` 键后缀找到,`form` 用
|
|
14
|
+
* `ctx.configForms.get(rowId)` 复刻官方 `formFor`(`{state,mutate}`),
|
|
15
|
+
* `t` 用 `ctx.locale.bind(entry.locale)` 补齐。子插件页面永远是单一事实
|
|
16
|
+
* 源,本文件不复制任何字段定义。
|
|
17
|
+
*
|
|
18
|
+
* 主题:#3388BB(`--cfg-accent`),其余全部走 --dsw-alias-* 中性令牌。
|
|
19
|
+
* 零构建:closure factory,只 require('react') + 可选 ui-primitives(Modal
|
|
20
|
+
* 兜底为自绘 overlay);不 fetch、不直接写 DOM。
|
|
15
21
|
*/
|
|
16
22
|
|
|
17
23
|
window.__ModuleLoader__.load({
|
|
@@ -19,19 +25,43 @@ window.__ModuleLoader__.load({
|
|
|
19
25
|
factory: (require) => {
|
|
20
26
|
var module = { exports: {} };
|
|
21
27
|
var exports = module.exports;
|
|
22
|
-
const
|
|
28
|
+
const React = require('react');
|
|
29
|
+
const { createElement: h, useEffect, useMemo, useState, useSyncExternalStore } = React;
|
|
30
|
+
|
|
31
|
+
/** ui-primitives 是可选依赖:拿不到时弹窗退化为自绘 overlay。 */
|
|
32
|
+
let Primitives = null;
|
|
33
|
+
try {
|
|
34
|
+
Primitives = require('@deepseek-ai/dsh-client-ui-primitives');
|
|
35
|
+
} catch {
|
|
36
|
+
Primitives = null;
|
|
37
|
+
}
|
|
38
|
+
const Modal = Primitives?.Modal ?? null;
|
|
23
39
|
|
|
24
40
|
const NS = 'apollo';
|
|
41
|
+
const PACKAGE_NAME = '@menghuan1918/dsh-apollo';
|
|
42
|
+
const LEGACY_PACKAGE_NAME = 'dsh-apollo';
|
|
25
43
|
// 新键是 Plugins 页按“bundle 依赖名 + 行 id”算出的(= npm 包名 + #apollo-skills);
|
|
26
44
|
// 旧键并排注册,只为仍把依赖槽记成旧 unscoped 名的 profile 保留配置入口。
|
|
27
45
|
const ROW_KEYS = ['@menghuan1918/dsh-apollo#apollo-skills', 'dsh-apollo#apollo-skills'];
|
|
46
|
+
const DETAIL_ACTIONS_SLOT = 'plugins.detail.actions';
|
|
47
|
+
const ROW_CONFIG_SLOT = 'plugins.row.config';
|
|
48
|
+
const HUB_ENTRY_ID = 'apollo-settings-hub';
|
|
49
|
+
|
|
50
|
+
/** 聚合弹窗展示的设置命名空间(= 行 id),按 nav 顺序排列。 */
|
|
51
|
+
const HUB_ROWS = [
|
|
52
|
+
{ rowId: 'apollo-skills', titleKey: 'pluginApollo' },
|
|
53
|
+
{ rowId: 'dsh-better-subagent', titleKey: 'pluginSubagent' },
|
|
54
|
+
{ rowId: 'dsh-better-compact', titleKey: 'pluginCompact' },
|
|
55
|
+
{ rowId: 'dsh-better-goal', titleKey: 'pluginGoal' },
|
|
56
|
+
{ rowId: 'dsh-better-ask-user', titleKey: 'pluginAskUser' },
|
|
57
|
+
];
|
|
28
58
|
|
|
29
59
|
const zh = {
|
|
30
60
|
summary: 'Apollo 集成设置:后台任务退出守卫与迁移说明',
|
|
31
61
|
title: 'Apollo 集成设置',
|
|
32
62
|
intro: 'Apollo 负责技能、工程预设和独立插件的装配。这里只保留后台任务退出守卫;保存后即时生效,配置写入当前 profile 的行配置(cordis.patch.yml)。',
|
|
33
63
|
migratedTitle: '已迁移的能力',
|
|
34
|
-
migratedBody: 'CLI 子代理、wait_subagent、运行中子代理守卫、模型分档与子代理设置页由 @menghuan1918/dsh-better-subagent 拥有;压缩模式与预算由 dsh-better-compact 拥有。请到 Plugins
|
|
64
|
+
migratedBody: 'CLI 子代理、wait_subagent、运行中子代理守卫、模型分档与子代理设置页由 @menghuan1918/dsh-better-subagent 拥有;压缩模式与预算由 dsh-better-compact 拥有。请到 Plugins 中对应的行配置,或使用包详情页的集成设置弹窗。',
|
|
35
65
|
deprecatedBody: '旧 Apollo 行里的 tiers / subagents / trustedHosts 只为迁移旧 profile 保留,运行时不再生效;迁移在启动后自动补齐新插件的缺失值,目标行已有显式设置时不会被覆盖。',
|
|
36
66
|
reloadBody: '从 0.4.x 升级、或同时显式选中了独立 bundle 时,行可能显示为只读/不可寻址;请只保留一个显式入口并重载 profile。压缩设置的保存需要重载 profile 才会被引擎采用。',
|
|
37
67
|
loading: '正在读取配置…',
|
|
@@ -49,6 +79,18 @@ window.__ModuleLoader__.load({
|
|
|
49
79
|
saveFailed: '保存失败:',
|
|
50
80
|
readonlyNote: '只读:当前无法写入宿主配置。',
|
|
51
81
|
unavailableNote: '不可写:当前连接下该配置不可用。',
|
|
82
|
+
hubButton: '集成设置',
|
|
83
|
+
hubTitle: 'Apollo 集成设置',
|
|
84
|
+
hubIntro: '统一查看和修改 Apollo 及各子插件的设置;各页面与各插件行配置共用同一份表单。',
|
|
85
|
+
hubEmpty: '暂无可用的子插件设置:子插件客户端尚未注册设置页,或本部署未组合设置服务。',
|
|
86
|
+
done: '完成',
|
|
87
|
+
close: '关闭',
|
|
88
|
+
entryError: '此设置区加载失败:',
|
|
89
|
+
pluginApollo: 'Apollo 核心',
|
|
90
|
+
pluginSubagent: 'Better Subagent',
|
|
91
|
+
pluginCompact: 'Better Compact',
|
|
92
|
+
pluginGoal: 'Better Goal',
|
|
93
|
+
pluginAskUser: 'Better Ask User',
|
|
52
94
|
};
|
|
53
95
|
|
|
54
96
|
const en = {
|
|
@@ -56,7 +98,7 @@ window.__ModuleLoader__.load({
|
|
|
56
98
|
title: 'Apollo integration settings',
|
|
57
99
|
intro: 'Apollo owns the skills, the engineering presets and the assembly of the independent plugins. Only the background-job exit guard remains here; saving applies immediately and persists in the active profile row config (cordis.patch.yml).',
|
|
58
100
|
migratedTitle: 'Migrated capabilities',
|
|
59
|
-
migratedBody: 'CLI subagents, wait_subagent, the running-child guard, model tiers and the subagent settings page are owned by @menghuan1918/dsh-better-subagent; compaction modes and budgets are owned by dsh-better-compact.
|
|
101
|
+
migratedBody: 'CLI subagents, wait_subagent, the running-child guard, model tiers and the subagent settings page are owned by @menghuan1918/dsh-better-subagent; compaction modes and budgets are owned by dsh-better-compact. Configure their rows under Plugins, or use the integrated settings dialog on the package page.',
|
|
60
102
|
deprecatedBody: 'The old Apollo tiers / subagents / trustedHosts fields are kept only so 0.4.x profiles load; they are inert at runtime. After boot the migration fills the new plugins’ missing values and never overwrites an explicit target setting.',
|
|
61
103
|
reloadBody: 'Upgrading from 0.4.x, or selecting both the standalone and Apollo bundles explicitly, can make the row read-only/unaddressable: keep a single explicit entry and reload the profile. Saved compaction settings take effect only after the profile reloads.',
|
|
62
104
|
loading: 'Loading configuration…',
|
|
@@ -74,6 +116,18 @@ window.__ModuleLoader__.load({
|
|
|
74
116
|
saveFailed: 'Save failed: ',
|
|
75
117
|
readonlyNote: 'Read-only: host configuration cannot be written.',
|
|
76
118
|
unavailableNote: 'Not writable: this configuration is unavailable on the current connection.',
|
|
119
|
+
hubButton: 'Integrated settings',
|
|
120
|
+
hubTitle: 'Apollo integrated settings',
|
|
121
|
+
hubIntro: 'Review and edit Apollo and every sub-plugin’s settings in one place; each pane shares the same form as that plugin’s row configuration page.',
|
|
122
|
+
hubEmpty: 'No sub-plugin settings are available yet: the sub-plugin clients have not registered their pages, or this deployment does not compose the settings service.',
|
|
123
|
+
done: 'Done',
|
|
124
|
+
close: 'Close',
|
|
125
|
+
entryError: 'This settings pane failed to load: ',
|
|
126
|
+
pluginApollo: 'Apollo core',
|
|
127
|
+
pluginSubagent: 'Better Subagent',
|
|
128
|
+
pluginCompact: 'Better Compact',
|
|
129
|
+
pluginGoal: 'Better Goal',
|
|
130
|
+
pluginAskUser: 'Better Ask User',
|
|
77
131
|
};
|
|
78
132
|
|
|
79
133
|
function interpolate(text, params) {
|
|
@@ -85,37 +139,83 @@ window.__ModuleLoader__.load({
|
|
|
85
139
|
|
|
86
140
|
const STYLE_ID = '@menghuan1918/dsh-apollo/settings.css';
|
|
87
141
|
const CSS = [
|
|
88
|
-
|
|
89
|
-
'.apollo-
|
|
90
|
-
'.apollo-
|
|
142
|
+
/* 主题:#3388BB accent;其余全部中性 --dsw-alias-* 令牌。 */
|
|
143
|
+
'.apollo-cfg,.apollo-root{--cfg-accent:#3388BB;--cfg-accent-hover:#2e7aa8;--cfg-accent-fg:#fff}',
|
|
144
|
+
'.apollo-cfg{display:flex;flex-direction:column;gap:16px;max-width:760px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-primary)}',
|
|
145
|
+
'.apollo-intro{margin:0;padding:0 2px;font-size:13px;line-height:20px;color:var(--dsw-alias-label-tertiary)}',
|
|
146
|
+
'.apollo-note{margin:0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;font-size:12px;line-height:18px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-secondary)}',
|
|
147
|
+
'.apollo-note[data-tone=error]{background:color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);border-color:color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent);color:var(--dsw-alias-state-error-primary)}',
|
|
91
148
|
'.apollo-note[data-tone=warn]{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-label-secondary)}',
|
|
92
149
|
'.apollo-pulse{animation:apollo-pulse 2s cubic-bezier(.36,0,.64,1) infinite}',
|
|
93
150
|
'@keyframes apollo-pulse{0%{opacity:1}40%{opacity:.6}80%,to{opacity:1}}',
|
|
94
151
|
'@media (prefers-reduced-motion:reduce){.apollo-pulse{animation:none}}',
|
|
95
|
-
|
|
96
|
-
'.apollo-
|
|
97
|
-
'.apollo-
|
|
98
|
-
'.apollo-
|
|
99
|
-
'.apollo-
|
|
100
|
-
'.apollo-
|
|
101
|
-
|
|
102
|
-
'.apollo-
|
|
103
|
-
'.apollo-
|
|
104
|
-
'.apollo-
|
|
152
|
+
/* 分组容器卡:1px hairline + 16px 圆角 + layer-3 底。 */
|
|
153
|
+
'.apollo-group{display:flex;flex-direction:column;gap:6px;padding:16px 18px;box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:16px;background:var(--dsw-alias-bg-layer-3)}',
|
|
154
|
+
'.apollo-groupHead{display:flex;align-items:baseline;gap:7px;padding:0 2px 8px;font-size:13px;line-height:20px;font-weight:600;color:var(--dsw-alias-label-primary)}',
|
|
155
|
+
'.apollo-count{padding:1px 8px;border-radius:999px;background:color-mix(in srgb, var(--cfg-accent) 10%, transparent);font-size:11px;line-height:16px;font-weight:500;color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums}',
|
|
156
|
+
'.apollo-title{margin:0;font-size:14px;font-weight:600;line-height:22px;color:var(--dsw-alias-label-primary)}',
|
|
157
|
+
'.apollo-hint{margin:0;font-size:12px;line-height:17px;color:var(--dsw-alias-label-tertiary)}',
|
|
158
|
+
/* 设置行:标题/描述居左,控件居右,行间 hairline。 */
|
|
159
|
+
'.apollo-row{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:11px 2px;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
160
|
+
'.apollo-row:last-child{border-bottom:none}',
|
|
161
|
+
'.apollo-rowText{display:flex;flex-direction:column;gap:3px;min-width:0;flex:1}',
|
|
162
|
+
'.apollo-rowTitle{font-size:13.5px;line-height:20px;font-weight:500;color:var(--dsw-alias-label-primary)}',
|
|
163
|
+
'.apollo-rowDesc{font-size:12px;line-height:17px;color:var(--dsw-alias-label-tertiary)}',
|
|
164
|
+
/* Switch:36x20 track,accent 实心 + 白点滑动。 */
|
|
165
|
+
'.apollo-switch{box-sizing:border-box;position:relative;flex:none;width:36px;height:20px;margin:2px 0 0;padding:2px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-2);cursor:pointer;appearance:none;transition:background 150ms ease,border-color 150ms ease}',
|
|
166
|
+
'.apollo-switch:hover{border-color:var(--dsw-alias-label-dimmed)}',
|
|
167
|
+
'.apollo-switch[aria-checked=true]{border-color:var(--cfg-accent);background:var(--cfg-accent)}',
|
|
105
168
|
'.apollo-switch:disabled{opacity:.4;cursor:not-allowed}',
|
|
106
|
-
'.apollo-switchThumb{display:block;width:
|
|
107
|
-
'.apollo-switch[aria-checked=true] .apollo-switchThumb{transform:translateX(16px)}',
|
|
108
|
-
'
|
|
109
|
-
'
|
|
169
|
+
'.apollo-switchThumb{display:block;width:14px;height:14px;border-radius:50%;background:var(--dsw-alias-label-tertiary);transition:transform 150ms ease,background 150ms ease}',
|
|
170
|
+
'.apollo-switch[aria-checked=true] .apollo-switchThumb{transform:translateX(16px);background:var(--cfg-accent-fg)}',
|
|
171
|
+
'.apollo-switch:focus-visible{outline:2px solid var(--cfg-accent);outline-offset:2px}',
|
|
172
|
+
'@media (prefers-reduced-motion:reduce){.apollo-switch,.apollo-switchThumb{transition:none}}',
|
|
173
|
+
/* 按钮:primary=accent 实心白字;outline=1px 边;hover 走中性填充。 */
|
|
174
|
+
'.apollo-button{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:6px;height:30px;padding:0 14px;border:1px solid transparent;border-radius:8px;font:inherit;font-size:12.5px;line-height:18px;font-weight:500;color:var(--dsw-alias-label-primary);background:transparent;cursor:pointer;transition:background 120ms ease,border-color 120ms ease,color 120ms ease}',
|
|
110
175
|
'.apollo-button:disabled{opacity:.4;cursor:not-allowed}',
|
|
111
|
-
'.apollo-button[data-variant=primary]{background:var(--
|
|
112
|
-
'.apollo-button[data-variant=
|
|
113
|
-
'.apollo-button
|
|
114
|
-
'.apollo-
|
|
176
|
+
'.apollo-button[data-variant=primary]{background:var(--cfg-accent);color:var(--cfg-accent-fg)}',
|
|
177
|
+
'.apollo-button[data-variant=primary]:hover:not(:disabled){background:var(--cfg-accent-hover)}',
|
|
178
|
+
'.apollo-button[data-variant=outline]{border-color:var(--dsw-alias-border-l3)}',
|
|
179
|
+
'.apollo-button[data-variant=outline]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}',
|
|
180
|
+
'.apollo-button[data-variant=ghost]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}',
|
|
181
|
+
'.apollo-button:focus-visible{outline:2px solid var(--cfg-accent);outline-offset:2px}',
|
|
182
|
+
'.apollo-input,.apollo-select{box-sizing:border-box;min-width:0;height:30px;padding:0 10px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-3);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;line-height:20px}',
|
|
183
|
+
'.apollo-input:hover,.apollo-select:hover{border-color:var(--dsw-alias-label-dimmed)}',
|
|
184
|
+
'.apollo-input:focus,.apollo-select:focus{outline:none;border-color:var(--cfg-accent);box-shadow:0 0 0 2px color-mix(in srgb, var(--cfg-accent) 25%, transparent)}',
|
|
185
|
+
'.apollo-input:disabled,.apollo-select:disabled{opacity:.4;cursor:not-allowed}',
|
|
186
|
+
'.apollo-input[aria-invalid=true]{border-color:var(--dsw-alias-state-error-primary)}',
|
|
187
|
+
'.apollo-select{appearance:none;padding-right:26px;background-image:url("data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' width=\'10\' height=\'6\' viewBox=\'0 0 10 6\'%3E%3Cpath d=\'M1 1l4 4 4-4\' stroke=\'%23999\' stroke-width=\'1.5\' fill=\'none\' stroke-linecap=\'round\'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 9px center}',
|
|
188
|
+
'.apollo-footer{display:flex;align-items:center;gap:8px;padding-top:14px;border-top:1px solid var(--dsw-alias-border-l2)}',
|
|
115
189
|
'.apollo-status{flex:1;min-width:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}',
|
|
116
190
|
'.apollo-status[data-tone=ok]{color:var(--dsw-alias-state-success-primary)}',
|
|
117
191
|
'.apollo-status[data-tone=error]{color:var(--dsw-alias-state-error-primary)}',
|
|
118
192
|
'.apollo-status[data-tone=warn]{color:var(--dsw-alias-state-warn-label,var(--dsw-alias-label-secondary))}',
|
|
193
|
+
/* ── 集成设置弹窗 ────────────────────────────────────────────── */
|
|
194
|
+
'.apollo-hubGlyph{flex:none;color:var(--cfg-accent)}',
|
|
195
|
+
/* ui-primitives Modal 加宽(双类提升优先级,同 better-sidebar 做法)。 */
|
|
196
|
+
'.apollo-modal.apollo-modal{width:min(880px, calc(100% - 32px))}',
|
|
197
|
+
'.apollo-hub{display:flex;gap:0;min-height:320px;max-height:60vh;--cfg-accent:#3388BB;--cfg-accent-hover:#2e7aa8;--cfg-accent-fg:#fff}',
|
|
198
|
+
'.apollo-hubNav{flex:none;width:184px;display:flex;flex-direction:column;gap:2px;padding-right:12px;border-right:1px solid var(--dsw-alias-border-l2);overflow-y:auto;scrollbar-width:thin}',
|
|
199
|
+
'.apollo-hubNavItem{display:flex;flex-direction:column;gap:1px;padding:8px 10px;border:0;border-radius:8px;background:transparent;font:inherit;text-align:left;cursor:pointer;color:var(--dsw-alias-label-secondary);transition:background 120ms ease,color 120ms ease}',
|
|
200
|
+
'.apollo-hubNavItem:hover{background:var(--dsw-alias-interactive-bg-hover)}',
|
|
201
|
+
'.apollo-hubNavItem[data-active]{background:color-mix(in srgb, var(--cfg-accent) 10%, transparent);color:var(--dsw-alias-label-primary)}',
|
|
202
|
+
'.apollo-hubNavItem:focus-visible{outline:2px solid var(--cfg-accent);outline-offset:1px}',
|
|
203
|
+
'.apollo-hubNavName{font-size:13px;line-height:18px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
|
|
204
|
+
'.apollo-hubNavId{font-size:11px;line-height:15px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}',
|
|
205
|
+
'.apollo-hubContent{flex:1;min-width:0;overflow-y:auto;padding-left:18px;scrollbar-width:thin}',
|
|
206
|
+
'.apollo-hubPane[hidden]{display:none}',
|
|
207
|
+
'@media (max-width:640px){.apollo-hub{flex-direction:column}.apollo-hubNav{width:auto;flex-direction:row;gap:4px;padding:0 0 10px;border-right:0;border-bottom:1px solid var(--dsw-alias-border-l2);overflow-x:auto}.apollo-hubNavId{display:none}.apollo-hubContent{padding-left:0;padding-top:12px}}',
|
|
208
|
+
/* 自绘 overlay(ui-primitives 缺失时的兜底弹窗)。 */
|
|
209
|
+
'.apollo-mask{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box;background:rgba(0,0,0,.4)}',
|
|
210
|
+
'.apollo-modalCard{display:flex;flex-direction:column;width:min(880px,100%);max-height:calc(100vh - 64px);box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);border-radius:14px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);box-shadow:0 12px 48px rgba(0,0,0,.22)}',
|
|
211
|
+
'.apollo-modalHead{display:flex;flex-direction:column;gap:4px;padding:18px 20px 12px;border-bottom:1px solid var(--dsw-alias-border-l2)}',
|
|
212
|
+
'.apollo-modalTitleRow{display:flex;align-items:center;justify-content:space-between;gap:12px}',
|
|
213
|
+
'.apollo-modalTitle{margin:0;font-size:15px;font-weight:600;line-height:22px}',
|
|
214
|
+
'.apollo-modalDesc{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-tertiary)}',
|
|
215
|
+
'.apollo-modalBody{flex:1;min-height:0;overflow:hidden;display:flex;padding:14px 20px}',
|
|
216
|
+
'.apollo-modalFoot{display:flex;justify-content:flex-end;gap:8px;padding:12px 20px;border-top:1px solid var(--dsw-alias-border-l2)}',
|
|
217
|
+
'.apollo-iconBtn{display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;border:0;border-radius:8px;background:transparent;color:var(--dsw-alias-label-tertiary);font-size:16px;line-height:1;cursor:pointer}',
|
|
218
|
+
'.apollo-iconBtn:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}',
|
|
119
219
|
].join('\n');
|
|
120
220
|
|
|
121
221
|
/** 宿主快照只投影这一个可编辑字段。 */
|
|
@@ -125,10 +225,10 @@ window.__ModuleLoader__.load({
|
|
|
125
225
|
}
|
|
126
226
|
|
|
127
227
|
function SwitchRow({ id, label, hint, checked, disabled, onChange }) {
|
|
128
|
-
return h('div', { className: 'apollo-
|
|
129
|
-
h('div', { className: 'apollo-
|
|
130
|
-
h('label', { className: 'apollo-
|
|
131
|
-
hint === undefined ? null : h('
|
|
228
|
+
return h('div', { className: 'apollo-row' },
|
|
229
|
+
h('div', { className: 'apollo-rowText' },
|
|
230
|
+
h('label', { className: 'apollo-rowTitle', htmlFor: id }, label),
|
|
231
|
+
hint === undefined ? null : h('span', { className: 'apollo-rowDesc' }, hint)),
|
|
132
232
|
h('button', {
|
|
133
233
|
type: 'button',
|
|
134
234
|
id,
|
|
@@ -192,13 +292,10 @@ window.__ModuleLoader__.load({
|
|
|
192
292
|
: h('span', { className: 'apollo-status', role: 'status' }, writable ? '' : translate('readonlyNote'));
|
|
193
293
|
|
|
194
294
|
return h('div', { className: 'apollo-cfg' },
|
|
195
|
-
h('
|
|
196
|
-
h('h3', { className: 'apollo-title' }, translate('title')),
|
|
197
|
-
h('p', { className: 'apollo-intro' }, translate('intro'))),
|
|
295
|
+
h('p', { className: 'apollo-intro' }, translate('intro')),
|
|
198
296
|
writable ? null : h('p', { className: 'apollo-note', 'data-tone': 'warn' }, translate('readonly')),
|
|
199
|
-
h('div', { className: 'apollo-
|
|
200
|
-
h('div', { className: 'apollo-
|
|
201
|
-
h('h3', { className: 'apollo-title' }, translate('exitGuardLabel'))),
|
|
297
|
+
h('div', { className: 'apollo-group' },
|
|
298
|
+
h('div', { className: 'apollo-groupHead' }, translate('title')),
|
|
202
299
|
h(SwitchRow, {
|
|
203
300
|
id: 'apollo-exit-guard',
|
|
204
301
|
label: translate('exitGuardLabel'),
|
|
@@ -207,9 +304,8 @@ window.__ModuleLoader__.load({
|
|
|
207
304
|
disabled: !writable || busy,
|
|
208
305
|
onChange: (next) => { setDraft({ revision: state.revision, value: { exitGuard: next } }); setNotice(null); },
|
|
209
306
|
})),
|
|
210
|
-
h('div', { className: 'apollo-
|
|
211
|
-
h('div', { className: 'apollo-
|
|
212
|
-
h('h3', { className: 'apollo-title' }, translate('migratedTitle'))),
|
|
307
|
+
h('div', { className: 'apollo-group' },
|
|
308
|
+
h('div', { className: 'apollo-groupHead' }, translate('migratedTitle')),
|
|
213
309
|
h('p', { className: 'apollo-note' }, translate('migratedBody')),
|
|
214
310
|
h('p', { className: 'apollo-note' }, translate('deprecatedBody')),
|
|
215
311
|
h('p', { className: 'apollo-note', 'data-tone': 'warn' }, translate('reloadBody'))),
|
|
@@ -236,12 +332,217 @@ window.__ModuleLoader__.load({
|
|
|
236
332
|
* 样式随组件树内的 <style> 挂载一次(不写 DOM,重复渲染不重复注入)。
|
|
237
333
|
*/
|
|
238
334
|
function ApolloPanel(props) {
|
|
239
|
-
if (props.view === 'summary') return h('span', null, t('summary'));
|
|
335
|
+
if (props.view === 'summary') return h('span', null, (props.t ?? t)('summary'));
|
|
240
336
|
return h('div', { className: 'apollo-root' },
|
|
241
337
|
h('style', { 'data-plugin-css': STYLE_ID }, CSS),
|
|
242
338
|
h(ApolloConfigPage, { form: props.form, t: props.t ?? t }));
|
|
243
339
|
}
|
|
244
340
|
|
|
341
|
+
// ── 集成设置弹窗 ─────────────────────────────────────────────────
|
|
342
|
+
|
|
343
|
+
function gearGlyph() {
|
|
344
|
+
return h('svg', { width: 13, height: 13, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': 'true', className: 'apollo-hubGlyph' },
|
|
345
|
+
h('path', {
|
|
346
|
+
d: 'M8 10.4a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8Z',
|
|
347
|
+
stroke: 'currentColor', strokeWidth: 1.4,
|
|
348
|
+
}),
|
|
349
|
+
h('path', {
|
|
350
|
+
d: 'M13.3 8c0-.4-.1-.8-.2-1.2l1.1-.9-1.2-2-1.3.5c-.6-.5-1.2-.8-1.9-1L9.3 1.7H6.7l-.5 1.7c-.7.2-1.3.5-1.9 1l-1.3-.5-1.2 2 1.1.9a4.9 4.9 0 0 0 0 2.4l-1.1.9 1.2 2 1.3-.5c.6.5 1.2.8 1.9 1l.5 1.7h2.6l.5-1.7c.7-.2 1.3-.5 1.9-1l1.3.5 1.2-2-1.1-.9c.1-.4.2-.8.2-1.2Z',
|
|
351
|
+
stroke: 'currentColor', strokeWidth: 1.2, strokeLinejoin: 'round',
|
|
352
|
+
}));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* 按 HUB_ROWS 顺序收集已注册的子插件设置组件:entry.options.key 以
|
|
357
|
+
* `#<rowId>` 结尾即归该行;同 rowId 多个键(独立/Apollo 两种装配)内容
|
|
358
|
+
* 相同,优先取 Apollo bundle 键。
|
|
359
|
+
*/
|
|
360
|
+
function collectPluginEntries(ctx) {
|
|
361
|
+
const read = typeof ctx.slots.entriesOfSlot === 'function'
|
|
362
|
+
? () => ctx.slots.entriesOfSlot(ROW_CONFIG_SLOT)
|
|
363
|
+
: () => (typeof ctx.slots.entries === 'function' ? ctx.slots.entries(ROW_CONFIG_SLOT) : []);
|
|
364
|
+
let entries = [];
|
|
365
|
+
try {
|
|
366
|
+
entries = read() ?? [];
|
|
367
|
+
} catch {
|
|
368
|
+
entries = [];
|
|
369
|
+
}
|
|
370
|
+
const found = new Map();
|
|
371
|
+
for (const row of HUB_ROWS) {
|
|
372
|
+
const suffix = '#' + row.rowId;
|
|
373
|
+
for (const entry of entries) {
|
|
374
|
+
const key = entry?.options?.key;
|
|
375
|
+
if (typeof key !== 'string' || !key.endsWith(suffix) || entry.component === undefined || entry.component === null) continue;
|
|
376
|
+
const score = key.startsWith(PACKAGE_NAME + '#') ? 0 : 1;
|
|
377
|
+
const prev = found.get(row.rowId);
|
|
378
|
+
if (prev === undefined || score < prev.score) found.set(row.rowId, { entry });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const items = [];
|
|
382
|
+
for (const row of HUB_ROWS) {
|
|
383
|
+
const hit = found.get(row.rowId);
|
|
384
|
+
if (hit !== undefined) items.push({ rowId: row.rowId, titleKey: row.titleKey, entry: hit.entry });
|
|
385
|
+
}
|
|
386
|
+
return items;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/** 复刻官方插件页的 formFor:describe 镜像决定可见性,get() 拿共享表单。 */
|
|
390
|
+
function formFor(ctx, mirror, rowId) {
|
|
391
|
+
const namespaces = mirror?.view?.namespaces;
|
|
392
|
+
if (!Array.isArray(namespaces) || !namespaces.some((view) => view?.ns === rowId)) return undefined;
|
|
393
|
+
try {
|
|
394
|
+
const form = ctx.configForms.get(rowId);
|
|
395
|
+
return { state: form.getSnapshot(), mutate: (ops, revision) => form.mutate(ops, revision) };
|
|
396
|
+
} catch {
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** entry 声明的 locale 命名空间 → t;没有则给 undefined(组件自带兜底)。 */
|
|
402
|
+
function boundT(ctx, entry) {
|
|
403
|
+
if (typeof entry?.locale !== 'string' || entry.locale === '') return undefined;
|
|
404
|
+
try {
|
|
405
|
+
return typeof ctx.locale?.bind === 'function' ? ctx.locale.bind(entry.locale) : undefined;
|
|
406
|
+
} catch {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function subscribeSlot(ctx, listener) {
|
|
412
|
+
try {
|
|
413
|
+
return typeof ctx.slots.subscribe === 'function' ? ctx.slots.subscribe(ROW_CONFIG_SLOT, listener) : () => {};
|
|
414
|
+
} catch {
|
|
415
|
+
return () => {};
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function slotVersion(ctx) {
|
|
420
|
+
try {
|
|
421
|
+
return typeof ctx.slots.getVersion === 'function' ? ctx.slots.getVersion(ROW_CONFIG_SLOT) : 0;
|
|
422
|
+
} catch {
|
|
423
|
+
return 0;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** 单个设置面板崩溃不炸掉整个弹窗。 */
|
|
428
|
+
class EntryBoundary extends React.Component {
|
|
429
|
+
constructor(props) {
|
|
430
|
+
super(props);
|
|
431
|
+
this.state = { error: null };
|
|
432
|
+
}
|
|
433
|
+
static getDerivedStateFromError(error) {
|
|
434
|
+
return { error };
|
|
435
|
+
}
|
|
436
|
+
render() {
|
|
437
|
+
if (this.state.error !== null) {
|
|
438
|
+
return h('p', { className: 'apollo-note', 'data-tone': 'error', role: 'alert' },
|
|
439
|
+
t('entryError') + String(this.state.error?.message ?? this.state.error));
|
|
440
|
+
}
|
|
441
|
+
return this.props.children;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/** 弹窗主体:左 nav + 右面板(全部挂载,display:none 保住未存草稿)。 */
|
|
446
|
+
function HubBody({ ctx }) {
|
|
447
|
+
const version = useSyncExternalStore((listener) => subscribeSlot(ctx, listener), () => slotVersion(ctx));
|
|
448
|
+
const items = useMemo(() => collectPluginEntries(ctx), [ctx, version]);
|
|
449
|
+
const describe = ctx.configForms.describe();
|
|
450
|
+
const mirror = useSyncExternalStore(
|
|
451
|
+
(listener) => (typeof describe?.subscribe === 'function' ? describe.subscribe(listener) : () => {}),
|
|
452
|
+
() => describe?.getSnapshot?.());
|
|
453
|
+
useEffect(() => { void describe?.ensure?.(); }, [describe]);
|
|
454
|
+
const [active, setActive] = useState(null);
|
|
455
|
+
const current = items.find((item) => item.rowId === active) ?? items[0];
|
|
456
|
+
|
|
457
|
+
if (items.length === 0) {
|
|
458
|
+
return h('p', { className: 'apollo-note', role: 'status' }, t('hubEmpty'));
|
|
459
|
+
}
|
|
460
|
+
return h('div', { className: 'apollo-hub' },
|
|
461
|
+
h('div', { className: 'apollo-hubNav', role: 'tablist', 'aria-label': t('hubTitle') },
|
|
462
|
+
items.map((item) => h('button', {
|
|
463
|
+
key: item.rowId,
|
|
464
|
+
type: 'button',
|
|
465
|
+
role: 'tab',
|
|
466
|
+
className: 'apollo-hubNavItem',
|
|
467
|
+
'data-active': item === current ? true : undefined,
|
|
468
|
+
'aria-selected': item === current,
|
|
469
|
+
onClick: () => setActive(item.rowId),
|
|
470
|
+
},
|
|
471
|
+
h('span', { className: 'apollo-hubNavName' }, t(item.titleKey)),
|
|
472
|
+
h('span', { className: 'apollo-hubNavId' }, item.rowId)))),
|
|
473
|
+
h('div', { className: 'apollo-hubContent', role: 'tabpanel' },
|
|
474
|
+
items.map((item) => h('div', {
|
|
475
|
+
key: item.rowId,
|
|
476
|
+
className: 'apollo-hubPane',
|
|
477
|
+
hidden: item !== current,
|
|
478
|
+
},
|
|
479
|
+
h(EntryBoundary, null,
|
|
480
|
+
h(item.entry.component, {
|
|
481
|
+
view: 'page',
|
|
482
|
+
form: formFor(ctx, mirror, item.rowId),
|
|
483
|
+
t: boundT(ctx, item.entry),
|
|
484
|
+
}))))));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** 自绘弹窗兜底:fixed mask + card,Esc/遮罩点击关闭(不依赖 ui-primitives)。 */
|
|
488
|
+
function FallbackModal({ onClose, children }) {
|
|
489
|
+
useEffect(() => {
|
|
490
|
+
const doc = globalThis['document'];
|
|
491
|
+
if (doc === undefined || doc === null) return undefined;
|
|
492
|
+
const onKey = (event) => { if (event.key === 'Escape') onClose(); };
|
|
493
|
+
doc.addEventListener('keydown', onKey);
|
|
494
|
+
return () => doc.removeEventListener('keydown', onKey);
|
|
495
|
+
}, [onClose]);
|
|
496
|
+
return h('div', {
|
|
497
|
+
className: 'apollo-mask',
|
|
498
|
+
role: 'presentation',
|
|
499
|
+
onClick: (event) => { if (event.target === event.currentTarget) onClose(); },
|
|
500
|
+
},
|
|
501
|
+
h('div', { className: 'apollo-modalCard', role: 'dialog', 'aria-label': t('hubTitle') },
|
|
502
|
+
h('div', { className: 'apollo-modalHead' },
|
|
503
|
+
h('div', { className: 'apollo-modalTitleRow' },
|
|
504
|
+
h('h2', { className: 'apollo-modalTitle' }, t('hubTitle')),
|
|
505
|
+
h('button', { type: 'button', className: 'apollo-iconBtn', 'aria-label': t('close'), onClick: onClose }, '×')),
|
|
506
|
+
h('p', { className: 'apollo-modalDesc' }, t('hubIntro'))),
|
|
507
|
+
h('div', { className: 'apollo-modalBody' }, children),
|
|
508
|
+
h('div', { className: 'apollo-modalFoot' },
|
|
509
|
+
h('button', { type: 'button', className: 'apollo-button', 'data-variant': 'primary', onClick: onClose }, t('done')))));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function SettingsModal({ ctx, onClose }) {
|
|
513
|
+
const body = h(HubBody, { ctx });
|
|
514
|
+
if (Modal === null) return h(FallbackModal, { onClose }, body);
|
|
515
|
+
return h(Modal, {
|
|
516
|
+
open: true,
|
|
517
|
+
onClose,
|
|
518
|
+
title: t('hubTitle'),
|
|
519
|
+
description: t('hubIntro'),
|
|
520
|
+
closeLabel: t('close'),
|
|
521
|
+
className: 'apollo-modal',
|
|
522
|
+
contentClassName: 'apollo-hubModalContent',
|
|
523
|
+
footer: h('button', { type: 'button', className: 'apollo-button', 'data-variant': 'primary', onClick: onClose }, t('done')),
|
|
524
|
+
}, body);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/** `plugins.detail.actions` 入口:仅 Apollo bundle 详情页渲染触发按钮。 */
|
|
528
|
+
function HubEntry(props) {
|
|
529
|
+
const subject = props.subject;
|
|
530
|
+
const isApolloBundle = subject?.kind === 'bundle'
|
|
531
|
+
&& (subject.pkg?.name === PACKAGE_NAME || subject.pkg?.name === LEGACY_PACKAGE_NAME);
|
|
532
|
+
const [open, setOpen] = useState(false);
|
|
533
|
+
if (!isApolloBundle) return null;
|
|
534
|
+
return h(React.Fragment, null,
|
|
535
|
+
h('style', { 'data-plugin-css': STYLE_ID + ':hub' }, CSS),
|
|
536
|
+
h('button', {
|
|
537
|
+
type: 'button',
|
|
538
|
+
className: 'apollo-button',
|
|
539
|
+
'data-variant': 'outline',
|
|
540
|
+
'data-apollo-hub': 'trigger',
|
|
541
|
+
onClick: () => setOpen(true),
|
|
542
|
+
}, gearGlyph(), (props.t ?? t)('hubButton')),
|
|
543
|
+
open ? h(SettingsModal, { ctx: props.ctx, onClose: () => setOpen(false) }) : null);
|
|
544
|
+
}
|
|
545
|
+
|
|
245
546
|
const inject = ['slots', 'locale'];
|
|
246
547
|
|
|
247
548
|
function apply(ctx) {
|
|
@@ -252,12 +553,12 @@ window.__ModuleLoader__.load({
|
|
|
252
553
|
} catch {
|
|
253
554
|
// locale 服务缺失时保持中文默认。
|
|
254
555
|
}
|
|
255
|
-
ctx.effect(() => ctx.slots.inject(
|
|
556
|
+
ctx.effect(() => ctx.slots.inject(ROW_CONFIG_SLOT, () => {
|
|
256
557
|
const disposers = [];
|
|
257
558
|
for (const key of ROW_KEYS) {
|
|
258
559
|
try {
|
|
259
560
|
const dispose = ctx.slots.register({
|
|
260
|
-
name:
|
|
561
|
+
name: ROW_CONFIG_SLOT,
|
|
261
562
|
key,
|
|
262
563
|
locale: NS,
|
|
263
564
|
}, ApolloPanel);
|
|
@@ -270,6 +571,27 @@ window.__ModuleLoader__.load({
|
|
|
270
571
|
for (const dispose of disposers) dispose();
|
|
271
572
|
};
|
|
272
573
|
}), '@menghuan1918/dsh-apollo: row config page');
|
|
574
|
+
|
|
575
|
+
// 集成设置弹窗依赖 configForms(读取各子插件命名空间的共享表单);
|
|
576
|
+
// 服务缺失时仅弹窗缺席,行配置页照常工作。
|
|
577
|
+
try {
|
|
578
|
+
ctx.inject(['configForms'], (sctx) => {
|
|
579
|
+
const register = () => sctx.slots.inject(DETAIL_ACTIONS_SLOT, () =>
|
|
580
|
+
sctx.slots.register({
|
|
581
|
+
name: DETAIL_ACTIONS_SLOT,
|
|
582
|
+
id: HUB_ENTRY_ID,
|
|
583
|
+
order: 0,
|
|
584
|
+
locale: NS,
|
|
585
|
+
}, (props) => h(HubEntry, { ctx: sctx, subject: props.subject, t: props.t ?? t })));
|
|
586
|
+
if (typeof sctx.effect === 'function') {
|
|
587
|
+
sctx.effect(register, '@menghuan1918/dsh-apollo: settings hub');
|
|
588
|
+
return undefined;
|
|
589
|
+
}
|
|
590
|
+
return register();
|
|
591
|
+
});
|
|
592
|
+
} catch {
|
|
593
|
+
// 无 configForms 的组合:集成弹窗不注册。
|
|
594
|
+
}
|
|
273
595
|
}
|
|
274
596
|
|
|
275
597
|
exports.inject = inject;
|