{"_id":"@angineer/docs-ui","_rev":"5-20c622ea85841cb9f6ba305de1c32313","name":"@angineer/docs-ui","dist-tags":{"latest":"0.2.4"},"versions":{"0.2.0":{"name":"@angineer/docs-ui","version":"0.2.0","license":"MIT","_id":"@angineer/docs-ui@0.2.0","maintainers":[{"name":"0mao0","email":"orangen@aliyun.com"}],"dist":{"shasum":"c7f2ffbc1ada7f65d8f20652203a11ebf5d7f156","tarball":"https://registry.npmjs.org/@angineer/docs-ui/-/docs-ui-0.2.0.tgz","fileCount":55,"integrity":"sha512-uuqD3JsUFJnmo0A+61ya8wz0e7s5NsHvkoroC4+QNi4o8LsxDjiGH6+TXS2QdXmg0AkLtqVO18r/6lalmZtTqA==","signatures":[{"sig":"MEYCIQC9P85mGltLN+wUYwXmCPocesAX2oxamJ4wTWVSVHLSuwIhALE8z9/9zmS5AMnHpT98ISpWuLjp8Y9xQs5V92gnOm6G","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":520210},"main":"./src/index.ts","type":"module","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"},"./style":"./src/styles/index.less","./vite-pdf-wasm":{"types":"./vite-pdf-wasm.d.mts","import":"./vite-pdf-wasm.mjs"}},"scripts":{"test":"pnpm dlx tsx --test test/citationTarget.test.ts test/display-roots.test.mjs test/highlightGroup.test.ts test/pageRange.test.ts test/pdfHoverText.test.ts test/pdfSearchTarget.test.ts","typecheck":"vue-tsc --noEmit"},"_npmUser":{"name":"0mao0","email":"orangen@aliyun.com"},"_npmVersion":"11.4.2","description":"Vue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。","directories":{},"_nodeVersion":"24.3.0","dependencies":{"xlsx":"^0.18.5","katex":"0.18.4","pdfjs-dist":"6.2.108","docx-preview":"^0.4.0","@angineer/smartree":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"3.5.41","less":"^4.2.0","vite":"^5.2.0","pinia":"^2.1.7","vue-tsc":"^2.0.6","typescript":"~5.4.0","ant-design-vue":"4.2.6","@vitejs/plugin-vue":"^5.0.4","@ant-design/icons-vue":"7.0.1"},"peerDependencies":{"vue":"3.5.41","ant-design-vue":"4.2.6","@ant-design/icons-vue":"7.0.1"},"_npmOperationalInternal":{"tmp":"tmp/docs-ui_0.2.0_1787835492096_0.08042423056632875","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@angineer/docs-ui","version":"0.2.1","license":"MIT","_id":"@angineer/docs-ui@0.2.1","maintainers":[{"name":"0mao0","email":"orangen@aliyun.com"}],"dist":{"shasum":"0534209d6f8e471ba65437b80c560336b9d25e4c","tarball":"https://registry.npmjs.org/@angineer/docs-ui/-/docs-ui-0.2.1.tgz","fileCount":55,"integrity":"sha512-qIcd+UxQ1k86CbwCbipUv3c7OJUv2TB6AzWKOFjGFBUyj6gqBvKkjDUFshoyZRggWN8CldsxPnjaZAYs6f105g==","signatures":[{"sig":"MEQCIAsgg1ktvS1hyMCCtuD1OlS1u15VemgY/1paPuNI2FgCAiB45ZxpBJnvo+vVLSfRKXYJ06ET9XQiwsflwUEL+x9tRA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":520213},"main":"./src/index.ts","type":"module","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"},"./style":"./src/styles/index.less","./vite-pdf-wasm":{"types":"./vite-pdf-wasm.d.mts","import":"./vite-pdf-wasm.mjs"}},"scripts":{"test":"pnpm dlx tsx --test test/citationTarget.test.ts test/display-roots.test.mjs test/highlightGroup.test.ts test/pageRange.test.ts test/pdfHoverText.test.ts test/pdfSearchTarget.test.ts","typecheck":"vue-tsc --noEmit"},"_npmUser":{"name":"0mao0","email":"orangen@aliyun.com"},"_npmVersion":"11.4.2","description":"Vue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。","directories":{},"_nodeVersion":"24.3.0","dependencies":{"xlsx":"^0.18.5","katex":"0.18.4","pdfjs-dist":"6.2.108","docx-preview":"^0.4.0","@angineer/smartree":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"3.5.41","less":"^4.2.0","vite":"^5.2.0","pinia":"^2.1.7","vue-tsc":"^2.0.6","typescript":"~5.4.0","ant-design-vue":"4.2.6","@vitejs/plugin-vue":"^5.0.4","@ant-design/icons-vue":"7.0.1"},"peerDependencies":{"vue":"3.5.41","ant-design-vue":"4.2.6","@ant-design/icons-vue":"7.0.1"},"_npmOperationalInternal":{"tmp":"tmp/docs-ui_0.2.1_1787835981571_0.7396237552861067","host":"s3://npm-registry-packages-npm-production"}},"0.2.2":{"name":"@angineer/docs-ui","version":"0.2.2","license":"MIT","_id":"@angineer/docs-ui@0.2.2","maintainers":[{"name":"0mao0","email":"orangen@aliyun.com"}],"homepage":"https://github.com/0mao0/angineer-docs-ui#readme","bugs":{"url":"https://github.com/0mao0/angineer-docs-ui/issues"},"dist":{"shasum":"0afafa3ab7ef00ae8b74bc455b528fe5c87a2e8b","tarball":"https://registry.npmjs.org/@angineer/docs-ui/-/docs-ui-0.2.2.tgz","fileCount":55,"integrity":"sha512-4CQBEJK9MM7ZUiL2apyCo9GUIind7V9N/YI1NlWWuUXXewjvd5gcUQM+O2k1B+nL9/Eu+SAasUPZYSgqcn0w2w==","signatures":[{"sig":"MEUCICOTk8vnjOQT5UcB/YVEBT5DfrYTJB9FwgPWVTlxL3jgAiEAhOGT49DKqnqTLTX4pEbVFPn6/Ls0GFDH3nOuCZPSYSs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@angineer%2fdocs-ui@0.2.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":510824},"main":"./src/index.ts","type":"module","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"},"./style":"./src/styles/index.less","./vite-pdf-wasm":{"types":"./vite-pdf-wasm.d.mts","import":"./vite-pdf-wasm.mjs"}},"gitHead":"da869ac0bf822dda5223cddff137b778e223dbe6","scripts":{"test":"pnpm dlx tsx --test test/citationTarget.test.ts test/display-roots.test.mjs test/highlightGroup.test.ts test/pageRange.test.ts test/pdfHoverText.test.ts test/pdfSearchTarget.test.ts","typecheck":"vue-tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:ac143a76-fd59-47ad-a872-a32cb1238f17"}},"repository":{"url":"git+https://github.com/0mao0/angineer-docs-ui.git","type":"git"},"_npmVersion":"11.19.0","description":"Vue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。","directories":{},"_nodeVersion":"24.20.0","dependencies":{"xlsx":"^0.18.5","katex":"0.18.4","pdfjs-dist":"6.2.108","docx-preview":"^0.4.0","@angineer/smartree":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"3.5.41","less":"^4.2.0","vite":"^5.2.0","pinia":"^2.1.7","vue-tsc":"^2.0.6","typescript":"~5.4.0","ant-design-vue":"4.2.6","@vitejs/plugin-vue":"^5.0.4","@ant-design/icons-vue":"7.0.1"},"peerDependencies":{"vue":"3.5.41","ant-design-vue":"4.2.6","@ant-design/icons-vue":"7.0.1"},"_npmOperationalInternal":{"tmp":"tmp/docs-ui_0.2.2_1789044112625_0.8400352971478986","host":"s3://npm-registry-packages-npm-production"}},"0.2.3":{"name":"@angineer/docs-ui","version":"0.2.3","license":"MIT","_id":"@angineer/docs-ui@0.2.3","maintainers":[{"name":"0mao0","email":"orangen@aliyun.com"}],"homepage":"https://github.com/0mao0/angineer-docs-ui#readme","bugs":{"url":"https://github.com/0mao0/angineer-docs-ui/issues"},"dist":{"shasum":"43c9fbfb2d5b837d90bd01c8dae856df94dfe06b","tarball":"https://registry.npmjs.org/@angineer/docs-ui/-/docs-ui-0.2.3.tgz","fileCount":55,"integrity":"sha512-GAdLERfwL/YOFzBHor7pCQf3kzf3aQIqFG3sp80a4uf4wWYAzOlq9zzC7e4QnnGOB7rc+wCez5Kz+kQekO1gag==","signatures":[{"sig":"MEUCIQDmjScr6bOaGm9EpMOdOmwVFvB0n5cUFHjwChQwv/PP5QIgGvHD5kYBKLV4orwh2XxTN7aYO7G4XUAzwgrkfoXcwEk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQC8pkHYd13kdJi33G+N4A7/OcrCJnSVchq/HWiqVCj+sQIgbCVpiW40aqvBxMPv3/sxtmoMifiQedjsQTDjq5uEVm4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@angineer%2fdocs-ui@0.2.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":513464},"main":"./src/index.ts","type":"module","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"},"./style":"./src/styles/index.less","./vite-pdf-wasm":{"types":"./vite-pdf-wasm.d.mts","import":"./vite-pdf-wasm.mjs"},"./composables/useKnowledgeParse":{"types":"./src/composables/useKnowledgeParse.ts","import":"./src/composables/useKnowledgeParse.ts"}},"gitHead":"61a2445bf31c63875d0f4102a4d9bffbcfdf561a","scripts":{"test":"pnpm dlx tsx --test test/citationTarget.test.ts test/display-roots.test.mjs test/highlightGroup.test.ts test/pageRange.test.ts test/pdfHoverText.test.ts test/pdfSearchTarget.test.ts","typecheck":"vue-tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:ac143a76-fd59-47ad-a872-a32cb1238f17"}},"repository":{"url":"git+https://github.com/0mao0/angineer-docs-ui.git","type":"git"},"_npmVersion":"11.19.0","description":"Vue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。","directories":{},"sideEffects":["**/*.css","**/*.less","**/styles/**"],"_nodeVersion":"24.20.0","dependencies":{"xlsx":"^0.18.5","katex":"0.18.4","pdfjs-dist":"6.2.108","docx-preview":"^0.4.0","@angineer/smartree":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"3.5.41","less":"^4.2.0","vite":"^5.2.0","pinia":"^2.1.7","vue-tsc":"^2.0.6","typescript":"~5.4.0","ant-design-vue":"4.2.6","@vitejs/plugin-vue":"^5.0.4","@ant-design/icons-vue":"7.0.1"},"peerDependencies":{"vue":"3.5.41","ant-design-vue":"4.2.6","@ant-design/icons-vue":"7.0.1"},"_npmOperationalInternal":{"tmp":"tmp/docs-ui_0.2.3_1789197283671_0.3540595576590173","host":"s3://npm-registry-packages-npm-production"}},"0.2.4":{"_id":"@angineer/docs-ui@0.2.4","bugs":{"url":"https://github.com/0mao0/angineer-docs-ui/issues"},"dist":{"shasum":"667bac9c7be6efba7b019e63942d85f95e21bb6c","tarball":"https://registry.npmjs.org/@angineer/docs-ui/-/docs-ui-0.2.4.tgz","fileCount":55,"integrity":"sha512-nYtMperzBqaOPJkm21UQbPx/qWU6x0lltirSXsKP14F5M7VeuGZwB7mp3Tqm0qH+kpvGUTrx+gwAFL6iFvgtlQ==","signatures":[{"sig":"MEQCIHw4CG2h/IIC6TSqMeiElcy0hbylMhM3g6DLAfA6Dgk4AiA3lc+8jwITc2OBv7rMFWj8beqNEMVgaLld2eEu6kUnVw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDtfba9kU1KAc2iwIo8pMJkyc26YUJ7rCavQWYHPW/QhAIhALDYxPb+kHnpj26AilT6OBqauZL3nbTgCDrg/L+iofGu"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@angineer%2fdocs-ui@0.2.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":513696},"main":"./src/index.ts","name":"@angineer/docs-ui","type":"module","types":"./src/index.ts","exports":{".":{"types":"./src/index.ts","import":"./src/index.ts"},"./style":"./src/styles/index.less","./vite-pdf-wasm":{"types":"./vite-pdf-wasm.d.mts","import":"./vite-pdf-wasm.mjs"},"./composables/useKnowledgeParse":{"types":"./src/composables/useKnowledgeParse.ts","import":"./src/composables/useKnowledgeParse.ts"}},"gitHead":"b682a6b21364f36b7a89cf1f7095f61d93610809","license":"MIT","scripts":{"test":"pnpm dlx tsx --test test/citationTarget.test.ts test/display-roots.test.mjs test/highlightGroup.test.ts test/pageRange.test.ts test/pdfHoverText.test.ts test/pdfSearchTarget.test.ts","typecheck":"vue-tsc --noEmit"},"version":"0.2.4","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:ac143a76-fd59-47ad-a872-a32cb1238f17"}},"homepage":"https://github.com/0mao0/angineer-docs-ui#readme","repository":{"url":"git+https://github.com/0mao0/angineer-docs-ui.git","type":"git"},"_npmVersion":"11.19.0","description":"Vue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。","directories":{},"maintainers":[{"name":"0mao0","email":"orangen@aliyun.com"}],"sideEffects":["**/*.css","**/*.less","**/styles/**"],"_nodeVersion":"24.20.0","dependencies":{"xlsx":"https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz","katex":"0.18.4","pdfjs-dist":"6.2.108","docx-preview":"^0.4.0","@angineer/smartree":"^0.1.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vue":"3.5.41","less":"^4.2.0","vite":"^5.2.0","pinia":"^2.1.7","vue-tsc":"^2.0.6","typescript":"~5.4.0","ant-design-vue":"4.2.6","@vitejs/plugin-vue":"^5.0.4","@ant-design/icons-vue":"7.0.1"},"peerDependencies":{"vue":"3.5.41","ant-design-vue":"4.2.6","@ant-design/icons-vue":"7.0.1"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/docs-ui_0.2.4_1789307383201_0.8742458779465454"}}},"time":{"created":"2026-08-27T12:58:11.866Z","modified":"2026-09-13T13:49:43.729Z","0.2.0":"2026-08-27T12:58:12.250Z","0.2.1":"2026-08-27T13:06:21.737Z","0.2.2":"2026-09-10T12:41:52.765Z","0.2.3":"2026-09-12T07:14:43.781Z","0.2.4":"2026-09-13T13:49:43.309Z"},"bugs":{"url":"https://github.com/0mao0/angineer-docs-ui/issues"},"license":"MIT","homepage":"https://github.com/0mao0/angineer-docs-ui#readme","repository":{"url":"git+https://github.com/0mao0/angineer-docs-ui.git","type":"git"},"description":"Vue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。","maintainers":[{"name":"0mao0","email":"orangen@aliyun.com"}],"readme":"# angineer-docs-ui\n\nVue 3 知识库文档工作台组件库：PDF 查看器、结构化索引（索引树/索引图）、知识树、对话与检索 composables。\n\n## 安装\n\n已发布到 npm registry：\n\n```bash\npnpm add @angineer/docs-ui\n```\n\n> docs-ui 依赖 `@angineer/smartree`，从 npm 安装时作为传递依赖自动解析（^0.1.1）；从 GitHub 钉 tag 安装时需一并安装 `@angineer/smartree`（`github:0mao0/angineer-smartree-ui#v0.1.2`）。\n\n**环境要求**：peer 依赖 `vue@3.5.41`、`ant-design-vue@4.2.6`、`@ant-design/icons-vue@7.0.1`。包为源码分发（无构建产物），宿主需用 Vite + `@vitejs/plugin-vue` 与 less 编译。\n\n## 使用\n\n```ts\nimport { PDF_Viewer, PDFParsedWorkspace, KnowledgeTree } from '@angineer/docs-ui'\nimport '@angineer/docs-ui/style' // 可选：.docs-component 基础排版（组件颜色均走 CSS 变量，不导入也可渲染）\n```\n\n## 导出\n\n- 组件：`SmartTree`（桥接 `@angineer/smartree`）、`KnowledgeTree`、`PDFParsedWorkspace`、`PDFParsedViewerCombo`、`PDF_Viewer`、`Preview_Markdown`\n- Composables：`useKnowledgeTree`、`useDocBlocksGraph`、`useParsedPdfViewer`、`useParsedPdfIndexTree`、`useWorkspaceLinkage`、`useWorkspacePreview`、`useKnowledgeCitation`、`useKnowledgeParse`、`useKnowledgeStructuredIndex`、`useRefAnchor`、`useResourceAdapter` 等\n- 子路径导出：`@angineer/docs-ui/style`\n\n## 主题定制\n\n组件颜色经 CSS 变量解析且使用处自带 fallback：查看器私有 `--dp-*` → 包级 `--docs-*` → 内置默认值的三层链（PDF_Viewer 另有 `--dp-*-override` 层供单实例覆盖）；同时引用宿主语义变量（`--text-secondary`、`--primary-color` 等）跟随 dark/light 主题，宿主在 `:root` / `[data-theme=\"dark\"]` 定义同名变量即可。\n\n## 测试\n\n```bash\npnpm --filter @angineer/docs-ui test        # 31 个用例（tsx + node:test，含 Vite ssr 集成用例）\npnpm --filter @angineer/docs-ui typecheck   # vue-tsc\n```\n\n## 仓库说明\n\n本仓库为独立发布仓，代码唯一真相源在 [AnGIneer](https://github.com/0mao0/AnGIneer) monorepo 的 `packages/docs-ui`，经 `scripts/sync-standalone.ps1` 同步；版本以 git tag（vx.y.z）与 npm registry（`@angineer/docs-ui`）同步发布。变更历史见 [CHANGELOG.md](./CHANGELOG.md)。\n\n---\n\n## 内部架构说明（A/B/C/D/U 分层）\n\n本文档用于 `packages/docs-ui` 的后续改造与日常阅读，目标是统一分层认知、调用边界和文件职责。\n\n## 1. 分层定义\n\n### A 层：可视层（Components + Styles）\n- 只负责 UI 展示、交互触发、事件透传。\n- 不直接实现重业务流程，不直接持久化数据。\n- 允许存在局部 UI 状态（弹窗开关、hover、tab）。\n\n### B 层：逻辑层（Composables）\n- 负责业务流程编排、状态联动、副作用控制。\n- 管理运行时状态（`ref/computed/watch`）。\n- 调用 D 层接口并把结果映射为 A 层可消费状态。\n\n### C 层：契约层（Types）\n- 负责类型契约、结构约束、跨层参数统一。\n- 不承载运行时状态，不包含副作用。\n\n### D 层：数据层（API / Storage / Data Source）\n- 前端视角：HTTP API（`/api/*`）是 D 层入口。\n- 后端视角：文件系统/JSON/DB/第三方解析服务是 D 层落点。\n\n### U 层：工具层（Utils，跨层纯函数）\n- 纯函数、无状态、无组件上下文依赖。\n- 用于格式化、映射、解析、渲染转换等可复用逻辑。\n\n---\n\n## 2. 当前目录映射（docs-ui）\n\n```text\nsrc/\n├─ components/                 # A 层\n│  ├─ common/\n│  │  ├─ PDFParsedWorkspace.vue\n│  │  ├─ PDFParsedViewerCombo.vue\n│  │  ├─ PDF_Viewer.vue\n│  │  ├─ Preview_Markdown.vue\n│  │  ├─ Preview_IndexTree.vue\n│  │  ├─ Preview_IndexGraph.vue\n│  │  ├─ SmartTree.vue\n│  │  ├─ SOPTree.vue\n│  └─ index.ts\n├─ composables/                # B 层\n│  ├─ useWorkspacePreview.ts\n│  ├─ useWorkspaceLinkage.ts\n│  ├─ useWorkspaceIngest.ts\n│  ├─ useParsedPdfViewer.ts\n│  ├─ useDocBlocksGraph.ts\n│  ├─ useKnowledgeTree.ts\n│  ├─ useKnowledgeChat.ts\n│  ├─ useSopTree.ts\n│  ├─ useDocument.ts\n│  ├─ useQuery.ts\n│  ├─ useRefAnchor.ts\n│  ├─ useResourceAdapter.ts\n│  └─ index.ts\n├─ types/                      # C 层\n│  ├─ knowledge.ts\n│  ├─ tree.ts\n│  ├─ document.ts\n│  ├─ reference.ts\n│  ├─ resource.ts\n│  ├─ table.ts\n│  └─ index.ts\n├─ utils/                      # U 层\n│  ├─ knowledge.ts\n│  └─ index.ts\n├─ styles/                     # A 层样式\n│  ├─ index.less\n│  └─ variables.less\n└─ index.ts                    # 包出口\n```\n\n---\n\n## 3. 层间调用规则（硬约束）\n\n1. A -> B：允许（组件调用 composable）。\n2. A -> C：允许（组件 import type）。\n3. A -> U：允许（纯函数辅助展示）。\n4. B -> C：允许（逻辑层依赖类型契约）。\n5. B -> U：允许（业务流程中使用纯函数）。\n6. B -> D：允许（fetch/API 调用）。\n7. C 不依赖 A/B/D（只做契约）。\n8. U 不依赖 A/B 的运行时上下文（只做纯函数）。\n9. 禁止 A 直接写 D（组件不直接做复杂数据写入流程，统一由 B 收口）。\n\n---\n\n## 4. 关键调用关系（主流程）\n\n## 4.1 Workspace 解析/预览链路\n\n1. A 层入口：`PDFParsedWorkspace.vue`\n2. 该组件组合调用：\n   - `useWorkspacePreview`（预览状态与滚动同步）\n   - `useWorkspaceIngest`（入库状态与策略）\n   - `useWorkspaceLinkage`（左右联动高亮）\n3. 右侧编排组件 `PDFParsedViewerCombo.vue` 再调用 `useParsedPdfViewer` 管理索引展示、树图状态、分页与视口。\n4. 左侧 `PDF_Viewer.vue` / 右侧 `Preview_*` 子组件通过事件进行双向联动。\n\n## 4.2 结构化索引重建（入库）链路\n\n1. 用户在 A 层触发入库（`trigger-ingest`）。\n2. `PDFParsedWorkspace.vue` 调用 B 层 `useWorkspaceIngest.triggerIngest`。\n3. 校验通过后触发 `emit('rebuild-structured', strategy)` 给宿主应用。\n4. 宿主应用调用 D 层接口（后端 `POST /parse/structured-index`）执行重建。\n5. 返回结果后回填到 `structuredItems / structuredStats / graphData`，A 层自动重渲染。\n\n## 4.3 左右联动链路（高亮/定位）\n\n1. 左侧选中高亮：`PDF_Viewer` 触发 `select-highlight`。\n2. A 层容器交给 `useWorkspaceLinkage.onSelectHighlightFromLeft`。\n3. B 层解析 `graphData + structuredItems + markdownContent` 映射关系，得到：\n   - `activeLeftHighlightId`\n   - `activeLinkedLineRange`\n4. 右侧 `Preview_Markdown` 根据行号滚动定位，必要时更新 `pdfPage`。\n\n## 4.4 SmartTree 交互链路\n\n1. `SmartTree.vue` 只发业务事件（如 `add-file`、`add-folder`、`drop`、`search`）。\n2. 上层容器接收事件后进入 B 层逻辑（如 `useKnowledgeTree` 或宿主逻辑）。\n3. B 层调用 D 层 API / 存储后更新状态，A 层回显。\n\n---\n\n## 5. A 层文件清单（组件职责 + 事件契约）\n\n### 5.1 组件总览\n\n| 文件 | 角色 | 对外事件（核心） | 主要下游依赖 |\n|---|---|---|---|\n| `PDFParsedWorkspace.vue` | 文档解析工作区编排器（左侧 PDF + 可折叠解析面板） | `parse` `save-content` `change-strategy` `query-structured` `rebuild-structured` `update:sidePanelOpen` | `useWorkspacePreview` `useWorkspaceIngest` `useWorkspaceLinkage` |\n| `PDFParsedViewerCombo.vue` | 右侧 pane 编排器 | `update:activeTab` `update:editableContent` `save-markdown` `cancel-markdown` `strategy-change` `trigger-ingest` `content-scroll` `hover-item` `select-item` `toggle-tree-expand` `toggle-graph-expand` `update-graph-viewport` `select-line` | `useParsedPdfViewer` + `Preview_*` |\n| `PDF_Viewer.vue` | 左侧文件预览/高亮层，支持通用可折叠侧栏插槽 | `download` `text-scroll` `hover-highlight` `select-highlight` `update:sidePanelOpen` | 由 Workspace 注入数据 |\n| `Preview_Markdown.vue` | Markdown 编辑/预览 | `update:editableContent` `select-line` | 被 `PDFParsedViewerCombo` 使用 |\n| `Preview_IndexTree.vue` | 索引树视图 | `toggle` `select` `edit` `toggle-check` `context-action` | 虚拟滚动树容器（原 `IndexTreeFlatRow` 行渲染已并入） |\n| `Preview_IndexGraph.vue` | 索引图视图 | `toggle` `select` `update-viewport` | 内聚图算法+交互（已合并原 `DocBlocksGraph`） |\n| `SmartTree.vue` | 通用资源树组件 | `select` `rename` `add-folder` `add-file` `delete` `view` `drop` `search` `file-drop` `drop-invalid` `drop-root` | `types/tree` |\n| `SOPTree.vue` | 经验库语义树组件 | `select` `view` `search` | `useSopTree` |\n\n### 5.2 Workspace 核心编排函数（`DocumentParsedWorkspace.vue`）\n\n- `saveMarkdown`：把右侧编辑内容回传为 `save-content`。\n- `cancelMarkdownEdit`：回滚编辑缓存并复位 dirty 状态。\n- `triggerIngest`：调用 `requestIngest` 后触发 `rebuild-structured`。\n- `onStrategyChange`：同步策略并发出 `change-strategy`。\n- `openIndexFromIngestModal`：切换到索引 tab 并关闭 modal。\n\n---\n\n## 6. B 层文件清单（函数/状态与职责）\n\n### 6.1 composables 导出总表\n\n| 文件 | 导出函数 | 核心职责 |\n|---|---|---|\n| `useWorkspacePreview.ts` | `useWorkspacePreview` | 文件类型识别、预览 URL、左右滚动同步、PDF 页码映射 |\n| `useWorkspaceIngest.ts` | `useWorkspaceIngest` | 入库弹窗状态、策略切换、入库可用性校验、统计聚合 |\n| `useWorkspaceLinkage.ts` | `useWorkspaceLinkage` | structured item 与 graph node 映射、左/右选择联动、行号与页码同步 |\n| `useParsedPdfViewer.ts` | `useParsedPdfViewer` | 右侧索引列表/树/图的统一状态、分页、展开、视口持久 |\n| `useDocBlocksGraph.ts` | `useDocBlocksGraph` | 图结构状态容器（nodeMap/childrenMap/roots）与视图模式控制 |\n| `useKnowledgeTree.ts` | `useKnowledgeTree` | 知识树运行时状态与节点操作（选中、状态更新、上传任务） |\n| `useKnowledgeChat.ts` | `useKnowledgeChat` | 对话流式发送、上下文窗口管理、终止/清空、token 估算 |\n| `useSopTree.ts` | `useSopTree` | 经验库树默认数据、查找与选中状态管理 |\n| `useDocument.ts` | `useDocument` | 文档/库查询、块查询 |\n| `useQuery.ts` | `useQuery` | 表查询与语义查询、历史记录 |\n| `useRefAnchor.ts` | `useRefAnchor` | 引用上下文管理、引用拉取、复制 |\n| `useResourceAdapter.ts` | `createResourceNodeFrom*` `createOpenResourcePayload` | 资源结构适配与工作台打开 payload 构建 |\n\n### 6.2 关键函数清单（按文件）\n\n#### `useWorkspacePreview.ts`\n- 状态：`textContent` `leftScrollPercent` `rightScrollPercent` `pdfPage`\n- 计算：`isPdf` `isOffice` `isImage` `isText` `fileUrl` `pdfViewerUrl` `inferredPdfPageCount`\n- 方法：`onRightPaneScrollPercent` `onLeftTextScrollPercent` `downloadFile` `resetPreviewState`\n\n#### `useWorkspaceIngest.ts`\n- 状态：`ingestModalVisible` `selectedStrategy`\n- 计算：`ingestProgressStatus` `ingestStageText` `strategyValue` `structuredTotal` `indexSummaryStats` `ingestButtonText` `canIngest`\n- 方法：`onStrategyChange` `triggerIngest` `openIngestModal` `closeIngestModal`\n\n#### `useWorkspaceLinkage.ts`\n- 状态：`activeLinkedItemId` `highlightLinkEnabled`\n- 计算：`linkedHighlights` `showHighlightToggle` `activeLeftHighlightId` `activeLinkedLineRange`\n- 方法：`onHoverLinkedItem` `onSelectHighlightFromLeft` `onSelectItemFromRight` `onSelectLineFromRight` `toggleHighlightLink` `resetLinkageState`\n\n#### `useParsedPdfViewer.ts`\n- 状态：`indexCurrentPage` `expandedNodeIds` `expandedGraphNodeIds` `graphViewportState`\n- 计算：`flatIndexItems` `nodeMap` `childrenMap` `roots` `activeNodeIdForGraphTree`\n- 方法：`onTabChange` `onIndexPageChange` `onTreeToggle` `onGraphToggle` `onNodeSelect` `onViewportUpdate` `expandAncestors` `setViewMode`\n\n#### `useDocBlocksGraph.ts`\n- 状态：`graph` `activeNodeId` `expandedNodeIds` `expandedGraphNodeIds` `viewMode` `viewportState`\n- 方法：`loadGraph` `setActiveNode` `toggleExpand` `toggleGraphExpand` `expandAll` `collapseAll` `setViewMode` `setViewportState`\n- 查询：`getAncestors` `expandAncestors` `getNodeLevel` `getNodeText` `getChildren`\n\n#### `useKnowledgeTree.ts`\n- 状态：`treeData` `selectedKeys` `expandedKeys` `selectedNode` `uploadTasks`\n- 方法：`buildTree` `findNode` `getChildCount` `getFolderName` `selectNode` `updateNodeStatus` `addUploadTask` `updateUploadTask` `setTreeData`\n\n#### `useKnowledgeChat.ts`\n- 状态：`messages` `inputText` `loading` `currentStreamContent`\n- 方法：`sendMessage` `stopGeneration` `clearMessages` `getContextTokens` `getContextRounds` `generateMessageId`\n- 类型：`KnowledgeChatMessage` `KnowledgeChatRequest` `KnowledgeChatStreamEvent` `KnowledgeChatContextConfig`\n\n#### `useDocument.ts`\n- 状态：`document` `loading` `error`\n- 方法：`fetchDocument` `fetchLibrary` `searchDocuments` `getBlock` `getBlocksByType`\n\n#### `useQuery.ts`\n- 状态：`result` `loading` `error` `history`\n- 方法：`queryTable` `semanticQuery` `clearHistory`\n\n#### `useRefAnchor.ts`\n- 状态：`contextItems` `loading`\n- 方法：`addContext` `removeContext` `clearContext` `fetchReference` `copyReference` `getContextText`\n\n#### `useResourceAdapter.ts`\n- 适配函数：`createResourceNodeFromKnowledge` `createResourceNodeFromProject` `createResourceNodeFromSop`\n- 跳转函数：`createOpenResourcePayload`\n\n---\n\n## 7. C 层契约清单（types）\n\n### 7.1 `types/knowledge.ts`\n- `KnowledgeStrategy = 'doc_blocks_graph_v1'`\n- `IngestStatus = 'idle' | 'processing' | 'completed' | 'failed'`\n- `ParseTaskInfo`\n- `StructuredIndexItem`\n- `StructuredStats`\n- `DocumentStorageManifest`\n- `DocBlockNode`\n- `DocBlockEdge`\n- `DocBlocksGraph`\n- `DocBlocksGraphState`\n\n### 7.2 `types/tree.ts`\n- `SmartTreeNode`\n- `TreeNodeAction`\n\n### 7.3 其他契约\n- `types/document.ts`：`Document` `DocumentBlock` `BlockLocation` `DocumentMetadata` `Library`\n- `types/resource.ts`：`ResourceType` `WorkbenchTabType` `ResourceNode` `OpenResourcePayload`\n- `types/reference.ts`：`Reference` `ReferenceLocation` `ReferenceContext` `CrossReference`\n- `types/table.ts`：`TableData` `TableType` `TableDimension` `ValueRange` `TableOutput` `TableRule` `TableQueryResult`\n\n---\n\n## 8. U 层工具清单（utils）\n\n### `utils/knowledge.ts`\n- `getFileExtension(path)`：扩展名提取\n- `getPreviewFileType(node)`：预览类型映射（pdf/word/markdown/image/text/file）\n- `mapParseStageText(stage, parseError)`：解析阶段文案映射\n- `mapNodeStatusText(status)`：节点状态文案映射\n- `renderMarkdownToHtml(content, sourceFilePath)`：Markdown + KaTeX 渲染为 HTML，并处理资源路径\n\n工具层定位：可在 A/B 任意层调用，但保持“纯函数、无运行时状态”。\n\n---\n\n## 9. D 层数据源映射（当前项目实际）\n\n> `docs-ui` 前端通过 `/api/*` 与后端交互；数据持久化和解析产物在 `services/docs-core` 完成。\n\n### 9.1 前端调用入口（B -> D）\n- `useDocument.ts`：`/api/docs/documents/*` `/api/docs/libraries/*` `/api/docs/search`\n- `useQuery.ts`：`/api/docs/tables/*/query` `/api/docs/query/semantic`\n- `useRefAnchor.ts`：`/api/docs/references/*`\n- `useKnowledgeChat.ts`：`/api/chat`\n- 预览文件：`/api/files?path=...`\n\n### 9.2 后端解析/存储关键点（D 层落盘）\n- 解析任务接口：`services/docs-api/docs_routes.py`\n  - `POST /parse`\n  - `GET /parse/{task_id}`\n  - `POST /parse/structured-index`\n- 存储实现：`services/docs-core/src/docs_core/storage/document_storage.py`\n  - 根目录：`data/knowledge_base`\n  - 文档目录：`libraries/{library_id}/documents/{doc_id}/`\n  - 关键文件：`parsed/content.md` `parsed/doc_blocks_graph.jsonl` `parsed/doc_blocks_graph_meta.json` `parsed/mineru_blocks.json` `parsed/raw/*` `edited/current.md`\n\n---\n\n## 10. 包出口与使用建议\n\n### 10.1 出口文件\n- `src/index.ts`：聚合导出 components/composables/types/utils\n- `src/components/index.ts`：导出 `SmartTree` `KnowledgeTree` `PDFParsedWorkspace` `PDFParsedViewerCombo` `PDF_Viewer` `Preview_Markdown`\n- `PDF_Viewer` 支持 `side-panel` 插槽与 `sidePanelOpen` / `showSidePanelToggle` / `sidePanelWidth` props；`PDFParsedWorkspace` 支持 `sidePanelOpen` / `sidePanelDefaultOpen` / `sidePanelWidth` / `defaultParsedTab` props，可控制解析面板展开/收起、宽度与默认 Tab。\n- `PDFParsedWorkspace` 支持 `fileUrlResolver` prop，可自定义文件地址拼接规则；不传时默认使用 `/api/files?path=...`。\n- `src/composables/index.ts`：导出全部 composables 及关键类型\n- `src/types/index.ts`：导出所有契约类型\n- `src/utils/index.ts`：导出工具函数\n\n### 10.2 新功能放置规范（改造准则）\n- 新 UI：优先放 A 层 `components/common/*`，样式仅放本组件所需。\n- 新业务流程：优先放 B 层 composable，组件只消费状态与方法。\n- 新数据结构：先定义到 C 层 type/interface，再落逻辑实现。\n- 新纯算法/映射：放 U 层 utils，保持无副作用。\n- 新 API/存储交互：统一由 B 层发起，不从 A 层直接发起复杂流程。\n\n---\n\n## 11. 改造检查清单（提交前自检）\n\n1. 是否把流程逻辑从组件下沉到 composable。\n2. 是否存在组件直接处理复杂 API/落盘流程（应避免）。\n3. 类型是否先在 C 层定义，再被 A/B 复用。\n4. utils 是否仍保持纯函数，不引入状态/副作用。\n5. 事件命名是否保持语义一致（`select-*`、`toggle-*`、`update:*`）。\n6. Workspace 是否继续作为编排器，而非逻辑堆积点。\n\n---\n\n## 12. 逐文件外部依赖与调用矩阵（细化）\n\n> 这一节聚焦“文件 -> 引用谁 -> 对外暴露什么 -> 在分层里承担什么角色”。\n\n### 12.1 A 层组件矩阵（common）\n\n| 文件 | 直接引用的外部 TS/函数 | 对外事件（emit） | 分层结论 |\n|---|---|---|---|\n| `DocumentParsedWorkspace.vue` | `useWorkspacePreview` `useWorkspaceIngest` `useWorkspaceLinkage` `mapParseStageText` `renderMarkdownToHtml` `KnowledgeTreeNode` `KnowledgeStrategy/IngestStatus/Structured*` | `parse` `save-content` `change-strategy` `query-structured` `rebuild-structured` | A 层编排器，强依赖 B/C/U，合理 |\n| `ParsedPDF_Viewer.vue` | `useParsedPdfViewer` `PreviewMode` `KnowledgeStrategy` `StructuredIndexItem` `DocBlocksGraph` | `update:activeTab` `update:editableContent` `save-markdown` `cancel-markdown` `strategy-change` `trigger-ingest` `content-scroll` `hover-item` `select-item` `toggle-tree-expand` `toggle-graph-expand` `update-graph-viewport` `select-line` | A 层二级编排器，依赖 B/C |\n| `PDF_Viewer.vue` | `KnowledgeTreeNode`（来自 `types/tree`） | `download` `text-scroll` `hover-highlight` `select-highlight` | A 层渲染组件，轻依赖 C |\n| `Preview_Markdown.vue` | 无项目外部 TS（仅 Vue） | `update:editableContent` `select-line` | A 层纯展示 |\n| `Preview_IndexTree.vue` | `DocBlockNode`（C） | `toggle` `select` `edit` `toggle-check` `context-action` | A 层虚拟滚动树组件（内聚交互） |\n| `Preview_IndexGraph.vue` | `DocBlockNode`（C） | `toggle` `select` `update-viewport` | A 层图组件（内聚交互） |\n| `SmartTree.vue` | `SmartTreeNode`（C） `TreeProps` `useTheme` | `select` `rename` `add-folder` `add-file` `delete` `view` `drop` `search` `file-drop` `drop-invalid` `drop-root` | A 层通用组件，不直接做 D 层写入 |\n| `SOPTree.vue` | `SOPTreeNode`（C） + `SmartTree` | `select` `view` `search` | A 层经验库树语义包装 |\n\n### 12.2 B 层 composables 矩阵\n\n| 文件 | 主要输入 | 主要输出（状态/函数） | 是否直接触达 D 层 |\n|---|---|---|---|\n| `useWorkspacePreview.ts` | `node/filePath/graphData` | `isPdf/isOffice/...` `fileUrl` `pdfViewerUrl` `onRightPaneScrollPercent` `onLeftTextScrollPercent` | 否（仅读取文件 URL） |\n| `useWorkspaceIngest.ts` | `node/content/isContentDirty/ingestStatus/structuredStats` | `ingestModalVisible` `strategyValue` `canIngest` `triggerIngest` | 否 |\n| `useWorkspaceLinkage.ts` | `graphData/structuredItems/markdownContent/isPdf/pdfPage/rightScrollPercent` | `linkedHighlights` `activeLinkedLineRange` `onSelect*` | 否 |\n| `useParsedPdfViewer.ts` | 右侧 pane props + emit | `flatIndexItems/nodeMap/childrenMap` `onTabChange` `onTreeToggle` | 否 |\n| `useDocBlocksGraph.ts` | `DocBlocksGraph` | `nodeMap/childrenMap/roots` `toggleExpand` `setViewMode` | 否 |\n| `useKnowledgeTree.ts` | 运行时树数据 | `treeData/selectedNode` `buildTree/selectNode/updateNodeStatus` | 否 |\n| `useKnowledgeChat.ts` | 对话参数与上下文配置 | `messages/loading/sendMessage/stopGeneration` | 是（`/api/chat`）**已废弃，请使用 ui-kit 的 useAIChat** |\n| `useSopTree.ts` | 经验库树数据 | `treeData/selectedNode/resetToDefaultTree/findNode` | 否 |\n| `useDocument.ts` | `docId/libraryId/query` | `document/loading/error` `fetchDocument` `searchDocuments` | 是（`/api/docs/*`） |\n| `useQuery.ts` | `tableId/params/question` | `result/history/queryTable/semanticQuery` | 是（`/api/docs/*`） |\n| `useRefAnchor.ts` | `blockId/reference` | `contextItems` `fetchReference` `copyReference` | 是（`/api/docs/references/*`） |\n| `useResourceAdapter.ts` | `SmartTreeNode/ProjectItem/SopItem` | `ResourceNode/OpenResourcePayload` | 否（纯适配） |\n\n### 12.3 C 层契约文件矩阵\n\n| 文件 | 核心契约 |\n|---|---|\n| `types/knowledge.ts` | `KnowledgeStrategy` `IngestStatus` `StructuredIndexItem` `StructuredStats` `DocBlockNode` `DocBlocksGraph` |\n| `types/tree.ts` | `SmartTreeNode` `TreeNodeAction` |\n| `types/document.ts` | `Document` `DocumentBlock` `Library` |\n| `types/reference.ts` | `Reference` `ReferenceContext` |\n| `types/resource.ts` | `ResourceNode` `OpenResourcePayload` |\n| `types/table.ts` | `TableQueryResult` 等表结构契约 |\n\n### 12.4 U 层工具函数矩阵\n\n| 文件 | 函数 | 常见调用方 |\n|---|---|---|\n| `utils/knowledge.ts` | `getFileExtension` `getPreviewFileType` | A/B 层文件类型识别 |\n| `utils/knowledge.ts` | `mapParseStageText` `mapNodeStatusText` | A 层状态文案展示 |\n| `utils/knowledge.ts` | `renderMarkdownToHtml` | `DocumentParsedWorkspace.vue` |\n\n---\n\n## 13. 架构演进建议（按触发条件）\n\n### 13.1 何时引入 Pinia Store（非强制）\n\n满足任意两条时建议引入：\n\n1. 同一份状态被 3 个以上页面/区域复用。\n2. A 层出现明显 props drilling（多级透传）。\n3. 同一逻辑在多个 composable 重复实现。\n4. 需要跨页面缓存、乐观更新、统一回滚。\n5. 需要统一收口 websocket/polling 增量数据。\n\n### 13.2 推荐的最小 Store 切入点\n\n可先落一个 `knowledgeWorkspaceStore`，仅收口这三类状态：\n\n1. `activeDocId` / `activeTab`\n2. `structuredItems` / `graphData`\n3. `ingestStatus` / `ingestStage`\n\n其余状态继续留在 composables，避免一次性大迁移。\n\n### 13.3 保持稳定的边界规则\n\n1. Store 持有“跨组件共享状态”，不持有重展示逻辑。\n2. Composable 负责“流程与副作用”，可读写 store。\n3. Utils 继续保持纯函数，不依赖 store。\n4. Types 始终先于实现定义，作为改造护栏。\n\n---\n\n## 14. 架构关系图（3 张）\n\n### 14.1 分层总览图（A/B/C/D/U）\n\n```mermaid\nflowchart TD\n  U[U 工具层\\nutils/*.ts\\n纯函数]\n  C[C 契约层\\ntypes/*.ts\\n类型约束]\n  A[A 可视层\\ncomponents + styles]\n  B[B 逻辑层\\ncomposables/*.ts]\n  D[D 数据层\\nAPI/Storage/DB/JSON]\n\n  A -->|调用 composable| B\n  A -->|import type| C\n  A -->|调用纯函数| U\n  B -->|import type| C\n  B -->|调用纯函数| U\n  B -->|fetch/api| D\n```\n\n### 14.2 Workspace 运行时调用图（你当前主链路）\n\n```mermaid\nsequenceDiagram\n  participant User as 用户\n  participant W as DocumentParsedWorkspace(A)\n  participant P as useWorkspacePreview(B)\n  participant I as useWorkspaceIngest(B)\n  participant L as useWorkspaceLinkage(B)\n  participant R as ParsedPDF_Viewer(A)\n  participant API as /api + docs-core(D)\n\n  User->>W: 点击操作(入库/切换/选中)\n  W->>P: 预览与滚动同步\n  W->>I: triggerIngest / onStrategyChange\n  W->>L: onSelectHighlightFromLeft / onSelectItemFromRight\n  W->>R: 传递 activeTab/content/highlight\n  R-->>W: 事件回传(update:activeTab, select-line...)\n  W->>API: rebuild-structured / parse / query\n  API-->>W: structuredItems/graphData/status\n  W-->>User: UI 更新与联动高亮\n```\n\n### 14.3 组件与 composables 依赖图（核心模块）\n\n```mermaid\ngraph LR\n  W[DocumentParsedWorkspace.vue]\n  R[DocumentParsedViewerSpace.vue]\n  V[PDF_Viewer.vue]\n  IT[Preview_IndexTree.vue]\n  IG[Preview_IndexGraph.vue]\n  PM[Preview_Markdown.vue]\n  ST[SmartTree.vue]\n\n  WP[useWorkspacePreview.ts]\n  WI[useWorkspaceIngest.ts]\n  WL[useWorkspaceLinkage.ts]\n  PV[useParsedPdfViewer.ts]\n  KT[useKnowledgeTree.ts]\n  UA[useKnowledgeChat.ts]\n\n  TY[types/*.ts]\n  UT[utils/knowledge.ts]\n  AP[/api/* + docs-core/]\n\n  W --> WP\n  W --> WI\n  W --> WL\n  W --> R\n  W --> V\n  W --> TY\n  W --> UT\n\n  R --> PV\n  R --> IT\n  R --> IG\n  R --> PM\n  R --> TY\n\n  ST --> TY\n  AC --> UA\n  PV --> TY\n  WI --> TY\n  WL --> TY\n\n  UA --> AP\n  WP --> AP\n```\n\n---\n\n## 15. 第一批改造清单（逐文件可执行）\n\n### 15.1 已落地（低风险去冗余）\n\n1. `useWorkspacePreview.ts`：删除本地扩展名/预览类型重复判断，统一复用 `utils/knowledge.ts` 的 `getFileExtension` 与 `getPreviewFileType`。\n2. `Preview_IndexList.vue`：列表模式已下线，组件已删除；结构导航统一由树形视图承担。\n3. `useParsedPdfViewer.ts` + `useDocBlocksGraph.ts`：抽出 `buildDocBlocksGraphIndex`，统一构建 `nodeMap/childrenMap/parentMap/roots`，删除重复图索引逻辑。\n4. `useKnowledgeTree.ts` + `types/tree.ts`：把知识树契约集中到 `types/tree.ts`，统一为 `KnowledgeTreeNode`（不再保留 `TreeNode` 别名），移除 composable 内重复类型声明。\n5. `DocumentParsedWorkspace.vue` + `ParsedPDF_Viewer.vue` + `useParsedPdfViewer.ts`：统一 `PreviewMode` 与图视口事件载荷类型，收敛 `update-graph-viewport` 的重复对象定义。\n6. `DocumentParsedWorkspace.vue` + `types/knowledge.ts`：提炼 `DocumentParsedWorkspaceEventMap`，统一 `parse/query/rebuild/save-content/change-strategy` 事件契约。\n7. `Preview_IndexTree.vue` + `Preview_IndexGraph.vue` + `types/knowledge.ts`：提炼 `PreviewIndexInteractionEventMap`，统一 `toggle/select/update-viewport` 事件命名与载荷结构。\n8. `Preview_IndexTree.vue` + `Preview_IndexGraph.vue`：图树事件源统一为 `toggle/select/update-viewport`，移除命名适配分支。\n9. 结果：预览链路、图链路与知识树链路共享统一契约入口，减少结构漂移风险。\n\n### 15.2 下一批建议（按优先级）\n\n1. 暂无（第一批改造清单已完成）。\n\n### 15.3 每项改造验收标准\n\n1. 单项改造后 `pnpm run lint` 必须通过。\n2. 不改变现有交互行为（入库、联动高亮、页签切换）。\n3. 同类能力只保留一个实现位置（函数/映射/类型）。\n4. A 层不新增对 D 层的直接依赖。\n\n## 16. PDF 高亮悬停显示原文（v0.1.2）\n\n`PDF_Viewer` 内置\"证据高亮框悬停显示原文\"浮框：悬停 `.pdf-highlight-box` 时展示该\nbbox 区域原文，命中段加粗。取字复用组件内部按页缓存的 pdfjs `getTextContent()`，\n不二次加载 PDF；仅高亮锚点触发，普通文本区域 hover 无浮框。\n\n新增 props（均向后兼容，默认开启）：\n\n| prop | 类型 | 默认 | 说明 |\n| :--- | :--- | :--- | :--- |\n| `highlightHoverText` | `boolean` | `true` | 是否启用高亮悬停原文 |\n| `highlightHoverFontSize` | `number` | `13` | 浮框字号 |\n| `highlightHoverMaxWidth` | `number` | `340` | 浮框最大宽度（超出滚动） |\n| `highlightHoverMaxHeight` | `number` | `180` | 浮框最大高度（超出滚动） |\n\n`highlights` 条目新增可选字段：\n\n- `text?`: bbox 原文全文，优先展示（未提供时按 bbox 内部提取）；\n- `matchText?`: 雷同段，浮框内加粗高亮（未提供时尝试全文定位，或不做加粗）；\n- `excerpt?`: 兼容别名，`matchText` 未提供时当 `matchText` 用。\n\n浮框行为：`pointer-events: none` 不拦截交互；靠近边缘自动翻转防溢出；翻页、缩放、\n搜索、定位时自动隐藏；亮色/暗色主题均适配。\n\n## 17. PDF 子集渲染与加载事件（v0.1.3）\n\n`pdf-page-range?: number[]`：绝对页码数组，仅渲染这些页（虚拟滚动、工具栏、高亮、\n悬停取字均按子集工作）；undefined 或空数组 = 整篇；越界页码自动过滤，全部越界时\n退化为整篇。页码输入框与 `current-pdf-page` 均使用绝对页码，子集外输入就近吸附\n（距离相等取较小页）。\n\n`pdf-loaded` 事件：文档加载完成后触发，payload 为当前源 URL，可用于加载完成后\n重放定位等场景。\n\n## 18. 流式加载降级与日志清理（v0.1.4）\n\n`PDF_Viewer` 优先以 Range/流式方式加载 PDF；当流式加载因中断（`Loading aborted`）\n失败时，自动降级为全量数组缓冲加载，并且同会话内记住该源，后续直接走全量加载，\n不再重复失败的流式尝试（`Loading aborted` 属预期竞态，不再输出警告日志）。\n文档切换/卸载的调试日志同步移除。\n","readmeFilename":"README.md"}