@jingyi0605/codingns4dsh 0.2.1-beta.5 → 0.2.1-beta.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/README.en.md +112 -161
- package/README.md +106 -263
- package/assets/provider-icons/doubao.png +0 -0
- package/data/build/dist/client/avatar/catalog-panel.d.ts +1 -1
- package/data/build/dist/client/avatar/catalog-panel.d.ts.map +1 -1
- package/data/build/dist/client/avatar/catalog-panel.js +13 -11
- package/data/build/dist/client/avatar/catalog-panel.js.map +1 -1
- package/data/build/dist/client/avatar/engine.d.ts.map +1 -1
- package/data/build/dist/client/avatar/engine.js +26 -7
- package/data/build/dist/client/avatar/engine.js.map +1 -1
- package/data/build/dist/client/avatar/manager.d.ts.map +1 -1
- package/data/build/dist/client/avatar/manager.js +5 -1
- package/data/build/dist/client/avatar/manager.js.map +1 -1
- package/data/build/dist/client/avatar/slot.d.ts.map +1 -1
- package/data/build/dist/client/avatar/slot.js +4 -1
- package/data/build/dist/client/avatar/slot.js.map +1 -1
- package/data/build/dist/client/bundle.js +49155 -99509
- package/data/build/dist/client/bundle.js.map +1 -1
- package/data/build/dist/client/cli-catalog.d.ts +2 -0
- package/data/build/dist/client/cli-catalog.d.ts.map +1 -1
- package/data/build/dist/client/cli-catalog.js +8 -0
- package/data/build/dist/client/cli-catalog.js.map +1 -1
- package/data/build/dist/client/cli-slots.js +2 -2
- package/data/build/dist/client/cli-slots.js.map +1 -1
- package/data/build/dist/client/client.assistant-configuration-view.js +4526 -0
- package/data/build/dist/client/client.assistant-configuration-view.js.map +1 -0
- package/data/build/dist/client/client.assistant-debug-workbench.js +764 -0
- package/data/build/dist/client/client.assistant-debug-workbench.js.map +1 -0
- package/data/build/dist/client/client.assistant-prompt-editor.js +76 -0
- package/data/build/dist/client/client.assistant-prompt-editor.js.map +1 -0
- package/data/build/dist/client/client.assistant-prompts.js +44 -0
- package/data/build/dist/client/client.assistant-prompts.js.map +1 -0
- package/data/build/dist/client/client.assistant-voice-sessions.js +144 -0
- package/data/build/dist/client/client.assistant-voice-sessions.js.map +1 -0
- package/data/build/dist/client/client.assistant-workbench.js +2929 -0
- package/data/build/dist/client/client.assistant-workbench.js.map +1 -0
- package/data/build/dist/client/client.delegate-command.js +1315 -0
- package/data/build/dist/client/client.delegate-command.js.map +1 -0
- package/data/build/dist/client/client.editor-engine.js +32486 -0
- package/data/build/dist/client/client.editor-engine.js.map +1 -0
- package/data/build/dist/client/client.manager.js +3454 -0
- package/data/build/dist/client/client.manager.js.map +1 -0
- package/data/build/dist/client/client.model-catalog-cache.js +543 -0
- package/data/build/dist/client/client.model-catalog-cache.js.map +1 -0
- package/data/build/dist/client/client.native-workspace-store.js +689 -0
- package/data/build/dist/client/client.native-workspace-store.js.map +1 -0
- package/data/build/dist/client/client.primitives-adapter.js +333 -0
- package/data/build/dist/client/client.primitives-adapter.js.map +1 -0
- package/data/build/dist/client/client.react-dom.js +7286 -0
- package/data/build/dist/client/client.react-dom.js.map +1 -0
- package/data/build/dist/client/client.rolldown-runtime.js +59 -0
- package/data/build/dist/client/client.theme.js +386 -0
- package/data/build/dist/client/client.theme.js.map +1 -0
- package/data/build/dist/client/client.transport.js +3323 -0
- package/data/build/dist/client/client.transport.js.map +1 -0
- package/data/build/dist/client/client.xterm-engine.js +13064 -0
- package/data/build/dist/client/client.xterm-engine.js.map +1 -0
- package/data/build/dist/client/editor-engine.d.ts +4 -0
- package/data/build/dist/client/editor-engine.d.ts.map +1 -0
- package/data/build/dist/client/editor-engine.js +50 -0
- package/data/build/dist/client/editor-engine.js.map +1 -0
- package/data/build/dist/client/engine-loader.d.ts +5 -0
- package/data/build/dist/client/engine-loader.d.ts.map +1 -0
- package/data/build/dist/client/engine-loader.js +35 -0
- package/data/build/dist/client/engine-loader.js.map +1 -0
- package/data/build/dist/client/features/assistant-configuration-loader.d.ts +14 -0
- package/data/build/dist/client/features/assistant-configuration-loader.d.ts.map +1 -0
- package/data/build/dist/client/features/assistant-configuration-loader.js +13 -0
- package/data/build/dist/client/features/assistant-configuration-loader.js.map +1 -0
- package/data/build/dist/client/features/assistant-configuration-session.d.ts.map +1 -1
- package/data/build/dist/client/features/assistant-configuration-session.js +13 -4
- package/data/build/dist/client/features/assistant-configuration-session.js.map +1 -1
- package/data/build/dist/client/features/assistant-configuration-view.d.ts +66 -0
- package/data/build/dist/client/features/assistant-configuration-view.d.ts.map +1 -0
- package/data/build/dist/client/features/assistant-configuration-view.js +133 -0
- package/data/build/dist/client/features/assistant-configuration-view.js.map +1 -0
- package/data/build/dist/client/features/assistant-debug-workbench.d.ts +4 -11
- package/data/build/dist/client/features/assistant-debug-workbench.d.ts.map +1 -1
- package/data/build/dist/client/features/assistant-debug-workbench.js +41 -68
- package/data/build/dist/client/features/assistant-debug-workbench.js.map +1 -1
- package/data/build/dist/client/features/assistant-display-store.d.ts +57 -0
- package/data/build/dist/client/features/assistant-display-store.d.ts.map +1 -0
- package/data/build/dist/client/features/assistant-display-store.js +189 -0
- package/data/build/dist/client/features/assistant-display-store.js.map +1 -0
- package/data/build/dist/client/features/assistant-prompt-editor.d.ts +12 -0
- package/data/build/dist/client/features/assistant-prompt-editor.d.ts.map +1 -0
- package/data/build/dist/client/features/assistant-prompt-editor.js +14 -0
- package/data/build/dist/client/features/assistant-prompt-editor.js.map +1 -0
- package/data/build/dist/client/features/assistant-view-loader.d.ts +17 -0
- package/data/build/dist/client/features/assistant-view-loader.d.ts.map +1 -0
- package/data/build/dist/client/features/assistant-view-loader.js +49 -0
- package/data/build/dist/client/features/assistant-view-loader.js.map +1 -0
- package/data/build/dist/client/features/assistant-workbench.d.ts +1 -60
- package/data/build/dist/client/features/assistant-workbench.d.ts.map +1 -1
- package/data/build/dist/client/features/assistant-workbench.js +41 -169
- package/data/build/dist/client/features/assistant-workbench.js.map +1 -1
- package/data/build/dist/client/features/cli-adapters.js +3 -7
- package/data/build/dist/client/features/cli-adapters.js.map +1 -1
- package/data/build/dist/client/features/global-voice-assistant.d.ts.map +1 -1
- package/data/build/dist/client/features/global-voice-assistant.js +21 -20
- package/data/build/dist/client/features/global-voice-assistant.js.map +1 -1
- package/data/build/dist/client/features/peer-host.d.ts.map +1 -1
- package/data/build/dist/client/features/peer-host.js +14 -25
- package/data/build/dist/client/features/peer-host.js.map +1 -1
- package/data/build/dist/client/file-management-dom.d.ts +3 -1
- package/data/build/dist/client/file-management-dom.d.ts.map +1 -1
- package/data/build/dist/client/file-management-dom.js +85 -79
- package/data/build/dist/client/file-management-dom.js.map +1 -1
- package/data/build/dist/client/locales/assistantAvatar.d.ts +1 -1
- package/data/build/dist/client/locales/assistantAvatar.js +2 -2
- package/data/build/dist/client/locales/assistantAvatar.js.map +1 -1
- package/data/build/dist/client/locales/fileWorkspace.d.ts.map +1 -1
- package/data/build/dist/client/locales/fileWorkspace.js +2 -0
- package/data/build/dist/client/locales/fileWorkspace.js.map +1 -1
- package/data/build/dist/client/peer-host-management-api.d.ts +2 -2
- package/data/build/dist/client/peer-host-management-api.d.ts.map +1 -1
- package/data/build/dist/client/peer-host-management-api.js +8 -5
- package/data/build/dist/client/peer-host-management-api.js.map +1 -1
- package/data/build/dist/client/provider-icon-assets.js +5 -39
- package/data/build/dist/client/provider-icon-assets.js.map +1 -1
- package/data/build/dist/client/provider-icon-bridge.d.ts +6 -0
- package/data/build/dist/client/provider-icon-bridge.d.ts.map +1 -0
- package/data/build/dist/client/provider-icon-bridge.js +65 -0
- package/data/build/dist/client/provider-icon-bridge.js.map +1 -0
- package/data/build/dist/client/provider-icons.d.ts +1 -0
- package/data/build/dist/client/provider-icons.d.ts.map +1 -1
- package/data/build/dist/client/provider-icons.js +2 -1
- package/data/build/dist/client/provider-icons.js.map +1 -1
- package/data/build/dist/client/remote-web-context.d.ts +2 -0
- package/data/build/dist/client/remote-web-context.d.ts.map +1 -1
- package/data/build/dist/client/remote-web-context.js +50 -2
- package/data/build/dist/client/remote-web-context.js.map +1 -1
- package/data/build/dist/client/serial-polling.d.ts +3 -1
- package/data/build/dist/client/serial-polling.d.ts.map +1 -1
- package/data/build/dist/client/serial-polling.js +10 -3
- package/data/build/dist/client/serial-polling.js.map +1 -1
- package/data/build/dist/client/settings-section.js +8 -2
- package/data/build/dist/client/settings-section.js.map +1 -1
- package/data/build/dist/client/terminal/xterm-engine.d.ts +4 -0
- package/data/build/dist/client/terminal/xterm-engine.d.ts.map +1 -0
- package/data/build/dist/client/terminal/xterm-engine.js +5 -0
- package/data/build/dist/client/terminal/xterm-engine.js.map +1 -0
- package/data/build/dist/client/terminal/xterm-view.d.ts.map +1 -1
- package/data/build/dist/client/terminal/xterm-view.js +33 -25
- package/data/build/dist/client/terminal/xterm-view.js.map +1 -1
- package/data/build/dist/host/avatar/temporary-previews.d.ts +3 -1
- package/data/build/dist/host/avatar/temporary-previews.d.ts.map +1 -1
- package/data/build/dist/host/avatar/temporary-previews.js +7 -2
- package/data/build/dist/host/avatar/temporary-previews.js.map +1 -1
- package/data/build/dist/host/cli-adapters/antigravity-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/antigravity-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/antigravity-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/binary-detection.d.ts +19 -0
- package/data/build/dist/host/cli-adapters/binary-detection.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/binary-detection.js +71 -0
- package/data/build/dist/host/cli-adapters/binary-detection.js.map +1 -0
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-driver.js +4 -0
- package/data/build/dist/host/cli-adapters/claude-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-model-options.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-model-options.js +2 -1
- package/data/build/dist/host/cli-adapters/claude-model-options.js.map +1 -1
- package/data/build/dist/host/cli-adapters/claude-process.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/claude-process.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/claude-process.js +59 -0
- package/data/build/dist/host/cli-adapters/claude-process.js.map +1 -0
- package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/codebuddy-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/codebuddy-driver.js +18 -5
- package/data/build/dist/host/cli-adapters/codebuddy-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +0 -1
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/command-code-driver.js +6 -29
- package/data/build/dist/host/cli-adapters/command-code-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/doubao-app.d.ts +27 -0
- package/data/build/dist/host/cli-adapters/doubao-app.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-app.js +110 -0
- package/data/build/dist/host/cli-adapters/doubao-app.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts +16 -0
- package/data/build/dist/host/cli-adapters/doubao-artifact-store.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-artifact-store.js +142 -0
- package/data/build/dist/host/cli-adapters/doubao-artifact-store.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts +16 -0
- package/data/build/dist/host/cli-adapters/doubao-artifacts.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-artifacts.js +34 -0
- package/data/build/dist/host/cli-adapters/doubao-artifacts.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts +49 -0
- package/data/build/dist/host/cli-adapters/doubao-cdp.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-cdp.js +177 -0
- package/data/build/dist/host/cli-adapters/doubao-cdp.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-driver.d.ts +33 -0
- package/data/build/dist/host/cli-adapters/doubao-driver.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-driver.js +291 -0
- package/data/build/dist/host/cli-adapters/doubao-driver.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-events.d.ts +21 -0
- package/data/build/dist/host/cli-adapters/doubao-events.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-events.js +126 -0
- package/data/build/dist/host/cli-adapters/doubao-events.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts +6 -0
- package/data/build/dist/host/cli-adapters/doubao-runtime.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-runtime.js +176 -0
- package/data/build/dist/host/cli-adapters/doubao-runtime.js.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-tools.d.ts +8 -0
- package/data/build/dist/host/cli-adapters/doubao-tools.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/doubao-tools.js +139 -0
- package/data/build/dist/host/cli-adapters/doubao-tools.js.map +1 -0
- package/data/build/dist/host/cli-adapters/driver.d.ts +3 -2
- package/data/build/dist/host/cli-adapters/driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/feature.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/feature.js +2 -0
- package/data/build/dist/host/cli-adapters/feature.js.map +1 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +3 -1
- package/data/build/dist/host/cli-adapters/json-rpc-process.js.map +1 -1
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +1 -0
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js.map +1 -1
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/model-catalog.js +10 -0
- package/data/build/dist/host/cli-adapters/model-catalog.js.map +1 -1
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +1 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/opencode-driver.js +9 -28
- package/data/build/dist/host/cli-adapters/opencode-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/process-utils.d.ts +4 -2
- package/data/build/dist/host/cli-adapters/process-utils.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/process-utils.js +64 -11
- package/data/build/dist/host/cli-adapters/process-utils.js.map +1 -1
- package/data/build/dist/host/cli-adapters/qoder-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/qoder-driver.js +13 -33
- package/data/build/dist/host/cli-adapters/qoder-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/registry.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/registry.js +31 -4
- package/data/build/dist/host/cli-adapters/registry.js.map +1 -1
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +2 -11
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +1 -28
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js.map +1 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -3
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts.map +1 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +12 -37
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js.map +1 -1
- package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts +12 -0
- package/data/build/dist/host/cli-adapters/windows-command-environment.d.ts.map +1 -0
- package/data/build/dist/host/cli-adapters/windows-command-environment.js +53 -0
- package/data/build/dist/host/cli-adapters/windows-command-environment.js.map +1 -0
- package/data/build/dist/host/features/assistant-avatar-runtime.d.ts.map +1 -1
- package/data/build/dist/host/features/assistant-avatar-runtime.js +23 -14
- package/data/build/dist/host/features/assistant-avatar-runtime.js.map +1 -1
- package/data/build/dist/host/features/assistant-background.d.ts +44 -0
- package/data/build/dist/host/features/assistant-background.d.ts.map +1 -0
- package/data/build/dist/host/features/assistant-background.js +116 -0
- package/data/build/dist/host/features/assistant-background.js.map +1 -0
- package/data/build/dist/host/features/assistant-index-updates.d.ts +5 -0
- package/data/build/dist/host/features/assistant-index-updates.d.ts.map +1 -1
- package/data/build/dist/host/features/assistant-index-updates.js +29 -6
- package/data/build/dist/host/features/assistant-index-updates.js.map +1 -1
- package/data/build/dist/host/features/assistant-source-cache.d.ts +2 -2
- package/data/build/dist/host/features/assistant-source-cache.d.ts.map +1 -1
- package/data/build/dist/host/features/assistant-source-cache.js +4 -2
- package/data/build/dist/host/features/assistant-source-cache.js.map +1 -1
- package/data/build/dist/host/features/global-voice-rpc.d.ts.map +1 -1
- package/data/build/dist/host/features/global-voice-rpc.js +269 -110
- package/data/build/dist/host/features/global-voice-rpc.js.map +1 -1
- package/data/build/dist/host/features/peer-host.d.ts.map +1 -1
- package/data/build/dist/host/features/peer-host.js +61 -21
- package/data/build/dist/host/features/peer-host.js.map +1 -1
- package/data/build/dist/host/features/types.d.ts +6 -4
- package/data/build/dist/host/features/types.d.ts.map +1 -1
- package/data/build/dist/host/index.d.ts.map +1 -1
- package/data/build/dist/host/index.js +29 -19
- package/data/build/dist/host/index.js.map +1 -1
- package/data/build/dist/host/provider-icon-assets.d.ts +5 -0
- package/data/build/dist/host/provider-icon-assets.d.ts.map +1 -0
- package/data/build/dist/host/provider-icon-assets.js +61 -0
- package/data/build/dist/host/provider-icon-assets.js.map +1 -0
- package/data/build/dist/host/rpc.js +1 -1
- package/data/build/dist/host/rpc.js.map +1 -1
- package/data/build/dist/host/session-migration-repair.d.ts +4 -0
- package/data/build/dist/host/session-migration-repair.d.ts.map +1 -1
- package/data/build/dist/host/session-migration-repair.js +75 -2
- package/data/build/dist/host/session-migration-repair.js.map +1 -1
- package/data/build/dist/shared/assistant-avatar-engine.d.ts +2 -0
- package/data/build/dist/shared/assistant-avatar-engine.d.ts.map +1 -1
- package/data/build/dist/shared/assistant-avatar-engine.js +11 -1
- package/data/build/dist/shared/assistant-avatar-engine.js.map +1 -1
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +3 -0
- package/data/build/dist/shared/contracts/cli-adapter.d.ts.map +1 -1
- package/data/build/dist/shared/contracts/version.d.ts +1 -1
- package/data/build/dist/shared/contracts/version.js +1 -1
- package/data/build/dist/shared/provider-icon-resources.d.ts +4 -0
- package/data/build/dist/shared/provider-icon-resources.d.ts.map +1 -0
- package/data/build/dist/shared/provider-icon-resources.js +24 -0
- package/data/build/dist/shared/provider-icon-resources.js.map +1 -0
- package/package.json +2 -2
- package/profile/README.md +2 -2
- package/profile/package.json +2 -2
- package/profile/version.json +1 -1
- package/version.json +1 -1
|
@@ -0,0 +1,1315 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@jingyi0605/codingns4dsh",
|
|
3
|
+
chunk: "client.delegate-command.js",
|
|
4
|
+
factory: (require) => {
|
|
5
|
+
var module = { exports: {} };
|
|
6
|
+
var exports = module.exports;
|
|
7
|
+
const require_model_catalog_cache = require("@jingyi0605/codingns4dsh/client.model-catalog-cache.js");
|
|
8
|
+
const require_primitives_adapter = require("@jingyi0605/codingns4dsh/client.primitives-adapter.js");
|
|
9
|
+
//#region src/shared/delegation-carrier.ts
|
|
10
|
+
const CARRIER_PREFIX = "<!--codingns:delegate:v1:";
|
|
11
|
+
const CARRIER_V2_PREFIX = "<!--codingns:delegate:v2:";
|
|
12
|
+
const CARRIER_V1_PATTERN = /<!--codingns:delegate:v1:([^:>]+):([^>]+)-->/gu;
|
|
13
|
+
const CARRIER_V2_PATTERN = /<!--codingns:delegate:v2:([^:>]+):([^:>]+):([^>]+)-->/gu;
|
|
14
|
+
const MALFORMED_CARRIER_PATTERN = /<!--codingns:delegate:[^>]*-->/gu;
|
|
15
|
+
function encodeDelegationCarrier(adapterId, label, modelId) {
|
|
16
|
+
const normalizedId = adapterId.trim();
|
|
17
|
+
const normalizedLabel = label.trim() || normalizedId;
|
|
18
|
+
const normalizedModel = modelId?.trim() || "";
|
|
19
|
+
if (normalizedId === "") throw new Error("委派 carrier 的 adapterId 不能为空");
|
|
20
|
+
if (normalizedModel !== "") return `@${normalizedLabel}${CARRIER_V2_PREFIX}${encodeURIComponent(normalizedId)}:${encodeURIComponent(normalizedModel)}:${encodeURIComponent(normalizedLabel)}-->`;
|
|
21
|
+
return `@${normalizedLabel}${CARRIER_PREFIX}${encodeURIComponent(normalizedId)}:${encodeURIComponent(normalizedLabel)}-->`;
|
|
22
|
+
}
|
|
23
|
+
function insertDelegationCarrier(draft, adapterId, label, modelId) {
|
|
24
|
+
if (parseDelegationCarriers(draft).carriers.some((item) => item.adapterId === adapterId.trim())) return draft;
|
|
25
|
+
const carrier = encodeDelegationCarrier(adapterId, label, modelId);
|
|
26
|
+
const trimmed = draft.trimEnd();
|
|
27
|
+
return trimmed === "" ? carrier : `${trimmed} ${carrier}`;
|
|
28
|
+
}
|
|
29
|
+
/** 解析并移除草稿中的全部委派 carrier;不依据显示名称猜测目标。 */
|
|
30
|
+
function parseDelegationCarriers(input) {
|
|
31
|
+
const text = typeof input === "string" ? input : "";
|
|
32
|
+
const carriers = [];
|
|
33
|
+
const errors = [];
|
|
34
|
+
const ranges = [];
|
|
35
|
+
let found = false;
|
|
36
|
+
for (const match of text.matchAll(CARRIER_V2_PATTERN)) {
|
|
37
|
+
found = true;
|
|
38
|
+
const rawId = match[1] ?? "";
|
|
39
|
+
const rawModel = match[2] ?? "";
|
|
40
|
+
const rawLabel = match[3] ?? "";
|
|
41
|
+
const index = match.index ?? -1;
|
|
42
|
+
if (index < 0) continue;
|
|
43
|
+
let adapterId = "";
|
|
44
|
+
let modelId = "";
|
|
45
|
+
let label = "";
|
|
46
|
+
try {
|
|
47
|
+
adapterId = decodeURIComponent(rawId).trim();
|
|
48
|
+
modelId = decodeURIComponent(rawModel).trim();
|
|
49
|
+
label = decodeURIComponent(rawLabel).trim();
|
|
50
|
+
} catch {
|
|
51
|
+
errors.push("DELEGATE_CARRIER_INVALID: carrier 编码损坏");
|
|
52
|
+
}
|
|
53
|
+
if (adapterId === "" || modelId === "" || label === "") errors.push("DELEGATE_CARRIER_INVALID: carrier 缺少目标标识");
|
|
54
|
+
const mention = label === "" ? "" : `@${label}`;
|
|
55
|
+
const start = mention !== "" && text.slice(Math.max(0, index - mention.length), index) === mention ? index - mention.length : index;
|
|
56
|
+
ranges.push([start, index + match[0].length]);
|
|
57
|
+
if (adapterId !== "" && modelId !== "" && label !== "" && !carriers.some((item) => item.adapterId === adapterId)) carriers.push({
|
|
58
|
+
version: 2,
|
|
59
|
+
adapterId,
|
|
60
|
+
label,
|
|
61
|
+
modelId
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
for (const match of text.matchAll(CARRIER_V1_PATTERN)) {
|
|
65
|
+
found = true;
|
|
66
|
+
const rawId = match[1] ?? "";
|
|
67
|
+
const rawLabel = match[2] ?? "";
|
|
68
|
+
const index = match.index ?? -1;
|
|
69
|
+
if (index < 0) continue;
|
|
70
|
+
let adapterId = "";
|
|
71
|
+
let label = "";
|
|
72
|
+
try {
|
|
73
|
+
adapterId = decodeURIComponent(rawId).trim();
|
|
74
|
+
label = decodeURIComponent(rawLabel).trim();
|
|
75
|
+
} catch {
|
|
76
|
+
errors.push("DELEGATE_CARRIER_INVALID: carrier 编码损坏");
|
|
77
|
+
}
|
|
78
|
+
if (adapterId === "" || label === "") errors.push("DELEGATE_CARRIER_INVALID: carrier 缺少目标标识");
|
|
79
|
+
const mention = label === "" ? "" : `@${label}`;
|
|
80
|
+
const start = mention !== "" && text.slice(Math.max(0, index - mention.length), index) === mention ? index - mention.length : index;
|
|
81
|
+
ranges.push([start, index + match[0].length]);
|
|
82
|
+
if (adapterId !== "" && label !== "" && !carriers.some((item) => item.adapterId === adapterId)) carriers.push({
|
|
83
|
+
version: 1,
|
|
84
|
+
adapterId,
|
|
85
|
+
label
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
for (const match of text.matchAll(MALFORMED_CARRIER_PATTERN)) {
|
|
89
|
+
const index = match.index ?? -1;
|
|
90
|
+
if (index < 0 || /^<!--codingns:delegate:v1:[^:>]+:[^>]+-->$/u.test(match[0]) || /^<!--codingns:delegate:v2:[^:>]+:[^:>]+:[^>]+-->$/u.test(match[0])) continue;
|
|
91
|
+
found = true;
|
|
92
|
+
errors.push("DELEGATE_CARRIER_INVALID: carrier 格式不受支持");
|
|
93
|
+
ranges.push([index, index + match[0].length]);
|
|
94
|
+
}
|
|
95
|
+
if (ranges.length === 0) return {
|
|
96
|
+
carriers: [],
|
|
97
|
+
text,
|
|
98
|
+
errors,
|
|
99
|
+
found
|
|
100
|
+
};
|
|
101
|
+
ranges.sort((a, b) => a[0] - b[0]);
|
|
102
|
+
let clean = "";
|
|
103
|
+
let cursor = 0;
|
|
104
|
+
for (const [start, end] of ranges) {
|
|
105
|
+
if (start < cursor) continue;
|
|
106
|
+
clean += text.slice(cursor, start);
|
|
107
|
+
cursor = end;
|
|
108
|
+
}
|
|
109
|
+
clean += text.slice(cursor);
|
|
110
|
+
return {
|
|
111
|
+
carriers,
|
|
112
|
+
text: clean.replace(/[ \t]{2,}/gu, " ").trim(),
|
|
113
|
+
errors,
|
|
114
|
+
found
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/client/delegate-plan.ts
|
|
119
|
+
/**
|
|
120
|
+
* `/委派` 命令的纯逻辑层。
|
|
121
|
+
*
|
|
122
|
+
* 这里不导入任何 DSH Client 包,因此可以被 Node 单测直接加载;命令注册、
|
|
123
|
+
* DSH 服务探测和界面文案留在 `delegate-command.ts`。
|
|
124
|
+
*/
|
|
125
|
+
/** 命令名(不带前导斜杠);`/` 菜单里的稳定标识。 */
|
|
126
|
+
const DELEGATE_COMMAND_NAME = "delegate";
|
|
127
|
+
/**
|
|
128
|
+
* 把适配器目录映射为委派选项。
|
|
129
|
+
*
|
|
130
|
+
* 只列出已安装且已启用的 Agent。`dsh` 由 Host 作为内置适配器提供,和外部 CLI
|
|
131
|
+
* 一样允许用户明确选择;未安装或已停用的适配器即使出现在目录里也会在 Host 侧拒绝。
|
|
132
|
+
*/
|
|
133
|
+
function delegateAdapterOptions(catalog) {
|
|
134
|
+
const options = [];
|
|
135
|
+
for (const adapter of catalog) {
|
|
136
|
+
if (!adapter.installed || !adapter.enabled) continue;
|
|
137
|
+
const detail = adapter.version?.trim();
|
|
138
|
+
options.push({
|
|
139
|
+
id: adapter.id,
|
|
140
|
+
label: adapter.name.trim() === "" ? adapter.id : adapter.name,
|
|
141
|
+
...detail === void 0 || detail === "" ? {} : { detail }
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return options;
|
|
145
|
+
}
|
|
146
|
+
/** 把选择结果追加到草稿;重复目标按 adapterId 去重。 */
|
|
147
|
+
function appendDelegateCarrier(draft, adapterId, adapterName, modelId) {
|
|
148
|
+
return insertDelegationCarrier(draft, adapterId, adapterName, modelId);
|
|
149
|
+
}
|
|
150
|
+
//#endregion
|
|
151
|
+
//#region src/client/delegate-ui-dom.ts
|
|
152
|
+
/**
|
|
153
|
+
* `/委派` 的界面增强(DOM 注入)。
|
|
154
|
+
*
|
|
155
|
+
* DSH 的 `/` 菜单把「添加」分类写死在 `sectionRows` 的名单里(file/goal/plan/feedback),
|
|
156
|
+
* 客户端贡献项只能落到「指令」分类;popupSelect 的 `SelectOption` 契约也没有图标字段。
|
|
157
|
+
* 这两点都无法通过公开 API 表达,因此这里按仓库既有的 DOM 注入惯例做两件事:
|
|
158
|
+
*
|
|
159
|
+
* 1. 把插件自己的委派行放进「添加」分类。真实行由 React 拥有,插件**只加属性隐藏它**,
|
|
160
|
+
* 绝不搬动它;「添加」分类末尾放一个克隆行,交互事件转发回真实行。
|
|
161
|
+
* - 不用 CSS `order` 让真实行「看起来」在添加分类里:`order` 只改视觉位置,
|
|
162
|
+
* 而菜单高亮由 store 的候选顺序(等于 DOM 顺序)驱动,两者会分叉——方向键
|
|
163
|
+
* 走到委派时高亮会突然跳到上面去。克隆行则让视觉与键盘顺序保持一致。
|
|
164
|
+
* - 绝不移动 React 拥有的节点,否则下一次协调时 React 的 insertBefore 会抛
|
|
165
|
+
* NotFoundError 打断整个菜单。
|
|
166
|
+
* - 克隆行**不能继承**隐藏标记,否则真实行和克隆行都被隐藏,委派入口整个消失;
|
|
167
|
+
* 隐藏规则本身也用 `:not()` 排除克隆行作为第二道保险。
|
|
168
|
+
* 2. 给委派弹层里的适配器行补上 Provider 图标。
|
|
169
|
+
*
|
|
170
|
+
* 依赖的 DSH 0.2.0-rc.2 契约(任何一条不成立都只是不生效,不会误改宿主界面):
|
|
171
|
+
* - `/` 菜单行是 `button[role="option"]`,id 前缀 `dsh-slash-option-`,标题在
|
|
172
|
+
* `span[class*="itemName"]`;分类标题在 `div[class*="sectionTitle"]`,「添加」恒为首个;
|
|
173
|
+
* - 委派弹层的搜索框 placeholder 由插件自己提供,行标题在 `span[class*="labelText"]`。
|
|
174
|
+
*/
|
|
175
|
+
/** 被打在真实委派菜单行上的隐藏标记。 */
|
|
176
|
+
const DELEGATE_MENU_ROW_ATTRIBUTE = "data-codingns-delegate-row";
|
|
177
|
+
/** 插件在「添加」分类里创建的克隆行标记。 */
|
|
178
|
+
const DELEGATE_MENU_PROXY_ATTRIBUTE = "data-codingns-delegate-proxy";
|
|
179
|
+
/** 插件在委派弹层适配器行前插入的图标标记。 */
|
|
180
|
+
const DELEGATE_POPUP_LOGO_ATTRIBUTE = "data-codingns-delegate-logo";
|
|
181
|
+
/** 委派输入 chip 前插入的 Provider Logo 标记。 */
|
|
182
|
+
const DELEGATE_CHIP_LOGO_ATTRIBUTE = "data-codingns-delegate-chip-logo";
|
|
183
|
+
/** DSH 消息时间线中由插件投影的委派 Agent chip 标记。 */
|
|
184
|
+
const DELEGATE_TIMELINE_CHIP_ATTRIBUTE = "data-codingns-delegate-timeline-chip";
|
|
185
|
+
/** 时间线 chip 保存的原始 carrier,停用插件时用于恢复 React 之外的 DOM。 */
|
|
186
|
+
const DELEGATE_TIMELINE_RAW_ATTRIBUTE = "data-codingns-delegate-timeline-raw";
|
|
187
|
+
/** DSH 时间线中由 Host 改写消息投影的委派任务卡片标记。 */
|
|
188
|
+
const DELEGATE_TIMELINE_CARD_ATTRIBUTE = "data-codingns-delegation-card";
|
|
189
|
+
/** 委派卡片保存原始指令,停用插件时用于恢复 React 之外的 DOM。 */
|
|
190
|
+
const DELEGATE_TIMELINE_CARD_RAW_ATTRIBUTE = "data-codingns-delegation-card-raw";
|
|
191
|
+
/** DSH 空状态输入提示保存原始文案的标记。 */
|
|
192
|
+
const DELEGATE_PLACEHOLDER_BASE_ATTRIBUTE = "data-codingns-delegate-placeholder-base";
|
|
193
|
+
const DELEGATE_PLACEHOLDER_MARK_ATTRIBUTE = "data-codingns-delegate-placeholder";
|
|
194
|
+
/** 插件样式标签的幂等键。 */
|
|
195
|
+
const DELEGATE_UI_STYLE_ID = "codingns4dsh-delegate-ui-style";
|
|
196
|
+
const MENU_OPTION_SELECTOR = "[role=\"option\"]";
|
|
197
|
+
const MENU_LISTBOX_SELECTOR = "[role=\"listbox\"]";
|
|
198
|
+
const MENU_OPTION_ID_PREFIX = "dsh-slash-option-";
|
|
199
|
+
const ITEM_NAME_SELECTOR = "[class*=\"itemName\"]";
|
|
200
|
+
const SECTION_TITLE_SELECTOR = "[class*=\"sectionTitle\"]";
|
|
201
|
+
const LABEL_TEXT_SELECTOR = "[class*=\"labelText\"]";
|
|
202
|
+
const DELEGATION_INSTRUCTION_MARKER = "[CodingNS 委派指令]";
|
|
203
|
+
const DELEGATE_UI_STYLE_TEXT = `[${DELEGATE_MENU_ROW_ATTRIBUTE}]:not([${DELEGATE_MENU_PROXY_ATTRIBUTE}]){display:none!important}[${DELEGATE_MENU_PROXY_ATTRIBUTE}][data-active="true"]{background:var(--dsw-alias-interactive-bg-hover)}[${DELEGATE_POPUP_LOGO_ATTRIBUTE}]{width:16px;height:16px;flex:none;object-fit:contain;border-radius:3px;margin-right:2px}[${DELEGATE_CHIP_LOGO_ATTRIBUTE}]{width:14px;height:14px;flex:none;align-self:center;object-fit:contain;border-radius:3px;margin-right:2px}[data-composer-chip="codingns-delegate"]:has([${DELEGATE_CHIP_LOGO_ATTRIBUTE}]) svg{display:none!important}[${DELEGATE_TIMELINE_CHIP_ATTRIBUTE}]{display:inline-flex;align-items:center;vertical-align:baseline;gap:3px;padding:0 5px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary);white-space:nowrap;font-size:.95em;line-height:1.45}[${DELEGATE_TIMELINE_CHIP_ATTRIBUTE}] img{width:14px;height:14px;flex:none;object-fit:contain;border-radius:3px}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}]{box-sizing:border-box;display:flex;flex-direction:column;align-self:stretch;width:100%;max-width:100%;min-width:0;gap:8px;margin:2px 0;padding:10px 12px;border:1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.2));border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.06));line-height:1.5;overflow-wrap:anywhere}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-title]{display:flex;align-items:center;min-width:0;gap:6px;font-weight:600;overflow-wrap:anywhere}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-targets]{display:flex;flex-wrap:wrap;min-width:0;width:100%;max-width:100%;gap:5px}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-target]{box-sizing:border-box;display:inline-flex;align-items:center;flex:0 1 auto;min-width:0;max-width:100%;gap:3px;padding:1px 7px;border-radius:999px;background:var(--dsw-alias-interactive-bg-hover);font-size:.9em;white-space:normal;overflow-wrap:anywhere;word-break:break-word}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-target] img{flex:0 0 auto;width:14px;height:14px;object-fit:contain;border-radius:3px}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-target] span{min-width:0;overflow-wrap:anywhere;word-break:break-word}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-task]{box-sizing:border-box;width:100%;max-width:100%;min-width:0;white-space:pre-wrap;overflow-wrap:anywhere;word-break:break-word}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-status]{min-width:0;color:var(--dsw-alias-label-secondary,GrayText);font-size:.88em;overflow-wrap:anywhere;word-break:break-word}@media (max-width:768px){[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}]{gap:6px;margin:2px 0;padding:8px 10px;border-radius:8px;font-size:.95em}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-targets]{gap:4px}[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}] [data-codingns-delegation-card-target]{padding:2px 6px;font-size:.88em}}`;
|
|
204
|
+
/** 克隆行 → 真实行;真实行被 React 重建时据此判定需要重建克隆。 */
|
|
205
|
+
const proxySources = /* @__PURE__ */ new WeakMap();
|
|
206
|
+
let popupOptions = [];
|
|
207
|
+
let requestRefresh;
|
|
208
|
+
/**
|
|
209
|
+
* 登记当前委派弹层的适配器选项。
|
|
210
|
+
*
|
|
211
|
+
* `/委派` 的 `options()` 每次加载后调用;图标注入据此把行标题反查回适配器 id。
|
|
212
|
+
* 行标题就是适配器显示名,因此这张表同时充当标题到 id 的映射。
|
|
213
|
+
*/
|
|
214
|
+
function setDelegatePopupOptions(options) {
|
|
215
|
+
popupOptions = options;
|
|
216
|
+
requestRefresh?.();
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 安装 `/委派` 的界面增强。
|
|
220
|
+
*
|
|
221
|
+
* @param options - 注入点(document/观察器/本地化文案/图标表),便于单测替换。
|
|
222
|
+
* @returns 控制器;`document` 缺失时退化为空操作。
|
|
223
|
+
*/
|
|
224
|
+
function startDelegateUiDom(options) {
|
|
225
|
+
const dom = options.document ?? (typeof document === "undefined" ? void 0 : document);
|
|
226
|
+
const Observer = options.MutationObserver ?? (typeof MutationObserver === "undefined" ? void 0 : MutationObserver);
|
|
227
|
+
const iconUrl = options.iconUrlForAdapter ?? require_model_catalog_cache.providerIconUrl;
|
|
228
|
+
let disposed = false;
|
|
229
|
+
let scanQueued = false;
|
|
230
|
+
let observer;
|
|
231
|
+
const observe = () => {
|
|
232
|
+
if (disposed || dom === void 0 || observer === void 0) return;
|
|
233
|
+
observer.observe(dom.documentElement, {
|
|
234
|
+
childList: true,
|
|
235
|
+
subtree: true,
|
|
236
|
+
attributes: true,
|
|
237
|
+
attributeFilter: ["aria-selected"]
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
const runScan = () => {
|
|
241
|
+
if (disposed || dom === void 0) return;
|
|
242
|
+
const listboxes = [...dom.querySelectorAll(MENU_LISTBOX_SELECTOR)];
|
|
243
|
+
if (listboxes.length > 0) {
|
|
244
|
+
relocateMenuRow(listboxes, options.menuLabel());
|
|
245
|
+
decoratePopupRows(listboxes, options.popupPlaceholder(), iconUrl);
|
|
246
|
+
}
|
|
247
|
+
decorateReferenceChips(dom, iconUrl);
|
|
248
|
+
decorateTimelineMessages(dom, iconUrl, options);
|
|
249
|
+
decorateComposerPlaceholders(dom, options.composerPlaceholder);
|
|
250
|
+
};
|
|
251
|
+
const scan = () => {
|
|
252
|
+
if (disposed || dom === void 0) return;
|
|
253
|
+
observer?.disconnect();
|
|
254
|
+
try {
|
|
255
|
+
runScan();
|
|
256
|
+
} finally {
|
|
257
|
+
observe();
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
const scheduleScan = () => {
|
|
261
|
+
if (disposed || scanQueued) return;
|
|
262
|
+
scanQueued = true;
|
|
263
|
+
queueMicrotask(() => {
|
|
264
|
+
scanQueued = false;
|
|
265
|
+
scan();
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
requestRefresh = scheduleScan;
|
|
269
|
+
if (dom !== void 0) installDelegateUiStyles(dom);
|
|
270
|
+
observer = dom === void 0 || Observer === void 0 ? void 0 : new Observer(scheduleScan);
|
|
271
|
+
observe();
|
|
272
|
+
scan();
|
|
273
|
+
return {
|
|
274
|
+
refresh: scheduleScan,
|
|
275
|
+
dispose() {
|
|
276
|
+
if (disposed) return;
|
|
277
|
+
disposed = true;
|
|
278
|
+
observer?.disconnect();
|
|
279
|
+
if (requestRefresh === scheduleScan) requestRefresh = void 0;
|
|
280
|
+
if (dom !== void 0) removeDelegateNodes(dom);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
/** 把插件自己的 `/` 菜单行搬进「添加」分类。 */
|
|
285
|
+
function relocateMenuRow(listboxes, label) {
|
|
286
|
+
const listbox = findSlashMenuListbox(listboxes);
|
|
287
|
+
if (listbox === void 0) return;
|
|
288
|
+
const row = findMenuRow(listbox, label);
|
|
289
|
+
const section = findAddSectionTitle(listbox);
|
|
290
|
+
if (row === void 0 || section === void 0) {
|
|
291
|
+
removeProxies(listbox);
|
|
292
|
+
if (row !== void 0) row.removeAttribute(DELEGATE_MENU_ROW_ATTRIBUTE);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const existing = listbox.querySelector(`[${DELEGATE_MENU_PROXY_ATTRIBUTE}]`);
|
|
296
|
+
let proxy = existing !== null && proxySources.get(existing) === row ? existing : void 0;
|
|
297
|
+
if (proxy === void 0) {
|
|
298
|
+
existing?.remove();
|
|
299
|
+
proxy = buildProxyRow(row);
|
|
300
|
+
insertAfterSectionOptions(section, proxy);
|
|
301
|
+
proxySources.set(proxy, row);
|
|
302
|
+
}
|
|
303
|
+
if (proxy.parentNode === null) row.removeAttribute(DELEGATE_MENU_ROW_ATTRIBUTE);
|
|
304
|
+
else row.setAttribute(DELEGATE_MENU_ROW_ATTRIBUTE, "");
|
|
305
|
+
syncProxyActive(row, proxy);
|
|
306
|
+
}
|
|
307
|
+
/** 克隆真实行:视觉、图标与文案与原生行完全一致,交互转发回真实行。 */
|
|
308
|
+
function buildProxyRow(row) {
|
|
309
|
+
const proxy = row.cloneNode(true);
|
|
310
|
+
proxy.removeAttribute(DELEGATE_MENU_ROW_ATTRIBUTE);
|
|
311
|
+
proxy.removeAttribute("id");
|
|
312
|
+
proxy.removeAttribute("aria-selected");
|
|
313
|
+
stripActiveClass(proxy);
|
|
314
|
+
proxy.setAttribute(DELEGATE_MENU_PROXY_ATTRIBUTE, "");
|
|
315
|
+
proxy.setAttribute("data-active", "false");
|
|
316
|
+
forwardEvent(proxy, row, "mousemove");
|
|
317
|
+
forwardEvent(proxy, row, "mousedown");
|
|
318
|
+
proxy.addEventListener("click", (event) => {
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
event.stopPropagation();
|
|
321
|
+
});
|
|
322
|
+
return proxy;
|
|
323
|
+
}
|
|
324
|
+
function forwardEvent(proxy, target, type) {
|
|
325
|
+
proxy.addEventListener(type, (event) => {
|
|
326
|
+
event.preventDefault();
|
|
327
|
+
dispatchLike(target, type);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
/** 在真实行上派发一次可冒泡事件,让 React 的合成事件把它当成真实交互。 */
|
|
331
|
+
function dispatchLike(target, type) {
|
|
332
|
+
const MouseEventCtor = (target.ownerDocument?.defaultView)?.MouseEvent ?? (typeof MouseEvent === "undefined" ? void 0 : MouseEvent);
|
|
333
|
+
const event = MouseEventCtor === void 0 ? new Event(type, {
|
|
334
|
+
bubbles: true,
|
|
335
|
+
cancelable: true
|
|
336
|
+
}) : new MouseEventCtor(type, {
|
|
337
|
+
bubbles: true,
|
|
338
|
+
cancelable: true
|
|
339
|
+
});
|
|
340
|
+
target.dispatchEvent(event);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* 去掉克隆行上的原生高亮类。
|
|
344
|
+
*
|
|
345
|
+
* 类名带 CSS Modules 哈希(如 `_3e4SsG_active`),不能硬编码;这里按「最后一个下划线
|
|
346
|
+
* 片段等于 active」识别,同时兼容没有哈希的 `active`。
|
|
347
|
+
*/
|
|
348
|
+
function stripActiveClass(proxy) {
|
|
349
|
+
const classes = proxy.getAttribute("class");
|
|
350
|
+
if (classes === null || classes === "") return;
|
|
351
|
+
const kept = classes.split(/\s+/u).filter((name) => name !== "active" && !name.endsWith("_active"));
|
|
352
|
+
proxy.setAttribute("class", kept.join(" "));
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* 真实行被方向键/悬停高亮时,克隆行同步高亮。
|
|
356
|
+
*
|
|
357
|
+
* 每次写入前都必须先比较现值:这里的属性正是观察器盯着的那个,多余的写会白白
|
|
358
|
+
* 触发一轮重扫(真正的死循环由 scan() 的断开-重连兜住,这里只减少无谓触发)。
|
|
359
|
+
*/
|
|
360
|
+
function syncProxyActive(row, proxy) {
|
|
361
|
+
const active = row.getAttribute("aria-selected") === "true";
|
|
362
|
+
const next = String(active);
|
|
363
|
+
if (proxy.getAttribute("data-active") !== next) proxy.setAttribute("data-active", next);
|
|
364
|
+
const current = proxy.getAttribute("aria-selected");
|
|
365
|
+
if (active) {
|
|
366
|
+
if (current !== "true") proxy.setAttribute("aria-selected", "true");
|
|
367
|
+
proxy.scrollIntoView?.({ block: "nearest" });
|
|
368
|
+
} else if (current !== null) proxy.removeAttribute("aria-selected");
|
|
369
|
+
}
|
|
370
|
+
function findSlashMenuListbox(listboxes) {
|
|
371
|
+
for (const listbox of listboxes) if (listbox.querySelector(`[id^="${MENU_OPTION_ID_PREFIX}"]`) !== null) return listbox;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* 定位插件自己的菜单行。
|
|
375
|
+
*
|
|
376
|
+
* 必须跳过克隆行:它与真实行同名,先命中克隆行会让后续判定全部错位。
|
|
377
|
+
*/
|
|
378
|
+
function findMenuRow(listbox, label) {
|
|
379
|
+
for (const row of listbox.querySelectorAll(MENU_OPTION_SELECTOR)) {
|
|
380
|
+
if (row.hasAttribute("data-codingns-delegate-proxy")) continue;
|
|
381
|
+
if (row.getAttribute("id")?.startsWith(MENU_OPTION_ID_PREFIX) !== true) continue;
|
|
382
|
+
const name = row.querySelector(ITEM_NAME_SELECTOR);
|
|
383
|
+
if (name !== null && (name.textContent ?? "").trim() === label) return row;
|
|
384
|
+
}
|
|
385
|
+
for (const row of listbox.querySelectorAll(MENU_OPTION_SELECTOR)) {
|
|
386
|
+
if (row.hasAttribute("data-codingns-delegate-proxy")) continue;
|
|
387
|
+
if (row.getAttribute("id")?.startsWith(MENU_OPTION_ID_PREFIX) !== true) continue;
|
|
388
|
+
if ((row.textContent ?? "").includes(label)) return row;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* 「添加」分类标题。
|
|
393
|
+
*
|
|
394
|
+
* DSH 的 `sectionRows` 恒把添加分类排在指令分类之前,因此首个分类标题就是它;
|
|
395
|
+
* 这里不硬编码「添加」字样,避免绑定语言。
|
|
396
|
+
*/
|
|
397
|
+
function findAddSectionTitle(listbox) {
|
|
398
|
+
return listbox.querySelectorAll(SECTION_TITLE_SELECTOR)[0];
|
|
399
|
+
}
|
|
400
|
+
/** 插入到该分类最后一行之后;分类为空时紧贴标题。 */
|
|
401
|
+
function insertAfterSectionOptions(section, proxy) {
|
|
402
|
+
let anchor = section;
|
|
403
|
+
let sibling = section.nextElementSibling;
|
|
404
|
+
while (sibling !== null && sibling.matches(MENU_OPTION_SELECTOR)) {
|
|
405
|
+
anchor = sibling;
|
|
406
|
+
sibling = sibling.nextElementSibling;
|
|
407
|
+
}
|
|
408
|
+
const parent = anchor.parentNode;
|
|
409
|
+
if (parent === null) return;
|
|
410
|
+
parent.insertBefore(proxy, anchor.nextSibling);
|
|
411
|
+
}
|
|
412
|
+
/** 给委派弹层里的适配器行补图标。 */
|
|
413
|
+
function decoratePopupRows(listboxes, placeholder, iconUrl) {
|
|
414
|
+
const listbox = findDelegatePopupListbox(listboxes, placeholder);
|
|
415
|
+
if (listbox === void 0) return;
|
|
416
|
+
for (const row of listbox.querySelectorAll(MENU_OPTION_SELECTOR)) {
|
|
417
|
+
if (row.querySelector(`[data-codingns-delegate-logo]`) !== null) continue;
|
|
418
|
+
const adapterId = adapterIdForLabel(readPopupOptionLabel(row));
|
|
419
|
+
if (adapterId === void 0) continue;
|
|
420
|
+
const url = iconUrl(adapterId);
|
|
421
|
+
if (url === void 0) continue;
|
|
422
|
+
const image = listbox.ownerDocument.createElement("img");
|
|
423
|
+
image.setAttribute("src", url);
|
|
424
|
+
image.setAttribute("alt", "");
|
|
425
|
+
image.setAttribute("aria-hidden", "true");
|
|
426
|
+
image.setAttribute(DELEGATE_POPUP_LOGO_ATTRIBUTE, adapterId);
|
|
427
|
+
row.insertBefore(image, row.firstChild);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* 给 DSH 原生 ReferenceChip 补上外部 Agent 的品牌图标。
|
|
432
|
+
*
|
|
433
|
+
* ReferenceInsert 的 appearance 只支持 session/file/folder,不能传入
|
|
434
|
+
* Provider 资产。只扫描本插件自己的 chip,并按 chip 标题反查委派目录;
|
|
435
|
+
* 目录尚未就绪时保留 DSH 的通用 session 图标,避免误标其它引用。
|
|
436
|
+
*/
|
|
437
|
+
function decorateReferenceChips(dom, iconUrl) {
|
|
438
|
+
for (const chip of dom.querySelectorAll("[data-composer-chip=\"codingns-delegate\"]")) {
|
|
439
|
+
const labelNode = chip.querySelector("[title]");
|
|
440
|
+
const adapterId = adapterIdForLabel(labelNode?.getAttribute("title")?.trim() || labelNode?.textContent?.trim() || chip.textContent?.trim() || "");
|
|
441
|
+
if (adapterId === void 0) continue;
|
|
442
|
+
const src = iconUrl(adapterId);
|
|
443
|
+
if (src === void 0 || src === "") continue;
|
|
444
|
+
const existing = chip.querySelector(`[${DELEGATE_CHIP_LOGO_ATTRIBUTE}]`);
|
|
445
|
+
if (existing !== null) {
|
|
446
|
+
if (existing.getAttribute("src") !== src) existing.setAttribute("src", src);
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const logo = dom.createElement("img");
|
|
450
|
+
logo.setAttribute(DELEGATE_CHIP_LOGO_ATTRIBUTE, adapterId);
|
|
451
|
+
logo.setAttribute("src", src);
|
|
452
|
+
logo.setAttribute("alt", "");
|
|
453
|
+
logo.setAttribute("aria-hidden", "true");
|
|
454
|
+
chip.insertBefore(logo, chip.firstChild);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* 统一 DSH 新建会话和已有会话的输入框提示。
|
|
459
|
+
*
|
|
460
|
+
* DSH 没有公开修改内置 placeholder 的接口,因此保存原文后更新显示文本。
|
|
461
|
+
* 观察器会在 React 重绘或会话切换后重新应用,确保两种会话状态始终一致。
|
|
462
|
+
*/
|
|
463
|
+
function decorateComposerPlaceholders(dom, textFactory) {
|
|
464
|
+
const text = textFactory?.().trim();
|
|
465
|
+
if (text === void 0 || text === "") return;
|
|
466
|
+
for (const placeholder of dom.querySelectorAll("[data-composer-placeholder]")) {
|
|
467
|
+
const base = placeholder.getAttribute("data-codingns-delegate-placeholder-base") ?? placeholder.textContent?.trim() ?? "";
|
|
468
|
+
if (base === "") continue;
|
|
469
|
+
if (!placeholder.hasAttribute("data-codingns-delegate-placeholder-base")) placeholder.setAttribute(DELEGATE_PLACEHOLDER_BASE_ATTRIBUTE, base);
|
|
470
|
+
placeholder.setAttribute(DELEGATE_PLACEHOLDER_MARK_ATTRIBUTE, "");
|
|
471
|
+
if (placeholder.textContent !== text) placeholder.textContent = text;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* 将已经提交到 DSH 时间线的 carrier 投影为可读的 Agent chip。
|
|
476
|
+
*
|
|
477
|
+
* DSH 的输入 codec 只有一个序列化结果:Host 需要的 carrier;因此 durable
|
|
478
|
+
* `user/message` 也会携带这段传输文本。这里仅处理 DSH 的 userRow/bubble,
|
|
479
|
+
* 不触碰 assistant、代码块和普通页面文本,避免把模型内容中的 carrier 改写。
|
|
480
|
+
*/
|
|
481
|
+
function decorateTimelineMessages(dom, iconUrl, options) {
|
|
482
|
+
const rows = [...dom.querySelectorAll("[class*=\"userRow\"]")];
|
|
483
|
+
for (const row of dom.querySelectorAll("[data-submission-echo]")) if (!rows.includes(row)) rows.push(row);
|
|
484
|
+
for (const row of rows) {
|
|
485
|
+
const bubble = row.querySelector("[class*=\"bubble\"]") ?? row.querySelector("[class*=\"preview\"]");
|
|
486
|
+
if (bubble === null) continue;
|
|
487
|
+
decorateTimelineNode(bubble, dom, iconUrl, options);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function decorateTimelineNode(node, dom, iconUrl, options) {
|
|
491
|
+
if (node.nodeType === 3) {
|
|
492
|
+
const value = node.nodeValue ?? "";
|
|
493
|
+
if (value.includes("<!--codingns:delegate:") || value.includes(DELEGATION_INSTRUCTION_MARKER)) replaceTimelineTextNode(node, value, dom, iconUrl, options);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (node.nodeType !== 1 || node.hasAttribute("data-codingns-delegate-timeline-chip") || node.hasAttribute("data-codingns-delegation-card")) return;
|
|
497
|
+
for (const child of [...node.childNodes]) decorateTimelineNode(child, dom, iconUrl, options);
|
|
498
|
+
}
|
|
499
|
+
const TIMELINE_CARRIER_V1_PATTERN = /<!--codingns:delegate:v1:([^:>]+):([^>]+)-->/gu;
|
|
500
|
+
const TIMELINE_CARRIER_V2_PATTERN = /<!--codingns:delegate:v2:([^:>]+):([^:>]+):([^>]+)-->/gu;
|
|
501
|
+
const DELEGATION_INSTRUCTION_PATTERN = /\[CodingNS 委派指令\]\s*\n任务:([\s\S]*?)\n允许使用的外部 Agent:\s*\n([\s\S]*?)(?=\n请先根据任务语义制定简短的角色和步骤计划:|\n然后调用已有 agent_subagent 工具执行委派。|$)/u;
|
|
502
|
+
const DEFAULT_ADAPTER_MODEL_LABEL = String.fromCodePoint(36866, 37197, 22120, 40664, 35748);
|
|
503
|
+
const DELEGATION_TARGET_PATTERN = /^-\s*([^\s((]+)\s*[((]([^,,))]+)(?:[,,]\s*模型:([^,,))]*))?(?:[,,][^))]*)?[))]\s*$/gmu;
|
|
504
|
+
/** 解析 Host 改写后的委派指令,提取用户真正关心的任务和目标 Agent。 */
|
|
505
|
+
function parseDelegationTimelineCard(value) {
|
|
506
|
+
const match = DELEGATION_INSTRUCTION_PATTERN.exec(value);
|
|
507
|
+
if (match === null) return void 0;
|
|
508
|
+
const task = (match[1] ?? "").trim().replace(/^\/\s*/u, "").trim();
|
|
509
|
+
if (task === "") return void 0;
|
|
510
|
+
const targets = [];
|
|
511
|
+
for (const target of (match[2] ?? "").matchAll(DELEGATION_TARGET_PATTERN)) {
|
|
512
|
+
const adapterId = target[1]?.trim() ?? "";
|
|
513
|
+
const label = target[2]?.trim() ?? "";
|
|
514
|
+
const modelId = target[3]?.trim() ?? "";
|
|
515
|
+
if (adapterId === "" || label === "" || targets.some((item) => item.adapterId === adapterId)) continue;
|
|
516
|
+
targets.push({
|
|
517
|
+
adapterId,
|
|
518
|
+
label,
|
|
519
|
+
...modelId !== "" && modelId !== DEFAULT_ADAPTER_MODEL_LABEL ? { modelId } : {}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
if (targets.length === 0) return void 0;
|
|
523
|
+
return {
|
|
524
|
+
raw: value,
|
|
525
|
+
task,
|
|
526
|
+
targets
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
/** 解析时间线文本中的有效 carrier;无效编码原样保留,避免误删用户内容。 */
|
|
530
|
+
function parseDelegationTimelineCarriers(value) {
|
|
531
|
+
const mentions = [];
|
|
532
|
+
const collect = (pattern, labelGroup) => {
|
|
533
|
+
for (const match of value.matchAll(pattern)) {
|
|
534
|
+
const carrierStart = match.index ?? -1;
|
|
535
|
+
const carrier = match[0] ?? "";
|
|
536
|
+
const adapterId = decodeCarrierPart(match[1]);
|
|
537
|
+
const label = decodeCarrierPart(match[labelGroup]);
|
|
538
|
+
if (carrierStart < 0 || carrier === "" || adapterId === void 0 || label === void 0) continue;
|
|
539
|
+
const mention = `@${label}`;
|
|
540
|
+
const start = carrierStart >= mention.length && value.slice(carrierStart - mention.length, carrierStart) === mention ? carrierStart - mention.length : carrierStart;
|
|
541
|
+
const raw = value.slice(start, carrierStart + carrier.length);
|
|
542
|
+
mentions.push({
|
|
543
|
+
raw,
|
|
544
|
+
adapterId,
|
|
545
|
+
label,
|
|
546
|
+
start,
|
|
547
|
+
end: start + raw.length
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
collect(TIMELINE_CARRIER_V1_PATTERN, 2);
|
|
552
|
+
collect(TIMELINE_CARRIER_V2_PATTERN, 3);
|
|
553
|
+
return mentions.sort((a, b) => a.start - b.start);
|
|
554
|
+
}
|
|
555
|
+
function replaceTimelineTextNode(node, value, dom, iconUrl, options) {
|
|
556
|
+
const card = parseDelegationTimelineCard(value);
|
|
557
|
+
if (card !== void 0 && node.parentNode !== null) {
|
|
558
|
+
node.parentNode.replaceChild(buildTimelineCard(dom, card, iconUrl, options.cardTitle, options.cardStatus, options.cardTarget), node);
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
const matches = parseDelegationTimelineCarriers(value);
|
|
562
|
+
if (matches.length === 0 || node.parentNode === null) return;
|
|
563
|
+
const fragment = dom.createDocumentFragment();
|
|
564
|
+
let cursor = 0;
|
|
565
|
+
for (const match of matches) {
|
|
566
|
+
if (match.start < cursor) continue;
|
|
567
|
+
if (match.start > cursor) fragment.append(dom.createTextNode(value.slice(cursor, match.start)));
|
|
568
|
+
fragment.append(buildTimelineChip(dom, match.raw, match.adapterId, match.label, iconUrl(match.adapterId)));
|
|
569
|
+
cursor = match.end;
|
|
570
|
+
}
|
|
571
|
+
if (cursor === 0) return;
|
|
572
|
+
if (cursor < value.length) fragment.append(dom.createTextNode(value.slice(cursor)));
|
|
573
|
+
node.parentNode.replaceChild(fragment, node);
|
|
574
|
+
}
|
|
575
|
+
function buildTimelineCard(dom, card, iconUrl, cardTitle = () => "Delegation task", cardStatus = () => "Delegating", cardTarget = ({ label, adapterId, modelId }) => `${label} (${adapterId}${modelId === void 0 ? "" : ` · ${modelId}`})`) {
|
|
576
|
+
const root = dom.createElement("div");
|
|
577
|
+
root.setAttribute(DELEGATE_TIMELINE_CARD_ATTRIBUTE, "");
|
|
578
|
+
root.setAttribute(DELEGATE_TIMELINE_CARD_RAW_ATTRIBUTE, card.raw);
|
|
579
|
+
const title = dom.createElement("div");
|
|
580
|
+
title.setAttribute("data-codingns-delegation-card-title", "");
|
|
581
|
+
title.textContent = cardTitle();
|
|
582
|
+
const targets = dom.createElement("div");
|
|
583
|
+
targets.setAttribute("data-codingns-delegation-card-targets", "");
|
|
584
|
+
for (const target of card.targets) {
|
|
585
|
+
const badge = dom.createElement("span");
|
|
586
|
+
badge.setAttribute("data-codingns-delegation-card-target", target.adapterId);
|
|
587
|
+
const logoUrl = iconUrl(target.adapterId);
|
|
588
|
+
if (logoUrl !== void 0 && logoUrl !== "") {
|
|
589
|
+
const logo = dom.createElement("img");
|
|
590
|
+
logo.src = logoUrl;
|
|
591
|
+
logo.alt = "";
|
|
592
|
+
logo.setAttribute("aria-hidden", "true");
|
|
593
|
+
badge.append(logo);
|
|
594
|
+
}
|
|
595
|
+
const label = dom.createElement("span");
|
|
596
|
+
label.textContent = cardTarget(target);
|
|
597
|
+
badge.append(label);
|
|
598
|
+
targets.append(badge);
|
|
599
|
+
}
|
|
600
|
+
const task = dom.createElement("div");
|
|
601
|
+
task.setAttribute("data-codingns-delegation-card-task", "");
|
|
602
|
+
task.textContent = card.task;
|
|
603
|
+
const status = dom.createElement("div");
|
|
604
|
+
status.setAttribute("data-codingns-delegation-card-status", "");
|
|
605
|
+
status.textContent = cardStatus();
|
|
606
|
+
root.append(title, targets, task, status);
|
|
607
|
+
return root;
|
|
608
|
+
}
|
|
609
|
+
function decodeCarrierPart(value) {
|
|
610
|
+
if (value === void 0 || value === "") return void 0;
|
|
611
|
+
try {
|
|
612
|
+
const decoded = decodeURIComponent(value).trim();
|
|
613
|
+
return decoded === "" ? void 0 : decoded;
|
|
614
|
+
} catch {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
function buildTimelineChip(dom, raw, adapterId, label, iconUrl) {
|
|
619
|
+
const chip = dom.createElement("span");
|
|
620
|
+
chip.setAttribute(DELEGATE_TIMELINE_CHIP_ATTRIBUTE, adapterId);
|
|
621
|
+
chip.setAttribute(DELEGATE_TIMELINE_RAW_ATTRIBUTE, raw);
|
|
622
|
+
chip.setAttribute("title", `@${label}`);
|
|
623
|
+
if (iconUrl !== void 0 && iconUrl !== "") {
|
|
624
|
+
const logo = dom.createElement("img");
|
|
625
|
+
logo.setAttribute("src", iconUrl);
|
|
626
|
+
logo.setAttribute("alt", "");
|
|
627
|
+
logo.setAttribute("aria-hidden", "true");
|
|
628
|
+
chip.append(logo);
|
|
629
|
+
}
|
|
630
|
+
const text = dom.createElement("span");
|
|
631
|
+
text.textContent = `@${label}`;
|
|
632
|
+
chip.append(text);
|
|
633
|
+
return chip;
|
|
634
|
+
}
|
|
635
|
+
/** 通过插件自己的搜索占位符识别委派弹层,避免影响其它 popupSelect 命令。 */
|
|
636
|
+
function findDelegatePopupListbox(listboxes, placeholder) {
|
|
637
|
+
for (const listbox of listboxes) {
|
|
638
|
+
if (listbox.querySelector(`[id^="${MENU_OPTION_ID_PREFIX}"]`) !== null) continue;
|
|
639
|
+
if ((listbox.parentNode?.querySelector?.("input") ?? null)?.getAttribute("placeholder") === placeholder) return listbox;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
function readPopupOptionLabel(row) {
|
|
643
|
+
return (row.querySelector(LABEL_TEXT_SELECTOR)?.textContent ?? row.textContent ?? "").trim();
|
|
644
|
+
}
|
|
645
|
+
function adapterIdForLabel(label) {
|
|
646
|
+
if (label === "") return void 0;
|
|
647
|
+
return popupOptions.find((option) => option.label === label)?.id;
|
|
648
|
+
}
|
|
649
|
+
function removeProxies(scope) {
|
|
650
|
+
for (const proxy of scope.querySelectorAll(`[${DELEGATE_MENU_PROXY_ATTRIBUTE}]`)) proxy.remove();
|
|
651
|
+
}
|
|
652
|
+
function removeDelegateNodes(dom) {
|
|
653
|
+
for (const proxy of dom.querySelectorAll(`[${DELEGATE_MENU_PROXY_ATTRIBUTE}]`)) proxy.remove();
|
|
654
|
+
for (const logo of dom.querySelectorAll(`[${DELEGATE_POPUP_LOGO_ATTRIBUTE}]`)) logo.remove();
|
|
655
|
+
for (const logo of dom.querySelectorAll(`[${DELEGATE_CHIP_LOGO_ATTRIBUTE}]`)) logo.remove();
|
|
656
|
+
for (const chip of dom.querySelectorAll(`[${DELEGATE_TIMELINE_CHIP_ATTRIBUTE}]`)) {
|
|
657
|
+
const raw = chip.getAttribute(DELEGATE_TIMELINE_RAW_ATTRIBUTE);
|
|
658
|
+
const parent = chip.parentNode;
|
|
659
|
+
if (raw !== null && parent !== null) parent.replaceChild(dom.createTextNode(raw), chip);
|
|
660
|
+
}
|
|
661
|
+
for (const card of dom.querySelectorAll(`[${DELEGATE_TIMELINE_CARD_ATTRIBUTE}]`)) {
|
|
662
|
+
const raw = card.getAttribute(DELEGATE_TIMELINE_CARD_RAW_ATTRIBUTE);
|
|
663
|
+
const parent = card.parentNode;
|
|
664
|
+
if (raw !== null && parent !== null) parent.replaceChild(dom.createTextNode(raw), card);
|
|
665
|
+
}
|
|
666
|
+
for (const placeholder of dom.querySelectorAll(`[${DELEGATE_PLACEHOLDER_MARK_ATTRIBUTE}]`)) {
|
|
667
|
+
const base = placeholder.getAttribute(DELEGATE_PLACEHOLDER_BASE_ATTRIBUTE);
|
|
668
|
+
if (base !== null) placeholder.textContent = base;
|
|
669
|
+
placeholder.removeAttribute(DELEGATE_PLACEHOLDER_BASE_ATTRIBUTE);
|
|
670
|
+
placeholder.removeAttribute(DELEGATE_PLACEHOLDER_MARK_ATTRIBUTE);
|
|
671
|
+
}
|
|
672
|
+
for (const row of dom.querySelectorAll(`[${DELEGATE_MENU_ROW_ATTRIBUTE}]`)) row.removeAttribute(DELEGATE_MENU_ROW_ATTRIBUTE);
|
|
673
|
+
dom.querySelector(`style[data-plugin-css="${DELEGATE_UI_STYLE_ID}"]`)?.remove();
|
|
674
|
+
}
|
|
675
|
+
/** 样式只装一次;重复安装由 data-plugin-css 幂等键挡住。 */
|
|
676
|
+
function installDelegateUiStyles(dom) {
|
|
677
|
+
if (dom.querySelector(`style[data-plugin-css="codingns4dsh-delegate-ui-style"]`) !== null) return;
|
|
678
|
+
const style = dom.createElement("style");
|
|
679
|
+
style.setAttribute("data-plugin", "codingns4dsh");
|
|
680
|
+
style.setAttribute("data-plugin-css", DELEGATE_UI_STYLE_ID);
|
|
681
|
+
style.textContent = DELEGATE_UI_STYLE_TEXT;
|
|
682
|
+
dom.head.appendChild(style);
|
|
683
|
+
}
|
|
684
|
+
//#endregion
|
|
685
|
+
//#region src/client/delegate-command.ts
|
|
686
|
+
const DELEGATE_REFERENCE_SOURCE = "codingns-delegate";
|
|
687
|
+
const delegateReferenceLabels = /* @__PURE__ */ new Map();
|
|
688
|
+
const delegateReferenceSelections = /* @__PURE__ */ new Map();
|
|
689
|
+
function delegateReferenceKey(adapterId, modelId) {
|
|
690
|
+
return modelId?.trim() ? `${adapterId}\u0000${modelId.trim()}` : adapterId;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* 把 `/委派` 注册进 `/` 菜单。
|
|
694
|
+
*
|
|
695
|
+
* @param ctx - Client 根 Context;只用于解析 `commandUi` 等 DSH 服务。
|
|
696
|
+
* @param options - RPC 边界与语言运行时。
|
|
697
|
+
* @returns 注销函数;`commandUi` 不可用时返回空操作。
|
|
698
|
+
*/
|
|
699
|
+
function registerDelegateCommand(ctx, options) {
|
|
700
|
+
const t = options.locale.bind("codingns");
|
|
701
|
+
const ui = startDelegateUiDom({
|
|
702
|
+
menuLabel: () => t("delegate.label"),
|
|
703
|
+
popupPlaceholder: () => t("delegate.searchPlaceholder"),
|
|
704
|
+
composerPlaceholder: () => t("delegate.composerPlaceholder"),
|
|
705
|
+
cardTitle: () => t("delegate.cardTitle"),
|
|
706
|
+
cardStatus: () => t("delegate.cardStatus"),
|
|
707
|
+
cardTarget: (value) => t("delegate.cardTarget", {
|
|
708
|
+
label: value.label,
|
|
709
|
+
adapter: value.adapterId,
|
|
710
|
+
model: value.modelId === void 0 ? "" : ` · ${value.modelId}`
|
|
711
|
+
})
|
|
712
|
+
});
|
|
713
|
+
const fiber = ctx.inject(["commandUi"], (scope) => {
|
|
714
|
+
registerDelegateCommandIn(scope, options);
|
|
715
|
+
});
|
|
716
|
+
return () => {
|
|
717
|
+
ui.dispose();
|
|
718
|
+
fiber.dispose();
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
/** 在 commandUi 就绪的作用域内注册 `/委派`;失败只降级为不注册。 */
|
|
722
|
+
function registerDelegateCommandIn(ctx, options) {
|
|
723
|
+
const commandUi = readCommandUi(ctx);
|
|
724
|
+
if (commandUi === void 0) {
|
|
725
|
+
require_model_catalog_cache.debugWarn("codingns4dsh: /委派 未注册,当前 DSH 未提供 commandUi 服务");
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
const t = options.locale.bind("codingns");
|
|
729
|
+
try {
|
|
730
|
+
registerDelegateReferenceSource(ctx, options);
|
|
731
|
+
ctx.effect(() => commandUi.register({
|
|
732
|
+
name: DELEGATE_COMMAND_NAME,
|
|
733
|
+
label: () => t("delegate.label"),
|
|
734
|
+
description: () => t("delegate.description"),
|
|
735
|
+
icon: require_primitives_adapter.resolveDelegateIcon(),
|
|
736
|
+
available: () => true,
|
|
737
|
+
ui: {
|
|
738
|
+
kind: "popupSelect",
|
|
739
|
+
searchMode: "fuzzy-label",
|
|
740
|
+
searchLabels: () => ({
|
|
741
|
+
placeholder: t("delegate.searchPlaceholder"),
|
|
742
|
+
empty: t("delegate.searchEmpty"),
|
|
743
|
+
noResults: t("delegate.searchNoResults")
|
|
744
|
+
}),
|
|
745
|
+
async options(session) {
|
|
746
|
+
const capability = await require_model_catalog_cache.callCliRpc(options.rpc, "delegate/capability", { sessionId: session.sessionId });
|
|
747
|
+
if (!capability.supported) throw new Error(capability.message);
|
|
748
|
+
const catalog = await require_model_catalog_cache.callCliRpc(options.rpc, "catalog", { sessionId: session.sessionId });
|
|
749
|
+
const list = delegateAdapterOptions(require_model_catalog_cache.adapterCatalogWithDsh(catalog, options.dshVersion));
|
|
750
|
+
setDelegatePopupOptions(list);
|
|
751
|
+
return list;
|
|
752
|
+
},
|
|
753
|
+
async onSelect(option, session) {
|
|
754
|
+
const input = readSessionInput(ctx, session.sessionId);
|
|
755
|
+
if (input === void 0) return;
|
|
756
|
+
const snapshot = input.state.getSnapshot();
|
|
757
|
+
const draft = snapshot.draft ?? "";
|
|
758
|
+
delegateReferenceLabels.set(option.id, option.label);
|
|
759
|
+
const selection = input.caretSpan?.();
|
|
760
|
+
const span = snapshot.draftRev === void 0 ? void 0 : delegateCommandSpan(draft, snapshot.draftRev, selection) ?? (selection === void 0 ? void 0 : {
|
|
761
|
+
...selection,
|
|
762
|
+
draftRev: snapshot.draftRev
|
|
763
|
+
});
|
|
764
|
+
if (span !== void 0 && input.insertReference?.({
|
|
765
|
+
source: DELEGATE_REFERENCE_SOURCE,
|
|
766
|
+
ref: option.id,
|
|
767
|
+
label: option.label,
|
|
768
|
+
appearance: "session",
|
|
769
|
+
clipboardText: `@${option.label}`
|
|
770
|
+
}, span) === true) return;
|
|
771
|
+
if (input.setDraft === void 0) {
|
|
772
|
+
input?.notify("error", t("delegate.draftUnsupported"));
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
input.setDraft(appendDelegateCarrier(draft, option.id, option.label));
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}), "codingns4dsh: delegate command");
|
|
779
|
+
require_model_catalog_cache.debugInfo("codingns4dsh: /委派 命令已注册");
|
|
780
|
+
} catch (error) {
|
|
781
|
+
require_model_catalog_cache.debugWarn("codingns4dsh: /委派 命令注册失败", { error: error instanceof Error ? error.message : String(error) });
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* 找到当前 `/委派` 命令令牌,供 popupSelect 选择结果直接替换为 chip。
|
|
786
|
+
* 命令弹层的 onSelect 在消费令牌前触发,此时公开的 caretSpan 可能只是折叠光标,
|
|
787
|
+
* 因而不能依赖它来推断命令范围。
|
|
788
|
+
*/
|
|
789
|
+
function delegateCommandSpan(draft, draftRev, selection) {
|
|
790
|
+
const caret = selection?.end ?? draft.length;
|
|
791
|
+
const token = /\/(?:delegate|委派)(?=\s|$)/gu;
|
|
792
|
+
let match;
|
|
793
|
+
while ((match = token.exec(draft)) !== null) {
|
|
794
|
+
const start = match.index;
|
|
795
|
+
const end = start + match[0].length;
|
|
796
|
+
const before = draft[start - 1];
|
|
797
|
+
if (start > 0 && before !== void 0 && !/\s/u.test(before)) continue;
|
|
798
|
+
if (caret >= start && caret <= end + 1) return {
|
|
799
|
+
start,
|
|
800
|
+
end,
|
|
801
|
+
draftRev
|
|
802
|
+
};
|
|
803
|
+
if (caret > end && draft.slice(end, caret).trim() === "") return {
|
|
804
|
+
start,
|
|
805
|
+
end,
|
|
806
|
+
draftRev
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
/** 注册结构化 Agent 引用源:显示层使用 chip,提交层再序列化为 Host carrier。 */
|
|
811
|
+
function registerDelegateReferenceSource(ctx, options) {
|
|
812
|
+
const inputTriggers = readService(ctx, "inputTriggers");
|
|
813
|
+
const source = {
|
|
814
|
+
trigger: "@",
|
|
815
|
+
name: DELEGATE_REFERENCE_SOURCE,
|
|
816
|
+
async candidates(session, request) {
|
|
817
|
+
try {
|
|
818
|
+
const catalog = await require_model_catalog_cache.callCliRpc(options.rpc, "catalog", { sessionId: session.sessionId });
|
|
819
|
+
const query = request.query.trim().toLowerCase();
|
|
820
|
+
const available = delegateAdapterOptions(require_model_catalog_cache.adapterCatalogWithDsh(catalog, options.dshVersion));
|
|
821
|
+
setDelegatePopupOptions(available);
|
|
822
|
+
return available.filter((option) => query === "" || option.id.toLowerCase().includes(query) || option.label.toLowerCase().includes(query)).map((option) => {
|
|
823
|
+
delegateReferenceLabels.set(option.id, option.label);
|
|
824
|
+
delegateReferenceSelections.set(option.id, {
|
|
825
|
+
adapterId: option.id,
|
|
826
|
+
label: option.label
|
|
827
|
+
});
|
|
828
|
+
return {
|
|
829
|
+
name: option.id,
|
|
830
|
+
label: option.label,
|
|
831
|
+
value: option.id,
|
|
832
|
+
icon: "session"
|
|
833
|
+
};
|
|
834
|
+
});
|
|
835
|
+
} catch {
|
|
836
|
+
return [];
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
onPick(pick) {
|
|
840
|
+
const ref = pick.candidate.value ?? pick.candidate.name;
|
|
841
|
+
const selection = delegateReferenceSelections.get(ref);
|
|
842
|
+
const label = pick.candidate.label?.trim() || selection?.label || delegateReferenceLabels.get(ref) || ref;
|
|
843
|
+
delegateReferenceLabels.set(ref, label);
|
|
844
|
+
return { insert: {
|
|
845
|
+
source: DELEGATE_REFERENCE_SOURCE,
|
|
846
|
+
ref,
|
|
847
|
+
label,
|
|
848
|
+
appearance: "session",
|
|
849
|
+
clipboardText: `@${label}`
|
|
850
|
+
} };
|
|
851
|
+
},
|
|
852
|
+
codec: {
|
|
853
|
+
clipboardText(ref) {
|
|
854
|
+
return `@${delegateReferenceLabels.get(ref) || ref}`;
|
|
855
|
+
},
|
|
856
|
+
async serialize(ref) {
|
|
857
|
+
const selection = delegateReferenceSelections.get(ref);
|
|
858
|
+
const adapterId = selection?.adapterId || ref.split("\0", 1)[0] || ref;
|
|
859
|
+
return encodeDelegationCarrier(adapterId, selection?.label || delegateReferenceLabels.get(ref) || adapterId, selection?.modelId);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
try {
|
|
864
|
+
ctx.effect(() => {
|
|
865
|
+
const disposeHashShortcut = registerDelegateHashShortcut(ctx, options);
|
|
866
|
+
const disposeSource = inputTriggers !== void 0 && typeof inputTriggers.registerSource === "function" ? inputTriggers.registerSource(source) : void 0;
|
|
867
|
+
return () => {
|
|
868
|
+
disposeHashShortcut();
|
|
869
|
+
disposeSource?.();
|
|
870
|
+
};
|
|
871
|
+
}, "codingns4dsh: delegate reference source");
|
|
872
|
+
} catch (error) {
|
|
873
|
+
require_model_catalog_cache.debugWarn("codingns4dsh: Agent 引用源注册失败", { error: error instanceof Error ? error.message : String(error) });
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* 为 DSH 输入框补充 `#` 委派快捷入口。
|
|
878
|
+
*
|
|
879
|
+
* 原生 inputTriggers 只有 `/` 与 `@` 两个触发字符,而且 onPick 是同步契约,无法在
|
|
880
|
+
* 选择适配器后等待 `cli/models` 再切换二级目录。因此这里使用插件自己的轻量弹层:
|
|
881
|
+
* `#` 触发适配器列表,点击适配器后立即请求它的模型目录,点击模型时才写入结构化
|
|
882
|
+
* ReferenceChip。草稿里不会留下“已选择,请继续输入”的持久提示。
|
|
883
|
+
*/
|
|
884
|
+
function registerDelegateHashShortcut(ctx, options) {
|
|
885
|
+
if (typeof document === "undefined" || typeof window === "undefined") return () => void 0;
|
|
886
|
+
const t = options.locale.bind("codingns");
|
|
887
|
+
let popup;
|
|
888
|
+
let popupSessionId = "";
|
|
889
|
+
let popupMode = "adapter";
|
|
890
|
+
let popupAdapter;
|
|
891
|
+
let popupOptions = [];
|
|
892
|
+
let popupModels = [];
|
|
893
|
+
let popupEditor;
|
|
894
|
+
let popupToken;
|
|
895
|
+
let popupRequestId = 0;
|
|
896
|
+
let refreshTimer;
|
|
897
|
+
const cancelRefresh = () => {
|
|
898
|
+
if (refreshTimer === void 0) return;
|
|
899
|
+
clearTimeout(refreshTimer);
|
|
900
|
+
refreshTimer = void 0;
|
|
901
|
+
};
|
|
902
|
+
const close = () => {
|
|
903
|
+
popupRequestId += 1;
|
|
904
|
+
popup?.remove();
|
|
905
|
+
popup = void 0;
|
|
906
|
+
popupEditor = void 0;
|
|
907
|
+
popupToken = void 0;
|
|
908
|
+
popupAdapter = void 0;
|
|
909
|
+
popupOptions = [];
|
|
910
|
+
popupModels = [];
|
|
911
|
+
popupMode = "adapter";
|
|
912
|
+
};
|
|
913
|
+
const inputFor = (sessionId) => {
|
|
914
|
+
try {
|
|
915
|
+
const conversation = readConversation(ctx);
|
|
916
|
+
const actx = readSessions(ctx)?.scope(sessionId);
|
|
917
|
+
if (actx !== void 0 && conversation !== void 0) return conversation.input.for(actx);
|
|
918
|
+
return conversation?.input.shell?.(sessionId);
|
|
919
|
+
} catch {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const position = () => {
|
|
924
|
+
if (popup === void 0 || popupEditor === void 0) return;
|
|
925
|
+
const rect = popupEditor.getBoundingClientRect();
|
|
926
|
+
const viewport = window.visualViewport;
|
|
927
|
+
const viewportLeft = viewport?.offsetLeft ?? 0;
|
|
928
|
+
const viewportTop = viewport?.offsetTop ?? 0;
|
|
929
|
+
const viewportWidth = viewport?.width ?? window.innerWidth;
|
|
930
|
+
const viewportHeight = viewport?.height ?? window.innerHeight;
|
|
931
|
+
const popupWidth = Math.min(340, Math.max(220, rect.width));
|
|
932
|
+
const gap = 6;
|
|
933
|
+
const minEdge = 8;
|
|
934
|
+
const below = rect.bottom + gap;
|
|
935
|
+
const above = rect.top - gap - popup.offsetHeight;
|
|
936
|
+
const maxTop = viewportTop + viewportHeight - minEdge - popup.offsetHeight;
|
|
937
|
+
const top = above >= viewportTop + minEdge ? above : Math.min(maxTop, Math.max(viewportTop + minEdge, below));
|
|
938
|
+
const left = Math.max(viewportLeft + minEdge, Math.min(rect.left, viewportLeft + viewportWidth - popupWidth - minEdge));
|
|
939
|
+
Object.assign(popup.style, {
|
|
940
|
+
left: `${left}px`,
|
|
941
|
+
top: `${top}px`,
|
|
942
|
+
minWidth: `${popupWidth}px`
|
|
943
|
+
});
|
|
944
|
+
};
|
|
945
|
+
const ensurePopup = (editor, sessionId) => {
|
|
946
|
+
if (popup !== void 0 && popupSessionId === sessionId) {
|
|
947
|
+
popupEditor = editor;
|
|
948
|
+
position();
|
|
949
|
+
return popup;
|
|
950
|
+
}
|
|
951
|
+
close();
|
|
952
|
+
popupSessionId = sessionId;
|
|
953
|
+
popupEditor = editor;
|
|
954
|
+
popup = document.createElement("div");
|
|
955
|
+
popup.setAttribute("data-codingns-delegate-hash-popup", "");
|
|
956
|
+
popup.setAttribute("role", "listbox");
|
|
957
|
+
Object.assign(popup.style, {
|
|
958
|
+
position: "fixed",
|
|
959
|
+
zIndex: "2147483647",
|
|
960
|
+
maxWidth: "min(360px, calc(100vw - 16px))",
|
|
961
|
+
maxHeight: "min(420px, calc(100vh - 24px))",
|
|
962
|
+
overflowY: "auto",
|
|
963
|
+
padding: "6px",
|
|
964
|
+
border: "1px solid var(--dsw-alias-border-l2,rgba(127,127,127,.35))",
|
|
965
|
+
borderRadius: "10px",
|
|
966
|
+
background: "var(--dsw-alias-bg-layer-3,#242526)",
|
|
967
|
+
color: "var(--dsw-alias-label-primary,#f5f5f5)",
|
|
968
|
+
boxShadow: "0 10px 30px rgba(0,0,0,.28)",
|
|
969
|
+
font: "13px var(--dsw-font,system-ui,sans-serif)"
|
|
970
|
+
});
|
|
971
|
+
document.body.append(popup);
|
|
972
|
+
position();
|
|
973
|
+
return popup;
|
|
974
|
+
};
|
|
975
|
+
const render = (title, rows) => {
|
|
976
|
+
if (popup === void 0) return;
|
|
977
|
+
popup.replaceChildren();
|
|
978
|
+
const heading = document.createElement("div");
|
|
979
|
+
heading.textContent = title;
|
|
980
|
+
Object.assign(heading.style, {
|
|
981
|
+
padding: "5px 8px 7px",
|
|
982
|
+
color: "var(--dsw-alias-label-secondary,#a9adb5)",
|
|
983
|
+
fontSize: "12px",
|
|
984
|
+
fontWeight: "600"
|
|
985
|
+
});
|
|
986
|
+
popup.append(heading);
|
|
987
|
+
if (rows.length === 0) {
|
|
988
|
+
const empty = document.createElement("div");
|
|
989
|
+
empty.textContent = t("delegate.popupNoResults");
|
|
990
|
+
Object.assign(empty.style, {
|
|
991
|
+
padding: "8px",
|
|
992
|
+
color: "var(--dsw-alias-label-secondary,#a9adb5)"
|
|
993
|
+
});
|
|
994
|
+
popup.append(empty);
|
|
995
|
+
position();
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
for (const row of rows) {
|
|
999
|
+
const button = document.createElement("button");
|
|
1000
|
+
button.type = "button";
|
|
1001
|
+
button.setAttribute("role", "option");
|
|
1002
|
+
button.dataset.value = row.id;
|
|
1003
|
+
Object.assign(button.style, {
|
|
1004
|
+
display: "flex",
|
|
1005
|
+
width: "100%",
|
|
1006
|
+
minWidth: "0",
|
|
1007
|
+
gap: "8px",
|
|
1008
|
+
alignItems: "center",
|
|
1009
|
+
padding: "8px",
|
|
1010
|
+
border: "0",
|
|
1011
|
+
borderRadius: "7px",
|
|
1012
|
+
background: "transparent",
|
|
1013
|
+
color: "inherit",
|
|
1014
|
+
textAlign: "left",
|
|
1015
|
+
cursor: "pointer"
|
|
1016
|
+
});
|
|
1017
|
+
button.addEventListener("mouseenter", () => {
|
|
1018
|
+
button.style.background = "var(--dsw-alias-interactive-bg-hover,rgba(127,127,127,.16))";
|
|
1019
|
+
});
|
|
1020
|
+
button.addEventListener("mouseleave", () => {
|
|
1021
|
+
button.style.background = "transparent";
|
|
1022
|
+
});
|
|
1023
|
+
button.addEventListener("click", row.onClick);
|
|
1024
|
+
const iconUrl = row.iconId === void 0 ? void 0 : require_model_catalog_cache.providerIconUrl(row.iconId);
|
|
1025
|
+
if (iconUrl !== void 0 && iconUrl !== "" && row.iconId !== void 0) {
|
|
1026
|
+
const image = document.createElement("img");
|
|
1027
|
+
image.setAttribute("src", iconUrl);
|
|
1028
|
+
image.setAttribute("alt", "");
|
|
1029
|
+
image.setAttribute("aria-hidden", "true");
|
|
1030
|
+
image.setAttribute(DELEGATE_POPUP_LOGO_ATTRIBUTE, row.iconId);
|
|
1031
|
+
Object.assign(image.style, {
|
|
1032
|
+
width: "16px",
|
|
1033
|
+
height: "16px",
|
|
1034
|
+
flex: "0 0 16px",
|
|
1035
|
+
objectFit: "contain",
|
|
1036
|
+
borderRadius: "3px"
|
|
1037
|
+
});
|
|
1038
|
+
button.append(image);
|
|
1039
|
+
}
|
|
1040
|
+
const text = document.createElement("span");
|
|
1041
|
+
text.textContent = row.label;
|
|
1042
|
+
Object.assign(text.style, {
|
|
1043
|
+
minWidth: "0",
|
|
1044
|
+
overflowWrap: "anywhere",
|
|
1045
|
+
flex: "1 1 auto"
|
|
1046
|
+
});
|
|
1047
|
+
button.append(text);
|
|
1048
|
+
if (row.detail) {
|
|
1049
|
+
const detail = document.createElement("span");
|
|
1050
|
+
detail.textContent = row.detail;
|
|
1051
|
+
Object.assign(detail.style, {
|
|
1052
|
+
flex: "0 1 auto",
|
|
1053
|
+
minWidth: "0",
|
|
1054
|
+
maxWidth: "45%",
|
|
1055
|
+
overflowWrap: "anywhere",
|
|
1056
|
+
color: "var(--dsw-alias-label-secondary,#a9adb5)",
|
|
1057
|
+
fontSize: "12px"
|
|
1058
|
+
});
|
|
1059
|
+
button.append(detail);
|
|
1060
|
+
}
|
|
1061
|
+
popup.append(button);
|
|
1062
|
+
}
|
|
1063
|
+
position();
|
|
1064
|
+
};
|
|
1065
|
+
const selectModel = (model) => {
|
|
1066
|
+
const input = inputFor(popupSessionId);
|
|
1067
|
+
const token = popupToken;
|
|
1068
|
+
const adapter = popupAdapter;
|
|
1069
|
+
if (input === void 0 || token === void 0 || adapter === void 0) return;
|
|
1070
|
+
const ref = delegateReferenceKey(adapter.id, model.id);
|
|
1071
|
+
delegateReferenceSelections.set(ref, {
|
|
1072
|
+
adapterId: adapter.id,
|
|
1073
|
+
label: adapter.label,
|
|
1074
|
+
modelId: model.id
|
|
1075
|
+
});
|
|
1076
|
+
delegateReferenceLabels.set(ref, adapter.label);
|
|
1077
|
+
if (!(input.insertReference?.({
|
|
1078
|
+
source: DELEGATE_REFERENCE_SOURCE,
|
|
1079
|
+
ref,
|
|
1080
|
+
label: adapter.label,
|
|
1081
|
+
appearance: "session",
|
|
1082
|
+
clipboardText: `@${adapter.label}`
|
|
1083
|
+
}, token) === true)) {
|
|
1084
|
+
const draft = input.state.getSnapshot().draft ?? "";
|
|
1085
|
+
const replacement = encodeDelegationCarrier(adapter.id, adapter.label, model.id);
|
|
1086
|
+
input.setDraft?.(`${draft.slice(0, token.start)}${replacement}${draft.slice(token.end)}`);
|
|
1087
|
+
}
|
|
1088
|
+
close();
|
|
1089
|
+
};
|
|
1090
|
+
const selectAdapter = async (adapter) => {
|
|
1091
|
+
cancelRefresh();
|
|
1092
|
+
popupAdapter = adapter;
|
|
1093
|
+
popupMode = "model";
|
|
1094
|
+
const requestId = ++popupRequestId;
|
|
1095
|
+
const sessionId = popupSessionId;
|
|
1096
|
+
render(t("delegate.selectModel", { name: adapter.label }), [{
|
|
1097
|
+
id: "loading",
|
|
1098
|
+
label: t("delegate.loadingModels"),
|
|
1099
|
+
onClick: () => void 0
|
|
1100
|
+
}]);
|
|
1101
|
+
try {
|
|
1102
|
+
popupModels = (await require_model_catalog_cache.loadModelCatalog(options.rpc, adapter.id, sessionId)).groups.flatMap((group) => group.models.map((model) => {
|
|
1103
|
+
const efforts = model.efforts.map((effort) => model.effortLabels?.[effort] ?? effort);
|
|
1104
|
+
const effortsLabel = efforts.length === 0 ? void 0 : t("cli.thinkingLevel", { value: efforts.join(t("common.listSeparator")) });
|
|
1105
|
+
const details = [model.description, effortsLabel].filter((value) => value !== void 0 && value.trim() !== "");
|
|
1106
|
+
return {
|
|
1107
|
+
id: model.id,
|
|
1108
|
+
label: model.name || model.id,
|
|
1109
|
+
...details.length === 0 ? {} : { detail: details.join(" · ") }
|
|
1110
|
+
};
|
|
1111
|
+
}));
|
|
1112
|
+
if (popup === void 0 || popupMode !== "model" || popupRequestId !== requestId || popupSessionId !== sessionId || popupAdapter?.id !== adapter.id) return;
|
|
1113
|
+
render(t("delegate.selectModel", { name: adapter.label }), popupModels.map((model) => ({
|
|
1114
|
+
...model,
|
|
1115
|
+
onClick: () => selectModel(model)
|
|
1116
|
+
})));
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
if (popup === void 0 || popupMode !== "model" || popupRequestId !== requestId || popupSessionId !== sessionId || popupAdapter?.id !== adapter.id) return;
|
|
1119
|
+
render(t("delegate.selectModel", { name: adapter.label }), [{
|
|
1120
|
+
id: "error",
|
|
1121
|
+
label: error instanceof Error ? error.message : t("delegate.modelCatalogFailed"),
|
|
1122
|
+
onClick: () => void 0
|
|
1123
|
+
}]);
|
|
1124
|
+
}
|
|
1125
|
+
};
|
|
1126
|
+
const sessionIdFor = (element) => {
|
|
1127
|
+
const direct = element?.closest("[data-conversation-session]")?.getAttribute("data-conversation-session")?.trim();
|
|
1128
|
+
if (direct) return direct;
|
|
1129
|
+
const editor = element?.closest("[contenteditable=\"true\"], textarea, input, [role=\"textbox\"], [data-composer-input=\"true\"]");
|
|
1130
|
+
if (editor === null || editor === void 0) return void 0;
|
|
1131
|
+
const candidates = [...document.querySelectorAll("[data-conversation-session]")].filter((root) => root.querySelector("[contenteditable=\"true\"], textarea, input, [role=\"textbox\"], [data-composer-input=\"true\"]") !== null);
|
|
1132
|
+
return candidates.length === 1 ? candidates[0]?.getAttribute("data-conversation-session")?.trim() : void 0;
|
|
1133
|
+
};
|
|
1134
|
+
const refresh = (target) => {
|
|
1135
|
+
const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null;
|
|
1136
|
+
const editor = element?.closest("[contenteditable=\"true\"], textarea, input, [role=\"textbox\"], [data-composer-input=\"true\"]");
|
|
1137
|
+
const sessionId = sessionIdFor(element);
|
|
1138
|
+
if (!(editor instanceof HTMLElement) || sessionId === void 0 || sessionId === "") return;
|
|
1139
|
+
const input = inputFor(sessionId);
|
|
1140
|
+
if (input === void 0) return;
|
|
1141
|
+
const snapshot = input.state.getSnapshot();
|
|
1142
|
+
if (snapshot.phase !== void 0 && snapshot.phase !== "plain") {
|
|
1143
|
+
close();
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
const snapshotDraft = snapshot.draft ?? "";
|
|
1147
|
+
const selection = input.caretSpan?.();
|
|
1148
|
+
const domCaret = editorCaretOffset(editor);
|
|
1149
|
+
const caret = Math.max(0, selection?.end ?? domCaret ?? snapshotDraft.length);
|
|
1150
|
+
const domDraft = editorDraft(editor);
|
|
1151
|
+
const snapshotToken = hashTokenAtCaret(snapshotDraft, caret);
|
|
1152
|
+
const token = (domDraft === void 0 ? void 0 : hashTokenAtCaret(domDraft, caret)) ?? snapshotToken;
|
|
1153
|
+
if (token === void 0) {
|
|
1154
|
+
close();
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
popupSessionId = sessionId;
|
|
1158
|
+
popupToken = {
|
|
1159
|
+
...token,
|
|
1160
|
+
end: caret,
|
|
1161
|
+
draftRev: selection?.draftRev ?? snapshot.draftRev ?? 0
|
|
1162
|
+
};
|
|
1163
|
+
const root = ensurePopup(editor, sessionId);
|
|
1164
|
+
const query = token.query.trim().toLowerCase();
|
|
1165
|
+
if (popupMode === "model" && popupAdapter !== void 0) {
|
|
1166
|
+
render(t("delegate.selectModel", { name: popupAdapter.label }), popupModels.filter((model) => query === "" || model.id.toLowerCase().includes(query) || model.label.toLowerCase().includes(query)).map((model) => ({
|
|
1167
|
+
...model,
|
|
1168
|
+
onClick: () => selectModel(model)
|
|
1169
|
+
})));
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
popupMode = "adapter";
|
|
1173
|
+
popupAdapter = void 0;
|
|
1174
|
+
const requestId = ++popupRequestId;
|
|
1175
|
+
require_model_catalog_cache.callCliRpc(options.rpc, "catalog", { sessionId }).then((catalog) => {
|
|
1176
|
+
if (popup === void 0 || popupSessionId !== sessionId || popupMode !== "adapter" || popupRequestId !== requestId) return;
|
|
1177
|
+
popupOptions = delegateAdapterOptions(require_model_catalog_cache.adapterCatalogWithDsh(catalog, options.dshVersion));
|
|
1178
|
+
setDelegatePopupOptions(popupOptions);
|
|
1179
|
+
const rows = popupOptions.filter((item) => query === "" || item.id.toLowerCase().includes(query) || item.label.toLowerCase().includes(query)).map((item) => {
|
|
1180
|
+
const row = {
|
|
1181
|
+
id: item.id,
|
|
1182
|
+
label: item.label,
|
|
1183
|
+
iconId: item.id,
|
|
1184
|
+
onClick: () => {
|
|
1185
|
+
selectAdapter(item);
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
if (item.detail !== void 0) row.detail = item.detail;
|
|
1189
|
+
return row;
|
|
1190
|
+
});
|
|
1191
|
+
render(t("delegate.selectAdapter"), rows);
|
|
1192
|
+
}).catch(() => {
|
|
1193
|
+
if (popup === root && popupRequestId === requestId) render(t("delegate.selectAdapter"), []);
|
|
1194
|
+
});
|
|
1195
|
+
};
|
|
1196
|
+
const scheduleRefresh = (target) => {
|
|
1197
|
+
queueMicrotask(() => refresh(target));
|
|
1198
|
+
cancelRefresh();
|
|
1199
|
+
refreshTimer = setTimeout(() => {
|
|
1200
|
+
refreshTimer = void 0;
|
|
1201
|
+
refresh(target);
|
|
1202
|
+
}, 32);
|
|
1203
|
+
};
|
|
1204
|
+
const onInput = (event) => {
|
|
1205
|
+
scheduleRefresh(event.target);
|
|
1206
|
+
};
|
|
1207
|
+
const onCompositionEnd = (event) => {
|
|
1208
|
+
scheduleRefresh(event.target);
|
|
1209
|
+
};
|
|
1210
|
+
const onFocus = (event) => {
|
|
1211
|
+
scheduleRefresh(event.target);
|
|
1212
|
+
};
|
|
1213
|
+
const onKeyup = (event) => {
|
|
1214
|
+
if (event.key.length === 1 || event.key === "Backspace" || event.key === "Delete") scheduleRefresh(event.target);
|
|
1215
|
+
};
|
|
1216
|
+
const onPointer = (event) => {
|
|
1217
|
+
const target = event.target;
|
|
1218
|
+
if (popup !== void 0 && target instanceof Node && !popup.contains(target)) {
|
|
1219
|
+
if (!(target instanceof Element ? target : target.parentElement)?.closest("[contenteditable=\"true\"], textarea, input, [role=\"textbox\"], [data-composer-input=\"true\"]")) close();
|
|
1220
|
+
}
|
|
1221
|
+
};
|
|
1222
|
+
const onKey = (event) => {
|
|
1223
|
+
if (event.key === "Escape") close();
|
|
1224
|
+
};
|
|
1225
|
+
const onResize = () => position();
|
|
1226
|
+
document.addEventListener("input", onInput, true);
|
|
1227
|
+
document.addEventListener("compositionend", onCompositionEnd, true);
|
|
1228
|
+
document.addEventListener("focusin", onFocus, true);
|
|
1229
|
+
document.addEventListener("keyup", onKeyup, true);
|
|
1230
|
+
document.addEventListener("pointerdown", onPointer, true);
|
|
1231
|
+
document.addEventListener("keydown", onKey, true);
|
|
1232
|
+
window.addEventListener("resize", onResize);
|
|
1233
|
+
window.visualViewport?.addEventListener("resize", onResize);
|
|
1234
|
+
window.visualViewport?.addEventListener("scroll", onResize);
|
|
1235
|
+
return () => {
|
|
1236
|
+
close();
|
|
1237
|
+
cancelRefresh();
|
|
1238
|
+
document.removeEventListener("input", onInput, true);
|
|
1239
|
+
document.removeEventListener("compositionend", onCompositionEnd, true);
|
|
1240
|
+
document.removeEventListener("focusin", onFocus, true);
|
|
1241
|
+
document.removeEventListener("keyup", onKeyup, true);
|
|
1242
|
+
document.removeEventListener("pointerdown", onPointer, true);
|
|
1243
|
+
document.removeEventListener("keydown", onKey, true);
|
|
1244
|
+
window.removeEventListener("resize", onResize);
|
|
1245
|
+
window.visualViewport?.removeEventListener("resize", onResize);
|
|
1246
|
+
window.visualViewport?.removeEventListener("scroll", onResize);
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
1249
|
+
/** 读取编辑器当前文本;状态快照发布前先用 DOM 文本识别本轮输入。 */
|
|
1250
|
+
function editorDraft(editor) {
|
|
1251
|
+
if ("value" in editor && typeof editor.value === "string") return editor.value;
|
|
1252
|
+
return typeof editor.textContent === "string" ? editor.textContent : void 0;
|
|
1253
|
+
}
|
|
1254
|
+
/** 返回光标在 contenteditable 文本投影中的偏移;无法读取时由调用方回退到 draft 末尾。 */
|
|
1255
|
+
function editorCaretOffset(editor) {
|
|
1256
|
+
if ("selectionStart" in editor && typeof editor.selectionStart === "number") return editor.selectionStart;
|
|
1257
|
+
const selection = window.getSelection();
|
|
1258
|
+
if (selection === null || selection.rangeCount === 0 || selection.anchorNode === null || !editor.contains(selection.anchorNode)) return void 0;
|
|
1259
|
+
try {
|
|
1260
|
+
const range = selection.getRangeAt(0).cloneRange();
|
|
1261
|
+
range.selectNodeContents(editor);
|
|
1262
|
+
range.setEnd(selection.anchorNode, selection.anchorOffset);
|
|
1263
|
+
return range.toString().length;
|
|
1264
|
+
} catch {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
/** 匹配光标前最后一个以边界开头的 `#name` token;跨空格后停止,避免误触发。 */
|
|
1269
|
+
function hashTokenAtCaret(draft, caret) {
|
|
1270
|
+
const before = draft.slice(0, Math.max(0, caret));
|
|
1271
|
+
const hash = before.lastIndexOf("#");
|
|
1272
|
+
if (hash < 0 || /\s/u.test(before.slice(hash + 1))) return void 0;
|
|
1273
|
+
const previous = before[hash - 1];
|
|
1274
|
+
if (hash > 0 && previous !== void 0 && /[\p{L}\p{N}_]/u.test(previous)) return void 0;
|
|
1275
|
+
return {
|
|
1276
|
+
start: hash,
|
|
1277
|
+
query: before.slice(hash + 1)
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
function readSessionInput(ctx, sessionId) {
|
|
1281
|
+
try {
|
|
1282
|
+
const actx = readSessions(ctx)?.scope(sessionId);
|
|
1283
|
+
if (actx === void 0) return void 0;
|
|
1284
|
+
return readConversation(ctx)?.input.for(actx);
|
|
1285
|
+
} catch {
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
function readCommandUi(ctx) {
|
|
1290
|
+
const service = readService(ctx, "commandUi");
|
|
1291
|
+
return service !== void 0 && typeof service.register === "function" ? service : void 0;
|
|
1292
|
+
}
|
|
1293
|
+
function readSessions(ctx) {
|
|
1294
|
+
const service = readService(ctx, "sessions");
|
|
1295
|
+
return service !== void 0 && typeof service.scope === "function" ? service : void 0;
|
|
1296
|
+
}
|
|
1297
|
+
function readConversation(ctx) {
|
|
1298
|
+
const service = readService(ctx, "conversation");
|
|
1299
|
+
return service !== void 0 && typeof service.input?.for === "function" ? service : void 0;
|
|
1300
|
+
}
|
|
1301
|
+
/** Cordis 直接读取未声明的服务会抛 without-inject;探测统一走这里。 */
|
|
1302
|
+
function readService(ctx, name) {
|
|
1303
|
+
try {
|
|
1304
|
+
return ctx.get(name);
|
|
1305
|
+
} catch {
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
//#endregion
|
|
1310
|
+
exports.registerDelegateCommand = registerDelegateCommand;
|
|
1311
|
+
return module.exports;
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
//# sourceMappingURL=client.delegate-command.js.map
|