Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
140 commits
Select commit Hold shift + click to select a range
0fe9eba
fix(charts): hovered bar disappearing, and a softer hover band
broisnischal Aug 6, 2026
57b9aba
fix(pg): decode interval columns instead of dumping their wire bytes
broisnischal Aug 6, 2026
b83ac7e
perf(db): take three fixed costs off the connection path
broisnischal Aug 6, 2026
5be6912
feat(ai): set up OmniRoute from the app, and filter to free models
broisnischal Aug 6, 2026
90d8aab
feat(sidebar): list the server's other databases, and filter every se…
broisnischal Aug 6, 2026
088dc5a
feat(ui): one alert dialog, and stop the connection modal dismissing …
broisnischal Aug 6, 2026
456db10
feat(ui): drop the last native selects, and make every field border m…
broisnischal Aug 6, 2026
288da3b
fix(ui): window chrome stays clickable under a modal; icon and theme …
broisnischal Aug 6, 2026
078084f
feat(export): PNG, SVG and copy for charts, ERDs and diagrams
broisnischal Aug 6, 2026
d61c279
fix(ai): tables render and scroll, responses follow the font, smoothe…
broisnischal Aug 6, 2026
18988ed
feat(loading): credits in the loading rotation, with no repeats
broisnischal Aug 6, 2026
99a23fe
chore: sync lockfile with the 1.20.0 release
broisnischal Aug 6, 2026
eac3e3e
fix(ai): remove the dangling _scrollRafId cleanup that crashed the chat
broisnischal Aug 6, 2026
653baba
perf(connect): bound every connect attempt, and stop the license chec…
broisnischal Aug 6, 2026
29406dd
feat(insights): edit server settings from the Insights page
broisnischal Aug 7, 2026
884d1fa
feat(connect): open the databases already running on this machine
broisnischal Aug 7, 2026
228da13
feat(schema): read the live schema as Prisma or Drizzle
broisnischal Aug 7, 2026
7afe125
feat(pg): decode pgvector and PostGIS values, and make a vector readable
broisnischal Aug 7, 2026
728c712
fix(erd): keep relationship lines out of the cards, and the cards apart
broisnischal Aug 7, 2026
4cf8859
perf(connect): stop restarting handshakes that were about to succeed
broisnischal Aug 7, 2026
bed8ada
fix(ai): retry the stream, recover printed tool calls, say what failed
broisnischal Aug 7, 2026
4b42338
fix(ui): read-only writes, the virtual columns panel, the sidebar pla…
broisnischal Aug 7, 2026
531eaf7
fix(json): size the cell lightbox to its content
broisnischal Aug 7, 2026
deea575
fix(vector): keep the dialog's close button off the Raw tab
broisnischal Aug 7, 2026
71ad27d
feat(insights): rework the Instance Insights dashboard
broisnischal Aug 7, 2026
380c6a0
fix(pg): read columns that have no binary output instead of failing t…
broisnischal Aug 7, 2026
f98fe61
feat(geo): map view for PostGIS layers
broisnischal Aug 7, 2026
4b4a179
fix(table): don't claim a table is empty while its rows are still loa…
broisnischal Aug 7, 2026
1e10903
test(fixtures): PostGIS and pgvector databases for testing extension …
broisnischal Aug 7, 2026
385b60d
chore: stop tracking local agent notes and personal SQL notebooks
broisnischal Aug 8, 2026
5bd638b
chore: drop the tracked todo list
broisnischal Aug 8, 2026
5ec862b
ci(cla): match bot accounts by pattern
broisnischal Aug 8, 2026
5509f6e
chore(cargo): fill in the package metadata, and abort on panic in rel…
broisnischal Aug 8, 2026
e4fb853
chore(npm): describe the package instead of leaving the defaults
broisnischal Aug 8, 2026
b322102
docs(license): state the source-available terms in full
broisnischal Aug 8, 2026
78aa4ce
docs(readme): Redis, the license terms, and shortcuts that match the app
broisnischal Aug 8, 2026
5d3d773
docs: add a contributing guide
broisnischal Aug 8, 2026
91be241
test(fixtures): use a multi-arch PostGIS image
broisnischal Aug 8, 2026
53c16af
perf(pg): route common column types instead of walking the whole deco…
broisnischal Aug 8, 2026
7b1b6c0
perf(mysql): route common column types, and share the capped-cell hel…
broisnischal Aug 8, 2026
2597f25
perf(mysql): read a table's catalog metadata once per fetch
broisnischal Aug 8, 2026
b018380
perf(sql): convert rows to JSON as they stream instead of holding both
broisnischal Aug 8, 2026
1231867
fix(mysql): run an INSERT and its LAST_INSERT_ID on one connection
broisnischal Aug 8, 2026
89ebb66
perf(db): delete rows with a single-column primary key in batches
broisnischal Aug 8, 2026
37b0573
perf(db): skip the PK/FK lookups when the frontend already holds them
broisnischal Aug 8, 2026
b36e121
fix(pg): give the background row count its own timeout
broisnischal Aug 8, 2026
cd25a2d
perf(schema): batch the incoming-FK pragmas for D1 and libSQL
broisnischal Aug 8, 2026
2352409
fix(cells): cap oversized values on the remaining engines
broisnischal Aug 8, 2026
29d87b2
perf(clickhouse): run the count and the page concurrently
broisnischal Aug 8, 2026
9734cd3
fix(backup): say when catalog objects could not be exported
broisnischal Aug 8, 2026
44584e0
fix(backup): export MySQL decimals, dates and unsigned bigints correctly
broisnischal Aug 8, 2026
d8fe7eb
fix(backup): restore a MySQL dump on a single connection
broisnischal Aug 8, 2026
a948fb0
fix(live): stop a watcher whose connection is gone
broisnischal Aug 8, 2026
511fd47
feat(ai): cancel an in-flight streaming request in the backend
broisnischal Aug 8, 2026
49670c8
perf(license): compute the device fingerprint once, and reuse the HTT…
broisnischal Aug 8, 2026
1c90cd3
fix(omniroute): kill the proxy when the app exits
broisnischal Aug 8, 2026
0ff95d6
refactor(mcp): one serializer for truncated results
broisnischal Aug 8, 2026
f5a3d59
refactor(providers): iterate provider JSON without cloning it
broisnischal Aug 8, 2026
ae74a69
fix(d1): pick the local miniflare database by stored size, not the ma…
broisnischal Aug 8, 2026
42d348b
chore(db): prune stale comments and an unused import
broisnischal Aug 8, 2026
a819337
fix(connections): delete a connection's data along with the connection
broisnischal Aug 8, 2026
4e30094
fix(connections): stop defaulting every engine's port to 5432
broisnischal Aug 8, 2026
d1cc344
feat(connections): remember the last schema per connection
broisnischal Aug 8, 2026
f1d4d80
fix(stores): flush a pending save before switching connection
broisnischal Aug 8, 2026
1dc018c
perf(history): find the last statement with a cursor, not the whole log
broisnischal Aug 8, 2026
320755e
fix(snapshots): don't store an empty snapshot, and quote identifiers …
broisnischal Aug 8, 2026
576866f
refactor(tabs): one factory and one title map for the singleton tabs
broisnischal Aug 8, 2026
cf1ba27
perf(charts): index lookups by map, and guard the sunburst against cy…
broisnischal Aug 8, 2026
73cd8f3
refactor(api): route every command through inv(), and drop unused wra…
broisnischal Aug 8, 2026
4fdbd45
feat(ai): tell the backend to stop downloading when a stream is cance…
broisnischal Aug 8, 2026
44ffd7f
feat(vector): show the standard deviation and the value distribution
broisnischal Aug 8, 2026
b9a1b35
feat(geo): a viewer for geometry cells
broisnischal Aug 8, 2026
07d22eb
perf(grid): skip the columns scrolled off to the left
broisnischal Aug 8, 2026
bf43b10
perf(grid): let the scroll loop own the frame it is painting
broisnischal Aug 8, 2026
31e3879
fix(grid): scope saved column order to the connection
broisnischal Aug 8, 2026
342b749
perf(grid): resolve export column indices once, and drop stale vector…
broisnischal Aug 8, 2026
e54a0c8
fix(grid): release every parent-bound callback on destroy
broisnischal Aug 8, 2026
18a81ff
chore(grid): remove the parked range-selection sources and dead helpers
broisnischal Aug 8, 2026
fb07663
feat(shell): reopen on the schema you were last using
broisnischal Aug 8, 2026
b068674
perf(shell): stop the tab-row cache and the MRU growing forever
broisnischal Aug 8, 2026
1390e57
perf(shell): keep infinite-scroll rows out of the reactive proxy
broisnischal Aug 8, 2026
3656b52
fix(shell): drop the schema caches when the connection changes
broisnischal Aug 8, 2026
8d92857
perf(shell): load SQL completion hints once per connection and schema
broisnischal Aug 8, 2026
50eb432
feat(shell): a window title that says which database you are in
broisnischal Aug 8, 2026
788a46f
refactor(shell): one guard for closing a tab with unsaved changes
broisnischal Aug 8, 2026
30a64cd
refactor(shell): reuse the shared SQL and primary-key helpers
broisnischal Aug 8, 2026
ba494d7
feat(status): show how many rows are selected
broisnischal Aug 8, 2026
69a9ae3
chore(shell): drop props, imports and helpers nothing reads
broisnischal Aug 8, 2026
0378d91
perf(ai): fetch SQLite table info in parallel
broisnischal Aug 8, 2026
13e2e2e
fix(ai): answer pending confirmations when a turn is stopped
broisnischal Aug 8, 2026
2f5b247
fix(ai): Esc stops generation, and sampled rows don't cross connections
broisnischal Aug 8, 2026
11df633
feat(ai): read SQLite, D1 and libSQL schemas from the sidebar tools
broisnischal Aug 8, 2026
a8c6a4b
perf(ai): track the transcript's bottom with an observer
broisnischal Aug 8, 2026
23a9e0f
chore(ai): remove the unused image viewer and dead parameters
broisnischal Aug 8, 2026
82d0c02
refactor(export): one row-to-object helper
broisnischal Aug 8, 2026
d4fb320
perf(sql): reuse one collator when sorting results
broisnischal Aug 8, 2026
aa2c9b8
perf(sql): cache the statement split per editor revision
broisnischal Aug 8, 2026
4484ab7
perf(monaco): drop the per-editor theme observers
broisnischal Aug 8, 2026
7213b52
perf(data-diff): debounce the filter and yield while comparing
broisnischal Aug 8, 2026
702ac33
perf(schema-timeline): cap the diff matrix, and report a failed delete
broisnischal Aug 8, 2026
22eeb7f
perf(redis): bound the TYPE fan-out and skip offscreen keys
broisnischal Aug 8, 2026
3d74f87
perf(structure): coalesce column resizes to one per frame
broisnischal Aug 8, 2026
7511717
perf(sidebar): check the key before touching layout
broisnischal Aug 8, 2026
ba1f4b5
fix(connect): surface provider sign-in failures instead of hiding them
broisnischal Aug 8, 2026
51d9e41
fix(ai-settings): stop the Copilot model refetch loop, and report sav…
broisnischal Aug 8, 2026
601cc6d
fix(json): align the tree's indent guide and closing bracket
broisnischal Aug 8, 2026
e25527d
fix(row-expand): fill the frozen gutter beside an expanded row
broisnischal Aug 8, 2026
5c14be2
fix(charts): export this panel's canvas, not the first one on the page
broisnischal Aug 8, 2026
2e01c8e
fix(backup): key log rows by sequence, and cap the buffers
broisnischal Aug 8, 2026
efd564c
fix(search): stop the workers when the page or palette closes
broisnischal Aug 8, 2026
f76b5b8
fix(shortcuts): clear the search and focus it on open
broisnischal Aug 8, 2026
2ba9673
perf(ui): stop background work the user can't see
broisnischal Aug 8, 2026
92977d2
fix(ui): clear timers and drag listeners when a component goes away
broisnischal Aug 8, 2026
4d643f3
fix(icons): use the eye for views so the sidebar matches the tab bar
broisnischal Aug 8, 2026
c59fd3b
fix(titlebar): truncate a long title instead of running it under the …
broisnischal Aug 8, 2026
282a4c1
chore(stores): drop legacy shims and exports nothing imports
broisnischal Aug 8, 2026
3e5e263
chore(ui): remove dead props, bindings and duplicated handlers
broisnischal Aug 8, 2026
44b8c36
feat(connect): ask before discarding an edited connection form
broisnischal Aug 8, 2026
10c4ab4
fix(connect): make the one-click error fixes reach every engine's form
broisnischal Aug 8, 2026
dd039b2
refactor(connect): share the saved-connection builder, and reformat t…
broisnischal Aug 8, 2026
a5706bd
docs(changelog): record the unreleased sweep
broisnischal Aug 8, 2026
43ccd10
feat(geo): put a geometry on a real map you can pan and zoom
broisnischal Aug 8, 2026
13e47a0
fix(insert): say auto-increment instead of demanding a value for one
broisnischal Aug 8, 2026
7276db0
feat(insert): type-and-pick fields for enum and boolean columns
broisnischal Aug 8, 2026
217aab5
docs(changelog): the geometry map and the insert-row fields
broisnischal Aug 8, 2026
c483cf1
fix(omniroute): find Node the way the user's shell does, not the way …
broisnischal Aug 8, 2026
58b4a54
fix(ai): show the error the provider actually sent, once
broisnischal Aug 8, 2026
821bd97
fix(ai-settings): tell 'no models' apart from 'not answering'
broisnischal Aug 8, 2026
fe6820e
fix(ai-settings): import onDestroy (#78)
broisnischal Aug 8, 2026
e8db649
feat(ai-settings): suggest models from Ollama's registry, cloud included
broisnischal Aug 8, 2026
ea9e3a0
feat(ai-settings): make Ollama Cloud models pickable without pulling
broisnischal Aug 8, 2026
9a26396
feat(insert): every field takes typing, including generated ones and …
broisnischal Aug 8, 2026
779a94b
fix(ai-settings): load the Ollama model list through Rust, once
broisnischal Aug 8, 2026
2d2d031
fix(toolbar): let the search shrink so the pager stops clipping at hi…
broisnischal Aug 8, 2026
862d2f7
fix(statusbar): one control height, so the spacing reads as even
broisnischal Aug 8, 2026
a7e415e
docs(readme): document the features the app actually has
broisnischal Aug 8, 2026
7c41b63
feat(analytics): report anonymous usage, opt-out, nothing about your …
broisnischal Aug 8, 2026
6acad46
docs(changelog): the rest of the sweep
broisnischal Aug 8, 2026
3492ad0
docs: the files an open repo is expected to have
broisnischal Aug 8, 2026
c4763b2
chore(deps): close every advisory that can be closed without a regres…
broisnischal Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Bug report
description: Something behaves incorrectly
labels: [bug]
body:
- type: markdown
attributes:
value: |
Please don't paste connection strings, credentials, or real data. A
redacted example that still reproduces the problem is more useful than a
real one you have to scrub afterwards.
- type: textarea
id: what
attributes:
label: What happened
description: What you did, what you expected, and what you got instead.
validations: { required: true }
- type: input
id: version
attributes:
label: Stroke version
description: Shown at the right-hand end of the status bar.
placeholder: "1.21.0"
validations: { required: true }
- type: dropdown
id: os
attributes:
label: Operating system
options: [macOS, Windows, Linux]
validations: { required: true }
- type: dropdown
id: engine
attributes:
label: Database
description: Which engine, if the bug involves one.
options:
- Not database-specific
- PostgreSQL
- MySQL
- MariaDB
- CockroachDB
- SQLite
- Turso / libSQL
- Cloudflare D1
- ClickHouse
- DuckDB
- SQL Server
- Redis
- type: textarea
id: error
attributes:
label: Error text
description: >
Any message the app showed. If it hit the crash screen, "Report issue"
there fills this in for you.
render: text
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: Security vulnerability
url: https://github.com/broisnischal/stroke/security/advisories/new
about: Report privately. Please do not open a public issue.
- name: Question or help
url: https://stroke.click
about: Docs, downloads, and support.
22 changes: 22 additions & 0 deletions .github/ISSUE_TEMPLATE/feature.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Feature request
description: Suggest something Stroke should do
labels: [enhancement]
body:
- type: textarea
id: problem
attributes:
label: What are you trying to do
description: >
The task, not the feature. Knowing what you are actually trying to
accomplish often turns up a better answer than the one being asked for.
validations: { required: true }
- type: textarea
id: today
attributes:
label: How do you do it today
description: In Stroke, or in whatever tool you reach for instead.
- type: textarea
id: idea
attributes:
label: What you have in mind
description: Optional — the problem is the important part.
21 changes: 21 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## What this changes

<!-- What is different afterwards, and why it is worth doing. -->

## Why

<!-- The problem. If it fixes an issue: Fixes #123 -->

## How it was checked

<!-- What you ran, and what you saw. Delete what does not apply. -->

- [ ] `npm test`
- [ ] `npm run build`
- [ ] `cargo check` (in `src-tauri/`)
- [ ] Ran the app and used the change against a real database

<!--
Engine-specific? Say which you tested on. Stroke supports eleven, and a change
that is right for Postgres is not automatically right for SQLite or D1.
-->
2 changes: 1 addition & 1 deletion .github/workflows/cla.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ jobs:
custom-pr-sign-comment: >
I have read the CLA Document and I hereby sign the CLA
# Allowlist — bots and the repo owner don't need to sign.
allowlist: bot,broisnischal
allowlist: broisnischal,*[bot],bot*
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ packaging/aur/discerns/*.tar.zstlogs/
.env.local
.env.*.local

# Local agent/editor notes — kept on disk, not shipped in the repo
AGENT.md

# Personal SQL notebooks — these can embed real query results (customer data);
# they belong in your documents folder, never in the repo
*.sqlnb

# Build/tooling cruft that must never be committed
squashfs-root/
*.AppImage
Expand Down
193 changes: 3 additions & 190 deletions AGENT.md
Original file line number Diff line number Diff line change
@@ -1,193 +1,6 @@
# AGENT.md — Stroke

Instructions for AI agents working in this repository. Read this file before making changes.
This file is superseded. For AI agents and contributors working in this repository:

## Project overview

**Stroke** is a desktop database management app (Drizzle Studio–style) built with:

| Layer | Stack |
|-------|--------|
| Shell | [Tauri 2](https://tauri.app/) |
| Frontend | Svelte 5 + Vite |
| UI | [shadcn-svelte](https://shadcn-svelte.com/) + Tailwind CSS v4 |
| Editor | Monaco (`monaco-editor`) — SQL console |
| Hotkeys | [@tanstack/svelte-hotkeys](https://tanstack.com/hotkeys/latest/docs/installation#svelte) |
| Backend | Rust + SQLx (PostgreSQL) |

Supported databases today: **PostgreSQL only**. Other engines are future work.

## What agents should do

### Always

1. **Read `README.md` and this file** before large changes.
2. **Use Tailwind utility classes** for layout and styling. Do not add custom CSS in `src/app.css` except shadcn theme tokens (`:root`, `@theme inline`, `@layer base`).
3. **Use shadcn-svelte components** from `$lib/components/ui/*` (Button, Dialog, Input, Table, Tabs, etc.). Run `npx shadcn-svelte@latest add <component> -y` if a component is missing.
4. **Use `$lib/utils.js` `cn()`** for conditional classes — never manual string concatenation for Tailwind.
5. **Use `@lucide/svelte` icons** — not custom SVG icon components.
6. **Keep Tauri commands thin** — DB logic in `src-tauri/src/db/`, expose via `src-tauri/src/commands.rs`.
7. **Match existing patterns** — Svelte 5 runes (`$state`, `$derived`, `$props`, `$bindable`), camelCase JSON from Rust (`#[serde(rename_all = "camelCase")]`).
8. **Minimize diff scope** — only change what the task requires.
9. **Follow [guidelines](.cursor/skills/guidelines/SKILL.md)** — think before coding, simplicity first, surgical edits, verifiable success criteria.

### Frontend rules (shadcn)

- Semantic colors only: `bg-background`, `text-muted-foreground`, `border-border`, etc. No raw `bg-zinc-900` unless necessary.
- Spacing: `flex` + `gap-*`, not `space-y-*` / `space-x-*`.
- Equal dimensions: `size-*`, not `w-* h-*` when equal.
- Compose UI: Dialog for modals, Tabs for tabbed forms, Table for data grids.
- Import aliases: `$lib/...` (see `jsconfig.json` and `vite.config.js`).

### Backend rules

- Async Tauri commands must be `Send` — do not hold `MutexGuard` across `.await`.
- SQL identifiers: quote schema/table names; use parameterized values for data.
- Errors: return `Result<_, String>` with clear messages for the UI.

### Do not

- Add large custom CSS blocks to `app.css` (theme tokens only).
- Commit secrets (`.env`, passwords in repo files).
- Force-push `main` / skip git hooks unless the user explicitly asks.
- Create git commits unless the user asks.
- Reimplement shadcn primitives (buttons, inputs, dialogs) by hand.

## Key paths

```
src/
App.svelte # Root → StudioShell
app.css # Tailwind + shadcn theme only
lib/
api.js # Tauri invoke wrappers
utils.js # cn()
stores/connections.js # localStorage saved connections
stores/settings.js # theme + font size (localStorage)
components/
StudioShell.svelte # Main layout + data loading + hotkeys
CommandPalette.svelte # shadcn Command dialog (⌘K)
SqlEditor.svelte # Monaco SQL editor
SqlConsole.svelte # Run query + results
ConnectionModal.svelte
Sidebar.svelte
DataTable.svelte
RowDetailPanel.svelte # Right inspector (Shiki, Normal/JSON)
ShikiBlock.svelte
TableToolbar.svelte
monaco-env.js # Monaco worker setup for Vite
ui/ # shadcn-svelte (do not hand-edit unless fixing bugs)
src-tauri/
src/db/ # connection, schema, query
src/commands.rs
src/lib.rs
components.json # shadcn-svelte config
AGENT.md # this file
```

## Tauri commands (PostgreSQL)

| Command | Purpose |
|---------|---------|
| `test_postgres_connection` | Validate config without persisting pool |
| `connect_postgres` | Open pool + store config |
| `disconnect_postgres` | Close pool |
| `pg_list_schemas` | User schemas |
| `pg_list_tables` | Tables + row counts for schema |
| `pg_get_table_rows` | Paginated rows + column metadata |
| `pg_execute_sql` | Run SQL in SQL editor (SELECT vs DML) |
| `pg_update_table_cell` | Update one cell (requires primary key) |
| `pg_delete_table_row` | Delete one row by primary key |
| `pg_delete_table_rows` | Batch delete rows (`IN` or `VALUES` query) |
| `pg_insert_table_row` | Insert one row (`RETURNING *`; Postgres / SQLite / D1) |

## Keyboard shortcuts

| Shortcut | Action |
|----------|--------|
| `Mod+K` | Open command palette |
| `Mod+B` | Toggle nav sidebar |
| `Mod+Shift+D` | Data view (table tabs) |
| `Mod+Shift+S` | SQL editor view |
| `Mod+R` | Refresh tables (sidebar focus) or refresh rows / re-run SQL (main area) |
| `Mod+Enter` | Run SQL (SQL view) |
| `Mod+S` | Format SQL (SQL view); save inline cell edit (table view) |
| `Mod+Backspace` | Delete selected rows (table view) |
| Double-click cell | Edit cell value |
| `Enter` | Save edit (in cell input) |
| `Escape` | Close command palette (⌘K), cancel cell edit, or close row inspector |
| `Mod+M` | Toggle light / dark theme |
| `Mod+=` / `Mod+-` / `Mod+0` | Zoom in / out / reset |

`Mod` is Cmd on macOS and Ctrl on Windows/Linux ([TanStack Hotkeys](https://tanstack.com/hotkeys/latest/docs/installation#svelte)).

## Running locally

```bash
npm install
npm run tauri dev # full app (required for DB APIs)
npm run dev # Vite only — UI only, invokes will fail
```

## UI / display

- **Fonts**: Geist Sans + Geist Mono via `@fontsource-variable/geist` (see `src/app.css`).
- **Theme**: `light` | `dark` via `stores/settings.js` — toggles `html.dark` class. **Ctrl/Cmd + M** to toggle.
- **Zoom**: CSS `zoom` on `#app` (80%–150% steps). Settings gear icon or **Ctrl + Plus / Minus / 0**. Tauri `zoomHotkeysEnabled` is **off** (use app zoom, not webview zoom).
- Default window: 1280×800 (`src-tauri/tauri.conf.json`).

## Feature status

| Feature | Status |
|---------|--------|
| PostgreSQL connect / test / saved connections | Done |
| Schema + table sidebar | Done |
| Paginated table viewer | Done |
| Theme (light/dark) + font size settings | Done |
| SQL console | Placeholder |
| Add row | Done (toolbar Add → insert dialog) |
| Edit / delete rows | Table grid + batch delete via toolbar ⋯ menu (⌘⌫) |
| Filter / sort UI | Placeholder |
| MySQL / SQLite / etc. | Not started |

## Adding shadcn components

```bash
npx shadcn-svelte@latest add <name> -y
```

Config: `components.json`. Requires `jsconfig.json` paths for `$lib`.

## When implementing new features

1. Add Rust handler + types in `src-tauri/src/db/` if data is needed.
2. Register command in `commands.rs` and `lib.rs` `invoke_handler`.
3. Add `invoke` wrapper in `src/lib/api.js`.
4. Wire UI in `StudioShell` or a focused component under `src/lib/components/`.
5. Use shadcn components; verify with `npm run build`.

## Release builds (GitHub Actions)

Workflow: `.github/workflows/release.yml`

| Trigger | How |
|---------|-----|
| Tag push | `git tag v0.1.0 && git push origin v0.1.0` |
| Manual | Actions → **Release** → Run workflow (set tag, e.g. `v0.1.0`) |

Builds **Linux** (`.deb`, `.AppImage`) on `ubuntu-22.04` and **Windows** (`.msi`) on `windows-latest`, then uploads assets to [GitHub Releases](https://github.com/broisnischal/stroke/releases).

Local build:

```bash
npm run tauri:build # native
npm run tauri:build -- --target x86_64-pc-windows-msvc # Windows cross (needs toolchain)
```

Tag must match `version` in `src-tauri/tauri.conf.json` (e.g. tag `v0.1.0` ↔ version `0.1.0`).

## Commit / PR conventions

- Concise commit messages focused on **why**.
- PRs: Summary bullets + Test plan checklist.
- User rules override: no commits unless asked.
- **[CLAUDE.md](CLAUDE.md)** — the canonical agent guide (architecture, coding patterns, conventions, commands).
- **[DESIGN_SYSTEM.md](DESIGN_SYSTEM.md)** — the source of truth for all UI (type scale, tokens, spacing, components).
Loading
Loading