# ChatGPT Desktop Application — Administrator & Developer Guide

This guide is the deep reference for the **lencx/ChatGPT 2.0** desktop application: its architecture, configuration, build and release pipeline, security model, and internals. It is written for developers, packagers, and administrators who need to build, deploy, audit, or extend the app. End users should start with the [User Guide](lencx__ChatGPT__UserGuide.md).

---

## 1. Executive Summary

**ChatGPT** is an unofficial, cross-platform desktop client that renders the official ChatGPT website (`https://chatgpt.com`) inside a native window built with the [Tauri 2](https://v2.tauri.app) framework. The 2.0 line is a ground-up rewrite: a compact Rust backend manages the native window, child web views, configuration persistence, theming, and OS integration, while a small React + TypeScript frontend renders the custom toolbar, the Quick Ask input, and a (placeholder) settings view inside Tauri web views.

| Attribute | Value |
|---|---|
| Project | `lencx/ChatGPT` |
| Version | `2.0.0` (frontend `package.json`); crate version `0.0.0` |
| License | `AGPL-3.0` |
| Author | lencx `<[email removed]>` |
| Bundle identifier | `com.nofwl.chatgpt` |
| Product name | `ChatGPT` |
| Frameworks | Tauri 2 (beta), React 18, TypeScript 5, Tailwind CSS 3, Vite 5 |
| Rust edition / MSRV | 2021 / `1.77.1` |
| Target platforms | macOS, Windows, Linux |
| Source commit documented | `a6de9a8` (2024-08-30) |

> [!NOTE]
> The two version numbers are intentional: the user-facing product version comes from the frontend `package.json` (`2.0.0`), which `tauri.conf.json` references via `"version": "../package.json"`. The Rust crate's own `version` field is `0.0.0` and is not the release version.

> [!IMPORTANT]
> This codebase pins **beta** releases of Tauri 2 and its plugins (for example `@tauri-apps/api` `2.0.0-beta.15`, `tauri = "2.0.0-beta"`). Anyone building or maintaining this app should expect beta-era API surfaces and pin dependencies carefully; newer stable Tauri 2 releases may require migration.

---

## 2. Architecture

### 2.1 High-level model

The app is a **single native window** (label `core`) that hosts **three child web views**, plus an optional **second native window** for settings.

```text
┌─────────────────────────── Native window: "core" ───────────────────────────┐
│                                                                              │
│   Webview "titlebar"  ── React UI (custom toolbar)                           │
│   ──────────────────────────────────────────────────────────────────────    │
│                                                                              │
│   Webview "main"      ── https://chatgpt.com  (the actual ChatGPT website)   │
│                          + injected scripts: ask.js, INIT_SCRIPT             │
│                                                                              │
│   ──────────────────────────────────────────────────────────────────────    │
│   Webview "ask"       ── React UI (Quick Ask input box, height 120px)        │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

         ┌──────────── Native window: "settings" (opened on demand) ────────────┐
         │   Webview ── React UI (placeholder "Settings" view)                  │
         └──────────────────────────────────────────────────────────────────────┘
```

The React bundle (`index.html` → `src/main.tsx` → `src/App.tsx`) is loaded into the `titlebar`, `ask`, and `settings` web views. `App.tsx` inspects `getCurrentWebview().label` and renders the matching component:

```tsx
const viewMap = {
  titlebar: <Titlebar />,
  ask: <Ask />,
  settings: <Settings />,
};

export default function App() {
  const webview = getCurrentWebview();
  return viewMap[webview.label as keyof typeof viewMap] || null;
}
```

The `main` web view is **not** React — it loads `chatgpt.com` directly. The Rust backend injects two scripts into it at startup (see §2.4).

> [!NOTE]
> This "one window, multiple web views" layout is what lets a native toolbar and a native input box float over a remote website without modifying that website. The backend continuously repositions and resizes the three web views as the window changes.

### 2.2 Layout geometry

The backend computes web view positions from two constants and the current window size and scale factor:

| Constant | Value | Meaning |
|---|---|---|
| `TITLEBAR_HEIGHT` | `28.0` | Height (logical px) of the custom toolbar web view |
| `ASK_HEIGHT` | `120.0` | Height (logical px) of the Quick Ask web view when enabled (0 when disabled) |

Defined in `src-tauri/src/core/constant.rs`. The main web view occupies the remaining space. Platform branches differ:

- **macOS:** toolbar is pinned to the **top** (`y = 0`); main view starts below it (`y = TITLEBAR_HEIGHT`); Quick Ask sits at the bottom.
- **Windows / Linux:** main view starts at the **top** (`y = 0`); toolbar and Quick Ask are stacked at the **bottom**.

Repositioning happens in two places: `setup::init` (initial build) and the window `Resized` event handler, both in `setup.rs`, and again in `cmd::set_view_ask` when Quick Ask is toggled.

### 2.3 Process / module map

```text
src-tauri/                      Rust backend (the native app)
├── build.rs                    Build script; sets MACOSX_DEPLOYMENT_TARGET=10.13, runs tauri_build
├── tauri.conf.json             Tauri app config (product, bundle, build hooks, security)
├── Info.plist                  macOS bundle metadata
├── capabilities/desktop.json   Capability/permission grants (ACL)
├── scripts/ask.js              Userscript injected into chatgpt.com (ChatAsk class)
└── src/
    ├── main.rs                 Entry point; registers commands + setup
    └── core/
        ├── mod.rs              Module declarations
        ├── cmd.rs              10 #[command] handlers invoked from the frontend
        ├── conf.rs             AppConf struct + load/save/amend/theme persistence
        ├── constant.rs         Layout constants + INIT_SCRIPT
        ├── setup.rs            Builds the core window, 3 web views, download + resize handlers
        ├── template.rs         Manages the bundled ask.js script (version-aware update)
        └── window.rs           open_settings command (settings window)

src/                            React frontend (loaded into titlebar/ask/settings web views)
├── main.tsx                    React root render
├── App.tsx                     Renders component by current web view label
├── types.d.ts                  AppConf + SVG TypeScript types
├── base.css                    Tailwind base styles
├── components/WinTitlebar.tsx  Min/max/close window buttons (non-macOS)
├── hooks/useInfo.tsx           Platform detection (isMac)
├── hooks/useTheme.tsx          Reactive window theme
├── icons/*.tsx                 16 inline SVG icon components
└── view/
    ├── Titlebar.tsx            The toolbar UI + all invoke() calls
    ├── Ask.tsx                 Quick Ask input box
    └── Settings.tsx            Placeholder ("Settings")

index.html                     Vite entry; mounts #root and src/main.tsx
vite.config.ts                 Vite config (port 1420, ignores src-tauri)
tailwind.config.js             Tailwind theme (custom app colors, tablet breakpoint)
```

### 2.4 Injected scripts (main web view)

Two scripts are injected into the `chatgpt.com` web view at initialization (`setup.rs`):

1. **`ask.js`** (`src-tauri/scripts/ask.js`) — defines `window.ChatAsk` with two static methods used by Quick Ask:
   - `ChatAsk.sync(message)` — finds the page `<textarea>` and sets its value using the native setter, dispatching an `input` event so the page's React state updates.
   - `ChatAsk.submit()` — finds the last `<button>` inside `main form` and clicks it to send.
   The file carries a userscript-style header (`@name`, `@version 0.1.0`, `@url`) that the backend uses for version-based updates (see §6.3).

2. **`INIT_SCRIPT`** (string constant in `constant.rs`) — runs on `DOMContentLoaded` and:
   - Tracks SPA navigation by wrapping `history.pushState` / `history.replaceState` and listening for `popstate`, emitting a `navigation:change` event (with the new URL) to the `titlebar` web view.
   - Forces in-page links whose `target` is set (and isn't `_blank`) to open as `_blank`, so the link-handling logic routes them to the system browser.

> [!IMPORTANT]
> `tauri.conf.json` sets `"withGlobalTauri": true`, exposing `window.__TAURI__` inside web views. The injected scripts rely on this (for example `__TAURI__.webview.Webview.getByLabel('ask')` and `window.__TAURI__.webviewWindow...emit(...)`). Disabling global Tauri would break navigation tracking and Quick Ask focus handling.

---

## 3. Frontend ↔ Backend Command Reference

The frontend communicates with Rust exclusively through Tauri's `invoke()` mechanism. All commands are registered in `src-tauri/src/main.rs` and defined in `cmd.rs` (and `window.rs` for `open_settings`).

| Command | Params | Returns | Effect |
|---|---|---|---|
| `view_reload` | — | — | Reloads the `main` web view (`window.location.reload()`) |
| `view_url` | — | `tauri::Url` | Returns the current URL of the `main` web view |
| `view_go_forward` | — | — | `window.history.forward()` in the `main` web view |
| `view_go_back` | — | — | `window.history.back()` in the `main` web view |
| `window_pin` | `pin: bool` | — | Persists `stay_on_top` and calls `set_always_on_top(pin)` |
| `ask_sync` | `message: String` | — | Evaluates `ChatAsk.sync(<message>)` in the main view (live mirror) |
| `ask_send` | — | — | Evaluates `ChatAsk.submit()`, then refocuses the `ask` web view after 500 ms |
| `set_theme` | `theme: String` | — | Persists `theme`, then **restarts the app** (`app.restart()`) |
| `get_app_conf` | — | `AppConf` | Returns the current configuration object |
| `set_view_ask` | `enabled: bool` | — | Persists `ask_mode`, resizes/repositions all three web views, sets focus |
| `open_settings` | — | — | Shows the existing `settings` window or builds it (`window.rs`) |

> [!NOTE]
> `ask_sync` is debounced on the frontend (`Ask.tsx`, 300 ms) so keystrokes mirror into the page without flooding the bridge. `ask_send` is bound to `⌘`/`Ctrl` + `Enter` via `react-hotkeys-hook`.

### 3.1 Frontend wiring notes

- **`Titlebar.tsx`** is the hub: on mount it calls `get_app_conf` to hydrate toolbar state (`ask_mode`, `stay_on_top`, `theme`, `mac_titlebar_hidden`), listens for `navigation:change` to display the current hostname, and listens for `tauri://resize` to track full-screen state.
- **`Ask.tsx`** mirrors input via `ask_sync` (debounced) and submits via `ask_send`.
- **`useInfo.tsx`** resolves the platform through `@tauri-apps/plugin-os` to choose macOS vs. non-macOS behavior (toolbar padding, send shortcut).
- **`WinTitlebar.tsx`** provides minimize / maximize / close for the `core` window on non-macOS platforms by calling `Window.getByLabel('core')` methods directly (no Rust command needed).
- Path aliases (`~view`, `~icons`, `~hooks`, `~`) are resolved by `vite-tsconfig-paths` from `tsconfig.json`.

---

## 4. Configuration

### 4.1 Runtime configuration (`AppConf`)

User preferences persist to a JSON file managed by `conf.rs`.

**Location:** `<OS config dir>/com.nofwl.chatgpt/config.json`

| Platform | Resolved path (typical) |
|---|---|
| macOS | `~/Library/Application Support/com.nofwl.chatgpt/config.json` |
| Windows | `%APPDATA%\com.nofwl.chatgpt\config.json` |
| Linux | `~/.config/com.nofwl.chatgpt/config.json` |

**Schema (`AppConf`):**

| Field | Type | Default | Meaning |
|---|---|---|---|
| `theme` | `string` | `"system"` | One of `system`, `light`, `dark` |
| `stay_on_top` | `bool` | `false` | Window pinned always-on-top |
| `ask_mode` | `bool` | `false` | Quick Ask box visible |
| `mac_titlebar_hidden` | `bool` | `true` on macOS, `false` elsewhere | Hide custom toolbar until hover (macOS) |

Example `config.json`:

```json
{
  "ask_mode": false,
  "mac_titlebar_hidden": true,
  "stay_on_top": false,
  "theme": "system"
}
```

> [!TIP]
> You can edit `config.json` by hand while the app is closed to preset preferences (for example, force `"theme": "dark"` or enable `"ask_mode": true` for a fleet). Keys are serialized in sorted order because `amend()` round-trips through a `BTreeMap`.

**Loading and self-healing.** `AppConf::load`:

1. If the file is missing, writes defaults and returns them.
2. If the file exists but fails to deserialize cleanly, it logs the error, starts from defaults, **amends** them with whatever valid keys were present, saves the repaired file, and returns it.

This means a malformed or partial config is repaired rather than fatal — useful when adding fields across versions. `amend()` merges a JSON object over the current config via a `BTreeMap<String, Value>` and re-validates against `AppConf`.

**Theme resolution (`get_theme`).** When `theme` is `"system"`, the backend uses the `dark-light` crate to detect the OS appearance (defaulting to Light when undetermined); `"dark"` and `"light"` map directly. The resolved `Theme` is applied to the window at build time.

### 4.2 Build-time configuration (`tauri.conf.json`)

```json
{
  "productName": "ChatGPT",
  "version": "../package.json",
  "identifier": "com.nofwl.chatgpt",
  "build": {
    "beforeDevCommand": "pnpm dev",
    "devUrl": "http://localhost:1420",
    "beforeBuildCommand": "pnpm build",
    "frontendDist": "../dist"
  },
  "app": {
    "withGlobalTauri": true,
    "windows": [],
    "security": { "csp": null }
  },
  "bundle": {
    "active": true,
    "targets": "all",
    "icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"]
  }
}
```

| Key | Value | Notes |
|---|---|---|
| `build.devUrl` | `http://localhost:1420` | Must match Vite's fixed port (`vite.config.ts`, `strictPort: true`) |
| `build.frontendDist` | `../dist` | Where `pnpm build` (tsc + vite build) emits the bundle |
| `app.windows` | `[]` | No windows are declared statically — all windows are created at runtime in `setup.rs` / `window.rs` |
| `app.security.csp` | `null` | No Content-Security-Policy is enforced by Tauri (see §7) |
| `bundle.targets` | `all` | Produce every installer type the host toolchain supports |

### 4.3 Other config files

| File | Purpose |
|---|---|
| `vite.config.ts` | React + tsconfig-paths plugins; dev server on port `1420` (`strictPort`), ignores `src-tauri/**` |
| `tailwind.config.js` | Custom colors (`app-gray-1/2/3`, `app-active: #10a37f`), `tablet: 480px` breakpoint, typography + autoprefixer plugins |
| `postcss.config.js` | Tailwind + autoprefixer |
| `tsconfig.json` / `tsconfig.node.json` | TypeScript + path aliases (bundler resolution) |
| `rustfmt.toml` | Rust formatting (100 col, 4-space, edition 2021) |
| `build.rs` | Sets `MACOSX_DEPLOYMENT_TARGET=10.13`; invokes `tauri_build::build()` |
| `Cargo.toml` (workspace) | Release profile: `panic = "abort"`, `opt-level = "s"`, `lto = true`, `codegen-units = 1` — optimized for small binary size |

---

## 5. Requirements

### 5.1 Build toolchain

| Tool | Version / constraint | Why |
|---|---|---|
| Rust | `>= 1.77.1` (edition 2021) | `rust-version` in `src-tauri/Cargo.toml` |
| Node.js | `>= 18.18` | Required by `@tauri-apps/api` engines |
| pnpm | current | Lockfile is `pnpm-lock.yaml` (`lockfileVersion: 9.0`); build hooks call `pnpm` |
| Tauri CLI | `2.0.0-beta.22` | Dev dependency `@tauri-apps/cli` |
| Platform build deps | per [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) | e.g. WebView2 (Windows), webkit2gtk (Linux), Xcode CLT (macOS) |

### 5.2 Runtime

- A working internet connection to reach `https://chatgpt.com`.
- An OS web view runtime: WebView2 on Windows, WKWebView on macOS, WebKitGTK on Linux (provided/installed per Tauri's platform requirements).
- An OpenAI account for sign-in.

---

## 6. Build, Run, and Release

### 6.1 Development

```bash
# Install JS dependencies
pnpm install

# Run the app in dev mode (starts Vite on :1420, then launches the Tauri shell)
pnpm tauri dev
```

```text
# Tauri compiles the Rust backend, Vite serves the frontend on http://localhost:1420,
# and a native ChatGPT window opens loading chatgpt.com.
```

The `tauri dev` command runs `beforeDevCommand` (`pnpm dev`) automatically and points the shell at `devUrl`.

> [!TIP]
> The frontend alone can be served with `pnpm dev`, but the app is only meaningful inside the Tauri shell — `getCurrentWebview()`, `invoke()`, and the OS plugins require the Tauri runtime.

### 6.2 Production build

```bash
# Type-check + build the frontend, then bundle native installers for the host platform
pnpm tauri build
```

```text
# Output installers are written under src-tauri/target/release/bundle/<type>/.
# "targets": "all" produces every bundle type the host toolchain can build.
```

`tauri build` runs `beforeBuildCommand` (`pnpm build`, i.e. `tsc && vite build`) to emit `../dist`, then compiles the optimized Rust binary using the size-tuned release profile.

> [!NOTE]
> Cross-platform installers are produced by building on each target OS (and/or CI), not from a single host. The published [Releases](https://github.com/lencx/ChatGPT/releases) cover macOS, Windows, and Linux.

### 6.3 The bundled `ask.js` lifecycle

`template.rs` embeds `scripts/ask.js` at compile time (`include_bytes!`) and, at startup (`Template::new`, called from `setup::init`), writes/updates a working copy into `<config dir>/com.nofwl.chatgpt/scripts/ask.js`:

- It parses the `@version` header from both the bundled and the on-disk copies (via regex).
- It overwrites the on-disk file only when the bundled version is **newer** (semver comparison), when the on-disk version is missing/unparseable, or when the file doesn't exist.

At web view creation, `AppConf::load_script(&handle, "ask.js")` reads that on-disk copy and injects it as an initialization script.

> [!IMPORTANT]
> Because the runtime injects the **on-disk** `scripts/ask.js`, advanced users can patch Quick Ask behavior by editing that file — but on the next launch the bundled version will overwrite it if its `@version` is greater. To make a sticky local change, bump the `@version` in your edited copy above the bundled value.

---

## 7. Security

### 7.1 Capability / permission model

Tauri 2 gates backend functionality behind an explicit allowlist. `capabilities/desktop.json` grants a broad set:

```json
{
  "identifier": "desktop-capability",
  "windows": ["*"],
  "remote": { "urls": ["https://chatgpt.com/*"] },
  "platforms": ["linux", "macOS", "windows"],
  "permissions": [
    "window:default", "window:allow-create", "window:allow-start-dragging",
    "window:allow-toggle-maximize", "window:allow-minimize", "window:allow-close",
    "webview:default", "webview:allow-internal-toggle-devtools",
    "webview:allow-set-webview-zoom", "webview:allow-create-webview",
    "webview:allow-create-webview-window", "webview:allow-set-webview-focus",
    "event:default", "event:allow-emit", "event:allow-emit-to",
    "event:allow-listen", "event:allow-unlisten",
    "shell:default", "shell:allow-execute", "shell:allow-open",
    "os:allow-arch", "os:allow-platform", "os:allow-version",
    "os:allow-os-type", "os:default", "shell:default"
  ]
}
```

| Permission group | Grants | Used by |
|---|---|---|
| `window:*` | Create, drag, maximize, minimize, close windows | Toolbar, `WinTitlebar`, settings window |
| `webview:*` | Create web views/windows, set zoom, focus, toggle devtools | Multi-web-view layout, Quick Ask focus |
| `event:*` | Emit/listen to events | `navigation:change`, resize tracking |
| `shell:allow-open` | Open URLs/files with the OS default handler | Address-chip "open in browser", auto-open downloads |
| `shell:allow-execute` | Execute shell commands | Granted via the shell plugin |
| `os:*` | Query arch/platform/version/type | `useInfo` platform detection |

> [!CAUTION]
> The `remote.urls` ACL (`https://chatgpt.com/*`) is what scopes these capabilities to remote content loaded from ChatGPT. Combined with `withGlobalTauri: true`, the remote ChatGPT page has `window.__TAURI__` available within the bounds of the granted permissions. This is required for the injected scripts to function, but it also means the trust boundary includes whatever `chatgpt.com` serves. Anyone forking this app should review whether `shell:allow-execute` is actually needed (the code paths observed use only `shell:allow-open`), and consider tightening `remote.urls` and removing unused permissions.

> [!WARNING]
> `app.security.csp` is `null`, so Tauri enforces **no Content-Security-Policy**. The app relies on loading only `chatgpt.com`. Hardening deployments should consider defining a CSP and pruning the permission set to the minimum the features require.

### 7.2 Download handling

The `main` web view registers an `on_download` handler (`setup.rs`):

- On `DownloadEvent::Requested`, the destination is rewritten to the OS **Downloads** directory (`path().download_dir()`), so files always land there.
- On `DownloadEvent::Finished { success: true, .. }`, the file is opened with the OS default application via the shell plugin.

> [!CAUTION]
> Auto-opening downloaded files is a convenience that carries risk: any file ChatGPT (or a page served under the allowed origin) hands to the download flow is opened automatically on completion. In higher-security environments, consider disabling auto-open and requiring an explicit user action.

### 7.3 Data handling

The app stores no conversation data itself. Authentication and chat history live in the embedded web view's session storage and in the user's OpenAI account. The only local persistence is `config.json` (UI preferences) and the working copy of `scripts/ask.js`.

### 7.4 Platform notes

- On Windows release builds, `#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]` suppresses the extra console window. **Do not remove** this attribute.
- `build.rs` sets `MACOSX_DEPLOYMENT_TARGET=10.13` for macOS compatibility.
- Builds are unsigned by default; distribution requires the operator to handle code signing / notarization if desired.

---

## 8. Operations & Troubleshooting

### 8.1 Resetting an installation

```bash
# Close the app first, then remove its config directory to restore defaults.
# macOS
rm -rf ~/Library/Application\ Support/com.nofwl.chatgpt
# Linux
rm -rf ~/.config/com.nofwl.chatgpt
```

```text
# On next launch, AppConf::load() recreates config.json with defaults
# and template.rs re-creates scripts/ask.js from the bundled copy.
```

On Windows, delete `%APPDATA%\com.nofwl.chatgpt`.

### 8.2 Known issues and developer-facing notes

These are drawn from the community issue tracker. Several reported issues reference other ChatGPT clients (including Electron-based and official apps) and do not apply to this Tauri 2 codebase — triage accordingly.

| Symptom | Likely area | Notes |
|---|---|---|
| Session expired / login fails (macOS) | Embedded web view session | Reload the `main` view; sessions behave like browser sessions |
| Zoom shortcuts (`Ctrl` `+`/`-`/`0`) inert | No global zoom shortcut wired | `webview:allow-set-webview-zoom` is granted but not bound to a shortcut/command in this revision |
| Multiple windows on link click | Link handling | `INIT_SCRIPT` forces targeted links to `_blank`; these are routed to the system browser |
| Context-menu crash on previous chats | Upstream / web view | Originates in the embedded page; reproduce at `chatgpt.com` to confirm scope |
| Inline LaTeX not rendering | Upstream ChatGPT site | Not controlled by this app |

> [!TIP]
> When triaging a bug report, first determine whether it reproduces in a plain browser at `chatgpt.com`. If it does, it belongs to OpenAI's site, not this wrapper. Only behavior tied to the toolbar, Quick Ask, theming, pin, downloads, window management, or config belongs to this codebase.

### 8.3 Logging

The backend uses the `log` crate (`info!` / `error!`) in `conf.rs` and `template.rs`, and `eprintln!` for view-positioning failures. Run `pnpm tauri dev` to see backend output in the terminal during development; release builds suppress the console on Windows.

---

## 9. Code Quality & Conventions

- **Separation of concerns:** layout math lives in `setup.rs`/`cmd.rs`, persistence in `conf.rs`, constants in `constant.rs`, script lifecycle in `template.rs`, and window creation in `window.rs`. The frontend keeps all backend calls in `view/Titlebar.tsx` and `view/Ask.tsx`.
- **Configuration robustness:** `AppConf` self-heals malformed configs rather than crashing (§4.1).
- **Size-tuned release profile:** `panic = "abort"`, `opt-level = "s"`, `lto = true`, `codegen-units = 1`.
- **Error handling pattern:** backend command handlers favor `.unwrap()` on Tauri/web view calls. This is concise but means an unexpected `None`/`Err` (for example a missing web view) will panic the command. View-positioning is the exception — it logs via `eprintln!` and continues.
- **`template.rs` uses `anyhow`** for ergonomic error context; `conf.rs` returns `Box<dyn std::error::Error>`.

> [!NOTE]
> AST extraction over this repo surfaced 20 functions and 2 types (`AppConf`, `Template`); there are no CLI commands, HTTP endpoints, or server-side config keys — consistent with a thin desktop wrapper whose "API" is the set of Tauri commands documented in §3.

---

## 10. Potential Improvements

These are observations for maintainers, derived from the source as documented.

- [ ] **Tighten the capability set.** `shell:allow-execute` is granted but no observed code path executes shell commands; only `shell:allow-open` is used. Removing unused permissions reduces the attack surface.
- [ ] **Define a CSP.** `security.csp` is `null`; an explicit policy would harden the remote-content trust boundary.
- [ ] **Reduce `.unwrap()` in command handlers.** Returning `Result` and surfacing errors to the frontend would make failures (missing web view, config I/O) recoverable instead of fatal.
- [ ] **Finish the Settings window.** `view/Settings.tsx` is a placeholder; surfacing `AppConf` fields (and future options) there would remove the need to hand-edit `config.json`.
- [ ] **Wire zoom shortcuts.** The zoom permission is granted but not bound; binding `Ctrl`/`⌘` `+`/`-`/`0` to `set_webview_zoom` would address a recurring request.
- [ ] **Gate download auto-open.** Make the auto-open-on-finish behavior configurable for security-sensitive users.
- [ ] **Reconsider the dual version numbers.** The crate version is `0.0.0`; aligning it with the product version would reduce confusion in build artifacts and logs.

---

## 11. Quick Reference

| Item | Value |
|---|---|
| Bundle identifier | `com.nofwl.chatgpt` |
| Config file | `<OS config dir>/com.nofwl.chatgpt/config.json` |
| Injected script (working copy) | `<OS config dir>/com.nofwl.chatgpt/scripts/ask.js` |
| Dev server | `http://localhost:1420` (fixed, `strictPort`) |
| Frontend build output | `dist/` (`tsc && vite build`) |
| Native build output | `src-tauri/target/release/bundle/` |
| Embedded site | `https://chatgpt.com` |
| Backend commands | 11 (10 in `cmd.rs`, 1 in `window.rs`) |
| Web views | `main`, `titlebar`, `ask` (in `core`) + settings window |
| Layout constants | `TITLEBAR_HEIGHT = 28`, `ASK_HEIGHT = 120` |
| License | `AGPL-3.0` |

**Build & run cheat-sheet:**

```bash
pnpm install        # install JS deps
pnpm tauri dev      # develop (Vite :1420 + native shell)
pnpm tauri build    # produce native installers
pnpm dev            # frontend only (Vite)
pnpm build          # tsc + vite build → dist/
```

---

_Generated June 2026 from lencx/ChatGPT at a6de9a8. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
