# Blogging Desktop Server (bDS)

Desktop-first blogging workspace built with Electron + TypeScript + React + SQLite (Drizzle), with filesystem-based content as source of truth.

## Current State (February 2026)

Implemented and actively used:

- **Offline-first by default** — full local editing, preview, generation, and git-based workflows; all online features are optional
- **Multi-project workflow** with active project switching and optional custom data paths
- **Post & page management** (draft/published/archived), pagination, filtering, multi-language full-text search (Snowball stemming), canonical URL handling, inter-post link graph with backlinks/outlinks
- **Media pipeline** with import, metadata sidecars, three thumbnail sizes (Sharp), linked-media references, and cleanup tooling
- **Tag management** including merge/rename/sync from content, color support
- **Menu editor** for hierarchical site navigation (OPML-based), with page picker and submenu nesting
- **WordPress WXR import pipeline** (analysis, import definitions, 4-phase execution with progress tracking, HTML-to-Markdown conversion)
- **Blogmark deep links** — `bds://new-post` custom protocol for one-click bookmark saving from any browser, with bookmarklet generation and transform script pipeline
- **Static site generation** — full blog output with single-post pages, paginated category/tag/date archive routes, standalone page routes, plus `sitemap.xml`, `rss.xml`, `atom.xml`, and `calendar.json`; content-hash-based incremental writes
- **Site validation** — compares sitemap against generated HTML, detects missing/extra/stale pages, and auto-repairs with targeted re-rendering
- **SSH publishing** via `scp` or `rsync` with parallel upload of HTML, thumbnails, and media
- **Preview server** with local assets only, Liquid templates, macro processing, archive routes, draft preview support, and style preview
- **Style & theming** — 19 Pico CSS color themes with live preview (auto/light/dark modes), all assets served locally
- **Python scripting** (Pyodide WebAssembly runtime) — three script kinds:
    - *Transform scripts* — modify incoming blogmark posts before creation
    - *Macro scripts* — custom `[[macro_name ...]]` blocks that expand during preview/generation, with full access to the bDS Python API (~65+ methods)
    - *Utility scripts* — general-purpose Python automation
- **Built-in macros** — `youtube`, `vimeo`, `gallery` (lightbox), `photo_archive`, `tag_cloud` (D3 word cloud)
- **Liquid template system** for all generated pages with i18n support (English, German, French, Italian, Spanish) and custom Markdown filter with internal link rewriting
- **AI assistant** (OpenCode Zen API, supporting Claude, GPT-5, Gemini 3, and more) with streaming responses, tool use (search/read/update posts, media analysis including image vision, blog stats), and rich interactive responses via the A2UI protocol (charts, tables, forms, cards, metrics, lists, tabs)
- **Git integration** (status, diff, commit history with sync status, fetch/pull/push, init, .gitignore management, LFS configure/prune, branch operations)
- **Metadata diff tooling** for comparing filesystem metadata vs database metadata with sync in either direction
- **Rich editing** — Milkdown WYSIWYG Markdown editor for posts, Monaco code editor for Python scripts
- **Task system** for background progress reporting across long-running operations with grouping, cancellation, and status bar integration
- **i18n** — UI language follows OS locale; render/preview language from project preferences; 5 supported languages

## Architecture

```text
src/
├── main/
│   ├── database/        # Drizzle schema, migrations, connection
│   ├── engine/          # Core business logic (no UI)
│   │   ├── PostEngine             # Post CRUD, FTS indexing, link graph
│   │   ├── MediaEngine            # Media files, Sharp thumbnails
│   │   ├── PostMediaEngine        # Post ↔ media linking
│   │   ├── ProjectEngine          # Multi-project management
│   │   ├── MetaEngine             # Project metadata, categories
│   │   ├── TagEngine              # Tag CRUD, merge, rename, colors
│   │   ├── ScriptEngine           # Python script management & Git sync
│   │   ├── MenuEngine             # OPML-based navigation menus
│   │   ├── PreviewServer          # Local HTTP preview with Liquid
│   │   ├── BlogGenerationEngine   # Static site generation
│   │   ├── PublishEngine          # SSH upload (scp / rsync)
│   │   ├── SiteValidationDiffService  # Sitemap ↔ HTML validation
│   │   ├── Import* engines        # WXR parsing, analysis, execution
│   │   ├── Blogmark* services     # Deep link handling, transforms
│   │   ├── PythonMacroWorkerRuntime   # Pyodide macro execution
│   │   ├── GitEngine              # Git operations (simple-git)
│   │   ├── MetadataDiffEngine     # DB ↔ file metadata comparison
│   │   ├── ChatEngine / OpenCodeManager  # AI chat & model integration
│   │   └── TaskManager            # Background task queue
│   ├── a2ui/            # A2UI protocol: types, catalog, generator
│   ├── ipc/             # IPC handlers; bridges renderer to engines
│   ├── shared/          # Shared contracts/types (Electron API)
│   └── main.ts          # Electron app + menu + window lifecycle
└── renderer/
        ├── components/      # UI (editor, sidebars, panels, modals, views)
        ├── a2ui/            # A2UI surface rendering (17 component types)
        ├── store/           # Zustand app state
        ├── macros/          # Macro authoring/runtime support
        ├── python/          # Python scripting UI integration
        ├── navigation/      # Activity routing, tab policy, sidebar views
        └── App.tsx          # App composition + event wiring
```

## Data Model & Storage

- **Primary DB**: `{userData}/bds.db`
- **Projects**: stored in DB with an active project
- **Per-project data**:
    - default/internal: `{userData}/projects/{projectId}/`
    - optional custom `dataPath` (posts/media/meta/thumbnails live together)
- **Filesystem remains source of truth** for post/media files; DB is query/index/state layer

Typical per-project folders:

```text
{projectDataDir}/
├── posts/
├── media/
├── meta/          # tags, categories, menu.opml
├── scripts/       # Python scripts (.py with YAML frontmatter)
└── thumbnails/    # small / medium / large
```

## Menu / Shortcuts (Cross-Platform)

Key shortcuts (defined as `CmdOrCtrl`):

- `CmdOrCtrl+N` New post
- `CmdOrCtrl+I` Import media
- `CmdOrCtrl+S` Save
- `CmdOrCtrl+1` Posts view
- `CmdOrCtrl+2` Media view
- `CmdOrCtrl+B` Toggle sidebar
- `CmdOrCtrl+J` Toggle panel
- `CmdOrCtrl+Shift+P` Publish selected
- `CmdOrCtrl+Shift+V` Preview active post in browser

## Development

### Prerequisites

- Node.js 18+
- npm

### Install

```bash
npm install
```

### Run (Development)

```bash
# Runs main TypeScript watch, Vite dev server, and Electron together
npm run dev
```

Alternative renderer+Electron flow:

```bash
npm start
```

### Tests

```bash
npm test
npm run test:watch
npm run test:coverage
```

### Build

```bash
# Generates drizzle artifacts, builds main process and renderer
npm run build

# Package app directory only (no installer)
npm run package

# Package distributables for all supported systems
npm run dist

# Package by target platform
npm run dist:mac
npm run dist:win
npm run dist:linux
```

### Distribution Notes

- Custom protocol `bds://` is declared in electron-builder metadata for packaged apps.
- Build targets: macOS (DMG+ZIP, arm64, notarized), Windows (NSIS, x64+arm64), Linux (AppImage/deb/rpm).
- macOS signing/notarization is scaffolded via `scripts/notarize.mjs` and runs when these env vars are set:
    - `APPLE_ID`
    - `APPLE_APP_SPECIFIC_PASSWORD`
    - `APPLE_TEAM_ID`

## Further Documentation

- [DOCUMENTATION.md](DOCUMENTATION.md) — end-user guide covering workflows, macros, scripting, AI assistant, and editorial best practices
- [API.md](API.md) — full Python runtime API reference for macro and utility scripts (`bds_api` module)
- [VISION.md](VISION.md) — long-term product vision and design notes

## License

MIT
