@harness-mix/cli 0.1.4 → 0.1.6
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/CHANGELOG.md +8 -0
- package/README.md +26 -96
- package/docs/chatgpt-web-sidebar.md +7 -1
- package/docs/cross-platform.md +15 -0
- package/docs/harness-handoff-design.md +377 -0
- package/docs/harness-management.md +28 -1
- package/docs/multi-agent-collaboration.md +9 -9
- package/docs/native-codex.md +21 -10
- package/output/native-build/harness-mix-shim.exe +0 -0
- package/output/native-build/renderer-extension.js +2596 -135
- package/package.json +20 -10
- package/scripts/chatgpt-context-ui-smoke.cjs +16 -0
- package/scripts/codex-accounts-test.cjs +81 -0
- package/scripts/codex-accounts-ui-smoke.cjs +70 -0
- package/scripts/collaboration-test.cjs +8 -3
- package/scripts/collaboration-ui-smoke.cjs +40 -26
- package/scripts/e2e-integrations.cjs +52 -0
- package/scripts/e2e-native-host.cjs +39 -0
- package/scripts/handoff-checkpoint-test.cjs +83 -0
- package/scripts/install-native.cjs +4 -1
- package/scripts/integrations-test.cjs +123 -0
- package/scripts/integrations-ui-smoke.cjs +124 -0
- package/scripts/native-protocol-test.cjs +180 -5
- package/scripts/native-secret-test.cjs +2 -2
- package/scripts/native-shim-test.cjs +20 -4
- package/scripts/native-sidebar-test.cjs +7 -0
- package/scripts/native-skill-roots-test.cjs +90 -0
- package/scripts/native-ui-smoke.cjs +58 -2
- package/scripts/publish-native-package.cjs +8 -5
- package/scripts/support/integrations-mcp-fixture.cjs +15 -0
- package/scripts/switch-harness-test.cjs +27 -1
- package/scripts/test-live-mentions.cjs +152 -0
- package/src/main/adapters/acp.js +3 -3
- package/src/main/adapters/antigravity.js +17 -17
- package/src/main/adapters/claude.js +50 -10
- package/src/main/adapters/codebuddy.js +1 -0
- package/src/main/adapters/codex-app-server.js +20 -13
- package/src/main/adapters/codex.js +25 -5
- package/src/main/adapters/cursor.js +5 -1
- package/src/main/adapters/dsh.js +9 -1
- package/src/main/adapters/grok.js +7 -3
- package/src/main/adapters/hermes.js +8 -0
- package/src/main/adapters/kiro.js +2 -1
- package/src/main/adapters/managed-mcp.js +25 -0
- package/src/main/adapters/native-acp.js +4 -4
- package/src/main/adapters/omp.js +11 -0
- package/src/main/adapters/openclaw.js +6 -0
- package/src/main/adapters/opencode.js +13 -3
- package/src/main/adapters/pi.js +5 -0
- package/src/main/adapters/qoder.js +1 -0
- package/src/main/adapters/trae.js +4 -0
- package/src/main/adapters/zcode.js +3 -0
- package/src/main/harness-adapter/manifest.js +17 -2
- package/src/main/host/collaboration.js +57 -11
- package/src/main/host/handoff-access.js +53 -0
- package/src/main/host/handoff-checkpoints.js +206 -0
- package/src/main/host/handoff-mcp.cjs +38 -0
- package/src/main/host/handoff-tools.js +13 -0
- package/src/main/host/handoff.js +28 -6
- package/src/main/host/integrations.js +339 -0
- package/src/main/host/runtime.js +95 -15
- package/src/main/host/store.js +4 -4
- package/src/main/native/codex-accounts.js +260 -0
- package/src/main/native/host.js +1 -1
- package/src/main/native/launcher.js +2 -2
- package/src/main/native/protocol.js +176 -14
- package/src/main/native/redact.js +1 -1
- package/src/main/native/rs/crates/shim/src/main.rs +155 -12
- package/src/main/native/thread-list.js +3 -0
- package/src/native-ui/desktop-control/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/dist/types/harness-mix-settings.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/index.d.ts +4 -0
- package/src/native-ui/renderer-extension/dist/types/index.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-agent-picker.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-binding-probe.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-chatgpt-context.d.ts +36 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-chatgpt-context.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-codex-account-state.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts +3 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-composer-dom.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts +53 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-handoff.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts +11 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-harness-mentions.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-integrations-client.d.ts +105 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-integrations-client.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts +7 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-model-client.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/renderer-settings-lifecycle.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts +2 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-list.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/accounts-page.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/integrations-page.d.ts +6 -0
- package/src/native-ui/renderer-extension/dist/types/settings/integrations-page.d.ts.map +1 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts +5 -0
- package/src/native-ui/renderer-extension/dist/types/settings/localization.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts +8 -4
- package/src/native-ui/renderer-extension/dist/types/settings/pages.d.ts.map +1 -1
- package/src/native-ui/renderer-extension/dist/types/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/renderer-extension/src/harness-mix-settings.ts +4 -1
- package/src/native-ui/renderer-extension/src/index.ts +28 -0
- package/src/native-ui/renderer-extension/src/renderer-agent-icon.ts +4 -4
- package/src/native-ui/renderer-extension/src/renderer-agent-picker.ts +46 -6
- package/src/native-ui/renderer-extension/src/renderer-binding-probe.ts +115 -18
- package/src/native-ui/renderer-extension/src/renderer-chatgpt-context.ts +87 -0
- package/src/native-ui/renderer-extension/src/renderer-codex-account-state.ts +10 -0
- package/src/native-ui/renderer-extension/src/renderer-composer-dom.ts +10 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-handoff.ts +330 -0
- package/src/native-ui/renderer-extension/src/renderer-harness-mentions.ts +556 -123
- package/src/native-ui/renderer-extension/src/renderer-integrations-client.ts +59 -0
- package/src/native-ui/renderer-extension/src/renderer-model-client.ts +17 -1
- package/src/native-ui/renderer-extension/src/renderer-settings-lifecycle.ts +2 -0
- package/src/native-ui/renderer-extension/src/settings/accounts-list.ts +16 -1
- package/src/native-ui/renderer-extension/src/settings/accounts-page.ts +32 -3
- package/src/native-ui/renderer-extension/src/settings/integrations-page.ts +1474 -0
- package/src/native-ui/renderer-extension/src/settings/localization.ts +32 -13
- package/src/native-ui/renderer-extension/src/settings/pages.ts +49 -18
- package/src/native-ui/renderer-extension/src/settings/shell.css +103 -0
- package/src/native-ui/renderer-extension/test/renderer-agent-picker.test.ts +44 -13
- package/src/native-ui/renderer-extension/test/renderer-chatgpt-context.test.ts +21 -0
- package/src/native-ui/renderer-extension/test/renderer-codex-account-state.test.ts +13 -1
- package/src/native-ui/renderer-extension/test/renderer-harness-handoff.test.ts +20 -0
- package/src/native-ui/renderer-extension/test/renderer-model-client.test.ts +9 -2
- package/src/native-ui/renderer-extension/test/settings/localization.test.ts +1 -1
- package/src/native-ui/renderer-extension/test/settings/pages.test.ts +77 -8
- package/src/native-ui/renderer-extension/test/settings/shell.test.ts +6 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.d.ts +19 -4
- package/src/native-ui/shared-contracts/dist/codex-accounts.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/codex-accounts.js +2 -0
- package/src/native-ui/shared-contracts/dist/codex-accounts.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/index.d.ts +2 -2
- package/src/native-ui/shared-contracts/dist/index.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/index.js +1 -1
- package/src/native-ui/shared-contracts/dist/index.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts +29 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.d.ts.map +1 -1
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js +13 -0
- package/src/native-ui/shared-contracts/dist/thread-harness-switch.js.map +1 -1
- package/src/native-ui/shared-contracts/dist/tsconfig.tsbuildinfo +1 -1
- package/src/native-ui/shared-contracts/src/codex-accounts.ts +2 -0
- package/src/native-ui/shared-contracts/src/index.ts +8 -1
- package/src/native-ui/shared-contracts/src/thread-harness-switch.ts +17 -0
|
@@ -15580,7 +15580,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15580
15580
|
planType: codexAccountPlanTypeSchema.optional(),
|
|
15581
15581
|
codexHome: nonBlankTextSchema3.max(16384),
|
|
15582
15582
|
active: external_exports.boolean(),
|
|
15583
|
-
isDefault: external_exports.boolean()
|
|
15583
|
+
isDefault: external_exports.boolean(),
|
|
15584
|
+
authenticated: external_exports.boolean().optional(),
|
|
15585
|
+
management: external_exports.enum(["native", "isolated"]).optional()
|
|
15584
15586
|
}).strict();
|
|
15585
15587
|
var codexAccountListResultSchema = external_exports.object({ accounts: external_exports.array(codexAccountSchema).max(128) }).strict();
|
|
15586
15588
|
var codexAccountCreateParamsSchema = external_exports.object({ label: nonBlankTextSchema3.max(256).optional() }).strict();
|
|
@@ -15698,13 +15700,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
15698
15700
|
}).strict();
|
|
15699
15701
|
|
|
15700
15702
|
// src/native-ui/shared-contracts/src/thread-harness-switch.ts
|
|
15703
|
+
var harnessHandoffIntentSchema = external_exports.enum(["continue", "execute-plan", "review", "reanalyze"]);
|
|
15704
|
+
var harnessHandoffIncludesSchema = external_exports.object({
|
|
15705
|
+
conversation: external_exports.boolean(),
|
|
15706
|
+
plan: external_exports.boolean(),
|
|
15707
|
+
evidence: external_exports.boolean(),
|
|
15708
|
+
files: external_exports.boolean(),
|
|
15709
|
+
unresolved: external_exports.boolean()
|
|
15710
|
+
}).strict();
|
|
15701
15711
|
var threadHarnessSwitchParamsSchema = external_exports.object({
|
|
15702
15712
|
threadId: hostThreadIdSchema,
|
|
15703
15713
|
harnessId: external_exports.string().min(1),
|
|
15704
|
-
note: external_exports.string().max(2e3).optional()
|
|
15714
|
+
note: external_exports.string().max(2e3).optional(),
|
|
15715
|
+
intent: harnessHandoffIntentSchema.optional(),
|
|
15716
|
+
includes: harnessHandoffIncludesSchema.optional()
|
|
15705
15717
|
}).strict();
|
|
15706
15718
|
var threadHarnessSwitchResultSchema = external_exports.object({
|
|
15707
|
-
threadId: hostThreadIdSchema
|
|
15719
|
+
threadId: hostThreadIdSchema,
|
|
15720
|
+
checkpointId: external_exports.string().min(1)
|
|
15708
15721
|
}).strict();
|
|
15709
15722
|
|
|
15710
15723
|
// src/native-ui/shared-contracts/src/harness-commands.ts
|
|
@@ -16051,7 +16064,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16051
16064
|
|
|
16052
16065
|
// src/native-ui/renderer-extension/src/renderer-agent-icon.ts
|
|
16053
16066
|
var RENDERER_AGENT_LABELS = {
|
|
16054
|
-
codex: "Codex",
|
|
16067
|
+
codex: "Codex\uFF08\u5B98\u65B9\u539F\u751F\uFF09",
|
|
16055
16068
|
pi: "Pi",
|
|
16056
16069
|
"claude-code": "Claude Code",
|
|
16057
16070
|
"deepseek-harness": "DeepSeek Harness",
|
|
@@ -16067,7 +16080,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
16067
16080
|
zcode: "ZCode",
|
|
16068
16081
|
trae: "Trae",
|
|
16069
16082
|
"cursor-cli": "Cursor",
|
|
16070
|
-
"codex-harness": "Codex\
|
|
16083
|
+
"codex-harness": "Codex\uFF08Harness Mix \u534F\u4F5C\uFF09"
|
|
16071
16084
|
};
|
|
16072
16085
|
var PI_PATHS = [
|
|
16073
16086
|
{
|
|
@@ -18023,8 +18036,8 @@ ${error51.stderrTail}`] : []
|
|
|
18023
18036
|
sessionImportRetryOpen: "Try opening again",
|
|
18024
18037
|
sessionImportRetrying: "Opening...",
|
|
18025
18038
|
connectionsDescription: "View runtime status by Host. Select an item to inspect details or complete its setup.",
|
|
18026
|
-
accountsDescription: "
|
|
18027
|
-
accountTaskHint: "
|
|
18039
|
+
accountsDescription: "Manage isolated native Codex accounts and compare each account's live limits.",
|
|
18040
|
+
accountTaskHint: "Signing in or switching accounts does not affect tasks already running. New tasks use the selected account.",
|
|
18028
18041
|
accountConnected: "Connected accounts",
|
|
18029
18042
|
accountDefaultBadge: "Default",
|
|
18030
18043
|
accountAdd: "Add Account",
|
|
@@ -18051,6 +18064,11 @@ ${error51.stderrTail}`] : []
|
|
|
18051
18064
|
accountActive: "Default",
|
|
18052
18065
|
accountUse: "Set as default",
|
|
18053
18066
|
accountSignIn: "Sign in",
|
|
18067
|
+
accountSignOut: "Sign out",
|
|
18068
|
+
accountSignOutConfirm: "Sign out of the current official Codex account?",
|
|
18069
|
+
accountSigningOut: "Signing out of official Codex...",
|
|
18070
|
+
accountSignedOut: "Signed out of official Codex.",
|
|
18071
|
+
accountSignOutFailed: "Could not sign out of official Codex.",
|
|
18054
18072
|
accountSigningIn: "Starting device sign-in...",
|
|
18055
18073
|
accountDeviceCodePrerequisite: "Before signing in, enable \u201CEnable device code authorization for Codex\u201D in Web Settings \u2192 Account security & sign-in.",
|
|
18056
18074
|
accountVerificationDescription: "Open the verification page and enter this one-time code:",
|
|
@@ -18214,6 +18232,8 @@ ${error51.stderrTail}`] : []
|
|
|
18214
18232
|
pageLabels: Object.freeze({
|
|
18215
18233
|
connections: "Connections",
|
|
18216
18234
|
accounts: "Accounts",
|
|
18235
|
+
mcp: "MCP",
|
|
18236
|
+
skills: "Skills",
|
|
18217
18237
|
"session-import": "Session Import",
|
|
18218
18238
|
updates: "Updates",
|
|
18219
18239
|
about: "About"
|
|
@@ -18263,8 +18283,8 @@ ${error51.stderrTail}`] : []
|
|
|
18263
18283
|
sessionImportRetryOpen: "\u91CD\u8BD5\u6253\u5F00",
|
|
18264
18284
|
sessionImportRetrying: "\u6B63\u5728\u6253\u5F00\u2026\u2026",
|
|
18265
18285
|
connectionsDescription: "\u6309 Host \u67E5\u770B\u8FD0\u884C\u65F6\u72B6\u6001\u3002\u9009\u62E9\u4E00\u9879\uFF0C\u5728\u53F3\u4FA7\u68C0\u67E5\u8BE6\u60C5\u6216\u5B8C\u6210\u914D\u7F6E\u3002",
|
|
18266
|
-
accountsDescription: "\
|
|
18267
|
-
accountTaskHint: "\u5207\u6362\
|
|
18286
|
+
accountsDescription: "\u7BA1\u7406\u76F8\u4E92\u9694\u79BB\u7684\u539F\u751F Codex \u8D26\u53F7\uFF0C\u5E76\u5BF9\u6BD4\u6BCF\u4E2A\u8D26\u53F7\u7684\u5B9E\u65F6\u989D\u5EA6\u3002",
|
|
18287
|
+
accountTaskHint: "\u767B\u5F55\u6216\u5207\u6362\u8D26\u53F7\u4E0D\u4F1A\u5F71\u54CD\u5DF2\u5F00\u59CB\u7684\u4EFB\u52A1\uFF1B\u65B0\u4EFB\u52A1\u4F7F\u7528\u5F53\u524D\u9009\u4E2D\u7684\u8D26\u53F7\u3002",
|
|
18268
18288
|
accountConnected: "\u5DF2\u8FDE\u63A5\u8D26\u53F7",
|
|
18269
18289
|
accountDefaultBadge: "\u9ED8\u8BA4",
|
|
18270
18290
|
accountAdd: "\u6DFB\u52A0\u8D26\u53F7",
|
|
@@ -18291,6 +18311,11 @@ ${error51.stderrTail}`] : []
|
|
|
18291
18311
|
accountActive: "\u9ED8\u8BA4\u8D26\u53F7",
|
|
18292
18312
|
accountUse: "\u8BBE\u4E3A\u9ED8\u8BA4",
|
|
18293
18313
|
accountSignIn: "\u767B\u5F55",
|
|
18314
|
+
accountSignOut: "\u9000\u51FA\u767B\u5F55",
|
|
18315
|
+
accountSignOutConfirm: "\u786E\u5B9A\u9000\u51FA\u5F53\u524D\u5B98\u65B9 Codex \u8D26\u53F7\u5417\uFF1F",
|
|
18316
|
+
accountSigningOut: "\u6B63\u5728\u9000\u51FA\u5B98\u65B9 Codex...",
|
|
18317
|
+
accountSignedOut: "\u5DF2\u9000\u51FA\u5B98\u65B9 Codex\u3002",
|
|
18318
|
+
accountSignOutFailed: "\u9000\u51FA\u5B98\u65B9 Codex \u5931\u8D25\u3002",
|
|
18294
18319
|
accountSigningIn: "\u6B63\u5728\u542F\u52A8\u8BBE\u5907\u767B\u5F55...",
|
|
18295
18320
|
accountDeviceCodePrerequisite: "\u767B\u5F55\u524D\uFF0C\u8BF7\u5148\u5728 Web \u7AEF\u7684\u201C\u8BBE\u7F6E \u2192 \u8D26\u53F7\u5B89\u5168\u4E0E\u767B\u5F55\u201D\u4E2D\u5F00\u542F\u201C\u4E3A Codex \u542F\u7528\u8BBE\u5907\u4EE3\u7801\u6388\u6743\u201D\u3002",
|
|
18296
18321
|
accountVerificationDescription: "\u6253\u5F00\u9A8C\u8BC1\u9875\u9762\u5E76\u8F93\u5165\u4EE5\u4E0B\u4E00\u6B21\u6027\u4EE3\u7801\uFF1A",
|
|
@@ -18454,6 +18479,8 @@ ${error51.stderrTail}`] : []
|
|
|
18454
18479
|
pageLabels: Object.freeze({
|
|
18455
18480
|
connections: "\u8FDE\u63A5",
|
|
18456
18481
|
accounts: "\u8D26\u53F7",
|
|
18482
|
+
mcp: "MCP",
|
|
18483
|
+
skills: "Skills",
|
|
18457
18484
|
"session-import": "\u4F1A\u8BDD\u5BFC\u5165",
|
|
18458
18485
|
updates: "\u66F4\u65B0",
|
|
18459
18486
|
about: "\u5173\u4E8E"
|
|
@@ -18536,10 +18563,11 @@ ${error51.stderrTail}`] : []
|
|
|
18536
18563
|
return `Agent: ${RENDERER_AGENT_LABELS[state.agent]}${account}${state.phase === "locked" ? " (locked)" : ""}`;
|
|
18537
18564
|
}
|
|
18538
18565
|
function rendererAgentPickerView(state, adapterState, switching, agents, availability = {}, codexAccountCount = 0) {
|
|
18566
|
+
const handoffMode = state.phase === "locked" && state.agent !== "codex";
|
|
18539
18567
|
const optionDisabled = Object.fromEntries(
|
|
18540
18568
|
agents.map((agent) => [
|
|
18541
18569
|
agent,
|
|
18542
|
-
switching || state.phase === "locked" || agent !== "codex" && (adapterState !== "ready" || availability[agent] !== "ready")
|
|
18570
|
+
switching || state.phase === "locked" && (!handoffMode || agent === "codex" || agent === state.agent) || agent !== "codex" && (adapterState !== "ready" || availability[agent] !== "ready")
|
|
18543
18571
|
])
|
|
18544
18572
|
);
|
|
18545
18573
|
const downloadVisible = Object.fromEntries(
|
|
@@ -18550,7 +18578,7 @@ ${error51.stderrTail}`] : []
|
|
|
18550
18578
|
);
|
|
18551
18579
|
return {
|
|
18552
18580
|
label: RENDERER_AGENT_LABELS[state.agent],
|
|
18553
|
-
triggerDisabled: switching || state.phase === "locked" || agents.length < 2 && codexAccountCount < 2,
|
|
18581
|
+
triggerDisabled: switching || (state.phase === "locked" ? !handoffMode || !agents.some((agent) => optionDisabled[agent] === false) : agents.length < 2 && codexAccountCount < 2),
|
|
18554
18582
|
nativeModelHidden: switching || state.agent !== "codex",
|
|
18555
18583
|
optionDisabled,
|
|
18556
18584
|
downloadVisible,
|
|
@@ -18631,7 +18659,21 @@ ${error51.stderrTail}`] : []
|
|
|
18631
18659
|
ownershipError.setAttribute("aria-hidden", "true");
|
|
18632
18660
|
ownershipError.style.display = "none";
|
|
18633
18661
|
ownershipError.style.font = "bold 16px/1 system-ui, sans-serif";
|
|
18634
|
-
|
|
18662
|
+
const handoffBadge = document.createElement("span");
|
|
18663
|
+
handoffBadge.textContent = "\u2197";
|
|
18664
|
+
handoffBadge.setAttribute("aria-hidden", "true");
|
|
18665
|
+
handoffBadge.style.display = "none";
|
|
18666
|
+
handoffBadge.style.position = "absolute";
|
|
18667
|
+
handoffBadge.style.right = "1px";
|
|
18668
|
+
handoffBadge.style.bottom = "0";
|
|
18669
|
+
handoffBadge.style.width = "11px";
|
|
18670
|
+
handoffBadge.style.height = "11px";
|
|
18671
|
+
handoffBadge.style.borderRadius = "999px";
|
|
18672
|
+
handoffBadge.style.background = "#4f7ff0";
|
|
18673
|
+
handoffBadge.style.color = "white";
|
|
18674
|
+
handoffBadge.style.font = "700 9px/11px system-ui, sans-serif";
|
|
18675
|
+
handoffBadge.style.textAlign = "center";
|
|
18676
|
+
trigger.append(iconSlot, spinner, ownershipError, handoffBadge);
|
|
18635
18677
|
const menu = document.createElement("div");
|
|
18636
18678
|
menu.id = `${composerId}-agent-menu`;
|
|
18637
18679
|
menu.setAttribute("role", "menu");
|
|
@@ -18656,6 +18698,11 @@ ${error51.stderrTail}`] : []
|
|
|
18656
18698
|
const options = {};
|
|
18657
18699
|
const rowsByAgent = /* @__PURE__ */ new Map();
|
|
18658
18700
|
const groupMessages = pickerGroupMessages();
|
|
18701
|
+
const modeHeading = document.createElement("div");
|
|
18702
|
+
modeHeading.hidden = true;
|
|
18703
|
+
modeHeading.style.padding = "6px 8px 7px";
|
|
18704
|
+
modeHeading.style.font = "600 11px/1 system-ui, sans-serif";
|
|
18705
|
+
modeHeading.style.opacity = "0.58";
|
|
18659
18706
|
const close = () => {
|
|
18660
18707
|
if (!popoverOpen(menu)) return;
|
|
18661
18708
|
if (typeof menu.hidePopover === "function") menu.hidePopover();
|
|
@@ -18930,7 +18977,7 @@ ${error51.stderrTail}`] : []
|
|
|
18930
18977
|
});
|
|
18931
18978
|
regroup();
|
|
18932
18979
|
const unsubscribeGroup = groupPreference.subscribe(regroup);
|
|
18933
|
-
menu.append(mainGroup, moreToggle, morePanel, cta);
|
|
18980
|
+
menu.append(modeHeading, mainGroup, moreToggle, morePanel, cta);
|
|
18934
18981
|
root.append(trigger, menu);
|
|
18935
18982
|
const onTriggerClick = () => {
|
|
18936
18983
|
if (popoverOpen(menu)) close();
|
|
@@ -18979,6 +19026,8 @@ ${error51.stderrTail}`] : []
|
|
|
18979
19026
|
iconSlot,
|
|
18980
19027
|
spinner,
|
|
18981
19028
|
ownershipError,
|
|
19029
|
+
handoffBadge,
|
|
19030
|
+
modeHeading,
|
|
18982
19031
|
menu,
|
|
18983
19032
|
agents: [...enabledAgents],
|
|
18984
19033
|
options,
|
|
@@ -19022,7 +19071,7 @@ ${error51.stderrTail}`] : []
|
|
|
19022
19071
|
control.trigger.setAttribute("aria-busy", String(switching));
|
|
19023
19072
|
control.trigger.setAttribute(
|
|
19024
19073
|
"aria-label",
|
|
19025
|
-
ownershipError ? pickerGroupMessages().ownershipErrorLabel : state.phase === "locked" ? `Agent: ${view.label}` : `Select Agent, current ${view.label}`
|
|
19074
|
+
ownershipError ? pickerGroupMessages().ownershipErrorLabel : state.phase === "locked" ? state.agent === "codex" ? `Agent: ${view.label}` : `Hand off task from ${view.label}` : `Select Agent, current ${view.label}`
|
|
19026
19075
|
);
|
|
19027
19076
|
const activeAccount = codexAccounts.find(({ active }) => active);
|
|
19028
19077
|
control.codexAccountGroup.render({
|
|
@@ -19031,12 +19080,16 @@ ${error51.stderrTail}`] : []
|
|
|
19031
19080
|
disabled: switching || state.phase === "locked",
|
|
19032
19081
|
showBadge: state.agent === "codex" && codexAccounts.length > 1
|
|
19033
19082
|
});
|
|
19034
|
-
control.trigger.title = ownershipError ? pickerGroupMessages().ownershipErrorLabel : rendererAgentPickerTooltip(state, activeAccount);
|
|
19083
|
+
control.trigger.title = ownershipError ? pickerGroupMessages().ownershipErrorLabel : state.phase === "locked" && state.agent !== "codex" ? `Hand off this task from ${view.label} to another Harness` : rendererAgentPickerTooltip(state, activeAccount);
|
|
19035
19084
|
control.trigger.style.cursor = control.trigger.disabled ? "not-allowed" : "pointer";
|
|
19036
19085
|
control.trigger.style.opacity = control.trigger.disabled && !switching ? "0.72" : "1";
|
|
19037
19086
|
control.iconSlot.style.display = switching || ownershipError ? "none" : "inline-flex";
|
|
19038
19087
|
control.spinner.style.display = switching ? "block" : "none";
|
|
19039
19088
|
control.ownershipError.style.display = ownershipError && !switching ? "block" : "none";
|
|
19089
|
+
const handoffMode = state.phase === "locked" && state.agent !== "codex";
|
|
19090
|
+
control.handoffBadge.style.display = handoffMode && !switching && !ownershipError ? "block" : "none";
|
|
19091
|
+
control.modeHeading.hidden = !handoffMode;
|
|
19092
|
+
control.modeHeading.textContent = pickerGroupMessages().locale === "zh-CN" ? `\u63A5\u529B\u5230\u5176\u4ED6 Harness` : "Hand off to another Harness";
|
|
19040
19093
|
if (control.trigger.disabled) control.close();
|
|
19041
19094
|
for (const agent of control.agents) {
|
|
19042
19095
|
const option = control.options[agent];
|
|
@@ -21277,6 +21330,291 @@ ${error51.stderrTail}`] : []
|
|
|
21277
21330
|
return control;
|
|
21278
21331
|
}
|
|
21279
21332
|
|
|
21333
|
+
// src/native-ui/renderer-extension/src/renderer-harness-handoff.ts
|
|
21334
|
+
var HARNESS_HANDOFF_NOTE_MAX_LENGTH = 2e3;
|
|
21335
|
+
function rendererHarnessHandoffMessages(locale) {
|
|
21336
|
+
return locale === "zh-CN" ? {
|
|
21337
|
+
title: "\u63A5\u529B\u5F53\u524D\u4EFB\u52A1",
|
|
21338
|
+
description: "\u4FDD\u7559\u6B64\u4EFB\u52A1\u7A97\u53E3\u3001\u5BF9\u8BDD\u8BB0\u5F55\u548C\u6587\u4EF6\u73B0\u573A\uFF0C\u53EA\u66F4\u6362\u7EE7\u7EED\u6267\u884C\u7684 Harness\u3002",
|
|
21339
|
+
noteLabel: "\u4EA4\u63A5\u8BF4\u660E\uFF08\u53EF\u9009\uFF09",
|
|
21340
|
+
intentLabel: "\u63A5\u529B\u65B9\u5F0F",
|
|
21341
|
+
intentOptions: { continue: "\u7EE7\u7EED\u6267\u884C", "execute-plan": "\u6267\u884C\u4E0A\u4E00\u65B9\u6848", review: "\u72EC\u7ACB\u5BA1\u67E5", reanalyze: "\u91CD\u65B0\u5206\u6790" },
|
|
21342
|
+
includeLabel: "\u5C06\u8981\u4EA4\u63A5",
|
|
21343
|
+
includeOptions: { conversation: "\u8FD1\u671F\u5BF9\u8BDD", plan: "\u5F53\u524D\u8BA1\u5212", evidence: "\u6D4B\u8BD5\u4E0E\u5DE5\u5177\u8BC1\u636E", files: "\u6587\u4EF6\u53D8\u66F4", unresolved: "\u672A\u89E3\u51B3\u95EE\u9898" },
|
|
21344
|
+
notePlaceholder: "\u4F8B\u5982\uFF1A\u6309\u4E0A\u9762\u7684\u65B9\u6848\u7EE7\u7EED\u5B9E\u73B0\uFF0C\u5148\u6838\u5BF9\u5F53\u524D\u6587\u4EF6\u72B6\u6001\u548C\u672A\u5B8C\u6210\u6B65\u9AA4",
|
|
21345
|
+
context: "\u4E0B\u4E00\u6761\u6D88\u606F\u4F1A\u81EA\u52A8\u643A\u5E26\u8FD1\u671F\u5BF9\u8BDD\u3001\u6700\u65B0\u65B9\u6848\u3001\u8BA1\u5212\u548C\u53D8\u66F4\u6587\u4EF6\u3002",
|
|
21346
|
+
running: "\u82E5\u5F53\u524D\u56DE\u5408\u4ECD\u5728\u8FD0\u884C\uFF0C\u8BF7\u5148\u505C\u6B62\u6216\u7B49\u5F85\u5B8C\u6210\u3002",
|
|
21347
|
+
cancel: "\u53D6\u6D88",
|
|
21348
|
+
confirm: "\u786E\u8BA4\u63A5\u529B",
|
|
21349
|
+
submitting: "\u6B63\u5728\u5207\u6362\u2026"
|
|
21350
|
+
} : {
|
|
21351
|
+
title: "Hand off this task",
|
|
21352
|
+
description: "Keep this task, transcript, and working tree; only change the Harness that continues it.",
|
|
21353
|
+
noteLabel: "Handoff note (optional)",
|
|
21354
|
+
intentLabel: "Handoff mode",
|
|
21355
|
+
intentOptions: { continue: "Continue", "execute-plan": "Execute prior plan", review: "Independent review", reanalyze: "Reanalyze" },
|
|
21356
|
+
includeLabel: "Include",
|
|
21357
|
+
includeOptions: { conversation: "Recent conversation", plan: "Current plan", evidence: "Test and tool evidence", files: "File changes", unresolved: "Open issues" },
|
|
21358
|
+
notePlaceholder: "For example: implement the plan above; verify the working tree and remaining steps first",
|
|
21359
|
+
context: "Your next message will include recent context, the latest solution, plan, and changed files.",
|
|
21360
|
+
running: "If a turn is still running, stop it or wait for it to finish first.",
|
|
21361
|
+
cancel: "Cancel",
|
|
21362
|
+
confirm: "Hand off",
|
|
21363
|
+
submitting: "Switching\u2026"
|
|
21364
|
+
};
|
|
21365
|
+
}
|
|
21366
|
+
function setButtonChrome(button, primary = false) {
|
|
21367
|
+
button.style.height = "34px";
|
|
21368
|
+
button.style.padding = "0 14px";
|
|
21369
|
+
button.style.border = primary ? "1px solid color-mix(in srgb, #5b8cff 70%, CanvasText 30%)" : "1px solid color-mix(in srgb, CanvasText 18%, transparent)";
|
|
21370
|
+
button.style.borderRadius = "7px";
|
|
21371
|
+
button.style.background = primary ? "#4f7ff0" : "color-mix(in srgb, Canvas 94%, CanvasText 6%)";
|
|
21372
|
+
button.style.color = primary ? "white" : "CanvasText";
|
|
21373
|
+
button.style.font = "500 13px/1 system-ui, sans-serif";
|
|
21374
|
+
button.style.cursor = "pointer";
|
|
21375
|
+
}
|
|
21376
|
+
function mountRendererHarnessHandoff(composerId, onConfirm) {
|
|
21377
|
+
const dialog = document.createElement("dialog");
|
|
21378
|
+
dialog.id = `${composerId}-harness-handoff`;
|
|
21379
|
+
dialog.setAttribute("aria-modal", "true");
|
|
21380
|
+
dialog.style.width = "min(440px, calc(100vw - 32px))";
|
|
21381
|
+
dialog.style.boxSizing = "border-box";
|
|
21382
|
+
dialog.style.padding = "0";
|
|
21383
|
+
dialog.style.border = "1px solid color-mix(in srgb, CanvasText 18%, transparent)";
|
|
21384
|
+
dialog.style.borderRadius = "14px";
|
|
21385
|
+
dialog.style.background = "Canvas";
|
|
21386
|
+
dialog.style.color = "CanvasText";
|
|
21387
|
+
dialog.style.boxShadow = "0 24px 70px rgba(0, 0, 0, 0.42)";
|
|
21388
|
+
dialog.style.font = "13px/1.45 system-ui, sans-serif";
|
|
21389
|
+
dialog.style.zIndex = "2147483647";
|
|
21390
|
+
const frame = document.createElement("div");
|
|
21391
|
+
frame.style.display = "flex";
|
|
21392
|
+
frame.style.flexDirection = "column";
|
|
21393
|
+
frame.style.gap = "14px";
|
|
21394
|
+
frame.style.padding = "20px";
|
|
21395
|
+
const title = document.createElement("h2");
|
|
21396
|
+
title.style.margin = "0";
|
|
21397
|
+
title.style.font = "600 17px/1.25 system-ui, sans-serif";
|
|
21398
|
+
const description = document.createElement("p");
|
|
21399
|
+
description.style.margin = "-6px 0 0";
|
|
21400
|
+
description.style.opacity = "0.72";
|
|
21401
|
+
const route = document.createElement("div");
|
|
21402
|
+
route.style.display = "grid";
|
|
21403
|
+
route.style.gridTemplateColumns = "1fr auto 1fr";
|
|
21404
|
+
route.style.alignItems = "center";
|
|
21405
|
+
route.style.gap = "10px";
|
|
21406
|
+
route.style.padding = "12px";
|
|
21407
|
+
route.style.borderRadius = "10px";
|
|
21408
|
+
route.style.background = "color-mix(in srgb, CanvasText 6%, transparent)";
|
|
21409
|
+
const fromBox = document.createElement("div");
|
|
21410
|
+
const toBox = document.createElement("div");
|
|
21411
|
+
for (const box of [fromBox, toBox]) {
|
|
21412
|
+
box.style.display = "flex";
|
|
21413
|
+
box.style.alignItems = "center";
|
|
21414
|
+
box.style.gap = "8px";
|
|
21415
|
+
box.style.minWidth = "0";
|
|
21416
|
+
box.style.fontWeight = "600";
|
|
21417
|
+
}
|
|
21418
|
+
const arrow = document.createElement("span");
|
|
21419
|
+
arrow.textContent = "\u2192";
|
|
21420
|
+
arrow.setAttribute("aria-hidden", "true");
|
|
21421
|
+
arrow.style.opacity = "0.55";
|
|
21422
|
+
route.append(fromBox, arrow, toBox);
|
|
21423
|
+
const intentLabel = document.createElement("label");
|
|
21424
|
+
intentLabel.style.display = "flex";
|
|
21425
|
+
intentLabel.style.flexDirection = "column";
|
|
21426
|
+
intentLabel.style.gap = "7px";
|
|
21427
|
+
const intentLabelText = document.createElement("span");
|
|
21428
|
+
intentLabelText.style.fontWeight = "600";
|
|
21429
|
+
const intent = document.createElement("select");
|
|
21430
|
+
intent.style.height = "36px";
|
|
21431
|
+
intent.style.padding = "0 10px";
|
|
21432
|
+
intent.style.border = "1px solid color-mix(in srgb, CanvasText 20%, transparent)";
|
|
21433
|
+
intent.style.borderRadius = "8px";
|
|
21434
|
+
intent.style.background = "Canvas";
|
|
21435
|
+
intent.style.color = "CanvasText";
|
|
21436
|
+
intent.style.font = "13px/1 system-ui, sans-serif";
|
|
21437
|
+
for (const value of ["continue", "execute-plan", "review", "reanalyze"]) {
|
|
21438
|
+
const option = document.createElement("option");
|
|
21439
|
+
option.value = value;
|
|
21440
|
+
intent.append(option);
|
|
21441
|
+
}
|
|
21442
|
+
intentLabel.append(intentLabelText, intent);
|
|
21443
|
+
const includeSection = document.createElement("fieldset");
|
|
21444
|
+
includeSection.style.margin = "0";
|
|
21445
|
+
includeSection.style.padding = "10px 11px";
|
|
21446
|
+
includeSection.style.border = "1px solid color-mix(in srgb, CanvasText 14%, transparent)";
|
|
21447
|
+
includeSection.style.borderRadius = "8px";
|
|
21448
|
+
const includeLegend = document.createElement("legend");
|
|
21449
|
+
includeLegend.style.padding = "0 5px";
|
|
21450
|
+
includeLegend.style.fontWeight = "600";
|
|
21451
|
+
includeSection.append(includeLegend);
|
|
21452
|
+
const includeGrid = document.createElement("div");
|
|
21453
|
+
includeGrid.style.display = "grid";
|
|
21454
|
+
includeGrid.style.gridTemplateColumns = "repeat(2, minmax(0, 1fr))";
|
|
21455
|
+
includeGrid.style.gap = "8px 12px";
|
|
21456
|
+
const includeInputs = {};
|
|
21457
|
+
const includeLabels = {};
|
|
21458
|
+
for (const key of ["conversation", "plan", "evidence", "files", "unresolved"]) {
|
|
21459
|
+
const row = document.createElement("label");
|
|
21460
|
+
row.style.display = "flex";
|
|
21461
|
+
row.style.alignItems = "center";
|
|
21462
|
+
row.style.gap = "7px";
|
|
21463
|
+
const input = document.createElement("input");
|
|
21464
|
+
input.type = "checkbox";
|
|
21465
|
+
input.checked = true;
|
|
21466
|
+
const text = document.createTextNode("");
|
|
21467
|
+
row.append(input, text);
|
|
21468
|
+
includeGrid.append(row);
|
|
21469
|
+
includeInputs[key] = input;
|
|
21470
|
+
includeLabels[key] = text;
|
|
21471
|
+
}
|
|
21472
|
+
includeSection.append(includeGrid);
|
|
21473
|
+
const label = document.createElement("label");
|
|
21474
|
+
label.style.display = "flex";
|
|
21475
|
+
label.style.flexDirection = "column";
|
|
21476
|
+
label.style.gap = "7px";
|
|
21477
|
+
const labelText = document.createElement("span");
|
|
21478
|
+
labelText.style.fontWeight = "600";
|
|
21479
|
+
const note = document.createElement("textarea");
|
|
21480
|
+
note.rows = 3;
|
|
21481
|
+
note.maxLength = HARNESS_HANDOFF_NOTE_MAX_LENGTH;
|
|
21482
|
+
note.style.boxSizing = "border-box";
|
|
21483
|
+
note.style.width = "100%";
|
|
21484
|
+
note.style.minHeight = "76px";
|
|
21485
|
+
note.style.resize = "vertical";
|
|
21486
|
+
note.style.padding = "10px 11px";
|
|
21487
|
+
note.style.border = "1px solid color-mix(in srgb, CanvasText 20%, transparent)";
|
|
21488
|
+
note.style.borderRadius = "8px";
|
|
21489
|
+
note.style.outline = "none";
|
|
21490
|
+
note.style.background = "color-mix(in srgb, Canvas 96%, CanvasText 4%)";
|
|
21491
|
+
note.style.color = "CanvasText";
|
|
21492
|
+
note.style.font = "13px/1.45 system-ui, sans-serif";
|
|
21493
|
+
label.append(labelText, note);
|
|
21494
|
+
const context = document.createElement("p");
|
|
21495
|
+
context.style.margin = "0";
|
|
21496
|
+
context.style.padding = "9px 10px";
|
|
21497
|
+
context.style.borderRadius = "8px";
|
|
21498
|
+
context.style.background = "color-mix(in srgb, #5b8cff 10%, transparent)";
|
|
21499
|
+
const running = document.createElement("p");
|
|
21500
|
+
running.style.margin = "-8px 0 0";
|
|
21501
|
+
running.style.opacity = "0.65";
|
|
21502
|
+
const error51 = document.createElement("p");
|
|
21503
|
+
error51.setAttribute("role", "alert");
|
|
21504
|
+
error51.hidden = true;
|
|
21505
|
+
error51.style.margin = "-8px 0 0";
|
|
21506
|
+
error51.style.color = "#ef6666";
|
|
21507
|
+
const actions = document.createElement("div");
|
|
21508
|
+
actions.style.display = "flex";
|
|
21509
|
+
actions.style.justifyContent = "flex-end";
|
|
21510
|
+
actions.style.gap = "8px";
|
|
21511
|
+
const cancel = document.createElement("button");
|
|
21512
|
+
cancel.type = "button";
|
|
21513
|
+
setButtonChrome(cancel);
|
|
21514
|
+
const confirm = document.createElement("button");
|
|
21515
|
+
confirm.type = "button";
|
|
21516
|
+
setButtonChrome(confirm, true);
|
|
21517
|
+
actions.append(cancel, confirm);
|
|
21518
|
+
frame.append(title, description, route, intentLabel, includeSection, label, context, running, error51, actions);
|
|
21519
|
+
dialog.append(frame);
|
|
21520
|
+
document.body.append(dialog);
|
|
21521
|
+
let from = "pi";
|
|
21522
|
+
let to = "claude-code";
|
|
21523
|
+
let locale = "en";
|
|
21524
|
+
let submitting = false;
|
|
21525
|
+
const render = () => {
|
|
21526
|
+
const messages = rendererHarnessHandoffMessages(locale);
|
|
21527
|
+
title.textContent = messages.title;
|
|
21528
|
+
description.textContent = messages.description;
|
|
21529
|
+
labelText.textContent = messages.noteLabel;
|
|
21530
|
+
intentLabelText.textContent = messages.intentLabel;
|
|
21531
|
+
includeLegend.textContent = messages.includeLabel;
|
|
21532
|
+
for (const [value, text] of Object.entries(messages.intentOptions)) {
|
|
21533
|
+
const option = [...intent.options].find((candidate) => candidate.value === value);
|
|
21534
|
+
if (option) option.textContent = text;
|
|
21535
|
+
}
|
|
21536
|
+
for (const key of Object.keys(includeInputs)) includeLabels[key].data = messages.includeOptions[key];
|
|
21537
|
+
note.placeholder = messages.notePlaceholder;
|
|
21538
|
+
context.textContent = messages.context;
|
|
21539
|
+
running.textContent = messages.running;
|
|
21540
|
+
cancel.textContent = messages.cancel;
|
|
21541
|
+
confirm.textContent = submitting ? messages.submitting : messages.confirm;
|
|
21542
|
+
fromBox.replaceChildren(createRendererAgentIcon(from, 24), document.createTextNode(RENDERER_AGENT_LABELS[from]));
|
|
21543
|
+
toBox.replaceChildren(createRendererAgentIcon(to, 24), document.createTextNode(RENDERER_AGENT_LABELS[to]));
|
|
21544
|
+
note.disabled = submitting;
|
|
21545
|
+
intent.disabled = submitting;
|
|
21546
|
+
includeSection.disabled = submitting;
|
|
21547
|
+
cancel.disabled = submitting;
|
|
21548
|
+
confirm.disabled = submitting;
|
|
21549
|
+
cancel.style.cursor = submitting ? "not-allowed" : "pointer";
|
|
21550
|
+
confirm.style.cursor = submitting ? "wait" : "pointer";
|
|
21551
|
+
confirm.setAttribute("aria-busy", String(submitting));
|
|
21552
|
+
};
|
|
21553
|
+
const close = () => {
|
|
21554
|
+
if (submitting) return;
|
|
21555
|
+
if (dialog.open) dialog.close();
|
|
21556
|
+
};
|
|
21557
|
+
cancel.addEventListener("click", close);
|
|
21558
|
+
confirm.addEventListener("click", () => {
|
|
21559
|
+
if (submitting) return;
|
|
21560
|
+
onConfirm({
|
|
21561
|
+
from,
|
|
21562
|
+
to,
|
|
21563
|
+
note: note.value.trim(),
|
|
21564
|
+
intent: intent.value,
|
|
21565
|
+
includes: Object.fromEntries(Object.entries(includeInputs).map(([key, input]) => [key, input.checked]))
|
|
21566
|
+
});
|
|
21567
|
+
});
|
|
21568
|
+
note.addEventListener("keydown", (event) => {
|
|
21569
|
+
if ((event.ctrlKey || event.metaKey) && event.key === "Enter" && !submitting) {
|
|
21570
|
+
event.preventDefault();
|
|
21571
|
+
confirm.click();
|
|
21572
|
+
}
|
|
21573
|
+
});
|
|
21574
|
+
dialog.addEventListener("cancel", (event) => {
|
|
21575
|
+
if (submitting) event.preventDefault();
|
|
21576
|
+
});
|
|
21577
|
+
const control = {
|
|
21578
|
+
dialog,
|
|
21579
|
+
note,
|
|
21580
|
+
intent,
|
|
21581
|
+
includeInputs,
|
|
21582
|
+
confirm,
|
|
21583
|
+
cancel,
|
|
21584
|
+
error: error51,
|
|
21585
|
+
open(nextFrom, nextTo, nextLocale) {
|
|
21586
|
+
from = nextFrom;
|
|
21587
|
+
to = nextTo;
|
|
21588
|
+
locale = nextLocale;
|
|
21589
|
+
submitting = false;
|
|
21590
|
+
note.value = "";
|
|
21591
|
+
intent.value = "continue";
|
|
21592
|
+
for (const input of Object.values(includeInputs)) input.checked = true;
|
|
21593
|
+
error51.hidden = true;
|
|
21594
|
+
error51.textContent = "";
|
|
21595
|
+
render();
|
|
21596
|
+
if (!dialog.open) dialog.showModal();
|
|
21597
|
+
queueMicrotask(() => note.focus());
|
|
21598
|
+
},
|
|
21599
|
+
close,
|
|
21600
|
+
setSubmitting(nextSubmitting) {
|
|
21601
|
+
submitting = nextSubmitting;
|
|
21602
|
+
render();
|
|
21603
|
+
},
|
|
21604
|
+
showError(message) {
|
|
21605
|
+
error51.textContent = message;
|
|
21606
|
+
error51.hidden = false;
|
|
21607
|
+
},
|
|
21608
|
+
dispose() {
|
|
21609
|
+
submitting = false;
|
|
21610
|
+
if (dialog.open) dialog.close();
|
|
21611
|
+
dialog.remove();
|
|
21612
|
+
}
|
|
21613
|
+
};
|
|
21614
|
+
render();
|
|
21615
|
+
return control;
|
|
21616
|
+
}
|
|
21617
|
+
|
|
21280
21618
|
// src/native-ui/renderer-extension/src/renderer-composer-dom.ts
|
|
21281
21619
|
var CODEX_COMPOSER_SELECTOR = "[data-codex-composer-root]";
|
|
21282
21620
|
var EDITOR_SELECTOR = 'textarea, [contenteditable="true"], [role="textbox"]';
|
|
@@ -21564,7 +21902,7 @@ ${error51.stderrTail}`] : []
|
|
|
21564
21902
|
setNativeControlHidden(control.nativeContextUsageControl, false);
|
|
21565
21903
|
setNativeControlHidden(control.nativePermissionModeControl, hidePermissionMode);
|
|
21566
21904
|
}
|
|
21567
|
-
function mountComposerAgentControl(composer, composerId, sendButton, enabledAgents, onSelect, onDownload, onSelectCodexAccount, onOpenProviderPicker, onSelectModel, onSelectThinking, onSelectPermissionMode, onSelectCommand) {
|
|
21905
|
+
function mountComposerAgentControl(composer, composerId, sendButton, enabledAgents, onSelect, onDownload, onSelectCodexAccount, onOpenProviderPicker, onSelectModel, onSelectThinking, onSelectPermissionMode, onSelectCommand, onConfirmHandoff) {
|
|
21568
21906
|
const nativeModelControl = captureNativeControl(nativeModelControlForComposer(composer));
|
|
21569
21907
|
const nativeContextUsageControl = captureNativeControl(
|
|
21570
21908
|
nativeContextUsageControlForComposer(composer)
|
|
@@ -21592,6 +21930,7 @@ ${error51.stderrTail}`] : []
|
|
|
21592
21930
|
trailingActionAnchor(sendButton),
|
|
21593
21931
|
onSelectCommand
|
|
21594
21932
|
);
|
|
21933
|
+
const harnessHandoff = mountRendererHarnessHandoff(composerId, onConfirmHandoff);
|
|
21595
21934
|
const permissionParent = nativePermissionModeControl?.element.parentElement;
|
|
21596
21935
|
if (permissionParent && nativePermissionModeControl && nativePermissionModeControlVerified) {
|
|
21597
21936
|
permissionParent.insertBefore(permissionModePicker.root, nativePermissionModeControl.element);
|
|
@@ -21613,6 +21952,7 @@ ${error51.stderrTail}`] : []
|
|
|
21613
21952
|
credits,
|
|
21614
21953
|
usage: null,
|
|
21615
21954
|
harnessCommands,
|
|
21955
|
+
harnessHandoff,
|
|
21616
21956
|
sendButton,
|
|
21617
21957
|
sendDisabledBeforeSwitch: null
|
|
21618
21958
|
};
|
|
@@ -21690,6 +22030,7 @@ ${error51.stderrTail}`] : []
|
|
|
21690
22030
|
control.usage?.dispose();
|
|
21691
22031
|
control.usage = null;
|
|
21692
22032
|
control.harnessCommands.dispose();
|
|
22033
|
+
control.harnessHandoff.dispose();
|
|
21693
22034
|
control.permissionModePicker.dispose();
|
|
21694
22035
|
control.modelPicker.dispose();
|
|
21695
22036
|
control.picker.dispose();
|
|
@@ -21705,6 +22046,10 @@ ${error51.stderrTail}`] : []
|
|
|
21705
22046
|
selectedAccountId: validOverrideAccountId ?? activeAccountId
|
|
21706
22047
|
};
|
|
21707
22048
|
}
|
|
22049
|
+
function codexAccountRouteOverride(accounts, accountId) {
|
|
22050
|
+
if (!accountId) return null;
|
|
22051
|
+
return accounts.find((account) => account.accountId === accountId)?.management === "isolated" ? accountId : null;
|
|
22052
|
+
}
|
|
21708
22053
|
var RendererCodexAccountState = class {
|
|
21709
22054
|
constructor(client) {
|
|
21710
22055
|
this.client = client;
|
|
@@ -22511,6 +22856,39 @@ ${error51.stderrTail}`] : []
|
|
|
22511
22856
|
};
|
|
22512
22857
|
}
|
|
22513
22858
|
|
|
22859
|
+
// src/native-ui/renderer-extension/src/renderer-integrations-client.ts
|
|
22860
|
+
var serverSchema = external_exports.object({
|
|
22861
|
+
id: external_exports.string(),
|
|
22862
|
+
name: external_exports.string(),
|
|
22863
|
+
transportType: external_exports.enum(["stdio", "streamable_http"]).optional(),
|
|
22864
|
+
command: external_exports.string().optional().default(""),
|
|
22865
|
+
args: external_exports.array(external_exports.string()).optional().default([]),
|
|
22866
|
+
env: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
22867
|
+
env_vars: external_exports.array(external_exports.string()).optional(),
|
|
22868
|
+
cwd: external_exports.string().nullable().optional(),
|
|
22869
|
+
url: external_exports.string().optional(),
|
|
22870
|
+
bearer_token_env_var: external_exports.string().optional(),
|
|
22871
|
+
http_headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
22872
|
+
env_http_headers: external_exports.record(external_exports.string(), external_exports.string()).optional(),
|
|
22873
|
+
enabled: external_exports.boolean(),
|
|
22874
|
+
scope: external_exports.enum(["global", "project"]),
|
|
22875
|
+
editable: external_exports.boolean(),
|
|
22876
|
+
effective: external_exports.boolean(),
|
|
22877
|
+
appliedSessions: external_exports.number()
|
|
22878
|
+
});
|
|
22879
|
+
var skillSchema = external_exports.object({ name: external_exports.string(), path: external_exports.string(), root: external_exports.string(), enabled: external_exports.boolean(), writable: external_exports.boolean(), scope: external_exports.enum(["global", "project"]), status: external_exports.string() });
|
|
22880
|
+
var snapshotSchema = external_exports.object({ harnessId: external_exports.string(), mcpSupported: external_exports.boolean(), skillsSupported: external_exports.boolean(), servers: external_exports.array(serverSchema), native: external_exports.array(external_exports.object({ sessionId: external_exports.string(), name: external_exports.string(), status: external_exports.string(), tools: external_exports.array(external_exports.string()) })), skills: external_exports.array(skillSchema), note: external_exports.string() });
|
|
22881
|
+
var catalogSchema = external_exports.object({ harnesses: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), available: external_exports.boolean(), mcp: external_exports.boolean(), skills: external_exports.boolean() })) });
|
|
22882
|
+
function createRendererIntegrationsClient(send) {
|
|
22883
|
+
return {
|
|
22884
|
+
integrationCatalog: async () => catalogSchema.parse(await send("codexhost/integrations/catalog", {})),
|
|
22885
|
+
listIntegrations: async (input) => snapshotSchema.parse(await send("codexhost/integrations/list", input)),
|
|
22886
|
+
saveMcp: (input) => send("codexhost/integrations/mcp/save", input),
|
|
22887
|
+
removeMcp: (input) => send("codexhost/integrations/mcp/remove", input),
|
|
22888
|
+
changeSkill: (input) => send("codexhost/integrations/skill/change", input)
|
|
22889
|
+
};
|
|
22890
|
+
}
|
|
22891
|
+
|
|
22514
22892
|
// src/native-ui/renderer-extension/src/renderer-session-import-client.ts
|
|
22515
22893
|
var RendererSessionImportUnavailableError = class extends Error {
|
|
22516
22894
|
constructor() {
|
|
@@ -22578,6 +22956,7 @@ ${error51.stderrTail}`] : []
|
|
|
22578
22956
|
var UPDATE_CHECK_METHOD = "codexhost/update/check";
|
|
22579
22957
|
var UPDATE_START_METHOD = "codexhost/update/start";
|
|
22580
22958
|
var UPDATE_STATUS_METHOD = "codexhost/update/status";
|
|
22959
|
+
var RUNTIME_VERSION_METHOD = "harness-mix/runtime/version";
|
|
22581
22960
|
var CODEX_ACCOUNT_LIST_METHOD = "codexhost/account/list";
|
|
22582
22961
|
var CODEX_ACCOUNT_REFRESH_METHOD = "codexhost/account/refresh";
|
|
22583
22962
|
var CODEX_ACCOUNT_CREATE_METHOD = "codexhost/account/create";
|
|
@@ -22691,6 +23070,7 @@ ${error51.stderrTail}`] : []
|
|
|
22691
23070
|
if (!Array.isArray(result) || !result.every((a) => a && typeof a.id === "string" && typeof a.name === "string" && typeof a.available === "boolean" && typeof a.lead === "boolean")) throw new Error("Invalid collaboration catalog");
|
|
22692
23071
|
return result;
|
|
22693
23072
|
},
|
|
23073
|
+
...createRendererIntegrationsClient((method, params) => manager.sendRequest(method, params)),
|
|
22694
23074
|
...createRendererSessionImportClient(
|
|
22695
23075
|
async (method, params) => manager.sendRequest(method, params)
|
|
22696
23076
|
),
|
|
@@ -22817,6 +23197,13 @@ ${error51.stderrTail}`] : []
|
|
|
22817
23197
|
);
|
|
22818
23198
|
return updateStatusResultSchema.parse(result);
|
|
22819
23199
|
},
|
|
23200
|
+
async readCurrentVersion() {
|
|
23201
|
+
const result = await manager.sendRequest(RUNTIME_VERSION_METHOD, {});
|
|
23202
|
+
if (!isRecord6(result) || typeof result.version !== "string" || !result.version.trim()) {
|
|
23203
|
+
throw new Error("Invalid Harness Mix version response");
|
|
23204
|
+
}
|
|
23205
|
+
return { version: result.version.trim() };
|
|
23206
|
+
},
|
|
22820
23207
|
async inspectCodexAccountUsage(input) {
|
|
22821
23208
|
const result = await manager.sendRequest(
|
|
22822
23209
|
"codexhost/account/usage/inspect",
|
|
@@ -22879,6 +23266,10 @@ ${error51.stderrTail}`] : []
|
|
|
22879
23266
|
);
|
|
22880
23267
|
return codexAccountLoginCancelResultSchema.parse(result);
|
|
22881
23268
|
},
|
|
23269
|
+
async logoutCodexAccount() {
|
|
23270
|
+
const result = await manager.sendRequest("codexhost/account/logout", {});
|
|
23271
|
+
return codexAccountMutationResultSchema.parse(result);
|
|
23272
|
+
},
|
|
22882
23273
|
subscribeCodexAccountLogin(listener) {
|
|
22883
23274
|
const notifications = notificationTarget(source);
|
|
22884
23275
|
if (!notifications?.addNotificationCallback) {
|
|
@@ -23710,8 +24101,66 @@ ${error51.stderrTail}`] : []
|
|
|
23710
24101
|
}
|
|
23711
24102
|
|
|
23712
24103
|
// src/native-ui/renderer-extension/src/renderer-harness-mentions.ts
|
|
23713
|
-
var entryKey = (entry) =>
|
|
23714
|
-
|
|
24104
|
+
var entryKey = (entry) => {
|
|
24105
|
+
if (entry.kind === "agent") return `agent:${entry.id}`;
|
|
24106
|
+
if (entry.kind === "session") return `session:${entry.id}`;
|
|
24107
|
+
return `codex:${entry.id}`;
|
|
24108
|
+
};
|
|
24109
|
+
var entryLiteral = (entry) => {
|
|
24110
|
+
if (entry.kind === "agent") return `#${entry.id} `;
|
|
24111
|
+
if (entry.kind === "session") return `#[${entry.title.replace(/[\[\]()\r\n]/g, "")}](harness-mix://session/${entry.id}) `;
|
|
24112
|
+
return "";
|
|
24113
|
+
};
|
|
24114
|
+
var AGENT_DESCRIPTIONS = {
|
|
24115
|
+
codex: "\u5B98\u65B9 Codex CLI \u539F\u751F\u534F\u540C",
|
|
24116
|
+
"codex-cli": "\u5B98\u65B9 Codex CLI \u539F\u751F\u534F\u540C",
|
|
24117
|
+
pi: "\u8F7B\u91CF\u5FEB\u901F\u539F\u751F\u7F16\u7801 Harness",
|
|
24118
|
+
omp: "Oh My Pi \u589E\u5F3A\u7248\u4EA4\u4E92 Harness",
|
|
24119
|
+
dsh: "DeepSeek Harness \u539F\u751F\u6DF1\u5EA6\u63A8\u7406\u4E0E\u5DE5\u5177\u8C03\u7528",
|
|
24120
|
+
"deepseek-harness": "DeepSeek Harness \u539F\u751F\u6DF1\u5EA6\u63A8\u7406\u4E0E\u5DE5\u5177\u8C03\u7528",
|
|
24121
|
+
claude: "Anthropic Claude Code \u539F\u751F\u534F\u540C",
|
|
24122
|
+
"claude-code": "Anthropic Claude Code \u539F\u751F\u534F\u540C",
|
|
24123
|
+
antigravity: "Google Antigravity 2.0 \u539F\u751F Harness",
|
|
24124
|
+
opencode: "\u5F00\u6E90\u81EA\u4E3B\u7F16\u7801\u667A\u80FD\u4F53",
|
|
24125
|
+
grok: "xAI Grok \u539F\u751F\u534F\u540C\u4E0E\u6DF1\u5EA6\u63A8\u7406",
|
|
24126
|
+
openclaw: "OpenClaw \u4E2A\u4EBA AI \u81EA\u4E3B\u667A\u80FD\u4F53",
|
|
24127
|
+
hermes: "Nous Research Hermes \u81EA\u6211\u8FED\u4EE3\u667A\u80FD\u4F53",
|
|
24128
|
+
qoder: "Qoder \u7F16\u7A0B\u52A9\u624B",
|
|
24129
|
+
codebuddy: "CodeBuddy \u539F\u751F\u667A\u80FD\u4F53",
|
|
24130
|
+
zcode: "ZCode \u81EA\u52A8\u5316\u5DE5\u7A0B\u4EE3\u7406",
|
|
24131
|
+
trae: "Trae \u539F\u751F\u534F\u540C",
|
|
24132
|
+
"kiro-cli": "Kiro CLI \u667A\u80FD\u7F16\u7A0B",
|
|
24133
|
+
"cursor-cli": "Cursor CLI \u539F\u751F\u534F\u540C"
|
|
24134
|
+
};
|
|
24135
|
+
var AGENT_COLORS = {
|
|
24136
|
+
codex: "#3b82f6",
|
|
24137
|
+
"codex-cli": "#3b82f6",
|
|
24138
|
+
"codex-harness": "#3b82f6",
|
|
24139
|
+
grok: "#8b5cf6",
|
|
24140
|
+
opencode: "#a855f7",
|
|
24141
|
+
"claude-code": "#ea580c",
|
|
24142
|
+
claude: "#ea580c",
|
|
24143
|
+
antigravity: "#0284c7",
|
|
24144
|
+
pi: "#d97706",
|
|
24145
|
+
omp: "#d97706",
|
|
24146
|
+
dsh: "#2563eb",
|
|
24147
|
+
"deepseek-harness": "#2563eb",
|
|
24148
|
+
"kiro-cli": "#db2777",
|
|
24149
|
+
openclaw: "#dc2626",
|
|
24150
|
+
hermes: "#059669",
|
|
24151
|
+
qoder: "#7c3aed",
|
|
24152
|
+
codebuddy: "#0891b2",
|
|
24153
|
+
zcode: "#4f46e5",
|
|
24154
|
+
trae: "#0284c7",
|
|
24155
|
+
"cursor-cli": "#475569"
|
|
24156
|
+
};
|
|
24157
|
+
function getAgentColor(agentId) {
|
|
24158
|
+
const normalized = agentId.toLowerCase();
|
|
24159
|
+
for (const [key, color] of Object.entries(AGENT_COLORS)) {
|
|
24160
|
+
if (normalized.includes(key)) return color;
|
|
24161
|
+
}
|
|
24162
|
+
return "#6366f1";
|
|
24163
|
+
}
|
|
23715
24164
|
function installHarnessMentions(load) {
|
|
23716
24165
|
const menu = document.createElement("div");
|
|
23717
24166
|
menu.dataset.harnessMixMentions = "true";
|
|
@@ -23721,24 +24170,74 @@ ${error51.stderrTail}`] : []
|
|
|
23721
24170
|
menu.hidden = true;
|
|
23722
24171
|
document.body.append(menu);
|
|
23723
24172
|
const style = document.createElement("style");
|
|
23724
|
-
style.textContent =
|
|
24173
|
+
style.textContent = `
|
|
24174
|
+
[data-harness-mix-has-mentions="true"] {
|
|
24175
|
+
display: flex !important;
|
|
24176
|
+
flex-wrap: wrap !important;
|
|
24177
|
+
align-items: baseline !important;
|
|
24178
|
+
gap: 8px !important;
|
|
24179
|
+
}
|
|
24180
|
+
[data-harness-mix-has-mentions="true"] > [data-harness-mix-selected-mentions] {
|
|
24181
|
+
display: inline-flex !important;
|
|
24182
|
+
align-items: center !important;
|
|
24183
|
+
gap: 8px !important;
|
|
24184
|
+
flex-wrap: wrap !important;
|
|
24185
|
+
vertical-align: baseline !important;
|
|
24186
|
+
line-height: 1.5 !important;
|
|
24187
|
+
}
|
|
24188
|
+
[data-harness-mix-has-mentions="true"] > textarea,
|
|
24189
|
+
[data-harness-mix-has-mentions="true"] > [contenteditable="true"],
|
|
24190
|
+
[data-harness-mix-has-mentions="true"] > .ProseMirror {
|
|
24191
|
+
display: inline-block !important;
|
|
24192
|
+
width: auto !important;
|
|
24193
|
+
min-width: 60px !important;
|
|
24194
|
+
flex: 1 1 auto !important;
|
|
24195
|
+
margin: 0 !important;
|
|
24196
|
+
padding: 0 !important;
|
|
24197
|
+
vertical-align: baseline !important;
|
|
24198
|
+
}
|
|
24199
|
+
[data-harness-mix-mention-badge]:hover {
|
|
24200
|
+
background: rgba(128, 128, 128, 0.14) !important;
|
|
24201
|
+
}
|
|
24202
|
+
`;
|
|
23725
24203
|
document.head.append(style);
|
|
23726
24204
|
let activeComposer = null;
|
|
23727
24205
|
let generation = 0, disposed = false, selected = 0;
|
|
23728
24206
|
let editor = null;
|
|
23729
24207
|
let range = null, start = 0, end = 0;
|
|
23730
24208
|
let entries = [];
|
|
23731
|
-
let matches = []
|
|
24209
|
+
let matches = [];
|
|
24210
|
+
let activeKind = "agent";
|
|
24211
|
+
let canDelegate = true;
|
|
23732
24212
|
const catalogs = /* @__PURE__ */ new Map();
|
|
23733
24213
|
const badges = /* @__PURE__ */ new Map();
|
|
23734
|
-
const
|
|
24214
|
+
const sessionSelections = /* @__PURE__ */ new Map();
|
|
24215
|
+
const conversationKeyFor = (target) => {
|
|
24216
|
+
const composer = target.closest("[data-codex-composer-root]");
|
|
24217
|
+
if (!composer) return target.__harnessMixEditorKey || "default";
|
|
24218
|
+
const portal = composer.querySelector("[data-above-composer-portal]");
|
|
24219
|
+
const portalId = portal?.getAttribute("data-above-composer-conversation-id");
|
|
24220
|
+
if (portalId) return `conversation:${portalId}`;
|
|
24221
|
+
const convEl = composer.closest("[data-conversation-id]") || composer.querySelector("[data-conversation-id]");
|
|
24222
|
+
const convId = convEl?.getAttribute("data-conversation-id");
|
|
24223
|
+
if (convId) return `conversation:${convId}`;
|
|
24224
|
+
const draftId = composer.getAttribute("data-composer-draft-id");
|
|
24225
|
+
if (draftId) return `draft:${draftId}`;
|
|
24226
|
+
const all = composer.querySelectorAll('textarea, [contenteditable="true"]');
|
|
24227
|
+
if (all.length > 1) {
|
|
24228
|
+
const index = Array.from(all).indexOf(target);
|
|
24229
|
+
return `editor:${index >= 0 ? index : "default"}`;
|
|
24230
|
+
}
|
|
24231
|
+
return "default";
|
|
24232
|
+
};
|
|
23735
24233
|
const selectionsFor = (target) => {
|
|
23736
|
-
|
|
23737
|
-
|
|
23738
|
-
|
|
23739
|
-
|
|
24234
|
+
const key = conversationKeyFor(target);
|
|
24235
|
+
let perSession = sessionSelections.get(key);
|
|
24236
|
+
if (!perSession) {
|
|
24237
|
+
perSession = /* @__PURE__ */ new Map();
|
|
24238
|
+
sessionSelections.set(key, perSession);
|
|
23740
24239
|
}
|
|
23741
|
-
return
|
|
24240
|
+
return perSession;
|
|
23742
24241
|
};
|
|
23743
24242
|
const replaceSelectionWith = (target, replacement) => {
|
|
23744
24243
|
if (target instanceof HTMLTextAreaElement) {
|
|
@@ -23772,66 +24271,76 @@ ${error51.stderrTail}`] : []
|
|
|
23772
24271
|
return true;
|
|
23773
24272
|
};
|
|
23774
24273
|
const syncBadges = (target) => {
|
|
23775
|
-
for (const [old, strip2] of badges)
|
|
23776
|
-
|
|
23777
|
-
|
|
23778
|
-
|
|
24274
|
+
for (const [old, strip2] of badges) {
|
|
24275
|
+
if (!old.isConnected) {
|
|
24276
|
+
strip2.remove();
|
|
24277
|
+
badges.delete(old);
|
|
24278
|
+
catalogs.delete(old);
|
|
24279
|
+
}
|
|
23779
24280
|
}
|
|
23780
|
-
const perEditor =
|
|
24281
|
+
const perEditor = selectionsFor(target);
|
|
23781
24282
|
const hasAny = !!perEditor && perEditor.size > 0;
|
|
23782
24283
|
let strip = badges.get(target);
|
|
24284
|
+
const container = target.parentElement;
|
|
24285
|
+
if (hasAny) {
|
|
24286
|
+
container?.setAttribute("data-harness-mix-has-mentions", "true");
|
|
24287
|
+
} else {
|
|
24288
|
+
container?.removeAttribute("data-harness-mix-has-mentions");
|
|
24289
|
+
}
|
|
23783
24290
|
if (!strip && !hasAny) return;
|
|
23784
24291
|
if (!strip) {
|
|
23785
24292
|
strip = document.createElement("div");
|
|
23786
24293
|
strip.dataset.harnessMixSelectedMentions = "true";
|
|
23787
24294
|
strip.setAttribute("aria-label", "\u5DF2\u63D0\u53CA\u7684\u534F\u4F5C\u8005");
|
|
23788
|
-
strip.style.cssText = "display:flex;gap:
|
|
23789
|
-
target.
|
|
24295
|
+
strip.style.cssText = "display:inline-flex;align-items:center;gap:8px;flex-wrap:wrap;padding:0;margin:0 4px 0 0;vertical-align:baseline;line-height:normal;";
|
|
24296
|
+
target.before(strip);
|
|
23790
24297
|
badges.set(target, strip);
|
|
23791
24298
|
}
|
|
24299
|
+
const newKeys = perEditor ? [...perEditor.values()].map((e) => e.kind === "agent" ? `agent:${e.id}` : e.kind === "session" ? `session:${e.id}` : `codex:${e.id}`).join(",") : "";
|
|
24300
|
+
const currentKeys = [...strip.querySelectorAll("[data-harness-mix-mention-badge]")].map((b) => b.dataset.harnessMixMentionBadge).join(",");
|
|
24301
|
+
if (strip.isConnected && currentKeys === newKeys && strip.style.display === (hasAny ? "inline-flex" : "none")) {
|
|
24302
|
+
return;
|
|
24303
|
+
}
|
|
23792
24304
|
strip.replaceChildren();
|
|
23793
|
-
strip.style.display = hasAny ? "flex" : "none";
|
|
24305
|
+
strip.style.display = hasAny ? "inline-flex" : "none";
|
|
23794
24306
|
if (!perEditor) return;
|
|
23795
24307
|
for (const entry of perEditor.values()) {
|
|
23796
|
-
const badge = document.createElement("
|
|
23797
|
-
badge.
|
|
23798
|
-
badge.
|
|
23799
|
-
const
|
|
23800
|
-
|
|
23801
|
-
|
|
23802
|
-
|
|
23803
|
-
|
|
23804
|
-
)
|
|
23805
|
-
|
|
23806
|
-
remove.type = "button";
|
|
23807
|
-
remove.dataset.harnessMixMentionRemove = entry.kind === "agent" ? `agent:${entry.id}` : `session:${entry.id}`;
|
|
23808
|
-
const removeLabel = entry.kind === "agent" ? `\u79FB\u9664 ${entry.name}` : `\u79FB\u9664\u4F1A\u8BDD ${entry.title}`;
|
|
23809
|
-
remove.setAttribute("aria-label", removeLabel);
|
|
23810
|
-
remove.title = removeLabel;
|
|
23811
|
-
remove.textContent = "\xD7";
|
|
23812
|
-
remove.style.cssText = "border:0;background:transparent;color:inherit;cursor:pointer;padding:0 0 0 2px;margin:0;font:600 14px/1 inherit;opacity:.55;line-height:1;display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;border-radius:50%";
|
|
23813
|
-
remove.addEventListener("mouseenter", () => {
|
|
23814
|
-
remove.style.opacity = "1";
|
|
23815
|
-
remove.style.background = "#8883";
|
|
24308
|
+
const badge = document.createElement("button");
|
|
24309
|
+
badge.type = "button";
|
|
24310
|
+
badge.dataset.harnessMixMentionBadge = entry.kind === "agent" ? `agent:${entry.id}` : entry.kind === "session" ? `session:${entry.id}` : `codex:${entry.id}`;
|
|
24311
|
+
const removeLabel = entry.kind === "agent" ? `\u79FB\u9664\u534F\u4F5C Agent ${entry.name}` : entry.kind === "session" ? `\u79FB\u9664\u5386\u53F2\u4F1A\u8BDD ${entry.title}` : `\u79FB\u9664 Codex \u9009\u9879 ${entry.title}`;
|
|
24312
|
+
badge.setAttribute("aria-label", removeLabel);
|
|
24313
|
+
badge.title = `${removeLabel}\uFF08\u70B9\u51FB\u79FB\u9664\uFF09`;
|
|
24314
|
+
const agentColor = entry.kind === "agent" ? getAgentColor(entry.id) : entry.kind === "codex" ? "#0ea5e9" : "#8b5cf6";
|
|
24315
|
+
badge.style.cssText = `display:inline-flex;align-items:center;gap:4px;padding:1px 5px;border:0;border-radius:4px;background:transparent;color:${agentColor};font:500 15px/1.4 system-ui,-apple-system,BlinkMacSystemFont,sans-serif;user-select:none;cursor:pointer;transition:background 0.15s,opacity 0.15s;`;
|
|
24316
|
+
badge.addEventListener("mouseenter", () => {
|
|
24317
|
+
badge.style.background = "rgba(128,128,128,0.12)";
|
|
23816
24318
|
});
|
|
23817
|
-
|
|
23818
|
-
|
|
23819
|
-
remove.style.background = "transparent";
|
|
24319
|
+
badge.addEventListener("mouseleave", () => {
|
|
24320
|
+
badge.style.background = "transparent";
|
|
23820
24321
|
});
|
|
23821
|
-
|
|
24322
|
+
badge.addEventListener("click", (event) => {
|
|
23822
24323
|
event.preventDefault();
|
|
23823
24324
|
event.stopPropagation();
|
|
23824
|
-
|
|
23825
|
-
remove.addEventListener("click", (event) => {
|
|
23826
|
-
event.preventDefault();
|
|
23827
|
-
event.stopPropagation();
|
|
23828
|
-
const live = selections.get(target);
|
|
23829
|
-
if (!live) return;
|
|
24325
|
+
const live = selectionsFor(target);
|
|
23830
24326
|
live.delete(entryKey(entry));
|
|
23831
24327
|
syncBadges(target);
|
|
24328
|
+
target.focus();
|
|
23832
24329
|
});
|
|
23833
|
-
|
|
23834
|
-
|
|
24330
|
+
const iconWrap = document.createElement("span");
|
|
24331
|
+
iconWrap.style.cssText = "display:inline-flex;align-items:center;gap:4px;";
|
|
24332
|
+
if (entry.kind === "agent" || entry.kind === "session") {
|
|
24333
|
+
iconWrap.append(
|
|
24334
|
+
collaborationIcon(entry.kind === "agent" ? entry.id : entry.harnessId, entry.kind === "agent" ? entry.name : entry.title, 16),
|
|
24335
|
+
document.createTextNode(entry.kind === "agent" ? entry.name : entry.title)
|
|
24336
|
+
);
|
|
24337
|
+
} else {
|
|
24338
|
+
const iconSpan = document.createElement("span");
|
|
24339
|
+
iconSpan.textContent = entry.icon || "\u{1F4CC}";
|
|
24340
|
+
iconSpan.style.cssText = "font-size:14px;line-height:1;";
|
|
24341
|
+
iconWrap.append(iconSpan, document.createTextNode(entry.title));
|
|
24342
|
+
}
|
|
24343
|
+
badge.append(iconWrap);
|
|
23835
24344
|
strip.append(badge);
|
|
23836
24345
|
}
|
|
23837
24346
|
};
|
|
@@ -23839,14 +24348,33 @@ ${error51.stderrTail}`] : []
|
|
|
23839
24348
|
generation++;
|
|
23840
24349
|
menu.hidden = true;
|
|
23841
24350
|
activeComposer?.removeAttribute("data-harness-mix-mention-active");
|
|
24351
|
+
activeComposer?.removeAttribute("data-harness-mix-show-native");
|
|
23842
24352
|
activeComposer = null;
|
|
23843
24353
|
};
|
|
23844
24354
|
const choose = (entry) => {
|
|
23845
|
-
if (!editor
|
|
24355
|
+
if (!editor) return;
|
|
24356
|
+
if (entry.kind === "agent" && (!entry.available || !canDelegate)) return;
|
|
24357
|
+
if (entry.kind === "session" && entry.running === true) return;
|
|
23846
24358
|
const target = editor;
|
|
23847
24359
|
close();
|
|
23848
24360
|
target.focus();
|
|
23849
24361
|
replaceSelectionWith(target, "");
|
|
24362
|
+
if (entry.kind === "codex") {
|
|
24363
|
+
const nativeOverlay = document.querySelector('[data-composer-overlay-floating-ui="true"]');
|
|
24364
|
+
if (nativeOverlay) {
|
|
24365
|
+
const buttons = [...nativeOverlay.querySelectorAll('button, [role="option"]')];
|
|
24366
|
+
const match = buttons.find((b) => (b.innerText || b.textContent || "").includes(entry.title));
|
|
24367
|
+
if (match) {
|
|
24368
|
+
match.click();
|
|
24369
|
+
return;
|
|
24370
|
+
}
|
|
24371
|
+
}
|
|
24372
|
+
if (entry.action === "files") {
|
|
24373
|
+
const fileInput = document.querySelector('input[type="file"]');
|
|
24374
|
+
fileInput?.click();
|
|
24375
|
+
}
|
|
24376
|
+
return;
|
|
24377
|
+
}
|
|
23850
24378
|
const perEditor = selectionsFor(target);
|
|
23851
24379
|
const key = entryKey(entry);
|
|
23852
24380
|
if (perEditor.has(key)) perEditor.delete(key);
|
|
@@ -23858,16 +24386,21 @@ ${error51.stderrTail}`] : []
|
|
|
23858
24386
|
const tabs = document.createElement("div");
|
|
23859
24387
|
tabs.setAttribute("role", "tablist");
|
|
23860
24388
|
tabs.style.cssText = "display:flex;gap:4px;padding:2px 2px 6px;border-bottom:1px solid #8882;margin-bottom:4px";
|
|
23861
|
-
|
|
24389
|
+
const tabDefinitions = [
|
|
24390
|
+
{ kind: "agent", label: "Agents" },
|
|
24391
|
+
{ kind: "session", label: "\u4F1A\u8BDD" }
|
|
24392
|
+
];
|
|
24393
|
+
for (const tabInfo of tabDefinitions) {
|
|
23862
24394
|
const tab = document.createElement("button");
|
|
23863
24395
|
tab.type = "button";
|
|
23864
24396
|
tab.setAttribute("role", "tab");
|
|
23865
|
-
tab.setAttribute("aria-selected", String(activeKind === kind));
|
|
23866
|
-
|
|
23867
|
-
tab.
|
|
24397
|
+
tab.setAttribute("aria-selected", String(activeKind === tabInfo.kind));
|
|
24398
|
+
const count = matches.filter((entry) => entry.kind === tabInfo.kind).length;
|
|
24399
|
+
tab.textContent = `${tabInfo.label} ${count}`;
|
|
24400
|
+
tab.style.cssText = `border:0;border-radius:6px;padding:6px 10px;color:inherit;font:inherit;cursor:pointer;background:${activeKind === tabInfo.kind ? "#8882" : "transparent"};transition:background .15s;`;
|
|
23868
24401
|
tab.addEventListener("mousedown", (event) => event.preventDefault());
|
|
23869
24402
|
tab.addEventListener("click", () => {
|
|
23870
|
-
activeKind = kind;
|
|
24403
|
+
activeKind = tabInfo.kind;
|
|
23871
24404
|
selected = 0;
|
|
23872
24405
|
render();
|
|
23873
24406
|
});
|
|
@@ -23890,7 +24423,7 @@ ${error51.stderrTail}`] : []
|
|
|
23890
24423
|
row.type = "button";
|
|
23891
24424
|
row.setAttribute("role", "option");
|
|
23892
24425
|
row.setAttribute("aria-selected", String(index === selected));
|
|
23893
|
-
const available = entry.kind === "agent" ? entry.available && canDelegate : entry.running !== true;
|
|
24426
|
+
const available = entry.kind === "agent" ? entry.available && canDelegate : entry.kind === "session" ? entry.running !== true : true;
|
|
23894
24427
|
row.disabled = !available;
|
|
23895
24428
|
row.id = `harness-mention-option-${index}`;
|
|
23896
24429
|
row.style.cssText = `display:flex;align-items:center;gap:8px;width:100%;text-align:left;border:0;border-radius:7px;padding:7px 8px;color:inherit;font:inherit;cursor:pointer;background:${index === selected ? "#8882" : "transparent"};opacity:${available ? 1 : 0.45}`;
|
|
@@ -23898,15 +24431,24 @@ ${error51.stderrTail}`] : []
|
|
|
23898
24431
|
copy.style.cssText = "display:flex;flex-direction:column;gap:3px;flex:1;min-width:0";
|
|
23899
24432
|
const name = document.createElement("span");
|
|
23900
24433
|
name.textContent = entry.kind === "agent" ? entry.name : entry.title;
|
|
23901
|
-
name.style.cssText = "overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
24434
|
+
name.style.cssText = "overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:500;";
|
|
23902
24435
|
const detail = document.createElement("span");
|
|
23903
|
-
detail.textContent = entry.kind === "agent" ?
|
|
24436
|
+
detail.textContent = entry.kind === "agent" ? AGENT_DESCRIPTIONS[entry.id] || entry.description || `#${entry.id}` : entry.kind === "session" ? entry.cwd : entry.description || entry.category;
|
|
23904
24437
|
detail.style.cssText = "font-size:11px;opacity:.55;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
|
|
23905
24438
|
copy.append(name, detail);
|
|
23906
24439
|
const status = document.createElement("span");
|
|
23907
|
-
status.textContent = entry.kind === "agent" ? entry.available ? "\u53EF\u534F\u4F5C" : "\u672A\u5C31\u7EEA" : entry.running ? "\u8FD0\u884C\u4E2D" : "\u5F15\u7528";
|
|
24440
|
+
status.textContent = entry.kind === "agent" ? entry.available ? "\u53EF\u534F\u4F5C" : "\u672A\u5C31\u7EEA" : entry.kind === "session" ? entry.running ? "\u8FD0\u884C\u4E2D" : "\u5F15\u7528" : entry.category || "\u539F\u751F";
|
|
23908
24441
|
status.style.cssText = "font-size:11px;opacity:.6";
|
|
23909
|
-
|
|
24442
|
+
let iconEl;
|
|
24443
|
+
if (entry.kind === "agent" || entry.kind === "session") {
|
|
24444
|
+
iconEl = collaborationIcon(entry.kind === "agent" ? entry.id : entry.harnessId, name.textContent || "", 18);
|
|
24445
|
+
} else {
|
|
24446
|
+
const iconSpan = document.createElement("span");
|
|
24447
|
+
iconSpan.textContent = entry.icon || "\u{1F4CE}";
|
|
24448
|
+
iconSpan.style.cssText = "font-size:16px;line-height:1;width:18px;text-align:center;";
|
|
24449
|
+
iconEl = iconSpan;
|
|
24450
|
+
}
|
|
24451
|
+
row.append(iconEl, copy, status);
|
|
23910
24452
|
row.addEventListener("mousedown", (event) => event.preventDefault());
|
|
23911
24453
|
row.addEventListener("click", () => choose(entry));
|
|
23912
24454
|
list.append(row);
|
|
@@ -23946,7 +24488,7 @@ ${error51.stderrTail}`] : []
|
|
|
23946
24488
|
end = prefix.length;
|
|
23947
24489
|
range = caret.cloneRange();
|
|
23948
24490
|
}
|
|
23949
|
-
const match = /(?:^|[\s,。;:])
|
|
24491
|
+
const match = /(?:^|[\s,。;:])#([\p{L}\p{N}_-]*)$/u.exec(prefix);
|
|
23950
24492
|
if (!match) {
|
|
23951
24493
|
close();
|
|
23952
24494
|
return;
|
|
@@ -23961,15 +24503,18 @@ ${error51.stderrTail}`] : []
|
|
|
23961
24503
|
syncBadges(editable);
|
|
23962
24504
|
const query = match[1].toLowerCase();
|
|
23963
24505
|
canDelegate = catalog.canDelegate !== false;
|
|
23964
|
-
|
|
23965
|
-
|
|
23966
|
-
|
|
23967
|
-
|
|
23968
|
-
|
|
24506
|
+
const matchingAgents = catalog.agents.filter((a) => a.id.toLowerCase().includes(query) || a.name.toLowerCase().includes(query)).map((a) => ({ kind: "agent", title: a.name, ...a }));
|
|
24507
|
+
const matchingSessions = catalog.sessions.filter((s) => `${s.title} ${s.cwd}`.toLowerCase().includes(query)).map((s) => ({ kind: "session", ...s }));
|
|
24508
|
+
matches = [...matchingAgents, ...matchingSessions];
|
|
24509
|
+
if (query && !matchingAgents.length && matchingSessions.length) {
|
|
24510
|
+
activeKind = "session";
|
|
24511
|
+
} else {
|
|
24512
|
+
activeKind = "agent";
|
|
24513
|
+
}
|
|
23969
24514
|
entries = matches.filter((entry) => entry.kind === activeKind);
|
|
23970
|
-
selected = Math.max(0, entries.findIndex((entry) => entry.kind === "agent" ? entry.available && canDelegate : entry.running !== true));
|
|
24515
|
+
selected = Math.max(0, entries.findIndex((entry) => entry.kind === "agent" ? entry.available && canDelegate : entry.kind === "session" ? entry.running !== true : true));
|
|
23971
24516
|
render();
|
|
23972
|
-
menu.hidden = !
|
|
24517
|
+
menu.hidden = !matches.length && catalog.canDelegate === void 0;
|
|
23973
24518
|
activeComposer?.removeAttribute("data-harness-mix-mention-active");
|
|
23974
24519
|
activeComposer = !menu.hidden ? editable.closest("[data-codex-composer-root]") : null;
|
|
23975
24520
|
activeComposer?.setAttribute("data-harness-mix-mention-active", "true");
|
|
@@ -23985,6 +24530,33 @@ ${error51.stderrTail}`] : []
|
|
|
23985
24530
|
}
|
|
23986
24531
|
};
|
|
23987
24532
|
const keydown = (event) => {
|
|
24533
|
+
const target = event.target;
|
|
24534
|
+
if (target instanceof HTMLElement && target.closest("[data-codex-composer-root]")) {
|
|
24535
|
+
const editable = target.closest('textarea,[contenteditable="true"]');
|
|
24536
|
+
if (editable && event.key === "Backspace") {
|
|
24537
|
+
let isAtStart = false;
|
|
24538
|
+
if (editable instanceof HTMLTextAreaElement) {
|
|
24539
|
+
isAtStart = editable.selectionStart === 0 && editable.selectionEnd === 0;
|
|
24540
|
+
} else {
|
|
24541
|
+
const sel = window.getSelection();
|
|
24542
|
+
if (sel && sel.isCollapsed && sel.rangeCount > 0) {
|
|
24543
|
+
const r = sel.getRangeAt(0);
|
|
24544
|
+
isAtStart = r.startOffset === 0 && (r.startContainer === editable || r.startContainer.textContent === "" || editable.firstChild === r.startContainer);
|
|
24545
|
+
}
|
|
24546
|
+
}
|
|
24547
|
+
if (isAtStart) {
|
|
24548
|
+
const perSession = selectionsFor(editable);
|
|
24549
|
+
if (perSession && perSession.size > 0) {
|
|
24550
|
+
const keys = [...perSession.keys()];
|
|
24551
|
+
const lastKey = keys[keys.length - 1];
|
|
24552
|
+
if (lastKey) perSession.delete(lastKey);
|
|
24553
|
+
syncBadges(editable);
|
|
24554
|
+
event.preventDefault();
|
|
24555
|
+
return;
|
|
24556
|
+
}
|
|
24557
|
+
}
|
|
24558
|
+
}
|
|
24559
|
+
}
|
|
23988
24560
|
if (menu.hidden || event.isComposing) return;
|
|
23989
24561
|
if (event.key === "Escape") {
|
|
23990
24562
|
close();
|
|
@@ -23993,9 +24565,12 @@ ${error51.stderrTail}`] : []
|
|
|
23993
24565
|
if (!["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight", "Enter", "Tab", "Escape"].includes(event.key)) return;
|
|
23994
24566
|
event.preventDefault();
|
|
23995
24567
|
event.stopImmediatePropagation();
|
|
23996
|
-
if (event.key === "
|
|
23997
|
-
|
|
23998
|
-
|
|
24568
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
24569
|
+
const kinds = ["agent", "session"];
|
|
24570
|
+
const curIdx = kinds.indexOf(activeKind);
|
|
24571
|
+
const nextIdx = (curIdx + (event.key === "ArrowRight" ? 1 : -1) + kinds.length) % kinds.length;
|
|
24572
|
+
const next = kinds[nextIdx];
|
|
24573
|
+
if (next) activeKind = next;
|
|
23999
24574
|
selected = 0;
|
|
24000
24575
|
render();
|
|
24001
24576
|
} else if (event.key === "Enter" || event.key === "Tab") {
|
|
@@ -24005,7 +24580,7 @@ ${error51.stderrTail}`] : []
|
|
|
24005
24580
|
for (let i = 0; i < entries.length; i++) {
|
|
24006
24581
|
selected = (selected + (event.key === "ArrowDown" ? 1 : -1) + entries.length) % entries.length;
|
|
24007
24582
|
const entry = entries[selected];
|
|
24008
|
-
if (entry && (entry.kind === "agent" ? entry.available && canDelegate : entry.running !== true)) break;
|
|
24583
|
+
if (entry && (entry.kind === "agent" ? entry.available && canDelegate : entry.kind === "session" ? entry.running !== true : true)) break;
|
|
24009
24584
|
}
|
|
24010
24585
|
render();
|
|
24011
24586
|
menu.querySelector('[role="option"][aria-selected="true"]')?.scrollIntoView({ block: "nearest" });
|
|
@@ -24020,7 +24595,7 @@ ${error51.stderrTail}`] : []
|
|
|
24020
24595
|
return root.querySelector('textarea,[contenteditable="true"]');
|
|
24021
24596
|
};
|
|
24022
24597
|
const injectMentionsInto = (target) => {
|
|
24023
|
-
const perEditor =
|
|
24598
|
+
const perEditor = selectionsFor(target);
|
|
24024
24599
|
if (!perEditor || perEditor.size === 0) return false;
|
|
24025
24600
|
const text = [...perEditor.values()].map(entryLiteral).join("");
|
|
24026
24601
|
if (!text) return false;
|
|
@@ -24029,16 +24604,18 @@ ${error51.stderrTail}`] : []
|
|
|
24029
24604
|
target.value = `${text}${target.value}`;
|
|
24030
24605
|
target.dispatchEvent(new Event("input", { bubbles: true }));
|
|
24031
24606
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
24032
|
-
|
|
24033
|
-
|
|
24034
|
-
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24607
|
+
} else {
|
|
24608
|
+
const selection = window.getSelection();
|
|
24609
|
+
const fresh = document.createRange();
|
|
24610
|
+
fresh.selectNodeContents(target);
|
|
24611
|
+
fresh.collapse(true);
|
|
24612
|
+
selection?.removeAllRanges();
|
|
24613
|
+
selection?.addRange(fresh);
|
|
24614
|
+
document.dispatchEvent(new Event("selectionchange"));
|
|
24615
|
+
document.execCommand("insertText", false, text);
|
|
24616
|
+
}
|
|
24617
|
+
perEditor.clear();
|
|
24618
|
+
syncBadges(target);
|
|
24042
24619
|
return true;
|
|
24043
24620
|
};
|
|
24044
24621
|
const onSubmitCapture = (event) => {
|
|
@@ -24049,26 +24626,70 @@ ${error51.stderrTail}`] : []
|
|
|
24049
24626
|
if (!editorEl.closest("[data-codex-composer-root]")) return;
|
|
24050
24627
|
injectMentionsInto(editorEl);
|
|
24051
24628
|
};
|
|
24629
|
+
const onFocusIn = (event) => {
|
|
24630
|
+
const target = event.target;
|
|
24631
|
+
if (target instanceof HTMLElement && target.closest("[data-codex-composer-root]")) {
|
|
24632
|
+
const editable = target.closest('textarea,[contenteditable="true"]');
|
|
24633
|
+
if (editable) syncBadges(editable);
|
|
24634
|
+
}
|
|
24635
|
+
};
|
|
24636
|
+
const observer = new MutationObserver((mutations) => {
|
|
24637
|
+
let shouldSync = false;
|
|
24638
|
+
for (const m of mutations) {
|
|
24639
|
+
if (m.target instanceof Element) {
|
|
24640
|
+
if (m.target.closest("[data-harness-mix-selected-mentions]") || m.target.closest("[data-harness-mix-mentions]")) {
|
|
24641
|
+
continue;
|
|
24642
|
+
}
|
|
24643
|
+
}
|
|
24644
|
+
shouldSync = true;
|
|
24645
|
+
break;
|
|
24646
|
+
}
|
|
24647
|
+
if (!shouldSync) return;
|
|
24648
|
+
const composers = document.querySelectorAll("[data-codex-composer-root]");
|
|
24649
|
+
for (const composer of composers) {
|
|
24650
|
+
const el = findEditorIn(composer);
|
|
24651
|
+
if (el) syncBadges(el);
|
|
24652
|
+
}
|
|
24653
|
+
});
|
|
24654
|
+
try {
|
|
24655
|
+
observer.observe(document.body, {
|
|
24656
|
+
childList: true,
|
|
24657
|
+
subtree: true,
|
|
24658
|
+
attributes: true,
|
|
24659
|
+
attributeFilter: ["data-above-composer-conversation-id", "data-conversation-id", "data-composer-draft-id"]
|
|
24660
|
+
});
|
|
24661
|
+
} catch {
|
|
24662
|
+
}
|
|
24052
24663
|
document.addEventListener("input", input, true);
|
|
24053
24664
|
document.addEventListener("keydown", keydown, true);
|
|
24054
24665
|
document.addEventListener("mousedown", outside);
|
|
24055
24666
|
document.addEventListener("submit", onSubmitCapture, true);
|
|
24667
|
+
document.addEventListener("focusin", onFocusIn, true);
|
|
24056
24668
|
window.addEventListener("blur", close);
|
|
24057
|
-
return {
|
|
24058
|
-
|
|
24059
|
-
|
|
24060
|
-
|
|
24061
|
-
|
|
24062
|
-
|
|
24063
|
-
|
|
24064
|
-
|
|
24065
|
-
|
|
24066
|
-
|
|
24067
|
-
|
|
24068
|
-
|
|
24069
|
-
|
|
24070
|
-
|
|
24071
|
-
|
|
24669
|
+
return {
|
|
24670
|
+
sync(target) {
|
|
24671
|
+
syncBadges(target);
|
|
24672
|
+
},
|
|
24673
|
+
dispose() {
|
|
24674
|
+
disposed = true;
|
|
24675
|
+
close();
|
|
24676
|
+
menu.remove();
|
|
24677
|
+
style.remove();
|
|
24678
|
+
observer.disconnect();
|
|
24679
|
+
for (const el of document.querySelectorAll("[data-harness-mix-has-mentions]")) {
|
|
24680
|
+
el.removeAttribute("data-harness-mix-has-mentions");
|
|
24681
|
+
}
|
|
24682
|
+
for (const strip of badges.values()) strip.remove();
|
|
24683
|
+
badges.clear();
|
|
24684
|
+
catalogs.clear();
|
|
24685
|
+
document.removeEventListener("input", input, true);
|
|
24686
|
+
document.removeEventListener("keydown", keydown, true);
|
|
24687
|
+
document.removeEventListener("mousedown", outside);
|
|
24688
|
+
document.removeEventListener("submit", onSubmitCapture, true);
|
|
24689
|
+
document.removeEventListener("focusin", onFocusIn, true);
|
|
24690
|
+
window.removeEventListener("blur", close);
|
|
24691
|
+
}
|
|
24692
|
+
};
|
|
24072
24693
|
}
|
|
24073
24694
|
|
|
24074
24695
|
// src/native-ui/renderer-extension/src/renderer-collab-cards.ts
|
|
@@ -24398,6 +25019,9 @@ ${error51.stderrTail}`] : []
|
|
|
24398
25019
|
|
|
24399
25020
|
// src/native-ui/renderer-extension/src/settings/accounts-list.ts
|
|
24400
25021
|
var resetDetailsSequence = 0;
|
|
25022
|
+
function isCodexAccountAuthenticated(account) {
|
|
25023
|
+
return account.authenticated ?? Boolean(account.email);
|
|
25024
|
+
}
|
|
24401
25025
|
function accountPlanLabel(planType) {
|
|
24402
25026
|
if (!planType || planType === "unknown") return null;
|
|
24403
25027
|
if (planType === "free") return "Free";
|
|
@@ -24530,7 +25154,7 @@ ${error51.stderrTail}`] : []
|
|
|
24530
25154
|
activate.addEventListener("click", input.onActivate);
|
|
24531
25155
|
actions.append(activate);
|
|
24532
25156
|
}
|
|
24533
|
-
if (!account
|
|
25157
|
+
if (!isCodexAccountAuthenticated(account)) {
|
|
24534
25158
|
const signIn = document2.createElement("button");
|
|
24535
25159
|
signIn.type = "button";
|
|
24536
25160
|
signIn.className = "settings-account-action";
|
|
@@ -24540,6 +25164,16 @@ ${error51.stderrTail}`] : []
|
|
|
24540
25164
|
signIn.addEventListener("click", input.onSignIn);
|
|
24541
25165
|
actions.append(signIn);
|
|
24542
25166
|
}
|
|
25167
|
+
if (account.management === "native" && isCodexAccountAuthenticated(account) && input.onSignOut) {
|
|
25168
|
+
const signOut = document2.createElement("button");
|
|
25169
|
+
signOut.type = "button";
|
|
25170
|
+
signOut.className = "settings-account-action";
|
|
25171
|
+
signOut.textContent = messages.accountSignOut;
|
|
25172
|
+
signOut.dataset.accountFocus = `${account.accountId}:logout`;
|
|
25173
|
+
signOut.disabled = input.actionsDisabled;
|
|
25174
|
+
signOut.addEventListener("click", input.onSignOut);
|
|
25175
|
+
actions.append(signOut);
|
|
25176
|
+
}
|
|
24543
25177
|
if (!account.isDefault) {
|
|
24544
25178
|
const remove = document2.createElement("button");
|
|
24545
25179
|
remove.type = "button";
|
|
@@ -24904,12 +25538,12 @@ ${error51.stderrTail}`] : []
|
|
|
24904
25538
|
const restoreFocus = accountListFocusRestorer(list, search);
|
|
24905
25539
|
body.replaceChildren();
|
|
24906
25540
|
status.textContent = loginMessage ?? "";
|
|
24907
|
-
connectedCount.textContent = String(accounts.filter(
|
|
25541
|
+
connectedCount.textContent = String(accounts.filter(isCodexAccountAuthenticated).length);
|
|
24908
25542
|
search.disabled = login !== null || loginStartingAccountId !== null;
|
|
24909
25543
|
for (const [display, button] of displayButtons) {
|
|
24910
25544
|
button.setAttribute("aria-pressed", String(display === usageDisplay));
|
|
24911
25545
|
}
|
|
24912
|
-
refreshUsage.disabled = (!getClient()?.inspectCodexAccountUsage || !accounts.some(
|
|
25546
|
+
refreshUsage.disabled = (!getClient()?.inspectCodexAccountUsage || !accounts.some(isCodexAccountAuthenticated)) && !getClient()?.listHarnessAccounts || harnessAccounts?.refreshing === true || [...usageByAccountId.values()].some((usage) => usage.status === "loading") || accountBusy();
|
|
24913
25547
|
const query = search.value.trim().toLocaleLowerCase();
|
|
24914
25548
|
harnessAccounts?.update(query, usageDisplay);
|
|
24915
25549
|
const visibleAccounts = accounts.filter(
|
|
@@ -24924,6 +25558,7 @@ ${error51.stderrTail}`] : []
|
|
|
24924
25558
|
emptyRow.append(emptyCell);
|
|
24925
25559
|
body.append(emptyRow);
|
|
24926
25560
|
}
|
|
25561
|
+
add.hidden = false;
|
|
24927
25562
|
add.disabled = accountBusy();
|
|
24928
25563
|
for (const account of visibleAccounts) {
|
|
24929
25564
|
body.append(
|
|
@@ -24936,6 +25571,7 @@ ${error51.stderrTail}`] : []
|
|
|
24936
25571
|
resetExpanded: expandedResetAccounts.has(account.accountId),
|
|
24937
25572
|
onActivate: () => mutate(() => client().activateCodexAccount({ accountId: account.accountId })),
|
|
24938
25573
|
onSignIn: () => startLogin(account.accountId),
|
|
25574
|
+
...account.management === "native" && getClient()?.logoutCodexAccount ? { onSignOut: () => signOut() } : {},
|
|
24939
25575
|
onDelete: () => deleteAccount(account.accountId),
|
|
24940
25576
|
onRetry: () => {
|
|
24941
25577
|
usageByAccountId.delete(account.accountId);
|
|
@@ -25009,7 +25645,7 @@ ${error51.stderrTail}`] : []
|
|
|
25009
25645
|
};
|
|
25010
25646
|
const loadUsage = (nextAccounts) => {
|
|
25011
25647
|
const inspect = getClient()?.inspectCodexAccountUsage;
|
|
25012
|
-
const signedIn = nextAccounts.filter(
|
|
25648
|
+
const signedIn = nextAccounts.filter(isCodexAccountAuthenticated);
|
|
25013
25649
|
const keep = new Set(signedIn.map((account) => account.accountId));
|
|
25014
25650
|
for (const accountId of [...usageByAccountId.keys()]) {
|
|
25015
25651
|
if (!keep.has(accountId)) usageByAccountId.delete(accountId);
|
|
@@ -25120,6 +25756,27 @@ ${error51.stderrTail}`] : []
|
|
|
25120
25756
|
}
|
|
25121
25757
|
});
|
|
25122
25758
|
};
|
|
25759
|
+
const signOut = () => {
|
|
25760
|
+
const logout = getClient()?.logoutCodexAccount;
|
|
25761
|
+
if (!logout || accountBusy()) return;
|
|
25762
|
+
if (document2.defaultView?.confirm?.(messages.accountSignOutConfirm) === false) return;
|
|
25763
|
+
accountActivating = true;
|
|
25764
|
+
loginMessage = messages.accountSigningOut;
|
|
25765
|
+
render();
|
|
25766
|
+
void context.runLatest(() => logout(), {
|
|
25767
|
+
success(result) {
|
|
25768
|
+
accountActivating = false;
|
|
25769
|
+
loginMessage = messages.accountSignedOut;
|
|
25770
|
+
usageByAccountId.clear();
|
|
25771
|
+
setAccounts([result.account]);
|
|
25772
|
+
},
|
|
25773
|
+
failure(error51) {
|
|
25774
|
+
accountActivating = false;
|
|
25775
|
+
loginMessage = errorMessage(error51, messages.accountSignOutFailed);
|
|
25776
|
+
render();
|
|
25777
|
+
}
|
|
25778
|
+
});
|
|
25779
|
+
};
|
|
25123
25780
|
const createAndLogin = () => {
|
|
25124
25781
|
if (accountBusy()) return;
|
|
25125
25782
|
accountCreating = true;
|
|
@@ -26030,6 +26687,1717 @@ ${error51.stderrTail}`] : []
|
|
|
26030
26687
|
}
|
|
26031
26688
|
}
|
|
26032
26689
|
|
|
26690
|
+
// src/native-ui/renderer-extension/src/settings/integrations-page.ts
|
|
26691
|
+
function createIntegrationSettingsPage(kind, messages, getClient) {
|
|
26692
|
+
const zh = messages.locale === "zh-CN";
|
|
26693
|
+
const tr = (cn, en) => zh ? cn : en;
|
|
26694
|
+
return Object.freeze({
|
|
26695
|
+
id: kind,
|
|
26696
|
+
label: kind === "mcp" ? "MCP" : "Skills",
|
|
26697
|
+
icon: kind === "mcp" ? "connections" : "session-import",
|
|
26698
|
+
mount(context) {
|
|
26699
|
+
const { content } = context;
|
|
26700
|
+
const lifecycle = { signal: context.signal, runLatest: context.runLatest, get disposed() {
|
|
26701
|
+
return context.signal.aborted;
|
|
26702
|
+
} };
|
|
26703
|
+
const doc = content.ownerDocument;
|
|
26704
|
+
const el = (tag, text = "") => {
|
|
26705
|
+
const node = doc.createElement(tag);
|
|
26706
|
+
node.textContent = text;
|
|
26707
|
+
return node;
|
|
26708
|
+
};
|
|
26709
|
+
const title = el("h2", kind === "mcp" ? "MCP" : "Skills");
|
|
26710
|
+
const note = el("p", kind === "mcp" ? tr("\u6309 Harness \u7BA1\u7406 MCP \u670D\u52A1\u5668\u3002\u914D\u7F6E\u4F1A\u5728\u4E0B\u6B21\u6253\u5F00\u539F\u751F\u4F1A\u8BDD\u65F6\u4F20\u5165\u3002", "Manage MCP servers per Harness. Configuration is passed to the next native session.") : tr("\u628A\u6280\u80FD\u6587\u4EF6\u6216\u5B8C\u6574\u6280\u80FD\u6587\u4EF6\u5939\u62D6\u5230\u8FD9\u91CC\uFF0C\u76F4\u63A5\u5B89\u88C5\u5230\u539F\u751F\u6280\u80FD\u76EE\u5F55\u3002", "Drop a skill file or a complete skill folder here to install it in the native skill directory."));
|
|
26711
|
+
note.className = "settings-page-description";
|
|
26712
|
+
const controls = el("div");
|
|
26713
|
+
controls.className = "settings-integrations-controls";
|
|
26714
|
+
const harness = el("select");
|
|
26715
|
+
harness.setAttribute("aria-label", "Harness");
|
|
26716
|
+
const level = el("select");
|
|
26717
|
+
level.setAttribute("aria-label", tr("\u4F5C\u7528\u8303\u56F4", "Scope"));
|
|
26718
|
+
for (const [value, label] of [["global", tr("\u5168\u5C40", "Global")], ["project", tr("\u9879\u76EE", "Project")]]) {
|
|
26719
|
+
const option = el("option", label);
|
|
26720
|
+
option.value = value;
|
|
26721
|
+
level.append(option);
|
|
26722
|
+
}
|
|
26723
|
+
const cwd = el("input");
|
|
26724
|
+
cwd.placeholder = tr("\u9879\u76EE\u7EDD\u5BF9\u8DEF\u5F84", "Absolute project path");
|
|
26725
|
+
cwd.setAttribute("aria-label", cwd.placeholder);
|
|
26726
|
+
cwd.hidden = true;
|
|
26727
|
+
const refreshButton = el("button", tr("\u5237\u65B0", "Refresh"));
|
|
26728
|
+
refreshButton.type = "button";
|
|
26729
|
+
refreshButton.className = "settings-command-button settings-command-button--secondary";
|
|
26730
|
+
const status = el("p");
|
|
26731
|
+
status.className = "settings-integrations-status";
|
|
26732
|
+
status.setAttribute("role", "status");
|
|
26733
|
+
status.setAttribute("aria-live", "polite");
|
|
26734
|
+
const body = el("div");
|
|
26735
|
+
body.className = "settings-integrations-body";
|
|
26736
|
+
let busy = false;
|
|
26737
|
+
const selectedScope = () => ({ harnessId: harness.value, scope: level.value === "project" ? "project" : "global", ...level.value === "project" ? { cwd: cwd.value.trim() } : {} });
|
|
26738
|
+
const client = () => {
|
|
26739
|
+
const value = getClient();
|
|
26740
|
+
if (!value) throw new Error(tr("Host \u5C1A\u672A\u8FDE\u63A5\uFF0C\u8BF7\u8FDE\u63A5\u540E\u5237\u65B0\u3002", "Host is not connected. Connect and refresh."));
|
|
26741
|
+
return value;
|
|
26742
|
+
};
|
|
26743
|
+
const fail = (error51) => {
|
|
26744
|
+
status.textContent = error51 instanceof Error ? error51.message : String(error51);
|
|
26745
|
+
status.setAttribute("role", "alert");
|
|
26746
|
+
};
|
|
26747
|
+
const setBusy = (value) => {
|
|
26748
|
+
busy = value;
|
|
26749
|
+
content.dataset.integrationBusy = String(value);
|
|
26750
|
+
for (const node of content.querySelectorAll("button, input, select, textarea")) node.disabled = value || node.dataset.readonly === "true";
|
|
26751
|
+
};
|
|
26752
|
+
const button = (label, action, primary = false) => {
|
|
26753
|
+
const node = el("button", label);
|
|
26754
|
+
node.type = "button";
|
|
26755
|
+
node.className = `settings-command-button ${primary ? "settings-command-button--primary" : "settings-command-button--secondary"}`;
|
|
26756
|
+
node.addEventListener("click", action, { signal: lifecycle.signal });
|
|
26757
|
+
return node;
|
|
26758
|
+
};
|
|
26759
|
+
const mutate = async (operation) => {
|
|
26760
|
+
if (busy) return;
|
|
26761
|
+
const selected = selectedScope();
|
|
26762
|
+
setBusy(true);
|
|
26763
|
+
status.textContent = tr("\u6B63\u5728\u4FDD\u5B58\u2026", "Saving\u2026");
|
|
26764
|
+
status.setAttribute("role", "status");
|
|
26765
|
+
try {
|
|
26766
|
+
await operation(client(), selected);
|
|
26767
|
+
if (!lifecycle.disposed) {
|
|
26768
|
+
setBusy(false);
|
|
26769
|
+
await refresh();
|
|
26770
|
+
status.textContent = tr("\u5DF2\u4FDD\u5B58\uFF0C\u4E0B\u6B21\u6253\u5F00\u539F\u751F\u4F1A\u8BDD\u65F6\u751F\u6548\u3002", "Saved. Applies on the next native session open.");
|
|
26771
|
+
}
|
|
26772
|
+
} catch (error51) {
|
|
26773
|
+
if (!lifecycle.disposed) {
|
|
26774
|
+
setBusy(false);
|
|
26775
|
+
fail(error51);
|
|
26776
|
+
}
|
|
26777
|
+
}
|
|
26778
|
+
};
|
|
26779
|
+
const mutateCustom = async (operation) => {
|
|
26780
|
+
if (busy) return;
|
|
26781
|
+
setBusy(true);
|
|
26782
|
+
status.textContent = tr("\u6B63\u5728\u4FDD\u5B58\u2026", "Saving\u2026");
|
|
26783
|
+
status.setAttribute("role", "status");
|
|
26784
|
+
try {
|
|
26785
|
+
const message = await operation(client());
|
|
26786
|
+
if (!lifecycle.disposed) {
|
|
26787
|
+
setBusy(false);
|
|
26788
|
+
await refresh();
|
|
26789
|
+
status.textContent = message || tr("\u5DF2\u4FDD\u5B58\uFF0C\u4E0B\u6B21\u6253\u5F00\u539F\u751F\u4F1A\u8BDD\u65F6\u751F\u6548\u3002", "Saved. Applies on the next native session open.");
|
|
26790
|
+
}
|
|
26791
|
+
} catch (error51) {
|
|
26792
|
+
if (!lifecycle.disposed) {
|
|
26793
|
+
setBusy(false);
|
|
26794
|
+
fail(error51);
|
|
26795
|
+
}
|
|
26796
|
+
}
|
|
26797
|
+
};
|
|
26798
|
+
const bytesToBase64 = (buffer) => {
|
|
26799
|
+
const bytes = new Uint8Array(buffer);
|
|
26800
|
+
let binary = "";
|
|
26801
|
+
for (let offset = 0; offset < bytes.length; offset += 32768) binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
|
|
26802
|
+
return btoa(binary);
|
|
26803
|
+
};
|
|
26804
|
+
const fileFromEntry = (entry) => new Promise((resolve, reject) => entry.file?.(resolve, reject));
|
|
26805
|
+
const entriesFromReader = async (entry) => {
|
|
26806
|
+
const reader = entry.createReader?.();
|
|
26807
|
+
if (!reader) return [];
|
|
26808
|
+
const all = [];
|
|
26809
|
+
while (true) {
|
|
26810
|
+
const batch = await new Promise((resolve, reject) => reader.readEntries(resolve, reject));
|
|
26811
|
+
if (!batch.length) break;
|
|
26812
|
+
all.push(...batch);
|
|
26813
|
+
}
|
|
26814
|
+
return all;
|
|
26815
|
+
};
|
|
26816
|
+
const walkEntry = async (entry, prefix, output) => {
|
|
26817
|
+
if (entry.isFile) {
|
|
26818
|
+
output.push({ path: `${prefix}${entry.name}`, file: await fileFromEntry(entry) });
|
|
26819
|
+
return;
|
|
26820
|
+
}
|
|
26821
|
+
if (entry.isDirectory) for (const child of await entriesFromReader(entry)) await walkEntry(child, `${prefix}${entry.name}/`, output);
|
|
26822
|
+
};
|
|
26823
|
+
const parseDroppedSkills = async (incoming) => {
|
|
26824
|
+
if (!incoming.length) throw new Error(tr("\u6CA1\u6709\u8BFB\u53D6\u5230\u6587\u4EF6\u3002", "No files were read."));
|
|
26825
|
+
if (incoming.length > 500 || incoming.reduce((sum, item) => sum + item.file.size, 0) > 10 * 1024 * 1024) throw new Error(tr("\u6280\u80FD\u8D85\u8FC7 10 MB \u6216 500 \u4E2A\u6587\u4EF6\u3002", "Skill exceeds 10 MB or 500 files."));
|
|
26826
|
+
let normalized = incoming;
|
|
26827
|
+
if (incoming.length === 1) {
|
|
26828
|
+
if (!/\.md$/i.test(incoming[0].file.name)) throw new Error(tr("\u5355\u6587\u4EF6\u6280\u80FD\u5FC5\u987B\u662F Markdown \u6587\u4EF6\u3002", "A single-file skill must be a Markdown file."));
|
|
26829
|
+
normalized = [{ path: "SKILL.md", file: incoming[0].file }];
|
|
26830
|
+
} else {
|
|
26831
|
+
const paths = incoming.map((item) => item.path.replaceAll("\\", "/"));
|
|
26832
|
+
const first = paths[0].split("/")[0];
|
|
26833
|
+
if (first && paths.every((value) => value.startsWith(`${first}/`))) normalized = incoming.map((item, index) => ({ ...item, path: paths[index].slice(first.length + 1) }));
|
|
26834
|
+
if (!normalized.some((item) => item.path === "SKILL.md")) throw new Error(tr("\u6280\u80FD\u6587\u4EF6\u5939\u6839\u76EE\u5F55\u5FC5\u987B\u5305\u542B SKILL.md\u3002", "The skill folder root must contain SKILL.md."));
|
|
26835
|
+
}
|
|
26836
|
+
const skillFile = normalized.find((item) => item.path === "SKILL.md");
|
|
26837
|
+
const markdown = await skillFile.file.text();
|
|
26838
|
+
const declared = markdown.match(/^---[\s\S]*?^name:\s*["']?([^\r\n"']+)/m)?.[1]?.trim();
|
|
26839
|
+
const fallback = incoming.length === 1 ? incoming[0].file.name.replace(/\.md$/i, "") : incoming[0].path.split(/[\\/]/)[0];
|
|
26840
|
+
const name = (declared || fallback || "skill").toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
26841
|
+
if (!name) throw new Error(tr("\u65E0\u6CD5\u4ECE\u6587\u4EF6\u4E2D\u786E\u5B9A\u6709\u6548\u7684\u6280\u80FD\u540D\u79F0\u3002", "Could not determine a valid skill name from the files."));
|
|
26842
|
+
const files = [];
|
|
26843
|
+
for (const item of normalized) files.push({ path: item.path.replaceAll("\\", "/"), contentBase64: bytesToBase64(await item.file.arrayBuffer()) });
|
|
26844
|
+
return { name, files };
|
|
26845
|
+
};
|
|
26846
|
+
const field = (form, label, node) => {
|
|
26847
|
+
const wrap = el("label", label);
|
|
26848
|
+
wrap.className = "settings-integrations-field";
|
|
26849
|
+
wrap.append(node);
|
|
26850
|
+
form.append(wrap);
|
|
26851
|
+
};
|
|
26852
|
+
let targetEditServerName = void 0;
|
|
26853
|
+
const editInHarness = (harnessId, serverName) => {
|
|
26854
|
+
harness.value = harnessId;
|
|
26855
|
+
targetEditServerName = serverName;
|
|
26856
|
+
void refresh();
|
|
26857
|
+
};
|
|
26858
|
+
const toManagedForCopy = (s, newEnabled) => {
|
|
26859
|
+
const isHttp = s.transportType === "streamable_http" || typeof s.url === "string" && s.url.trim().length > 0;
|
|
26860
|
+
if (isHttp) {
|
|
26861
|
+
return {
|
|
26862
|
+
name: s.name,
|
|
26863
|
+
transportType: "streamable_http",
|
|
26864
|
+
enabled: newEnabled !== void 0 ? newEnabled : s.enabled ?? true,
|
|
26865
|
+
url: s.url || "",
|
|
26866
|
+
...s.bearer_token_env_var?.trim() ? { bearer_token_env_var: s.bearer_token_env_var.trim() } : {},
|
|
26867
|
+
...s.http_headers && Object.keys(s.http_headers).length ? { http_headers: s.http_headers } : {},
|
|
26868
|
+
...s.env_http_headers && Object.keys(s.env_http_headers).length ? { env_http_headers: s.env_http_headers } : {}
|
|
26869
|
+
};
|
|
26870
|
+
}
|
|
26871
|
+
return {
|
|
26872
|
+
name: s.name,
|
|
26873
|
+
transportType: "stdio",
|
|
26874
|
+
enabled: newEnabled !== void 0 ? newEnabled : s.enabled ?? true,
|
|
26875
|
+
command: s.command || "",
|
|
26876
|
+
...s.args?.length ? { args: s.args } : {},
|
|
26877
|
+
...s.env && Object.keys(s.env).length ? { env: s.env } : {},
|
|
26878
|
+
...s.env_vars?.length ? { env_vars: s.env_vars } : {},
|
|
26879
|
+
...s.cwd?.trim() ? { cwd: s.cwd.trim() } : {}
|
|
26880
|
+
};
|
|
26881
|
+
};
|
|
26882
|
+
const renderMcp = (snapshot) => {
|
|
26883
|
+
const toolbar = el("div");
|
|
26884
|
+
toolbar.className = "settings-integrations-toolbar";
|
|
26885
|
+
const search = el("input");
|
|
26886
|
+
search.type = "search";
|
|
26887
|
+
search.placeholder = tr("\u641C\u7D22 MCP \u670D\u52A1\u5668", "Search MCP servers");
|
|
26888
|
+
search.setAttribute("aria-label", search.placeholder);
|
|
26889
|
+
const list = el("div");
|
|
26890
|
+
list.className = "settings-integrations-list";
|
|
26891
|
+
const editor = el("div");
|
|
26892
|
+
editor.className = "settings-mcp-editor";
|
|
26893
|
+
editor.hidden = true;
|
|
26894
|
+
const showList = () => {
|
|
26895
|
+
editor.hidden = true;
|
|
26896
|
+
list.hidden = false;
|
|
26897
|
+
toolbar.hidden = false;
|
|
26898
|
+
};
|
|
26899
|
+
const showEditor = (server) => {
|
|
26900
|
+
editor.replaceChildren();
|
|
26901
|
+
editor.hidden = false;
|
|
26902
|
+
list.hidden = true;
|
|
26903
|
+
toolbar.hidden = true;
|
|
26904
|
+
const heading = el("div");
|
|
26905
|
+
heading.className = "settings-integrations-editor-head";
|
|
26906
|
+
const backBtn = button(tr("\u2039 \u8FD4\u56DE", "\u2039 Back"), showList);
|
|
26907
|
+
const title2 = el("h3", server ? tr(`\u66F4\u65B0 ${server.name} MCP`, `Update ${server.name} MCP`) : tr("\u8FDE\u63A5\u81F3\u81EA\u5B9A\u4E49 MCP", "Connect to a custom MCP"));
|
|
26908
|
+
const docsLink = el("a", tr("\u6587\u6863 \u2197", "Docs \u2197"));
|
|
26909
|
+
docsLink.href = "https://modelcontextprotocol.io/introduction";
|
|
26910
|
+
docsLink.target = "_blank";
|
|
26911
|
+
docsLink.rel = "noreferrer noopener";
|
|
26912
|
+
docsLink.className = "settings-mcp-docs-link";
|
|
26913
|
+
heading.append(backBtn, title2, docsLink);
|
|
26914
|
+
if (server?.editable) {
|
|
26915
|
+
const uninstallBtn = button(tr("\u5378\u8F7D", "Uninstall"), () => {
|
|
26916
|
+
void mutate(async (value, selected) => {
|
|
26917
|
+
await value.removeMcp({ ...selected, id: server.id });
|
|
26918
|
+
if (!lifecycle.disposed) showList();
|
|
26919
|
+
});
|
|
26920
|
+
});
|
|
26921
|
+
uninstallBtn.classList.add("settings-command-button--danger");
|
|
26922
|
+
heading.append(uninstallBtn);
|
|
26923
|
+
}
|
|
26924
|
+
const createTrashBtn = (onRemove) => {
|
|
26925
|
+
const btn = el("button");
|
|
26926
|
+
btn.type = "button";
|
|
26927
|
+
btn.className = "settings-mcp-icon-btn";
|
|
26928
|
+
btn.setAttribute("aria-label", tr("\u79FB\u9664", "Remove"));
|
|
26929
|
+
btn.title = tr("\u79FB\u9664", "Remove");
|
|
26930
|
+
btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>';
|
|
26931
|
+
btn.addEventListener("click", onRemove);
|
|
26932
|
+
return btn;
|
|
26933
|
+
};
|
|
26934
|
+
let transportType = server?.transportType === "streamable_http" || !!server?.url ? "streamable_http" : "stdio";
|
|
26935
|
+
const form = el("form");
|
|
26936
|
+
form.className = "settings-integrations-form";
|
|
26937
|
+
const basicCard = el("div");
|
|
26938
|
+
basicCard.className = "settings-integrations-form--card";
|
|
26939
|
+
const name = el("input");
|
|
26940
|
+
name.required = true;
|
|
26941
|
+
name.pattern = "[a-zA-Z0-9](?:[a-zA-Z0-9_]|-){0,63}";
|
|
26942
|
+
name.placeholder = "MCP server name";
|
|
26943
|
+
name.value = server?.name ?? "";
|
|
26944
|
+
field(basicCard, tr("\u540D\u79F0", "Name"), name);
|
|
26945
|
+
const typeField = el("div");
|
|
26946
|
+
typeField.className = "settings-integrations-field";
|
|
26947
|
+
typeField.append(el("span", tr("\u7C7B\u578B", "Type")));
|
|
26948
|
+
const toggle = el("div");
|
|
26949
|
+
toggle.className = "settings-mcp-type-toggle";
|
|
26950
|
+
const stdioBtn = el("button", "STDIO");
|
|
26951
|
+
stdioBtn.type = "button";
|
|
26952
|
+
stdioBtn.className = "settings-mcp-type-btn";
|
|
26953
|
+
const httpBtn = el("button", tr("\u6D41\u5F0F HTTP", "Streamable HTTP"));
|
|
26954
|
+
httpBtn.type = "button";
|
|
26955
|
+
httpBtn.className = "settings-mcp-type-btn";
|
|
26956
|
+
toggle.append(stdioBtn, httpBtn);
|
|
26957
|
+
typeField.append(toggle);
|
|
26958
|
+
basicCard.append(typeField);
|
|
26959
|
+
if (server) {
|
|
26960
|
+
const switchNotice = el("p", tr("\u5982\u9700\u5207\u6362 MCP \u670D\u52A1\u5668\u7C7B\u578B\uFF0C\u8BF7\u5148\u5378\u8F7D\u5F53\u524D\u914D\u7F6E\u3002", "If you would like to switch MCP server type, please uninstall first."));
|
|
26961
|
+
switchNotice.className = "settings-mcp-notice";
|
|
26962
|
+
basicCard.append(switchNotice);
|
|
26963
|
+
}
|
|
26964
|
+
const transportCard = el("div");
|
|
26965
|
+
transportCard.className = "settings-integrations-form--card settings-mcp-transport-card";
|
|
26966
|
+
const stdioSection = el("div");
|
|
26967
|
+
stdioSection.className = "settings-mcp-section";
|
|
26968
|
+
const command = el("input");
|
|
26969
|
+
command.placeholder = "openai-dev-mcp serve-sqlite";
|
|
26970
|
+
command.value = server?.command ?? "";
|
|
26971
|
+
field(stdioSection, tr("\u542F\u52A8\u547D\u4EE4", "Command to launch"), command);
|
|
26972
|
+
const argsField = el("div");
|
|
26973
|
+
argsField.className = "settings-integrations-field";
|
|
26974
|
+
argsField.append(el("span", tr("\u53C2\u6570", "Arguments")));
|
|
26975
|
+
const argsList = el("div");
|
|
26976
|
+
argsList.className = "settings-mcp-args";
|
|
26977
|
+
const addArg = (value = "") => {
|
|
26978
|
+
const row = el("div");
|
|
26979
|
+
row.className = "settings-mcp-arg";
|
|
26980
|
+
const input = el("input");
|
|
26981
|
+
input.value = value;
|
|
26982
|
+
input.placeholder = tr("\u547D\u4EE4\u53C2\u6570", "Command argument");
|
|
26983
|
+
row.append(input, createTrashBtn(() => row.remove()));
|
|
26984
|
+
argsList.append(row);
|
|
26985
|
+
};
|
|
26986
|
+
for (const value of server?.args?.length ? server.args : transportType === "stdio" ? [""] : []) addArg(value);
|
|
26987
|
+
const addArgBtn = button(tr("\uFF0B \u6DFB\u52A0\u53C2\u6570", "+ Add argument"), () => addArg());
|
|
26988
|
+
addArgBtn.classList.add("settings-mcp-add-arg");
|
|
26989
|
+
argsField.append(argsList, addArgBtn);
|
|
26990
|
+
stdioSection.append(argsField);
|
|
26991
|
+
const envField = el("div");
|
|
26992
|
+
envField.className = "settings-integrations-field";
|
|
26993
|
+
envField.append(el("span", tr("\u73AF\u5883\u53D8\u91CF", "Environment variables")));
|
|
26994
|
+
const envList = el("div");
|
|
26995
|
+
envList.className = "settings-mcp-args";
|
|
26996
|
+
const addEnv = (key = "", val = "") => {
|
|
26997
|
+
const row = el("div");
|
|
26998
|
+
row.className = "settings-mcp-kv-row";
|
|
26999
|
+
const keyInput = el("input");
|
|
27000
|
+
keyInput.value = key;
|
|
27001
|
+
keyInput.placeholder = tr("\u952E", "Key");
|
|
27002
|
+
const valInput = el("input");
|
|
27003
|
+
valInput.value = val;
|
|
27004
|
+
valInput.placeholder = tr("\u503C", "Value");
|
|
27005
|
+
row.append(keyInput, valInput, createTrashBtn(() => row.remove()));
|
|
27006
|
+
envList.append(row);
|
|
27007
|
+
};
|
|
27008
|
+
if (server?.env) {
|
|
27009
|
+
for (const [k, v] of Object.entries(server.env)) addEnv(k, String(v ?? ""));
|
|
27010
|
+
}
|
|
27011
|
+
const addEnvBtn = button(tr("\uFF0B \u6DFB\u52A0\u73AF\u5883\u53D8\u91CF", "+ Add environment variable"), () => addEnv());
|
|
27012
|
+
addEnvBtn.classList.add("settings-mcp-add-arg");
|
|
27013
|
+
envField.append(envList, addEnvBtn);
|
|
27014
|
+
stdioSection.append(envField);
|
|
27015
|
+
const envPassField = el("div");
|
|
27016
|
+
envPassField.className = "settings-integrations-field";
|
|
27017
|
+
envPassField.append(el("span", tr("\u73AF\u5883\u53D8\u91CF\u4F20\u9012", "Environment variable passthrough")));
|
|
27018
|
+
const envPassList = el("div");
|
|
27019
|
+
envPassList.className = "settings-mcp-args";
|
|
27020
|
+
const addEnvPass = (value = "") => {
|
|
27021
|
+
const row = el("div");
|
|
27022
|
+
row.className = "settings-mcp-arg";
|
|
27023
|
+
const input = el("input");
|
|
27024
|
+
input.value = value;
|
|
27025
|
+
input.placeholder = tr("\u53D8\u91CF\u540D\uFF0C\u4F8B\u5982\uFF1AGITHUB_TOKEN", "Variable name, e.g. GITHUB_TOKEN");
|
|
27026
|
+
row.append(input, createTrashBtn(() => row.remove()));
|
|
27027
|
+
envPassList.append(row);
|
|
27028
|
+
};
|
|
27029
|
+
if (server?.env_vars) {
|
|
27030
|
+
for (const v of server.env_vars) addEnvPass(v);
|
|
27031
|
+
}
|
|
27032
|
+
const addEnvPassBtn = button(tr("\uFF0B \u6DFB\u52A0\u53D8\u91CF", "+ Add variable"), () => addEnvPass());
|
|
27033
|
+
addEnvPassBtn.classList.add("settings-mcp-add-arg");
|
|
27034
|
+
envPassField.append(envPassList, addEnvPassBtn);
|
|
27035
|
+
stdioSection.append(envPassField);
|
|
27036
|
+
const cwd2 = el("input");
|
|
27037
|
+
cwd2.placeholder = "~/code";
|
|
27038
|
+
cwd2.value = server?.cwd ?? "";
|
|
27039
|
+
field(stdioSection, tr("\u5DE5\u4F5C\u76EE\u5F55", "Working directory"), cwd2);
|
|
27040
|
+
const httpSection = el("div");
|
|
27041
|
+
httpSection.className = "settings-mcp-section";
|
|
27042
|
+
const url2 = el("input");
|
|
27043
|
+
url2.placeholder = "https://mcp.example.com/mcp";
|
|
27044
|
+
url2.value = server?.url ?? "";
|
|
27045
|
+
field(httpSection, "URL", url2);
|
|
27046
|
+
const bearerToken = el("input");
|
|
27047
|
+
bearerToken.placeholder = "MCP_BEARER_TOKEN";
|
|
27048
|
+
bearerToken.value = server?.bearer_token_env_var ?? "";
|
|
27049
|
+
field(httpSection, tr("Bearer \u4EE4\u724C\u73AF\u5883\u53D8\u91CF", "Bearer token env var"), bearerToken);
|
|
27050
|
+
const headersField = el("div");
|
|
27051
|
+
headersField.className = "settings-integrations-field";
|
|
27052
|
+
headersField.append(el("span", tr("\u6807\u5934", "Headers")));
|
|
27053
|
+
const headersList = el("div");
|
|
27054
|
+
headersList.className = "settings-mcp-args";
|
|
27055
|
+
const addHeader = (key = "", val = "") => {
|
|
27056
|
+
const row = el("div");
|
|
27057
|
+
row.className = "settings-mcp-kv-row";
|
|
27058
|
+
const keyInput = el("input");
|
|
27059
|
+
keyInput.value = key;
|
|
27060
|
+
keyInput.placeholder = tr("\u952E", "Key");
|
|
27061
|
+
const valInput = el("input");
|
|
27062
|
+
valInput.value = val;
|
|
27063
|
+
valInput.placeholder = tr("\u503C", "Value");
|
|
27064
|
+
row.append(keyInput, valInput, createTrashBtn(() => row.remove()));
|
|
27065
|
+
headersList.append(row);
|
|
27066
|
+
};
|
|
27067
|
+
if (server?.http_headers) {
|
|
27068
|
+
for (const [k, v] of Object.entries(server.http_headers)) addHeader(k, String(v ?? ""));
|
|
27069
|
+
}
|
|
27070
|
+
const addHeaderBtn = button(tr("\uFF0B \u6DFB\u52A0\u6807\u5934", "+ Add header"), () => addHeader());
|
|
27071
|
+
addHeaderBtn.classList.add("settings-mcp-add-arg");
|
|
27072
|
+
headersField.append(headersList, addHeaderBtn);
|
|
27073
|
+
httpSection.append(headersField);
|
|
27074
|
+
const envHeadersField = el("div");
|
|
27075
|
+
envHeadersField.className = "settings-integrations-field";
|
|
27076
|
+
envHeadersField.append(el("span", tr("\u6765\u81EA\u73AF\u5883\u53D8\u91CF\u7684\u6807\u5934", "Headers from environment variables")));
|
|
27077
|
+
const envHeadersList = el("div");
|
|
27078
|
+
envHeadersList.className = "settings-mcp-args";
|
|
27079
|
+
const addEnvHeader = (key = "", val = "") => {
|
|
27080
|
+
const row = el("div");
|
|
27081
|
+
row.className = "settings-mcp-kv-row";
|
|
27082
|
+
const keyInput = el("input");
|
|
27083
|
+
keyInput.value = key;
|
|
27084
|
+
keyInput.placeholder = tr("\u6807\u5934\u540D\u79F0", "Header name");
|
|
27085
|
+
const valInput = el("input");
|
|
27086
|
+
valInput.value = val;
|
|
27087
|
+
valInput.placeholder = tr("\u73AF\u5883\u53D8\u91CF\u540D\u79F0", "Environment variable name");
|
|
27088
|
+
row.append(keyInput, valInput, createTrashBtn(() => row.remove()));
|
|
27089
|
+
envHeadersList.append(row);
|
|
27090
|
+
};
|
|
27091
|
+
if (server?.env_http_headers) {
|
|
27092
|
+
for (const [k, v] of Object.entries(server.env_http_headers)) addEnvHeader(k, String(v ?? ""));
|
|
27093
|
+
}
|
|
27094
|
+
const addEnvHeaderBtn = button(tr("\uFF0B \u6DFB\u52A0\u53D8\u91CF", "+ Add variable"), () => addEnvHeader());
|
|
27095
|
+
addEnvHeaderBtn.classList.add("settings-mcp-add-arg");
|
|
27096
|
+
envHeadersField.append(envHeadersList, addEnvHeaderBtn);
|
|
27097
|
+
httpSection.append(envHeadersField);
|
|
27098
|
+
const updateTypeView = () => {
|
|
27099
|
+
stdioBtn.dataset.active = String(transportType === "stdio");
|
|
27100
|
+
httpBtn.dataset.active = String(transportType === "streamable_http");
|
|
27101
|
+
stdioSection.hidden = transportType !== "stdio";
|
|
27102
|
+
httpSection.hidden = transportType !== "streamable_http";
|
|
27103
|
+
command.required = transportType === "stdio";
|
|
27104
|
+
url2.required = transportType === "streamable_http";
|
|
27105
|
+
};
|
|
27106
|
+
stdioBtn.addEventListener("click", () => {
|
|
27107
|
+
transportType = "stdio";
|
|
27108
|
+
updateTypeView();
|
|
27109
|
+
});
|
|
27110
|
+
httpBtn.addEventListener("click", () => {
|
|
27111
|
+
transportType = "streamable_http";
|
|
27112
|
+
updateTypeView();
|
|
27113
|
+
});
|
|
27114
|
+
updateTypeView();
|
|
27115
|
+
transportCard.append(stdioSection, httpSection);
|
|
27116
|
+
const security = el("p", tr("\u51ED\u636E\u548C\u73AF\u5883\u53D8\u91CF\u5C06\u5728\u539F\u751F Harness \u8FD0\u884C\u73AF\u5883\u4E2D\u89E3\u6790\u4E0E\u6CE8\u5165\uFF0C\u4E0D\u4F1A\u6CC4\u9732\u5728\u5BBF\u4E3B\u8FDB\u7A0B\u4E2D\u3002", "Credentials and environment variables are resolved and injected within the native Harness environment."));
|
|
27117
|
+
security.className = "settings-integrations-callout";
|
|
27118
|
+
const save = button(tr("\u4FDD\u5B58 MCP", "Save MCP"), () => {
|
|
27119
|
+
}, true);
|
|
27120
|
+
save.type = "submit";
|
|
27121
|
+
form.append(basicCard, transportCard, security, save);
|
|
27122
|
+
form.addEventListener("submit", (event) => {
|
|
27123
|
+
event.preventDefault();
|
|
27124
|
+
if (transportType === "stdio") {
|
|
27125
|
+
const args = [...argsList.querySelectorAll("input")].map((input) => input.value.trim()).filter((value) => value.length > 0);
|
|
27126
|
+
const env = {};
|
|
27127
|
+
for (const row of envList.querySelectorAll(".settings-mcp-kv-row")) {
|
|
27128
|
+
const [k, v] = row.querySelectorAll("input");
|
|
27129
|
+
if (k?.value.trim() && v?.value) env[k.value.trim()] = v.value;
|
|
27130
|
+
}
|
|
27131
|
+
const env_vars = [...envPassList.querySelectorAll("input")].map((input) => input.value.trim()).filter((v) => v.length > 0);
|
|
27132
|
+
const cwdVal = cwd2.value.trim();
|
|
27133
|
+
void mutate((value, selected) => value.saveMcp({
|
|
27134
|
+
...selected,
|
|
27135
|
+
server: {
|
|
27136
|
+
...server?.id ? { id: server.id } : {},
|
|
27137
|
+
name: name.value.trim(),
|
|
27138
|
+
transportType: "stdio",
|
|
27139
|
+
command: command.value.trim(),
|
|
27140
|
+
args,
|
|
27141
|
+
...Object.keys(env).length ? { env } : {},
|
|
27142
|
+
...env_vars.length ? { env_vars } : {},
|
|
27143
|
+
...cwdVal ? { cwd: cwdVal } : {},
|
|
27144
|
+
enabled: server?.enabled ?? true
|
|
27145
|
+
}
|
|
27146
|
+
}));
|
|
27147
|
+
} else {
|
|
27148
|
+
const http_headers = {};
|
|
27149
|
+
for (const row of headersList.querySelectorAll(".settings-mcp-kv-row")) {
|
|
27150
|
+
const [k, v] = row.querySelectorAll("input");
|
|
27151
|
+
if (k?.value.trim() && v?.value) http_headers[k.value.trim()] = v.value;
|
|
27152
|
+
}
|
|
27153
|
+
const env_http_headers = {};
|
|
27154
|
+
for (const row of envHeadersList.querySelectorAll(".settings-mcp-kv-row")) {
|
|
27155
|
+
const [k, v] = row.querySelectorAll("input");
|
|
27156
|
+
if (k?.value.trim() && v?.value.trim()) env_http_headers[k.value.trim()] = v.value.trim();
|
|
27157
|
+
}
|
|
27158
|
+
const token = bearerToken.value.trim();
|
|
27159
|
+
void mutate((value, selected) => value.saveMcp({
|
|
27160
|
+
...selected,
|
|
27161
|
+
server: {
|
|
27162
|
+
...server?.id ? { id: server.id } : {},
|
|
27163
|
+
name: name.value.trim(),
|
|
27164
|
+
transportType: "streamable_http",
|
|
27165
|
+
url: url2.value.trim(),
|
|
27166
|
+
...token ? { bearer_token_env_var: token } : {},
|
|
27167
|
+
...Object.keys(http_headers).length ? { http_headers } : {},
|
|
27168
|
+
...Object.keys(env_http_headers).length ? { env_http_headers } : {},
|
|
27169
|
+
enabled: server?.enabled ?? true
|
|
27170
|
+
}
|
|
27171
|
+
}));
|
|
27172
|
+
}
|
|
27173
|
+
}, { signal: lifecycle.signal });
|
|
27174
|
+
editor.append(heading, form);
|
|
27175
|
+
name.focus();
|
|
27176
|
+
};
|
|
27177
|
+
if (targetEditServerName) {
|
|
27178
|
+
const target = targetEditServerName === "__new__" ? void 0 : snapshot.servers.find((s) => s.name === targetEditServerName);
|
|
27179
|
+
targetEditServerName = void 0;
|
|
27180
|
+
showEditor(target);
|
|
27181
|
+
body.append(toolbar, list, editor);
|
|
27182
|
+
return;
|
|
27183
|
+
}
|
|
27184
|
+
const addButton = button(tr("\uFF0B \u6DFB\u52A0 MCP", "+ Add MCP"), () => showEditor(), true);
|
|
27185
|
+
toolbar.append(search, addButton);
|
|
27186
|
+
for (const native of snapshot.native) {
|
|
27187
|
+
const row = el("section");
|
|
27188
|
+
row.className = "settings-integrations-row settings-integrations-row--native";
|
|
27189
|
+
row.append(el("strong", `${tr("\u539F\u751F\u72B6\u6001", "Native status")} \xB7 ${native.name || native.sessionId}`), el("span", native.status), el("code", native.tools.join(", ")));
|
|
27190
|
+
list.append(row);
|
|
27191
|
+
}
|
|
27192
|
+
const toManaged = (s, newEnabled) => ({
|
|
27193
|
+
id: s.id,
|
|
27194
|
+
name: s.name,
|
|
27195
|
+
enabled: newEnabled,
|
|
27196
|
+
...s.transportType ? { transportType: s.transportType } : {},
|
|
27197
|
+
...s.command ? { command: s.command } : {},
|
|
27198
|
+
...s.args?.length ? { args: s.args } : {},
|
|
27199
|
+
...s.env ? { env: s.env } : {},
|
|
27200
|
+
...s.env_vars?.length ? { env_vars: s.env_vars } : {},
|
|
27201
|
+
...s.cwd ? { cwd: s.cwd } : {},
|
|
27202
|
+
...s.url ? { url: s.url } : {},
|
|
27203
|
+
...s.bearer_token_env_var ? { bearer_token_env_var: s.bearer_token_env_var } : {},
|
|
27204
|
+
...s.http_headers ? { http_headers: s.http_headers } : {},
|
|
27205
|
+
...s.env_http_headers ? { env_http_headers: s.env_http_headers } : {}
|
|
27206
|
+
});
|
|
27207
|
+
const serverRows = [];
|
|
27208
|
+
for (const server of snapshot.servers) {
|
|
27209
|
+
const row = el("section");
|
|
27210
|
+
row.className = "settings-integrations-row";
|
|
27211
|
+
const isHttp = server.transportType === "streamable_http" || !!server.url;
|
|
27212
|
+
row.dataset.search = `${server.name} ${server.command || ""} ${server.url || ""}`.toLowerCase();
|
|
27213
|
+
const detail = el("div");
|
|
27214
|
+
const state = !server.effective ? tr("\u88AB\u9879\u76EE\u914D\u7F6E\u8986\u76D6", "Overridden by project") : !server.enabled ? tr("\u5DF2\u505C\u7528", "Disabled") : server.appliedSessions ? tr(`\u5DF2\u4F20\u5165 ${server.appliedSessions} \u4E2A\u4F1A\u8BDD`, `Passed to ${server.appliedSessions} sessions`) : tr("\u5DF2\u914D\u7F6E", "Configured");
|
|
27215
|
+
const typeTag = isHttp ? " [HTTP]" : "";
|
|
27216
|
+
const summary = isHttp ? server.url || "" : [server.command, ...server.args || []].filter(Boolean).join(" ");
|
|
27217
|
+
detail.append(el("strong", `${server.name}${typeTag}`), el("p", `${server.scope === "global" ? tr("\u5168\u5C40", "Global") : tr("\u9879\u76EE", "Project")} \xB7 ${state}`), el("code", summary));
|
|
27218
|
+
row.append(detail);
|
|
27219
|
+
if (server.editable) {
|
|
27220
|
+
const editBtn = button(tr("\u7F16\u8F91", "Edit"), () => showEditor(server));
|
|
27221
|
+
const toggleBtn = button(server.enabled ? tr("\u505C\u7528", "Disable") : tr("\u542F\u7528", "Enable"), () => {
|
|
27222
|
+
void mutate((value, selected) => value.saveMcp({ ...selected, server: toManaged(server, !server.enabled) }));
|
|
27223
|
+
});
|
|
27224
|
+
const syncBtn = button(tr("\u540C\u6B65\u2026", "Sync\u2026"), () => {
|
|
27225
|
+
void client().integrationCatalog().then((catalog) => {
|
|
27226
|
+
const targets = catalog.harnesses.filter((h) => h.mcp && h.id !== harness.value);
|
|
27227
|
+
showMcpSyncDialog(server, { id: harness.value, name: harness.selectedOptions[0]?.text || harness.value }, targets);
|
|
27228
|
+
});
|
|
27229
|
+
});
|
|
27230
|
+
const delBtn = button(tr("\u5220\u9664", "Delete"), () => {
|
|
27231
|
+
void mutate((value, selected) => value.removeMcp({ ...selected, id: server.id }));
|
|
27232
|
+
});
|
|
27233
|
+
row.append(editBtn, toggleBtn, syncBtn, delBtn);
|
|
27234
|
+
}
|
|
27235
|
+
list.append(row);
|
|
27236
|
+
serverRows.push(row);
|
|
27237
|
+
}
|
|
27238
|
+
if (!snapshot.servers.length) {
|
|
27239
|
+
const empty = el("div", tr("\u8FD8\u6CA1\u6709 MCP \u670D\u52A1\u5668\uFF0C\u70B9\u51FB\u201C\u6DFB\u52A0 MCP\u201D\u5F00\u59CB\u914D\u7F6E\u3002", "No MCP servers yet. Select \u201CAdd MCP\u201D to configure one."));
|
|
27240
|
+
empty.className = "settings-integrations-empty";
|
|
27241
|
+
list.append(empty);
|
|
27242
|
+
}
|
|
27243
|
+
search.addEventListener("input", () => {
|
|
27244
|
+
const query = search.value.trim().toLowerCase();
|
|
27245
|
+
for (const row of serverRows) row.hidden = !!query && !row.dataset.search.includes(query);
|
|
27246
|
+
}, { signal: lifecycle.signal });
|
|
27247
|
+
if (!snapshot.mcpSupported) {
|
|
27248
|
+
addButton.disabled = true;
|
|
27249
|
+
const unavailable = el("div", tr("\u6B64 Harness \u5C1A\u65E0\u5DF2\u786E\u8BA4\u7684 MCP \u914D\u7F6E\u63A5\u53E3\u3002", "This Harness has no verified MCP configuration interface."));
|
|
27250
|
+
unavailable.className = "settings-integrations-callout";
|
|
27251
|
+
list.prepend(unavailable);
|
|
27252
|
+
}
|
|
27253
|
+
body.append(toolbar, list, editor);
|
|
27254
|
+
};
|
|
27255
|
+
const showMcpSyncDialog = (server, sourceHarness, targetHarnesses) => {
|
|
27256
|
+
if (!targetHarnesses.length) {
|
|
27257
|
+
fail(new Error(tr("\u6CA1\u6709\u5176\u4ED6\u652F\u6301 MCP \u7684\u539F\u751F Harness \u53EF\u4F9B\u540C\u6B65\u3002", "No other native Harnesses support MCP for sync.")));
|
|
27258
|
+
return;
|
|
27259
|
+
}
|
|
27260
|
+
const modal = el("dialog");
|
|
27261
|
+
modal.className = "settings-skills-sync-modal settings-mcp-sync-modal";
|
|
27262
|
+
const mTitle = el("h3", tr(`\u540C\u6B65 MCP\u3010${server.name}\u3011`, `Sync MCP [${server.name}]`));
|
|
27263
|
+
const isHttp = server.transportType === "streamable_http" || !!server.url;
|
|
27264
|
+
const summary = isHttp ? server.url || "" : [server.command, ...server.args || []].filter(Boolean).join(" ");
|
|
27265
|
+
const mDesc = el("p", tr(
|
|
27266
|
+
`\u4ECE ${sourceHarness.name} \u590D\u5236\u8BE5 MCP \u914D\u7F6E\uFF08${isHttp ? "HTTP" : "stdio"}\uFF1A${summary}\uFF09\u5230\u9009\u4E2D\u7684 Harness\uFF1A`,
|
|
27267
|
+
`Copy MCP configuration (${isHttp ? "HTTP" : "stdio"}: ${summary}) from ${sourceHarness.name} to selected Harnesses:`
|
|
27268
|
+
));
|
|
27269
|
+
const form = el("form");
|
|
27270
|
+
const checkList = el("div");
|
|
27271
|
+
checkList.style.display = "grid";
|
|
27272
|
+
checkList.style.gap = "8px";
|
|
27273
|
+
checkList.style.margin = "12px 0";
|
|
27274
|
+
checkList.style.maxHeight = "240px";
|
|
27275
|
+
checkList.style.overflowY = "auto";
|
|
27276
|
+
const checkboxes = [];
|
|
27277
|
+
for (const h of targetHarnesses) {
|
|
27278
|
+
const label = el("label");
|
|
27279
|
+
label.style.display = "flex";
|
|
27280
|
+
label.style.gap = "8px";
|
|
27281
|
+
label.style.alignItems = "center";
|
|
27282
|
+
label.style.cursor = "pointer";
|
|
27283
|
+
const cb = el("input");
|
|
27284
|
+
cb.type = "checkbox";
|
|
27285
|
+
cb.value = h.id;
|
|
27286
|
+
cb.checked = true;
|
|
27287
|
+
checkboxes.push(cb);
|
|
27288
|
+
label.append(cb, el("span", h.name));
|
|
27289
|
+
checkList.append(label);
|
|
27290
|
+
}
|
|
27291
|
+
const btnRow = el("div");
|
|
27292
|
+
btnRow.style.display = "flex";
|
|
27293
|
+
btnRow.style.gap = "8px";
|
|
27294
|
+
btnRow.style.justifyContent = "flex-end";
|
|
27295
|
+
btnRow.style.marginTop = "14px";
|
|
27296
|
+
const cancelBtn = button(tr("\u53D6\u6D88", "Cancel"), () => {
|
|
27297
|
+
modal.close();
|
|
27298
|
+
modal.remove();
|
|
27299
|
+
});
|
|
27300
|
+
const confirmBtn = button(tr("\u786E\u8BA4\u540C\u6B65", "Confirm Sync"), () => {
|
|
27301
|
+
const selectedHarnesses = checkboxes.filter((cb) => cb.checked).map((cb) => cb.value);
|
|
27302
|
+
modal.close();
|
|
27303
|
+
modal.remove();
|
|
27304
|
+
if (!selectedHarnesses.length) return;
|
|
27305
|
+
void mutateCustom(async (c) => {
|
|
27306
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
27307
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
27308
|
+
const successes = [];
|
|
27309
|
+
const errors = [];
|
|
27310
|
+
const cleanConfig = toManagedForCopy(server);
|
|
27311
|
+
for (const targetId of selectedHarnesses) {
|
|
27312
|
+
const targetH = targetHarnesses.find((h) => h.id === targetId);
|
|
27313
|
+
try {
|
|
27314
|
+
await c.saveMcp({
|
|
27315
|
+
harnessId: targetId,
|
|
27316
|
+
scope: scopeType,
|
|
27317
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
27318
|
+
server: cleanConfig
|
|
27319
|
+
});
|
|
27320
|
+
if (targetH) successes.push(targetH.name);
|
|
27321
|
+
} catch (err) {
|
|
27322
|
+
errors.push(`${targetH?.name || targetId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
27323
|
+
}
|
|
27324
|
+
}
|
|
27325
|
+
if (!successes.length && errors.length) throw new Error(errors.join("; "));
|
|
27326
|
+
return tr(
|
|
27327
|
+
`\u540C\u6B65\u5B8C\u6210\uFF01\u5DF2\u914D\u7F6E\u5230\uFF1A${successes.join("\u3001")}${errors.length ? `\uFF08\u8DF3\u8FC7: ${errors.join("; ")}\uFF09` : ""}`,
|
|
27328
|
+
`Sync complete! Configured on: ${successes.join(", ")}${errors.length ? ` (Skipped: ${errors.join("; ")})` : ""}`
|
|
27329
|
+
);
|
|
27330
|
+
});
|
|
27331
|
+
}, true);
|
|
27332
|
+
btnRow.append(cancelBtn, confirmBtn);
|
|
27333
|
+
form.append(checkList, btnRow);
|
|
27334
|
+
modal.append(mTitle, mDesc, form);
|
|
27335
|
+
content.append(modal);
|
|
27336
|
+
modal.showModal();
|
|
27337
|
+
};
|
|
27338
|
+
const showMcpDistributeDialog = (targetHarnesses) => {
|
|
27339
|
+
if (!targetHarnesses.length) {
|
|
27340
|
+
fail(new Error(tr("\u6CA1\u6709\u652F\u6301 MCP \u6CE8\u5165\u7684\u539F\u751F Harness\u3002", "No native Harnesses support MCP injection.")));
|
|
27341
|
+
return;
|
|
27342
|
+
}
|
|
27343
|
+
const modal = el("dialog");
|
|
27344
|
+
modal.className = "settings-mcp-distribute-modal";
|
|
27345
|
+
const mTitle = el("h3", tr("\u65B0\u5EFA\u5E76\u6279\u91CF\u5206\u53D1 MCP", "Create & Distribute MCP"));
|
|
27346
|
+
const mDesc = el("p", tr("\u914D\u7F6E MCP \u670D\u52A1\u5668\uFF0C\u5E76\u4E00\u6B21\u6027\u4FDD\u5B58\u5206\u53D1\u81F3\u9009\u4E2D\u7684\u6240\u6709 Harness\u3002", "Configure MCP server once and distribute to all selected Harnesses."));
|
|
27347
|
+
const form = el("form");
|
|
27348
|
+
form.className = "settings-integrations-form";
|
|
27349
|
+
let transportType = "stdio";
|
|
27350
|
+
const typeToggle = el("div");
|
|
27351
|
+
typeToggle.className = "settings-mcp-type-toggle";
|
|
27352
|
+
const stdioBtn = el("button", tr("\u6807\u51C6\u8F93\u5165\u8F93\u51FA (stdio)", "Standard I/O (stdio)"));
|
|
27353
|
+
stdioBtn.type = "button";
|
|
27354
|
+
stdioBtn.className = "settings-mcp-type-btn";
|
|
27355
|
+
stdioBtn.dataset.active = "true";
|
|
27356
|
+
const httpBtn = el("button", tr("\u6D41\u5F0F HTTP (SSE)", "Streamable HTTP (SSE)"));
|
|
27357
|
+
httpBtn.type = "button";
|
|
27358
|
+
httpBtn.className = "settings-mcp-type-btn";
|
|
27359
|
+
typeToggle.append(stdioBtn, httpBtn);
|
|
27360
|
+
const nameInput = el("input");
|
|
27361
|
+
nameInput.required = true;
|
|
27362
|
+
nameInput.placeholder = "my-mcp-server";
|
|
27363
|
+
nameInput.pattern = "[a-zA-Z0-9](?:[a-zA-Z0-9_]|-){0,63}";
|
|
27364
|
+
field(form, tr("MCP \u670D\u52A1\u5668\u540D\u79F0\uFF08\u5FC5\u586B\uFF09", "MCP server name (required)"), nameInput);
|
|
27365
|
+
const typeField = el("div");
|
|
27366
|
+
typeField.className = "settings-integrations-field";
|
|
27367
|
+
typeField.append(el("span", tr("\u4F20\u8F93\u534F\u8BAE", "Transport Protocol")), typeToggle);
|
|
27368
|
+
form.append(typeField);
|
|
27369
|
+
const stdioCard = el("div");
|
|
27370
|
+
stdioCard.className = "settings-integrations-form--card";
|
|
27371
|
+
const cmdInput = el("input");
|
|
27372
|
+
cmdInput.placeholder = "npx / uvx / node / ...";
|
|
27373
|
+
field(stdioCard, tr("\u53EF\u6267\u884C\u547D\u4EE4 (Command)", "Executable Command"), cmdInput);
|
|
27374
|
+
const argsInput = el("input");
|
|
27375
|
+
argsInput.placeholder = "-y @modelcontextprotocol/server-filesystem D:/data";
|
|
27376
|
+
field(stdioCard, tr("\u53C2\u6570\uFF08\u7A7A\u683C\u5206\u9694\uFF0C\u652F\u6301\u5F15\u53F7\uFF09", "Arguments (space separated, quotes supported)"), argsInput);
|
|
27377
|
+
const envInput = el("input");
|
|
27378
|
+
envInput.placeholder = "KEY1=val1, KEY2=val2";
|
|
27379
|
+
field(stdioCard, tr("\u73AF\u5883\u53D8\u91CF\uFF08\u53EF\u9009\uFF0CKEY=VAL \u9017\u53F7\u6216\u6362\u884C\u5206\u9694\uFF09", "Environment variables (optional, KEY=VAL separated)"), envInput);
|
|
27380
|
+
const httpCard = el("div");
|
|
27381
|
+
httpCard.className = "settings-integrations-form--card";
|
|
27382
|
+
httpCard.hidden = true;
|
|
27383
|
+
const urlInput = el("input");
|
|
27384
|
+
urlInput.type = "url";
|
|
27385
|
+
urlInput.placeholder = "https://mcp.example.com/sse";
|
|
27386
|
+
field(httpCard, tr("MCP \u670D\u52A1\u5668 URL", "MCP Server URL"), urlInput);
|
|
27387
|
+
const tokenInput = el("input");
|
|
27388
|
+
tokenInput.placeholder = "API_KEY_ENV_NAME";
|
|
27389
|
+
field(httpCard, tr("Bearer Token \u73AF\u5883\u53D8\u91CF\u540D\uFF08\u53EF\u9009\uFF09", "Bearer token env var name (optional)"), tokenInput);
|
|
27390
|
+
stdioBtn.addEventListener("click", () => {
|
|
27391
|
+
transportType = "stdio";
|
|
27392
|
+
stdioBtn.dataset.active = "true";
|
|
27393
|
+
delete httpBtn.dataset.active;
|
|
27394
|
+
stdioCard.hidden = false;
|
|
27395
|
+
httpCard.hidden = true;
|
|
27396
|
+
});
|
|
27397
|
+
httpBtn.addEventListener("click", () => {
|
|
27398
|
+
transportType = "streamable_http";
|
|
27399
|
+
httpBtn.dataset.active = "true";
|
|
27400
|
+
delete stdioBtn.dataset.active;
|
|
27401
|
+
stdioCard.hidden = true;
|
|
27402
|
+
httpCard.hidden = false;
|
|
27403
|
+
});
|
|
27404
|
+
form.append(stdioCard, httpCard);
|
|
27405
|
+
const targetSection = el("div");
|
|
27406
|
+
targetSection.className = "settings-integrations-form--card";
|
|
27407
|
+
const targetHead = el("div");
|
|
27408
|
+
targetHead.style.display = "flex";
|
|
27409
|
+
targetHead.style.justifyContent = "space-between";
|
|
27410
|
+
targetHead.style.alignItems = "center";
|
|
27411
|
+
targetHead.style.marginBottom = "8px";
|
|
27412
|
+
const targetTitle = el("strong", tr("\u5206\u53D1\u76EE\u6807 Harness\uFF1A", "Target Harnesses:"));
|
|
27413
|
+
targetTitle.style.fontSize = "12px";
|
|
27414
|
+
const toggleAllBtn = el("button", tr("\u5168\u9009 / \u53D6\u6D88\u5168\u9009", "Select / Deselect All"));
|
|
27415
|
+
toggleAllBtn.type = "button";
|
|
27416
|
+
toggleAllBtn.className = "settings-command-button settings-command-button--secondary";
|
|
27417
|
+
toggleAllBtn.style.fontSize = "11px";
|
|
27418
|
+
toggleAllBtn.style.padding = "3px 8px";
|
|
27419
|
+
targetHead.append(targetTitle, toggleAllBtn);
|
|
27420
|
+
targetSection.append(targetHead);
|
|
27421
|
+
const checkList = el("div");
|
|
27422
|
+
checkList.style.display = "grid";
|
|
27423
|
+
checkList.style.gridTemplateColumns = "repeat(auto-fill, minmax(130px, 1fr))";
|
|
27424
|
+
checkList.style.gap = "8px";
|
|
27425
|
+
const checkboxes = [];
|
|
27426
|
+
for (const h of targetHarnesses) {
|
|
27427
|
+
const label = el("label");
|
|
27428
|
+
label.style.display = "flex";
|
|
27429
|
+
label.style.gap = "6px";
|
|
27430
|
+
label.style.alignItems = "center";
|
|
27431
|
+
label.style.cursor = "pointer";
|
|
27432
|
+
label.style.fontSize = "12px";
|
|
27433
|
+
const cb = el("input");
|
|
27434
|
+
cb.type = "checkbox";
|
|
27435
|
+
cb.value = h.id;
|
|
27436
|
+
cb.checked = true;
|
|
27437
|
+
checkboxes.push(cb);
|
|
27438
|
+
label.append(cb, el("span", h.name));
|
|
27439
|
+
checkList.append(label);
|
|
27440
|
+
}
|
|
27441
|
+
targetSection.append(checkList);
|
|
27442
|
+
form.append(targetSection);
|
|
27443
|
+
toggleAllBtn.addEventListener("click", () => {
|
|
27444
|
+
const anyChecked = checkboxes.some((cb) => cb.checked);
|
|
27445
|
+
for (const cb of checkboxes) cb.checked = !anyChecked;
|
|
27446
|
+
});
|
|
27447
|
+
const btnRow = el("div");
|
|
27448
|
+
btnRow.style.display = "flex";
|
|
27449
|
+
btnRow.style.gap = "8px";
|
|
27450
|
+
btnRow.style.justifyContent = "flex-end";
|
|
27451
|
+
btnRow.style.marginTop = "14px";
|
|
27452
|
+
const cancelBtn = button(tr("\u53D6\u6D88", "Cancel"), () => {
|
|
27453
|
+
modal.close();
|
|
27454
|
+
modal.remove();
|
|
27455
|
+
});
|
|
27456
|
+
const submitBtn = el("button", tr("\u786E\u8BA4\u5E76\u5206\u53D1", "Confirm & Distribute"));
|
|
27457
|
+
submitBtn.type = "submit";
|
|
27458
|
+
submitBtn.className = "settings-command-button settings-command-button--primary";
|
|
27459
|
+
btnRow.append(cancelBtn, submitBtn);
|
|
27460
|
+
form.append(btnRow);
|
|
27461
|
+
form.addEventListener("submit", (event) => {
|
|
27462
|
+
event.preventDefault();
|
|
27463
|
+
const serverName = nameInput.value.trim();
|
|
27464
|
+
if (!serverName) return;
|
|
27465
|
+
const selectedHarnesses = checkboxes.filter((cb) => cb.checked).map((cb) => cb.value);
|
|
27466
|
+
if (!selectedHarnesses.length) {
|
|
27467
|
+
fail(new Error(tr("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u4E2A\u76EE\u6807 Harness\u3002", "Please select at least one target Harness.")));
|
|
27468
|
+
return;
|
|
27469
|
+
}
|
|
27470
|
+
let serverConfig;
|
|
27471
|
+
if (transportType === "streamable_http") {
|
|
27472
|
+
const url2 = urlInput.value.trim();
|
|
27473
|
+
if (!url2) {
|
|
27474
|
+
fail(new Error(tr("\u6D41\u5F0F HTTP \u6A21\u5F0F\u5FC5\u987B\u586B\u5199 URL\u3002", "URL is required for streamable HTTP.")));
|
|
27475
|
+
return;
|
|
27476
|
+
}
|
|
27477
|
+
serverConfig = {
|
|
27478
|
+
name: serverName,
|
|
27479
|
+
transportType: "streamable_http",
|
|
27480
|
+
enabled: true,
|
|
27481
|
+
url: url2,
|
|
27482
|
+
...tokenInput.value.trim() ? { bearer_token_env_var: tokenInput.value.trim() } : {}
|
|
27483
|
+
};
|
|
27484
|
+
} else {
|
|
27485
|
+
const cmd = cmdInput.value.trim();
|
|
27486
|
+
if (!cmd) {
|
|
27487
|
+
fail(new Error(tr("stdio \u6A21\u5F0F\u5FC5\u987B\u586B\u5199\u547D\u4EE4\u3002", "Command is required for stdio mode.")));
|
|
27488
|
+
return;
|
|
27489
|
+
}
|
|
27490
|
+
const rawArgs = argsInput.value.trim().match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g)?.map((s) => s.replace(/^['"]|['"]$/g, "")) || [];
|
|
27491
|
+
const envMap = {};
|
|
27492
|
+
for (const part of envInput.value.split(/[,;\n]/)) {
|
|
27493
|
+
const eq = part.indexOf("=");
|
|
27494
|
+
if (eq > 0) {
|
|
27495
|
+
const k = part.slice(0, eq).trim();
|
|
27496
|
+
const v = part.slice(eq + 1).trim();
|
|
27497
|
+
if (k) envMap[k] = v;
|
|
27498
|
+
}
|
|
27499
|
+
}
|
|
27500
|
+
serverConfig = {
|
|
27501
|
+
name: serverName,
|
|
27502
|
+
transportType: "stdio",
|
|
27503
|
+
enabled: true,
|
|
27504
|
+
command: cmd,
|
|
27505
|
+
...rawArgs.length ? { args: rawArgs } : {},
|
|
27506
|
+
...Object.keys(envMap).length ? { env: envMap } : {}
|
|
27507
|
+
};
|
|
27508
|
+
}
|
|
27509
|
+
modal.close();
|
|
27510
|
+
modal.remove();
|
|
27511
|
+
void mutateCustom(async (c) => {
|
|
27512
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
27513
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
27514
|
+
const successes = [];
|
|
27515
|
+
const errors = [];
|
|
27516
|
+
for (const targetId of selectedHarnesses) {
|
|
27517
|
+
const targetH = targetHarnesses.find((h) => h.id === targetId);
|
|
27518
|
+
try {
|
|
27519
|
+
await c.saveMcp({
|
|
27520
|
+
harnessId: targetId,
|
|
27521
|
+
scope: scopeType,
|
|
27522
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
27523
|
+
server: serverConfig
|
|
27524
|
+
});
|
|
27525
|
+
if (targetH) successes.push(targetH.name);
|
|
27526
|
+
} catch (err) {
|
|
27527
|
+
errors.push(`${targetH?.name || targetId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
27528
|
+
}
|
|
27529
|
+
}
|
|
27530
|
+
if (!successes.length && errors.length) throw new Error(errors.join("; "));
|
|
27531
|
+
return tr(
|
|
27532
|
+
`\u5DF2\u6210\u529F\u5206\u53D1\u3010${serverName}\u3011\u81F3\uFF1A${successes.join("\u3001")}${errors.length ? `\uFF08\u8DF3\u8FC7: ${errors.join("; ")}\uFF09` : ""}`,
|
|
27533
|
+
`Distributed [${serverName}] to: ${successes.join(", ")}${errors.length ? ` (Skipped: ${errors.join("; ")})` : ""}`
|
|
27534
|
+
);
|
|
27535
|
+
});
|
|
27536
|
+
});
|
|
27537
|
+
modal.append(mTitle, mDesc, form);
|
|
27538
|
+
content.append(modal);
|
|
27539
|
+
modal.showModal();
|
|
27540
|
+
};
|
|
27541
|
+
const renderMcpOverview = (_catalog, items) => {
|
|
27542
|
+
const supported = items.filter((i) => i.harness.mcp);
|
|
27543
|
+
const uniqueServers = /* @__PURE__ */ new Map();
|
|
27544
|
+
let totalServersCount = 0;
|
|
27545
|
+
let totalNativeCount = 0;
|
|
27546
|
+
for (const item of items) {
|
|
27547
|
+
if (item.snapshot?.servers) {
|
|
27548
|
+
totalServersCount += item.snapshot.servers.length;
|
|
27549
|
+
for (const s of item.snapshot.servers) {
|
|
27550
|
+
const list = uniqueServers.get(s.name) || [];
|
|
27551
|
+
list.push({ harness: item.harness, server: s });
|
|
27552
|
+
uniqueServers.set(s.name, list);
|
|
27553
|
+
}
|
|
27554
|
+
}
|
|
27555
|
+
if (item.snapshot?.native) {
|
|
27556
|
+
totalNativeCount += item.snapshot.native.length;
|
|
27557
|
+
}
|
|
27558
|
+
}
|
|
27559
|
+
const statsBar = el("div");
|
|
27560
|
+
statsBar.className = "settings-skills-overview-stats settings-mcp-overview-stats";
|
|
27561
|
+
const statsSummary = el("span", tr(
|
|
27562
|
+
`\u{1F4CA} \u5171 ${items.length} \u4E2A Harness \xB7 ${supported.length} \u4E2A\u652F\u6301\u539F\u751F MCP \u6CE8\u5165 \xB7 \u5DF2\u914D\u7F6E ${uniqueServers.size} \u79CD\u72EC\u7ACB MCP\uFF08\u5171 ${totalServersCount} \u4E2A\u5B9E\u4F8B\uFF09${totalNativeCount ? ` \xB7 ${totalNativeCount} \u4E2A\u6D3B\u8DC3\u539F\u751F\u8FDE\u63A5` : ""}`,
|
|
27563
|
+
`\u{1F4CA} ${items.length} Harnesses \xB7 ${supported.length} with native MCP injection \xB7 ${uniqueServers.size} unique MCPs (${totalServersCount} instances)${totalNativeCount ? ` \xB7 ${totalNativeCount} active native connections` : ""}`
|
|
27564
|
+
));
|
|
27565
|
+
statsBar.append(statsSummary);
|
|
27566
|
+
const toolbar = el("div");
|
|
27567
|
+
toolbar.className = "settings-integrations-toolbar";
|
|
27568
|
+
const searchInput = el("input");
|
|
27569
|
+
searchInput.type = "search";
|
|
27570
|
+
searchInput.placeholder = tr("\u6309\u540D\u79F0\u3001\u547D\u4EE4\u6216 URL \u8FC7\u6EE4 MCP \u670D\u52A1\u5668\u2026", "Filter MCP servers by name, command, or URL\u2026");
|
|
27571
|
+
searchInput.setAttribute("aria-label", searchInput.placeholder);
|
|
27572
|
+
const tabsWrap = el("div");
|
|
27573
|
+
tabsWrap.className = "settings-skills-tabs settings-mcp-tabs";
|
|
27574
|
+
const btnByHarness = el("button", tr("\u6309 Harness \u67E5\u770B", "By Harness"));
|
|
27575
|
+
btnByHarness.type = "button";
|
|
27576
|
+
btnByHarness.className = "settings-skills-tab-btn";
|
|
27577
|
+
btnByHarness.dataset.active = "true";
|
|
27578
|
+
const btnByServer = el("button", tr("\u6309 Server \u6C47\u603B", "By Server"));
|
|
27579
|
+
btnByServer.type = "button";
|
|
27580
|
+
btnByServer.className = "settings-skills-tab-btn";
|
|
27581
|
+
tabsWrap.append(btnByHarness, btnByServer);
|
|
27582
|
+
const distributeBtn = button(tr("\uFF0B \u65B0\u5EFA\u5E76\u5206\u53D1 MCP", "\uFF0B New & Distribute MCP"), () => {
|
|
27583
|
+
showMcpDistributeDialog(supported.map((i) => i.harness));
|
|
27584
|
+
}, true);
|
|
27585
|
+
toolbar.append(searchInput, tabsWrap, distributeBtn);
|
|
27586
|
+
const viewsContainer = el("div");
|
|
27587
|
+
viewsContainer.className = "settings-skills-views settings-mcp-views";
|
|
27588
|
+
let activeTab = "harness";
|
|
27589
|
+
const updateView = () => {
|
|
27590
|
+
viewsContainer.replaceChildren();
|
|
27591
|
+
const query = searchInput.value.trim().toLowerCase();
|
|
27592
|
+
if (activeTab === "harness") {
|
|
27593
|
+
const grid = el("div");
|
|
27594
|
+
grid.className = "settings-skills-overview-grid settings-mcp-overview-grid";
|
|
27595
|
+
for (const item of items) {
|
|
27596
|
+
const allServers = item.snapshot?.servers || [];
|
|
27597
|
+
const allNative = item.snapshot?.native || [];
|
|
27598
|
+
const matchingServers = query ? allServers.filter(
|
|
27599
|
+
(s) => s.name.toLowerCase().includes(query) || s.command && s.command.toLowerCase().includes(query) || s.url && s.url.toLowerCase().includes(query) || s.args && s.args.some((a) => a.toLowerCase().includes(query))
|
|
27600
|
+
) : allServers;
|
|
27601
|
+
const matchingNative = query ? allNative.filter(
|
|
27602
|
+
(n) => n.name && n.name.toLowerCase().includes(query) || n.sessionId && n.sessionId.toLowerCase().includes(query) || n.tools && n.tools.some((t) => t.toLowerCase().includes(query))
|
|
27603
|
+
) : allNative;
|
|
27604
|
+
if (query && !item.harness.name.toLowerCase().includes(query) && matchingServers.length === 0 && matchingNative.length === 0) {
|
|
27605
|
+
continue;
|
|
27606
|
+
}
|
|
27607
|
+
const card = el("section");
|
|
27608
|
+
card.className = "settings-skills-harness-card settings-mcp-harness-card";
|
|
27609
|
+
const head = el("div");
|
|
27610
|
+
head.className = "settings-skills-harness-head settings-mcp-harness-head";
|
|
27611
|
+
const hTitle = el("strong", item.harness.name);
|
|
27612
|
+
const badge = el("span");
|
|
27613
|
+
badge.className = `settings-skills-badge ${item.harness.mcp ? "settings-skills-badge--active" : ""}`;
|
|
27614
|
+
badge.textContent = item.harness.mcp ? tr(`${allServers.length} \u4E2A MCP`, `${allServers.length} MCPs`) : tr("\u672A\u63D0\u4F9B MCP \u63A5\u53E3", "No native MCP interface");
|
|
27615
|
+
head.append(hTitle, badge);
|
|
27616
|
+
card.append(head);
|
|
27617
|
+
if (!item.harness.mcp) {
|
|
27618
|
+
const note2 = el("p", tr("\u6B64 Harness \u5C1A\u65E0\u5DF2\u786E\u8BA4\u7684 MCP \u914D\u7F6E\u63A5\u53E3\u3002", "This Harness has no verified MCP configuration interface."));
|
|
27619
|
+
note2.className = "settings-mcp-notice";
|
|
27620
|
+
card.append(note2);
|
|
27621
|
+
} else if (item.error) {
|
|
27622
|
+
const errNote = el("p", item.error);
|
|
27623
|
+
errNote.className = "settings-mcp-notice";
|
|
27624
|
+
errNote.style.color = "#ef4444";
|
|
27625
|
+
card.append(errNote);
|
|
27626
|
+
} else {
|
|
27627
|
+
if (matchingNative.length > 0) {
|
|
27628
|
+
const nativeBox = el("div");
|
|
27629
|
+
nativeBox.className = "settings-mcp-native-box";
|
|
27630
|
+
const nativeTitle = el("div");
|
|
27631
|
+
nativeTitle.className = "settings-mcp-native-title";
|
|
27632
|
+
nativeTitle.textContent = tr(`\u{1F7E2} \u539F\u751F\u8FD0\u884C\u65F6\u6D3B\u8DC3 (${matchingNative.length})`, `\u{1F7E2} Active Native Sessions (${matchingNative.length})`);
|
|
27633
|
+
nativeBox.append(nativeTitle);
|
|
27634
|
+
for (const n of matchingNative) {
|
|
27635
|
+
const nRow = el("div");
|
|
27636
|
+
nRow.className = "settings-mcp-native-item";
|
|
27637
|
+
const nHeader = el("div");
|
|
27638
|
+
nHeader.append(
|
|
27639
|
+
el("strong", n.name || n.sessionId),
|
|
27640
|
+
el("span", ` \xB7 ${n.status}${n.tools?.length ? ` \xB7 ${n.tools.length} ${tr("\u5DE5\u5177", "tools")}` : ""}`)
|
|
27641
|
+
);
|
|
27642
|
+
nRow.append(nHeader);
|
|
27643
|
+
if (n.tools?.length) {
|
|
27644
|
+
const toolPreview = n.tools.slice(0, 4).join(", ") + (n.tools.length > 4 ? ` +${n.tools.length - 4}` : "");
|
|
27645
|
+
nRow.append(el("code", toolPreview));
|
|
27646
|
+
}
|
|
27647
|
+
nativeBox.append(nRow);
|
|
27648
|
+
}
|
|
27649
|
+
card.append(nativeBox);
|
|
27650
|
+
}
|
|
27651
|
+
if (matchingServers.length === 0) {
|
|
27652
|
+
const empty = el("p", query ? tr("\u65E0\u5339\u914D MCP \u670D\u52A1\u5668", "No matching MCP servers") : tr("\u6682\u672A\u914D\u7F6E MCP \u670D\u52A1\u5668", "No MCP servers configured"));
|
|
27653
|
+
empty.className = "settings-mcp-notice";
|
|
27654
|
+
card.append(empty);
|
|
27655
|
+
} else {
|
|
27656
|
+
const list = el("div");
|
|
27657
|
+
list.className = "settings-skills-list-compact settings-mcp-list-compact";
|
|
27658
|
+
for (const s of matchingServers) {
|
|
27659
|
+
const row = el("div");
|
|
27660
|
+
row.className = "settings-skills-item-row settings-mcp-item-row";
|
|
27661
|
+
const isHttp = s.transportType === "streamable_http" || !!s.url;
|
|
27662
|
+
const summary = isHttp ? s.url || "" : [s.command, ...s.args || []].filter(Boolean).join(" ");
|
|
27663
|
+
const stateText = !s.effective ? tr(" \xB7 \u88AB\u9879\u76EE\u8986\u76D6", " \xB7 Overridden") : !s.enabled ? tr(" \xB7 \u5DF2\u505C\u7528", " \xB7 Disabled") : s.appliedSessions ? tr(` \xB7 \u4F20\u5165 ${s.appliedSessions} \u4F1A\u8BDD`, ` \xB7 ${s.appliedSessions} sessions`) : tr(" \xB7 \u5DF2\u914D\u7F6E", " \xB7 Configured");
|
|
27664
|
+
const info = el("div");
|
|
27665
|
+
info.append(
|
|
27666
|
+
el("strong", `${s.name}${isHttp ? " [HTTP]" : " [stdio]"}`),
|
|
27667
|
+
el("span", stateText),
|
|
27668
|
+
el("code", summary)
|
|
27669
|
+
);
|
|
27670
|
+
const actions = el("div");
|
|
27671
|
+
actions.className = "settings-skills-item-actions settings-mcp-item-actions";
|
|
27672
|
+
actions.append(button(s.enabled ? tr("\u505C\u7528", "Disable") : tr("\u542F\u7528", "Enable"), () => {
|
|
27673
|
+
void mutateCustom(async (c) => {
|
|
27674
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
27675
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
27676
|
+
await c.saveMcp({
|
|
27677
|
+
harnessId: item.harness.id,
|
|
27678
|
+
scope: scopeType,
|
|
27679
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
27680
|
+
server: toManagedForCopy(s, !s.enabled)
|
|
27681
|
+
});
|
|
27682
|
+
return tr(
|
|
27683
|
+
`\u5DF2${s.enabled ? "\u505C\u7528" : "\u542F\u7528"}\u3010${s.name}\u3011\uFF08${item.harness.name}\uFF09\u3002`,
|
|
27684
|
+
`${s.enabled ? "Disabled" : "Enabled"} [${s.name}] (${item.harness.name}).`
|
|
27685
|
+
);
|
|
27686
|
+
});
|
|
27687
|
+
}));
|
|
27688
|
+
const otherSupported = supported.filter((h) => h.harness.id !== item.harness.id);
|
|
27689
|
+
if (otherSupported.length > 0) {
|
|
27690
|
+
actions.append(button(tr("\u540C\u6B65\u2026", "Sync\u2026"), () => {
|
|
27691
|
+
showMcpSyncDialog(s, item.harness, otherSupported.map((h) => h.harness));
|
|
27692
|
+
}));
|
|
27693
|
+
}
|
|
27694
|
+
actions.append(button(tr("\u7F16\u8F91", "Edit"), () => {
|
|
27695
|
+
editInHarness(item.harness.id, s.name);
|
|
27696
|
+
}));
|
|
27697
|
+
row.append(info, actions);
|
|
27698
|
+
list.append(row);
|
|
27699
|
+
}
|
|
27700
|
+
card.append(list);
|
|
27701
|
+
}
|
|
27702
|
+
const cardFooter = el("div");
|
|
27703
|
+
cardFooter.className = "settings-mcp-card-footer";
|
|
27704
|
+
const addBtn = button(tr("\uFF0B \u6DFB\u52A0 MCP", "\uFF0B Add MCP"), () => {
|
|
27705
|
+
editInHarness(item.harness.id, "__new__");
|
|
27706
|
+
});
|
|
27707
|
+
cardFooter.append(addBtn);
|
|
27708
|
+
card.append(cardFooter);
|
|
27709
|
+
}
|
|
27710
|
+
grid.append(card);
|
|
27711
|
+
}
|
|
27712
|
+
viewsContainer.append(grid);
|
|
27713
|
+
} else {
|
|
27714
|
+
const matrixList = el("div");
|
|
27715
|
+
matrixList.className = "settings-skills-matrix-list settings-mcp-matrix-list";
|
|
27716
|
+
let matchedCount = 0;
|
|
27717
|
+
for (const [serverName, installations] of uniqueServers.entries()) {
|
|
27718
|
+
const firstServer = installations[0].server;
|
|
27719
|
+
const isHttp = firstServer.transportType === "streamable_http" || !!firstServer.url;
|
|
27720
|
+
const summary = isHttp ? firstServer.url || "" : [firstServer.command, ...firstServer.args || []].filter(Boolean).join(" ");
|
|
27721
|
+
if (query && !serverName.toLowerCase().includes(query) && !summary.toLowerCase().includes(query) && !installations.some((i) => i.harness.name.toLowerCase().includes(query))) {
|
|
27722
|
+
continue;
|
|
27723
|
+
}
|
|
27724
|
+
matchedCount++;
|
|
27725
|
+
const card = el("section");
|
|
27726
|
+
card.className = "settings-skills-matrix-card settings-mcp-matrix-card";
|
|
27727
|
+
const head = el("div");
|
|
27728
|
+
head.className = "settings-skills-matrix-head settings-mcp-matrix-head";
|
|
27729
|
+
const titleWrap = el("div");
|
|
27730
|
+
titleWrap.style.display = "flex";
|
|
27731
|
+
titleWrap.style.alignItems = "center";
|
|
27732
|
+
titleWrap.style.gap = "8px";
|
|
27733
|
+
titleWrap.append(el("strong", serverName), el("span", `[${isHttp ? "HTTP" : "stdio"}]`));
|
|
27734
|
+
head.append(
|
|
27735
|
+
titleWrap,
|
|
27736
|
+
el("span", tr(`\u5DF2\u5728 ${installations.length} \u4E2A Harness \u914D\u7F6E`, `Configured in ${installations.length} harnesses`))
|
|
27737
|
+
);
|
|
27738
|
+
const codeSummary = el("code");
|
|
27739
|
+
codeSummary.style.fontSize = "11px";
|
|
27740
|
+
codeSummary.style.color = "var(--settings-muted)";
|
|
27741
|
+
codeSummary.style.overflowWrap = "anywhere";
|
|
27742
|
+
codeSummary.textContent = summary;
|
|
27743
|
+
const tags = el("div");
|
|
27744
|
+
tags.className = "settings-skills-tags settings-mcp-tags";
|
|
27745
|
+
for (const inst of installations) {
|
|
27746
|
+
const tag = el("span");
|
|
27747
|
+
tag.className = `settings-skills-tag settings-mcp-tag ${inst.server.enabled ? "" : "settings-mcp-tag--disabled"}`;
|
|
27748
|
+
tag.textContent = `\u2713 ${inst.harness.name}${inst.server.enabled ? "" : tr(" (\u5DF2\u505C\u7528)", " (disabled)")}`;
|
|
27749
|
+
tags.append(tag);
|
|
27750
|
+
}
|
|
27751
|
+
const installedIds = new Set(installations.map((i) => i.harness.id));
|
|
27752
|
+
const missingHarnesses = supported.filter((h) => !installedIds.has(h.harness.id));
|
|
27753
|
+
const syncRow = el("div");
|
|
27754
|
+
syncRow.className = "settings-skills-sync-actions settings-mcp-sync-actions";
|
|
27755
|
+
if (missingHarnesses.length > 0) {
|
|
27756
|
+
const label = el("span", tr("\u672A\u914D\u7F6E\u4E8E\uFF1A", "Not configured in: "));
|
|
27757
|
+
label.style.fontSize = "11px";
|
|
27758
|
+
label.style.color = "var(--settings-muted)";
|
|
27759
|
+
syncRow.append(label);
|
|
27760
|
+
for (const missing of missingHarnesses) {
|
|
27761
|
+
const installBtn = button(`+ ${missing.harness.name}`, () => {
|
|
27762
|
+
void mutateCustom(async (c) => {
|
|
27763
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
27764
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
27765
|
+
await c.saveMcp({
|
|
27766
|
+
harnessId: missing.harness.id,
|
|
27767
|
+
scope: scopeType,
|
|
27768
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
27769
|
+
server: toManagedForCopy(firstServer)
|
|
27770
|
+
});
|
|
27771
|
+
return tr(`\u5DF2\u5C06 MCP\u3010${serverName}\u3011\u540C\u6B65\u5230 ${missing.harness.name}\u3002`, `Configured [${serverName}] to ${missing.harness.name}.`);
|
|
27772
|
+
});
|
|
27773
|
+
});
|
|
27774
|
+
syncRow.append(installBtn);
|
|
27775
|
+
}
|
|
27776
|
+
if (missingHarnesses.length > 1) {
|
|
27777
|
+
const installAllBtn = button(tr("\u4E00\u952E\u540C\u6B65\u5230\u5168\u90E8\u652F\u6301\u7684 Harness", "Sync to all supported"), () => {
|
|
27778
|
+
void mutateCustom(async (c) => {
|
|
27779
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
27780
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
27781
|
+
const successes = [];
|
|
27782
|
+
for (const missing of missingHarnesses) {
|
|
27783
|
+
try {
|
|
27784
|
+
await c.saveMcp({
|
|
27785
|
+
harnessId: missing.harness.id,
|
|
27786
|
+
scope: scopeType,
|
|
27787
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
27788
|
+
server: toManagedForCopy(firstServer)
|
|
27789
|
+
});
|
|
27790
|
+
successes.push(missing.harness.name);
|
|
27791
|
+
} catch {
|
|
27792
|
+
}
|
|
27793
|
+
}
|
|
27794
|
+
return tr(`\u5DF2\u5C06 MCP\u3010${serverName}\u3011\u540C\u6B65\u5230\uFF1A${successes.join("\u3001")}\u3002`, `Synced [${serverName}] to: ${successes.join(", ")}.`);
|
|
27795
|
+
});
|
|
27796
|
+
}, true);
|
|
27797
|
+
syncRow.append(installAllBtn);
|
|
27798
|
+
}
|
|
27799
|
+
} else {
|
|
27800
|
+
const complete = el("span", tr("\u2713 \u5DF2\u5728\u6240\u6709\u652F\u6301\u7684 Harness \u4E2D\u914D\u7F6E\u5C31\u7EEA", "\u2713 Ready across all supported Harnesses"));
|
|
27801
|
+
complete.style.fontSize = "11px";
|
|
27802
|
+
complete.style.color = "#10a37f";
|
|
27803
|
+
syncRow.append(complete);
|
|
27804
|
+
}
|
|
27805
|
+
card.append(head, codeSummary, tags, syncRow);
|
|
27806
|
+
matrixList.append(card);
|
|
27807
|
+
}
|
|
27808
|
+
if (matchedCount === 0) {
|
|
27809
|
+
const empty = el("div", tr("\u672A\u5339\u914D\u5230\u4EFB\u4F55 MCP \u670D\u52A1\u5668\u3002", "No MCP servers matched the search."));
|
|
27810
|
+
empty.className = "settings-integrations-empty";
|
|
27811
|
+
matrixList.append(empty);
|
|
27812
|
+
}
|
|
27813
|
+
viewsContainer.append(matrixList);
|
|
27814
|
+
}
|
|
27815
|
+
};
|
|
27816
|
+
btnByHarness.addEventListener("click", () => {
|
|
27817
|
+
activeTab = "harness";
|
|
27818
|
+
btnByHarness.dataset.active = "true";
|
|
27819
|
+
delete btnByServer.dataset.active;
|
|
27820
|
+
updateView();
|
|
27821
|
+
}, { signal: lifecycle.signal });
|
|
27822
|
+
btnByServer.addEventListener("click", () => {
|
|
27823
|
+
activeTab = "server";
|
|
27824
|
+
btnByServer.dataset.active = "true";
|
|
27825
|
+
delete btnByHarness.dataset.active;
|
|
27826
|
+
updateView();
|
|
27827
|
+
}, { signal: lifecycle.signal });
|
|
27828
|
+
searchInput.addEventListener("input", () => {
|
|
27829
|
+
updateView();
|
|
27830
|
+
}, { signal: lifecycle.signal });
|
|
27831
|
+
updateView();
|
|
27832
|
+
body.append(statsBar, toolbar, viewsContainer);
|
|
27833
|
+
};
|
|
27834
|
+
const showSyncDialog = (skill, sourceHarness, targetHarnesses) => {
|
|
27835
|
+
const sourceDir = skill.path.replace(/[/\\][^/\\]+$/, "");
|
|
27836
|
+
if (!targetHarnesses.length) {
|
|
27837
|
+
fail(new Error(tr("\u6CA1\u6709\u5176\u4ED6\u652F\u6301\u6280\u80FD\u7684\u539F\u751F Harness \u53EF\u4F9B\u540C\u6B65\u3002", "No other native Harnesses support skills for sync.")));
|
|
27838
|
+
return;
|
|
27839
|
+
}
|
|
27840
|
+
const modal = el("dialog");
|
|
27841
|
+
modal.className = "settings-skills-sync-modal";
|
|
27842
|
+
const mTitle = el("h3", tr(`\u540C\u6B65\u6280\u80FD\u3010${skill.name}\u3011`, `Sync skill [${skill.name}]`));
|
|
27843
|
+
const mDesc = el("p", tr(`\u4ECE ${sourceHarness.name} \u590D\u5236\u8BE5\u6280\u80FD\u5230\u4EE5\u4E0B\u9009\u4E2D\u7684 Harness\uFF1A`, `Copy skill from ${sourceHarness.name} to selected Harnesses:`));
|
|
27844
|
+
const form = el("form");
|
|
27845
|
+
const checkList = el("div");
|
|
27846
|
+
checkList.style.display = "grid";
|
|
27847
|
+
checkList.style.gap = "8px";
|
|
27848
|
+
checkList.style.margin = "12px 0";
|
|
27849
|
+
const checkboxes = [];
|
|
27850
|
+
for (const h of targetHarnesses) {
|
|
27851
|
+
const label = el("label");
|
|
27852
|
+
label.style.display = "flex";
|
|
27853
|
+
label.style.gap = "8px";
|
|
27854
|
+
label.style.alignItems = "center";
|
|
27855
|
+
label.style.cursor = "pointer";
|
|
27856
|
+
const cb = el("input");
|
|
27857
|
+
cb.type = "checkbox";
|
|
27858
|
+
cb.value = h.id;
|
|
27859
|
+
cb.checked = true;
|
|
27860
|
+
checkboxes.push(cb);
|
|
27861
|
+
label.append(cb, el("span", h.name));
|
|
27862
|
+
checkList.append(label);
|
|
27863
|
+
}
|
|
27864
|
+
const btnRow = el("div");
|
|
27865
|
+
btnRow.style.display = "flex";
|
|
27866
|
+
btnRow.style.gap = "8px";
|
|
27867
|
+
btnRow.style.justifyContent = "flex-end";
|
|
27868
|
+
btnRow.style.marginTop = "14px";
|
|
27869
|
+
const cancelBtn = button(tr("\u53D6\u6D88", "Cancel"), () => {
|
|
27870
|
+
modal.close();
|
|
27871
|
+
modal.remove();
|
|
27872
|
+
});
|
|
27873
|
+
const confirmBtn = button(tr("\u786E\u8BA4\u540C\u6B65", "Confirm Sync"), () => {
|
|
27874
|
+
const selectedHarnesses = checkboxes.filter((cb) => cb.checked).map((cb) => cb.value);
|
|
27875
|
+
modal.close();
|
|
27876
|
+
modal.remove();
|
|
27877
|
+
if (!selectedHarnesses.length) return;
|
|
27878
|
+
void mutateCustom(async (c) => {
|
|
27879
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
27880
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
27881
|
+
const successes = [];
|
|
27882
|
+
const errors = [];
|
|
27883
|
+
for (const targetId of selectedHarnesses) {
|
|
27884
|
+
const targetH = targetHarnesses.find((h) => h.id === targetId);
|
|
27885
|
+
try {
|
|
27886
|
+
await c.changeSkill({
|
|
27887
|
+
harnessId: targetId,
|
|
27888
|
+
scope: scopeType,
|
|
27889
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
27890
|
+
name: skill.name,
|
|
27891
|
+
source: sourceDir,
|
|
27892
|
+
action: "install"
|
|
27893
|
+
});
|
|
27894
|
+
if (targetH) successes.push(targetH.name);
|
|
27895
|
+
} catch (err) {
|
|
27896
|
+
errors.push(`${targetH?.name || targetId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
27897
|
+
}
|
|
27898
|
+
}
|
|
27899
|
+
return tr(
|
|
27900
|
+
`\u540C\u6B65\u5B8C\u6210\uFF01\u5DF2\u5B89\u88C5\u5230\uFF1A${successes.join("\u3001")}${errors.length ? `\uFF08\u8DF3\u8FC7: ${errors.join("; ")}\uFF09` : ""}`,
|
|
27901
|
+
`Sync complete! Installed to: ${successes.join(", ")}${errors.length ? ` (Skipped: ${errors.join("; ")})` : ""}`
|
|
27902
|
+
);
|
|
27903
|
+
});
|
|
27904
|
+
}, true);
|
|
27905
|
+
btnRow.append(cancelBtn, confirmBtn);
|
|
27906
|
+
form.append(checkList, btnRow);
|
|
27907
|
+
modal.append(mTitle, mDesc, form);
|
|
27908
|
+
content.append(modal);
|
|
27909
|
+
modal.showModal();
|
|
27910
|
+
};
|
|
27911
|
+
const renderSkills = (snapshot) => {
|
|
27912
|
+
const drop = el("div");
|
|
27913
|
+
drop.className = "settings-skill-dropzone";
|
|
27914
|
+
drop.tabIndex = 0;
|
|
27915
|
+
drop.setAttribute("role", "button");
|
|
27916
|
+
const dropTitle = el("strong", tr("\u62D6\u62FD\u6280\u80FD\u5230\u8FD9\u91CC", "Drop a skill here"));
|
|
27917
|
+
const dropHint = el("span", tr("\u652F\u6301\u5355\u4E2A Markdown \u6587\u4EF6\u6216\u5305\u542B SKILL.md \u7684\u6587\u4EF6\u5939 \xB7 \u6700\u5927 10 MB / 500 \u4E2A\u6587\u4EF6", "Supports one Markdown file or a folder containing SKILL.md \xB7 up to 10 MB / 500 files"));
|
|
27918
|
+
const picker = el("input");
|
|
27919
|
+
picker.type = "file";
|
|
27920
|
+
picker.multiple = true;
|
|
27921
|
+
picker.hidden = true;
|
|
27922
|
+
picker.setAttribute("webkitdirectory", "");
|
|
27923
|
+
const choose = button(tr("\u9009\u62E9\u6280\u80FD\u6587\u4EF6\u5939", "Choose skill folder"), () => picker.click());
|
|
27924
|
+
drop.append(dropTitle, dropHint, choose, picker);
|
|
27925
|
+
const list = el("div");
|
|
27926
|
+
list.className = "settings-integrations-list";
|
|
27927
|
+
const installFiles = async (incoming) => {
|
|
27928
|
+
const parsed = await parseDroppedSkills(incoming);
|
|
27929
|
+
await mutate((value, selected) => value.changeSkill({ ...selected, name: parsed.name, files: parsed.files, action: "install" }));
|
|
27930
|
+
};
|
|
27931
|
+
const receiveFiles = async (transfer) => {
|
|
27932
|
+
try {
|
|
27933
|
+
let incoming = [];
|
|
27934
|
+
if ("items" in transfer && transfer.items.length) {
|
|
27935
|
+
const entries = [...transfer.items].map((item) => item.webkitGetAsEntry?.() ?? null).filter((entry) => entry !== null);
|
|
27936
|
+
if (entries.length) for (const entry of entries) await walkEntry(entry, "", incoming);
|
|
27937
|
+
}
|
|
27938
|
+
const fileList = "files" in transfer ? transfer.files : transfer;
|
|
27939
|
+
if (!incoming.length) incoming = [...fileList].map((file2) => ({ path: file2.webkitRelativePath || file2.name, file: file2 }));
|
|
27940
|
+
await installFiles(incoming);
|
|
27941
|
+
} catch (error51) {
|
|
27942
|
+
fail(error51);
|
|
27943
|
+
}
|
|
27944
|
+
};
|
|
27945
|
+
for (const eventName of ["dragenter", "dragover"]) drop.addEventListener(eventName, (event) => {
|
|
27946
|
+
event.preventDefault();
|
|
27947
|
+
if (!busy) drop.dataset.dragOver = "true";
|
|
27948
|
+
}, { signal: lifecycle.signal });
|
|
27949
|
+
drop.addEventListener("dragleave", () => {
|
|
27950
|
+
delete drop.dataset.dragOver;
|
|
27951
|
+
}, { signal: lifecycle.signal });
|
|
27952
|
+
drop.addEventListener("drop", (event) => {
|
|
27953
|
+
event.preventDefault();
|
|
27954
|
+
delete drop.dataset.dragOver;
|
|
27955
|
+
if (!busy && event.dataTransfer) void receiveFiles(event.dataTransfer);
|
|
27956
|
+
}, { signal: lifecycle.signal });
|
|
27957
|
+
picker.addEventListener("change", () => {
|
|
27958
|
+
if (picker.files?.length) void receiveFiles(picker.files);
|
|
27959
|
+
picker.value = "";
|
|
27960
|
+
}, { signal: lifecycle.signal });
|
|
27961
|
+
if (!snapshot.skillsSupported) {
|
|
27962
|
+
drop.dataset.disabled = "true";
|
|
27963
|
+
choose.disabled = true;
|
|
27964
|
+
dropHint.textContent = tr("\u6B64 Harness \u7684\u539F\u751F\u6280\u80FD\u76EE\u5F55\u5C1A\u672A\u786E\u8BA4\u3002", "This Harness has no verified native skill directory.");
|
|
27965
|
+
}
|
|
27966
|
+
for (const skill of snapshot.skills) {
|
|
27967
|
+
const row = el("section");
|
|
27968
|
+
row.className = "settings-integrations-row";
|
|
27969
|
+
const detail = el("div");
|
|
27970
|
+
detail.append(el("strong", skill.name), el("p", skill.enabled ? tr("\u5DF2\u5B89\u88C5 \xB7 \u52A0\u8F7D\u72B6\u6001\u672A\u62A5\u544A", "Installed \xB7 load status unreported") : tr("\u5DF2\u505C\u7528 \xB7 \u6587\u4EF6\u5DF2\u4FDD\u7559", "Disabled \xB7 files retained")), el("code", skill.path));
|
|
27971
|
+
row.append(detail);
|
|
27972
|
+
if (skill.writable) {
|
|
27973
|
+
row.append(button(skill.enabled ? tr("\u505C\u7528", "Disable") : tr("\u6062\u590D", "Restore"), () => {
|
|
27974
|
+
void mutate((value, selected) => value.changeSkill({ ...selected, name: skill.name, root: skill.root, action: skill.enabled ? "disable" : "enable" }));
|
|
27975
|
+
}));
|
|
27976
|
+
} else {
|
|
27977
|
+
row.append(el("span", tr("\u94FE\u63A5\u76EE\u5F55\uFF0C\u53EA\u8BFB", "Linked directory, read-only")));
|
|
27978
|
+
}
|
|
27979
|
+
const syncBtn = button(tr("\u540C\u6B65\u5230\u2026", "Sync to\u2026"), () => {
|
|
27980
|
+
void lifecycle.runLatest(() => client().integrationCatalog(), {
|
|
27981
|
+
success: (cat) => {
|
|
27982
|
+
const targetHarnesses = cat.harnesses.filter((h) => h.skills && h.id !== snapshot.harnessId);
|
|
27983
|
+
showSyncDialog(skill, { id: snapshot.harnessId, name: snapshot.harnessId }, targetHarnesses);
|
|
27984
|
+
},
|
|
27985
|
+
failure: fail
|
|
27986
|
+
});
|
|
27987
|
+
});
|
|
27988
|
+
row.append(syncBtn);
|
|
27989
|
+
list.append(row);
|
|
27990
|
+
}
|
|
27991
|
+
if (!snapshot.skills.length) {
|
|
27992
|
+
const empty = el("div", tr("\u8FD8\u6CA1\u6709\u53D1\u73B0\u6280\u80FD\u3002\u62D6\u5165\u6587\u4EF6\u5373\u53EF\u5B89\u88C5\u3002", "No skills found. Drop files above to install one."));
|
|
27993
|
+
empty.className = "settings-integrations-empty";
|
|
27994
|
+
list.append(empty);
|
|
27995
|
+
}
|
|
27996
|
+
body.append(drop, list);
|
|
27997
|
+
};
|
|
27998
|
+
const renderSkillsOverview = (catalog, items) => {
|
|
27999
|
+
const drop = el("div");
|
|
28000
|
+
drop.className = "settings-skill-dropzone";
|
|
28001
|
+
drop.tabIndex = 0;
|
|
28002
|
+
drop.setAttribute("role", "button");
|
|
28003
|
+
const dropTitle = el("strong", tr("\u62D6\u62FD\u6280\u80FD\u5230\u8FD9\u91CC\uFF0C\u4E00\u952E\u5206\u53D1\u5B89\u88C5", "Drop a skill here to distribute and install"));
|
|
28004
|
+
const dropHint = el("span", tr("\u652F\u6301\u5355\u4E2A Markdown \u6587\u4EF6\u6216\u5305\u542B SKILL.md \u7684\u6587\u4EF6\u5939 \xB7 \u81EA\u52A8\u5B89\u88C5\u81F3\u6240\u9009 Harness", "Supports one Markdown file or folder with SKILL.md \xB7 Installs to target Harnesses"));
|
|
28005
|
+
const targetRow = el("div");
|
|
28006
|
+
targetRow.className = "settings-skills-target-row";
|
|
28007
|
+
const targetLabel = el("span", tr("\u5B89\u88C5\u76EE\u6807\uFF1A", "Target: "));
|
|
28008
|
+
const targetSelect = el("select");
|
|
28009
|
+
targetSelect.setAttribute("aria-label", tr("\u9009\u62E9\u76EE\u6807 Harness", "Select target Harness"));
|
|
28010
|
+
const allOption = el("option", tr("\u5168\u90E8\u652F\u6301\u7684 Harness", "All supported Harnesses"));
|
|
28011
|
+
allOption.value = "__all__";
|
|
28012
|
+
targetSelect.append(allOption);
|
|
28013
|
+
for (const item of items) {
|
|
28014
|
+
if (item.harness.skills) {
|
|
28015
|
+
const opt = el("option", item.harness.name);
|
|
28016
|
+
opt.value = item.harness.id;
|
|
28017
|
+
targetSelect.append(opt);
|
|
28018
|
+
}
|
|
28019
|
+
}
|
|
28020
|
+
const picker = el("input");
|
|
28021
|
+
picker.type = "file";
|
|
28022
|
+
picker.multiple = true;
|
|
28023
|
+
picker.hidden = true;
|
|
28024
|
+
picker.setAttribute("webkitdirectory", "");
|
|
28025
|
+
const choose = button(tr("\u9009\u62E9\u6280\u80FD\u6587\u4EF6\u5939", "Choose skill folder"), () => picker.click());
|
|
28026
|
+
targetRow.append(targetLabel, targetSelect, choose, picker);
|
|
28027
|
+
drop.append(dropTitle, dropHint, targetRow);
|
|
28028
|
+
const receiveOverviewFiles = async (transfer) => {
|
|
28029
|
+
try {
|
|
28030
|
+
let incoming = [];
|
|
28031
|
+
if ("items" in transfer && transfer.items.length) {
|
|
28032
|
+
const entries = [...transfer.items].map((item) => item.webkitGetAsEntry?.() ?? null).filter((entry) => entry !== null);
|
|
28033
|
+
if (entries.length) for (const entry of entries) await walkEntry(entry, "", incoming);
|
|
28034
|
+
}
|
|
28035
|
+
const fileList = "files" in transfer ? transfer.files : transfer;
|
|
28036
|
+
if (!incoming.length) incoming = [...fileList].map((file2) => ({ path: file2.webkitRelativePath || file2.name, file: file2 }));
|
|
28037
|
+
const parsed = await parseDroppedSkills(incoming);
|
|
28038
|
+
await mutateCustom(async (c) => {
|
|
28039
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
28040
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
28041
|
+
const targets = targetSelect.value === "__all__" ? items.filter((i) => i.harness.skills).map((i) => i.harness) : items.filter((i) => i.harness.id === targetSelect.value).map((i) => i.harness);
|
|
28042
|
+
if (!targets.length) throw new Error(tr("\u6CA1\u6709\u53EF\u5B89\u88C5\u7684\u76EE\u6807 Harness\u3002", "No available target Harnesses."));
|
|
28043
|
+
const successNames = [];
|
|
28044
|
+
const errors = [];
|
|
28045
|
+
for (const target of targets) {
|
|
28046
|
+
try {
|
|
28047
|
+
await c.changeSkill({
|
|
28048
|
+
harnessId: target.id,
|
|
28049
|
+
scope: scopeType,
|
|
28050
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
28051
|
+
name: parsed.name,
|
|
28052
|
+
files: parsed.files,
|
|
28053
|
+
action: "install"
|
|
28054
|
+
});
|
|
28055
|
+
successNames.push(target.name);
|
|
28056
|
+
} catch (err) {
|
|
28057
|
+
errors.push(`${target.name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
28058
|
+
}
|
|
28059
|
+
}
|
|
28060
|
+
if (!successNames.length && errors.length) throw new Error(errors.join("; "));
|
|
28061
|
+
return tr(
|
|
28062
|
+
`\u5DF2\u6210\u529F\u5C06\u6280\u80FD\u3010${parsed.name}\u3011\u5B89\u88C5\u5230\uFF1A${successNames.join("\u3001")}${errors.length ? `\uFF08\u8DF3\u8FC7: ${errors.join("; ")}\uFF09` : ""}`,
|
|
28063
|
+
`Successfully installed skill [${parsed.name}] to: ${successNames.join(", ")}${errors.length ? ` (Skipped: ${errors.join("; ")})` : ""}`
|
|
28064
|
+
);
|
|
28065
|
+
});
|
|
28066
|
+
} catch (error51) {
|
|
28067
|
+
fail(error51);
|
|
28068
|
+
}
|
|
28069
|
+
};
|
|
28070
|
+
for (const eventName of ["dragenter", "dragover"]) drop.addEventListener(eventName, (event) => {
|
|
28071
|
+
event.preventDefault();
|
|
28072
|
+
if (!busy) drop.dataset.dragOver = "true";
|
|
28073
|
+
}, { signal: lifecycle.signal });
|
|
28074
|
+
drop.addEventListener("dragleave", () => {
|
|
28075
|
+
delete drop.dataset.dragOver;
|
|
28076
|
+
}, { signal: lifecycle.signal });
|
|
28077
|
+
drop.addEventListener("drop", (event) => {
|
|
28078
|
+
event.preventDefault();
|
|
28079
|
+
delete drop.dataset.dragOver;
|
|
28080
|
+
if (!busy && event.dataTransfer) void receiveOverviewFiles(event.dataTransfer);
|
|
28081
|
+
}, { signal: lifecycle.signal });
|
|
28082
|
+
picker.addEventListener("change", () => {
|
|
28083
|
+
if (picker.files?.length) void receiveOverviewFiles(picker.files);
|
|
28084
|
+
picker.value = "";
|
|
28085
|
+
}, { signal: lifecycle.signal });
|
|
28086
|
+
const supported = items.filter((i) => i.harness.skills);
|
|
28087
|
+
const uniqueSkills = /* @__PURE__ */ new Map();
|
|
28088
|
+
for (const item of items) {
|
|
28089
|
+
if (!item.snapshot?.skills) continue;
|
|
28090
|
+
for (const s of item.snapshot.skills) {
|
|
28091
|
+
const list = uniqueSkills.get(s.name) || [];
|
|
28092
|
+
list.push({ harness: item.harness, skill: s });
|
|
28093
|
+
uniqueSkills.set(s.name, list);
|
|
28094
|
+
}
|
|
28095
|
+
}
|
|
28096
|
+
const statsBar = el("div");
|
|
28097
|
+
statsBar.className = "settings-skills-overview-stats";
|
|
28098
|
+
const statsSummary = el("span", tr(
|
|
28099
|
+
`\u{1F4CA} \u5171 ${items.length} \u4E2A Harness \xB7 ${supported.length} \u4E2A\u5DF2\u914D\u7F6E\u539F\u751F\u6280\u80FD\u76EE\u5F55 \xB7 \u53D1\u73B0 ${uniqueSkills.size} \u4E2A\u53BB\u91CD\u6280\u80FD`,
|
|
28100
|
+
`\u{1F4CA} ${items.length} Harnesses \xB7 ${supported.length} with native skills \xB7 ${uniqueSkills.size} unique skills found`
|
|
28101
|
+
));
|
|
28102
|
+
statsBar.append(statsSummary);
|
|
28103
|
+
const toolbar = el("div");
|
|
28104
|
+
toolbar.className = "settings-integrations-toolbar";
|
|
28105
|
+
const searchInput = el("input");
|
|
28106
|
+
searchInput.type = "search";
|
|
28107
|
+
searchInput.placeholder = tr("\u6309\u540D\u79F0\u8FC7\u6EE4\u6280\u80FD\u2026", "Filter skills by name\u2026");
|
|
28108
|
+
searchInput.setAttribute("aria-label", searchInput.placeholder);
|
|
28109
|
+
const tabsWrap = el("div");
|
|
28110
|
+
tabsWrap.className = "settings-skills-tabs";
|
|
28111
|
+
const btnByHarness = el("button", tr("\u6309 Harness \u67E5\u770B", "By Harness"));
|
|
28112
|
+
btnByHarness.type = "button";
|
|
28113
|
+
btnByHarness.className = "settings-skills-tab-btn";
|
|
28114
|
+
btnByHarness.dataset.active = "true";
|
|
28115
|
+
const btnBySkill = el("button", tr("\u6309 Skill \u6C47\u603B", "By Skill"));
|
|
28116
|
+
btnBySkill.type = "button";
|
|
28117
|
+
btnBySkill.className = "settings-skills-tab-btn";
|
|
28118
|
+
tabsWrap.append(btnByHarness, btnBySkill);
|
|
28119
|
+
toolbar.append(searchInput, tabsWrap);
|
|
28120
|
+
const viewsContainer = el("div");
|
|
28121
|
+
viewsContainer.className = "settings-skills-views";
|
|
28122
|
+
let activeTab = "harness";
|
|
28123
|
+
const updateView = () => {
|
|
28124
|
+
viewsContainer.replaceChildren();
|
|
28125
|
+
const query = searchInput.value.trim().toLowerCase();
|
|
28126
|
+
if (activeTab === "harness") {
|
|
28127
|
+
const grid = el("div");
|
|
28128
|
+
grid.className = "settings-skills-overview-grid";
|
|
28129
|
+
for (const item of items) {
|
|
28130
|
+
const allSkills = item.snapshot?.skills || [];
|
|
28131
|
+
const matchingSkills = query ? allSkills.filter((s) => s.name.toLowerCase().includes(query) || s.path.toLowerCase().includes(query)) : allSkills;
|
|
28132
|
+
if (query && !item.harness.name.toLowerCase().includes(query) && matchingSkills.length === 0) {
|
|
28133
|
+
continue;
|
|
28134
|
+
}
|
|
28135
|
+
const card = el("section");
|
|
28136
|
+
card.className = "settings-skills-harness-card";
|
|
28137
|
+
const head = el("div");
|
|
28138
|
+
head.className = "settings-skills-harness-head";
|
|
28139
|
+
const hTitle = el("strong", item.harness.name);
|
|
28140
|
+
const badge = el("span");
|
|
28141
|
+
badge.className = `settings-skills-badge ${item.harness.skills ? "settings-skills-badge--active" : ""}`;
|
|
28142
|
+
badge.textContent = item.harness.skills ? tr(`${allSkills.length} \u4E2A\u6280\u80FD`, `${allSkills.length} skills`) : tr("\u672A\u914D\u7F6E\u76EE\u5F55", "No native directory");
|
|
28143
|
+
head.append(hTitle, badge);
|
|
28144
|
+
card.append(head);
|
|
28145
|
+
if (!item.harness.skills) {
|
|
28146
|
+
const note2 = el("p", tr("\u6B64 Harness \u6682\u672A\u914D\u7F6E\u539F\u751F\u6280\u80FD\u76EE\u5F55\u3002", "This Harness has no verified native skill directory."));
|
|
28147
|
+
note2.className = "settings-mcp-notice";
|
|
28148
|
+
card.append(note2);
|
|
28149
|
+
} else if (matchingSkills.length === 0) {
|
|
28150
|
+
const empty = el("p", query ? tr("\u65E0\u5339\u914D\u6280\u80FD", "No matching skills") : tr("\u6682\u672A\u5B89\u88C5\u6280\u80FD", "No skills installed"));
|
|
28151
|
+
empty.className = "settings-mcp-notice";
|
|
28152
|
+
card.append(empty);
|
|
28153
|
+
} else {
|
|
28154
|
+
const list = el("div");
|
|
28155
|
+
list.className = "settings-skills-list-compact";
|
|
28156
|
+
for (const s of matchingSkills) {
|
|
28157
|
+
const row = el("div");
|
|
28158
|
+
row.className = "settings-skills-item-row";
|
|
28159
|
+
const info = el("div");
|
|
28160
|
+
info.append(
|
|
28161
|
+
el("strong", s.name),
|
|
28162
|
+
el("span", s.enabled ? tr(" \xB7 \u5DF2\u5B89\u88C5", " \xB7 Installed") : tr(" \xB7 \u5DF2\u505C\u7528", " \xB7 Disabled")),
|
|
28163
|
+
el("code", s.path)
|
|
28164
|
+
);
|
|
28165
|
+
const actions = el("div");
|
|
28166
|
+
actions.className = "settings-skills-item-actions";
|
|
28167
|
+
if (s.writable) {
|
|
28168
|
+
actions.append(button(s.enabled ? tr("\u505C\u7528", "Disable") : tr("\u6062\u590D", "Restore"), () => {
|
|
28169
|
+
void mutateCustom(async (c) => {
|
|
28170
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
28171
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
28172
|
+
await c.changeSkill({
|
|
28173
|
+
harnessId: item.harness.id,
|
|
28174
|
+
scope: scopeType,
|
|
28175
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
28176
|
+
name: s.name,
|
|
28177
|
+
root: s.root,
|
|
28178
|
+
action: s.enabled ? "disable" : "enable"
|
|
28179
|
+
});
|
|
28180
|
+
});
|
|
28181
|
+
}));
|
|
28182
|
+
}
|
|
28183
|
+
const otherSupported = supported.filter((h) => h.harness.id !== item.harness.id);
|
|
28184
|
+
if (otherSupported.length > 0) {
|
|
28185
|
+
const syncBtn = button(tr("\u540C\u6B65\u2026", "Sync\u2026"), () => {
|
|
28186
|
+
showSyncDialog(s, item.harness, otherSupported.map((h) => h.harness));
|
|
28187
|
+
});
|
|
28188
|
+
actions.append(syncBtn);
|
|
28189
|
+
}
|
|
28190
|
+
row.append(info, actions);
|
|
28191
|
+
list.append(row);
|
|
28192
|
+
}
|
|
28193
|
+
card.append(list);
|
|
28194
|
+
}
|
|
28195
|
+
grid.append(card);
|
|
28196
|
+
}
|
|
28197
|
+
viewsContainer.append(grid);
|
|
28198
|
+
} else {
|
|
28199
|
+
const matrixList = el("div");
|
|
28200
|
+
matrixList.className = "settings-skills-matrix-list";
|
|
28201
|
+
let matchedCount = 0;
|
|
28202
|
+
for (const [skillName, installations] of uniqueSkills.entries()) {
|
|
28203
|
+
if (query && !skillName.toLowerCase().includes(query) && !installations.some((i) => i.skill.path.toLowerCase().includes(query))) {
|
|
28204
|
+
continue;
|
|
28205
|
+
}
|
|
28206
|
+
matchedCount++;
|
|
28207
|
+
const card = el("section");
|
|
28208
|
+
card.className = "settings-skills-matrix-card";
|
|
28209
|
+
const head = el("div");
|
|
28210
|
+
head.className = "settings-skills-matrix-head";
|
|
28211
|
+
head.append(
|
|
28212
|
+
el("strong", skillName),
|
|
28213
|
+
el("span", tr(`\u5DF2\u5728 ${installations.length} \u4E2A Harness \u5B89\u88C5`, `Installed in ${installations.length} harnesses`))
|
|
28214
|
+
);
|
|
28215
|
+
const tags = el("div");
|
|
28216
|
+
tags.className = "settings-skills-tags";
|
|
28217
|
+
for (const inst of installations) {
|
|
28218
|
+
const tag = el("span");
|
|
28219
|
+
tag.className = "settings-skills-tag";
|
|
28220
|
+
tag.textContent = `\u2713 ${inst.harness.name}${inst.skill.enabled ? "" : tr(" (\u5DF2\u505C\u7528)", " (disabled)")}`;
|
|
28221
|
+
tags.append(tag);
|
|
28222
|
+
}
|
|
28223
|
+
const installedIds = new Set(installations.map((i) => i.harness.id));
|
|
28224
|
+
const missingHarnesses = supported.filter((h) => !installedIds.has(h.harness.id));
|
|
28225
|
+
const syncRow = el("div");
|
|
28226
|
+
syncRow.className = "settings-skills-sync-actions";
|
|
28227
|
+
if (missingHarnesses.length > 0) {
|
|
28228
|
+
const label = el("span", tr("\u672A\u5B89\u88C5\u4E8E\uFF1A", "Not installed in: "));
|
|
28229
|
+
label.style.fontSize = "11px";
|
|
28230
|
+
label.style.color = "var(--settings-muted)";
|
|
28231
|
+
syncRow.append(label);
|
|
28232
|
+
const sourceSkill = installations[0].skill;
|
|
28233
|
+
const sourceDir = sourceSkill.path.replace(/[/\\][^/\\]+$/, "");
|
|
28234
|
+
for (const missing of missingHarnesses) {
|
|
28235
|
+
const installBtn = button(`+ ${missing.harness.name}`, () => {
|
|
28236
|
+
void mutateCustom(async (c) => {
|
|
28237
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
28238
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
28239
|
+
await c.changeSkill({
|
|
28240
|
+
harnessId: missing.harness.id,
|
|
28241
|
+
scope: scopeType,
|
|
28242
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
28243
|
+
name: skillName,
|
|
28244
|
+
source: sourceDir,
|
|
28245
|
+
action: "install"
|
|
28246
|
+
});
|
|
28247
|
+
return tr(`\u5DF2\u5C06\u3010${skillName}\u3011\u540C\u6B65\u5B89\u88C5\u5230 ${missing.harness.name}\u3002`, `Installed [${skillName}] to ${missing.harness.name}.`);
|
|
28248
|
+
});
|
|
28249
|
+
});
|
|
28250
|
+
syncRow.append(installBtn);
|
|
28251
|
+
}
|
|
28252
|
+
if (missingHarnesses.length > 1) {
|
|
28253
|
+
const installAllBtn = button(tr("\u4E00\u952E\u540C\u6B65\u5230\u5168\u90E8", "Sync to all"), () => {
|
|
28254
|
+
void mutateCustom(async (c) => {
|
|
28255
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
28256
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
28257
|
+
const successes = [];
|
|
28258
|
+
for (const missing of missingHarnesses) {
|
|
28259
|
+
try {
|
|
28260
|
+
await c.changeSkill({
|
|
28261
|
+
harnessId: missing.harness.id,
|
|
28262
|
+
scope: scopeType,
|
|
28263
|
+
...currentCwd ? { cwd: currentCwd } : {},
|
|
28264
|
+
name: skillName,
|
|
28265
|
+
source: sourceDir,
|
|
28266
|
+
action: "install"
|
|
28267
|
+
});
|
|
28268
|
+
successes.push(missing.harness.name);
|
|
28269
|
+
} catch {
|
|
28270
|
+
}
|
|
28271
|
+
}
|
|
28272
|
+
return tr(`\u5DF2\u5C06\u3010${skillName}\u3011\u540C\u6B65\u5B89\u88C5\u5230\uFF1A${successes.join("\u3001")}\u3002`, `Synced [${skillName}] to: ${successes.join(", ")}.`);
|
|
28273
|
+
});
|
|
28274
|
+
}, true);
|
|
28275
|
+
syncRow.append(installAllBtn);
|
|
28276
|
+
}
|
|
28277
|
+
} else {
|
|
28278
|
+
const complete = el("span", tr("\u5DF2\u5728\u6240\u6709\u652F\u6301\u7684 Harness \u4E2D\u5C31\u7EEA", "Ready across all supported Harnesses"));
|
|
28279
|
+
complete.style.fontSize = "11px";
|
|
28280
|
+
complete.style.color = "#10a37f";
|
|
28281
|
+
syncRow.append(complete);
|
|
28282
|
+
}
|
|
28283
|
+
card.append(head, tags, syncRow);
|
|
28284
|
+
matrixList.append(card);
|
|
28285
|
+
}
|
|
28286
|
+
if (matchedCount === 0) {
|
|
28287
|
+
const empty = el("div", tr("\u672A\u5339\u914D\u5230\u4EFB\u4F55\u6280\u80FD\u3002", "No skills matched the search."));
|
|
28288
|
+
empty.className = "settings-integrations-empty";
|
|
28289
|
+
matrixList.append(empty);
|
|
28290
|
+
}
|
|
28291
|
+
viewsContainer.append(matrixList);
|
|
28292
|
+
}
|
|
28293
|
+
};
|
|
28294
|
+
btnByHarness.addEventListener("click", () => {
|
|
28295
|
+
activeTab = "harness";
|
|
28296
|
+
btnByHarness.dataset.active = "true";
|
|
28297
|
+
delete btnBySkill.dataset.active;
|
|
28298
|
+
updateView();
|
|
28299
|
+
}, { signal: lifecycle.signal });
|
|
28300
|
+
btnBySkill.addEventListener("click", () => {
|
|
28301
|
+
activeTab = "skill";
|
|
28302
|
+
btnBySkill.dataset.active = "true";
|
|
28303
|
+
delete btnByHarness.dataset.active;
|
|
28304
|
+
updateView();
|
|
28305
|
+
}, { signal: lifecycle.signal });
|
|
28306
|
+
searchInput.addEventListener("input", () => {
|
|
28307
|
+
updateView();
|
|
28308
|
+
}, { signal: lifecycle.signal });
|
|
28309
|
+
updateView();
|
|
28310
|
+
body.append(drop, statsBar, toolbar, viewsContainer);
|
|
28311
|
+
};
|
|
28312
|
+
const render = (snapshot) => {
|
|
28313
|
+
body.replaceChildren();
|
|
28314
|
+
if (kind === "mcp") renderMcp(snapshot);
|
|
28315
|
+
else renderSkills(snapshot);
|
|
28316
|
+
};
|
|
28317
|
+
const refresh = async () => {
|
|
28318
|
+
if (busy || lifecycle.disposed) return;
|
|
28319
|
+
if (level.value === "project" && !cwd.value.trim()) {
|
|
28320
|
+
body.replaceChildren();
|
|
28321
|
+
status.textContent = tr("\u586B\u5199\u9879\u76EE\u8DEF\u5F84\u540E\u5373\u53EF\u8BFB\u53D6\u3002", "Enter the project path to continue.");
|
|
28322
|
+
cwd.focus();
|
|
28323
|
+
return;
|
|
28324
|
+
}
|
|
28325
|
+
setBusy(true);
|
|
28326
|
+
status.textContent = tr("\u6B63\u5728\u8BFB\u53D6\u2026", "Loading\u2026");
|
|
28327
|
+
status.setAttribute("role", "status");
|
|
28328
|
+
if (harness.value === "__all__") {
|
|
28329
|
+
await lifecycle.runLatest(async () => {
|
|
28330
|
+
const catalog = await client().integrationCatalog();
|
|
28331
|
+
const scopeType = level.value === "project" ? "project" : "global";
|
|
28332
|
+
const currentCwd = level.value === "project" ? cwd.value.trim() : void 0;
|
|
28333
|
+
const items = await Promise.all(
|
|
28334
|
+
catalog.harnesses.map(async (h) => {
|
|
28335
|
+
try {
|
|
28336
|
+
const snap = await client().listIntegrations({
|
|
28337
|
+
harnessId: h.id,
|
|
28338
|
+
scope: scopeType,
|
|
28339
|
+
...currentCwd ? { cwd: currentCwd } : {}
|
|
28340
|
+
});
|
|
28341
|
+
return { harness: h, snapshot: snap, error: null };
|
|
28342
|
+
} catch (err) {
|
|
28343
|
+
return { harness: h, snapshot: null, error: err instanceof Error ? err.message : String(err) };
|
|
28344
|
+
}
|
|
28345
|
+
})
|
|
28346
|
+
);
|
|
28347
|
+
return { catalog, items };
|
|
28348
|
+
}, {
|
|
28349
|
+
success: (data) => {
|
|
28350
|
+
body.replaceChildren();
|
|
28351
|
+
if (kind === "mcp") {
|
|
28352
|
+
renderMcpOverview(data.catalog, data.items);
|
|
28353
|
+
} else {
|
|
28354
|
+
renderSkillsOverview(data.catalog, data.items);
|
|
28355
|
+
}
|
|
28356
|
+
status.textContent = "";
|
|
28357
|
+
},
|
|
28358
|
+
failure: fail
|
|
28359
|
+
});
|
|
28360
|
+
} else {
|
|
28361
|
+
await lifecycle.runLatest(() => client().listIntegrations(selectedScope()), { success: (data) => {
|
|
28362
|
+
render(data);
|
|
28363
|
+
status.textContent = "";
|
|
28364
|
+
}, failure: fail });
|
|
28365
|
+
}
|
|
28366
|
+
if (!lifecycle.disposed) setBusy(false);
|
|
28367
|
+
};
|
|
28368
|
+
refreshButton.addEventListener("click", () => {
|
|
28369
|
+
void refresh();
|
|
28370
|
+
}, { signal: lifecycle.signal });
|
|
28371
|
+
controls.append(harness, level, cwd, refreshButton);
|
|
28372
|
+
content.append(title, note, controls, status, body);
|
|
28373
|
+
harness.addEventListener("change", () => {
|
|
28374
|
+
void refresh();
|
|
28375
|
+
}, { signal: lifecycle.signal });
|
|
28376
|
+
level.addEventListener("change", () => {
|
|
28377
|
+
cwd.hidden = level.value !== "project";
|
|
28378
|
+
void refresh();
|
|
28379
|
+
}, { signal: lifecycle.signal });
|
|
28380
|
+
cwd.addEventListener("change", () => {
|
|
28381
|
+
void refresh();
|
|
28382
|
+
}, { signal: lifecycle.signal });
|
|
28383
|
+
void lifecycle.runLatest(() => client().integrationCatalog(), { success: (data) => {
|
|
28384
|
+
const allOption = el("option", tr("\u5168\u90E8 Harness\uFF08\u603B\u89C8\uFF09", "All Harnesses (Overview)"));
|
|
28385
|
+
allOption.value = "__all__";
|
|
28386
|
+
harness.append(allOption);
|
|
28387
|
+
for (const item of data.harnesses) {
|
|
28388
|
+
const option = el("option", `${item.name}${item.available ? "" : tr(" \xB7 \u672A\u5C31\u7EEA", " \xB7 not ready")}`);
|
|
28389
|
+
option.value = item.id;
|
|
28390
|
+
harness.append(option);
|
|
28391
|
+
}
|
|
28392
|
+
void refresh();
|
|
28393
|
+
}, failure: fail });
|
|
28394
|
+
return void 0;
|
|
28395
|
+
}
|
|
28396
|
+
});
|
|
28397
|
+
}
|
|
28398
|
+
var createMcpSettingsPage = (messages, getClient) => createIntegrationSettingsPage("mcp", messages, getClient);
|
|
28399
|
+
var createSkillsSettingsPage = (messages, getClient) => createIntegrationSettingsPage("skills", messages, getClient);
|
|
28400
|
+
|
|
26033
28401
|
// src/native-ui/renderer-extension/src/settings/update-request.ts
|
|
26034
28402
|
var RENDERER_UPDATE_REQUEST_TIMEOUT_MS = 15e3;
|
|
26035
28403
|
var RendererUpdateRequestTimeoutError = class extends Error {
|
|
@@ -26149,7 +28517,7 @@ ${error51.stderrTail}`] : []
|
|
|
26149
28517
|
}
|
|
26150
28518
|
return `${scaled.toFixed(scaled >= 10 ? 0 : 1)} ${unit}`;
|
|
26151
28519
|
}
|
|
26152
|
-
function aboutPage(messages) {
|
|
28520
|
+
function aboutPage(messages, getClient = () => null) {
|
|
26153
28521
|
return Object.freeze({
|
|
26154
28522
|
id: "about",
|
|
26155
28523
|
label: messages.pageLabels.about,
|
|
@@ -26171,7 +28539,7 @@ ${error51.stderrTail}`] : []
|
|
|
26171
28539
|
versionRow.className = "settings-about-version-row";
|
|
26172
28540
|
const versionBadge = document2.createElement("span");
|
|
26173
28541
|
versionBadge.className = "settings-about-version-badge";
|
|
26174
|
-
versionBadge.textContent = "
|
|
28542
|
+
versionBadge.textContent = "v\u2026";
|
|
26175
28543
|
const checkUpdateBtn = document2.createElement("button");
|
|
26176
28544
|
checkUpdateBtn.type = "button";
|
|
26177
28545
|
checkUpdateBtn.className = "settings-command-button settings-command-button--secondary";
|
|
@@ -26213,6 +28581,20 @@ ${error51.stderrTail}`] : []
|
|
|
26213
28581
|
repositorySection.append(openSource, repository);
|
|
26214
28582
|
panel.append(product, tagline, versionRow, introduction, starCallout, repositorySection);
|
|
26215
28583
|
context.content.append(heading, panel);
|
|
28584
|
+
const client = getClient();
|
|
28585
|
+
const readVersion = client?.readCurrentVersion ? () => client.readCurrentVersion() : client ? () => client.checkUpdate().then(({ currentVersion }) => ({ version: currentVersion })) : null;
|
|
28586
|
+
if (readVersion) {
|
|
28587
|
+
void context.runLatest(readVersion, {
|
|
28588
|
+
success(result) {
|
|
28589
|
+
versionBadge.textContent = `v${result.version}`;
|
|
28590
|
+
},
|
|
28591
|
+
failure() {
|
|
28592
|
+
versionBadge.textContent = "v\u2014";
|
|
28593
|
+
}
|
|
28594
|
+
});
|
|
28595
|
+
} else {
|
|
28596
|
+
versionBadge.textContent = "v\u2014";
|
|
28597
|
+
}
|
|
26216
28598
|
return void 0;
|
|
26217
28599
|
}
|
|
26218
28600
|
});
|
|
@@ -26526,16 +28908,18 @@ ${error51.stderrTail}`] : []
|
|
|
26526
28908
|
}
|
|
26527
28909
|
});
|
|
26528
28910
|
}
|
|
26529
|
-
function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable"))) {
|
|
28911
|
+
function createDefaultRendererSettingsPages(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread = () => Promise.reject(new Error("Imported Thread navigation is unavailable")), getIntegrationsClient = () => null) {
|
|
26530
28912
|
return Object.freeze([
|
|
26531
28913
|
createConnectionsSettingsPage(messages, getDiagnostics),
|
|
26532
28914
|
createAccountsSettingsPage(messages, getAccountClient),
|
|
28915
|
+
createMcpSettingsPage(messages, getIntegrationsClient),
|
|
28916
|
+
createSkillsSettingsPage(messages, getIntegrationsClient),
|
|
26533
28917
|
createSessionImportSettingsPage(messages, getSessionImportClient, openImportedThread),
|
|
26534
28918
|
updatesPage(messages, getUpdateClient),
|
|
26535
|
-
aboutPage(messages)
|
|
28919
|
+
aboutPage(messages, getUpdateClient)
|
|
26536
28920
|
]);
|
|
26537
28921
|
}
|
|
26538
|
-
function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread) {
|
|
28922
|
+
function createDefaultRendererSettingsRegistry(messages = DEFAULT_RENDERER_SETTINGS_MESSAGES, getUpdateClient = () => null, getDiagnostics = () => null, getAccountClient = () => null, getSessionImportClient = () => null, openImportedThread, getIntegrationsClient) {
|
|
26539
28923
|
return createRendererSettingsPageRegistry(
|
|
26540
28924
|
createDefaultRendererSettingsPages(
|
|
26541
28925
|
messages,
|
|
@@ -26543,13 +28927,14 @@ ${error51.stderrTail}`] : []
|
|
|
26543
28927
|
getDiagnostics,
|
|
26544
28928
|
getAccountClient,
|
|
26545
28929
|
getSessionImportClient,
|
|
26546
|
-
openImportedThread
|
|
28930
|
+
openImportedThread,
|
|
28931
|
+
getIntegrationsClient
|
|
26547
28932
|
)
|
|
26548
28933
|
);
|
|
26549
28934
|
}
|
|
26550
28935
|
|
|
26551
28936
|
// src/native-ui/renderer-extension/src/settings/shell.css
|
|
26552
|
-
var shell_default = ':host {\r\n --settings-bg: light-dark(#ffffff, #202020);\r\n --settings-sidebar: light-dark(#f7f7f8, #191919);\r\n --settings-panel: light-dark(#ffffff, #232323);\r\n --settings-surface: light-dark(#f6f6f6, #292929);\r\n --settings-surface-hover: light-dark(#eeeeee, #303030);\r\n --settings-inset: light-dark(#f3f3f3, #161616);\r\n --settings-text: light-dark(#0d0d0d, #ececec);\r\n --settings-muted: light-dark(#676767, #a3a3a3);\r\n --settings-subtle: light-dark(#8a8a8a, #777777);\r\n --settings-border: light-dark(rgb(13 13 13 / 9%), rgb(255 255 255 / 9%));\r\n --settings-divider: light-dark(rgb(13 13 13 / 7%), rgb(255 255 255 / 7%));\r\n --settings-hover: light-dark(rgb(13 13 13 / 5%), rgb(255 255 255 / 7%));\r\n --settings-active: light-dark(rgb(13 13 13 / 7%), rgb(255 255 255 / 9%));\r\n --settings-focus: light-dark(#0b75d1, #66aaf9);\r\n --settings-primary: light-dark(#171717, #f4f4f4);\r\n --settings-primary-hover: light-dark(#2f2f2f, #ffffff);\r\n --settings-primary-text: light-dark(#ffffff, #171717);\r\n --settings-warning: light-dark(#8a5500, #f5c96a);\r\n --settings-warning-bg: light-dark(#fff8e6, rgb(245 201 106 / 8%));\r\n --settings-warning-border: light-dark(#ead8a8, rgb(245 201 106 / 24%));\r\n --settings-danger: light-dark(#b42318, #f87171);\r\n --settings-danger-bg: light-dark(#fff1f0, rgb(239 68 68 / 8%));\r\n --settings-danger-border: light-dark(#f3c7c3, rgb(239 68 68 / 22%));\r\n --settings-success: light-dark(#16784a, #4ade80);\r\n color: var(--settings-text);\r\n color-scheme: inherit;\r\n font:\r\n 13px/1.5 system-ui,\r\n -apple-system,\r\n BlinkMacSystemFont,\r\n "Segoe UI",\r\n sans-serif;\r\n letter-spacing: 0;\r\n}\r\n\r\n*,\r\n*::before,\r\n*::after {\r\n box-sizing: border-box;\r\n letter-spacing: 0;\r\n}\r\n\r\nbutton,\r\ninput,\r\nselect {\r\n font: inherit;\r\n}\r\n\r\nbutton {\r\n color: inherit;\r\n}\r\n\r\n[hidden] {\r\n display: none !important;\r\n}\r\n\r\n.settings-visually-hidden {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip-path: inset(50%);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.codexhost-settings-dialog {\r\n width: min(1120px, calc(100vw - 32px));\r\n height: min(780px, calc(100vh - 32px));\r\n max-width: none;\r\n max-height: none;\r\n margin: auto;\r\n padding: 0;\r\n overflow: hidden;\r\n color: var(--settings-text);\r\n background: var(--settings-bg);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 14px;\r\n box-shadow: light-dark(0 16px 42px rgb(0 0 0 / 16%), 0 20px 54px rgb(0 0 0 / 42%));\r\n}\r\n\r\n.codexhost-settings-dialog::backdrop {\r\n background: light-dark(rgb(0 0 0 / 24%), rgb(0 0 0 / 56%));\r\n backdrop-filter: blur(1px);\r\n}\r\n\r\n.settings-frame,\r\n.settings-layout {\r\n width: 100%;\r\n height: 100%;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n.settings-frame {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.settings-layout {\r\n flex: 1;\r\n display: grid;\r\n grid-template-columns: 184px minmax(0, 1fr);\r\n background: var(--settings-bg);\r\n}\r\n\r\n.settings-sidebar {\r\n display: flex;\r\n min-width: 0;\r\n min-height: 0;\r\n flex-direction: column;\r\n overflow: hidden;\r\n background: var(--settings-sidebar);\r\n border-right: 1px solid var(--settings-border);\r\n padding-top: 14px;\r\n}\r\n\r\n.settings-header {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n height: 48px;\r\n flex: none;\r\n padding: 0 12px;\r\n border-bottom: 1px solid var(--settings-border);\r\n}\r\n\r\n.settings-header-actions {\r\n display: flex;\r\n align-items: center;\r\n flex: none;\r\n gap: 12px;\r\n margin-left: auto;\r\n}\r\n\r\n.settings-brand {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 7px;\r\n}\r\n\r\n.settings-brand__mark {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n flex: none;\r\n color: var(--settings-text);\r\n}\r\n\r\n.settings-brand__mark .codexhost-settings-icon {\r\n width: 24px;\r\n height: 24px;\r\n object-fit: contain;\r\n}\r\n\r\n.settings-brand__copy {\r\n display: flex;\r\n min-width: 0;\r\n align-items: baseline;\r\n gap: 0;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-brand__name {\r\n overflow: hidden;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-brand__title {\r\n margin-left: 8px;\r\n padding-left: 8px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n font-weight: 400;\r\n border-left: 1px solid var(--settings-border);\r\n}\r\n\r\n.settings-nav {\r\n display: flex;\r\n min-width: 0;\r\n min-height: 0;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: 2px;\r\n padding: 0 7px 10px;\r\n overflow-y: auto;\r\n}\r\n\r\n.settings-nav-section-label {\r\n min-height: 24px;\r\n padding: 4px 9px 2px;\r\n color: var(--settings-subtle);\r\n font-size: 11px;\r\n font-weight: 600;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-nav-section-label:not(:first-child) {\r\n margin-top: 8px;\r\n}\r\n\r\n.settings-nav-button {\r\n display: grid;\r\n grid-template-columns: 16px minmax(0, 1fr);\r\n align-items: center;\r\n width: 100%;\r\n position: relative;\r\n min-height: 34px;\r\n flex: none;\r\n gap: 9px;\r\n padding: 6px 9px;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n line-height: 20px;\r\n text-align: left;\r\n background: transparent;\r\n border: 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-nav-button .codexhost-settings-icon {\r\n width: 16px;\r\n height: 16px;\r\n opacity: 0.9;\r\n}\r\n\r\n.settings-nav-button:hover {\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-nav-button[aria-current="page"] {\r\n background: var(--settings-active);\r\n color: var(--settings-text);\r\n font-weight: 500;\r\n}\r\n\r\n.settings-nav-button span {\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-nav-star-link {\r\n margin-top: 0;\r\n color: var(--settings-text);\r\n text-decoration: none;\r\n}\r\n\r\n.settings-nav-star-link .codexhost-settings-icon {\r\n color: var(--settings-warning);\r\n}\r\n\r\n.settings-nav-star-link:hover {\r\n color: var(--settings-warning);\r\n background: var(--settings-warning-bg);\r\n}\r\n\r\n.settings-icon-button {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n flex: none;\r\n padding: 0;\r\n color: var(--settings-muted);\r\n background: transparent;\r\n border: 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-icon-button:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-icon-button:focus-visible,\r\n.settings-nav-button:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 1px;\r\n}\r\n\r\n.settings-page {\r\n display: block;\r\n position: relative;\r\n min-width: 0;\r\n min-height: 0;\r\n overflow-y: auto;\r\n background: var(--settings-bg);\r\n scrollbar-gutter: stable;\r\n}\r\n\r\n.settings-page__content {\r\n width: min(930px, calc(100% - 80px));\r\n margin-inline: auto;\r\n}\r\n\r\n.settings-page__content {\r\n min-width: 0;\r\n padding: 28px 0 36px;\r\n}\r\n\r\n.settings-section-label {\r\n min-height: auto;\r\n margin: 0;\r\n padding: 0 0 18px;\r\n color: var(--settings-text);\r\n font-size: 18px;\r\n font-weight: 600;\r\n line-height: 26px;\r\n}\r\n\r\n.settings-status-list,\r\n.settings-empty {\r\n overflow: hidden;\r\n background: var(--settings-panel);\r\n}\r\n\r\n.settings-status-row {\r\n display: grid;\r\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\r\n position: relative;\r\n align-items: center;\r\n min-height: 72px;\r\n gap: 20px;\r\n padding: 12px 0;\r\n}\r\n\r\n.settings-status-row:not(:last-child)::after {\r\n content: "";\r\n position: absolute;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n height: 1px;\r\n background: var(--settings-divider);\r\n}\r\n\r\n.settings-status-row__identity {\r\n display: inline-flex;\r\n align-items: center;\r\n min-width: 0;\r\n color: var(--settings-text);\r\n gap: 10px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-status-row__icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n flex: none;\r\n color: var(--settings-muted);\r\n background: var(--settings-surface);\r\n border-radius: 50%;\r\n}\r\n\r\n.settings-status-badge {\r\n flex: none;\r\n padding: 3px 8px;\r\n color: var(--settings-muted);\r\n font-size: 11px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-status-row__detail {\r\n min-width: 0;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-page-description {\r\n max-width: 760px;\r\n margin: -10px 0 20px;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-session-import-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: 24px;\r\n}\r\n\r\n.settings-session-import-header .settings-section-label {\r\n min-width: 0;\r\n}\r\n\r\n.settings-session-import-harness {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 12px;\r\n margin: -12px 0 16px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n}\r\n\r\n.settings-session-import-harness > span {\r\n flex: none;\r\n}\r\n\r\n.settings-session-import-harness__options {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n flex: 1;\r\n gap: 6px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n scrollbar-width: none;\r\n}\r\n\r\n.settings-session-import-harness__options::-webkit-scrollbar {\r\n display: none;\r\n}\r\n\r\n.settings-session-import-harness__option {\r\n min-height: 32px;\r\n flex: none;\r\n padding: 6px 10px;\r\n color: var(--settings-muted);\r\n font: inherit;\r\n font-size: 12px;\r\n white-space: nowrap;\r\n background: rgb(255 255 255 / 4%);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-session-import-harness__option[aria-pressed="true"] {\r\n color: var(--settings-text);\r\n background: var(--settings-active);\r\n border-color: rgb(51 156 255 / 50%);\r\n}\r\n\r\n.settings-session-import-harness__option:disabled {\r\n opacity: 0.38;\r\n cursor: not-allowed;\r\n}\r\n\r\n.settings-session-import-harness__option:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-session-import-harness + .settings-page-description {\r\n margin-top: 0;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.settings-session-import-availability-note {\r\n margin-top: 0;\r\n}\r\n\r\n.settings-session-import-search,\r\n.settings-session-import-pagination {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 10px;\r\n margin: 16px 0;\r\n}\r\n\r\n.settings-session-import-search input {\r\n flex: 1;\r\n min-width: 160px;\r\n}\r\n\r\n.settings-session-import-search input,\r\n.settings-session-import-pagination select {\r\n min-height: 36px;\r\n padding: 6px 10px;\r\n color: var(--settings-text);\r\n background: var(--settings-bg);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-session-import-search input:focus-visible,\r\n.settings-session-import-pagination select:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-session-import-pagination {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n}\r\n\r\n.settings-session-import-pagination label {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.settings-session-import-pagination > span {\r\n flex: 1;\r\n min-width: 130px;\r\n}\r\n\r\n.settings-session-import-pagination__navigation {\r\n display: flex;\r\n gap: 8px;\r\n margin-left: auto;\r\n}\r\n\r\n.settings-session-import-content,\r\n.settings-session-import-list {\r\n display: grid;\r\n gap: 10px;\r\n}\r\n\r\n.settings-session-import-status {\r\n display: flex;\r\n align-items: center;\r\n min-height: 64px;\r\n gap: 12px;\r\n padding: 16px;\r\n color: var(--settings-muted);\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-session-import-status.is-error {\r\n color: #ef4444;\r\n}\r\n\r\n.settings-session-import-recovery {\r\n display: grid;\r\n justify-items: start;\r\n gap: 14px;\r\n padding: 16px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-session-import-recovery:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-session-import-recovery__copy {\r\n display: grid;\r\n gap: 4px;\r\n}\r\n\r\n.settings-session-import-recovery__copy strong {\r\n color: var(--settings-text);\r\n font-size: 14px;\r\n}\r\n\r\n.settings-session-import-recovery__copy p {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-session-import-recovery__path {\r\n display: flex;\r\n align-items: stretch;\r\n width: 100%;\r\n min-width: 0;\r\n overflow: hidden;\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-session-import-recovery__path code {\r\n min-width: 0;\r\n flex: 1;\r\n padding: 8px 10px;\r\n overflow-x: auto;\r\n color: var(--settings-text);\r\n font-size: 12px;\r\n line-height: 20px;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-session-import-recovery__path .settings-command-button {\r\n min-height: 0;\r\n flex: none;\r\n border-width: 0 0 0 1px;\r\n border-radius: 0;\r\n}\r\n\r\n.settings-session-import-row {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1fr) auto;\r\n align-items: center;\r\n gap: 20px;\r\n min-width: 0;\r\n padding: 16px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-session-import-row__copy {\r\n display: grid;\r\n min-width: 0;\r\n gap: 4px;\r\n}\r\n\r\n.settings-session-import-row__copy strong {\r\n overflow: hidden;\r\n color: var(--settings-text);\r\n font-size: 14px;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-session-import-row__copy span,\r\n.settings-session-import-row__cwd {\r\n overflow: hidden;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-session-import-row__cwd {\r\n display: block;\r\n min-width: 0;\r\n background: transparent;\r\n}\r\n\r\n.settings-session-import-row__action {\r\n display: flex;\r\n align-items: flex-end;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.settings-session-import-running {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n}\r\n\r\n.settings-preference-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 32px;\r\n min-height: 88px;\r\n padding: 18px 0;\r\n border-top: 1px solid var(--settings-divider);\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-preference-row__copy {\r\n display: flex;\r\n flex: 1 1 auto;\r\n flex-direction: column;\r\n min-width: 0;\r\n gap: 5px;\r\n}\r\n\r\n.settings-preference-row__copy strong {\r\n color: var(--settings-text);\r\n font-size: 15px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-preference-row__copy span {\r\n max-width: 680px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-preference-switch {\r\n display: inline-flex;\r\n align-items: center;\r\n width: 42px;\r\n height: 24px;\r\n flex: none;\r\n padding: 2px;\r\n border: 0;\r\n border-radius: 999px;\r\n background: var(--settings-surface-hover);\r\n cursor: pointer;\r\n transition: background 120ms ease;\r\n}\r\n\r\n.settings-preference-switch[aria-checked="true"] {\r\n justify-content: flex-end;\r\n background: var(--settings-focus);\r\n}\r\n\r\n.settings-preference-switch:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-preference-switch__thumb {\r\n width: 20px;\r\n height: 20px;\r\n border-radius: 50%;\r\n background: var(--settings-primary-text);\r\n box-shadow: 0 1px 3px rgb(0 0 0 / 24%);\r\n}\r\n\r\n.settings-connection-page-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: 18px;\r\n margin-bottom: 18px;\r\n}\r\n\r\n.settings-connection-page-header .settings-section-label {\r\n padding-bottom: 0;\r\n}\r\n\r\n.settings-connection-page-header .settings-page-description {\r\n margin: 5px 0 0;\r\n}\r\n\r\n.settings-connections-content,\r\n.settings-connections-layout,\r\n.settings-connection-list,\r\n.settings-connection-rows {\r\n min-width: 0;\r\n}\r\n\r\n.settings-connections-layout {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1.55fr) minmax(280px, 0.85fr);\r\n align-items: start;\r\n gap: 18px;\r\n}\r\n\r\n.settings-connection-list,\r\n.settings-connection-inspector {\r\n overflow: hidden;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-host-strip {\r\n display: grid;\r\n grid-template-columns: 30px minmax(0, 1fr) 30px;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 5px;\r\n padding: 9px 10px;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-host-scroll {\r\n display: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 30px;\r\n height: 34px;\r\n padding: 0;\r\n color: var(--settings-muted);\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-host-strip[data-connection-host-overflow="true"]\r\n .settings-connection-host-scroll {\r\n display: inline-flex;\r\n}\r\n\r\n.settings-connection-host-strip:not([data-connection-host-overflow="true"]) {\r\n grid-template-columns: minmax(0, 1fr);\r\n}\r\n\r\n.settings-connection-host-scroll:hover:not(:disabled) {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-host-scroll:disabled {\r\n cursor: default;\r\n opacity: 0.35;\r\n}\r\n\r\n.settings-connection-host-scroll:focus-visible,\r\n.settings-connection-host-tab:focus-visible,\r\n.settings-connection-row:focus-visible,\r\n.settings-connection-install-link:focus-visible,\r\n.settings-connection-view-error:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 1px;\r\n}\r\n\r\n.settings-connection-host-tabs {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 6px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n overscroll-behavior-x: contain;\r\n scrollbar-width: none;\r\n scroll-behavior: smooth;\r\n scroll-snap-type: x proximity;\r\n touch-action: pan-x;\r\n}\r\n\r\n.settings-connection-host-tabs::-webkit-scrollbar {\r\n display: none;\r\n}\r\n\r\n.settings-connection-host-tab {\r\n display: inline-flex;\r\n align-items: center;\r\n height: 30px;\r\n min-width: max-content;\r\n flex: none;\r\n padding: 0 9px;\r\n color: var(--settings-muted);\r\n font: inherit;\r\n font-size: 12px;\r\n line-height: 18px;\r\n white-space: nowrap;\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 7px;\r\n cursor: pointer;\r\n scroll-snap-align: start;\r\n}\r\n\r\n.settings-connection-host-tab:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-host-tab[aria-selected="true"] {\r\n color: var(--settings-text);\r\n font-weight: 600;\r\n background: var(--settings-active);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-connection-table-header,\r\n.settings-connection-row {\r\n display: grid;\r\n grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 10px;\r\n}\r\n\r\n.settings-connection-table-header {\r\n min-height: 30px;\r\n padding: 0 10px;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.06em;\r\n background: transparent;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-row {\r\n min-height: 52px;\r\n padding: 8px 10px;\r\n cursor: pointer;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-row:last-child {\r\n border-bottom: 0;\r\n}\r\n\r\n.settings-connection-row:hover {\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-row[data-connection-selected="true"] {\r\n background: var(--settings-surface);\r\n}\r\n\r\n.settings-connection-row__identity,\r\n.settings-connection-inspector__identity {\r\n display: inline-flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 8px;\r\n color: var(--settings-text);\r\n}\r\n\r\n.settings-connection-row__identity strong,\r\n.settings-connection-inspector__identity strong {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n font-size: 12px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-row__mark {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n flex: none;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n font-weight: 700;\r\n background: var(--settings-surface);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-connection-row__mark--logo {\r\n color: var(--settings-text);\r\n background: transparent;\r\n}\r\n\r\n.settings-connection-row__mark--logo > img,\r\n.settings-connection-row__mark--logo > svg {\r\n display: block;\r\n max-width: 18px;\r\n max-height: 18px;\r\n}\r\n\r\n.settings-connection-row__status {\r\n display: inline-flex;\r\n align-items: center;\r\n width: fit-content;\r\n gap: 5px;\r\n color: var(--settings-muted);\r\n font-size: 11px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-row__status::before {\r\n width: 6px;\r\n height: 6px;\r\n flex: none;\r\n content: "";\r\n background: currentColor;\r\n border-radius: 50%;\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="ready"] {\r\n color: var(--settings-success);\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="checking"] {\r\n color: var(--settings-warning);\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="setup"] {\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="setup"]::before {\r\n background: transparent;\r\n border: 1.5px solid currentColor;\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="failed"] {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-connection-row__action {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n min-width: 0;\r\n gap: 4px;\r\n}\r\n\r\n.settings-connection-row__handle {\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 16px;\r\n color: var(--settings-muted);\r\n cursor: grab;\r\n opacity: 0.6;\r\n}\r\n\r\n.settings-connection-row__handle:hover {\r\n opacity: 1;\r\n}\r\n\r\n.settings-connection-row[draggable="true"] {\r\n cursor: grab;\r\n}\r\n\r\n.settings-connection-row[data-connection-dragging="true"] {\r\n opacity: 0.4;\r\n}\r\n\r\n.settings-connection-row[data-connection-drop-indicator="before"] {\r\n box-shadow: inset 0 2px 0 0 var(--settings-focus);\r\n}\r\n\r\n.settings-connection-row[data-connection-drop-indicator="after"] {\r\n box-shadow: inset 0 -2px 0 0 var(--settings-focus);\r\n}\r\n\r\n.settings-connection-row__group-toggle {\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n color: var(--settings-muted);\r\n background: transparent;\r\n border: 0;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-row__group-toggle:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-group-divider {\r\n padding: 10px 12px 6px;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.06em;\r\n /* No border-top here: the row immediately above already carries its own\r\n border-bottom (it is no longer :last-child once this divider follows\r\n it), so adding one here would double the separator line. */\r\n}\r\n\r\n.settings-connection-group-more[data-connection-drag-over="true"] {\r\n background: rgb(51 156 255 / 8%);\r\n}\r\n\r\n.settings-connection-group-hint {\r\n display: grid;\r\n gap: 3px;\r\n margin: 0 12px 12px;\r\n padding: 14px 16px;\r\n text-align: center;\r\n border: 1.5px dashed var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-group-more[data-connection-drag-over="true"] .settings-connection-group-hint {\r\n border-color: var(--settings-focus);\r\n}\r\n\r\n.settings-connection-group-hint strong {\r\n color: var(--settings-text);\r\n font-size: 12.5px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-group-hint span {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 17px;\r\n}\r\n\r\n.settings-connection-group-reset {\r\n display: flex;\r\n width: 100%;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 10px 12px;\r\n color: var(--settings-muted);\r\n font: inherit;\r\n font-size: 12px;\r\n text-align: left;\r\n background: transparent;\r\n border: 0;\r\n border-top: 1px solid var(--settings-divider);\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-group-reset:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-install-link {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n color: var(--settings-focus);\r\n text-decoration: none;\r\n background: transparent;\r\n border: 1px solid transparent;\r\n border-radius: 7px;\r\n opacity: 0;\r\n transition: opacity 120ms ease;\r\n}\r\n\r\n.settings-connection-row:hover .settings-connection-install-link,\r\n.settings-connection-install-link:focus-visible {\r\n opacity: 1;\r\n}\r\n\r\n.settings-connection-install-link:hover {\r\n color: var(--settings-focus);\r\n background: var(--settings-active);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-connection-view-error {\r\n padding: 4px 7px;\r\n color: var(--settings-danger);\r\n font: inherit;\r\n font-size: 11px;\r\n line-height: 18px;\r\n background: transparent;\r\n border: 0;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-view-error:hover {\r\n color: var(--settings-danger);\r\n background: var(--settings-danger-bg);\r\n}\r\n\r\n.settings-connection-inspector {\r\n position: sticky;\r\n top: 18px;\r\n}\r\n\r\n.settings-connection-inspector__header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n min-width: 0;\r\n gap: 10px;\r\n padding: 12px;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-inspector__body {\r\n display: grid;\r\n min-width: 0;\r\n gap: 12px;\r\n padding: 12px;\r\n}\r\n\r\n.settings-connection-install-callout {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 14px;\r\n padding: 14px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-install-head {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 12px;\r\n min-width: 0;\r\n}\r\n\r\n.settings-connection-install-head > div {\r\n min-width: 0;\r\n flex: 1;\r\n}\r\n\r\n.settings-connection-install-callout__icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n flex: none;\r\n color: var(--settings-focus);\r\n background: var(--settings-active);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-connection-install-callout strong,\r\n.settings-connection-install-head strong,\r\n.settings-connection-error-summary strong,\r\n.settings-connection-state-summary strong {\r\n display: block;\r\n margin: 0 0 4px;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n font-weight: 600;\r\n line-height: 19px;\r\n}\r\n\r\n.settings-connection-install-callout p,\r\n.settings-connection-install-head p,\r\n.settings-connection-error-summary p,\r\n.settings-connection-state-summary p,\r\n.settings-connection-issue-note {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-install-button {\r\n width: 100%;\r\n}\r\n\r\n.settings-connection-error-summary {\r\n padding: 13px 14px;\r\n color: var(--settings-danger);\r\n background: var(--settings-danger-bg);\r\n border: 1px solid var(--settings-danger-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-error-metadata {\r\n display: grid;\r\n gap: 7px;\r\n padding: 12px;\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 9px;\r\n}\r\n\r\n.settings-connection-detail-line {\r\n display: grid;\r\n grid-template-columns: 92px minmax(0, 1fr);\r\n gap: 10px;\r\n min-width: 0;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n line-height: 16px;\r\n}\r\n\r\n.settings-connection-detail-line code {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n color: var(--settings-text);\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n white-space: pre-wrap;\r\n}\r\n\r\n.settings-connection-error-log-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 10px;\r\n}\r\n\r\n.settings-connection-error-log-header strong {\r\n color: var(--settings-text);\r\n font-size: 11px;\r\n font-weight: 600;\r\n}\r\n\r\n.settings-connection-error-log-header .settings-command-button {\r\n min-height: 30px;\r\n padding: 5px 9px;\r\n font-size: 11px;\r\n}\r\n\r\n.settings-connection-stderr {\r\n max-height: 220px;\r\n min-width: 0;\r\n margin: 0;\r\n padding: 11px;\r\n overflow: auto;\r\n color: var(--settings-text);\r\n font:\r\n 10px/16px ui-monospace,\r\n "SFMono-Regular",\r\n Consolas,\r\n "Liberation Mono",\r\n monospace;\r\n white-space: pre-wrap;\r\n overflow-wrap: anywhere;\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 9px;\r\n}\r\n\r\n.settings-connection-error-actions {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1fr);\r\n gap: 8px;\r\n}\r\n\r\n.settings-connection-error-actions .settings-command-button {\r\n width: 100%;\r\n}\r\n\r\n.settings-connection-issue-note {\r\n margin-top: -4px;\r\n}\r\n\r\n.settings-connection-state-summary {\r\n padding: 14px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-empty {\r\n display: flex;\r\n align-items: center;\r\n min-height: 72px;\r\n padding: 12px 16px;\r\n}\r\n\r\n.settings-empty > div {\r\n display: grid;\r\n min-width: 0;\r\n gap: 2px;\r\n}\r\n\r\n.settings-empty strong {\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n font-weight: 500;\r\n line-height: 19px;\r\n}\r\n\r\n.settings-empty span {\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 19px;\r\n}\r\n\r\n.settings-about-panel {\r\n display: grid;\r\n max-width: 720px;\r\n gap: 18px;\r\n padding: 24px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 12px;\r\n}\r\n\r\n.settings-about-product {\r\n color: var(--settings-text);\r\n font-size: 24px;\r\n font-weight: 700;\r\n line-height: 32px;\r\n}\r\n\r\n.settings-about-tagline {\r\n color: var(--settings-text);\r\n font-size: 16px;\r\n font-weight: 600;\r\n line-height: 24px;\r\n}\r\n\r\n.settings-about-version-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n margin: 4px 0 8px;\r\n}\r\n\r\n.settings-about-version-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--settings-text);\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n padding: 4px 10px;\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-about-copy {\r\n display: grid;\r\n max-width: 660px;\r\n gap: 12px;\r\n}\r\n\r\n.settings-about-copy p,\r\n.settings-about-repository p {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 21px;\r\n}\r\n\r\n.settings-about-star-callout {\r\n margin: 2px 0;\r\n color: var(--settings-warning);\r\n font-size: 14px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-about-repository {\r\n display: grid;\r\n gap: 10px;\r\n padding-top: 18px;\r\n border-top: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-about-repository-link {\r\n display: flex;\r\n align-items: center;\r\n width: fit-content;\r\n min-width: 0;\r\n max-width: 100%;\r\n gap: 8px;\r\n color: var(--settings-focus);\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-decoration: none;\r\n}\r\n\r\n.settings-about-repository-link:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.settings-about-repository-link code {\r\n min-width: 0;\r\n overflow: hidden;\r\n color: var(--settings-muted);\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n font-size: 11px;\r\n font-weight: 400;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-page-error {\r\n padding: 16px;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 20px;\r\n}\r\n\r\n.settings-update-metadata {\r\n display: grid;\r\n grid-template-columns: repeat(3, minmax(0, 1fr));\r\n gap: 24px;\r\n margin-bottom: 24px;\r\n padding: 0 4px 20px;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-update-metadata__item {\r\n display: grid;\r\n min-width: 0;\r\n gap: 4px;\r\n}\r\n\r\n.settings-update-metadata__item span {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-update-metadata__item strong {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n color: var(--settings-text);\r\n font-size: 15px;\r\n font-weight: 500;\r\n line-height: 22px;\r\n}\r\n\r\n/* The only emphasis on this screen: a latest version newer than the installed one. */\r\n.settings-update-metadata__value--newer {\r\n color: var(--settings-focus);\r\n}\r\n\r\n.settings-update-panel {\r\n display: grid;\r\n gap: 12px;\r\n padding: 20px;\r\n color: var(--settings-text);\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-update-panel__head {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 8px;\r\n}\r\n\r\n.settings-update-panel__head .codexhost-settings-icon {\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-update-panel[data-update-state="available"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="downloading"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="installing"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="restarting"] .codexhost-settings-icon {\r\n color: var(--settings-focus);\r\n}\r\n\r\n.settings-update-panel[data-update-state="current"] .codexhost-settings-icon {\r\n color: var(--settings-success);\r\n}\r\n\r\n.settings-update-panel[data-update-state="failed"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="error"] .codexhost-settings-icon {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-update-panel__title {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n color: var(--settings-text);\r\n font-size: 14px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-update-summary,\r\n.settings-update-error {\r\n margin: 0;\r\n overflow-wrap: anywhere;\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-update-summary {\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-update-error {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-update-progress {\r\n width: 100%;\r\n height: 8px;\r\n accent-color: var(--settings-focus);\r\n}\r\n\r\n.settings-update-progress-detail {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n/* Release notes scroll with the page; a scroller inside a scroller traps the wheel. */\r\n.settings-update-notes-section {\r\n min-width: 0;\r\n margin-top: 24px;\r\n padding-top: 24px;\r\n border-top: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-update-notes-section:empty {\r\n display: none;\r\n}\r\n\r\n.settings-update-notes {\r\n display: grid;\r\n min-width: 0;\r\n gap: 12px;\r\n color: var(--settings-muted);\r\n font-size: 14px;\r\n line-height: 22px;\r\n overflow-wrap: anywhere;\r\n}\r\n\r\n.settings-update-notes > :first-child {\r\n margin-top: 0;\r\n}\r\n\r\n.settings-update-notes > :last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.settings-update-notes h1,\r\n.settings-update-notes h2,\r\n.settings-update-notes h3,\r\n.settings-update-notes h4,\r\n.settings-update-notes h5,\r\n.settings-update-notes h6 {\r\n margin: 6px 0 0;\r\n color: var(--settings-text);\r\n font-weight: 600;\r\n}\r\n\r\n.settings-update-notes h1 {\r\n font-size: 18px;\r\n line-height: 26px;\r\n}\r\n\r\n.settings-update-notes h2 {\r\n font-size: 17px;\r\n line-height: 24px;\r\n}\r\n\r\n.settings-update-notes h3,\r\n.settings-update-notes h4,\r\n.settings-update-notes h5,\r\n.settings-update-notes h6 {\r\n font-size: 14px;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-update-notes p,\r\n.settings-update-notes ul,\r\n.settings-update-notes ol,\r\n.settings-update-notes pre,\r\n.settings-update-notes blockquote {\r\n margin: 0;\r\n}\r\n\r\n.settings-update-notes ul,\r\n.settings-update-notes ol {\r\n padding-left: 24px;\r\n}\r\n\r\n.settings-update-notes li {\r\n padding-left: 4px;\r\n}\r\n\r\n.settings-update-notes li::marker {\r\n color: var(--settings-muted);\r\n font-size: 0.85em;\r\n}\r\n\r\n.settings-update-notes li + li {\r\n margin-top: 14px;\r\n}\r\n\r\n.settings-update-notes .release-note-translation {\r\n display: block;\r\n margin-top: 3px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-update-notes blockquote {\r\n padding: 8px 12px;\r\n color: var(--settings-muted);\r\n border-left: 3px solid var(--settings-focus);\r\n background: var(--settings-surface);\r\n}\r\n\r\n.settings-update-notes hr {\r\n width: 100%;\r\n margin: 2px 0;\r\n border: 0;\r\n border-top: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-update-notes strong {\r\n color: var(--settings-text);\r\n font-weight: 600;\r\n}\r\n\r\n.settings-update-notes a {\r\n color: var(--settings-focus);\r\n text-decoration: none;\r\n}\r\n\r\n.settings-update-notes a:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.settings-update-notes code {\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n font-size: 12px;\r\n}\r\n\r\n.settings-update-notes :not(pre) > code {\r\n padding: 1px 5px;\r\n background: var(--settings-surface);\r\n border-radius: 4px;\r\n}\r\n\r\n.settings-update-notes pre {\r\n min-width: 0;\r\n max-width: 100%;\r\n padding: 10px 12px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n background: var(--settings-inset);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-update-notes pre code {\r\n line-height: 18px;\r\n white-space: pre-wrap;\r\n}\r\n\r\n/* Manual update sits directly under the status panel, not behind a disclosure:\r\n automatic updates can fail for machine-local reasons and this is the way out. */\r\n.settings-update-controls {\r\n display: grid;\r\n min-width: 0;\r\n gap: 10px;\r\n margin-top: 24px;\r\n}\r\n\r\n.settings-update-manual-title {\r\n color: var(--settings-text);\r\n font-size: 15px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-update-manual {\r\n display: grid;\r\n min-width: 0;\r\n gap: 8px;\r\n}\r\n\r\n.settings-update-manual[hidden] {\r\n display: none;\r\n}\r\n\r\n.settings-update-manual-description {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-update-manual-description.is-fallback {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-update-command {\r\n display: flex;\r\n align-items: stretch;\r\n min-width: 0;\r\n overflow: hidden;\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-update-command code {\r\n min-width: 0;\r\n flex: 1;\r\n padding: 9px 12px;\r\n overflow-x: auto;\r\n color: var(--settings-text);\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n font-size: 12px;\r\n line-height: 20px;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-update-command__copy {\r\n display: inline-flex;\r\n align-items: center;\r\n flex: none;\r\n gap: 6px;\r\n padding: 0 12px;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n background: transparent;\r\n border: 0;\r\n border-left: 1px solid var(--settings-border);\r\n cursor: pointer;\r\n}\r\n\r\n.settings-update-command__copy:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-update-command__copy:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: -2px;\r\n}\r\n\r\n.settings-update-actions {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 12px;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.settings-command-button,\r\n.settings-update-link {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex: none;\r\n width: max-content;\r\n max-width: 100%;\r\n min-height: 30px;\r\n gap: 6px;\r\n padding: 5px 10px;\r\n white-space: nowrap;\r\n color: var(--settings-primary-text);\r\n font: inherit;\r\n font-size: 12px;\r\n text-decoration: none;\r\n background: var(--settings-primary);\r\n border: 1px solid var(--settings-primary);\r\n border-radius: 7px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-command-button--secondary,\r\n.settings-update-link {\r\n color: var(--settings-text);\r\n background: var(--settings-panel);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-command-button:hover:not(:disabled) {\r\n background: var(--settings-primary-hover);\r\n border-color: var(--settings-primary-hover);\r\n}\r\n\r\n.settings-command-button--secondary:hover:not(:disabled),\r\n.settings-update-link:hover {\r\n background: var(--settings-hover);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-command-button--danger {\r\n color: #f87171;\r\n}\r\n\r\n.settings-command-button--danger:hover:not(:disabled) {\r\n color: #fecaca;\r\n background: rgb(127 29 29 / 24%);\r\n border-color: rgb(248 113 113 / 45%);\r\n}\r\n\r\n.settings-command-button:disabled {\r\n cursor: default;\r\n opacity: 0.45;\r\n}\r\n\r\n.settings-command-button:disabled,\r\n.settings-command-button[aria-disabled="true"] {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n filter: none;\r\n}\r\n\r\n.settings-command-button:focus-visible,\r\n.settings-update-link:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.codexhost-settings-icon {\r\n display: block;\r\n flex: none;\r\n stroke: currentColor;\r\n}\r\n\r\n@media (max-width: 720px) {\r\n .codexhost-settings-dialog {\r\n width: calc(100vw - 16px);\r\n height: calc(100vh - 16px);\r\n border-radius: 10px;\r\n }\r\n\r\n .settings-layout {\r\n grid-template-columns: minmax(0, 1fr);\r\n grid-template-rows: auto minmax(0, 1fr);\r\n }\r\n\r\n .settings-session-import-row {\r\n grid-template-columns: minmax(0, 1fr);\r\n }\r\n\r\n .settings-session-import-row__action {\r\n align-items: flex-start;\r\n }\r\n\r\n .settings-sidebar {\r\n border-right: 0;\r\n border-bottom: 1px solid var(--settings-border);\r\n }\r\n\r\n .settings-header {\r\n height: 56px;\r\n padding-inline: 14px;\r\n }\r\n\r\n .settings-sidebar {\r\n padding-top: 20px;\r\n }\r\n\r\n .settings-nav {\r\n flex: none;\r\n flex-direction: row;\r\n gap: 4px;\r\n padding: 7px 8px 8px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n }\r\n\r\n .settings-nav-section-label {\r\n display: none;\r\n }\r\n\r\n .settings-nav-button {\r\n width: auto;\r\n min-width: max-content;\r\n min-height: 34px;\r\n grid-template-columns: 16px auto;\r\n border-radius: 10px;\r\n }\r\n\r\n .settings-nav-star-link {\r\n margin-top: 0;\r\n }\r\n\r\n .settings-page__content {\r\n width: calc(100% - 40px);\r\n }\r\n\r\n .settings-page__content {\r\n padding-top: 32px;\r\n padding-bottom: 32px;\r\n }\r\n\r\n .settings-section-label {\r\n padding-bottom: 20px;\r\n font-size: 20px;\r\n line-height: 26px;\r\n }\r\n\r\n .settings-update-metadata {\r\n grid-template-columns: minmax(0, 1fr);\r\n gap: 16px;\r\n }\r\n\r\n .settings-status-row {\r\n grid-template-columns: minmax(0, 1fr) auto;\r\n gap: 16px;\r\n }\r\n\r\n .settings-account-header,\r\n .settings-connection-page-header {\r\n flex-direction: column;\r\n }\r\n\r\n .settings-account-header > .settings-command-button,\r\n .settings-connection-page-header > .settings-command-button {\r\n width: 100%;\r\n }\r\n\r\n .settings-connections-layout {\r\n grid-template-columns: minmax(0, 1fr);\r\n }\r\n\r\n .settings-connection-inspector {\r\n position: static;\r\n }\r\n\r\n .settings-status-row__detail {\r\n grid-column: 1 / -1;\r\n margin: -8px 0 0 58px;\r\n }\r\n}\r\n\r\n@media (forced-colors: active) {\r\n :host {\r\n --settings-bg: Canvas;\r\n --settings-sidebar: Canvas;\r\n --settings-panel: Canvas;\r\n --settings-surface: Canvas;\r\n --settings-surface-hover: Highlight;\r\n --settings-inset: Canvas;\r\n --settings-text: CanvasText;\r\n --settings-muted: GrayText;\r\n --settings-subtle: GrayText;\r\n --settings-border: ButtonBorder;\r\n --settings-divider: ButtonBorder;\r\n --settings-hover: Highlight;\r\n --settings-active: Highlight;\r\n --settings-focus: Highlight;\r\n --settings-primary: ButtonFace;\r\n --settings-primary-hover: Highlight;\r\n --settings-primary-text: ButtonText;\r\n --settings-warning: CanvasText;\r\n --settings-warning-bg: Canvas;\r\n --settings-warning-border: ButtonBorder;\r\n --settings-danger: CanvasText;\r\n --settings-danger-bg: Canvas;\r\n --settings-danger-border: ButtonBorder;\r\n --settings-success: CanvasText;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n *,\r\n *::before,\r\n *::after {\r\n scroll-behavior: auto !important;\r\n transition-duration: 0s !important;\r\n }\r\n}\r\n\r\n/* Harness Mix: Model Configuration & One-click Install */\r\n.settings-model-config-panel {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n padding: 14px;\r\n margin-top: 14px;\r\n border-radius: 8px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n}\r\n\r\n.settings-model-config-head {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n}\r\n\r\n.settings-model-config-title {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--settings-text);\r\n}\r\n\r\n.settings-model-config-description {\r\n font-size: 11px;\r\n color: var(--settings-muted);\r\n margin: 0;\r\n}\r\n\r\n.settings-model-config-loading,\r\n.settings-model-config-empty {\r\n font-size: 12px;\r\n color: var(--settings-muted);\r\n padding: 8px 0;\r\n}\r\n\r\n.settings-model-config-form {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n margin-top: 4px;\r\n}\r\n\r\n.settings-model-config-field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n\r\n.settings-model-config-label {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--settings-muted);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.settings-model-config-select {\r\n height: 32px;\r\n padding: 0 10px;\r\n border-radius: 6px;\r\n border: 1px solid var(--settings-border);\r\n background: var(--settings-panel);\r\n color: var(--settings-text);\r\n font-size: 12px;\r\n outline: none;\r\n}\r\n\r\n.settings-model-config-select:focus {\r\n border-color: var(--settings-primary);\r\n}\r\n\r\n.settings-model-config-actions {\r\n display: flex;\r\n align-items: center;\r\n flex-wrap: wrap;\r\n gap: 8px;\r\n margin-top: 6px;\r\n}\r\n\r\n.settings-model-config-feedback {\r\n font-size: 12px;\r\n font-weight: 500;\r\n}\r\n\r\n.settings-feedback-success {\r\n color: var(--settings-success);\r\n}\r\n\r\n.settings-feedback-error {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-feedback-info {\r\n color: var(--settings-primary);\r\n}\r\n\r\n.settings-install-command-box {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n min-width: 0;\r\n margin: 0;\r\n}\r\n\r\n.settings-install-command-label {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-install-command-row {\r\n display: flex;\r\n align-items: center;\r\n background: var(--settings-inset);\r\n padding: 8px 12px;\r\n border-radius: 6px;\r\n border: 1px solid var(--settings-border);\r\n min-width: 0;\r\n overflow-x: auto;\r\n}\r\n\r\n.settings-install-command-row code {\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--settings-text);\r\n user-select: all;\r\n white-space: pre-wrap;\r\n word-break: break-all;\r\n}\r\n\r\n.settings-install-actions {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1fr);\r\n gap: 8px;\r\n margin-top: 4px;\r\n}\r\n\r\n.settings-install-actions .settings-command-button {\r\n width: 100%;\r\n justify-content: center;\r\n}\r\n\r\n.settings-install-feedback {\r\n font-size: 12px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n margin-top: 2px;\r\n overflow-wrap: anywhere;\r\n}\r\n\r\n.settings-install-manual-card {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n background: var(--settings-inset);\r\n padding: 12px 14px;\r\n border-radius: 8px;\r\n border: 1px solid var(--settings-border);\r\n margin-top: 6px;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.settings-install-manual-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--settings-accent, #10a37f);\r\n background: var(--settings-accent-subtle, rgba(16, 163, 127, 0.1));\r\n padding: 2px 8px;\r\n border-radius: 4px;\r\n width: fit-content;\r\n}\r\n\r\n.settings-install-manual-notice {\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--settings-text);\r\n margin: 0;\r\n}\r\n\r\n.settings-install-steps {\r\n margin: 0;\r\n padding-left: 18px;\r\n font-size: 12px;\r\n line-height: 20px;\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-install-steps li {\r\n margin-bottom: 2px;\r\n}\r\n';
|
|
28937
|
+
var shell_default = ':host {\r\n --settings-bg: light-dark(#ffffff, #202020);\r\n --settings-sidebar: light-dark(#f7f7f8, #191919);\r\n --settings-panel: light-dark(#ffffff, #232323);\r\n --settings-surface: light-dark(#f6f6f6, #292929);\r\n --settings-surface-hover: light-dark(#eeeeee, #303030);\r\n --settings-inset: light-dark(#f3f3f3, #161616);\r\n --settings-text: light-dark(#0d0d0d, #ececec);\r\n --settings-muted: light-dark(#676767, #a3a3a3);\r\n --settings-subtle: light-dark(#8a8a8a, #777777);\r\n --settings-border: light-dark(rgb(13 13 13 / 9%), rgb(255 255 255 / 9%));\r\n --settings-divider: light-dark(rgb(13 13 13 / 7%), rgb(255 255 255 / 7%));\r\n --settings-hover: light-dark(rgb(13 13 13 / 5%), rgb(255 255 255 / 7%));\r\n --settings-active: light-dark(rgb(13 13 13 / 7%), rgb(255 255 255 / 9%));\r\n --settings-focus: light-dark(#0b75d1, #66aaf9);\r\n --settings-primary: light-dark(#171717, #f4f4f4);\r\n --settings-primary-hover: light-dark(#2f2f2f, #ffffff);\r\n --settings-primary-text: light-dark(#ffffff, #171717);\r\n --settings-warning: light-dark(#8a5500, #f5c96a);\r\n --settings-warning-bg: light-dark(#fff8e6, rgb(245 201 106 / 8%));\r\n --settings-warning-border: light-dark(#ead8a8, rgb(245 201 106 / 24%));\r\n --settings-danger: light-dark(#b42318, #f87171);\r\n --settings-danger-bg: light-dark(#fff1f0, rgb(239 68 68 / 8%));\r\n --settings-danger-border: light-dark(#f3c7c3, rgb(239 68 68 / 22%));\r\n --settings-success: light-dark(#16784a, #4ade80);\r\n color: var(--settings-text);\r\n color-scheme: inherit;\r\n font:\r\n 13px/1.5 system-ui,\r\n -apple-system,\r\n BlinkMacSystemFont,\r\n "Segoe UI",\r\n sans-serif;\r\n letter-spacing: 0;\r\n}\r\n\r\n*,\r\n*::before,\r\n*::after {\r\n box-sizing: border-box;\r\n letter-spacing: 0;\r\n}\r\n\r\nbutton,\r\ninput,\r\nselect {\r\n font: inherit;\r\n}\r\n\r\nbutton {\r\n color: inherit;\r\n}\r\n\r\n[hidden] {\r\n display: none !important;\r\n}\r\n\r\n.settings-visually-hidden {\r\n position: absolute;\r\n width: 1px;\r\n height: 1px;\r\n padding: 0;\r\n margin: -1px;\r\n overflow: hidden;\r\n clip-path: inset(50%);\r\n white-space: nowrap;\r\n border: 0;\r\n}\r\n\r\n.codexhost-settings-dialog {\r\n width: min(1120px, calc(100vw - 32px));\r\n height: min(780px, calc(100vh - 32px));\r\n max-width: none;\r\n max-height: none;\r\n margin: auto;\r\n padding: 0;\r\n overflow: hidden;\r\n color: var(--settings-text);\r\n background: var(--settings-bg);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 14px;\r\n box-shadow: light-dark(0 16px 42px rgb(0 0 0 / 16%), 0 20px 54px rgb(0 0 0 / 42%));\r\n}\r\n\r\n.codexhost-settings-dialog::backdrop {\r\n background: light-dark(rgb(0 0 0 / 24%), rgb(0 0 0 / 56%));\r\n backdrop-filter: blur(1px);\r\n}\r\n\r\n.settings-frame,\r\n.settings-layout {\r\n width: 100%;\r\n height: 100%;\r\n min-width: 0;\r\n min-height: 0;\r\n}\r\n\r\n.settings-frame {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.settings-layout {\r\n flex: 1;\r\n display: grid;\r\n grid-template-columns: 184px minmax(0, 1fr);\r\n background: var(--settings-bg);\r\n}\r\n\r\n.settings-sidebar {\r\n display: flex;\r\n min-width: 0;\r\n min-height: 0;\r\n flex-direction: column;\r\n overflow: hidden;\r\n background: var(--settings-sidebar);\r\n border-right: 1px solid var(--settings-border);\r\n padding-top: 14px;\r\n}\r\n\r\n.settings-header {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n height: 48px;\r\n flex: none;\r\n padding: 0 12px;\r\n border-bottom: 1px solid var(--settings-border);\r\n}\r\n\r\n.settings-header-actions {\r\n display: flex;\r\n align-items: center;\r\n flex: none;\r\n gap: 12px;\r\n margin-left: auto;\r\n}\r\n\r\n.settings-brand {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 7px;\r\n}\r\n\r\n.settings-brand__mark {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n flex: none;\r\n color: var(--settings-text);\r\n}\r\n\r\n.settings-brand__mark .codexhost-settings-icon {\r\n width: 24px;\r\n height: 24px;\r\n object-fit: contain;\r\n}\r\n\r\n.settings-brand__copy {\r\n display: flex;\r\n min-width: 0;\r\n align-items: baseline;\r\n gap: 0;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-brand__name {\r\n overflow: hidden;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-brand__title {\r\n margin-left: 8px;\r\n padding-left: 8px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n font-weight: 400;\r\n border-left: 1px solid var(--settings-border);\r\n}\r\n\r\n.settings-nav {\r\n display: flex;\r\n min-width: 0;\r\n min-height: 0;\r\n flex: 1;\r\n flex-direction: column;\r\n gap: 2px;\r\n padding: 0 7px 10px;\r\n overflow-y: auto;\r\n}\r\n\r\n.settings-nav-section-label {\r\n min-height: 24px;\r\n padding: 4px 9px 2px;\r\n color: var(--settings-subtle);\r\n font-size: 11px;\r\n font-weight: 600;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-nav-section-label:not(:first-child) {\r\n margin-top: 8px;\r\n}\r\n\r\n.settings-nav-button {\r\n display: grid;\r\n grid-template-columns: 16px minmax(0, 1fr);\r\n align-items: center;\r\n width: 100%;\r\n position: relative;\r\n min-height: 34px;\r\n flex: none;\r\n gap: 9px;\r\n padding: 6px 9px;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n line-height: 20px;\r\n text-align: left;\r\n background: transparent;\r\n border: 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-nav-button .codexhost-settings-icon {\r\n width: 16px;\r\n height: 16px;\r\n opacity: 0.9;\r\n}\r\n\r\n.settings-nav-button:hover {\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-nav-button[aria-current="page"] {\r\n background: var(--settings-active);\r\n color: var(--settings-text);\r\n font-weight: 500;\r\n}\r\n\r\n.settings-nav-button span {\r\n min-width: 0;\r\n overflow: hidden;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-nav-star-link {\r\n margin-top: 0;\r\n color: var(--settings-text);\r\n text-decoration: none;\r\n}\r\n\r\n.settings-nav-star-link .codexhost-settings-icon {\r\n color: var(--settings-warning);\r\n}\r\n\r\n.settings-nav-star-link:hover {\r\n color: var(--settings-warning);\r\n background: var(--settings-warning-bg);\r\n}\r\n\r\n.settings-icon-button {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 28px;\r\n height: 28px;\r\n flex: none;\r\n padding: 0;\r\n color: var(--settings-muted);\r\n background: transparent;\r\n border: 0;\r\n border-radius: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-icon-button:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-icon-button:focus-visible,\r\n.settings-nav-button:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 1px;\r\n}\r\n\r\n.settings-page {\r\n display: block;\r\n position: relative;\r\n min-width: 0;\r\n min-height: 0;\r\n overflow-y: auto;\r\n background: var(--settings-bg);\r\n scrollbar-gutter: stable;\r\n}\r\n\r\n.settings-page__content {\r\n width: min(930px, calc(100% - 80px));\r\n margin-inline: auto;\r\n}\r\n\r\n.settings-page__content {\r\n min-width: 0;\r\n padding: 28px 0 36px;\r\n}\r\n\r\n.settings-section-label {\r\n min-height: auto;\r\n margin: 0;\r\n padding: 0 0 18px;\r\n color: var(--settings-text);\r\n font-size: 18px;\r\n font-weight: 600;\r\n line-height: 26px;\r\n}\r\n\r\n.settings-status-list,\r\n.settings-empty {\r\n overflow: hidden;\r\n background: var(--settings-panel);\r\n}\r\n\r\n.settings-status-row {\r\n display: grid;\r\n grid-template-columns: minmax(170px, 1fr) auto minmax(280px, 1.45fr);\r\n position: relative;\r\n align-items: center;\r\n min-height: 72px;\r\n gap: 20px;\r\n padding: 12px 0;\r\n}\r\n\r\n.settings-status-row:not(:last-child)::after {\r\n content: "";\r\n position: absolute;\r\n right: 0;\r\n bottom: 0;\r\n left: 0;\r\n height: 1px;\r\n background: var(--settings-divider);\r\n}\r\n\r\n.settings-status-row__identity {\r\n display: inline-flex;\r\n align-items: center;\r\n min-width: 0;\r\n color: var(--settings-text);\r\n gap: 10px;\r\n font-size: 14px;\r\n font-weight: 500;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-status-row__icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 36px;\r\n height: 36px;\r\n flex: none;\r\n color: var(--settings-muted);\r\n background: var(--settings-surface);\r\n border-radius: 50%;\r\n}\r\n\r\n.settings-status-badge {\r\n flex: none;\r\n padding: 3px 8px;\r\n color: var(--settings-muted);\r\n font-size: 11px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-status-row__detail {\r\n min-width: 0;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-page-description {\r\n max-width: 760px;\r\n margin: -10px 0 20px;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-session-import-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: 24px;\r\n}\r\n\r\n.settings-session-import-header .settings-section-label {\r\n min-width: 0;\r\n}\r\n\r\n.settings-session-import-harness {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 12px;\r\n margin: -12px 0 16px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n}\r\n\r\n.settings-session-import-harness > span {\r\n flex: none;\r\n}\r\n\r\n.settings-session-import-harness__options {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n flex: 1;\r\n gap: 6px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n scrollbar-width: none;\r\n}\r\n\r\n.settings-session-import-harness__options::-webkit-scrollbar {\r\n display: none;\r\n}\r\n\r\n.settings-session-import-harness__option {\r\n min-height: 32px;\r\n flex: none;\r\n padding: 6px 10px;\r\n color: var(--settings-muted);\r\n font: inherit;\r\n font-size: 12px;\r\n white-space: nowrap;\r\n background: rgb(255 255 255 / 4%);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-session-import-harness__option[aria-pressed="true"] {\r\n color: var(--settings-text);\r\n background: var(--settings-active);\r\n border-color: rgb(51 156 255 / 50%);\r\n}\r\n\r\n.settings-session-import-harness__option:disabled {\r\n opacity: 0.38;\r\n cursor: not-allowed;\r\n}\r\n\r\n.settings-session-import-harness__option:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-session-import-harness + .settings-page-description {\r\n margin-top: 0;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.settings-session-import-availability-note {\r\n margin-top: 0;\r\n}\r\n\r\n.settings-session-import-search,\r\n.settings-session-import-pagination {\r\n display: flex;\r\n flex-wrap: wrap;\r\n align-items: center;\r\n gap: 10px;\r\n margin: 16px 0;\r\n}\r\n\r\n.settings-session-import-search input {\r\n flex: 1;\r\n min-width: 160px;\r\n}\r\n\r\n.settings-session-import-search input,\r\n.settings-session-import-pagination select {\r\n min-height: 36px;\r\n padding: 6px 10px;\r\n color: var(--settings-text);\r\n background: var(--settings-bg);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-session-import-search input:focus-visible,\r\n.settings-session-import-pagination select:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-session-import-pagination {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n}\r\n\r\n.settings-session-import-pagination label {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n}\r\n\r\n.settings-session-import-pagination > span {\r\n flex: 1;\r\n min-width: 130px;\r\n}\r\n\r\n.settings-session-import-pagination__navigation {\r\n display: flex;\r\n gap: 8px;\r\n margin-left: auto;\r\n}\r\n\r\n.settings-session-import-content,\r\n.settings-session-import-list {\r\n display: grid;\r\n gap: 10px;\r\n}\r\n\r\n.settings-session-import-status {\r\n display: flex;\r\n align-items: center;\r\n min-height: 64px;\r\n gap: 12px;\r\n padding: 16px;\r\n color: var(--settings-muted);\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-session-import-status.is-error {\r\n color: #ef4444;\r\n}\r\n\r\n.settings-session-import-recovery {\r\n display: grid;\r\n justify-items: start;\r\n gap: 14px;\r\n padding: 16px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-session-import-recovery:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-session-import-recovery__copy {\r\n display: grid;\r\n gap: 4px;\r\n}\r\n\r\n.settings-session-import-recovery__copy strong {\r\n color: var(--settings-text);\r\n font-size: 14px;\r\n}\r\n\r\n.settings-session-import-recovery__copy p {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-session-import-recovery__path {\r\n display: flex;\r\n align-items: stretch;\r\n width: 100%;\r\n min-width: 0;\r\n overflow: hidden;\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-session-import-recovery__path code {\r\n min-width: 0;\r\n flex: 1;\r\n padding: 8px 10px;\r\n overflow-x: auto;\r\n color: var(--settings-text);\r\n font-size: 12px;\r\n line-height: 20px;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-session-import-recovery__path .settings-command-button {\r\n min-height: 0;\r\n flex: none;\r\n border-width: 0 0 0 1px;\r\n border-radius: 0;\r\n}\r\n\r\n.settings-session-import-row {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1fr) auto;\r\n align-items: center;\r\n gap: 20px;\r\n min-width: 0;\r\n padding: 16px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-session-import-row__copy {\r\n display: grid;\r\n min-width: 0;\r\n gap: 4px;\r\n}\r\n\r\n.settings-session-import-row__copy strong {\r\n overflow: hidden;\r\n color: var(--settings-text);\r\n font-size: 14px;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-session-import-row__copy span,\r\n.settings-session-import-row__cwd {\r\n overflow: hidden;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-session-import-row__cwd {\r\n display: block;\r\n min-width: 0;\r\n background: transparent;\r\n}\r\n\r\n.settings-session-import-row__action {\r\n display: flex;\r\n align-items: flex-end;\r\n flex-direction: column;\r\n gap: 8px;\r\n}\r\n\r\n.settings-session-import-running {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n}\r\n\r\n.settings-preference-row {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 32px;\r\n min-height: 88px;\r\n padding: 18px 0;\r\n border-top: 1px solid var(--settings-divider);\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-preference-row__copy {\r\n display: flex;\r\n flex: 1 1 auto;\r\n flex-direction: column;\r\n min-width: 0;\r\n gap: 5px;\r\n}\r\n\r\n.settings-preference-row__copy strong {\r\n color: var(--settings-text);\r\n font-size: 15px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-preference-row__copy span {\r\n max-width: 680px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-preference-switch {\r\n display: inline-flex;\r\n align-items: center;\r\n width: 42px;\r\n height: 24px;\r\n flex: none;\r\n padding: 2px;\r\n border: 0;\r\n border-radius: 999px;\r\n background: var(--settings-surface-hover);\r\n cursor: pointer;\r\n transition: background 120ms ease;\r\n}\r\n\r\n.settings-preference-switch[aria-checked="true"] {\r\n justify-content: flex-end;\r\n background: var(--settings-focus);\r\n}\r\n\r\n.settings-preference-switch:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.settings-preference-switch__thumb {\r\n width: 20px;\r\n height: 20px;\r\n border-radius: 50%;\r\n background: var(--settings-primary-text);\r\n box-shadow: 0 1px 3px rgb(0 0 0 / 24%);\r\n}\r\n\r\n.settings-connection-page-header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n gap: 18px;\r\n margin-bottom: 18px;\r\n}\r\n\r\n.settings-connection-page-header .settings-section-label {\r\n padding-bottom: 0;\r\n}\r\n\r\n.settings-connection-page-header .settings-page-description {\r\n margin: 5px 0 0;\r\n}\r\n\r\n.settings-connections-content,\r\n.settings-connections-layout,\r\n.settings-connection-list,\r\n.settings-connection-rows {\r\n min-width: 0;\r\n}\r\n\r\n.settings-connections-layout {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1.55fr) minmax(280px, 0.85fr);\r\n align-items: start;\r\n gap: 18px;\r\n}\r\n\r\n.settings-connection-list,\r\n.settings-connection-inspector {\r\n overflow: hidden;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-host-strip {\r\n display: grid;\r\n grid-template-columns: 30px minmax(0, 1fr) 30px;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 5px;\r\n padding: 9px 10px;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-host-scroll {\r\n display: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 30px;\r\n height: 34px;\r\n padding: 0;\r\n color: var(--settings-muted);\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-host-strip[data-connection-host-overflow="true"]\r\n .settings-connection-host-scroll {\r\n display: inline-flex;\r\n}\r\n\r\n.settings-connection-host-strip:not([data-connection-host-overflow="true"]) {\r\n grid-template-columns: minmax(0, 1fr);\r\n}\r\n\r\n.settings-connection-host-scroll:hover:not(:disabled) {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-host-scroll:disabled {\r\n cursor: default;\r\n opacity: 0.35;\r\n}\r\n\r\n.settings-connection-host-scroll:focus-visible,\r\n.settings-connection-host-tab:focus-visible,\r\n.settings-connection-row:focus-visible,\r\n.settings-connection-install-link:focus-visible,\r\n.settings-connection-view-error:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 1px;\r\n}\r\n\r\n.settings-connection-host-tabs {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 6px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n overscroll-behavior-x: contain;\r\n scrollbar-width: none;\r\n scroll-behavior: smooth;\r\n scroll-snap-type: x proximity;\r\n touch-action: pan-x;\r\n}\r\n\r\n.settings-connection-host-tabs::-webkit-scrollbar {\r\n display: none;\r\n}\r\n\r\n.settings-connection-host-tab {\r\n display: inline-flex;\r\n align-items: center;\r\n height: 30px;\r\n min-width: max-content;\r\n flex: none;\r\n padding: 0 9px;\r\n color: var(--settings-muted);\r\n font: inherit;\r\n font-size: 12px;\r\n line-height: 18px;\r\n white-space: nowrap;\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 7px;\r\n cursor: pointer;\r\n scroll-snap-align: start;\r\n}\r\n\r\n.settings-connection-host-tab:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-host-tab[aria-selected="true"] {\r\n color: var(--settings-text);\r\n font-weight: 600;\r\n background: var(--settings-active);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-connection-table-header,\r\n.settings-connection-row {\r\n display: grid;\r\n grid-template-columns: minmax(160px, 1fr) minmax(92px, auto) 108px;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 10px;\r\n}\r\n\r\n.settings-connection-table-header {\r\n min-height: 30px;\r\n padding: 0 10px;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.06em;\r\n background: transparent;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-row {\r\n min-height: 52px;\r\n padding: 8px 10px;\r\n cursor: pointer;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-row:last-child {\r\n border-bottom: 0;\r\n}\r\n\r\n.settings-connection-row:hover {\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-row[data-connection-selected="true"] {\r\n background: var(--settings-surface);\r\n}\r\n\r\n.settings-connection-row__identity,\r\n.settings-connection-inspector__identity {\r\n display: inline-flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 8px;\r\n color: var(--settings-text);\r\n}\r\n\r\n.settings-connection-row__identity strong,\r\n.settings-connection-inspector__identity strong {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n font-size: 12px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-row__mark {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 24px;\r\n height: 24px;\r\n flex: none;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n font-weight: 700;\r\n background: var(--settings-surface);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-connection-row__mark--logo {\r\n color: var(--settings-text);\r\n background: transparent;\r\n}\r\n\r\n.settings-connection-row__mark--logo > img,\r\n.settings-connection-row__mark--logo > svg {\r\n display: block;\r\n max-width: 18px;\r\n max-height: 18px;\r\n}\r\n\r\n.settings-connection-row__status {\r\n display: inline-flex;\r\n align-items: center;\r\n width: fit-content;\r\n gap: 5px;\r\n color: var(--settings-muted);\r\n font-size: 11px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-row__status::before {\r\n width: 6px;\r\n height: 6px;\r\n flex: none;\r\n content: "";\r\n background: currentColor;\r\n border-radius: 50%;\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="ready"] {\r\n color: var(--settings-success);\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="checking"] {\r\n color: var(--settings-warning);\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="setup"] {\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="setup"]::before {\r\n background: transparent;\r\n border: 1.5px solid currentColor;\r\n}\r\n\r\n.settings-connection-row__status[data-connection-tone="failed"] {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-connection-row__action {\r\n display: flex;\r\n align-items: center;\r\n justify-content: flex-end;\r\n min-width: 0;\r\n gap: 4px;\r\n}\r\n\r\n.settings-connection-row__handle {\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 16px;\r\n color: var(--settings-muted);\r\n cursor: grab;\r\n opacity: 0.6;\r\n}\r\n\r\n.settings-connection-row__handle:hover {\r\n opacity: 1;\r\n}\r\n\r\n.settings-connection-row[draggable="true"] {\r\n cursor: grab;\r\n}\r\n\r\n.settings-connection-row[data-connection-dragging="true"] {\r\n opacity: 0.4;\r\n}\r\n\r\n.settings-connection-row[data-connection-drop-indicator="before"] {\r\n box-shadow: inset 0 2px 0 0 var(--settings-focus);\r\n}\r\n\r\n.settings-connection-row[data-connection-drop-indicator="after"] {\r\n box-shadow: inset 0 -2px 0 0 var(--settings-focus);\r\n}\r\n\r\n.settings-connection-row__group-toggle {\r\n display: inline-flex;\r\n flex: none;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n color: var(--settings-muted);\r\n background: transparent;\r\n border: 0;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-row__group-toggle:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-group-divider {\r\n padding: 10px 12px 6px;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n font-weight: 600;\r\n line-height: 16px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.06em;\r\n /* No border-top here: the row immediately above already carries its own\r\n border-bottom (it is no longer :last-child once this divider follows\r\n it), so adding one here would double the separator line. */\r\n}\r\n\r\n.settings-connection-group-more[data-connection-drag-over="true"] {\r\n background: rgb(51 156 255 / 8%);\r\n}\r\n\r\n.settings-connection-group-hint {\r\n display: grid;\r\n gap: 3px;\r\n margin: 0 12px 12px;\r\n padding: 14px 16px;\r\n text-align: center;\r\n border: 1.5px dashed var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-group-more[data-connection-drag-over="true"] .settings-connection-group-hint {\r\n border-color: var(--settings-focus);\r\n}\r\n\r\n.settings-connection-group-hint strong {\r\n color: var(--settings-text);\r\n font-size: 12.5px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-group-hint span {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 17px;\r\n}\r\n\r\n.settings-connection-group-reset {\r\n display: flex;\r\n width: 100%;\r\n align-items: center;\r\n gap: 6px;\r\n padding: 10px 12px;\r\n color: var(--settings-muted);\r\n font: inherit;\r\n font-size: 12px;\r\n text-align: left;\r\n background: transparent;\r\n border: 0;\r\n border-top: 1px solid var(--settings-divider);\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-group-reset:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-connection-install-link {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 26px;\r\n height: 26px;\r\n color: var(--settings-focus);\r\n text-decoration: none;\r\n background: transparent;\r\n border: 1px solid transparent;\r\n border-radius: 7px;\r\n opacity: 0;\r\n transition: opacity 120ms ease;\r\n}\r\n\r\n.settings-connection-row:hover .settings-connection-install-link,\r\n.settings-connection-install-link:focus-visible {\r\n opacity: 1;\r\n}\r\n\r\n.settings-connection-install-link:hover {\r\n color: var(--settings-focus);\r\n background: var(--settings-active);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-connection-view-error {\r\n padding: 4px 7px;\r\n color: var(--settings-danger);\r\n font: inherit;\r\n font-size: 11px;\r\n line-height: 18px;\r\n background: transparent;\r\n border: 0;\r\n border-radius: 6px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-connection-view-error:hover {\r\n color: var(--settings-danger);\r\n background: var(--settings-danger-bg);\r\n}\r\n\r\n.settings-connection-inspector {\r\n position: sticky;\r\n top: 18px;\r\n}\r\n\r\n.settings-connection-inspector__header {\r\n display: flex;\r\n align-items: flex-start;\r\n justify-content: space-between;\r\n min-width: 0;\r\n gap: 10px;\r\n padding: 12px;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-connection-inspector__body {\r\n display: grid;\r\n min-width: 0;\r\n gap: 12px;\r\n padding: 12px;\r\n}\r\n\r\n.settings-connection-install-callout {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 14px;\r\n padding: 14px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-install-head {\r\n display: flex;\r\n align-items: flex-start;\r\n gap: 12px;\r\n min-width: 0;\r\n}\r\n\r\n.settings-connection-install-head > div {\r\n min-width: 0;\r\n flex: 1;\r\n}\r\n\r\n.settings-connection-install-callout__icon {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 32px;\r\n height: 32px;\r\n flex: none;\r\n color: var(--settings-focus);\r\n background: var(--settings-active);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-connection-install-callout strong,\r\n.settings-connection-install-head strong,\r\n.settings-connection-error-summary strong,\r\n.settings-connection-state-summary strong {\r\n display: block;\r\n margin: 0 0 4px;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n font-weight: 600;\r\n line-height: 19px;\r\n}\r\n\r\n.settings-connection-install-callout p,\r\n.settings-connection-install-head p,\r\n.settings-connection-error-summary p,\r\n.settings-connection-state-summary p,\r\n.settings-connection-issue-note {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-connection-install-button {\r\n width: 100%;\r\n}\r\n\r\n.settings-connection-error-summary {\r\n padding: 13px 14px;\r\n color: var(--settings-danger);\r\n background: var(--settings-danger-bg);\r\n border: 1px solid var(--settings-danger-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-connection-error-metadata {\r\n display: grid;\r\n gap: 7px;\r\n padding: 12px;\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 9px;\r\n}\r\n\r\n.settings-connection-detail-line {\r\n display: grid;\r\n grid-template-columns: 92px minmax(0, 1fr);\r\n gap: 10px;\r\n min-width: 0;\r\n color: var(--settings-muted);\r\n font-size: 10px;\r\n line-height: 16px;\r\n}\r\n\r\n.settings-connection-detail-line code {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n color: var(--settings-text);\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n white-space: pre-wrap;\r\n}\r\n\r\n.settings-connection-error-log-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: space-between;\r\n gap: 10px;\r\n}\r\n\r\n.settings-connection-error-log-header strong {\r\n color: var(--settings-text);\r\n font-size: 11px;\r\n font-weight: 600;\r\n}\r\n\r\n.settings-connection-error-log-header .settings-command-button {\r\n min-height: 30px;\r\n padding: 5px 9px;\r\n font-size: 11px;\r\n}\r\n\r\n.settings-connection-stderr {\r\n max-height: 220px;\r\n min-width: 0;\r\n margin: 0;\r\n padding: 11px;\r\n overflow: auto;\r\n color: var(--settings-text);\r\n font:\r\n 10px/16px ui-monospace,\r\n "SFMono-Regular",\r\n Consolas,\r\n "Liberation Mono",\r\n monospace;\r\n white-space: pre-wrap;\r\n overflow-wrap: anywhere;\r\n background: var(--settings-inset);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 9px;\r\n}\r\n\r\n.settings-connection-error-actions {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1fr);\r\n gap: 8px;\r\n}\r\n\r\n.settings-connection-error-actions .settings-command-button {\r\n width: 100%;\r\n}\r\n\r\n.settings-connection-issue-note {\r\n margin-top: -4px;\r\n}\r\n\r\n.settings-connection-state-summary {\r\n padding: 14px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 10px;\r\n}\r\n\r\n.settings-empty {\r\n display: flex;\r\n align-items: center;\r\n min-height: 72px;\r\n padding: 12px 16px;\r\n}\r\n\r\n.settings-empty > div {\r\n display: grid;\r\n min-width: 0;\r\n gap: 2px;\r\n}\r\n\r\n.settings-empty strong {\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n font-weight: 500;\r\n line-height: 19px;\r\n}\r\n\r\n.settings-empty span {\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 19px;\r\n}\r\n\r\n.settings-about-panel {\r\n display: grid;\r\n max-width: 720px;\r\n gap: 18px;\r\n padding: 24px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 12px;\r\n}\r\n\r\n.settings-about-product {\r\n color: var(--settings-text);\r\n font-size: 24px;\r\n font-weight: 700;\r\n line-height: 32px;\r\n}\r\n\r\n.settings-about-tagline {\r\n color: var(--settings-text);\r\n font-size: 16px;\r\n font-weight: 600;\r\n line-height: 24px;\r\n}\r\n\r\n.settings-about-version-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n margin: 4px 0 8px;\r\n}\r\n\r\n.settings-about-version-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--settings-text);\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n padding: 4px 10px;\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-about-copy {\r\n display: grid;\r\n max-width: 660px;\r\n gap: 12px;\r\n}\r\n\r\n.settings-about-copy p,\r\n.settings-about-repository p {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 21px;\r\n}\r\n\r\n.settings-about-star-callout {\r\n margin: 2px 0;\r\n color: var(--settings-warning);\r\n font-size: 14px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-about-repository {\r\n display: grid;\r\n gap: 10px;\r\n padding-top: 18px;\r\n border-top: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-about-repository-link {\r\n display: flex;\r\n align-items: center;\r\n width: fit-content;\r\n min-width: 0;\r\n max-width: 100%;\r\n gap: 8px;\r\n color: var(--settings-focus);\r\n font-size: 13px;\r\n font-weight: 600;\r\n text-decoration: none;\r\n}\r\n\r\n.settings-about-repository-link:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.settings-about-repository-link code {\r\n min-width: 0;\r\n overflow: hidden;\r\n color: var(--settings-muted);\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n font-size: 11px;\r\n font-weight: 400;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-page-error {\r\n padding: 16px;\r\n color: var(--settings-text);\r\n font-size: 13px;\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 20px;\r\n}\r\n\r\n.settings-update-metadata {\r\n display: grid;\r\n grid-template-columns: repeat(3, minmax(0, 1fr));\r\n gap: 24px;\r\n margin-bottom: 24px;\r\n padding: 0 4px 20px;\r\n border-bottom: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-update-metadata__item {\r\n display: grid;\r\n min-width: 0;\r\n gap: 4px;\r\n}\r\n\r\n.settings-update-metadata__item span {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-update-metadata__item strong {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n color: var(--settings-text);\r\n font-size: 15px;\r\n font-weight: 500;\r\n line-height: 22px;\r\n}\r\n\r\n/* The only emphasis on this screen: a latest version newer than the installed one. */\r\n.settings-update-metadata__value--newer {\r\n color: var(--settings-focus);\r\n}\r\n\r\n.settings-update-panel {\r\n display: grid;\r\n gap: 12px;\r\n padding: 20px;\r\n color: var(--settings-text);\r\n background: var(--settings-panel);\r\n border: 1px solid var(--settings-border);\r\n border-radius: 8px;\r\n}\r\n\r\n.settings-update-panel__head {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 8px;\r\n}\r\n\r\n.settings-update-panel__head .codexhost-settings-icon {\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-update-panel[data-update-state="available"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="downloading"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="installing"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="restarting"] .codexhost-settings-icon {\r\n color: var(--settings-focus);\r\n}\r\n\r\n.settings-update-panel[data-update-state="current"] .codexhost-settings-icon {\r\n color: var(--settings-success);\r\n}\r\n\r\n.settings-update-panel[data-update-state="failed"] .codexhost-settings-icon,\r\n.settings-update-panel[data-update-state="error"] .codexhost-settings-icon {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-update-panel__title {\r\n min-width: 0;\r\n overflow-wrap: anywhere;\r\n color: var(--settings-text);\r\n font-size: 14px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-update-summary,\r\n.settings-update-error {\r\n margin: 0;\r\n overflow-wrap: anywhere;\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-update-summary {\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-update-error {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-update-progress {\r\n width: 100%;\r\n height: 8px;\r\n accent-color: var(--settings-focus);\r\n}\r\n\r\n.settings-update-progress-detail {\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n/* Release notes scroll with the page; a scroller inside a scroller traps the wheel. */\r\n.settings-update-notes-section {\r\n min-width: 0;\r\n margin-top: 24px;\r\n padding-top: 24px;\r\n border-top: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-update-notes-section:empty {\r\n display: none;\r\n}\r\n\r\n.settings-update-notes {\r\n display: grid;\r\n min-width: 0;\r\n gap: 12px;\r\n color: var(--settings-muted);\r\n font-size: 14px;\r\n line-height: 22px;\r\n overflow-wrap: anywhere;\r\n}\r\n\r\n.settings-update-notes > :first-child {\r\n margin-top: 0;\r\n}\r\n\r\n.settings-update-notes > :last-child {\r\n margin-bottom: 0;\r\n}\r\n\r\n.settings-update-notes h1,\r\n.settings-update-notes h2,\r\n.settings-update-notes h3,\r\n.settings-update-notes h4,\r\n.settings-update-notes h5,\r\n.settings-update-notes h6 {\r\n margin: 6px 0 0;\r\n color: var(--settings-text);\r\n font-weight: 600;\r\n}\r\n\r\n.settings-update-notes h1 {\r\n font-size: 18px;\r\n line-height: 26px;\r\n}\r\n\r\n.settings-update-notes h2 {\r\n font-size: 17px;\r\n line-height: 24px;\r\n}\r\n\r\n.settings-update-notes h3,\r\n.settings-update-notes h4,\r\n.settings-update-notes h5,\r\n.settings-update-notes h6 {\r\n font-size: 14px;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-update-notes p,\r\n.settings-update-notes ul,\r\n.settings-update-notes ol,\r\n.settings-update-notes pre,\r\n.settings-update-notes blockquote {\r\n margin: 0;\r\n}\r\n\r\n.settings-update-notes ul,\r\n.settings-update-notes ol {\r\n padding-left: 24px;\r\n}\r\n\r\n.settings-update-notes li {\r\n padding-left: 4px;\r\n}\r\n\r\n.settings-update-notes li::marker {\r\n color: var(--settings-muted);\r\n font-size: 0.85em;\r\n}\r\n\r\n.settings-update-notes li + li {\r\n margin-top: 14px;\r\n}\r\n\r\n.settings-update-notes .release-note-translation {\r\n display: block;\r\n margin-top: 3px;\r\n color: var(--settings-muted);\r\n font-size: 13px;\r\n line-height: 20px;\r\n}\r\n\r\n.settings-update-notes blockquote {\r\n padding: 8px 12px;\r\n color: var(--settings-muted);\r\n border-left: 3px solid var(--settings-focus);\r\n background: var(--settings-surface);\r\n}\r\n\r\n.settings-update-notes hr {\r\n width: 100%;\r\n margin: 2px 0;\r\n border: 0;\r\n border-top: 1px solid var(--settings-divider);\r\n}\r\n\r\n.settings-update-notes strong {\r\n color: var(--settings-text);\r\n font-weight: 600;\r\n}\r\n\r\n.settings-update-notes a {\r\n color: var(--settings-focus);\r\n text-decoration: none;\r\n}\r\n\r\n.settings-update-notes a:hover {\r\n text-decoration: underline;\r\n}\r\n\r\n.settings-update-notes code {\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n font-size: 12px;\r\n}\r\n\r\n.settings-update-notes :not(pre) > code {\r\n padding: 1px 5px;\r\n background: var(--settings-surface);\r\n border-radius: 4px;\r\n}\r\n\r\n.settings-update-notes pre {\r\n min-width: 0;\r\n max-width: 100%;\r\n padding: 10px 12px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n background: var(--settings-inset);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-update-notes pre code {\r\n line-height: 18px;\r\n white-space: pre-wrap;\r\n}\r\n\r\n/* Manual update sits directly under the status panel, not behind a disclosure:\r\n automatic updates can fail for machine-local reasons and this is the way out. */\r\n.settings-update-controls {\r\n display: grid;\r\n min-width: 0;\r\n gap: 10px;\r\n margin-top: 24px;\r\n}\r\n\r\n.settings-update-manual-title {\r\n color: var(--settings-text);\r\n font-size: 15px;\r\n font-weight: 600;\r\n line-height: 22px;\r\n}\r\n\r\n.settings-update-manual {\r\n display: grid;\r\n min-width: 0;\r\n gap: 8px;\r\n}\r\n\r\n.settings-update-manual[hidden] {\r\n display: none;\r\n}\r\n\r\n.settings-update-manual-description {\r\n margin: 0;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n line-height: 18px;\r\n}\r\n\r\n.settings-update-manual-description.is-fallback {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-update-command {\r\n display: flex;\r\n align-items: stretch;\r\n min-width: 0;\r\n overflow: hidden;\r\n border: 1px solid var(--settings-border);\r\n border-radius: 6px;\r\n}\r\n\r\n.settings-update-command code {\r\n min-width: 0;\r\n flex: 1;\r\n padding: 9px 12px;\r\n overflow-x: auto;\r\n color: var(--settings-text);\r\n font-family: ui-monospace, "SFMono-Regular", Consolas, "Liberation Mono", monospace;\r\n font-size: 12px;\r\n line-height: 20px;\r\n white-space: nowrap;\r\n}\r\n\r\n.settings-update-command__copy {\r\n display: inline-flex;\r\n align-items: center;\r\n flex: none;\r\n gap: 6px;\r\n padding: 0 12px;\r\n color: var(--settings-muted);\r\n font-size: 12px;\r\n background: transparent;\r\n border: 0;\r\n border-left: 1px solid var(--settings-border);\r\n cursor: pointer;\r\n}\r\n\r\n.settings-update-command__copy:hover {\r\n color: var(--settings-text);\r\n background: var(--settings-hover);\r\n}\r\n\r\n.settings-update-command__copy:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: -2px;\r\n}\r\n\r\n.settings-update-actions {\r\n display: flex;\r\n align-items: center;\r\n min-width: 0;\r\n gap: 12px;\r\n flex-wrap: wrap;\r\n}\r\n\r\n.settings-command-button,\r\n.settings-update-link {\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n flex: none;\r\n width: max-content;\r\n max-width: 100%;\r\n min-height: 30px;\r\n gap: 6px;\r\n padding: 5px 10px;\r\n white-space: nowrap;\r\n color: var(--settings-primary-text);\r\n font: inherit;\r\n font-size: 12px;\r\n text-decoration: none;\r\n background: var(--settings-primary);\r\n border: 1px solid var(--settings-primary);\r\n border-radius: 7px;\r\n cursor: pointer;\r\n}\r\n\r\n.settings-command-button--secondary,\r\n.settings-update-link {\r\n color: var(--settings-text);\r\n background: var(--settings-panel);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-command-button:hover:not(:disabled) {\r\n background: var(--settings-primary-hover);\r\n border-color: var(--settings-primary-hover);\r\n}\r\n\r\n.settings-command-button--secondary:hover:not(:disabled),\r\n.settings-update-link:hover {\r\n background: var(--settings-hover);\r\n border-color: var(--settings-border);\r\n}\r\n\r\n.settings-command-button--danger {\r\n color: #f87171;\r\n}\r\n\r\n.settings-command-button--danger:hover:not(:disabled) {\r\n color: #fecaca;\r\n background: rgb(127 29 29 / 24%);\r\n border-color: rgb(248 113 113 / 45%);\r\n}\r\n\r\n.settings-command-button:disabled {\r\n cursor: default;\r\n opacity: 0.45;\r\n}\r\n\r\n.settings-command-button:disabled,\r\n.settings-command-button[aria-disabled="true"] {\r\n opacity: 0.55;\r\n cursor: not-allowed;\r\n filter: none;\r\n}\r\n\r\n.settings-command-button:focus-visible,\r\n.settings-update-link:focus-visible {\r\n outline: 2px solid var(--settings-focus);\r\n outline-offset: 2px;\r\n}\r\n\r\n.codexhost-settings-icon {\r\n display: block;\r\n flex: none;\r\n stroke: currentColor;\r\n}\r\n\r\n@media (max-width: 720px) {\r\n .codexhost-settings-dialog {\r\n width: calc(100vw - 16px);\r\n height: calc(100vh - 16px);\r\n border-radius: 10px;\r\n }\r\n\r\n .settings-layout {\r\n grid-template-columns: minmax(0, 1fr);\r\n grid-template-rows: auto minmax(0, 1fr);\r\n }\r\n\r\n .settings-session-import-row {\r\n grid-template-columns: minmax(0, 1fr);\r\n }\r\n\r\n .settings-session-import-row__action {\r\n align-items: flex-start;\r\n }\r\n\r\n .settings-sidebar {\r\n border-right: 0;\r\n border-bottom: 1px solid var(--settings-border);\r\n }\r\n\r\n .settings-header {\r\n height: 56px;\r\n padding-inline: 14px;\r\n }\r\n\r\n .settings-sidebar {\r\n padding-top: 20px;\r\n }\r\n\r\n .settings-nav {\r\n flex: none;\r\n flex-direction: row;\r\n gap: 4px;\r\n padding: 7px 8px 8px;\r\n overflow-x: auto;\r\n overflow-y: hidden;\r\n }\r\n\r\n .settings-nav-section-label {\r\n display: none;\r\n }\r\n\r\n .settings-nav-button {\r\n width: auto;\r\n min-width: max-content;\r\n min-height: 34px;\r\n grid-template-columns: 16px auto;\r\n border-radius: 10px;\r\n }\r\n\r\n .settings-nav-star-link {\r\n margin-top: 0;\r\n }\r\n\r\n .settings-page__content {\r\n width: calc(100% - 40px);\r\n }\r\n\r\n .settings-page__content {\r\n padding-top: 32px;\r\n padding-bottom: 32px;\r\n }\r\n\r\n .settings-section-label {\r\n padding-bottom: 20px;\r\n font-size: 20px;\r\n line-height: 26px;\r\n }\r\n\r\n .settings-update-metadata {\r\n grid-template-columns: minmax(0, 1fr);\r\n gap: 16px;\r\n }\r\n\r\n .settings-status-row {\r\n grid-template-columns: minmax(0, 1fr) auto;\r\n gap: 16px;\r\n }\r\n\r\n .settings-account-header,\r\n .settings-connection-page-header {\r\n flex-direction: column;\r\n }\r\n\r\n .settings-account-header > .settings-command-button,\r\n .settings-connection-page-header > .settings-command-button {\r\n width: 100%;\r\n }\r\n\r\n .settings-connections-layout {\r\n grid-template-columns: minmax(0, 1fr);\r\n }\r\n\r\n .settings-connection-inspector {\r\n position: static;\r\n }\r\n\r\n .settings-status-row__detail {\r\n grid-column: 1 / -1;\r\n margin: -8px 0 0 58px;\r\n }\r\n}\r\n\r\n@media (forced-colors: active) {\r\n :host {\r\n --settings-bg: Canvas;\r\n --settings-sidebar: Canvas;\r\n --settings-panel: Canvas;\r\n --settings-surface: Canvas;\r\n --settings-surface-hover: Highlight;\r\n --settings-inset: Canvas;\r\n --settings-text: CanvasText;\r\n --settings-muted: GrayText;\r\n --settings-subtle: GrayText;\r\n --settings-border: ButtonBorder;\r\n --settings-divider: ButtonBorder;\r\n --settings-hover: Highlight;\r\n --settings-active: Highlight;\r\n --settings-focus: Highlight;\r\n --settings-primary: ButtonFace;\r\n --settings-primary-hover: Highlight;\r\n --settings-primary-text: ButtonText;\r\n --settings-warning: CanvasText;\r\n --settings-warning-bg: Canvas;\r\n --settings-warning-border: ButtonBorder;\r\n --settings-danger: CanvasText;\r\n --settings-danger-bg: Canvas;\r\n --settings-danger-border: ButtonBorder;\r\n --settings-success: CanvasText;\r\n }\r\n}\r\n\r\n@media (prefers-reduced-motion: reduce) {\r\n *,\r\n *::before,\r\n *::after {\r\n scroll-behavior: auto !important;\r\n transition-duration: 0s !important;\r\n }\r\n}\r\n\r\n/* Harness Mix: Model Configuration & One-click Install */\r\n.settings-model-config-panel {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n padding: 14px;\r\n margin-top: 14px;\r\n border-radius: 8px;\r\n background: var(--settings-surface);\r\n border: 1px solid var(--settings-border);\r\n}\r\n\r\n.settings-model-config-head {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 4px;\r\n}\r\n\r\n.settings-model-config-title {\r\n font-size: 13px;\r\n font-weight: 600;\r\n color: var(--settings-text);\r\n}\r\n\r\n.settings-model-config-description {\r\n font-size: 11px;\r\n color: var(--settings-muted);\r\n margin: 0;\r\n}\r\n\r\n.settings-model-config-loading,\r\n.settings-model-config-empty {\r\n font-size: 12px;\r\n color: var(--settings-muted);\r\n padding: 8px 0;\r\n}\r\n\r\n.settings-model-config-form {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 12px;\r\n margin-top: 4px;\r\n}\r\n\r\n.settings-model-config-field {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n}\r\n\r\n.settings-model-config-label {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--settings-muted);\r\n text-transform: uppercase;\r\n letter-spacing: 0.5px;\r\n}\r\n\r\n.settings-model-config-select {\r\n height: 32px;\r\n padding: 0 10px;\r\n border-radius: 6px;\r\n border: 1px solid var(--settings-border);\r\n background: var(--settings-panel);\r\n color: var(--settings-text);\r\n font-size: 12px;\r\n outline: none;\r\n}\r\n\r\n.settings-model-config-select:focus {\r\n border-color: var(--settings-primary);\r\n}\r\n\r\n.settings-model-config-actions {\r\n display: flex;\r\n align-items: center;\r\n flex-wrap: wrap;\r\n gap: 8px;\r\n margin-top: 6px;\r\n}\r\n\r\n.settings-model-config-feedback {\r\n font-size: 12px;\r\n font-weight: 500;\r\n}\r\n\r\n.settings-feedback-success {\r\n color: var(--settings-success);\r\n}\r\n\r\n.settings-feedback-error {\r\n color: var(--settings-danger);\r\n}\r\n\r\n.settings-feedback-info {\r\n color: var(--settings-primary);\r\n}\r\n\r\n.settings-install-command-box {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 6px;\r\n min-width: 0;\r\n margin: 0;\r\n}\r\n\r\n.settings-install-command-label {\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-install-command-row {\r\n display: flex;\r\n align-items: center;\r\n background: var(--settings-inset);\r\n padding: 8px 12px;\r\n border-radius: 6px;\r\n border: 1px solid var(--settings-border);\r\n min-width: 0;\r\n overflow-x: auto;\r\n}\r\n\r\n.settings-install-command-row code {\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--settings-text);\r\n user-select: all;\r\n white-space: pre-wrap;\r\n word-break: break-all;\r\n}\r\n\r\n.settings-install-actions {\r\n display: grid;\r\n grid-template-columns: minmax(0, 1fr);\r\n gap: 8px;\r\n margin-top: 4px;\r\n}\r\n\r\n.settings-install-actions .settings-command-button {\r\n width: 100%;\r\n justify-content: center;\r\n}\r\n\r\n.settings-install-feedback {\r\n font-size: 12px;\r\n font-weight: 500;\r\n line-height: 18px;\r\n margin-top: 2px;\r\n overflow-wrap: anywhere;\r\n}\r\n\r\n.settings-install-manual-card {\r\n display: flex;\r\n flex-direction: column;\r\n gap: 8px;\r\n background: var(--settings-inset);\r\n padding: 12px 14px;\r\n border-radius: 8px;\r\n border: 1px solid var(--settings-border);\r\n margin-top: 6px;\r\n margin-bottom: 4px;\r\n}\r\n\r\n.settings-install-manual-badge {\r\n display: inline-flex;\r\n align-items: center;\r\n font-size: 11px;\r\n font-weight: 600;\r\n color: var(--settings-accent, #10a37f);\r\n background: var(--settings-accent-subtle, rgba(16, 163, 127, 0.1));\r\n padding: 2px 8px;\r\n border-radius: 4px;\r\n width: fit-content;\r\n}\r\n\r\n.settings-install-manual-notice {\r\n font-size: 12px;\r\n line-height: 18px;\r\n color: var(--settings-text);\r\n margin: 0;\r\n}\r\n\r\n.settings-install-steps {\r\n margin: 0;\r\n padding-left: 18px;\r\n font-size: 12px;\r\n line-height: 20px;\r\n color: var(--settings-muted);\r\n}\r\n\r\n.settings-install-steps li {\r\n margin-bottom: 2px;\r\n}\r\n/* MCP and Skills share native settings controls while keeping separate flows. */\n.settings-integrations-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 18px 0 12px; }\n.settings-integrations-controls input { flex: 1; min-width: 180px; }\n.settings-integrations-controls input[hidden] { display: none; }\n.settings-integrations-controls select, .settings-integrations-controls input,\n.settings-integrations-field input, .settings-integrations-toolbar input { box-sizing: border-box; padding: 9px 11px; border: 1px solid var(--settings-border, #8885); border-radius: 8px; color: inherit; background: var(--settings-input, transparent); font: inherit; }\n.settings-integrations-status { min-height: 18px; margin: 0 0 6px; color: var(--settings-muted); font-size: 12px; }\n.settings-integrations-body p { font-size: 12px; line-height: 1.6; opacity: .8; }\n.settings-integrations-toolbar { display: flex; gap: 10px; align-items: center; margin: 8px 0 16px; }\n.settings-integrations-toolbar input { flex: 1; }\n.settings-integrations-list { display: grid; gap: 8px; }\n.settings-integrations-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; border: 1px solid var(--settings-border, #8885); padding: 14px; border-radius: 10px; background: var(--settings-surface, transparent); }\n.settings-integrations-row > div { flex: 1; min-width: 180px; }\n.settings-integrations-row p { margin: 3px 0; }\n.settings-integrations-row code { display: block; overflow-wrap: anywhere; font-size: 11px; color: var(--settings-muted); }\n.settings-integrations-row--native { border-style: dashed; }\n.settings-integrations-empty { padding: 36px 18px; border: 1px dashed var(--settings-border, #8885); border-radius: 10px; text-align: center; color: var(--settings-muted); font-size: 12px; }\n.settings-integrations-editor-head { display: flex; align-items: center; gap: 12px; }\n.settings-integrations-editor-head h3 { margin: 0; }\n.settings-integrations-form { display: grid; gap: 14px; margin: 16px 0 28px; }\n.settings-integrations-form--card { padding: 16px; border: 1px solid var(--settings-border, #8885); border-radius: 12px; }\n.settings-integrations-field { display: grid; gap: 6px; font-size: 12px; }\n.settings-integrations-form > button { justify-self: start; }\n.settings-mcp-args { display: grid; gap: 7px; }\n.settings-mcp-arg { display: flex; gap: 8px; align-items: center; }\n.settings-mcp-arg input { flex: 1; }\n.settings-mcp-kv-row { display: flex; gap: 8px; align-items: center; }\n.settings-mcp-kv-row input { flex: 1; }\n.settings-mcp-add-arg { margin-top: 7px; }\n.settings-mcp-docs-link { display: inline-flex; align-items: center; gap: 4px; color: var(--settings-muted, #888); font-size: 13px; text-decoration: none; transition: color 150ms ease; }\n.settings-mcp-docs-link:hover { color: var(--settings-text, #fff); text-decoration: underline; }\n.settings-integrations-editor-head .settings-command-button--danger { margin-left: auto; }\n.settings-mcp-type-toggle { display: inline-flex; padding: 3px; border-radius: 8px; background: var(--settings-inset, rgba(255, 255, 255, 0.06)); border: 1px solid var(--settings-border, #8885); gap: 2px; width: fit-content; }\n.settings-mcp-type-btn { padding: 5px 14px; border-radius: 6px; border: none; background: transparent; color: var(--settings-muted, #888); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 120ms ease; }\n.settings-mcp-type-btn:hover { color: var(--settings-text, #fff); }\n.settings-mcp-type-btn[data-active="true"] { background: var(--settings-active, rgba(255, 255, 255, 0.15)); color: var(--settings-text, #fff); font-weight: 600; }\n.settings-mcp-notice { margin: 4px 0 0; color: var(--settings-muted, #888); font-size: 12px; line-height: 1.5; }\n.settings-mcp-section { display: grid; gap: 14px; }\n.settings-mcp-icon-btn { display: inline-flex; align-items: center; justify-content: center; width: 34px; height: 34px; padding: 0; border: 1px solid var(--settings-border, #8885); border-radius: 8px; background: var(--settings-input, transparent); color: var(--settings-muted, #888); cursor: pointer; flex: none; transition: all 120ms ease; }\n.settings-mcp-icon-btn:hover:not(:disabled) { background: rgba(239, 68, 68, 0.15); border-color: rgba(239, 68, 68, 0.4); color: #f87171; }\n.settings-mcp-icon-btn svg { width: 14px; height: 14px; display: block; }\n.settings-integrations-callout { margin: 0; padding: 10px 12px; border-radius: 8px; background: var(--settings-inset); }\n.settings-skill-dropzone { display: grid; justify-items: center; gap: 8px; padding: 34px 22px; margin: 8px 0 18px; border: 1.5px dashed var(--settings-border, #8887); border-radius: 14px; background: var(--settings-inset); text-align: center; transition: border-color 120ms ease, background 120ms ease, transform 120ms ease; }\n.settings-skill-dropzone > span { max-width: 520px; color: var(--settings-muted); font-size: 12px; line-height: 18px; }\n.settings-skill-dropzone[data-drag-over="true"] { border-color: var(--settings-accent, #10a37f); background: var(--settings-accent-subtle, rgba(16, 163, 127, .1)); transform: scale(.995); }\n.settings-skill-dropzone[data-disabled="true"] { opacity: .55; }\n.settings-skills-target-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; justify-content: center; margin-top: 6px; }\n.settings-skills-target-row select { padding: 6px 10px; border: 1px solid var(--settings-border, #8885); border-radius: 8px; background: var(--settings-input, transparent); color: inherit; font: inherit; font-size: 12px; }\n.settings-skills-overview-stats { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin: 6px 0 10px; font-size: 12px; color: var(--settings-muted); }\n.settings-skills-tabs { display: inline-flex; padding: 3px; border-radius: 8px; background: var(--settings-inset, rgba(255, 255, 255, 0.06)); border: 1px solid var(--settings-border, #8885); gap: 2px; }\n.settings-skills-tab-btn { padding: 5px 14px; border-radius: 6px; border: none; background: transparent; color: var(--settings-muted, #888); font: inherit; font-size: 12px; font-weight: 500; cursor: pointer; transition: all 120ms ease; }\n.settings-skills-tab-btn:hover { color: var(--settings-text, #fff); }\n.settings-skills-tab-btn[data-active="true"] { background: var(--settings-active, rgba(255, 255, 255, 0.15)); color: var(--settings-text, #fff); font-weight: 600; }\n.settings-skills-overview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; margin-top: 10px; }\n.settings-skills-harness-card { border: 1px solid var(--settings-border, #8885); border-radius: 12px; padding: 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 10px; }\n.settings-skills-harness-head { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--settings-border, #8883); padding-bottom: 8px; }\n.settings-skills-harness-head strong { font-size: 14px; }\n.settings-skills-badge { font-size: 11px; padding: 2px 8px; border-radius: 10px; background: var(--settings-inset, rgba(255, 255, 255, 0.08)); color: var(--settings-muted); }\n.settings-skills-badge--active { background: rgba(16, 163, 127, 0.15); color: #10a37f; }\n.settings-skills-list-compact { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow-y: auto; }\n.settings-skills-item-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--settings-inset, rgba(255, 255, 255, 0.03)); font-size: 12px; }\n.settings-skills-item-row strong { font-size: 13px; }\n.settings-skills-item-row code { font-size: 10px; color: var(--settings-muted); overflow-wrap: anywhere; display: block; }\n.settings-skills-item-actions { display: flex; gap: 6px; align-items: center; flex: none; }\n.settings-skills-matrix-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }\n.settings-skills-matrix-card { border: 1px solid var(--settings-border, #8885); border-radius: 10px; padding: 12px 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 8px; }\n.settings-skills-matrix-head { display: flex; justify-content: space-between; align-items: center; }\n.settings-skills-matrix-head span { font-size: 11px; color: var(--settings-muted); }\n.settings-skills-tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.settings-skills-tag { font-size: 11px; padding: 3px 8px; border-radius: 6px; background: rgba(16, 163, 127, 0.15); color: #10a37f; border: 1px solid rgba(16, 163, 127, 0.3); }\n.settings-skills-sync-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 4px; }\n.settings-skills-sync-modal { border: 1px solid var(--settings-border, #8885); border-radius: 12px; background: var(--settings-bg, #1e1e1e); color: inherit; padding: 20px 24px; max-width: 420px; box-shadow: 0 16px 32px rgba(0, 0, 0, 0.5); }\n.settings-skills-sync-modal::backdrop { background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(2px); }\n.settings-skills-sync-modal h3 { margin: 0 0 8px; font-size: 16px; }\n.settings-skills-sync-modal p { margin: 0 0 12px; font-size: 12px; color: var(--settings-muted); }\n.settings-mcp-overview-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: 14px; margin-top: 10px; }\n.settings-mcp-harness-card { border: 1px solid var(--settings-border, #8885); border-radius: 12px; padding: 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 10px; }\n.settings-mcp-harness-head { display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--settings-border, #8883); padding-bottom: 8px; }\n.settings-mcp-harness-head strong { font-size: 14px; }\n.settings-mcp-native-box { padding: 8px 10px; border-radius: 8px; background: rgba(16, 163, 127, 0.08); border: 1px dashed rgba(16, 163, 127, 0.3); font-size: 11px; display: flex; flex-direction: column; gap: 4px; }\n.settings-mcp-native-title { font-weight: 600; color: #10a37f; display: flex; align-items: center; gap: 6px; }\n.settings-mcp-native-item { display: flex; flex-direction: column; gap: 2px; color: var(--settings-muted); }\n.settings-mcp-native-item code { font-size: 10px; overflow-wrap: anywhere; }\n.settings-mcp-list-compact { display: flex; flex-direction: column; gap: 8px; max-height: 280px; overflow-y: auto; }\n.settings-mcp-item-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; background: var(--settings-inset, rgba(255, 255, 255, 0.03)); font-size: 12px; }\n.settings-mcp-item-row strong { font-size: 13px; }\n.settings-mcp-item-row code { font-size: 10px; color: var(--settings-muted); overflow-wrap: anywhere; display: block; }\n.settings-mcp-item-actions { display: flex; gap: 6px; align-items: center; flex: none; }\n.settings-mcp-card-footer { display: flex; justify-content: flex-end; margin-top: auto; padding-top: 6px; border-top: 1px solid var(--settings-border, #8882); }\n.settings-mcp-matrix-list { display: flex; flex-direction: column; gap: 10px; margin-top: 10px; }\n.settings-mcp-matrix-card { border: 1px solid var(--settings-border, #8885); border-radius: 10px; padding: 12px 14px; background: var(--settings-surface, transparent); display: flex; flex-direction: column; gap: 8px; }\n.settings-mcp-matrix-head { display: flex; justify-content: space-between; align-items: center; }\n.settings-mcp-matrix-head span { font-size: 11px; color: var(--settings-muted); }\n.settings-mcp-tags { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }\n.settings-mcp-tag { font-size: 11px; padding: 3px 8px; border-radius: 6px; background: rgba(16, 163, 127, 0.15); color: #10a37f; border: 1px solid rgba(16, 163, 127, 0.3); }\n.settings-mcp-tag--disabled { background: rgba(255, 255, 255, 0.05); color: var(--settings-muted); border-color: var(--settings-border, #8885); }\n.settings-mcp-sync-actions { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 4px; }\n.settings-mcp-distribute-modal { border: 1px solid var(--settings-border, #8885); border-radius: 12px; background: var(--settings-bg, #1e1e1e); color: inherit; padding: 20px 24px; max-width: 540px; width: 92%; max-height: 85vh; overflow-y: auto; box-shadow: 0 16px 32px rgba(0, 0, 0, 0.5); }\n.settings-mcp-distribute-modal::backdrop { background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(2px); }\n.settings-mcp-distribute-modal h3 { margin: 0 0 8px; font-size: 16px; }\n.settings-mcp-distribute-modal p { margin: 0 0 12px; font-size: 12px; color: var(--settings-muted); }\n\n\n';
|
|
26553
28938
|
|
|
26554
28939
|
// src/native-ui/renderer-extension/src/settings/accounts.css
|
|
26555
28940
|
var accounts_default = '.settings-harness-accounts {\n margin-top: 32px;\n}\n\n.settings-harness-accounts__title {\n margin: 0 0 16px;\n font-size: 14px;\n font-weight: 500;\n}\n\n.settings-harness-account__identity {\n min-width: 0;\n}\n\n.settings-harness-account__identity > .settings-account-email {\n display: block;\n max-width: 24ch;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-harness-account__logo {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 40px;\n height: 40px;\n flex: none;\n}\n\n.settings-harness-account {\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex-wrap: wrap;\n gap: 20px;\n padding: 20px 24px;\n}\n\n.settings-harness-account > .settings-account-row__person {\n flex: 1 1 280px;\n min-width: 0;\n}\n\n.settings-harness-account > .settings-account-usage {\n flex: 1 1 220px;\n min-width: 0;\n}\n\n.settings-harness-account > .settings-harness-account__actions {\n flex: 0 0 auto;\n}\n\n.settings-harness-account__badge {\n display: inline-flex;\n align-items: center;\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 500;\n line-height: 16px;\n}\n\n.settings-harness-account__badge--ready {\n background: rgba(16, 185, 129, 0.12);\n color: #10b981;\n border: 1px solid rgba(16, 185, 129, 0.28);\n}\n\n.settings-harness-account__badge--unconfigured {\n background: rgba(245, 158, 11, 0.12);\n color: #f59e0b;\n border: 1px solid rgba(245, 158, 11, 0.28);\n}\n\n.settings-harness-account__badge--not_installed {\n background: rgba(156, 163, 175, 0.12);\n color: var(--settings-muted);\n border: 1px solid rgba(156, 163, 175, 0.25);\n}\n\n.settings-harness-account__config-hint {\n margin-top: 6px;\n font-size: 11px;\n color: var(--settings-muted);\n display: flex;\n align-items: center;\n gap: 4px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.settings-harness-account__config-hint code {\n font-family: ui-monospace, Consolas, monospace;\n font-size: 11px;\n background: var(--settings-surface);\n border: 1px solid var(--settings-border);\n padding: 1px 5px;\n border-radius: 4px;\n}\n\n.settings-harness-account__actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.settings-harness-account__login-btn {\n font-size: 12px;\n padding: 6px 12px;\n white-space: nowrap;\n}\n\n.settings-harness-account + .settings-harness-account {\n border-top: 1px solid var(--settings-border);\n}\n\n.settings-harness-account > div {\n min-width: 0;\n overflow-wrap: anywhere;\n}\n\n@media (max-width: 700px) {\n .settings-harness-account {\n grid-template-columns: minmax(0, 1fr);\n padding: 16px;\n }\n}\n\n.settings-account-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 20px;\n}\n\n.settings-account-eyebrow {\n margin: 0 0 8px;\n color: var(--settings-muted);\n font-size: 11px;\n letter-spacing: 0.13em;\n}\n\n.settings-account-header > :first-child {\n min-width: 0;\n flex: 1 1 auto;\n}\n\n.settings-account-header > .settings-command-button {\n flex: none;\n white-space: nowrap;\n margin-top: 20px;\n min-height: 36px;\n padding: 8px 12px;\n border-radius: 7px;\n}\n\n.settings-account-header .settings-section-label {\n margin: 0;\n padding: 0;\n font-size: 27px;\n line-height: 1.4;\n font-weight: 650;\n letter-spacing: -0.035em;\n}\n.settings-account-header .settings-page-description {\n margin: 8px 0 0;\n font-size: 13px;\n}\n\n.settings-account-status {\n margin: 0;\n color: var(--settings-muted);\n font-size: 12px;\n}\n.settings-account-status:empty {\n display: none;\n}\n\n.settings-account-help-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n margin-top: 20px;\n flex-wrap: wrap;\n}\n.settings-account-task-hint {\n display: flex;\n align-items: flex-start;\n gap: 9px;\n margin: 0;\n color: var(--settings-muted);\n font-size: 12px;\n flex: 1 1 280px;\n}\n.settings-account-task-hint svg {\n flex: none;\n margin-top: 1px;\n}\n.settings-account-help-button {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n padding: 8px 12px;\n color: var(--settings-muted);\n font-size: 12px;\n background: transparent;\n border: 0;\n border-radius: 7px;\n cursor: pointer;\n}\n.settings-account-help-button:hover {\n color: var(--settings-text);\n background: var(--settings-hover);\n}\n.settings-account-device-code-note {\n margin: 12px 0 0;\n font-size: 12px;\n padding: 10px 12px;\n color: var(--settings-warning);\n background: var(--settings-warning-bg);\n border: 1px solid var(--settings-warning-border);\n border-radius: 8px;\n}\n\n.settings-account-toolbar {\n margin-top: 27px;\n padding: 18px 0;\n border-top: 1px solid var(--settings-border);\n display: flex;\n align-items: center;\n gap: 14px;\n flex-wrap: wrap;\n}\n.settings-account-count {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n white-space: nowrap;\n}\n.settings-account-count > :last-child {\n color: var(--settings-muted);\n font-variant-numeric: tabular-nums;\n}\n.settings-account-search {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--settings-muted);\n margin-right: auto;\n min-width: 0;\n max-width: 100%;\n}\n.settings-account-search svg {\n flex: none;\n}\n.settings-account-toolbar input {\n min-width: 80px;\n width: 150px;\n max-width: 100%;\n padding: 5px 0;\n color: var(--settings-text);\n background: transparent;\n border: 0;\n font-size: 13px;\n}\n.settings-account-toolbar input::placeholder {\n color: var(--settings-muted);\n}\n.settings-account-display-controls {\n display: flex;\n padding: 3px;\n border: 1px solid var(--settings-border);\n border-radius: 7px;\n background: var(--settings-inset);\n}\n.settings-account-display-controls button {\n padding: 4px 10px;\n color: var(--settings-muted);\n background: transparent;\n border: 0;\n border-radius: 4px;\n font-size: 12px;\n cursor: pointer;\n}\n.settings-account-display-controls button:hover {\n color: var(--settings-text);\n}\n.settings-account-display-controls button[aria-pressed="true"] {\n color: var(--settings-text);\n background: var(--settings-active);\n}\n\n.settings-account-list {\n --account-border: light-dark(#e2e2e8, #36363b);\n --account-muted: light-dark(#676773, #a5a5af);\n border: 1px solid var(--account-border);\n border-radius: 11px;\n overflow: hidden;\n container-type: inline-size;\n}\n.settings-account-table {\n width: 100%;\n table-layout: fixed;\n border-collapse: collapse;\n font-size: 12px;\n}\n.settings-account-table th {\n padding: 13px 20px;\n color: var(--account-muted);\n background: light-dark(#f8f8fa, #202024);\n font-size: 11px;\n font-weight: 500;\n text-align: left;\n}\n.settings-account-table th:nth-child(1) {\n width: 32%;\n}\n.settings-account-table th:nth-child(2) {\n width: 38%;\n}\n.settings-account-table th:nth-child(3) {\n width: 12%;\n}\n.settings-account-table th:nth-child(4) {\n width: 18%;\n text-align: right;\n}\n.settings-account-table td {\n padding: 19px 20px;\n vertical-align: middle;\n border-top: 1px solid var(--settings-divider);\n}\n.settings-account-row {\n background: light-dark(#ffffff, #232326);\n}\n.settings-account-row:hover {\n background: light-dark(#f7f7fa, #26262a);\n}\n.settings-account-row__person {\n display: grid;\n grid-template-columns: 34px minmax(0, 1fr);\n align-items: center;\n gap: 11px;\n min-width: 0;\n}\n.settings-account-row__mark {\n display: grid;\n place-items: center;\n width: 34px;\n height: 34px;\n color: light-dark(#67558d, #c3baff);\n background: light-dark(#f0ecf7, #353044);\n border: 1px solid light-dark(#d9d0e6, #4d435f);\n border-radius: 10px;\n}\n.settings-account-row__identity {\n min-width: 0;\n}\n.settings-account-title {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n.settings-account-email {\n min-width: 0;\n overflow: hidden;\n font-size: 13px;\n font-weight: 550;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.settings-account-metadata {\n display: flex;\n align-items: center;\n min-width: 0;\n gap: 7px;\n margin-top: 4px;\n color: var(--settings-muted);\n}\n.settings-account-domain {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.settings-account-plan-separator {\n flex: none;\n}\n.settings-account-plan {\n flex: none;\n padding: 1px 6px;\n color: var(--settings-muted);\n border: 1px solid var(--settings-border);\n border-radius: 5px;\n font-size: 11px;\n white-space: nowrap;\n}\n.settings-account-plan--highlighted {\n color: light-dark(#654f91, #c8bfff);\n background: light-dark(#f2edf9, #a99bff18);\n border-color: light-dark(#d9ccea, #a99bff52);\n font-weight: 600;\n}\n.settings-account-active {\n flex: none;\n padding: 2px 7px;\n color: light-dark(#66548e, #bdb4ff);\n background: light-dark(#f4f0fb, #aca0ff12);\n border: 1px solid light-dark(#dfd5f1, #aca0ff24);\n border-radius: 5px;\n font-size: 11px;\n white-space: nowrap;\n}\n\n.settings-account-usage {\n display: grid;\n gap: 14px;\n max-width: 380px;\n}\n.settings-account-usage__meter {\n display: grid;\n grid-template-columns: 48px minmax(36px, 1fr) 96px;\n align-items: center;\n gap: 3px 10px;\n}\n:host([lang="zh-CN"]) .settings-account-usage__meter {\n grid-template-columns: 48px minmax(36px, 1fr) 72px;\n}\n\n.settings-account-usage__title {\n color: light-dark(#5d5d68, #c3c3cd);\n font-size: 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.settings-account-usage__percent {\n display: flex;\n align-items: baseline;\n justify-content: flex-end;\n gap: 4px;\n font-size: 14px;\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n}\n.settings-account-usage__value-label {\n color: var(--settings-muted);\n font-size: 11px;\n font-weight: 400;\n}\n.settings-account-usage__percent--warn {\n color: var(--settings-warning);\n}\n.settings-account-usage__percent--hot {\n color: var(--settings-danger);\n}\n.settings-account-usage__bar {\n height: 4px;\n overflow: hidden;\n background: light-dark(#e3e1e8, #3c3c43);\n border-radius: 4px;\n}\n.settings-account-usage__bar > span {\n display: block;\n height: 100%;\n background: light-dark(#81769e, #9993b8);\n border-radius: inherit;\n}\n.settings-account-usage__bar--warn > span {\n background: var(--settings-warning);\n}\n.settings-account-usage__bar--hot > span {\n background: var(--settings-danger);\n}\n.settings-account-usage__sub {\n grid-column: 2 / -1;\n color: var(--account-muted);\n font-size: 11px;\n font-variant-numeric: tabular-nums;\n overflow-wrap: anywhere;\n}\n.settings-account-usage__message,\n.settings-account-unknown {\n color: var(--settings-muted);\n}\n.settings-account-usage > button {\n justify-self: start;\n}\n.settings-account-actions {\n display: flex;\n justify-content: flex-end;\n align-items: center;\n gap: 4px;\n flex-wrap: wrap;\n}\n.settings-account-action {\n padding: 8px 10px;\n min-height: 36px;\n font-size: 12px;\n color: var(--settings-text);\n background: transparent;\n border: 1px solid transparent;\n border-radius: 7px;\n white-space: nowrap;\n cursor: pointer;\n}\n.settings-account-action:hover {\n background: var(--settings-hover);\n}\n.settings-account-action:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n.settings-account-actions .settings-account-delete {\n color: var(--settings-muted);\n}\n.settings-account-actions .settings-account-delete:hover,\n.settings-account-actions .settings-account-delete:focus-visible {\n color: var(--settings-danger);\n background: var(--settings-danger-bg);\n}\n.settings-account-mobile-label {\n display: none;\n}\n.settings-account-reset-summary {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n min-height: 32px;\n padding: 3px 0;\n font-size: 12px;\n white-space: nowrap;\n color: var(--settings-text);\n background: transparent;\n border: 0;\n border-radius: 5px;\n cursor: pointer;\n}\n.settings-account-reset-summary:hover {\n background: var(--settings-hover);\n}\n.settings-account-reset-summary[aria-expanded="true"] > svg:last-child {\n transform: rotate(90deg);\n}\n.settings-account-details-row > td {\n background: var(--settings-inset);\n}\n.settings-account-reset-details {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n}\n.settings-account-reset-details strong {\n font-size: 12px;\n}\n.settings-account-reset-expiry {\n margin: 6px 0;\n color: var(--settings-muted);\n font-size: 11px;\n}\n.settings-account-reset-expiry.is-warn {\n color: var(--settings-warning);\n}\n.settings-account-reset-expiry.is-hot {\n color: var(--settings-danger);\n}\n.settings-account-reset-list {\n margin: 8px 0 0;\n padding: 0;\n list-style: none;\n color: var(--settings-muted);\n font-size: 11px;\n}\n.settings-account-reset-list li + li {\n margin-top: 4px;\n}\n.settings-account-empty {\n text-align: center;\n color: var(--settings-muted);\n}\n.settings-account-verification {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n color: var(--settings-muted);\n}\n.settings-account-verification a {\n color: var(--settings-focus);\n overflow-wrap: anywhere;\n}\n.settings-account-verification code {\n padding: 5px 8px;\n color: var(--settings-text);\n font-size: 15px;\n font-weight: 600;\n letter-spacing: 0.08em;\n background: var(--settings-inset);\n border-radius: 6px;\n}\n.settings-account-list :focus-visible,\n.settings-account-toolbar :focus-visible,\n.settings-account-help-button:focus-visible {\n outline: 2px solid var(--settings-focus);\n outline-offset: -2px;\n}\n\n@container (min-width: 611px) and (max-width: 800px) {\n .settings-account-table th,\n .settings-account-table td {\n padding-left: 12px;\n padding-right: 12px;\n }\n .settings-account-table th:nth-child(1) {\n width: 30%;\n }\n .settings-account-table th:nth-child(2) {\n width: 37%;\n }\n .settings-account-table th:nth-child(4) {\n width: 21%;\n }\n}\n\n@container (max-width: 610px) {\n .settings-account-table,\n .settings-account-table tbody {\n display: block;\n }\n .settings-account-table thead {\n display: none;\n }\n .settings-account-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n padding: 16px;\n gap: 12px;\n border-bottom: 1px solid var(--settings-divider);\n }\n .settings-account-table .settings-account-row > td {\n padding: 0;\n border: 0;\n }\n .settings-account-row > td:nth-child(1),\n .settings-account-row > td:nth-child(2) {\n grid-column: 1 / -1;\n }\n .settings-account-row > td:nth-child(3) {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .settings-account-mobile-label {\n display: inline;\n color: var(--settings-muted);\n font-size: 11px;\n }\n .settings-account-usage {\n max-width: none;\n }\n .settings-account-table tr:not(.settings-account-row),\n .settings-account-table tr:not(.settings-account-row) > td {\n display: block;\n }\n}\n\n@media (forced-colors: active) {\n .settings-account-usage__bar {\n border: 1px solid CanvasText;\n }\n .settings-account-usage__bar > span {\n background: Highlight;\n }\n}\n';
|
|
@@ -27014,8 +29399,10 @@ ${accounts_default}`;
|
|
|
27014
29399
|
const pages = [
|
|
27015
29400
|
createConnectionsSettingsPage(messages, options.getConnectionDiagnostics ?? (() => null)),
|
|
27016
29401
|
createAccountsSettingsPage(messages, options.getAccountClient ?? (() => null)),
|
|
29402
|
+
createMcpSettingsPage(messages, options.getIntegrationsClient ?? (() => null)),
|
|
29403
|
+
createSkillsSettingsPage(messages, options.getIntegrationsClient ?? (() => null)),
|
|
27017
29404
|
updatesPage(messages, options.getUpdateClient ?? (() => null)),
|
|
27018
|
-
aboutPage(messages)
|
|
29405
|
+
aboutPage(messages, options.getUpdateClient ?? (() => null))
|
|
27019
29406
|
];
|
|
27020
29407
|
const nextShell = installRendererSettingsShell(pages, messages, ownerWindow.document);
|
|
27021
29408
|
const nextTrigger = installRendererSettingsHeaderTrigger({
|
|
@@ -27401,13 +29788,19 @@ ${accounts_default}`;
|
|
|
27401
29788
|
const composer = editor.closest("[data-codex-composer-root]");
|
|
27402
29789
|
if (!composer) return { agents: [], sessions: [] };
|
|
27403
29790
|
const state = controller.get(composer);
|
|
27404
|
-
if (state.agent === "codex") return { agents: [], sessions: [] };
|
|
27405
29791
|
const client = modelClientForHost(modelControl?.currentHostId?.() ?? "local");
|
|
27406
29792
|
const [agentResult, sessionResult] = await Promise.allSettled([
|
|
27407
29793
|
client?.listCollaborationAgents?.() ?? Promise.resolve([]),
|
|
27408
29794
|
client?.listHarnessSessions?.({ harnessId: harnessIdSchema.parse("all-harnesses"), query, offset: 0, limit: 12 }) ?? Promise.resolve({ candidates: [], total: 0 })
|
|
27409
29795
|
]);
|
|
27410
29796
|
const agents = agentResult.status === "fulfilled" ? agentResult.value : [];
|
|
29797
|
+
window.__lastMentionDebug = {
|
|
29798
|
+
hasClient: !!client,
|
|
29799
|
+
agentStatus: agentResult.status,
|
|
29800
|
+
agentValue: agentResult.status === "fulfilled" ? agentResult.value : agentResult.reason?.message,
|
|
29801
|
+
sessionStatus: sessionResult.status,
|
|
29802
|
+
sessionValue: sessionResult.status === "fulfilled" ? sessionResult.value : sessionResult.reason?.message
|
|
29803
|
+
};
|
|
27411
29804
|
const sessions = sessionResult.status === "fulfilled" ? sessionResult.value.candidates.map((candidate) => {
|
|
27412
29805
|
const match = /^\[([^\]]+)\]\s*/.exec(candidate.title ?? "");
|
|
27413
29806
|
return { id: candidate.nativeSessionId, title: (candidate.title ?? "\u672A\u547D\u540D\u4F1A\u8BDD").replace(/^\[[^\]]+\]\s*/, ""), harnessId: match?.[1] ?? "codex", cwd: candidate.cwd, running: candidate.running };
|
|
@@ -27430,6 +29823,11 @@ ${accounts_default}`;
|
|
|
27430
29823
|
getUpdateClient: () => modelControl,
|
|
27431
29824
|
getAccountClient: () => modelControl,
|
|
27432
29825
|
getConnectionDiagnostics: () => connectionDiagnostics,
|
|
29826
|
+
getIntegrationsClient: () => {
|
|
29827
|
+
const client = modelClientForHost("local");
|
|
29828
|
+
if (!client?.integrationCatalog || !client.listIntegrations || !client.saveMcp || !client.removeMcp || !client.changeSkill) return null;
|
|
29829
|
+
return { integrationCatalog: () => client.integrationCatalog(), listIntegrations: (input) => client.listIntegrations(input), saveMcp: (input) => client.saveMcp(input), removeMcp: (input) => client.removeMcp(input), changeSkill: (input) => client.changeSkill(input) };
|
|
29830
|
+
},
|
|
27433
29831
|
getSessionImportClient: () => {
|
|
27434
29832
|
const client = modelClientForHost("local");
|
|
27435
29833
|
const sources = client?.listSessionImportSources;
|
|
@@ -27553,7 +29951,7 @@ ${accounts_default}`;
|
|
|
27553
29951
|
mounted.control,
|
|
27554
29952
|
controller.get(mounted.composer),
|
|
27555
29953
|
adapterStatus.state,
|
|
27556
|
-
accounts?.switching === true || controller.isSwitching(mounted.composer) || mounted.ownershipStatus === "loading",
|
|
29954
|
+
accounts?.switching === true || controller.isSwitching(mounted.composer) || mounted.harnessSwitching || mounted.ownershipStatus === "loading",
|
|
27557
29955
|
activeHarnessAvailabilityState().availability,
|
|
27558
29956
|
mounted.modelView,
|
|
27559
29957
|
mounted.permissionModeView,
|
|
@@ -27825,6 +30223,54 @@ ${accounts_default}`;
|
|
|
27825
30223
|
}
|
|
27826
30224
|
}
|
|
27827
30225
|
};
|
|
30226
|
+
const handoffComposerThread = async (mounted, request) => {
|
|
30227
|
+
const threadId = threadIdFromComposerModelTarget(mounted.modelTarget);
|
|
30228
|
+
const current = controller.get(mounted.composer);
|
|
30229
|
+
if (!threadId || current.phase !== "locked" || current.agent === "codex" || current.agent !== request.from || request.to === current.agent || mounted.harnessSwitching) {
|
|
30230
|
+
return;
|
|
30231
|
+
}
|
|
30232
|
+
const client = modelClientForHostFrom(modelControl, mounted.hostId);
|
|
30233
|
+
if (!client?.switchHarness) {
|
|
30234
|
+
mounted.control.harnessHandoff.showError(
|
|
30235
|
+
settingsLifecycle.locale === "zh-CN" ? "\u5F53\u524D\u4EFB\u52A1\u65E0\u6CD5\u4F7F\u7528 Harness \u63A5\u529B\u3002" : "Harness handoff is unavailable for this task."
|
|
30236
|
+
);
|
|
30237
|
+
return;
|
|
30238
|
+
}
|
|
30239
|
+
mounted.harnessSwitching = true;
|
|
30240
|
+
mounted.control.harnessHandoff.setSubmitting(true);
|
|
30241
|
+
renderMounted(mounted);
|
|
30242
|
+
try {
|
|
30243
|
+
await client.switchHarness({
|
|
30244
|
+
threadId,
|
|
30245
|
+
harnessId: externalHarnessIds[request.to],
|
|
30246
|
+
...request.note ? { note: request.note } : {},
|
|
30247
|
+
intent: request.intent,
|
|
30248
|
+
includes: request.includes
|
|
30249
|
+
});
|
|
30250
|
+
if (disposed || mountedByComposer.get(mounted.composer) !== mounted || threadIdFromComposerModelTarget(mounted.modelTarget) !== threadId) {
|
|
30251
|
+
return;
|
|
30252
|
+
}
|
|
30253
|
+
mounted.harnessSwitching = false;
|
|
30254
|
+
mounted.control.harnessHandoff.setSubmitting(false);
|
|
30255
|
+
mounted.control.harnessHandoff.close();
|
|
30256
|
+
mounted.modelView = { status: "loading" };
|
|
30257
|
+
mounted.permissionModeView = { status: "loading" };
|
|
30258
|
+
mounted.threadConfiguration = void 0;
|
|
30259
|
+
mounted.usage = null;
|
|
30260
|
+
mounted.accountCredits = null;
|
|
30261
|
+
await loadThreadOwnership(mounted);
|
|
30262
|
+
} catch (error51) {
|
|
30263
|
+
mounted.control.harnessHandoff.showError(
|
|
30264
|
+
error51 instanceof Error ? error51.message : String(error51)
|
|
30265
|
+
);
|
|
30266
|
+
} finally {
|
|
30267
|
+
if (mountedByComposer.get(mounted.composer) === mounted) {
|
|
30268
|
+
mounted.harnessSwitching = false;
|
|
30269
|
+
mounted.control.harnessHandoff.setSubmitting(false);
|
|
30270
|
+
renderMounted(mounted);
|
|
30271
|
+
}
|
|
30272
|
+
}
|
|
30273
|
+
};
|
|
27828
30274
|
const refreshMountedConversationTarget = (mounted) => {
|
|
27829
30275
|
const currentTarget = findComposerModelTarget(mounted.composer);
|
|
27830
30276
|
const resolution = lateConversationTargetResolution(
|
|
@@ -28517,9 +30963,8 @@ ${accounts_default}`;
|
|
|
28517
30963
|
await policy.clear();
|
|
28518
30964
|
if (!isCurrent() || window.__codexhostDraftPrewarmPolicyV1 !== policy) return;
|
|
28519
30965
|
if (!policy.selectAccount) throw new Error("Codex Account selection is unavailable");
|
|
28520
|
-
|
|
28521
|
-
|
|
28522
|
-
accounts.overrideAccountId = override;
|
|
30966
|
+
policy.selectAccount(codexAccountRouteOverride(accounts.accounts, accountId));
|
|
30967
|
+
accounts.overrideAccountId = accountId === accounts.selection.activeAccountId ? null : accountId;
|
|
28523
30968
|
} catch {
|
|
28524
30969
|
if (isCurrent()) void loadCodexAccounts();
|
|
28525
30970
|
} finally {
|
|
@@ -28809,6 +31254,13 @@ ${accounts_default}`;
|
|
|
28809
31254
|
(agent) => {
|
|
28810
31255
|
const mounted2 = mountedByComposer.get(composer);
|
|
28811
31256
|
if (!composer.isConnected || !mounted2) return;
|
|
31257
|
+
const current = controller.get(composer);
|
|
31258
|
+
if (current.phase === "locked") {
|
|
31259
|
+
if (current.agent !== "codex" && agent !== "codex") {
|
|
31260
|
+
mounted2.control.harnessHandoff.open(current.agent, agent, settingsLifecycle.locale);
|
|
31261
|
+
}
|
|
31262
|
+
return;
|
|
31263
|
+
}
|
|
28812
31264
|
void switchComposerAgent(mounted2, agent);
|
|
28813
31265
|
},
|
|
28814
31266
|
openInstallPage,
|
|
@@ -28838,6 +31290,11 @@ ${accounts_default}`;
|
|
|
28838
31290
|
(command) => {
|
|
28839
31291
|
const mounted2 = mountedByComposer.get(composer);
|
|
28840
31292
|
if (mounted2) selectCommand(mounted2, command);
|
|
31293
|
+
},
|
|
31294
|
+
(request) => {
|
|
31295
|
+
const mounted2 = mountedByComposer.get(composer);
|
|
31296
|
+
if (!composer.isConnected || !mounted2) return;
|
|
31297
|
+
void handoffComposerThread(mounted2, request);
|
|
28841
31298
|
}
|
|
28842
31299
|
);
|
|
28843
31300
|
const mounted = {
|
|
@@ -28853,7 +31310,8 @@ ${accounts_default}`;
|
|
|
28853
31310
|
accountCredits: inherited?.accountCredits ?? null,
|
|
28854
31311
|
hostId: inherited?.hostId ?? hostId,
|
|
28855
31312
|
usageRequestGeneration: 0,
|
|
28856
|
-
commandRequestGeneration: 0
|
|
31313
|
+
commandRequestGeneration: 0,
|
|
31314
|
+
harnessSwitching: false
|
|
28857
31315
|
};
|
|
28858
31316
|
mountedByComposer.set(composer, mounted);
|
|
28859
31317
|
if (isComposerModelWriteAllowed(modelTarget)) {
|
|
@@ -29020,7 +31478,10 @@ ${accounts_default}`;
|
|
|
29020
31478
|
if (current.agent === "codex") {
|
|
29021
31479
|
const selection = composerCodexAccounts(composer)?.selection;
|
|
29022
31480
|
const accountId = controller.isSubmissionPending(composer) && current.codexAccountId ? current.codexAccountId : selection?.selectedAccountId;
|
|
29023
|
-
const override =
|
|
31481
|
+
const override = codexAccountRouteOverride(
|
|
31482
|
+
composerCodexAccounts(composer)?.accounts ?? [],
|
|
31483
|
+
accountId
|
|
31484
|
+
);
|
|
29024
31485
|
const policy = window.__codexhostDraftPrewarmPolicyV1;
|
|
29025
31486
|
if (override !== null && (policy?.hostId !== mounted.hostId || !policy.selectAccount)) {
|
|
29026
31487
|
return false;
|
|
@@ -29065,7 +31526,7 @@ ${accounts_default}`;
|
|
|
29065
31526
|
const onKeyDown = (event) => {
|
|
29066
31527
|
const composer = isComposerInputIntent(event) ? composerForTarget(event.target) : null;
|
|
29067
31528
|
const mounted = composer ? mountedByComposer.get(composer) : void 0;
|
|
29068
|
-
if (composer && (composerCodexAccounts(composer)?.switching || controller.isSwitching(composer))) {
|
|
31529
|
+
if (composer && (composerCodexAccounts(composer)?.switching || controller.isSwitching(composer) || mounted?.harnessSwitching)) {
|
|
29069
31530
|
if (isComposerSubmissionKey(event)) blockEvent(event);
|
|
29070
31531
|
return;
|
|
29071
31532
|
}
|