@lynn123411/dsh-chat-translate 1.3.2 → 1.7.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.
Files changed (32) hide show
  1. package/README.md +14 -9
  2. package/docs//351/205/215/347/275/256.md +11 -1
  3. package/lib/client.js +1527 -489
  4. package/lib/client.js.map +4 -4
  5. package/lib/index.js +565 -173
  6. package/lib/index.js.map +4 -4
  7. package/lib/types/client/index.d.ts +7 -5
  8. package/lib/types/client/locales.d.ts +51 -4
  9. package/lib/types/client/reading-settings-page.d.ts +1 -1
  10. package/lib/types/client/settings/store.d.ts +17 -4
  11. package/lib/types/client/settings/ui.d.ts +5 -3
  12. package/lib/types/client/translate/api.d.ts +13 -0
  13. package/lib/types/client/translate/client-cache.d.ts +6 -1
  14. package/lib/types/client/translate/hold.d.ts +135 -0
  15. package/lib/types/client/translate/lazy.d.ts +8 -0
  16. package/lib/types/client/translate/mount.d.ts +22 -0
  17. package/lib/types/client/translate/observer.d.ts +57 -0
  18. package/lib/types/client/translate/think-button.d.ts +19 -0
  19. package/lib/types/client/translate/think.d.ts +55 -0
  20. package/lib/types/client/translate/viewport-observer.d.ts +14 -2
  21. package/lib/types/index.d.ts +11 -18
  22. package/lib/types/server/adapters/base.d.ts +1 -1
  23. package/lib/types/server/adapters/openai.d.ts +2 -2
  24. package/lib/types/server/cache.d.ts +5 -1
  25. package/lib/types/server/config.d.ts +35 -7
  26. package/lib/types/server/dispatcher.d.ts +30 -3
  27. package/lib/types/server/pipeline/mask-tokens.d.ts +54 -48
  28. package/lib/types/server/pipeline/masking.d.ts +35 -12
  29. package/lib/types/server/pipeline/think.d.ts +49 -0
  30. package/lib/types/server/router.d.ts +24 -3
  31. package/lib/types/server/types.d.ts +18 -1
  32. package/package.json +23 -10
package/lib/client.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/index.ts", "../src/server/pipeline/mask-tokens.ts", "../src/server/pipeline/masking.ts", "../src/client/translate/client-cache.ts", "../src/describe-error.ts", "../src/client/translate/api.ts", "../src/client/translate/mount.ts", "../src/client/translate/viewport-observer.ts", "../src/client/translate/lazy.ts", "../src/client/translate/observer.ts", "../src/client/settings/ui.tsx", "../src/client/settings/store.ts", "../src/client/settings/styles.ts", "../src/client/reading-settings-page.tsx", "../src/client/locales.ts"],
4
- "sourcesContent": ["import { chatTranslateObserver } from './translate/observer.ts';\nimport { setupSettingsUi } from './settings/ui.tsx';\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 shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page (page claim\n * plus the card registration), settingsScope for the per-namespace settings\n * mirror, and the remote + remote.credentials pair for the credentials Remote\n * namespace \u2014 the runtime withholds any service not declared here, so\n * `ctx.remote.credentials` would be undefined otherwise. The locale service is\n * optional and read through `ctx.get`, like the renderer's slots registry.\n */\nexport const inject = ['slots', 'settingsScope', 'remote', 'remote.credentials'];\n\ninterface ClientContext {\n effect(factory: () => void | (() => void), label: string): void;\n get?(serviceName: string): any;\n slots?: any;\n settingsScope?: any;\n locale?: any;\n remote?: { credentials?: any };\n}\n\n/**\n * Mount the tool-call / think-summary translation observer and the settings UI\n * card inside the shared reading-settings page.\n * @param ctx - DSH browser client context.\n */\nexport function apply(ctx: ClientContext): void {\n // 1. Mount tool title / think summary translation observer (current session only)\n ctx.effect(() => chatTranslateObserver.start(document), 'dsh-chat-translate: title translate observer');\n\n // 2. 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\nexport { chatTranslateObserver } from './translate/observer.ts';\nexport { setupSettingsUi } from './settings/ui.tsx';\nexport { NonDestructiveTranslationMount } from './translate/mount.ts';\nexport { StreamDebounceViewportObserver } from './translate/viewport-observer.ts';\nexport { clientCache } from './translate/client-cache.ts';\nexport { lazyQueue } from './translate/lazy.ts';\nexport { settingsStore } from './settings/store.ts';\n", "/**\n * Mask placeholder tokens \u2014 single source of truth for the wire format, the\n * tolerant matcher and the leftover detector.\n *\n * Wire format: `__DSHMASKx<letters>_<index>__` (e.g. `__DSHMASKxkbdt_3__`).\n *\n * Design constraints, each one a measured failure of the previous\n * `__DSH_MASK_<index>__` format against real MT engines:\n *\n * - No underscore between `DSH` and `MASK`. Small models routinely drop that\n * one separator (`__DSH_MASK_1__` -> `__DSHMASK_1__`), which defeated the\n * old strict unmask regex and leaked the raw token into the UI.\n * - A random letter run (`x<letters>`) makes the token collision-free against\n * the source text and unambiguous to parse: the index is the digit run after\n * the last `_`, so no lookbehind is needed.\n * - The leading/trailing `__` stay: they survive MT and mark the token as\n * emphasis to the model.\n *\n * `matchMaskToken` stays tolerant of the *legacy* format and of the spacing /\n * casing / separator damage observed in the wild, because poisoned translations\n * already sit in the on-disk and browser caches.\n */\n\n/** Random part of the token. Letters only: never confuses the index scan. */\nconst RANDOM_LETTERS = 'abcdefghijklmnopqrstuvwxyz';\n\nexport function randomTokenId(): string {\n let out = '';\n for (let i = 0; i < 4; i++) {\n out += RANDOM_LETTERS[Math.floor(Math.random() * RANDOM_LETTERS.length)];\n }\n return out;\n}\n\nexport interface MaskTokenFormat {\n /** Unique per `mask()` call; embedded in every token of that call. */\n id: string;\n /** Build the placeholder written into the text sent to the translator. */\n token: (index: number) => string;\n}\n\nexport function createMaskTokenFormat(): MaskTokenFormat {\n const id = randomTokenId();\n return {\n id,\n token: (index: number) => `__DSHMASKx${id}_${index}__`,\n };\n}\n\n/**\n * Loose matcher covering every observed engine rewrite:\n *\n * __DSHMASKxkbdt_3__ exact\n * __DSHMASKXKBDT_3__ case folded\n * __DSH MASK xkbdt _ 3__ spaces inserted around the structural underscores\n * __DSHMASKkbdt_3__ the `x` id separator eaten\n * _DSHMASKxkbdt_3_ one boundary underscore dropped\n * __DSH_MASK_3__ / __dsh_mask_3__ legacy format (no random id)\n *\n * Both boundaries and the internal separators are optional, and the `x` id\n * separator may be replaced by whitespace/dot/hyphen. The two index branches\n * stay unambiguous: an id is always a letter run, the index always digits.\n * Capture groups: `[1] = legacy index` (undefined for the current format),\n * `[2] = token id` (undefined for the legacy format), `[3] = current index`.\n */\nexport const MASK_TOKEN_PATTERN_SOURCE =\n '_*\\\\s*DSH\\\\s*_*\\\\s*MASK\\\\s*(?:_?\\\\s*(\\\\d+)|_*[xX]?[\\\\s._-]*([a-z]{2,8})\\\\s*_+\\\\s*(\\\\d+))(?:_{0,2}(?=[^\\\\w]|$))?';\n\nexport interface MaskTokenMatch {\n index: number;\n /** false when the token carried an id from a different `mask()` call. */\n acceptsId: boolean;\n /** Length of the matched token in characters. */\n length: number;\n}\n\n/**\n * Match a single mask token at `start` in `text`.\n *\n * `acceptsId` is false when the token's random id belongs to another masking\n * pass \u2014 the caller must then leave the token untouched, because its index\n * would resolve to unrelated content.\n */\nexport function matchMaskToken(text: string, start: number, id: string): MaskTokenMatch | null {\n const anchored = new RegExp(`^(?:${MASK_TOKEN_PATTERN_SOURCE})`, 'i').exec(text.slice(start));\n if (!anchored) return null;\n\n // Group 1 is the legacy index, group 2 the random id, group 3 the current\n // index. Whichever index group matched, `parseInt` reads it as a plain\n // number: \"0\" and \"xabcd_0\" both resolve to 0.\n const index = Number.parseInt(anchored[1] ?? anchored[3] ?? '', 10);\n if (Number.isNaN(index)) return null;\n\n const tokenId = anchored[2];\n const acceptsId = tokenId === undefined || tokenId.toLowerCase() === id.toLowerCase();\n return { index, acceptsId, length: anchored[0].length };\n}\n\n/** True when translated text still carries a mask token of any format. */\nexport function hasMaskResidue(text: string): boolean {\n if (!text) return false;\n return new RegExp(MASK_TOKEN_PATTERN_SOURCE, 'i').test(text);\n}\n\n/** Every mask-token-looking fragment in `text`, verbatim. */\nexport function maskResidues(text: string): string[] {\n if (!text) return [];\n return text.match(new RegExp(MASK_TOKEN_PATTERN_SOURCE, 'gi')) ?? [];\n}\n\n/**\n * True when `translatedText` exposes a mask token that was NOT already part of\n * `originalText`.\n *\n * A placeholder surviving the round trip is a leak; but a source text that\n * legitimately talks *about* placeholders (this plugin's own documentation,\n * say) must survive translation, so tokens the source already carried are not\n * treated as residue.\n */\nexport function hasNewMaskResidue(originalText: string, translatedText: string): boolean {\n const residues = maskResidues(translatedText);\n if (residues.length === 0) return false;\n\n const presentInSource = new Map<string, number>();\n for (const fragment of maskResidues(originalText)) {\n const key = fragment.toLowerCase();\n presentInSource.set(key, (presentInSource.get(key) ?? 0) + 1);\n }\n for (const fragment of residues) {\n const key = fragment.toLowerCase();\n const remaining = presentInSource.get(key) ?? 0;\n if (remaining === 0) return true;\n presentInSource.set(key, remaining - 1);\n }\n return false;\n}\n", "import {\n createMaskTokenFormat,\n hasMaskResidue,\n hasNewMaskResidue,\n matchMaskToken,\n MASK_TOKEN_PATTERN_SOURCE,\n} from './mask-tokens.ts';\n\nexport interface MaskResult {\n maskedText: string;\n unmask: (translatedText: string) => string;\n}\n\n/** A match that ate an already-inserted mask token. Dropped, never re-masked. */\nconst MASK_MARKER = /DSH\\s*_?\\s*MASK/i;\n\n/** Bound the unmask scanner so a poisoned translation cannot spin forever. */\nconst MAX_UNMASK_PASSES = 10_000;\n\nexport class ContentMaskingPipeline {\n mask(text: string): MaskResult {\n if (!text || typeof text !== 'string') {\n return {\n maskedText: text,\n unmask: (t: string) => t,\n };\n }\n\n const masks: string[] = [];\n const format = createMaskTokenFormat();\n // Rules may only match the original, never-masked text: a match that\n // swallows an inserted token would hide a whole region from unmasking.\n const addMask = (match: string): string => {\n if (MASK_MARKER.test(match)) return match;\n const idx = masks.length;\n masks.push(match);\n return format.token(idx);\n };\n\n let processed = text;\n\n // 1. Multi-line code blocks (```...``` or ~~~...~~~)\n processed = processed.replace(/(?:```|~~~)[\\s\\S]*?(?:```|~~~)/g, (m) => addMask(m));\n\n // 2. Inline code (`...`)\n processed = processed.replace(/`[^`\\n]+`/g, (m) => addMask(m));\n\n // 3. URLs\n processed = processed.replace(/https?:\\/\\/[^\\s)\\];,;\"'<>]+/g, (m) => addMask(m));\n\n // 4. File paths and filenames with known extensions.\n // Every alternative is anchored on a left boundary (`.`/`/` accepted\n // only when preceded by a word char or start) so a match can never start\n // mid-path: `src/server` inside `src/server/dispatcher.ts` used to split\n // the path into `src__DSH_MASK_1__`, masking the slash away.\n processed = processed.replace(\n /(?:\\/[\\w.\\-\\\\\\/]+|(?<=[\\w.\\-])\\.\\.?[\\\\\\/][\\w.\\-\\\\\\/]+|(?<=^|[\\s([{\"'`])[a-zA-Z]:[\\\\\\/][\\w.\\-\\\\\\/]+|(?<![^\\s([{\"'`])\\b(?:[\\w.\\-]+\\/)+[\\w.\\-]+\\.[a-zA-Z0-9]+\\b|(?<![^\\s([{\"'`])\\b[\\w.\\-]+\\.(?:ts|tsx|js|jsx|json|ya?ml|md|py|go|rs|c|cpp|h|hpp|css|scss|html|sh|bash|mjs|cjs|toml|lock|log|env|svg|png|jpe?g|gif|tar|gz|zip|xml|sql)\\b)/g,\n (m) => addMask(m)\n );\n\n // 5. CLI flags / options (--flag, --flag=value, -f)\n processed = processed.replace(\n /(?<=^|[\\s(\\[{\"'])((?:--[a-zA-Z0-9_\\-]+(?:=[^\\s\"'<>]+)?)|(?:-[a-zA-Z0-9]+))(?=[\\s)\\]}\",:;!?]|$)/g,\n (m) => addMask(m)\n );\n\n const unmask = (translatedText: string): string => {\n if (!translatedText || masks.length === 0) {\n return translatedText;\n }\n return replaceMaskTokens(translatedText, format.id, (index) =>\n index >= 0 && index < masks.length ? masks[index] : undefined\n );\n };\n\n return {\n maskedText: processed,\n unmask,\n };\n }\n}\n\n/**\n * Resolve every in-range mask token in `text` through `resolve`.\n *\n * A token whose random id belongs to another masking pass, an out-of-range\n * index and any unmatched text are all kept verbatim; `hasMaskResidue()` on the\n * result is what flags a leaked token.\n */\nexport function replaceMaskTokens(\n text: string,\n id: string,\n resolve: (index: number) => string | undefined\n): string {\n const pattern = new RegExp(MASK_TOKEN_PATTERN_SOURCE, 'i');\n let out = '';\n let cursor = 0;\n let passes = 0;\n\n while (cursor < text.length && passes++ < MAX_UNMASK_PASSES) {\n const candidate = pattern.exec(text.slice(cursor));\n if (!candidate) break;\n\n const start = cursor + candidate.index;\n const match = matchMaskToken(text, start, id);\n // `matchMaskToken` owns the authoritative token length: the scanning and\n // anchored patterns must never disagree on where a token ends.\n const length = match?.length ?? candidate[0].length;\n if (length <= 0) {\n cursor = start + 1;\n continue;\n }\n\n const replacement = match?.acceptsId ? resolve(match.index) : undefined;\n // Not ours (foreign id / out of range): keep the token verbatim.\n out += text.slice(cursor, start) + (replacement ?? text.slice(start, start + length));\n cursor = start + length;\n }\n\n return out + text.slice(cursor);\n}\n\n/**\n * True when a translated string would still show a mask token to the user.\n * Callers use it to discard a poisoned translation (and to evict poisoned\n * cache entries written by earlier releases).\n */\nexport function isMaskLeak(translatedText: string): boolean {\n return hasMaskResidue(translatedText);\n}\n\n/**\n * Same as `isMaskLeak`, but a token the source text already contained is not a\n * leak: text that talks about placeholders (this plugin's docs, a bug report)\n * must round-trip untouched.\n */\nexport function isMaskLeakAgainst(originalText: string, translatedText: string): boolean {\n return hasNewMaskResidue(originalText, translatedText);\n}\n", "import { isMaskLeak } from '../../server/pipeline/masking.ts';\n\nconst CACHE_KEY = 'dsh-chat-translate:cache';\nconst MAX_LOCAL_ENTRIES = 500;\n/** Entries older than this are treated as expired. */\nconst TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\ninterface CacheEntry {\n t: number; // epoch ms; 0 = legacy entry without timestamp\n v: string;\n}\n\nexport class ClientCache {\n // Map preserves insertion order in JS, enabling true LRU semantics.\n private memCache = new Map<string, CacheEntry>();\n private dirty = false;\n private saveTimer: number | null = null;\n\n constructor() {\n this.load();\n }\n\n private load(): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const raw = localStorage.getItem(CACHE_KEY);\n if (raw) {\n const obj = JSON.parse(raw);\n if (obj && typeof obj === 'object') {\n for (const [k, entry] of Object.entries(obj)) {\n if (typeof entry === 'string') {\n // Legacy entry from an older release.\n this.memCache.set(k, { t: 0, v: entry });\n } else if (entry && typeof entry === 'object' && typeof (entry as CacheEntry).v === 'string') {\n this.memCache.set(k, entry as CacheEntry);\n }\n }\n }\n }\n } catch {\n // Ignore parse failure\n }\n }\n\n get(text: string): string | undefined {\n const key = text.trim().toLowerCase();\n const entry = this.memCache.get(key);\n if (entry === undefined) return undefined;\n // A translation still showing a mask placeholder leaked from an older\n // release \u2014 evict it so the text is requested again instead of rendered.\n if (typeof entry.v === 'string' && isMaskLeak(entry.v)) {\n this.memCache.delete(key);\n this.dirty = true;\n this.scheduleSave();\n return undefined;\n }\n // Drop dirty fallback entries where value equals key\n if (entry.v && entry.v.trim().toLowerCase() === key) {\n this.memCache.delete(key);\n this.dirty = true;\n this.scheduleSave();\n return undefined;\n }\n if (entry.t > 0 && Date.now() - entry.t > TTL_MS) {\n this.memCache.delete(key);\n this.dirty = true;\n this.scheduleSave();\n return undefined;\n }\n // True LRU: Re-insert to move to the end (most recently used)\n this.memCache.delete(key);\n this.memCache.set(key, entry);\n return entry.v;\n }\n\n set(text: string, translated: string): void {\n const key = text.trim().toLowerCase();\n if (this.memCache.has(key)) {\n this.memCache.delete(key);\n } else if (this.memCache.size >= MAX_LOCAL_ENTRIES) {\n // Evict least recently used (first item in Map)\n const oldestKey = this.memCache.keys().next().value;\n if (oldestKey !== undefined) {\n this.memCache.delete(oldestKey);\n }\n }\n this.memCache.set(key, { t: Date.now(), v: translated });\n this.dirty = true;\n this.scheduleSave();\n }\n\n private scheduleSave(): void {\n if (this.saveTimer !== null || typeof window === 'undefined') return;\n this.saveTimer = window.setTimeout(() => {\n this.saveTimer = null;\n this.flushSync();\n }, 2000);\n }\n\n flushSync(): void {\n if (!this.dirty || typeof localStorage === 'undefined') return;\n this.dirty = false;\n try {\n const obj: Record<string, CacheEntry> = {};\n for (const [k, v] of this.memCache.entries()) {\n obj[k] = v;\n }\n localStorage.setItem(CACHE_KEY, JSON.stringify(obj));\n } catch {\n // Ignore quota error\n }\n }\n\n clear(): void {\n this.memCache.clear();\n this.dirty = true;\n this.flushSync();\n }\n\n size(): number {\n return this.memCache.size;\n }\n}\n\nexport const clientCache = new ClientCache();", "/**\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\nexport interface TranslateItemResult {\n original: string;\n translated: string;\n channel: string;\n cached: boolean;\n}\n\nexport interface TranslateBatchOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\nexport async function requestTranslateBatch(\n texts: string[],\n options: TranslateBatchOptions = {}\n): Promise<TranslateItemResult[]> {\n if (!Array.isArray(texts) || texts.length === 0) return [];\n \n // Filter out empty or whitespace-only texts\n const validTexts = texts.map((t) => (typeof t === 'string' ? t : ''));\n if (validTexts.length === 0) return [];\n\n const controller = new AbortController();\n const timeoutId = options.timeoutMs\n ? setTimeout(() => controller.abort(), options.timeoutMs)\n : null;\n\n const effectiveSignal = options.signal\n ? (options.signal.aborted ? options.signal : controller.signal)\n : controller.signal;\n\n if (options.signal) {\n options.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch('/api/dsh-chat-translate/translate', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ texts: validTexts }),\n signal: effectiveSignal,\n });\n\n if (res.ok) {\n const data = (await res.json()) as { ok: boolean; results?: TranslateItemResult[]; error?: string };\n if (data.ok && Array.isArray(data.results) && data.results.length > 0) {\n return data.results;\n } else if (data.error) {\n console.warn(`[dsh-chat-translate] \u7FFB\u8BD1\u63A5\u53E3\u9519\u8BEF: ${data.error}`);\n }\n } else {\n console.warn(`[dsh-chat-translate] \u7FFB\u8BD1\u8BF7\u6C42\u5931\u8D25 (HTTP ${res.status})`);\n }\n } catch (err: any) {\n if (err?.name === 'AbortError') {\n // Intentionally aborted\n }\n } finally {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n }\n\n // Host route unreachable or error \u2014 leave the original text untouched\n return validTexts.map((t) => ({\n original: t,\n translated: t,\n channel: 'fallback-client',\n cached: false,\n }));\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", "/**\n * Non-destructive translation DOM mount mechanism.\n *\n * Preserves the original React Fiber tree, event listeners, and nested DOM nodes\n * by wrapping them in a hidden container (<span class=\"dsh-tidy-original-hidden\">)\n * rather than destroying them with `element.textContent = translated`.\n *\n * Displays the translated text in a companion container (<span class=\"dsh-tidy-translated-block\">).\n * Clicking the translation toggles between original and translated text in-place.\n */\n\nexport const CLASS_ORIGINAL_HIDDEN = 'dsh-tidy-original-hidden';\nexport const CLASS_ORIGINAL_SHOWN = 'dsh-tidy-original-shown';\nexport const CLASS_TRANSLATED_BLOCK = 'dsh-tidy-translated-block';\n\nexport interface MountOptions {\n originalText?: string;\n interactive?: boolean;\n}\n\nexport class NonDestructiveTranslationMount {\n /**\n * Mounts a translated string onto the target element non-destructively.\n */\n static mount(\n element: HTMLElement,\n translated: string,\n options: MountOptions = {}\n ): void {\n if (!element || !element.ownerDocument) return;\n const doc = element.ownerDocument;\n\n // Check if already mounted\n let transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n let origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n\n if (transWrapper && origWrapper) {\n // Update existing translation\n transWrapper.textContent = translated;\n element.dataset.tidyTranslated = 'true';\n if (options.originalText) element.dataset.original = options.originalText;\n return;\n }\n\n const originalText = options.originalText ?? this.extractVisibleText(element);\n\n // Create wrapper for original children\n origWrapper = doc.createElement('span');\n origWrapper.className = CLASS_ORIGINAL_HIDDEN;\n origWrapper.style.display = 'none';\n\n // Move all existing child nodes into origWrapper\n while (element.firstChild) {\n origWrapper.appendChild(element.firstChild);\n }\n\n // Create translation block\n transWrapper = doc.createElement('span');\n transWrapper.className = CLASS_TRANSLATED_BLOCK;\n transWrapper.textContent = translated;\n\n // Interactive bilingual toggle\n const interactive = options.interactive !== false;\n if (interactive) {\n\n const showOriginal = (e: MouseEvent): void => {\n e.stopPropagation();\n if (!origWrapper || !transWrapper) return;\n origWrapper.style.display = 'inline';\n origWrapper.className = CLASS_ORIGINAL_SHOWN;\n transWrapper.style.display = 'none';\n };\n\n const showTranslated = (e: MouseEvent): void => {\n e.stopPropagation();\n if (!origWrapper || !transWrapper) return;\n origWrapper.style.display = 'none';\n origWrapper.className = CLASS_ORIGINAL_HIDDEN;\n transWrapper.style.display = 'inline';\n };\n\n transWrapper.addEventListener('click', showOriginal);\n origWrapper.addEventListener('click', showTranslated);\n }\n\n // Append both to target element\n element.appendChild(transWrapper);\n element.appendChild(origWrapper);\n\n element.dataset.tidyTranslated = 'true';\n element.dataset.original = originalText;\n }\n\n /**\n * Unmounts translation and restores original DOM nodes completely.\n */\n static unmount(element: HTMLElement): void {\n if (!element) return;\n\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n const transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n\n if (origWrapper) {\n // Move all original children back to element\n while (origWrapper.firstChild) {\n element.insertBefore(origWrapper.firstChild, origWrapper);\n }\n origWrapper.remove();\n }\n\n if (transWrapper) {\n transWrapper.remove();\n }\n\n // Fallback if no wrapper was created but text was modified directly\n if (!origWrapper && element.dataset.original) {\n element.textContent = element.dataset.original;\n }\n\n delete element.dataset.tidyTranslated;\n delete element.dataset.original;\n }\n\n /**\n * Checks if an element has non-destructive translation mounted.\n */\n static isMounted(element: HTMLElement): boolean {\n return (\n element.dataset.tidyTranslated === 'true' &&\n !!element.querySelector(`:scope > .${CLASS_TRANSLATED_BLOCK}`)\n );\n }\n\n /**\n * Gets the original text recorded on the element or contained in origWrapper.\n */\n static getOriginal(element: HTMLElement): string | undefined {\n if (element.dataset.original) return element.dataset.original;\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n return origWrapper ? origWrapper.textContent?.trim() : undefined;\n }\n\n /**\n * Extracts text content excluding our own translation wrappers.\n */\n static extractVisibleText(element: HTMLElement): string {\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n if (origWrapper) {\n return origWrapper.textContent?.trim() || '';\n }\n const transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n if (transWrapper) {\n return transWrapper.textContent?.trim() || '';\n }\n return element.textContent?.trim() || '';\n }\n}", "/**\n * StreamDebounceViewportObserver\n *\n * Combines:\n * 1. True IntersectionObserver for viewport visibility detection (rootMargin: '150px 0px').\n * 2. Typing/streaming debounce (default 400ms) to avoid translating partial streaming sentences.\n * 3. Batch queuing to group multiple visible elements into efficient batch requests.\n */\n\nexport interface ViewportObserverOptions {\n rootMargin?: string;\n debounceMs?: number;\n onVisibleBatch: (items: Array<{ element: HTMLElement; text: string }>) => void;\n}\n\nexport class StreamDebounceViewportObserver {\n private intersectionObserver: IntersectionObserver | null = null;\n /**\n * Pending per-element streaming debounce timers. A Map (not a WeakMap)\n * because disconnect() must enumerate and clear every pending timer: a\n * WeakMap cannot be iterated, so a debounce armed just before the switch was\n * turned off would still fire registerForViewport() afterwards.\n */\n private streamingTimers = new Map<HTMLElement, number>();\n private pendingQueue: Array<{ element: HTMLElement; text: string }> = [];\n private batchFlushTimer: number | null = null;\n private options: Required<Omit<ViewportObserverOptions, 'onVisibleBatch'>> & {\n onVisibleBatch: ViewportObserverOptions['onVisibleBatch'];\n };\n\n constructor(options: ViewportObserverOptions) {\n this.options = {\n rootMargin: options.rootMargin ?? '150px 0px',\n debounceMs: options.debounceMs ?? 400,\n onVisibleBatch: options.onVisibleBatch,\n };\n this.initIntersectionObserver();\n }\n\n private initIntersectionObserver(): void {\n if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {\n return;\n }\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n const el = entry.target;\n // Stop observing once it enters viewport and is queued\n this.intersectionObserver?.unobserve(el);\n const text = el.dataset.tidyPendingText || el.textContent?.trim() || '';\n if (text) {\n delete el.dataset.tidyPendingText;\n this.enqueueBatch(el, text);\n }\n }\n }\n },\n {\n root: null, // viewport\n rootMargin: this.options.rootMargin,\n threshold: 0,\n }\n );\n }\n\n /**\n * Observe an element with streaming debounce.\n * If streaming updates characterData repeatedly within debounceMs, the timer resets.\n */\n observeWithDebounce(element: HTMLElement, text: string, immediate = false): void {\n if (!element || !text) return;\n\n // Clear any active streaming timer for this element\n const existingTimer = this.streamingTimers.get(element);\n if (existingTimer !== undefined) {\n clearTimeout(existingTimer);\n this.streamingTimers.delete(element);\n }\n\n if (immediate || this.options.debounceMs <= 0) {\n this.registerForViewport(element, text);\n return;\n }\n\n const timer = window.setTimeout(() => {\n // Stale-callback guard: disconnect() clears the map, so a callback that\n // is no longer the registered timer must not re-enter the observer.\n if (this.streamingTimers.get(element) !== timer) return;\n this.streamingTimers.delete(element);\n if (element.isConnected) {\n // Read latest text content after streaming settles\n const latestText = element.textContent?.trim() || text;\n this.registerForViewport(element, latestText);\n }\n }, this.options.debounceMs);\n\n this.streamingTimers.set(element, timer);\n }\n\n private registerForViewport(element: HTMLElement, text: string): void {\n if (!element.isConnected) return;\n\n if (!this.intersectionObserver) {\n // Fallback if IntersectionObserver is unsupported: enqueue immediately\n this.enqueueBatch(element, text);\n return;\n }\n\n element.dataset.tidyPendingText = text;\n this.intersectionObserver.observe(element);\n }\n\n private enqueueBatch(element: HTMLElement, text: string): void {\n this.pendingQueue.push({ element, text });\n if (this.batchFlushTimer === null && typeof window !== 'undefined') {\n this.batchFlushTimer = window.setTimeout(() => {\n this.batchFlushTimer = null;\n this.flushQueue();\n }, 50);\n }\n }\n\n private flushQueue(): void {\n if (this.pendingQueue.length === 0) return;\n const batch = [...this.pendingQueue];\n this.pendingQueue = [];\n this.options.onVisibleBatch(batch);\n }\n\n unobserve(element: HTMLElement): void {\n const timer = this.streamingTimers.get(element);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.streamingTimers.delete(element);\n }\n delete element.dataset.tidyPendingText;\n if (this.intersectionObserver) {\n this.intersectionObserver.unobserve(element);\n }\n }\n\n disconnect(): void {\n if (this.batchFlushTimer !== null) {\n clearTimeout(this.batchFlushTimer);\n this.batchFlushTimer = null;\n }\n // Drop every pending streaming debounce: after a disable none of them may\n // fire registerForViewport() (the Map is iterable, so this is exhaustive).\n for (const timer of this.streamingTimers.values()) {\n clearTimeout(timer);\n }\n this.streamingTimers.clear();\n this.pendingQueue = [];\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.initIntersectionObserver();\n }\n }\n}\n", "import { clientCache } from './client-cache.ts';\nimport { requestTranslateBatch } from './api.ts';\nimport { NonDestructiveTranslationMount } from './mount.ts';\nimport { StreamDebounceViewportObserver } from './viewport-observer.ts';\n\ntype TranslateTask = {\n element: HTMLElement;\n text: string;\n};\n\nclass LazyTranslationQueue {\n private enabled = true;\n private viewportObserver: StreamDebounceViewportObserver;\n\n constructor() {\n this.viewportObserver = new StreamDebounceViewportObserver({\n rootMargin: '150px 0px',\n debounceMs: 400,\n onVisibleBatch: (items) => this.handleVisibleBatch(items),\n });\n }\n\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n if (!enabled) {\n this.viewportObserver.disconnect();\n }\n }\n\n observe(element: HTMLElement, text: string, immediate = false): void {\n if (!this.enabled || !element.isConnected) return;\n\n // 1. If cached, apply immediately non-destructively\n const cached = clientCache.get(text);\n if (cached) {\n this.applyTranslation(element, cached, text);\n return;\n }\n\n // 2. Delegate to streaming-debounced viewport observer\n this.viewportObserver.observeWithDebounce(element, text, immediate);\n }\n\n private async handleVisibleBatch(items: TranslateTask[]): Promise<void> {\n if (!this.enabled || items.length === 0) return;\n\n // Document order: translations appear in reading order (top-to-bottom),\n // never out-of-order even when IntersectionObserver fires entries randomly.\n const sorted = [...items].sort((a, b) => {\n if (a.element === b.element) return 0;\n const pos = a.element.compareDocumentPosition(b.element);\n return pos & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n });\n\n // Group elements by text to deduplicate API requests\n const textMap = new Map<string, HTMLElement[]>();\n for (const item of sorted) {\n if (!item.element.isConnected) continue;\n\n const cached = clientCache.get(item.text);\n if (cached) {\n this.applyTranslation(item.element, cached, item.text);\n continue;\n }\n\n const list = textMap.get(item.text) || [];\n list.push(item.element);\n textMap.set(item.text, list);\n }\n\n const uniqueTexts = Array.from(textMap.keys());\n if (uniqueTexts.length === 0) return;\n\n const results = await requestTranslateBatch(uniqueTexts);\n\n for (const res of results) {\n if (\n res.translated &&\n res.translated.trim() &&\n res.channel !== 'fallback' &&\n res.channel !== 'fallback-client' &&\n res.translated.trim() !== res.original.trim()\n ) {\n clientCache.set(res.original, res.translated);\n const entries = textMap.get(res.original) || [];\n for (const entry of entries) {\n if (entry.isConnected && this.enabled) {\n this.applyTranslation(entry, res.translated, res.original);\n }\n }\n } else if (res.channel === 'fallback' || res.channel === 'fallback-client') {\n console.debug(`[dsh-chat-translate] \u7FFB\u8BD1\u672A\u6210\u529F (\u964D\u7EA7\u4FDD\u7559\u539F\u6587): \"${res.original.slice(0, 40)}\"`);\n }\n }\n }\n\n private applyTranslation(element: HTMLElement, translated: string, original: string): void {\n if (!element.isConnected || !this.enabled) return;\n NonDestructiveTranslationMount.mount(element, translated, {\n originalText: original,\n });\n }\n\n disconnect(): void {\n this.viewportObserver.disconnect();\n }\n}\n\nexport const lazyQueue = new LazyTranslationQueue();", "import { clientCache } from './client-cache.ts';\nimport { lazyQueue } from './lazy.ts';\nimport { NonDestructiveTranslationMount } from './mount.ts';\n\n// Case-insensitive match: DSH builds vary the summary class casing across\n// versions \u2014 lowercase \"summary\" for tool-call rows (MISisG_summary /\n// _48RFeq_summary) vs camelCase \"thinkSummary\" for the Think card row.\n// Attribute values are matched case-sensitively by default in HTML, which is\n// why the old lowercase-only selector silently missed think summaries.\nconst TOOL_TITLE_SELECTOR = '[class*=\"summary\" i]';\n\n/**\n * Current-session scroll container (the conversation layout re-renders this\n * whole subtree when the active session changes), falling back to the chat\n * flow. Both are emitted by the DSH web UI.\n */\nconst SESSION_ROOT_SELECTOR = '[data-conversation-scroll], [data-chat-flow]';\n\n/** How often (ms) we re-check that the observed root is still the live one. */\nconst ROOT_CHECK_INTERVAL_MS = 3000;\n\nfunction isToolSummarySpan(span: HTMLElement): boolean {\n if (!span || span.nodeType !== 1) return false;\n if (span.hasAttribute('aria-hidden')) return false;\n\n // Never match parent rows or containers that contain title, leading icon, chevron or nested summary\n if (span.querySelector?.('[class*=\"title\"], [class*=\"leading\"], [class*=\"chevron\"], [class*=\"sep\"], [class*=\"summary\" i]')) {\n return false;\n }\n\n const cls = span.className || '';\n if (/title|leading|icon|badge|chevron|separator|sep\\b|row\\b|root\\b|card\\b/i.test(cls)) return false;\n\n // Never translate fold toggle (\"\u5C55\u5F00\"/\"\u6536\u8D77\") or Think card's own title badge (\"Think\"/\"\u601D\u8003\")\n const rawToggle = (span.textContent || '').trim();\n if (\n rawToggle.length <= 12 &&\n /^(\u5C55\u5F00|\u6536\u8D77|\u5C55\u5F00\u5168\u90E8|\u6536\u8D77\u5168\u90E8|Expand|Collapse|Show more|Show less|Think|\u601D\u8003)$/i.test(rawToggle)\n ) {\n return false;\n }\n if (rawToggle === 'Think' || rawToggle === '\u601D\u8003') return false;\n if (\n span.closest('button, [role=\"button\"]') &&\n rawToggle.length <= 12 &&\n /\u5C55\u5F00|\u6536\u8D77|Expand|Collapse|Think|\u601D\u8003/i.test(rawToggle)\n ) {\n return false;\n }\n\n // Must belong to tool call or think block card\n if (\n span.closest(\n '[data-chat-call-id], [data-slot=\"tool.call.toolview\"], [data-sample], [data-variant], [data-tool], [data-disclosure-row]'\n )\n ) {\n return true;\n }\n return false;\n}\n\n/**\n * DSH's Think card (ReasoningRow) carries `data-state=\"running\"` on its root\n * while the reasoning block is still streaming and flips it to `\"ok\"` once it\n * has finished. While running, the collapsed summary follows the live tail of\n * the reasoning text (`.summary[data-follow-end]`), so translating it would\n * capture an intermediate line that is replaced by the stable first-line\n * summary when the block ends. Defer instead: the observed `data-state`\n * mutation re-scans this span, at which point the final summary is processed.\n */\nfunction thinkBlockStillRunning(span: HTMLElement): boolean {\n const thinkRoot = span.closest<HTMLElement>('[data-variant=\"think\"]');\n return !!thinkRoot && thinkRoot.dataset.state === 'running';\n}\n\nexport class ChatTranslateObserver {\n private observer: MutationObserver | null = null;\n private rootElement: HTMLElement | null = null;\n private rootCheckTimer: number | null = null;\n private isEnabled = true;\n\n constructor() {\n this.handleMutations = this.handleMutations.bind(this);\n }\n\n setEnabled(enabled: boolean): void {\n this.isEnabled = enabled;\n if (enabled) {\n lazyQueue.setEnabled(true);\n this.start();\n } else {\n this.restoreOriginals();\n this.disconnect();\n lazyQueue.setEnabled(false);\n }\n }\n\n private restoreOriginals(): void {\n const scope = this.rootElement ?? document;\n const spans = scope.querySelectorAll<HTMLElement>('[data-tidy-translated=\"true\"]');\n for (const span of spans) {\n NonDestructiveTranslationMount.unmount(span);\n }\n }\n\n /**\n * The active session's scroll container. Only the currently-viewed session\n * is translated; switching sessions replaces this subtree and the observer\n * naturally follows the new content.\n */\n private isVisible(el: HTMLElement): boolean {\n if (el.hasAttribute('hidden')) return false;\n if (el.style.display === 'none') return false;\n try {\n return el.getClientRects().length > 0;\n } catch {\n return true;\n }\n }\n\n private findRoot(documentRef: Document): HTMLElement {\n if (!documentRef.body) {\n return documentRef.documentElement;\n }\n const candidates = documentRef.querySelectorAll<HTMLElement>(SESSION_ROOT_SELECTOR);\n for (const el of candidates) {\n if (this.isVisible(el)) {\n return el;\n }\n }\n return documentRef.body;\n }\n\n start(documentRef: Document = document): () => void {\n if (!this.isEnabled || typeof window === 'undefined') {\n return () => {};\n }\n\n const findAndObserveRoot = () => {\n const root = this.findRoot(documentRef);\n\n this.rootElement = root;\n\n // 1. Initial scan of existing tool elements\n this.scanContainer(root);\n\n // 2. Setup MutationObserver\n if (!this.observer) {\n this.observer = new MutationObserver(this.handleMutations);\n this.observer.observe(root, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n attributeFilter: ['data-state', 'data-tool', 'data-variant', 'data-sample', 'aria-expanded'],\n });\n }\n };\n\n findAndObserveRoot();\n\n // Defense: if the observed root is detached or hidden (e.g. the user\n // switched to another view), re-probe for the live session container.\n this.scheduleRootCheck();\n\n return () => {\n this.disconnect();\n };\n }\n\n private scheduleRootCheck(): void {\n if (this.rootCheckTimer !== null || typeof window === 'undefined') return;\n this.rootCheckTimer = window.setInterval(() => {\n if (this.rootElement && this.rootElement.isConnected && this.isVisible(this.rootElement)) {\n return;\n }\n this.restart();\n }, ROOT_CHECK_INTERVAL_MS);\n }\n\n private restart(): void {\n if (typeof window === 'undefined') return;\n const wasEnabled = this.isEnabled;\n this.disconnect();\n if (wasEnabled) {\n this.start();\n }\n }\n\n private handleMutations(mutations: MutationRecord[]): void {\n if (!this.isEnabled) return;\n\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n for (let i = 0; i < mutation.addedNodes.length; i++) {\n const node = mutation.addedNodes[i];\n if (node instanceof HTMLElement) {\n if (\n node.classList?.contains('dsh-tidy-translated-block') ||\n node.classList?.contains('dsh-tidy-original-hidden') ||\n node.classList?.contains('dsh-tidy-original-shown')\n ) {\n continue;\n }\n this.scanNode(node);\n }\n }\n } else if (mutation.type === 'attributes') {\n const target = mutation.target;\n if (target instanceof HTMLElement) {\n if (\n target.classList?.contains('dsh-tidy-translated-block') ||\n target.classList?.contains('dsh-tidy-original-hidden') ||\n target.classList?.contains('dsh-tidy-original-shown')\n ) {\n continue;\n }\n this.scanNode(target);\n }\n } else if (mutation.type === 'characterData') {\n const parent = mutation.target.parentElement;\n if (parent instanceof HTMLElement) {\n if (\n parent.classList?.contains('dsh-tidy-translated-block') ||\n parent.classList?.contains('dsh-tidy-original-hidden') ||\n parent.classList?.contains('dsh-tidy-original-shown')\n ) {\n continue;\n }\n this.scanNode(parent);\n }\n }\n }\n }\n\n private scanContainer(container: HTMLElement): void {\n const spans = container.querySelectorAll<HTMLElement>(TOOL_TITLE_SELECTOR);\n spans.forEach((span) => {\n if (isToolSummarySpan(span)) {\n this.processSpan(span);\n }\n });\n }\n\n private scanNode(node: HTMLElement): void {\n if (node.matches?.(TOOL_TITLE_SELECTOR) && isToolSummarySpan(node)) {\n this.processSpan(node);\n }\n\n const spans = node.querySelectorAll<HTMLElement>(TOOL_TITLE_SELECTOR);\n spans.forEach((span) => {\n if (isToolSummarySpan(span)) {\n this.processSpan(span);\n }\n });\n }\n\n private processSpan(span: HTMLElement): void {\n // Think summaries are translated only after the reasoning block has fully\n // finished streaming \u2014 never while `data-state=\"running\"` (the summary is\n // then a live tail line that would be replaced moments later). The\n // transition to \"ok\" triggers a re-scan via the observed data-state\n // attribute, which is when this span becomes processable.\n if (thinkBlockStillRunning(span)) return;\n\n if (NonDestructiveTranslationMount.isMounted(span)) {\n const original = NonDestructiveTranslationMount.getOriginal(span);\n if (original) {\n const cached = clientCache.get(original);\n if (cached) return;\n }\n return;\n }\n\n const text = NonDestructiveTranslationMount.extractVisibleText(span);\n if (!text) return;\n\n // Check fast client cache\n const cached = clientCache.get(text);\n if (cached) {\n NonDestructiveTranslationMount.mount(span, cached, {\n originalText: text,\n });\n return;\n }\n\n // Send to viewport lazy queue\n lazyQueue.observe(span, text);\n }\n\n disconnect(): void {\n if (this.rootCheckTimer !== null) {\n clearInterval(this.rootCheckTimer);\n this.rootCheckTimer = null;\n }\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n lazyQueue.disconnect();\n this.rootElement = null;\n }\n}\n\nexport const chatTranslateObserver = new ChatTranslateObserver();\n", "import { useState, useEffect } from 'react';\nimport * as React from 'react';\nimport type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';\nimport { settingsStore, SETTINGS_NAMESPACE, type ClientSettingsState } 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\nfunction Switch(props: { checked: boolean; onChange: () => void; label: string }): React.ReactElement {\n return (\n <button\n type=\"button\"\n className=\"dsh-tidy-switch\"\n role=\"switch\"\n aria-checked={props.checked}\n onClick={props.onChange}\n aria-label={props.label}\n />\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.\n * @returns The card element.\n */\nexport function TidySettingsPanel(_props: PropsRuntime<'reading.settings.item'>): React.ReactElement {\n ensureSettingsStyles();\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 ? `\u8FDE\u63A5\u6B63\u5E38\uFF0C\u5EF6\u8FDF ${res.latencyMs}ms`\n : `\u5931\u8D25\uFF1A${res.error || '\u672A\u77E5\u9519\u8BEF'}`;\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 ? '\u5DF2\u6E05\u9664 API Key\uFF08AI \u901A\u9053\u5C06\u4E0D\u53EF\u7528\uFF0CBing \u515C\u5E95\uFF09'\n : '\u5DF2\u4FDD\u5B58\u5230 ~/.dsh/.credentials.yaml\uFF0C\u7ACB\u5373\u751F\u6548',\n });\n setApiKeyInput('');\n } else {\n setKeyMsg({ ok: false, text: `\u4FDD\u5B58\u5931\u8D25\uFF1A${res.error || '\u672A\u77E5\u9519\u8BEF'}` });\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>\u5DE5\u5177\u8C03\u7528\u4E0E\u601D\u8003\u6458\u8981\u7FFB\u8BD1</span>\n <Switch\n checked={state.enabled}\n onChange={() => settingsStore.update({ enabled: !state.enabled })}\n label=\"\u542F\u7528\u5DE5\u5177\u8C03\u7528\u4E0E\u6458\u8981\u7FFB\u8BD1\"\n />\n </div>\n <div className=\"dsh-tidy-desc\">\n \u81EA\u52A8\u5C06\u5F53\u524D\u4F1A\u8BDD\u4E2D\u5DE5\u5177\u8C03\u7528\u6807\u9898\u4E0E\u601D\u8003\u6298\u53E0\u6458\u8981\uFF08\u5982 <code>Locate DSH home directory structure</code>\uFF09\u7FFB\u8BD1\u4E3A\u4E2D\u6587\uFF0C\n \u70B9\u51FB\u8BD1\u6587\u53EF\u539F\u5730\u5207\u6362\u539F\u6587/\u8BD1\u6587\u3002\u601D\u8003\u6298\u53E0\u6458\u8981\u4EC5\u5728\u601D\u8003\u5B8C\u5168\u7ED3\u675F\u540E\u624D\u7FFB\u8BD1\uFF08\u4E0D\u7FFB\u8BD1\u6D41\u5F0F\u4E2D\u95F4\u6001\uFF09\u3002\n \u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u67E5\u770B\u7684\u4F1A\u8BDD\uFF0C\u5BF9\u8BDD\u6B63\u6587\u6C38\u4E0D\u7FFB\u8BD1\u3002\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>AI \u7FFB\u8BD1\uFF08OpenAI \u517C\u5BB9\u534F\u8BAE\uFF09</span>\n <Switch\n checked={state.aiEnabled}\n onChange={() => settingsStore.update({ aiEnabled: !state.aiEnabled })}\n label=\"\u542F\u7528 AI \u7FFB\u8BD1\u901A\u9053\"\n />\n </div>\n <div className=\"dsh-tidy-desc\">\n <span className={`dsh-tidy-badge ${state.aiConfigured ? 'dsh-tidy-badge-ok' : 'dsh-tidy-badge-warn'}`}>\n {state.aiConfigured ? '\u5DF2\u914D\u7F6E' : '\u672A\u914D\u7F6E'}\n </span>{' '}\n \u672A\u914D\u7F6E\u65F6 AI \u901A\u9053\u81EA\u52A8\u8DF3\u8FC7\uFF0C\u7531 Bing \u515C\u5E95\u3002\n </div>\n\n {state.aiEnabled && (\n <>\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">API Key</div>\n <div className=\"dsh-tidy-row-desc\">\n \u4FDD\u5B58\u81F3 <code>~/.dsh/.credentials.yaml</code> \u7684 <code>TRANSLATE_API_KEY</code>\uFF0C\u4FDD\u5B58\u540E\u7ACB\u5373\u751F\u6548\uFF1B\u7559\u7A7A\u4FDD\u5B58 = \u6E05\u9664\n </div>\n </div>\n <div className=\"dsh-tidy-input-row\">\n <input\n type=\"password\"\n className=\"dsh-tidy-input\"\n placeholder={state.aiConfigured ? '\u5DF2\u914D\u7F6E\uFF08\u5982\u9700\u66F4\u6362\u8BF7\u76F4\u63A5\u8F93\u5165\uFF09' : 'sk-...'}\n value={apiKeyInput}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setApiKeyInput(e.target.value)}\n style={{ width: '260px' }}\n aria-label=\"API Key\"\n />\n <button type=\"button\" className=\"dsh-tidy-btn\" disabled={savingKey} onClick={handleSaveKey}>\n {savingKey ? '\u4FDD\u5B58\u4E2D\u2026' : '\u4FDD\u5B58'}\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\">Base URL</div>\n <div className=\"dsh-tidy-row-desc\">\n \u4EFB\u610F OpenAI \u517C\u5BB9\u670D\u52A1\u7AEF\u70B9\uFF0C\u5982 <code>https://api.openai.com/v1</code>\u3001{' '}\n <code>https://api.deepseek.com/v1</code>\n </div>\n </div>\n <input\n type=\"text\"\n className=\"dsh-tidy-input\"\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 style={{ width: '260px' }}\n aria-label=\"AI Base URL\"\n />\n </div>\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">\u6A21\u578B</div>\n <div className=\"dsh-tidy-row-desc\">\n \u5982 <code>gpt-4o-mini</code>\u3001<code>deepseek-chat</code>\uFF1B\u7559\u7A7A\u89C6\u4E3A\u672A\u914D\u7F6E\n </div>\n </div>\n <input\n type=\"text\"\n className=\"dsh-tidy-input\"\n placeholder=\"gpt-4o-mini\"\n value={state.model}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) =>\n settingsStore.update({ model: e.target.value })\n }\n style={{ width: '260px' }}\n aria-label=\"AI \u6A21\u578B\"\n />\n </div>\n <div className=\"dsh-tidy-row\">\n <button\n type=\"button\"\n className=\"dsh-tidy-btn\"\n disabled={testing?.running}\n onClick={() => runTest('openai')}\n >\n {testing?.running ? '\u6D4B\u8BD5\u4E2D\u2026' : '\u6D4B\u8BD5 AI \u901A\u9053'}\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 </>\n )}\n </div>\n\n {/* 3. Bing \u901A\u9053 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-title\">\n <span>Bing \u7F51\u9875\u7FFB\u8BD1\uFF08\u514D Key \u515C\u5E95\uFF09</span>\n <Switch\n checked={state.bingEnabled}\n onChange={() => settingsStore.update({ bingEnabled: !state.bingEnabled })}\n label=\"\u542F\u7528 Bing \u7FFB\u8BD1\u901A\u9053\"\n />\n </div>\n <div className=\"dsh-tidy-desc\">\n \u5185\u7F6E\u514D Key \u7FFB\u8BD1\u901A\u9053\uFF0C\u65E0\u9700\u4EFB\u4F55\u914D\u7F6E\u3002AI \u672A\u914D\u7F6E\u6216\u8BF7\u6C42\u5931\u8D25\u65F6\u81EA\u52A8\u515C\u5E95\uFF1BAI \u4E0E Bing \u540C\u65F6\u5173\u95ED\u5219\u4E0D\u7FFB\u8BD1\u3002\n </div>\n </div>\n\n {/* 4. \u884C\u4E3A\u8BF4\u660E */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-row-title\">\u901A\u9053\u884C\u4E3A</div>\n <ul className=\"dsh-tidy-desc dsh-tidy-behavior-list\">\n <li>AI \u5F00\u542F\u4E14\u5DF2\u914D\u7F6E \u2192 AI \u4F18\u5148\u7FFB\u8BD1\uFF0C\u5931\u8D25\u81EA\u52A8\u964D\u7EA7 Bing</li>\n <li>AI \u5F00\u542F\u4F46\u672A\u914D\u7F6E + Bing \u5F00\u542F \u2192 \u7531 Bing \u7FFB\u8BD1</li>\n <li>AI \u5F00\u542F\u4F46\u672A\u914D\u7F6E + Bing \u5173\u95ED \u2192 \u4E0D\u7FFB\u8BD1</li>\n <li>AI \u5173\u95ED + Bing \u5F00\u542F \u2192 \u76F4\u63A5\u4F7F\u7528 Bing</li>\n <li>AI \u5173\u95ED + Bing \u5173\u95ED \u2192 \u4E0D\u7FFB\u8BD1</li>\n </ul>\n </div>\n\n {/* 5. \u5E76\u53D1\u63A7\u5236 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">\u6700\u5927\u7FFB\u8BD1\u5E76\u53D1\u6570</div>\n <div className=\"dsh-tidy-row-desc\">\n \u63A7\u5236\u89C6\u53E3\u6EDA\u52A8\u4E0E\u591A\u5DE5\u5177\u5361\u7247\u65F6\u7684\u6700\u5927\u5E76\u884C\u8BF7\u6C42\u6570\uFF08\u8303\u56F4 1-100\uFF0C\u63A8\u8350 3\uFF09\u3002\n </div>\n </div>\n <input\n type=\"number\"\n className=\"dsh-tidy-input\"\n min={1}\n max={100}\n step={1}\n value={state.concurrency}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const val = parseInt(e.target.value, 10);\n if (!Number.isFinite(val)) return;\n settingsStore.update({ concurrency: Math.min(Math.max(val, 1), 100) });\n }}\n style={{ width: '88px' }}\n aria-label=\"\u6700\u5927\u7FFB\u8BD1\u5E76\u53D1\u6570\"\n />\n </div>\n </div>\n </>\n )}\n </div>\n );\n}\n\n/**\n * Bind the settings store to DSH's native settings/credentials services and\n * join the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page: whichever participant activates\n * first claims the page, everyone registers a card into its child slot.\n * @param ctx - DSH browser client context; services are resolved defensively.\n */\nexport function setupSettingsUi(ctx: any): void {\n if (typeof window === 'undefined') return;\n\n // Ride DSH's own settings surface: the `settingsScope` service mirrors the\n // host document (per-namespace describe) and the `credentials` Remote API\n // writes the API key. No custom config HTTP endpoint since 1.2.\n try {\n // Declared via inject: 'settingsScope' and 'remote.credentials' (plus the\n // 'remote' root). Fall back to optional lookup so a degraded environment\n // degrades to an in-memory store instead of failing loudly.\n const settingsScope = ctx?.settingsScope || (ctx?.get ? ctx.get('settingsScope') : null);\n const remoteCredentials =\n ctx?.remote?.credentials || (ctx?.get ? ctx.get('remote.credentials') : null);\n if (settingsScope && typeof settingsScope.bind === 'function') {\n const scope = settingsScope.bind({ namespace: SETTINGS_NAMESPACE });\n settingsStore.attach(scope, remoteCredentials ?? null);\n }\n } catch (err) {\n console.warn('[dsh-chat-translate] Failed to bind settings scope:', err);\n }\n\n // The locale service is optional, like the services above: the page label and\n // the card's `t` seat fall back to the Chinese copy without it. It is read\n // through `ctx.get` and never as `ctx.locale` \u2014 the kernel guard throws\n // `cannot get property \"locale\" without inject` for any service the plugin did\n // not declare, and locale is deliberately not one of them (an undeclared read\n // here aborted the whole client apply and took the plugin off the page).\n const locale = typeof ctx?.get === 'function' ? ctx.get('locale') : null;\n if (locale && typeof locale.register === 'function' && typeof ctx?.effect === '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: an earlier revision returned `zh.pageNav` for EVERY\n // key, which labelled the shared page's tab with the page name instead of\n // this plugin's own title whenever the locale service was not yet there.\n : (key: ChatTranslateLocaleKey): string => zh[key] ?? key;\n\n try {\n const slots = ctx?.slots || (ctx?.get ? ctx.get('slots') : null);\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\u3001dsh-oil-sticky-prompt\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\u4E09\u5BB6\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 slots.inject('settings.section', () =>\n claimReadingSettingsPage(ctx, () => t('pageNav'), NS)\n );\n\n slots.inject(READING_ITEM_SLOT, () =>\n slots.register(\n {\n name: READING_ITEM_SLOT,\n // id = \u672C\u63D2\u4EF6\u7684 Host \u8BBE\u7F6E\u547D\u540D\u7A7A\u95F4\uFF08settings.section \u65F6\u4EE3\u7684 id \u6CBF\u7528\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 } catch (err) {\n console.warn('[dsh-chat-translate] Failed to inject settings section:', err);\n }\n}\n", "import { describeError } from '../../describe-error.ts';\nimport { chatTranslateObserver } from '../translate/observer.ts';\nimport { testServerChannel } from '../translate/api.ts';\n\nexport interface ClientSettingsState {\n enabled: boolean;\n concurrency: number;\n aiEnabled: boolean;\n bingEnabled: boolean;\n baseUrl: string;\n model: string;\n aiConfigured: boolean;\n}\n\n/** 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 * Minimal structural shapes of the DSH browser services this store rides on:\n * `settingsScope` (from @deepseek-ai/dsh-client-ui-settings) and the\n * `credentials` Remote namespace. Keeping them structural keeps this bundle\n * free of host-service imports and lets tests inject fakes.\n */\nexport interface SettingsScopeLike {\n getSnapshot(): {\n status: string;\n value?: Record<string, unknown>;\n writable: boolean;\n };\n subscribe(listener: () => void): () => void;\n set(field: string, value: unknown): Promise<unknown>;\n unset(field: string): 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 concurrency: 3,\n aiEnabled: true,\n bingEnabled: true,\n baseUrl: '',\n model: '',\n aiConfigured: false,\n};\n\n/**\n * Client settings store backed by the DSH settings namespace.\n *\n * Since 1.2 there is no localStorage overlay and no custom config HTTP\n * endpoint: the store derives from the bound `settingsScope` (which mirrors\n * the host document and folds every write answer back), and writes go\n * through `scope.set` \u2014 serialized, revision-checked, persisted by DSH.\n * Without a bound scope (e.g. non-loopback pages, unit tests) it degrades to\n * an in-memory store with the same semantics DSH itself uses for memory\n * persistence.\n */\nclass SettingsStore {\n private state: ClientSettingsState = { ...DEFAULT_STATE };\n private listeners = new Set<() => void>();\n private scope: SettingsScopeLike | null = null;\n private credentials: CredentialsRemoteLike | null = null;\n private unsubscribeScope: (() => 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(scope: SettingsScopeLike | null, credentials: CredentialsRemoteLike | null): void {\n if (this.unsubscribeScope) {\n this.unsubscribeScope();\n this.unsubscribeScope = null;\n }\n this.scope = scope;\n this.credentials = credentials;\n if (scope) {\n this.unsubscribeScope = scope.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 namespace value into client state and notify. */\n private derive(): void {\n if (!this.scope) return;\n const snap = this.scope.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 c = value.concurrency;\n if (typeof c === 'number' && Number.isFinite(c)) {\n next.concurrency = Math.min(Math.max(Math.round(c), 1), 100);\n }\n if (typeof value.aiEnabled === 'boolean') next.aiEnabled = value.aiEnabled;\n if (typeof value.bingEnabled === 'boolean') next.bingEnabled = value.bingEnabled;\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 const enabledChanged = next.enabled !== this.state.enabled;\n this.state = next;\n if (enabledChanged) {\n try {\n chatTranslateObserver.setEnabled(this.state.enabled);\n } catch {}\n }\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 * settings scope. 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 scope\n * reload 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 sanitizedConcurrency = this.state.concurrency;\n if (typeof partial.concurrency === 'number' && Number.isFinite(partial.concurrency)) {\n sanitizedConcurrency = Math.min(Math.max(Math.round(partial.concurrency), 1), 100);\n }\n const next: ClientSettingsState = {\n ...this.state,\n ...partial,\n concurrency: sanitizedConcurrency,\n };\n this.applyState(next);\n\n if (this.scope) {\n const fields = ['enabled', 'concurrency', 'aiEnabled', 'bingEnabled', '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.scope) return;\n const fields = [...this.pendingFields];\n this.pendingFields.clear();\n const writes: Promise<unknown>[] = [];\n for (const field of fields) {\n writes.push(this.scope.set(field, this.state[field as keyof ClientSettingsState]));\n }\n await Promise.all(writes).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.unsubscribeScope) {\n this.unsubscribeScope();\n this.unsubscribeScope = null;\n }\n this.scope = 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, inherit);\n font-family: inherit;\n}\n\n.dsh-tidy-card {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.2));\n border-radius: 12px;\n padding: 16px 18px;\n background: var(--dsw-alias-bg-card, rgba(128, 128, 128, 0.05));\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.dsh-tidy-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, inherit);\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.dsh-tidy-desc {\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary, rgba(128, 128, 128, 0.8));\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: 1px solid var(--dsw-alias-border-l3, rgba(128, 128, 128, 0.1));\n}\n\n.dsh-tidy-row-info {\n flex: 1;\n min-width: 0;\n}\n\n.dsh-tidy-row-title {\n font-size: 13px;\n font-weight: 500;\n}\n\n.dsh-tidy-row-desc {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, rgba(128, 128, 128, 0.7));\n margin-top: 2px;\n line-height: 1.4;\n}\n\n/* Switch */\n.dsh-tidy-switch {\n position: relative;\n width: 38px;\n height: 22px;\n flex: none;\n cursor: pointer;\n border-radius: 999px;\n border: none;\n background: rgba(128, 128, 128, 0.3);\n transition: background 0.15s ease;\n padding: 0;\n outline: none;\n}\n\n.dsh-tidy-switch[aria-checked=\"true\"] {\n background: #3b82f6;\n}\n\n.dsh-tidy-switch::after {\n content: \"\";\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #ffffff;\n transition: transform 0.15s ease;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);\n}\n\n.dsh-tidy-switch[aria-checked=\"true\"]::after {\n transform: translateX(16px);\n}\n\n/* Inputs */\n.dsh-tidy-input-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.dsh-tidy-label {\n font-size: 12px;\n font-weight: 500;\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.dsh-tidy-input-row {\n display: flex;\n gap: 8px;\n}\n\n.dsh-tidy-input {\n flex: 1;\n min-width: 0;\n padding: 8px 12px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n background: var(--dsw-alias-bg-input, rgba(0, 0, 0, 0.05));\n color: inherit;\n font-size: 12px;\n outline: none;\n transition: border-color 0.15s;\n}\n\n.dsh-tidy-input:focus {\n border-color: #3b82f6;\n}\n\n.dsh-tidy-btn {\n padding: 6px 14px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.3));\n background: transparent;\n color: inherit;\n font-size: 12px;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.dsh-tidy-btn:hover {\n background: rgba(128, 128, 128, 0.1);\n border-color: rgba(128, 128, 128, 0.5);\n}\n\n.dsh-tidy-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n.dsh-tidy-badge {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 500;\n}\n\n.dsh-tidy-badge-ok {\n background: rgba(34, 197, 94, 0.15);\n color: #22c55e;\n}\n\n.dsh-tidy-badge-none {\n background: rgba(156, 163, 175, 0.15);\n color: #9ca3af;\n}\n\n.dsh-tidy-badge-warn {\n background: rgba(245, 158, 11, 0.15);\n color: #f59e0b;\n}\n\n.dsh-tidy-test-result {\n font-size: 12px;\n margin-left: 10px;\n}\n\n.dsh-tidy-test-result.ok {\n color: #22c55e;\n}\n\n.dsh-tidy-test-result.fail {\n color: #ef4444;\n}\n\n.dsh-tidy-behavior-list {\n padding-left: 18px;\n margin: 4px 0 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n/* Priority list */\n.dsh-tidy-priority-list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.dsh-tidy-priority-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n border-radius: 8px;\n background: rgba(128, 128, 128, 0.06);\n border: 1px solid var(--dsw-alias-border-l3, rgba(128, 128, 128, 0.12));\n}\n\n.dsh-tidy-priority-name {\n font-size: 12px;\n font-weight: 500;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.dsh-tidy-order-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #3b82f6;\n color: #ffffff;\n font-size: 11px;\n font-weight: 600;\n}\n\n.dsh-tidy-btn-group {\n display: flex;\n gap: 4px;\n}\n\n.dsh-tidy-icon-btn {\n width: 26px;\n height: 26px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n border: 1px solid rgba(128, 128, 128, 0.2);\n background: transparent;\n color: inherit;\n cursor: pointer;\n font-size: 12px;\n}\n\n.dsh-tidy-icon-btn:hover:not(:disabled) {\n background: rgba(128, 128, 128, 0.15);\n}\n\n.dsh-tidy-icon-btn:disabled {\n opacity: 0.3;\n cursor: default;\n}\n\n/* Slider / Select */\n.dsh-tidy-select {\n padding: 6px 10px;\n border-radius: 8px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.3));\n background: var(--dsw-alias-bg-input, rgba(0, 0, 0, 0.05));\n color: inherit;\n font-size: 12px;\n outline: none;\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-oil-sticky-prompt`, `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\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/** A registration label is a plain string or a thunk re-read per projection. */\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: 'inherit',\n fontSize: '13px',\n lineHeight: '20px',\n color: 'var(--dsw-alias-label-tertiary, inherit)',\n} as const\n\nconst TAB_ACTIVE_STYLE = {\n ...TAB_STYLE,\n color: 'var(--dsw-alias-label-primary, inherit)',\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, currentColor)',\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/** Locale keys this plugin renders. */\nexport type ChatTranslateLocaleKey = 'pageNav' | 'title';\n\n/** English copy. */\nexport const en: Record<ChatTranslateLocaleKey, string> = {\n pageNav: 'Reading',\n title: 'Chat translate',\n};\n\n/** Chinese copy. */\nexport const zh: Record<ChatTranslateLocaleKey, string> = {\n pageNav: '\u9605\u8BFB\u4F53\u9A8C',\n title: '\u804A\u5929\u7FFB\u8BD1',\n};\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'settings.chatTranslate': ChatTranslateLocaleKey;\n }\n}\n"],
5
- "mappings": "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",
6
- "names": ["cached", "import_react"]
3
+ "sources": ["../src/client/index.ts", "../src/server/pipeline/mask-tokens.ts", "../src/server/pipeline/masking.ts", "../src/client/translate/client-cache.ts", "../src/client/translate/mount.ts", "../src/client/translate/hold.ts", "../src/describe-error.ts", "../src/client/translate/api.ts", "../src/client/translate/viewport-observer.ts", "../src/client/translate/lazy.ts", "../src/server/pipeline/think.ts", "../src/client/translate/think-button.ts", "../src/client/translate/think.ts", "../src/client/translate/observer.ts", "../src/client/settings/ui.tsx", "../src/client/settings/store.ts", "../src/client/settings/styles.ts", "../src/client/reading-settings-page.tsx", "../src/client/locales.ts"],
4
+ "sourcesContent": ["import { chatTranslateObserver } from './translate/observer.ts';\nimport { setupSettingsUi } from './settings/ui.tsx';\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 shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page (page claim\n * plus the card registration), settingsScope for the per-namespace settings\n * mirror, the remote + remote.credentials pair for the credentials Remote\n * namespace, and locale for the card's own dictionary. The runtime withholds any\n * service not declared here; `locale` is declared rather than sampled because\n * `ctx.get('locale')` at apply time can beat the locale plugin's `provide`,\n * which silently left the card's dictionary unregistered.\n */\nexport const inject = ['slots', 'settingsScope', 'remote', 'remote.credentials', 'locale'];\n\ninterface ClientContext {\n effect(factory: () => void | (() => void), label: string): void;\n get?(serviceName: string): any;\n slots?: any;\n settingsScope?: any;\n locale?: any;\n remote?: { credentials?: any };\n}\n\n/**\n * Mount the tool-call / think-chain translation observer and the settings UI\n * card inside the shared reading-settings page.\n * @param ctx - DSH browser client context.\n */\nexport function apply(ctx: ClientContext): void {\n // 1. Mount tool title / think summary translation observer (current session only)\n ctx.effect(() => chatTranslateObserver.start(document), 'dsh-chat-translate: title translate observer');\n\n // 2. 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\nexport { chatTranslateObserver } from './translate/observer.ts';\nexport { setupSettingsUi } from './settings/ui.tsx';\nexport { NonDestructiveTranslationMount } from './translate/mount.ts';\nexport { StreamDebounceViewportObserver } from './translate/viewport-observer.ts';\nexport { clientCache } from './translate/client-cache.ts';\nexport { rowHold } from './translate/hold.ts';\nexport { lazyQueue } from './translate/lazy.ts';\nexport { settingsStore } from './settings/store.ts';\n", "/**\n * Mask placeholder tokens \u2014 single source of truth for the wire format, the\n * matcher and the leftover detector.\n *\n * Wire format: `\u27E6xkbdt3\u27E7` \u2014 U+27E6/U+27E7 (mathematical white square brackets)\n * around `<4-letter random id><index>`.\n *\n * Design constraints, each one a measured failure of an earlier format against\n * real MT engines:\n *\n * - No letters spelling a pronounceable word and no underscores. The previous\n * `__DSHMASKxkbdt_3__` format named a token that small models abbreviated\n * back to its recognizable core: the UI showed bare `DSH` runs instead of\n * the protected fragments.\n * - Random letters keep two mask passes in the same session from colliding and\n * make the token unambiguous in the translated text.\n * - `matchMaskToken` accepts exactly what this module emits (plus the same\n * token with a dropped closing bracket, an engine rewrite seen in practice).\n * It deliberately does NOT accept anything else: a translation that damaged\n * a token beyond recognition is discarded rather than repaired, because\n * repairing it is what put mixed or duplicated text on screen.\n */\n\n/** Random part of the token. Letters only: never confuses the index scan. */\nconst RANDOM_LETTERS = 'abcdefghijklmnopqrstuvwxyz';\n\nexport function randomTokenId(): string {\n let out = '';\n for (let i = 0; i < 4; i++) {\n out += RANDOM_LETTERS[Math.floor(Math.random() * RANDOM_LETTERS.length)];\n }\n return out;\n}\n\nexport interface MaskTokenFormat {\n /** Unique per `mask()` call; embedded in every token of that call. */\n id: string;\n /** Build the placeholder written into the text sent to the translator. */\n token: (index: number) => string;\n}\n\nexport function createMaskTokenFormat(): MaskTokenFormat {\n const id = randomTokenId();\n return {\n id,\n token: (index: number) => `\u27E6${id}${index}\u27E7`,\n };\n}\n\n/**\n * Scanner for complete tokens of this format. A token whose closing bracket the\n * engine dropped is still resolved (see `matchMaskToken`), but it is not\n * recognized as a complete token here: the space `mask()` inserts sits outside\n * that token, and matching a truncated one would strip a space of the\n * translation's own.\n */\nexport const MASK_TOKEN_PATTERN_SOURCE = '\u27E6([a-z]{4})(\\\\d+)\u27E7';\n\n/**\n * The opening bracket, the id and the index of a token, without its closing\n * bracket. Used to resolve a token the engine truncated.\n */\nexport const MASK_TOKEN_PREFIX_PATTERN_SOURCE = '\u27E6\\\\s*([a-z]{4})(\\\\d+)';\n\n/**\n * Retired `__DSH_MASK_<index>__` / `__DSHMASKx<id>_<index>__` tokens. Nothing\n * emits them anymore: `mask()` rewrites any that appear in the source into the\n * current format, and the detectors below evict cache entries written by\n * releases that emitted them.\n */\nconst LEGACY_MASK_PATTERN_SOURCE =\n '_{1,2}DSH\\\\s*_*\\\\s*MASKx?\\\\s*(?:_?\\\\s*\\\\d+|_*([a-z]{2,8})\\\\s*_+\\\\s*(\\\\d+))_{0,2}';\n\n/** Regex source for one retired-format token, for text that still contains one. */\nexport const LEGACY_MASK_TOKEN_PATTERN_SOURCE = LEGACY_MASK_PATTERN_SOURCE;\n\nexport interface MaskTokenMatch {\n /** Index into the mask list of the `mask()` call that produced the token. */\n index: number;\n /** Length of the matched token in characters. */\n length: number;\n /** True when the closing bracket was missing and only the prefix matched. */\n truncated: boolean;\n}\n\n/**\n * Match exactly one mask token of this format at `start` in `text`.\n *\n * The id must match the id of the masking pass that owns the token: every mask\n * pass carries its own random id, so a token carrying another id belongs to\n * another call and must not be resolved here.\n */\nexport function matchMaskToken(text: string, start: number, id: string): MaskTokenMatch | null {\n const complete = new RegExp(`^(?:${MASK_TOKEN_PATTERN_SOURCE})`, 'iu').exec(text.slice(start));\n // The closing bracket is optional here so a token the engine truncated is\n // still resolved; the id and index inside must survive intact.\n const truncated = complete\n ? null\n : new RegExp(`^(?:${MASK_TOKEN_PREFIX_PATTERN_SOURCE})`, 'iu').exec(text.slice(start));\n\n const anchored = complete ?? truncated;\n if (!anchored) return null;\n if (anchored[1].toLowerCase() !== id.toLowerCase()) return null;\n\n const index = Number.parseInt(anchored[2], 10);\n if (Number.isNaN(index)) return null;\n\n return { index, length: anchored[0].length, truncated: complete === null };\n}\n\n/** Every token of this format in `text`, left to right. */\nexport function findMaskTokens(text: string): Array<{ match: MaskTokenMatch; raw: string }> {\n const found: Array<{ match: MaskTokenMatch; raw: string }> = [];\n const pattern = new RegExp(MASK_TOKEN_PATTERN_SOURCE, 'giu');\n let candidate = pattern.exec(text);\n while (candidate) {\n const id = candidate[1].toLowerCase();\n const index = Number.parseInt(candidate[2], 10);\n if (!Number.isNaN(index)) {\n found.push({\n match: { index, length: candidate[0].length, truncated: false },\n raw: candidate[0],\n });\n }\n if (pattern.lastIndex === candidate.index) pattern.lastIndex++;\n candidate = pattern.exec(text);\n }\n return found;\n}\n\n/** True when `text` still carries a token of the current format. */\nexport function hasMaskResidue(text: string): boolean {\n if (!text) return false;\n return new RegExp(MASK_TOKEN_PATTERN_SOURCE, 'iu').test(text);\n}\n\n/** True when `text` carries a token of a retired format. */\nexport function hasLegacyMaskResidue(text: string): boolean {\n if (!text) return false;\n return new RegExp(LEGACY_MASK_PATTERN_SOURCE, 'iu').test(text);\n}\n\n/**\n * Residue test for a value that must never be shown or cached: either a\n * current-format token or any retired-format token.\n */\nexport function hasAnyMaskResidue(text: string): boolean {\n return hasMaskResidue(text) || hasLegacyMaskResidue(text);\n}\n\n/**\n * Retired-format tokens inside source text, so `mask()` can protect them with a\n * current-format token of their own. A source that talks about the old\n * placeholder format must survive translation, and the unmask step rejects any\n * retired-format token it sees.\n */\nexport function findLegacyMaskTokens(text: string): string[] {\n if (!text) return [];\n return text.match(new RegExp(LEGACY_MASK_TOKEN_PATTERN_SOURCE, 'giu')) ?? [];\n}\n", "import {\n createMaskTokenFormat,\n hasAnyMaskResidue,\n hasMaskResidue,\n LEGACY_MASK_TOKEN_PATTERN_SOURCE,\n matchMaskToken,\n MASK_TOKEN_PATTERN_SOURCE,\n MASK_TOKEN_PREFIX_PATTERN_SOURCE,\n} from './mask-tokens.ts';\n\nexport interface MaskResult {\n maskedText: string;\n /**\n * Restore every protected fragment. Throws `MaskRestoreError` when the\n * translated text did not carry the token sequence back intact; the caller\n * must discard that translation instead of showing a repaired guess.\n */\n unmask: (translatedText: string) => string;\n /**\n * Retired-format tokens the source itself contained. They come back on\n * purpose and are content, so the caller's leak check must not flag them.\n */\n legacyFragments: string[];\n}\n\n/** Which spaces `mask()` inserted directly before and after a token. */\nexport interface InsertedSpacing {\n leading: boolean;\n trailing: boolean;\n}\n\n/** Bound the unmask scanner so a poisoned translation cannot spin forever. */\nconst MAX_UNMASK_PASSES = 10_000;\n\n/** Chinese, Japanese and Korean text is written without spaces between words. */\nconst ASCII_WORD = /[A-Za-z0-9_]/;\n\n/** A single leading or trailing space: the one this pipeline inserted. */\nconst INSERTED_SPACE = /^ | $/;\n\nexport class MaskRestoreError extends Error {\n readonly expectedCount: number;\n readonly foundCount: number;\n\n constructor(message: string, expectedCount: number, foundCount: number) {\n super(message);\n this.name = 'MaskRestoreError';\n this.expectedCount = expectedCount;\n this.foundCount = foundCount;\n }\n}\n\nexport class ContentMaskingPipeline {\n mask(text: string): MaskResult {\n if (!text || typeof text !== 'string') {\n return {\n maskedText: text,\n unmask: (t: string) => t,\n legacyFragments: [],\n };\n }\n\n const masks: string[] = [];\n /** Indices whose fragment already was a retired-format token. */\n const legacyIndexes = new Set<number>();\n /** Which spaces `placeToken` inserted around a fragment. */\n const inserted: InsertedSpacing[] = [];\n const format = createMaskTokenFormat();\n\n /**\n * Record one protected fragment and return the text that replaces it.\n *\n * A match that already carries a token from an earlier rule is returned\n * unchanged: that inner token already protects the fragment, and recording\n * the surrounding text again would put one fragment under two indices.\n * `before` and `after` are the characters on either side of the match in the\n * never-masked text. A token touching a Latin word gets one space on that\n * side: the engine then sees a standalone word and keeps it separate, while\n * a token glued to source text (`Fixsrc/\u2026`) came back merged into its\n * neighbour. The inserted spaces are recorded so `unmask` removes exactly\n * them and never a space the translation produced.\n */\n const addFragment = (\n match: string,\n before: string | undefined,\n after: string | undefined,\n onPlace?: (index: number) => void\n ): string => {\n if (hasMaskResidue(match)) return match;\n const index = masks.length;\n masks.push(match);\n const leading = before !== undefined && ASCII_WORD.test(before);\n const trailing = after !== undefined && ASCII_WORD.test(after);\n inserted.push({ leading, trailing });\n onPlace?.(index);\n return `${leading ? ' ' : ''}${format.token(index)}${trailing ? ' ' : ''}`;\n };\n\n const wrapRule = (input: string, pattern: RegExp, onPlace?: (index: number) => void): string =>\n input.replace(pattern, (...args: unknown[]) => {\n const match = args[0] as string;\n const offset = args[args.length - 2] as number;\n const whole = args[args.length - 1] as string;\n return addFragment(match, whole[offset - 1], whole[offset + match.length], onPlace);\n });\n\n // 0. Retired-format placeholders in the source are protected first: the\n // unmask step rejects retired tokens, so leaving one in the text sent to\n // the engine would discard an otherwise usable translation of a sentence\n // that merely documents the old format. Runs before the code rules, so a\n // legacy token inside inline code survives as one fragment instead of\n // being swallowed by the surrounding code match.\n let processed = wrapRule(\n text,\n new RegExp(LEGACY_MASK_TOKEN_PATTERN_SOURCE, 'giu'),\n (index) => legacyIndexes.add(index)\n );\n\n // 1. Multi-line code blocks (```...``` or ~~~...~~~)\n processed = wrapRule(processed, /(?:```|~~~)[\\s\\S]*?(?:```|~~~)/g);\n\n // 2. Inline code (`...`)\n processed = wrapRule(processed, /`[^`\\n]+`/g);\n\n // 3. URLs\n processed = wrapRule(processed, /https?:\\/\\/[^\\s)\\];,;\"'<>]+/g);\n\n // 4. File paths and filenames with known extensions.\n // Every alternative is anchored on a left boundary (`.`/`/` accepted\n // only when preceded by a word char or start) so a match can never start\n // mid-path: `src/server` inside `src/server/dispatcher.ts` used to split\n // the path into `src\u27E6\u20260\u27E7`, masking the slash away.\n processed = wrapRule(\n processed,\n /(?:\\/[\\w.\\-\\\\\\/]+|(?<=[\\w.\\-])\\.\\.?[\\\\\\/][\\w.\\-\\\\\\/]+|(?<=^|[\\s([{\"'`])[a-zA-Z]:[\\\\\\/][\\w.\\-\\\\\\/]+|(?<![^\\s([{\"'`])\\b(?:[\\w.\\-]+\\/)+[\\w.\\-]+\\.[a-zA-Z0-9]+\\b|(?<![^\\s([{\"'`])\\b[\\w.\\-]+\\.(?:ts|tsx|js|jsx|json|ya?ml|md|py|go|rs|c|cpp|h|hpp|css|scss|html|sh|bash|mjs|cjs|toml|lock|log|env|svg|png|jpe?g|gif|tar|gz|zip|xml|sql)\\b)/g\n );\n\n // 5. CLI flags / options (--flag, --flag=value, -f)\n processed = wrapRule(\n processed,\n /(?<=^|[\\s(\\[{\"'])((?:--[a-zA-Z0-9_\\-]+(?:=[^\\s\"'<>]+)?)|(?:-[a-zA-Z0-9]+))(?=[\\s)\\]}\",:;!?]|$)/g\n );\n\n const unmask = (translatedText: string): string => {\n if (!translatedText) return translatedText;\n if (masks.length === 0) {\n if (hasAnyMaskResidue(translatedText)) {\n throw new MaskRestoreError(\n 'translation carries a mask token while the source had none',\n 0,\n 0\n );\n }\n return translatedText;\n }\n return replaceMaskTokens(translatedText, format.id, masks, inserted, legacyIndexes);\n };\n\n return {\n maskedText: processed,\n unmask,\n legacyFragments: [...legacyIndexes].map((index) => masks[index]).filter((f) => f !== undefined),\n };\n }\n}\n\n/**\n * Restore every token of `id` in `text` from `masks`.\n *\n * Each fragment of the pass is resolved exactly once, no matter how the engine\n * reordered the sentence: the index embedded in the token identifies the\n * fragment, and the random id identifies the masking pass that owns it. The\n * spaces recorded in `inserted` are removed together with their token, so the\n * spacing the translation produced around the fragment survives untouched.\n *\n * @throws MaskRestoreError when the text does not carry every token exactly\n * once, or still shows a current-format token or a retired token that the\n * source did not already contain \u2014 the caller discards such a translation\n * instead of rendering a repaired guess.\n */\nexport function replaceMaskTokens(\n text: string,\n id: string,\n masks: string[],\n inserted?: InsertedSpacing[],\n legacyIndexes: ReadonlySet<number> = new Set()\n): string {\n // Two scanners, one pass: the complete-token scanner drives the loop, and the\n // prefix scanner catches a complete token whose closing bracket the engine\n // dropped (the complete-token scanner steps over such a prefix entirely).\n const complete = new RegExp(MASK_TOKEN_PATTERN_SOURCE, 'giu');\n const prefix = new RegExp(MASK_TOKEN_PREFIX_PATTERN_SOURCE, 'giu');\n const seen = new Set<number>();\n let out = '';\n let cursor = 0;\n let passes = 0;\n let whole = complete.exec(text);\n let prefixCandidate = prefix.exec(text);\n\n const nextStart = (wholeMatch: RegExpExecArray | null, prefixMatch: RegExpExecArray | null): number =>\n Math.min(\n wholeMatch ? wholeMatch.index : Number.POSITIVE_INFINITY,\n prefixMatch ? prefixMatch.index : Number.POSITIVE_INFINITY\n );\n\n let start = nextStart(whole, prefixCandidate);\n while (start !== Number.POSITIVE_INFINITY && passes++ < MAX_UNMASK_PASSES) {\n // A complete token and a truncated one share a start position only when the\n // token is complete; a truncated one found past it is the shorter match.\n const isPrefix = prefixCandidate !== null && prefixCandidate.index === start && whole?.index !== start;\n const raw = isPrefix ? prefixCandidate![0] : whole![0];\n const match = matchMaskToken(text, start, id);\n if (!match) {\n // A token of another pass or a damaged one: kept verbatim. The\n // completeness check below turns any such leftover into a rejection.\n out += text.slice(cursor, start) + raw;\n cursor = start + raw.length;\n } else {\n if (match.index < 0 || match.index >= masks.length || seen.has(match.index)) {\n throw new MaskRestoreError(\n `translation does not reproduce every protected fragment (bad index ${match.index})`,\n masks.length,\n seen.size\n );\n }\n seen.add(match.index);\n const spacing = isPrefix ? undefined : inserted?.[match.index];\n let before = text.slice(cursor, start);\n // The trailing space the previous fragment brought with it.\n if (spacing?.leading) before = before.replace(INSERTED_SPACE, '');\n if (spacing?.trailing) out = out.replace(INSERTED_SPACE, '');\n out += before + masks[match.index];\n cursor = start + raw.length;\n }\n\n // Skip past every candidate the resolved token already covers.\n while (whole && whole.index < cursor) whole = complete.exec(text);\n while (prefixCandidate && prefixCandidate.index < cursor) prefixCandidate = prefix.exec(text);\n start = nextStart(whole, prefixCandidate);\n }\n\n out += text.slice(cursor);\n\n // Retired-format tokens restored from documented source text are content, not\n // residue; every other occurrence still means a token reached the screen.\n const documented = new Set<string>();\n for (const index of legacyIndexes) {\n const fragment = masks[index];\n if (fragment !== undefined) documented.add(fragment.toLowerCase());\n }\n const residue = (out.match(new RegExp(LEGACY_MASK_TOKEN_PATTERN_SOURCE, 'giu')) ?? []).filter(\n (fragment) => !documented.has(fragment.toLowerCase())\n );\n if (seen.size !== masks.length || hasMaskResidue(out) || residue.length > 0) {\n throw new MaskRestoreError(\n `translation does not reproduce every protected fragment (expected ${masks.length}, found ${seen.size})`,\n masks.length,\n seen.size\n );\n }\n\n return out;\n}\n\n/**\n * True when a translated string still shows a mask token to the user.\n * Callers use it to discard a poisoned translation (and to evict poisoned\n * cache entries written by earlier releases).\n */\nexport function isMaskLeak(translatedText: string): boolean {\n return hasAnyMaskResidue(translatedText);\n}\n\nexport { hasMaskResidue };\n", "import { isMaskLeak } from '../../server/pipeline/masking.ts';\n\n/** \u5DE5\u5177\u6807\u9898\u8BD1\u6587\u7684\u7F13\u5B58\u952E\u4E0E\u5BB9\u91CF\u3002 */\nconst TITLE_CACHE_KEY = 'dsh-chat-translate:cache';\nconst TITLE_MAX_ENTRIES = 500;\n/** \u601D\u8003\u6B63\u6587\u8BD1\u6587\u7684\u7F13\u5B58\u952E\u4E0E\u5BB9\u91CF\uFF1A\u4E0E\u5DE5\u5177\u6807\u9898\u5206\u6C60\uFF0C\u4E92\u4E0D\u6324\u5360\u3002 */\nconst THINK_CACHE_KEY = 'dsh-chat-translate:think-cache';\nconst THINK_MAX_ENTRIES = 300;\n/** Entries older than this are treated as expired. */\nconst TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days\n\ninterface CacheEntry {\n t: number; // epoch ms; 0 = legacy entry without timestamp\n v: string;\n}\n\nexport class ClientCache {\n // Map preserves insertion order in JS, enabling true LRU semantics.\n private memCache = new Map<string, CacheEntry>();\n private dirty = false;\n private saveTimer: number | null = null;\n private storageKey: string;\n private maxEntries: number;\n\n constructor(storageKey: string = TITLE_CACHE_KEY, maxEntries: number = TITLE_MAX_ENTRIES) {\n this.storageKey = storageKey;\n this.maxEntries = maxEntries;\n this.load();\n }\n\n private load(): void {\n if (typeof localStorage === 'undefined') return;\n try {\n const raw = localStorage.getItem(this.storageKey);\n if (raw) {\n const obj = JSON.parse(raw);\n if (obj && typeof obj === 'object') {\n for (const [k, entry] of Object.entries(obj)) {\n if (typeof entry === 'string') {\n // Legacy entry from an older release.\n this.memCache.set(k, { t: 0, v: entry });\n } else if (entry && typeof entry === 'object' && typeof (entry as CacheEntry).v === 'string') {\n this.memCache.set(k, entry as CacheEntry);\n }\n }\n }\n }\n } catch {\n // Ignore parse failure\n }\n }\n\n get(text: string): string | undefined {\n const key = text.trim().toLowerCase();\n const entry = this.memCache.get(key);\n if (entry === undefined) return undefined;\n // A translation still showing a mask placeholder leaked from an older\n // release \u2014 evict it so the text is requested again instead of rendered.\n if (typeof entry.v === 'string' && isMaskLeak(entry.v)) {\n this.memCache.delete(key);\n this.dirty = true;\n this.scheduleSave();\n return undefined;\n }\n // Drop dirty fallback entries where value equals key\n if (entry.v && entry.v.trim().toLowerCase() === key) {\n this.memCache.delete(key);\n this.dirty = true;\n this.scheduleSave();\n return undefined;\n }\n if (entry.t > 0 && Date.now() - entry.t > TTL_MS) {\n this.memCache.delete(key);\n this.dirty = true;\n this.scheduleSave();\n return undefined;\n }\n // True LRU: Re-insert to move to the end (most recently used)\n this.memCache.delete(key);\n this.memCache.set(key, entry);\n return entry.v;\n }\n\n set(text: string, translated: string): void {\n const key = text.trim().toLowerCase();\n if (this.memCache.has(key)) {\n this.memCache.delete(key);\n } else if (this.memCache.size >= this.maxEntries) {\n // Evict least recently used (first item in Map)\n const oldestKey = this.memCache.keys().next().value;\n if (oldestKey !== undefined) {\n this.memCache.delete(oldestKey);\n }\n }\n this.memCache.set(key, { t: Date.now(), v: translated });\n this.dirty = true;\n this.scheduleSave();\n }\n\n private scheduleSave(): void {\n if (this.saveTimer !== null || typeof window === 'undefined') return;\n this.saveTimer = window.setTimeout(() => {\n this.saveTimer = null;\n this.flushSync();\n }, 2000);\n }\n\n flushSync(): void {\n if (!this.dirty || typeof localStorage === 'undefined') return;\n this.dirty = false;\n try {\n const obj: Record<string, CacheEntry> = {};\n for (const [k, v] of this.memCache.entries()) {\n obj[k] = v;\n }\n localStorage.setItem(this.storageKey, JSON.stringify(obj));\n } catch {\n // Ignore quota error\n }\n }\n\n clear(): void {\n this.memCache.clear();\n this.dirty = true;\n this.flushSync();\n }\n\n size(): number {\n return this.memCache.size;\n }\n}\n\n/** \u5DE5\u5177\u6807\u9898\u8BD1\u6587\u7F13\u5B58\u3002 */\nexport const clientCache = new ClientCache();\n\n/** \u601D\u8003\u6B63\u6587\u8BD1\u6587\u7F13\u5B58\uFF1A\u72EC\u7ACB\u5B58\u50A8\u952E\u4E0E\u5BB9\u91CF\uFF0C\u4E0D\u6324\u5360\u5DE5\u5177\u6807\u9898\u7684\u7F13\u5B58\u3002 */\nexport const thinkClientCache = new ClientCache(THINK_CACHE_KEY, THINK_MAX_ENTRIES);", "/**\n * Non-destructive translation DOM mount mechanism.\n *\n * Preserves the original React Fiber tree, event listeners, and nested DOM nodes\n * by wrapping them in a hidden container (<span class=\"dsh-tidy-original-hidden\">)\n * rather than destroying them with `element.textContent = translated`.\n *\n * Displays the translated text in a companion container (<span class=\"dsh-tidy-translated-block\">).\n * Clicking the translation toggles between original and translated text in-place.\n */\n\nexport const CLASS_ORIGINAL_HIDDEN = 'dsh-tidy-original-hidden';\nexport const CLASS_ORIGINAL_SHOWN = 'dsh-tidy-original-shown';\nexport const CLASS_TRANSLATED_BLOCK = 'dsh-tidy-translated-block';\n/** \u6DF7\u5408\u5757\u91CC\u7684\u884C\u5185\u7247\u6BB5\u5BB9\u5668\uFF08\u6211\u4EEC\u81EA\u5EFA\u3001\u81EA\u62C6\uFF09\u3002 */\nexport const CLASS_RUN = 'dsh-tidy-run';\nconst RUN_ATTRIBUTE = 'data-tidy-run';\n\nexport interface MountOptions {\n originalText?: string;\n interactive?: boolean;\n}\n\nexport class NonDestructiveTranslationMount {\n /**\n * Mounts a translated string onto the target element non-destructively.\n */\n static mount(\n element: HTMLElement,\n translated: string,\n options: MountOptions = {}\n ): void {\n if (!element || !element.ownerDocument) return;\n const doc = element.ownerDocument;\n\n // Check if already mounted\n let transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n let origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n\n if (transWrapper && origWrapper) {\n // Update existing translation\n transWrapper.textContent = translated;\n element.dataset.tidyTranslated = 'true';\n if (options.originalText) element.dataset.original = options.originalText;\n return;\n }\n\n const originalText = options.originalText ?? this.extractVisibleText(element);\n\n // Create wrapper for original children\n origWrapper = doc.createElement('span');\n origWrapper.className = CLASS_ORIGINAL_HIDDEN;\n origWrapper.style.display = 'none';\n\n // Move all existing child nodes into origWrapper\n while (element.firstChild) {\n origWrapper.appendChild(element.firstChild);\n }\n\n // Create translation block\n transWrapper = doc.createElement('span');\n transWrapper.className = CLASS_TRANSLATED_BLOCK;\n transWrapper.textContent = translated;\n\n // Interactive bilingual toggle\n const interactive = options.interactive !== false;\n if (interactive) {\n\n const showOriginal = (e: MouseEvent): void => {\n e.stopPropagation();\n if (!origWrapper || !transWrapper) return;\n origWrapper.style.display = 'inline';\n origWrapper.className = CLASS_ORIGINAL_SHOWN;\n transWrapper.style.display = 'none';\n };\n\n const showTranslated = (e: MouseEvent): void => {\n e.stopPropagation();\n if (!origWrapper || !transWrapper) return;\n origWrapper.style.display = 'none';\n origWrapper.className = CLASS_ORIGINAL_HIDDEN;\n transWrapper.style.display = 'inline';\n };\n\n transWrapper.addEventListener('click', showOriginal);\n origWrapper.addEventListener('click', showTranslated);\n }\n\n // Append both to target element\n element.appendChild(transWrapper);\n element.appendChild(origWrapper);\n\n element.dataset.tidyTranslated = 'true';\n element.dataset.original = originalText;\n }\n\n /**\n * \u628A\u4E00\u6BB5\u8FDE\u7EED\u7684\u884C\u5185\u8282\u70B9\u5305\u8FDB\u81EA\u5EFA\u5BB9\u5668\u540E\u6302\u8F7D\u8BD1\u6587\u3002\n *\n * \u6DF7\u5408\u5757\uFF08\u5217\u8868\u9879\u6587\u5B57 + \u5B50\u5217\u8868 / \u4EE3\u7801\u5757\uFF09\u91CC\u7684\u884C\u5185\u7247\u6BB5\u6CA1\u6709\u81EA\u5DF1\u7684\u5143\u7D20\uFF0C\n * \u53EA\u80FD\u5148\u5305\u4E00\u5C42\u518D\u8D70\u540C\u4E00\u6761\u975E\u4FB5\u5165\u5F0F\u6302\u8F7D\uFF1B\u5D4C\u5957\u7684\u5757\u7EA7\u5B50\u8282\u70B9\u7559\u5728\u5BB9\u5668\u4E4B\u5916\uFF0C\n * \u6392\u7248\u539F\u6837\u4FDD\u7559\u3002\n * @returns \u81EA\u5EFA\u5BB9\u5668\uFF1B\u8282\u70B9\u4E0D\u518D\u76F8\u90BB\u6216\u6CA1\u6709\u5171\u540C\u7236\u8282\u70B9\u65F6\u8FD4\u56DE null\u3002\n */\n static mountRun(nodes: Node[], translated: string, options: MountOptions = {}): HTMLElement | null {\n const first = nodes[0];\n const parent = first?.parentNode;\n if (!first || !parent || !parent.ownerDocument) return null;\n if (!nodes.every((node) => node.parentNode === parent)) return null;\n\n const originalText =\n options.originalText ?? nodes.map((node) => node.textContent ?? '').join('').trim();\n if (!originalText) return null;\n\n const run = parent.ownerDocument.createElement('span');\n run.className = CLASS_RUN;\n run.setAttribute(RUN_ATTRIBUTE, 'true');\n parent.insertBefore(run, first);\n for (const node of nodes) run.appendChild(node);\n this.mount(run, translated, { originalText, interactive: options.interactive });\n return run;\n }\n\n /** \u8FD8\u539F\u884C\u5185\u7247\u6BB5\u5E76\u64A4\u6389\u81EA\u5EFA\u5BB9\u5668\u3002 */\n static unmountRun(run: HTMLElement): void {\n this.unmount(run);\n const parent = run.parentNode;\n if (!parent) return;\n while (run.firstChild) parent.insertBefore(run.firstChild, run);\n parent.removeChild(run);\n }\n\n /** \u8FD8\u539F\u4E00\u4E2A\u4F5C\u7528\u57DF\u5185\u6211\u4EEC\u6302\u8FC7\u7684\u5168\u90E8\u8BD1\u6587\uFF08\u5143\u7D20\u5355\u4F4D\u4E0E\u884C\u5185\u7247\u6BB5\u90FD\u7B97\uFF09\u3002 */\n static restore(scope: ParentNode): void {\n const mounted = scope.querySelectorAll<HTMLElement>('[data-tidy-translated=\"true\"]');\n for (const element of mounted) {\n if (element.getAttribute(RUN_ATTRIBUTE) === 'true') this.unmountRun(element);\n else this.unmount(element);\n }\n }\n\n /** \u6211\u4EEC\u81EA\u5DF1\u7684\u6302\u8F7D\u5BB9\u5668\u4E0E\u8FD8\u539F\u5BB9\u5668\uFF0C\u89C2\u5BDF\u5668\u5FC5\u987B\u8DF3\u8FC7\u5B83\u4EEC\u3002 */\n static isOwnNode(element: HTMLElement): boolean {\n return (\n element.classList?.contains(CLASS_TRANSLATED_BLOCK) === true ||\n element.classList?.contains(CLASS_ORIGINAL_HIDDEN) === true ||\n element.classList?.contains(CLASS_ORIGINAL_SHOWN) === true ||\n element.classList?.contains(CLASS_RUN) === true\n );\n }\n\n /**\n * \u628A\u4E00\u4E2A\u4F5C\u7528\u57DF\u91CC\u7684\u5168\u90E8\u6302\u8F7D\u8BD1\u6587\u4E00\u8D77\u7FFB\u9762\u3002\u601D\u8003\u94FE\u53EA\u7531\u5361\u7247\u6807\u9898\u53F3\u4FA7\u7684\u6309\u94AE\u8C03\u7528\n * \u5B83\uFF0C\u6B63\u6587\u91CC\u7684\u70B9\u51FB\u4E0D\u518D\u6539\u53D8\u539F\u6587\u4E0E\u8BD1\u6587\u3002\n */\n static toggleGroup(scope: ParentNode, showOriginal: boolean): void {\n const mounted = scope.querySelectorAll<HTMLElement>('[data-tidy-translated=\"true\"]');\n for (const element of mounted) {\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n const transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n if (!origWrapper || !transWrapper) continue;\n if (showOriginal) {\n origWrapper.style.display = 'inline';\n origWrapper.className = CLASS_ORIGINAL_SHOWN;\n transWrapper.style.display = 'none';\n } else {\n origWrapper.style.display = 'none';\n origWrapper.className = CLASS_ORIGINAL_HIDDEN;\n transWrapper.style.display = 'inline';\n }\n }\n }\n\n /**\n * Unmounts translation and restores original DOM nodes completely.\n */\n static unmount(element: HTMLElement): void {\n if (!element) return;\n\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n const transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n\n if (origWrapper) {\n // Move all original children back to element\n while (origWrapper.firstChild) {\n element.insertBefore(origWrapper.firstChild, origWrapper);\n }\n origWrapper.remove();\n }\n\n if (transWrapper) {\n transWrapper.remove();\n }\n\n // Fallback if no wrapper was created but text was modified directly\n if (!origWrapper && element.dataset.original) {\n element.textContent = element.dataset.original;\n }\n\n delete element.dataset.tidyTranslated;\n delete element.dataset.original;\n }\n\n /**\n * Checks if an element has non-destructive translation mounted.\n */\n static isMounted(element: HTMLElement): boolean {\n return (\n element.dataset.tidyTranslated === 'true' &&\n !!element.querySelector(`:scope > .${CLASS_TRANSLATED_BLOCK}`)\n );\n }\n\n /**\n * Gets the original text recorded on the element or contained in origWrapper.\n */\n static getOriginal(element: HTMLElement): string | undefined {\n if (element.dataset.original) return element.dataset.original;\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n return origWrapper ? origWrapper.textContent?.trim() : undefined;\n }\n\n /**\n * Extracts text content excluding our own translation wrappers.\n */\n static extractVisibleText(element: HTMLElement): string {\n const origWrapper = element.querySelector<HTMLElement>(\n `:scope > .${CLASS_ORIGINAL_HIDDEN}, :scope > .${CLASS_ORIGINAL_SHOWN}`\n );\n if (origWrapper) {\n return origWrapper.textContent?.trim() || '';\n }\n const transWrapper = element.querySelector<HTMLElement>(`:scope > .${CLASS_TRANSLATED_BLOCK}`);\n if (transWrapper) {\n return transWrapper.textContent?.trim() || '';\n }\n return element.textContent?.trim() || '';\n }\n}", "/**\n * \u6263\u7559\u961F\u5217\uFF1A\u5DE5\u5177\u8C03\u7528 / \u547D\u4EE4\u884C\u5728\u6807\u9898\u8BD1\u6587\u5C31\u7EEA\u4E4B\u524D\u6574\u884C\u4E0D\u53EF\u89C1\uFF0C\u539F\u6587\u4E0D\u843D\u5C4F\u3002\n *\n * \u6263\u7559\u6309\u9605\u8BFB\u987A\u5E8F\u653E\u884C\u3002\u961F\u9996\u90A3\u4E00\u884C\u8FD8\u5728\u7B49\u8BD1\u6587\u65F6\uFF0C\u6392\u5728\u5B83\u540E\u9762\u7684\u6BCF\u4E2A\u6D41\u7A0B\u6761\u76EE\u6574\u6761\u4E0D\u53EF\u89C1\n * \u5E76\u6302\u4E0A {@link REVEAL_HOLD_ATTRIBUTE}\uFF1A\u6807\u8BB0\u8BA9 smooth-stream \u4E0D\u518D\u63A8\u8FDB\u6761\u76EE\u91CC\u7684\u9010\u5B57\n * \u63ED\u793A\uFF08\u79EF\u538B\u539F\u6837\u7559\u7740\uFF09\uFF0C`display: none` \u8BA9\u6761\u76EE\u91CC\u7684\u4EFB\u4F55\u4E1C\u897F\u90FD\u4E0A\u4E0D\u4E86\u5C4F\u2014\u2014\u4E0D\u7FFB\u8BD1\u7684\u884C\u3001\n * \u5BBF\u4E3B\u76F4\u63A5\u843D\u76D8\u7684\u6587\u672C\u3001\u5DF2\u7ECF\u9010\u5B57reveal\u4E86\u4E00\u534A\u7684\u6B63\u6587\uFF0C\u90FD\u5F97\u7B49\u8F6E\u5230\u5B83\u3002\u961F\u9996\u4E00\u5C31\u7EEA\uFF0C\u5B83\u5148\u51FA\u73B0\n * \uFF08\u8BD1\u6587\u6302\u8F7D + \u6574\u884C\u5BF9\u6570\u6DE1\u5165\uFF09\uFF0C\u518D\u8F6E\u5230\u4E0B\u4E00\u6761\u3002\u6CA1\u6709\u8FD9\u5C42\u987A\u5E8F\uFF0C\u9760\u540E\u7684\u884C\u4F1A\u5148\u5360\u4F4F\u4F4D\u7F6E\uFF0C\u7B49\n * \u9760\u524D\u7684\u884C\u51FA\u73B0\u65F6\u518D\u88AB\u9876\u4E0B\u53BB\u3002\n *\n * \u85CF\u6CD5\u662F\u6574\u6761 `display: none`\uFF0C\u4E0D\u662F\u300C\u5360\u7740\u4F4D\u7F6E\u4F46\u900F\u660E\u300D\uFF1A\u6761\u76EE\u4E00\u65E6\u8FDB\u5165\u5E03\u5C40\u5C31\u5E26\u4E0A\u4E86\n * \u4F1A\u8BDD\u6D41\u7684 16px \u5144\u5F1F\u95F4\u8DDD\uFF08`.column > :not(:empty) ~ :not(:empty)`\uFF09\uFF0C\u79EF\u538B\u51E0\u884C\u5C31\u662F\n * \u5C4F\u5E55\u5E95\u90E8\u4E00\u6761\u51E0\u5341\u50CF\u7D20\u7684\u7A7A\u767D\uFF0C\u4E14\u8FD9\u7A7A\u767D\u8981\u7B49\u8F6E\u5230\u5B83\u624D\u88AB\u586B\u4E0A\u3002\u79FB\u9664\u6574\u4E2A\u76D2\u5B50\uFF0C\u7B49\u5F85\u671F\u95F4\n * \u7684\u5360\u4F4D\u5C31\u662F\u96F6\u3002\n *\n * \u653E\u884C\u662F\u4E00\u6B65\u4E00\u884C\uFF1A\u961F\u9996\u89E3\u9501\u65F6\u6392\u5728\u5B83\u540E\u9762\u7684\u884C\u53EF\u80FD\u5DF2\u7ECF\u79EF\u4E86\u4E00\u957F\u4E32\uFF08\u5DE5\u5177\u884C\u3001\u601D\u8003\u884C\u90FD\u5728\n * \u7EE7\u7EED\u4EA7\u51FA\uFF09\uFF0C\u4E00\u6B21\u5168\u653E\u4F1A\u8BA9\u6574\u5C4F\u540C\u65F6\u6DE1\u5165\u3002\u6240\u4EE5\u89E3\u9501\u7684\u884C\u5148\u8FDB {@link RowHoldQueue} \u7684\n * \u653E\u884C\u961F\u5217\uFF0C\u7531\u5B9A\u65F6\u5668\u9010\u884C\u5410\u51FA\uFF0C\u5F85\u653E\u884C\u8D8A\u591A\u6B65\u957F\u8D8A\u77ED\uFF08\u89C1 RELEASE_BUDGET_MS\uFF09\uFF0C\u65E2\u4E0D\n * \u7A81\u5140\u4E5F\u4E0D\u4F1A\u8BA9\u957F\u961F\u62D6\u4F4F\u5C4F\u5E55\u3002\n *\n * \u7B49\u5F85\u6709\u4E0A\u9650\uFF1A\u8D85\u8FC7 HOLD_DEADLINE_MS \u4ECD\u65E0\u8BD1\u6587\u5C31\u653E\u884C\u539F\u6587\uFF0C\u8BD1\u6587\u4E4B\u540E\u5230\u8FBE\u65F6\u518D\u539F\u5730\u6302\u8F7D\u3002\n */\n\nimport { NonDestructiveTranslationMount } from './mount.ts';\n\n/** \u4E00\u884C\u6700\u591A\u88AB\u6263\u7559\u591A\u4E45\uFF0C\u5230\u70B9\u653E\u884C\u539F\u6587\u3002 */\nexport const HOLD_DEADLINE_MS = 5000;\n\n/**\n * \u9010\u884C\u653E\u884C\u7684\u603B\u9884\u7B97\uFF1A\u5F85\u653E\u884C N \u884C\u65F6\u6BCF\u4E00\u6B65\u7EA6 BUDGET/N\uFF0C\u5939\u5728\n * {@link RELEASE_STEP_MIN_MS} \u4E0E {@link RELEASE_STEP_MAX_MS} \u4E4B\u95F4\u3002\n */\nconst RELEASE_BUDGET_MS = 900;\n/** \u6B65\u957F\u4E0B\u9650\uFF1A\u518D\u957F\u7684\u961F\u4F0D\u4E5F\u4E0D\u5FEB\u4E8E\u8FD9\u4E2A\u95F4\u9694\uFF0C\u4FDD\u4F4F\u300C\u4E00\u884C\u4E00\u884C\u300D\u7684\u89C2\u611F\u3002 */\nconst RELEASE_STEP_MIN_MS = 60;\n/** \u6B65\u957F\u4E0A\u9650\uFF1A\u961F\u4F0D\u77ED\u65F6\u8BA9\u4E0A\u4E00\u884C\u7684 240ms \u6DE1\u5165\u8D70\u5B8C\u518D\u653E\u4E0B\u4E00\u6761\u3002 */\nconst RELEASE_STEP_MAX_MS = 220;\n\n/**\n * \u6302\u8D77\u6807\u8BB0\uFF1A\u6253\u5728\u4F1A\u8BDD\u6D41\u6761\u76EE\uFF08`[data-chat-flow-key]`\uFF09\u4E0A\uFF0C\u8868\u793A\u300C\u8FD9\u6761\u8FD8\u6CA1\u8F6E\u5230\u4E0A\u5C4F\u300D\u3002\n * smooth-stream \u8BFB\u5B83\u6765\u6682\u505C\u6761\u76EE\u5185\u7684\u63ED\u793A\u5F15\u64CE\uFF0C\u5B57\u9762\u91CF\u5728\u4E24\u8FB9\u5404\u5199\u4E00\u4EFD\n * \uFF08`src/client/revealHold.ts`\uFF09\u3002\n */\nexport const REVEAL_HOLD_ATTRIBUTE = 'data-dsh-reveal-hold';\n\n/**\n * smooth-stream \u7684\u884C\u5165\u573A\u5305\u88F9\u5C42\u3002\u5B83\u7528 `data-entrance` \u95E8\u63A7\u6574\u884C\u7684\u5BF9\u6570\u6DE1\u5165\uFF0C\n * \u52A8\u753B\u5B9A\u4E49\u5728\u5B83\u81EA\u5DF1\u7684\u6837\u5F0F\u91CC\u3002\n */\nconst ROW_ENTRANCE_HOST_SELECTOR = '[data-entrance]';\n\n/** \u4F1A\u8BDD\u6D41\u5BB9\u5668\u4E0E\u6D41\u6761\u76EE\u3002 */\nconst FLOW_SELECTOR = '[data-chat-flow]';\nconst FLOW_ITEM_SELECTOR = '[data-chat-flow-key]';\n\n/** \u7528\u6237\u81EA\u5DF1\u8D34\u8FDB\u6D41\u91CC\u7684\u6761\u76EE\uFF1A\u6263\u7559\u4E0D\u80FD\u628A\u5B83\u4EEC\u4E5F\u85CF\u8D77\u6765\u3002 */\nconst USER_KINDS = new Set(['user', 'steering', 'command-input']);\n\n/**\n * \u653E\u884C\u65F6\u91CD\u653E\u884C\u5165\u573A\u52A8\u753B\u3002\n *\n * \u5165\u573A\u52A8\u753B\u968F React \u6302\u8F7D\u5C31\u5F00\u59CB\uFF0C\u800C\u90A3\u4E00\u523B\u884C\u8FD8\u88AB\u6211\u4EEC\u85CF\u7740\uFF0C\u4E8E\u662F\u5B83\u6574\u6BB5\u90FD\u5728\u4E0D\u53EF\u89C1\n * \u72B6\u6001\u4E0B\u8DD1\u5B8C\uFF1Bsmooth-stream \u53EA\u5728\u300C\u9AD8\u5EA6\u589E\u957F\u300D\u65F6\u91CD\u653E\uFF0C\u800C\u5DE5\u5177\u4ECD\u5728\u8FD0\u884C\u7684\u884C\u88AB\u5B83\n * \u6709\u610F\u8DF3\u8FC7\u2014\u2014\u8FD9\u6B63\u662F\u300C\u6709\u7684\u884C\u6709\u6D41\u52A8\u6548\u679C\u3001\u6709\u7684\u76F4\u63A5\u95EA\u51FA\u6765\u300D\u7684\u6765\u6E90\u3002\n *\n * \u52A8\u753B\u5B9A\u4E49\u53EA\u6709 smooth-stream \u4E00\u5904\uFF0C\u8FD9\u91CC\u53EA\u628A\u5B83\u7684 `data-entrance` \u95E8\u63A7\u91CD\u65B0\u62E8\u5230\n * active\uFF0C\u8BA9\u6D4F\u89C8\u5668\u6309\u540C\u4E00\u5957\u6837\u5F0F\u4ECE\u5934\u53D1\u4E00\u904D\uFF1B\u968F\u6302\u8F7D\u5165\u573A\u7684\u8001\u884C\u4E0E\u6263\u7559\u653E\u884C\u7684\u884C\u56E0\u6B64\n * \u770B\u5230\u7684\u662F\u540C\u4E00\u6BB5\u5BF9\u6570\u6DE1\u5165\u3002\n */\nfunction replayRowEntrance(element: HTMLElement): void {\n // \u5361\u7247\u6761\u76EE\u5F80\u4E0A\u627E\u5F97\u5230\u5305\u88F9\u5C42\uFF1B\u6574\u4E2A\u6D41\u7A0B\u6761\u76EE\u8981\u627E\u5B83\u91CC\u9762\u7684\u90A3\u4E00\u4E2A\u3002\n const host = element.closest<HTMLElement>(ROW_ENTRANCE_HOST_SELECTOR)\n ?? element.querySelector<HTMLElement>(ROW_ENTRANCE_HOST_SELECTOR);\n if (!host) return;\n\n if (host.getAttribute('data-entrance') === 'active') {\n // \u89C4\u5219\u5DF2\u7ECF\u6302\u7740\uFF08\u4E0A\u4E00\u6B21\u52A8\u753B\u65E9\u5DF2\u8DD1\u5B8C\uFF09\uFF1A\u6E05\u4E00\u6B21\u518D\u6062\u590D\uFF0C\u8BA9\u52A8\u753B\u4ECE\u5934\u5F00\u59CB\u3002\n host.style.animation = 'none';\n void host.offsetWidth;\n host.style.animation = '';\n return;\n }\n host.setAttribute('data-entrance', 'active');\n}\n\nexport interface RowHoldEntry {\n /** \u4F1A\u8BDD\u6D41\u6761\u76EE\uFF1A\u6263\u7559\u7684\u8FB9\u754C\uFF0C\u6302\u5728\u5B83\u4E0A\u9762\u7684\u6807\u8BB0\u51B3\u5B9A\u540E\u9762\u54EA\u4E9B\u6761\u76EE\u88AB\u6302\u8D77\u3002 */\n item: HTMLElement;\n /** \u88AB\u7FFB\u8BD1\u7684\u6458\u8981 span\uFF1BReact \u53EF\u80FD\u5728\u6263\u7559\u671F\u95F4\u6362\u6389\u5B83\u3002\u5DE5\u5177\u884C\u672C\u8EAB\u4E0D\u7FFB\u8BD1\u65F6\u4E3A null\u3002 */\n span: HTMLElement | null;\n /** \u884C\u6839\uFF08`[data-variant]` \u5361\u7247\uFF09\u3002\u5B83\u53EA\u7528\u6765\u8FA8\u8BA4\u300C\u540C\u4E00\u884C\u300D\u662F\u5426\u5DF2\u7ECF\u5165\u961F\u3002 */\n root: HTMLElement;\n /** \u5F53\u524D\u6B63\u5728\u7B49\u5F85\u8BD1\u6587\u7684\u539F\u6587\u3002 */\n text: string;\n /** \u62FF\u5230\u7684\u8BD1\u6587\uFF1Bnull \u8868\u793A\u8FD9\u4E00\u884C\u786E\u5B9A\u6CA1\u6709\u8BD1\u6587\u53EF\u7B49\uFF08\u5931\u8D25 / \u8D85\u65F6\uFF09\u3002 */\n translation: string | null;\n /** \u8BD1\u6587\uFF08\u6216\u5931\u8D25\u5224\u5B9A\uFF09\u5DF2\u5C31\u7EEA\uFF0C\u53EA\u7B49\u6392\u5230\u961F\u9996\u3002\u6CA1\u6709\u8BD1\u6587\u53EF\u7B49\u7684\u884C\u5165\u573A\u5373\u5C31\u7EEA\u3002 */\n ready: boolean;\n timer: number | null;\n}\n\nclass RowHoldQueue {\n /** \u6309\u9605\u8BFB\u987A\u5E8F\u6392\u5217\u7684\u6263\u7559\u884C\u3002 */\n private entries: RowHoldEntry[] = [];\n /** \u7ED3\u679C\u56DE\u6765\u65F6\u53EA\u77E5\u9053 span\uFF0C\u4E0D\u77E5\u9053\u6761\u76EE\uFF0C\u6240\u4EE5\u53E6\u7559\u4E00\u4EFD\u7D22\u5F15\u3002 */\n private bySpan = new WeakMap<HTMLElement, RowHoldEntry>();\n /** \u5F53\u524D\u6302\u8D77\u7740\u7684\u6D41\u7A0B\u6761\u76EE \u2192 \u9690\u85CF\u4E4B\u524D\u5B83\u81EA\u5DF1\u7684\u884C\u5185 display\u3002 */\n private marked = new Map<HTMLElement, string>();\n /** \u5DF2\u8F6E\u5230\u4E0A\u5C4F\u3001\u6B63\u5728\u6309\u9605\u8BFB\u987A\u5E8F\u9010\u884C\u5410\u51FA\u7684\u6761\u76EE\u3002 */\n private releasing: HTMLElement[] = [];\n private releaseTimer: number | null = null;\n\n /**\n * \u9690\u85CF\u6574\u884C\u5E76\u542F\u52A8\u653E\u884C\u4E0A\u9650\u3002\u5E42\u7B49\uFF1A\u540C\u4E00\u884C\u5728\u9014\u8BF7\u6C42\u671F\u95F4\u6587\u672C\u53C8\u53D8\u4E86\u65F6\u53EA\u66F4\u65B0\u6587\u672C\u5E76\n * \u91CD\u65B0\u7B49\u5F85\uFF0C\u4E0D\u91CD\u7F6E\u4E0D\u53EF\u89C1\u7A97\u53E3\u3002\n */\n hold(span: HTMLElement, root: HTMLElement, text: string): void {\n const existing = this.bySpan.get(span) ?? this.entries.find((entry) => entry.root === root);\n if (existing) {\n if (existing.span !== span) {\n if (existing.span !== null) this.bySpan.delete(existing.span);\n existing.span = span;\n this.bySpan.set(span, existing);\n }\n if (existing.text !== text) {\n // \u539F\u6587\u88AB\u4E0A\u6E38\u6539\u5199\uFF08ask_user \u8BA1\u6570\u3001\u5931\u8D25\u6458\u8981\u3001todo \u8BA1\u6570\uFF09\uFF1A\u8FD9\u4E00\u4EFD\u8BD1\u6587\u4F5C\u5E9F\uFF0C\n // \u91CD\u65B0\u7B49\u65B0\u6587\u672C\u7684\u7ED3\u679C\uFF1B\u7B49\u5F85\u7A97\u53E3\u7167\u65E7\u6709\u4E0A\u9650\u3002\n existing.text = text;\n existing.translation = null;\n existing.ready = false;\n if (existing.timer === null && typeof window !== 'undefined') {\n existing.timer = window.setTimeout(() => this.readyEntry(existing, null), HOLD_DEADLINE_MS);\n }\n }\n this.sync();\n return;\n }\n\n const item = root.closest<HTMLElement>(FLOW_ITEM_SELECTOR);\n if (!item) return;\n\n const entry: RowHoldEntry = {\n item,\n span,\n root,\n text,\n translation: null,\n ready: false,\n timer: null,\n };\n if (typeof window !== 'undefined') {\n entry.timer = window.setTimeout(() => this.readyEntry(entry, null), HOLD_DEADLINE_MS);\n }\n this.insert(entry);\n this.bySpan.set(span, entry);\n this.sync();\n }\n\n /** \u8BE5\u6587\u672C\u6240\u5C5E\u884C\u7684\u6263\u7559\u72B6\u6001\uFF1B\u8FD4\u56DE undefined \u8868\u793A\u6CA1\u6709\u6263\u7559\u3002 */\n stateFor(span: HTMLElement): RowHoldEntry | undefined {\n return this.bySpan.get(span);\n }\n\n /**\n * \u8BD1\u6587\uFF08\u6216\u5931\u8D25\u5224\u5B9A\uFF09\u5C31\u7EEA\u3002\u771F\u6B63\u4E0A\u5C4F\u8981\u7B49\u5B83\u6392\u5230\u961F\u9996\uFF1A\u5165\u961F\u987A\u5E8F\u5C31\u662F\u9605\u8BFB\u987A\u5E8F\u3002\n * @param span - \u7B49\u5F85\u4E2D\u7684\u6458\u8981 span\u3002\n * @param translation - \u8BD1\u6587\uFF1Bnull \u8868\u793A\u8FD9\u4E00\u884C\u6CA1\u6709\u8BD1\u6587\u53EF\u7B49\uFF0C\u653E\u884C\u539F\u6587\u3002\n */\n ready(span: HTMLElement, translation: string | null): void {\n const entry = this.bySpan.get(span);\n if (!entry) return;\n // \u5DF2\u7ECF\u5C31\u7EEA\u7684\u884C\u53EA\u5728\u300C\u5148\u524D\u786E\u5B9A\u6CA1\u6709\u8BD1\u6587\u3001\u73B0\u5728\u8BD1\u6587\u53C8\u5230\u4E86\u300D\u65F6\u624D\u6539\u5199\uFF1B\u540C\u4E00\u4EFD\u539F\u6587\n // \u7684\u91CD\u590D\u7ED3\u679C\u76F4\u63A5\u5FFD\u7565\u3002\n if (entry.ready && (translation === null || entry.translation !== null)) return;\n this.readyEntry(entry, translation);\n }\n\n /**\n * \u6263\u7559\u671F\u95F4\u65B0\u51FA\u73B0\u7684\u4F1A\u8BDD\u6D41\u5185\u5BB9\uFF1A\u6392\u5728\u961F\u9996\u540E\u9762\u7684\u4E00\u5F8B\u5148\u4E0D\u4E0A\u5C4F\u3002\n *\n * \u85CF\u7684\u662F\u6574\u4E2A\u6D41\u7A0B\u6761\u76EE\uFF0C\u4E0D\u662F\u67D0\u4E00\u5F20\u5361\u7247\uFF1A\u6761\u76EE\u91CC\u7684\u4E1C\u897F\u6709\u7684\u5F52\u63ED\u793A\u5F15\u64CE\u7BA1\uFF08\u56DE\u7B54\u6B63\u6587\u3001\n * \u601D\u8003\u6B63\u6587\u3001\u8BD1\u6587\uFF0C\u6807\u8BB0\u4F1A\u8BA9\u5B83\u4EEC\u53EA\u79EF\u538B\u4E0D\u63A8\u8FDB\uFF09\uFF0C\u6709\u7684\u5BBF\u4E3B\u76F4\u63A5\u843D\u76D8\uFF08\u5DE5\u5177\u5361\u7247\u6B63\u6587\u3001\n * \u4E0D\u7FFB\u8BD1\u7684\u8BFB\u5199\u6587\u4EF6\u884C\uFF09\uFF0C\u53EA\u51BB\u7ED3\u63ED\u793A\u6321\u4E0D\u4F4F\u540E\u8005\u3002\u6574\u6761\u85CF\u8D77\u6765\u4E24\u79CD\u90FD\u8986\u76D6\uFF0C\u800C\u4E14\u6761\u76EE\n * \u672C\u8EAB\u5728 React \u91CD\u6E32\u67D3\u91CC\u4E0D\u4F1A\u88AB\u6362\u6389\uFF0C\u5361\u7247\u88AB\u6362\u6389\u4E5F\u7167\u6837\u85CF\u5F97\u4F4F\u3002\n * @param node - \u8FD9\u6B21\u53D8\u66F4\u91CC\u65B0\u589E\u7684\u5143\u7D20\uFF0C\u6216\u5176\u7956\u5148\u6240\u5728\u7684\u90A3\u4E00\u7247\u5B50\u6811\u3002\n */\n noteAppended(node: HTMLElement): void {\n const head = this.entries[0];\n if (!head) return;\n const enclosed = node.matches(FLOW_ITEM_SELECTOR)\n ? [node, ...node.querySelectorAll<HTMLElement>(FLOW_ITEM_SELECTOR)]\n : [...node.querySelectorAll<HTMLElement>(FLOW_ITEM_SELECTOR)];\n if (enclosed.length > 0) {\n for (const item of enclosed) this.holdItem(item, head);\n return;\n }\n // \u843D\u5728\u5DF2\u6709\u6761\u76EE\u91CC\u7684\u4E00\u6B21\u63D0\u4EA4\uFF08\u5B50\u4EE3\u7406\u7684\u8C03\u7528\u884C\u3001\u5361\u7247\u5185\u90E8\u91CD\u6E32\u67D3\uFF09\uFF1A\u53D7\u5F71\u54CD\u7684\n // \u662F\u5B83\u6240\u5728\u7684\u90A3\u4E2A\u6761\u76EE\u3002\n const item = node.closest<HTMLElement>(FLOW_ITEM_SELECTOR);\n if (item !== null) this.holdItem(item, head);\n }\n\n /** \u6302\u8D77\u4E00\u4E2A\u6761\u76EE\uFF1A\u6574\u6761\u4E0D\u53EF\u89C1\uFF0C\u5E76\u4E14\u4E0D\u63A8\u8FDB\u5B83\u91CC\u9762\u7684\u63ED\u793A\u3002 */\n private holdItem(item: HTMLElement, head: RowHoldEntry): void {\n if (!this.follows(head.item, item)) return;\n const kind = item.getAttribute('data-chat-flow-kind');\n if (kind !== null && USER_KINDS.has(kind)) return;\n this.mark(item);\n }\n\n private follows(head: HTMLElement, item: HTMLElement): boolean {\n return (head.compareDocumentPosition(item) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;\n }\n\n /**\n * \u85CF\u4F4F\u6574\u6761\u6D41\u7A0B\u6761\u76EE\u3002\u7528 `display: none` \u800C\u4E0D\u662F `opacity`/`visibility`\uFF1A\u6761\u76EE\u5185\u90E8\u7684\n * \u8282\u70B9\u53EF\u4EE5\u663E\u5F0F\u58F0\u660E `visibility: visible`\uFF08\u7CFB\u7EDF\u63D0\u793A\u5361\u7247\u5C31\u662F\u8FD9\u4E48\u81EA\u7BA1\u7684\uFF09\uFF0C\u90A3\u6837\u4F1A\u4ECE\n * \u9690\u85CF\u7684\u7956\u5148\u91CC\u9003\u51FA\u6765\uFF1B\u800C\u4E14\u53EA\u8981\u6761\u76EE\u8FD8\u53C2\u4E0E\u5E03\u5C40\uFF0C\u4F1A\u8BDD\u6D41\u7684 16px \u5144\u5F1F\u95F4\u8DDD\u5C31\u8FD8\u5728\u2014\u2014\u6BCF\n * \u4E00\u6761\u88AB\u6263\u7559\u7684\u884C\u90FD\u4F1A\u5728\u5C4F\u5E55\u5E95\u90E8\u7559\u4E00\u6BB5\u7B49\u5BBD\u7684\u7A7A\u767D\u3002\u6574\u4E2A\u76D2\u5B50\u79FB\u9664\uFF0C\u7B49\u5F85\u671F\u95F4\u5360\u4F4D\u4E3A\u96F6\uFF0C\n * \u8F6E\u5230\u5B83\u65F6\u518D\u8FDE\u540C\u5165\u573A\u6DE1\u5165\u4E00\u8D77\u6491\u5F00\u3002\n */\n private mark(item: HTMLElement): void {\n if (this.marked.has(item)) return;\n this.marked.set(item, item.style.display);\n item.setAttribute(REVEAL_HOLD_ATTRIBUTE, '');\n item.style.display = 'none';\n }\n\n /** \u89E3\u9664\u6302\u8D77\uFF1B`animate` \u4E3A\u771F\u65F6\u8865\u4E00\u6BB5\u884C\u5165\u573A\u6DE1\u5165\uFF0C\u907F\u514D\u5185\u5BB9\u4E00\u4E0B\u5B50\u8E66\u51FA\u6765\u3002 */\n private unmark(item: HTMLElement, animate: boolean): void {\n const previous = this.marked.get(item);\n if (previous === undefined) return;\n this.marked.delete(item);\n item.removeAttribute(REVEAL_HOLD_ATTRIBUTE);\n item.style.display = previous;\n if (animate) replayRowEntrance(item);\n }\n\n /**\n * \u6392\u8FDB\u653E\u884C\u961F\u5217\u3002\u6309\u9605\u8BFB\u987A\u5E8F\u63D2\u5165\uFF0C\u6240\u4EE5\u9010\u884C\u5410\u51FA\u7684\u987A\u5E8F\u5C31\u662F\u884C\u5728\u6D41\u91CC\u7684\u987A\u5E8F\u3002\n */\n private enqueueRelease(item: HTMLElement): void {\n if (this.releasing.includes(item)) return;\n const index = this.releasing.findIndex(\n (other) => (item.compareDocumentPosition(other) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0,\n );\n if (index === -1) this.releasing.push(item);\n else this.releasing.splice(index, 0, item);\n }\n\n /**\n * \u53D6\u51FA\u4E0B\u4E00\u4E2A\u8FD8\u8FDE\u5728\u6587\u6863\u91CC\u7684\u5F85\u653E\u884C\u6761\u76EE\u3002\n * @returns \u5F85\u653E\u884C\u6761\u76EE\uFF1B\u961F\u5217\u7A7A\u6216\u53EA\u5269\u5DF2\u65AD\u5F00\u8282\u70B9\u65F6\u8FD4\u56DE undefined\u3002\n */\n private takeRelease(): HTMLElement | undefined {\n while (this.releasing.length > 0) {\n const item = this.releasing.shift()!;\n if (item.isConnected) return item;\n this.unmark(item, false);\n }\n return undefined;\n }\n\n /** \u961F\u4F0D\u8D8A\u957F\u6B65\u957F\u8D8A\u77ED\uFF1A\u51E0\u884C\u65F6\u8BA9\u6DE1\u5165\u8DD1\u5B8C\uFF0C\u51E0\u5341\u884C\u65F6\u4E5F\u4E0D\u81F3\u4E8E\u8BA9\u8BFB\u8005\u5E72\u7B49\u3002 */\n private releaseStepMs(): number {\n const step = RELEASE_BUDGET_MS / (this.releasing.length + 1);\n return Math.min(RELEASE_STEP_MAX_MS, Math.max(RELEASE_STEP_MIN_MS, step));\n }\n\n /**\n * \u9010\u884C\u653E\u884C\uFF1A\u6BCF\u4E00\u6B65\u53EA\u8BA9\u4E00\u884C\u51FA\u73B0\uFF0C\u7136\u540E\u6309\u5F53\u65F6\u7684\u961F\u4F0D\u957F\u5EA6\u5B89\u6392\u4E0B\u4E00\u6B65\u3002\u961F\u9996\uFF08\u521A\u89E3\u9501\n * \u7684\u90A3\u4E00\u884C\uFF09\u8D70\u7684\u662F\u540C\u6B65\u7684\u90A3\u4E00\u6B65\uFF0C\u6240\u4EE5\u5B83\u4E0D\u4F1A\u6BD4\u8BD1\u6587\u665A\u4E00\u5E27\u4E0A\u5C4F\u3002\n */\n private pumpRelease(): void {\n if (this.releaseTimer !== null || typeof window === 'undefined') return;\n const item = this.takeRelease();\n if (item === undefined) return;\n this.unmark(item, true);\n if (this.releasing.length === 0) return;\n this.releaseTimer = window.setTimeout(() => {\n this.releaseTimer = null;\n this.pumpRelease();\n }, this.releaseStepMs());\n }\n\n /** \u4E22\u6389\u9010\u884C\u653E\u884C\u7684\u8FDB\u5EA6\uFF08\u5173\u95ED\u3001\u6362\u4F1A\u8BDD\u3001\u65AD\u5F00\u89C2\u5BDF\u5668\u65F6\u7528\uFF09\u3002 */\n private clearReleases(): void {\n if (this.releaseTimer !== null && typeof window !== 'undefined') {\n window.clearTimeout(this.releaseTimer);\n }\n this.releaseTimer = null;\n this.releasing = [];\n }\n\n /** \u6309\u9605\u8BFB\u987A\u5E8F\u5165\u961F\uFF1A\u5165\u961F\u987A\u5E8F\u5C31\u662F\u4E0A\u5C4F\u987A\u5E8F\u3002 */\n private insert(entry: RowHoldEntry): void {\n const index = this.entries.findIndex((other) => this.follows(entry.item, other.item));\n if (index === -1) this.entries.push(entry);\n else this.entries.splice(index, 0, entry);\n }\n\n /** \u8BD1\u6587\uFF08\u6216\u8D85\u65F6\u5224\u5B9A\uFF09\u843D\u5730\uFF0C\u4EA4\u7ED9\u961F\u9996\u5224\u5B9A\u3002 */\n private readyEntry(entry: RowHoldEntry, translation: string | null): void {\n if (entry.timer !== null && typeof window !== 'undefined') {\n window.clearTimeout(entry.timer);\n entry.timer = null;\n }\n entry.translation = translation;\n entry.ready = true;\n this.drain();\n }\n\n /**\n * \u6309\u987A\u5E8F\u89E3\u9501\uFF1A\u961F\u9996\u5C31\u7EEA\u5C31\u6302\u8BD1\u6587\u5E76\u628A\u5B83\u4EA4\u7ED9\u9010\u884C\u653E\u884C\uFF0C\u7136\u540E\u770B\u65B0\u7684\u961F\u9996\u3002\u8BD1\u6587\u5728\u6761\u76EE\n * \u4ECD\u662F `display: none` \u65F6\u6302\u4E0A\uFF0C\u6240\u4EE5\u5C4F\u5E55\u4E0A\u4E0D\u4F1A\u51FA\u73B0\u5B8C\u6574\u4E2D\u6587\u88AB\u753B\u4E00\u5E27\u518D\u88AB\u622A\u65AD\u7684\u95EA\u70C1\uFF1B\n * \u771F\u6B63\u6491\u5F00\u8FD9\u4E00\u884C\u7531 {@link pumpRelease} \u6267\u884C\u3002\n */\n private drain(): void {\n this.entries = this.entries.filter((entry) => {\n if (entry.item.isConnected) return true;\n if (entry.span !== null) this.bySpan.delete(entry.span);\n return false;\n });\n\n while (this.entries.length > 0 && this.entries[0]!.ready) {\n const entry = this.entries.shift()!;\n if (entry.span !== null) this.bySpan.delete(entry.span);\n if (entry.span !== null && entry.translation !== null && entry.span.isConnected) {\n NonDestructiveTranslationMount.mount(entry.span, entry.translation, {\n originalText: entry.text,\n });\n }\n }\n // \u89E3\u9501\u7684\u884C\u4EA4\u7ED9\u9010\u884C\u653E\u884C\uFF1A\u8FD9\u4E00\u6B65\u987A\u5E26\u8865\u5165\u573A\u6DE1\u5165\uFF0C\u6574\u6761\u4ECE\u4E0D\u53EF\u89C1\u53D8\u53EF\u89C1\u5C31\u662F\u5B83\u7684\u51FA\u573A\u3002\n this.sync();\n }\n\n /**\n * \u8BA9\u6302\u8D77\u6807\u8BB0\u4E0E\u961F\u9996\u5BF9\u9F50\uFF1A\u961F\u9996\u81EA\u5DF1\u4EE5\u53CA\u6392\u5728\u5B83\u540E\u9762\u7684\u6BCF\u4E2A\u6D41\u7A0B\u6761\u76EE\u90FD\u85CF\u8D77\u6765\uFF0C\u5176\u4F59\n * \u4EA4\u7ED9\u9010\u884C\u653E\u884C\u3002\u653E\u884C\u8BA9\u961F\u9996\u524D\u79FB\u65F6\uFF0C\u4E4B\u524D\u88AB\u5B83\u6321\u4F4F\u7684\u6761\u76EE\u5C31\u5728\u8FD9\u4E00\u6B65\u8FDB\u5165\u653E\u884C\u961F\u5217\u3002\n *\n * \u6CA1\u6709\u300C\u7ACB\u5373\u6062\u590D\u300D\u7684\u65C1\u8DEF\uFF1A\u653E\u884C\u4E00\u5F8B\u6392\u961F\u9010\u884C\u5410\u51FA\uFF0C\u6240\u4EE5\u653E\u884C\u9014\u4E2D\u65B0\u5230\u7684\u961F\u9996\u4E0D\u4F1A\u628A\n * \u6B63\u5728\u7B49\u7684\u90A3\u4E9B\u884C\u4E00\u6B21\u6027\u6296\u51FA\u6765\uFF08`releaseAll` \u662F\u552F\u4E00\u7684\u7ACB\u5373\u8DEF\u5F84\uFF09\u3002\n */\n private sync(): void {\n const head = this.entries[0];\n const wanted = new Set<HTMLElement>();\n if (head) {\n const flow = head.item.closest<HTMLElement>(FLOW_SELECTOR);\n let reached = false;\n if (flow) {\n for (const item of flow.querySelectorAll<HTMLElement>(FLOW_ITEM_SELECTOR)) {\n if (item === head.item) reached = true;\n if (reached) wanted.add(item);\n }\n }\n if (!reached) wanted.add(head.item);\n }\n\n for (const item of [...this.marked.keys()]) {\n if (!wanted.has(item)) this.enqueueRelease(item);\n }\n // \u91CD\u65B0\u88AB\u961F\u9996\u6321\u4F4F\u7684\u6761\u76EE\u9000\u51FA\u653E\u884C\u961F\u5217\uFF1A\u5B83\u5FC5\u987B\u7EE7\u7EED\u7B49\u3002\n if (this.releasing.length > 0) {\n this.releasing = this.releasing.filter((item) => !wanted.has(item));\n }\n for (const item of wanted) this.mark(item);\n this.pumpRelease();\n }\n\n /**\n * \u5168\u90E8\u653E\u884C\uFF1A\u65AD\u5F00\u89C2\u5BDF\u5668\u4E0E\u5173\u95ED\u603B\u5F00\u5173\u65F6\u7528\u3002\u6279\u91CF\u653E\u884C\u4E0D\u505A\u5165\u573A\u52A8\u753B\uFF0C\u4E5F\u4E0D\u6392\u961F\uFF0C\n * \u5426\u5219\u6574\u5C4F\u4F1A\u4E00\u8D77\u95EA\u4E00\u904D\u3001\u6216\u8005\u7559\u4E0B\u4E00\u4E2A\u8DD1\u4E0D\u5B8C\u7684\u5B9A\u65F6\u5668\u3002\n */\n releaseAll(): void {\n this.clearReleases();\n for (const entry of this.entries) {\n if (entry.timer !== null && typeof window !== 'undefined') {\n window.clearTimeout(entry.timer);\n }\n if (entry.span !== null) this.bySpan.delete(entry.span);\n }\n this.entries = [];\n for (const item of [...this.marked.keys()]) this.unmark(item, false);\n }\n}\n\nexport const rowHold = new RowHoldQueue();\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\nexport interface TranslateItemResult {\n original: string;\n translated: string;\n channel: string;\n cached: boolean;\n}\n\nexport interface TranslateBatchOptions {\n signal?: AbortSignal;\n timeoutMs?: number;\n}\n\n/** \u601D\u8003\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 ThinkBlockResult {\n original: string;\n translated: string;\n ok: boolean;\n cached: boolean;\n channel: string;\n}\n\nexport async function requestTranslateBatch(\n texts: string[],\n options: TranslateBatchOptions = {}\n): Promise<TranslateItemResult[]> {\n if (!Array.isArray(texts) || texts.length === 0) return [];\n \n // Filter out empty or whitespace-only texts\n const validTexts = texts.map((t) => (typeof t === 'string' ? t : ''));\n if (validTexts.length === 0) return [];\n\n const controller = new AbortController();\n const timeoutId = options.timeoutMs\n ? setTimeout(() => controller.abort(), options.timeoutMs)\n : null;\n\n const effectiveSignal = options.signal\n ? (options.signal.aborted ? options.signal : controller.signal)\n : controller.signal;\n\n if (options.signal) {\n options.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch('/api/dsh-chat-translate/translate', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ texts: validTexts }),\n signal: effectiveSignal,\n });\n\n if (res.ok) {\n const data = (await res.json()) as { ok: boolean; results?: TranslateItemResult[]; error?: string };\n if (data.ok && Array.isArray(data.results) && data.results.length > 0) {\n return data.results;\n } else if (data.error) {\n console.warn(`[dsh-chat-translate] \u7FFB\u8BD1\u63A5\u53E3\u9519\u8BEF: ${data.error}`);\n }\n } else {\n console.warn(`[dsh-chat-translate] \u7FFB\u8BD1\u8BF7\u6C42\u5931\u8D25 (HTTP ${res.status})`);\n }\n } catch (err: any) {\n if (err?.name === 'AbortError') {\n // Intentionally aborted\n }\n } finally {\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n }\n\n // Host route unreachable or error \u2014 leave the original text untouched\n return validTexts.map((t) => ({\n original: t,\n translated: t,\n channel: 'fallback-client',\n cached: false,\n }));\n}\n\n/**\n * \u8BF7\u6C42\u7FFB\u8BD1\u601D\u8003\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\u601D\u8003\u5757\u53EF\u80FD\u8981\u51E0\u5206\u949F\uFF09\uFF0C\u8FD9\u91CC\u4E0D\u8BBE\u81EA\u5DF1\u7684\u622A\u6B62\u65F6\u95F4\u3002\n */\nexport async function requestTranslateThink(blocks: string[]): Promise<ThinkBlockResult[]> {\n const fallback = (): ThinkBlockResult[] =>\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-think', {\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?: ThinkBlockResult[];\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] \u601D\u8003\u94FE\u7FFB\u8BD1\u63A5\u53E3\u9519\u8BEF: ${data.error}`);\n }\n } else {\n console.warn(`[dsh-chat-translate] \u601D\u8003\u94FE\u7FFB\u8BD1\u8BF7\u6C42\u5931\u8D25 (HTTP ${res.status})`);\n }\n } catch (err: any) {\n console.warn(`[dsh-chat-translate] \u601D\u8003\u94FE\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", "/**\n * StreamDebounceViewportObserver\n *\n * Combines:\n * 1. True IntersectionObserver for viewport visibility detection (rootMargin: '150px 0px').\n * 2. Typing/streaming debounce (default 400ms) to avoid translating partial streaming sentences.\n * 3. Batch queuing to group multiple visible elements into efficient batch requests.\n *\n * \u6263\u7559\u884C\uFF08{@link observeHeld}\uFF09\u590D\u7528\u540C\u4E00\u6761\u9632\u6296\u4E0E\u6279\u6B21\uFF0C\u53EA\u8DF3\u8FC7\u53EF\u89C1\u6027\u5224\u5B9A\uFF1A\n * \u884C\u6B64\u523B\u662F\u9690\u85CF\u7684\uFF0C\u7B49\u4E0D\u5230 IntersectionObserver \u56DE\u8C03\u3002\u6CE8\u518C\u65F6\u518D\u515C\u4E00\u9053\uFF1A\n * \u4EFB\u4F55\u843D\u5728\u88AB\u6302\u8D77\u6761\u76EE\uFF08`data-dsh-reveal-hold`\uFF09\u91CC\u7684\u5143\u7D20\u540C\u6837\u76F4\u63A5\u8FDB\u6279\u6B21\u2014\u2014\n * \u6574\u6761 `display: none` \u7684\u6761\u76EE\u6C38\u8FDC\u4E0D intersecting\uFF0C\u5305\u62EC\u90A3\u4E9B\u5DF2\u7ECF\u8FC7\u4E86\u6263\u7559\u961F\u5217\u3001\n * \u6B63\u6392\u5728\u9010\u884C\u653E\u884C\u961F\u4F0D\u91CC\u7684\u884C\u3002\n */\n\nimport { NonDestructiveTranslationMount } from './mount.ts';\nimport { REVEAL_HOLD_ATTRIBUTE } from './hold.ts';\n\nexport interface ViewportObserverOptions {\n rootMargin?: string;\n debounceMs?: number;\n onVisibleBatch: (items: Array<{ element: HTMLElement; text: string }>) => void;\n}\n\nexport class StreamDebounceViewportObserver {\n private intersectionObserver: IntersectionObserver | null = null;\n /**\n * Pending per-element streaming debounce timers. A Map (not a WeakMap)\n * because disconnect() must enumerate and clear every pending timer: a\n * WeakMap cannot be iterated, so a debounce armed just before the switch was\n * turned off would still fire register() afterwards.\n */\n private streamingTimers = new Map<HTMLElement, number>();\n private pendingQueue: Array<{ element: HTMLElement; text: string }> = [];\n private batchFlushTimer: number | null = null;\n private options: Required<Omit<ViewportObserverOptions, 'onVisibleBatch'>> & {\n onVisibleBatch: ViewportObserverOptions['onVisibleBatch'];\n };\n\n constructor(options: ViewportObserverOptions) {\n this.options = {\n rootMargin: options.rootMargin ?? '150px 0px',\n debounceMs: options.debounceMs ?? 400,\n onVisibleBatch: options.onVisibleBatch,\n };\n this.initIntersectionObserver();\n }\n\n private initIntersectionObserver(): void {\n if (typeof window === 'undefined' || typeof IntersectionObserver === 'undefined') {\n return;\n }\n\n this.intersectionObserver = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (entry.isIntersecting && entry.target instanceof HTMLElement) {\n const el = entry.target;\n // Stop observing once it enters viewport and is queued\n this.intersectionObserver?.unobserve(el);\n const text = el.dataset.tidyPendingText\n || NonDestructiveTranslationMount.extractVisibleText(el)\n || '';\n if (text) {\n delete el.dataset.tidyPendingText;\n this.enqueueBatch(el, text);\n }\n }\n }\n },\n {\n root: null, // viewport\n rootMargin: this.options.rootMargin,\n threshold: 0,\n }\n );\n }\n\n /**\n * Observe an element with streaming debounce.\n * If streaming updates characterData repeatedly within debounceMs, the timer resets.\n */\n observeWithDebounce(element: HTMLElement, text: string, immediate = false): void {\n this.armDebounce(element, text, immediate, false);\n }\n\n /**\n * \u6263\u7559\u884C\u7684\u53D6\u6587\u672C\u8DEF\u5F84\uFF1A\u4FDD\u7559\u540C\u4E00\u5957\u6D41\u5F0F\u9632\u6296\uFF0C\u8DF3\u8FC7\u53EF\u89C1\u6027\u5224\u5B9A\u2014\u2014\u884C\u6B64\u523B\u662F\n * \u9690\u85CF\u7684\uFF08\u6C38\u8FDC\u4E0D\u8FDB\u5165\u89C6\u53E3\uFF09\uFF0C\u653E\u884C\u7531\u6263\u7559\u63A7\u5236\u5668\u8D1F\u8D23\u3002\n */\n observeHeld(element: HTMLElement, text: string): void {\n this.armDebounce(element, text, false, true);\n }\n\n private armDebounce(\n element: HTMLElement,\n text: string,\n immediate: boolean,\n skipViewport: boolean\n ): void {\n if (!element || !text) return;\n\n // Clear any active streaming timer for this element\n const existingTimer = this.streamingTimers.get(element);\n if (existingTimer !== undefined) {\n clearTimeout(existingTimer);\n this.streamingTimers.delete(element);\n }\n\n if (immediate || this.options.debounceMs <= 0) {\n this.register(element, text, skipViewport);\n return;\n }\n\n const timer = window.setTimeout(() => {\n // Stale-callback guard: disconnect() clears the map, so a callback that\n // is no longer the registered timer must not re-enter the observer.\n if (this.streamingTimers.get(element) !== timer) return;\n this.streamingTimers.delete(element);\n if (element.isConnected) {\n // Read latest text content after streaming settles. \u5DF2\u6302\u8F7D\u7684\u884C\u5FC5\u987B\n // \u6392\u9664\u6211\u4EEC\u81EA\u5DF1\u7684\u8BD1\u6587\u4E0E\u539F\u6587\u5BB9\u5668\uFF0C\u5426\u5219\u8BFB\u5230\u7684\u4F1A\u662F\u4E24\u8005\u7684\u62FC\u63A5\u3002\n const latestText = NonDestructiveTranslationMount.extractVisibleText(element) || text;\n this.register(element, latestText, skipViewport);\n }\n }, this.options.debounceMs);\n\n this.streamingTimers.set(element, timer);\n }\n\n private register(element: HTMLElement, text: string, skipViewport: boolean): void {\n if (!element.isConnected) return;\n\n // \u6263\u7559\u884C\u3001\u4E0D\u652F\u6301 IntersectionObserver \u7684\u73AF\u5883\uFF0C\u4EE5\u53CA\u4EFB\u4F55\u843D\u5728\u88AB\u6302\u8D77\u6761\u76EE\u91CC\u7684\n // \u5143\u7D20\u90FD\u76F4\u63A5\u8FDB\u6279\u6B21\uFF1A\u88AB\u6263\u7559\u7684\u6761\u76EE\u6574\u6761 `display: none`\uFF0C\u51E0\u4F55\u4E0A\u6C38\u8FDC\u4E0D\u4F1A\n // intersecting\uFF0C\u7B49\u89C2\u5BDF\u5668\u56DE\u8C03\u5C31\u662F\u6C38\u8FDC\u7B49\u4E0D\u5230\u3002\n if (\n skipViewport\n || !this.intersectionObserver\n || element.closest(`[${REVEAL_HOLD_ATTRIBUTE}]`) !== null\n ) {\n this.enqueueBatch(element, text);\n return;\n }\n\n element.dataset.tidyPendingText = text;\n this.intersectionObserver.observe(element);\n }\n\n private enqueueBatch(element: HTMLElement, text: string): void {\n this.pendingQueue.push({ element, text });\n if (this.batchFlushTimer === null && typeof window !== 'undefined') {\n this.batchFlushTimer = window.setTimeout(() => {\n this.batchFlushTimer = null;\n this.flushQueue();\n }, 50);\n }\n }\n\n private flushQueue(): void {\n if (this.pendingQueue.length === 0) return;\n const batch = [...this.pendingQueue];\n this.pendingQueue = [];\n this.options.onVisibleBatch(batch);\n }\n\n unobserve(element: HTMLElement): void {\n const timer = this.streamingTimers.get(element);\n if (timer !== undefined) {\n clearTimeout(timer);\n this.streamingTimers.delete(element);\n }\n delete element.dataset.tidyPendingText;\n if (this.intersectionObserver) {\n this.intersectionObserver.unobserve(element);\n }\n }\n\n disconnect(): void {\n if (this.batchFlushTimer !== null) {\n clearTimeout(this.batchFlushTimer);\n this.batchFlushTimer = null;\n }\n // Drop every pending streaming debounce: after a disable none of them may\n // fire registerForViewport() (the Map is iterable, so this is exhaustive).\n for (const timer of this.streamingTimers.values()) {\n clearTimeout(timer);\n }\n this.streamingTimers.clear();\n this.pendingQueue = [];\n if (this.intersectionObserver) {\n this.intersectionObserver.disconnect();\n this.initIntersectionObserver();\n }\n }\n}\n", "import { clientCache } from './client-cache.ts';\nimport { requestTranslateBatch } from './api.ts';\nimport { rowHold } from './hold.ts';\nimport { NonDestructiveTranslationMount } from './mount.ts';\nimport { StreamDebounceViewportObserver } from './viewport-observer.ts';\n\ntype TranslateTask = {\n element: HTMLElement;\n text: string;\n};\n\nclass LazyTranslationQueue {\n private enabled = true;\n private viewportObserver: StreamDebounceViewportObserver;\n\n constructor() {\n this.viewportObserver = new StreamDebounceViewportObserver({\n rootMargin: '150px 0px',\n debounceMs: 400,\n onVisibleBatch: (items) => this.handleVisibleBatch(items),\n });\n }\n\n setEnabled(enabled: boolean): void {\n this.enabled = enabled;\n if (!enabled) {\n this.viewportObserver.disconnect();\n }\n }\n\n observe(element: HTMLElement, text: string, immediate = false): void {\n if (!this.enabled || !element.isConnected) return;\n\n // 1. If cached, apply immediately non-destructively\n const cached = clientCache.get(text);\n if (cached) {\n this.applyTranslation(element, cached, text);\n return;\n }\n\n // 2. Delegate to streaming-debounced viewport observer\n this.viewportObserver.observeWithDebounce(element, text, immediate);\n }\n\n /**\n * \u6263\u7559\u884C\u7684\u53D6\u6587\u672C\u8DEF\u5F84\u3002\u547D\u4E2D\u7F13\u5B58\u65F6\u540C\u6B65\u5C31\u7EEA\uFF08\u6CA1\u6709\u9700\u8981\u906E\u4F4F\u7684\u7B49\u5F85\uFF09\uFF0C\u5426\u5219\u8D70\u65E0\u53EF\u89C1\n * \u6027\u5224\u5B9A\u7684\u9632\u6296\u6279\u6B21\uFF1B\u8BD1\u6587\u6302\u8F7D\u7531\u6263\u7559\u961F\u5217\u5728\u8F6E\u5230\u8FD9\u4E00\u884C\u65F6\u6267\u884C\uFF0C\u597D\u8BA9\u9010\u5B57\u4E0E\u5BF9\u6570\u6DE1\u5165\n * \u4ECE\u5B83\u771F\u6B63\u4E0A\u5C4F\u7684\u90A3\u4E00\u523B\u5F00\u59CB\u3002\n */\n observeHeld(element: HTMLElement, text: string): void {\n if (!this.enabled || !element.isConnected) return;\n\n const cached = clientCache.get(text);\n if (cached) {\n rowHold.ready(element, cached);\n return;\n }\n\n this.viewportObserver.observeHeld(element, text);\n }\n\n private async handleVisibleBatch(items: TranslateTask[]): Promise<void> {\n if (!this.enabled || items.length === 0) return;\n\n // Document order: translations appear in reading order (top-to-bottom),\n // never out-of-order even when IntersectionObserver fires entries randomly.\n const sorted = [...items].sort((a, b) => {\n if (a.element === b.element) return 0;\n const pos = a.element.compareDocumentPosition(b.element);\n return pos & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n });\n\n // Group elements by text to deduplicate API requests\n const textMap = new Map<string, HTMLElement[]>();\n for (const item of sorted) {\n if (!item.element.isConnected) continue;\n\n const cached = clientCache.get(item.text);\n if (cached) {\n if (this.stillMatches(item.element, item.text)) {\n if (rowHold.stateFor(item.element)) rowHold.ready(item.element, cached);\n else this.applyTranslation(item.element, cached, item.text);\n }\n continue;\n }\n\n const list = textMap.get(item.text) || [];\n list.push(item.element);\n textMap.set(item.text, list);\n }\n\n const uniqueTexts = Array.from(textMap.keys());\n if (uniqueTexts.length === 0) return;\n\n const results = await requestTranslateBatch(uniqueTexts);\n\n for (const res of results) {\n const entries = textMap.get(res.original) || [];\n if (\n res.translated &&\n res.translated.trim() &&\n res.channel !== 'fallback' &&\n res.channel !== 'fallback-client' &&\n res.translated.trim() !== res.original.trim()\n ) {\n clientCache.set(res.original, res.translated);\n for (const entry of entries) {\n if (!entry.isConnected || !this.enabled) continue;\n const held = rowHold.stateFor(entry);\n // \u6587\u672C\u5728\u8BF7\u6C42\u5728\u9014\u65F6\u53C8\u53D8\u4E86\uFF1A\u8FD9\u4E00\u4EFD\u7ED3\u679C\u5DF2\u7ECF\u8FC7\u65F6\uFF0C\u7B49\u65B0\u6587\u672C\u81EA\u5DF1\u7684\u7ED3\u679C\u3002\n if (held && held.text !== res.original) continue;\n if (!this.stillMatches(entry, res.original)) continue;\n // \u6263\u7559\u884C\u5728\u8FD9\u91CC\u53EA\u300C\u5C31\u7EEA\u300D\uFF1A\u771F\u6B63\u6302\u8F7D\u4E0E\u4E0A\u5C4F\u7531\u961F\u5217\u6309\u9605\u8BFB\u987A\u5E8F\u6267\u884C\u3002\n if (held) rowHold.ready(entry, res.translated);\n else this.applyTranslation(entry, res.translated, res.original);\n }\n } else {\n // \u660E\u786E\u5931\u8D25\uFF08\u964D\u7EA7 / \u901A\u9053\u5173\u95ED / \u7A7A\u8BD1\u6587 / \u4E0E\u539F\u6587\u76F8\u540C\uFF09\u6CA1\u6709\u53EF\u7B49\u7684\u8BD1\u6587\uFF0C\n // \u76F4\u63A5\u653E\u884C\u539F\u6587\u3002\n for (const entry of entries) {\n const held = rowHold.stateFor(entry);\n if (held && held.text === res.original) rowHold.ready(entry, null);\n }\n console.debug(`[dsh-chat-translate] \u7FFB\u8BD1\u672A\u6210\u529F (\u964D\u7EA7\u4FDD\u7559\u539F\u6587): \"${res.original.slice(0, 40)}\"`);\n }\n }\n }\n\n /** \u5143\u7D20\u5F53\u524D\u627F\u8F7D\u7684\u539F\u6587\u662F\u5426\u4ECD\u662F\u8FD9\u6B21\u7ED3\u679C\u5BF9\u5E94\u7684\u90A3\u4E00\u4EFD\u3002 */\n private stillMatches(element: HTMLElement, original: string): boolean {\n const current = NonDestructiveTranslationMount.extractVisibleText(element);\n return !current || current === original;\n }\n\n private applyTranslation(element: HTMLElement, translated: string, original: string): void {\n if (!element.isConnected || !this.enabled) return;\n NonDestructiveTranslationMount.mount(element, translated, {\n originalText: original,\n });\n }\n\n disconnect(): void {\n this.viewportObserver.disconnect();\n }\n}\n\nexport const lazyQueue = new LazyTranslationQueue();", "import { randomTokenId } from './mask-tokens.ts';\n\n/**\n * \u601D\u8003\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 THINK_MAX_INPUT_TOKENS = 4096;\nexport const THINK_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 = THINK_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 ThinkPieceShell<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 = THINK_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 ThinkBatchFormat {\n id: string;\n token: (index: number) => string;\n}\n\nexport function createThinkBatchFormat(): ThinkBatchFormat {\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: ThinkBatchFormat): 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: ThinkBatchFormat,\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 hasThinkBatchResidue(text: string): boolean {\n return new RegExp(BATCH_TOKEN_PATTERN_SOURCE, 'u').test(text);\n}\n", "/**\n * \u601D\u8003\u5361\u6807\u9898\u53F3\u4FA7\u7684\u7FFB\u8BD1\u6309\u94AE\u3002\n *\n * \u4E09\u6001\uFF1Aidle\uFF08\u63CF\u8FB9\u300C\u8BD1\u300D\u5B57\uFF0C\u70B9\u51FB\u5F00\u59CB\u7FFB\u8BD1\uFF09\u3001working\uFF08\u5708\u5185\u5706\u73AF\u65CB\u8F6C\uFF0C\u70B9\u51FB\u5FFD\u7565\uFF09\u3001\n * translated\uFF08\u5B9E\u5FC3\u9AD8\u4EAE\uFF0C\u70B9\u51FB\u6574\u6761\u5207\u56DE\u539F\u6587\uFF09\u3002\u6837\u5F0F\u53EA\u5728\u9996\u6B21\u4F7F\u7528\u6CE8\u5165\u4E00\u6B21\uFF0C\u914D\u8272\u6CBF\u7528\n * \u5185\u6838\u7684 dsw-* \u53D8\u91CF\uFF1B\u7CFB\u7EDF\u5F00\u542F\u300C\u51CF\u5F31\u52A8\u6001\u6548\u679C\u300D\u65F6\u5706\u73AF\u4E0D\u65CB\u8F6C\u3002\n */\n\nexport type ThinkButtonState = 'idle' | 'working' | 'translated';\n\nexport const THINK_BUTTON_CLASS = 'dsh-tidy-think-button';\n\nconst STYLE_TAG_ID = '@lynn123411/dsh-chat-translate/think-button.css';\n\nconst THINK_BUTTON_CSS = `\n/* \u601D\u8003\u5361\u6807\u9898\u53F3\u4FA7\u7684\u7FFB\u8BD1\u6309\u94AE\uFF1A\u4E09\u6001\uFF08\u672A\u7FFB\u8BD1 / \u7FFB\u8BD1\u4E2D / \u5DF2\u7FFB\u8BD1\uFF09\u4E0E\u65CB\u8F6C\u52A8\u753B\u3002 */\n.dsh-tidy-think-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n flex: none;\n width: 18px;\n height: 18px;\n margin: 0 6px 0 2px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, currentColor);\n font-family: inherit;\n font-size: 10px;\n line-height: 1;\n cursor: pointer;\n}\n\n.dsh-tidy-think-button:hover:not([disabled]) {\n border-color: var(--dsw-alias-label-secondary, rgba(128, 128, 128, 0.6));\n color: var(--dsw-alias-label-primary, currentColor);\n}\n\n.dsh-tidy-think-button[data-state='translated'] {\n border-color: transparent;\n background: color-mix(in srgb, var(--dsw-static-deepseek-500, #4d6bfe) 18%, transparent);\n color: var(--dsw-static-deepseek-500, #4d6bfe);\n}\n\n.dsh-tidy-think-button[data-state='working'] {\n color: transparent;\n}\n\n.dsh-tidy-think-button[data-state='working']::after {\n content: '';\n position: absolute;\n inset: 3px;\n border: 1.5px solid var(--dsw-alias-label-caption, rgba(128, 128, 128, 0.45));\n border-top-color: var(--dsw-static-deepseek-500, #4d6bfe);\n border-radius: 50%;\n animation: dsh-tidy-think-spin 0.8s linear infinite;\n}\n\n.dsh-tidy-think-button[disabled] {\n opacity: 0.35;\n cursor: default;\n}\n\n@keyframes dsh-tidy-think-spin {\n to { transform: rotate(360deg); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dsh-tidy-think-button[data-state='working']::after {\n animation: none;\n border-color: var(--dsw-static-deepseek-500, #4d6bfe);\n }\n}\n`;\n\n/** \u6CE8\u5165\u6309\u94AE\u6837\u5F0F\uFF0C\u91CD\u590D\u8C03\u7528\u65E0\u526F\u4F5C\u7528\u3002 */\nexport function ensureThinkButtonStyles(): void {\n if (typeof document === 'undefined') return;\n if (document.querySelector('style[data-plugin-css=\"' + STYLE_TAG_ID + '\"]') !== null) return;\n const style = document.createElement('style');\n style.dataset.plugin = '@lynn123411/dsh-chat-translate';\n style.dataset.pluginCss = STYLE_TAG_ID;\n style.textContent = THINK_BUTTON_CSS;\n document.head?.appendChild(style);\n}\n\nexport interface ThinkButtonHandle {\n element: HTMLButtonElement;\n getState(): ThinkButtonState;\n setState(state: ThinkButtonState): void;\n setDisabled(disabled: boolean, reason: string): void;\n}\n\n/** \u521B\u5EFA\u4E00\u4E2A\u6309\u94AE\u5B9E\u4F8B\uFF1BonActivate \u7531\u8C03\u7528\u65B9\u51B3\u5B9A\u70B9\u4E0B\u53BB\u505A\u4EC0\u4E48\u3002 */\nexport function createThinkButton(onActivate: () => void): ThinkButtonHandle {\n ensureThinkButtonStyles();\n const element = document.createElement('button');\n element.type = 'button';\n element.className = THINK_BUTTON_CLASS;\n element.textContent = '\u8BD1';\n element.dataset.state = 'idle';\n element.setAttribute('aria-label', '\u7FFB\u8BD1\u601D\u8003\u94FE');\n element.title = '\u7FFB\u8BD1\u601D\u8003\u94FE';\n // \u6574\u884C\u53EF\u70B9\uFF08\u5C55\u5F00/\u6536\u8D77\uFF09\uFF0C\u6309\u94AE\u5FC5\u987B\u81EA\u5DF1\u62E6\u4E0B\u5192\u6CE1\u3002\n element.addEventListener('click', (event) => {\n event.stopPropagation();\n event.preventDefault();\n if (element.disabled) return;\n onActivate();\n });\n\n const setState = (state: ThinkButtonState): void => {\n element.dataset.state = state;\n const label = state === 'translated' ? '\u663E\u793A\u601D\u8003\u539F\u6587' : '\u7FFB\u8BD1\u601D\u8003\u94FE';\n element.setAttribute('aria-label', label);\n element.title = label;\n };\n\n return {\n element,\n getState: () => (element.dataset.state ?? 'idle') as ThinkButtonState,\n setState,\n setDisabled: (disabled, reason) => {\n element.disabled = disabled;\n if (disabled) element.title = reason;\n else setState((element.dataset.state ?? 'idle') as ThinkButtonState);\n },\n };\n}", "import { thinkClientCache } from './client-cache.ts';\nimport { requestTranslateThink } from './api.ts';\nimport { NonDestructiveTranslationMount, CLASS_TRANSLATED_BLOCK } from './mount.ts';\nimport { estimateTokens } from '../../server/pipeline/think.ts';\nimport { createThinkButton, THINK_BUTTON_CLASS, type ThinkButtonHandle, type ThinkButtonState } from './think-button.ts';\n\n/**\n * \u601D\u8003\u6B63\u6587\u7684\u7FFB\u8BD1\uFF0C\u53EA\u7531\u5361\u7247\u6807\u9898\u53F3\u4FA7\u90A3\u4E2A\u6309\u94AE\u89E6\u53D1\uFF1B\u6309\u94AE\u53EA\u5728\u5361\u7247\u5C55\u5F00\u65F6\u5B58\u5728\u3002\n *\n * \u76EE\u6807\u53EA\u53D6\u601D\u8003\u5361\u91CC\u7684\u6B63\u6587\u5BB9\u5668\uFF0C\u7FFB\u8BD1\u5355\u4F4D\u662F markdown \u6E32\u67D3\u51FA\u6765\u7684\u5757\u7EA7\u5143\u7D20\uFF1B\u6DF7\u5408\u5757\n * \uFF08\u5217\u8868\u9879\u6587\u5B57 + \u5B50\u5217\u8868 / \u4EE3\u7801\u5757\uFF09\u62C6\u6210\u884C\u5185\u7247\u6BB5\uFF0C\u5D4C\u5957\u7684\u5757\u7EA7\u5B50\u8282\u70B9\u7559\u5728\u539F\u4F4D\uFF1B\n * \u4EE3\u7801\u5757\uFF08pre\uFF09\u53CA\u5176\u5185\u90E8\u4E00\u5F8B\u4E0D\u52A8\u3002\n *\n * \u4E0D\u81EA\u52A8\u7FFB\u8BD1\uFF1A\u5C55\u5F00\u53EA\u5728\u8BE5\u6761\u5168\u90E8\u5355\u4F4D\u90FD\u547D\u4E2D\u5BA2\u6237\u7AEF\u7F13\u5B58\u65F6\u76F4\u63A5\u663E\u793A\u4E2D\u6587\uFF08\u4E0D\u53D1\u8BF7\u6C42\uFF09\uFF0C\n * \u5176\u4F59\u60C5\u51B5\u4E00\u5F8B\u7B49\u6309\u94AE\u88AB\u70B9\u51FB\u3002\n */\n\n/** Think cards, the root both renderers mark. */\nconst THINK_CARD_SELECTOR = '[data-variant=\"think\"]';\n/** \u6B63\u6587\u5BB9\u5668\uFF1A\u5185\u6838\u4E0E smooth-stream \u7684\u7C7B\u540D\u90FD\u542B thinkBody\u3002 */\nconst THINK_BODY_SELECTOR = '[class*=\"thinkBody\" i]';\n/** \u5C55\u5F00\u7684\u5361\u7247\u4F1A\u5728\u8FD9\u4E2A\u5BB9\u5668\u4E0A\u5E26 data-open\u3002 */\nconst EXPANDED_SELECTOR = '[data-open]';\n\nconst BLOCK_TAGS = new Set([\n 'P',\n 'H1',\n 'H2',\n 'H3',\n 'H4',\n 'H5',\n 'H6',\n 'LI',\n 'BLOCKQUOTE',\n 'TD',\n 'TH',\n 'DT',\n 'DD',\n 'FIGCAPTION',\n]);\n\nconst CONTAINER_TAGS = new Set(['UL', 'OL', 'DL', 'TABLE', 'THEAD', 'TBODY', 'TR', 'DIV', 'SECTION']);\n\n/** \u51FA\u73B0\u8FD9\u4E9B\u540E\u4EE3\u5C31\u8BF4\u660E\u5F53\u524D\u5757\u662F\u6DF7\u5408\u5757\uFF0C\u5FC5\u987B\u62C6\u7247\u6BB5\u800C\u4E0D\u662F\u6574\u5757\u5F53\u5355\u4F4D\u3002 */\nconst NESTED_BOUNDARY_SELECTOR = [...BLOCK_TAGS, ...CONTAINER_TAGS, 'PRE']\n .join(',')\n .toLowerCase();\n\n/**\n * \u4E00\u6B21\u5BA2\u6237\u7AEF\u8BF7\u6C42\u88C5\u591A\u5C11\u4E2A\u5355\u4F4D\uFF1A\u4F30\u7B97 token \u4F4E\u4E8E\u5BBF\u4E3B\u7684\u6253\u5305\u4E0A\u9650\uFF0C\u8BA9\u6BCF\u4E2A\u8BF7\u6C42\u5728\n * \u5BBF\u4E3B\u4FA7\u901A\u5E38\u53EA\u5BF9\u5E94\u4E00\u6B21\u6A21\u578B\u8C03\u7528\uFF1B\u8BF7\u6C42\u9010\u5757\u53D1\u51FA\u3001\u8BD1\u6587\u9010\u5757\u6302\u8F7D\u3002\n */\nconst REQUEST_TOKENS = 2048;\n\n/** \u89C2\u5BDF\u5668\u628A\u5361\u7247\u53D8\u5316\u4EA4\u7ED9\u63A7\u5236\u5668\uFF0C\u8FD9\u91CC\u7684\u9632\u6296\u53EA\u7528\u4E8E\u5408\u5E76\u540C\u4E00\u6279 DOM \u53D8\u52A8\u3002 */\nconst SYNC_DEBOUNCE_MS = 120;\n\n/** \u7A33\u5B9A\u7684\u7FFB\u8BD1\u5355\u4F4D\uFF1A\u4E00\u5757\u7EA7\u5143\u7D20\uFF0C\u6216\u4E00\u6BB5\u884C\u5185\u8282\u70B9\u3002 */\nexport interface ThinkUnit {\n element?: HTMLElement;\n nodes?: Node[];\n text: string;\n anchor: Node;\n}\n\nfunction isMountedElement(element: HTMLElement): boolean {\n return element.dataset?.tidyTranslated === 'true';\n}\n\nfunction hasNestedBoundary(element: HTMLElement): boolean {\n return element.querySelector(NESTED_BOUNDARY_SELECTOR) !== null;\n}\n\n/** \u6536\u96C6\u6B63\u6587\u91CC\u5168\u90E8\u53EF\u7FFB\u8BD1\u5355\u4F4D\uFF0C\u8DF3\u8FC7\u5DF2\u6302\u8BD1\u6587\u7684\u90E8\u5206\u4E0E\u4EE3\u7801\u5757\u3002 */\nexport function collectThinkUnits(body: HTMLElement): ThinkUnit[] {\n const units: ThinkUnit[] = [];\n\n const pushRun = (nodes: Node[]): void => {\n if (nodes.length === 0) return;\n const text = nodes.map((node) => node.textContent ?? '').join('').trim();\n if (!text) return;\n const anchor = nodes[0];\n if (anchor === undefined) return;\n units.push({ nodes, text, anchor });\n };\n\n const walk = (container: HTMLElement): void => {\n let run: Node[] = [];\n const flush = (): void => {\n pushRun(run);\n run = [];\n };\n\n for (const node of Array.from(container.childNodes)) {\n if (node.nodeType !== 1) {\n run.push(node);\n continue;\n }\n const element = node as HTMLElement;\n if (NonDestructiveTranslationMount.isOwnNode(element)) {\n flush();\n continue;\n }\n if (element.tagName === 'PRE') {\n flush();\n continue;\n }\n if (BLOCK_TAGS.has(element.tagName)) {\n flush();\n collectBlock(element);\n continue;\n }\n if (CONTAINER_TAGS.has(element.tagName)) {\n flush();\n walk(element);\n continue;\n }\n // \u884C\u5185\u5143\u7D20\uFF08strong / code / a / em \u7B49\uFF09\u4E0E\u76F8\u90BB\u6587\u672C\u5408\u6210\u4E00\u4E2A\u7247\u6BB5\n run.push(element);\n }\n flush();\n };\n\n const collectBlock = (element: HTMLElement): void => {\n if (isMountedElement(element)) return;\n if (!hasNestedBoundary(element)) {\n const text = (element.textContent ?? '').trim();\n if (text) units.push({ element, text, anchor: element });\n return;\n }\n walk(element);\n };\n\n walk(body);\n return units;\n}\n\n/** \u6309\u4F30\u7B97 token \u628A\u5355\u4F4D\u5207\u6210\u82E5\u5E72\u6B21\u5BA2\u6237\u7AEF\u8BF7\u6C42\uFF0C\u987A\u5E8F\u4E0D\u53D8\u3002 */\nexport function chunkUnits(units: ThinkUnit[], maxTokens: number = REQUEST_TOKENS): ThinkUnit[][] {\n const chunks: ThinkUnit[][] = [];\n let current: ThinkUnit[] = [];\n let tokens = 0;\n for (const unit of units) {\n const cost = Math.max(estimateTokens(unit.text), 1);\n if (current.length > 0 && tokens + cost > maxTokens) {\n chunks.push(current);\n current = [];\n tokens = 0;\n }\n current.push(unit);\n tokens += cost;\n }\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\ninterface CardState {\n button: ThinkButtonHandle | null;\n state: ThinkButtonState;\n /** \u672C\u6B21\u5C55\u5F00\u662F\u5426\u5DF2\u7ECF\u505A\u8FC7\u300C\u5168\u90E8\u547D\u4E2D\u7F13\u5B58\u5C31\u76F4\u63A5\u663E\u793A\u300D\u7684\u5224\u65AD\u3002 */\n expandedHandled: boolean;\n}\n\nclass ThinkTranslateController {\n /** \u603B\u5F00\u5173\u4E0E\u8BBE\u7F6E\u91CC\u7684\u601D\u8003\u94FE\u5F00\u5173\u540C\u65F6\u6253\u5F00\u3002 */\n private enabled = false;\n /** AI \u901A\u9053\u5F00\u5173\u6253\u5F00\u4E14\u5DF2\u914D\u7F6E\uFF1B\u7F3A\u4EFB\u4F55\u4E00\u6761\u90FD\u4E0D\u6CE8\u5165\u6309\u94AE\u3002 */\n private configured = false;\n private states = new WeakMap<HTMLElement, CardState>();\n private syncTimers = new WeakMap<HTMLElement, number>();\n /** \u53EA\u5904\u7406\u5F53\u524D\u4F1A\u8BDD\u7684\u5BB9\u5668\uFF1B\u7531\u89C2\u5BDF\u5668\u5728\u4F1A\u8BDD\u5207\u6362\u65F6\u66F4\u65B0\u3002 */\n private scope: ParentNode | null = null;\n\n /** \u89C2\u5BDF\u5668\u628A\u5F53\u524D\u4F1A\u8BDD\u7684\u6839\u4EA4\u7ED9\u63A7\u5236\u5668\uFF0C\u63D2\u5165\u6309\u94AE\u65F6\u53EA\u770B\u8FD9\u4E00\u68F5\u5B50\u6811\u3002 */\n setScope(scope: ParentNode): void {\n this.scope = scope;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n setEnabled(enabled: boolean): void {\n if (enabled === this.enabled) return;\n this.enabled = enabled;\n this.applyVisibility();\n }\n\n setConfigured(configured: boolean): void {\n if (configured === this.configured) return;\n this.configured = configured;\n this.applyVisibility();\n }\n\n /** \u6309\u94AE\u662F\u5426\u53EF\u4EE5\u5B58\u5728\uFF1A\u4E24\u4E2A\u5F00\u5173\u4E0E AI \u901A\u9053\u914D\u7F6E\u7F3A\u4E00\u4E0D\u53EF\u3002 */\n private usable(): boolean {\n return this.enabled && this.configured;\n }\n\n private applyVisibility(): void {\n if (typeof document === 'undefined') return;\n if (this.usable()) {\n this.syncAll();\n return;\n }\n for (const element of document.querySelectorAll<HTMLElement>('.' + THINK_BUTTON_CLASS)) {\n element.remove();\n }\n for (const card of document.querySelectorAll<HTMLElement>(THINK_CARD_SELECTOR)) {\n const state = this.states.get(card);\n if (state !== undefined) {\n state.button = null;\n state.state = 'idle';\n state.expandedHandled = false;\n }\n NonDestructiveTranslationMount.restore(card);\n }\n }\n\n /** \u626B\u63CF\u5F53\u524D\u4F1A\u8BDD\u91CC\u7684\u5168\u90E8\u601D\u8003\u5361\uFF08\u5F00\u5173\u3001\u914D\u7F6E\u6216\u4F1A\u8BDD\u53D8\u5316\u540E\u8C03\u7528\uFF09\u3002 */\n syncAll(): void {\n const scope = this.scope ?? (typeof document === 'undefined' ? null : document);\n if (scope === null) return;\n for (const card of scope.querySelectorAll<HTMLElement>(THINK_CARD_SELECTOR)) {\n this.syncCard(card);\n }\n }\n\n /** \u9632\u6296\u540E\u540C\u6B65\u4E00\u5F20\u5361\u7247\u3002 */\n syncCard(card: HTMLElement): void {\n if (typeof window === 'undefined') return;\n const existing = this.syncTimers.get(card);\n if (existing !== undefined) window.clearTimeout(existing);\n const timer = window.setTimeout(() => {\n this.syncTimers.delete(card);\n this.runSync(card);\n }, SYNC_DEBOUNCE_MS);\n this.syncTimers.set(card, timer);\n }\n\n private stateFor(card: HTMLElement): CardState {\n let state = this.states.get(card);\n if (state === undefined) {\n state = { button: null, state: 'idle', expandedHandled: false };\n this.states.set(card, state);\n }\n return state;\n }\n\n private runSync(card: HTMLElement): void {\n if (!card.isConnected) return;\n const state = this.stateFor(card);\n if (!this.usable()) {\n this.discardCard(card, state);\n return;\n }\n\n const expanded = card.querySelector(EXPANDED_SELECTOR) !== null;\n if (expanded) this.ensureButton(card, state);\n else {\n this.removeButton(state);\n state.expandedHandled = false;\n }\n\n if (state.state !== 'working') {\n state.state = this.isShowingTranslation(card) ? 'translated' : 'idle';\n }\n state.button?.setState(state.state);\n this.updateDisabled(card, state);\n\n if (!expanded || state.expandedHandled || state.state === 'working') return;\n state.expandedHandled = true;\n this.showCachedWhenComplete(card, state);\n }\n\n private updateDisabled(card: HTMLElement, state: CardState): void {\n const running = card.dataset.state === 'running';\n state.button?.setDisabled(running && state.state !== 'working', '\u601D\u8003\u8F93\u51FA\u4E2D\uFF0C\u7ED3\u675F\u540E\u53EF\u7FFB\u8BD1');\n }\n\n private ensureButton(card: HTMLElement, state: CardState): void {\n if (state.button !== null) return;\n const row = card.querySelector<HTMLElement>('[data-disclosure-row]');\n if (row === null) return;\n const button = createThinkButton(() => this.activate(card));\n const title = row.querySelector<HTMLElement>('[class*=\"title\" i]');\n if (title !== null && title.parentElement === row) {\n row.insertBefore(button.element, title.nextSibling);\n } else {\n row.appendChild(button.element);\n }\n state.button = button;\n button.setState(state.state);\n }\n\n /** \u6298\u53E0\u6216\u505C\u7528\u65F6\u64A4\u6389\u6309\u94AE\uFF0C\u5361\u7247\u7684\u7FFB\u8BD1\u72B6\u6001\u4E0D\u53D8\u3002 */\n private removeButton(state: CardState): void {\n state.button?.element.remove();\n state.button = null;\n }\n\n /** \u63D2\u4EF6\u505C\u7528\uFF1A\u64A4\u6389\u6309\u94AE\u5E76\u8FD8\u539F\u8FD9\u5F20\u5361\u7247\u4E0A\u7684\u5168\u90E8\u8BD1\u6587\u3002 */\n private discardCard(card: HTMLElement, state: CardState): void {\n this.removeButton(state);\n state.state = 'idle';\n state.expandedHandled = false;\n NonDestructiveTranslationMount.restore(card);\n }\n\n /** \u5361\u7247\u5F53\u524D\u663E\u793A\u7684\u662F\u8BD1\u6587\uFF08\u81F3\u5C11\u6709\u4E00\u4E2A\u8BD1\u6587\u5757\u53EF\u89C1\uFF09\u3002 */\n private isShowingTranslation(card: HTMLElement): boolean {\n const blocks = card.querySelectorAll<HTMLElement>('.' + CLASS_TRANSLATED_BLOCK);\n for (const block of blocks) {\n if (block.style.display !== 'none') return true;\n }\n return false;\n }\n\n private bodyOf(card: HTMLElement): HTMLElement | null {\n return card.querySelector<HTMLElement>(THINK_BODY_SELECTOR);\n }\n\n /** \u5C55\u5F00\u65F6\u82E5\u6574\u6761\u90FD\u547D\u4E2D\u7F13\u5B58\uFF0C\u76F4\u63A5\u663E\u793A\u4E2D\u6587\uFF0C\u4E0D\u70B9\u6309\u94AE\u4E5F\u4E0D\u53D1\u8BF7\u6C42\u3002 */\n private showCachedWhenComplete(card: HTMLElement, state: CardState): void {\n const body = this.bodyOf(card);\n if (body === null || state.state === 'working') return;\n const units = collectThinkUnits(body);\n if (units.length === 0) return;\n const cached = units.map((unit) => thinkClientCache.get(unit.text));\n if (cached.some((value) => value === undefined)) return;\n\n units.forEach((unit, index) => this.applyUnit(unit, cached[index]!));\n state.state = this.isShowingTranslation(card) ? 'translated' : 'idle';\n state.button?.setState(state.state);\n }\n\n /** \u6309\u94AE\u88AB\u70B9\u51FB\uFF1B\u6309\u94AE\u53EA\u5728\u5C55\u5F00\u7684\u5361\u7247\u4E0A\u5B58\u5728\u3002 */\n private activate(card: HTMLElement): void {\n const state = this.stateFor(card);\n if (!this.usable() || state.state === 'working') return;\n if (card.dataset.state === 'running') return;\n\n if (state.state === 'translated') {\n NonDestructiveTranslationMount.toggleGroup(card, true);\n state.state = 'idle';\n state.button?.setState('idle');\n return;\n }\n\n this.translateCard(card, state);\n }\n\n /** \u7FFB\u8BD1\u8FD9\u5F20\u5361\u7247\u7684\u5168\u90E8\u5355\u4F4D\uFF1A\u547D\u4E2D\u7F13\u5B58\u7684\u5148\u6302\u4E0A\uFF0C\u5176\u4F59\u6309\u5757\u8BF7\u6C42\u3002 */\n private translateCard(card: HTMLElement, state: CardState): void {\n const body = this.bodyOf(card);\n if (body === null) return;\n // \u4E4B\u524D\u7FFB\u8FC7\u3001\u5F53\u524D\u9762\u671D\u539F\u6587\uFF1A\u5148\u628A\u5DF2\u6709\u8BD1\u6587\u7FFB\u56DE\u6765\uFF0C\u518D\u8865\u7FFB\u7F3A\u7684\u90E8\u5206\u3002\n if (card.querySelector('.' + CLASS_TRANSLATED_BLOCK) !== null) {\n NonDestructiveTranslationMount.toggleGroup(card, false);\n }\n const units = collectThinkUnits(body);\n if (units.length === 0) {\n state.state = this.isShowingTranslation(card) ? 'translated' : 'idle';\n state.button?.setState(state.state);\n return;\n }\n\n const missing: ThinkUnit[] = [];\n for (const unit of units) {\n const cached = thinkClientCache.get(unit.text);\n if (cached === undefined) missing.push(unit);\n else this.applyUnit(unit, cached);\n }\n if (missing.length === 0) {\n state.state = this.isShowingTranslation(card) ? 'translated' : 'idle';\n state.button?.setState(state.state);\n return;\n }\n\n state.state = 'working';\n state.button?.setState('working');\n void this.translateUnits(card, state, missing);\n }\n\n private async translateUnits(\n card: HTMLElement,\n state: CardState,\n units: ThinkUnit[]\n ): Promise<void> {\n let failed = false;\n for (const chunk of chunkUnits(units, REQUEST_TOKENS)) {\n if (!this.usable()) {\n failed = true;\n break;\n }\n const texts = chunk.map((unit) => unit.text);\n let results: Awaited<ReturnType<typeof requestTranslateThink>> = [];\n try {\n results = await requestTranslateThink(texts);\n } catch {\n failed = true;\n }\n const byText = new Map(results.map((result) => [result.original, result]));\n for (const unit of chunk) {\n const result = byText.get(unit.text);\n // \u4E0E\u5DE5\u5177\u6807\u9898\u540C\u4E00\u6761\u5224\u636E\uFF1A\u8BD1\u6587\u4E3A\u7A7A\u3001\u4E0E\u539F\u6587\u76F8\u540C\u3001\u6216\u7FFB\u8BD1\u5931\u8D25\u90FD\u4E0D\u6302\u8F7D\u3002\n if (\n result === undefined ||\n !result.ok ||\n !result.translated.trim() ||\n result.translated.trim() === unit.text.trim()\n ) {\n failed = true;\n continue;\n }\n thinkClientCache.set(unit.text, result.translated);\n this.applyUnit(unit, result.translated);\n }\n }\n\n state.state = !failed && this.isShowingTranslation(card) ? 'translated' : 'idle';\n state.button?.setState(state.state);\n this.updateDisabled(card, state);\n }\n\n /** \u628A\u8BD1\u6587\u6302\u5230\u5355\u4F4D\u4E0A\uFF1B\u671F\u95F4\u6587\u672C\u6216\u8282\u70B9\u5DF2\u7ECF\u53D8\u52A8\u7684\u5355\u4F4D\u76F4\u63A5\u653E\u5F03\uFF08\u7ED3\u679C\u5DF2\u8FDB\u7F13\u5B58\uFF09\u3002 */\n private applyUnit(unit: ThinkUnit, translated: string): void {\n if (unit.element !== undefined) {\n const element = unit.element;\n if (!element.isConnected || isMountedElement(element)) return;\n if ((element.textContent ?? '').trim() !== unit.text) return;\n NonDestructiveTranslationMount.mount(element, translated, {\n originalText: unit.text,\n interactive: false,\n });\n return;\n }\n\n const nodes = unit.nodes ?? [];\n if (nodes.length === 0 || nodes.some((node) => !node.isConnected)) return;\n const parent = nodes[0]?.parentNode ?? null;\n if (parent === null || !nodes.every((node) => node.parentNode === parent)) return;\n const text = nodes.map((node) => node.textContent ?? '').join('').trim();\n if (text !== unit.text) return;\n NonDestructiveTranslationMount.mountRun(nodes, translated, {\n originalText: unit.text,\n interactive: false,\n });\n }\n\n}\n\nexport const thinkTranslator = new ThinkTranslateController();\n", "import { clientCache } from './client-cache.ts';\nimport { rowHold } from './hold.ts';\nimport { lazyQueue } from './lazy.ts';\nimport {\n CLASS_ORIGINAL_HIDDEN,\n CLASS_ORIGINAL_SHOWN,\n NonDestructiveTranslationMount,\n} from './mount.ts';\nimport { thinkTranslator } from './think.ts';\n\n// Case-insensitive match: DSH builds vary the summary class casing across\n// versions (MISisG_summary / _48RFeq_summary). Only tool-call rows are targets;\n// a Think card's collapsed summary is excluded in isToolSummarySpan.\nconst TOOL_TITLE_SELECTOR = '[class*=\"summary\" i]';\n\n/** Think cards, the root both renderers mark. */\nconst THINK_CARD_SELECTOR = '[data-variant=\"think\"]';\n\n/**\n * Current-session scroll container (the conversation layout re-renders this\n * whole subtree when the active session changes), falling back to the chat\n * flow. Both are emitted by the DSH web UI.\n */\nconst SESSION_ROOT_SELECTOR = '[data-conversation-scroll], [data-chat-flow]';\n\n/** How often (ms) we re-check that the observed root is still the live one. */\nconst ROOT_CHECK_INTERVAL_MS = 3000;\n\n/**\n * \u6458\u8981\u65C1\u8FB9\u7684\u540E\u7F00\u6587\u672C\uFF08`+2`\u3001`+12 -3`\uFF09\u3002\u5B83\u4E5F\u547D\u4E2D\u6458\u8981\u9009\u62E9\u5668\u3001\u4E5F\u4F1A\u88AB\u7FFB\u8BD1\uFF0C\n * \u4F46\u4E0D\u53C2\u4E0E\u6574\u884C\u6263\u7559\u7684\u653E\u884C\u5224\u5B9A\u3002\n */\nconst SUMMARY_SUFFIX_PATTERN = /suffix/i;\n\n/**\n * harness \u7684 turn \u8FD0\u884C\u6807\u8BB0\uFF08ChatView \u91CC\u7684 TurnStatus\uFF09\uFF0C\u53EA\u5728 turn \u8FD0\u884C\u671F\u95F4\n * \u6E32\u67D3\u5728\u4F1A\u8BDD\u6D41\u91CC\u3002\u5B83\u5C31\u662F\u300C\u5F53\u524D turn \u8FD8\u5728\u4EA7\u51FA\u300D\u7684\u5224\u636E\u3002\n */\nconst LIVE_TURN_SELECTOR = '[role=\"status\"][aria-live=\"polite\"]';\n\n/** \u4E00\u884C\u5DE5\u5177 / \u547D\u4EE4\u884C\u672C\u8EAB\uFF1A\u56FE\u6807\u3001\u6807\u9898\u3001\u5206\u9694\u70B9\u3001\u6458\u8981\u3001\u540E\u7F00\u3001chevron \u90FD\u5728\u5B83\u91CC\u9762\u3002 */\nconst ROW_ROOT_SELECTOR = '[data-variant]';\n\n/** \u4F1A\u8BDD\u6D41\u7684\u4E00\u884C\uFF08harness \u7684 flowItem\uFF09\uFF0C\u884C\u6839\u5305\u5728\u5B83\u91CC\u9762\u3002 */\nconst FLOW_ITEM_SELECTOR = '[data-chat-flow-key]';\n\n/** \u4F1A\u8BDD\u6D41\u5BB9\u5668\u3002 */\nconst FLOW_SELECTOR = '[data-chat-flow]';\n\nfunction isSummarySuffixSpan(span: HTMLElement): boolean {\n return SUMMARY_SUFFIX_PATTERN.test(span.className || '');\n}\n\nfunction isToolSummarySpan(span: HTMLElement): boolean {\n if (!span || span.nodeType !== 1) return false;\n if (span.hasAttribute('aria-hidden')) return false;\n\n // The Think card's collapsed summary \u2014 the truncated first line of the\n // reasoning \u2014 is never translated. Its body is handled by thinkTranslator\n // instead, so exclude the whole card here.\n if (span.closest(THINK_CARD_SELECTOR)) return false;\n\n // Never match parent rows or containers that contain title, leading icon, chevron or nested summary\n if (span.querySelector?.('[class*=\"title\"], [class*=\"leading\"], [class*=\"chevron\"], [class*=\"sep\"], [class*=\"summary\" i]')) {\n return false;\n }\n\n const cls = span.className || '';\n if (/title|leading|icon|badge|chevron|separator|sep\\b|row\\b|root\\b|card\\b/i.test(cls)) return false;\n\n // Never translate fold toggle (\"\u5C55\u5F00\"/\"\u6536\u8D77\") or Think card's own title badge (\"Think\"/\"\u601D\u8003\")\n const rawToggle = (span.textContent || '').trim();\n if (\n rawToggle.length <= 12 &&\n /^(\u5C55\u5F00|\u6536\u8D77|\u5C55\u5F00\u5168\u90E8|\u6536\u8D77\u5168\u90E8|Expand|Collapse|Show more|Show less|Think|\u601D\u8003)$/i.test(rawToggle)\n ) {\n return false;\n }\n if (rawToggle === 'Think' || rawToggle === '\u601D\u8003') return false;\n if (\n span.closest('button, [role=\"button\"]') &&\n rawToggle.length <= 12 &&\n /\u5C55\u5F00|\u6536\u8D77|Expand|Collapse|Think|\u601D\u8003/i.test(rawToggle)\n ) {\n return false;\n }\n\n // Must belong to tool call or think block card\n if (\n span.closest(\n '[data-chat-call-id], [data-slot=\"tool.call.toolview\"], [data-sample], [data-variant], [data-tool], [data-disclosure-row]'\n )\n ) {\n return true;\n }\n return false;\n}\n\nexport class ChatTranslateObserver {\n private observer: MutationObserver | null = null;\n private rootElement: HTMLElement | null = null;\n private rootCheckTimer: number | null = null;\n private isEnabled = true;\n /** \u8BBE\u7F6E\u91CC\u7684\u601D\u8003\u94FE\u5F00\u5173\uFF1B\u4E0E\u603B\u5F00\u5173\u4E00\u8D77\u51B3\u5B9A\u6309\u94AE\u662F\u5426\u5B58\u5728\u3002 */\n private thinkEnabled = false;\n /**\n * \u81F3\u5C11\u4E00\u6761\u5DE5\u5177\u6807\u9898\u901A\u9053\u53EF\u7528\uFF08AI \u914D\u7F6E\u9F50\u5168\u6216 Bing \u6253\u5F00\uFF09\u3002\u4E24\u6761\u90FD\u5173\u65F6\u6CA1\u6709\n * \u53EF\u7B49\u7684\u8BD1\u6587\uFF0C\u6263\u7559\u7ACB\u5373\u5931\u53BB\u610F\u4E49\u3002\n */\n private channelsAvailable = true;\n /**\n * \u6BCF\u4E2A\u4F1A\u8BDD\u6D41\u5DF2\u7ECF\u89C1\u8FC7\u7684\u672B\u5C3E\u884C\u3002\u8FFD\u52A0\u5728\u5B83\u4E4B\u540E\u7684\u884C\u7B97\u300C\u65B0\u51FA\u73B0\u300D\uFF0C\u63D2\u5728\u5B83\u4E4B\u524D\n * \u7684\u884C\uFF08\u5386\u53F2\u56DE\u653E\u3001\u52A0\u8F7D\u66F4\u65E9\uFF09\u4E0D\u7B97\u3002\n */\n private flowTail = new WeakMap<HTMLElement, HTMLElement>();\n /** \u521D\u59CB\u626B\u63CF\u671F\u95F4\u4E0D\u6263\u7559\uFF1A\u90A3\u65F6\u770B\u5230\u7684\u884C\u90FD\u662F\u5386\u53F2\u3002 */\n private suppressHold = false;\n\n constructor() {\n this.handleMutations = this.handleMutations.bind(this);\n }\n\n setEnabled(enabled: boolean): void {\n this.isEnabled = enabled;\n thinkTranslator.setEnabled(enabled && this.thinkEnabled);\n if (enabled) {\n lazyQueue.setEnabled(true);\n this.start();\n } else {\n rowHold.releaseAll();\n this.restoreOriginals();\n this.disconnect();\n lazyQueue.setEnabled(false);\n }\n }\n\n private restoreOriginals(): void {\n const scope = this.rootElement ?? document;\n NonDestructiveTranslationMount.restore(scope);\n }\n\n /**\n * \u8BBE\u7F6E\u91CC\u7684\u601D\u8003\u94FE\u5F00\u5173\uFF1A\u5173\u6389\u5C31\u64A4\u6389\u6240\u6709\u7FFB\u8BD1\u6309\u94AE\u5E76\u8FD8\u539F\u601D\u8003\u5361\u91CC\u7684\u8BD1\u6587\uFF0C\u5DE5\u5177\u6807\u9898\n * \u8BD1\u6587\u4E0D\u52A8\u3002\n */\n setThinkEnabled(enabled: boolean): void {\n this.thinkEnabled = enabled;\n thinkTranslator.setEnabled(this.isEnabled && enabled);\n }\n\n /**\n * AI \u901A\u9053\u7684\u53EF\u7528\u6027\uFF08\u901A\u9053\u5F00\u5173\u6253\u5F00\u4E14 Key / Base URL / \u6A21\u578B\u9F50\u5168\uFF09\u3002\u4E0D\u53EF\u7528\u65F6\u540C\u6837\n * \u4E0D\u6CE8\u5165\u6309\u94AE\uFF1A\u601D\u8003\u94FE\u7FFB\u8BD1\u53EA\u80FD\u8D70\u8FD9\u6761\u901A\u9053\u3002\n */\n setThinkConfigured(configured: boolean): void {\n thinkTranslator.setConfigured(configured);\n }\n\n /** \u5DE5\u5177\u6807\u9898\u901A\u9053\u53EF\u7528\u6027\uFF1A\u4E24\u6761\u90FD\u5173\u65F6\u4E0D\u6263\u7559\uFF0C\u4E5F\u6CA1\u6709\u8BD1\u6587\u53EF\u7B49\u3002 */\n setChannelsAvailable(available: boolean): void {\n this.channelsAvailable = available;\n }\n\n /**\n * The active session's scroll container. Only the currently-viewed session\n * is translated; switching sessions replaces this subtree and the observer\n * naturally follows the new content.\n */\n private isVisible(el: HTMLElement): boolean {\n if (el.hasAttribute('hidden')) return false;\n if (el.style.display === 'none') return false;\n try {\n return el.getClientRects().length > 0;\n } catch {\n return true;\n }\n }\n\n private findRoot(documentRef: Document): HTMLElement {\n if (!documentRef.body) {\n return documentRef.documentElement;\n }\n const candidates = documentRef.querySelectorAll<HTMLElement>(SESSION_ROOT_SELECTOR);\n for (const el of candidates) {\n if (this.isVisible(el)) {\n return el;\n }\n }\n return documentRef.body;\n }\n\n start(documentRef: Document = document): () => void {\n if (!this.isEnabled || typeof window === 'undefined') {\n return () => {};\n }\n\n const findAndObserveRoot = () => {\n const root = this.findRoot(documentRef);\n\n this.rootElement = root;\n thinkTranslator.setScope(root);\n\n // 1. Initial scan of existing tool elements and think cards. Whatever is\n // already on screen when a session root appears is history: those rows\n // are translated in place and never held back.\n this.suppressHold = true;\n try {\n this.scanContainer(root);\n } finally {\n this.suppressHold = false;\n }\n this.scanThink(root);\n\n // 2. Setup MutationObserver\n if (!this.observer) {\n this.observer = new MutationObserver(this.handleMutations);\n this.observer.observe(root, {\n childList: true,\n subtree: true,\n attributes: true,\n characterData: true,\n attributeFilter: [\n 'data-state',\n 'data-tool',\n 'data-variant',\n 'data-sample',\n 'aria-expanded',\n 'data-open',\n 'data-expanded',\n ],\n });\n }\n };\n\n findAndObserveRoot();\n\n // Defense: if the observed root is detached or hidden (e.g. the user\n // switched to another view), re-probe for the live session container.\n this.scheduleRootCheck();\n\n return () => {\n this.disconnect();\n };\n }\n\n private scheduleRootCheck(): void {\n if (this.rootCheckTimer !== null || typeof window === 'undefined') return;\n this.rootCheckTimer = window.setInterval(() => {\n if (this.rootElement && this.rootElement.isConnected && this.isVisible(this.rootElement)) {\n return;\n }\n this.restart();\n }, ROOT_CHECK_INTERVAL_MS);\n }\n\n private restart(): void {\n if (typeof window === 'undefined') return;\n const wasEnabled = this.isEnabled;\n this.disconnect();\n if (wasEnabled) {\n this.start();\n }\n }\n\n private handleMutations(mutations: MutationRecord[]): void {\n if (!this.isEnabled) return;\n\n for (const mutation of mutations) {\n if (mutation.type === 'childList') {\n let addedElement = false;\n let changedOwner: HTMLElement | null = null;\n for (let i = 0; i < mutation.addedNodes.length; i++) {\n const node = mutation.addedNodes[i];\n if (node instanceof HTMLElement) {\n const owner = this.mountedOwner(node);\n if (owner) {\n changedOwner = owner;\n continue;\n }\n if (NonDestructiveTranslationMount.isOwnNode(node)) continue;\n addedElement = true;\n this.noteRevealHold(node);\n this.scanNode(node);\n this.scanThink(node);\n }\n }\n // \u53EA\u6362\u6389\u4E86\u6587\u672C\u8282\u70B9\uFF08\u6D41\u5F0F\u91CD\u6E32\u67D3\u91CC\u6700\u5E38\u89C1\u7684\u4E00\u79CD\u63D0\u4EA4\uFF09\u65F6\uFF0C\u65B0\u589E\u8282\u70B9\u91CC\u6CA1\u6709\n // \u5143\u7D20\uFF0C\u5FC5\u987B\u56DE\u5230\u7236\u5143\u7D20\u91CD\u626B\uFF0C\u5426\u5219\u6D41\u5F0F\u4E2D\u7684\u65B0\u6BB5\u843D\u6C38\u8FDC\u7B49\u4E0D\u5230\u7FFB\u8BD1\u3002\n const target = mutation.target;\n if (target instanceof HTMLElement) {\n const owner = this.mountedOwner(target);\n if (owner) {\n changedOwner = owner;\n } else if (!addedElement && !NonDestructiveTranslationMount.isOwnNode(target)) {\n this.scanNode(target);\n this.scanThink(target);\n }\n }\n if (changedOwner) this.handleOriginalTextChange(changedOwner);\n } else if (mutation.type === 'attributes') {\n const target = mutation.target;\n if (target instanceof HTMLElement) {\n if (NonDestructiveTranslationMount.isOwnNode(target)) continue;\n this.scanNode(target);\n this.scanThink(target);\n }\n } else if (mutation.type === 'characterData') {\n const owner = this.mountedOwner(mutation.target);\n if (owner) {\n this.handleOriginalTextChange(owner);\n continue;\n }\n const parent = mutation.target.parentElement;\n if (parent instanceof HTMLElement) {\n if (NonDestructiveTranslationMount.isOwnNode(parent)) continue;\n this.scanNode(parent);\n this.scanThink(parent);\n }\n }\n }\n }\n\n /**\n * \u6263\u7559\u671F\u95F4\u65B0\u51FA\u73B0\u7684\u4F1A\u8BDD\u6D41\u5185\u5BB9\uFF08\u4E0B\u4E00\u4E2A\u56DE\u7B54\u8282\u70B9\u3001\u4E0B\u4E00\u4E2A\u5DE5\u5177\u884C\uFF09\u540C\u6837\u8981\u5148\u6302\u8D77\uFF1A\n * \u5B83\u4EEC\u6392\u5728\u8FD8\u6CA1\u4E0A\u5C4F\u7684\u884C\u540E\u9762\uFF0C\u5148\u51FA\u73B0\u5C31\u4F1A\u5360\u4F4F\u90A3\u4E00\u884C\u7684\u4F4D\u7F6E\u3002\n */\n private noteRevealHold(node: HTMLElement): void {\n rowHold.noteAppended(node);\n }\n\n /**\n * \u53D8\u66F4\u53D1\u751F\u5728\u6211\u4EEC\u79FB\u8FDB\u9690\u85CF\u5BB9\u5668\u7684\u539F\u6587\u91CC\u65F6\uFF0C\u8FD4\u56DE\u6302\u8F7D\u4E86\u8BD1\u6587\u7684\u90A3\u4E2A\u5143\u7D20\u3002React\n * \u5C31\u5730\u6539\u5199\u5B83\u81EA\u5DF1\u521B\u5EFA\u7684\u6587\u672C\u8282\u70B9\uFF0C\u6240\u4EE5\u300C\u539F\u6587\u53D8\u4E86\u300D\u53EA\u80FD\u4ECE\u8FD9\u6761\u8DEF\u5F84\u770B\u5230\u3002\n */\n private mountedOwner(node: Node): HTMLElement | null {\n const element = node instanceof HTMLElement ? node : node.parentElement;\n if (!element) return null;\n const wrapper = element.closest<HTMLElement>(\n `.${CLASS_ORIGINAL_HIDDEN}, .${CLASS_ORIGINAL_SHOWN}`\n );\n if (!wrapper) return null;\n const owner = wrapper.parentElement;\n return owner instanceof HTMLElement && owner.dataset.tidyTranslated === 'true' ? owner : null;\n }\n\n /**\n * \u5DF2\u6302\u8F7D\u884C\u7684\u9690\u85CF\u539F\u6587\u88AB\u4E0A\u6E38\u6539\u5199\u4E86\uFF08ask_user \u8BA1\u6570\u3001\u5931\u8D25\u6458\u8981\u3001todo \u8BA1\u6570\uFF09\uFF1A\n * \u547D\u4E2D\u7F13\u5B58\u5C31\u539F\u5730\u6362\u8BD1\u6587\uFF0C\u5426\u5219\u8BF7\u6C42\u65B0\u8BD1\u6587\u540E\u539F\u5730\u6362\u3002\u884C\u59CB\u7EC8\u53EF\u89C1\uFF0C\u4E0D\u51FA\u73B0\u82F1\u6587\u3002\n */\n private handleOriginalTextChange(owner: HTMLElement): void {\n if (!isToolSummarySpan(owner)) return;\n const current = NonDestructiveTranslationMount.extractVisibleText(owner);\n if (!current || current === owner.dataset.original) return;\n\n const cached = clientCache.get(current);\n if (cached) {\n NonDestructiveTranslationMount.mount(owner, cached, { originalText: current });\n return;\n }\n lazyQueue.observe(owner, current);\n }\n\n /**\n * \u8BE5\u884C\u662F\u5426\u8BE5\u88AB\u6263\u7559\uFF1A\u5F53\u524D turn \u4ECD\u5728\u4EA7\u51FA\u3001\u8FD9\u884C\u662F\u65B0\u8FFD\u52A0\u5230\u6D41\u672B\u5C3E\u7684\u3001\u4E0D\u662F\u540E\u7F00\u3001\n * \u901A\u9053\u53EF\u7528\uFF0C\u4E14\u4E0D\u5728\u521D\u59CB\u626B\u63CF\u671F\u95F4\u3002\u5224\u65AD\u987A\u5E26\u628A\u6BCF\u4E2A\u4F1A\u8BDD\u6D41\u89C1\u8FC7\u7684\u672B\u5C3E\u884C\u63A8\u8FDB\u5230\n * \u6700\u65B0\uFF0C\u4F9B\u540E\u7EED\u5224\u5B9A\u4F7F\u7528\uFF1B\u7B2C\u4E00\u6B21\u89C1\u5230\u7684\u4F1A\u8BDD\u6D41\u5148\u6309\u5386\u53F2\u5904\u7406\u3002\n */\n private shouldHold(span: HTMLElement): boolean {\n if (isSummarySuffixSpan(span)) return false;\n\n const flow = span.closest<HTMLElement>(FLOW_SELECTOR);\n if (!flow) return false;\n const item = span.closest<HTMLElement>(FLOW_ITEM_SELECTOR);\n if (!item) return false;\n\n const known = this.flowTail.get(flow);\n if (known === undefined) {\n // \u7B2C\u4E00\u6B21\u89C1\u5230\u7684\u4F1A\u8BDD\u6D41\uFF1A\u8FD9\u4E00\u523B\u5728\u5C4F\u4E0A\u7684\u90FD\u662F\u5DF2\u7ECF\u5B58\u5728\u7684\u5386\u53F2\uFF08\u521A\u5207\u8FC7\u6765\u7684\u4F1A\u8BDD\u3001\n // \u91CD\u653E\u7684\u8F6C\u5F55\uFF09\uFF0C\u5148\u628A\u672B\u5C3E\u8BB0\u4E0B\u6765\u518D\u8BF4\u2014\u2014\u5426\u5219\u65B0\u4F1A\u8BDD\u91CC\u6700\u5148\u88AB\u5904\u7406\u7684\u90A3\u51E0\u884C\u4F1A\u88AB\n // \u5F53\u6210\u300C\u521A\u8FFD\u52A0\u7684\u5B9E\u65F6\u884C\u300D\u6574\u6761\u6263\u4F4F\uFF0C\u51FA\u73B0\u51E0\u884C\u7A7A\u767D\u7B49\u8BD1\u6587\u7684\u60C5\u5F62\u3002\n const items = flow.querySelectorAll<HTMLElement>(FLOW_ITEM_SELECTOR);\n this.flowTail.set(flow, items[items.length - 1] ?? item);\n return false;\n }\n const appended =\n known === item ||\n (known.compareDocumentPosition(item) & Node.DOCUMENT_POSITION_FOLLOWING) !== 0;\n if (appended) this.flowTail.set(flow, item);\n\n if (this.suppressHold || !appended) return false;\n if (!this.channelsAvailable) return false;\n return flow.querySelector(LIVE_TURN_SELECTOR) !== null;\n }\n\n private scanContainer(container: HTMLElement): void {\n const spans = container.querySelectorAll<HTMLElement>(TOOL_TITLE_SELECTOR);\n spans.forEach((span) => {\n if (isToolSummarySpan(span)) {\n this.processSpan(span);\n }\n });\n }\n\n private scanNode(node: HTMLElement): void {\n if (node.matches?.(TOOL_TITLE_SELECTOR) && isToolSummarySpan(node)) {\n this.processSpan(node);\n }\n\n const spans = node.querySelectorAll<HTMLElement>(TOOL_TITLE_SELECTOR);\n spans.forEach((span) => {\n if (isToolSummarySpan(span)) {\n this.processSpan(span);\n }\n });\n }\n\n /**\n * \u4EA4\u7ED9\u601D\u8003\u6B63\u6587\u7FFB\u8BD1\u63A7\u5236\u5668\uFF1A\u547D\u4E2D\u8282\u70B9\u6240\u5728\u7684\u5361\u7247\uFF0C\u4EE5\u53CA\u5B83\u4E0B\u9762\uFF08\u6216\u5B83\u5C31\u662F\uFF09\u7684\n * \u5168\u90E8\u601D\u8003\u5361\u7247\u3002\n */\n private scanThink(node: ParentNode): void {\n if (!thinkTranslator.isEnabled()) return;\n if (node instanceof HTMLElement) {\n const card = node.closest<HTMLElement>(THINK_CARD_SELECTOR);\n if (card) thinkTranslator.syncCard(card);\n }\n if (!node.querySelectorAll) return;\n node.querySelectorAll<HTMLElement>(THINK_CARD_SELECTOR).forEach((card) => {\n thinkTranslator.syncCard(card);\n });\n }\n\n private processSpan(span: HTMLElement): void {\n if (NonDestructiveTranslationMount.isMounted(span)) {\n // \u5DF2\u6302\u8F7D\u7684\u884C\u7531 handleOriginalTextChange \u8DDF\u8FDB\u539F\u6587\u53D8\u5316\u3002\n return;\n }\n\n const text = NonDestructiveTranslationMount.extractVisibleText(span);\n if (!text) return;\n\n // \u65B0\u51FA\u73B0\u7684\u884C\u5148\u6574\u884C\u6263\u4F4F\uFF0C\u7B49\u8F6E\u5230\u5B83\u518D\u653E\u884C\uFF1B\u5DF2\u7ECF\u5728\u6263\u7559\u4E2D\u7684\u884C\u8D70\u540C\u4E00\u6761\u8DEF\uFF08\u6587\u672C\u88AB\n // \u4E0A\u6E38\u6539\u5199\u65F6\u66F4\u65B0\u7B49\u5F85\u7684\u539F\u6587\uFF09\uFF0C\u5426\u5219\u5B83\u9690\u85CF\u7740\u7B49\u4E0D\u5230 IntersectionObserver\u3002\n const root = span.closest<HTMLElement>(ROW_ROOT_SELECTOR);\n const held = rowHold.stateFor(span) !== undefined;\n if (root && (held || this.shouldHold(span))) {\n rowHold.hold(span, root, text);\n this.translateHeld(span, text);\n return;\n }\n\n // Check fast client cache\n const cached = clientCache.get(text);\n if (cached) {\n NonDestructiveTranslationMount.mount(span, cached, {\n originalText: text,\n });\n return;\n }\n\n // Send to viewport lazy queue\n lazyQueue.observe(span, text);\n }\n\n /**\n * \u6263\u7559\u884C\u7684\u53D6\u6587\u672C\u8DEF\u5F84\uFF1A\u547D\u4E2D\u7F13\u5B58\u7ACB\u5373\u5C31\u7EEA\uFF0C\u5176\u4F59\u4EA4\u7ED9\u61D2\u52A0\u8F7D\u961F\u5217\u3002\u8BD1\u6587\u771F\u6B63\u6302\u8F7D\u8981\u7B49\n * \u961F\u5217\u8F6E\u5230\u5B83\u2014\u2014\u63D0\u524D\u6302\u4E0A\u53BB\u4F1A\u628A\u9010\u5B57\u4E0E\u5BF9\u6570\u6DE1\u5165\u5728\u88AB\u6302\u8D77\u671F\u95F4\u8DD1\u5B8C\u3002\n */\n private translateHeld(span: HTMLElement, text: string): void {\n if (rowHold.stateFor(span)?.ready === true) return;\n const cached = clientCache.get(text);\n if (cached) {\n rowHold.ready(span, cached);\n return;\n }\n lazyQueue.observeHeld(span, text);\n }\n\n disconnect(): void {\n if (this.rootCheckTimer !== null) {\n clearInterval(this.rootCheckTimer);\n this.rootCheckTimer = null;\n }\n if (this.observer) {\n this.observer.disconnect();\n this.observer = null;\n }\n rowHold.releaseAll();\n lazyQueue.disconnect();\n this.rootElement = null;\n }\n}\n\nexport const chatTranslateObserver = new ChatTranslateObserver();", "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: 'Locate DSH home directory structure' })}\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 <Switch\n checked={state.aiEnabled}\n onChange={(next: boolean) => settingsStore.update({ aiEnabled: next })}\n label={t('enableAi')}\n />\n </div>\n <div className=\"dsh-tidy-desc\">\n <Tag tone={state.aiConfigured ? 'success' : 'warning'}>\n {t(state.aiConfigured ? 'badgeConfigured' : 'badgeUnconfigured')}\n </Tag>{' '}\n {t('aiFallbackNote')}\n </div>\n\n {state.aiEnabled && (\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 <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\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('thinkEnableTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">{t('thinkEnableDesc')}</div>\n </div>\n <Switch\n checked={state.thinkEnabled}\n onChange={(next: boolean) => settingsStore.update({ thinkEnabled: next })}\n label={t('thinkEnableTitle')}\n />\n </div>\n\n {state.thinkEnabled && (\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('thinkTimeoutTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">{t('thinkTimeoutDesc')}</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.thinkTimeoutMs}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const val = parseInt(e.target.value, 10);\n if (!Number.isFinite(val)) return;\n settingsStore.update({\n thinkTimeoutMs: Math.min(Math.max(val, 500), 900000),\n });\n }}\n aria-label={t('thinkTimeoutTitle')}\n />\n </div>\n )}\n </>\n )}\n </div>\n\n {/* 3. Bing \u901A\u9053 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-title\">\n <span>{t('bingTitle')}</span>\n <Switch\n checked={state.bingEnabled}\n onChange={(next: boolean) => settingsStore.update({ bingEnabled: next })}\n label={t('enableBing')}\n />\n </div>\n <div className=\"dsh-tidy-desc\">{t('bingDesc')}</div>\n </div>\n\n {/* 4. \u884C\u4E3A\u8BF4\u660E */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-row-title\">{t('behaviorTitle')}</div>\n <ul className=\"dsh-tidy-desc dsh-tidy-behavior-list\">\n <li>{t('behavior1')}</li>\n <li>{t('behavior2')}</li>\n <li>{t('behavior3')}</li>\n <li>{t('behavior4')}</li>\n <li>{t('behavior5')}</li>\n </ul>\n </div>\n\n {/* 5. \u5E76\u53D1\u63A7\u5236 */}\n <div className=\"dsh-tidy-card\">\n <div className=\"dsh-tidy-row\">\n <div className=\"dsh-tidy-row-info\">\n <div className=\"dsh-tidy-row-title\">{t('concurrencyTitle')}</div>\n <div className=\"dsh-tidy-row-desc\">{t('concurrencyDesc')}</div>\n </div>\n <Input\n type=\"number\"\n className=\"dsh-tidy-field-count\"\n min={1}\n max={100}\n step={1}\n value={state.concurrency}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => {\n const val = parseInt(e.target.value, 10);\n if (!Number.isFinite(val)) return;\n settingsStore.update({ concurrency: Math.min(Math.max(val, 1), 100) });\n }}\n aria-label={t('concurrencyTitle')}\n />\n </div>\n </div>\n </>\n )}\n </div>\n );\n}\n\n/**\n * Bind the settings store to DSH's native settings/credentials services and\n * join the shared \u300C\u9605\u8BFB\u4F53\u9A8C\u300D settings page: whichever participant activates\n * first claims the page, everyone registers a card into its child slot.\n * @param ctx - DSH browser client context; services are resolved defensively.\n */\nexport function setupSettingsUi(ctx: any): void {\n if (typeof window === 'undefined') return;\n\n // Ride DSH's own settings surface: the `settingsScope` service mirrors the\n // host document (per-namespace describe) and the `credentials` Remote API\n // writes the API key. No custom config HTTP endpoint since 1.2.\n try {\n // Declared via inject: 'settingsScope' and 'remote.credentials' (plus the\n // 'remote' root). Fall back to optional lookup so a degraded environment\n // degrades to an in-memory store instead of failing loudly.\n const settingsScope = ctx?.settingsScope || (ctx?.get ? ctx.get('settingsScope') : null);\n const remoteCredentials =\n ctx?.remote?.credentials || (ctx?.get ? ctx.get('remote.credentials') : null);\n if (settingsScope && typeof settingsScope.bind === 'function') {\n const scope = settingsScope.bind({ namespace: SETTINGS_NAMESPACE });\n settingsStore.attach(scope, remoteCredentials ?? null);\n }\n } catch (err) {\n console.warn('[dsh-chat-translate] Failed to bind settings scope:', 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.get('locale')` cannot miss it.\n // Registering the dictionary HERE is what the card's seat `t` resolves\n // against; a non-waiting sample left it unregistered and the card then fell\n // back to the shell's `common` namespace, rendering every unknown key as its\n // own key text.\n const locale = typeof ctx?.get === 'function' ? ctx.get('locale') : null;\n if (locale && typeof locale.register === 'function' && typeof ctx?.effect === '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: an earlier revision returned `zh.pageNav` for EVERY\n // key, which labelled the shared page's tab with the page name instead of\n // this plugin's own title whenever the locale service was not yet there.\n : (key: ChatTranslateLocaleKey): string => zh[key] ?? key;\n\n try {\n const slots = ctx?.slots || (ctx?.get ? ctx.get('slots') : null);\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 slots.inject('settings.section', () =>\n claimReadingSettingsPage(ctx, () => t('pageNav'), NS)\n );\n\n slots.inject(READING_ITEM_SLOT, () =>\n slots.register(\n {\n name: READING_ITEM_SLOT,\n // id = \u672C\u63D2\u4EF6\u7684 Host \u8BBE\u7F6E\u547D\u540D\u7A7A\u95F4\uFF08settings.section \u65F6\u4EE3\u7684 id \u6CBF\u7528\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 } catch (err) {\n console.warn('[dsh-chat-translate] Failed to inject settings section:', err);\n }\n}", "import { describeError } from '../../describe-error.ts';\nimport { chatTranslateObserver } from '../translate/observer.ts';\nimport { testServerChannel } from '../translate/api.ts';\n\nexport interface ClientSettingsState {\n enabled: boolean;\n concurrency: number;\n aiEnabled: boolean;\n bingEnabled: boolean;\n thinkEnabled: boolean;\n thinkTimeoutMs: number;\n baseUrl: string;\n model: string;\n aiConfigured: boolean;\n}\n\n/** \u601D\u8003\u94FE\u7FFB\u8BD1\u8D85\u65F6\u7684\u53D6\u503C\u8303\u56F4\uFF0C\u4E0E\u5BBF\u4E3B\u534A\u8FB9\u4FDD\u6301\u4E00\u81F4\u3002 */\nexport const THINK_TIMEOUT_MIN = 500;\nexport const THINK_TIMEOUT_MAX = 900000;\n\n/** 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 * Minimal structural shapes of the DSH browser services this store rides on:\n * `settingsScope` (from @deepseek-ai/dsh-client-ui-settings) and the\n * `credentials` Remote namespace. Keeping them structural keeps this bundle\n * free of host-service imports and lets tests inject fakes.\n */\nexport type SettingsPathOpLike =\n | { op: 'set'; path: string[]; value: unknown }\n | { op: 'unset'; path: string[] };\n\nexport interface SettingsScopeLike {\n getSnapshot(): {\n status: string;\n value?: Record<string, unknown>;\n writable: boolean;\n };\n subscribe(listener: () => void): () => void;\n mutate(ops: readonly SettingsPathOpLike[]): 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 concurrency: 3,\n aiEnabled: true,\n bingEnabled: true,\n thinkEnabled: true,\n thinkTimeoutMs: 600000,\n baseUrl: '',\n model: '',\n aiConfigured: false,\n};\n\n/**\n * Client settings store backed by the DSH settings namespace.\n *\n * Since 1.2 there is no localStorage overlay and no custom config HTTP\n * endpoint: the store derives from the bound `settingsScope` (which mirrors\n * the host document and folds every write answer back), and each debounced\n * flush commits its touched fields as one `scope.mutate` batch \u2014 one\n * revision fence, serialized and persisted by DSH.\n * Without a bound scope (e.g. non-loopback pages, unit tests) it degrades to\n * an in-memory store with the same semantics DSH itself uses for memory\n * persistence.\n */\nclass SettingsStore {\n private state: ClientSettingsState = { ...DEFAULT_STATE };\n private listeners = new Set<() => void>();\n private scope: SettingsScopeLike | null = null;\n private credentials: CredentialsRemoteLike | null = null;\n private unsubscribeScope: (() => 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(scope: SettingsScopeLike | null, credentials: CredentialsRemoteLike | null): void {\n if (this.unsubscribeScope) {\n this.unsubscribeScope();\n this.unsubscribeScope = null;\n }\n this.scope = scope;\n this.credentials = credentials;\n if (scope) {\n this.unsubscribeScope = scope.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 namespace value into client state and notify. */\n private derive(): void {\n if (!this.scope) return;\n const snap = this.scope.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 c = value.concurrency;\n if (typeof c === 'number' && Number.isFinite(c)) {\n next.concurrency = Math.min(Math.max(Math.round(c), 1), 100);\n }\n if (typeof value.aiEnabled === 'boolean') next.aiEnabled = value.aiEnabled;\n if (typeof value.bingEnabled === 'boolean') next.bingEnabled = value.bingEnabled;\n if (typeof value.thinkEnabled === 'boolean') next.thinkEnabled = value.thinkEnabled;\n const thinkTimeout = value.thinkTimeoutMs;\n if (typeof thinkTimeout === 'number' && Number.isFinite(thinkTimeout)) {\n next.thinkTimeoutMs = Math.min(\n Math.max(Math.round(thinkTimeout), THINK_TIMEOUT_MIN),\n THINK_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 const enabledChanged = next.enabled !== this.state.enabled;\n this.state = next;\n if (enabledChanged) {\n try {\n chatTranslateObserver.setEnabled(this.state.enabled);\n } catch {}\n }\n // \u601D\u8003\u94FE\u7FFB\u8BD1\u53EA\u5728\u603B\u5F00\u5173\u3001\u81EA\u8EAB\u5F00\u5173\u3001AI \u901A\u9053\u5F00\u5173\u4E0E AI \u914D\u7F6E\u90FD\u9F50\u5907\u65F6\u624D\u63D0\u4F9B\n // \u6309\u94AE\uFF1B\u4EFB\u4F55\u4E00\u6761\u4E0D\u6EE1\u8DB3\u90FD\u4F1A\u64A4\u6389\u6309\u94AE\u5E76\u8FD8\u539F\u5DF2\u6302\u8F7D\u7684\u601D\u8003\u6B63\u6587\u8BD1\u6587\u3002\n try {\n chatTranslateObserver.setThinkEnabled(this.state.enabled && this.state.thinkEnabled);\n chatTranslateObserver.setThinkConfigured(this.state.aiEnabled && this.state.aiConfigured);\n } catch {}\n // \u5DE5\u5177\u6807\u9898\u6263\u7559\u4EE5\u300C\u6709\u8BD1\u6587\u53EF\u7B49\u300D\u4E3A\u524D\u63D0\uFF1A\u4E24\u6761\u901A\u9053\u90FD\u5173\u65F6\u4E0D\u9690\u85CF\u4EFB\u4F55\u884C\u3002\n try {\n chatTranslateObserver.setChannelsAvailable(\n (this.state.aiEnabled && this.state.aiConfigured) || this.state.bingEnabled\n );\n } catch {}\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 * settings scope. 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 scope\n * reload 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 sanitizedConcurrency = this.state.concurrency;\n if (typeof partial.concurrency === 'number' && Number.isFinite(partial.concurrency)) {\n sanitizedConcurrency = Math.min(Math.max(Math.round(partial.concurrency), 1), 100);\n }\n let sanitizedThinkTimeout = this.state.thinkTimeoutMs;\n if (typeof partial.thinkTimeoutMs === 'number' && Number.isFinite(partial.thinkTimeoutMs)) {\n sanitizedThinkTimeout = Math.min(\n Math.max(Math.round(partial.thinkTimeoutMs), THINK_TIMEOUT_MIN),\n THINK_TIMEOUT_MAX\n );\n }\n const next: ClientSettingsState = {\n ...this.state,\n ...partial,\n concurrency: sanitizedConcurrency,\n thinkTimeoutMs: sanitizedThinkTimeout,\n };\n this.applyState(next);\n\n if (this.scope) {\n const fields = [\n 'enabled',\n 'concurrency',\n 'aiEnabled',\n 'bingEnabled',\n 'thinkEnabled',\n 'thinkTimeoutMs',\n 'baseUrl',\n 'model',\n ] 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.scope) 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.scope.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.unsubscribeScope) {\n this.unsubscribeScope();\n this.unsubscribeScope = null;\n }\n this.scope = 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-field-count {\n width: 88px;\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.dsh-tidy-behavior-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 4px 0 0;\n padding-left: 18px;\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\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/** A registration label is a plain string or a thunk re-read per projection. */\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: '\u804A\u5929\u7FFB\u8BD1',\n\n masterTitle: '\u7FFB\u8BD1\u603B\u5F00\u5173',\n enableTranslation: '\u542F\u7528\u7FFB\u8BD1',\n masterDesc:\n '\u81EA\u52A8\u5C06\u5F53\u524D\u4F1A\u8BDD\u4E2D\u5DE5\u5177\u8C03\u7528\u6807\u9898\uFF08\u5982 {example}\uFF09\u7FFB\u8BD1\u4E3A\u4E2D\u6587\uFF0C\u70B9\u51FB\u8BD1\u6587\u53EF\u539F\u5730\u5207\u6362\u539F\u6587/\u8BD1\u6587\u3002\u601D\u8003\u6B63\u6587\u9ED8\u8BA4\u4E0D\u7FFB\u8BD1\uFF0C\u7531 AI \u5361\u7247\u91CC\u7684\u300C\u663E\u793A\u601D\u8003\u94FE\u7FFB\u8BD1\u6309\u94AE\u300D\u63A7\u5236\uFF1A\u6253\u5F00\u540E\u5C55\u5F00\u7684\u601D\u8003\u5361\u53F3\u4FA7\u51FA\u73B0\u6309\u94AE\uFF0C\u70B9\u51FB\u624D\u7FFB\u8BD1\u3002\u601D\u8003\u5361\u6298\u53E0\u8D77\u6765\u7684\u90A3\u4E00\u884C\u6458\u8981\u6C38\u4E0D\u7FFB\u8BD1\u3002\u4EC5\u4F5C\u7528\u4E8E\u5F53\u524D\u67E5\u770B\u7684\u4F1A\u8BDD\uFF0C\u56DE\u7B54\u6B63\u6587\u6C38\u4E0D\u7FFB\u8BD1\u3002',\n\n aiTitle: 'AI \u7FFB\u8BD1\uFF08OpenAI \u517C\u5BB9\u534F\u8BAE\uFF09',\n enableAi: '\u542F\u7528 AI \u7FFB\u8BD1\u901A\u9053',\n badgeConfigured: '\u5DF2\u914D\u7F6E',\n badgeUnconfigured: '\u672A\u914D\u7F6E',\n aiFallbackNote: '\u672A\u914D\u7F6E\u65F6 AI \u901A\u9053\u81EA\u52A8\u8DF3\u8FC7\uFF0C\u7531 Bing \u515C\u5E95\u3002',\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\uFF08AI \u901A\u9053\u5C06\u4E0D\u53EF\u7528\uFF0CBing \u515C\u5E95\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 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 thinkEnableTitle: '\u663E\u793A\u601D\u8003\u94FE\u7FFB\u8BD1\u6309\u94AE',\n thinkEnableDesc:\n '\u5728\u5C55\u5F00\u7684\u601D\u8003\u5361\u7684\u300CThink\u300D\u53F3\u4FA7\u663E\u793A\u4E00\u4E2A\u6309\u94AE\uFF0C\u70B9\u51FB\u624D\u7FFB\u8BD1\u8FD9\u6761\u601D\u8003\u6B63\u6587\uFF08\u4E0D\u4F1A\u81EA\u52A8\u7FFB\u8BD1\uFF09\uFF1B\u6298\u53E0\u8D77\u6765\u7684\u5361\u7247\u6CA1\u6709\u6309\u94AE\uFF0C\u5C55\u5F00\u540E\u6309\u94AE\u624D\u51FA\u73B0\u3002\u7FFB\u8BD1\u4E2D\u6309\u94AE\u8F6C\u5708\uFF0C\u7FFB\u597D\u540E\u5B9E\u5FC3\u9AD8\u4EAE\uFF0C\u518D\u70B9\u4E00\u4E0B\u6574\u6761\u5207\u56DE\u539F\u6587\uFF08\u6B63\u6587\u91CC\u7684\u70B9\u51FB\u4E0D\u5207\u6362\uFF09\u3002\u4EE3\u7801\u5757\u4E0E\u6298\u53E0\u6458\u8981\u4E0D\u52A8\uFF0CBing \u901A\u9053\u4E0D\u53C2\u4E0E\u3002\u5173\u95ED\u6B64\u5F00\u5173\u4F1A\u64A4\u6389\u6309\u94AE\u5E76\u8FD8\u539F\u5DF2\u7FFB\u8BD1\u5185\u5BB9\u3002',\n thinkTimeoutTitle: '\u601D\u8003\u94FE\u7FFB\u8BD1\u8D85\u65F6',\n thinkTimeoutDesc: '\u5355\u4F4D\u6BEB\u79D2\uFF0C\u8303\u56F4 500-900000\u3002\u6574\u5757\u601D\u8003\u53EF\u80FD\u8981\u51E0\u5206\u949F\uFF0C\u9ED8\u8BA4 600000\u3002',\n\n bingTitle: 'Bing \u7F51\u9875\u7FFB\u8BD1\uFF08\u514D Key \u515C\u5E95\uFF09',\n enableBing: '\u542F\u7528 Bing \u7FFB\u8BD1\u901A\u9053',\n bingDesc:\n '\u5185\u7F6E\u514D Key \u7FFB\u8BD1\u901A\u9053\uFF0C\u65E0\u9700\u4EFB\u4F55\u914D\u7F6E\uFF0C\u53EA\u7FFB\u8BD1\u5DE5\u5177\u8C03\u7528\u6807\u9898\u3002AI \u672A\u914D\u7F6E\u6216\u8BF7\u6C42\u5931\u8D25\u65F6\u81EA\u52A8\u515C\u5E95\uFF1B\u601D\u8003\u94FE\u7FFB\u8BD1\u6C38\u8FDC\u4E0D\u8D70\u8FD9\u6761\u901A\u9053\u3002AI \u4E0E Bing \u540C\u65F6\u5173\u95ED\u5219\u5DE5\u5177\u8C03\u7528\u6807\u9898\u4E5F\u4E0D\u7FFB\u8BD1\u3002',\n\n behaviorTitle: '\u901A\u9053\u884C\u4E3A',\n behavior1: 'AI \u5F00\u542F\u4E14\u5DF2\u914D\u7F6E \u2192 AI \u4F18\u5148\u7FFB\u8BD1\uFF0C\u5931\u8D25\u81EA\u52A8\u964D\u7EA7 Bing',\n behavior2: 'AI \u5F00\u542F\u4F46\u672A\u914D\u7F6E + Bing \u5F00\u542F \u2192 \u7531 Bing \u7FFB\u8BD1',\n behavior3: 'AI \u5F00\u542F\u4F46\u672A\u914D\u7F6E + Bing \u5173\u95ED \u2192 \u4E0D\u7FFB\u8BD1',\n behavior4: 'AI \u5173\u95ED + Bing \u5F00\u542F \u2192 \u76F4\u63A5\u4F7F\u7528 Bing',\n behavior5: 'AI \u5173\u95ED + Bing \u5173\u95ED \u2192 \u4E0D\u7FFB\u8BD1',\n\n concurrencyTitle: '\u6700\u5927\u7FFB\u8BD1\u5E76\u53D1\u6570',\n concurrencyDesc: '\u63A7\u5236\u89C6\u53E3\u6EDA\u52A8\u4E0E\u591A\u5DE5\u5177\u5361\u7247\u65F6\u7684\u6700\u5927\u5E76\u884C\u8BF7\u6C42\u6570\uFF08\u8303\u56F4 1-100\uFF0C\u63A8\u8350 3\uFF09\u3002',\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: 'Chat translate',\n\n masterTitle: 'Translation master switch',\n enableTranslation: 'Enable translation',\n masterDesc:\n 'Translates tool-call titles in the current session (e.g. {example}) into Chinese; click a translation to switch back to the original in place. Think text stays untranslated by default and is controlled by \u201Cshow think-chain translate button\u201D in the AI card: once on, a button appears at the right of every expanded think card and translates only when clicked. The collapsed summary line of a think card is never translated. Applies to the session you are viewing only; reply text is never translated.',\n\n aiTitle: 'AI translation (OpenAI-compatible)',\n enableAi: 'Enable the AI translation channel',\n badgeConfigured: 'Configured',\n badgeUnconfigured: 'Not configured',\n aiFallbackNote: 'When unconfigured the AI channel is skipped and Bing takes over.',\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 (the AI channel becomes unavailable and Bing takes over)',\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 test: 'Test the AI channel',\n testing: 'Testing\u2026',\n testOk: 'Connected, {latency}ms latency',\n testFail: 'Failed: {error}',\n\n thinkEnableTitle: 'Show the think-chain translate button',\n thinkEnableDesc:\n 'Shows a button to the right of \u201CThink\u201D on every expanded think card; it translates that think text only when clicked, never automatically. Collapsed cards carry no button; it appears once the card is expanded. While translating, the button spins; once done it highlights solid, and clicking it again switches the whole block back to the original (clicks inside the text do not switch). Code blocks and the collapsed summary stay untouched, and the Bing channel is not involved. Turning this off removes the button and restores already translated content.',\n thinkTimeoutTitle: 'Think-chain translation timeout',\n thinkTimeoutDesc: 'In milliseconds, range 500-900000. A whole think block can take minutes; default 600000.',\n\n bingTitle: 'Bing web translation (key-free fallback)',\n enableBing: 'Enable the Bing translation channel',\n bingDesc:\n 'A built-in key-free channel that needs no configuration and translates tool-call titles only. It takes over automatically when AI is unconfigured or a request fails; think-chain translation never uses this channel. With both AI and Bing off, tool-call titles are not translated either.',\n\n behaviorTitle: 'Channel behavior',\n behavior1: 'AI on and configured \u2192 AI translates first, falling back to Bing on failure',\n behavior2: 'AI on but unconfigured + Bing on \u2192 Bing translates',\n behavior3: 'AI on but unconfigured + Bing off \u2192 nothing is translated',\n behavior4: 'AI off + Bing on \u2192 Bing is used directly',\n behavior5: 'AI off + Bing off \u2192 nothing is translated',\n\n concurrencyTitle: 'Max translation concurrency',\n concurrencyDesc: 'Caps parallel requests while the viewport scrolls across multiple tool cards (range 1-100, recommended 3).',\n};\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n 'settings.chatTranslate': ChatTranslateLocaleKey;\n }\n}"],
5
+ "mappings": "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",
6
+ "names": ["entry", "item", "THINK_CARD_SELECTOR", "FLOW_ITEM_SELECTOR", "FLOW_SELECTOR", "import_react", "name"]
7
7
  }