diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 178d85d..ab7004a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,7 +18,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.0.x
+ dotnet-version: 10.0.x
- name: Build
run: dotnet build src/MandoCode.Desktop/MandoCode.Desktop.csproj -c Release
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c1558fb..664557e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -23,7 +23,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
- dotnet-version: 8.0.x
+ dotnet-version: 10.0.x
- name: Publish (self-contained win-x64)
run: dotnet publish src/MandoCode.Desktop/MandoCode.Desktop.csproj -c Release -r win-x64 --self-contained -o publish
diff --git a/README.md b/README.md
index 72775fc..d199bc2 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,97 @@
-# MandoCode Desktop (WinUI 3)
-
-The MandoCode AI coding agent with a native Windows desktop interface. Same engine
-as the [MandoCode CLI](https://github.com/DevMando/MandoCode) — literally: the CLI
-repo is pinned here as a **git submodule** (`/MandoCode`) and this app project-references
-`MandoCode/src/MandoCode/MandoCode.csproj`, reusing the entire harness (AIService,
-task planner, plugins, MCP, skills, config, approvals, token tracking). Only the
-user interface is different: WinUI 3 instead of RazorConsole.
+
+
+
+
+MandoCode Desktop
+An AI coding assistant that lives on your machine, not someone else's server.
+
+
+
+
+
+
+
+
+Made with <3 by Mando
+
+---
+
+Most AI coding tools assume you're always online, sending your code to someone else's servers, on
+someone else's subscription. MandoCode doesn't have to. It runs entirely on your own machine through
+[Ollama](https://ollama.com) — free, private, no API key, nothing metered per token — or, when you
+want more horsepower, a cloud model when *you* choose to reach for one. Same assistant either way,
+same conversation, your call.
+
+MandoCode Desktop takes that assistant and gives it an actual home on Windows, instead of a single
+chat window bolted onto a terminal: several agents working in parallel, a real integrated shell, a
+git-aware file browser, a notes pad, and enough personality in the theming that it doesn't have to
+look like every other dev tool on your taskbar.
+
+It's built on the exact same engine as the [MandoCode CLI](https://github.com/DevMando/MandoCode) —
+literally the same code, pinned in as a submodule — so nothing about how it thinks is different.
+Only the interface is.
+
+## Screenshots
+
+*(Drop your images into `docs/screenshots/` with the filenames below and they'll show up here —
+see `docs/screenshots/README.md` for the exact list.)*
+
+
+
+
+
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+
+## Why it exists
+
+Two problems, one app:
+
+- **You shouldn't need a cloud subscription to have an AI pair programmer.** MandoCode is
+ local-first — point it at Ollama and it runs on your own hardware, on your own data, for free. A
+ cloud model is there if you want the extra ceiling, never because the app requires it.
+- **A coding assistant is something you live in all day, not a popup.** So it gets a real app:
+ multiple agents open at once, a real shell, a file tree that actually knows about git, a place to
+ jot down a thought without opening a text editor, and a look you can make your own instead of one
+ fixed dark theme.
+
+## Features
+
+- **Agent tabs, and Split view for up to four at once** — each tab is its own independent
+ conversation, project folder, and model. Split view puts two, three, or four side by side so you
+ can watch several agents work in parallel instead of babysitting one at a time.
+- **A real integrated terminal** — PowerShell 7, Windows PowerShell, cmd, Git Bash, or WSL, running
+ as an actual shell (not a fake console), opened in the active agent's project folder.
+- **Git-aware file explorer** — a live file tree with branch, status, and dirty badges, inline diff
+ cards, one-click commit, and drag-to-reference straight into the chat.
+- **Context snapshots & session history** — closing an agent archives its conversation instead of
+ deleting it; reopen any past conversation later with its transcript and, when the model supports
+ it, its full memory. Snapshots let you carry an AI-written recap of one conversation into a
+ completely different model or a fresh agent.
+- **Notes** — an always-there jot pad, separate from any project, with optional AI help. The
+ assistant has no file tools here at all — the only way a reply reaches your note is a button you
+ press, so nothing gets written without you.
+- **Skills & MCP** — teach the assistant new, reusable capabilities (install one from a folder or a
+ zip, or have it write its own), and connect external tools over MCP.
+- **16 built-in themes** — from Dracula, Tokyo Night, and One Dark Pro to a genuinely period-correct
+ Windows 98 desktop and a flickering Cathode Ray CRT tube, plus your own background image behind
+ the chat.
+- **A tiny built-in music player** — lofi and synthwave come bundled, or point it at any folder of
+ your own MP3s.
+- **Guided first-run setup** — on first launch it finds (or installs) Ollama for you and helps you
+ pick a starter model, right in the chat. No config file to hand-edit before you can say hello.
## Install (no build needed)
@@ -13,17 +99,16 @@ Download the latest `MandoCode.Desktop-*-win-x64.zip` from
[Releases](https://github.com/DevMando/MandoCode.Desktop/releases), extract it anywhere, and run
`MandoCode.Desktop.exe`. The zip is fully self-contained — **no .NET install required**.
-On first launch the app runs a **guided setup right in the chat**: it finds Ollama (offering to
+On first launch the app runs a guided setup right in the chat: it finds Ollama (offering to
install it via winget if it's missing), starts the daemon, and helps you pick a first model — a
cloud model (best quality, no GPU needed, free ollama.com sign-in) or a local one from a short
-list with size and hardware hints. Re-run the wizard any time by typing `/setup` or with the
-**Run guided setup** button in Settings.
+list with size and hardware hints. Re-run the wizard any time with `/setup` or the **Run guided
+setup** button in Settings.
Requirements: Windows 10 (1809+) or Windows 11, with the WebView2 runtime — preinstalled on
-Windows 11 and kept current by Edge on Windows 10. Models are served by
-[Ollama](https://ollama.com); you don't need it installed beforehand, the wizard handles it.
+Windows 11 and kept current by Edge on Windows 10.
-## Clone & build
+## Build from source
```
git clone --recursive https://github.com/DevMando/MandoCode.Desktop.git
@@ -35,331 +120,24 @@ dotnet run --project src/MandoCode.Desktop
Already cloned without `--recursive`? The `MandoCode/` folder will be empty — run
`git submodule update --init` and build again.
-Built exe: `src\MandoCode.Desktop\bin\Debug\net8.0-windows10.0.19041.0\win-x64\MandoCode.Desktop.exe`
-- `MandoCode.Desktop.exe ` — open with that folder as the project root
- (otherwise the current directory; changeable in-app via the folder button).
-
-Requires the WebView2 runtime (preinstalled on Windows 11) and a reachable
-Ollama (`ollama serve`). Uses the same config file as the CLI, so both apps
-share endpoint/model/settings.
-
-## The harness submodule
-
-`/MandoCode` is pinned at a specific CLI commit — radical changes in the CLI repo
-can never break this app until the pin is deliberately moved. To roll the harness
-forward (do this against CLI release tags, not random commits):
-
-```
-cd MandoCode
-git fetch && git checkout v0.14.0 # or origin/main
-cd ..
-dotnet build src/MandoCode.Desktop/MandoCode.Desktop.csproj # fix what the compiler flags
-git add MandoCode && git commit -m "Roll harness to v0.14.0"
-```
-
-When rolling, also eyeball the three *ported* seams for behavioral drift (they
-compile independently of the CLI's originals):
-
-| Desktop port | CLI original |
-|---|---|
-| `ViewModels/ChatController.cs` | `Components/App.razor` interactive loop |
-| `Services/WinUiApprovalService.cs` | `Services/Approval/DiffApprovalHandler.cs` |
-| `Services/TranscriptHtmlBuilder.cs` | `MarkdownHtmlRenderer` / `OperationDisplayRenderer` |
-
-The harness is safe to instantiate once per agent — its statics are pure functions and readonly
-`Regex`, and `AIService` takes every collaborator by constructor. Rolling the pin forward, watch
-for that changing.
-
-## Architecture
-
-| Layer | CLI (MandoCode) | Desktop (this app) |
-|---|---|---|
-| Orchestrator | `Components/App.razor` interactive loop | `ViewModels/ChatController.cs` (faithful port), one per agent |
-| Approvals | `DiffApprovalHandler` (Spectre panels) | `Services/WinUiApprovalService.cs` + each agent's own XAML overlay (same labels, bypass state, `DiffApprovalResult` contract) |
-| Transcript | ANSI scrollback + Spectre renderables | WebView2 + `TranscriptHtmlBuilder` (Markdig HTML, themed) |
-| Busy/spinner | `SpinnerService` (ANSI) | `BusyStateService` → ProgressRing |
-| Onboarding | `OnboardingFlow` terminal prompts | In-chat guided wizard (auto on first launch, `/setup` after) + Settings page |
-| Everything else | `Services/`, `Plugins/`, `Models/` | **reused verbatim via project reference** |
-
-Key seams the harness already provided (unchanged): `AIService.ChatStreamAsync`,
-`OnWrite/Delete/CommandApprovalRequested` delegates, `PlanHandoff.OnPlanRequested`,
-`McpApprovalGate.OnApprovalRequested`, `TaskPlannerService.ExecutePlanAsync`
-progress events, `DiffService` diff models.
-
-## Agents
-
-Each tab is an independent agent. `Services/AgentSession.cs` hand-constructs one agent's object
-graph; `SessionManager` owns the set of them. The split matters:
-
-| Per agent | App-wide |
-|---|---|
-| `AIService` (its conversation, its model), `ChatController`, `TaskPlannerService` | The `MandoCodeConfig` on disk — the **defaults** a new agent starts on |
-| `MandoCodeConfig` clone, `ProjectRootAccessor`, `SkillLoader`, `FileAutocompleteProvider` | `McpClientManager` (one set of server processes) |
-| `TokenTrackingService`, `PlanHandoff`, `TranscriptWriter`, `BusyStateService`, `ShellRunner` | `MusicPlayerService`, `ThemeManager` (static), `TranscriptHtmlBuilder`, `SpinnerService` |
-| `WinUiApprovalService`, `ApprovalPromptGate`, `McpApprovalGate` | `ConfigCoordinator`, `McpCoordinator`, `SkillCoordinator`, `SessionManager`, `SnapshotStore`, `SessionArchiveStore`, `NoteStore`, `UiUpdateCheckService` |
-
-Tabs default to `Agent 1`, `Agent 2`, … (the folder shows in the header); the number reuses the
-lowest free slot, and a rename or folder change never overwrites it. Each tab's `⋯` options menu
-carries Rename, Take snapshot, Export transcript, and Close. The model in each header opens a
-quick-switch dropdown (cloud first, `cloud`/`local` badges) rather than a full-screen picker.
-
-Closing the **last** agent is allowed: it leaves a clean empty state (showing the chat background)
-with a one-click way to start a new agent. Actions that need an agent to act on — the Settings and
-MCP pages, and snapshot Import — disable while none is open, then re-enable when you open one.
-
-### Split view (2–4 agents at once)
-
-The **Split** button puts two agents side by side in a resizable view, and further panes are added
-from the split bar's **Add pane** button or a tab's **Add to split view** (its `⋯` menu, or
-right-click). **Add pane** is a `SplitButton`: clicking it panes the next agent that isn't shown
-yet, while its chevron lists the agents still available so you can pick a specific one — the same
-shape as the terminal's shell picker. The layout follows the pane count: two side by side, three
-across, four as a 2×2 —
-past three, columns alone leave each pane too narrow for a transcript plus an input box. Every
-divider is draggable, and each one repartitions only the two panes either side of it, so adjusting
-one split never nudges a third pane.
-
-It's called *split view* rather than *compare* because comparing two models on the same prompt is
-only one of the things it's for: with three or four panes open you're usually watching agents work
-in parallel on different folders, not comparing their output.
-
-The pane set is an explicit, remembered choice — never set by plain-clicking a tab. Clicking a paned
-agent's tab shows the split; clicking any other agent shows it normally while the set waits.
-Dropping below two panes turns the split off and leaves you on the agent that survived. The set and
-its divider positions persist across restarts (in `workspace.json`, keyed by each agent's durable
-persist-key so a skipped project folder drops one pane rather than shifting all of them).
-
-Panes are ordinary agent views moved between grid cells with `Grid.SetColumn`/`Grid.SetRow` —
-**never re-parented**, so every WebView (and its live transcript) survives the switch, which is the
-whole reason the tab surface is built the way it is (see below). The row and column tracks are
-rebuilt in code per pane count, interleaving a divider track between adjacent panes; track
-definitions and dividers are the only things that change, so no agent view ever leaves the tree.
-The geometry and divider math live in `Services/PaneLayout.cs`, kept free of WinUI types so they're
-unit tested directly.
-
-The split bar uses chips with `MenuFlyout` pickers rather than `ComboBox`es on purpose: rebuilding
-ComboBox items as containers makes WinUI throw `COMException 0x80070490` on the next selection.
+`MandoCode.Desktop.exe ` opens with that folder as the project root (otherwise the current
+directory; changeable in-app via the folder button). Requires a reachable Ollama (`ollama serve`).
+Uses the same config file as the CLI, so both apps share endpoint/model/settings.
-The three approval services are per-agent for **correctness**, not tidiness. Shared, they break
-in ways that are invisible until a second tab exists: `WinUiApprovalService` holds the
-"don't ask again" bypass set, so one agent's blanket approval would auto-approve writes in every
-other; `ApprovalPromptGate` is a `SemaphoreSlim(1,1)` built for one console, so an unanswered
-approval in one agent would stop another's from ever rendering; and `ChatController` **assigns**
-(not `+=`) five approval delegates, so on shared services the last agent constructed silently
-steals every approval.
+## Under the hood
-One setting can't be per-agent and is labelled app-wide in the UI: **Appearance** is a property
-of the window (and lives outside the shared config). **Context window** used to share that label —
-it was applied as `OLLAMA_CONTEXT_LENGTH` at daemon start, one daemon, one window — but it now
-rides on every chat request as `num_ctx`, so each agent's own value governs its own conversations,
-like any other per-agent setting.
-
-### Settings and the config file
-
-`~/.mandocode/config.json` is not "the current settings". It is the **defaults a new agent starts
-on**. Editing Settings changes the selected agent, live, for that session; **Make Default for New
-Agents** is the only action that writes the file (plus corrections like a healed endpoint URL, the
-onboarding flag, and the app-wide MCP server list).
-
-`ConfigCoordinator` is the only code that calls `MandoCodeConfig.Save()`. The rule can't be
-enforced by the type system — `Save()` is public and non-virtual on a harness type — so the
-`MANDO001` build target fails the build if `ChatController` calls `_config.Save()` on its clone,
-which would publish one agent's model as everybody's default. Cloning is a JSON round-trip
-**followed by `ValidateAndClamp()`**: `System.Text.Json` rebuilds `McpServers` with a
-case-sensitive comparer, and without the clamp every MCP lookup in the clone silently misses on
-a casing difference.
-
-### Context snapshots
-
-A snapshot is a portable, AI-written recap of a conversation — save the gist of one agent's
-context and carry it into another model or a fresh agent. Snapshots are offered when switching a
-model would clear the conversation, and on demand via `Take snapshot` (tab `⋯` menu), which goes
-straight to the save step. The recap is generated by `SnapshotEnhancer` (a bare, tool-less Ollama
-kernel, map-reduce over the full history so nothing is truncated) using a summarizer model you
-pick; a snapshot is therefore always born with a real recap — there is no "light"/un-enhanced state.
-Leave the name blank and the summarizer proposes a short title, which is then made unique against
-existing titles in code (`SnapshotNaming`) — an LLM can't be trusted to guarantee that itself.
-
-The **Snapshots** rail icon opens a global panel (the `SnapshotStore` is app-wide, one list for
-every tab, **persisted** to `snapshots.json`). Cards **group by project** and are **searchable**,
-and each project group is a collapsible `Expander` whose fold state is remembered
-(`PanelState` → `panel-state.json`). **Import** arms a snapshot's recap to ride along, invisibly,
-with the active agent's next message — carrying context into any model. The rail badge is an
-**unread count** (snapshots captured since you last opened the panel), not a running total, and
-clears when you open it.
-
-### Session history (reopen closed conversations)
-
-Closing an agent no longer discards its conversation — it **archives** it. `SessionArchiveStore`
-keeps an app-wide index (`sessions.json`) of closed conversations; the transcript, model memory, and
-conversation-log journals stay on disk (see [docs/session-persistence.md](docs/session-persistence.md)).
-The **History** rail panel lists them (grouped by project, searchable, collapsible), and **Open**
-reopens one as a fresh tab on its original persist-key so the normal restore cascade replays the
-transcript and — when the model supports it — rehydrates the full memory. `/clear` still forgets a
-conversation for good; only *closing* softened from "gone" to "recoverable." The archive is capped
-(newest 60); evicting a row deletes its journals so the on-disk stores stay bounded.
-
-### Notes (the jot pad)
-
-The **Notes** rail panel is an always-there scratchpad: plain text files under
-`~\.mandocode\notes`, beside the config file the CLI shares. **New** creates one and opens an editor docked next to the
-chat — autosaved on a 1.2s debounce (plus Ctrl+S, leaving the note, closing the panel, closing the
-window), renameable in place, with **Show in Explorer**.
-
-Notes are **app-wide**, the same call as snapshots and session history. A note is something you want
-to write down *now*, which is often between projects or before an agent is even open, so nothing here
-needs one. What survives of "which project was this about" is a plain **subfolder**: a new note is
-filed under the active agent's folder name when there is one, and sits loose at the top when there
-isn't. Grouping therefore costs no metadata, can't drift, and is corrected by dragging files around
-in Explorer.
-
-**The filesystem is the store** — no index, no JSON, which is also why the pad lives in
-`~\.mandocode` rather than LocalAppData: these are your files, meant to be greppable, syncable, and
-openable in any editor. Discovery walks one folder plus its immediate subfolders (one level only — a
-jot pad with a hierarchy is a filing system, and the search box is a better answer to "where did I
-put it"). In exchange, no row can ever point at a file that isn't there: a note added in Notepad
-shows up, and one deleted outside the app disappears. Search matches note **bodies** and quotes the
-matching line on the card; only search and the assistant see a body truncated, capped at
-`NoteStore.MaxTextBytes` (128 KB).
-
-#### The prompt bar
-
-Both surfaces — the open note and the list — carry a prompt at the bottom. It's chat-shaped, but the
-document above it is your note rather than a transcript, so replies land in the bar's own strip and
-reach a note only through **Insert** (at the cursor, replacing the selection if there is one) or
-**Replace note**. With text selected, the question is about the selection.
-
-The note's text travels **in the message**, taken from the live editor buffer — so the model always
-sees the note as it is right now, including keystrokes autosave hasn't written yet, with nothing to
-fall out of sync. Only the current message carries the note; earlier turns in the thread keep just
-their words, so a long back-and-forth doesn't ship five stale copies. On the list, the question is
-about the pad: every note's title and first line, plus the full text of whatever the search box is
-currently matching, and the bar states what it was given (`12 notes listed · 3 read in full`) because
-a capped read that looks total is the one thing an "ask about all my notes" box must not do.
-
-`Services/NoteAssistant.cs` builds a bare Ollama kernel with **no plugins, filters, or tools** — the
-same shape as `SnapshotEnhancer`. That's the design, not an optimization: with no file tools, "nothing
-writes your note but you" is true by construction rather than by policy, and no approval machinery is
-needed because the only route from a reply into a note is a button you pressed. It also isn't an
-`AIService` agent, because those exist to change your files (the opposite of what a notepad wants)
-and are scoped to a project root the pad deliberately sits outside of. Its model is picked from the
-chip under the prompt, defaults to the app-wide default model, and is remembered; the thread is
-per-note, in memory, and cleared when you switch notes — notes aren't conversations.
-
-The editor is not the only thing that writes these files, and doesn't assume it is. A
-`FileSystemWatcher` compares the file against what the editor last wrote: identical means the write
-was ours; changed-while-clean is adopted silently; changed-while-you-were-typing raises a conflict you
-resolve — *use the version on disk* or *keep what I typed*. A note deleted from under unsaved edits
-offers to save it back. `Services/NoteText.cs` handles the newline round trip, because a WinUI
-`TextBox` normalizes every newline to a bare CR — write that straight back out and a Notepad-authored
-CRLF note becomes one endless line. Both of those are why `NoteStore`, `NoteEntry`, and `NoteText` are
-WinUI-free with an injectable pad root: the tests drive them against real temp folders.
-
-Notes deliberately lack the **Delete all *n*** group button that Snapshots and History carry: those
-are artifacts the app generated, a note is something you wrote by hand, and one button that deletes a
-folder's worth of writing is a different class of risk.
-
-
-### Why the tab strip isn't a `TabView`
-
-WinUI's `TabView` hosts only the selected item's content, which detaches the previous tab and
-closes its `CoreWebView2`. `TranscriptWriter` retains nothing — the WebView2 DOM is the only copy
-of a conversation — so that would destroy the transcript on every tab switch. The strip carries
-headers only; content lives in `Visibility`-toggled sibling panels and is never re-parented.
+MandoCode Desktop reuses the CLI's entire harness — the AI service, task planner, plugins, MCP,
+skills, config, approvals, token tracking — as a pinned git submodule. Only the interface layer
+(agents, split view, the terminal, notes, theming, and everything else that makes this a native app
+rather than a console) is new. If you want the deep dive — how the harness submodule is pinned and
+rolled forward, how agents and split view are wired, how snapshots/notes/history actually persist —
+see **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)**.
## Releasing
-Push a tag (`git tag v0.2.0 && git push --tags`) and the Release workflow
-publishes a self-contained win-x64 zip to GitHub Releases. The app's built-in
-update checker watches those releases — older installs show an update notice
-within 24 hours.
-
-## What's in v0.1
-
-- Chat with streaming turns, function-call operation cards, markdown transcript
-- Write / command / delete / MCP approvals with native diff viewer (same
- approve / don't-ask-again / deny / new-instructions / cancel-plan semantics)
-- propose_plan flow: plan table, execute/reject/cancel, per-step progress bar,
- step-failure skip/cancel
-- Slash commands with autocomplete: /help /setup /clear /model /config /retry /learn
- /copy /copy-code /skills /force-skill /mcp /mcp tools /mcp remove /mcp-reload
- /music* /command /exit — plus `!cmd` shell escape and `@file` references
-- Token tracking + per-response summaries, per agent
-- Agent tabs — `+` opens another agent (`Agent 1`, `Agent 2`, …) with its own
- conversation, project folder, model, and settings; an approval waiting in a
- background agent badges its tab and the toast names it. Each tab's `⋯` menu:
- Rename, Take snapshot, Export transcript, Close. The header model opens a
- quick-switch dropdown (cloud first, `cloud`/`local` badges). Closing the last
- agent is allowed and leaves an empty state that shows the chat background
-- Split view — the **Split** button shows two agents side by side, and **Add pane**
- (or a tab's **Add to split view**) grows that to three across or four as a 2×2, every
- divider draggable; the pane set is a remembered, explicit choice, so clicking
- other tabs navigates without disturbing it, and it survives a restart
-- Session history — closing an agent archives its conversation instead of deleting
- it; the **History** panel reopens any past conversation as a new tab (with its
- transcript, and full memory when the model supports it), grouped by project and
- searchable. `/clear` still forgets for good
-- History cards show both ends of a conversation — the opening message (what it was
- about, since rows are titled by agent name) and a dimmer **last ·** line with your
- most recent message (whether it's worth resuming)
-- Full-text history search — the search box reads each archived conversation's whole
- text, not just its title and preview, and quotes the matching line on the card so
- every hit explains itself. Debounced and off the UI thread behind a per-session
- cache, so typing never waits on file IO
-- Bulk cleanup — opening a project group of two or more in History or Snapshots
- reveals a **Delete all *n*** button at the top of it, clearing the whole group
- after one confirmation, batched into a single store write rather than one per item
-- Context snapshots — save an AI-written recap of a conversation (summarized by a
- model you pick) and Import it into another model or a fresh agent; a global
- left-rail panel lists them, **persisted**, grouped by project, searchable, with
- collapsible groups. Unnamed snapshots get an auto-generated, unique title
-- Notes — a **Notes** rail panel that jots into the project itself: **New** writes
- a plain text file under `~\.mandocode\notes` and opens an editor beside the chat
- (autosave, rename in place, Show in Explorer). App-wide like snapshots, so a note never
- needs an agent open; new notes are filed under the folder you're working in when there is
- one, which is what the panel groups by. The filesystem is the store — no index, so a note
- written in Notepad appears and one deleted outside the app is gone. Searchable across note
- bodies with the matching line quoted
-- A prompt bar under both notes surfaces — ask about the open note (it's sent the live
- buffer, so the model always sees what you see) or about the whole pad, with the bar
- stating what it was given. Replies land in their own strip and reach a note only via
- **Insert** / **Replace note**; the assistant has no tools at all, so "nothing writes your
- note but you" is structural. External edits are adopted into the open editor, while an
- external change landing on unsaved typing raises a conflict you resolve — never a silent
- overwrite
-- Rail badges on History and Snapshots are unread counts that clear when you open
- the panel (persisted), not running totals
-- Integrated terminal — a sliding panel (Ctrl+` / Ctrl+Shift+` to maximize) running a
- real shell via ConPTY, rendered with xterm.js in WebView2; a shell picker chooses the
- shell and it opens in the active agent's project folder
-- Per-agent file explorer with git awareness — a live file tree (FileSystemWatcher) plus a
- Changes tab (GitQuickStatus): branch chip, add/modify/delete status, dirty badges, inline
- diff cards, one-click commit and per-file undo; tree items drag into the input as `@`-refs.
- WorkspaceDeltaTracker notes commits/reverts/branch switches made outside the conversation
-- Sidebar: Settings, MCP, and Skills as full-screen pages, acting on the selected agent
- - Settings — the whole config as a native form (toggles, sliders, number boxes,
- grouped Appearance/Connection/Generation/Behavior/Limits/Integrations); every
- change is validated through the shared ConfigKeySetter, same as the CLI, and
- applies to that agent alone. "Make Default for New Agents" saves it to disk
- - MCP — live server list with status/tool counts; add/edit servers in a single
- form modal with a Test button (isolated connection check + tool table preview).
- Servers are one app-wide set; each agent chooses whether to attach their tools
- - Skills — list installed skills (searchable, filterable, enabled per agent), install
- from a folder or zip, and an editor that can generate or refine a skill body with a
- model you pick (`SkillAuthor`); `SkillCoordinator` fans changes to every open agent
-- Guided wizards, built on the approval-overlay select + text primitives:
- - First-run setup — fires automatically in the chat on a fresh install: probe/start
- Ollama (offering a winget install when the CLI is missing), change endpoint, pick a
- starter model from a curated list (cloud recommended, or local tiers with size and
- hardware hints), pull it with live progress, cloud-auth check + sign-in walkthrough.
- Re-run any time via `/setup` or Settings → Run guided setup
- - `/model`, `/force-skill`, `/music-playlist` — pickers
- - 401 auto-recovery — a cloud 401 offers the `ollama signin` walkthrough inline
-- Branded application icon across the exe, taskbar, and window title bar
-- Update check against this repo's GitHub Releases (24h throttle, fail-silent)
-
-Not ported: matrix easter eggs; the CLI's ANSI terminal-theme service (N/A — the app ships
-its own integrated terminal instead, see above).
+Push a tag (`git tag v0.2.0 && git push --tags`) and the Release workflow publishes a
+self-contained win-x64 zip to GitHub Releases. The app's built-in update checker watches those
+releases — older installs show an update notice within 24 hours.
## License
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
new file mode 100644
index 0000000..d259e43
--- /dev/null
+++ b/docs/ARCHITECTURE.md
@@ -0,0 +1,328 @@
+# Architecture
+
+This is the internals doc for contributors — how MandoCode Desktop is put together, how it relates
+to the [MandoCode CLI](https://github.com/DevMando/MandoCode), and the reasoning behind the less
+obvious decisions. If you just want to use the app, see the [README](../README.md) instead.
+
+## The harness submodule
+
+The MandoCode AI coding agent with a native Windows desktop interface. Same engine
+as the [MandoCode CLI](https://github.com/DevMando/MandoCode) — literally: the CLI
+repo is pinned here as a **git submodule** (`/MandoCode`) and this app project-references
+`MandoCode/src/MandoCode/MandoCode.csproj`, reusing the entire harness (AIService,
+task planner, plugins, MCP, skills, config, approvals, token tracking). Only the
+user interface is different: WinUI 3 instead of RazorConsole.
+
+`/MandoCode` is pinned at a specific CLI commit — radical changes in the CLI repo
+can never break this app until the pin is deliberately moved. To roll the harness
+forward (do this against CLI release tags, not random commits):
+
+```
+cd MandoCode
+git fetch && git checkout v0.14.0 # or origin/main
+cd ..
+dotnet build src/MandoCode.Desktop/MandoCode.Desktop.csproj # fix what the compiler flags
+git add MandoCode && git commit -m "Roll harness to v0.14.0"
+```
+
+When rolling, also eyeball the three *ported* seams for behavioral drift (they
+compile independently of the CLI's originals):
+
+| Desktop port | CLI original |
+|---|---|
+| `ViewModels/ChatController.cs` | `Components/App.razor` interactive loop |
+| `Services/WinUiApprovalService.cs` | `Services/Approval/DiffApprovalHandler.cs` |
+| `Services/TranscriptHtmlBuilder.cs` | `MarkdownHtmlRenderer` / `OperationDisplayRenderer` |
+
+The harness is safe to instantiate once per agent — its statics are pure functions and readonly
+`Regex`, and `AIService` takes every collaborator by constructor. Rolling the pin forward, watch
+for that changing.
+
+## Layer map
+
+| Layer | CLI (MandoCode) | Desktop (this app) |
+|---|---|---|
+| Orchestrator | `Components/App.razor` interactive loop | `ViewModels/ChatController.cs` (faithful port), one per agent |
+| Approvals | `DiffApprovalHandler` (Spectre panels) | `Services/WinUiApprovalService.cs` + each agent's own XAML overlay (same labels, bypass state, `DiffApprovalResult` contract) |
+| Transcript | ANSI scrollback + Spectre renderables | WebView2 + `TranscriptHtmlBuilder` (Markdig HTML, themed) |
+| Busy/spinner | `SpinnerService` (ANSI) | `BusyStateService` → ProgressRing |
+| Onboarding | `OnboardingFlow` terminal prompts | In-chat guided wizard (auto on first launch, `/setup` after) + Settings page |
+| Everything else | `Services/`, `Plugins/`, `Models/` | **reused verbatim via project reference** |
+
+Key seams the harness already provided (unchanged): `AIService.ChatStreamAsync`,
+`OnWrite/Delete/CommandApprovalRequested` delegates, `PlanHandoff.OnPlanRequested`,
+`McpApprovalGate.OnApprovalRequested`, `TaskPlannerService.ExecutePlanAsync`
+progress events, `DiffService` diff models.
+
+## Agents
+
+Each tab is an independent agent. `Services/AgentSession.cs` hand-constructs one agent's object
+graph; `SessionManager` owns the set of them. The split matters:
+
+| Per agent | App-wide |
+|---|---|
+| `AIService` (its conversation, its model), `ChatController`, `TaskPlannerService` | The `MandoCodeConfig` on disk — the **defaults** a new agent starts on |
+| `MandoCodeConfig` clone, `ProjectRootAccessor`, `SkillLoader`, `FileAutocompleteProvider` | `McpClientManager` (one set of server processes) |
+| `TokenTrackingService`, `PlanHandoff`, `TranscriptWriter`, `BusyStateService`, `ShellRunner` | `MusicPlayerService`, `ThemeManager` (static), `TranscriptHtmlBuilder`, `SpinnerService` |
+| `WinUiApprovalService`, `ApprovalPromptGate`, `McpApprovalGate` | `ConfigCoordinator`, `McpCoordinator`, `SkillCoordinator`, `SessionManager`, `SnapshotStore`, `SessionArchiveStore`, `NoteStore`, `UiUpdateCheckService` |
+
+Tabs default to `Agent 1`, `Agent 2`, … (the folder shows in the header); the number reuses the
+lowest free slot, and a rename or folder change never overwrites it. Each tab's `⋯` options menu
+carries Rename, Take snapshot, Export transcript, and Close. The model in each header opens a
+quick-switch dropdown (cloud first, `cloud`/`local` badges) rather than a full-screen picker.
+
+Closing the **last** agent is allowed: it leaves a clean empty state (showing the chat background)
+with a one-click way to start a new agent. Actions that need an agent to act on — the Settings and
+MCP pages, and snapshot Import — disable while none is open, then re-enable when you open one.
+
+### Split view (2–4 agents at once)
+
+The **Split** button puts two agents side by side in a resizable view, and further panes are added
+from the split bar's **Add pane** button or a tab's **Add to split view** (its `⋯` menu, or
+right-click). **Add pane** is a `SplitButton`: clicking it panes the next agent that isn't shown
+yet, while its chevron lists the agents still available so you can pick a specific one — the same
+shape as the terminal's shell picker. The layout follows the pane count: two side by side, three
+across, four as a 2×2 —
+past three, columns alone leave each pane too narrow for a transcript plus an input box. Every
+divider is draggable, and each one repartitions only the two panes either side of it, so adjusting
+one split never nudges a third pane.
+
+It's called *split view* rather than *compare* because comparing two models on the same prompt is
+only one of the things it's for: with three or four panes open you're usually watching agents work
+in parallel on different folders, not comparing their output.
+
+The pane set is an explicit, remembered choice — never set by plain-clicking a tab. Clicking a paned
+agent's tab shows the split; clicking any other agent shows it normally while the set waits.
+Dropping below two panes turns the split off and leaves you on the agent that survived. The set and
+its divider positions persist across restarts (in `workspace.json`, keyed by each agent's durable
+persist-key so a skipped project folder drops one pane rather than shifting all of them).
+
+Panes are ordinary agent views moved between grid cells with `Grid.SetColumn`/`Grid.SetRow` —
+**never re-parented**, so every WebView (and its live transcript) survives the switch, which is the
+whole reason the tab surface is built the way it is (see below). The row and column tracks are
+rebuilt in code per pane count, interleaving a divider track between adjacent panes; track
+definitions and dividers are the only things that change, so no agent view ever leaves the tree.
+The geometry and divider math live in `Services/PaneLayout.cs`, kept free of WinUI types so they're
+unit tested directly.
+
+The split bar uses chips with `MenuFlyout` pickers rather than `ComboBox`es on purpose: rebuilding
+ComboBox items as containers makes WinUI throw `COMException 0x80070490` on the next selection.
+
+The three approval services are per-agent for **correctness**, not tidiness. Shared, they break
+in ways that are invisible until a second tab exists: `WinUiApprovalService` holds the
+"don't ask again" bypass set, so one agent's blanket approval would auto-approve writes in every
+other; `ApprovalPromptGate` is a `SemaphoreSlim(1,1)` built for one console, so an unanswered
+approval in one agent would stop another's from ever rendering; and `ChatController` **assigns**
+(not `+=`) five approval delegates, so on shared services the last agent constructed silently
+steals every approval.
+
+One setting can't be per-agent and is labelled app-wide in the UI: **Appearance** is a property
+of the window (and lives outside the shared config). **Context window** used to share that label —
+it was applied as `OLLAMA_CONTEXT_LENGTH` at daemon start, one daemon, one window — but it now
+rides on every chat request as `num_ctx`, so each agent's own value governs its own conversations,
+like any other per-agent setting.
+
+### Settings and the config file
+
+`~/.mandocode/config.json` is not "the current settings". It is the **defaults a new agent starts
+on**. Editing Settings changes the selected agent, live, for that session; **Make Default for New
+Agents** is the only action that writes the file (plus corrections like a healed endpoint URL, the
+onboarding flag, and the app-wide MCP server list).
+
+`ConfigCoordinator` is the only code that calls `MandoCodeConfig.Save()`. The rule can't be
+enforced by the type system — `Save()` is public and non-virtual on a harness type — so the
+`MANDO001` build target fails the build if `ChatController` calls `_config.Save()` on its clone,
+which would publish one agent's model as everybody's default. Cloning is a JSON round-trip
+**followed by `ValidateAndClamp()`**: `System.Text.Json` rebuilds `McpServers` with a
+case-sensitive comparer, and without the clamp every MCP lookup in the clone silently misses on
+a casing difference.
+
+### Context snapshots
+
+A snapshot is a portable, AI-written recap of a conversation — save the gist of one agent's
+context and carry it into another model or a fresh agent. Snapshots are offered when switching a
+model would clear the conversation, and on demand via `Take snapshot` (tab `⋯` menu), which goes
+straight to the save step. The recap is generated by `SnapshotEnhancer` (a bare, tool-less Ollama
+kernel, map-reduce over the full history so nothing is truncated) using a summarizer model you
+pick; a snapshot is therefore always born with a real recap — there is no "light"/un-enhanced state.
+Leave the name blank and the summarizer proposes a short title, which is then made unique against
+existing titles in code (`SnapshotNaming`) — an LLM can't be trusted to guarantee that itself.
+
+The **Snapshots** rail icon opens a global panel (the `SnapshotStore` is app-wide, one list for
+every tab, **persisted** to `snapshots.json`). Cards **group by project** and are **searchable**,
+and each project group is a collapsible `Expander` whose fold state is remembered
+(`PanelState` → `panel-state.json`). **Import** arms a snapshot's recap to ride along, invisibly,
+with the active agent's next message — carrying context into any model. The rail badge is an
+**unread count** (snapshots captured since you last opened the panel), not a running total, and
+clears when you open it.
+
+### Session history (reopen closed conversations)
+
+Closing an agent no longer discards its conversation — it **archives** it. `SessionArchiveStore`
+keeps an app-wide index (`sessions.json`) of closed conversations; the transcript, model memory, and
+conversation-log journals stay on disk (see [session-persistence.md](session-persistence.md)).
+The **History** rail panel lists them (grouped by project, searchable, collapsible), and **Open**
+reopens one as a fresh tab on its original persist-key so the normal restore cascade replays the
+transcript and — when the model supports it — rehydrates the full memory. `/clear` still forgets a
+conversation for good; only *closing* softened from "gone" to "recoverable." The archive is capped
+(newest 60); evicting a row deletes its journals so the on-disk stores stay bounded.
+
+### Notes (the jot pad)
+
+The **Notes** rail panel is an always-there scratchpad: plain text files under
+`~\.mandocode\notes`, beside the config file the CLI shares. **New** creates one and opens an editor docked next to the
+chat — autosaved on a 1.2s debounce (plus Ctrl+S, leaving the note, closing the panel, closing the
+window), renameable in place, with **Show in Explorer**.
+
+Notes are **app-wide**, the same call as snapshots and session history. A note is something you want
+to write down *now*, which is often between projects or before an agent is even open, so nothing here
+needs one. What survives of "which project was this about" is a plain **subfolder**: a new note is
+filed under the active agent's folder name when there is one, and sits loose at the top when there
+isn't. Grouping therefore costs no metadata, can't drift, and is corrected by dragging files around
+in Explorer.
+
+**The filesystem is the store** — no index, no JSON, which is also why the pad lives in
+`~\.mandocode` rather than LocalAppData: these are your files, meant to be greppable, syncable, and
+openable in any editor. Discovery walks one folder plus its immediate subfolders (one level only — a
+jot pad with a hierarchy is a filing system, and the search box is a better answer to "where did I
+put it"). In exchange, no row can ever point at a file that isn't there: a note added in Notepad
+shows up, and one deleted outside the app disappears. Search matches note **bodies** and quotes the
+matching line on the card; only search and the assistant see a body truncated, capped at
+`NoteStore.MaxTextBytes` (128 KB).
+
+#### The prompt bar
+
+Both surfaces — the open note and the list — carry a prompt at the bottom. It's chat-shaped, but the
+document above it is your note rather than a transcript, so replies land in the bar's own strip and
+reach a note only through **Insert** (at the cursor, replacing the selection if there is one) or
+**Replace note**. With text selected, the question is about the selection.
+
+The note's text travels **in the message**, taken from the live editor buffer — so the model always
+sees the note as it is right now, including keystrokes autosave hasn't written yet, with nothing to
+fall out of sync. Only the current message carries the note; earlier turns in the thread keep just
+their words, so a long back-and-forth doesn't ship five stale copies. On the list, the question is
+about the pad: every note's title and first line, plus the full text of whatever the search box is
+currently matching, and the bar states what it was given (`12 notes listed · 3 read in full`) because
+a capped read that looks total is the one thing an "ask about all my notes" box must not do.
+
+`Services/NoteAssistant.cs` builds a bare Ollama kernel with **no plugins, filters, or tools** — the
+same shape as `SnapshotEnhancer`. That's the design, not an optimization: with no file tools, "nothing
+writes your note but you" is true by construction rather than by policy, and no approval machinery is
+needed because the only route from a reply into a note is a button you pressed. It also isn't an
+`AIService` agent, because those exist to change your files (the opposite of what a notepad wants)
+and are scoped to a project root the pad deliberately sits outside of. Its model is picked from the
+chip under the prompt, defaults to the app-wide default model, and is remembered; the thread is
+per-note, in memory, and cleared when you switch notes — notes aren't conversations.
+
+The editor is not the only thing that writes these files, and doesn't assume it is. A
+`FileSystemWatcher` compares the file against what the editor last wrote: identical means the write
+was ours; changed-while-clean is adopted silently; changed-while-you-were-typing raises a conflict you
+resolve — *use the version on disk* or *keep what I typed*. A note deleted from under unsaved edits
+offers to save it back. `Services/NoteText.cs` handles the newline round trip, because a WinUI
+`TextBox` normalizes every newline to a bare CR — write that straight back out and a Notepad-authored
+CRLF note becomes one endless line. Both of those are why `NoteStore`, `NoteEntry`, and `NoteText` are
+WinUI-free with an injectable pad root: the tests drive them against real temp folders.
+
+Notes deliberately lack the **Delete all *n*** group button that Snapshots and History carry: those
+are artifacts the app generated, a note is something you wrote by hand, and one button that deletes a
+folder's worth of writing is a different class of risk.
+
+Undo/Redo for the assistant's inserts and replacements is a single-level **toggle** rather than a
+history stack: the editor remembers the buffer right before and right after the last assistant edit,
+and Undo/Redo just flip between those two exact states. Typing anything that lands outside both
+retires the toggle — the same rule a full undo stack uses to clear its redo branch on a new edit,
+just with only one edit remembered.
+
+### Why the tab strip isn't a `TabView`
+
+WinUI's `TabView` hosts only the selected item's content, which detaches the previous tab and
+closes its `CoreWebView2`. `TranscriptWriter` retains nothing — the WebView2 DOM is the only copy
+of a conversation — so that would destroy the transcript on every tab switch. The strip carries
+headers only; content lives in `Visibility`-toggled sibling panels and is never re-parented.
+
+## Full feature inventory (v0.1)
+
+- Chat with streaming turns, function-call operation cards, markdown transcript
+- Write / command / delete / MCP approvals with native diff viewer (same
+ approve / don't-ask-again / deny / new-instructions / cancel-plan semantics)
+- propose_plan flow: plan table, execute/reject/cancel, per-step progress bar,
+ step-failure skip/cancel
+- Slash commands with autocomplete: /help /setup /clear /model /config /retry /learn
+ /copy /copy-code /skills /force-skill /mcp /mcp tools /mcp remove /mcp-reload
+ /music* /command /exit — plus `!cmd` shell escape and `@file` references
+- Token tracking + per-response summaries, per agent
+- Agent tabs — `+` opens another agent (`Agent 1`, `Agent 2`, …) with its own
+ conversation, project folder, model, and settings; an approval waiting in a
+ background agent badges its tab and the toast names it. Each tab's `⋯` menu:
+ Rename, Take snapshot, Export transcript, Close. The header model opens a
+ quick-switch dropdown (cloud first, `cloud`/`local` badges). Closing the last
+ agent is allowed and leaves an empty state that shows the chat background
+- Split view — the **Split** button shows two agents side by side, and **Add pane**
+ (or a tab's **Add to split view**) grows that to three across or four as a 2×2, every
+ divider draggable; the pane set is a remembered, explicit choice, so clicking
+ other tabs navigates without disturbing it, and it survives a restart
+- Session history — closing an agent archives its conversation instead of deleting
+ it; the **History** panel reopens any past conversation as a new tab (with its
+ transcript, and full memory when the model supports it), grouped by project and
+ searchable. `/clear` still forgets for good
+- History cards show both ends of a conversation — the opening message (what it was
+ about, since rows are titled by agent name) and a dimmer **last ·** line with your
+ most recent message (whether it's worth resuming)
+- Full-text history search — the search box reads each archived conversation's whole
+ text, not just its title and preview, and quotes the matching line on the card so
+ every hit explains itself. Debounced and off the UI thread behind a per-session
+ cache, so typing never waits on file IO
+- Bulk cleanup — opening a project group of two or more in History or Snapshots
+ reveals a **Delete all *n*** button at the top of it, clearing the whole group
+ after one confirmation, batched into a single store write rather than one per item
+- Context snapshots — save an AI-written recap of a conversation (summarized by a
+ model you pick) and Import it into another model or a fresh agent; a global
+ left-rail panel lists them, **persisted**, grouped by project, searchable, with
+ collapsible groups. Unnamed snapshots get an auto-generated, unique title
+- Notes — a **Notes** rail panel that jots into the project itself: **New** writes
+ a plain text file under `~\.mandocode\notes` and opens an editor beside the chat
+ (autosave, rename in place, Show in Explorer). App-wide like snapshots, so a note never
+ needs an agent open; new notes are filed under the folder you're working in when there is
+ one, which is what the panel groups by. The filesystem is the store — no index, so a note
+ written in Notepad appears and one deleted outside the app is gone. Searchable across note
+ bodies with the matching line quoted
+- A prompt bar under both notes surfaces — ask about the open note (it's sent the live
+ buffer, so the model always sees what you see) or about the whole pad, with the bar
+ stating what it was given. Replies land in their own strip and reach a note only via
+ **Insert** / **Replace note**; the assistant has no tools at all, so "nothing writes your
+ note but you" is structural. External edits are adopted into the open editor, while an
+ external change landing on unsaved typing raises a conflict you resolve — never a silent
+ overwrite. Undo/Redo toggles the assistant's last insert/replace
+- Rail badges on History and Snapshots are unread counts that clear when you open
+ the panel (persisted), not running totals
+- Integrated terminal — a sliding panel (Ctrl+` / Ctrl+Shift+` to maximize) running a
+ real shell via ConPTY, rendered with xterm.js in WebView2; a shell picker chooses the
+ shell and it opens in the active agent's project folder
+- Per-agent file explorer with git awareness — a live file tree (FileSystemWatcher) plus a
+ Changes tab (GitQuickStatus): branch chip, add/modify/delete status, dirty badges, inline
+ diff cards, one-click commit and per-file undo; tree items drag into the input as `@`-refs.
+ WorkspaceDeltaTracker notes commits/reverts/branch switches made outside the conversation
+- Sidebar: Settings, MCP, and Skills as full-screen pages, acting on the selected agent
+ - Settings — the whole config as a native form (toggles, sliders, number boxes,
+ grouped Appearance/Connection/Generation/Behavior/Limits/Integrations); every
+ change is validated through the shared ConfigKeySetter, same as the CLI, and
+ applies to that agent alone. "Make Default for New Agents" saves it to disk
+ - MCP — live server list with status/tool counts; add/edit servers in a single
+ form modal with a Test button (isolated connection check + tool table preview).
+ Servers are one app-wide set; each agent chooses whether to attach their tools
+ - Skills — list installed skills (searchable, filterable, enabled per agent), install
+ from a folder or zip, and an editor that can generate or refine a skill body with a
+ model you pick (`SkillAuthor`); `SkillCoordinator` fans changes to every open agent
+- Guided wizards, built on the approval-overlay select + text primitives:
+ - First-run setup — fires automatically in the chat on a fresh install: probe/start
+ Ollama (offering a winget install when the CLI is missing), change endpoint, pick a
+ starter model from a curated list (cloud recommended, or local tiers with size and
+ hardware hints), pull it with live progress, cloud-auth check + sign-in walkthrough.
+ Re-run any time via `/setup` or Settings → Run guided setup
+ - `/model`, `/force-skill`, `/music-playlist` — pickers
+ - 401 auto-recovery — a cloud 401 offers the `ollama signin` walkthrough inline
+- Branded application icon across the exe, taskbar, and window title bar
+- Update check against this repo's GitHub Releases (24h throttle, fail-silent)
+
+Not ported: matrix easter eggs; the CLI's ANSI terminal-theme service (N/A — the app ships
+its own integrated terminal instead, see above).
diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md
new file mode 100644
index 0000000..926a92a
--- /dev/null
+++ b/docs/screenshots/README.md
@@ -0,0 +1,20 @@
+# Screenshots
+
+Drop images in this folder with the filenames below and they'll automatically appear in the main
+`README.md` — nothing else needs to change.
+
+| Filename | What it should show |
+|---|---|
+| `chat-and-agents.png` | The main chat view with a couple of agent tabs open — the "hero" shot, wide |
+| `split-view.png` | Split view with two or more agents side by side |
+| `terminal.png` | The integrated terminal panel open, running a real shell |
+| `notes.png` | The Notes panel with a note open (and, ideally, the AI prompt bar in view) |
+| `explorer.png` | The file explorer panel with git status badges / a diff card visible |
+| `theme-crt.png` | The app in the "Cathode Ray (CRT)" theme |
+| `theme-w98.png` | The app in the "W98 - Y2K" theme |
+
+PNG or JPG both work. No fixed size — GitHub scales them to the column width — but a consistent
+window size across shots makes the table on the README line up more cleanly.
+
+Feel free to add more (a snapshots/history panel, the skills page, one of the other themes) — just
+add a matching `
` tag in the README's Screenshots section.
diff --git a/src/MandoCode.Desktop/MandoCode.Desktop.csproj b/src/MandoCode.Desktop/MandoCode.Desktop.csproj
index 9ab53c6..ca9514f 100644
--- a/src/MandoCode.Desktop/MandoCode.Desktop.csproj
+++ b/src/MandoCode.Desktop/MandoCode.Desktop.csproj
@@ -2,7 +2,7 @@
WinExe
- net8.0-windows10.0.19041.0
+ net10.0-windows10.0.19041.0
10.0.19041.0
MandoCode.Desktop
app.manifest