diff --git a/docs/security-architecture.md b/docs/security-architecture.md index d1153fbf..072cd369 100644 --- a/docs/security-architecture.md +++ b/docs/security-architecture.md @@ -354,8 +354,9 @@ is by id (`GET /api/sessions/:id/attachments/:attachmentId/raw`, same download c the `/root` and `/etc` trees, extendable via `attachmentBlockedPaths` / `CODEMAN_ATTACHMENT_BLOCKED_PATHS`) on every request. Unlike the workspace file routes, attachments are intentionally **cross‑workspace** — so the effective gate -is the blocklist + a 6‑extension allowlist (`png/pdf/docx/pptx/md/txt`), not -realpath containment. +is the blocklist + an extension allowlist (`SUPPORTED_ATTACHMENT_EXTENSIONS` in +`src/attachment-registry.ts`: images, pdf/docx/pptx/xlsx, audio/video, md/txt and +other text), not realpath containment. Two registration paths, with **different trust**: diff --git a/src/web/public/i18n.js b/src/web/public/i18n.js index 9f791263..ff810617 100644 --- a/src/web/public/i18n.js +++ b/src/web/public/i18n.js @@ -858,6 +858,18 @@ 'Wrap lines': '自动换行', 'Unsaved changes': '未保存的更改', Saved: '已保存', + 'Loading spreadsheet…': '正在加载电子表格…', + 'This workbook is too large to preview (10 MB limit).': '此工作簿太大,无法预览(上限 10 MB)。', + 'This workbook has no visible worksheets.': '此工作簿没有可见的工作表。', + 'This worksheet is empty.': '此工作表为空。', + 'Some workbook features are not shown': '部分工作簿功能未显示', + 'Spreadsheet preview timed out.': '电子表格预览超时。', + 'Spreadsheet preview failed': '电子表格预览失败', + 'Spreadsheet parser failed.': '电子表格解析器出错。', + 'Spreadsheet parser failed to start': '电子表格解析器启动失败', + 'Spreadsheet parser message failed.': '电子表格解析器消息出错。', + 'Spreadsheet parser message failed': '电子表格解析器消息出错', + 'Spreadsheet preview is unavailable.': '电子表格预览不可用。', 'Export as JSON': '导出为 JSON', 'Export as Markdown': '导出为 Markdown', 'Mark all read': '全部标为已读', diff --git a/src/web/public/spreadsheet-preview.js b/src/web/public/spreadsheet-preview.js index 2930c121..d43334a1 100644 --- a/src/web/public/spreadsheet-preview.js +++ b/src/web/public/spreadsheet-preview.js @@ -251,6 +251,13 @@ }); } for (const cell of tile.cells.slice(0, 2500)) { + const merge = mergeByAnchor.get(`${cell.row}:${cell.col}`); + const height = rowSpan(cell.row, merge?.r2 || cell.row); + const width = colSpan(cell.col, merge?.c2 || cell.col); + // A cell clipped to nothing at the spacer's edge (or sized 0 px) is + // skipped like its heading: padding and border would still draw it as a + // small box below the spacer and grow the scroll area. + if (height <= 0 || width <= 0) continue; const element = document.createElement('div'); element.className = `spreadsheet-cell spreadsheet-style-${Number(cell.styleId) || 0}`; element.dataset.row = String(cell.row); @@ -258,9 +265,8 @@ element.textContent = String(cell.text ?? ''); element.style.top = `${rowTop(cell.row)}px`; element.style.left = `${colLeft(cell.col)}px`; - const merge = mergeByAnchor.get(`${cell.row}:${cell.col}`); - element.style.height = `${rowSpan(cell.row, merge?.r2 || cell.row)}px`; - element.style.width = `${colSpan(cell.col, merge?.c2 || cell.col)}px`; + element.style.height = `${height}px`; + element.style.width = `${width}px`; cellsLayer.appendChild(element); } // Headings take their size from the same axis math as the cells, so custom