---
title: Google Sheets integration (agent-driven spreadsheets with rich formatting)
---
# Google Sheets integration (agent-driven spreadsheets with rich formatting)

## What it is

Omniscio's Google Sheets integration lets you view and edit Google Sheets directly inside Omniscio. The built-in Sheets virtual project renders spreadsheets with their native Google Sheets formatting — background colors, text colors, bold, italic, font size, horizontal alignment, and actual column widths — so the grid inside Omniscio looks like the real spreadsheet, not a plain-text table. Claude also gets ten Sheets tool-use calls inside any chat conversation, so you can ask in plain English ("read the first sheet of the Q1 budget and tell me total spend", "add a row with today's date and these values to my expenses sheet") and the agent decides which tool to call. Sheets shares the same single "Connect Google" OAuth as Calendar, Drive, and Gmail — one consent, all four integrations.

## Where to find it

Connect Google at **Settings → Google**, turn Sheets on at **Settings → Google Sheets**, then open the built-in **Sheets** virtual project in the sidebar.

## How it behaves

### How to use it

### Connecting and enabling

1. **Connect Google once.** Settings → **Google** → **Connect Google**. A browser tab opens; approve the scopes for Calendar, Drive, Sheets, and Gmail. Omniscio catches the OAuth redirect on a loopback HTTP server, encrypts the refresh token via `safeStorage`, and stores it.
2. **Enable Sheets.** Settings → **Google Sheets** → toggle `sheetsEnabled` on. The OAuth scopes were already requested at connect time; this flag controls whether Omniscio actually sends Sheets requests at runtime.

### Viewing spreadsheets with rich formatting

Open the **Sheets** virtual project in the sidebar. Select a spreadsheet from the list. Omniscio renders the sheet grid with formatting that matches Google Sheets:

- **Background colors** — colored cells display their actual background (default white is filtered out to keep the grid clean).
- **Text colors** — non-black text renders in its Google Sheets color.
- **Bold, italic** — text weight and style are preserved.
- **Font size** — non-default sizes (anything other than 10pt) are rendered at their actual size.
- **Horizontal alignment** — LEFT, CENTER, and RIGHT alignment is applied to each cell.
- **Column widths** — columns render at their actual Google Sheets pixel width instead of a fixed 100px.

Previously, all cells were plain text at fixed-width columns. The backend now uses `spreadsheets.get` with `includeGridData: true` to fetch values and formatting in a single API call, rather than the old `spreadsheets.values.get` which returned raw values only. Default formatting (white background, black text, 10pt Arial) is filtered out to keep the data payload clean.

### The spreadsheet list — ordering, pinning, and trusted auto-pin

The Sheets list shows your recently modified spreadsheets, but you can keep specific sheets at the top:

- **Pin / unpin** — hover a row and click the pin icon to pin a spreadsheet (on touch/mobile the icon is always shown). Pinned sheets sort above the unpinned, recently-modified ones; click the pin again to unpin.
- **Trusted sheets are auto-pinned** — any spreadsheet configured as a Trusted Sheet (see below) shows a solid pin with **no unpin button** (its tooltip reads "Trusted spreadsheet (auto-pinned)") and always sorts to the very top, above manually-pinned sheets. Trusted sheets are managed from Settings, not from this list.
- **Ordering** — the list is grouped **trusted → pinned → recent**. When any trusted or pinned sheet is present, a small **Pinned** header appears above the group.
- **Always visible** — trusted and pinned spreadsheets appear even when they fall outside your recent-activity window, so a sheet you rarely edit but rely on never drops off the list. Omniscio fetches each pinned/trusted sheet the recent list didn't already return and prepends it.

### Asking the agent

In any Claude session, type natural-language requests. Ten tools are available: list spreadsheets, get spreadsheet metadata, read a range, update cells, create a new spreadsheet, add a sheet/tab, delete a sheet, insert rows, insert columns, and apply formatting. Results render as markdown tables; ranges and updates link back to the spreadsheet.

### Trusted Sheets (column-level edit permissions)

### What trusted sheets are

By default, double-clicking a cell in the Sheets grid shows a warning dialog about Apps Scripts and formulas before enabling editing. **Trusted Sheets** let you mark specific spreadsheets as safe and specify which columns are editable. For a trusted sheet, the editable columns unlock automatically with no warning dialog, while non-editable columns remain locked.

### Visual indicators

When a trusted sheet is open:

- **Editable column headers** are tinted green (`bg-green-50 text-green-700`), signaling that those columns accept input.
- **Non-editable columns** show reduced opacity and a default cursor (not the cell-edit cursor).
- The **lock button** in the sheet editor header shows the editable column letters (e.g., "C, D editable") instead of the generic "Locked" label.
- Double-clicking an **editable column** starts editing immediately — no confirmation dialog.
- Double-clicking a **non-editable column** shows a "Column not editable" message explaining that only the configured columns are editable, with a pointer to Settings.

### How to set up a trusted sheet

1. Open the spreadsheet in Omniscio's Sheets project to verify it loads correctly.
2. Go to **Settings → Google Sheets → Trusted Sheets → Add trusted sheet**.
3. Enter the **Spreadsheet ID** — the long string from the Google Sheets URL between `/d/` and `/edit` (e.g., `1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms`).
4. Enter a **Label** (e.g., "PLMS") — a friendly name shown in the settings list.
5. Enter the **Editable columns** as comma-separated letters (e.g., "C, D"). These are converted to 0-based column indices internally.
6. Click **Add**.

To remove a trusted sheet, click the trash icon next to it in the Settings list. A confirmation dialog prevents accidental deletion.

### How it works

### Rich formatting pipeline

The rich data path flows through these layers:

1. **Backend API** — `getSheetDataRich()` in [/src/main/services/sheets-api-service.ts](/src/main/services/sheets-api-service.ts) calls the Google Sheets API with `spreadsheets.get({ includeGridData: true })` and a targeted `fields` mask to fetch only `effectiveValue`, `formattedValue`, `effectiveFormat`, and `columnMetadata.pixelSize`. It returns `SheetDataRichResult` containing `CellRichValue[][]` (cells with optional `CellFormat`) and `ColumnMeta[]` (column pixel widths).
2. **IPC channel** — `SHEETS_READ_RICH` in [/src/main/ipc/sheets-handlers.ts](/src/main/ipc/sheets-handlers.ts) exposes the rich data to the renderer. Zod-validated input: `{ spreadsheetId, range }`.
3. **Store** — `useSheetsStore` in [/src/renderer/src/stores/sheets-store.ts](/src/renderer/src/stores/sheets-store.ts) calls `SHEETS_READ_RICH` in `loadSheetData()` and stores the result as `cellData` (rich cells) and `columnWidths` (column metadata). The store also holds `trustedConfig` (the active `TrustedSheet | null`) and exposes `isColumnEditable(col)` and `setTrustedConfig()`.
4. **Grid rendering** — `buildCellStyle()` in [/src/renderer/src/features/sheets/cell-format-helpers.ts](/src/renderer/src/features/sheets/cell-format-helpers.ts) converts `CellFormat` into inline CSS properties (backgroundColor, color, fontWeight, fontStyle, fontSize, textAlign, justifyContent, width). The `CellGrid` component in [/src/renderer/src/features/sheets/CellGrid.tsx](/src/renderer/src/features/sheets/CellGrid.tsx) applies these styles to each cell and uses dynamic column widths in the virtualizer.

### Trusted sheets configuration

- **Types** — `CellRichValue` (extends `CellValue` with optional `CellFormat`), `ColumnMeta` (carries `widthPx`), and `TrustedSheet` (carries `spreadsheetId`, `label`, `editableColumns` as 0-based column indices) are defined in [/src/shared/types.ts](/src/shared/types.ts).
- **AppSettings** — `trustedSheets: TrustedSheet[]` persisted in `config.json`. Zod schema in [/src/shared/ipc-schemas.ts](/src/shared/ipc-schemas.ts) validates each entry (max 50 sheets, column indices 0–702).
- **Store** — `isColumnEditable(col)` returns `true` if the column is in the active `trustedConfig.editableColumns`, or if no trusted config is active and `readOnly` is false. `startEditing(row, col)` checks editability and either enters edit mode (editable column) or sets `editBlockedAt` (non-editable column, triggers the dialog).
- **Settings UI** — `SheetsSettings` component in [/src/renderer/src/features/settings/sections/sheets/SheetsSettings.tsx](/src/renderer/src/features/settings/sections/sheets/SheetsSettings.tsx) renders the add/remove interface. Column letters (A, B, C, ...) are converted to 0-based indices on save and back to letters for display.

### Spreadsheet list ordering

`fetchSpreadsheets()` in [/src/renderer/src/stores/sheets-store.ts](/src/renderer/src/stores/sheets-store.ts) is the single source for the list: it loads the recent spreadsheets (`SHEETS_LIST`), reads `trustedSheets` + `pinnedSheetIds` from settings in the same pass, then back-fills any trusted/pinned sheet missing from the recent results via `SHEETS_GET` and prepends them (this replaced the former standalone `loadPinnedSheets` action and its mount effect). [/src/renderer/src/features/sheets/SpreadsheetList.tsx](/src/renderer/src/features/sheets/SpreadsheetList.tsx) sorts by priority — trusted `0`, pinned `1`, recent `2` — and renders the **Pinned** header when any priority sheet is present; [/src/renderer/src/features/sheets/SpreadsheetListItem.tsx](/src/renderer/src/features/sheets/SpreadsheetListItem.tsx) derives `isPinned`/`isTrusted` from the store and renders trusted rows as a static (non-toggle) pin.

### Agent tool-use path (unchanged)

Google OAuth lives in [/src/main/services/google/google-auth-service.ts](/src/main/services/google/google-auth-service.ts) — single flow, loopback HTTP redirect, encrypted refresh token. The Sheets AI service is [/src/main/services/sheets-ai-service.ts](/src/main/services/sheets-ai-service.ts): ten tools (`list_spreadsheets`, `get_spreadsheet`, `read_range`, `update_cells`, `create_spreadsheet`, `add_sheet`, `delete_sheet`, `insert_rows`, `insert_columns`, `format_cells`) wrapping the `googleapis` npm package. They run inside a token loop — _agent message → Claude returns tool_use → Omniscio executes via googleapis → result appended → repeat until `stop_reason='end_turn'`_ — with a circuit breaker that backs off on repeated API failures and per-account cost tracking. Errors return as tool results so the agent can recover. IPC handler: [/src/main/ipc/sheets-handlers.ts](/src/main/ipc/sheets-handlers.ts) (channels `SHEETS_AI_CHAT`, `SHEETS_READ_RICH`, plus non-AI list/CRUD endpoints). Settings flag: `sheetsEnabled` in [/src/shared/types.ts](/src/shared/types.ts).

## Related

- [INDEX.md](INDEX.md) — full library index
- [google-integrations.md](google-integrations.md) — umbrella doc for Calendar + Drive + Sheets, shared OAuth
- [drive-integration.md](drive-integration.md) — sibling Google integration with similar agent-driven UX
- [gmail-integration.md](gmail-integration.md) — Gmail uses the same Google OAuth flow
