# Architecture

## Overview

`wikied` is a packaged local web application. A Node CLI starts an Express server, the server exposes filesystem and git APIs and serves a prebuilt React app, and the browser UI uses those APIs to edit Markdown files under the selected root.

Only the server touches the filesystem and git. The client sends root-relative intent, and the server validates each path before reading, writing, creating, renaming, copying assets, or running git commands.

## Runtime Flow

```mermaid
flowchart LR
  User[Terminal] --> CLI[wikied CLI]
  CLI -->|optional --git clone| Git[git clone]
  CLI --> Server[Express server on 127.0.0.1]
  Server --> Static[dist/client React app]
  Browser[Browser UI] --> Static
  Browser --> API[REST API]
  API --> Safety[Path safety layer]
  Safety --> FS[Workspace files]
  API --> GitOps[Git operations]
  GitOps --> Repo[.git repository]
  API --> Settings[~/.wikied/settings.json]
```

## Boundaries

### CLI

The CLI lives in `src/cli` and is responsible for:

* Parsing the optional root path.
* Parsing `--port`, `--no-open`, `--dev-client`, and `--git <url>`.
* Resolving the opened root.
* Running a pre-flight `git clone` when `--git` is supplied (before the server starts).
* Loading user-level settings.
* Starting the Express server.
* Printing the local URL.
* Opening the browser when settings allow it and `--no-open` is absent.
* Running `wikied convert --input <file.gliffy> --output <file.md>` when `convert` is detected in `process.argv` — converts a Gliffy file to a Markdown file with a Mermaid code block, then exits without starting the server.

### Server

The server lives in `src/server` and is responsible for:

* Serving the React build.
* Listing the file tree.
* Reading and writing Markdown files (with UTF-16 LE BOM detection on read).
* Validating paths against the opened root.
* Rejecting symlink escapes and traversal attempts.
* Creating and renaming Markdown files.
* Copying image assets.
* Detecting save conflicts using revision tokens.
* Reading and writing user-level settings.
* Reporting workspace state (`isEmpty`, `isGitRepo`) on the health endpoint.
* Cloning a git repository into an empty root (`POST /api/clone`).
* Running git status, commit, push, and pull when the workspace is a git repository.
* Returning structured API errors.

### Client

The React app lives in `src/client` and is responsible for:

* Rendering the Mantine `AppShell` with a top bar, conditionally visible right aside, and editor main area.
* Showing a clone-on-empty-root modal when `GET /api/health` reports `isEmpty: true`.
* Displaying the filename pill with an inline file picker combobox (`Ctrl+P`).
* Showing an orange dirty indicator dot beside the filename pill.
* Managing the active document state.
* Caching unsaved edits in memory when switching away from a dirty file so Save all can restore and save them later.
* Showing Rich and Preview modes.
* Tracking dirty state across multiple files.
* Triggering save, autosave, and relative-link navigation flows.
* Rendering a Save button with a dirty-count badge; Save all iterates the dirty cache.
* Rendering a Git button (when `isGitRepo: true`) with a changed-files badge; popover offers Commit, Push, and Pull.
* Rendering the Outline and Recent panels as drawers or a pinned aside.
* Rendering Markdown preview and Mermaid diagrams.
* Applying theme (System / Light / Dark) via Mantine's color scheme system.

### Shared Utilities

Shared code lives in `src/shared` and contains:

* API request and response types.
* Error code constants (including `GIT_ERROR`).
* Markdown extension detection.
* Client path normalization.
* **Gliffy converter** (`src/shared/gliffy/`) — parses `.gliffy` files (plain JSON or ZIP) and converts them to Mermaid text. Used by both the browser client (`ImportGliffyModal`) and the Node CLI (`wikied convert`). Three modules: `types.ts` (type definitions and error classes), `gliffyParser.ts` (ZIP/JSON → `GliffyDoc`), `gliffyToMermaid.ts` (dispatches to per-type converters for flowchart, sequence, ER, and org chart).

### Git Helper Module

`src/server/git.ts` provides thin shell-out helpers used by the server endpoints:

* `isEmptyDir(path)` — checks whether a directory has no entries.
* `isGitRepo(path)` — checks whether `path/.git` exists.
* `gitClone(url, cwd)` — spawns `git clone <url> .` with a 60-second timeout.
* `gitStatus(cwd)` — returns the count of changed lines from `git status --short`.
* `gitCommit(message, cwd)` — runs `git add -A` then `git commit -m`.
* `gitPush(cwd)` — runs `git push`.
* `gitPull(cwd)` — runs `git pull`.

All helpers spawn child processes with a 30-second timeout (60 s for clone) and propagate stderr as the error message on non-zero exit.

## Data Flow

### Initial Load

```mermaid
sequenceDiagram
  participant UI as React UI
  participant API as Express API
  participant Settings as Settings file
  participant FS as Workspace

  UI->>API: GET /api/health
  API-->>UI: ok, isEmpty, isGitRepo
  UI->>API: GET /api/settings
  API->>Settings: Read ~/.wikied/settings.json or defaults
  UI->>API: GET /api/tree
  API->>FS: Recursively list root
  API-->>UI: Settings and tree (parallel)
  alt isEmpty true
    UI->>UI: Show clone-or-start-empty modal
  end
```

### Clone From Browser

```mermaid
sequenceDiagram
  participant UI as React UI
  participant API as Express API
  participant Git as git CLI

  UI->>API: POST /api/clone {url}
  API->>API: Validate URL format and root is still empty
  API->>Git: git clone <url> .
  Git-->>API: Exit code
  alt Success
    API-->>UI: 204 No Content
    UI->>API: GET /api/health + GET /api/tree
    UI->>UI: Dismiss modal, load workspace
  else Failure
    API-->>UI: 500 with stderr message
    UI->>UI: Show error inline
  end
```

### Opening A File

```mermaid
sequenceDiagram
  participant UI as React UI
  participant API as Express API
  participant FS as Filesystem

  UI->>API: GET /api/files/content?path=guide.md
  API->>API: Validate path under root and extension
  API->>FS: Read file bytes (detect UTF-16 LE BOM)
  FS-->>API: Content, mtime, size, hash
  API-->>UI: Content plus revision token
  UI->>UI: Populate editor and mark clean
```

### Saving A File

```mermaid
sequenceDiagram
  participant UI as React UI
  participant API as Express API
  participant FS as Filesystem

  UI->>API: PUT /api/files/content with path, content, revision
  API->>API: Validate path and compare disk revision
  alt Revision matches
    API->>FS: Write file
    FS-->>API: New stat and hash
    API-->>UI: Saved with new revision
    UI->>UI: Update activeFile.revision in App state
  else Revision differs
    API-->>UI: 409 conflict with latest disk content
  end
```

### Switching Files With Unsaved Edits

```mermaid
sequenceDiagram
  participant UI as React UI
  participant Cache as App dirty cache

  Note over UI: File A is dirty, user opens File B
  UI->>Cache: Store {content, revision} for File A
  UI->>UI: Fetch and open File B normally
  Note over UI: User switches back to File A
  UI->>Cache: Retrieve cached content for File A
  UI->>UI: Open File A with cached content as initialContent<br/>disk content as diskContent (EditorWorkspace starts dirty)
```

### Opening A Relative Preview Link

```mermaid
sequenceDiagram
  participant Preview as Markdown Preview
  participant Editor as EditorWorkspace
  participant App as App state
  participant API as Express API

  Preview->>Editor: Relative link clicked
  Editor->>Editor: Resolve href from active document directory
  alt Dirty document
    Editor->>Editor: Confirm save or cancel
    Editor->>API: PUT current document when confirmed
  end
  Editor->>App: Open resolved path
  App->>API: GET /api/files/content?path=resolved.md
```

## Safety Model

All filesystem operations resolve the requested path to an absolute real path and verify that it stays under the opened root.

The server rejects:

* `..` traversal.
* Absolute paths from client requests.
* Symlink paths that resolve outside the root.
* Editable operations for unsupported extensions.
* Rename operations that overwrite an existing file.

Git clone URL validation rejects:

* Empty values.
* Dash-prefixed values (injection prevention).
* URLs that do not match `http://`, `https://`, `git://`, `ssh://`, or SSH shorthand (`user@host:path`).

Git operation endpoints return `400` if the workspace root is not a git repository.

## Configuration Model

Settings are stored outside the project at `~/.wikied/settings.json`.

Current settings:

* `autoOpenBrowser`
* `autosave`
* `assetFolder`
* `recentFiles`

The app does not write project-local metadata.

## Deployment Model

`wikied` is packaged as an npm-style tarball with:

* A binary entry in `package.json`.
* Compiled CLI, server, and shared JavaScript in `dist`.
* A prebuilt React app in `dist/client`.
* Project documentation in `docs/project`.

End users do not need a Vite development server.