Skip to content

feat: AI providers, dashboard, settings refresh, production fixes#172

Merged
tomymaritano merged 153 commits into
mainfrom
develop
Mar 20, 2026
Merged

feat: AI providers, dashboard, settings refresh, production fixes#172
tomymaritano merged 153 commits into
mainfrom
develop

Conversation

@tomymaritano

@tomymaritano tomymaritano commented Mar 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Major release merging develop into main with:

AI System

  • OpenAI provider (GPT-4o, o1, GPT-4 Turbo) with SSE streaming + tool calling
  • Ollama provider (local models) with NDJSON streaming
  • Secure API key storage via OS keychain (safeStorage)
  • "Connect" flow UI — one-click provider setup
  • AI context scoping: chat mode uses only current note

Admin Dashboard

  • Standalone dashboard at /dashboard with sidebar layout
  • Overview: stat cards, sync volume chart, quick links
  • Users table with plan badges
  • Sync activity feed
  • Admin API endpoints (/admin/stats, /users, /sync)

Settings Visual Refresh

  • Card-based SettingGroup layout
  • Tighter controls and sidebar
  • Modernized AI panel sidebar

Production Fixes

  • Stripe webhook secret fixed
  • DB migrations applied (5 tables)
  • Shared notes markdown rendering
  • Newsletter unsubscribe page

Other

  • MCP server for Claude Code integration
  • Share store + note list UI improvements

Test plan

  • pnpm dev starts without errors
  • AI settings shows Connect flow for Anthropic/OpenAI/Ollama
  • Dashboard loads at /dashboard with admin token
  • Shared notes render markdown at /shared?slug=xxx

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added support for OpenAI and Ollama AI providers alongside existing provider
    • Introduced note sharing with shareable web links and unshare capability
    • Added newsletter unsubscribe page
    • New admin dashboard for monitoring service metrics, users, and sync activity
  • Improvements

    • Redesigned settings interface with updated controls, layout, and typography
    • Enhanced AI panel styling and messaging experience

tomymaritano and others added 30 commits March 11, 2026 01:41
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Notebooks now sync before notes in syncNow() to ensure note-notebook
dependencies are satisfied. Adds pullNotebooks/pushNotebooks methods
and applyRemoteNotebookChange for bidirectional notebook sync.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move validateNotebookTree from inline test definition to a shared module
so it can be reused by the API route and other consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add conflict state to SyncStatusIndicator with amber warning icon
and count. Conflicts now take priority over idle state so users
discover them without navigating to Settings.

Also export ConflictResolver from sync components barrel.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DatabaseConnection.transaction() already calls the inner fn — no need
for extra () at call site.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix pullNotebooks() to only advance cursor to last successfully
  applied change (prevents skipping failed changes on retry)
- Fix tree validation snapshot to properly exclude deleted notebooks
  (prevents ghost parent references in validation)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: add bidirectional notebook sync
test: add sync-core unit tests (62 tests)
feat: surface sync conflicts in status indicator
# Conflicts:
#	apps/desktop/src/main/services/apiClient.ts
#	apps/desktop/src/main/services/syncService.ts
#	packages/api/src/db/schema.ts
#	packages/api/src/routes/sync.ts
#	packages/storage-sqlite/src/migrations/index.ts
Configure automated code review with path-specific instructions
for core, storage, desktop, and API packages.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ration

Add optional metadata (name, version, priority) to registerRemarkPlugin
and registerRehypePlugin signatures for debugging and execution ordering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
tomymaritano and others added 16 commits March 14, 2026 18:08
## Summary

- **New `@readied/ai-core` package** — provider-agnostic AI architecture
with LLMProvider interface, ProviderRegistry, streaming SSE parser,
retry logic with exponential backoff, context builder with token
budgeting, and AIService orchestrator
- **Full Electron integration** — IPC bridge with batched streaming
events, preload API, and renderer AiPanel wired to the new streaming
architecture
- **UX fixes** — unified dual AI panel entry points (Cmd+K and Sparkles
button) into single panel, fixed panel height to fill container, made
React DevTools dev-only via dynamic import
- **Monorepo fix** — pinned `@types/react` to 18.x via pnpm overrides to
resolve lucide-react type mismatch

## Architecture

```
Renderer (AiPanel) → IPC → Main (ai-core bridge) → AIService → ProviderRegistry → AnthropicProvider → SSE stream
                   ←  batched LLMEvents (text/error/done)  ←
```

## What's included

### packages/ai-core (new)
- `LLMProvider` interface + `ProviderRegistry` for pluggable providers
- `AnthropicProvider` with native SSE streaming (no SDK dependency)
- `ContextBuilder` with token budgeting and automatic content trimming
- `AIService` orchestrator with retry, cancellation, and context
building
- `LLMEvent` protocol: `text`, `error`, `done`, `tool_call`,
`tool_result`
- Reusable SSE stream parser

### apps/desktop
- IPC bridge with 50ms batched event streaming (main → renderer)
- Preload API: `window.readied.ai.chat()`, `.onEvent()`, `.cancel()`
- Unified AI panel (single instance, both Cmd+K and Sparkles button
toggle it)
- Panel height fix (fills container instead of 350px cutoff)
- Dynamic import for electron-devtools-installer (excluded from prod)

### Monorepo
- Removed deprecated `@readied/ai-assistant` package
- Added `pnpm.overrides` to pin `@types/react@18.3.27` across workspace

## Test plan

- [x] All 16 packages pass `pnpm test`
- [x] `pnpm typecheck` passes (17/17 tasks)
- [x] `pnpm build` succeeds
- [ ] Manual: Cmd+K opens AI panel in ask-notes mode
- [ ] Manual: Sparkles button toggles same panel
- [ ] Manual: AI chat streams responses correctly
- [ ] Manual: Panel fills full height of sidebar

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
  * Added streaming AI chat responses for real-time interaction
  * Introduced provider selection (Anthropic, OpenAI, Ollama support)
  * Added ability to cancel in-flight AI requests
  * Implemented configuration validation for AI providers
  * Enhanced chat interface with conversation modes

* **Refactor**
  * Restructured AI system for multi-provider support
  * Updated settings schema with provider configuration

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary

- Replace manual release process with a two-stage automated pipeline
using **semantic-release**
- **Stage 1** (`release.yml`): `workflow_dispatch` on main →
semantic-release analyzes commits, bumps version, creates tag + draft
GitHub Release (~30s)
- **Stage 2** (`build.yml`): tag push triggers parallel mac/win/linux
builds → signs + notarizes macOS → uploads artifacts → undrafts release
→ tweets → auto-PR syncs main→develop

## Changes

- Add `release.config.js` (conventionalcommits preset, draft release,
beta channel ready)
- Add `scripts/bump-version.js` with tests (syncs version across
monorepo)
- Replace `.github/workflows/release.yml` (200 lines → 46 lines
semantic-release workflow)
- Create `.github/workflows/build.yml` (parallel platform builds +
publish + tweet + sync-develop)
- Delete `.github/workflows/auto-tag.yml` (replaced by semantic-release)
- Update CLAUDE.md Git Flow section with release process + rollback docs
- Add semantic-release + plugins as devDependencies

## Required: New GitHub Secret

Create a `GH_TOKEN` repository secret — a Personal Access Token with
`contents: write` scope. Needed because:
1. semantic-release must push tags that trigger other workflows
(GITHUB_TOKEN can't do this)
2. electron-builder needs write access to upload artifacts to GitHub
Releases

## Test plan

- [x] All existing tests pass (`pnpm test`)
- [x] `scripts/bump-version.test.js` — 2 new tests passing
- [x] `release.config.js` loads correctly
- [x] All workflow YAML files are syntactically valid
- [ ] Create `GH_TOKEN` secret in repo settings
- [ ] After merge to main: click "Run workflow" on Release action to
test end-to-end

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Implemented automated release pipeline with semantic versioning,
generating changelogs automatically.
* Added multi-platform build automation for simultaneous macOS, Windows,
and Linux releases.

* **Documentation**
* Updated Git Flow and release process documentation to reflect new
automated workflows.

* **Tests**
  * Added tests for automated version management.

* **Chores**
  * Refined release workflows for improved efficiency and reliability.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary

- Adds `cleanUrls: true` to `vercel.json`
- Fixes the 404 on `/auth/verify` (and all other static pages without
`.html` extension)

## Root cause

Next.js static export generates `auth/verify.html`, but without
`cleanUrls: true`, Vercel doesn't know to serve it when the URL is
`/auth/verify` (no `.html`). This affects the magic link email
verification flow.

## Test plan

- [ ] Deploy to Vercel preview
- [ ] Visit `/auth/verify` — should show "Invalid Link" (no token)
- [ ] Visit `/auth/verify?token=test` — should show "Opening Readied..."
- [ ] Verify other pages still work (`/download`, `/pricing`,
`/changelog`)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Updated application configuration to enable clean URLs, improving URL
structure and formatting across the platform.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary
- **ai-core**: Add `ToolRegistry`, `runToolLoop` orchestrator,
`chatWithTools()` on `AIService`, tool_use SSE parsing in
AnthropicProvider, and `stop` event with `tool_use`/`tool_result`
ContentPart types
- **desktop (main)**: Register 6 built-in tools (search_notes,
read_note, list_notebooks, create_note, insert_text, replace_selection)
with confirmation flow and renderer-executed tool IPC bridge
- **desktop (renderer)**: `ToolCallBlock` component with status
indicators/icons, tool call tracking in `AiPanel`, and renderer-side
execution of editor tools (insert_text, replace_selection)

### Tools
| Tool | Type | Confirmation |
|------|------|-------------|
| `search_notes` | Read (main) | Auto |
| `read_note` | Read (main) | Auto |
| `list_notebooks` | Read (main) | Auto |
| `create_note` | Write (main) | Required |
| `insert_text` | Write (renderer) | Required |
| `replace_selection` | Write (renderer) | Required |

## Test plan
- [x] All 153 tests pass (`pnpm test`)
- [x] Typecheck clean (`pnpm typecheck` — main, preload, renderer)
- [x] Build succeeds (`pnpm build`)
- [ ] Manual: open AI panel, send message with `tools: true`, verify
tool calls render
- [ ] Manual: approve/reject tool confirmation flow
- [ ] Manual: verify insert_text and replace_selection modify the editor


🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Streaming AI chat with multi-turn conversations and built-in note
management tools (search, read, create, and editor actions)
* Tool confirmation UI allowing users to approve/reject AI tool
executions before they run
* Enhanced chat interface with provider validation and improved request
controls

* **Documentation**
* Added comprehensive AI architecture and tool-use implementation guides

* **Style**
* Updated visual color opacity tokens across UI components for
consistency
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…#166)

## Summary

- **Extended `sharedNotes` schema** with `tags`, `backlinks`,
`wordCount`, and `notebookName` columns (all `NOT NULL DEFAULT` —
backward-compatible)
- **Added `GET /share/public/:userId`** list endpoint with pagination
(`?limit=20&offset=0`) and permissive CORS for cross-origin portfolio
consumption
- **Enriched `GET /share/:slug`** detail endpoint to return metadata
(tags, backlinks, wordCount, notebookName)
- **Updated `POST /share`** to accept and persist metadata fields (Zod
defaults keep old clients working)
- **Desktop app** now sends full note metadata (tags, wordCount,
notebookName, backlinks) when sharing
- **Hand-corrected migration** (`0005`) uses safe `ALTER TABLE ADD
COLUMN` instead of Drizzle's auto-generated `CREATE TABLE`

## Test plan

- [x] `pnpm typecheck` passes (17/17 tasks)
- [x] `pnpm test` passes (42/42 tests across 7 test files)
- [ ] Manual: share a note from desktop → verify API receives
tags/wordCount/notebookName
- [ ] Manual: `curl
"https://api.readied.app/share/public/<userId>?limit=5"` returns
paginated notes with metadata
- [ ] Manual: `curl "https://api.readied.app/share/<slug>"` returns
enriched response with tags/backlinks
- [ ] Manual: fetch from different origin (portfolio domain) succeeds
for `/share/public/*` (CORS)
- [ ] Run `pnpm db:migrate` on staging to verify migration applies
cleanly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added ability to share notes with enhanced metadata including tags,
word count, and notebook context.
  * Introduced public notes listing endpoint with pagination support.

* **Documentation**
* Updated documentation component infrastructure for improved content
rendering.

* **Style**
* Refined color and gradient utilities across the web interface for
improved visual consistency.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
# Conflicts:
#	.claude/settings.local.json
#	.github/workflows/build.yml
#	apps/desktop/package.json
#	apps/desktop/src/main/ai/ipc-ai.ts
#	apps/desktop/src/main/ai/setup.ts
#	apps/desktop/src/main/index.ts
#	apps/desktop/src/preload/index.ts
#	apps/desktop/src/renderer/components/ai/AiPanel.tsx
#	apps/desktop/src/renderer/stores/settings/settingsStore.ts
#	apps/web/app/(marketing)/auth/verify/AuthVerifyContent.tsx
#	apps/web/app/(marketing)/changelog/page.tsx
#	apps/web/app/(marketing)/download/page.tsx
#	apps/web/app/(marketing)/pricing/page.tsx
#	apps/web/app/docs/[[...slug]]/page.tsx
#	apps/web/components/Footer.tsx
#	apps/web/components/Navbar.tsx
#	apps/web/components/landing/Hero.tsx
#	apps/web/components/landing/VideoGuides.tsx
#	apps/web/components/magicui/animated-shiny-text.tsx
#	apps/web/components/magicui/border-beam.tsx
#	apps/web/components/magicui/hero-video-dialog.tsx
#	package.json
#	packages/ai-core/src/ai-service.ts
#	packages/ai-core/src/context-builder.ts
#	packages/ai-core/src/index.ts
#	packages/ai-core/src/providers/anthropic.ts
#	packages/ai-core/src/providers/sse-parser.ts
#	packages/ai-core/src/retry.ts
#	packages/ai-core/src/types.ts
#	packages/ai-core/tests/ai-service.test.ts
#	packages/ai-core/tests/retry.test.ts
#	packages/ai-core/tests/types.test.ts
#	pnpm-lock.yaml
#	vercel.json
…reated webhook

- Parse markdown content with `marked` in SharedNoteContent instead of
  rendering raw text via dangerouslySetInnerHTML
- Add customer.subscription.created webhook handler as fallback when
  checkout.session.completed is not configured in Stripe events
- Clean up temporary debug logging from license handlers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add shareStore (Zustand + persist) for tracking shared note state
- Update ActionsPanel with share-on-web functionality
- NoteEditor and NoteList UI improvements
- Note list styling updates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ript

- AI chat mode now only uses the current note as context (no search)
- AI ask-notes mode still searches across all notes for relevant context
- Add newsletter unsubscribe page at /newsletter/unsubscribe
- Add create-note.sh script for creating notes from the terminal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Exposes Readied notes to Claude Code via Model Context Protocol:
- readied_list_notes: List notes with filters
- readied_read_note: Read note by ID or title
- readied_create_note: Create new notes
- readied_update_note: Update existing notes
- readied_search_notes: Search across notes
- readied_list_notebooks: List notebooks
- readied_trash_note: Move to trash

Uses sql.js (WASM) to avoid native module conflicts with Electron.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- User messages in bubble style with rounded corners
- Smooth fade-in animation on new messages
- Blinking cursor for typing indicator
- Better input with focus ring
- Cleaner tool call blocks with subtle borders
- Improved spacing and typography throughout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## AI Providers
- Add OpenAI provider: GPT-4o, o1, GPT-4 Turbo with SSE streaming + tools
- Add Ollama provider: local models with auto-detection, NDJSON streaming
- Register all 3 providers in setup.ts

## Secure Key Storage
- New AiKeyStorage service using Electron safeStorage (OS keychain)
- IPC handlers: saveKey, getKey, removeKey, hasKey, listConnectedProviders
- Preload bridge exposes all key management methods

## Settings UI — Connect Flow
- Replace manual API key input with "Connect" button flow
- "Get API Key" button opens provider's key page in browser
- Paste key → auto-validate → show "Connected" badge
- Connected state shows green status with "Disconnect" option
- Ollama: one-click connect (no key needed)
- Dynamic model list per provider
- Model & context settings only shown when connected

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…aner sidebar

- SettingGroup now renders as cards with dividers between rows
- SettingRow simplified: no individual borders/backgrounds, hover-only highlight
- Sidebar: tighter nav items, subtler active indicator, muted icons
- Controls: cleaner toggle, smaller inputs, simpler slider
- Section title: smaller, tighter spacing
- Content area: thinner scrollbar, reduced padding
- Color picker: smaller swatches with dot indicator

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
## API (packages/api)
- New /admin routes: /stats, /users, /sync
- Protected by ADMIN_TOKEN header
- Aggregated metrics: users, subscriptions, devices, sync volume, shared notes, newsletter

## Dashboard (apps/web)
- New /dashboard page with token auth
- Overview tab: stat cards + sync volume chart (7 days)
- Users tab: table with email, plan, devices, join date
- Sync tab: recent activity feed with operation colors

Token auto-saved to localStorage for convenience.
Page is noindex to prevent search engine crawling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mail auth

- Move dashboard from (marketing) to (dashboard) route group — no navbar/footer
- Standalone sidebar layout with nav, API health indicator, sign out
- Admin auth now accepts JWT from authenticated admin emails
- Improved stat cards, sync volume chart, quick links panel
- Login screen centered without marketing chrome
- Proper dark theme throughout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

async *chat(options: ChatOptions, config: ProviderConfig): AsyncIterable<LLMEvent> {
const { model, system, messages, maxTokens, signal, tools } = options;
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.
}

async validate(config: ProviderConfig): Promise<{ ok: boolean; error?: string }> {
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.
}

async listModels(config: ProviderConfig): Promise<ModelInfo[]> {
const baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '/'.
@vercel

vercel Bot commented Mar 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
readide Ready Ready Preview, Comment Mar 29, 2026 9:03pm

Request Review

@github-actions github-actions Bot added dependencies Pull requests that update a dependency file app:web app:desktop package:api labels Mar 20, 2026
@github-actions github-actions Bot enabled auto-merge (squash) March 20, 2026 12:06
@coderabbitai

coderabbitai Bot commented Mar 20, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 96965775-245c-4054-b287-b4f779c9b834

📥 Commits

Reviewing files that changed from the base of the PR and between f35e055 and 7fd0ee1.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (38)
  • .mcp.json
  • apps/desktop/src/main/ai/setup.ts
  • apps/desktop/src/main/index.ts
  • apps/desktop/src/main/services/aiKeyStorage.ts
  • apps/desktop/src/preload/index.ts
  • apps/desktop/src/renderer/components/NoteEditor.tsx
  • apps/desktop/src/renderer/components/NoteList.tsx
  • apps/desktop/src/renderer/components/ai/AiPanel.tsx
  • apps/desktop/src/renderer/components/editor/ActionsPanel/ActionsPanel.tsx
  • apps/desktop/src/renderer/pages/settings/SettingsApp.module.css
  • apps/desktop/src/renderer/pages/settings/components/SettingGroup.module.css
  • apps/desktop/src/renderer/pages/settings/components/SettingGroup.tsx
  • apps/desktop/src/renderer/pages/settings/components/SettingRow.module.css
  • apps/desktop/src/renderer/pages/settings/components/SettingsSidebar.module.css
  • apps/desktop/src/renderer/pages/settings/components/controls.module.css
  • apps/desktop/src/renderer/pages/settings/sections/AiSection.tsx
  • apps/desktop/src/renderer/pages/settings/sections/Section.module.css
  • apps/desktop/src/renderer/stores/shareStore.ts
  • apps/desktop/src/renderer/styles/ai-panel.css
  • apps/desktop/src/renderer/styles/note-list.css
  • apps/web/app/(dashboard)/dashboard/DashboardContent.tsx
  • apps/web/app/(dashboard)/dashboard/page.tsx
  • apps/web/app/(dashboard)/layout.tsx
  • apps/web/app/(marketing)/newsletter/unsubscribe/UnsubscribeContent.tsx
  • apps/web/app/(marketing)/newsletter/unsubscribe/page.tsx
  • apps/web/app/(marketing)/shared/SharedNoteContent.tsx
  • packages/ai-core/src/index.ts
  • packages/ai-core/src/providers/ollama.ts
  • packages/ai-core/src/providers/openai.ts
  • packages/api/src/db/client.ts
  • packages/api/src/index.ts
  • packages/api/src/routes/admin.ts
  • packages/api/src/routes/subscription.ts
  • packages/mcp-server/package.json
  • packages/mcp-server/src/db.ts
  • packages/mcp-server/src/index.ts
  • packages/mcp-server/tsconfig.json
  • scripts/create-note.sh

📝 Walkthrough

Walkthrough

This PR introduces multi-provider AI support (OpenAI and Ollama) with persistent API key management in the desktop app, adds a local MCP server for database access, implements note-sharing capabilities with client-side persistence, creates an admin dashboard for web app monitoring and user management, and updates UI styling across settings and AI components.

Changes

Cohort / File(s) Summary
AI Core Providers
packages/ai-core/src/providers/openai.ts, packages/ai-core/src/providers/ollama.ts, packages/ai-core/src/index.ts
New OpenAI and Ollama provider implementations with streaming chat, tool support, model listing, and validation endpoints; updated exports.
Desktop AI Infrastructure
apps/desktop/src/main/services/aiKeyStorage.ts, apps/desktop/src/main/index.ts, apps/desktop/src/main/ai/setup.ts, apps/desktop/src/preload/index.ts
New encrypted key storage service (AiKeyStorage), IPC handlers for provider API key management (ai:saveKey, ai:getKey, ai:removeKey, ai:hasKey, ai:listConnectedProviders), provider registration during service initialization, and preload API surface.
AI Settings UI
apps/desktop/src/renderer/pages/settings/sections/AiSection.tsx
Replaced test-connection flow with provider connection state machine, added provider metadata and model options, implemented connection/disconnection handlers with key persistence, and made model selection provider-specific with dynamic Ollama model fetching.
Note Sharing
apps/desktop/src/renderer/stores/shareStore.ts, apps/desktop/src/renderer/components/NoteEditor.tsx, apps/desktop/src/renderer/components/editor/ActionsPanel/ActionsPanel.tsx, apps/desktop/src/renderer/components/NoteList.tsx
New Zustand persisted share store with share metadata (slug, url, noteId), updated NoteEditor to manage share state and expose unshare/copy-link callbacks, enhanced ActionsPanel to conditionally render share controls, and added share indicators in note list.
MCP Server
packages/mcp-server/src/index.ts, packages/mcp-server/src/db.ts, packages/mcp-server/tsconfig.json, packages/mcp-server/package.json, .mcp.json
New local MCP server implementation with SQLite database access via sql.js, tools for note/notebook CRUD and search, executable configuration, and build/dev scripts.
Web Admin Dashboard
apps/web/app/(dashboard)/dashboard/DashboardContent.tsx, apps/web/app/(dashboard)/dashboard/page.tsx, apps/web/app/(dashboard)/layout.tsx, packages/api/src/routes/admin.ts, packages/api/src/index.ts
New admin dashboard with token-based auth (header token or admin email JWT whitelist), stats aggregation (users, subscriptions, sync activity), user/sync management views, and protected /admin API routes with CORS restrictions; added routing and middleware.
Web Newsletter
apps/web/app/(marketing)/newsletter/unsubscribe/UnsubscribeContent.tsx, apps/web/app/(marketing)/newsletter/unsubscribe/page.tsx
New unsubscribe flow reading email from query params, posting to external newsletter API, and displaying async result states (loading, success, error, invalid link).
Desktop UI — AI Panel & Chat
apps/desktop/src/renderer/styles/ai-panel.css, apps/desktop/src/renderer/components/ai/AiPanel.tsx
Redesigned right-sidebar chat UI with updated backgrounds, typography, message bubbles, code/list styling, tool-call rendering, animations (fade-in, typing indicator), and context-gathering logic branching by chat mode (ask-notes vs. chat).
Desktop UI — Settings & Sidebar
apps/desktop/src/renderer/pages/settings/components/SettingGroup.tsx, apps/desktop/src/renderer/pages/settings/components/SettingGroup.module.css, apps/desktop/src/renderer/pages/settings/components/SettingRow.module.css, apps/desktop/src/renderer/pages/settings/components/SettingSidebar.module.css, apps/desktop/src/renderer/pages/settings/components/controls.module.css, apps/desktop/src/renderer/pages/settings/SettingsApp.module.css, apps/desktop/src/renderer/pages/settings/sections/Section.module.css
Comprehensive CSS refinements to settings layout, typography, spacing, sidebar navigation, form controls (toggle, inputs, range slider, select, color picker) with reduced dimensions and updated color tokens, and scrollbar styling.
Web Shared Notes
apps/web/app/(marketing)/shared/SharedNoteContent.tsx
Changed markdown rendering from raw HTML injection to memoized marked.parse() conversion for safer content display.
Configuration & Database
packages/api/src/db/client.ts, packages/api/src/routes/subscription.ts, scripts/create-note.sh
Added ADMIN_TOKEN environment variable support, enhanced subscription webhook handling for creation events with fallback lookup, and new bash script for direct SQLite note insertion.
UI — Note List
apps/desktop/src/renderer/styles/note-list.css
Added CSS rule for share icon appearance in note titles (no-shrink, accent color with opacity).

Sequence Diagram(s)

sequenceDiagram
    participant User as Desktop User
    participant UI as Settings UI
    participant IPC as IPC Bridge
    participant Main as Main Process
    participant Storage as AiKeyStorage
    participant API as Provider API
    
    User->>UI: Click "Connect" for AI provider
    UI->>UI: Collect API key
    User->>UI: Submit connection
    UI->>IPC: invoke('ai:saveKey', provider, apiKey)
    IPC->>Main: Handle ai:saveKey
    Main->>API: validate(config with apiKey)
    alt Validation succeeds
        API-->>Main: { ok: true }
        Main->>Storage: saveKey(provider, apiKey)
        Storage->>Storage: readKeys() → decrypt/parse
        Storage->>Storage: Update provider → apiKey mapping
        Storage->>Storage: writeKeys() → encrypt & save to disk
        Storage-->>Main: Promise resolved
        Main-->>IPC: void (success)
        IPC-->>UI: Promise resolved
        UI->>UI: Update connectStatus to 'connected'
        UI->>UI: Fetch and store provider-specific settings
    else Validation fails
        API-->>Main: { ok: false, error: "..." }
        Main-->>IPC: Promise rejected
        IPC-->>UI: Catch error
        UI->>UI: Update connectStatus to 'error', set message
    end
    UI->>User: Display connection result
Loading
sequenceDiagram
    participant Admin as Admin User (Browser)
    participant Page as Dashboard Page
    participant API as /admin/* Routes
    participant DB as Database
    
    Admin->>Page: Visit /dashboard?token=xyz
    Page->>Page: Read token from query params
    Page->>Page: Save token to localStorage
    Page->>API: GET /admin/stats (header: x-admin-token)
    API->>API: Verify x-admin-token matches ADMIN_TOKEN
    API->>DB: Query aggregated stats (users, subscriptions, sync)
    DB-->>API: Return aggregated rows
    API-->>Page: { users, subscriptions, devices, syncEntries, ... }
    
    par Parallel API Calls
        Page->>API: GET /admin/users
        API->>DB: Query users with subscription info & device counts
        DB-->>API: Return user list
        API-->>Page: [{ id, email, plan, status, joinedDate, ... }]
    and
        Page->>API: GET /admin/sync
        API->>DB: Query recent sync entries and daily volume
        DB-->>API: Return sync activity
        API-->>Page: { recent: [...], dailyVolume: [...] }
    end
    
    Page->>Page: Render overview (stats cards, sync chart)
    Page->>Page: Render users table
    Page->>Page: Render sync activity list
    Page->>Admin: Display dashboard
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

This PR introduces substantial new functionality across multiple subsystems: two new LLM providers with streaming, tool support, and error classification; encrypted persistent key storage with Electron integration; a local MCP server with database access and seven tools; an admin dashboard with auth, multi-table queries, and API routes; client-side share state management; and pervasive UI refinements. The changes span heterogeneous domains (backend providers, IPC infrastructure, UI state, styling, database schema) with dense logic in the provider implementations (streaming NDJSON parsing, error classification, message/tool conversion) and admin routes (multi-table ORM queries, auth middleware). The scope encompasses new files and substantial modifications across packages/ai-core, packages/mcp-server, packages/api, apps/desktop, and apps/web, requiring separate reasoning for each domain.

Possibly related PRs

  • PR #160: Touches the same AI core and desktop provider code (provider exports, implementations, and desktop provider registration).
  • PR #156: Modifies the same ai-core provider architecture (interfaces, registry, and provider implementations).
  • PR #149: Implements overlapping AI, sync, and web redesign features (AI providers, settings UI, web components).

Suggested labels

size/XL, app:desktop, app:web, package:api, feature/ai, feature/sharing, feature/admin

✨ 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 develop
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@tomymaritano tomymaritano merged commit 36707e7 into main Mar 20, 2026
13 of 18 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7fd0ee1201

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +45 to +48
const jwt = authHeader.slice(7);
const [, payloadB64] = jwt.split('.');
if (payloadB64) {
const payload = JSON.parse(atob(payloadB64)) as { email?: string; exp?: number };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 Badge Verify admin bearer tokens before trusting their email

This middleware grants /admin/* access to any bearer token whose payload decodes to an allowed email, because it never verifies the JWT signature. Unlike packages/api/src/middleware/auth.ts, which calls jose.jwtVerify, this code just atobs the middle segment and trusts it, so an attacker can forge a token with {"email":"tomymaritano@gmail.com"} and read the admin stats/users/sync endpoints.

Useful? React with 👍 / 👎.

Authorization: `Bearer ${config.apiKey}`,
'Content-Type': 'application/json',
},
body: '',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the body from the OpenAI validation GET

createAIService() wires this provider to Electron's net.fetch, which follows standard Fetch semantics. Sending body: '' on a GET request throws before the request is dispatched, so OpenAIProvider.validate() will reject every key and the new Connect flow cannot save an OpenAI credential.

Useful? React with 👍 / 👎.

Comment on lines +123 to +127
// If no row was updated, create it (handles case where checkout.session.completed was missed)
if (updated.length === 0 && sub.customer) {
// Find user by Stripe customer ID
const [existing] = await db
.select()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Actually create the missing subscription in the fallback path

This branch is meant to recover when checkout.session.completed was missed, but it only looks up an existing row and logs when none is found. If Stripe delivers customer.subscription.created/updated before or instead of checkout completion, the user never gets a subscriptions record, so /subscription/status keeps reporting free/inactive and paid sync stays disabled.

Useful? React with 👍 / 👎.

Comment on lines +295 to +296
const db = await openDb();
const server = createServer(db);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reopen the MCP database instead of reusing one startup snapshot

main() opens the SQLite file once and then all tool calls share that in-memory sql.js database for the lifetime of the process. Because writes later call saveDb() to export the whole snapshot back to disk, any edits the desktop app makes after the MCP server starts are missing from this snapshot and will be overwritten on the next create/update/trash operation.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app:desktop app:web dependencies Pull requests that update a dependency file package:api size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants