@shgroup/dsh-serenity-hooks 1.59.0 → 1.59.2

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/lib/client.js CHANGED
@@ -3641,6 +3641,306 @@ window.__ModuleLoader__.load({
3641
3641
  });
3642
3642
  }
3643
3643
  //#endregion
3644
+ //#region src/client/AcpAccountsEditor.tsx
3645
+ /**
3646
+ * AcpAccountsEditor.tsx — 「ACP 账户台账」配置区块(v1.59.1,owner 2026-10-17 令)
3647
+ *
3648
+ * ## 为什么要有它
3649
+ * owner 原话:「**面板里只放个开关不放具体的条目吗?**」
3650
+ * 面 E 那个总开关只管"端口开不开",而**谁能连进来**在 CCC 的 `im.acp.accounts[]` 里。
3651
+ * 没有本区块时,加一台设备=找人改 JSON + 找 token —— 两件都不该由 owner 操心。
3652
+ *
3653
+ * ## 形状(照 `WeixinBridgeEditor`,不发明新范式)
3654
+ * - 顶部 **CCC 选择器**(数据源 `/serenity/cccs`)—— ACP 账户是 **CCC 级**数据,
3655
+ * 必须显式选目标,不靠"当前活跃会话"(面板是全局的,猜错就是改到别人家)
3656
+ * - 选中后:该 CCC 的通道状态 + 账户表(可用性 / 角色 / token 指纹)
3657
+ * - 每条可:改(角色/开关)/删 /**轮换 token**(唯一会让在用客户端断线的那一下)
3658
+ * - 新增:填 accountId + 角色 + 名字 ⇒ **保存即生成 token**,当场显示一次供抄走
3659
+ *
3660
+ * ## 🔴 两条安全纪律(写在这里,因为 UI 最容易违反)
3661
+ * ① **token 明文只在"刚生成"与"刚轮换"两处出现** —— 其余一律只显示指纹
3662
+ * (服务端 `acpChannelStatus` 本来就不回明文;这里的 state 也只是短暂持有)
3663
+ * ② **删账户会同时删掉它的 token**(服务端做),所以删除是不可逆的:UI 必须二次确认
3664
+ */
3665
+ const emptyDraft = () => ({
3666
+ accountId: "",
3667
+ role: "",
3668
+ name: ""
3669
+ });
3670
+ function AcpAccountsEditor() {
3671
+ const [cccs, setCccs] = (0, react.useState)([]);
3672
+ const [selectedRoot, setSelectedRoot] = (0, react.useState)("");
3673
+ const [status, setStatus] = (0, react.useState)(null);
3674
+ const [loadError, setLoadError] = (0, react.useState)(null);
3675
+ const [draft, setDraft] = (0, react.useState)(emptyDraft());
3676
+ const [busy, setBusy] = (0, react.useState)(false);
3677
+ const [error, setError] = (0, react.useState)(null);
3678
+ /** 刚生成 / 刚轮换出来的明文 token(**只在这两种时机出现**;刷新即消失) */
3679
+ const [freshToken, setFreshToken] = (0, react.useState)(null);
3680
+ /** 待确认删除的 accountId(二次确认;删会连带删 token,不可逆) */
3681
+ const [confirmRemove, setConfirmRemove] = (0, react.useState)(null);
3682
+ (0, react.useEffect)(() => {
3683
+ let alive = true;
3684
+ (async () => {
3685
+ try {
3686
+ const res = await fetch("/serenity/cccs", { headers: { accept: "application/json" } });
3687
+ if (!res.ok) return;
3688
+ const body = await res.json();
3689
+ if (!alive || !Array.isArray(body.cccs)) return;
3690
+ setCccs(body.cccs);
3691
+ if (body.cccs.length > 0) setSelectedRoot((prev) => prev || body.cccs[0].root);
3692
+ } catch {}
3693
+ })();
3694
+ return () => {
3695
+ alive = false;
3696
+ };
3697
+ }, []);
3698
+ /** 读一次该 CCC 的 ACP 台账(供初次加载与每次写操作后刷新) */
3699
+ const reload = async (root) => {
3700
+ setLoadError(null);
3701
+ try {
3702
+ const res = await fetch(`/serenity/acp?ccc=${encodeURIComponent(root)}`, { headers: {
3703
+ accept: "application/json",
3704
+ "x-serenity-ui": "1"
3705
+ } });
3706
+ if (!res.ok) {
3707
+ const body = await res.json().catch(() => null);
3708
+ setLoadError(body?.error ?? `HTTP ${res.status}`);
3709
+ return;
3710
+ }
3711
+ setStatus(await res.json());
3712
+ } catch {}
3713
+ };
3714
+ (0, react.useEffect)(() => {
3715
+ if (!selectedRoot) return;
3716
+ let alive = true;
3717
+ setStatus(null);
3718
+ (async () => {
3719
+ if (alive) await reload(selectedRoot);
3720
+ })();
3721
+ return () => {
3722
+ alive = false;
3723
+ };
3724
+ }, [selectedRoot]);
3725
+ /** 发一条写动作(三个 action 共用一条通道) */
3726
+ const post = async (payload) => {
3727
+ setBusy(true);
3728
+ setError(null);
3729
+ try {
3730
+ const res = await fetch("/serenity/acp", {
3731
+ method: "POST",
3732
+ headers: {
3733
+ "content-type": "application/json",
3734
+ "x-serenity-ui": "1"
3735
+ },
3736
+ body: JSON.stringify({
3737
+ ccc: selectedRoot,
3738
+ ...payload
3739
+ })
3740
+ });
3741
+ const body = await res.json().catch(() => null);
3742
+ if (!res.ok) {
3743
+ setError(typeof body?.error === "string" ? body.error : `HTTP ${res.status}`);
3744
+ return null;
3745
+ }
3746
+ await reload(selectedRoot);
3747
+ return body;
3748
+ } catch (err) {
3749
+ setError(err instanceof Error ? err.message : String(err));
3750
+ return null;
3751
+ } finally {
3752
+ setBusy(false);
3753
+ }
3754
+ };
3755
+ const addAccount = async () => {
3756
+ const accountId = draft.accountId.trim();
3757
+ if (accountId === "") {
3758
+ setError("账户 ID 必填(它同时是 token 的键名,如 agmente-phone)");
3759
+ return;
3760
+ }
3761
+ if (draft.role.trim() === "") {
3762
+ setError("角色必填(必须是本 CCC 已有的 skiff 角色)");
3763
+ return;
3764
+ }
3765
+ const body = await post({
3766
+ action: "upsert",
3767
+ account: {
3768
+ accountId,
3769
+ role: draft.role.trim(),
3770
+ name: draft.name.trim() || void 0
3771
+ }
3772
+ });
3773
+ if (body) {
3774
+ if (typeof body.token === "string" && body.token !== "") setFreshToken({
3775
+ accountId,
3776
+ token: body.token
3777
+ });
3778
+ setDraft(emptyDraft());
3779
+ }
3780
+ };
3781
+ const rotate = async (accountId) => {
3782
+ const body = await post({
3783
+ action: "rotate",
3784
+ accountId
3785
+ });
3786
+ if (body && typeof body.token === "string") setFreshToken({
3787
+ accountId,
3788
+ token: body.token
3789
+ });
3790
+ };
3791
+ const remove = async (accountId) => {
3792
+ await post({
3793
+ action: "remove",
3794
+ accountId
3795
+ });
3796
+ setConfirmRemove(null);
3797
+ };
3798
+ /** 该 CCC 已有的角色(新增账户时作为候选;避免手打出静默失败的角色名) */
3799
+ const rolesForSelected = cccs.find((c) => c.root === selectedRoot)?.roles ?? [];
3800
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3801
+ className: "ss-detailStack",
3802
+ children: [
3803
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3804
+ className: "ss-detailIntro",
3805
+ children: "ACP 账户台账(每台客户端一条:一枚 token ↔ 一个角色)\n· 连接归属哪个 CCC 由 **token** 决定(服务端权威),不看客户端填什么\n· 加一台设备 = 这里加一条 ⇒ **token 随即自动生成**,保存后当场显示一次,抄进客户端即可\n· 客户端地址:ws://<本机IP>:3200 + Authorization: Bearer <token>(或用 ?token=<token>)\n working directory 也可以只填目录名 —— ACC 会按 token 归属自动归到该 CCC 根\n· 「轮换」会让**在用的那台立刻断线** ⇒ 只在怀疑泄露时按;「删除」连带删 token,不可逆"
3806
+ }),
3807
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3808
+ className: "ss-switchRow",
3809
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "目标 CCC" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
3810
+ value: selectedRoot,
3811
+ onChange: (e) => setSelectedRoot(e.target.value),
3812
+ children: [cccs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3813
+ value: "",
3814
+ children: "(未发现 CCC)"
3815
+ }), cccs.map((c) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
3816
+ value: c.root,
3817
+ children: c.name
3818
+ }, c.root))]
3819
+ })]
3820
+ }),
3821
+ loadError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3822
+ className: "ss-detailIntro",
3823
+ children: `读取失败:${loadError}`
3824
+ }),
3825
+ status && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3826
+ className: "ss-detailIntro",
3827
+ children: `通道:${status.channel}${status.channel === "acp" ? "" : "(未选 ACP ⇒ 账户不生效)"} / 面开关:${status.enabled ? "开" : "关"}\n可用账户:${status.accounts.filter((a) => a.usable).length} / ${status.accounts.length}
3828
+ 每个可用账户在客户端里**只看到自己那一条对话**(第一次发消息时建立,之后一直用它)。`
3829
+ }),
3830
+ status && status.accounts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
3831
+ className: "ss-table",
3832
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
3833
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "账户" }),
3834
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "角色" }),
3835
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "状态" }),
3836
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "对话" }),
3837
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: "token" }),
3838
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", {})
3839
+ ] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: status.accounts.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
3840
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: a.accountId }), a.name ? ` · ${a.name}` : ""] }),
3841
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: a.role ?? "—" }),
3842
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: a.usable ? "✅ 可用" : `⚠️ ${a.blockedBy ?? "不可用"}` }),
3843
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", {
3844
+ title: a.mainSession ?? void 0,
3845
+ children: a.mainSession ? `…${a.mainSession.slice(-8)}` : "(还没建,客户端发第一条消息时建立)"
3846
+ }),
3847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: a.hasToken ? `…${a.tokenFingerprint ?? ""}` : "(无)" }),
3848
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3849
+ type: "button",
3850
+ disabled: busy,
3851
+ onClick: () => void rotate(a.accountId),
3852
+ children: "轮换"
3853
+ }), confirmRemove === a.accountId ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3854
+ type: "button",
3855
+ disabled: busy,
3856
+ onClick: () => void remove(a.accountId),
3857
+ children: "确认删除"
3858
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3859
+ type: "button",
3860
+ onClick: () => setConfirmRemove(null),
3861
+ children: "取消"
3862
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3863
+ type: "button",
3864
+ disabled: busy,
3865
+ onClick: () => setConfirmRemove(a.accountId),
3866
+ children: "删除"
3867
+ })] })
3868
+ ] }, a.accountId)) })]
3869
+ }),
3870
+ status && status.accounts.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3871
+ className: "ss-detailIntro",
3872
+ children: "该 CCC 还没有声明任何 ACP 账户 ⇒ 客户端一律连不上(fail-closed)。在下面加一条。"
3873
+ }),
3874
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3875
+ className: "ss-switchRow",
3876
+ children: [
3877
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "新增账户" }),
3878
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3879
+ placeholder: "账户 ID(如 agmente-phone)",
3880
+ value: draft.accountId,
3881
+ onChange: (e) => setDraft({
3882
+ ...draft,
3883
+ accountId: e.target.value
3884
+ })
3885
+ }),
3886
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3887
+ placeholder: "角色(必须是本 CCC 已有的)",
3888
+ value: draft.role,
3889
+ onChange: (e) => setDraft({
3890
+ ...draft,
3891
+ role: e.target.value
3892
+ }),
3893
+ list: `acp-roles-${selectedRoot}`
3894
+ }),
3895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("datalist", {
3896
+ id: `acp-roles-${selectedRoot}`,
3897
+ children: rolesForSelected.map((r) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", { value: r }, r))
3898
+ }),
3899
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
3900
+ placeholder: "显示名(可选)",
3901
+ value: draft.name,
3902
+ onChange: (e) => setDraft({
3903
+ ...draft,
3904
+ name: e.target.value
3905
+ })
3906
+ }),
3907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3908
+ type: "button",
3909
+ disabled: busy || !selectedRoot,
3910
+ onClick: () => void addAccount(),
3911
+ children: "保存并生成 token"
3912
+ })
3913
+ ]
3914
+ }),
3915
+ error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3916
+ className: "ss-detailIntro",
3917
+ children: `⚠️ ${error}`
3918
+ }),
3919
+ freshToken && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3920
+ className: "ss-detailStack",
3921
+ children: [
3922
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
3923
+ className: "ss-detailIntro",
3924
+ children: `🔑 ${freshToken.accountId} 的 token(**只显示这一次**,复制进客户端)\n记得随客户端配置一起保存。`
3925
+ }),
3926
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
3927
+ style: {
3928
+ wordBreak: "break-all",
3929
+ userSelect: "all"
3930
+ },
3931
+ children: freshToken.token
3932
+ }),
3933
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3934
+ type: "button",
3935
+ onClick: () => setFreshToken(null),
3936
+ children: "我已抄走,隐藏"
3937
+ })
3938
+ ]
3939
+ })
3940
+ ]
3941
+ });
3942
+ }
3943
+ //#endregion
3644
3944
  //#region \0sp-css:src/client/PersonaEditor
3645
3945
  const css$3 = "/* PersonaEditor.css — 彩蛋模式区块(v1.23.1,官方 settings 设计语言:rowCard 内分组)\n * token 合规(v1.27.14 修复):全部使用 design-platform.css 官方 --dsw-alias-* 词汇;\n * 弃用自造名(text-primary/accent/border/state-success 等)——未定义时深色 fallback\n * 在浅色主题下显示为黑框黑字(用户反馈白色主题通性问题)。 */\n\n.pe-wrap {\n display: flex;\n flex-direction: column;\n gap: 10px;\n width: 100%;\n padding: 2px 0 6px;\n}\n\n.pe-row {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.pe-label {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n}\n\n.pe-input,\n.pe-textarea {\n width: 100%;\n box-sizing: border-box;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n font-size: 13px;\n padding: 8px 10px;\n outline: none;\n}\n\n.pe-input:focus,\n.pe-textarea:focus {\n border-color: var(--dsw-alias-button-ghost-active-border);\n}\n\n.pe-textarea {\n resize: vertical;\n min-height: 72px; /* v1.27.5 紧凑化:默认 4 行(原 120px 过高),可拖拽扩大 */\n line-height: 1.5;\n font-family: var(--ds-font-family-code);\n font-size: 12px;\n}\n\n.pe-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n flex-wrap: wrap;\n}\n\n.pe-btn {\n border-radius: 6px;\n padding: 5px 12px;\n font-size: 12px;\n cursor: pointer;\n border: 1px solid var(--dsw-alias-border-l2);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n transition: opacity 0.15s;\n}\n\n.pe-btn:hover:not(:disabled) {\n opacity: 0.85;\n}\n\n.pe-btn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.pe-btnGhost {\n border-color: var(--dsw-alias-border-l2);\n}\n\n.pe-btnPrimary {\n background: var(--dsw-alias-button-primary-fill);\n border-color: transparent;\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n.pe-hint {\n font-size: 12px;\n margin: 0;\n}\n\n.pe-hintOk {\n color: var(--dsw-alias-state-success-primary);\n}\n\n.pe-hintErr {\n color: var(--dsw-alias-state-error-primary);\n}\n";
3646
3946
  if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"PersonaEditor\"]") === null) {
@@ -4295,7 +4595,7 @@ Instruction-following style:
4295
4595
  }
4296
4596
  //#endregion
4297
4597
  //#region \0sp-css:src/client/SettingsSection
4298
- const css$2 = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* ── v1.29 需求②:可展开行(整行点击 → 行内详设)── */\n.ss-expandable {\n flex-wrap: wrap; /* detail 换行占满整行 */\n cursor: default;\n}\n.ss-expandable .ss-rowText {\n cursor: pointer;\n}\n.ss-expandable .ss-rowText:hover .ss-rowName {\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expanded {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expandMark {\n display: inline-block;\n margin-left: 6px;\n font-size: 10px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n.ss-expanded .ss-expandMark {\n transform: rotate(90deg);\n}\n.ss-rowDetail {\n flex-basis: 100%;\n width: 100%;\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 10px;\n padding-top: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n}\n.ss-detailStack {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.ss-detailIntro {\n margin: 0;\n font-size: 12px;\n line-height: 19px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.06));\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary;v1.27.5 紧凑化:默认单行截断,hover title 看全文) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-border-l3);\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-brand-primary);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n/* Skiff 调试端口输入(number;关闭时跟随 disabled 半透明) */\n.ss-portInput {\n width: 96px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.ss-portInput:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 自主轨迹「立即唤起」按钮(v1.26.14 调试语义;官方 button 语言:primary 弱化) */\n.ss-wakeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-wakeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-wakeBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 错误提示(v1.29 会话清理等) */\n.ss-error {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary, #ef4444);\n background: rgba(239, 68, 68, 0.08);\n border-radius: 8px;\n padding: 8px 10px;\n white-space: pre-line;\n word-break: break-word;\n}\n\n/* ── v1.27.5 紧凑化:折叠组(details/summary)── */\n.ss-collapse {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.ss-collapseHead {\n display: flex;\n align-items: baseline;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n list-style: none;\n user-select: none;\n}\n\n.ss-collapseHead::-webkit-details-marker {\n display: none;\n}\n\n.ss-collapseHead::before {\n content: '▸';\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n\n.ss-collapse[open] > .ss-collapseHead::before {\n transform: rotate(90deg);\n}\n\n.ss-collapseHead:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.06));\n}\n\n.ss-collapseTitle {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.ss-collapseDesc {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.ss-collapseBody {\n padding: 10px 14px 14px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n}\n\n/* ── v1.27.5 紧凑化:只读定义列表(dl/dt/dd 2 列网格)── */\n.ss-defList {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px 20px;\n margin: 0;\n width: 100%;\n min-width: 0;\n}\n\n.ss-defItem {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.ss-defTerm {\n font-size: 11px;\n line-height: 16px;\n font-weight: 500;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.ss-defValue {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ── v1.27.5 紧凑化:开关 + 端口内联行 ── */\n.ss-switchRow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* ── v1.27.6 「?」帮助浮层(hover/焦点显示三行以上严谨说明)── */\n.ss-help {\n position: relative;\n display: inline-flex;\n align-items: center;\n margin-top: 2px;\n}\n\n.ss-helpMark {\n flex: none;\n width: 16px;\n height: 16px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 50%;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n line-height: 1;\n font-weight: 600;\n cursor: help;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: border-color 0.15s ease, color 0.15s ease;\n}\n\n.ss-helpMark:hover,\n.ss-helpMark:focus-visible {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n outline: none;\n}\n\n.ss-helpTip {\n position: absolute;\n left: 0;\n top: calc(100% + 6px);\n z-index: 30;\n width: 300px;\n max-width: 60vw;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);\n font-size: 12px;\n line-height: 19px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n white-space: pre-line;\n word-break: break-word;\n opacity: 0;\n pointer-events: none;\n transform: translateY(-4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n\n.ss-help:hover .ss-helpTip,\n.ss-helpMark:focus-visible + .ss-helpTip {\n opacity: 1;\n pointer-events: auto;\n transform: translateY(0);\n}\n\n/* 折叠组内帮助浮层定位修正(左侧行卡不会溢出面板) */\n.ss-rowCard .ss-helpTip {\n left: 0;\n}\n\n/* ── 微信桥(F4c-3,v1.27.0 实验性)── */\n\n/* CCC 选择器(下拉) */\n.ss-select {\n max-width: 280px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* 扫码绑定按钮(复用 wakeBtn 语言) */\n.ss-loginBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-loginBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-loginBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 移除账号按钮(危险语义:红边框) */\n.ss-removeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-removeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary, #ef4444);\n color: var(--dsw-alias-state-error-primary, #ef4444);\n}\n\n/* 二维码展示行 */\n.ss-qrRow {\n align-items: center;\n}\n\n.ss-qrSvg {\n flex: none;\n width: 120px; /* v1.27.5 紧凑化:160 → 120 */\n height: 120px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: #fff; /* 二维码需要白底才能被微信扫出 */\n padding: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.ss-qrSvg svg {\n width: 100%;\n height: 100%;\n}\n\n/* 路由表 JSON 编辑框 */\n.ss-routesEditor {\n width: 100%;\n box-sizing: border-box;\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-family: var(--ds-font-family-code);\n resize: vertical;\n}\n";
4598
+ const css$2 = "/* SettingsSection — dsh 原生设置面板 serenity-hooks 简单配置页\n v1.21.1 看齐 dsh 自身:官方 settings 设计语言——\n section > title + intro + group(groupTitle) + rowCard 行卡;\n 全部 --dsw-alias-* token(与 ui-settings-models 同词汇表)。 */\n\n/* 页面容器(官方 .section:flex column gap 12) */\n.ss-section {\n display: flex;\n flex-direction: column;\n gap: 14px;\n max-width: 720px;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面标题(官方 .title:16px/24 500) */\n.ss-title {\n margin: 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 页面说明(官方 .intro:14px/22 tertiary) */\n.ss-intro {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 分组(多级标题:分组标题 + 行卡列表) */\n.ss-group {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 分组标题(二级:12px/18 500 secondary——官方 fieldLabel 风格) */\n.ss-groupTitle {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 600;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* 行卡列表(官方 .rows:无 marker,gap 8) */\n.ss-rows {\n list-style: none;\n margin: 0;\n padding: 0;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n/* 行卡(官方 .rowCard:border-l2 r12 padding 12/14) */\n.ss-rowCard {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n padding: 12px 14px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n\n/* ── v1.29 需求②:可展开行(整行点击 → 行内详设)── */\n.ss-expandable {\n flex-wrap: wrap; /* detail 换行占满整行 */\n cursor: default;\n}\n.ss-expandable .ss-rowText {\n cursor: pointer;\n}\n.ss-expandable .ss-rowText:hover .ss-rowName {\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expanded {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.ss-expandMark {\n display: inline-block;\n margin-left: 6px;\n font-size: 10px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n.ss-expanded .ss-expandMark {\n transform: rotate(90deg);\n}\n.ss-rowDetail {\n flex-basis: 100%;\n width: 100%;\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin-top: 10px;\n padding-top: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n min-width: 0;\n}\n.ss-detailStack {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.ss-detailIntro {\n margin: 0;\n font-size: 12px;\n line-height: 19px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-line;\n word-break: break-word;\n overflow-wrap: anywhere;\n padding: 8px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.06));\n}\n\n/* 行卡文本(标题 + 说明 左列) */\n.ss-rowText {\n display: flex;\n flex-direction: column;\n gap: 3px;\n min-width: 0;\n flex: 1;\n}\n\n/* 行卡标题(官方 .rowName:14px/22 500) */\n.ss-rowName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n/* 行卡说明(12px/18 tertiary;v1.27.5 紧凑化:默认单行截断,hover title 看全文) */\n.ss-rowDesc {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* 行卡控件(右置) */\n.ss-rowControl {\n flex: none;\n display: flex;\n align-items: center;\n}\n\n/* 开关(官方 token 的自绘 toggle:track 22px,thumb 16px) */\n.ss-switch {\n position: relative;\n flex: none;\n width: 40px;\n height: 22px;\n cursor: pointer;\n}\n\n.ss-switch input {\n position: absolute;\n opacity: 0;\n width: 100%;\n height: 100%;\n margin: 0;\n cursor: pointer;\n}\n\n.ss-switchTrack {\n position: absolute;\n inset: 0;\n border-radius: 11px;\n background: var(--dsw-alias-border-l3);\n transition: background var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switchTrack::after {\n content: '';\n position: absolute;\n top: 3px;\n left: 3px;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n background: var(--dsw-alias-label-primary-foreground);\n transition: transform var(--ds-transition-duration-fast, 0.15s) var(--ds-ease-in-out, ease);\n}\n\n.ss-switch input:checked + .ss-switchTrack {\n background: var(--dsw-alias-brand-primary);\n}\n\n.ss-switch input:checked + .ss-switchTrack::after {\n transform: translateX(18px);\n}\n\n.ss-switch input:disabled + .ss-switchTrack {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 阈值行(滑块 + 值) */\n.ss-threshold {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.ss-threshold input[type='range'] {\n width: 160px;\n}\n\n/* Skiff 调试端口输入(number;关闭时跟随 disabled 半透明) */\n.ss-portInput {\n width: 96px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n font-variant-numeric: tabular-nums;\n text-align: right;\n}\n\n.ss-portInput:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.ss-value {\n min-width: 40px;\n font-variant-numeric: tabular-nums;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n text-align: right;\n}\n\n/* 自主轨迹「立即唤起」按钮(v1.26.14 调试语义;官方 button 语言:primary 弱化) */\n.ss-wakeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-wakeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-wakeBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 底部帮助行 */\n.ss-hint {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 降级提示 */\n.ss-degrade {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-label);\n word-break: break-word;\n overflow-wrap: anywhere;\n}\n\n/* 错误提示(v1.29 会话清理等) */\n.ss-error {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary, #ef4444);\n background: rgba(239, 68, 68, 0.08);\n border-radius: 8px;\n padding: 8px 10px;\n white-space: pre-line;\n word-break: break-word;\n}\n\n/* ── v1.27.5 紧凑化:折叠组(details/summary)── */\n.ss-collapse {\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.ss-collapseHead {\n display: flex;\n align-items: baseline;\n gap: 10px;\n padding: 10px 14px;\n cursor: pointer;\n list-style: none;\n user-select: none;\n}\n\n.ss-collapseHead::-webkit-details-marker {\n display: none;\n}\n\n.ss-collapseHead::before {\n content: '▸';\n flex: none;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n transition: transform 0.15s ease;\n}\n\n.ss-collapse[open] > .ss-collapseHead::before {\n transform: rotate(90deg);\n}\n\n.ss-collapseHead:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(128, 128, 128, 0.06));\n}\n\n.ss-collapseTitle {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n.ss-collapseDesc {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n}\n\n.ss-collapseBody {\n padding: 10px 14px 14px;\n border-top: 1px solid var(--dsw-alias-border-l1);\n}\n\n/* ── v1.27.5 紧凑化:只读定义列表(dl/dt/dd 2 列网格)── */\n.ss-defList {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 6px 20px;\n margin: 0;\n width: 100%;\n min-width: 0;\n}\n\n.ss-defItem {\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n}\n\n.ss-defTerm {\n font-size: 11px;\n line-height: 16px;\n font-weight: 500;\n letter-spacing: 0.02em;\n text-transform: uppercase;\n color: var(--dsw-alias-label-tertiary);\n}\n\n.ss-defValue {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n word-break: break-word;\n overflow-wrap: anywhere;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* ── v1.27.5 紧凑化:开关 + 端口内联行 ── */\n.ss-switchRow {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n/* ── v1.59.1 ACP 账户台账表(owner 2026-10-17:「面板里只放个开关不放具体的条目吗?」)\n *\n * 用**表格**而不是卡片列表:台账天生是\"一行一条、列对齐可比\"的数据\n * (账户 / 角色 / 状态 / token 指纹),卡片会把可比性拆散。\n * 窄屏靠横向滚动兜底——不折行,折行后\"哪一格对应哪一列\"就看不出来了。 */\n.ss-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 12px;\n overflow-x: auto;\n display: block;\n}\n\n.ss-table th,\n.ss-table td {\n padding: 4px 8px;\n text-align: left;\n border-bottom: 1px solid var(--dsw-alias-border-l2);\n white-space: nowrap;\n}\n\n.ss-table th {\n color: var(--dsw-alias-label-tertiary);\n font-weight: 600;\n}\n\n.ss-table td code {\n font-size: 11px;\n padding: 1px 4px;\n border-radius: 3px;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n}\n\n.ss-table button {\n margin-left: 4px;\n}\n\n/* ── v1.27.6 「?」帮助浮层(hover/焦点显示三行以上严谨说明)── */\n.ss-help {\n position: relative;\n display: inline-flex;\n align-items: center;\n margin-top: 2px;\n}\n\n.ss-helpMark {\n flex: none;\n width: 16px;\n height: 16px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 50%;\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-tertiary);\n font-size: 10px;\n line-height: 1;\n font-weight: 600;\n cursor: help;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n transition: border-color 0.15s ease, color 0.15s ease;\n}\n\n.ss-helpMark:hover,\n.ss-helpMark:focus-visible {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n outline: none;\n}\n\n.ss-helpTip {\n position: absolute;\n left: 0;\n top: calc(100% + 6px);\n z-index: 30;\n width: 300px;\n max-width: 60vw;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 10px;\n background: var(--dsw-alias-bg-overlay);\n box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);\n font-size: 12px;\n line-height: 19px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n white-space: pre-line;\n word-break: break-word;\n opacity: 0;\n pointer-events: none;\n transform: translateY(-4px);\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\n\n.ss-help:hover .ss-helpTip,\n.ss-helpMark:focus-visible + .ss-helpTip {\n opacity: 1;\n pointer-events: auto;\n transform: translateY(0);\n}\n\n/* 折叠组内帮助浮层定位修正(左侧行卡不会溢出面板) */\n.ss-rowCard .ss-helpTip {\n left: 0;\n}\n\n/* ── 微信桥(F4c-3,v1.27.0 实验性)── */\n\n/* CCC 选择器(下拉) */\n.ss-select {\n max-width: 280px;\n padding: 6px 8px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* 扫码绑定按钮(复用 wakeBtn 语言) */\n.ss-loginBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-loginBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-success-primary, #22c55e);\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n\n.ss-loginBtn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n/* 移除账号按钮(危险语义:红边框) */\n.ss-removeBtn {\n flex: none;\n padding: 5px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-interactive-bg-active, rgba(128, 128, 128, 0.12));\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n cursor: pointer;\n transition: opacity var(--ds-transition-duration-fast, 0.15s) ease;\n}\n\n.ss-removeBtn:hover:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary, #ef4444);\n color: var(--dsw-alias-state-error-primary, #ef4444);\n}\n\n/* 二维码展示行 */\n.ss-qrRow {\n align-items: center;\n}\n\n.ss-qrSvg {\n flex: none;\n width: 120px; /* v1.27.5 紧凑化:160 → 120 */\n height: 120px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: #fff; /* 二维码需要白底才能被微信扫出 */\n padding: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.ss-qrSvg svg {\n width: 100%;\n height: 100%;\n}\n\n/* 路由表 JSON 编辑框 */\n.ss-routesEditor {\n width: 100%;\n box-sizing: border-box;\n margin-top: 6px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 12px;\n line-height: 18px;\n font-family: var(--ds-font-family-code);\n resize: vertical;\n}\n";
4299
4599
  if (typeof document !== "undefined" && document.querySelector("style[data-sp-css=\"SettingsSection\"]") === null) {
4300
4600
  const tag = document.createElement("style");
4301
4601
  tag.setAttribute("data-sp-css", "SettingsSection");
@@ -4592,12 +4892,12 @@ Instruction-following style:
4592
4892
  checked: acpFaceOn,
4593
4893
  onChange: (on) => toggle("acpFaceEnabled", on)
4594
4894
  }),
4595
- detail: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4895
+ detail: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4596
4896
  className: "ss-detailStack",
4597
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4897
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
4598
4898
  className: "ss-detailIntro",
4599
- children: "ACP 面(面 E,2026-10-17 owner 令)\n· 作用:把**官方** @deepseek-ai/dsh-acp 挂进本进程,标准 ACP 客户端经 WebSocket 登入本机\n· 与「微信桥」的关系:两者是**入站通道的两个实现**,每个 CCC 二选一(在各自的\n .opencode/serenity.json 里配 im.channel)。本开关只管**端口开不开**,不管哪个 CCC 用哪条\n· 🔴 认证:本面绑 0.0.0.0 且 ACP 协议自身**不做认证** ⇒ 由 ACC 在握手前用\n **每 CCC 一枚 token**(localstore 键 IM_ACP_TOKEN)把关;没配 token 的 CCC 一律连不上\n· 怎么用:① 在目标 CCC 配 im.channel=acp + im.acp.role=<角色名> ② 生成 token\n ③ 客户端连 ws://<本机IP>:3200,带 Authorization: Bearer <token> 或 ?token=<token>\n· ⚠️ 明文 ws:// ⇒ 局域网内可抓包;**上公网必须走 wss + Cloudflare Access**\n· 协议无认证这一条由 ACC 补齐:未过 token 闸的连接**根本升不上来**(不是\"升上来再拒\")"
4600
- })
4899
+ children: "ACP 面(面 E,2026-10-17 owner 令)\n· 作用:把**官方** @deepseek-ai/dsh-acp 挂进本进程,标准 ACP 客户端经 WebSocket 登入本机\n· 与「微信桥」的关系:两者是**入站通道的两个实现**,每个 CCC 二选一(在各自的\n .opencode/serenity.json 里配 im.channel)。本开关只管**端口开不开**,不管哪个 CCC 用哪条\n· 🔴 认证:本面绑 0.0.0.0 且 ACP 协议自身**不做认证** ⇒ 由 ACC 在握手前用\n **每账户一枚 token**(localstore 键 IM_ACP_TOKEN_<账户ID>)把关;没配账户的 CCC 一律连不上\n· 账户台账在**下面那张表**里管(加设备/看指纹/轮换/删除),不必改 JSON 或手敲命令\n· ⚠️ 明文 ws:// ⇒ 局域网内可抓包;**上公网必须走 wss + Cloudflare Access**\n· 协议无认证这一条由 ACC 补齐:未过 token 闸的连接**根本升不上来**(不是\"升上来再拒\")"
4900
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AcpAccountsEditor, {})]
4601
4901
  })
4602
4902
  }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RowCard, {
4603
4903
  title: "图(diagram)",