@yumiai/chat-widget 0.2.1 → 0.2.2

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 (100) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/ExcelCore-EAXQMKZT.js +10 -0
  4. package/dist/{ExcelViewer-3YLLYYIQ.js → ExcelViewer-OGWM52ZT.js} +2 -3
  5. package/dist/{ExcelViewer-3YLLYYIQ.js.map → ExcelViewer-OGWM52ZT.js.map} +1 -1
  6. package/dist/{GerberViewerA2UI-X5FWAD5M.js → GerberViewerA2UI-AORHOCF6.js} +2 -3
  7. package/dist/{GerberViewerA2UI-X5FWAD5M.js.map → GerberViewerA2UI-AORHOCF6.js.map} +1 -1
  8. package/dist/{GraphStatsLegend-D5bPeXB_.d.cts → GraphStatsLegend-CV0Y3-cP.d.cts} +39 -0
  9. package/dist/{GraphStatsLegend-D5bPeXB_.d.ts → GraphStatsLegend-CV0Y3-cP.d.ts} +39 -0
  10. package/dist/JsonRenderStandalone-L5ROJ6XP.js +17 -0
  11. package/dist/{KicadViewer-GV6ZC4AQ.js → KicadViewer-H6YY6WVC.js} +3 -4
  12. package/dist/{KicadViewer-GV6ZC4AQ.js.map → KicadViewer-H6YY6WVC.js.map} +1 -1
  13. package/dist/KicadViewerCore-XVJE2FTQ.js +10 -0
  14. package/dist/{PdfViewer-CHPDRK46.js → PdfViewer-GKVDDNCZ.js} +9 -10
  15. package/dist/{PdfViewer-CHPDRK46.js.map → PdfViewer-GKVDDNCZ.js.map} +1 -1
  16. package/dist/{PdfViewerCore-HJPEHSRA.js → PdfViewerCore-SUVXHXYO.js} +24 -11
  17. package/dist/PdfViewerCore-SUVXHXYO.js.map +1 -0
  18. package/dist/PowerPointCore-IZ4G6HEQ.js +10 -0
  19. package/dist/{PowerPointViewer-LQTO6UCU.js → PowerPointViewer-5R2KSWWJ.js} +2 -3
  20. package/dist/{PowerPointViewer-LQTO6UCU.js.map → PowerPointViewer-5R2KSWWJ.js.map} +1 -1
  21. package/dist/{StepViewerCore-7W3L3R4E.js → StepViewerCore-RORWXIRU.js} +2 -3
  22. package/dist/{StepViewerCore-7W3L3R4E.js.map → StepViewerCore-RORWXIRU.js.map} +1 -1
  23. package/dist/{ThreeViewerCore-N3QJD5QI.js → ThreeViewerCore-GTUZKD5V.js} +2 -3
  24. package/dist/{ThreeViewerCore-N3QJD5QI.js.map → ThreeViewerCore-GTUZKD5V.js.map} +1 -1
  25. package/dist/WordCore-QFG5HTYD.js +10 -0
  26. package/dist/{WordViewer-ZHCQMHOH.js → WordViewer-7XUQFS4A.js} +2 -3
  27. package/dist/{WordViewer-ZHCQMHOH.js.map → WordViewer-7XUQFS4A.js.map} +1 -1
  28. package/dist/{chunk-QLVPIM6R.js → chunk-3CNQONCV.js} +79 -1
  29. package/dist/chunk-3CNQONCV.js.map +1 -0
  30. package/dist/{chunk-2UC7YLVX.js → chunk-3LOSHCSH.js} +3 -3
  31. package/dist/chunk-5VUKEGFC.js +108 -0
  32. package/dist/chunk-5VUKEGFC.js.map +1 -0
  33. package/dist/chunk-AO4YUJQT.js +479 -0
  34. package/dist/chunk-AO4YUJQT.js.map +1 -0
  35. package/dist/{chunk-KQV7IKET.js → chunk-ASV4TISB.js} +2 -2
  36. package/dist/{chunk-CFKGNAJM.js → chunk-AT6VLLOO.js} +15 -15
  37. package/dist/{chunk-56WRZM3R.js → chunk-EYWNUJVZ.js} +3 -3
  38. package/dist/{chunk-K4KGNVL5.js → chunk-EZ46FGQ6.js} +3 -3
  39. package/dist/{chunk-GYXTSY22.js → chunk-KXPR6SRW.js} +4 -4
  40. package/dist/{chunk-7S67DOHQ.js → chunk-MTN6SUUQ.js} +2 -2
  41. package/dist/chunk-Q5PLT3AI.js +29 -0
  42. package/dist/chunk-Q5PLT3AI.js.map +1 -0
  43. package/dist/{chunk-PZXSASDY.js → chunk-R5LHMOAC.js} +3 -3
  44. package/dist/chunk-X67V7257.js +238 -0
  45. package/dist/chunk-X67V7257.js.map +1 -0
  46. package/dist/citationContext-CILHTO2Z.js +25 -0
  47. package/dist/components/JsonRender/standalone.cjs +56 -9753
  48. package/dist/components/JsonRender/standalone.cjs.map +1 -1
  49. package/dist/components/JsonRender/standalone.js +10 -11
  50. package/dist/components/JsonRender/standalone.js.map +1 -1
  51. package/dist/{gerber-2d-entry-OQ4SQRBY.js → gerber-2d-entry-HEFXQGBK.js} +5 -9
  52. package/dist/{gerber-2d-entry-OQ4SQRBY.js.map → gerber-2d-entry-HEFXQGBK.js.map} +1 -1
  53. package/dist/{gerber-3d-entry-DEHDBOO2.js → gerber-3d-entry-KVTONA37.js} +5 -9
  54. package/dist/{gerber-3d-entry-DEHDBOO2.js.map → gerber-3d-entry-KVTONA37.js.map} +1 -1
  55. package/dist/{gerber-simulation-entry-EBDX72XE.js → gerber-simulation-entry-GZ62QX5H.js} +5 -9
  56. package/dist/{gerber-simulation-entry-EBDX72XE.js.map → gerber-simulation-entry-GZ62QX5H.js.map} +1 -1
  57. package/dist/index.cjs +6690 -13472
  58. package/dist/index.cjs.map +1 -1
  59. package/dist/index.css +776 -4
  60. package/dist/index.css.map +1 -1
  61. package/dist/index.d.cts +979 -26
  62. package/dist/index.d.ts +979 -26
  63. package/dist/index.js +4350 -1859
  64. package/dist/index.js.map +1 -1
  65. package/dist/provenance/index.cjs +78 -0
  66. package/dist/provenance/index.cjs.map +1 -1
  67. package/dist/provenance/index.d.cts +2 -2
  68. package/dist/provenance/index.d.ts +2 -2
  69. package/dist/provenance/index.js +2 -3
  70. package/dist/{resolveToArrayBuffer-AQIDZHSQ.js → resolveToArrayBuffer-PVSVIAII.js} +1 -3
  71. package/dist/{resolveToArrayBuffer-AQIDZHSQ.js.map → resolveToArrayBuffer-PVSVIAII.js.map} +1 -1
  72. package/dist/sseAdapter-LFXYGYC4.js +8 -0
  73. package/dist/sseAdapter-LFXYGYC4.js.map +1 -0
  74. package/package.json +2 -1
  75. package/dist/ExcelCore-DJOIVQMI.js +0 -11
  76. package/dist/JsonRenderStandalone-EIZM62JU.js +0 -18
  77. package/dist/KicadViewerCore-U7BWZHKJ.js +0 -11
  78. package/dist/PdfViewerCore-HJPEHSRA.js.map +0 -1
  79. package/dist/PowerPointCore-FPDR2BL4.js +0 -11
  80. package/dist/WordCore-JKSXK2XD.js +0 -11
  81. package/dist/chunk-7A4FY6FK.js +0 -10226
  82. package/dist/chunk-7A4FY6FK.js.map +0 -1
  83. package/dist/chunk-7D4SUZUM.js +0 -38
  84. package/dist/chunk-QLVPIM6R.js.map +0 -1
  85. package/dist/chunk-VXJWGLZ7.js +0 -21
  86. package/dist/chunk-VXJWGLZ7.js.map +0 -1
  87. /package/dist/{ExcelCore-DJOIVQMI.js.map → ExcelCore-EAXQMKZT.js.map} +0 -0
  88. /package/dist/{JsonRenderStandalone-EIZM62JU.js.map → JsonRenderStandalone-L5ROJ6XP.js.map} +0 -0
  89. /package/dist/{KicadViewerCore-U7BWZHKJ.js.map → KicadViewerCore-XVJE2FTQ.js.map} +0 -0
  90. /package/dist/{PowerPointCore-FPDR2BL4.js.map → PowerPointCore-IZ4G6HEQ.js.map} +0 -0
  91. /package/dist/{WordCore-JKSXK2XD.js.map → WordCore-QFG5HTYD.js.map} +0 -0
  92. /package/dist/{chunk-2UC7YLVX.js.map → chunk-3LOSHCSH.js.map} +0 -0
  93. /package/dist/{chunk-KQV7IKET.js.map → chunk-ASV4TISB.js.map} +0 -0
  94. /package/dist/{chunk-CFKGNAJM.js.map → chunk-AT6VLLOO.js.map} +0 -0
  95. /package/dist/{chunk-56WRZM3R.js.map → chunk-EYWNUJVZ.js.map} +0 -0
  96. /package/dist/{chunk-K4KGNVL5.js.map → chunk-EZ46FGQ6.js.map} +0 -0
  97. /package/dist/{chunk-GYXTSY22.js.map → chunk-KXPR6SRW.js.map} +0 -0
  98. /package/dist/{chunk-7S67DOHQ.js.map → chunk-MTN6SUUQ.js.map} +0 -0
  99. /package/dist/{chunk-PZXSASDY.js.map → chunk-R5LHMOAC.js.map} +0 -0
  100. /package/dist/{chunk-7D4SUZUM.js.map → citationContext-CILHTO2Z.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/jetPaveGerberViewer/src/viewer-src/common/archiveExtractor.js","../src/components/jetPaveGerberViewer/src/viewer-src/common/gerber-common.js"],"sourcesContent":["import { createExtractorFromData } from 'node-unrar-js'\r\n// 使用 Vite 的静态资源导入方式加载 WASM 文件\r\nimport wasmUrl from 'node-unrar-js/esm/js/unrar.wasm?url'\r\n\r\n/**\r\n * 判断文件是否是 Gerber 文件\r\n * @param {string} fileName - 文件名\r\n * @returns {boolean} - 是否是 Gerber 文件\r\n */\r\nfunction isGerberFile(fileName) {\r\n // 提取文件名(去掉路径,只保留文件名部分)\r\n // 处理 Windows 路径 (\\) 和 Unix 路径 (/)\r\n const fileNameOnly = fileName.split(/[/\\\\]/).pop()\r\n const fileNameUpper = fileNameOnly.toUpperCase()\r\n \r\n // 🚫 优先排除已知非Gerber文件(报告、日志、嵌套压缩包等)\r\n if (fileNameUpper.includes('STATUS REPORT') ||\r\n fileNameUpper.includes('NETLIST') ||\r\n fileNameUpper.endsWith('.REP') ||\r\n fileNameUpper.endsWith('.LOG') ||\r\n fileNameUpper.endsWith('.CSV') ||\r\n fileNameUpper.endsWith('.DRR') ||\r\n fileNameUpper.endsWith('.EXTREP') ||\r\n fileNameUpper.endsWith('.RUL') ||\r\n fileNameUpper.endsWith('.LDP') ||\r\n fileNameUpper.endsWith('.APR') ||\r\n fileNameUpper.endsWith('.APR_LIB') ||\r\n fileNameUpper.endsWith('.TGZ') ||\r\n fileNameUpper.endsWith('.ZIP') ||\r\n fileNameUpper.endsWith('.RAR')) {\r\n return false\r\n }\r\n \r\n // 先检查文件名后缀模式(优先级最高)\r\n // 如果文件名包含这些特殊后缀,即使扩展名不在列表中,也认为是有效文件\r\n const specialSuffixes = ['_SST', '_PMT', '_SMT', '_TOP', '_SMB', '_PMB', '_SSB', 'OUTLINE']\r\n for (const suffix of specialSuffixes) {\r\n if (fileNameUpper.endsWith(suffix) || fileNameUpper.includes(suffix + '.')) {\r\n return true\r\n }\r\n }\r\n \r\n // 检查 _INT+数字 模式(如 _INT1, _INT2 等)\r\n if (/.*_INT\\d+$/i.test(fileNameUpper) || /.*_INT\\d+\\./i.test(fileNameUpper)) {\r\n return true\r\n }\r\n \r\n const validExtensions = [\r\n '.gtl', '.gbl', '.gts', '.gbs', '.gto', '.gbo', \r\n '.gtp', '.gbp', '.gm1', '.drl', '.gbr', '.gko', \r\n '.gdl', '.gdd', '.gm', '.gd1', '.gg1', '.gpb', '.gpt', '.txt',\r\n '.art', '.d', '.rou', '.pho',\r\n // CAM350 等软件导出的扩展名\r\n '.bot', '.top', '.sob', '.sot', '.dri', '.ser',\r\n '.sst', '.ssb', '.smt', '.smb', '.pmt', '.pmb'\r\n ]\r\n const fileExtension = '.' + fileNameOnly.split('.').pop().toLowerCase()\r\n return validExtensions.includes(fileExtension) || \r\n /^\\.g\\d+$/i.test(fileExtension) || \r\n /^\\.gm\\d+$/i.test(fileExtension) ||\r\n /^\\.gp\\d+$/i.test(fileExtension) || // 支持 .gp1, .gp2, .gp3 等\r\n /^\\.tx\\d+$/i.test(fileExtension) || // 支持 .tx1, .tx2, .tx3 等\r\n /^\\.int\\d+$/i.test(fileExtension) // 支持 .int1, .int2, .int3 等\r\n}\r\n\r\n/**\r\n * 加载 WASM 二进制文件(用于 node-unrar-js)\r\n * @returns {Promise<ArrayBuffer|null>} - WASM 二进制数据,如果不需要则返回 null\r\n */\r\nasync function loadWasmBinary() {\r\n try {\r\n // node-unrar-js 在浏览器端需要 WASM 文件\r\n // 使用 Vite 的静态资源导入方式加载 WASM 文件\r\n console.log('尝试加载 WASM 文件,URL:', wasmUrl)\r\n \r\n const response = await fetch(wasmUrl)\r\n if (!response.ok) {\r\n throw new Error(`无法加载 WASM 文件: ${response.status} ${response.statusText}`)\r\n }\r\n \r\n const wasmBinary = await response.arrayBuffer()\r\n console.log('成功加载 WASM 文件,大小:', wasmBinary.byteLength, 'bytes')\r\n return wasmBinary\r\n } catch (error) {\r\n console.error('无法加载 WASM 文件:', error)\r\n // 如果无法加载 WASM,尝试让 node-unrar-js 自动处理\r\n // 某些情况下,node-unrar-js 可能会自动从 CDN 或其他位置加载 WASM\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * 提取RAR文件\r\n * @param {File} rarFile - RAR 文件\r\n * @returns {Promise<File[]>} - 解压后的 Gerber 文件列表\r\n */\r\nexport async function extractRar(rarFile) {\r\n console.log('[性能] extractRar 开始,文件大小:', (rarFile.size / 1024).toFixed(2), 'KB')\r\n const startTime = performance.now()\r\n const gerberFiles = []\r\n \r\n try {\r\n console.log('[性能] 读取 RAR 文件为 ArrayBuffer...')\r\n const startTimeBuffer = performance.now()\r\n const arrayBuffer = await rarFile.arrayBuffer()\r\n const data = new Uint8Array(arrayBuffer)\r\n const endTimeBuffer = performance.now()\r\n console.log(`[性能] ArrayBuffer 读取完成,耗时: ${(endTimeBuffer - startTimeBuffer).toFixed(2)}ms`)\r\n \r\n // 加载 WASM 文件(浏览器环境需要)\r\n console.log('[性能] 开始加载 WASM 文件...')\r\n const startTimeWasm = performance.now()\r\n const wasmBinary = await loadWasmBinary()\r\n const endTimeWasm = performance.now()\r\n console.log(`[性能] WASM 文件加载完成,耗时: ${(endTimeWasm - startTimeWasm).toFixed(2)}ms`)\r\n \r\n // node-unrar-js 的正确用法:createExtractorFromData 是异步的\r\n // 在浏览器环境中,需要传递 wasmBinary\r\n // 如果 wasmBinary 为 null,尝试不使用 wasmBinary(让库自动处理)\r\n let extractor\r\n if (wasmBinary) {\r\n console.log('使用提供的 WASM 二进制文件')\r\n extractor = await createExtractorFromData({ data, wasmBinary })\r\n } else {\r\n console.log('尝试不使用 wasmBinary,让 node-unrar-js 自动处理')\r\n // 尝试不使用 wasmBinary,看看库是否能自动加载\r\n try {\r\n extractor = await createExtractorFromData({ data })\r\n } catch (noWasmError) {\r\n console.error('不使用 wasmBinary 失败:', noWasmError)\r\n throw new Error('无法创建 RAR 提取器。WASM 文件加载失败,请检查 node-unrar-js 的配置。')\r\n }\r\n }\r\n \r\n // 获取文件列表:返回 { arcHeader, fileHeaders }\r\n // fileHeaders 是一个 Generator<FileHeader>(迭代器)\r\n const list = extractor.getFileList()\r\n \r\n if (!list || !list.fileHeaders) {\r\n console.error('RAR文件列表格式错误:', list)\r\n throw new Error('无法读取RAR文件列表')\r\n }\r\n \r\n // 遍历 fileHeaders 迭代器,收集所有 Gerber 文件名\r\n const gerberFileNames = []\r\n const pcbDocFiles = [] // 记录 PCB 源文件\r\n console.log('开始扫描 RAR 内容...')\r\n let fileCount = 0\r\n \r\n for (const fileHeader of list.fileHeaders) {\r\n const fileName = fileHeader.name\r\n // node-unrar-js 的 fileHeader 包含 flags\r\n const isDir = fileHeader.flags.directory\r\n \r\n fileCount++\r\n // 打印每个文件的信息,帮助调试\r\n // 检查文件名是否有效\r\n const isValid = isGerberFile(fileName)\r\n console.log(`[RAR扫描] 文件: ${fileName}, 目录: ${isDir}, 有效Gerber: ${isValid}`)\r\n \r\n if (fileName && !isDir) {\r\n if (isValid) {\r\n gerberFileNames.push(fileName)\r\n } else if (fileName.toUpperCase().endsWith('.PCBDOC')) {\r\n pcbDocFiles.push(fileName)\r\n }\r\n }\r\n }\r\n \r\n console.log(`RAR 扫描结束,共 ${fileCount} 个条目,找到 ${gerberFileNames.length} 个 Gerber 文件`)\r\n \r\n if (gerberFileNames.length === 0) {\r\n if (pcbDocFiles.length > 0) {\r\n throw new Error(`未找到 Gerber 文件,但发现了 PCB 源文件 (${pcbDocFiles[0]})。本查看器不支持直接读取 .PCBDOC 文件,请先在 EDA 软件中导出为 Gerber 文件。`)\r\n }\r\n console.warn('RAR文件中没有找到Gerber文件')\r\n return gerberFiles\r\n }\r\n \r\n // 提取所有 Gerber 文件\r\n // extract 返回 { arcHeader, files }\r\n // files 是一个 Generator<ArcFile>(迭代器)\r\n const extracted = extractor.extract({ files: gerberFileNames })\r\n \r\n // 遍历 files 迭代器,提取每个文件的内容\r\n for (const arcFile of extracted.files) {\r\n const fileName = arcFile.fileHeader.name\r\n const content = arcFile.extraction // Uint8Array\r\n \r\n if (content && content instanceof Uint8Array) {\r\n const blob = new Blob([content], { type: 'application/octet-stream' })\r\n gerberFiles.push(new File([blob], fileName, { type: 'application/octet-stream' }))\r\n }\r\n }\r\n \r\n const endTime = performance.now()\r\n const totalTime = endTime - startTime\r\n console.log(`[性能] extractRar 完成,总耗时: ${totalTime.toFixed(2)}ms,提取文件数: ${gerberFiles.length}`)\r\n console.log(`从 RAR 文件 ${rarFile.name} 中提取出 ${gerberFiles.length} 个 Gerber 文件`)\r\n return gerberFiles\r\n } catch (error) {\r\n console.error('解析RAR压缩包失败:', error)\r\n console.error('错误详情:', error.stack)\r\n // 如果是已知错误(如未找到Gerber文件),直接抛出,避免多层错误信息包装\r\n if (error.message.includes('未找到 Gerber 文件') || error.message.includes('无法创建 RAR 提取器')) {\r\n throw error\r\n }\r\n throw new Error('无法解析RAR压缩包,请确认文件是否损坏: ' + error.message)\r\n }\r\n}","/**\n * Gerber 可视化器公共函数\n * 用于 2d 和 2d-direct 两个页面的共享代码\n */\n\n/**\n * 解析 URL 参数获取 ID\n * 支持多种格式(按优先级):\n * 1. 查询参数格式(优先):http://localhost:5173/3dPage?id=xx 或 http://localhost:5173/?id=xx\n * 2. 查询参数格式(兼容旧格式):http://localhost:5173/?object={\"id\":\"...\"}\n * 3. 路径格式:http://localhost:5173/{id} 或 http://localhost:5173/2d/{id}\n * @returns {string|null} - 返回 ID 或 null\n */\nexport function parseUrlId() {\n try {\n // 优先从查询参数中提取 ID(新格式:?id=xx)\n const params = new URLSearchParams(window.location.search)\n const idParam = params.get('id')\n \n if (idParam) {\n console.log('[公共函数] 从 URL 查询参数获取到的 ID (id=):', idParam)\n return idParam\n }\n \n // 兼容旧格式:从查询参数中提取 object 参数\n const objectParam = params.get('object')\n \n if (objectParam) {\n const data = JSON.parse(objectParam)\n if (data && data.id) {\n console.log('[公共函数] 从 URL 查询参数获取到的 ID (object=):', data.id)\n return data.id\n }\n }\n \n // 最后尝试从路径中提取 ID(路径格式)\n const pathname = window.location.pathname\n // 移除开头的斜杠和末尾的 .html 等,提取 ID\n // 例如:/fdd1d12d-dfec-35da-a96d-3a1e3bd7f3f5 或 /index.html/fdd1d12d-dfec-35da-a96d-3a1e3bd7f3f5\n const pathParts = pathname.split('/').filter(part => part && part !== 'index.html')\n \n // 查找符合 UUID 格式的部分(包含连字符的 36 字符字符串)\n for (const part of pathParts) {\n // UUID 格式:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (36 字符)\n if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(part)) {\n console.log('[公共函数] 从 URL 路径获取到的 ID:', part)\n return part\n }\n }\n } catch (error) {\n console.error('[公共函数] 解析 URL 参数失败:', error)\n }\n return null\n}\n\n/**\n * 解析 URL 参数获取文件 URL\n * 支持:?url=xxx\n * @returns {string|null} - 返回 URL 或 null\n */\nexport function parseUrlFileUrl() {\n try {\n const params = new URLSearchParams(window.location.search)\n const urlParam = params.get('url')\n if (urlParam) {\n console.log('[公共函数] 从 URL 查询参数获取到的文件链接 (url=):', urlParam)\n return urlParam\n }\n } catch (error) {\n console.error('[公共函数] 解析 URL 参数失败 (url):', error)\n }\n return null\n}\n\n/**\n * 根据 ID 从接口获取文件\n * @param {string} id - 任务 ID\n * @param {Function} handleFileSelection - 文件处理回调函数\n * @param {Function} showStatus - 状态显示回调函数\n */\nexport async function fetchFileById(id, handleFileSelection, showStatus) {\n if (!id) {\n console.warn('[公共函数] ID 为空,跳过文件获取')\n return\n }\n \n const startTimeTotal = performance.now()\n \n try {\n showStatus('正在从服务器获取文件...', 'loading')\n console.log('[公共函数] 开始获取文件,ID:', id)\n \n const encodedId = encodeURIComponent(id)\n const apiUrl = `https://x.jiepei.com/mfr/app/p-cBTo-gerber-task/${encodedId}/output-file-by-id`\n \n console.log('[性能] 发起网络请求:', apiUrl)\n showStatus('正在从服务器获取文件(如果是首次访问可能需要10-15秒)...', 'loading')\n const startTimeFetch = performance.now()\n // const response = await fetch(`http://192.168.19.22/mfrApi/app/p-cBTo-gerber-task/${encodedId}/output-file-by-id`)\n const response = await fetch(apiUrl)\n const endTimeFetch = performance.now()\n const fetchTime = (endTimeFetch - startTimeFetch) / 1000 // 转换为秒\n console.log(`[性能] 服务器响应收到,耗时: ${fetchTime.toFixed(2)}秒, 状态: ${response.status}`)\n \n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n \n // 获取Content-Length以显示下载进度\n const contentLength = response.headers.get('Content-Length')\n if (contentLength) {\n console.log('[性能] 文件大小:', (parseInt(contentLength) / 1024).toFixed(2), 'KB')\n }\n \n console.log('[性能] 开始下载文件内容...')\n showStatus('正在下载文件...', 'loading')\n const startTimeBlob = performance.now()\n \n // 使用ReadableStream实现下载进度显示\n let blob\n if (contentLength && response.body) {\n const total = parseInt(contentLength)\n let loaded = 0\n const chunks = []\n \n const reader = response.body.getReader()\n const startDownloadTime = Date.now()\n let lastUpdateTime = startDownloadTime\n \n while (true) {\n const { done, value } = await reader.read()\n \n if (done) break\n \n chunks.push(value)\n loaded += value.length\n \n // 每500ms更新一次进度(避免更新太频繁)\n const now = Date.now()\n if (now - lastUpdateTime > 500) {\n const progress = ((loaded / total) * 100).toFixed(1)\n const speed = (loaded / 1024 / ((now - startDownloadTime) / 1000)).toFixed(1) // KB/s\n showStatus(`正在下载文件 ${progress}% (${speed} KB/s)...`, 'loading')\n lastUpdateTime = now\n }\n }\n \n blob = new Blob(chunks)\n } else {\n // 如果无法获取Content-Length,使用原来的方式\n blob = await response.blob()\n }\n \n const endTimeBlob = performance.now()\n const blobTime = (endTimeBlob - startTimeBlob) / 1000 // 转换为秒\n const downloadSpeed = (blob.size / 1024 / blobTime).toFixed(1) // KB/s\n console.log(`[性能] 文件下载完成,耗时: ${blobTime.toFixed(2)}秒, 实际大小: ${(blob.size / 1024).toFixed(2)}KB, 平均速度: ${downloadSpeed} KB/s`)\n \n // 如果下载速度太慢,给出警告\n if (parseFloat(downloadSpeed) < 100) {\n console.warn(`⚠️ [性能警告] 下载速度过慢(${downloadSpeed} KB/s),正常应该 > 500 KB/s`)\n console.warn(' 可能原因:')\n console.warn(' 1. 服务器带宽不足')\n console.warn(' 2. 网络连接质量差')\n console.warn(' 3. 服务器负载过高')\n console.warn(' 建议:使用CDN加速文件分发')\n }\n \n showStatus('文件下载完成,开始解析...', 'loading')\n \n // 智能识别文件名和类型\n console.log('[性能] 开始识别文件类型...')\n const startTimeIdentify = performance.now()\n let fileName = 'gerber.rar' // 默认回退\n \n // 1. 尝试从 Content-Disposition 获取\n const disposition = response.headers.get('Content-Disposition')\n if (disposition) {\n const filenameRegex = /filename[^;=\\n]*=((['\"]).*?\\2|[^;\\n]*)/\n const matches = filenameRegex.exec(disposition)\n if (matches != null && matches[1]) { \n fileName = matches[1].replace(/['\"]/g, '')\n }\n }\n \n // 2. 检查文件魔数以确认真实类型 (特别是当 Content-Disposition 缺失或被跨域屏蔽时)\n try {\n const headerBuffer = await blob.slice(0, 4).arrayBuffer()\n const headerView = new Uint8Array(headerBuffer)\n \n // ZIP Signature: 0x50 0x4B 0x03 0x04 (PK..)\n if (headerView[0] === 0x50 && headerView[1] === 0x4B) {\n // 如果检测到是 ZIP 但文件名不是 .zip 结尾,修正它\n if (!fileName.toLowerCase().endsWith('.zip')) {\n console.log('[公共函数] 检测到 ZIP 魔数 (PK..),修正扩展名为 .zip')\n fileName = fileName.replace(/\\.[^/.]+$/, \"\") + \".zip\"\n if (!fileName.endsWith('.zip')) fileName += '.zip' // 防止无扩展名的情况\n }\n }\n // RAR Signature: 0x52 0x61 0x72 0x21 (Rar!)\n else if (headerView[0] === 0x52 && headerView[1] === 0x61 && headerView[2] === 0x72 && headerView[3] === 0x21) {\n if (!fileName.toLowerCase().endsWith('.rar')) {\n console.log('[公共函数] 检测到 RAR 魔数 (Rar!),修正扩展名为 .rar')\n fileName = fileName.replace(/\\.[^/.]+$/, \"\") + \".rar\"\n if (!fileName.endsWith('.rar')) fileName += '.rar'\n }\n }\n } catch (e) {\n console.warn('[公共函数] 文件类型检测失败:', e)\n }\n \n const endTimeIdentify = performance.now()\n console.log(`[性能] 文件类型识别完成,耗时: ${(endTimeIdentify - startTimeIdentify).toFixed(2)}ms, 文件名: ${fileName}`)\n\n console.log('[性能] 创建 File 对象...')\n const startTimeFile = performance.now()\n const file = new File([blob], fileName, { type: fileName.endsWith('.zip') ? 'application/zip' : 'application/x-rar-compressed' })\n const endTimeFile = performance.now()\n console.log(`[性能] File 对象创建完成,耗时: ${(endTimeFile - startTimeFile).toFixed(2)}ms`)\n \n const endTimeTotal = performance.now()\n const totalTimeBeforeHandle = (endTimeTotal - startTimeTotal) / 1000 // 转换为秒\n console.log('[公共函数] 文件获取成功,大小:', (file.size / 1024).toFixed(2), 'KB', '文件名:', fileName)\n console.log(`[性能] ========== 接口调用到File对象创建总耗时: ${totalTimeBeforeHandle.toFixed(2)}秒 ==========`)\n \n // 如果接口调用超过5秒,给出优化建议\n if (totalTimeBeforeHandle > 5) {\n console.warn(`⚠️ [性能建议] 接口响应较慢(${totalTimeBeforeHandle.toFixed(1)}秒),建议后端优化:`)\n console.warn(' 1. 预先生成文件,而不是实时生成')\n console.warn(' 2. 使用CDN加速文件分发')\n console.warn(' 3. 启用文件压缩和缓存')\n }\n \n console.log('[性能] 开始调用 handleFileSelection...')\n const startTimeHandle = performance.now()\n await handleFileSelection([file])\n const endTimeHandle = performance.now()\n console.log(`[性能] handleFileSelection 完成,耗时: ${(endTimeHandle - startTimeHandle).toFixed(2)}ms`)\n \n const finalTime = performance.now()\n const grandTotal = (finalTime - startTimeTotal) / 1000 // 转换为秒\n console.log(`[性能] ========== 从开始获取到处理完成总耗时: ${grandTotal.toFixed(2)}秒 ==========`)\n } catch (error) {\n const endTime = performance.now()\n const errorTime = endTime - startTimeTotal\n console.error(`[公共函数] 获取文件失败 (耗时: ${errorTime.toFixed(2)}ms):`, error)\n showStatus(`获取文件失败: ${error.message}`, 'error')\n }\n}\n\n/**\n * 根据 URL 获取文件\n * @param {string} url - 文件 URL\n * @param {Function} handleFileSelection - 文件处理回调函数\n * @param {Function} showStatus - 状态显示回调函数\n */\nexport async function fetchFileByUrl(url, handleFileSelection, showStatus) {\n if (!url) {\n console.warn('[公共函数] URL 为空,跳过文件获取')\n return\n }\n\n const startTimeTotal = performance.now()\n try {\n showStatus('正在从链接获取文件...', 'loading')\n console.log('[公共函数] 开始获取文件,URL:', url)\n\n let proxyUrl = `https://x.jiepei.com/api/app/file-upload/temporary-url?oldOssUrl=${url}`\n console.log('[公共函数] 获取临时链接:', proxyUrl)\n let response = await fetch(proxyUrl)\n if (!response.ok) {\n if (response.status === 403 || response.status === 401) {\n throw new Error(`HTTP error! status: ${response.status} (链接无权限,请使用带签名的 URL 或改用 ?id=...)`)\n }\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n\n let downloadUrl = url\n const contentType = response.headers.get('Content-Type') || ''\n if (contentType.includes('application/json')) {\n const data = await response.json()\n const tempUrl = data?.url || data?.data || data?.result || data?.content || data?.temporaryUrl || data?.tempUrl\n if (!tempUrl || typeof tempUrl !== 'string') {\n throw new Error('临时链接响应格式异常')\n }\n downloadUrl = tempUrl.trim()\n console.log('[公共函数] 使用临时链接下载文件:', downloadUrl)\n response = await fetch(downloadUrl)\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n } else if (contentType.includes('text/plain')) {\n const text = await response.text()\n const tempUrl = text.trim()\n if (!/^https?:\\/\\//i.test(tempUrl)) {\n throw new Error('临时链接响应格式异常')\n }\n downloadUrl = tempUrl\n console.log('[公共函数] 使用临时链接下载文件:', downloadUrl)\n response = await fetch(downloadUrl)\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`)\n }\n }\n\n const contentLength = response.headers.get('Content-Length')\n if (contentLength) {\n console.log('[性能] 文件大小:', (parseInt(contentLength) / 1024).toFixed(2), 'KB')\n }\n\n showStatus('正在下载文件...', 'loading')\n const startTimeBlob = performance.now()\n let blob\n if (contentLength && response.body) {\n const total = parseInt(contentLength)\n let loaded = 0\n const chunks = []\n const reader = response.body.getReader()\n const startDownloadTime = Date.now()\n let lastUpdateTime = startDownloadTime\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n chunks.push(value)\n loaded += value.length\n\n const now = Date.now()\n if (now - lastUpdateTime > 500) {\n const progress = ((loaded / total) * 100).toFixed(1)\n const speed = (loaded / 1024 / ((now - startDownloadTime) / 1000)).toFixed(1)\n showStatus(`正在下载文件 ${progress}% (${speed} KB/s)...`, 'loading')\n lastUpdateTime = now\n }\n }\n blob = new Blob(chunks)\n } else {\n blob = await response.blob()\n }\n\n const endTimeBlob = performance.now()\n const blobTime = (endTimeBlob - startTimeBlob) / 1000\n console.log(`[性能] 文件下载完成,耗时: ${blobTime.toFixed(2)}秒, 实际大小: ${(blob.size / 1024).toFixed(2)}KB`)\n\n showStatus('文件下载完成,开始解析...', 'loading')\n\n let fileName = 'gerber.zip'\n const disposition = response.headers.get('Content-Disposition')\n if (disposition) {\n const filenameRegex = /filename[^;=\\n]*=((['\"]).*?\\2|[^;\\n]*)/\n const matches = filenameRegex.exec(disposition)\n if (matches != null && matches[1]) {\n fileName = matches[1].replace(/['\"]/g, '')\n }\n } else {\n try {\n const urlObj = new URL(downloadUrl)\n const base = urlObj.pathname.split('/').filter(Boolean).pop()\n if (base) fileName = base\n } catch (e) {\n console.warn('[公共函数] URL 解析失败,使用默认文件名:', e)\n }\n }\n\n try {\n const headerBuffer = await blob.slice(0, 4).arrayBuffer()\n const headerView = new Uint8Array(headerBuffer)\n if (headerView[0] === 0x50 && headerView[1] === 0x4B) {\n if (!fileName.toLowerCase().endsWith('.zip')) {\n fileName = fileName.replace(/\\.[^/.]+$/, \"\") + \".zip\"\n if (!fileName.endsWith('.zip')) fileName += '.zip'\n }\n } else if (headerView[0] === 0x52 && headerView[1] === 0x61 && headerView[2] === 0x72 && headerView[3] === 0x21) {\n if (!fileName.toLowerCase().endsWith('.rar')) {\n fileName = fileName.replace(/\\.[^/.]+$/, \"\") + \".rar\"\n if (!fileName.endsWith('.rar')) fileName += '.rar'\n }\n }\n } catch (e) {\n console.warn('[公共函数] 文件类型检测失败:', e)\n }\n\n const file = new File([blob], fileName, { type: fileName.endsWith('.zip') ? 'application/zip' : 'application/x-rar-compressed' })\n await handleFileSelection([file])\n\n const finalTime = performance.now()\n const totalTime = (finalTime - startTimeTotal) / 1000\n console.log(`[性能] ========== 从链接获取到处理完成总耗时: ${totalTime.toFixed(2)}秒 ==========`)\n } catch (error) {\n const endTime = performance.now()\n const errorTime = endTime - startTimeTotal\n console.error(`[公共函数] 获取文件失败 (耗时: ${errorTime.toFixed(2)}ms):`, error)\n showStatus(`获取文件失败: ${error.message}`, 'error')\n }\n}\n\n/**\n * 初始化 URL ID 处理\n * 如果 URL 中有 id,自动获取并处理文件\n * @param {Function} handleFileSelection - 文件处理回调函数\n * @param {Function} showStatus - 状态显示回调函数\n */\nexport function initUrlIdHandler(handleFileSelection, showStatus) {\n const id = parseUrlId()\n if (id) {\n fetchFileById(id, handleFileSelection, showStatus)\n return\n }\n\n const url = parseUrlFileUrl()\n if (url) {\n fetchFileByUrl(url, handleFileSelection, showStatus)\n }\n}\n\n"],"mappings":"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","names":[]}
@@ -2,7 +2,7 @@ import {
2
2
  I18nContext,
3
3
  resolveMessages,
4
4
  useChatWidgetI18n
5
- } from "./chunk-QLVPIM6R.js";
5
+ } from "./chunk-3CNQONCV.js";
6
6
 
7
7
  // src/provenance/ProvenanceGraphDrawerContent.tsx
8
8
  import React4, { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
@@ -1618,4 +1618,4 @@ export {
1618
1618
  FilterBar,
1619
1619
  GraphToolbar
1620
1620
  };
1621
- //# sourceMappingURL=chunk-KQV7IKET.js.map
1621
+ //# sourceMappingURL=chunk-ASV4TISB.js.map
@@ -6,12 +6,7 @@ import {
6
6
  MarkdownCore,
7
7
  TextCore,
8
8
  VideoCore
9
- } from "./chunk-2UC7YLVX.js";
10
- import {
11
- XREF_MARKER_PREFIX,
12
- XREF_MARKER_SUFFIX,
13
- replaceXrefsWithMarkers
14
- } from "./chunk-56WRZM3R.js";
9
+ } from "./chunk-3LOSHCSH.js";
15
10
  import {
16
11
  useProjectFiles
17
12
  } from "./chunk-2SKA3F5U.js";
@@ -19,9 +14,14 @@ import {
19
14
  injectStyles,
20
15
  useResource
21
16
  } from "./chunk-GAMA3VA7.js";
17
+ import {
18
+ XREF_MARKER_PREFIX,
19
+ XREF_MARKER_SUFFIX,
20
+ replaceXrefsWithMarkers
21
+ } from "./chunk-EYWNUJVZ.js";
22
22
  import {
23
23
  useChatWidgetI18n
24
- } from "./chunk-QLVPIM6R.js";
24
+ } from "./chunk-3CNQONCV.js";
25
25
 
26
26
  // src/components/JsonRender/JsonRenderStandalone.tsx
27
27
  import { useMemo as useMemo23 } from "react";
@@ -11400,7 +11400,7 @@ var SHEET35 = `
11400
11400
  }
11401
11401
  .jr-text-viewer--error { color: var(--jr-danger, #ef4444); }
11402
11402
  `;
11403
- var LazyExcelCore = React37.lazy(() => import("./ExcelCore-DJOIVQMI.js"));
11403
+ var LazyExcelCore = React37.lazy(() => import("./ExcelCore-EAXQMKZT.js"));
11404
11404
  function looksLikeCsv(text, fileName) {
11405
11405
  if (fileName) {
11406
11406
  const ext = fileName.split(".").pop()?.toLowerCase();
@@ -13784,12 +13784,12 @@ function lazyWrap(factory) {
13784
13784
  React53.createElement(LazyComp, props)
13785
13785
  );
13786
13786
  }
13787
- var PdfViewer = lazyWrap(() => import("./PdfViewer-CHPDRK46.js"));
13788
- var KicadViewer = lazyWrap(() => import("./KicadViewer-GV6ZC4AQ.js"));
13789
- var GerberViewer = lazyWrap(() => import("./GerberViewerA2UI-X5FWAD5M.js"));
13790
- var WordViewer = lazyWrap(() => import("./WordViewer-ZHCQMHOH.js"));
13791
- var ExcelViewer = lazyWrap(() => import("./ExcelViewer-3YLLYYIQ.js"));
13792
- var PowerPointViewer = lazyWrap(() => import("./PowerPointViewer-LQTO6UCU.js"));
13787
+ var PdfViewer = lazyWrap(() => import("./PdfViewer-GKVDDNCZ.js"));
13788
+ var KicadViewer = lazyWrap(() => import("./KicadViewer-H6YY6WVC.js"));
13789
+ var GerberViewer = lazyWrap(() => import("./GerberViewerA2UI-AORHOCF6.js"));
13790
+ var WordViewer = lazyWrap(() => import("./WordViewer-7XUQFS4A.js"));
13791
+ var ExcelViewer = lazyWrap(() => import("./ExcelViewer-OGWM52ZT.js"));
13792
+ var PowerPointViewer = lazyWrap(() => import("./PowerPointViewer-5R2KSWWJ.js"));
13793
13793
  var { registry } = defineRegistry(catalog, {
13794
13794
  components: {
13795
13795
  ThemeProvider,
@@ -14010,4 +14010,4 @@ export {
14010
14010
  registry,
14011
14011
  JsonRenderStandalone
14012
14012
  };
14013
- //# sourceMappingURL=chunk-CFKGNAJM.js.map
14013
+ //# sourceMappingURL=chunk-AT6VLLOO.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useRevokeBlobUrl
3
- } from "./chunk-VXJWGLZ7.js";
3
+ } from "./chunk-Q5PLT3AI.js";
4
4
 
5
5
  // src/components/ViewerCore/ExcelCore.tsx
6
6
  import { useEffect as useEffect2, useState, useMemo as useMemo2, useCallback, useRef as useRef2 } from "react";
@@ -266,7 +266,7 @@ var ExcelCore = ({ src, content, sheet, className, loadingLabel, rowsLabel = "ro
266
266
  if (inputContent) {
267
267
  wb = XLSX.read(inputContent, { type: "string" });
268
268
  } else {
269
- const { resolveToArrayBuffer } = await import("./resolveToArrayBuffer-AQIDZHSQ.js");
269
+ const { resolveToArrayBuffer } = await import("./resolveToArrayBuffer-PVSVIAII.js");
270
270
  const buf = await resolveToArrayBuffer(src);
271
271
  if (cancelled) return;
272
272
  wb = XLSX.read(buf, { type: "array" });
@@ -395,4 +395,4 @@ export {
395
395
  ExcelCore,
396
396
  ExcelCore_default
397
397
  };
398
- //# sourceMappingURL=chunk-56WRZM3R.js.map
398
+ //# sourceMappingURL=chunk-EYWNUJVZ.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useRevokeBlobUrl
3
- } from "./chunk-VXJWGLZ7.js";
3
+ } from "./chunk-Q5PLT3AI.js";
4
4
 
5
5
  // src/components/ViewerCore/WordCore.tsx
6
6
  import { useRef, useEffect, useState, useCallback } from "react";
@@ -20,7 +20,7 @@ var WordCore = ({ src, className, onLinkClick, loadingLabel, errorLabel }) => {
20
20
  setError(void 0);
21
21
  (async () => {
22
22
  try {
23
- const { resolveToArrayBuffer } = await import("./resolveToArrayBuffer-AQIDZHSQ.js");
23
+ const { resolveToArrayBuffer } = await import("./resolveToArrayBuffer-PVSVIAII.js");
24
24
  const arrayBuf = await resolveToArrayBuffer(src);
25
25
  if (cancelled || !el.isConnected) return;
26
26
  const docxPreview = await import("docx-preview");
@@ -74,4 +74,4 @@ export {
74
74
  WordCore,
75
75
  WordCore_default
76
76
  };
77
- //# sourceMappingURL=chunk-K4KGNVL5.js.map
77
+ //# sourceMappingURL=chunk-EZ46FGQ6.js.map
@@ -134,7 +134,7 @@ function Page2D({ content, url, onLayerSelect }) {
134
134
  setError(null);
135
135
  if (!isViewerReady()) {
136
136
  console.log("[Page2D] importing 2d entry module...");
137
- await import("./gerber-2d-entry-OQ4SQRBY.js");
137
+ await import("./gerber-2d-entry-HEFXQGBK.js");
138
138
  markViewerReady();
139
139
  console.log("[Page2D] module imported, checking DOM elements:", {
140
140
  canvas: !!document.getElementById("canvas"),
@@ -286,7 +286,7 @@ function Page3D({ content, url }) {
286
286
  setError(null);
287
287
  if (!isViewerReady2()) {
288
288
  await importAndBootLoad(
289
- () => import("./gerber-3d-entry-DEHDBOO2.js")
289
+ () => import("./gerber-3d-entry-KVTONA37.js")
290
290
  );
291
291
  markViewerReady2();
292
292
  }
@@ -513,7 +513,7 @@ function PageSimulation({ content, url }) {
513
513
  try {
514
514
  setError(null);
515
515
  if (!isViewerReady3()) {
516
- await import("./gerber-simulation-entry-EBDX72XE.js");
516
+ await import("./gerber-simulation-entry-GZ62QX5H.js");
517
517
  markViewerReady3();
518
518
  }
519
519
  if (cancelled) return;
@@ -636,4 +636,4 @@ var GerberViewer_default = GerberViewer;
636
636
  export {
637
637
  GerberViewer_default
638
638
  };
639
- //# sourceMappingURL=chunk-GYXTSY22.js.map
639
+ //# sourceMappingURL=chunk-KXPR6SRW.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useChatWidgetI18n
3
- } from "./chunk-QLVPIM6R.js";
3
+ } from "./chunk-3CNQONCV.js";
4
4
 
5
5
  // src/components/FileViewer/viewers/KicadViewerCore.tsx
6
6
  import { useRef, useState, useEffect, useMemo, Component } from "react";
@@ -433,4 +433,4 @@ export {
433
433
  KICAD_EXTS,
434
434
  KicadViewerCore_default
435
435
  };
436
- //# sourceMappingURL=chunk-7S67DOHQ.js.map
436
+ //# sourceMappingURL=chunk-MTN6SUUQ.js.map
@@ -0,0 +1,29 @@
1
+ // src/components/ViewerCore/hooks/useRevokeBlobUrl.ts
2
+ import { useEffect, useRef } from "react";
3
+ function useRevokeBlobUrl(src) {
4
+ const pendingRef = useRef(null);
5
+ useEffect(() => {
6
+ if (!src?.startsWith("blob:")) return;
7
+ if (pendingRef.current && pendingRef.current.src === src) {
8
+ pendingRef.current.cancel();
9
+ pendingRef.current = null;
10
+ }
11
+ return () => {
12
+ const handle = setTimeout(() => {
13
+ URL.revokeObjectURL(src);
14
+ if (pendingRef.current?.src === src) {
15
+ pendingRef.current = null;
16
+ }
17
+ }, 0);
18
+ pendingRef.current = {
19
+ src,
20
+ cancel: () => clearTimeout(handle)
21
+ };
22
+ };
23
+ }, [src]);
24
+ }
25
+
26
+ export {
27
+ useRevokeBlobUrl
28
+ };
29
+ //# sourceMappingURL=chunk-Q5PLT3AI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/ViewerCore/hooks/useRevokeBlobUrl.ts"],"sourcesContent":["import { useEffect, useRef } from 'react'\n\n/**\n * Revoke a blob: URL when the component unmounts or the src changes.\n * No-op for non-blob URLs.\n *\n * StrictMode 双调用安全:\n * - mount → cleanup → mount 顺序在 React 18 dev 模式下会立刻发生。如果 cleanup\n * 立即 `URL.revokeObjectURL(src)`,第二次 mount 拿到的 src 就是已死的 blob,\n * 下游 `fetch(blob:...)` 会以 `TypeError: Failed to fetch` 失败。\n * - 这里的处理:cleanup 不立即 revoke,而是排到下一个 macrotask;若第二次\n * mount 在 0ms 内紧接着发生,effect 会取消那次预定的 revoke,blob 得以\n * 存活到组件真正使用完毕。\n * - 真正的 unmount:cleanup 排队后没有后续 mount 取消,setTimeout 触发,blob\n * 被释放。\n * - src 切换到新 blob:旧 src 的 cleanup 排队 revoke;新 effect 因 src 不同\n * 不会取消,旧 blob 会被回收;新 blob 走同样的延迟生命周期。\n */\nexport function useRevokeBlobUrl(src: string | undefined): void {\n const pendingRef = useRef<{ src: string; cancel: () => void } | null>(null)\n\n useEffect(() => {\n if (!src?.startsWith('blob:')) return\n\n if (pendingRef.current && pendingRef.current.src === src) {\n pendingRef.current.cancel()\n pendingRef.current = null\n }\n\n return () => {\n const handle = setTimeout(() => {\n URL.revokeObjectURL(src)\n if (pendingRef.current?.src === src) {\n pendingRef.current = null\n }\n }, 0)\n pendingRef.current = {\n src,\n cancel: () => clearTimeout(handle),\n }\n }\n }, [src])\n}\n"],"mappings":";AAAA,SAAS,WAAW,cAAc;AAkB3B,SAAS,iBAAiB,KAA+B;AAC9D,QAAM,aAAa,OAAmD,IAAI;AAE1E,YAAU,MAAM;AACd,QAAI,CAAC,KAAK,WAAW,OAAO,EAAG;AAE/B,QAAI,WAAW,WAAW,WAAW,QAAQ,QAAQ,KAAK;AACxD,iBAAW,QAAQ,OAAO;AAC1B,iBAAW,UAAU;AAAA,IACvB;AAEA,WAAO,MAAM;AACX,YAAM,SAAS,WAAW,MAAM;AAC9B,YAAI,gBAAgB,GAAG;AACvB,YAAI,WAAW,SAAS,QAAQ,KAAK;AACnC,qBAAW,UAAU;AAAA,QACvB;AAAA,MACF,GAAG,CAAC;AACJ,iBAAW,UAAU;AAAA,QACnB;AAAA,QACA,QAAQ,MAAM,aAAa,MAAM;AAAA,MACnC;AAAA,IACF;AAAA,EACF,GAAG,CAAC,GAAG,CAAC;AACV;","names":[]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  useRevokeBlobUrl
3
- } from "./chunk-VXJWGLZ7.js";
3
+ } from "./chunk-Q5PLT3AI.js";
4
4
 
5
5
  // src/components/ViewerCore/PowerPointCore.tsx
6
6
  import { useEffect, useState, useRef } from "react";
@@ -22,7 +22,7 @@ var PowerPointCore = ({
22
22
  try {
23
23
  setLoading(true);
24
24
  setError(void 0);
25
- const { resolveToArrayBuffer } = await import("./resolveToArrayBuffer-AQIDZHSQ.js");
25
+ const { resolveToArrayBuffer } = await import("./resolveToArrayBuffer-PVSVIAII.js");
26
26
  const buf = await resolveToArrayBuffer(src);
27
27
  if (cancelled) return;
28
28
  const { init } = await import("pptx-preview");
@@ -80,4 +80,4 @@ export {
80
80
  PowerPointCore,
81
81
  PowerPointCore_default
82
82
  };
83
- //# sourceMappingURL=chunk-PZXSASDY.js.map
83
+ //# sourceMappingURL=chunk-R5LHMOAC.js.map
@@ -0,0 +1,238 @@
1
+ // src/provenance/rag/citationContext.ts
2
+ import { useSyncExternalStore as useSyncExternalStore2 } from "react";
3
+
4
+ // src/provenance/rag/sidecarCache.ts
5
+ import { useSyncExternalStore, useEffect } from "react";
6
+ var MAX_ENTRIES = 50;
7
+ var cache = /* @__PURE__ */ new Map();
8
+ var subscribers = /* @__PURE__ */ new Set();
9
+ var snapshotsByKey = /* @__PURE__ */ new Map();
10
+ var EMPTY_IDLE_SNAPSHOT = Object.freeze({ status: "idle", sidecar: void 0 });
11
+ function cacheKey(rid, commit) {
12
+ return `${rid}@${commit}`;
13
+ }
14
+ function notify() {
15
+ for (const fn of subscribers) fn();
16
+ }
17
+ function evictIfNeeded() {
18
+ while (cache.size > MAX_ENTRIES) {
19
+ const oldest = cache.keys().next().value;
20
+ if (oldest === void 0) break;
21
+ cache.delete(oldest);
22
+ snapshotsByKey.delete(oldest);
23
+ }
24
+ }
25
+ function getCachedSidecar(rid, commit) {
26
+ const k = cacheKey(rid, commit);
27
+ const entry = cache.get(k);
28
+ if (entry) {
29
+ cache.delete(k);
30
+ cache.set(k, entry);
31
+ }
32
+ return entry;
33
+ }
34
+ function setCachedSidecar(rid, commit, entry) {
35
+ const k = cacheKey(rid, commit);
36
+ cache.set(k, entry);
37
+ snapshotsByKey.delete(k);
38
+ evictIfNeeded();
39
+ notify();
40
+ }
41
+ function subscribe(fn) {
42
+ subscribers.add(fn);
43
+ return () => {
44
+ subscribers.delete(fn);
45
+ };
46
+ }
47
+ async function preloadSidecars(files, fetcher) {
48
+ const seen = /* @__PURE__ */ new Set();
49
+ const todo = [];
50
+ for (const f of files) {
51
+ const k = cacheKey(f.rid, f.commit);
52
+ if (seen.has(k)) continue;
53
+ seen.add(k);
54
+ const existing = cache.get(k);
55
+ if (existing && (existing.status === "ready" || existing.status === "loading")) continue;
56
+ todo.push(f);
57
+ }
58
+ if (todo.length === 0) return;
59
+ for (const { rid, commit } of todo) {
60
+ setCachedSidecar(rid, commit, { status: "loading" });
61
+ }
62
+ const groupedByCommit = /* @__PURE__ */ new Map();
63
+ for (const { rid, commit } of todo) {
64
+ const arr = groupedByCommit.get(commit) ?? [];
65
+ arr.push(rid);
66
+ groupedByCommit.set(commit, arr);
67
+ }
68
+ await Promise.all(
69
+ Array.from(groupedByCommit.entries()).map(async ([commit, rids]) => {
70
+ try {
71
+ const resp = await fetcher({ ids: rids.join(","), commit });
72
+ for (const rid of rids) {
73
+ const data = resp.items[rid];
74
+ if (data) {
75
+ setCachedSidecar(rid, commit, { status: "ready", data });
76
+ } else if (resp.stale.includes(rid)) {
77
+ setCachedSidecar(rid, commit, { status: "stale" });
78
+ } else {
79
+ setCachedSidecar(rid, commit, { status: "missing" });
80
+ }
81
+ }
82
+ } catch {
83
+ for (const rid of rids) {
84
+ setCachedSidecar(rid, commit, { status: "missing" });
85
+ }
86
+ }
87
+ })
88
+ );
89
+ }
90
+ var defaultFetcher = async ({ ids, commit }) => {
91
+ const base = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost";
92
+ const url = new URL("/api/sidecar", base);
93
+ url.searchParams.set("ids", ids);
94
+ if (commit) url.searchParams.set("commit", commit);
95
+ const resp = await fetch(url.toString());
96
+ if (!resp.ok) throw new Error(`sidecar api ${resp.status}`);
97
+ return await resp.json();
98
+ };
99
+ var activeFetcher = defaultFetcher;
100
+ function setSidecarFetcher(fetcher) {
101
+ activeFetcher = fetcher ?? defaultFetcher;
102
+ }
103
+ function getSidecarFetcher() {
104
+ return activeFetcher;
105
+ }
106
+ function readStableSnapshot(rid, commit) {
107
+ const k = cacheKey(rid, commit);
108
+ const entry = cache.get(k);
109
+ if (!entry) return EMPTY_IDLE_SNAPSHOT;
110
+ const cached = snapshotsByKey.get(k);
111
+ if (cached && cached.status === entry.status && cached.sidecar === entry.data) {
112
+ return cached;
113
+ }
114
+ const snap = Object.freeze({
115
+ status: entry.status,
116
+ sidecar: entry.data
117
+ });
118
+ snapshotsByKey.set(k, snap);
119
+ return snap;
120
+ }
121
+ function useSidecar(rid, commit) {
122
+ const snapshot = useSyncExternalStore(
123
+ subscribe,
124
+ () => readStableSnapshot(rid, commit),
125
+ () => EMPTY_IDLE_SNAPSHOT
126
+ );
127
+ useEffect(() => {
128
+ if (!rid || !commit) return;
129
+ if (snapshot.status === "idle") {
130
+ void preloadSidecars([{ rid, commit }], activeFetcher);
131
+ }
132
+ }, [rid, commit, snapshot.status]);
133
+ return snapshot;
134
+ }
135
+
136
+ // src/provenance/rag/citationContext.ts
137
+ var ridShortIndex = /* @__PURE__ */ new Map();
138
+ var revCounter = 0;
139
+ var subscribers2 = /* @__PURE__ */ new Set();
140
+ function notify2() {
141
+ revCounter += 1;
142
+ for (const fn of subscribers2) fn();
143
+ }
144
+ function ingestCitationContext(files) {
145
+ if (!Array.isArray(files) || files.length === 0) return;
146
+ for (const f of files) {
147
+ if (!f || !f.rid_short) continue;
148
+ if (process.env.NODE_ENV !== "production") {
149
+ const prev = ridShortIndex.get(f.rid_short);
150
+ if (prev && prev.rid !== f.rid) {
151
+ console.warn(
152
+ `[jetp-084 v3] rid_short conflict in session index: '${f.rid_short}' previously bound to rid='${prev.rid}', now rebinding to rid='${f.rid}'.`
153
+ );
154
+ }
155
+ }
156
+ ridShortIndex.set(f.rid_short, f);
157
+ }
158
+ void preloadSidecars(
159
+ files.map((f) => ({ rid: f.rid, commit: f.git_commit_hash })),
160
+ getSidecarFetcher()
161
+ );
162
+ notify2();
163
+ }
164
+ function __resetForSessionSwitch() {
165
+ ridShortIndex.clear();
166
+ notify2();
167
+ }
168
+ function __resetCitationContextForTests() {
169
+ ridShortIndex.clear();
170
+ revCounter = 0;
171
+ notify2();
172
+ }
173
+ function getFileByRidShort(ridShort) {
174
+ return ridShortIndex.get(ridShort) ?? null;
175
+ }
176
+ function useFileByRidShort(ridShort) {
177
+ return useSyncExternalStore2(
178
+ (fn) => {
179
+ subscribers2.add(fn);
180
+ return () => {
181
+ subscribers2.delete(fn);
182
+ };
183
+ },
184
+ () => getFileByRidShort(ridShort),
185
+ () => null
186
+ );
187
+ }
188
+ function getAllKnownFiles() {
189
+ return Array.from(ridShortIndex.values());
190
+ }
191
+ function persistFromMessage(_messageId, files, options) {
192
+ if (options?.preload === false) {
193
+ for (const f of files) {
194
+ if (f?.rid_short) ridShortIndex.set(f.rid_short, f);
195
+ }
196
+ notify2();
197
+ return;
198
+ }
199
+ ingestCitationContext(files);
200
+ }
201
+ function clearMessageContext(_messageId) {
202
+ }
203
+ function getMessageContext(_messageId) {
204
+ return getAllKnownFiles();
205
+ }
206
+ function useCitationContext(_messageId) {
207
+ return useSyncExternalStore2(
208
+ (fn) => {
209
+ subscribers2.add(fn);
210
+ return () => {
211
+ subscribers2.delete(fn);
212
+ };
213
+ },
214
+ () => ({ files: getAllKnownFiles() }),
215
+ () => ({ files: [] })
216
+ );
217
+ }
218
+
219
+ export {
220
+ getCachedSidecar,
221
+ setCachedSidecar,
222
+ preloadSidecars,
223
+ defaultFetcher,
224
+ setSidecarFetcher,
225
+ getSidecarFetcher,
226
+ useSidecar,
227
+ ingestCitationContext,
228
+ __resetForSessionSwitch,
229
+ __resetCitationContextForTests,
230
+ getFileByRidShort,
231
+ useFileByRidShort,
232
+ getAllKnownFiles,
233
+ persistFromMessage,
234
+ clearMessageContext,
235
+ getMessageContext,
236
+ useCitationContext
237
+ };
238
+ //# sourceMappingURL=chunk-X67V7257.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/provenance/rag/citationContext.ts","../src/provenance/rag/sidecarCache.ts"],"sourcesContent":["/**\n * JETP-084 v3 (Phase 10, 2026-05-27) — Session-scoped citation context.\n *\n * v3 ditches the v1/v2 per-message store entirely. The backend emits\n * a single `citation_context` SSE notification from the *read* tool\n * carrying the full session-known files list (no `message_id`); the\n * frontend maintains one global `rid_short → file` index for the\n * lifetime of the open session and resolves chips against it.\n *\n * Lifecycle:\n * - SSE callback → `ingestCitationContext(files)` → merges into the\n * index and notifies subscribers.\n * - Session switch / sign-out → `__resetForSessionSwitch()` wipes\n * the index so cross-session bleed-through is impossible.\n * - UI chips: `useFileByRidShort(ridShort)` returns the matching\n * file or null; no messageId argument.\n *\n * Why this is the correct shape (vs v1/v2 per-message):\n * - The backend pool is session-scoped (see\n * `jetagents/runtime/turn/citation_pool.py`), so each emit already\n * carries every file the session has surfaced. Maintaining a parallel\n * per-message index on the frontend just multiplies the failure modes\n * (ID alignment between LLM message IDs and SSE deliveries was the\n * entire v2 chip-render bug).\n * - The rid_short identifier is deterministic per (session, rid) on\n * the backend, so any frontend resolution that finds a match is\n * guaranteed to be correct as long as the session matches.\n *\n * Spec: `doc/proposals/JETP-084-rag-paragraph-provenance/chat-widget/05_sidecar_cache.md` §5–§6.\n */\n\nimport { useSyncExternalStore } from 'react'\nimport type { CitationContextFile } from './types'\nimport { preloadSidecars, getSidecarFetcher } from './sidecarCache'\n\n/** Single session-scoped index. Last-write-wins on rid_short conflicts. */\nconst ridShortIndex = new Map<string, CitationContextFile>()\n\n/** Identity counter so React snapshots compare cheaply. */\nlet revCounter = 0\nconst subscribers = new Set<() => void>()\n\nfunction notify(): void {\n revCounter += 1\n for (const fn of subscribers) fn()\n}\n\n/**\n * Ingest a v3 citation_context payload. The full session-known\n * `files` list is merged into the global index; entries are upserted\n * by `rid_short` (the deterministic identifier). Stale rid_shorts not\n * present in the new payload are intentionally kept around — the LLM\n * may still cite them from memory and the chip should resolve. The\n * backend's session-level pool TTL / LRU enforces the upper bound.\n */\nexport function ingestCitationContext(files: CitationContextFile[]): void {\n if (!Array.isArray(files) || files.length === 0) return\n for (const f of files) {\n if (!f || !f.rid_short) continue\n if (process.env.NODE_ENV !== 'production') {\n const prev = ridShortIndex.get(f.rid_short)\n if (prev && prev.rid !== f.rid) {\n // eslint-disable-next-line no-console\n console.warn(\n `[jetp-084 v3] rid_short conflict in session index: ` +\n `'${f.rid_short}' previously bound to rid='${prev.rid}', ` +\n `now rebinding to rid='${f.rid}'.`,\n )\n }\n }\n ridShortIndex.set(f.rid_short, f)\n }\n void preloadSidecars(\n files.map(f => ({ rid: f.rid, commit: f.git_commit_hash })),\n getSidecarFetcher(),\n )\n notify()\n}\n\n/**\n * Reset the session-scoped index. Called by the chat-widget controller\n * when the user switches sessions or signs out so the next session\n * starts with a clean rid_short namespace.\n */\nexport function __resetForSessionSwitch(): void {\n ridShortIndex.clear()\n notify()\n}\n\n/** TESTING ONLY — alias of `__resetForSessionSwitch`. */\nexport function __resetCitationContextForTests(): void {\n ridShortIndex.clear()\n revCounter = 0\n notify()\n}\n\n/**\n * Resolve a rid_short to its file (synchronous, non-reactive).\n * Returns null when the rid_short is unknown in this session.\n */\nexport function getFileByRidShort(\n ridShort: string,\n): CitationContextFile | null {\n return ridShortIndex.get(ridShort) ?? null\n}\n\n/**\n * React-reactive lookup. Re-evaluates when the session index mutates;\n * returns the stable file reference so referential equality short-\n * circuits unnecessary re-renders.\n */\nexport function useFileByRidShort(\n ridShort: string,\n): CitationContextFile | null {\n return useSyncExternalStore(\n fn => {\n subscribers.add(fn)\n return () => {\n subscribers.delete(fn)\n }\n },\n () => getFileByRidShort(ridShort),\n () => null,\n )\n}\n\n/**\n * Snapshot the full session index — primarily for debugging / dev\n * tooling. Returns a fresh array so callers can mutate without\n * disturbing the store.\n */\nexport function getAllKnownFiles(): CitationContextFile[] {\n return Array.from(ridShortIndex.values())\n}\n\n// ─── Deprecated v1/v2 surface — kept as no-op shims for one release ─────\n//\n// These exports are retained so existing callers in the chat-widget that\n// have not been migrated to the v3 lookup signature don't break at\n// import time. They will be deleted in T-10.12 once every call site has\n// been swept.\n\n/** @deprecated v1/v2 per-message store removed. v3 ignores `messageId`. */\nexport function persistFromMessage(\n _messageId: string,\n files: CitationContextFile[],\n options?: { preload?: boolean },\n): void {\n if (options?.preload === false) {\n // Don't trigger preload; just merge.\n for (const f of files) {\n if (f?.rid_short) ridShortIndex.set(f.rid_short, f)\n }\n notify()\n return\n }\n ingestCitationContext(files)\n}\n\n/** @deprecated v1/v2 per-message store removed. v3 is session-scoped. */\nexport function clearMessageContext(_messageId: string): void {\n // no-op — v3 doesn't index by message_id\n}\n\n/** @deprecated v1/v2 per-message getter — returns the *session* snapshot. */\nexport function getMessageContext(_messageId: string): CitationContextFile[] {\n return getAllKnownFiles()\n}\n\n/** @deprecated v1/v2 hook — use {@link useFileByRidShort} (no messageId arg). */\nexport function useCitationContext(_messageId: string): { files: CitationContextFile[] } {\n return useSyncExternalStore(\n fn => {\n subscribers.add(fn)\n return () => {\n subscribers.delete(fn)\n }\n },\n () => ({ files: getAllKnownFiles() }),\n () => ({ files: [] }),\n )\n}\n","/**\n * JETP-084 — Sidecar LRU cache + batch preloader + React hook.\n *\n * Memory model:\n * - Single browser-tab in-memory `Map<key, CacheEntry>`; insertion order is\n * LRU order (oldest first).\n * - On `get` we re-insert to bump recency.\n * - On `set` we evict from the front until size ≤ MAX_ENTRIES.\n *\n * Network model:\n * - `preloadSidecars` groups requested (rid, commit) tuples by commit and\n * fires one batched fetch per commit. Failures mark each affected entry as\n * `missing` so the UI never spins forever.\n * - `useSidecar` is a thin reactive layer on top via `useSyncExternalStore`.\n */\n\nimport { useSyncExternalStore, useEffect } from 'react'\nimport type { ChunksSidecar, SidecarStatus } from './types'\n\nexport interface CacheEntry {\n status: SidecarStatus\n data?: ChunksSidecar\n /** Tracks an in-flight request; used to dedupe parallel hover requests. */\n promise?: Promise<void>\n}\n\nexport interface BatchFetcherResponse {\n /** Keyed by `rid`. */\n items: Record<string, ChunksSidecar>\n /** Resource ids that have no sidecar at all (404 equivalent). */\n missing: string[]\n /** Resource ids whose sidecar commit ≠ requested commit. */\n stale: string[]\n}\n\nexport interface BatchFetcher {\n (params: { ids: string; commit: string }): Promise<BatchFetcherResponse>\n}\n\nexport interface SidecarSnapshot {\n status: SidecarStatus\n sidecar: ChunksSidecar | undefined\n}\n\nexport const MAX_ENTRIES = 50\n\nconst cache = new Map<string, CacheEntry>()\nconst subscribers = new Set<() => void>()\n/**\n * Per-(rid, commit) frozen snapshot returned by `useSyncExternalStore`.\n * Must be referentially stable across renders when nothing changed, otherwise\n * React will loop. We invalidate the entry on every `setCachedSidecar`.\n */\nconst snapshotsByKey = new Map<string, SidecarSnapshot>()\nconst EMPTY_IDLE_SNAPSHOT: SidecarSnapshot = Object.freeze({ status: 'idle', sidecar: undefined })\n\nfunction cacheKey(rid: string, commit: string): string {\n return `${rid}@${commit}`\n}\n\nfunction notify(): void {\n for (const fn of subscribers) fn()\n}\n\nfunction evictIfNeeded(): void {\n while (cache.size > MAX_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest === undefined) break\n cache.delete(oldest)\n snapshotsByKey.delete(oldest)\n }\n}\n\n/** Public — return entry and bump LRU recency. Returns `undefined` if miss. */\nexport function getCachedSidecar(rid: string, commit: string): CacheEntry | undefined {\n const k = cacheKey(rid, commit)\n const entry = cache.get(k)\n if (entry) {\n cache.delete(k)\n cache.set(k, entry)\n }\n return entry\n}\n\n/** Public — write/replace an entry. Triggers notify + LRU eviction. */\nexport function setCachedSidecar(rid: string, commit: string, entry: CacheEntry): void {\n const k = cacheKey(rid, commit)\n cache.set(k, entry)\n snapshotsByKey.delete(k)\n evictIfNeeded()\n notify()\n}\n\n/** Subscribe to *any* cache change. Returns an unsubscribe fn. */\nexport function subscribe(fn: () => void): () => void {\n subscribers.add(fn)\n return () => {\n subscribers.delete(fn)\n }\n}\n\n/** TESTING ONLY — wipe all state. Not exported from package barrel. */\nexport function __resetSidecarCacheForTests(): void {\n cache.clear()\n snapshotsByKey.clear()\n notify()\n}\n\n/** TESTING ONLY — current cache size. */\nexport function __cacheSizeForTests(): number {\n return cache.size\n}\n\n// -------------------- batch preload --------------------\n\n/**\n * Preload sidecars for the given `(rid, commit)` tuples. Skips already-ready\n * and in-flight entries. Groups by commit so a single message with N citations\n * from a single commit ⇒ exactly 1 network request.\n */\nexport async function preloadSidecars(\n files: Array<{ rid: string; commit: string }>,\n fetcher: BatchFetcher,\n): Promise<void> {\n // Dedupe input.\n const seen = new Set<string>()\n const todo: Array<{ rid: string; commit: string }> = []\n for (const f of files) {\n const k = cacheKey(f.rid, f.commit)\n if (seen.has(k)) continue\n seen.add(k)\n const existing = cache.get(k)\n if (existing && (existing.status === 'ready' || existing.status === 'loading')) continue\n todo.push(f)\n }\n if (todo.length === 0) return\n\n // Mark loading up front so concurrent callers dedupe.\n for (const { rid, commit } of todo) {\n setCachedSidecar(rid, commit, { status: 'loading' })\n }\n\n const groupedByCommit = new Map<string, string[]>()\n for (const { rid, commit } of todo) {\n const arr = groupedByCommit.get(commit) ?? []\n arr.push(rid)\n groupedByCommit.set(commit, arr)\n }\n\n await Promise.all(\n Array.from(groupedByCommit.entries()).map(async ([commit, rids]) => {\n try {\n const resp = await fetcher({ ids: rids.join(','), commit })\n for (const rid of rids) {\n const data = resp.items[rid]\n if (data) {\n setCachedSidecar(rid, commit, { status: 'ready', data })\n } else if (resp.stale.includes(rid)) {\n setCachedSidecar(rid, commit, { status: 'stale' })\n } else {\n // Default to `missing` when neither items nor stale list mentions it.\n setCachedSidecar(rid, commit, { status: 'missing' })\n }\n }\n } catch {\n for (const rid of rids) {\n setCachedSidecar(rid, commit, { status: 'missing' })\n }\n }\n }),\n )\n}\n\n/** Default fetcher hitting `/api/sidecar`. Override in tests / non-browser contexts. */\nexport const defaultFetcher: BatchFetcher = async ({ ids, commit }) => {\n const base =\n typeof window !== 'undefined' && window.location\n ? window.location.origin\n : 'http://localhost'\n const url = new URL('/api/sidecar', base)\n url.searchParams.set('ids', ids)\n if (commit) url.searchParams.set('commit', commit)\n const resp = await fetch(url.toString())\n if (!resp.ok) throw new Error(`sidecar api ${resp.status}`)\n return (await resp.json()) as BatchFetcherResponse\n}\n\n/** Pluggable global fetcher. Default points at `/api/sidecar`. */\nlet activeFetcher: BatchFetcher = defaultFetcher\nexport function setSidecarFetcher(fetcher: BatchFetcher | null): void {\n activeFetcher = fetcher ?? defaultFetcher\n}\nexport function getSidecarFetcher(): BatchFetcher {\n return activeFetcher\n}\n\n// -------------------- React hook --------------------\n\nfunction readStableSnapshot(rid: string, commit: string): SidecarSnapshot {\n const k = cacheKey(rid, commit)\n // Peek without bumping LRU — `useSyncExternalStore.getSnapshot` MUST be pure.\n const entry = cache.get(k)\n if (!entry) return EMPTY_IDLE_SNAPSHOT\n const cached = snapshotsByKey.get(k)\n if (cached && cached.status === entry.status && cached.sidecar === entry.data) {\n return cached\n }\n const snap: SidecarSnapshot = Object.freeze({\n status: entry.status,\n sidecar: entry.data,\n })\n snapshotsByKey.set(k, snap)\n return snap\n}\n\n/**\n * Reactive accessor for a single (rid, commit) entry. Triggers a lazy fetch on\n * `idle` (e.g. first hover). Returns a stable snapshot identity for the same\n * underlying entry so React can skip re-renders.\n */\nexport function useSidecar(rid: string, commit: string): SidecarSnapshot {\n const snapshot = useSyncExternalStore(\n subscribe,\n () => readStableSnapshot(rid, commit),\n () => EMPTY_IDLE_SNAPSHOT,\n )\n\n useEffect(() => {\n if (!rid || !commit) return\n if (snapshot.status === 'idle') {\n void preloadSidecars([{ rid, commit }], activeFetcher)\n }\n }, [rid, commit, snapshot.status])\n\n return snapshot\n}\n"],"mappings":"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","names":["useSyncExternalStore","subscribers","notify","useSyncExternalStore"]}
@@ -0,0 +1,25 @@
1
+ import {
2
+ __resetCitationContextForTests,
3
+ __resetForSessionSwitch,
4
+ clearMessageContext,
5
+ getAllKnownFiles,
6
+ getFileByRidShort,
7
+ getMessageContext,
8
+ ingestCitationContext,
9
+ persistFromMessage,
10
+ useCitationContext,
11
+ useFileByRidShort
12
+ } from "./chunk-X67V7257.js";
13
+ export {
14
+ __resetCitationContextForTests,
15
+ __resetForSessionSwitch,
16
+ clearMessageContext,
17
+ getAllKnownFiles,
18
+ getFileByRidShort,
19
+ getMessageContext,
20
+ ingestCitationContext,
21
+ persistFromMessage,
22
+ useCitationContext,
23
+ useFileByRidShort
24
+ };
25
+ //# sourceMappingURL=citationContext-CILHTO2Z.js.map