# 设置、槽位与包契约 ## Settings | 身份 | 字段/值 | 所有者 | | --- | --- | --- | | `ui-theme.preference` | system / light / dark | 官方 Appearance | | `theme-studio.activeThemeId` | null 或主题 id(builtin 或 imported) | Theme Studio | | `theme-studio.importedThemes` | 导入的 `dsh-theme.json` 源对象数组,默认 `[]` | Theme Studio | | `theme-studio.creatorAgentMode` | `off` / `gesture` / `catalog`,默认 `gesture` | Theme Studio | | `THEME_STUDIO_SETTINGS_NAMESPACE` | theme-studio | Host namespace / profile entry id | | `ACTIVE_THEME_ID_FIELD` | activeThemeId | 常量与 schema | | `IMPORTED_THEMES_FIELD` | importedThemes | 常量与 schema | | `CREATOR_AGENT_MODE_FIELD` | creatorAgentMode | 常量与 schema | `activeThemeId` 的 Host schema 是 string/null union,默认 null,不把主题 id 清单固化进 schema;由 runtime 在读取 ready snapshot 后识别未知 id。`importedThemes` 的 Host schema 是宽松 `array`,默认 `[]`——严格逐文件校验在 Client 进行(见[文件格式](./theme-file-format.md)),无法解析的条目被跳过并在 UI 计入 `droppedImports`,不会让整个 namespace 快照失败;这使旧版本读新格式条目降级为跳过而不是丢数据。导入主题的原文件对象原样存储,导出可逐字回写。手动创建主题同样复用 `importedThemes` 追加路径,不新增 durable 字段,与 `creatorAgentMode` 无关。`DEFAULT_THEME_STUDIO_SETTINGS` 与 `Config` 从主入口导出;旧 namespace schema 与 Config 用新建 field 避免 volatile 改写共享对象。 `creatorAgentMode` 控制 Theme Creator skill 的注册:`off` 不注册且隐藏 UI 入口,`gesture` 仅允许 `/dsh-theme-creator` 用户手势(不进模型目录,默认),`catalog` 同时允许模型路由。Host schema 接受这三个字符串;缺失或未知值由 `creatorAgentModeOf` 规范化为 `gesture`,旧版本快照对降级安全。Host 经 `ctx.inject(['skills'])` 接入技能注册表,Client 经 `ctx.get('uiWorkspace')` 读取可选会话启动能力——两者都是可选服务,缺席时对应功能关闭而主题功能不受影响。Themes 菜单的创作入口还要求 `remote.skills` 目录 Remote 在场(手势解析路径唯一的 Client 可见信号,经 `ctx.inject(['remote.skills'])` 反应式探测);没有该 namespace 的连接不显示入口,但 durable `creatorAgentMode` 仍可照常读写(见 [ADR-0010](../decisions/ADR-0010-theme-creator-runtime-skill.md))。 ### 旧 Host / Client `settings.register('theme-studio', schema)` 注册 durable namespace。这个方法返回 owner namespace scope,是普通对象;provider 自己将注册挂到调用 fiber。适配层不把 scope 返回给 Cordis apply,防止 Invalid effect 和回滚。Client 使用 `settingsScope.bind({ namespace: 'theme-studio' })`。 ### 新 Host / Client Host 暴露 `Config`,如果 schema 支持 volatile 则将 `activeThemeId` 与 `importedThemes` 都标为 live field;`settings.configure({ auto: false }, ownerFiber)` 关闭自动配置 UI,因为 Themes row 已自定义。Client 使用 `configForms.get('theme-studio')`。legacy register 与 configure 同时存在时优先 register,依据结构选择,不在业务逻辑里比较版本。 共同 handle 接口为 getSnapshot、subscribe 和返回 Promise 的 set。set 的 resolved 值可以是 void 或 boolean,runtime 等待 settlement 后核对 accepted snapshot 中的 active id 与完整导入列表;旧通道拒绝后即使返回 void,也不能视为成功。实际版本划分与证据见[兼容矩阵](../development/dsh-compatibility.md)。 ## Client injection and slots Client 必需服务是 `theme`、`slots`、`locale`、`connection`、`remote`。两个 settings transport 分别通过 ctx.inject 等待,不一起写入必需 inject 清单。即使 Host.section snapshot 是 unavailable,child 已存在时仍可挂载 row;而完全没有 transport 时不会启动 row。 | 注册项 | 标识 | 行为 | | --- | --- | --- | | 通用设置槽位 | settings.general.item / themes / order 10.5 | 下拉选择器、预览与应用 | | Locale namespace | settings.theme-studio | en、zh;zh 为 key set 真源 | | Public Cordis service | themeStudio | readonly catalog,与父 plugin fiber 同寿命 | 官方 Appearance 为 order 10、字号为 order 11,Themes 取 10.5 位于两者之间。Default 选项使用 DEFAULT_PREVIEW,不是 catalog 成员。theme id → locale key 从 namespaced suffix 推导;预览名使用 NAME_KEYS,新增主题需同步两个位置及词典,避免状态栏回落到 Default。 ## Package exports | Export | Runtime | 声明 | 内容 | | --- | --- | --- | --- | | `.` | lib/index.js | lib/types/index.d.ts | Host name/apply、Config/schema、settings 与 source 常量 | | `./client` | lib/client.js | lib/types/client/index.d.ts | browser apply、runtime、adapter、catalog、presets、store、对比度、theme-file/theme-import 辅助函数与公开类型 | | `./package.json` | package.json | 不适用 | 包 metadata | 公共 JSON Schema 位于包内 `schemas/dsh-theme.schema.json`(随 `files` 清单发布),供编辑器 `$schema` 引用与一致性测试使用;不注册到 exports,以文件路径取用。`lib/validate-theme.js` 同样只在 `files` 清单:它是给 Theme Creator skill 与用户使用的命令行校验器(用法与报告格式见[文件格式](./theme-file-format.md#命令行校验器)),不是模块入口。 没有 `./invariant` export:官方 ThemeRuntime 负责 overlay consistency,settings 服务负责 persistence。本包不得复制一个宿主 invariant provider。历史移除见 `d08aac9`。 `lib/client.js` 是 loader wrapper,不是可直接 Node import 执行的普通 ESM。源码的 import type 仅用于 declaration merging,不触发 browser loader;运行时代码由 consuming bundle 的 loader require 和 `dsh.client.inject` 建图。 ## Profile composition ```yaml - insert: - id: theme-studio name: '@dsh-electron/dsh-theme-studio' ``` 这是 [cordis.patch.yml](../../cordis.patch.yml) 的载体行。必须保留 bare package name,官方插件管理器和客户端 metadata 扫描以它为入口。不能只挂 `@dsh-electron/dsh-theme-studio/client` 或虚构 invariant 子路径。插件不注册 plugins.item、plugins.detail.badge、plugins.detail.section;正式包管理器从安装 metadata 显示包。 `dsh.client.platform=web`,`dsh.bundle.patch=./cordis.patch.yml`。构建 external/metadata 中的依赖分类以 package.json 与 standalone build config 为准。所有 dependencies、devDependencies、peers 使用 registry 范围或精确版本;没有 workspace 协议。 ## Distribution and portability npm scope `@dsh-electron/` 是发布者标识;它不意味着 Electron runtime。Desktop 镜像路径为 `apps/electron/runtime/plugins/dsh-theme-studio`,独立仓库是权威。tsconfig 不延伸到外层 repo,Desktop 可从源码重新生成两半产物;标准 DSH Web 使用同包。 files 清单包含 Host/Client JS、声明、patch、README 语言文件、CHANGELOG 和 LICENSE;开发脚本与 docs 树没有全部进入 tarball。完整工程文档在源码仓库,安装包 README 的源码文档链接使用 GitHub 页面入口。实际发版 tarball 需检查,不只凭 files 声明推断。 ## Related documents [构建流程](../development/plugin-development-workflow.md)、[发布流程](../development/release-workflow.md)、[ADR-0004](../decisions/ADR-0004-official-plugin-package-registration.md)、[ADR-0008](../decisions/ADR-0008-standalone-build-and-tagged-releases.md)。