Skip to content

feat(desktop): add Excel/spreadsheet viewer with diff support#1

Merged
MocA-Love merged 1 commit intomainfrom
feat/excel-viewer
Mar 27, 2026
Merged

feat(desktop): add Excel/spreadsheet viewer with diff support#1
MocA-Love merged 1 commit intomainfrom
feat/excel-viewer

Conversation

@MocA-Love
Copy link
Copy Markdown
Owner

@MocA-Love MocA-Love commented Mar 27, 2026

Summary

  • Excel系ファイル(.xlsx, .xls, .xlsm, .xlsb, .ods)をバイナリではなくスプレッドシートとして表示できるようにした
  • ExcelJSによるフル書式対応(罫線、結合セル、フォント、テーマカラー、リッチテキスト、行高さ、テキスト折り返し、縦書き)
  • printArea対応による正確な表示範囲制限
  • コンテナ幅への自動フィット
  • 複数シートのタブ切り替え
  • サイドバイサイドdiffビューア(セル単位の変更ハイライト、青枠線表示)
  • diff内ナビゲーション(Prev/Next)、左右同期スクロール
  • gitからバイナリファイルを取得するreadGitFileBinary tRPCプロシージャを追加

Changes

New files (6)

  • SpreadsheetViewer/ — Excel表示コンポーネント群
    • parseWorkbook.ts — ExcelJSパースロジック(書式、罫線、テーマカラー、結合セル対応)
    • SpreadsheetViewer.tsx — 通常表示コンポーネント
    • SpreadsheetDiffViewer.tsx — サイドバイサイドdiff表示
    • useSpreadsheetData.ts — ファイル読み込み+パースhook
    • useSpreadsheetDiff.ts — diff用データ取得+比較hook
    • index.ts — barrel export

Modified files (7)

  • file-types.tsisSpreadsheetFile() 追加 (+10行)
  • file-contents.tsreadGitFileBinary tRPCプロシージャ追加 (+49行)
  • FileViewerContent.tsx — バイナリ/diff分岐にスプレッドシート判定追加 (+37行)
  • FileViewerPane.tsx — 新props受け渡し (+4行)
  • WorkspaceFilePreviewContent.tsx — V2プレビューにスプレッドシート分岐 (+10行)
  • package.json — exceljs依存追加
  • bun.lock — lockfile更新

Test plan

  • .xlsxファイルを開いてスプレッドシット表示を確認
  • 複数シートのタブ切り替えを確認
  • 結合セル・罫線・色・フォントが正しく表示されるか確認
  • ウィンドウリサイズで列幅が自動調整されるか確認
  • xlsxファイルを変更してChangesパネルからdiff表示を確認
  • Prev/Nextボタンで差分箇所にジャンプできるか確認
  • 左右パネルのスクロール同期を確認
  • 通常のテキストファイルのdiff表示に影響がないか確認

Summary by CodeRabbit

Release Notes

  • New Features
    • Added spreadsheet file preview - Spreadsheet files are now viewable directly in the file previewer with support for multiple sheets and formatting display
    • Added spreadsheet diff viewer - Review spreadsheet changes side-by-side between different versions to identify added, removed, and modified cells

Add the ability to view .xlsx, .xls, .xlsm, .xlsb, and .ods files
natively instead of showing "Binary file preview not supported".

Features:
- Full spreadsheet rendering with ExcelJS (formatting, borders, merged
  cells, fonts, colors, theme colors with tint, rich text, row heights,
  text wrapping, vertical text)
- Print area aware column/row range detection
- Auto fit-to-width column scaling
- Multiple sheet tab navigation
- Side-by-side diff viewer with cell-level change highlighting
- Diff navigation (Prev/Next) with synchronized scrolling
- Git binary file read via new readGitFileBinary tRPC procedure
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 88324a1e-ae63-41d2-b4ae-0a079561e643

📥 Commits

Reviewing files that changed from the base of the PR and between 2c38833 and a7816ee.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • apps/desktop/package.json
  • apps/desktop/src/lib/trpc/routers/changes/file-contents.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/v2-workspace/$workspaceId/components/WorkspaceFiles/components/WorkspaceFilePreview/components/WorkspaceFilePreviewContent/WorkspaceFilePreviewContent.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/FileViewerPane.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/FileViewerContent/FileViewerContent.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetDiffViewer.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/SpreadsheetViewer.tsx
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/index.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/parseWorkbook.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetData.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/ContentView/TabsContent/TabView/FileViewerPane/components/SpreadsheetViewer/useSpreadsheetDiff.ts
  • apps/desktop/src/shared/file-types.ts

📝 Walkthrough

Walkthrough

This PR adds comprehensive spreadsheet file viewing and diffing support to the desktop application. It introduces a new tRPC endpoint for reading binary git blobs, Excel workbook parsing logic, dedicated UI components for viewing spreadsheets, and routing updates to display spreadsheets in specialized viewers instead of generic binary file messages.

Changes

Cohort / File(s) Summary
Dependencies
apps/desktop/package.json
Added exceljs (^4.4.0) dependency for Excel workbook parsing.
Binary File Reading
apps/desktop/src/lib/trpc/routers/changes/file-contents.ts
Introduced new readGitFileBinary tRPC procedure that fetches binary git blobs with size validation and base64 encoding.
Shared Utilities
apps/desktop/src/shared/file-types.ts
Added isSpreadsheetFile() utility function and SPREADSHEET_EXTENSIONS set to detect spreadsheet file formats (xlsx, xls, xlsm, xlsb, ods).
Spreadsheet Parsing & Hooks
apps/desktop/src/renderer/screens/.../SpreadsheetViewer/parseWorkbook.ts, useSpreadsheetData.ts, useSpreadsheetDiff.ts
Implemented Excel workbook parser, data-fetching hook with size limits, and diff-computation hook that loads two workbook versions and computes per-cell changes.
Spreadsheet Viewer Components
apps/desktop/src/renderer/screens/.../SpreadsheetViewer/SpreadsheetViewer.tsx, SpreadsheetDiffViewer.tsx, index.ts
Added two new React components: SpreadsheetViewer for displaying active spreadsheets with multi-sheet support and synchronized side-by-side panes, and SpreadsheetDiffViewer for rendering diff results with visual change indicators. Barrel export file created for component imports.
File Preview & Viewer Integration
apps/desktop/src/renderer/.../WorkspaceFilePreviewContent.tsx, FileViewerPane.tsx, FileViewerContent.tsx
Updated file preview and viewer components to detect spreadsheet files and route them to dedicated viewers; added context props (workspaceId, worktreePath, diffCategory, commitHash) for spreadsheet-aware rendering paths.

Sequence Diagrams

sequenceDiagram
    participant User
    participant UI as File Viewer
    participant TRPC as tRPC Client
    participant Server
    participant Git as Git Repository
    participant Parser as Workbook Parser
    participant Renderer as Spreadsheet Renderer

    User->>UI: View spreadsheet file
    UI->>TRPC: readFile(filePath) query
    TRPC->>Server: Fetch file data
    Server->>Git: cat-file -p (read blob)
    Git-->>Server: Binary blob
    Server-->>TRPC: Base64-encoded content
    TRPC-->>UI: File data received
    
    UI->>Parser: parseWorkbook(base64Content)
    Parser->>Parser: Decode & extract sheets, rows, cells
    Parser->>Parser: Apply styling, merges, formulas
    Parser-->>UI: ParsedSheet[]
    
    UI->>Renderer: Render with sheets, column widths
    Renderer->>Renderer: Calculate column widths
    Renderer->>Renderer: Render rows with cells & styling
    Renderer-->>User: Spreadsheet table display
Loading
sequenceDiagram
    participant User
    participant DiffUI as Spreadsheet Diff Viewer
    participant Hook as useSpreadsheetDiff
    participant TRPC as tRPC Client
    participant Server
    participant Parser as Workbook Parser
    participant Differ as Diff Engine
    participant Renderer as Diff Renderer

    User->>DiffUI: View spreadsheet diff
    DiffUI->>Hook: Load diff (workspaceId, filePath, diffCategory, commitHash)
    
    Hook->>TRPC: Query original version (HEAD)
    TRPC->>Server: Fetch blob at HEAD:path
    Server-->>TRPC: Base64 content
    
    Hook->>TRPC: Query modified version (working copy)
    TRPC->>Server: Fetch file from worktree
    Server-->>TRPC: Base64 content
    
    Hook->>Parser: parseWorkbook(original base64)
    Parser-->>Hook: ParsedSheet[] (original)
    Hook->>Parser: parseWorkbook(modified base64)
    Parser-->>Hook: ParsedSheet[] (modified)
    
    Hook->>Differ: Align sheets, compute row/cell diffs
    Differ->>Differ: Mark cells as added/removed/modified
    Differ-->>Hook: DiffParsedSheet[]
    Hook-->>DiffUI: Diff data ready
    
    DiffUI->>Renderer: Render side-by-side tables with diff colors
    Renderer->>Renderer: Color cells (red=removed, green=added, yellow=modified)
    Renderer->>Renderer: Sync scroll positions between panes
    Renderer-->>User: Synchronized diff display
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Spreadsheets hop into view,
Excel sheets parsed clean and true!
Side-by-side, the diffs align—
With colors bright, changes shine,
From binary blobs to tables fine! ✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/excel-viewer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@MocA-Love MocA-Love self-assigned this Mar 27, 2026
@MocA-Love MocA-Love merged commit ed0f764 into main Mar 27, 2026
1 check was pending
@MocA-Love MocA-Love deleted the feat/excel-viewer branch March 29, 2026 05:05
MocA-Love added a commit that referenced this pull request Apr 4, 2026
#1 ダッシュボード戻り時のポーリング永続: activeWorkspaceId=null時に
   deactivateAll()を呼ぶ。setActiveWorkspace(null)対応追加

#2 deactivateされたWSのMapエントリ蓄積: 問題の根本はregisterが増え続ける
   ことではなく、全WSがisActive:trueで起動していたこと。#3で解消

#3 起動直後の全WSポーリング並走: registerWorkspaceをisActive:falseに変更。
   タイマーはactivateWorkspace/setActiveWorkspaceでのみ起動

#4 WS切替時の初回表示5s遅延: activateWorkspace/setActiveWorkspace時に
   即時sync(syncPRStatus+syncPRComments)を実行

追加改善:
- startTimersに防衛的stopTimers追加(二重タイマー防止)
- onWindowFocus()をデッドコードとして削除
- deactivateAll()メソッド追加
MocA-Love added a commit that referenced this pull request Apr 21, 2026
…sions

#1 Target.closeTarget UI integrity
  v1/v2 secondary tab registry が webview "close" イベントを購読。
  MCP の Target.closeTarget で Chromium が webContents を破棄すると
  guest 側が close を発火し、registry が closeTab → unregisterTab
  経由で paneTabTargetIds を整理。tab バー UI と CDP allowedTargetIds
  が同期した状態を保つ。

#2 target="_blank" / window.open を MCP 可視に
  windowOpenHandler の非 new-window 分岐で new-window event を emit
  していた箇所を create-tab-requested:${paneId} に置換。同じペイン
  内の secondary tab として生成されるので paneTabTargetIds に入り、
  MCP が list_pages / select_page で扱える。Chrome の target="_blank"
  デフォルト (新タブ) 挙動に揃う。split-pane / workspace-tab が
  欲しいケースは既存の "Open in Split" コンテキストメニューでカバー。

#3 非 media 権限の UI prompt 化
  SITE_PERMISSION_KINDS に geolocation / notifications / clipboard-read
  を追加。browser-site-permission-manager が Electron の
  setPermissionRequestHandler で media 同様の consent flow に乗せる。
  既存の permissionRequested イベント経路はそのまま再利用。
  認識しない permission は従来通り permissive で許可。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant