@lynn123411/dsh-chat-translate 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/index.ts", "../src/client/settings/ui.tsx", "../src/describe-error.ts", "../src/client/translate/api.ts", "../src/client/settings/store.ts", "../src/client/settings/styles.ts", "../src/client/reading-settings-page.tsx", "../src/client/locales.ts", "../src/client/composer/dock-toggle.tsx", "../src/client/chat/assistant-step.tsx", "../src/server/pipeline/blocks.ts", "../src/client/chat/translate-store.ts", "../src/client/chat/row-plan.ts", "../src/client/chat/click-guard.ts", "../src/client/chat/styles.ts", "../src/client/chat/presentation.ts"],
4
- "sourcesContent": ["import { setupSettingsUi } from './settings/ui.tsx';\nimport { setupComposerToggle } from './composer/dock-toggle.tsx';\nimport { AssistantStepView } from './chat/assistant-step.tsx';\nimport { chatPresentation } from './chat/presentation.ts';\nimport { ensureAssistantStyles } from './chat/styles.ts';\n// Type-only, erased from the bundle: the renderer augments Context with the\n// `slots` service the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D page shell consumes, and the settings\n// contract declares the `settings.section` slot its page component renders into.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client';\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client';\n\n/** Client plugin name, shared with the browser bundle id. */\nexport const name = 'dsh-chat-translate';\n\n/**\n * Declared services: slots for the keyed assistant-row takeover, the shared\n * \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page (page claim plus the card registration) and the\n * composer dock pill; configForms for this plugin's own configuration form\n * plus the host `ui-chat` entry whose `transcriptView` field drives the\n * reasoning-row presentation policy; the remote + remote.credentials pair for\n * the credentials Remote namespace; and locale for the card's own dictionary.\n * The runtime withholds any service not declared here; `locale` is declared\n * rather than sampled because `ctx.get('locale')` at apply time can beat the\n * locale plugin's `provide`, which silently left the card's dictionary\n * unregistered.\n */\nexport const inject = ['slots', 'configForms', 'remote', 'remote.credentials', 'locale'];\n\ninterface ClientContext {\n effect(factory: () => void | (() => void), label?: string): void;\n get?(serviceName: string): any;\n slots?: {\n inject(name: string, register: () => unknown): () => void;\n register(options: Record<string, unknown>, component: unknown): () => void;\n };\n configForms?: { get(namespace: string): any };\n locale?: any;\n remote?: { credentials?: any };\n}\n\n/**\n * Mount the assistant-row takeover (keyed `conversation.chat.node` /\n * `assistant-step`), the settings UI card inside the shared reading-settings\n * page, and the translation toggle in the composer dock.\n * @param ctx - DSH browser client context.\n */\nexport function apply(ctx: ClientContext): void {\n ensureAssistantStyles();\n\n // 1. \u63A5\u7BA1\u52A9\u624B\u884C\uFF1Akeyed slot \u7684\u66FF\u6362\u8BED\u4E49\uFF0Cpriority -1 \u6392\u5728\u5BBF\u4E3B\u9ED8\u8BA4\u6CE8\u518C\uFF080\uFF09\u4E4B\u524D\n // \u6E32\u67D3\uFF1B\u672C\u6E32\u67D3\u5668\u629B\u9519\u65F6\u6309\u6846\u67B6\u9000\u4F4D\u89C4\u5219\u9000\u51FA cell\uFF0C\u5BBF\u4E3B\u539F\u6E32\u67D3\u5668\u56DE\u4F4D\u3002\n ctx.effect(() => {\n const slots = ctx.slots;\n if (!slots || typeof slots.inject !== 'function') return () => {};\n return slots.inject('conversation.chat.node', () =>\n slots.register(\n {\n name: 'conversation.chat.node',\n key: 'assistant-step',\n // \u884C\u5185\u7684\u6298\u53E0\u3001\u505C\u6B62\u3001\u672A\u77E5\u5757\u7B49 chrome \u6587\u6848\u590D\u7528\u5BBF\u4E3B `chat` \u8BCD\u5178\uFF0C\n // \u4E24\u4FA7\u6E32\u67D3\u9010\u5B57\u4E00\u81F4\uFF1Bt \u7531\u5EA7\u6B21\u6309\u8BE5\u547D\u540D\u7A7A\u95F4\u5408\u6210\u3002\n locale: 'chat',\n priority: -1,\n // \u601D\u8003\u884C\u7684 settledReasoningPreview \u6765\u81EA\u6D3B\u7684\u5DE5\u4F5C\u7EC6\u8282\u6A21\u5F0F\u3002\n inject: () => ({ hooks: { presentation: chatPresentation } }),\n },\n AssistantStepView\n )\n );\n }, 'dsh-chat-translate: assistant step takeover');\n\n // 2. \u300C\u5DE5\u4F5C\u7EC6\u8282\u300D\u6A21\u5F0F\u8DDF\u968F\u5BBF\u4E3B\uFF1A\u8BFB ui-chat \u6761\u76EE\u7684 transcriptView\u3002\n ctx.effect(() => {\n let form: unknown = null;\n try {\n form = ctx.configForms?.get('ui-chat') ?? null;\n } catch {\n form = null;\n }\n chatPresentation.attach(form as Parameters<typeof chatPresentation.attach>[0]);\n return () => chatPresentation.detach();\n }, 'dsh-chat-translate: chat presentation source');\n\n // 3. Join the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page (claim it, or register a card into it)\n ctx.effect(() => setupSettingsUi(ctx), 'dsh-chat-translate: settings section');\n\n // 4. \u300C\u8BD1\u300D\u5F00\u5173\uFF1A\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\uFF0C\u4E0E\u8BBE\u7F6E\u9875\u603B\u5F00\u5173\u540C\u4E00\u4E2A enabled\n ctx.effect(() => setupComposerToggle(ctx), 'dsh-chat-translate: composer dock toggle');\n}\n\nexport { AssistantStepView } from './chat/assistant-step.tsx';\nexport { planAssistantRow } from './chat/row-plan.ts';\nexport { chatTranslate, createTranslateStore } from './chat/translate-store.ts';\nexport { chatPresentation, createChatPresentation, POLICY_BY_MODE } from './chat/presentation.ts';\nexport { ensureAssistantStyles } from './chat/styles.ts';\nexport { setupSettingsUi } from './settings/ui.tsx';\nexport { setupComposerToggle, ComposerTranslateToggle } from './composer/dock-toggle.tsx';\nexport { settingsStore } from './settings/store.ts';\n", "import { useState, useEffect } from 'react';\nimport * as React from 'react';\nimport type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';\nimport { Button, Input, Switch, Tag } from '@deepseek-ai/dsh-client-ui-primitives';\nimport {\n settingsStore,\n SETTINGS_NAMESPACE,\n TRANSLATE_API_KEY_REF,\n type ClientSettingsState,\n} from './store.ts';\nimport { SETTINGS_CSS } from './styles.ts';\nimport { claimReadingSettingsPage, READING_ITEM_SLOT } from '../reading-settings-page.tsx';\nimport { NS, en, zh, type ChatTranslateLocaleKey } from '../locales.ts';\nlet stylesInjected = false;\nfunction ensureSettingsStyles(): void {\n if (stylesInjected || typeof document === 'undefined') return;\n const el = document.createElement('style');\n el.dataset.tidySettings = 'true';\n el.textContent = SETTINGS_CSS;\n document.head.appendChild(el);\n stylesInjected = true;\n}\n\n/** Path the credentials Remote API persists the key to (technical sample). */\nconst CREDENTIALS_PATH = '~/.dsh/.credentials.yaml';\n\n/**\n * Render a localized template whose `{name}` markers carry technical samples\n * (paths, env refs, endpoints, model ids). The samples stay at the render site,\n * so the dictionaries hold prose only.\n * @param text - the localized template, still carrying its `{name}` markers.\n * @param samples - marker name to the literal sample, rendered as inline code.\n * @returns The template with every resolved marker replaced by `<code>`.\n */\nfunction withSamples(text: string, samples: Record<string, string>): React.ReactNode {\n return text.split(/(\\{\\w+\\})/g).map((part, index) => {\n const name = /^\\{\\w+\\}$/.test(part) ? part.slice(1, -1) : '';\n const sample = name === '' ? undefined : samples[name];\n return sample === undefined ? part : <code key={index}>{sample}</code>;\n });\n}\n\n/**\n * Chat-translate's card inside the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page. The card\n * keeps its own chrome (title rows, switches, credential form); the shared page\n * only stacks the participants' cards.\n * @param props - renderer-bound seat of the `reading.settings.item` slot plus\n * the `t` seat its `locale: NS` registration injects.\n * @returns The card element.\n */\nexport function TidySettingsPanel(\n props: PropsRuntime<'reading.settings.item'> & PropsLocale<typeof NS>\n): React.ReactElement {\n ensureSettingsStyles();\n const { t } = props;\n\n const [state, setState] = useState<ClientSettingsState>(() => settingsStore.getState());\n const [testing, setTesting] = useState<{ channel: string; running: boolean; ok?: boolean; message?: string } | null>(null);\n const [apiKeyInput, setApiKeyInput] = useState('');\n const [savingKey, setSavingKey] = useState(false);\n const [keyMsg, setKeyMsg] = useState<{ ok: boolean; text: string } | null>(null);\n\n useEffect(() => {\n return settingsStore.subscribe(() => {\n setState(settingsStore.getState());\n });\n }, []);\n\n const runTest = async (channel: string): Promise<void> => {\n setTesting({ channel, running: true });\n const res = await settingsStore.testChannel(channel);\n const message = res.ok\n ? t('testOk').replace('{latency}', String(res.latencyMs))\n : t('testFail').replace('{error}', res.error || t('unknownError'));\n setTesting({ channel, running: false, ok: res.ok, message });\n };\n\n const handleSaveKey = async (): Promise<void> => {\n setSavingKey(true);\n setKeyMsg(null);\n const res = await settingsStore.saveApiKey(apiKeyInput);\n setSavingKey(false);\n if (res.ok) {\n const cleared = !apiKeyInput.trim();\n setKeyMsg({\n ok: true,\n text: cleared\n ? t('keyCleared')\n : t('keySaved').replace('{home}', CREDENTIALS_PATH),\n });\n setApiKeyInput('');\n } else {\n setKeyMsg({\n ok: false,\n text: t('saveFailed').replace('{error}', res.error || t('unknownError')),\n });\n }\n };\n\n return (\n <div className=\"dsh-tidy-settings\">\n {/* 1. \u603B\u5F00\u5173 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-title\">\n <span>{t('masterTitle')}</span>\n <Switch\n checked={state.enabled}\n onChange={(next: boolean) => settingsStore.update({ enabled: next })}\n label={t('enableTranslation')}\n />\n </div>\n <div className=\"dsh-tidy-desc\">\n {withSamples(t('masterDesc'), { example: 'I traced the failing path to a stale lock file.' })}\n </div>\n </div>\n\n {state.enabled && (\n <>\n {/* 2. AI \u901A\u9053 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-title\">\n <span>{t('aiTitle')}</span>\n <Tag tone={state.aiConfigured ? 'success' : 'warning'}>\n {t(state.aiConfigured ? 'badgeConfigured' : 'badgeUnconfigured')}\n </Tag>\n </div>\n <div className=\"dsh-tidy-desc\">{t('aiRule')}</div>\n\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('apiKeyTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">\n {withSamples(t('apiKeyDesc'), {\n home: CREDENTIALS_PATH,\n ref: TRANSLATE_API_KEY_REF,\n })}\n </div>\n </div>\n <div className=\"dsh-tidy-input-row\">\n <Input\n type=\"password\"\n className=\"dsh-tidy-field-wide\"\n placeholder={state.aiConfigured ? t('apiKeyPlaceholderConfigured') : 'sk-...'}\n value={apiKeyInput}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setApiKeyInput(e.target.value)}\n aria-label={t('apiKeyTitle')}\n />\n <Button\n variant=\"primary\"\n size=\"sm\"\n disabled={savingKey}\n onClick={handleSaveKey}\n >\n {savingKey ? t('saving') : t('save')}\n </Button>\n </div>\n </div>\n {keyMsg && (\n <div className={`dsh-tidy-test-result ${keyMsg.ok ? 'ok' : 'fail'}`}>{keyMsg.text}</div>\n )}\n\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('baseUrlTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">\n {withSamples(t('baseUrlDesc'), {\n url1: 'https://api.openai.com/v1',\n url2: 'https://api.deepseek.com/v1',\n })}\n </div>\n </div>\n <Input\n type=\"text\"\n className=\"dsh-tidy-field-wide\"\n placeholder=\"https://api.openai.com/v1\"\n value={state.baseUrl}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n settingsStore.update({ baseUrl: e.target.value })\n }\n aria-label={t('baseUrlTitle')}\n />\n </div>\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('modelTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">\n {withSamples(t('modelDesc'), {\n model1: 'gpt-4o-mini',\n model2: 'deepseek-chat',\n })}\n </div>\n </div>\n <Input\n type=\"text\"\n className=\"dsh-tidy-field-wide\"\n placeholder=\"gpt-4o-mini\"\n value={state.model}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n settingsStore.update({ model: e.target.value })\n }\n aria-label={t('modelTitle')}\n />\n </div>\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('timeoutTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">{t('timeoutDesc')}</div>\n </div>\n <Input\n type=\"number\"\n className=\"dsh-tidy-field-timeout\"\n min={500}\n max={900000}\n step={1000}\n value={state.aiTimeoutMs}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const val = parseInt(e.target.value, 10);\n if (!Number.isFinite(val)) return;\n settingsStore.update({\n aiTimeoutMs: Math.min(Math.max(val, 500), 900000),\n });\n }}\n aria-label={t('timeoutTitle')}\n />\n </div>\n\n <div className=\"dsh-tidy-row\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={testing?.running}\n onClick={() => runTest('openai')}\n >\n {testing?.running ? t('testing') : t('test')}\n </Button>\n {testing?.channel === 'openai' && !testing.running && (\n <span className={`dsh-tidy-test-result ${testing.ok ? 'ok' : 'fail'}`}>{testing.message}</span>\n )}\n </div>\n </div>\n\n {/* 3. \u884C\u4E3A\u8BF4\u660E */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-row-title\">{t('behaviorTitle')}</div>\n <div className=\"dsh-tidy-desc\">{t('behaviorRule')}</div>\n </div>\n </>\n )}\n </div>\n );\n}\n\n/**\n * Bind the settings store to this plugin's own configuration form and the\n * credentials Remote API, then join the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page:\n * whichever participant activates first claims the page, everyone registers a\n * card into its child slot.\n * @param ctx - DSH browser client context.\n */\nexport function setupSettingsUi(ctx: any): void {\n if (typeof window === 'undefined') return;\n\n // Ride DSH's shared configuration forms: `ctx.configForms.get(<profile entry\n // id>)` is the read/write face of THIS plugin's own Config schema \u2014 it\n // mirrors the Host's config section for the entry and folds every accepted\n // write back. `remote.credentials` writes the API key. There is no custom\n // config HTTP endpoint and no per-namespace settings scope.\n const configForms = ctx.configForms;\n const remoteCredentials = ctx.remote?.credentials ?? null;\n try {\n settingsStore.attach(configForms.get(SETTINGS_NAMESPACE), remoteCredentials);\n } catch (err) {\n console.warn('[dsh-chat-translate] Failed to bind the configuration form:', err);\n }\n\n // The locale service is declared through the client `inject` table, so cordis\n // installs it before this body runs and `ctx.locale` cannot miss it.\n // Registering the dictionary HERE is what the card's seat `t` resolves\n // against; without the registration the card falls back to the shell's\n // `common` namespace and renders every unknown key as its own key text.\n const locale = ctx.locale;\n if (locale && typeof locale.register === 'function') {\n ctx.effect(\n () => locale.register(NS, { zh, en }),\n 'dsh-chat-translate: locale dictionaries'\n );\n }\n const t = locale && typeof locale.bind === 'function'\n ? locale.bind(NS)\n // Key-aware fallback: one fixed label for every key would dress the shared\n // page's tab in the wrong text whenever the locale service is not yet\n // there, so the fallback answers each key with its own zh value.\n : (key: ChatTranslateLocaleKey): string => zh[key] ?? key;\n\n try {\n const slots = ctx.slots;\n if (!slots || typeof slots.inject !== 'function') return;\n\n // \u5171\u4EAB\u300C\u9605\u8BFB\u4F53\u9A8C\u300D\u8BBE\u7F6E\u9875\uFF1A\u672C\u63D2\u4EF6\u4E0E dsh-smooth-stream\n // \u5171\u7528\u4E00\u9875\uFF0C\u5185\u6838\u4E0D\u5141\u8BB8 settings.section \u7684\u540C\u4E00 id \u88AB\u6CE8\u518C\u4E24\u6B21\u3001\u4E5F\u4E0D\u5141\u8BB8\u5B50 slot\n // \u88AB\u58F0\u660E\u4E24\u6B21\uFF0C\u56E0\u6B64\u5404\u53C2\u4E0E\u8005\u643A\u5E26\u540C\u4E00\u4EFD\u9875\u58F3\u3001\u5148\u5230\u5148\u5F97\u5F53\u9009\uFF1A\u5F53\u9009\u8005\u6CE8\u518C\u9875\u9762\u5E76\u58F0\u660E\n // reading.settings.item \u5B50 slot\uFF0C\u672A\u5F53\u9009\u8005\u53EA\u628A\u5361\u7247\u6CE8\u518C\u8FDB\u8BE5\u5B50 slot \u7B49\u9875\u9762\u51FA\u73B0\u3002\n // \u5171\u4EAB\u9875\u58F3\u9700\u8981\u771F ctx\uFF1A\u5B83\u7528 ctx.slots \u8BFB\u6CE8\u518C\u8868\uFF0C\u5E76\u7528 ctx.get('locale') \u5728\u8BED\u8A00\n // \u5207\u6362\u65F6\u91CD\u8BFB tab \u6807\u7B7E\uFF08\u53EF\u9009\u670D\u52A1\uFF0C\u5FC5\u987B\u8D70 ctx.get\uFF0C\u4E0D\u80FD\u7528 ctx.locale\uFF09\u3002\n //\n // \u6574\u9875\uFF08\u542B\u672C\u63D2\u4EF6\u90A3\u5F20\u5361\u7247\uFF09\u53EA\u5728\u5BBF\u4E3B\u771F\u7684\u670D\u52A1\u672C\u63D2\u4EF6\u7684\u914D\u7F6E\u6761\u76EE\u65F6\u5B58\u5728\uFF1A\n // `whileServed` \u662F 0.1.7 \u7684\u6B63\u5F0F\u5951\u7EA6\uFF0C\u6CA1\u88C5/\u6CA1\u52A0\u8F7D\u5BBF\u4E3B\u534A\u8FB9\u7684\u90E8\u7F72\u4E0D\u4F1A\u7559\u4E0B\u9875\u7684\u75D5\u8FF9\u3002\n ctx.effect(\n () => configForms.whileServed([SETTINGS_NAMESPACE], () => {\n const offPage = slots.inject('settings.section', () =>\n claimReadingSettingsPage(ctx, () => t('pageNav'), NS)\n );\n const offCard = slots.inject(READING_ITEM_SLOT, () =>\n slots.register(\n {\n name: READING_ITEM_SLOT,\n // id = \u672C\u63D2\u4EF6\u7684\u914D\u7F6E\u6761\u76EE id\uFF08= profile entry id = \u8BBE\u7F6E\u547D\u540D\u7A7A\u95F4\uFF09\n id: SETTINGS_NAMESPACE,\n // \u5361\u7247\u5728\u5171\u4EAB\u9875\u91CC\u7684\u987A\u5E8F\uFF1A\u4E1D\u6ED1\u6D41\u5F0F 10\u3001\u5438\u9876\u63D0\u793A 20\u3001\u804A\u5929\u7FFB\u8BD1 30\n order: 30,\n // \u5171\u4EAB\u9875\u6309\u6B64\u6807\u7B7E\u6E32\u67D3 tab\n label: () => t('title'),\n locale: NS,\n },\n TidySettingsPanel\n )\n );\n return () => {\n offCard();\n offPage();\n };\n }),\n 'dsh-chat-translate: shared reading page'\n );\n } catch (err) {\n console.warn('[dsh-chat-translate] Failed to inject settings section:', err);\n }\n}", "/**\n * \u628A fetch / \u7F51\u7EDC\u5931\u8D25\u7684\u53EF\u8BFB\u539F\u56E0\u5C55\u5F00\u6210\u5355\u884C\u6587\u672C\uFF08\u5BBF\u4E3B\u4E0E\u5BA2\u6237\u7AEF\u5171\u7528\uFF09\u3002\n *\n * \u80CC\u666F\uFF1ANode 18+ \u4E0E\u6D4F\u89C8\u5668\u7684 fetch \u5728\u7F51\u7EDC\u5C42\u5931\u8D25\u65F6\u53EA\u7ED9\u4E00\u53E5 `TypeError: fetch failed`\n * \uFF08\u6D4F\u89C8\u5668\u662F `Failed to fetch`\uFF09\uFF0C\u771F\u5B9E\u539F\u56E0\u85CF\u5728 `err.cause` \u91CC\u2014\u2014\u4F8B\u5982\n * `connect ENETUNREACH 172.24.52.126:8081`\u3001`other side closed [UND_ERR_SOCKET]`\u3001\n * `getaddrinfo ENOTFOUND`\u3002\u53EA\u4E0A\u62A5 `err.message` \u4F1A\u8BA9\u300C\u7AEF\u70B9\u4E0D\u53EF\u8FBE / \u88AB\u4EE3\u7406\u65AD\u5F00\u300D\u548C\n * \u300C\u63D2\u4EF6\u81EA\u8EAB\u51FA\u9519\u300D\u957F\u5F97\u4E00\u6A21\u4E00\u6837\uFF1A\u7528\u6237\u53EA\u770B\u5230\u65E0\u4FE1\u606F\u91CF\u7684 fetch failed\uFF0C\u65E0\u4ECE\u5224\u65AD\u8BE5\u6539\u4EC0\u4E48\u3002\n *\n * \u8FD9\u91CC\u628A message \u4E0E cause \u94FE\uFF08\u542B AggregateError \u7684\u591A\u5730\u5740\u5931\u8D25\uFF09\u62FC\u6210 `A \u2190 B \u2190 C`\u3002\n */\nexport function describeError(err: unknown): string {\n if (err === null || err === undefined) return String(err);\n const parts: string[] = [];\n const push = (s: string): void => {\n const t = String(s || '').trim();\n if (t && !parts.includes(t)) parts.push(t);\n };\n const head = (err as { message?: string }).message || String(err);\n push(head);\n const headCode = (err as { code?: string }).code;\n if (headCode && !head.includes(headCode)) push('[' + headCode + ']');\n let cause = (err as { cause?: unknown }).cause as\n | { message?: string; code?: string; cause?: unknown; errors?: unknown }\n | undefined;\n for (let depth = 0; cause && depth < 4; depth++) {\n const label = cause.message || String(cause);\n push(cause.code && !label.includes(cause.code) ? label + ' [' + cause.code + ']' : label);\n // AggregateError\uFF1A\u591A\u5730\u5740\u5168\u90E8\u5931\u8D25\uFF08happy-eyeballs \u53CC\u6808\u5E38\u89C1\uFF09\uFF0C\u5C55\u5F00\u524D\u51E0\u6761\n if (Array.isArray(cause.errors)) {\n for (const sub of (cause.errors as { message?: string; code?: string }[]).slice(0, 3)) {\n if (sub) push(sub.code || sub.message || String(sub));\n }\n }\n cause = cause.cause as typeof cause;\n }\n return parts.join(' \u2190 ');\n}", "import { describeError } from '../../describe-error.ts';\n\n/** \u6B63\u6587\u4E00\u4E2A\u5757\u7684\u7FFB\u8BD1\u7ED3\u679C\uFF0C\u4E0E\u8BF7\u6C42\u7684\u5757\u5217\u8868\u6309\u4E0B\u6807\u5BF9\u9F50\u3002 */\nexport interface ReplyBlockResult {\n original: string;\n translated: string;\n ok: boolean;\n cached: boolean;\n channel: string;\n}\n\n/**\n * \u8BF7\u6C42\u7FFB\u8BD1\u56DE\u7B54\u6B63\u6587\u7684\u5757\u3002\u5BBF\u4E3B\u4FA7\u6309\u8F93\u5165\u4E0A\u9650\u6253\u5305\u3001\u4E32\u884C\u53D1\u9001\uFF0C\u5BA2\u6237\u7AEF\u8D85\u65F6\u4EA4\u7ED9\n * \u5BBF\u4E3B\uFF08\u4E00\u4E2A\u957F\u56DE\u7B54\u53EF\u80FD\u8981\u51E0\u5206\u949F\uFF09\uFF0C\u8FD9\u91CC\u4E0D\u8BBE\u81EA\u5DF1\u7684\u622A\u6B62\u65F6\u95F4\u3002\n */\nexport async function requestTranslateReply(blocks: string[]): Promise<ReplyBlockResult[]> {\n const fallback = (): ReplyBlockResult[] =>\n blocks.map((block) => ({\n original: block,\n translated: block,\n ok: false,\n cached: false,\n channel: 'fallback-client',\n }));\n\n if (!Array.isArray(blocks) || blocks.length === 0) return [];\n try {\n const res = await fetch('/api/dsh-chat-translate/translate-reply', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ blocks }),\n });\n if (res.ok) {\n const data = (await res.json()) as {\n ok: boolean;\n results?: ReplyBlockResult[];\n error?: string;\n };\n if (data.ok && Array.isArray(data.results) && data.results.length === blocks.length) {\n return data.results;\n }\n if (data.error) {\n console.warn(`[dsh-chat-translate] \u6B63\u6587\u7FFB\u8BD1\u63A5\u53E3\u9519\u8BEF: ${data.error}`);\n }\n } else {\n console.warn(`[dsh-chat-translate] \u6B63\u6587\u7FFB\u8BD1\u8BF7\u6C42\u5931\u8D25 (HTTP ${res.status})`);\n }\n } catch (err: any) {\n console.warn(`[dsh-chat-translate] \u6B63\u6587\u7FFB\u8BD1\u8BF7\u6C42\u5F02\u5E38: ${describeError(err)}`);\n }\n return fallback();\n}\n\nexport async function testServerChannel(channel: string): Promise<{ ok: boolean; latencyMs: number; error?: string }> {\n try {\n const res = await fetch('/api/dsh-chat-translate/test-channel', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ channel }),\n });\n return await res.json();\n } catch (err: any) {\n return { ok: false, latencyMs: 0, error: describeError(err) };\n }\n}\n", "import { describeError } from '../../describe-error.ts';\nimport { testServerChannel } from '../translate/api.ts';\n\nexport interface ClientSettingsState {\n enabled: boolean;\n aiTimeoutMs: number;\n baseUrl: string;\n model: string;\n /** \u6D3E\u751F\u503C\uFF1AKey / Base URL / \u6A21\u578B\u9F50\u5168\uFF0C\u6B63\u6587\u624D\u4F1A\u88AB\u7FFB\u8BD1\u3002 */\n aiConfigured: boolean;\n}\n\n/** \u6B63\u6587\u5355\u6B21\u8BF7\u6C42\u8D85\u65F6\u7684\u53D6\u503C\u8303\u56F4\uFF0C\u4E0E\u5BBF\u4E3B\u534A\u8FB9\u4FDD\u6301\u4E00\u81F4\u3002 */\nexport const AI_TIMEOUT_MIN = 500;\nexport const AI_TIMEOUT_MAX = 900000;\n\n/** Profile entry id (== settings namespace) + credentials ref, mirroring the host constants. */\nexport const SETTINGS_NAMESPACE = 'dsh-chat-translate';\nexport const TRANSLATE_API_KEY_REF = 'TRANSLATE_API_KEY';\n\n/**\n * One path-addressed edit, matching the wire shape the shared configuration\n * form's `mutate` accepts.\n */\nexport type SettingsPathOpLike =\n | { op: 'set'; path: string[]; value: unknown }\n | { op: 'unset'; path: string[] };\n\n/**\n * Minimal structural shape of the DSH shared configuration form this store\n * rides on \u2014 `ConfigForm<T>` from @deepseek-ai/dsh-client-ui-settings, reached\n * as `ctx.configForms.get(<profile entry id>)`. Keeping it structural keeps\n * this bundle free of the settings package and lets tests inject fakes; the\n * real form is a structural superset (it also carries `base`, `user` and\n * `mode`).\n */\nexport interface ConfigFormLike {\n getSnapshot(): {\n /** `unavailable` when the Host does not serve this entry to this client. */\n status: 'loading' | 'ready' | 'unavailable';\n /** Resolved section: schema defaults, then composition base, then user layer. */\n value?: Record<string, unknown>;\n /** Whether the Host document accepts writes; memory mode never does. */\n writable: boolean;\n /** Namespace revision fencing the next write. */\n revision?: number;\n };\n subscribe(listener: () => void): () => void;\n /** One revision-fenced batch of path writes; resolves to Host acceptance. */\n mutate(ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<unknown>;\n}\n\n/** Shape of every DSH client Remote call: {ok, value} / {ok, error} wrapper. */\nexport type RemoteResult<T> =\n | { ok: true; value: T }\n | { ok: false; error: { code?: string; message: string; details?: unknown } };\n\nexport interface CredentialsRemoteLike {\n describe(\n refs: string[]\n ): Promise<RemoteResult<Record<string, { configured: boolean; source?: string; writable: boolean }>>>;\n set(ref: string, value: string): Promise<RemoteResult<void>>;\n unset(ref: string): Promise<RemoteResult<void>>;\n}\n\nconst DEFAULT_STATE: ClientSettingsState = {\n enabled: true,\n aiTimeoutMs: 600000,\n baseUrl: '',\n model: '',\n aiConfigured: false,\n};\n\n/**\n * Client settings store backed by this plugin's own configuration form.\n *\n * There is no localStorage overlay and no custom config HTTP endpoint: the\n * store derives from the bound `ConfigForm`, which mirrors the profile entry's\n * config section (schema defaults \u2192 composition base \u2192 user layer) and folds\n * every accepted write back, and each debounced flush commits its touched\n * fields as one `mutate` batch \u2014 one revision fence, serialized and persisted\n * by DSH. Without a bound form (unit tests) it degrades to an in-memory store\n * with the same semantics.\n */\nclass SettingsStore {\n private state: ClientSettingsState = { ...DEFAULT_STATE };\n private listeners = new Set<() => void>();\n private form: ConfigFormLike | null = null;\n private credentials: CredentialsRemoteLike | null = null;\n private unsubscribeForm: (() => void) | null = null;\n private keyConfigured = false;\n private writeTimer: number | null = null;\n private pendingFields = new Set<string>();\n\n /**\n * Bind the DSH services. Called once from the settings UI setup; re-binding\n * (e.g. after a reconnect) detaches the previous subscription first.\n */\n attach(form: ConfigFormLike | null, credentials: CredentialsRemoteLike | null): void {\n if (this.unsubscribeForm) {\n this.unsubscribeForm();\n this.unsubscribeForm = null;\n }\n this.form = form;\n this.credentials = credentials;\n if (form) {\n this.unsubscribeForm = form.subscribe(() => this.derive());\n this.derive();\n }\n void this.refreshKeyStatus();\n }\n\n getState(): ClientSettingsState {\n return { ...this.state };\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Map the resolved section into client state and notify. */\n private derive(): void {\n if (!this.form) return;\n const snap = this.form.getSnapshot();\n const value = snap.value;\n if (!value || typeof value !== 'object') return;\n const next: ClientSettingsState = { ...this.state };\n if (typeof value.enabled === 'boolean') next.enabled = value.enabled;\n const timeout = value.aiTimeoutMs;\n if (typeof timeout === 'number' && Number.isFinite(timeout)) {\n next.aiTimeoutMs = Math.min(\n Math.max(Math.round(timeout), AI_TIMEOUT_MIN),\n AI_TIMEOUT_MAX\n );\n }\n if (typeof value.baseUrl === 'string') next.baseUrl = value.baseUrl;\n if (typeof value.model === 'string') next.model = value.model;\n this.applyState(next);\n }\n\n private applyState(next: ClientSettingsState): void {\n next.aiConfigured = Boolean(next.baseUrl && next.model && this.keyConfigured);\n this.state = next;\n this.notify();\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n try {\n listener();\n } catch {}\n }\n }\n\n /** Pull the key's status-only view and re-derive aiConfigured. */\n private async refreshKeyStatus(): Promise<void> {\n if (!this.credentials) return;\n try {\n const res = await this.credentials.describe([TRANSLATE_API_KEY_REF]);\n if (!res.ok) return;\n const info = res.value[TRANSLATE_API_KEY_REF];\n const configured = Boolean(info?.configured);\n if (configured !== this.keyConfigured) {\n this.keyConfigured = configured;\n this.applyState({ ...this.state });\n }\n } catch {\n // Credentials surface unavailable \u2014 keep the last known status.\n }\n }\n\n /**\n * Optimistically apply locally, then persist each touched field through the\n * configuration form. Writes are trailing-debounced (300ms) so typing in the\n * baseUrl/model inputs collapses into a single queued mutation instead of\n * one write per keystroke \u2014 which would otherwise flash stale mirror values\n * back into the inputs between commits. A failed write makes the form reload\n * its mirror, which re-derives this store from the Host document\n * (conflict-safe recovery).\n */\n async update(partial: Partial<ClientSettingsState>): Promise<void> {\n let sanitizedTimeout = this.state.aiTimeoutMs;\n if (typeof partial.aiTimeoutMs === 'number' && Number.isFinite(partial.aiTimeoutMs)) {\n sanitizedTimeout = Math.min(\n Math.max(Math.round(partial.aiTimeoutMs), AI_TIMEOUT_MIN),\n AI_TIMEOUT_MAX\n );\n }\n const next: ClientSettingsState = {\n ...this.state,\n ...partial,\n aiTimeoutMs: sanitizedTimeout,\n };\n this.applyState(next);\n\n if (this.form) {\n const fields = ['enabled', 'aiTimeoutMs', 'baseUrl', 'model'] as const;\n for (const field of fields) {\n if (partial[field] !== undefined) {\n this.pendingFields.add(field);\n }\n }\n this.scheduleWrite();\n }\n }\n\n private scheduleWrite(): void {\n if (this.writeTimer !== null || typeof window === 'undefined') return;\n this.writeTimer = window.setTimeout(() => {\n this.writeTimer = null;\n void this.flushWrite();\n }, 300);\n }\n\n private async flushWrite(): Promise<void> {\n if (!this.form) return;\n const fields = [...this.pendingFields];\n this.pendingFields.clear();\n if (fields.length === 0) return;\n const ops: SettingsPathOpLike[] = fields.map((field) => ({\n op: 'set',\n path: [field],\n value: this.state[field as keyof ClientSettingsState],\n }));\n await this.form.mutate(ops).catch(() => {});\n }\n\n async testChannel(channel: string): Promise<{ ok: boolean; latencyMs: number; error?: string }> {\n return testServerChannel(channel);\n }\n\n /** Write (or clear) the API key through the credentials Remote API. */\n async saveApiKey(apiKey: string): Promise<{ ok: boolean; error?: string }> {\n if (!this.credentials) {\n return { ok: false, error: '\u51ED\u636E\u670D\u52A1\u4E0D\u53EF\u7528\uFF08\u975E\u56DE\u73AF\u9875\u9762\uFF09' };\n }\n try {\n const key = apiKey.trim();\n // Remote calls resolve with {ok:false} instead of rejecting on refusal\n // (e.g. env-shadowed refs), so the .ok check decides the outcome.\n const res = key\n ? await this.credentials.set(TRANSLATE_API_KEY_REF, key)\n : await this.credentials.unset(TRANSLATE_API_KEY_REF);\n if (!res.ok) {\n return { ok: false, error: res.error?.message || '\u4FDD\u5B58\u5931\u8D25' };\n }\n await this.refreshKeyStatus();\n return { ok: true };\n } catch (err: any) {\n return { ok: false, error: describeError(err) };\n }\n }\n\n dispose(): void {\n if (this.writeTimer !== null && typeof window !== 'undefined') {\n clearTimeout(this.writeTimer);\n this.writeTimer = null;\n }\n this.pendingFields.clear();\n if (this.unsubscribeForm) {\n this.unsubscribeForm();\n this.unsubscribeForm = null;\n }\n this.form = null;\n this.credentials = null;\n this.listeners.clear();\n }\n}\n\nexport const settingsStore = new SettingsStore();\n", "export const SETTINGS_CSS = String.raw`\n.dsh-tidy-settings {\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-width: 580px;\n padding-bottom: 32px;\n color: var(--dsw-alias-label-primary);\n font: var(--dsw-font-s-14);\n}\n\n.dsh-tidy-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px 18px;\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n /* The shipped settings-card surface; Input's own bg-layer-1 then reads as a\n recessed field in the dark theme instead of vanishing into the card. */\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.dsh-tidy-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font: var(--dsw-font-s-strong-14);\n}\n\n.dsh-tidy-desc {\n color: var(--dsw-alias-label-secondary);\n font: var(--dsw-font-xxs-12);\n}\n\n.dsh-tidy-desc code {\n font-family: var(--ds-font-family-code);\n}\n\n.dsh-tidy-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 14px;\n padding: 8px 0;\n}\n\n.dsh-tidy-row + .dsh-tidy-row {\n border-top: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n.dsh-tidy-row-info {\n flex: 1;\n min-width: 0;\n}\n\n.dsh-tidy-row-title {\n font: var(--dsw-font-xs-strong-13);\n}\n\n.dsh-tidy-row-desc {\n margin-top: 2px;\n color: var(--dsw-alias-label-secondary);\n font: var(--dsw-font-xxxs-11);\n}\n\n/* Field widths ride Input's wrapper span; the atom owns its own appearance. */\n.dsh-tidy-input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.dsh-tidy-field-wide {\n width: 260px;\n}\n\n.dsh-tidy-field-timeout {\n width: 120px;\n}\n\n.dsh-tidy-test-result {\n margin-left: 10px;\n font: var(--dsw-font-xxs-12);\n}\n\n.dsh-tidy-test-result.ok {\n color: var(--dsw-alias-state-success-primary);\n}\n\n.dsh-tidy-test-result.fail {\n color: var(--dsw-alias-state-error-primary);\n}\n\n`;", "/**\n * The shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page.\n *\n * Several plugins contribute their configuration to ONE settings page, but the\n * kernel cannot declare that page jointly: `settings.section` is a list slot\n * that rejects a duplicate `id` at the same priority (\"already has an entry\n * with id\"), and a child slot may be declared exactly once (\"slot \u2026 is already\n * declared\"). Composing several cards into one page therefore takes one\n * declarer, so every participating plugin carries this same shell and the\n * FIRST one to activate claims the page; the others register their card into\n * {@link READING_ITEM_SLOT} and wait for the winner's declaration through\n * `slots.inject`. Uninstalling the winner promotes another participant on the\n * next boot, so no participant is a fixed owner.\n *\n * The page renders ONE TAB PER REGISTERED CARD: the tab roster comes from the\n * child slot's own registrations (id + `label` + `order`, the same shape the\n * kernel's own Plugins page uses for its tabs), and each panel dispatches\n * through `renderSlot(READING_ITEM_SLOT, {}, { only: id })`. Every panel stays\n * mounted but hidden, so a card's local state survives a tab switch.\n *\n * Keep this file identical across the participating plugins\n * (`dsh-smooth-stream`, `dsh-chat-translate`).\n * Participants own their own card component, locale dictionaries, settings\n * namespace and Host half \u2014 only the page shell below is shared, because\n * cross-plugin value imports are forbidden by the client bundle purity gate.\n * It imports nothing but `react` plus type-only DSH packages on purpose, so\n * every participant's build configuration compiles it unchanged.\n *\n * Settings storage belongs to the card, not to this shell: since 0.1.7 a\n * participant reads and writes its own Config through\n * `ctx.configForms.get(<profile entry id>)` and gates the registration on\n * `ctx.configForms.whileServed(...)`. The shell only stacks cards \u2014 it holds no\n * settings service of its own, which is why it needs nothing beyond\n * `ctx.slots` and the optional `locale`.\n */\n\nimport { createElement, useState, useSyncExternalStore } from 'react'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { InjectFace, LocaleNamespaceMap, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n\n/** Page id claimed by the first participating plugin to activate. */\nexport const READING_PAGE_ID = 'reading'\n/** Sidebar position of the shared page; own plugins start at 110. */\nexport const READING_PAGE_ORDER = 110\n/** The page's one child slot: every participant's card registers here. */\nexport const READING_ITEM_SLOT = 'reading.settings.item'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n /** One participating plugin's card inside the shared reading page. */\n 'reading.settings.item': { kind: 'list'; scope: 'root' }\n }\n}\n\n/** One participant's tab, derived from its card registration. */\nexport interface ReadingTab {\n /** Registration id \u2014 the participant's Host settings namespace. */\n id: string\n /** Registration order, ascending in the tab bar. */\n order: number\n /** Registration label, already resolved for the active locale. */\n label: string\n}\n\n/** Live tab roster handed to the page component through its inject face. */\nexport interface ReadingTabsFace {\n getSnapshot(): readonly ReadingTab[]\n subscribe(listener: () => void): () => void\n}\n\n/** Inject face of the shared page. */\nexport interface ReadingPageFace {\n readingTabs: ReadingTabsFace\n}\n\ntype ReadingPageProps =\n PropsRuntime<'settings.section'>\n & PropsRenderSlots<typeof READING_ITEM_SLOT>\n & InjectFace<ReadingPageFace>\n\n/**\n * A registration label is a plain string or a thunk re-read per projection \u2014\n * the same `SlotLabel` the shipped `resolveSlotLabel` resolves. It is inlined\n * rather than imported so this shared file stays value-import-free (see the\n * header): every participant's build configuration compiles it unchanged.\n */\nfunction readLabel(label: unknown): string {\n if (typeof label === 'function') return (label as () => string)()\n return typeof label === 'string' ? label : ''\n}\n\n/**\n * Tab chrome mirrors the kernel's own settings tabs (`.tabs` / `.tab` in\n * `ui-settings-plugins`): tertiary label, 13px, 22px gutter. The marker is the\n * ACTIVE TAB'S OWN bottom border and the bar draws no rail of its own \u2014 a\n * shared underline reads as \"every tab is selected\", which is exactly the bug\n * this replaces.\n */\nconst TABLIST_STYLE = {\n display: 'flex',\n alignItems: 'flex-end',\n gap: '22px',\n marginTop: '2px',\n marginBottom: '16px',\n} as const\n\nconst TAB_STYLE = {\n appearance: 'none',\n background: 'transparent',\n // No border on ANY tab: the marker below is the active tab's own element, so\n // an inactive tab has nothing that could render a line.\n border: 'none',\n position: 'relative',\n padding: '7px 1px 11px',\n cursor: 'pointer',\n font: 'var(--dsw-font-xs-13)',\n color: 'var(--dsw-alias-label-tertiary)',\n} as const\n\nconst TAB_ACTIVE_STYLE = {\n ...TAB_STYLE,\n color: 'var(--dsw-alias-label-primary)',\n} as const\n\n/** The kernel's own tab marker: a 2px rounded bar under the active label. */\nconst TAB_MARKER_STYLE = {\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n height: '2px',\n borderRadius: '2px 2px 0 0',\n background: 'var(--dsw-alias-label-primary)',\n} as const\n\n/** Panels stay mounted (hidden) so each card keeps its local state. */\nconst PANEL_STYLE = { listStyle: 'none', margin: 0, padding: 0 } as const\nconst PANEL_HIDDEN_STYLE = { ...PANEL_STYLE, display: 'none' } as const\n\n/**\n * Build the live tab roster over the child slot's registrations. `locale` is\n * read through `ctx.get`: a participant needs it only to re-read localized\n * labels on a language switch, and reaching an undeclared service as\n * `ctx.locale` would trip the kernel's inject guard.\n * @param ctx - browser context carrying the slot registry.\n * @returns The tab store consumed by the page component.\n */\nfunction createReadingTabs(ctx: ClientContext): ReadingTabsFace {\n const locale = ctx.get('locale') as\n | { getSnapshot(): { revision: number }; subscribe(listener: () => void): () => void }\n | undefined\n let version = -1\n let revision = -1\n let tabs: readonly ReadingTab[] = []\n return {\n getSnapshot: () => {\n const nextVersion = ctx.slots.getVersion(READING_ITEM_SLOT)\n const nextRevision = locale === undefined ? 0 : locale.getSnapshot().revision\n if (nextVersion === version && nextRevision === revision) return tabs\n version = nextVersion\n revision = nextRevision\n tabs = ctx.slots.entries(READING_ITEM_SLOT)\n .map(entry => ({\n id: entry.options.id ?? '',\n order: entry.options.order ?? 0,\n label: readLabel(entry.options.label),\n }))\n .sort((left, right) => left.order - right.order)\n return tabs\n },\n subscribe: (listener) => {\n const offSlots = ctx.slots.subscribe(READING_ITEM_SLOT, listener)\n const offLocale = locale?.subscribe(listener)\n return () => {\n offSlots()\n offLocale?.()\n }\n },\n }\n}\n\n/**\n * Page body: one tab per registered card, plus the selected card's panel.\n * The shell supplies the section's own seats and `renderSlot` bound to the\n * child slot declared at registration time.\n */\nexport function ReadingSettingsSection({ renderSlot, readingTabs }: ReadingPageProps) {\n const tabs = useSyncExternalStore(\n readingTabs.subscribe,\n readingTabs.getSnapshot,\n readingTabs.getSnapshot,\n )\n const [requested, setRequested] = useState<string | null>(null)\n const selected = requested !== null && tabs.some(tab => tab.id === requested)\n ? requested\n : tabs[0]?.id ?? null\n if (selected === null) return null\n return createElement(\n 'div',\n null,\n createElement(\n 'div',\n { role: 'tablist', style: TABLIST_STYLE },\n tabs.map(tab => createElement(\n 'button',\n {\n key: tab.id,\n type: 'button',\n role: 'tab',\n 'aria-selected': tab.id === selected,\n style: tab.id === selected ? TAB_ACTIVE_STYLE : TAB_STYLE,\n onClick: () => { setRequested(tab.id) },\n },\n tab.label,\n tab.id === selected ? createElement('span', { style: TAB_MARKER_STYLE, 'aria-hidden': true }) : null,\n )),\n ),\n tabs.map(tab => createElement(\n 'ul',\n {\n key: tab.id,\n role: 'tabpanel',\n hidden: tab.id !== selected,\n style: tab.id === selected ? PANEL_STYLE : PANEL_HIDDEN_STYLE,\n },\n renderSlot(READING_ITEM_SLOT, {}, { only: tab.id }),\n )),\n )\n}\n\n/** Whether a participant already holds the shared page. */\nexport function readingPageClaimed(ctx: ClientContext): boolean {\n return ctx.slots.entries('settings.section').some(entry => entry.options.id === READING_PAGE_ID)\n}\n\n/**\n * Claim the shared page when no participant holds it yet. Call inside\n * `ctx.slots.inject('settings.section', \u2026)`: injection order decides the\n * winner, and the losers stay silent instead of colliding with the kernel's\n * duplicate-id and duplicate-declaration guards.\n * @param ctx - browser context carrying the slot registry.\n * @param label - page label, re-read by the shell on every projection.\n * @param locale - locale namespace the label thunk translates through.\n * @returns The page registration's disposer, or a no-op when another\n * participant already holds the page.\n */\nexport function claimReadingSettingsPage(\n ctx: ClientContext,\n label: () => string,\n locale: keyof LocaleNamespaceMap & string,\n): () => void {\n if (readingPageClaimed(ctx)) return () => {}\n const readingTabs = createReadingTabs(ctx)\n return ctx.slots.register({\n name: 'settings.section',\n id: READING_PAGE_ID,\n order: READING_PAGE_ORDER,\n label,\n locale,\n inject: () => ({ readingTabs }),\n children: { 'reading.settings.item': { kind: 'list', scope: 'root' } },\n }, ReadingSettingsSection)\n}\n", "/** Locale bundles for the chat-translate card inside the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D page. */\n\n/** Dictionary namespace owned by this plugin's settings card. */\nexport const NS = 'settings.chatTranslate';\n\n/**\n * Chinese copy \u2014 the single source of truth for the key set. `{name}` markers\n * are either template params filled through `t(key, params)` / `.replace()`, or\n * inline technical samples rendered by `withSamples` at the call site.\n */\nexport const zh = {\n pageNav: '\u9605\u8BFB\u4F53\u9A8C',\n title: '\u6B63\u6587\u7FFB\u8BD1',\n\n masterTitle: '\u7FFB\u8BD1\u603B\u5F00\u5173',\n enableTranslation: '\u542F\u7528\u7FFB\u8BD1',\n masterDesc:\n '\u81EA\u52A8\u628A\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u7FFB\u6210\u4E2D\u6587\uFF08\u5982 {example}\uFF09\uFF1A\u6298\u53E0\u5757\u91CC\u7684\u8FC7\u7A0B\u6B63\u6587\u4E0E\u6700\u7EC8\u6C47\u603B\u4E00\u89C6\u540C\u4EC1\uFF0C\u6574\u6BB5\u4E2D\u6587\u7684\u5757\u4E5F\u7167\u9001\uFF0C\u7531\u6A21\u578B\u4E00\u5E76\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\u3001\u987A\u6389\u673A\u5668\u8154\u3002Think \u5361\u4E0E\u5DE5\u5177\u8C03\u7528\u884C\u4E0D\u7FFB\uFF0C\u4EE3\u7801\u5757\u539F\u6837\u4FDD\u7559\u3002\u7FFB\u8BD1\u8FC7\u7684\u5757\u5DE6\u7F18\u6709\u4E00\u6761\u7AD6\u7EBF\uFF1A\u663E\u793A\u8BD1\u6587\u65F6\u662F\u7A0D\u7C97\u7684\u84DD\u7EBF\uFF0C\u70B9\u56DE\u539F\u6587\u53D8\u7EC6\u7070\u7EBF\uFF08\u63D0\u793A\u8FD9\u91CC\u5907\u7740\u8BD1\u6587\uFF0C\u518D\u70B9\u5373\u5207\u56DE\uFF09\uFF0C\u6CA1\u8BD1\u6210\u7684\u6CA1\u6709\u7EBF\u2014\u2014\u7EBF\u7684\u989C\u8272\u4E0E\u7C97\u7EC6\u4E00\u773C\u5206\u6E05\u300C\u6709\u6CA1\u6709\u8BD1\u300D\u548C\u300C\u6B63\u5728\u8BFB\u54EA\u4E00\u7248\u300D\u3002\u70B9\u51FB\u5DF2\u7FFB\u8BD1\u7684\u5757\u53EF\u5728\u8BD1\u6587\u4E0E\u539F\u6587\u95F4\u5207\u6362\uFF0C\u6A21\u578B\u8BA4\u4E3A\u539F\u6837\u6700\u597D\u3001\u8FD4\u56DE\u4E00\u6A21\u4E00\u6837\u7684\u6587\u5B57\u65F6\u540C\u6837\u7B97\u7FFB\u8BD1\u8FC7\u3001\u540C\u6837\u53EF\u5207\u3002\u8BD1\u6587\u6309\u539F markdown \u683C\u5F0F\u91CD\u65B0\u6E32\u67D3\uFF0C\u8868\u683C\u3001\u5217\u8868\u3001\u52A0\u7C97\u4E0E\u884C\u5185\u4EE3\u7801\u4FDD\u6301\u7ED3\u6784\u3002\u56DE\u7B54\u8FD8\u5728\u6D41\u5F0F\u8F93\u51FA\u65F6\u4E0D\u7FFB\uFF0C\u843D\u5B9A\u540E\u6309\u9605\u8BFB\u987A\u5E8F\u9010\u6BB5\u51FA\u4E2D\u6587\u3002\u53EA\u4F5C\u7528\u4E8E\u5F53\u524D\u67E5\u770B\u7684\u4F1A\u8BDD\uFF0C\u8BD1\u6587\u4E0D\u5199\u56DE\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\u6709\u540C\u6B3E\u5F00\u5173\uFF0C\u4E24\u5904\u72B6\u6001\u540C\u6B65\u3002',\n\n aiTitle: 'AI \u7FFB\u8BD1\uFF08OpenAI \u517C\u5BB9\u534F\u8BAE\uFF09',\n aiRule: '\u53EA\u6709\u8FD9\u4E00\u6761\u901A\u9053\uFF1AKey / Base URL / \u6A21\u578B\u9F50\u5907\u624D\u4F1A\u7FFB\u8BD1\uFF0C\u7F3A\u4EFB\u4F55\u4E00\u9879\u90FD\u4FDD\u6301\u539F\u6587\u3002',\n badgeConfigured: '\u5DF2\u914D\u7F6E',\n badgeUnconfigured: '\u672A\u914D\u7F6E',\n\n apiKeyTitle: 'API Key',\n apiKeyDesc: '\u4FDD\u5B58\u81F3 {home} \u7684 {ref}\uFF0C\u4FDD\u5B58\u540E\u7ACB\u5373\u751F\u6548\uFF1B\u7559\u7A7A\u4FDD\u5B58 = \u6E05\u9664',\n apiKeyPlaceholderConfigured: '\u5DF2\u914D\u7F6E\uFF08\u5982\u9700\u66F4\u6362\u8BF7\u76F4\u63A5\u8F93\u5165\uFF09',\n save: '\u4FDD\u5B58',\n saving: '\u4FDD\u5B58\u4E2D\u2026',\n keySaved: '\u5DF2\u4FDD\u5B58\u5230 {home}\uFF0C\u7ACB\u5373\u751F\u6548',\n keyCleared: '\u5DF2\u6E05\u9664 API Key\uFF08\u672A\u914D\u7F6E\u65F6\u6B63\u6587\u4FDD\u6301\u539F\u6587\uFF0C\u4E0D\u4F1A\u7FFB\u8BD1\uFF09',\n saveFailed: '\u4FDD\u5B58\u5931\u8D25\uFF1A{error}',\n unknownError: '\u672A\u77E5\u9519\u8BEF',\n\n baseUrlTitle: 'Base URL',\n baseUrlDesc: '\u4EFB\u610F OpenAI \u517C\u5BB9\u670D\u52A1\u7AEF\u70B9\uFF0C\u5982 {url1}\u3001{url2}',\n modelTitle: '\u6A21\u578B',\n modelDesc: '\u5982 {model1}\u3001{model2}\uFF1B\u7559\u7A7A\u89C6\u4E3A\u672A\u914D\u7F6E',\n\n timeoutTitle: '\u5355\u6B21\u8BF7\u6C42\u8D85\u65F6',\n timeoutDesc: '\u5355\u4F4D\u6BEB\u79D2\uFF0C\u8303\u56F4 500-900000\u3002\u4E00\u6761\u957F\u56DE\u7B54\u53EF\u80FD\u8981\u51E0\u5206\u949F\uFF0C\u9ED8\u8BA4 600000\u3002',\n\n test: '\u6D4B\u8BD5 AI \u901A\u9053',\n testing: '\u6D4B\u8BD5\u4E2D\u2026',\n testOk: '\u8FDE\u63A5\u6B63\u5E38\uFF0C\u5EF6\u8FDF {latency}ms',\n testFail: '\u5931\u8D25\uFF1A{error}',\n\n behaviorTitle: '\u7FFB\u8BD1\u8303\u56F4',\n behaviorRule:\n '\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u4E00\u5F8B\u7FFB\u8BD1\uFF0C\u5305\u62EC\u6700\u7EC8\u6C47\u603B\u4E0E\u6574\u6BB5\u4E2D\u6587\u7684\u5757\uFF1A\u4E2D\u6587\u5757\u540C\u6837\u4EA4\u7ED9\u6A21\u578B\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\uFF0C\u610F\u4E49\u3001\u672F\u8BED\u4E0E\u6570\u5B57\u4E0D\u53D8\u3002\u601D\u8003\u94FE\u6B63\u6587\u3001\u5DE5\u5177\u8C03\u7528\u6807\u9898\u4E0E\u6298\u53E0\u6458\u8981\u4E0D\u7FFB\u8BD1\uFF0C\u4EE3\u7801\u5757\u53CA\u5176\u5185\u90E8\u539F\u6837\u4FDD\u7559\u3002',\n\n dockLabel: '\u8BD1',\n dockOn: '\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5F00\u542F\uFF0C\u70B9\u51FB\u5173\u95ED',\n dockOff: '\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5173\u95ED\uFF0C\u70B9\u51FB\u5F00\u542F',\n} as const;\n\n/** Locale keys this plugin renders. */\nexport type ChatTranslateLocaleKey = keyof typeof zh;\n\n/** English copy; the same key set as {@link zh}. */\nexport const en: Record<ChatTranslateLocaleKey, string> = {\n pageNav: 'Reading',\n title: 'Reply translate',\n\n masterTitle: 'Translation master switch',\n enableTranslation: 'Enable translation',\n masterDesc:\n 'Translates assistant reply prose (e.g. {example}) into Chinese: text inside disclosure blocks and the final summary are treated alike, and blocks written entirely in Chinese go through the model too, which rewrites them into natural Chinese and smooths out machine-flavored phrasing. Think cards and tool-call rows are never translated, and code blocks stay untouched. A translated block carries a left edge line: a slightly thicker blue one while its translation shows, a thin grey one while you read the original (a translation is waiting one click away), and none at all when nothing translated \u2014 colour and weight together tell at a glance both what exists and what you are reading. Click any translated block to switch between translation and original; even when the model decides the original wording is already best and returns it unchanged, it still counts as translated and stays switchable. Translations are re-rendered as Markdown, so tables, lists, bold runs and inline code keep their structure. A reply that is still streaming is left alone until it settles, then prose turns to Chinese paragraph by paragraph in reading order. Applies to the session you are viewing only; translations never enter the conversation context. The same switch sits on the row below the composer, sharing its state.',\n\n aiTitle: 'AI translation (OpenAI-compatible)',\n aiRule:\n 'This is the only channel: translation runs when Key, Base URL and model are all present; missing any one leaves the text as it is.',\n badgeConfigured: 'Configured',\n badgeUnconfigured: 'Not configured',\n\n apiKeyTitle: 'API Key',\n apiKeyDesc: 'Saved as {ref} in {home}; takes effect immediately. Saving an empty value clears it.',\n apiKeyPlaceholderConfigured: 'Configured (type a new value to replace it)',\n save: 'Save',\n saving: 'Saving\u2026',\n keySaved: 'Saved to {home}; takes effect immediately',\n keyCleared: 'API Key cleared (with nothing configured, replies stay in their original language)',\n saveFailed: 'Save failed: {error}',\n unknownError: 'Unknown error',\n\n baseUrlTitle: 'Base URL',\n baseUrlDesc: 'Any OpenAI-compatible endpoint, e.g. {url1} or {url2}',\n modelTitle: 'Model',\n modelDesc: 'e.g. {model1} or {model2}; empty counts as unconfigured',\n\n timeoutTitle: 'Per-request timeout',\n timeoutDesc: 'In milliseconds, range 500-900000. One long reply can take minutes; default 600000.',\n\n test: 'Test the AI channel',\n testing: 'Testing\u2026',\n testOk: 'Connected, {latency}ms latency',\n testFail: 'Failed: {error}',\n\n behaviorTitle: 'What gets translated',\n behaviorRule:\n 'All assistant reply prose, the final summary and Chinese-only blocks included: Chinese blocks are also handed to the model, which rewrites them into natural Chinese while keeping meaning, terms and numbers unchanged. Think-chain text, tool-call titles and folded summaries are never translated; code blocks and their contents stay untouched.',\n\n dockLabel: 'Translate',\n dockOn: 'Translation is on; click to turn it off',\n dockOff: 'Translation is off; click to turn it on',\n};\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'settings.chatTranslate': ChatTranslateLocaleKey;\n }\n}\n", "import * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';\nimport { Pill, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';\nimport { settingsStore, type ClientSettingsState } from '../settings/store.ts';\nimport { NS } from '../locales.ts';\n\n/**\n * `t` \u7531\u6CE8\u518C\u9879\u7684 `locale: NS` \u6CE8\u5165\uFF0C\u7C7B\u578B\u8D70\u5171\u4EAB\u7684 `PropsLocale`\u3002owner props\n * \uFF08`PropsRuntime<'conversation.composer.dock'>`\uFF09\u7684\u7C7B\u578B\u968F\n * `@deepseek-ai/dsh-client-ui-conversation` \u58F0\u660E\uFF0C\u672C\u5305\u4E0D\u4F9D\u8D56\u5B83\uFF08\u4E0E dsh-a6api\n * \u540C\u6CD5\uFF09\uFF0C\u7EC4\u4EF6\u4E5F\u4E0D\u8BFB\u90A3\u4E9B prop\u3002\n */\nexport type DockToggleProps = PropsLocale<typeof NS>;\n\n/**\n * \u5BF9\u8BDD\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\u7684\u7FFB\u8BD1\u5F00\u5173\uFF1A\u4E00\u4E2A\u80F6\u56CA\u6309\u94AE\uFF0C\u70B9\u4EAE\u8868\u793A\u6B63\u6587\u7FFB\u8BD1\u5F00\u7740\u3002\n * \u5B83\u4E0E\u8BBE\u7F6E\u9875\u300C\u603B\u5F00\u5173\u300D\u662F\u540C\u4E00\u4E2A `enabled` \u5B57\u6BB5\u2014\u2014\u70B9\u51FB\u5199 profile \u914D\u7F6E\uFF0C\u4E24\u5904\n * \u72B6\u6001\u7531\u540C\u4E00\u4E2A store \u540C\u6B65\uFF1B\u5173\u95ED\u4E0E\u91CD\u5F00\u7684\u884C\u4E3A\uFF08\u8FD8\u539F\u82F1\u6587\u3001\u91CD\u626B\u8865\u7FFB\uFF09\u4E5F\u8D70\n * \u540C\u4E00\u6761\u94FE\u8DEF\u3002Key \u6CA1\u914D\u9F50\u65F6\u7167\u5E38\u53EF\u5207\u6362\uFF1A\u7F3A\u914D\u7F6E\u53EA\u662F\u4E0D\u4EA7\u751F\u7FFB\u8BD1\uFF0C\u754C\u9762\u65E0\u7279\u6B8A\u6001\u3002\n */\nexport function ComposerTranslateToggle(props: DockToggleProps): ReactElement {\n const { t } = props;\n const [state, setState] = useState<ClientSettingsState>(() => settingsStore.getState());\n\n useEffect(() => settingsStore.subscribe(() => setState(settingsStore.getState())), []);\n\n return (\n <Tooltip label={() => (state.enabled ? t('dockOn') : t('dockOff'))} side=\"top\" align=\"end\" portal>\n <Pill\n active={state.enabled}\n onClick={() => void settingsStore.update({ enabled: !settingsStore.getState().enabled })}\n >\n {t('dockLabel')}\n </Pill>\n </Tooltip>\n );\n}\n\n/**\n * \u628A\u5F00\u5173\u6CE8\u518C\u8FDB `conversation.composer.dock`\u2014\u2014ui-conversation \u58F0\u660E\u7684\n * \u300C\u8F93\u5165\u6846\u5361\u7247\u4E0B\u65B9\u90A3\u4E00\u884C\u300D\u3002\u8BE5 slot \u53EA\u5728\u4F1A\u8BDD\u5F62\u6001\u6E32\u67D3\uFF0C\u65B0\u5EFA\u4F1A\u8BDD\u7684 Hero \u5F62\u6001\n * \u6CA1\u6709\u8FD9\u4E00\u884C\uFF0C\u6309\u94AE\u968F\u4E4B\u4E0D\u51FA\u73B0\u3002`slots.inject` \u7B49\u58F0\u660E\u843D\u5730\u518D\u6CE8\u518C\uFF0C\u58F0\u660E\u584C\u7F29\u65F6\n * \u6CE8\u518C\u4E00\u8D77\u9000\u573A\u3002\n * @param ctx - DSH browser client context\uFF08`slots` \u5DF2\u5728\u5BA2\u6237\u7AEF\u534A\u8FB9\u58F0\u660E\u6CE8\u5165\uFF09\u3002\n * @returns \u64A4\u9500\u672C\u6B21\u8D21\u732E\u7684 disposer\u3002\n */\nexport function setupComposerToggle(ctx: any): () => void {\n if (typeof window === 'undefined') return () => {};\n const slots = ctx?.slots;\n if (!slots || typeof slots.inject !== 'function') return () => {};\n return slots.inject('conversation.composer.dock', () =>\n slots.register(\n {\n name: 'conversation.composer.dock',\n id: 'dsh-chat-translate-toggle',\n // -2 \u6392\u5728 dsh-a6api \u80F6\u56CA\uFF08-1\uFF09\u7684\u5DE6\u4FA7\uFF0C\u6574\u884C\u6700\u5DE6\uFF1B\u5B98\u65B9\u7EDF\u8BA1\u80F6\u56CA\u662F 0\u3002\n order: -2,\n locale: NS,\n },\n ComposerTranslateToggle\n )\n );\n}\n", "/**\n * \u52A9\u624B\u884C\u6E32\u67D3\u5668\uFF1A\u7ECF keyed slot `conversation.chat.node`\uFF08key `assistant-step`\uFF09\n * \u66FF\u6362\u5BBF\u4E3B\u9ED8\u8BA4\u7684\u884C\u6E32\u67D3\u3002\n *\n * \u63A5\u7BA1\u6362\u6765\u4E24\u4EF6\u4E8B\u2014\u2014\n * 1. \u6B63\u6587\u5757\u4ECE\u6570\u636E\u5C42\u62FF**\u539F\u59CB markdown**\uFF08`AssistantBlock.text`\uFF09\uFF0C\u8BD1\u6587\u540C\u6837\u6309\n * markdown \u4EA4\u7ED9\u5BBF\u4E3B\u516C\u5F00\u57FA\u7EBF\u7EC4\u4EF6 `MarkdownText` \u91CD\u6E32\u67D3\uFF1A\u683C\u5F0F\u4FDD\u771F\u7531\u5B98\u65B9\n * \u6E32\u67D3\u5668\u8D1F\u8D23\uFF0C\u672C\u6587\u4EF6\u4E0D\u4EA7\u751F\u4EFB\u4F55\u81EA\u62FC markup\u3002\n * 2. \u300C\u5DF2\u7FFB\u8BD1\u300D\u662F\u6E32\u67D3\u72B6\u6001\uFF1A\u843D\u5B9A\u3001\u5F00\u5173\u5F00\u542F\u3001\u8FDB\u5165\u89C6\u53E3\u540E\u9010\u884C\u8BF7\u6C42\uFF1B\u6210\u529F\u7684\u5757\n * \uFF08\u5305\u62EC\u6A21\u578B\u8BA4\u4E3A\u539F\u6837\u6700\u597D\u7684\u5757\uFF09\u6302\u8BD1\u6587\u3001\u5DE6\u7F18\u4E00\u6761\u7EC6\u7EBF\u3001\u70B9\u51FB\u5728\u8BD1\u6587\u4E0E\u539F\u6587\u95F4\n * \u5207\u6362\uFF1B\u5931\u8D25\u7684\u5757\u4FDD\u6301\u539F\u6587\u3001\u65E0\u6807\u8BB0\uFF0C\u4E0B\u4E00\u6B21\u6761\u4EF6\u6EE1\u8DB3\u81EA\u7136\u91CD\u8BD5\u3002\n *\n * \u9664\u6B63\u6587\u5916\u7684\u884C\u5185\u5BB9\u4E0E\u5BBF\u4E3B\u9010\u5206\u652F\u7B49\u4EF7\uFF1Areasoning \u884C\u8D70\u6298\u53E0\uFF08\u542B Turn-process\n * \u9690\u85CF\u4E0E beforematch \u63ED\u793A\uFF09\u3001\u8FDE\u7EED image \u7EC4\u4EA4\u56DE owner \u7684 renderMessageImages\u3001\n * \u672A\u77E5\u5757\u843D JsonBlock\u3001tool-call \u5728\u52A9\u624B\u884C\u5185\u8DF3\u8FC7\uFF08\u5DE5\u5177\u884C\u7531\u5BBF\u4E3B\u72EC\u7ACB\u6E32\u67D3\uFF09\u3001\n * \u4E2D\u65AD\u524D\u7F00\u884C\u5C3E\u5E26\u300C\u5DF2\u505C\u6B62\u300D\u3002props \u5F62\u72B6\u662F\u5BBF\u4E3B contract \u7684\u7ED3\u6784\u6027\u955C\u50CF\uFF1A\u503C\u8FB9\n * \u4E0D\u8DE8\u5305\uFF08\u5BA2\u6237\u7AEF bundle \u7EAF\u51C0\u95E8\u7981\uFF09\uFF0C\u7C7B\u578B\u6CE8\u91CA\u6307\u660E\u5BF9\u5E94\u58F0\u660E\u3002\n * \u672C\u6E32\u67D3\u5668\u629B\u9519\u65F6\u6309 keyed slot \u7684\u9000\u4F4D\u8BED\u4E49\u9000\u51FA\u8BE5 cell\uFF0C\u5BBF\u4E3B\u539F\u6E32\u67D3\u5668\u56DE\u4F4D\u3002\n */\nimport * as React from 'react';\nimport { memo, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n DisclosureRow,\n IconThinkOutlineRegular,\n JsonBlock,\n MarkdownText,\n} from '@deepseek-ai/dsh-client-ui-primitives';\nimport type {\n MarkdownCodeLabels,\n MarkdownFileMentions,\n MarkdownLabels,\n MarkdownPathImages,\n} from '@deepseek-ai/dsh-client-ui-primitives';\nimport { settingsStore } from '../settings/store.ts';\nimport { chatTranslate } from './translate-store.ts';\nimport { planAssistantRow } from './row-plan.ts';\nimport { isBareBlockClick } from './click-guard.ts';\nimport type { AssistantBlockLike } from './row-plan.ts';\nimport { ensureAssistantStyles, proseClassNames } from './styles.ts';\n\n// ---- \u5BBF\u4E3B contract \u7684\u7ED3\u6784\u6027\u955C\u50CF\uFF08source of truth: ui-chat slots.d.ts\uFF09 ----\n\ninterface DisclosureState {\n readonly expanded: boolean;\n readonly setExpanded: (open: boolean) => void;\n readonly toggle: () => void;\n}\n\ninterface PresentationLike {\n readonly settledReasoningPreview: boolean;\n}\n\ninterface NodeDataLike {\n readonly status: string;\n readonly turn: number;\n readonly step: number;\n readonly blocks: readonly AssistantBlockLike[];\n readonly finalNode?: { readonly seq: number };\n}\n\ninterface TurnProcessLike {\n readonly foldable: boolean;\n readonly open: boolean;\n readonly spec: { readonly answerStep: number; readonly inlineReasoning: boolean };\n readonly setOpen: (open: boolean) => void;\n}\n\nexport interface AssistantStepProps {\n readonly node: {\n readonly data: NodeDataLike;\n readonly location?: { readonly kind?: string; readonly turn?: { readonly status?: string } };\n readonly anchorSeq?: number;\n };\n readonly groupPart?: string;\n readonly useDisclosure: () => DisclosureState;\n readonly useTurnData: (key: string) => unknown;\n readonly turnProcess?: TurnProcessLike;\n readonly openFile: (path: string, options?: { line?: number }) => void;\n readonly renderMessageImages: (owner: {\n readonly images: readonly { readonly attachment: unknown }[];\n readonly align?: string;\n }) => ReactNode;\n readonly fileMentions: (owner: unknown) => MarkdownFileMentions | undefined;\n readonly usePresentation: <R>(select: (policy: PresentationLike) => R) => R;\n readonly t: (key: string, params?: Record<string, unknown>) => string;\n}\n\n// ---- \u4E0E\u5BBF\u4E3B\u9010\u5B57\u5BF9\u9F50\u7684\u5C0F\u4EF6 ----\n\n// \u56FE\u6807\u5728\u6E32\u67D3\u65F6\u521B\u5EFA\uFF0C\u6A21\u5757\u9876\u5C42\u4E0D\u89E6\u78B0\u8FD0\u884C\u65F6\u7EC4\u4EF6\u3002\nconst thinkIcon = (): ReactElement => React.createElement(IconThinkOutlineRegular, { size: 14 });\nconst NOOP = (): void => {};\n\nfunction markdownLabels(t: (key: string, params?: Record<string, unknown>) => string): MarkdownLabels {\n return {\n code: {\n copyLabel: t('copy'),\n copiedLabel: t('copied'),\n toolbarLabels: {\n codeLabel: t('codeBlock.title'),\n wrapLabel: t('codeBlock.wrap'),\n unwrapLabel: t('codeBlock.unwrap'),\n },\n } as MarkdownCodeLabels,\n footnotes: t('markdown.footnotes'),\n };\n}\n\nfunction isWindowsStylePath(value: string): boolean {\n return /^[A-Za-z]:[/\\\\]/.test(value) || value.startsWith('\\\\\\\\');\n}\n\nfunction isAbsoluteWorkspacePath(path: string): boolean {\n return path.startsWith('/') || isWindowsStylePath(path);\n}\n\n/** \u672C\u5730\u7EDD\u5BF9\u8DEF\u5F84\u7ECF\u8BA4\u8BC1\u7684 file \u8DEF\u7531\u5BFB\u5740\uFF08\u5BBF\u4E3B fileMediaUrl \u7B49\u4EF7\uFF09\u3002 */\nfunction fileMediaUrl(base: string, path: string): string | undefined {\n if ((!/^https?:/u.test(base) && !base.startsWith('dsh-app://app/')) || !isAbsoluteWorkspacePath(path)) return void 0;\n if (/^[/\\\\]{2}/u.test(path) || /[\\u0000-\\u001f\\u007f]/u.test(path)) return void 0;\n return new URL(`api/file?path=${encodeURIComponent(path)}`, base).href;\n}\n\nfunction localPathMediaUrl(base: string, value: string): string | undefined {\n let path: string;\n try {\n path = decodeURIComponent(value.split(/[?#]/u)[0] ?? '');\n } catch {\n return void 0;\n }\n return fileMediaUrl(base, path);\n}\n\nfunction firstLine(text: string): string {\n const newline = text.indexOf('\\n');\n return newline === -1 ? text : text.slice(0, newline);\n}\n\nfunction latestCompletedParagraphFirstLine(text: string): string {\n let summary = '';\n let paragraphStart = 0;\n const separator = /\\r?\\n(?:[\\t ]*\\r?\\n)+/g;\n for (;;) {\n const nextParagraph = separator.exec(text);\n const paragraphEnd = nextParagraph === null ? text.length : nextParagraph.index + nextParagraph[0].indexOf('\\n');\n const newline = text.indexOf('\\n', paragraphStart);\n if (newline !== -1 && newline <= paragraphEnd) {\n const candidate = text.slice(paragraphStart, newline).trim();\n if (candidate !== '') summary = candidate;\n }\n if (nextParagraph === null) return summary;\n paragraphStart = nextParagraph.index + nextParagraph[0].length;\n }\n}\n\n/** \u6298\u53E0\u7684\u9690\u85CF\u8282\u70B9\u5BF9\u9875\u5185\u641C\u7D22\u4FDD\u6301\u53EF\u8FBE\uFF1A\u547D\u4E2D\u524D\u7528 beforematch \u63ED\u793A\uFF08\u5BBF\u4E3B\u7B49\u4EF7\uFF09\u3002 */\nfunction useSearchableHidden(hidden: boolean, reveal: () => void): React.RefObject<HTMLDivElement | null> {\n const ref = React.useRef<HTMLDivElement | null>(null);\n React.useLayoutEffect(() => {\n const element = ref.current;\n if (element === null) return;\n if (hidden && element.contains(element.ownerDocument.activeElement)) {\n reveal();\n return;\n }\n if (hidden) element.setAttribute('hidden', 'until-found');\n else element.removeAttribute('hidden');\n }, [hidden, reveal]);\n React.useEffect(() => {\n const element = ref.current;\n if (element === null) return;\n element.addEventListener('beforematch', reveal);\n return () => {\n element.removeEventListener('beforematch', reveal);\n };\n }, [reveal]);\n return ref;\n}\n\nconst subscribeSettings = (listener: () => void): (() => void) => settingsStore.subscribe(listener);\nconst subscribeTranslate = (listener: () => void): (() => void) => chatTranslate.subscribe(listener);\n\n/**\n * \u884C\u7684\u89C6\u53E3\u5728\u573A\u72B6\u6001\uFF08150px \u7F13\u51B2\uFF09\uFF1A\u8FDB\u5165\u89C6\u53E3\u624D\u653E\u884C\u7FFB\u8BD1\u8BF7\u6C42\u3002\n * \u4E0D\u505A\u4E00\u6B21\u6027\u9501\u6B7B\u2014\u2014\u79BB\u5F00\u518D\u56DE\u6765\u4F1A\u628A\u503C\u7FFB\u56DE false\u2192true\uFF0C\u7ED9 partial \u884C\u7684\n * \u300C\u81EA\u7136\u91CD\u8BD5\u300D\u63D0\u4F9B\u89E6\u53D1\u70B9\uFF1Bdone \u884C\u7684 ensure \u672C\u5C31\u6709\u540C\u4EE3\u77ED\u8DEF\uFF0C\u91CD\u590D\u7FFB\u52A8\u96F6\u6210\u672C\u3002\n */\nfunction useSettledInView(ref: React.RefObject<HTMLElement | null>, armed: boolean): boolean {\n const noObserver = typeof IntersectionObserver === 'undefined';\n const [inView, setInView] = useState<boolean>(noObserver);\n useEffect(() => {\n if (noObserver || !armed) return;\n const element = ref.current;\n if (element === null) return;\n const observer = new IntersectionObserver(\n (entries) => {\n setInView(entries.some((entry) => entry.isIntersecting));\n },\n { rootMargin: '150px' }\n );\n observer.observe(element);\n return () => {\n observer.disconnect();\n };\n }, [armed, noObserver, ref]);\n return inView;\n}\n\n// ---- \u601D\u8003\u884C\uFF08\u5BBF\u4E3B ReasoningRow \u7B49\u4EF7\uFF0C\u7C7B\u540D\u6362\u672C\u63D2\u4EF6\u524D\u7F00\uFF09 ----\n\ninterface ReasoningRowProps {\n text: string;\n running: boolean;\n usePresentation: <R>(select: (policy: PresentationLike) => R) => R;\n useDisclosure: () => DisclosureState;\n t: (key: string, params?: Record<string, unknown>) => string;\n}\n\nconst ReasoningRow = memo(function ReasoningRow({\n text,\n running,\n usePresentation,\n useDisclosure,\n t,\n}: ReasoningRowProps): ReactElement {\n const { expanded, toggle } = useDisclosure();\n const labels = useMemo(() => markdownLabels(t), [t]);\n const summaryText = running ? latestCompletedParagraphFirstLine(text) : firstLine(text);\n const summary = useMemo(() => summaryText.replaceAll('**', ''), [summaryText]);\n const preview = usePresentation(\n (policy) => !expanded && summary !== '' && (running || policy.settledReasoningPreview)\n );\n const collapsedContent = useMemo(\n () =>\n React.createElement(\n React.Fragment,\n null,\n React.createElement('span', { className: 'dsh-ct-think-separator', 'aria-hidden': true }),\n React.createElement(\n 'span',\n { className: 'dsh-ct-think-summary', 'data-streaming': running || void 0 },\n React.createElement('span', { className: 'dsh-ct-think-summary-text' }, summary)\n )\n ),\n [running, summary]\n );\n const content = useMemo(\n () =>\n expanded\n ? React.createElement(\n 'div',\n { className: 'dsh-ct-think-body' },\n React.createElement(MarkdownText, { text, streaming: running, labels, variant: 'compact' })\n )\n : void 0,\n [expanded, labels, running, text]\n );\n return React.createElement(\n 'div',\n {\n className: 'dsh-ct-think-root',\n 'data-variant': 'think',\n 'data-state': running ? 'running' : 'ok',\n 'data-expanded': expanded || void 0,\n 'data-preview': preview || void 0,\n },\n running && React.createElement('span', { className: 'dsh-ct-visually-hidden' }, t('row.running')),\n React.createElement(\n DisclosureRow,\n {\n rowClassName: 'dsh-ct-think-row',\n leadingClassName: 'dsh-ct-think-leading',\n titleClassName: 'dsh-ct-think-title',\n chevronClassName: 'dsh-ct-think-chevron',\n icon: thinkIcon(),\n title: t('message.think'),\n open: expanded,\n expandable: true,\n expandOnRowClick: true,\n onToggle: toggle,\n collapsedContent,\n },\n content\n )\n );\n});\n\n// ---- \u6B63\u6587\u5757\uFF1A\u539F\u6587/\u8BD1\u6587\u4E00\u4E2A\u58F3\u91CC\u5207\u6362 ----\n\ninterface ProseBlockProps {\n text: string;\n /** null = \u6682\u65E0\u53EF\u7528\u8BD1\u6587\uFF08\u672A\u843D\u5B9A\u6216\u5931\u8D25\uFF09\uFF0C\u663E\u793A\u539F\u6587\u3001\u65E0\u6807\u8BB0\u3002 */\n translated: string | null;\n showOriginal: boolean;\n onToggle: () => void;\n streaming: boolean;\n labels: MarkdownLabels;\n mentions: MarkdownFileMentions | undefined;\n pathImages: MarkdownPathImages;\n}\n\n/**\n * \u5207\u6362\u5224\u5B9A\uFF1A\u843D\u5728\u5757\u5185\u4EA4\u4E92\u5143\u7D20\uFF08\u94FE\u63A5\u3001\u4EE3\u7801\u5757\u590D\u5236\u94AE\u7B49\uFF09\u4E0A\u7684\u70B9\u51FB\u5F52\u90A3\u4E2A\u5143\u7D20\uFF0C\n * \u4E0D\u8FDE\u5E26\u5207\u5757\uFF1B\u62D6\u9009\u8BD1\u6587\u677E\u624B\u4EA7\u751F\u7684 click \u540C\u6837\u5FFD\u7565\u3002\n */\nfunction blockClick(event: React.MouseEvent<HTMLDivElement>, onToggle: () => void): void {\n const guardEvent = {\n target: event.target as { closest?(selector: string): unknown } | null,\n currentTarget: event.currentTarget,\n };\n if (isBareBlockClick(guardEvent, typeof window === 'undefined' ? undefined : window)) onToggle();\n}\n\nfunction ProseBlock({\n text,\n translated,\n showOriginal,\n onToggle,\n streaming,\n labels,\n mentions,\n pathImages,\n}: ProseBlockProps): ReactElement {\n const processed = translated !== null;\n const showTranslation = processed && !showOriginal;\n const source = showTranslation ? (translated as string) : text;\n return React.createElement(\n 'div',\n {\n // \u8272\u76F8+\u7C97\u7EC6\u968F\u663E\u793A\u6001\uFF1A\u84DD\u7C97\u7EBF=\u8BD1\u6587\uFF0C\u7070\u7EC6\u7EBF=\u6709\u8BD1\u6587\u4F46\u8BFB\u539F\u6587\uFF08\u4ECD\u53EF\u70B9\u5207\u56DE\uFF09\uFF0C\u65E0\u7EBF=\u6CA1\u8BD1\u3002\n className: proseClassNames(processed, showOriginal),\n 'data-translated': showTranslation ? 'true' : void 0,\n // \u5BB9\u5668\u5185\u542B\u94FE\u63A5\u7B49\u4EA4\u4E92\u5185\u5BB9\uFF0C\u4E0D\u5957 role=button\uFF08\u975E\u6CD5\u5D4C\u5957\uFF09\uFF1B\u53EF\u805A\u7126 + \u952E\u76D8\n // Enter/Space \u5373\u5207\u6362\uFF0C\u6EE1\u8DB3\u5B98\u65B9\u300C\u952E\u76D8\u53EF\u8FBE\u300D\u95E8\u3002\n tabIndex: processed ? 0 : void 0,\n onClick: processed\n ? (event: React.MouseEvent<HTMLDivElement>) => blockClick(event, onToggle)\n : void 0,\n onKeyDown: processed\n ? (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n onToggle();\n }\n }\n : void 0,\n },\n React.createElement(MarkdownText, {\n text: source,\n streaming,\n labels,\n fileMentions: mentions,\n pathImages,\n })\n );\n}\n\n// ---- \u52A9\u624B\u884C\u672C\u4F53 ----\n\nexport const AssistantStepView = memo(function AssistantStepView(props: AssistantStepProps): ReactElement | null {\n ensureAssistantStyles();\n const { node, groupPart, useDisclosure, useTurnData, turnProcess, openFile, renderMessageImages, fileMentions, usePresentation, t } = props;\n const data = node.data;\n const streaming = data.status === 'running';\n const interrupted = data.status === 'interrupted';\n\n const enabled = useSyncExternalStore(subscribeSettings, () => settingsStore.getState().enabled);\n const aiConfigured = useSyncExternalStore(subscribeSettings, () => settingsStore.getState().aiConfigured);\n useSyncExternalStore(subscribeTranslate, () => chatTranslate.getVersion());\n\n const canTranslate = enabled && aiConfigured;\n const rowKey = `a${node.anchorSeq ?? `t${data.turn}:s${data.step}`}`;\n\n const labels = useMemo(() => markdownLabels(t), [t]);\n const pathImages = useMemo<MarkdownPathImages>(\n () => ({ resolve: (value: string) => localPathMediaUrl(document.baseURI, value) }),\n []\n );\n\n // mentions \u7684 owner \u5224\u5B9A\u4E0E\u5BBF\u4E3B\u9010\u884C\u7B49\u4EF7\uFF1A\u5C01\u95ED Turn \u7684\u6536\u5C3E\u6D88\u606F\u624D\u6709\u6587\u4EF6\u63D0\u53CA\u8BCD\u8868\u3002\n const turn = node.location?.kind === 'turn' || node.location?.kind === 'step' ? node.location.turn : undefined;\n const tail = useTurnData('turn-tail') as\n | { closing?: { finalNode?: { seq: number } } }\n | undefined;\n const owner = useMemo(() => {\n if (turn?.status !== 'closed' || data.finalNode === void 0) return void 0;\n if (tail?.closing?.finalNode?.seq !== data.finalNode.seq) return void 0;\n return { turn, seq: data.finalNode.seq, openFile };\n }, [data.finalNode, openFile, tail, turn]);\n const mentions = useMemo(() => (owner === void 0 ? void 0 : fileMentions(owner)), [fileMentions, owner]);\n\n // \u8BA1\u5212\u5C42\u7ED9\u51FA\u6587\u672C\u6E05\u5355\u4E0E\u6E32\u67D3\u5206\u652F\uFF1B\u884C\u6E32\u67D3\u5148\u4E8E\u6302\u8F7D\uFF0C\u6C60\u6309\u5F53\u524D\u6587\u672C\u767B\u8BB0\u3002\n const blocks = useMemo(() => data.blocks ?? [], [data.blocks]);\n const plan = useMemo(\n () =>\n planAssistantRow({\n blocks,\n streaming,\n interrupted,\n groupPart,\n canTranslate,\n outcomes: undefined,\n }),\n [blocks, streaming, interrupted, groupPart, canTranslate]\n );\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const inView = useSettledInView(rootRef, !streaming && canTranslate && plan.entries !== null && plan.texts.length > 0);\n\n useEffect(() => {\n if (streaming || !canTranslate || !inView || plan.entries === null || plan.texts.length === 0) return;\n chatTranslate.ensure(rowKey, plan.texts);\n }, [streaming, canTranslate, inView, rowKey, plan]);\n\n const rowState = chatTranslate.getState(rowKey);\n const displayed = useMemo(\n () =>\n planAssistantRow({\n blocks,\n streaming,\n interrupted,\n groupPart,\n canTranslate,\n outcomes: rowState?.outcomes,\n }),\n [blocks, streaming, interrupted, groupPart, canTranslate, rowState]\n );\n\n const reasoningHidden =\n turnProcess !== void 0 &&\n turnProcess.foldable &&\n turnProcess.spec.answerStep === data.step &&\n turnProcess.spec.inlineReasoning &&\n !turnProcess.open;\n const revealProcess = useCallback(() => {\n turnProcess?.setOpen(true);\n }, [turnProcess]);\n\n const [originalKeys, setOriginalKeys] = useState<ReadonlySet<number>>(() => new Set());\n const toggleBlock = useCallback((key: number) => {\n setOriginalKeys((current) => {\n const next = new Set(current);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n }, []);\n\n // \u65E9\u9000\u5728\u6240\u6709 hook \u4E4B\u540E\uFF1A\u884C\u5F62\u6001\u5728 null \u4E0E\u53EF\u89C1\u4E4B\u95F4\u5207\u6362\u65F6 hook \u5E8F\u5217\u4E0D\u53D8\u3002\n if (displayed.entries === null) return null;\n\n const last = blocks.length - 1;\n const rendered: ReactNode[] = [];\n for (const entry of displayed.entries) {\n switch (entry.type) {\n case 'prose':\n rendered.push(\n React.createElement(ProseBlock, {\n key: entry.key,\n text: entry.text,\n translated: entry.translated,\n showOriginal: originalKeys.has(entry.key),\n onToggle: () => toggleBlock(entry.key),\n streaming,\n labels,\n mentions,\n pathImages,\n })\n );\n break;\n case 'reasoning':\n rendered.push(\n React.createElement(\n ProcessReasoning,\n {\n key: entry.key,\n hidden: reasoningHidden,\n reveal: revealProcess,\n children: React.createElement(ReasoningRow, {\n text: entry.text,\n running: streaming && entry.blockIndex === last,\n usePresentation,\n useDisclosure,\n t,\n }),\n }\n )\n );\n break;\n case 'images':\n rendered.push(\n React.createElement(React.Fragment, {\n key: entry.key,\n }, renderMessageImages({ images: entry.attachments.map((attachment) => ({ attachment })), align: 'start' }))\n );\n break;\n case 'unknown':\n rendered.push(\n React.createElement(JsonBlock, {\n key: entry.key,\n label: t('message.unknownBlock'),\n payload: entry.block,\n truncatedLabel: (total: number) => t('json.truncated', { total }),\n })\n );\n break;\n case 'stopped':\n rendered.push(React.createElement('span', { key: entry.key, className: 'dsh-ct-stopped' }, t('message.stopped')));\n break;\n }\n }\n\n return React.createElement(\n 'div',\n { ref: rootRef, className: 'dsh-ct-root', 'data-streaming': streaming || void 0 },\n React.createElement('div', { className: 'dsh-ct-body' }, rendered)\n );\n});\n\nfunction ProcessReasoning(props: {\n hidden: boolean;\n reveal: () => void;\n children: ReactNode;\n}): ReactElement {\n const ref = useSearchableHidden(props.hidden, props.reveal);\n return React.createElement(\n 'div',\n { ref, 'data-turn-process-inline': props.hidden || void 0 },\n props.children\n );\n}\n", "import { randomTokenId } from './mask-tokens.ts';\n\n/**\n * \u56DE\u7B54\u6B63\u6587\u7FFB\u8BD1\u7684\u8BF7\u6C42\u9884\u7B97\u3002\u6BCF\u6B21\u8BF7\u6C42\u7684\u8F93\u5165\u4F30\u7B97 token \u4E0A\u9650\uFF0C\u4E0E\u672C\u5730\u670D\u52A1\u7684\n * prefill \u6279\u6B21\uFF08MAX_NUM_BATCHED_TOKENS\uFF09\u4FDD\u6301\u540C\u4E00\u91CF\u7EA7\uFF1B\u8F93\u51FA\u4E0A\u9650\u5199\u8FDB\u8BF7\u6C42\u4F53\u7684\n * max_tokens\uFF0C\u907F\u514D\u957F\u6587\u672C\u628A\u5269\u4F59\u4E0A\u4E0B\u6587\u5168\u90E8\u82B1\u5728\u751F\u6210\u4E0A\u3002\n */\nexport const REPLY_MAX_INPUT_TOKENS = 4096;\nexport const REPLY_MAX_OUTPUT_TOKENS = 8192;\n\n/** \u6C49\u5B57\u4E0E\u5168\u89D2\u6807\u70B9\u6309\u4E00\u5B57\u4E00 token \u8BA1\u3002 */\nconst CJK_CHAR = /[\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\u3000-\\u303f\\uff00-\\uffef]/;\n\n/**\n * \u4FDD\u5B88\u4F30\u7B97\u4E00\u6BB5\u6587\u672C\u5360\u7528\u7684 token \u6570\uFF1A\u6C49\u5B57\u4E00\u5B57\u4E00 token\uFF0C\u5176\u4F59\u6BCF\u4E09\u4E2A\u5B57\u7B26\u4E00 token\u3002\n * \u5B9E\u6D4B\u672C\u673A hy-mt2-1.8b \u7684\u82F1\u6587\u8BED\u6599\u7EA6\u4E3A\u6BCF 3.3 \u5B57\u7B26\u4E00 token\uFF0C\u8FD9\u91CC\u53D6 3 \u4F5C\u4E3A\u4E0A\u754C\uFF0C\n * \u5B81\u53EF\u628A\u8BF7\u6C42\u5207\u5F97\u6BD4\u5B9E\u9645\u9700\u8981\u66F4\u788E\uFF0C\u4E5F\u4E0D\u8BA9\u670D\u52A1\u7AEF\u6536\u5230\u8D85\u51FA\u9884\u671F\u7684 prefill\u3002\n */\nexport function estimateTokens(text: string): number {\n let cjk = 0;\n for (const char of text) {\n if (CJK_CHAR.test(char)) cjk += 1;\n }\n return cjk + Math.ceil((text.length - cjk) / 3);\n}\n\n/** \u6309\u6355\u83B7\u7EC4\u5206\u9694\u7B26\u5207\u5206\uFF0C\u5E76\u8BA9\u5206\u9694\u7B26\u7559\u5728\u524D\u4E00\u4E2A\u7247\u6BB5\u672B\u5C3E\u3002 */\nfunction splitKeepingSeparator(text: string, pattern: RegExp): string[] {\n const parts = text.split(pattern);\n const chunks: string[] = [];\n for (let index = 0; index < parts.length; index += 2) {\n const content = parts[index] ?? '';\n const separator = parts[index + 1] ?? '';\n if (content === '' && separator === '') continue;\n chunks.push(content + separator);\n }\n return chunks;\n}\n\n/** \u6309\u53E5\u672B\u6807\u70B9\u5207\u5206\uFF0C\u4F5C\u4E3A\u7A7A\u884C\u4E0E\u5355\u884C\u90FD\u5207\u4E0D\u5F00\u65F6\u7684\u6700\u540E\u4E00\u6863\u3002 */\nfunction sentenceChunks(text: string): string[] {\n const chunks = text.match(/[^.!?\u3002\uFF01\uFF1F\\n]+[.!?\u3002\uFF01\uFF1F]*[ \\t]*\\n?/g);\n return chunks === null ? [text] : chunks.filter((chunk) => chunk.length > 0);\n}\n\n/** \u5355\u5757\u5185\u90E8\u627E\u4E0D\u5230\u81EA\u7136\u8FB9\u754C\u65F6\u7684\u515C\u5E95\uFF1A\u6309\u4F30\u7B97 token \u786C\u5207\u3002 */\nfunction hardSlice(text: string, maxTokens: number): string[] {\n const out: string[] = [];\n let current = '';\n let tokens = 0;\n for (const char of text) {\n const cost = CJK_CHAR.test(char) ? 1 : 1 / 3;\n if (current !== '' && tokens + cost > maxTokens) {\n out.push(current);\n current = '';\n tokens = 0;\n }\n current += char;\n tokens += cost;\n }\n if (current !== '') out.push(current);\n return out;\n}\n\n/**\n * \u628A\u4E00\u4E2A\u5757\u5207\u6210\u4E0D\u8D85\u8FC7\u4E0A\u9650\u7684\u7247\u6BB5\uFF1A\u4F18\u5148\u7A7A\u884C\uFF0C\u5176\u6B21\u5355\u884C\uFF0C\u518D\u6B21\u53E5\u672B\uFF0C\u6700\u540E\u786C\u5207\u3002\n * \u7247\u6BB5\u6309\u539F\u987A\u5E8F\u62FC\u56DE\u5373\u4E3A\u8BE5\u5757\u7684\u5B8C\u6574\u8BD1\u6587\u3002\n */\nexport function splitOversizedBlock(\n text: string,\n maxTokens: number = REPLY_MAX_INPUT_TOKENS\n): string[] {\n if (estimateTokens(text) <= maxTokens) return [text];\n\n let chunks = splitKeepingSeparator(text, /(\\n{2,})/);\n if (chunks.length <= 1) chunks = splitKeepingSeparator(text, /(\\n)/);\n if (chunks.length <= 1) chunks = sentenceChunks(text);\n\n const out: string[] = [];\n let current = '';\n for (const chunk of chunks) {\n if (estimateTokens(chunk) > maxTokens) {\n if (current !== '') {\n out.push(current);\n current = '';\n }\n out.push(...hardSlice(chunk, maxTokens));\n continue;\n }\n if (current !== '' && estimateTokens(current + chunk) > maxTokens) {\n out.push(current);\n current = '';\n }\n current += chunk;\n }\n if (current !== '') out.push(current);\n return out;\n}\n\n/** \u4E00\u4E2A\u5F85\u7FFB\u8BD1\u7684\u7247\u6BB5\uFF1A\u5C5E\u4E8E\u54EA\u4E2A\u5757\u3001\u5757\u5185\u7B2C\u51E0\u6BB5\u3001\u4EE5\u53CA\u8BE5\u7247\u6BB5\u7684\u63A9\u7801\u7ED3\u679C\u3002 */\nexport interface ReplyPieceShell<TMask> {\n block: number;\n index: number;\n text: string;\n mask: TMask;\n}\n\n/** \u628A\u7247\u6BB5\u6309\u539F\u987A\u5E8F\u6253\u5305\u6210\u5C3D\u91CF\u5C11\u7684\u8BF7\u6C42\uFF0C\u4E14\u6BCF\u6279\u4E0D\u8D85\u8FC7\u8F93\u5165\u4E0A\u9650\u3002 */\nexport function packPieces<T extends { text: string }>(\n pieces: T[],\n maxTokens: number = REPLY_MAX_INPUT_TOKENS\n): T[][] {\n const batches: T[][] = [];\n let current: T[] = [];\n let tokens = 0;\n for (const piece of pieces) {\n const cost = estimateTokens(piece.text);\n if (current.length > 0 && tokens + cost > maxTokens) {\n batches.push(current);\n current = [];\n tokens = 0;\n }\n current.push(piece);\n tokens += cost;\n }\n if (current.length > 0) batches.push(current);\n return batches;\n}\n\n/**\n * \u5757\u5206\u9694\u6807\u8BB0\uFF1A`\u27EA<4 letters><index>\u27EB`\u3002\u5916\u62EC\u53F7\u523B\u610F\u4E0E\u63A9\u7801\u5360\u4F4D\u7B26\u7684 \u27E6\u27E7 \u4E0D\u540C\u5F62\uFF0C\n * \u63A9\u7801\u6B8B\u7559\u68C0\u6D4B\uFF08\u53EA\u8BA4 \u27E6\u27E7\uFF09\u56E0\u6B64\u4E0D\u4F1A\u628A\u8FD9\u4E2A\u6807\u8BB0\u5F53\u6210\u6CC4\u6F0F\u3002\n */\nexport interface BatchFormat {\n id: string;\n token: (index: number) => string;\n}\n\nexport function createBatchFormat(): BatchFormat {\n const id = randomTokenId();\n return {\n id,\n token: (index: number) => `\u27EA${id}${index}\u27EB`,\n };\n}\n\n/** \u6807\u8BB0\u51FA\u73B0\u5728\u88AB\u7FFB\u8BD1\u6587\u672C\u91CC\u7684\u901A\u7528\u5F62\u72B6\uFF0C\u4E0D\u95EE id\u3002 */\nconst BATCH_TOKEN_PATTERN_SOURCE = '\u27EA([a-z]{4})(\\\\d+)\u27EB';\n\n/** \u6BCF\u4E2A\u7247\u6BB5\u4EE5\u5355\u72EC\u4E00\u884C\u6807\u8BB0\u5F00\u5934\uFF0C\u6BB5\u4E0E\u6BB5\u4E4B\u95F4\u7A7A\u4E00\u884C\u3002 */\nexport function buildBatchPayload(pieces: string[], format: BatchFormat): string {\n return pieces.map((text, index) => `${format.token(index)}\\n${text}`).join('\\n\\n');\n}\n\n/**\n * \u6309\u6807\u8BB0\u628A\u6574\u6279\u8BD1\u6587\u5207\u56DE\u6BCF\u4E2A\u7247\u6BB5\u3002\n *\n * \u5E8F\u53F7\u5FC5\u987B\u6070\u597D\u51FA\u73B0\u4E00\u6B21\u3001\u6309 0..count-1 \u5347\u5E8F\u3001\u6BCF\u6BB5\u975E\u7A7A\uFF0C\u4E14\u4E0D\u5F97\u6DF7\u5165\u5176\u5B83\u6279\u6B21\n * \u7684\u6807\u8BB0\uFF1B\u4EFB\u4F55\u4E00\u6761\u4E0D\u6EE1\u8DB3\u90FD\u8FD4\u56DE null\uFF0C\u7531\u8C03\u7528\u65B9\u4F5C\u5E9F\u6574\u6279\u5E76\u9000\u56DE\u5355\u5757\u91CD\u8BD5\u3002\n */\nexport function splitBatchTranslation(\n translated: string,\n format: BatchFormat,\n count: number\n): string[] | null {\n const pattern = new RegExp(BATCH_TOKEN_PATTERN_SOURCE, 'gu');\n const found: Array<{ index: number; start: number; end: number }> = [];\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(translated)) !== null) {\n if (match[1].toLowerCase() !== format.id.toLowerCase()) return null;\n const index = Number.parseInt(match[2] ?? '', 10);\n if (Number.isNaN(index)) return null;\n found.push({ index, start: match.index, end: match.index + match[0].length });\n }\n if (found.length !== count) return null;\n\n const parts: string[] = [];\n for (let index = 0; index < count; index++) {\n const entry = found[index]!;\n if (entry.index !== index) return null;\n const next = found[index + 1];\n const body = translated.slice(entry.end, next === undefined ? translated.length : next.start).trim();\n if (body === '') return null;\n parts.push(body);\n }\n return parts;\n}\n\n/** \u7FFB\u8BD1\u7ED3\u679C\u91CC\u662F\u5426\u8FD8\u7559\u7740\u5757\u6807\u8BB0\uFF08\u7528\u4E8E\u5224\u5B9A\u6574\u6279\u4F5C\u5E9F\uFF09\u3002 */\nexport function hasBatchResidue(text: string): boolean {\n return new RegExp(BATCH_TOKEN_PATTERN_SOURCE, 'u').test(text);\n}\n", "import { requestTranslateReply, type ReplyBlockResult } from '../translate/api.ts';\nimport { estimateTokens, REPLY_MAX_INPUT_TOKENS } from '../../server/pipeline/blocks.ts';\n\n/** \u4E00\u884C\u6B63\u6587\u7684\u6309\u5757\u7FFB\u8BD1\u7ED3\u679C\uFF0C\u4E0E\u8BF7\u6C42\u7684 texts \u6570\u7EC4\u6309\u4E0B\u6807\u5BF9\u9F50\u3002 */\nexport interface BlockOutcome {\n /** \u6A21\u578B\u4EA7\u51FA\u7684\u4E2D\u6587\uFF08Markdown \u6E90\u6587\u672C\uFF09\uFF1B\u5931\u8D25\u65F6\u7B49\u4E8E\u539F\u6587\u3002 */\n translated: string;\n /** \u8BE5\u5757\u662F\u5426\u7FFB\u8BD1\u6210\u529F\u3002\u5931\u8D25\u7684\u5757\u754C\u9762\u4FDD\u6301\u539F\u6587\u3001\u4E0D\u6807\u8BB0\u3002 */\n ok: boolean;\n}\n\n/**\n * \u4E00\u884C\u6B63\u6587\u7684\u7FFB\u8BD1\u72B6\u6001\u3002outcomes \u4E0E texts \u540C\u4E0B\u6807\uFF0Cnull = \u8BE5\u5757\u6240\u5728\u6279\u5C1A\u672A\u843D\u5B9A\uFF1A\n * \u6BCF\u6279\u8FD4\u56DE\u5C31\u5730\u586B\u5165\u5E76\u901A\u77E5\uFF0C\u957F\u56DE\u7B54\u6309\u9605\u8BFB\u987A\u5E8F\u9010\u6BB5\u51FA\u4E2D\u6587\uFF0C\u4E0D\u618B\u5230\u6700\u540E\u4E00\u6279\u3002\n * \u5168\u90E8\u6279\u843D\u5B9A\u540E\uFF1A\u65E0\u5931\u8D25\u5373 done\uFF1B\u6709\u5931\u8D25\u6807 partial\u2014\u2014partial \u884C\u5141\u8BB8\u540E\u7EED ensure\n * \u91CD\u8D70\uFF08\u6E32\u67D3\u5C42\u7684\u4E0B\u4E00\u6B21\u89E6\u53D1\u662F\u6EDA\u51FA\u89C6\u53E3\u518D\u56DE\u6765\u3001\u6216\u884C\u91CD\u6302\u8F7D\uFF09\uFF0C\u5931\u8D25\u7684\u5757\u56E0\u6B64\n * \u81EA\u7136\u91CD\u8BD5\uFF0C\u6210\u529F\u7684\u5757\u7531\u5BBF\u4E3B\u78C1\u76D8\u7F13\u5B58\u76F4\u63A5\u547D\u4E2D\u3001\u4E0D\u518D\u82B1\u6A21\u578B\u8C03\u7528\u3002\u91CD\u8D70\u6709\u4E0A\u9650\n * \uFF08MAX_ROW_ATTEMPTS\uFF09\uFF1A\u786E\u5B9A\u6027\u5931\u8D25\uFF08\u5982\u5F31\u6A21\u578B\u4E22\u5360\u4F4D\u7B26\uFF09\u4E0D\u8BE5\u88AB\u6EDA\u52A8\u65E0\u9650\u7EED\u676F\uFF0C\n * \u5C01\u9876\u540E\u4FDD\u6301\u539F\u6587\u76F4\u5230\u6587\u672C\u6362\u4EE3\u91CD\u7F6E\u3002\n */\nexport interface RowState {\n status: 'pending' | 'partial' | 'done';\n texts: readonly string[];\n outcomes: readonly (BlockOutcome | null)[];\n /** \u8FD9\u4E00\u4EE3\u6587\u672C\u5DF2\u7ECF\u5B8C\u6574\u8DD1\u8FC7\u51E0\u8F6E\uFF08\u9996\u8DD1\u8BA1 1\uFF09\u3002 */\n attempts: number;\n}\n\n/** \u9001\u8BD1\u4E00\u884C\u6B63\u6587\u7684\u53D6\u6570\u9762\uFF1A\u9ED8\u8BA4\u8D70\u5BBF\u4E3B\u8DEF\u7531\uFF0C\u6D4B\u8BD5\u6CE8\u5165\u5047\u5B9E\u73B0\u3002 */\nexport type ReplyFetcher = (texts: string[]) => Promise<ReplyBlockResult[]>;\n\n/** \u540C\u65F6\u5728\u6C60\u7684\u884C\u6570\u4E0A\u9650\uFF1ALRU \u6DD8\u6C70\u6700\u4E45\u672A\u8BFB\u7684\u6574\u884C\uFF1B\u884C\u6587\u672C\u672C\u8EAB\u4ECD\u5F52\u5BBF\u4E3B\u4F1A\u8BDD\u6301\u6709\u3002 */\nexport const MAX_TRANSLATED_ROWS = 200;\n\n/** \u540C\u4E00\u4EE3\u6587\u672C\u6700\u591A\u5B8C\u6574\u8DD1\u51E0\u8F6E\uFF1B\u5C01\u9876\u540E\u4E0D\u518D\u6253\u901A\u9053\uFF0C\u76F4\u5230\u6587\u672C\u6362\u4EE3\u91CD\u7F6E\u989D\u5EA6\u3002 */\nexport const MAX_ROW_ATTEMPTS = 3;\n\n/**\n * \u4E00\u6B21 HTTP \u8BF7\u6C42\u88C5\u8F7D\u7684\u4F30\u7B97 token \u4E0A\u9650\uFF1A\u4E0E\u5BBF\u4E3B\u7684\u6253\u5305\u8F93\u5165\u4E0A\u9650\u540C\u503C\u540C\u6E90\n * \uFF08blocks.ts \u7684 REPLY_MAX_INPUT_TOKENS\uFF09\u2014\u2014\u5BA2\u6237\u7AEF\u6309\u5BBF\u4E3B\u7684\u6253\u5305\u7A97\u53E3\u5207\u6279\uFF0C\n * \u6BCF\u6279\u5728\u5BBF\u4E3B\u4FA7\u901A\u5E38\u6B63\u597D\u843D\u8FDB\u4E00\u6B21\u6A21\u578B\u8C03\u7528\u3002\n */\nexport const REQUEST_TOKEN_BUDGET = REPLY_MAX_INPUT_TOKENS;\n\n/** \u6309\u51FA\u73B0\u987A\u5E8F\u628A\u6574\u884C\u6587\u672C\u5207\u6210\u4E0D\u8D85\u9884\u7B97\u7684\u6279\uFF1B\u5355\u5757\u8D85\u9884\u7B97\u65F6\u72EC\u81EA\u6210\u6279\u3002 */\nexport function chunkTexts(texts: readonly string[]): string[][] {\n const batches: string[][] = [];\n let current: string[] = [];\n let tokens = 0;\n for (const text of texts) {\n const cost = Math.max(estimateTokens(text), 1);\n if (current.length > 0 && tokens + cost > REQUEST_TOKEN_BUDGET) {\n batches.push(current);\n current = [];\n tokens = 0;\n }\n current.push(text);\n tokens += cost;\n }\n if (current.length > 0) batches.push(current);\n return batches;\n}\n\n/**\n * \u52A9\u624B\u884C\u7684\u7FFB\u8BD1\u6C60\uFF1A\u6309\u884C\u952E\u5B58\u300C\u8BE5\u884C\u5168\u90E8\u6B63\u6587\u5757\u7684\u7FFB\u8BD1\u7ED3\u679C\u300D\u3002\n *\n * \u884C\u952E\u7531\u6E32\u67D3\u5C42\u7ED9\u51FA\uFF08\u4F1A\u8BDD\u5185\u951A\u70B9\u5E8F\u53F7\uFF09\uFF0C\u6587\u672C\u6570\u7EC4\u53C2\u4E0E\u4E00\u81F4\u6027\u5224\u65AD\uFF1A\u540C\u952E\u4E0D\u540C\u6587\u672C\n * \u89C6\u4E3A\u884C\u6362\u4E86\u4E00\u4EE3\u5185\u5BB9\uFF0C\u6574\u884C\u91CD\u65B0\u8BF7\u6C42\u2014\u2014\u8FD9\u4FDD\u8BC1\u91CD\u6E32\u67D3\u6216\u4F1A\u8BDD\u5207\u6362\u540E\u4E0D\u4F1A\u628A\u65E7\u8BD1\u6587\n * \u914D\u5230\u65B0\u6587\u672C\u4E0A\u3002\u8BF7\u6C42\u5E42\u7B49\uFF1A\u540C\u952E\u540C\u6587\u672C\u3001\u4E14\u4E0A\u4E00\u4EE3\u5DF2\u843D\u5B9A\u6210\u529F\u6216\u4ECD\u5728\u9014\u7684 ensure\n * \u4E0D\u518D\u53D1\u8BF7\u6C42\uFF1B\u53EA\u6709 partial\uFF08\u6709\u5931\u8D25\u5757\uFF09\u7684\u540C\u4EE3 ensure \u4F1A\u91CD\u8D70\uFF0C\u4E14\u540C\u4EE3\u5C01\u9876\u4E09\u6B21\u3002\u5728\u9014\u671F\u95F4\u6362\u4EE3\uFF0C\n * \u8FDF\u5230\u7684\u65E7\u4EE3\u7ED3\u679C\u76F4\u63A5\u4E22\u5F03\u3002\n *\n * \u672C\u6A21\u5757\u4E0D\u4F9D\u8D56 React\uFF1A\u6E32\u67D3\u5C42\u7528 useSyncExternalStore \u8BA2\u9605\u7248\u672C\uFF0C\u518D\u8BFB getState\u3002\n */\nexport class ChatTranslateStore {\n private rows = new Map<string, RowState>();\n private listeners = new Set<() => void>();\n private version = 0;\n private fetch: ReplyFetcher;\n\n constructor(fetcher: ReplyFetcher) {\n this.fetch = fetcher;\n }\n\n /** \u5355\u8C03\u7248\u672C\u53F7\uFF1A\u4EFB\u4F55\u72B6\u6001\u53D8\u5316\uFF08\u542B\u5355\u6279\u843D\u5B9A\uFF09\u90FD\u524D\u8FDB\u4E00\u4F4D\uFF0C\u4F9B\u6E32\u67D3\u5C42\u5224\u7B49\u91CD\u6E32\u3002 */\n getVersion(): number {\n return this.version;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /** \u8BFB\u4E00\u884C\u72B6\u6001\uFF0C\u5E76\u628A\u8BE5\u884C\u5237\u6210\u6700\u8FD1\u4F7F\u7528\u3002 */\n getState(rowKey: string): RowState | undefined {\n const state = this.rows.get(rowKey);\n if (state === undefined) return undefined;\n // Refresh LRU order (re-insert at the end).\n this.rows.delete(rowKey);\n this.rows.set(rowKey, state);\n return state;\n }\n\n /**\n * \u786E\u4FDD\u8BE5\u884C\u6309\u5F53\u524D\u6587\u672C\u88AB\u7FFB\u8BD1\u3002\n * @returns \u662F\u5426\u53D1\u51FA\u4E86\u65B0\u7684\u8BF7\u6C42\uFF08\u540C\u4EE3 done/pending \u4E0E\u6362\u4EE3\u524D\u7684\u767B\u8BB0\u4E4B\u5916\u90FD\u662F false\uFF09\u3002\n */\n ensure(rowKey: string, texts: readonly string[]): boolean {\n const current = this.rows.get(rowKey);\n let attempts = 1;\n if (current !== undefined && sameTexts(current.texts, texts)) {\n // \u540C\u4EE3\uFF1Adone/pending \u77ED\u8DEF\uFF1Bpartial \u91CD\u8D70\u2014\u2014\u5931\u8D25\u5757\u7684\u300C\u81EA\u7136\u91CD\u8BD5\u300D\u5165\u53E3\uFF0C\n // \u4F46\u540C\u4EE3\u5C01\u9876\uFF1A\u786E\u5B9A\u6027\u5931\u8D25\u4E0D\u8BE5\u88AB\u6EDA\u52A8\u65E0\u9650\u7EED\u676F\u3002\n if (current.status !== 'partial') return false;\n if (current.attempts >= MAX_ROW_ATTEMPTS) return false;\n attempts = current.attempts + 1;\n }\n const snapshot = [...texts];\n const next: RowState = {\n status: 'pending',\n texts: snapshot,\n outcomes: snapshot.map(() => null),\n attempts,\n };\n this.rows.delete(rowKey);\n this.rows.set(rowKey, next);\n this.prune();\n this.bump();\n if (snapshot.length > 0) void this.run(rowKey, snapshot);\n return true;\n }\n\n private async run(rowKey: string, texts: readonly string[]): Promise<void> {\n // \u53BB\u91CD\u9760 ensure \u7684\u540C\u4EE3\u77ED\u8DEF\uFF1B\u6362\u4EE3\u540E\u7684\u5E76\u53D1\u5728\u9014\u5141\u8BB8\u5B58\u5728\u2014\u2014\u8FDF\u5230\u7684\u65E7\u4EE3\u7ED3\u679C\n // \u7531\u6BCF\u6279\u843D\u5B9A\u5904\u7684\u5FEB\u7167\u6838\u5BF9\u4E22\u5F03\u3002\n const outcomes: (BlockOutcome | null)[] = texts.map(() => null);\n let offset = 0;\n for (const batch of chunkTexts(texts)) {\n let results: ReplyBlockResult[] = [];\n try {\n results = await this.fetch(batch);\n } catch {\n // \u53D6\u6570\u9762\u7684\u5F02\u5E38\u4E0E\u5931\u8D25\u7ED3\u679C\u540C\u4E49\uFF1A\u8BE5\u6279\u4FDD\u6301\u539F\u6587\u3002api \u5C42\u81EA\u8EAB\u5DF2\u515C\u5E95\uFF0C\u8FD9\u91CC\n // \u9632\u7684\u662F fetcher \u5B9E\u73B0\u629B\u9519\u7684\u5F62\u6001\u3002\n results = [];\n }\n for (let i = 0; i < batch.length; i++) {\n const result = results[i];\n if (result !== undefined && result.ok && result.translated && result.translated.trim()) {\n outcomes[offset + i] = { translated: result.translated, ok: true };\n } else {\n outcomes[offset + i] = { translated: batch[i] ?? texts[offset + i] ?? '', ok: false };\n }\n }\n offset += batch.length;\n // \u9010\u6279\u53EF\u89C1\uFF1A\u884C\u4ECD\u662F\u8FD9\u4E00\u4EE3\u624D\u5199\u5165\uFF0C\u5199\u5B8C\u901A\u77E5\u2014\u2014\u5148\u56DE\u7684\u6BB5\u5148\u51FA\u4E2D\u6587\u3002\n const live = this.rows.get(rowKey);\n if (live === undefined || live.status !== 'pending' || !sameTexts(live.texts, texts)) return;\n this.rows.set(rowKey, {\n status: 'pending',\n texts: live.texts,\n outcomes: [...outcomes],\n attempts: live.attempts,\n });\n this.bump();\n }\n const state = this.rows.get(rowKey);\n if (state?.status === 'pending' && sameTexts(state.texts, texts)) {\n const anyFailed = outcomes.some((outcome) => outcome === null || !outcome.ok);\n this.rows.set(rowKey, {\n status: anyFailed ? 'partial' : 'done',\n texts: state.texts,\n outcomes,\n attempts: state.attempts,\n });\n this.bump();\n }\n }\n\n private prune(): void {\n while (this.rows.size > MAX_TRANSLATED_ROWS) {\n const oldest = this.rows.keys().next().value;\n if (oldest === undefined) break;\n this.rows.delete(oldest);\n }\n }\n\n private bump(): void {\n this.version += 1;\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch {\n // \u4E00\u4E2A\u8BA2\u9605\u8005\u7684\u5F02\u5E38\u4E0D\u5F71\u54CD\u5176\u4ED6\u8BA2\u9605\u8005\u3002\n }\n }\n }\n}\n\nfunction sameTexts(a: readonly string[], b: readonly string[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/** \u4F9B\u6D4B\u8BD5\u6CE8\u5165\u53D6\u6570\u9762\u521B\u5EFA\u72EC\u7ACB\u6C60\u3002 */\nexport function createTranslateStore(fetcher: ReplyFetcher): ChatTranslateStore {\n return new ChatTranslateStore(fetcher);\n}\n\n/** \u6B63\u6587\u7FFB\u8BD1\u6C60\uFF1A\u5BA2\u6237\u7AEF\u534A\u8FB9\u552F\u4E00\u7684\u6309\u884C\u8BD1\u6587\u767B\u8BB0\u5904\u3002 */\nexport const chatTranslate = createTranslateStore(requestTranslateReply);\n", "/**\n * \u52A9\u624B\u884C\u7684\u6E32\u67D3\u8BA1\u5212\uFF1A\u7EAF\u51FD\u6570\uFF0C\u4E0D\u542B React\uFF0C\u65E0\u6D4F\u89C8\u5668\u4F9D\u8D56\u3002\n *\n * \u8BA1\u5212\u5C42\u627F\u62C5\u4E24\u7C7B\u5224\u65AD\u2014\u2014\n * 1. \u4E0E\u5BBF\u4E3B `AssistantMarkdown` \u9010\u6761\u7B49\u4EF7\u7684\u5206\u652F\uFF1A\u4EC0\u4E48\u65F6\u5019\u6574\u884C\u4E0D\u6E32\u67D3\u3001\n * groupPart \u600E\u4E48\u8FC7\u6EE4\u3001tool-call \u5757\u4E3A\u4EC0\u4E48\u5728\u8FD9\u91CC\u88AB\u8DF3\u8FC7\uFF08\u5DE5\u5177\u884C\u662F flow \u91CC\n * \u72EC\u7ACB\u7684\u884C\uFF0C\u7531\u5BBF\u4E3B\u81EA\u5DF1\u6E32\u67D3\uFF09\u3001\u8FDE\u7EED image \u600E\u4E48\u5408\u6210\u4E00\u7EC4\u3001\u672A\u77E5\u5757\u843D JSON\u3001\n * \u300C\u5DF2\u505C\u6B62\u300D\u6807\u8BB0\u7684\u51FA\u73B0\u6761\u4EF6\u3002\n * 2. \u7FFB\u8BD1\u7684\u5448\u73B0\u5224\u65AD\uFF1A\u6B63\u6587\u5757\u5728\u4EC0\u4E48\u72B6\u6001\u4E0B\u663E\u793A\u8BD1\u6587\u5E76\u5E26\u300C\u5DF2\u7FFB\u8BD1\u300D\u6807\u8BB0\u3002\u6807\u51C6\u53EA\u6709\n * \u4E00\u6761\u2014\u2014\u8BE5\u5757\u7684\u7FFB\u8BD1\u6210\u529F\u843D\u5B9A\u3002\u300C\u6A21\u578B\u8BA4\u4E3A\u539F\u6837\u6700\u597D\u3001\u8FD4\u56DE\u540C\u6837\u7684\u6587\u5B57\u300D\u540C\u6837\u662F\n * \u6210\u529F\uFF1A\u7167\u5E38\u6302\u8F7D\u3001\u7167\u5E38\u6807\u8BB0\uFF0C\u6807\u8BB0\u4F20\u8FBE\u7684\u662F\u300C\u8FD9\u6BB5\u8FC7\u4E86\u6A21\u578B\u300D\uFF0C\u4E0D\u662F\u300C\u8FD9\u6BB5\u53D8\u8FC7\u300D\u3002\n *\n * \u6E32\u67D3\u5C42\uFF08assistant-step.tsx\uFF09\u628A\u8BA1\u5212\u53D8\u6210 JSX\uFF1B\u6539\u52A8\u8FD9\u91CC\u7684\u7B49\u4EF7\u5206\u652F\u5FC5\u987B\u540C\u65F6\u5BF9\u7167\n * \u5BBF\u4E3B\u5B9E\u73B0\u3002\n */\nimport type { BlockOutcome } from './translate-store.ts';\n\n/** \u52A9\u624B\u884C\u5757\u6570\u636E\u7684\u7ED3\u6784\u6027\u955C\u50CF\uFF08\u503C\u8FB9\u4E0D\u8DE8\u5305\uFF0Ctype-only \u4E16\u754C\uFF09\u3002 */\nexport interface AssistantBlockLike {\n readonly kind: string;\n readonly text?: string;\n readonly attachment?: unknown;\n readonly block?: unknown;\n}\n\nexport type RowPlanEntry =\n /** \u601D\u8003\u5757\uFF1AblockIndex \u4F9B\u300C\u6D41\u5F0F\u5C3E\u5757\u300D\u5224\u5B9A\uFF0Ckey \u662F\u6761\u76EE\u5728\u8BA1\u5212\u91CC\u7684\u4F4D\u7F6E\uFF08React \u952E\uFF09\u3002 */\n | { type: 'reasoning'; key: number; blockIndex: number; text: string }\n /** \u6B63\u6587\u5757\uFF1Atranslated \u975E null \u65F6\u663E\u793A\u8BE5\u8BD1\u6587\u5E76\u5E26\u300C\u5DF2\u7FFB\u8BD1\u300D\u6807\u8BB0\uFF0C\u5426\u5219\u663E\u793A text\u3002 */\n | { type: 'prose'; key: number; text: string; translated: string | null }\n | { type: 'images'; key: number; attachments: readonly unknown[] }\n | { type: 'unknown'; key: number; block: unknown }\n | { type: 'stopped'; key: number };\n\nexport interface RowPlan {\n /** null = \u6574\u884C\u4E0D\u6E32\u67D3\uFF08\u4E0E\u5BBF\u4E3B\u7684\u7A7A\u884C\u5B88\u536B\u4E00\u81F4\uFF09\u3002 */\n entries: RowPlanEntry[] | null;\n /** \u9001\u7FFB\u8BD1\u6C60\u7684\u6B63\u6587\u6587\u672C\uFF0C\u6309\u6B63\u6587\u5757\u51FA\u73B0\u987A\u5E8F\uFF0C\u4E0E\u7ED3\u679C\u4E0B\u6807\u5BF9\u9F50\u3002 */\n texts: string[];\n}\n\nexport interface RowPlanInput {\n blocks: readonly AssistantBlockLike[];\n /** \u884C\u5728\u6D41\u5F0F\u4E2D\uFF1A\u6D41\u5F0F\u671F\u95F4\u4E00\u5F8B\u539F\u6587\uFF0C\u843D\u5B9A\u540E\u624D\u6709\u8BD1\u6587\u3002 */\n streaming: boolean;\n /** \u4E2D\u65AD\u524D\u7F00\uFF1A\u884C\u5C3E\u5E26\u300C\u5DF2\u505C\u6B62\u300D\u6807\u8BB0\u3002 */\n interrupted: boolean;\n /** flow \u5206\u7EC4\uFF1A'reasoning' \u53EA\u8981\u601D\u8003\u5757\uFF0C'response' \u6392\u9664\u601D\u8003\u5757\uFF0C\u7F3A\u7701\u5168\u8981\u3002 */\n groupPart?: string;\n /** \u53EF\u8BD1\u95E8\uFF08enabled \u4E14\u901A\u9053\u4E09\u8981\u7D20\u9F50\u5907\uFF09\uFF1Afalse \u65F6\u4E00\u5207\u6309\u539F\u6587\u5448\u73B0\uFF0C\u4E0D\u6302\u7EBF\u3002 */\n canTranslate: boolean;\n /**\n * \u8BE5\u884C\u6B63\u6587\u5757\u7684\u7FFB\u8BD1\u7ED3\u679C\uFF0C\u4E0E texts \u540C\u4E0B\u6807\uFF1Bnull = \u8BE5\u6279\u5C1A\u672A\u843D\u5B9A\uFF08\u9010\u6279\u51FA\u6587\u7684\n * \u4E2D\u95F4\u6001\uFF09\u3002\u672A\u767B\u8BB0\u6216\u6362\u4EE3\u65F6\u6574\u4E2A\u7F3A\u7701\u3002\n */\n outcomes?: readonly (BlockOutcome | null)[];\n}\n\nexport function planAssistantRow(input: RowPlanInput): RowPlan {\n const { blocks, streaming, interrupted, groupPart, canTranslate, outcomes } = input;\n\n // \u5BBF\u4E3B\u5B88\u536B\u9010\u5B57\u7B49\u4EF7\uFF1A\u53EA\u6709 tool-call \u5757\u3001\u53C8\u975E\u6D41\u5F0F\u975E\u4E2D\u65AD\u7684\u884C\u4E0D\u6E32\u67D3\u3002\n const hasVisible = streaming || interrupted || blocks.some((block) => block.kind !== 'tool-call');\n if (!hasVisible) return { entries: null, texts: [] };\n\n // \u9001\u8BD1\u6587\u672C = \u672C\u5206\u7EC4\u771F\u6B63\u4F1A\u6E32\u67D3\u51FA\u6765\u7684 text \u5757\uFF1Areasoning \u5206\u7EC4\u4E0D\u6E32\u67D3\u4EFB\u4F55\u6B63\u6587\uFF0C\n // \u4E5F\u5C31\u4E0D\u9001\u8BD1\u2014\u2014texts\u3001textIndex\u3001outcomes \u4E09\u8005\u59CB\u7EC8\u540C\u57DF\u5BF9\u9F50\u3002\n const texts: string[] = [];\n if (groupPart !== 'reasoning') {\n for (const block of blocks) {\n if (block.kind === 'text') texts.push(block.text ?? '');\n }\n }\n\n const entries: RowPlanEntry[] = [];\n let textIndex = 0;\n let imageRun: unknown[] | null = null;\n\n const flushImages = (): void => {\n if (imageRun !== null) {\n entries.push({ type: 'images', key: entries.length, attachments: imageRun });\n imageRun = null;\n }\n };\n\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n if (block === undefined) continue;\n if (groupPart === 'reasoning' && block.kind !== 'reasoning') continue;\n if (groupPart === 'response' && block.kind === 'reasoning') continue;\n\n switch (block.kind) {\n case 'text': {\n flushImages();\n const index = textIndex;\n textIndex += 1;\n // key \u4E00\u5F8B\u7528\u6761\u76EE\u4F4D\u7F6E\uFF1A\u5757\u4E0B\u6807\u5728 groupPart \u8FC7\u6EE4\u540E\u53EF\u80FD\u4E0E\u884C\u5C3E\u8FFD\u52A0\u9879\u649E\n // React \u952E\uFF08\u5982 reasoning \u88AB\u8FC7\u6EE4\u7684 interrupted \u884C\uFF09\u3002\n const outcome = canTranslate && !streaming && outcomes !== undefined ? outcomes[index] : null;\n entries.push({\n type: 'prose',\n key: entries.length,\n text: block.text ?? '',\n translated: outcome !== null && outcome !== undefined && outcome.ok ? outcome.translated : null,\n });\n break;\n }\n case 'reasoning':\n flushImages();\n entries.push({ type: 'reasoning', key: entries.length, blockIndex: i, text: block.text ?? '' });\n break;\n case 'image':\n if (imageRun === null) imageRun = [];\n imageRun.push(block.attachment);\n break;\n case 'tool-call':\n flushImages();\n break;\n default:\n flushImages();\n entries.push({ type: 'unknown', key: entries.length, block: block.block ?? null });\n }\n }\n flushImages();\n\n // \u5BBF\u4E3B\u7684\u505C\u6B62\u6807\u8BB0\u6761\u4EF6\u9010\u5B57\u7B49\u4EF7\u3002\n const showStopped =\n interrupted &&\n (groupPart === undefined ||\n groupPart === 'response' ||\n !blocks.some((block) => block.kind !== 'reasoning' && block.kind !== 'tool-call'));\n if (showStopped) entries.push({ type: 'stopped', key: entries.length });\n\n return { entries, texts };\n}\n", "/**\n * \u5DF2\u7FFB\u8BD1\u5757\u7684\u70B9\u51FB\u5B88\u536B\uFF1A\u5207\u6362\u53EA\u8BA4\u300C\u843D\u5728\u5757\u7A7A\u767D\u6B63\u6587\u4E0A\u7684\u88F8\u70B9\u51FB\u300D\u3002\n *\n * \u5757\u5185\u542B\u94FE\u63A5\u3001\u4EE3\u7801\u5757\u590D\u5236\u94AE\u7B49\u4EA4\u4E92\u5143\u7D20\u2014\u2014\u5B83\u4EEC\u7684\u70B9\u51FB\u5F52\u81EA\u5DF1\uFF0C\u4E0D\u8FDE\u5E26\u5207\u5757\uFF1B\n * \u62D6\u9009\u8BD1\u6587\u677E\u624B\u4EA7\u751F\u7684 click \u540C\u6837\u5FFD\u7565\u3002\u7EAF DOM \u5224\u5B9A\uFF0C\u4E0D\u4F9D\u8D56 React\uFF0C\u53EF\u72EC\u7ACB\u6D4B\u8BD5\u3002\n */\n\ninterface MinimalPointerEvent {\n /** \u70B9\u51FB\u547D\u4E2D\u7684\u6700\u6DF1\u5143\u7D20\u3002 */\n target: { closest?(selector: string): unknown } | null;\n /** \u6302\u4E86\u672C\u5B88\u536B\u7684\u5DF2\u7FFB\u8BD1\u5757\u5BB9\u5668\u3002 */\n currentTarget: unknown;\n}\n\ninterface MinimalWindow {\n getSelection?(): { isCollapsed?: boolean; toString?(): string } | null;\n}\n\n/**\n * @param event - click \u4E8B\u4EF6\uFF08\u7ED3\u6784\u9762\uFF1Atarget/currentTarget\uFF09\u3002\n * @param view - \u53EF\u6CE8\u5165\u7684 window\uFF08\u6D4B\u8BD5\u4F20\u5047\u5BF9\u8C61\uFF1B\u8FD0\u884C\u65F6\u4F20\u5168\u5C40 window\uFF09\u3002\n * @returns \u8BE5\u70B9\u51FB\u662F\u5426\u5E94\u5F53\u89E6\u53D1\u539F\u6587/\u8BD1\u6587\u5207\u6362\u3002\n */\nexport function isBareBlockClick(event: MinimalPointerEvent, view: MinimalWindow | undefined): boolean {\n const target = event.target;\n const interactive = typeof target?.closest === 'function' ? target.closest('a,button,[role=\"button\"]') : null;\n if (interactive !== null && interactive !== undefined && interactive !== event.currentTarget) return false;\n const selection = typeof view?.getSelection === 'function' ? view.getSelection() : null;\n if (selection !== null && selection !== undefined) {\n if (selection.isCollapsed === false && String(selection.toString?.() ?? '') !== '') return false;\n }\n return true;\n}\n", "/**\n * \u52A9\u624B\u884C\u63A5\u7BA1\u6240\u9700\u7684\u6837\u5F0F\uFF1A\u4E0E\u5BBF\u4E3B `AssistantMarkdown` / `ReasoningRow` \u9010\u6761\u5BF9\u9F50\n * \u7684\u7B49\u4EF7\u89C4\u5219\uFF08\u5B57\u53F7\u3001\u884C\u8DDD\u3001gap\u3001\u7C98\u6027\u6298\u53E0\u5934\u3001running \u5FAE\u5149\u3001summary \u906E\u7F69\u7B49\uFF09\uFF0C\n * \u52A0\u4E0A\u672C\u63D2\u4EF6\u552F\u4E00\u65B0\u589E\u7684\u89C6\u89C9\u8BCD\u6C47\u2014\u2014\u5DE6\u7F18\u7EBF\u7684\u8272\u76F8\u4E0E\u7C97\u7EC6\u6807\u8BB0\uFF1A\u84DD\u7C97\u7EBF\uFF081px\uFF09=\u6B63\u5728\n * \u8BFB\u8BD1\u6587\uFF0C\u7070\u7EC6\u7EBF\uFF080.5px\uFF09=\u6709\u8BD1\u6587\u4F46\u6B63\u5728\u8BFB\u539F\u6587\uFF08\u518D\u70B9\u5373\u5207\u56DE\uFF09\uFF0C\u65E0\u7EBF=\u6CA1\u8BD1\u6210\u3002\n *\n * \u5168\u90E8\u989C\u8272\u4E0E\u5C3A\u5BF8\u8D70 `--dsw-alias-*` / `--dsh-*` \u4E3B\u9898\u522B\u540D\uFF0C\u4E2D\u6027\u5E73\u8FB9\u6309\u5B98\u65B9\u89C4\u5219\n * \u753B 0.5px hairline\uFF1B\u52A8\u753B\u58F0\u660E\u5C0A\u91CD `prefers-reduced-motion`\u3002\u6837\u5F0F\u968F bundle \u6CE8\u5165\n * \u4E00\u6B21\uFF08`<style data-plugin-css>`\uFF09\uFF0C\u7C7B\u540D\u7528\u56FA\u5B9A\u524D\u7F00\uFF0C\u4E0D\u4F9D\u8D56\u5BBF\u4E3B\u54C8\u5E0C\u7C7B\u540D\u3002\n */\n\nconst STYLE_TAG_ID = '@lynn123411/dsh-chat-translate/chat-assistant.css';\n\nexport const ASSISTANT_CSS = [\n // ---- \u884C\u9AA8\u67B6\uFF08\u5BBF\u4E3B AssistantMarkdown.root/body \u7B49\u4EF7\uFF09 ----\n '.dsh-ct-root{display:flex;flex-direction:column;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary)}',\n '.dsh-ct-body{display:flex;flex-direction:column;gap:16px}',\n '.dsh-ct-body>[data-turn-process-inline][hidden]{margin-bottom:-16px}',\n // \u5BBD\u8868\u683C\u6EA2\u51FA\u5185\u5BB9\u5217\uFF08\u5BBF\u4E3B\u7B49\u4EF7\u89C4\u5219\uFF0C\u7C7B\u540D md-table-wide \u7531 MarkdownText \u81EA\u5DF1\u4EA7\u51FA\uFF09\n '.dsh-ct-body .md-table-wide{--dsh-table-spare:max(0px, calc((100cqw - var(--dsh-chat-content-width)) / 2));--dsh-table-lead:calc(var(--dsh-table-spare) + min(var(--dsh-chat-content-width), 100cqw) - 100%);box-sizing:border-box;width:calc(100% + var(--dsh-table-lead) + var(--dsh-table-spare));max-width:none;margin-left:calc(-1 * var(--dsh-table-lead));padding-left:var(--dsh-table-lead)}',\n '.dsh-ct-body .md-table-wide>table{z-index:1;position:relative}',\n // ---- \u5DF2\u505C\u6B62\u6807\u8BB0\uFF08\u5BBF\u4E3B .hWmORq_stopped \u7B49\u4EF7\uFF09 ----\n '.dsh-ct-stopped{border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);align-self:flex-start;padding:0 6px;font-size:11px;line-height:18px}',\n // ---- \u300C\u5DF2\u7FFB\u8BD1\u300D\u4EA4\u4E92\u4E0E\u6807\u8BB0\uFF1A\u53EF\u70B9\u6001\u5E38\u9A7B\uFF08\u542B\u5DE6\u7F18\u5185\u7F29\uFF09\uFF1B\u533A\u5206\u8D70\u8272\u76F8+\u7C97\u7EC6\n // \uFF080.5px \u865A\u7EBF\u4F1A\u88AB\u6297\u952F\u9F7F\u7CCA\u6210\u5B9E\u7EBF\uFF09\u2014\u2014\u84DD\u7C97\u7EBF=\u8BFB\u8BD1\u6587\uFF0C\u7070\u7EC6\u7EBF=\u8BFB\u539F\u6587\u5907\u7740\u8BD1\u6587 ----\n '.dsh-ct-prose-clickable{padding-left:12px;cursor:pointer}',\n '.dsh-ct-prose-clickable:focus-visible{outline:1.5px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px;border-radius:var(--dsw-radius-sm)}',\n '.dsh-ct-prose-translated{border-left:1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 65%, transparent)}',\n '.dsh-ct-prose-original{border-left:0.5px solid var(--dsw-alias-border-l2)}',\n // ---- ReasoningRow\uFF08\u5BBF\u4E3B lcKema_* \u7B49\u4EF7\uFF0C\u524D\u7F00\u6362\u6210 dsh-ct-think\uFF09 ----\n '.dsh-ct-think-root{flex-direction:column;display:flex}',\n '.dsh-ct-think-root:not([data-expanded]){contain:size layout;height:calc(24px + var(--dsh-content-font-delta,0px))}',\n '.dsh-ct-think-row{position:relative;overflow:hidden}',\n '.dsh-ct-think-root[data-expanded] [data-open] [data-disclosure-row]{z-index:1;background:var(--dsw-alias-bg-base);position:sticky;top:0}',\n '.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{content:\"\";inset-block:0;background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite dsh-ct-reasoning-sweep;position:absolute;left:0}',\n '@keyframes dsh-ct-reasoning-sweep{0%{left:-300px}90%,to{left:100%}}',\n '@media (prefers-reduced-motion:reduce){.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{animation:none}}',\n '.dsh-ct-think-leading{flex-shrink:0}',\n '.dsh-ct-think-chevron{color:var(--dsw-alias-label-secondary)}',\n '.dsh-ct-think-title{font-weight:400}',\n '.dsh-ct-think-separator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}',\n '.dsh-ct-think-summary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));white-space:nowrap;flex:auto;overflow:hidden}',\n '.dsh-ct-think-summary-text{text-overflow:ellipsis;display:block;overflow:hidden}',\n '.dsh-ct-think-summary[data-streaming]{mask-image:linear-gradient(90deg,currentcolor calc(100% - 48px),transparent)}',\n '.dsh-ct-think-summary[data-streaming] .dsh-ct-think-summary-text{text-overflow:clip;overflow:visible}',\n '.dsh-ct-think-root:not([data-preview]) .dsh-ct-think-separator,.dsh-ct-think-root:not([data-preview]) .dsh-ct-think-summary{display:none}',\n '.dsh-ct-think-body{padding:4px 0 4px calc(22px + var(--dsh-content-font-delta,0px));min-width:0}',\n // ---- \u65E0\u969C\u788D\uFF1A\u5C4F\u5E55\u9605\u8BFB\u5668\u4E13\u7528\u6587\u672C ----\n '.dsh-ct-visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}',\n].join('');\n\n/**\n * \u6B63\u6587\u5757\u7684\u6807\u8BB0\u7C7B\u540D\u5355\u70B9\uFF1A\u6709\u8BD1\u6587\u624D\u53EF\u70B9\uFF08\u5E38\u9A7B\u5185\u7F29 + \u5149\u6807\uFF09\uFF1B\u5DE6\u7F18\u7EBF\u7684\u8272\u76F8\u4E0E\u7C97\u7EC6\n * \u968F\u663E\u793A\u6001\u2014\u2014\u8BD1\u6587\u6302 1px \u84DD\u7EBF\u3001\u539F\u6587\u6001\u6302 0.5px \u7070\u7EBF\uFF08\u4FDD\u7559\u300C\u8FD9\u5757\u6709\u8BD1\u6587\u3001\u70B9\u4E86\u80FD\n * \u5207\u56DE\u300D\u7684\u7EBF\u7D22\uFF09\uFF0C\u6CA1\u8BD1\u65E0\u7EBF\u3002\u7EBF\u5BBD\u53EA\u5DEE 0.5px\uFF0C\u5207\u6362\u4E0D\u8DF3\u5B57\u3002\n */\nexport function proseClassNames(processed: boolean, showOriginal: boolean): string | undefined {\n if (!processed) return void 0;\n return showOriginal\n ? 'dsh-ct-prose-clickable dsh-ct-prose-original'\n : 'dsh-ct-prose-clickable dsh-ct-prose-translated';\n}\n\n/** \u6CE8\u5165\u63A5\u7BA1\u6837\u5F0F\uFF1B\u91CD\u590D\u8C03\u7528\u662F\u7A7A\u64CD\u4F5C\u3002 */\nexport function ensureAssistantStyles(): void {\n if (typeof document === 'undefined') return;\n const selector = `style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`;\n if (document.querySelector(selector) !== null) return;\n const tag = document.createElement('style');\n tag.dataset.plugin = '@lynn123411/dsh-chat-translate';\n tag.dataset.pluginCss = STYLE_TAG_ID;\n tag.textContent = ASSISTANT_CSS;\n document.head.appendChild(tag);\n}\n", "/**\n * \u52A9\u624B\u884C\u6E32\u67D3\u6240\u9700\u7684\u300C\u5DE5\u4F5C\u7EC6\u8282\u300D\u5448\u73B0\u7B56\u7565\u3002\n *\n * \u5BBF\u4E3B\u628A\u8FD9\u4E00\u7B56\u7565\u6D3E\u751F\u81EA `ui-chat` \u914D\u7F6E\u6761\u76EE\u7684 `transcriptView` \u5B57\u6BB5\uFF1B\u672C\u63D2\u4EF6\u63A5\u7BA1\n * \u52A9\u624B\u884C\u6E32\u67D3\u540E\uFF0C\u7ECF `ctx.configForms.get('ui-chat')` \u8BFB\u540C\u4E00\u4E2A\u5B57\u6BB5\uFF0C\u6620\u5C04\u5230\u4E0E\u5BBF\u4E3B\n * \u540C\u5F62\u3001\u540C\u503C\u7684\u7B56\u7565\u8868\u3002\u7B56\u7565\u5BF9\u8C61\u6309\u6A21\u5F0F\u5E38\u9A7B\uFF08\u540C\u4E00\u6A21\u5F0F\u6052\u7B49\u8FD4\u56DE\u540C\u4E00\u5B9E\u4F8B\uFF09\uFF0C\n * \u9009\u62E9\u5668\u8BA2\u9605\u770B\u5230\u7684\u662F\u7A33\u5B9A\u5F15\u7528\u3002\n */\n\n/** \u4E0E\u5BBF\u4E3B ChatPresentationPolicy \u540C\u5F62\u7684\u7ED3\u6784\u6027\u955C\u50CF\u3002 */\nexport interface ChatPresentationPolicy {\n readonly mode: string;\n readonly foldCompletedTurns: boolean;\n readonly stepGrouping: 'collapsed' | 'history' | 'none';\n readonly liveProcessDetail: boolean;\n readonly settledReasoningPreview: boolean;\n}\n\nfunction policy(\n mode: string,\n foldCompletedTurns: boolean,\n stepGrouping: ChatPresentationPolicy['stepGrouping'],\n liveProcessDetail: boolean,\n settledReasoningPreview: boolean\n): ChatPresentationPolicy {\n return Object.freeze({ mode, foldCompletedTurns, stepGrouping, liveProcessDetail, settledReasoningPreview });\n}\n\n/**\n * \u6A21\u5F0F \u2192 \u7B56\u7565\u5E38\u91CF\u8868\uFF0C\u9010\u884C\u5BF9\u9F50\u5BBF\u4E3B ui-chat \u7684\u5B9E\u73B0\uFF08\u542B\u4E24\u4EE3 legacy \u5B58\u50A8\u503C\uFF1A\n * `normal` \u6309 standard \u8BFB\u3001`expanded` \u6309 detailed \u8BFB\uFF0C\u4ECE\u4E0D\u5199\u56DE\uFF09\u3002\n * \u6E32\u67D3\u5668\u53EA\u9009\u62E9\u5355\u4E2A\u5B57\u6BB5\uFF0C\u4E0D\u6309 mode \u5206\u652F\u3002\n */\nexport const POLICY_BY_MODE: Readonly<Record<string, ChatPresentationPolicy>> = Object.freeze({\n compact: policy('compact', true, 'collapsed', false, false),\n standard: policy('standard', true, 'collapsed', true, true),\n detailed: policy('detailed', true, 'history', true, true),\n verbose: policy('verbose', false, 'none', false, true),\n normal: policy('standard', true, 'collapsed', true, true),\n expanded: policy('detailed', true, 'history', true, true),\n});\n\n/** \u5BBF\u4E3B\u5728\u7528\u6237\u6CA1\u6709\u663E\u5F0F\u9009\u62E9\u65F6\u4F7F\u7528\u7684\u6A21\u5F0F\u3002 */\nexport const DEFAULT_MODE = 'standard';\n\n/** \u672C\u6A21\u5757\u53EA\u7528\u5230\u914D\u7F6E\u9762\u7684\u8FD9\u4E00\u5C0F\u7247\u5F62\u72B6\uFF08\u7ED3\u6784\u6027\u955C\u50CF\uFF0C\u503C\u8FB9\u4E0D\u8DE8\u5305\uFF09\u3002 */\ninterface ModeFormLike {\n getSnapshot(): { status: string; value?: Record<string, unknown> };\n subscribe(listener: () => void): () => void;\n}\n\n/**\n * \u8DDF\u968F `ui-chat` \u914D\u7F6E\u7684\u6D3B\u7B56\u7565\u6E90\u3002attach \u4E4B\u540E\uFF1A\u8BFB\u4E00\u6B21\u3001\u8BA2\u4E00\u6B21\uFF1B\u914D\u7F6E\u9762\u7F3A\u5E2D\n * \uFF08\u5BBF\u4E3B\u534A\u8FB9\u672A\u670D\u52A1\u8BE5\u6761\u76EE\u3001\u5185\u5B58\u6A21\u5F0F\uFF09\u65F6\u505C\u5728 standard\uFF0C\u4E0D\u53D1\u8BA2\u9605\u3002\n */\nexport class ChatPresentation {\n private policy: ChatPresentationPolicy = POLICY_BY_MODE[DEFAULT_MODE];\n private listeners = new Set<() => void>();\n private form: ModeFormLike | null = null;\n private unsubscribe: (() => void) | null = null;\n\n attach(form: ModeFormLike | null | undefined): void {\n this.detach();\n if (!form) {\n this.policy = POLICY_BY_MODE[DEFAULT_MODE];\n return;\n }\n this.form = form;\n this.read();\n this.unsubscribe = form.subscribe(() => this.read());\n }\n\n detach(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n this.form = null;\n }\n\n getSnapshot = (): ChatPresentationPolicy => this.policy;\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n private read(): void {\n const form = this.form;\n if (!form) return;\n let value: Record<string, unknown> | undefined;\n try {\n const snap = form.getSnapshot();\n value = snap.status === 'ready' ? snap.value : undefined;\n } catch {\n value = undefined;\n }\n const raw = typeof value?.transcriptView === 'string' ? value.transcriptView : '';\n const next = POLICY_BY_MODE[raw] ?? POLICY_BY_MODE[DEFAULT_MODE];\n if (next !== this.policy) {\n this.policy = next;\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch {\n // \u8BA2\u9605\u8005\u5F02\u5E38\u4E92\u4E0D\u5F71\u54CD\u3002\n }\n }\n }\n }\n}\n\nexport function createChatPresentation(): ChatPresentation {\n return new ChatPresentation();\n}\n\n/** \u6E32\u67D3\u5668\u4E0E\u914D\u7F6E\u9762\u4E4B\u95F4\u7684\u8FDB\u7A0B\u5185\u5355\u4F8B\uFF1A\u5728 client/index.ts \u63A5\u7EBF\u4E00\u6B21\u3002 */\nexport const chatPresentation = createChatPresentation();\n"],
5
- "mappings": "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",
3
+ "sources": ["../src/client/index.ts", "../src/client/settings/ui.tsx", "../src/describe-error.ts", "../src/client/translate/api.ts", "../src/client/settings/store.ts", "../src/client/settings/styles.ts", "../src/client/reading-settings-page.tsx", "../src/client/locales.ts", "../src/client/composer/dock-toggle.tsx", "../src/client/chat/assistant-step.tsx", "../src/client/chat/row-plan.ts", "../src/client/chat/click-guard.ts", "../src/client/chat/styles.ts", "../src/server/pipeline/blocks.ts", "../src/client/chat/translate-store.ts", "../src/client/chat/presentation.ts"],
4
+ "sourcesContent": ["import { setupSettingsUi } from './settings/ui.tsx';\nimport { setupComposerToggle } from './composer/dock-toggle.tsx';\nimport { AssistantStepView } from './chat/assistant-step.tsx';\nimport { chatPresentation } from './chat/presentation.ts';\nimport { ensureAssistantStyles } from './chat/styles.ts';\nimport { bindRowCopy } from './locales.ts';\n// Type-only, erased from the bundle: the renderer augments Context with the\n// `slots` service the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D page shell consumes, and the settings\n// contract declares the `settings.section` slot its page component renders into.\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/client';\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client';\n\n/** Client plugin name, shared with the browser bundle id. */\nexport const name = 'dsh-chat-translate';\n\n/**\n * Declared services: slots for the keyed assistant-row takeover, the shared\n * \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page (page claim plus the card registration) and the\n * composer dock pill; configForms for this plugin's own configuration form\n * plus the host `ui-chat` entry whose `transcriptView` field drives the\n * reasoning-row presentation policy; the remote + remote.credentials pair for\n * the credentials Remote namespace; and locale for the card's own dictionary.\n * The runtime withholds any service not declared here; `locale` is declared\n * rather than sampled because `ctx.get('locale')` at apply time can beat the\n * locale plugin's `provide`, which silently left the card's dictionary\n * unregistered.\n */\nexport const inject = ['slots', 'configForms', 'remote', 'remote.credentials', 'locale'];\n\ninterface ClientContext {\n effect(factory: () => void | (() => void), label?: string): void;\n get?(serviceName: string): any;\n slots?: {\n inject(name: string, register: () => unknown): () => void;\n register(options: Record<string, unknown>, component: unknown): () => void;\n };\n configForms?: { get(namespace: string): any };\n locale?: any;\n remote?: { credentials?: any };\n}\n\n/**\n * Mount the assistant-row takeover (keyed `conversation.chat.node` /\n * `assistant-step`), the settings UI card inside the shared reading-settings\n * page, and the translation toggle in the composer dock.\n * @param ctx - DSH browser client context.\n */\nexport function apply(ctx: ClientContext): void {\n ensureAssistantStyles();\n\n // 1. \u63A5\u7BA1\u52A9\u624B\u884C\uFF1Akeyed slot \u7684\u66FF\u6362\u8BED\u4E49\uFF0Cpriority -1 \u6392\u5728\u5BBF\u4E3B\u9ED8\u8BA4\u6CE8\u518C\uFF080\uFF09\u4E4B\u524D\n // \u6E32\u67D3\uFF1B\u672C\u6E32\u67D3\u5668\u629B\u9519\u65F6\u6309\u6846\u67B6\u9000\u4F4D\u89C4\u5219\u9000\u51FA cell\uFF0C\u5BBF\u4E3B\u539F\u6E32\u67D3\u5668\u56DE\u4F4D\u3002\n ctx.effect(() => {\n const slots = ctx.slots;\n if (!slots || typeof slots.inject !== 'function') return () => {};\n return slots.inject('conversation.chat.node', () =>\n slots.register(\n {\n name: 'conversation.chat.node',\n key: 'assistant-step',\n // \u884C\u5185\u7684\u6298\u53E0\u3001\u505C\u6B62\u3001\u672A\u77E5\u5757\u7B49 chrome \u6587\u6848\u590D\u7528\u5BBF\u4E3B `chat` \u8BCD\u5178\uFF0C\n // \u4E24\u4FA7\u6E32\u67D3\u9010\u5B57\u4E00\u81F4\uFF1Bt \u7531\u5EA7\u6B21\u6309\u8BE5\u547D\u540D\u7A7A\u95F4\u5408\u6210\u3002\n locale: 'chat',\n priority: -1,\n // \u601D\u8003\u884C\u7684 settledReasoningPreview \u6765\u81EA\u6D3B\u7684\u5DE5\u4F5C\u7EC6\u8282\u6A21\u5F0F\u3002\n inject: () => ({ hooks: { presentation: chatPresentation } }),\n },\n AssistantStepView\n )\n );\n }, 'dsh-chat-translate: assistant step takeover');\n\n // 2. \u300C\u5DE5\u4F5C\u7EC6\u8282\u300D\u6A21\u5F0F\u8DDF\u968F\u5BBF\u4E3B\uFF1A\u8BFB ui-chat \u6761\u76EE\u7684 transcriptView\u3002\n ctx.effect(() => {\n let form: unknown = null;\n try {\n form = ctx.configForms?.get('ui-chat') ?? null;\n } catch {\n form = null;\n }\n chatPresentation.attach(form as Parameters<typeof chatPresentation.attach>[0]);\n return () => chatPresentation.detach();\n }, 'dsh-chat-translate: chat presentation source');\n\n // 3. Join the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page (claim it, or register a card into it)\n ctx.effect(() => setupSettingsUi(ctx), 'dsh-chat-translate: settings section');\n\n // 4. \u300C\u8BD1\u300D\u5F00\u5173\uFF1A\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\uFF0C\u4E0E\u8BBE\u7F6E\u9875\u603B\u5F00\u5173\u540C\u4E00\u4E2A enabled\n ctx.effect(() => setupComposerToggle(ctx), 'dsh-chat-translate: composer dock toggle');\n\n // \u884C\u6E32\u67D3\u5668\u7684\u79C1\u6709\u6587\u6848\uFF08\u7EA2\u7EBF\u91CD\u8BD5\u63D0\u793A\uFF09\u7ED1\u4E0A\u672C\u547D\u540D\u7A7A\u95F4\u7684\u6D3B\u7FFB\u8BD1\u5668\u3002\u653E\u5728\u6700\u540E\uFF1A\n // \u6B64\u65F6 setupSettingsUi \u7684 locale.register(NS) \u5DF2\u843D\u5730\uFF0Cbind \u7ACB\u523B\u53EF\u89E3\u6790\u952E\u3002\n bindRowCopy(ctx.locale);\n}\n\nexport { AssistantStepView } from './chat/assistant-step.tsx';\nexport { planAssistantRow } from './chat/row-plan.ts';\nexport { chatTranslate, createTranslateStore } from './chat/translate-store.ts';\nexport { chatPresentation, createChatPresentation, POLICY_BY_MODE } from './chat/presentation.ts';\nexport { ensureAssistantStyles } from './chat/styles.ts';\nexport { setupSettingsUi } from './settings/ui.tsx';\nexport { setupComposerToggle, ComposerTranslateToggle } from './composer/dock-toggle.tsx';\nexport { settingsStore } from './settings/store.ts';\n", "import { useState, useEffect } from 'react';\nimport * as React from 'react';\nimport type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';\nimport { Button, Input, Switch, Tag } from '@deepseek-ai/dsh-client-ui-primitives';\nimport {\n settingsStore,\n SETTINGS_NAMESPACE,\n TRANSLATE_API_KEY_REF,\n type ClientSettingsState,\n} from './store.ts';\nimport { SETTINGS_CSS } from './styles.ts';\nimport { claimReadingSettingsPage, READING_ITEM_SLOT } from '../reading-settings-page.tsx';\nimport { NS, en, zh, type ChatTranslateLocaleKey } from '../locales.ts';\nlet stylesInjected = false;\nfunction ensureSettingsStyles(): void {\n if (stylesInjected || typeof document === 'undefined') return;\n const el = document.createElement('style');\n el.dataset.tidySettings = 'true';\n el.textContent = SETTINGS_CSS;\n document.head.appendChild(el);\n stylesInjected = true;\n}\n\n/** Path the credentials Remote API persists the key to (technical sample). */\nconst CREDENTIALS_PATH = '~/.dsh/.credentials.yaml';\n\n/**\n * Render a localized template whose `{name}` markers carry technical samples\n * (paths, env refs, endpoints, model ids). The samples stay at the render site,\n * so the dictionaries hold prose only.\n * @param text - the localized template, still carrying its `{name}` markers.\n * @param samples - marker name to the literal sample, rendered as inline code.\n * @returns The template with every resolved marker replaced by `<code>`.\n */\nfunction withSamples(text: string, samples: Record<string, string>): React.ReactNode {\n return text.split(/(\\{\\w+\\})/g).map((part, index) => {\n const name = /^\\{\\w+\\}$/.test(part) ? part.slice(1, -1) : '';\n const sample = name === '' ? undefined : samples[name];\n return sample === undefined ? part : <code key={index}>{sample}</code>;\n });\n}\n\n/**\n * Chat-translate's card inside the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page. The card\n * keeps its own chrome (title rows, switches, credential form); the shared page\n * only stacks the participants' cards.\n * @param props - renderer-bound seat of the `reading.settings.item` slot plus\n * the `t` seat its `locale: NS` registration injects.\n * @returns The card element.\n */\nexport function TidySettingsPanel(\n props: PropsRuntime<'reading.settings.item'> & PropsLocale<typeof NS>\n): React.ReactElement {\n ensureSettingsStyles();\n const { t } = props;\n\n const [state, setState] = useState<ClientSettingsState>(() => settingsStore.getState());\n const [testing, setTesting] = useState<{ channel: string; running: boolean; ok?: boolean; message?: string } | null>(null);\n const [apiKeyInput, setApiKeyInput] = useState('');\n const [savingKey, setSavingKey] = useState(false);\n const [keyMsg, setKeyMsg] = useState<{ ok: boolean; text: string } | null>(null);\n\n useEffect(() => {\n return settingsStore.subscribe(() => {\n setState(settingsStore.getState());\n });\n }, []);\n\n const runTest = async (channel: string): Promise<void> => {\n setTesting({ channel, running: true });\n const res = await settingsStore.testChannel(channel);\n const message = res.ok\n ? t('testOk').replace('{latency}', String(res.latencyMs))\n : t('testFail').replace('{error}', res.error || t('unknownError'));\n setTesting({ channel, running: false, ok: res.ok, message });\n };\n\n const handleSaveKey = async (): Promise<void> => {\n setSavingKey(true);\n setKeyMsg(null);\n const res = await settingsStore.saveApiKey(apiKeyInput);\n setSavingKey(false);\n if (res.ok) {\n const cleared = !apiKeyInput.trim();\n setKeyMsg({\n ok: true,\n text: cleared\n ? t('keyCleared')\n : t('keySaved').replace('{home}', CREDENTIALS_PATH),\n });\n setApiKeyInput('');\n } else {\n setKeyMsg({\n ok: false,\n text: t('saveFailed').replace('{error}', res.error || t('unknownError')),\n });\n }\n };\n\n return (\n <div className=\"dsh-tidy-settings\">\n {/* 1. \u603B\u5F00\u5173 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-title\">\n <span>{t('masterTitle')}</span>\n <Switch\n checked={state.enabled}\n onChange={(next: boolean) => settingsStore.update({ enabled: next })}\n label={t('enableTranslation')}\n />\n </div>\n <div className=\"dsh-tidy-desc\">\n {withSamples(t('masterDesc'), { example: 'I traced the failing path to a stale lock file.' })}\n </div>\n </div>\n\n {state.enabled && (\n <>\n {/* 2. AI \u901A\u9053 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-title\">\n <span>{t('aiTitle')}</span>\n <Tag tone={state.aiConfigured ? 'success' : 'warning'}>\n {t(state.aiConfigured ? 'badgeConfigured' : 'badgeUnconfigured')}\n </Tag>\n </div>\n <div className=\"dsh-tidy-desc\">{t('aiRule')}</div>\n\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('apiKeyTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">\n {withSamples(t('apiKeyDesc'), {\n home: CREDENTIALS_PATH,\n ref: TRANSLATE_API_KEY_REF,\n })}\n </div>\n </div>\n <div className=\"dsh-tidy-input-row\">\n <Input\n type=\"password\"\n className=\"dsh-tidy-field-wide\"\n placeholder={state.aiConfigured ? t('apiKeyPlaceholderConfigured') : 'sk-...'}\n value={apiKeyInput}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setApiKeyInput(e.target.value)}\n aria-label={t('apiKeyTitle')}\n />\n <Button\n variant=\"primary\"\n size=\"sm\"\n disabled={savingKey}\n onClick={handleSaveKey}\n >\n {savingKey ? t('saving') : t('save')}\n </Button>\n </div>\n </div>\n {keyMsg && (\n <div className={`dsh-tidy-test-result ${keyMsg.ok ? 'ok' : 'fail'}`}>{keyMsg.text}</div>\n )}\n\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('baseUrlTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">\n {withSamples(t('baseUrlDesc'), {\n url1: 'https://api.openai.com/v1',\n url2: 'https://api.deepseek.com/v1',\n })}\n </div>\n </div>\n <Input\n type=\"text\"\n className=\"dsh-tidy-field-wide\"\n placeholder=\"https://api.openai.com/v1\"\n value={state.baseUrl}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n settingsStore.update({ baseUrl: e.target.value })\n }\n aria-label={t('baseUrlTitle')}\n />\n </div>\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('modelTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">\n {withSamples(t('modelDesc'), {\n model1: 'gpt-4o-mini',\n model2: 'deepseek-chat',\n })}\n </div>\n </div>\n <Input\n type=\"text\"\n className=\"dsh-tidy-field-wide\"\n placeholder=\"gpt-4o-mini\"\n value={state.model}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n settingsStore.update({ model: e.target.value })\n }\n aria-label={t('modelTitle')}\n />\n </div>\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('timeoutTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">{t('timeoutDesc')}</div>\n </div>\n <Input\n type=\"number\"\n className=\"dsh-tidy-field-timeout\"\n min={500}\n max={900000}\n step={1000}\n value={state.aiTimeoutMs}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const val = parseInt(e.target.value, 10);\n if (!Number.isFinite(val)) return;\n settingsStore.update({\n aiTimeoutMs: Math.min(Math.max(val, 500), 900000),\n });\n }}\n aria-label={t('timeoutTitle')}\n />\n </div>\n\n <div className=\"dsh-tidy-row\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n disabled={testing?.running}\n onClick={() => runTest('openai')}\n >\n {testing?.running ? t('testing') : t('test')}\n </Button>\n {testing?.channel === 'openai' && !testing.running && (\n <span className={`dsh-tidy-test-result ${testing.ok ? 'ok' : 'fail'}`}>{testing.message}</span>\n )}\n </div>\n </div>\n\n {/* 3. \u884C\u4E3A\u8BF4\u660E */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-row-title\">{t('behaviorTitle')}</div>\n <div className=\"dsh-tidy-desc\">{t('behaviorRule')}</div>\n </div>\n </>\n )}\n </div>\n );\n}\n\n/**\n * Bind the settings store to this plugin's own configuration form and the\n * credentials Remote API, then join the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page:\n * whichever participant activates first claims the page, everyone registers a\n * card into its child slot.\n * @param ctx - DSH browser client context.\n */\nexport function setupSettingsUi(ctx: any): void {\n if (typeof window === 'undefined') return;\n\n // Ride DSH's shared configuration forms: `ctx.configForms.get(<profile entry\n // id>)` is the read/write face of THIS plugin's own Config schema \u2014 it\n // mirrors the Host's config section for the entry and folds every accepted\n // write back. `remote.credentials` writes the API key. There is no custom\n // config HTTP endpoint and no per-namespace settings scope.\n const configForms = ctx.configForms;\n const remoteCredentials = ctx.remote?.credentials ?? null;\n try {\n settingsStore.attach(configForms.get(SETTINGS_NAMESPACE), remoteCredentials);\n } catch (err) {\n console.warn('[dsh-chat-translate] Failed to bind the configuration form:', err);\n }\n\n // The locale service is declared through the client `inject` table, so cordis\n // installs it before this body runs and `ctx.locale` cannot miss it.\n // Registering the dictionary HERE is what the card's seat `t` resolves\n // against; without the registration the card falls back to the shell's\n // `common` namespace and renders every unknown key as its own key text.\n const locale = ctx.locale;\n if (locale && typeof locale.register === 'function') {\n ctx.effect(\n () => locale.register(NS, { zh, en }),\n 'dsh-chat-translate: locale dictionaries'\n );\n }\n const t = locale && typeof locale.bind === 'function'\n ? locale.bind(NS)\n // Key-aware fallback: one fixed label for every key would dress the shared\n // page's tab in the wrong text whenever the locale service is not yet\n // there, so the fallback answers each key with its own zh value.\n : (key: ChatTranslateLocaleKey): string => zh[key] ?? key;\n\n try {\n const slots = ctx.slots;\n if (!slots || typeof slots.inject !== 'function') return;\n\n // \u5171\u4EAB\u300C\u9605\u8BFB\u4F53\u9A8C\u300D\u8BBE\u7F6E\u9875\uFF1A\u672C\u63D2\u4EF6\u4E0E dsh-smooth-stream\n // \u5171\u7528\u4E00\u9875\uFF0C\u5185\u6838\u4E0D\u5141\u8BB8 settings.section \u7684\u540C\u4E00 id \u88AB\u6CE8\u518C\u4E24\u6B21\u3001\u4E5F\u4E0D\u5141\u8BB8\u5B50 slot\n // \u88AB\u58F0\u660E\u4E24\u6B21\uFF0C\u56E0\u6B64\u5404\u53C2\u4E0E\u8005\u643A\u5E26\u540C\u4E00\u4EFD\u9875\u58F3\u3001\u5148\u5230\u5148\u5F97\u5F53\u9009\uFF1A\u5F53\u9009\u8005\u6CE8\u518C\u9875\u9762\u5E76\u58F0\u660E\n // reading.settings.item \u5B50 slot\uFF0C\u672A\u5F53\u9009\u8005\u53EA\u628A\u5361\u7247\u6CE8\u518C\u8FDB\u8BE5\u5B50 slot \u7B49\u9875\u9762\u51FA\u73B0\u3002\n // \u5171\u4EAB\u9875\u58F3\u9700\u8981\u771F ctx\uFF1A\u5B83\u7528 ctx.slots \u8BFB\u6CE8\u518C\u8868\uFF0C\u5E76\u7528 ctx.get('locale') \u5728\u8BED\u8A00\n // \u5207\u6362\u65F6\u91CD\u8BFB tab \u6807\u7B7E\uFF08\u53EF\u9009\u670D\u52A1\uFF0C\u5FC5\u987B\u8D70 ctx.get\uFF0C\u4E0D\u80FD\u7528 ctx.locale\uFF09\u3002\n //\n // \u6574\u9875\uFF08\u542B\u672C\u63D2\u4EF6\u90A3\u5F20\u5361\u7247\uFF09\u53EA\u5728\u5BBF\u4E3B\u771F\u7684\u670D\u52A1\u672C\u63D2\u4EF6\u7684\u914D\u7F6E\u6761\u76EE\u65F6\u5B58\u5728\uFF1A\n // `whileServed` \u662F 0.1.7 \u7684\u6B63\u5F0F\u5951\u7EA6\uFF0C\u6CA1\u88C5/\u6CA1\u52A0\u8F7D\u5BBF\u4E3B\u534A\u8FB9\u7684\u90E8\u7F72\u4E0D\u4F1A\u7559\u4E0B\u9875\u7684\u75D5\u8FF9\u3002\n ctx.effect(\n () => configForms.whileServed([SETTINGS_NAMESPACE], () => {\n const offPage = slots.inject('settings.section', () =>\n claimReadingSettingsPage(ctx, () => t('pageNav'), NS)\n );\n const offCard = slots.inject(READING_ITEM_SLOT, () =>\n slots.register(\n {\n name: READING_ITEM_SLOT,\n // id = \u672C\u63D2\u4EF6\u7684\u914D\u7F6E\u6761\u76EE id\uFF08= profile entry id = \u8BBE\u7F6E\u547D\u540D\u7A7A\u95F4\uFF09\n id: SETTINGS_NAMESPACE,\n // \u5361\u7247\u5728\u5171\u4EAB\u9875\u91CC\u7684\u987A\u5E8F\uFF1A\u4E1D\u6ED1\u6D41\u5F0F 10\u3001\u5438\u9876\u63D0\u793A 20\u3001\u804A\u5929\u7FFB\u8BD1 30\n order: 30,\n // \u5171\u4EAB\u9875\u6309\u6B64\u6807\u7B7E\u6E32\u67D3 tab\n label: () => t('title'),\n locale: NS,\n },\n TidySettingsPanel\n )\n );\n return () => {\n offCard();\n offPage();\n };\n }),\n 'dsh-chat-translate: shared reading page'\n );\n } catch (err) {\n console.warn('[dsh-chat-translate] Failed to inject settings section:', err);\n }\n}", "/**\n * \u628A fetch / \u7F51\u7EDC\u5931\u8D25\u7684\u53EF\u8BFB\u539F\u56E0\u5C55\u5F00\u6210\u5355\u884C\u6587\u672C\uFF08\u5BBF\u4E3B\u4E0E\u5BA2\u6237\u7AEF\u5171\u7528\uFF09\u3002\n *\n * \u80CC\u666F\uFF1ANode 18+ \u4E0E\u6D4F\u89C8\u5668\u7684 fetch \u5728\u7F51\u7EDC\u5C42\u5931\u8D25\u65F6\u53EA\u7ED9\u4E00\u53E5 `TypeError: fetch failed`\n * \uFF08\u6D4F\u89C8\u5668\u662F `Failed to fetch`\uFF09\uFF0C\u771F\u5B9E\u539F\u56E0\u85CF\u5728 `err.cause` \u91CC\u2014\u2014\u4F8B\u5982\n * `connect ENETUNREACH 172.24.52.126:8081`\u3001`other side closed [UND_ERR_SOCKET]`\u3001\n * `getaddrinfo ENOTFOUND`\u3002\u53EA\u4E0A\u62A5 `err.message` \u4F1A\u8BA9\u300C\u7AEF\u70B9\u4E0D\u53EF\u8FBE / \u88AB\u4EE3\u7406\u65AD\u5F00\u300D\u548C\n * \u300C\u63D2\u4EF6\u81EA\u8EAB\u51FA\u9519\u300D\u957F\u5F97\u4E00\u6A21\u4E00\u6837\uFF1A\u7528\u6237\u53EA\u770B\u5230\u65E0\u4FE1\u606F\u91CF\u7684 fetch failed\uFF0C\u65E0\u4ECE\u5224\u65AD\u8BE5\u6539\u4EC0\u4E48\u3002\n *\n * \u8FD9\u91CC\u628A message \u4E0E cause \u94FE\uFF08\u542B AggregateError \u7684\u591A\u5730\u5740\u5931\u8D25\uFF09\u62FC\u6210 `A \u2190 B \u2190 C`\u3002\n */\nexport function describeError(err: unknown): string {\n if (err === null || err === undefined) return String(err);\n const parts: string[] = [];\n const push = (s: string): void => {\n const t = String(s || '').trim();\n if (t && !parts.includes(t)) parts.push(t);\n };\n const head = (err as { message?: string }).message || String(err);\n push(head);\n const headCode = (err as { code?: string }).code;\n if (headCode && !head.includes(headCode)) push('[' + headCode + ']');\n let cause = (err as { cause?: unknown }).cause as\n | { message?: string; code?: string; cause?: unknown; errors?: unknown }\n | undefined;\n for (let depth = 0; cause && depth < 4; depth++) {\n const label = cause.message || String(cause);\n push(cause.code && !label.includes(cause.code) ? label + ' [' + cause.code + ']' : label);\n // AggregateError\uFF1A\u591A\u5730\u5740\u5168\u90E8\u5931\u8D25\uFF08happy-eyeballs \u53CC\u6808\u5E38\u89C1\uFF09\uFF0C\u5C55\u5F00\u524D\u51E0\u6761\n if (Array.isArray(cause.errors)) {\n for (const sub of (cause.errors as { message?: string; code?: string }[]).slice(0, 3)) {\n if (sub) push(sub.code || sub.message || String(sub));\n }\n }\n cause = cause.cause as typeof cause;\n }\n return parts.join(' \u2190 ');\n}", "import { describeError } from '../../describe-error.ts';\nimport type { ReplyBlockResult } from '../../server/types.ts';\n\n/**\n * \u8BF7\u6C42\u7FFB\u8BD1\u56DE\u7B54\u6B63\u6587\u7684\u5757\u3002\u5BBF\u4E3B\u4FA7\u6309\u8F93\u5165\u4E0A\u9650\u6253\u5305\u3001\u4E32\u884C\u53D1\u9001\uFF0C\u5BA2\u6237\u7AEF\u8D85\u65F6\u4EA4\u7ED9\n * \u5BBF\u4E3B\uFF08\u4E00\u4E2A\u957F\u56DE\u7B54\u53EF\u80FD\u8981\u51E0\u5206\u949F\uFF09\uFF0C\u8FD9\u91CC\u4E0D\u8BBE\u81EA\u5DF1\u7684\u622A\u6B62\u65F6\u95F4\u3002\n */\nexport async function requestTranslateReply(blocks: string[]): Promise<ReplyBlockResult[]> {\n const fallback = (): ReplyBlockResult[] =>\n blocks.map((block) => ({\n original: block,\n translated: block,\n ok: false,\n cached: false,\n }));\n\n if (!Array.isArray(blocks) || blocks.length === 0) return [];\n try {\n const res = await fetch('/api/dsh-chat-translate/translate-reply', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ blocks }),\n });\n if (res.ok) {\n const data = (await res.json()) as {\n ok: boolean;\n results?: ReplyBlockResult[];\n error?: string;\n };\n if (data.ok && Array.isArray(data.results) && data.results.length === blocks.length) {\n return data.results;\n }\n if (data.error) {\n console.warn(`[dsh-chat-translate] \u6B63\u6587\u7FFB\u8BD1\u63A5\u53E3\u9519\u8BEF: ${data.error}`);\n }\n } else {\n console.warn(`[dsh-chat-translate] \u6B63\u6587\u7FFB\u8BD1\u8BF7\u6C42\u5931\u8D25 (HTTP ${res.status})`);\n }\n } catch (err: any) {\n console.warn(`[dsh-chat-translate] \u6B63\u6587\u7FFB\u8BD1\u8BF7\u6C42\u5F02\u5E38: ${describeError(err)}`);\n }\n return fallback();\n}\n\nexport async function testServerChannel(channel: string): Promise<{ ok: boolean; latencyMs: number; error?: string }> {\n try {\n const res = await fetch('/api/dsh-chat-translate/test-channel', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ channel }),\n });\n return await res.json();\n } catch (err: any) {\n return { ok: false, latencyMs: 0, error: describeError(err) };\n }\n}\n", "import { describeError } from '../../describe-error.ts';\nimport { testServerChannel } from '../translate/api.ts';\n\nexport interface ClientSettingsState {\n enabled: boolean;\n aiTimeoutMs: number;\n baseUrl: string;\n model: string;\n /** \u6D3E\u751F\u503C\uFF1AKey / Base URL / \u6A21\u578B\u9F50\u5168\uFF0C\u6B63\u6587\u624D\u4F1A\u88AB\u7FFB\u8BD1\u3002 */\n aiConfigured: boolean;\n}\n\n/** \u6B63\u6587\u5355\u6B21\u8BF7\u6C42\u8D85\u65F6\u7684\u53D6\u503C\u8303\u56F4\uFF0C\u4E0E\u5BBF\u4E3B\u534A\u8FB9\u4FDD\u6301\u4E00\u81F4\u3002 */\nexport const AI_TIMEOUT_MIN = 500;\nexport const AI_TIMEOUT_MAX = 900000;\n\n/** Profile entry id (== settings namespace) + credentials ref, mirroring the host constants. */\nexport const SETTINGS_NAMESPACE = 'dsh-chat-translate';\nexport const TRANSLATE_API_KEY_REF = 'TRANSLATE_API_KEY';\n\n/**\n * One path-addressed edit, matching the wire shape the shared configuration\n * form's `mutate` accepts.\n */\nexport type SettingsPathOpLike =\n | { op: 'set'; path: string[]; value: unknown }\n | { op: 'unset'; path: string[] };\n\n/**\n * Minimal structural shape of the DSH shared configuration form this store\n * rides on \u2014 `ConfigForm<T>` from @deepseek-ai/dsh-client-ui-settings, reached\n * as `ctx.configForms.get(<profile entry id>)`. Keeping it structural keeps\n * this bundle free of the settings package and lets tests inject fakes; the\n * real form is a structural superset (it also carries `base`, `user` and\n * `mode`).\n */\nexport interface ConfigFormLike {\n getSnapshot(): {\n /** `unavailable` when the Host does not serve this entry to this client. */\n status: 'loading' | 'ready' | 'unavailable';\n /** Resolved section: schema defaults, then composition base, then user layer. */\n value?: Record<string, unknown>;\n /** Whether the Host document accepts writes; memory mode never does. */\n writable: boolean;\n /** Namespace revision fencing the next write. */\n revision?: number;\n };\n subscribe(listener: () => void): () => void;\n /** One revision-fenced batch of path writes; resolves to Host acceptance. */\n mutate(ops: readonly SettingsPathOpLike[], expectedRevision?: number): Promise<unknown>;\n}\n\n/** Shape of every DSH client Remote call: {ok, value} / {ok, error} wrapper. */\nexport type RemoteResult<T> =\n | { ok: true; value: T }\n | { ok: false; error: { code?: string; message: string; details?: unknown } };\n\nexport interface CredentialsRemoteLike {\n describe(\n refs: string[]\n ): Promise<RemoteResult<Record<string, { configured: boolean; source?: string; writable: boolean }>>>;\n set(ref: string, value: string): Promise<RemoteResult<void>>;\n unset(ref: string): Promise<RemoteResult<void>>;\n}\n\nconst DEFAULT_STATE: ClientSettingsState = {\n enabled: true,\n aiTimeoutMs: 600000,\n baseUrl: '',\n model: '',\n aiConfigured: false,\n};\n\n/**\n * Client settings store backed by this plugin's own configuration form.\n *\n * There is no localStorage overlay and no custom config HTTP endpoint: the\n * store derives from the bound `ConfigForm`, which mirrors the profile entry's\n * config section (schema defaults \u2192 composition base \u2192 user layer) and folds\n * every accepted write back, and each debounced flush commits its touched\n * fields as one `mutate` batch \u2014 one revision fence, serialized and persisted\n * by DSH. Without a bound form (unit tests) it degrades to an in-memory store\n * with the same semantics.\n */\nclass SettingsStore {\n private state: ClientSettingsState = { ...DEFAULT_STATE };\n private listeners = new Set<() => void>();\n private form: ConfigFormLike | null = null;\n private credentials: CredentialsRemoteLike | null = null;\n private unsubscribeForm: (() => void) | null = null;\n private keyConfigured = false;\n private writeTimer: number | null = null;\n private pendingFields = new Set<string>();\n\n /**\n * Bind the DSH services. Called once from the settings UI setup; re-binding\n * (e.g. after a reconnect) detaches the previous subscription first.\n */\n attach(form: ConfigFormLike | null, credentials: CredentialsRemoteLike | null): void {\n if (this.unsubscribeForm) {\n this.unsubscribeForm();\n this.unsubscribeForm = null;\n }\n this.form = form;\n this.credentials = credentials;\n if (form) {\n this.unsubscribeForm = form.subscribe(() => this.derive());\n this.derive();\n }\n void this.refreshKeyStatus();\n }\n\n getState(): ClientSettingsState {\n return { ...this.state };\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /** Map the resolved section into client state and notify. */\n private derive(): void {\n if (!this.form) return;\n const snap = this.form.getSnapshot();\n const value = snap.value;\n if (!value || typeof value !== 'object') return;\n const next: ClientSettingsState = { ...this.state };\n if (typeof value.enabled === 'boolean') next.enabled = value.enabled;\n const timeout = value.aiTimeoutMs;\n if (typeof timeout === 'number' && Number.isFinite(timeout)) {\n next.aiTimeoutMs = Math.min(\n Math.max(Math.round(timeout), AI_TIMEOUT_MIN),\n AI_TIMEOUT_MAX\n );\n }\n if (typeof value.baseUrl === 'string') next.baseUrl = value.baseUrl;\n if (typeof value.model === 'string') next.model = value.model;\n this.applyState(next);\n }\n\n private applyState(next: ClientSettingsState): void {\n next.aiConfigured = Boolean(next.baseUrl && next.model && this.keyConfigured);\n this.state = next;\n this.notify();\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n try {\n listener();\n } catch {}\n }\n }\n\n /** Pull the key's status-only view and re-derive aiConfigured. */\n private async refreshKeyStatus(): Promise<void> {\n if (!this.credentials) return;\n try {\n const res = await this.credentials.describe([TRANSLATE_API_KEY_REF]);\n if (!res.ok) return;\n const info = res.value[TRANSLATE_API_KEY_REF];\n const configured = Boolean(info?.configured);\n if (configured !== this.keyConfigured) {\n this.keyConfigured = configured;\n this.applyState({ ...this.state });\n }\n } catch {\n // Credentials surface unavailable \u2014 keep the last known status.\n }\n }\n\n /**\n * Optimistically apply locally, then persist each touched field through the\n * configuration form. Writes are trailing-debounced (300ms) so typing in the\n * baseUrl/model inputs collapses into a single queued mutation instead of\n * one write per keystroke \u2014 which would otherwise flash stale mirror values\n * back into the inputs between commits. A failed write makes the form reload\n * its mirror, which re-derives this store from the Host document\n * (conflict-safe recovery).\n */\n async update(partial: Partial<ClientSettingsState>): Promise<void> {\n let sanitizedTimeout = this.state.aiTimeoutMs;\n if (typeof partial.aiTimeoutMs === 'number' && Number.isFinite(partial.aiTimeoutMs)) {\n sanitizedTimeout = Math.min(\n Math.max(Math.round(partial.aiTimeoutMs), AI_TIMEOUT_MIN),\n AI_TIMEOUT_MAX\n );\n }\n const next: ClientSettingsState = {\n ...this.state,\n ...partial,\n aiTimeoutMs: sanitizedTimeout,\n };\n this.applyState(next);\n\n if (this.form) {\n const fields = ['enabled', 'aiTimeoutMs', 'baseUrl', 'model'] as const;\n for (const field of fields) {\n if (partial[field] !== undefined) {\n this.pendingFields.add(field);\n }\n }\n this.scheduleWrite();\n }\n }\n\n private scheduleWrite(): void {\n if (this.writeTimer !== null || typeof window === 'undefined') return;\n this.writeTimer = window.setTimeout(() => {\n this.writeTimer = null;\n void this.flushWrite();\n }, 300);\n }\n\n private async flushWrite(): Promise<void> {\n if (!this.form) return;\n const fields = [...this.pendingFields];\n this.pendingFields.clear();\n if (fields.length === 0) return;\n const ops: SettingsPathOpLike[] = fields.map((field) => ({\n op: 'set',\n path: [field],\n value: this.state[field as keyof ClientSettingsState],\n }));\n await this.form.mutate(ops).catch(() => {});\n }\n\n async testChannel(channel: string): Promise<{ ok: boolean; latencyMs: number; error?: string }> {\n return testServerChannel(channel);\n }\n\n /** Write (or clear) the API key through the credentials Remote API. */\n async saveApiKey(apiKey: string): Promise<{ ok: boolean; error?: string }> {\n if (!this.credentials) {\n return { ok: false, error: '\u51ED\u636E\u670D\u52A1\u4E0D\u53EF\u7528\uFF08\u975E\u56DE\u73AF\u9875\u9762\uFF09' };\n }\n try {\n const key = apiKey.trim();\n // Remote calls resolve with {ok:false} instead of rejecting on refusal\n // (e.g. env-shadowed refs), so the .ok check decides the outcome.\n const res = key\n ? await this.credentials.set(TRANSLATE_API_KEY_REF, key)\n : await this.credentials.unset(TRANSLATE_API_KEY_REF);\n if (!res.ok) {\n return { ok: false, error: res.error?.message || '\u4FDD\u5B58\u5931\u8D25' };\n }\n await this.refreshKeyStatus();\n return { ok: true };\n } catch (err: any) {\n return { ok: false, error: describeError(err) };\n }\n }\n\n dispose(): void {\n if (this.writeTimer !== null && typeof window !== 'undefined') {\n clearTimeout(this.writeTimer);\n this.writeTimer = null;\n }\n this.pendingFields.clear();\n if (this.unsubscribeForm) {\n this.unsubscribeForm();\n this.unsubscribeForm = null;\n }\n this.form = null;\n this.credentials = null;\n this.listeners.clear();\n }\n}\n\nexport const settingsStore = new SettingsStore();\n", "export const SETTINGS_CSS = String.raw`\n.dsh-tidy-settings {\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-width: 580px;\n padding-bottom: 32px;\n color: var(--dsw-alias-label-primary);\n font: var(--dsw-font-s-14);\n}\n\n.dsh-tidy-card {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 16px 18px;\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n /* The shipped settings-card surface; Input's own bg-layer-1 then reads as a\n recessed field in the dark theme instead of vanishing into the card. */\n background: var(--dsw-alias-bg-layer-3);\n}\n\n.dsh-tidy-title {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font: var(--dsw-font-s-strong-14);\n}\n\n.dsh-tidy-desc {\n color: var(--dsw-alias-label-secondary);\n font: var(--dsw-font-xxs-12);\n}\n\n.dsh-tidy-desc code {\n font-family: var(--ds-font-family-code);\n}\n\n.dsh-tidy-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 14px;\n padding: 8px 0;\n}\n\n.dsh-tidy-row + .dsh-tidy-row {\n border-top: 0.5px solid var(--dsw-alias-border-l3);\n}\n\n.dsh-tidy-row-info {\n flex: 1;\n min-width: 0;\n}\n\n.dsh-tidy-row-title {\n font: var(--dsw-font-xs-strong-13);\n}\n\n.dsh-tidy-row-desc {\n margin-top: 2px;\n color: var(--dsw-alias-label-secondary);\n font: var(--dsw-font-xxxs-11);\n}\n\n/* Field widths ride Input's wrapper span; the atom owns its own appearance. */\n.dsh-tidy-input-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.dsh-tidy-field-wide {\n width: 260px;\n}\n\n.dsh-tidy-field-timeout {\n width: 120px;\n}\n\n.dsh-tidy-test-result {\n margin-left: 10px;\n font: var(--dsw-font-xxs-12);\n}\n\n.dsh-tidy-test-result.ok {\n color: var(--dsw-alias-state-success-primary);\n}\n\n.dsh-tidy-test-result.fail {\n color: var(--dsw-alias-state-error-primary);\n}\n\n`;", "/**\n * The shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page.\n *\n * Several plugins contribute their configuration to ONE settings page, but the\n * kernel cannot declare that page jointly: `settings.section` is a list slot\n * that rejects a duplicate `id` at the same priority (\"already has an entry\n * with id\"), and a child slot may be declared exactly once (\"slot \u2026 is already\n * declared\"). Composing several cards into one page therefore takes one\n * declarer, so every participating plugin carries this same shell and the\n * FIRST one to activate claims the page; the others register their card into\n * {@link READING_ITEM_SLOT} and wait for the winner's declaration through\n * `slots.inject`. Uninstalling the winner promotes another participant on the\n * next boot, so no participant is a fixed owner.\n *\n * The page renders ONE TAB PER REGISTERED CARD: the tab roster comes from the\n * child slot's own registrations (id + `label` + `order`, the same shape the\n * kernel's own Plugins page uses for its tabs), and each panel dispatches\n * through `renderSlot(READING_ITEM_SLOT, {}, { only: id })`. Every panel stays\n * mounted but hidden, so a card's local state survives a tab switch.\n *\n * Keep this file identical across the participating plugins\n * (`dsh-smooth-stream`, `dsh-chat-translate`).\n * Participants own their own card component, locale dictionaries, settings\n * namespace and Host half \u2014 only the page shell below is shared, because\n * cross-plugin value imports are forbidden by the client bundle purity gate.\n * It imports nothing but `react` plus type-only DSH packages on purpose, so\n * every participant's build configuration compiles it unchanged.\n *\n * Settings storage belongs to the card, not to this shell: since 0.1.7 a\n * participant reads and writes its own Config through\n * `ctx.configForms.get(<profile entry id>)` and gates the registration on\n * `ctx.configForms.whileServed(...)`. The shell only stacks cards \u2014 it holds no\n * settings service of its own, which is why it needs nothing beyond\n * `ctx.slots` and the optional `locale`.\n */\n\nimport { createElement, useState, useSyncExternalStore } from 'react'\nimport type { Context as ClientContext } from '@deepseek-ai/cordis'\nimport type { InjectFace, LocaleNamespaceMap, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'\n\n/** Page id claimed by the first participating plugin to activate. */\nexport const READING_PAGE_ID = 'reading'\n/** Sidebar position of the shared page; own plugins start at 110. */\nexport const READING_PAGE_ORDER = 110\n/** The page's one child slot: every participant's card registers here. */\nexport const READING_ITEM_SLOT = 'reading.settings.item'\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface SlotMap {\n /** One participating plugin's card inside the shared reading page. */\n 'reading.settings.item': { kind: 'list'; scope: 'root' }\n }\n}\n\n/** One participant's tab, derived from its card registration. */\nexport interface ReadingTab {\n /** Registration id \u2014 the participant's Host settings namespace. */\n id: string\n /** Registration order, ascending in the tab bar. */\n order: number\n /** Registration label, already resolved for the active locale. */\n label: string\n}\n\n/** Live tab roster handed to the page component through its inject face. */\nexport interface ReadingTabsFace {\n getSnapshot(): readonly ReadingTab[]\n subscribe(listener: () => void): () => void\n}\n\n/** Inject face of the shared page. */\nexport interface ReadingPageFace {\n readingTabs: ReadingTabsFace\n}\n\ntype ReadingPageProps =\n PropsRuntime<'settings.section'>\n & PropsRenderSlots<typeof READING_ITEM_SLOT>\n & InjectFace<ReadingPageFace>\n\n/**\n * A registration label is a plain string or a thunk re-read per projection \u2014\n * the same `SlotLabel` the shipped `resolveSlotLabel` resolves. It is inlined\n * rather than imported so this shared file stays value-import-free (see the\n * header): every participant's build configuration compiles it unchanged.\n */\nfunction readLabel(label: unknown): string {\n if (typeof label === 'function') return (label as () => string)()\n return typeof label === 'string' ? label : ''\n}\n\n/**\n * Tab chrome mirrors the kernel's own settings tabs (`.tabs` / `.tab` in\n * `ui-settings-plugins`): tertiary label, 13px, 22px gutter. The marker is the\n * ACTIVE TAB'S OWN bottom border and the bar draws no rail of its own \u2014 a\n * shared underline reads as \"every tab is selected\", which is exactly the bug\n * this replaces.\n */\nconst TABLIST_STYLE = {\n display: 'flex',\n alignItems: 'flex-end',\n gap: '22px',\n marginTop: '2px',\n marginBottom: '16px',\n} as const\n\nconst TAB_STYLE = {\n appearance: 'none',\n background: 'transparent',\n // No border on ANY tab: the marker below is the active tab's own element, so\n // an inactive tab has nothing that could render a line.\n border: 'none',\n position: 'relative',\n padding: '7px 1px 11px',\n cursor: 'pointer',\n font: 'var(--dsw-font-xs-13)',\n color: 'var(--dsw-alias-label-tertiary)',\n} as const\n\nconst TAB_ACTIVE_STYLE = {\n ...TAB_STYLE,\n color: 'var(--dsw-alias-label-primary)',\n} as const\n\n/** The kernel's own tab marker: a 2px rounded bar under the active label. */\nconst TAB_MARKER_STYLE = {\n position: 'absolute',\n left: 0,\n right: 0,\n bottom: 0,\n height: '2px',\n borderRadius: '2px 2px 0 0',\n background: 'var(--dsw-alias-label-primary)',\n} as const\n\n/** Panels stay mounted (hidden) so each card keeps its local state. */\nconst PANEL_STYLE = { listStyle: 'none', margin: 0, padding: 0 } as const\nconst PANEL_HIDDEN_STYLE = { ...PANEL_STYLE, display: 'none' } as const\n\n/**\n * Build the live tab roster over the child slot's registrations. `locale` is\n * read through `ctx.get`: a participant needs it only to re-read localized\n * labels on a language switch, and reaching an undeclared service as\n * `ctx.locale` would trip the kernel's inject guard.\n * @param ctx - browser context carrying the slot registry.\n * @returns The tab store consumed by the page component.\n */\nfunction createReadingTabs(ctx: ClientContext): ReadingTabsFace {\n const locale = ctx.get('locale') as\n | { getSnapshot(): { revision: number }; subscribe(listener: () => void): () => void }\n | undefined\n let version = -1\n let revision = -1\n let tabs: readonly ReadingTab[] = []\n return {\n getSnapshot: () => {\n const nextVersion = ctx.slots.getVersion(READING_ITEM_SLOT)\n const nextRevision = locale === undefined ? 0 : locale.getSnapshot().revision\n if (nextVersion === version && nextRevision === revision) return tabs\n version = nextVersion\n revision = nextRevision\n tabs = ctx.slots.entries(READING_ITEM_SLOT)\n .map(entry => ({\n id: entry.options.id ?? '',\n order: entry.options.order ?? 0,\n label: readLabel(entry.options.label),\n }))\n .sort((left, right) => left.order - right.order)\n return tabs\n },\n subscribe: (listener) => {\n const offSlots = ctx.slots.subscribe(READING_ITEM_SLOT, listener)\n const offLocale = locale?.subscribe(listener)\n return () => {\n offSlots()\n offLocale?.()\n }\n },\n }\n}\n\n/**\n * Page body: one tab per registered card, plus the selected card's panel.\n * The shell supplies the section's own seats and `renderSlot` bound to the\n * child slot declared at registration time.\n */\nexport function ReadingSettingsSection({ renderSlot, readingTabs }: ReadingPageProps) {\n const tabs = useSyncExternalStore(\n readingTabs.subscribe,\n readingTabs.getSnapshot,\n readingTabs.getSnapshot,\n )\n const [requested, setRequested] = useState<string | null>(null)\n const selected = requested !== null && tabs.some(tab => tab.id === requested)\n ? requested\n : tabs[0]?.id ?? null\n if (selected === null) return null\n return createElement(\n 'div',\n null,\n createElement(\n 'div',\n { role: 'tablist', style: TABLIST_STYLE },\n tabs.map(tab => createElement(\n 'button',\n {\n key: tab.id,\n type: 'button',\n role: 'tab',\n 'aria-selected': tab.id === selected,\n style: tab.id === selected ? TAB_ACTIVE_STYLE : TAB_STYLE,\n onClick: () => { setRequested(tab.id) },\n },\n tab.label,\n tab.id === selected ? createElement('span', { style: TAB_MARKER_STYLE, 'aria-hidden': true }) : null,\n )),\n ),\n tabs.map(tab => createElement(\n 'ul',\n {\n key: tab.id,\n role: 'tabpanel',\n hidden: tab.id !== selected,\n style: tab.id === selected ? PANEL_STYLE : PANEL_HIDDEN_STYLE,\n },\n renderSlot(READING_ITEM_SLOT, {}, { only: tab.id }),\n )),\n )\n}\n\n/** Whether a participant already holds the shared page. */\nexport function readingPageClaimed(ctx: ClientContext): boolean {\n return ctx.slots.entries('settings.section').some(entry => entry.options.id === READING_PAGE_ID)\n}\n\n/**\n * Claim the shared page when no participant holds it yet. Call inside\n * `ctx.slots.inject('settings.section', \u2026)`: injection order decides the\n * winner, and the losers stay silent instead of colliding with the kernel's\n * duplicate-id and duplicate-declaration guards.\n * @param ctx - browser context carrying the slot registry.\n * @param label - page label, re-read by the shell on every projection.\n * @param locale - locale namespace the label thunk translates through.\n * @returns The page registration's disposer, or a no-op when another\n * participant already holds the page.\n */\nexport function claimReadingSettingsPage(\n ctx: ClientContext,\n label: () => string,\n locale: keyof LocaleNamespaceMap & string,\n): () => void {\n if (readingPageClaimed(ctx)) return () => {}\n const readingTabs = createReadingTabs(ctx)\n return ctx.slots.register({\n name: 'settings.section',\n id: READING_PAGE_ID,\n order: READING_PAGE_ORDER,\n label,\n locale,\n inject: () => ({ readingTabs }),\n children: { 'reading.settings.item': { kind: 'list', scope: 'root' } },\n }, ReadingSettingsSection)\n}\n", "/** Locale bundles for the chat-translate card inside the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D page. */\n\n/** Dictionary namespace owned by this plugin's settings card. */\nexport const NS = 'settings.chatTranslate';\n\n/**\n * Chinese copy \u2014 the single source of truth for the key set. `{name}` markers\n * are either template params filled through `t(key, params)` / `.replace()`, or\n * inline technical samples rendered by `withSamples` at the call site.\n */\nexport const zh = {\n pageNav: '\u9605\u8BFB\u4F53\u9A8C',\n title: '\u6B63\u6587\u7FFB\u8BD1',\n\n masterTitle: '\u7FFB\u8BD1\u603B\u5F00\u5173',\n enableTranslation: '\u542F\u7528\u7FFB\u8BD1',\n masterDesc:\n '\u81EA\u52A8\u628A\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u7FFB\u6210\u4E2D\u6587\uFF08\u5982 {example}\uFF09\uFF1A\u6298\u53E0\u5757\u91CC\u7684\u8FC7\u7A0B\u6B63\u6587\u4E0E\u6700\u7EC8\u6C47\u603B\u4E00\u89C6\u540C\u4EC1\uFF0C\u6574\u6BB5\u4E2D\u6587\u7684\u5757\u4E5F\u7167\u9001\uFF0C\u7531\u6A21\u578B\u4E00\u5E76\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\u3001\u987A\u6389\u673A\u5668\u8154\u3002Think \u5361\u4E0E\u5DE5\u5177\u8C03\u7528\u884C\u4E0D\u7FFB\uFF0C\u4EE3\u7801\u5757\u539F\u6837\u4FDD\u7559\u3002\u6B63\u6587\u5757\u7684\u5DE6\u7F18\u4E00\u6761\u7AD6\u7EBF\u62A5\u51FA\u72B6\u6001\uFF1A\u6B63\u5728\u8BFB\u8BD1\u6587\u662F\u84DD\u7EBF\uFF0C\u5907\u7740\u8BD1\u6587\u6B63\u8BFB\u539F\u6587\u662F\u7070\u7EC6\u7EBF\uFF0C\u6CA1\u8BD1\u6210\u662F\u7EA2\u5B9E\u7EBF\uFF08\u9F20\u6807\u60AC\u505C\u62A5\u51FA\u901A\u9053\u7EC6\u8282\u2014\u2014\u8D85\u65F6\u3001\u65AD\u6D41\u6216\u7A7A\u8FD4\u56DE\uFF1B\u8BD1\u6587\u7684\u7ED3\u6784\u6F02\u79FB\u7531\u5BBF\u4E3B\u81EA\u52A8\u4FEE\u56DE\u6216\u91CD\u63B7\uFF0C\u4E0D\u518D\u56E0\u683C\u5F0F\u95EE\u9898\u6302\u7EA2\u7EBF\uFF09\uFF0C\u6B63\u5728\u8BF7\u6C42\u662F\u7070\u8109\u52A8\u7EBF\uFF0C\u6CA1\u9001\u8FC7\u6A21\u578B\u6216\u5F00\u5173\u5173\u95ED\u6CA1\u6709\u7EBF\u3002\u70B9\u51FB\u5DF2\u7FFB\u8BD1\u7684\u5757\u5728\u8BD1\u6587\u4E0E\u539F\u6587\u95F4\u5207\u6362\u2014\u2014\u6A21\u578B\u8BA4\u4E3A\u539F\u6837\u6700\u597D\u3001\u8FD4\u56DE\u4E00\u6A21\u4E00\u6837\u7684\u6587\u5B57\u65F6\u540C\u6837\u7B97\u7FFB\u8BD1\u8FC7\u3001\u540C\u6837\u53EF\u5207\uFF1B\u70B9\u51FB\u6302\u7EA2\u7EBF\u7684\u5757\u5219\u6574\u884C\u91CD\u65B0\u7FFB\u8BD1\uFF0C\u5DF2\u6210\u529F\u7684\u5757\u4FDD\u6301\u663E\u793A\u4E0D\u6253\u6270\u3002\u901A\u9053\u5931\u8D25\u4E0D\u81EA\u52A8\u91CD\u8BD5\uFF0C\u6062\u590D\u540E\u7531\u4F60\u7684\u70B9\u51FB\u6551\u6D3B\u3002\u8BD1\u6587\u6309\u539F markdown \u683C\u5F0F\u91CD\u65B0\u6E32\u67D3\uFF0C\u8868\u683C\u3001\u5217\u8868\u3001\u52A0\u7C97\u4E0E\u884C\u5185\u4EE3\u7801\u4FDD\u6301\u7ED3\u6784\u3002\u56DE\u7B54\u8FD8\u5728\u6D41\u5F0F\u8F93\u51FA\u65F6\u4E0D\u7FFB\uFF0C\u843D\u5B9A\u540E\u6309\u9605\u8BFB\u987A\u5E8F\u9010\u6BB5\u51FA\u4E2D\u6587\u3002\u53EA\u4F5C\u7528\u4E8E\u5F53\u524D\u67E5\u770B\u7684\u4F1A\u8BDD\uFF0C\u8BD1\u6587\u4E0D\u5199\u56DE\u4F1A\u8BDD\u4E0A\u4E0B\u6587\u3002\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\u6709\u540C\u6B3E\u5F00\u5173\uFF0C\u4E24\u5904\u72B6\u6001\u540C\u6B65\u3002',\n\n aiTitle: 'AI \u7FFB\u8BD1\uFF08OpenAI \u517C\u5BB9\u534F\u8BAE\uFF09',\n aiRule: '\u53EA\u6709\u8FD9\u4E00\u6761\u901A\u9053\uFF1AKey / Base URL / \u6A21\u578B\u9F50\u5907\u624D\u4F1A\u7FFB\u8BD1\uFF0C\u7F3A\u4EFB\u4F55\u4E00\u9879\u90FD\u4FDD\u6301\u539F\u6587\u3002',\n badgeConfigured: '\u5DF2\u914D\u7F6E',\n badgeUnconfigured: '\u672A\u914D\u7F6E',\n\n apiKeyTitle: 'API Key',\n apiKeyDesc: '\u4FDD\u5B58\u81F3 {home} \u7684 {ref}\uFF0C\u4FDD\u5B58\u540E\u7ACB\u5373\u751F\u6548\uFF1B\u7559\u7A7A\u4FDD\u5B58 = \u6E05\u9664',\n apiKeyPlaceholderConfigured: '\u5DF2\u914D\u7F6E\uFF08\u5982\u9700\u66F4\u6362\u8BF7\u76F4\u63A5\u8F93\u5165\uFF09',\n save: '\u4FDD\u5B58',\n saving: '\u4FDD\u5B58\u4E2D\u2026',\n keySaved: '\u5DF2\u4FDD\u5B58\u5230 {home}\uFF0C\u7ACB\u5373\u751F\u6548',\n keyCleared: '\u5DF2\u6E05\u9664 API Key\uFF08\u672A\u914D\u7F6E\u65F6\u6B63\u6587\u4FDD\u6301\u539F\u6587\uFF0C\u4E0D\u4F1A\u7FFB\u8BD1\uFF09',\n saveFailed: '\u4FDD\u5B58\u5931\u8D25\uFF1A{error}',\n unknownError: '\u672A\u77E5\u9519\u8BEF',\n\n baseUrlTitle: 'Base URL',\n baseUrlDesc: '\u4EFB\u610F OpenAI \u517C\u5BB9\u670D\u52A1\u7AEF\u70B9\uFF0C\u5982 {url1}\u3001{url2}',\n modelTitle: '\u6A21\u578B',\n modelDesc: '\u5982 {model1}\u3001{model2}\uFF1B\u7559\u7A7A\u89C6\u4E3A\u672A\u914D\u7F6E',\n\n timeoutTitle: '\u5355\u6B21\u8BF7\u6C42\u8D85\u65F6',\n timeoutDesc: '\u5355\u4F4D\u6BEB\u79D2\uFF0C\u8303\u56F4 500-900000\u3002\u4E00\u6761\u957F\u56DE\u7B54\u53EF\u80FD\u8981\u51E0\u5206\u949F\uFF0C\u9ED8\u8BA4 600000\u3002',\n\n test: '\u6D4B\u8BD5 AI \u901A\u9053',\n testing: '\u6D4B\u8BD5\u4E2D\u2026',\n testOk: '\u8FDE\u63A5\u6B63\u5E38\uFF0C\u5EF6\u8FDF {latency}ms',\n testFail: '\u5931\u8D25\uFF1A{error}',\n\n behaviorTitle: '\u7FFB\u8BD1\u8303\u56F4',\n behaviorRule:\n '\u52A9\u624B\u56DE\u7B54\u6B63\u6587\u4E00\u5F8B\u7FFB\u8BD1\uFF0C\u5305\u62EC\u6700\u7EC8\u6C47\u603B\u4E0E\u6574\u6BB5\u4E2D\u6587\u7684\u5757\uFF1A\u4E2D\u6587\u5757\u540C\u6837\u4EA4\u7ED9\u6A21\u578B\u6539\u5199\u6210\u81EA\u7136\u4E2D\u6587\uFF0C\u610F\u4E49\u3001\u672F\u8BED\u4E0E\u6570\u5B57\u4E0D\u53D8\uFF1B\u7A7A\u767D\u5757\u4E0D\u9001\u8BD1\u3001\u4E0D\u6302\u7EBF\u3002\u601D\u8003\u94FE\u6B63\u6587\u3001\u5DE5\u5177\u8C03\u7528\u6807\u9898\u4E0E\u6298\u53E0\u6458\u8981\u4E0D\u7FFB\u8BD1\uFF0C\u4EE3\u7801\u5757\u53CA\u5176\u5185\u90E8\u539F\u6837\u4FDD\u7559\u3002',\n\n dockLabel: '\u8BD1',\n dockOn: '\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5F00\u542F\uFF0C\u70B9\u51FB\u5173\u95ED',\n dockOff: '\u6B63\u6587\u7FFB\u8BD1\u5DF2\u5173\u95ED\uFF0C\u70B9\u51FB\u5F00\u542F',\n\n failTitle: '\u7FFB\u8BD1\u5931\u8D25',\n retryAria: '\u7FFB\u8BD1\u5931\u8D25\uFF0C\u6309 Enter \u91CD\u8BD5',\n} as const;\n\n/** Locale keys this plugin renders. */\nexport type ChatTranslateLocaleKey = keyof typeof zh;\n\n/** English copy; the same key set as {@link zh}. */\nexport const en: Record<ChatTranslateLocaleKey, string> = {\n pageNav: 'Reading',\n title: 'Reply translate',\n\n masterTitle: 'Translation master switch',\n enableTranslation: 'Enable translation',\n masterDesc:\n 'Automatically translates assistant reply prose (e.g. {example}) into Chinese: text inside disclosure blocks and the final summary are treated alike, and blocks written entirely in Chinese go through the model too, which rewrites them into natural Chinese and smooths out machine-flavored phrasing. Think cards and tool-call rows are never translated, and code blocks stay untouched. Each prose block reports its state with a left-edge line: blue while its translation reads, thin grey while the original reads with a translation waiting one click away, solid red when translation failed (hover the block to read the channel detail \u2014 a timeout, a broken stream or an empty return; the answer\\'s Markdown shape drift is repaired or re-rolled by the host, so formatting no longer draws a red line), a grey pulse while a request is in flight, and nothing at all when the block was never sent or the switch is off. Click a translated block to switch between translation and original \u2014 even when the model returns the wording unchanged it counts as translated and stays switchable; click a red-lined block to re-run the whole row, with already-successful blocks kept on screen undisturbed. Channel failures are never retried automatically: once the channel recovers, your click revives the row. Translations are re-rendered as Markdown, so tables, lists, bold runs and inline code keep their structure. A reply that is still streaming is left alone until it settles, then prose turns to Chinese paragraph by paragraph in reading order. Applies to the session you are viewing only; translations never enter the conversation context. The same switch sits on the row below the composer, sharing its state.',\n\n aiTitle: 'AI translation (OpenAI-compatible)',\n aiRule:\n 'This is the only channel: translation runs when Key, Base URL and model are all present; missing any one leaves the text as it is.',\n badgeConfigured: 'Configured',\n badgeUnconfigured: 'Not configured',\n\n apiKeyTitle: 'API Key',\n apiKeyDesc: 'Saved as {ref} in {home}; takes effect immediately. Saving an empty value clears it.',\n apiKeyPlaceholderConfigured: 'Configured (type a new value to replace it)',\n save: 'Save',\n saving: 'Saving\u2026',\n keySaved: 'Saved to {home}; takes effect immediately',\n keyCleared: 'API Key cleared (with nothing configured, replies stay in their original language)',\n saveFailed: 'Save failed: {error}',\n unknownError: 'Unknown error',\n\n baseUrlTitle: 'Base URL',\n baseUrlDesc: 'Any OpenAI-compatible endpoint, e.g. {url1} or {url2}',\n modelTitle: 'Model',\n modelDesc: 'e.g. {model1} or {model2}; empty counts as unconfigured',\n\n timeoutTitle: 'Per-request timeout',\n timeoutDesc: 'In milliseconds, range 500-900000. One long reply can take minutes; default 600000.',\n\n test: 'Test the AI channel',\n testing: 'Testing\u2026',\n testOk: 'Connected, {latency}ms latency',\n testFail: 'Failed: {error}',\n\n behaviorTitle: 'What gets translated',\n behaviorRule:\n 'All assistant reply prose, the final summary and Chinese-only blocks included: Chinese blocks are also handed to the model, which rewrites them into natural Chinese while keeping meaning, terms and numbers unchanged; blank blocks are never sent and never marked. Think-chain text, tool-call titles and folded summaries are never translated; code blocks and their contents stay untouched.',\n\n dockLabel: 'Translate',\n dockOn: 'Translation is on; click to turn it off',\n dockOff: 'Translation is off; click to turn it on',\n\n failTitle: 'Translation failed',\n retryAria: 'Translation failed \u2014 press Enter to retry',\n};\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'settings.chatTranslate': ChatTranslateLocaleKey;\n }\n}\n\n/**\n * \u52A9\u624B\u884C\u6E32\u67D3\u5668\u7684\u81EA\u5E26\u6587\u6848\u901A\u9053\uFF1A\u884C\u7684 `t` \u5EA7\u6B21\u662F\u5BBF\u4E3B `chat` \u8BCD\u5178\uFF08chrome \u6587\u6848\n * \u4E0E\u5BBF\u4E3B\u9010\u5B57\u4E00\u81F4\uFF09\uFF0C\u63D2\u4EF6\u79C1\u6709\u7684\u300C\u91CD\u8BD5\u300D\u8BCD\u6C47\u4E0D\u5728\u5176\u4E2D\u3002\u5BA2\u6237\u7AEF\u542F\u52A8\u65F6\u628A\u672C\u547D\u540D\n * \u7A7A\u95F4\u7684\u6D3B\u7FFB\u8BD1\u5668\u7ED1\u8FDB\u6765\uFF08`locale.bind(NS)`\uFF0C\u8BED\u8A00\u5207\u6362\u5373\u968F\u52A8\uFF09\uFF0C\u6E32\u67D3\u5C42\u5728\u6E32\u67D3\u65F6\n * \u7ECF {@link rowCopy} \u53D6\u3002\u672A\u7ED1\u5B9A\uFF08\u5355\u6D4B/\u65E0 locale \u670D\u52A1\uFF09\u56DE\u9000 zh \u5B57\u9762\u91CF\uFF0C\u4E0E\u5BBF\u4E3B\n * \u9ED8\u8BA4\u8BED\u8A00\u4E00\u81F4\u3002\n */\nlet rowT: (key: ChatTranslateLocaleKey) => string = (key) => zh[key] ?? key;\n\nexport function bindRowCopy(\n locale: { bind?(namespace: string): (key: string) => string } | null | undefined\n): void {\n if (locale && typeof locale.bind === 'function') {\n const bound = locale.bind(NS);\n if (typeof bound === 'function') rowT = bound as typeof rowT;\n }\n}\n\nexport function rowCopy(): {\n retryAria: string;\n /** \u5931\u8D25\u5757\u60AC\u505C\u6587\u6848\uFF1A\u672C\u5730\u5316\u6807\u7B7E + \u670D\u52A1\u7AEF\u539F\u6837\u5E26\u6765\u7684\u6280\u672F\u4E00\u53E5\uFF08\u901A\u9053\u4F24\uFF09\u3002 */\n failTitle: (detail?: string | null) => string;\n} {\n return {\n retryAria: rowT('retryAria'),\n failTitle: (detail) => {\n const label = rowT('failTitle');\n return detail === undefined || detail === '' || detail === null ? label : `${label} \u2014 ${detail}`;\n },\n };\n}\n", "import * as React from 'react';\nimport { useEffect, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';\nimport { Pill, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';\nimport { settingsStore, type ClientSettingsState } from '../settings/store.ts';\nimport { NS } from '../locales.ts';\n\n/**\n * `t` \u7531\u6CE8\u518C\u9879\u7684 `locale: NS` \u6CE8\u5165\uFF0C\u7C7B\u578B\u8D70\u5171\u4EAB\u7684 `PropsLocale`\u3002owner props\n * \uFF08`PropsRuntime<'conversation.composer.dock'>`\uFF09\u7684\u7C7B\u578B\u968F\n * `@deepseek-ai/dsh-client-ui-conversation` \u58F0\u660E\uFF0C\u672C\u5305\u4E0D\u4F9D\u8D56\u5B83\uFF08\u4E0E dsh-a6api\n * \u540C\u6CD5\uFF09\uFF0C\u7EC4\u4EF6\u4E5F\u4E0D\u8BFB\u90A3\u4E9B prop\u3002\n */\nexport type DockToggleProps = PropsLocale<typeof NS>;\n\n/**\n * \u5BF9\u8BDD\u8F93\u5165\u6846\u4E0B\u65B9\u90A3\u4E00\u884C\u7684\u7FFB\u8BD1\u5F00\u5173\uFF1A\u4E00\u4E2A\u80F6\u56CA\u6309\u94AE\uFF0C\u70B9\u4EAE\u8868\u793A\u6B63\u6587\u7FFB\u8BD1\u5F00\u7740\u3002\n * \u5B83\u4E0E\u8BBE\u7F6E\u9875\u300C\u603B\u5F00\u5173\u300D\u662F\u540C\u4E00\u4E2A `enabled` \u5B57\u6BB5\u2014\u2014\u70B9\u51FB\u5199 profile \u914D\u7F6E\uFF0C\u4E24\u5904\n * \u72B6\u6001\u7531\u540C\u4E00\u4E2A store \u540C\u6B65\uFF1B\u5173\u95ED\u4E0E\u91CD\u5F00\u7684\u884C\u4E3A\uFF08\u8FD8\u539F\u82F1\u6587\u3001\u91CD\u626B\u8865\u7FFB\uFF09\u4E5F\u8D70\n * \u540C\u4E00\u6761\u94FE\u8DEF\u3002Key \u6CA1\u914D\u9F50\u65F6\u7167\u5E38\u53EF\u5207\u6362\uFF1A\u7F3A\u914D\u7F6E\u53EA\u662F\u4E0D\u4EA7\u751F\u7FFB\u8BD1\uFF0C\u754C\u9762\u65E0\u7279\u6B8A\u6001\u3002\n */\nexport function ComposerTranslateToggle(props: DockToggleProps): ReactElement {\n const { t } = props;\n const [state, setState] = useState<ClientSettingsState>(() => settingsStore.getState());\n\n useEffect(() => settingsStore.subscribe(() => setState(settingsStore.getState())), []);\n\n return (\n <Tooltip label={() => (state.enabled ? t('dockOn') : t('dockOff'))} side=\"top\" align=\"end\" portal>\n <Pill\n active={state.enabled}\n onClick={() => void settingsStore.update({ enabled: !settingsStore.getState().enabled })}\n >\n {t('dockLabel')}\n </Pill>\n </Tooltip>\n );\n}\n\n/**\n * \u628A\u5F00\u5173\u6CE8\u518C\u8FDB `conversation.composer.dock`\u2014\u2014ui-conversation \u58F0\u660E\u7684\n * \u300C\u8F93\u5165\u6846\u5361\u7247\u4E0B\u65B9\u90A3\u4E00\u884C\u300D\u3002\u8BE5 slot \u53EA\u5728\u4F1A\u8BDD\u5F62\u6001\u6E32\u67D3\uFF0C\u65B0\u5EFA\u4F1A\u8BDD\u7684 Hero \u5F62\u6001\n * \u6CA1\u6709\u8FD9\u4E00\u884C\uFF0C\u6309\u94AE\u968F\u4E4B\u4E0D\u51FA\u73B0\u3002`slots.inject` \u7B49\u58F0\u660E\u843D\u5730\u518D\u6CE8\u518C\uFF0C\u58F0\u660E\u584C\u7F29\u65F6\n * \u6CE8\u518C\u4E00\u8D77\u9000\u573A\u3002\n * @param ctx - DSH browser client context\uFF08`slots` \u5DF2\u5728\u5BA2\u6237\u7AEF\u534A\u8FB9\u58F0\u660E\u6CE8\u5165\uFF09\u3002\n * @returns \u64A4\u9500\u672C\u6B21\u8D21\u732E\u7684 disposer\u3002\n */\nexport function setupComposerToggle(ctx: any): () => void {\n if (typeof window === 'undefined') return () => {};\n const slots = ctx?.slots;\n if (!slots || typeof slots.inject !== 'function') return () => {};\n return slots.inject('conversation.composer.dock', () =>\n slots.register(\n {\n name: 'conversation.composer.dock',\n id: 'dsh-chat-translate-toggle',\n // -2 \u6392\u5728 dsh-a6api \u80F6\u56CA\uFF08-1\uFF09\u7684\u5DE6\u4FA7\uFF0C\u6574\u884C\u6700\u5DE6\uFF1B\u5B98\u65B9\u7EDF\u8BA1\u80F6\u56CA\u662F 0\u3002\n order: -2,\n locale: NS,\n },\n ComposerTranslateToggle\n )\n );\n}\n", "/**\n * \u52A9\u624B\u884C\u6E32\u67D3\u5668\uFF1A\u7ECF keyed slot `conversation.chat.node`\uFF08key `assistant-step`\uFF09\n * \u66FF\u6362\u5BBF\u4E3B\u9ED8\u8BA4\u7684\u884C\u6E32\u67D3\u3002\n *\n * \u63A5\u7BA1\u6362\u6765\u4E24\u4EF6\u4E8B\u2014\u2014\n * 1. \u6B63\u6587\u5757\u4ECE\u6570\u636E\u5C42\u62FF**\u539F\u59CB markdown**\uFF08`AssistantBlock.text`\uFF09\uFF0C\u8BD1\u6587\u540C\u6837\u6309\n * markdown \u4EA4\u7ED9\u5BBF\u4E3B\u516C\u5F00\u57FA\u7EBF\u7EC4\u4EF6 `MarkdownText` \u91CD\u6E32\u67D3\uFF1A\u683C\u5F0F\u4FDD\u771F\u7531\u5B98\u65B9\n * \u6E32\u67D3\u5668\u8D1F\u8D23\uFF0C\u672C\u6587\u4EF6\u4E0D\u4EA7\u751F\u4EFB\u4F55\u81EA\u62FC markup\u3002\n * 2. \u300C\u5DF2\u7FFB\u8BD1/\u6CA1\u8BD1\u6210\u300D\u662F\u6E32\u67D3\u72B6\u6001\uFF1A\u843D\u5B9A\u3001\u5F00\u5173\u5F00\u542F\u3001\u8FDB\u5165\u89C6\u53E3\u540E\u9010\u884C\u9996\u8DD1\uFF1B\u6210\u529F\n * \u7684\u5757\uFF08\u5305\u62EC\u6A21\u578B\u8BA4\u4E3A\u539F\u6837\u6700\u597D\u7684\u5757\uFF09\u6302\u8BD1\u6587\u3001\u5DE6\u7F18\u84DD\u7EBF\u3001\u70B9\u51FB\u5728\u8BD1\u6587\u4E0E\u539F\u6587\u95F4\n * \u5207\u6362\uFF1B\u5931\u8D25\u7684\u5757\u4FDD\u6301\u539F\u6587\u3001\u5DE6\u7F18\u6302\u7EA2\u5B9E\u7EBF\uFF08\u60AC\u505C\u62A5\u51FA\u8D25\u56E0\uFF09\uFF0C\u6574\u5757\u53EF\u70B9=\u624B\u52A8\n * \u6574\u884C\u8865\u8DD1\uFF0C\u65E0\u9650\u6B21\u3001\u65E0\u81EA\u52A8\u91CD\u8BD5\uFF1B\u5DF2\u767B\u8BB0\u5728\u9014\u800C\u5C1A\u65E0\u7ED3\u679C\u7684\u5757\u663E\u7070\u8109\u52A8\u3002\n *\n * \u9664\u6B63\u6587\u5916\u7684\u884C\u5185\u5BB9\u4E0E\u5BBF\u4E3B\u9010\u5206\u652F\u7B49\u4EF7\uFF1Areasoning \u884C\u8D70\u6298\u53E0\uFF08\u542B Turn-process\n * \u9690\u85CF\u4E0E beforematch \u63ED\u793A\uFF09\u3001\u8FDE\u7EED image \u7EC4\u4EA4\u56DE owner \u7684 renderMessageImages\u3001\n * \u672A\u77E5\u5757\u843D JsonBlock\u3001tool-call \u5728\u52A9\u624B\u884C\u5185\u8DF3\u8FC7\uFF08\u5DE5\u5177\u884C\u7531\u5BBF\u4E3B\u72EC\u7ACB\u6E32\u67D3\uFF09\u3001\n * \u4E2D\u65AD\u524D\u7F00\u884C\u5C3E\u5E26\u300C\u5DF2\u505C\u6B62\u300D\u3002props \u5F62\u72B6\u662F\u5BBF\u4E3B contract \u7684\u7ED3\u6784\u6027\u955C\u50CF\uFF1A\u503C\u8FB9\n * \u4E0D\u8DE8\u5305\uFF08\u5BA2\u6237\u7AEF bundle \u7EAF\u51C0\u95E8\u7981\uFF09\uFF0C\u7C7B\u578B\u6CE8\u91CA\u6307\u660E\u5BF9\u5E94\u58F0\u660E\u3002\n * \u672C\u6E32\u67D3\u5668\u629B\u9519\u65F6\u6309 keyed slot \u7684\u9000\u4F4D\u8BED\u4E49\u9000\u51FA\u8BE5 cell\uFF0C\u5BBF\u4E3B\u539F\u6E32\u67D3\u5668\u56DE\u4F4D\u3002\n */\nimport * as React from 'react';\nimport { memo, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n DisclosureRow,\n IconRefreshOutlineRegular,\n IconThinkOutlineRegular,\n JsonBlock,\n MarkdownText,\n Tooltip,\n} from '@deepseek-ai/dsh-client-ui-primitives';\nimport type {\n MarkdownCodeLabels,\n MarkdownFileMentions,\n MarkdownLabels,\n MarkdownPathImages,\n} from '@deepseek-ai/dsh-client-ui-primitives';\nimport { settingsStore } from '../settings/store.ts';\nimport { planAssistantRow } from './row-plan.ts';\nimport { isBareBlockClick } from './click-guard.ts';\nimport { rowCopy } from '../locales.ts';\nimport type { AssistantBlockLike } from './row-plan.ts';\nimport type { ProseMark } from './styles.ts';\nimport { ensureAssistantStyles, proseAction, proseClassNames } from './styles.ts';\nimport { chatTranslate, sameTexts } from './translate-store.ts';\n\n// ---- \u5BBF\u4E3B contract \u7684\u7ED3\u6784\u6027\u955C\u50CF\uFF08source of truth: ui-chat slots.d.ts\uFF09 ----\n\ninterface DisclosureState {\n readonly expanded: boolean;\n readonly setExpanded: (open: boolean) => void;\n readonly toggle: () => void;\n}\n\ninterface PresentationLike {\n readonly settledReasoningPreview: boolean;\n}\n\ninterface NodeDataLike {\n readonly status: string;\n readonly turn: number;\n readonly step: number;\n readonly blocks: readonly AssistantBlockLike[];\n readonly finalNode?: { readonly seq: number };\n}\n\ninterface TurnProcessLike {\n readonly foldable: boolean;\n readonly open: boolean;\n readonly spec: { readonly answerStep: number; readonly inlineReasoning: boolean };\n readonly setOpen: (open: boolean) => void;\n}\n\nexport interface AssistantStepProps {\n readonly node: {\n readonly data: NodeDataLike;\n readonly location?: { readonly kind?: string; readonly turn?: { readonly status?: string } };\n readonly anchorSeq?: number;\n };\n readonly groupPart?: string;\n readonly useDisclosure: () => DisclosureState;\n readonly useTurnData: (key: string) => unknown;\n readonly turnProcess?: TurnProcessLike;\n readonly openFile: (path: string, options?: { line?: number }) => void;\n readonly renderMessageImages: (owner: {\n readonly images: readonly { readonly attachment: unknown }[];\n readonly align?: string;\n }) => ReactNode;\n readonly fileMentions: (owner: unknown) => MarkdownFileMentions | undefined;\n readonly usePresentation: <R>(select: (policy: PresentationLike) => R) => R;\n readonly t: (key: string, params?: Record<string, unknown>) => string;\n}\n\n// ---- \u4E0E\u5BBF\u4E3B\u9010\u5B57\u5BF9\u9F50\u7684\u5C0F\u4EF6 ----\n\n// \u56FE\u6807\u5728\u6E32\u67D3\u65F6\u521B\u5EFA\uFF0C\u6A21\u5757\u9876\u5C42\u4E0D\u89E6\u78B0\u8FD0\u884C\u65F6\u7EC4\u4EF6\u3002\nconst thinkIcon = (): ReactElement => React.createElement(IconThinkOutlineRegular, { size: 14 });\nconst NOOP = (): void => {};\n\nfunction markdownLabels(t: (key: string, params?: Record<string, unknown>) => string): MarkdownLabels {\n return {\n code: {\n copyLabel: t('copy'),\n copiedLabel: t('copied'),\n toolbarLabels: {\n codeLabel: t('codeBlock.title'),\n wrapLabel: t('codeBlock.wrap'),\n unwrapLabel: t('codeBlock.unwrap'),\n },\n } as MarkdownCodeLabels,\n footnotes: t('markdown.footnotes'),\n };\n}\n\nfunction isWindowsStylePath(value: string): boolean {\n return /^[A-Za-z]:[/\\\\]/.test(value) || value.startsWith('\\\\\\\\');\n}\n\nfunction isAbsoluteWorkspacePath(path: string): boolean {\n return path.startsWith('/') || isWindowsStylePath(path);\n}\n\n/** \u672C\u5730\u7EDD\u5BF9\u8DEF\u5F84\u7ECF\u8BA4\u8BC1\u7684 file \u8DEF\u7531\u5BFB\u5740\uFF08\u5BBF\u4E3B fileMediaUrl \u7B49\u4EF7\uFF09\u3002 */\nfunction fileMediaUrl(base: string, path: string): string | undefined {\n if ((!/^https?:/u.test(base) && !base.startsWith('dsh-app://app/')) || !isAbsoluteWorkspacePath(path)) return void 0;\n if (/^[/\\\\]{2}/u.test(path) || /[\\u0000-\\u001f\\u007f]/u.test(path)) return void 0;\n return new URL(`api/file?path=${encodeURIComponent(path)}`, base).href;\n}\n\nfunction localPathMediaUrl(base: string, value: string): string | undefined {\n let path: string;\n try {\n path = decodeURIComponent(value.split(/[?#]/u)[0] ?? '');\n } catch {\n return void 0;\n }\n return fileMediaUrl(base, path);\n}\n\nfunction firstLine(text: string): string {\n const newline = text.indexOf('\\n');\n return newline === -1 ? text : text.slice(0, newline);\n}\n\nfunction latestCompletedParagraphFirstLine(text: string): string {\n let summary = '';\n let paragraphStart = 0;\n const separator = /\\r?\\n(?:[\\t ]*\\r?\\n)+/g;\n for (;;) {\n const nextParagraph = separator.exec(text);\n const paragraphEnd = nextParagraph === null ? text.length : nextParagraph.index + nextParagraph[0].indexOf('\\n');\n const newline = text.indexOf('\\n', paragraphStart);\n if (newline !== -1 && newline <= paragraphEnd) {\n const candidate = text.slice(paragraphStart, newline).trim();\n if (candidate !== '') summary = candidate;\n }\n if (nextParagraph === null) return summary;\n paragraphStart = nextParagraph.index + nextParagraph[0].length;\n }\n}\n\n/** \u6298\u53E0\u7684\u9690\u85CF\u8282\u70B9\u5BF9\u9875\u5185\u641C\u7D22\u4FDD\u6301\u53EF\u8FBE\uFF1A\u547D\u4E2D\u524D\u7528 beforematch \u63ED\u793A\uFF08\u5BBF\u4E3B\u7B49\u4EF7\uFF09\u3002 */\nfunction useSearchableHidden(hidden: boolean, reveal: () => void): React.RefObject<HTMLDivElement | null> {\n const ref = React.useRef<HTMLDivElement | null>(null);\n React.useLayoutEffect(() => {\n const element = ref.current;\n if (element === null) return;\n if (hidden && element.contains(element.ownerDocument.activeElement)) {\n reveal();\n return;\n }\n if (hidden) element.setAttribute('hidden', 'until-found');\n else element.removeAttribute('hidden');\n }, [hidden, reveal]);\n React.useEffect(() => {\n const element = ref.current;\n if (element === null) return;\n element.addEventListener('beforematch', reveal);\n return () => {\n element.removeEventListener('beforematch', reveal);\n };\n }, [reveal]);\n return ref;\n}\n\nconst subscribeSettings = (listener: () => void): (() => void) => settingsStore.subscribe(listener);\nconst subscribeTranslate = (listener: () => void): (() => void) => chatTranslate.subscribe(listener);\n\n/**\n * \u884C\u7684\u89C6\u53E3\u5728\u573A\u72B6\u6001\uFF08150px \u7F13\u51B2\uFF09\uFF1A\u8FDB\u5165\u89C6\u53E3\u624D\u653E\u884C\u9996\u8DD1\u8BF7\u6C42\u3002\u843D\u5B9A\u540E\u7684\u884C\u4E0D\u518D\n * \u6709\u81EA\u52A8\u91CD\u8D70\u2014\u2014\u6EDA\u51FA\u518D\u56DE\u6765\u53EA\u89E6\u53D1 ensure \u7684\u540C\u4EE3\u77ED\u8DEF\uFF08pending/settled \u90FD\u4E0D\u53D1\n * \u7B2C\u4E8C\u4E2A\u8BF7\u6C42\uFF09\uFF0C\u5931\u8D25\u7684\u6551\u6D3B\u53EA\u7531\u5757\u4E0A\u7684\u70B9\u51FB\u53D1\u8D77\u3002\n */\nfunction useSettledInView(ref: React.RefObject<HTMLElement | null>, armed: boolean): boolean {\n const noObserver = typeof IntersectionObserver === 'undefined';\n const [inView, setInView] = useState<boolean>(noObserver);\n useEffect(() => {\n if (noObserver || !armed) return;\n const element = ref.current;\n if (element === null) return;\n const observer = new IntersectionObserver(\n (entries) => {\n setInView(entries.some((entry) => entry.isIntersecting));\n },\n { rootMargin: '150px' }\n );\n observer.observe(element);\n return () => {\n observer.disconnect();\n };\n }, [armed, noObserver, ref]);\n return inView;\n}\n\n// ---- \u601D\u8003\u884C\uFF08\u5BBF\u4E3B ReasoningRow \u7B49\u4EF7\uFF0C\u7C7B\u540D\u6362\u672C\u63D2\u4EF6\u524D\u7F00\uFF09 ----\n\ninterface ReasoningRowProps {\n text: string;\n running: boolean;\n usePresentation: <R>(select: (policy: PresentationLike) => R) => R;\n useDisclosure: () => DisclosureState;\n t: (key: string, params?: Record<string, unknown>) => string;\n}\n\nconst ReasoningRow = memo(function ReasoningRow({\n text,\n running,\n usePresentation,\n useDisclosure,\n t,\n}: ReasoningRowProps): ReactElement {\n const { expanded, toggle } = useDisclosure();\n const labels = useMemo(() => markdownLabels(t), [t]);\n const summaryText = running ? latestCompletedParagraphFirstLine(text) : firstLine(text);\n const summary = useMemo(() => summaryText.replaceAll('**', ''), [summaryText]);\n const preview = usePresentation(\n (policy) => !expanded && summary !== '' && (running || policy.settledReasoningPreview)\n );\n const collapsedContent = useMemo(\n () =>\n React.createElement(\n React.Fragment,\n null,\n React.createElement('span', { className: 'dsh-ct-think-separator', 'aria-hidden': true }),\n React.createElement(\n 'span',\n { className: 'dsh-ct-think-summary', 'data-streaming': running || void 0 },\n React.createElement('span', { className: 'dsh-ct-think-summary-text' }, summary)\n )\n ),\n [running, summary]\n );\n const content = useMemo(\n () =>\n expanded\n ? React.createElement(\n 'div',\n { className: 'dsh-ct-think-body' },\n React.createElement(MarkdownText, { text, streaming: running, labels, variant: 'compact' })\n )\n : void 0,\n [expanded, labels, running, text]\n );\n return React.createElement(\n 'div',\n {\n className: 'dsh-ct-think-root',\n 'data-variant': 'think',\n 'data-state': running ? 'running' : 'ok',\n 'data-expanded': expanded || void 0,\n 'data-preview': preview || void 0,\n },\n running && React.createElement('span', { className: 'dsh-ct-visually-hidden' }, t('row.running')),\n React.createElement(\n DisclosureRow,\n {\n rowClassName: 'dsh-ct-think-row',\n leadingClassName: 'dsh-ct-think-leading',\n titleClassName: 'dsh-ct-think-title',\n chevronClassName: 'dsh-ct-think-chevron',\n icon: thinkIcon(),\n title: t('message.think'),\n open: expanded,\n expandable: true,\n expandOnRowClick: true,\n onToggle: toggle,\n collapsedContent,\n },\n content\n )\n );\n});\n\n// ---- \u6B63\u6587\u5757\uFF1A\u8BD1\u6587/\u539F\u6587\u5207\u6362\u4E0E\u5931\u8D25\u91CD\u8BD5\u5171\u7528\u4E00\u4E2A\u58F3 ----\n\ninterface ProseBlockProps {\n text: string;\n /** \u6210\u529F\u843D\u5B9A\u7684\u4E2D\u6587\u6E90\u6587\u672C\uFF08\u4EC5 mark='translated' \u65F6\u4F5C\u4E3A\u6E32\u67D3\u6E90\uFF09\u3002 */\n translated: string | null;\n /** \u5DE6\u7F18\u7EBF\u72B6\u6001\uFF0C\u5355\u70B9\u7B97\u81EA row-plan\uFF1B\u53EF\u70B9\u6027\u3001\u91CD\u8BD5\u6307\u5F15\u3001\u8109\u52A8\u5168\u7531\u5B83\u51B3\u5B9A\u3002 */\n mark: ProseMark;\n /** \u4EC5 mark='failed' \u65F6\u975E null\uFF1A\u60AC\u505C\u6587\u6848\u7684\u6280\u672F\u7EC6\u8282\uFF08\u901A\u9053\u4F24\u4E00\u53E5\uFF09\u3002 */\n fail: string | null;\n onToggle: () => void;\n onRetry: () => void;\n streaming: boolean;\n labels: MarkdownLabels;\n mentions: MarkdownFileMentions | undefined;\n pathImages: MarkdownPathImages;\n}\n\n/**\n * \u70B9\u51FB\u5224\u5B9A\uFF1A\u843D\u5728\u5757\u5185\u4EA4\u4E92\u5143\u7D20\uFF08\u94FE\u63A5\u3001\u4EE3\u7801\u5757\u590D\u5236\u94AE\u7B49\uFF09\u4E0A\u7684\u70B9\u51FB\u5F52\u90A3\u4E2A\u5143\u7D20\uFF0C\n * \u4E0D\u8FDE\u5E26\u5207\u5757/\u91CD\u8BD5\uFF1B\u62D6\u9009\u8BD1\u6587\u677E\u624B\u4EA7\u751F\u7684 click \u540C\u6837\u5FFD\u7565\u3002\n */\nfunction blockClick(event: React.MouseEvent<HTMLDivElement>, action: () => void): void {\n const guardEvent = {\n target: event.target as { closest?(selector: string): unknown } | null,\n currentTarget: event.currentTarget,\n };\n if (isBareBlockClick(guardEvent, typeof window === 'undefined' ? undefined : window)) action();\n}\n\nfunction ProseBlock({\n text,\n translated,\n mark,\n fail,\n onToggle,\n onRetry,\n streaming,\n labels,\n mentions,\n pathImages,\n}: ProseBlockProps): ReactElement {\n const action = proseAction(mark);\n const clickable = action !== null;\n const retrying = action === 'retry';\n const handler = retrying ? onRetry : onToggle;\n const source = mark === 'translated' ? (translated as string) : text;\n const copy = rowCopy();\n const block = React.createElement(\n 'div',\n {\n className: proseClassNames(mark),\n 'data-translated': mark === 'translated' ? 'true' : void 0,\n // \u5BB9\u5668\u5185\u542B\u94FE\u63A5\u7B49\u4EA4\u4E92\u5185\u5BB9\uFF0C\u4E0D\u5957 role=button\uFF08\u975E\u6CD5\u5D4C\u5957\uFF09\uFF1B\u53EF\u805A\u7126 + \u952E\u76D8\n // Enter/Space \u5373\u5207\u6362/\u91CD\u8BD5\uFF0C\u6EE1\u8DB3\u5B98\u65B9\u300C\u952E\u76D8\u53EF\u8FBE\u300D\u95E8\u3002\u5728\u9014\u5757\u4E5F\u4FDD\u6301\u53EF\u805A\u7126\n // \uFF08Enter \u7A7A\u64CD\u4F5C\uFF09\u2014\u2014\u8865\u8DD1\u628A\u7EA2\u7EBF\u6298\u6210\u8109\u52A8\u65F6\uFF0C\u952E\u76D8\u7126\u70B9\u4E0D\u81F4\u4ECE\u884C\u4E0A\u6389\u56DE body\u3002\n tabIndex: clickable || mark === 'inflight' ? 0 : void 0,\n onClick: clickable ? (event: React.MouseEvent<HTMLDivElement>) => blockClick(event, handler) : void 0,\n onKeyDown: clickable\n ? (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.target !== event.currentTarget) return;\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handler();\n }\n }\n : void 0,\n },\n retrying &&\n React.createElement(\n 'span',\n { className: 'dsh-ct-retry', 'aria-hidden': true },\n React.createElement(IconRefreshOutlineRegular, { size: 10 })\n ),\n retrying && React.createElement('span', { className: 'dsh-ct-visually-hidden' }, copy.retryAria),\n React.createElement(MarkdownText, {\n text: source,\n streaming,\n labels,\n fileMentions: mentions,\n pathImages,\n })\n );\n if (!retrying) return block;\n // Tooltip \u951A\u5728\u5931\u8D25\u5757\u672C\u8EAB\uFF1A\u6B63\u6587\u4EFB\u610F\u5904\u60AC\u505C\u3001\u952E\u76D8\u805A\u7126\u90FD\u5373\u523B\u51FA\u6CE1\u2014\u2014\u951A\u5728\u90A3\u6761\n // 10px \u88C5\u9970\u56FE\u6807\u4E0A\u5219\u4E24\u4E2A\u901A\u9053\u90FD\u591F\u4E0D\u7740\uFF08\u56FE\u6807 aria-hidden\u3001\u4ECE\u4E0D\u63A5\u6536\u7126\u70B9\uFF09\u3002\u6CE1\u91CC\n // \u62A5\u7684\u662F\u8D25\u56E0\uFF08\u672C\u5730\u5316\u6807\u7B7E \u2014 \u670D\u52A1\u7AEF\u6280\u672F\u7EC6\u8282\uFF09\uFF0C\u4E0D\u518D\u662F\u300C\u600E\u4E48\u91CD\u8BD5\u300D\u7684\u6307\u5F15\u3002\n return React.createElement(Tooltip, {\n label: () => copy.failTitle(fail),\n side: 'right',\n portal: true,\n children: block as Parameters<typeof Tooltip>[0]['children'],\n });\n}\n\n// ---- \u52A9\u624B\u884C\u672C\u4F53 ----\n\nexport const AssistantStepView = memo(function AssistantStepView(props: AssistantStepProps): ReactElement | null {\n ensureAssistantStyles();\n const { node, groupPart, useDisclosure, useTurnData, turnProcess, openFile, renderMessageImages, fileMentions, usePresentation, t } = props;\n const data = node.data;\n const streaming = data.status === 'running';\n const interrupted = data.status === 'interrupted';\n\n const enabled = useSyncExternalStore(subscribeSettings, () => settingsStore.getState().enabled);\n const aiConfigured = useSyncExternalStore(subscribeSettings, () => settingsStore.getState().aiConfigured);\n useSyncExternalStore(subscribeTranslate, () => chatTranslate.getVersion());\n\n const canTranslate = enabled && aiConfigured;\n const rowKey = `a${node.anchorSeq ?? `t${data.turn}:s${data.step}`}`;\n\n const labels = useMemo(() => markdownLabels(t), [t]);\n const pathImages = useMemo<MarkdownPathImages>(\n () => ({ resolve: (value: string) => localPathMediaUrl(document.baseURI, value) }),\n []\n );\n\n // mentions \u7684 owner \u5224\u5B9A\u4E0E\u5BBF\u4E3B\u9010\u884C\u7B49\u4EF7\uFF1A\u5C01\u95ED Turn \u7684\u6536\u5C3E\u6D88\u606F\u624D\u6709\u6587\u4EF6\u63D0\u53CA\u8BCD\u8868\u3002\n const turn = node.location?.kind === 'turn' || node.location?.kind === 'step' ? node.location.turn : undefined;\n const tail = useTurnData('turn-tail') as\n | { closing?: { finalNode?: { seq: number } } }\n | undefined;\n const owner = useMemo(() => {\n if (turn?.status !== 'closed' || data.finalNode === void 0) return void 0;\n if (tail?.closing?.finalNode?.seq !== data.finalNode.seq) return void 0;\n return { turn, seq: data.finalNode.seq, openFile };\n }, [data.finalNode, openFile, tail, turn]);\n const mentions = useMemo(() => (owner === void 0 ? void 0 : fileMentions(owner)), [fileMentions, owner]);\n\n // \u8BA1\u5212\u5C42\u7ED9\u51FA\u6587\u672C\u6E05\u5355\u4E0E\u6E32\u67D3\u5206\u652F\uFF1B\u884C\u6E32\u67D3\u5148\u4E8E\u6302\u8F7D\uFF0C\u6C60\u6309\u5F53\u524D\u6587\u672C\u767B\u8BB0\u3002\n const blocks = useMemo(() => data.blocks ?? [], [data.blocks]);\n const plan = useMemo(\n () =>\n planAssistantRow({\n blocks,\n streaming,\n interrupted,\n groupPart,\n canTranslate,\n outcomes: undefined,\n }),\n [blocks, streaming, interrupted, groupPart, canTranslate]\n );\n\n const rootRef = useRef<HTMLDivElement | null>(null);\n const inView = useSettledInView(rootRef, !streaming && canTranslate && plan.entries !== null && plan.texts.length > 0);\n\n useEffect(() => {\n if (streaming || !canTranslate || !inView || plan.entries === null || plan.texts.length === 0) return;\n chatTranslate.ensure(rowKey, plan.texts);\n }, [streaming, canTranslate, inView, rowKey, plan]);\n\n const [originalKeys, setOriginalKeys] = useState<ReadonlySet<number>>(() => new Set());\n\n // \u53EA\u8BA4\u540C\u4EE3\u767B\u8BB0\uFF1ArowKey \u662F\u4F1A\u8BDD\u5185\u951A\u70B9\uFF0C\u8DE8\u4F1A\u8BDD\u53EF\u80FD\u91CD\u53F7\u2014\u2014texts \u9010\u4F4D\u76F8\u7B49\u624D\u628A\n // \u65E7 outcomes \u6302\u4E0A\u6765\uFF0C\u6362\u4EE3\u7684\u90A3\u4E00\u5E27\uFF08\u542B\u6362\u4F1A\u8BDD\u649E\u952E\uFF09\u6309\u300C\u672A\u767B\u8BB0\u300D\u6E32\u67D3\uFF0C\u7EAF\u539F\u6587\n // \u65E0\u7EBF\u65E0\u8109\u52A8\uFF0C\u968F\u540E ensure \u91CD\u65B0\u767B\u8BB0\u3002\n const registered = chatTranslate.getState(rowKey);\n const rowState = registered !== undefined && sameTexts(registered.texts, plan.texts) ? registered : undefined;\n const displayed = useMemo(\n () =>\n planAssistantRow({\n blocks,\n streaming,\n interrupted,\n groupPart,\n canTranslate,\n outcomes: rowState?.outcomes,\n rowStatus: rowState?.status,\n originalKeys,\n }),\n [blocks, streaming, interrupted, groupPart, canTranslate, rowState, originalKeys]\n );\n\n const reasoningHidden =\n turnProcess !== void 0 &&\n turnProcess.foldable &&\n turnProcess.spec.answerStep === data.step &&\n turnProcess.spec.inlineReasoning &&\n !turnProcess.open;\n const revealProcess = useCallback(() => {\n turnProcess?.setOpen(true);\n }, [turnProcess]);\n\n const toggleBlock = useCallback((key: number) => {\n setOriginalKeys((current) => {\n const next = new Set(current);\n if (next.has(key)) next.delete(key);\n else next.add(key);\n return next;\n });\n }, []);\n\n // \u624B\u52A8\u8865\u8DD1\uFF1A\u70B9\u4EFB\u4E00\u7EA2\u7EBF\u5757 = \u6574\u884C\u91CD\u53D1\uFF08\u65E0\u9650\u6B21\u3001\u4E0D\u8BBE\u989D\u5EA6\uFF09\uFF1B\u5728\u9014\u518D\u70B9\u662F\u7A7A\n // \u64CD\u4F5C\u2014\u2014store \u7684 pending \u77ED\u8DEF\u8BA9\u5B83\u662F\u5B89\u5168\u7684\uFF0C\u7070\u8109\u52A8\u672C\u8EAB\u5C31\u662F\u300C\u5DF2\u5728\u8DD1\u300D\u7684\u7B54\u590D\u3002\n const retryRow = useCallback(() => {\n chatTranslate.ensure(rowKey, plan.texts, true);\n }, [rowKey, plan]);\n\n // \u65E9\u9000\u5728\u6240\u6709 hook \u4E4B\u540E\uFF1A\u884C\u5F62\u6001\u5728 null \u4E0E\u53EF\u89C1\u4E4B\u95F4\u5207\u6362\u65F6 hook \u5E8F\u5217\u4E0D\u53D8\u3002\n if (displayed.entries === null) return null;\n\n const last = blocks.length - 1;\n const rendered: ReactNode[] = [];\n for (const entry of displayed.entries) {\n switch (entry.type) {\n case 'prose':\n rendered.push(\n React.createElement(ProseBlock, {\n key: entry.key,\n text: entry.text,\n translated: entry.translated,\n mark: entry.mark,\n fail: entry.fail,\n onToggle: () => toggleBlock(entry.key),\n onRetry: retryRow,\n streaming,\n labels,\n mentions,\n pathImages,\n })\n );\n break;\n case 'reasoning':\n rendered.push(\n React.createElement(\n ProcessReasoning,\n {\n key: entry.key,\n hidden: reasoningHidden,\n reveal: revealProcess,\n children: React.createElement(ReasoningRow, {\n text: entry.text,\n running: streaming && entry.blockIndex === last,\n usePresentation,\n useDisclosure,\n t,\n }),\n }\n )\n );\n break;\n case 'images':\n rendered.push(\n React.createElement(React.Fragment, {\n key: entry.key,\n }, renderMessageImages({ images: entry.attachments.map((attachment) => ({ attachment })), align: 'start' }))\n );\n break;\n case 'unknown':\n rendered.push(\n React.createElement(JsonBlock, {\n key: entry.key,\n label: t('message.unknownBlock'),\n payload: entry.block,\n truncatedLabel: (total: number) => t('json.truncated', { total }),\n })\n );\n break;\n case 'stopped':\n rendered.push(React.createElement('span', { key: entry.key, className: 'dsh-ct-stopped' }, t('message.stopped')));\n break;\n }\n }\n\n return React.createElement(\n 'div',\n { ref: rootRef, className: 'dsh-ct-root', 'data-streaming': streaming || void 0 },\n React.createElement('div', { className: 'dsh-ct-body' }, rendered)\n );\n});\n\nfunction ProcessReasoning(props: {\n hidden: boolean;\n reveal: () => void;\n children: ReactNode;\n}): ReactElement {\n const ref = useSearchableHidden(props.hidden, props.reveal);\n return React.createElement(\n 'div',\n { ref, 'data-turn-process-inline': props.hidden || void 0 },\n props.children\n );\n}\n", "/**\n * \u52A9\u624B\u884C\u7684\u6E32\u67D3\u8BA1\u5212\uFF1A\u7EAF\u51FD\u6570\uFF0C\u4E0D\u542B React\uFF0C\u65E0\u6D4F\u89C8\u5668\u4F9D\u8D56\u3002\n *\n * \u8BA1\u5212\u5C42\u627F\u62C5\u4E24\u7C7B\u5224\u65AD\u2014\u2014\n * 1. \u4E0E\u5BBF\u4E3B `AssistantMarkdown` \u9010\u6761\u7B49\u4EF7\u7684\u5206\u652F\uFF1A\u4EC0\u4E48\u65F6\u5019\u6574\u884C\u4E0D\u6E32\u67D3\u3001\n * groupPart \u600E\u4E48\u8FC7\u6EE4\u3001tool-call \u5757\u4E3A\u4EC0\u4E48\u5728\u8FD9\u91CC\u88AB\u8DF3\u8FC7\uFF08\u5DE5\u5177\u884C\u662F flow \u91CC\n * \u72EC\u7ACB\u7684\u884C\uFF0C\u7531\u5BBF\u4E3B\u81EA\u5DF1\u6E32\u67D3\uFF09\u3001\u8FDE\u7EED image \u600E\u4E48\u5408\u6210\u4E00\u7EC4\u3001\u672A\u77E5\u5757\u843D JSON\u3001\n * \u300C\u5DF2\u505C\u6B62\u300D\u6807\u8BB0\u7684\u51FA\u73B0\u6761\u4EF6\u3002\n * 2. \u7FFB\u8BD1\u5448\u73B0\u7684\u5355\u70B9\u5224\u65AD\uFF1A\u6B63\u6587\u5757\u5728\u4EC0\u4E48\u72B6\u6001\u4E0B\u6302\u4EC0\u4E48\u7EBF\uFF0C\u76F4\u63A5\u4EA7\u51FA styles.ts\n * \u7684 `ProseMark`\uFF08\u8BCD\u6C47\u4E0E\u7C7B\u540D\u6620\u5C04\u5355\u70B9\u5728 styles.ts\uFF09\u3002\u5224\u636E\u4E00\u6B21\u7B97\u6E05\uFF1A\u6210\u529F\u843D\u5B9A\n * =\u84DD\u7EBF\uFF08\u7528\u6237\u70B9\u4E86\u8BFB\u539F\u6587\u5219\u7070\u7EC6\u7EBF\u2014\u2014`originalKeys` \u8FDB\u6765\u6298\u8FDB mark\uFF09\uFF1B\u5931\u8D25\n * \uFF08\u901A\u9053\u4F24\uFF09=\u7EA2\u5B9E\u7EBF\u4E00\u6839\uFF0C`fail` \u4E00\u53E5\u6280\u672F\u7EC6\u8282\u53EA\u5582\u60AC\u505C\u6587\u6848\uFF1B\u884C\u5DF2\u767B\u8BB0\u5728\u9014\u4E14\n * \u8FD9\u5757\u5C1A\u65E0\u7ED3\u679C=\u7070\u8109\u52A8\uFF1B\u5176\u4F59\uFF08\u7A7A\u767D\u5757\u3001\u6D41\u5F0F\u3001\u5F00\u5173\u5173\u3001\u672A\u767B\u8BB0\uFF09=\u65E0\u7EBF\u3002\u6A21\u578B\n * \u539F\u6837\u8FD4\u56DE\u540C\u6837\u7B97\u6210\u529F\uFF1A\u6807\u8BB0\u4F20\u8FBE\u300C\u8FD9\u6BB5\u8FC7\u4E86\u6A21\u578B\u300D\uFF0C\u4E0D\u662F\u300C\u53D8\u8FC7\u300D\u3002\n * \u7A7A\u767D\u5757\uFF08trim \u540E\u4E3A\u7A7A\uFF09\u4E0D\u8FDB\u9001\u8BD1\u6E05\u5355\u3001\u4E0D\u5360\u4E0B\u6807\u3001\u6C38\u4E0D\u6302\u7EBF\u2014\u2014\u5B83\u6CA1\u9001\u8FC7\u6A21\u578B\uFF0C\n * \u91CD\u8BD5\u4E5F\u6C38\u8FDC\u4E0D\u4F1A\u6210\uFF0C\u6302\u7EBF\u53EA\u4F1A\u7559\u4E0B\u6D88\u4E0D\u6389\u7684\u6807\u8BB0\u3002\n *\n * \u6E32\u67D3\u5C42\uFF08assistant-step.tsx\uFF09\u53EA\u6D88\u8D39 mark\uFF1B\u6539\u52A8\u8FD9\u91CC\u7684\u7B49\u4EF7\u5206\u652F\u5FC5\u987B\u540C\u65F6\u5BF9\u7167\n * \u5BBF\u4E3B\u5B9E\u73B0\u3002\n */\nimport type { BlockOutcome } from './translate-store.ts';\nimport type { ProseMark } from './styles.ts';\n\n/** \u52A9\u624B\u884C\u5757\u6570\u636E\u7684\u7ED3\u6784\u6027\u955C\u50CF\uFF08\u503C\u8FB9\u4E0D\u8DE8\u5305\uFF0Ctype-only \u4E16\u754C\uFF09\u3002 */\nexport interface AssistantBlockLike {\n readonly kind: string;\n readonly text?: string;\n readonly attachment?: unknown;\n readonly block?: unknown;\n}\n\nexport type RowPlanEntry =\n /** \u601D\u8003\u5757\uFF1AblockIndex \u4F9B\u300C\u6D41\u5F0F\u5C3E\u5757\u300D\u5224\u5B9A\uFF0Ckey \u662F\u6761\u76EE\u5728\u8BA1\u5212\u91CC\u7684\u4F4D\u7F6E\uFF08React \u952E\uFF09\u3002 */\n | { type: 'reasoning'; key: number; blockIndex: number; text: string }\n /**\n * \u6B63\u6587\u5757\uFF1Amark \u5373\u5DE6\u7F18\u7EBF\u72B6\u6001\uFF08\u53EF\u70B9\u6027\u3001\u91CD\u8BD5\u6307\u5F15\u3001\u8109\u52A8\u5168\u7531\u5B83\u51B3\u5B9A\uFF09\uFF1B\n * translated \u4EC5\u5728\u6210\u529F\u65F6\u975E null\uFF0C\u4F9B mark==='translated' \u65F6\u6362\u6E90\u6587\u672C\u6E32\u67D3\uFF1B\n * fail \u4EC5\u5728 mark==='failed' \u65F6\u975E null\u2014\u2014\u670D\u52A1\u7AEF\u7684\u6280\u672F\u4E00\u53E5\uFF0CTooltip \u60AC\u505C\n * \u62A5\u56E0\u7528\uFF0C\u7F3A\u7701\u65F6\u53EA\u62A5\u300C\u7FFB\u8BD1\u5931\u8D25\u300D\u3002\u7EBF\u578B\u4E0D\u4ECE\u8FD9\u91CC\u51FA\uFF1A\u5931\u8D25\u4E00\u5F8B\u540C\u4E00\u6761\u7EA2\u5B9E\u7EBF\u3002\n */\n | {\n type: 'prose';\n key: number;\n text: string;\n translated: string | null;\n mark: ProseMark;\n fail: string | null;\n }\n | { type: 'images'; key: number; attachments: readonly unknown[] }\n | { type: 'unknown'; key: number; block: unknown }\n | { type: 'stopped'; key: number };\n\nexport interface RowPlan {\n /** null = \u6574\u884C\u4E0D\u6E32\u67D3\uFF08\u4E0E\u5BBF\u4E3B\u7684\u7A7A\u884C\u5B88\u536B\u4E00\u81F4\uFF09\u3002 */\n entries: RowPlanEntry[] | null;\n /** \u9001\u7FFB\u8BD1\u6C60\u7684\u6B63\u6587\u6587\u672C\uFF0C\u6309\u6B63\u6587\u5757\u51FA\u73B0\u987A\u5E8F\uFF08\u7A7A\u767D\u5757\u9664\u5916\uFF09\uFF0C\u4E0E\u7ED3\u679C\u4E0B\u6807\u5BF9\u9F50\u3002 */\n texts: string[];\n}\n\nexport interface RowPlanInput {\n blocks: readonly AssistantBlockLike[];\n /** \u884C\u5728\u6D41\u5F0F\u4E2D\uFF1A\u6D41\u5F0F\u671F\u95F4\u4E00\u5F8B\u539F\u6587\uFF0C\u843D\u5B9A\u540E\u624D\u6709\u8BD1\u6587\u3002 */\n streaming: boolean;\n /** \u4E2D\u65AD\u524D\u7F00\uFF1A\u884C\u5C3E\u5E26\u300C\u5DF2\u505C\u6B62\u300D\u6807\u8BB0\u3002 */\n interrupted: boolean;\n /** flow \u5206\u7EC4\uFF1A'reasoning' \u53EA\u8981\u601D\u8003\u5757\uFF0C'response' \u6392\u9664\u601D\u8003\u5757\uFF0C\u7F3A\u7701\u5168\u8981\u3002 */\n groupPart?: string;\n /** \u53EF\u8BD1\u95E8\uFF08enabled \u4E14\u901A\u9053\u4E09\u8981\u7D20\u9F50\u5907\uFF09\uFF1Afalse \u65F6\u4E00\u5207\u6309\u539F\u6587\u5448\u73B0\uFF0C\u4E0D\u6302\u7EBF\u3002 */\n canTranslate: boolean;\n /**\n * \u8BE5\u884C\u6B63\u6587\u5757\u7684\u7FFB\u8BD1\u7ED3\u679C\uFF0C\u4E0E texts \u540C\u4E0B\u6807\uFF1Bnull = \u8BE5\u6279\u5C1A\u672A\u843D\u5B9A\u3002\n * \u672A\u767B\u8BB0\u6216\u6362\u4EE3\u65F6\u6574\u4E2A\u7F3A\u7701\u3002\n */\n outcomes?: readonly (BlockOutcome | null)[];\n /** \u6C60\u91CC\u8FD9\u884C\u7684\u72B6\u6001\uFF1Apending \u65F6\u672A\u843D\u5B9A\u7684\u5757\u663E\u7070\u8109\u52A8\uFF0C\u672A\u767B\u8BB0\u4EC0\u4E48\u90FD\u4E0D\u663E\u3002 */\n rowStatus?: 'pending' | 'settled';\n /** \u7528\u6237\u70B9\u4E86\u300C\u8BFB\u539F\u6587\u300D\u7684\u6B63\u6587\u5757\uFF08\u6309\u6761\u76EE key\uFF09\uFF1A\u628A\u8BD1\u6587\u6001\u6298\u6210 original-view\u3002 */\n originalKeys?: ReadonlySet<number>;\n}\n\n/** trim \u540E\u4E3A\u7A7A\u7684\u5757\u4E0D\u9001\u8BD1\u3001\u4E0D\u6302\u7EBF\u3001\u4E0D\u5360\u4E0B\u6807\u2014\u2014\u5224\u636E\u5355\u70B9\u5728\u8FD9\u91CC\u3002 */\nfunction isBlankText(value: string | undefined): boolean {\n return (value ?? '').trim() === '';\n}\n\nexport function planAssistantRow(input: RowPlanInput): RowPlan {\n const { blocks, streaming, interrupted, groupPart, canTranslate, outcomes, rowStatus, originalKeys } =\n input;\n\n // \u5BBF\u4E3B\u5B88\u536B\u9010\u5B57\u7B49\u4EF7\uFF1A\u53EA\u6709 tool-call \u5757\u3001\u53C8\u975E\u6D41\u5F0F\u975E\u4E2D\u65AD\u7684\u884C\u4E0D\u6E32\u67D3\u3002\n const hasVisible = streaming || interrupted || blocks.some((block) => block.kind !== 'tool-call');\n if (!hasVisible) return { entries: null, texts: [] };\n\n // \u9001\u8BD1\u6587\u672C = \u672C\u5206\u7EC4\u771F\u6B63\u4F1A\u6E32\u67D3\u51FA\u6765\u7684\u975E\u7A7A\u767D text \u5757\uFF1Areasoning \u5206\u7EC4\u4E0D\u6E32\u67D3\u4EFB\u4F55\n // \u6B63\u6587\uFF0C\u4E5F\u5C31\u4E0D\u9001\u8BD1\u2014\u2014texts\u3001textIndex\u3001outcomes \u4E09\u8005\u59CB\u7EC8\u540C\u57DF\u5BF9\u9F50\u3002\n const texts: string[] = [];\n if (groupPart !== 'reasoning') {\n for (const block of blocks) {\n if (block.kind === 'text' && !isBlankText(block.text)) texts.push(block.text ?? '');\n }\n }\n\n const entries: RowPlanEntry[] = [];\n let textIndex = 0;\n let imageRun: unknown[] | null = null;\n\n const flushImages = (): void => {\n if (imageRun !== null) {\n entries.push({ type: 'images', key: entries.length, attachments: imageRun });\n imageRun = null;\n }\n };\n\n for (let i = 0; i < blocks.length; i++) {\n const block = blocks[i];\n if (block === undefined) continue;\n if (groupPart === 'reasoning' && block.kind !== 'reasoning') continue;\n if (groupPart === 'response' && block.kind === 'reasoning') continue;\n\n switch (block.kind) {\n case 'text': {\n flushImages();\n const blank = isBlankText(block.text);\n const index = textIndex;\n if (!blank) textIndex += 1;\n // key \u4E00\u5F8B\u7528\u6761\u76EE\u4F4D\u7F6E\uFF1A\u5757\u4E0B\u6807\u5728 groupPart \u8FC7\u6EE4\u540E\u53EF\u80FD\u4E0E\u884C\u5C3E\u8FFD\u52A0\u9879\u649E\n // React \u952E\uFF08\u5982 reasoning \u88AB\u8FC7\u6EE4\u7684 interrupted \u884C\uFF09\u3002\n const key = entries.length;\n const attemptable = !blank && canTranslate && !streaming;\n const outcome = attemptable && outcomes !== undefined ? outcomes[index] ?? null : null;\n let mark: ProseMark = null;\n let fail: string | null = null;\n if (outcome !== null && outcome.ok) {\n mark = originalKeys?.has(key) ? 'original-view' : 'translated';\n } else if (outcome !== null) {\n mark = 'failed';\n fail = outcome.detail ?? null;\n } else if (attemptable && rowStatus === 'pending') {\n mark = 'inflight';\n }\n entries.push({\n type: 'prose',\n key,\n text: block.text ?? '',\n translated: outcome !== null && outcome.ok ? outcome.translated : null,\n mark,\n fail,\n });\n break;\n }\n case 'reasoning':\n flushImages();\n entries.push({ type: 'reasoning', key: entries.length, blockIndex: i, text: block.text ?? '' });\n break;\n case 'image':\n if (imageRun === null) imageRun = [];\n imageRun.push(block.attachment);\n break;\n case 'tool-call':\n flushImages();\n break;\n default:\n flushImages();\n entries.push({ type: 'unknown', key: entries.length, block: block.block ?? null });\n }\n }\n flushImages();\n\n // \u5BBF\u4E3B\u7684\u505C\u6B62\u6807\u8BB0\u6761\u4EF6\u9010\u5B57\u7B49\u4EF7\u3002\n const showStopped =\n interrupted &&\n (groupPart === undefined ||\n groupPart === 'response' ||\n !blocks.some((block) => block.kind !== 'reasoning' && block.kind !== 'tool-call'));\n if (showStopped) entries.push({ type: 'stopped', key: entries.length });\n\n return { entries, texts };\n}\n", "/**\n * \u5DF2\u7FFB\u8BD1\u5757\u7684\u70B9\u51FB\u5B88\u536B\uFF1A\u5207\u6362\u53EA\u8BA4\u300C\u843D\u5728\u5757\u7A7A\u767D\u6B63\u6587\u4E0A\u7684\u88F8\u70B9\u51FB\u300D\u3002\n *\n * \u5757\u5185\u542B\u94FE\u63A5\u3001\u4EE3\u7801\u5757\u590D\u5236\u94AE\u7B49\u4EA4\u4E92\u5143\u7D20\u2014\u2014\u5B83\u4EEC\u7684\u70B9\u51FB\u5F52\u81EA\u5DF1\uFF0C\u4E0D\u8FDE\u5E26\u5207\u5757\uFF1B\n * \u62D6\u9009\u8BD1\u6587\u677E\u624B\u4EA7\u751F\u7684 click \u540C\u6837\u5FFD\u7565\u3002\u7EAF DOM \u5224\u5B9A\uFF0C\u4E0D\u4F9D\u8D56 React\uFF0C\u53EF\u72EC\u7ACB\u6D4B\u8BD5\u3002\n */\n\ninterface MinimalPointerEvent {\n /** \u70B9\u51FB\u547D\u4E2D\u7684\u6700\u6DF1\u5143\u7D20\u3002 */\n target: { closest?(selector: string): unknown } | null;\n /** \u6302\u4E86\u672C\u5B88\u536B\u7684\u5DF2\u7FFB\u8BD1\u5757\u5BB9\u5668\u3002 */\n currentTarget: unknown;\n}\n\ninterface MinimalWindow {\n getSelection?(): { isCollapsed?: boolean; toString?(): string } | null;\n}\n\n/**\n * @param event - click \u4E8B\u4EF6\uFF08\u7ED3\u6784\u9762\uFF1Atarget/currentTarget\uFF09\u3002\n * @param view - \u53EF\u6CE8\u5165\u7684 window\uFF08\u6D4B\u8BD5\u4F20\u5047\u5BF9\u8C61\uFF1B\u8FD0\u884C\u65F6\u4F20\u5168\u5C40 window\uFF09\u3002\n * @returns \u8BE5\u70B9\u51FB\u662F\u5426\u5E94\u5F53\u89E6\u53D1\u539F\u6587/\u8BD1\u6587\u5207\u6362\u3002\n */\nexport function isBareBlockClick(event: MinimalPointerEvent, view: MinimalWindow | undefined): boolean {\n const target = event.target;\n const interactive = typeof target?.closest === 'function' ? target.closest('a,button,[role=\"button\"]') : null;\n if (interactive !== null && interactive !== undefined && interactive !== event.currentTarget) return false;\n const selection = typeof view?.getSelection === 'function' ? view.getSelection() : null;\n if (selection !== null && selection !== undefined) {\n if (selection.isCollapsed === false && String(selection.toString?.() ?? '') !== '') return false;\n }\n return true;\n}\n", "/**\n * \u52A9\u624B\u884C\u63A5\u7BA1\u6240\u9700\u7684\u6837\u5F0F\uFF1A\u4E0E\u5BBF\u4E3B `AssistantMarkdown` / `ReasoningRow` \u9010\u6761\u5BF9\u9F50\n * \u7684\u7B49\u4EF7\u89C4\u5219\uFF08\u5B57\u53F7\u3001\u884C\u8DDD\u3001gap\u3001\u7C98\u6027\u6298\u53E0\u5934\u3001running \u5FAE\u5149\u3001summary \u906E\u7F69\u7B49\uFF09\uFF0C\n * \u52A0\u4E0A\u672C\u63D2\u4EF6\u552F\u4E00\u65B0\u589E\u7684\u89C6\u89C9\u8BCD\u6C47\u2014\u2014\u6B63\u6587\u5757\u5DE6\u7F18\u7684\u7EBF\uFF1A\n * \u84DD\u7C97\u7EBF\uFF081px \u4E3B\u8272\uFF09=\u6B63\u5728\u8BFB\u8BD1\u6587\uFF1B\u7070\u7EC6\u7EBF\uFF080.5px \u4E2D\u6027\uFF09=\u6709\u8BD1\u6587\u4F46\u6B63\u5728\u8BFB\u539F\u6587\n * \uFF08\u518D\u70B9\u5373\u5207\u56DE\uFF09\uFF1B\u7EA2\u5B9E\u7EBF\uFF081px error \u8272\uFF09=\u7FFB\u8BD1\u5931\u8D25\uFF08\u901A\u9053\u4F24\uFF1A\u8D85\u65F6\u3001\u65AD\u6D41\u3001\u7A7A\n * \u8FD4\u56DE\uFF09\uFF0C\u60AC\u505C\u62A5\u51FA\u7EC6\u8282\uFF0C\u70B9\u5B83\u6574\u884C\u8865\u8DD1\uFF1B\u7070\u8109\u52A8=\u6574\u884C\u5728\u9014\u3001\u8FD9\u5757\u5C1A\u65E0\u7ED3\u679C\uFF1B\n * \u65E0\u7EBF=\u6CA1\u9001\u8FC7\u6A21\u578B\uFF08\u542B\u7A7A\u767D\u5757\uFF09\u6216\u5F00\u5173\u5173\u95ED\u3002\n * \u8BD1\u6587\u7684\u7ED3\u6784\u6F02\u79FB\u4E0D\u518D\u6302\u7EA2\u7EBF\u2014\u2014\u7531\u5BBF\u4E3B\u6309\u539F\u6587\u4FEE\u56DE\u3001\u91CD\u63B7\u6216\u7167\u6536\uFF0C\u7EA2\u7EBF\u53EA\u5269\u901A\u9053\u4F24\u4E00\u79CD\u3002\n *\n * \u5168\u90E8\u989C\u8272\u4E0E\u5C3A\u5BF8\u8D70 `--dsw-alias-*` / `--dsh-*` \u4E3B\u9898\u522B\u540D\uFF0C\u4E2D\u6027\u5E73\u8FB9\u6309\u5B98\u65B9\u89C4\u5219\n * \u753B 0.5px hairline\uFF1B\u52A8\u753B\u58F0\u660E\u5C0A\u91CD `prefers-reduced-motion`\u3002\u6837\u5F0F\u968F bundle \u6CE8\u5165\n * \u4E00\u6B21\uFF08`<style data-plugin-css>`\uFF09\uFF0C\u7C7B\u540D\u7528\u56FA\u5B9A\u524D\u7F00\uFF0C\u4E0D\u4F9D\u8D56\u5BBF\u4E3B\u54C8\u5E0C\u7C7B\u540D\u3002\n */\n\nconst STYLE_TAG_ID = '@lynn123411/dsh-chat-translate/chat-assistant.css';\n\nexport const ASSISTANT_CSS = [\n // ---- \u884C\u9AA8\u67B6\uFF08\u5BBF\u4E3B AssistantMarkdown.root/body \u7B49\u4EF7\uFF09 ----\n '.dsh-ct-root{display:flex;flex-direction:column;font-size:var(--dsh-content-font-size,14px);line-height:calc(24px + var(--dsh-content-font-delta,0px));color:var(--dsw-alias-label-primary)}',\n '.dsh-ct-body{display:flex;flex-direction:column;gap:16px}',\n '.dsh-ct-body>[data-turn-process-inline][hidden]{margin-bottom:-16px}',\n // \u5BBD\u8868\u683C\u6EA2\u51FA\u5185\u5BB9\u5217\uFF08\u5BBF\u4E3B\u7B49\u4EF7\u89C4\u5219\uFF0C\u7C7B\u540D md-table-wide \u7531 MarkdownText \u81EA\u5DF1\u4EA7\u51FA\uFF09\n '.dsh-ct-body .md-table-wide{--dsh-table-spare:max(0px, calc((100cqw - var(--dsh-chat-content-width)) / 2));--dsh-table-lead:calc(var(--dsh-table-spare) + min(var(--dsh-chat-content-width), 100cqw) - 100%);box-sizing:border-box;width:calc(100% + var(--dsh-table-lead) + var(--dsh-table-spare));max-width:none;margin-left:calc(-1 * var(--dsh-table-lead));padding-left:var(--dsh-table-lead)}',\n '.dsh-ct-body .md-table-wide>table{z-index:1;position:relative}',\n // ---- \u5DF2\u505C\u6B62\u6807\u8BB0\uFF08\u5BBF\u4E3B .hWmORq_stopped \u7B49\u4EF7\uFF09 ----\n '.dsh-ct-stopped{border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-tertiary);align-self:flex-start;padding:0 6px;font-size:11px;line-height:18px}',\n // ---- \u300C\u5DF2\u7FFB\u8BD1\u300D\u4EA4\u4E92\u4E0E\u6807\u8BB0\uFF1A\u53EF\u70B9\u6001\u5E38\u9A7B\uFF08\u542B\u5DE6\u7F18\u5185\u7F29\uFF09\u2014\u2014\u84DD\u7C97\u7EBF=\u8BFB\u8BD1\u6587\uFF0C\n // \u7070\u7EC6\u7EBF=\u8BFB\u539F\u6587\u5907\u7740\u8BD1\u6587\uFF1B\u533A\u5206\u8D70\u8272\u76F8+\u7C97\u7EC6 ----\n '.dsh-ct-prose-clickable{padding-left:12px;cursor:pointer}',\n '.dsh-ct-prose-clickable:focus-visible{outline:1.5px solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px;border-radius:var(--dsw-radius-sm)}',\n '.dsh-ct-prose-translated{border-left:1px solid color-mix(in srgb, var(--dsw-alias-state-business-primary) 65%, transparent)}',\n '.dsh-ct-prose-original{border-left:0.5px solid var(--dsw-alias-border-l2)}',\n // ---- \u5931\u8D25\u6807\u8BB0\uFF1A1px error \u8272\u7EA2\u5B9E\u7EBF\uFF08\u53EA\u5269\u901A\u9053\u4F24\u4E00\u79CD\uFF0C\u60AC\u505C\u62A5\u7EC6\u8282\uFF09\uFF1B\n // \u53EF\u70B9\uFF0C\u70B9=\u6574\u884C\u624B\u52A8\u8865\u8DD1 ----\n '.dsh-ct-prose-failed{border-left:1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 65%, transparent)}',\n // ---- \u5728\u9014\uFF1A\u7070 1px \u7EBF\u8109\u52A8\uFF08\u4E0E\u6210\u529F\u6001\u540C padding\uFF0C\u8F6C\u84DD\u4E0D\u8DF3\u5B57\uFF09\uFF1B\n // prefers-reduced-motion \u964D\u7EA7\u4E3A\u9759\u6001\u7070\u7EBF ----\n '.dsh-ct-prose-inflight{padding-left:12px;border-left:1px solid var(--dsw-alias-border-l2);animation:1.4s ease-in-out infinite alternate dsh-ct-prose-inflight-pulse}',\n '@keyframes dsh-ct-prose-inflight-pulse{from{border-left-color:var(--dsw-alias-border-l2)}to{border-left-color:color-mix(in srgb, var(--dsw-alias-border-l2) 20%, transparent)}}',\n '@media (prefers-reduced-motion:reduce){.dsh-ct-prose-inflight{animation:none}}',\n // ---- \u5931\u8D25\u5757\u7684\u91CD\u8BD5\u6307\u5F15\uFF1A\u60AC\u505C/\u952E\u76D8\u805A\u7126\u65F6\u5728\u5DE6\u4FA7\u7EBF\u69FD\u5185\u9732\u51FA\u5C0F \u21BB\uFF08\u5B98\u65B9\u56FE\u6807\uFF0C\n // \u6587\u6848\u7531 Tooltip \u6302\u5728\u8BE5\u951A\u70B9\u4E0A\uFF09\uFF1B\u9759\u6001\u4E0D\u6270\u52A8\u9605\u8BFB ----\n '.dsh-ct-prose-retryable{position:relative}',\n '.dsh-ct-retry{position:absolute;left:2px;top:6px;display:flex;color:var(--dsw-alias-state-error-primary);opacity:0;visibility:hidden}',\n '.dsh-ct-prose-retryable:hover .dsh-ct-retry,.dsh-ct-prose-retryable:focus-visible .dsh-ct-retry{opacity:1;visibility:visible}',\n // ---- ReasoningRow\uFF08\u5BBF\u4E3B lcKema_* \u7B49\u4EF7\uFF0C\u524D\u7F00\u6362\u6210 dsh-ct-think\uFF09 ----\n '.dsh-ct-think-root{flex-direction:column;display:flex}',\n '.dsh-ct-think-root:not([data-expanded]){contain:size layout;height:calc(24px + var(--dsh-content-font-delta,0px))}',\n '.dsh-ct-think-row{position:relative;overflow:hidden}',\n '.dsh-ct-think-root[data-expanded] [data-open] [data-disclosure-row]{z-index:1;background:var(--dsw-alias-bg-base);position:sticky;top:0}',\n '.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{content:\"\";inset-block:0;background:linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--dsw-alias-bg-base) 60%, transparent) 55%, transparent 100%);pointer-events:none;width:300px;animation:2.6s ease-out infinite dsh-ct-reasoning-sweep;position:absolute;left:0}',\n '@keyframes dsh-ct-reasoning-sweep{0%{left:-300px}90%,to{left:100%}}',\n '@media (prefers-reduced-motion:reduce){.dsh-ct-think-root[data-state=running] .dsh-ct-think-row:after{animation:none}}',\n '.dsh-ct-think-leading{flex-shrink:0}',\n '.dsh-ct-think-chevron{color:var(--dsw-alias-label-secondary)}',\n '.dsh-ct-think-title{font-weight:400}',\n '.dsh-ct-think-separator{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}',\n '.dsh-ct-think-summary{min-width:0;color:var(--dsw-alias-label-tertiary);font-size:var(--dsh-content-font-size-secondary,13px);line-height:calc(20px + var(--dsh-content-font-delta-secondary,0px));white-space:nowrap;flex:auto;overflow:hidden}',\n '.dsh-ct-think-summary-text{text-overflow:ellipsis;display:block;overflow:hidden}',\n '.dsh-ct-think-summary[data-streaming]{mask-image:linear-gradient(90deg,currentcolor calc(100% - 48px),transparent)}',\n '.dsh-ct-think-summary[data-streaming] .dsh-ct-think-summary-text{text-overflow:clip;overflow:visible}',\n '.dsh-ct-think-root:not([data-preview]) .dsh-ct-think-separator,.dsh-ct-think-root:not([data-preview]) .dsh-ct-think-summary{display:none}',\n '.dsh-ct-think-body{padding:4px 0 4px calc(22px + var(--dsh-content-font-delta,0px));min-width:0}',\n // ---- \u65E0\u969C\u788D\uFF1A\u5C4F\u5E55\u9605\u8BFB\u5668\u4E13\u7528\u6587\u672C ----\n '.dsh-ct-visually-hidden{clip:rect(0 0 0 0);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}',\n].join('');\n\n/**\n * \u6B63\u6587\u5757\u5DE6\u7F18\u7EBF\u7684\u72B6\u6001\u8BCD\u6C47\uFF1A\u5355\u70B9\u5728 row-plan \u7B97\u51FA\uFF08`planAssistantRow` \u76F4\u63A5\u4EA7\u51FA\n * mark\uFF09\uFF0C\u6E32\u67D3\u5C42\u53EA\u6D88\u8D39\u3001\u6837\u5F0F\u5C42\u53EA\u7FFB\u6210\u7C7B\u540D\u2014\u2014\u300C\u7EBF\u578B\u4E0E\u8109\u52A8\u300D\u4E0D\u518D\u591A\u5904\u91CD\u7B97\u3002\n * \u5931\u8D25\u53EA\u6709 `failed` \u4E00\u79CD\uFF1A\u540C\u4E00\u6761\u7EA2\u5B9E\u7EBF\uFF0C\u8D25\u56E0\u8D70\u60AC\u505C\u6587\u6848\u3002\n */\nexport type ProseMark = 'translated' | 'original-view' | 'failed' | 'inflight' | null;\n\n/** mark \u2192 \u70B9\u51FB\u52A8\u4F5C\u5355\u70B9\uFF1A\u5207\u6362\u3001\u91CD\u8BD5\u3001\u4E0D\u53EF\u70B9\u4E09\u9009\u4E00\uFF08\u6E32\u67D3\u5C42\u7167\u6B64\u63A5\u7EBF\uFF09\u3002 */\nexport type ProseAction = 'toggle' | 'retry' | null;\n\nexport function proseAction(mark: ProseMark): ProseAction {\n switch (mark) {\n case 'translated':\n case 'original-view':\n return 'toggle';\n case 'failed':\n return 'retry';\n default:\n return null;\n }\n}\n\n/** mark \u2192 \u7C7B\u540D\u5355\u70B9\uFF1A\u53EF\u70B9\u6001\u5171\u7528 padding \u4E0E\u5149\u6807\uFF0C\u7EBF\u578B\u4E4B\u95F4\u5207\u6362\u4E0D\u8DF3\u5B57\u3002 */\nexport function proseClassNames(mark: ProseMark): string | undefined {\n switch (mark) {\n case 'translated':\n return 'dsh-ct-prose-clickable dsh-ct-prose-translated';\n case 'original-view':\n return 'dsh-ct-prose-clickable dsh-ct-prose-original';\n case 'failed':\n return 'dsh-ct-prose-clickable dsh-ct-prose-failed dsh-ct-prose-retryable';\n case 'inflight':\n return 'dsh-ct-prose-inflight';\n default:\n return void 0;\n }\n}\n\n/** \u6CE8\u5165\u63A5\u7BA1\u6837\u5F0F\uFF1B\u91CD\u590D\u8C03\u7528\u662F\u7A7A\u64CD\u4F5C\u3002 */\nexport function ensureAssistantStyles(): void {\n if (typeof document === 'undefined') return;\n const selector = `style[data-plugin-css=${JSON.stringify(STYLE_TAG_ID)}]`;\n if (document.querySelector(selector) !== null) return;\n const tag = document.createElement('style');\n tag.dataset.plugin = '@lynn123411/dsh-chat-translate';\n tag.dataset.pluginCss = STYLE_TAG_ID;\n tag.textContent = ASSISTANT_CSS;\n document.head.appendChild(tag);\n}\n", "\n/** \u6BCF\u6B21\u6253\u5305\u8BF7\u6C42\u91CD\u63B7\u7684 4 \u5B57\u6BCD\u968F\u673A id\uFF1A\u8BA9\u6807\u8BB0\u65E0\u6CD5\u88AB\u5F53\u6210\u8BCD\u3001\u4E5F\u4E0D\u4F1A\u8DE8\u8BF7\u6C42\u649E\u8F66\u3002 */\nfunction randomTokenId(): string {\n let out = '';\n for (let i = 0; i < 4; i++) {\n out += 'abcdefghijklmnopqrstuvwxyz'[Math.floor(Math.random() * 26)];\n }\n return out;\n}\n\n/**\n * \u56DE\u7B54\u6B63\u6587\u7FFB\u8BD1\u7684\u8BF7\u6C42\u9884\u7B97\u3002\u6BCF\u6B21\u8BF7\u6C42\u7684\u8F93\u5165\u4F30\u7B97 token \u4E0A\u9650\uFF0C\u4E0E\u672C\u5730\u670D\u52A1\u7684\n * prefill \u6279\u6B21\uFF08MAX_NUM_BATCHED_TOKENS\uFF09\u4FDD\u6301\u540C\u4E00\u91CF\u7EA7\uFF1B\u8F93\u51FA\u4E0A\u9650\u5199\u8FDB\u8BF7\u6C42\u4F53\u7684\n * max_tokens\uFF0C\u907F\u514D\u957F\u6587\u672C\u628A\u5269\u4F59\u4E0A\u4E0B\u6587\u5168\u90E8\u82B1\u5728\u751F\u6210\u4E0A\u3002\n */\nexport const REPLY_MAX_INPUT_TOKENS = 4096;\nexport const REPLY_MAX_OUTPUT_TOKENS = 8192;\n\n/** \u6C49\u5B57\u4E0E\u5168\u89D2\u6807\u70B9\u6309\u4E00\u5B57\u4E00 token \u8BA1\u3002 */\nconst CJK_CHAR = /[\\u3400-\\u4dbf\\u4e00-\\u9fff\\uf900-\\ufaff\\u3000-\\u303f\\uff00-\\uffef]/;\n\n/**\n * \u4FDD\u5B88\u4F30\u7B97\u4E00\u6BB5\u6587\u672C\u5360\u7528\u7684 token \u6570\uFF1A\u6C49\u5B57\u4E00\u5B57\u4E00 token\uFF0C\u5176\u4F59\u6BCF\u4E09\u4E2A\u5B57\u7B26\u4E00 token\u3002\n * \u5B9E\u6D4B\u672C\u673A hy-mt2-1.8b \u7684\u82F1\u6587\u8BED\u6599\u7EA6\u4E3A\u6BCF 3.3 \u5B57\u7B26\u4E00 token\uFF0C\u8FD9\u91CC\u53D6 3 \u4F5C\u4E3A\u4E0A\u754C\uFF0C\n * \u5B81\u53EF\u628A\u8BF7\u6C42\u5207\u5F97\u6BD4\u5B9E\u9645\u9700\u8981\u66F4\u788E\uFF0C\u4E5F\u4E0D\u8BA9\u670D\u52A1\u7AEF\u6536\u5230\u8D85\u51FA\u9884\u671F\u7684 prefill\u3002\n */\nexport function estimateTokens(text: string): number {\n let cjk = 0;\n for (const char of text) {\n if (CJK_CHAR.test(char)) cjk += 1;\n }\n return cjk + Math.ceil((text.length - cjk) / 3);\n}\n\n/** \u6309\u6355\u83B7\u7EC4\u5206\u9694\u7B26\u5207\u5206\uFF0C\u5E76\u8BA9\u5206\u9694\u7B26\u7559\u5728\u524D\u4E00\u4E2A\u7247\u6BB5\u672B\u5C3E\u3002 */\nfunction splitKeepingSeparator(text: string, pattern: RegExp): string[] {\n const parts = text.split(pattern);\n const chunks: string[] = [];\n for (let index = 0; index < parts.length; index += 2) {\n const content = parts[index] ?? '';\n const separator = parts[index + 1] ?? '';\n if (content === '' && separator === '') continue;\n chunks.push(content + separator);\n }\n return chunks;\n}\n\n/** \u6309\u53E5\u672B\u6807\u70B9\u5207\u5206\uFF0C\u4F5C\u4E3A\u7A7A\u884C\u4E0E\u5355\u884C\u90FD\u5207\u4E0D\u5F00\u65F6\u7684\u6700\u540E\u4E00\u6863\u3002 */\nfunction sentenceChunks(text: string): string[] {\n const chunks = text.match(/[^.!?\u3002\uFF01\uFF1F\\n]+[.!?\u3002\uFF01\uFF1F]*[ \\t]*\\n?/g);\n return chunks === null ? [text] : chunks.filter((chunk) => chunk.length > 0);\n}\n\n/** \u5355\u5757\u5185\u90E8\u627E\u4E0D\u5230\u81EA\u7136\u8FB9\u754C\u65F6\u7684\u515C\u5E95\uFF1A\u6309\u4F30\u7B97 token \u786C\u5207\u3002 */\nfunction hardSlice(text: string, maxTokens: number): string[] {\n const out: string[] = [];\n let current = '';\n let tokens = 0;\n for (const char of text) {\n const cost = CJK_CHAR.test(char) ? 1 : 1 / 3;\n if (current !== '' && tokens + cost > maxTokens) {\n out.push(current);\n current = '';\n tokens = 0;\n }\n current += char;\n tokens += cost;\n }\n if (current !== '') out.push(current);\n return out;\n}\n\n/**\n * \u628A\u4E00\u4E2A\u5757\u5207\u6210\u4E0D\u8D85\u8FC7\u4E0A\u9650\u7684\u7247\u6BB5\uFF1A\u4F18\u5148\u7A7A\u884C\uFF0C\u5176\u6B21\u5355\u884C\uFF0C\u518D\u6B21\u53E5\u672B\uFF0C\u6700\u540E\u786C\u5207\u3002\n * \u7247\u6BB5\u6309\u539F\u987A\u5E8F\u62FC\u56DE\u5373\u4E3A\u8BE5\u5757\u7684\u5B8C\u6574\u8BD1\u6587\u3002\n */\nexport function splitOversizedBlock(\n text: string,\n maxTokens: number = REPLY_MAX_INPUT_TOKENS\n): string[] {\n if (estimateTokens(text) <= maxTokens) return [text];\n\n let chunks = splitKeepingSeparator(text, /(\\n{2,})/);\n if (chunks.length <= 1) chunks = splitKeepingSeparator(text, /(\\n)/);\n if (chunks.length <= 1) chunks = sentenceChunks(text);\n\n const out: string[] = [];\n let current = '';\n for (const chunk of chunks) {\n if (estimateTokens(chunk) > maxTokens) {\n if (current !== '') {\n out.push(current);\n current = '';\n }\n out.push(...hardSlice(chunk, maxTokens));\n continue;\n }\n if (current !== '' && estimateTokens(current + chunk) > maxTokens) {\n out.push(current);\n current = '';\n }\n current += chunk;\n }\n if (current !== '') out.push(current);\n return out;\n}\n\n/** \u628A\u7247\u6BB5\u6309\u539F\u987A\u5E8F\u6253\u5305\u6210\u5C3D\u91CF\u5C11\u7684\u8BF7\u6C42\uFF0C\u4E14\u6BCF\u6279\u4E0D\u8D85\u8FC7\u8F93\u5165\u4E0A\u9650\u3002 */\nexport function packPieces<T extends { text: string }>(\n pieces: T[],\n maxTokens: number = REPLY_MAX_INPUT_TOKENS\n): T[][] {\n const batches: T[][] = [];\n let current: T[] = [];\n let tokens = 0;\n for (const piece of pieces) {\n const cost = estimateTokens(piece.text);\n if (current.length > 0 && tokens + cost > maxTokens) {\n batches.push(current);\n current = [];\n tokens = 0;\n }\n current.push(piece);\n tokens += cost;\n }\n if (current.length > 0) batches.push(current);\n return batches;\n}\n\n/**\n * \u6253\u5305\u6807\u8BB0\uFF1A`\u27EA<4 letters><index>\u27EB`\u2014\u2014\u6253\u5305\u8BF7\u6C42\u7684\u6846\u67B6\uFF0C\u6BCF\u6BB5\u4EE5\u6807\u8BB0\u884C\u5F00\u5934\u3002\n * \u5B83\u53EA\u6D3B\u5728\u4E00\u6B21\u8BF7\u6C42\u7684\u8D1F\u8F7D\u91CC\uFF1B\u8BD1\u6587\u6B8B\u7559 \u27EA\u2026\u27EB \u7531\u5BBF\u4E3B\u76F4\u63A5\u5265\u9664\uFF0C\u4E0D\u6210\u8D25\u56E0\u3002\n */\nexport interface BatchFormat {\n id: string;\n token: (index: number) => string;\n}\n\nexport function createBatchFormat(): BatchFormat {\n const id = randomTokenId();\n return {\n id,\n token: (index: number) => `\u27EA${id}${index}\u27EB`,\n };\n}\n\n/** \u6807\u8BB0\u51FA\u73B0\u5728\u88AB\u7FFB\u8BD1\u6587\u672C\u91CC\u7684\u901A\u7528\u5F62\u72B6\uFF0C\u4E0D\u95EE id\u3002 */\nconst BATCH_TOKEN_PATTERN_SOURCE = '\u27EA([a-z]{4})(\\\\d+)\u27EB';\n\n/** \u6BCF\u4E2A\u7247\u6BB5\u4EE5\u5355\u72EC\u4E00\u884C\u6807\u8BB0\u5F00\u5934\uFF0C\u6BB5\u4E0E\u6BB5\u4E4B\u95F4\u7A7A\u4E00\u884C\u3002 */\nexport function buildBatchPayload(pieces: string[], format: BatchFormat): string {\n return pieces.map((text, index) => `${format.token(index)}\\n${text}`).join('\\n\\n');\n}\n\n/**\n * \u6309\u6807\u8BB0\u628A\u6574\u6279\u8BD1\u6587\u5207\u56DE\u6BCF\u4E2A\u7247\u6BB5\u3002\n *\n * \u5E8F\u53F7\u5FC5\u987B\u6070\u597D\u51FA\u73B0\u4E00\u6B21\u3001\u6309 0..count-1 \u5347\u5E8F\u3001\u6BCF\u6BB5\u975E\u7A7A\uFF0C\u4E14\u4E0D\u5F97\u6DF7\u5165\u5176\u5B83\u6279\u6B21\n * \u7684\u6807\u8BB0\uFF1B\u4EFB\u4F55\u4E00\u6761\u4E0D\u6EE1\u8DB3\u90FD\u8FD4\u56DE null\uFF0C\u7531\u8C03\u7528\u65B9\u4F5C\u5E9F\u6574\u6279\u5E76\u9000\u56DE\u5355\u5757\u91CD\u8BD5\u3002\n */\nexport function splitBatchTranslation(\n translated: string,\n format: BatchFormat,\n count: number\n): string[] | null {\n const pattern = new RegExp(BATCH_TOKEN_PATTERN_SOURCE, 'gu');\n const found: Array<{ index: number; start: number; end: number }> = [];\n let match: RegExpExecArray | null;\n while ((match = pattern.exec(translated)) !== null) {\n if (match[1].toLowerCase() !== format.id.toLowerCase()) return null;\n const index = Number.parseInt(match[2] ?? '', 10);\n if (Number.isNaN(index)) return null;\n found.push({ index, start: match.index, end: match.index + match[0].length });\n }\n if (found.length !== count) return null;\n\n const parts: string[] = [];\n for (let index = 0; index < count; index++) {\n const entry = found[index]!;\n if (entry.index !== index) return null;\n const next = found[index + 1];\n const body = translated.slice(entry.end, next === undefined ? translated.length : next.start).trim();\n if (body === '') return null;\n parts.push(body);\n }\n return parts;\n}\n\n/**\n * \u5265\u9664\u8BD1\u6587\u91CC\u6B8B\u7559\u7684 \u27EA\u2026\u27EB \u6253\u5305\u6807\u8BB0\uFF1A\u6807\u8BB0\u53EA\u662F\u8BF7\u6C42\u811A\u624B\u67B6\uFF0C\u6A21\u578B\u5E26\u56DE\u6765\u5C31\u5220\u6389\u2014\u2014\n * \u5F62\u72B6\u95EE\u9898\u4E0D\u5426\u51B3\u5185\u5BB9\uFF0C\u6B8B\u7559\u4E5F\u4E0D\u518D\u662F\u62D2\u6536\u7406\u7531\u3002\n */\nexport function stripBatchMarkers(text: string): string {\n return text.replace(new RegExp(BATCH_TOKEN_PATTERN_SOURCE, 'gu'), '');\n}\n", "import { requestTranslateReply } from '../translate/api.ts';\nimport type { ReplyBlockResult } from '../../server/types.ts';\nimport { estimateTokens, REPLY_MAX_INPUT_TOKENS } from '../../server/pipeline/blocks.ts';\n\n/** \u4E00\u884C\u6B63\u6587\u7684\u6309\u5757\u7FFB\u8BD1\u7ED3\u679C\uFF0C\u4E0E\u8BF7\u6C42\u7684 texts \u6570\u7EC4\u6309\u4E0B\u6807\u5BF9\u9F50\u3002 */\nexport interface BlockOutcome {\n /** \u6A21\u578B\u4EA7\u51FA\u7684\u4E2D\u6587\uFF08Markdown \u6E90\u6587\u672C\uFF09\uFF1B\u5931\u8D25\u65F6\u7B49\u4E8E\u539F\u6587\u3002 */\n translated: string;\n /** \u8BE5\u5757\u662F\u5426\u7FFB\u8BD1\u6210\u529F\u3002 */\n ok: boolean;\n /** \u4EC5 ok=false \u65F6\u53EF\u80FD\u51FA\u73B0\uFF1A\u670D\u52A1\u7AEF\u7ED9\u51FA\u7684\u6280\u672F\u7EC6\u8282\uFF0C\u62FC\u5728\u6807\u7B7E\u540E\u8FDB\u60AC\u505C\u6587\u6848\u3002 */\n detail?: string;\n}\n\n/**\n * \u4E00\u884C\u6B63\u6587\u7684\u7FFB\u8BD1\u72B6\u6001\u3002outcomes \u4E0E texts \u540C\u4E0B\u6807\uFF0Cnull = \u8BE5\u5757\u6240\u5728\u6279\u5C1A\u672A\u843D\u5B9A\uFF1A\n * \u6BCF\u6279\u8FD4\u56DE\u5C31\u5730\u586B\u5165\u5E76\u901A\u77E5\uFF0C\u957F\u56DE\u7B54\u6309\u9605\u8BFB\u987A\u5E8F\u9010\u6BB5\u51FA\u4E2D\u6587\uFF0C\u4E0D\u618B\u5230\u6700\u540E\u4E00\u6279\u3002\n * \u5168\u90E8\u6279\u843D\u5B9A\u540E status \u8F6C settled\u2014\u2014\u843D\u5B9A\u5373\u7EC8\u6001\uFF1A\u8FD9\u91CC\u6CA1\u6709\u4EFB\u4F55\u81EA\u52A8\u8865\u8DD1\uFF0C\n * \u5931\u8D25\u5757\u7684\u6551\u6D3B\u53EA\u7531\u7528\u6237\u7684\u70B9\u51FB\u53D1\u8D77\uFF08ensure \u7684 manual \u53C2\u6570\uFF09\u3002\n */\nexport interface RowState {\n status: 'pending' | 'settled';\n texts: readonly string[];\n outcomes: readonly (BlockOutcome | null)[];\n}\n\n/** \u9001\u8BD1\u4E00\u884C\u6B63\u6587\u7684\u53D6\u6570\u9762\uFF1A\u9ED8\u8BA4\u8D70\u5BBF\u4E3B\u8DEF\u7531\uFF0C\u6D4B\u8BD5\u6CE8\u5165\u5047\u5B9E\u73B0\u3002 */\nexport type ReplyFetcher = (texts: string[]) => Promise<ReplyBlockResult[]>;\n\n/** \u540C\u65F6\u5728\u6C60\u7684\u884C\u6570\u4E0A\u9650\uFF1ALRU \u6DD8\u6C70\u6700\u4E45\u672A\u8BFB\u7684\u6574\u884C\uFF1B\u884C\u6587\u672C\u672C\u8EAB\u4ECD\u5F52\u5BBF\u4E3B\u4F1A\u8BDD\u6301\u6709\u3002 */\nexport const MAX_TRANSLATED_ROWS = 200;\n\n/**\n * \u4E00\u6B21 HTTP \u8BF7\u6C42\u88C5\u8F7D\u7684\u4F30\u7B97 token \u4E0A\u9650\uFF1A\u4E0E\u5BBF\u4E3B\u7684\u6253\u5305\u8F93\u5165\u4E0A\u9650\u540C\u503C\u540C\u6E90\n * \uFF08blocks.ts \u7684 REPLY_MAX_INPUT_TOKENS\uFF09\u2014\u2014\u5BA2\u6237\u7AEF\u6309\u5BBF\u4E3B\u7684\u6253\u5305\u7A97\u53E3\u5207\u6279\uFF0C\n * \u6BCF\u6279\u5728\u5BBF\u4E3B\u4FA7\u901A\u5E38\u6B63\u597D\u843D\u8FDB\u4E00\u6B21\u6A21\u578B\u8C03\u7528\u3002\n */\nexport const REQUEST_TOKEN_BUDGET = REPLY_MAX_INPUT_TOKENS;\n\n/** \u6309\u51FA\u73B0\u987A\u5E8F\u628A\u6574\u884C\u6587\u672C\u5207\u6210\u4E0D\u8D85\u9884\u7B97\u7684\u6279\uFF1B\u5355\u5757\u8D85\u9884\u7B97\u65F6\u72EC\u81EA\u6210\u6279\u3002 */\nexport function chunkTexts(texts: readonly string[]): string[][] {\n const batches: string[][] = [];\n let current: string[] = [];\n let tokens = 0;\n for (const text of texts) {\n const cost = Math.max(estimateTokens(text), 1);\n if (current.length > 0 && tokens + cost > REQUEST_TOKEN_BUDGET) {\n batches.push(current);\n current = [];\n tokens = 0;\n }\n current.push(text);\n tokens += cost;\n }\n if (current.length > 0) batches.push(current);\n return batches;\n}\n\n/**\n * \u52A9\u624B\u884C\u7684\u7FFB\u8BD1\u6C60\uFF1A\u6309\u884C\u952E\u5B58\u300C\u8BE5\u884C\u5168\u90E8\u6B63\u6587\u5757\u7684\u7FFB\u8BD1\u7ED3\u679C\u300D\u3002\n *\n * \u884C\u952E\u7531\u6E32\u67D3\u5C42\u7ED9\u51FA\uFF08\u4F1A\u8BDD\u5185\u951A\u70B9\u5E8F\u53F7\uFF09\uFF0C\u6587\u672C\u6570\u7EC4\u53C2\u4E0E\u4E00\u81F4\u6027\u5224\u65AD\uFF1A\u540C\u952E\u4E0D\u540C\u6587\u672C\n * \u89C6\u4E3A\u884C\u6362\u4E86\u4E00\u4EE3\u5185\u5BB9\uFF0C\u6574\u884C\u91CD\u65B0\u8BF7\u6C42\u2014\u2014\u8FD9\u4FDD\u8BC1\u91CD\u6E32\u67D3\u6216\u4F1A\u8BDD\u5207\u6362\u540E\u4E0D\u4F1A\u628A\u65E7\u8BD1\u6587\n * \u914D\u5230\u65B0\u6587\u672C\u4E0A\u3002\u8BF7\u6C42\u5E42\u7B49\uFF1A\u540C\u952E\u540C\u6587\u672C\u4E14\u4ECD\u5728\u9014\u7684 ensure \u4E0D\u53D1\u7B2C\u4E8C\u4E2A\u8BF7\u6C42\uFF1B\u5DF2\u843D\u5B9A\n * \u7684\u884C\u540C\u6837\u77ED\u8DEF\u2014\u2014**\u9664\u975E** manual=true\uFF0C\u90A3\u662F\u7528\u6237\u70B9\u4E86\u67D0\u4E2A\u7EA2\u7EBF\u5757\u53D1\u8D77\u7684\u624B\u52A8\u8865\u8DD1\uFF0C\n * \u65E0\u9650\u6B21\u3001\u4E0D\u8BBE\u989D\u5EA6\u3002\u624B\u52A8\u8865\u8DD1\u65F6\u4E0A\u4E00\u4EE3\u5DF2\u6210\u529F\u7684\u5757\u4FDD\u6301\u6302\u7EBF\uFF08\u7ED3\u679C\u4ECE\u4E0A\u4E00\u4EE3\u79CD\u4E0B\uFF0C\n * \u670D\u52A1\u7AEF\u78C1\u76D8\u7F13\u5B58\u8BA9\u5B83\u4EEC\u79D2\u56DE\uFF09\uFF0C\u53EA\u6709\u5C1A\u65E0\u8BD1\u6587\u7684\u5757\u663E\u793A\u5728\u9014\u8109\u52A8\u3002\u5728\u9014\u671F\u95F4\u6362\u4EE3\uFF0C\n * \u8FDF\u5230\u7684\u65E7\u4EE3\u7ED3\u679C\u76F4\u63A5\u4E22\u5F03\u3002\n *\n * \u672C\u6A21\u5757\u4E0D\u4F9D\u8D56 React\uFF1A\u6E32\u67D3\u5C42\u7528 useSyncExternalStore \u8BA2\u9605\u7248\u672C\uFF0C\u518D\u8BFB getState\u3002\n */\nexport class ChatTranslateStore {\n private rows = new Map<string, RowState>();\n private listeners = new Set<() => void>();\n private version = 0;\n private fetch: ReplyFetcher;\n\n constructor(fetcher: ReplyFetcher) {\n this.fetch = fetcher;\n }\n\n /** \u5355\u8C03\u7248\u672C\u53F7\uFF1A\u4EFB\u4F55\u72B6\u6001\u53D8\u5316\uFF08\u542B\u5355\u6279\u843D\u5B9A\uFF09\u90FD\u524D\u8FDB\u4E00\u4F4D\uFF0C\u4F9B\u6E32\u67D3\u5C42\u5224\u7B49\u91CD\u6E32\u3002 */\n getVersion(): number {\n return this.version;\n }\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n /** \u8BFB\u4E00\u884C\u72B6\u6001\uFF0C\u5E76\u628A\u8BE5\u884C\u5237\u6210\u6700\u8FD1\u4F7F\u7528\u3002 */\n getState(rowKey: string): RowState | undefined {\n const state = this.rows.get(rowKey);\n if (state === undefined) return undefined;\n // Refresh LRU order (re-insert at the end).\n this.rows.delete(rowKey);\n this.rows.set(rowKey, state);\n return state;\n }\n\n /**\n * \u786E\u4FDD\u8BE5\u884C\u6309\u5F53\u524D\u6587\u672C\u88AB\u7FFB\u8BD1\u3002\n * @param manual - \u7528\u6237\u70B9\u51FB\u5931\u8D25\u5757\u53D1\u8D77\u7684\u8865\u8DD1\uFF1A\u5DF2\u843D\u5B9A\u7684\u540C\u4EE3\u884C\u53EA\u6709\u8FD9\u6761\u8DEF\u4F1A\u91CD\u53D1\u3002\n * @returns \u662F\u5426\u53D1\u51FA\u4E86\u65B0\u7684\u8BF7\u6C42\u3002\n */\n ensure(rowKey: string, texts: readonly string[], manual = false): boolean {\n const current = this.rows.get(rowKey);\n let seed: (BlockOutcome | null)[] | null = null;\n if (current !== undefined && sameTexts(current.texts, texts)) {\n // \u5728\u9014\uFF1A\u518D\u70B9\u662F\u7A7A\u64CD\u4F5C\uFF0C\u8109\u52A8\u672C\u8EAB\u5C31\u662F\u300C\u5DF2\u5728\u8DD1\u300D\u7684\u7B54\u590D\u3002\n if (current.status === 'pending') return false;\n if (!manual) return false;\n // \u624B\u52A8\u8865\u8DD1\uFF1A\u4E0A\u4E00\u4EE3\u5DF2\u6210\u529F\u7684\u5757\u5F53\u573A\u79CD\u56DE\uFF0C\u91CD\u53D1\u671F\u95F4\u8BD1\u6587\u4E0D\u95EA\u3002\n seed = current.outcomes.map((outcome) => (outcome !== null && outcome.ok ? { ...outcome } : null));\n }\n const snapshot = [...texts];\n const seedOutcomes = seed ?? snapshot.map(() => null);\n const next: RowState = {\n status: 'pending',\n texts: snapshot,\n outcomes: seedOutcomes,\n };\n this.rows.delete(rowKey);\n this.rows.set(rowKey, next);\n this.prune();\n this.bump();\n if (snapshot.length > 0) void this.run(rowKey, snapshot, seedOutcomes);\n return true;\n }\n\n private async run(\n rowKey: string,\n texts: readonly string[],\n initial: readonly (BlockOutcome | null)[]\n ): Promise<void> {\n // \u53BB\u91CD\u9760 ensure \u7684\u540C\u4EE3\u77ED\u8DEF\uFF1B\u6362\u4EE3\u540E\u7684\u5E76\u53D1\u5728\u9014\u5141\u8BB8\u5B58\u5728\u2014\u2014\u8FDF\u5230\u7684\u65E7\u4EE3\u7ED3\u679C\n // \u7531\u6BCF\u6279\u843D\u5B9A\u5904\u7684\u5FEB\u7167\u6838\u5BF9\u4E22\u5F03\u3002\u5DE5\u4F5C\u6570\u7EC4\u53D6\u79C1\u6709\u526F\u672C\uFF1A\u767B\u8BB0\u5728\u6C60\u91CC\u7684\u90A3\u4EFD\n // \u53EA\u7ECF set+notify \u6574\u6279\u6362\u4EE3\uFF0C\u4E0D\u5B58\u5728\u300C\u5C31\u5730\u5199\u5165\u5374\u8FD8\u6CA1\u901A\u77E5\u300D\u7684\u4E2D\u95F4\u53EF\u89C1\u6001\u3002\n const outcomes: (BlockOutcome | null)[] = initial.map((o) => o);\n let offset = 0;\n for (const batch of chunkTexts(texts)) {\n let results: ReplyBlockResult[] = [];\n try {\n results = await this.fetch(batch);\n } catch {\n // \u53D6\u6570\u9762\u7684\u5F02\u5E38\u4E0E\u5931\u8D25\u7ED3\u679C\u540C\u4E49\uFF1A\u8BE5\u6279\u4FDD\u6301\u539F\u6587\u3002api \u5C42\u81EA\u8EAB\u5DF2\u515C\u5E95\uFF0C\u8FD9\u91CC\n // \u9632\u7684\u662F fetcher \u5B9E\u73B0\u629B\u9519\u7684\u5F62\u6001\u2014\u2014\u5F02\u5E38\u8BF4\u4E0D\u51FA\u7EC6\u8282\uFF0C\u60AC\u505C\u53EA\u62A5\u300C\u7FFB\u8BD1\u5931\u8D25\u300D\u3002\n results = [];\n }\n for (let i = 0; i < batch.length; i++) {\n const result = results[i];\n if (result !== undefined && result.ok && result.translated && result.translated.trim()) {\n outcomes[offset + i] = { translated: result.translated, ok: true };\n } else {\n const detail = typeof result?.detail === 'string' && result.detail !== '' ? result.detail : undefined;\n outcomes[offset + i] = {\n translated: batch[i] ?? texts[offset + i] ?? '',\n ok: false,\n ...(detail === undefined ? {} : { detail }),\n };\n }\n }\n offset += batch.length;\n // \u9010\u6279\u53EF\u89C1\uFF1A\u884C\u4ECD\u662F\u8FD9\u4E00\u4EE3\u624D\u5199\u5165\uFF0C\u5199\u5B8C\u901A\u77E5\u2014\u2014\u5148\u56DE\u7684\u6BB5\u5148\u51FA\u4E2D\u6587\u3002\n const live = this.rows.get(rowKey);\n if (live === undefined || live.status !== 'pending' || !sameTexts(live.texts, texts)) return;\n this.rows.set(rowKey, {\n status: 'pending',\n texts: live.texts,\n outcomes: [...outcomes],\n });\n this.bump();\n }\n const state = this.rows.get(rowKey);\n if (state?.status === 'pending' && sameTexts(state.texts, texts)) {\n this.rows.set(rowKey, {\n status: 'settled',\n texts: state.texts,\n outcomes: [...outcomes],\n });\n this.bump();\n }\n }\n\n private prune(): void {\n while (this.rows.size > MAX_TRANSLATED_ROWS) {\n const oldest = this.rows.keys().next().value;\n if (oldest === undefined) break;\n this.rows.delete(oldest);\n }\n }\n\n private bump(): void {\n this.version += 1;\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch {\n // \u4E00\u4E2A\u8BA2\u9605\u8005\u7684\u5F02\u5E38\u4E0D\u5F71\u54CD\u5176\u4ED6\u8BA2\u9605\u8005\u3002\n }\n }\n }\n}\n\n/** \u6587\u672C\u6E05\u5355\u662F\u5426\u540C\u4EE3\uFF08\u9010\u4F4D\u76F8\u7B49\uFF09\uFF1A\u6C60\u7684\u5E42\u7B49\u5224\u636E\uFF0C\u6E32\u67D3\u5C42\u62FF\u5B83\u6838\u5BF9\u767B\u8BB0\u884C\u5C5E\u4E8E\u5F53\u524D\u6587\u672C\u3002 */\nexport function sameTexts(a: readonly string[], b: readonly string[]): boolean {\n if (a === b) return true;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/** \u4F9B\u6D4B\u8BD5\u6CE8\u5165\u53D6\u6570\u9762\u521B\u5EFA\u72EC\u7ACB\u6C60\u3002 */\nexport function createTranslateStore(fetcher: ReplyFetcher): ChatTranslateStore {\n return new ChatTranslateStore(fetcher);\n}\n\n/** \u6B63\u6587\u7FFB\u8BD1\u6C60\uFF1A\u5BA2\u6237\u7AEF\u534A\u8FB9\u552F\u4E00\u7684\u6309\u884C\u8BD1\u6587\u767B\u8BB0\u5904\u3002 */\nexport const chatTranslate = createTranslateStore(requestTranslateReply);\n", "/**\n * \u52A9\u624B\u884C\u6E32\u67D3\u6240\u9700\u7684\u300C\u5DE5\u4F5C\u7EC6\u8282\u300D\u5448\u73B0\u7B56\u7565\u3002\n *\n * \u5BBF\u4E3B\u628A\u8FD9\u4E00\u7B56\u7565\u6D3E\u751F\u81EA `ui-chat` \u914D\u7F6E\u6761\u76EE\u7684 `transcriptView` \u5B57\u6BB5\uFF1B\u672C\u63D2\u4EF6\u63A5\u7BA1\n * \u52A9\u624B\u884C\u6E32\u67D3\u540E\uFF0C\u7ECF `ctx.configForms.get('ui-chat')` \u8BFB\u540C\u4E00\u4E2A\u5B57\u6BB5\uFF0C\u6620\u5C04\u5230\u4E0E\u5BBF\u4E3B\n * \u540C\u5F62\u3001\u540C\u503C\u7684\u7B56\u7565\u8868\u3002\u7B56\u7565\u5BF9\u8C61\u6309\u6A21\u5F0F\u5E38\u9A7B\uFF08\u540C\u4E00\u6A21\u5F0F\u6052\u7B49\u8FD4\u56DE\u540C\u4E00\u5B9E\u4F8B\uFF09\uFF0C\n * \u9009\u62E9\u5668\u8BA2\u9605\u770B\u5230\u7684\u662F\u7A33\u5B9A\u5F15\u7528\u3002\n */\n\n/** \u4E0E\u5BBF\u4E3B ChatPresentationPolicy \u540C\u5F62\u7684\u7ED3\u6784\u6027\u955C\u50CF\u3002 */\nexport interface ChatPresentationPolicy {\n readonly mode: string;\n readonly foldCompletedTurns: boolean;\n readonly stepGrouping: 'collapsed' | 'history' | 'none';\n readonly liveProcessDetail: boolean;\n readonly settledReasoningPreview: boolean;\n}\n\nfunction policy(\n mode: string,\n foldCompletedTurns: boolean,\n stepGrouping: ChatPresentationPolicy['stepGrouping'],\n liveProcessDetail: boolean,\n settledReasoningPreview: boolean\n): ChatPresentationPolicy {\n return Object.freeze({ mode, foldCompletedTurns, stepGrouping, liveProcessDetail, settledReasoningPreview });\n}\n\n/**\n * \u6A21\u5F0F \u2192 \u7B56\u7565\u5E38\u91CF\u8868\uFF0C\u9010\u884C\u5BF9\u9F50\u5BBF\u4E3B ui-chat \u7684\u5B9E\u73B0\uFF08\u542B\u4E24\u4EE3 legacy \u5B58\u50A8\u503C\uFF1A\n * `normal` \u6309 standard \u8BFB\u3001`expanded` \u6309 detailed \u8BFB\uFF0C\u4ECE\u4E0D\u5199\u56DE\uFF09\u3002\n * \u6E32\u67D3\u5668\u53EA\u9009\u62E9\u5355\u4E2A\u5B57\u6BB5\uFF0C\u4E0D\u6309 mode \u5206\u652F\u3002\n */\nexport const POLICY_BY_MODE: Readonly<Record<string, ChatPresentationPolicy>> = Object.freeze({\n compact: policy('compact', true, 'collapsed', false, false),\n standard: policy('standard', true, 'collapsed', true, true),\n detailed: policy('detailed', true, 'history', true, true),\n verbose: policy('verbose', false, 'none', false, true),\n normal: policy('standard', true, 'collapsed', true, true),\n expanded: policy('detailed', true, 'history', true, true),\n});\n\n/** \u5BBF\u4E3B\u5728\u7528\u6237\u6CA1\u6709\u663E\u5F0F\u9009\u62E9\u65F6\u4F7F\u7528\u7684\u6A21\u5F0F\u3002 */\nexport const DEFAULT_MODE = 'standard';\n\n/** \u672C\u6A21\u5757\u53EA\u7528\u5230\u914D\u7F6E\u9762\u7684\u8FD9\u4E00\u5C0F\u7247\u5F62\u72B6\uFF08\u7ED3\u6784\u6027\u955C\u50CF\uFF0C\u503C\u8FB9\u4E0D\u8DE8\u5305\uFF09\u3002 */\ninterface ModeFormLike {\n getSnapshot(): { status: string; value?: Record<string, unknown> };\n subscribe(listener: () => void): () => void;\n}\n\n/**\n * \u8DDF\u968F `ui-chat` \u914D\u7F6E\u7684\u6D3B\u7B56\u7565\u6E90\u3002attach \u4E4B\u540E\uFF1A\u8BFB\u4E00\u6B21\u3001\u8BA2\u4E00\u6B21\uFF1B\u914D\u7F6E\u9762\u7F3A\u5E2D\n * \uFF08\u5BBF\u4E3B\u534A\u8FB9\u672A\u670D\u52A1\u8BE5\u6761\u76EE\u3001\u5185\u5B58\u6A21\u5F0F\uFF09\u65F6\u505C\u5728 standard\uFF0C\u4E0D\u53D1\u8BA2\u9605\u3002\n */\nexport class ChatPresentation {\n private policy: ChatPresentationPolicy = POLICY_BY_MODE[DEFAULT_MODE];\n private listeners = new Set<() => void>();\n private form: ModeFormLike | null = null;\n private unsubscribe: (() => void) | null = null;\n\n attach(form: ModeFormLike | null | undefined): void {\n this.detach();\n if (!form) {\n this.policy = POLICY_BY_MODE[DEFAULT_MODE];\n return;\n }\n this.form = form;\n this.read();\n this.unsubscribe = form.subscribe(() => this.read());\n }\n\n detach(): void {\n this.unsubscribe?.();\n this.unsubscribe = null;\n this.form = null;\n }\n\n getSnapshot = (): ChatPresentationPolicy => this.policy;\n\n subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n };\n\n private read(): void {\n const form = this.form;\n if (!form) return;\n let value: Record<string, unknown> | undefined;\n try {\n const snap = form.getSnapshot();\n value = snap.status === 'ready' ? snap.value : undefined;\n } catch {\n value = undefined;\n }\n const raw = typeof value?.transcriptView === 'string' ? value.transcriptView : '';\n const next = POLICY_BY_MODE[raw] ?? POLICY_BY_MODE[DEFAULT_MODE];\n if (next !== this.policy) {\n this.policy = next;\n for (const listener of [...this.listeners]) {\n try {\n listener();\n } catch {\n // \u8BA2\u9605\u8005\u5F02\u5E38\u4E92\u4E0D\u5F71\u54CD\u3002\n }\n }\n }\n }\n}\n\nexport function createChatPresentation(): ChatPresentation {\n return new ChatPresentation();\n}\n\n/** \u6E32\u67D3\u5668\u4E0E\u914D\u7F6E\u9762\u4E4B\u95F4\u7684\u8FDB\u7A0B\u5185\u5355\u4F8B\uFF1A\u5728 client/index.ts \u63A5\u7EBF\u4E00\u6B21\u3002 */\nexport const chatPresentation = createChatPresentation();\n"],
5
+ "mappings": "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",
6
6
  "names": ["import_react", "name", "import_react", "import_dsh_client_ui_primitives", "import_jsx_runtime", "import_react", "import_dsh_client_ui_primitives", "ReasoningRow", "policy", "AssistantStepView"]
7
7
  }