@puddle-code/cli 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -33
- package/README.md +34 -0
- package/dist/index.js +99 -42
- package/dist/public/assets/Dashboard-zbeaP4WM.js +1 -0
- package/dist/public/assets/{EditorTabClose-Dal7HKgg.js → EditorTabClose-Bujs4cab.js} +1 -1
- package/dist/public/assets/{ModelRefcount-B12_Xdv2.js → ModelRefcount-BN4jRD-x.js} +1 -1
- package/dist/public/assets/{PaneEditorBody-D1npLlye.js → PaneEditorBody-COdCF9BY.js} +1 -1
- package/dist/public/assets/SettingsDialog-CKW2AM11.js +1 -0
- package/dist/public/assets/{Terminal-D03wYX9q.js → Terminal-hw3UjYEu.js} +2 -2
- package/dist/public/assets/Workspace-DrDkPyrd.js +6 -0
- package/dist/public/assets/{api-DnhKCI2U.js → api-BJKPJeFZ.js} +1 -1
- package/dist/public/assets/{buffer-store-CJsO7gGE.js → buffer-store-BGy4Fj9o.js} +2 -2
- package/dist/public/assets/context-menu-CnHdXMkI.js +1 -0
- package/dist/public/assets/{cssMode-D1iXtOo8.js → cssMode-DuLDfCcC.js} +1 -1
- package/dist/public/assets/{editor-tabs-DHG9ShX4.js → editor-tabs-Cw21CVB0.js} +1 -1
- package/dist/public/assets/{htmlMode-CBFRVNoG.js → htmlMode-DaGDGb6z.js} +1 -1
- package/dist/public/assets/index-wPaOVZ8U.js +10 -0
- package/dist/public/assets/index-y2Zzy0lJ.css +2 -0
- package/dist/public/assets/{jsonMode-CpQhQE0R.js → jsonMode-Ekn8HPfg.js} +1 -1
- package/dist/public/assets/ssh-mode-CghPpMrk.js +1 -0
- package/dist/public/assets/switch-CQITZ7yg.js +1 -0
- package/dist/public/assets/{tsMode-ET2kHbb0.js → tsMode-CsjWL1Qz.js} +1 -1
- package/dist/public/index.html +3 -3
- package/package.json +1 -1
- package/dist/public/assets/Dashboard-CcpYaLlK.js +0 -1
- package/dist/public/assets/SettingsDialog-Cp_1M1k7.js +0 -1
- package/dist/public/assets/Workspace-d0mqka1w.js +0 -6
- package/dist/public/assets/context-menu-BWOPZx3M.js +0 -1
- package/dist/public/assets/index-BcSTVRMs.js +0 -10
- package/dist/public/assets/index-D9uQqhHq.css +0 -2
- package/dist/public/assets/ssh-mode-C_dNI0ql.js +0 -1
- package/dist/public/assets/switch-RkFdxF0W.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,45 +7,30 @@ Past releases: see docs/changelogs/.
|
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
|
10
|
-
## [0.0.
|
|
10
|
+
## [0.0.3] — 2026-07-15
|
|
11
11
|
|
|
12
12
|
### Added
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
- macOS copy/paste in terminals: **⌘C** copies the terminal's selection to the clipboard (`Ctrl-C` stays the interrupt), and **⌘V** pastes. Only on Mac clients (SPEC §7).
|
|
21
|
-
- The file explorer is now VSCode-grade (SPEC §8): git decorations (a status colour + one-letter badge — **U**ntracked/**M**odified/**A**dded/**D**eleted/**R**enamed/**C**onflict, git-ignored files greyed, folders tinted by their most-significant descendant), colourful per-type file icons, rich right-click menus on files, folders and empty space, a cut/copy/paste clipboard with drag-to-move, multi-select (⌘/⇧-click), full arrow-key navigation with type-to-jump, inline create/rename with a delete confirmation, and a header utility bar (New File · New Folder · Refresh · Collapse Folders) whose branch title eases sideways on hover to reveal the tail the icons occlude. Backed by new daemon endpoints — `GET /git-status` and `POST /create|/rename|/copy|/delete` on `/api/worktrees/:sid`, every path confined to the worktree (additive; `PROTOCOL_VERSION` minor bump).
|
|
22
|
-
- Images, video, audio and PDFs now open as an inline preview tab instead of the "Binary — use Download" fallback. A new `GET /api/worktrees/:sid/media` streams the file with its real content-type (`image/*`, `video/*`, `audio/*`, `application/pdf`) and an `inline` disposition; the viewer fetches it through the authed API and renders from an object URL, so no token ever rides in an element `src`. Unknown binaries keep the download fallback (SPEC §8; additive `PROTOCOL_VERSION` minor bump).
|
|
23
|
-
- The file editor gained VSCode keybindings: **⌥Z** toggles line wrap (it flips the `editorWordWrap` client setting, so every open editor follows and the choice persists). ⌘/Ctrl+S still saves; Monaco's stock multi-cursor / comment / find bindings are unchanged (SPEC §8).
|
|
24
|
-
- Right-click a session — its sidebar row, its collapsed status dot, or its tab in the top strip — to open the same kill / resume / rename / archive / move menu as the row's ellipsis (SPEC §12).
|
|
25
|
-
- Settings → **Sessions** (renamed from Permissions & Safety) now edits the launch-text templates an agent receives when it starts a freshly created worktree (`{{rules}}` injects the repo's onboarding notes) or joins an existing/shared one. Either template may be cleared to send no preamble, or reset to the built-in default (SPEC §4).
|
|
26
|
-
- Archived sessions are no longer hidden away: they collapse under an **Archived** header at the bottom of the sessions sidebar and reopen (view-only, to read their terminal history) — archiving is not deletion (SPEC §4).
|
|
27
|
-
- `puddle start` and `puddle connect` now run the cockpit **in the background** once ready: the command bootstraps interactively (ssh prompts still land on your terminal), detaches, prints the URL, and exits — the terminal may close. `--foreground` keeps the old attached behaviour. New commands: `puddle list` shows running cockpits (verified by pid + a per-instance nonce the UI server echoes as `X-Puddle-Cockpit`, never by reachability alone) and `puddle kill [local | user@host | --all]` stops one — sessions keep running on the host. Cockpit records live in `~/.puddle/cockpits/`, background logs in `~/.puddle/logs/cockpit-<target>.log` (SPEC §10).
|
|
14
|
+
- **Unarchive a session** (protocol 6.2, `POST /api/sessions/:id/unarchive`). Archiving is now a **reversible hide**, not a teardown: it keeps the worktree, its branch, and the agent conversation, so archived rows carry the same ⋯ menu and can be brought straight back. If the worktree still exists the session resumes with its history; if it was pruned (or its branch deleted) the session returns visible for its history only, with resume disabled. Archiving itself no longer pops a dialog — one click hides it, and because nothing is destroyed a dirty worktree is safe (SPEC §4).
|
|
15
|
+
- **VSCode-style preview tabs** (SPEC §8): single-clicking a file, session, or terminal opens it in one reusable _preview_ tab (shown in italics) that the next single-click replaces; double-clicking the item — or its preview tab — pins it as a permanent tab.
|
|
16
|
+
- A third **launch-text template** in Settings → Sessions, "Resume after restart", sent when a session is resumed after a daemon restart or machine reboot (previously a fixed built-in note). Editable per profile; empty sends nothing (protocol 6.2, profile setting `restartTemplate`).
|
|
17
|
+
- The **Files** and **Search** navigator headers now name the worktree's **absolute path** (what they operate over), while Changes keeps naming the branch.
|
|
18
|
+
- `README.md` gained a **Development & teardown** section: running a local dev build (`--tarball`/`--foreground`) versus the published CLI, killing the cockpit vs. stopping the daemon service (launchd/systemd/nohup), and a full uninstall.
|
|
19
|
+
- A per-profile **tab-title template** (Settings → Sessions) composes each session's tab/sidebar label from `${…}` parts — `${name}`, `${branch}`, `${sequence}`, `${status}`, a conditional `${separator}`, and more — with a live preview; the default `${name}` reproduces the previous label. Terminals and agents without their own name now also take a name from the terminal-title escape the process sets (the `${sequence}` value). Protocol 6.0 → 6.1 (additive).
|
|
28
20
|
|
|
29
21
|
### Changed
|
|
30
22
|
|
|
31
|
-
- **
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
- The
|
|
35
|
-
- The daemon no longer crashes (or crash-loops under its supervisor) when its configured port is already in use: it now **falls back to an OS-assigned free port** and records where it actually bound in `~/.puddle/runtime.json`. `puddle connect`/`start` discover the daemon from that file (falling back to `config.json`, then 7434), always revalidating by token, so a busy 7434 no longer breaks a connection. Every start still prefers the configured port, so the daemon returns to it once the conflict clears (SPEC §10).
|
|
36
|
-
- A session's default display name is now the agent's own session name (for Claude Code, the transcript's ai-title / agent-name — what its resume picker shows) instead of the leading hex block of the puddle session id. A user rename still overrides it, and clearing the rename reverts to the agent's name (then the id). This retires the `.puddle/session-title` marker file, which could not work once several agents shared one worktree. Additive protocol change: the `sessions.agent_title` field and the `renamed` WS event's `agent_title` (`PROTOCOL_VERSION` minor bump).
|
|
37
|
-
- Releases no longer publish a `darwin-x64` daemon tarball: GitHub's last Intel Mac runner (`macos-13`) queues so long it blocked the whole release. Intel Mac hosts are unsupported until the entry is restored (or built under Rosetta); Apple-silicon Macs and Linux are unaffected.
|
|
23
|
+
- **Sidebar colours**: hint/metadata text (gitignored files, placeholders, search & empty-state hints, commit metadata, branch/path labels, setting descriptions) is now a neutral **grey** instead of the distracting warm golden bark. The gold survives as a deliberate accent — a new `--text-gold` token (gold in both themes) — for the sidebar's **glyph icons** and its **ALL-CAPS section headings**. Dark theme's muted was already grey.
|
|
24
|
+
- The left navigator header (Files/Search/Changes) no longer auto-scrolls its title on hover (distracting); it's a plain horizontally-scrollable strip **anchored to the right**, so the deepest part of an absolute path stays visible. The Worktrees tab keeps its calmer per-item hover-scroll.
|
|
25
|
+
- **Top bar** revamp: the puddle mark shrank and now sits beside the daemon's **host name** as one click-home block; the centre is a dimmed command field (hinting the active project name + ⌘K) that opens the palette; the separate ⌘K button is gone.
|
|
26
|
+
- The empty workspace pane no longer reserves a blank tab bar; it shows a large, muted puddle mark and a dimmed **⌘K** button that opens the command palette.
|
|
38
27
|
|
|
39
28
|
### Fixed
|
|
40
29
|
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
- A
|
|
45
|
-
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
- A request with a malformed percent-escape (e.g. `/100%_done.png`) no longer crashes the whole cockpit process: the static handler answers 404 instead of letting `decodeURIComponent` throw through the request listener.
|
|
49
|
-
- `puddle connect` no longer loops "tunnel lost — reconnecting… / tunnel restored". Three causes: the ssh forward ran with no keepalives, so idle NAT/firewall timeouts felled it on every quiet spell (`ServerAliveInterval=15`/`ServerAliveCountMax=3` on every ssh spawn now); a forward child killed after a failed readiness probe re-triggered the exit handler and spawned a second reconnect loop racing the first; and every sub-second blip printed a lost/restored pair (outages healing within a 2s grace window are now silent, unless the tunnel is genuinely flapping — a drop within 30s of a restore — which announces immediately). The forward also runs with `ExitOnForwardFailure=yes` so a failed `-L` bind dies visibly.
|
|
50
|
-
- `puddle start` can no longer be fooled by another cockpit's UI server sitting on the daemon's port. The UI server never auto-picks the daemon's own port (a `puddle connect` launched while 7433 was busy used to land exactly on 7434), and the daemon probe now verifies identity — only a 200 with this host's token counts; a 401/403 is reported as a clear port conflict instead of "token rejected" (or, worse, silently wiring the local cockpit to a remote daemon). A state directory without a managed install now bootstraps one rather than refusing (`start`/`connect` promise a running daemon — SPEC §10; the existing db/token/worktrees are untouched).
|
|
51
|
-
- The CLI build wipes `dist/` before bundling, so the published npm package can no longer pick up leftovers from earlier builds (v0.0.1 shipped three stray tsc artefacts from the pre-Phase-6 stub this way); the `bin` path is normalised to the form npm was auto-correcting at publish time. No protocol change.
|
|
30
|
+
- On a **brand-new project**, the first file/terminal you opened could show a blank pane until a reload: the one-time layout-tree migration effect persisted an _empty_ tree and, because it re-ran on every render (its dep changed identity each time), could commit **after** that first open and overwrite it. It now fires exactly once and skips persisting an empty tree, so it can't race — nor clobber — the first open.
|
|
31
|
+
- Creating a session or terminal now appears in the sidebar **immediately** instead of only after a reload: the create/rename/archive/kill/migrate mutations invalidated only the per-project session list, never the cross-project sidebar's `['sessions', 'profile', …]` query.
|
|
32
|
+
- The **Settings** dialog no longer intermittently fails to open (or silently closes) and need a reload: its open section is now a controlled store rather than being read live from `window.location.hash` — assigning the fragment its current value fires no `hashchange`, and react-router navigations can clear the fragment with no event at all. The URL `#settings/<section>` stays as a deep-link mirror, resynced on back/forward.
|
|
33
|
+
- A rename made **inside the agent** (e.g. claude-code's `/rename`) now propagates to the session's display name live, **including while the session sits idle**. Such renames are client-side (they rewrite the agent's transcript title) and change no puddle status, so the daemon re-reads the agent's own name on each status change, when the agent emits a terminal-title escape (OSC 0/1/2, throttled), and — the reliable path for an idle rename, which emits neither — on a low-frequency timer (a cheap tail read that early-returns when unchanged). The transcript stays the source of truth and a user rename still wins (SPEC §4).
|
|
34
|
+
- Filetree header: the branch title now uses the full sidebar width at rest. The utility, pin, and worktree-picker controls surfaced from an overlay on hover/focus instead of reserving (and so occluding) width while hidden — the title was clipped even when no icons showed (SPEC §12).
|
|
35
|
+
- Worktrees list: rows now extend to the sidebar's right edge — the hover-only prune control no longer reserves an empty gap after the badges.
|
|
36
|
+
- `puddle connect` / `status` no longer fail with "could not open a tunnel" against hosts reached over **Tailscale SSH** (or other non-OpenSSH servers). Over a multiplexed master such servers install the `-L` forward on the master and the spawned `ssh` client exits immediately while the forward keeps carrying traffic; the tunnel wrongly treated that client's exit — and required it to stay alive for readiness — as the tunnel dying. The forward is now judged by the **forward itself**: readiness is its local listener accepting plus an end-to-end probe the daemon answers `/api/version` through, and liveness is a periodic check of that listener — never the spawned client's fate. The `ExitOnForwardFailure=yes` flag (which the same servers trip) was dropped, and forwards abandoned on the master are cleaned up with `ssh -O cancel` since they outlive the client and would otherwise leak the port. Automatic remote-daemon port discovery (runtime.json → config.json → 7434) is unchanged (SPEC §10).
|
package/README.md
CHANGED
|
@@ -73,6 +73,40 @@ The CLI serves the UI at a stable local origin and reverse-proxies the API to th
|
|
|
73
73
|
|
|
74
74
|
Everything lives under `~/.puddle` on the host, installed without sudo. Uninstalling is stopping the service and deleting that directory.
|
|
75
75
|
|
|
76
|
+
## Development & teardown
|
|
77
|
+
|
|
78
|
+
**One daemon, many clients.** There is a single local daemon per machine, living under `~/.puddle` and run by one supervised service (launchd's `dev.puddle.puddled` on macOS, systemd's `puddled` on Linux). The global `puddle` and a repo-run `node packages/cli/dist/index.js` are **both just clients** that talk to — and, when needed, install — that same daemon. They never run side by side, and there is no separate "dev daemon" alongside a "production daemon".
|
|
79
|
+
|
|
80
|
+
**Dev build vs. production.** `npm i -g @puddle-code/cli` is the production path — its `puddle` fetches and upgrades the daemon from this repo's GitHub Releases. To exercise uncommitted changes, build and run from the repo:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
pnpm build && pnpm build:tarball
|
|
84
|
+
node packages/cli/dist/index.js start --tarball dist-release/puddled-v*.tar.gz --foreground
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`--tarball` sets the install _source_ only, and is consulted **only when the CLI actually installs the daemon** — when none is running, the daemon is stopped, or a protocol-major upgrade fires. If a compatible daemon (same protocol major) is already up, `start` just serves the cockpit against it and **the tarball is ignored** (even a newer app version — nothing compares app versions). So to load a fresh dev build over a running daemon you must **stop it first** (see _Kill_ below), then re-run `start --tarball …`. `--foreground` keeps the cockpit attached (`connect <user>@<host> --tarball …` is the remote form). Both clients share one `~/.puddle` daemon and cockpit registry, so `puddle list` / `puddle kill` see either — don't point both at the same host at once. (Never launch the daemon from inside a coding-agent shell: it inherits the agent's env and breaks conversation resume — use a plain terminal.)
|
|
88
|
+
|
|
89
|
+
**Kill.** `puddle kill --all` (or Ctrl-C in a `--foreground` run) stops the local cockpit UI only; the daemon and its agent sessions keep running. The daemon is auto-restarting (launchd `KeepAlive`, systemd `Restart=always`), so a plain `kill <pid>` bounces straight back — stop it through its supervisor:
|
|
90
|
+
|
|
91
|
+
```sh
|
|
92
|
+
launchctl bootout gui/$(id -u)/dev.puddle.puddled # macOS (launchd)
|
|
93
|
+
systemctl --user disable --now puddled # Linux (systemd user unit)
|
|
94
|
+
kill "$(cat ~/.puddle/puddled.pid)" # nohup fallback (no supervisor)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Restore the production daemon.** When you're done testing, put the release build back over your dev one: stop the daemon (above), clear the installed binaries with `rm -rf ~/.puddle/bin` (your `~/.puddle` state — profiles, sessions, worktrees — is untouched), then run the production `puddle start`, which refetches the daemon from GitHub Releases. (Clearing `bin` is what forces the refetch: the installer skips a version whose files are already present, so a dev build sharing the release's version number would otherwise stay put.)
|
|
98
|
+
|
|
99
|
+
**Uninstall.** Removing the CLI alone leaves the daemon installed and running — a full teardown stops the daemon, then removes its state, its service file, and the production CLI:
|
|
100
|
+
|
|
101
|
+
> ⚠️ `~/.puddle` **is** your local state — the SQLite database with every profile, account, and session (plus conversation history), the daemon's worktree tracking, and the auth token. Deleting it is irreversible and wipes all of it. Uninstall only when you mean to start clean.
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
rm -rf ~/.puddle
|
|
105
|
+
rm ~/Library/LaunchAgents/dev.puddle.puddled.plist # macOS
|
|
106
|
+
rm ~/.config/systemd/user/puddled.service && systemctl --user daemon-reload # Linux
|
|
107
|
+
npm uninstall -g @puddle-code/cli
|
|
108
|
+
```
|
|
109
|
+
|
|
76
110
|
## Licence
|
|
77
111
|
|
|
78
112
|
Puddle is licensed under the [MIT License](LICENSE). Copyright (c) 2026 Yiding Song.
|
package/dist/index.js
CHANGED
|
@@ -4111,7 +4111,7 @@ function openBrowser(url2, platform = process.platform) {
|
|
|
4111
4111
|
}
|
|
4112
4112
|
|
|
4113
4113
|
// ../shared/src/protocol.ts
|
|
4114
|
-
var PROTOCOL_VERSION = { major: 6, minor:
|
|
4114
|
+
var PROTOCOL_VERSION = { major: 6, minor: 2 };
|
|
4115
4115
|
|
|
4116
4116
|
// ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
|
|
4117
4117
|
var external_exports = {};
|
|
@@ -18729,10 +18729,20 @@ var profileSettingsSchema = external_exports.looseObject({
|
|
|
18729
18729
|
* daemon's built-in default; an EMPTY string is an intentional empty preamble.
|
|
18730
18730
|
* `onboardingTemplate` (freshly created worktree) supports a `{{rules}}` token
|
|
18731
18731
|
* where the repo's onboarding notes are injected; `concurrentTemplate` is used
|
|
18732
|
-
* when a session joins an existing/shared worktree
|
|
18732
|
+
* when a session joins an existing/shared worktree; `restartTemplate` is sent
|
|
18733
|
+
* when a session is resumed after a daemon restart or machine reboot.
|
|
18733
18734
|
*/
|
|
18734
18735
|
onboardingTemplate: external_exports.string().optional(),
|
|
18735
18736
|
concurrentTemplate: external_exports.string().optional(),
|
|
18737
|
+
restartTemplate: external_exports.string().optional(),
|
|
18738
|
+
/**
|
|
18739
|
+
* How a session's tab/label is composed from its parts (SPEC §4). A template
|
|
18740
|
+
* string interpolating `${…}` variables (see `TAB_TITLE_VARIABLES`); ABSENT
|
|
18741
|
+
* falls back to `DEFAULT_TAB_TITLE_TEMPLATE` (`${name}`), reproducing the
|
|
18742
|
+
* historical display name. Empty renders (all chosen variables blank) fall
|
|
18743
|
+
* back to the session-id prefix, so a tab is never nameless.
|
|
18744
|
+
*/
|
|
18745
|
+
tabTitleTemplate: external_exports.string().optional(),
|
|
18736
18746
|
/**
|
|
18737
18747
|
* The user's drag-order of projects on the homescreen (project ids). Projects
|
|
18738
18748
|
* absent from this list — newly created ones — sort to the top. The right
|
|
@@ -18910,6 +18920,15 @@ var sessionSchema = external_exports.object({
|
|
|
18910
18920
|
* older daemons omit it; null when the agent has not named the session yet.
|
|
18911
18921
|
*/
|
|
18912
18922
|
agent_title: external_exports.string().nullable().optional(),
|
|
18923
|
+
/**
|
|
18924
|
+
* The terminal-title "sequence" name: the last title the process set on its
|
|
18925
|
+
* PTY via an OSC 0/1/2 escape, normalised (leading spinner/status glyphs
|
|
18926
|
+
* stripped). This is the VSCode `${sequence}` variable — a name source for
|
|
18927
|
+
* agents and terminals that have no adapter-maintained `agent_title`. Optional:
|
|
18928
|
+
* older daemons omit it; null when the process has set no title. It never
|
|
18929
|
+
* overrides a user `title`; see `tabTitleTemplate` for how it is composed.
|
|
18930
|
+
*/
|
|
18931
|
+
osc_title: external_exports.string().nullable().optional(),
|
|
18913
18932
|
status: sessionStatusSchema,
|
|
18914
18933
|
skip_permissions: external_exports.boolean(),
|
|
18915
18934
|
created_at: isoTimestamp,
|
|
@@ -19027,7 +19046,8 @@ var layoutLeafSchema = external_exports.object({
|
|
|
19027
19046
|
kind: external_exports.literal("leaf"),
|
|
19028
19047
|
id: external_exports.string(),
|
|
19029
19048
|
tabs: external_exports.array(tabRefSchema).default([]),
|
|
19030
|
-
activeKey: external_exports.string().nullable().default(null)
|
|
19049
|
+
activeKey: external_exports.string().nullable().default(null),
|
|
19050
|
+
previewKey: external_exports.string().nullable().default(null)
|
|
19031
19051
|
});
|
|
19032
19052
|
var layoutNodeSchema = external_exports.lazy(
|
|
19033
19053
|
() => external_exports.discriminatedUnion("kind", [
|
|
@@ -19302,6 +19322,18 @@ async function waitForTcp(port, timeoutMs) {
|
|
|
19302
19322
|
}
|
|
19303
19323
|
return false;
|
|
19304
19324
|
}
|
|
19325
|
+
async function waitForHttp(url2, timeoutMs) {
|
|
19326
|
+
const deadline = Date.now() + timeoutMs;
|
|
19327
|
+
while (Date.now() < deadline) {
|
|
19328
|
+
try {
|
|
19329
|
+
await fetch(url2, { signal: AbortSignal.timeout(2e3) });
|
|
19330
|
+
return true;
|
|
19331
|
+
} catch {
|
|
19332
|
+
await sleep(250);
|
|
19333
|
+
}
|
|
19334
|
+
}
|
|
19335
|
+
return false;
|
|
19336
|
+
}
|
|
19305
19337
|
function sleep(ms) {
|
|
19306
19338
|
return new Promise((r) => setTimeout(r, ms));
|
|
19307
19339
|
}
|
|
@@ -19430,7 +19462,7 @@ import { readFileSync } from "node:fs";
|
|
|
19430
19462
|
import { dirname, join as join2 } from "node:path";
|
|
19431
19463
|
import { fileURLToPath } from "node:url";
|
|
19432
19464
|
function cliVersion() {
|
|
19433
|
-
if (true) return "0.0.
|
|
19465
|
+
if (true) return "0.0.3";
|
|
19434
19466
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
19435
19467
|
for (const candidate of [
|
|
19436
19468
|
join2(here, "..", "..", "package.json"),
|
|
@@ -19995,10 +20027,13 @@ var RECONNECT_INITIAL_MS = 500;
|
|
|
19995
20027
|
var RECONNECT_MAX_MS = 1e4;
|
|
19996
20028
|
var DOWN_GRACE_MS = 2e3;
|
|
19997
20029
|
var STABLE_MS = 3e4;
|
|
20030
|
+
var HEALTH_INTERVAL_MS = 1e3;
|
|
19998
20031
|
async function openTunnel(ssh, remotePort, opts = {}) {
|
|
19999
20032
|
const logger = opts.logger ?? silentLogger;
|
|
20000
20033
|
const sshBinary = opts.sshBinary ?? "ssh";
|
|
20001
20034
|
const downGraceMs = opts.downGraceMs ?? DOWN_GRACE_MS;
|
|
20035
|
+
const healthIntervalMs = opts.healthIntervalMs ?? HEALTH_INTERVAL_MS;
|
|
20036
|
+
const isReady = opts.ready ?? (() => Promise.resolve(true));
|
|
20002
20037
|
const eventCbs = /* @__PURE__ */ new Set();
|
|
20003
20038
|
const portCbs = /* @__PURE__ */ new Set();
|
|
20004
20039
|
const emit = (e) => eventCbs.forEach((cb) => cb(e));
|
|
@@ -20007,37 +20042,40 @@ async function openTunnel(ssh, remotePort, opts = {}) {
|
|
|
20007
20042
|
let stopping = false;
|
|
20008
20043
|
let lastRestoredAt = 0;
|
|
20009
20044
|
let reconnectActive = false;
|
|
20045
|
+
let healthTimer = null;
|
|
20010
20046
|
const spawnForward = async (port) => {
|
|
20011
20047
|
if (stopping) return false;
|
|
20012
20048
|
const child = spawn2(
|
|
20013
20049
|
sshBinary,
|
|
20014
|
-
|
|
20015
|
-
|
|
20016
|
-
ssh.args(
|
|
20017
|
-
"-o",
|
|
20018
|
-
"ExitOnForwardFailure=yes",
|
|
20019
|
-
"-N",
|
|
20020
|
-
"-L",
|
|
20021
|
-
`${port}:127.0.0.1:${remotePort}`,
|
|
20022
|
-
ssh.host
|
|
20023
|
-
),
|
|
20024
|
-
{
|
|
20025
|
-
stdio: ["ignore", "ignore", "inherit"]
|
|
20026
|
-
}
|
|
20050
|
+
ssh.args("-N", "-L", `${port}:127.0.0.1:${remotePort}`, ssh.host),
|
|
20051
|
+
{ stdio: ["ignore", "ignore", "inherit"] }
|
|
20027
20052
|
);
|
|
20028
|
-
child.on("close", () => onChildExit(child));
|
|
20029
20053
|
state.child = child;
|
|
20030
|
-
const ready = await waitForTcp(port, 5e3) &&
|
|
20031
|
-
if (!ready) discard(child);
|
|
20054
|
+
const ready = await waitForTcp(port, 5e3) && await isReady(port);
|
|
20055
|
+
if (!ready) discard(child, port);
|
|
20032
20056
|
return ready;
|
|
20033
20057
|
};
|
|
20034
|
-
const discard = (child) => {
|
|
20058
|
+
const discard = (child, port) => {
|
|
20035
20059
|
if (state.child === child) state.child = null;
|
|
20036
20060
|
child.kill("SIGTERM");
|
|
20061
|
+
void ssh.cancelForward(port, remotePort);
|
|
20037
20062
|
};
|
|
20038
|
-
const
|
|
20039
|
-
if (stopping ||
|
|
20040
|
-
|
|
20063
|
+
const scheduleHealthCheck = () => {
|
|
20064
|
+
if (stopping || healthTimer !== null) return;
|
|
20065
|
+
healthTimer = setTimeout(() => void healthCheck(), healthIntervalMs);
|
|
20066
|
+
};
|
|
20067
|
+
const healthCheck = async () => {
|
|
20068
|
+
healthTimer = null;
|
|
20069
|
+
if (stopping) return;
|
|
20070
|
+
if (reconnectActive) {
|
|
20071
|
+
scheduleHealthCheck();
|
|
20072
|
+
return;
|
|
20073
|
+
}
|
|
20074
|
+
if (await tcpListening(localPort)) {
|
|
20075
|
+
scheduleHealthCheck();
|
|
20076
|
+
return;
|
|
20077
|
+
}
|
|
20078
|
+
if (stopping || reconnectActive) return;
|
|
20041
20079
|
void reconnect();
|
|
20042
20080
|
};
|
|
20043
20081
|
const reconnect = async () => {
|
|
@@ -20090,15 +20128,10 @@ async function openTunnel(ssh, remotePort, opts = {}) {
|
|
|
20090
20128
|
if (restored) {
|
|
20091
20129
|
lastRestoredAt = Date.now();
|
|
20092
20130
|
if (announced) emit({ t: "tunnel-up" });
|
|
20093
|
-
if (state.child === null || state.child.exitCode !== null) {
|
|
20094
|
-
state.child = null;
|
|
20095
|
-
void reconnect();
|
|
20096
|
-
}
|
|
20097
20131
|
}
|
|
20132
|
+
scheduleHealthCheck();
|
|
20098
20133
|
};
|
|
20099
|
-
reconnectActive = true;
|
|
20100
20134
|
const initialReady = await spawnForward(localPort);
|
|
20101
|
-
reconnectActive = false;
|
|
20102
20135
|
if (!initialReady) {
|
|
20103
20136
|
stopping = true;
|
|
20104
20137
|
throw new CliError(
|
|
@@ -20107,10 +20140,7 @@ async function openTunnel(ssh, remotePort, opts = {}) {
|
|
|
20107
20140
|
"is the daemon running on the host? try: puddle status " + ssh.host
|
|
20108
20141
|
);
|
|
20109
20142
|
}
|
|
20110
|
-
|
|
20111
|
-
state.child = null;
|
|
20112
|
-
void reconnect();
|
|
20113
|
-
}
|
|
20143
|
+
scheduleHealthCheck();
|
|
20114
20144
|
return {
|
|
20115
20145
|
get localPort() {
|
|
20116
20146
|
return localPort;
|
|
@@ -20125,12 +20155,14 @@ async function openTunnel(ssh, remotePort, opts = {}) {
|
|
|
20125
20155
|
},
|
|
20126
20156
|
close() {
|
|
20127
20157
|
stopping = true;
|
|
20128
|
-
|
|
20129
|
-
|
|
20130
|
-
|
|
20131
|
-
|
|
20132
|
-
|
|
20133
|
-
|
|
20158
|
+
if (healthTimer !== null) {
|
|
20159
|
+
clearTimeout(healthTimer);
|
|
20160
|
+
healthTimer = null;
|
|
20161
|
+
}
|
|
20162
|
+
const child = state.child;
|
|
20163
|
+
state.child = null;
|
|
20164
|
+
child?.kill("SIGTERM");
|
|
20165
|
+
return ssh.cancelForward(localPort, remotePort);
|
|
20134
20166
|
}
|
|
20135
20167
|
};
|
|
20136
20168
|
}
|
|
@@ -20266,6 +20298,25 @@ var SshTransport = class {
|
|
|
20266
20298
|
child.on("close", (code) => resolve3(code === 0));
|
|
20267
20299
|
});
|
|
20268
20300
|
}
|
|
20301
|
+
/**
|
|
20302
|
+
* Best-effort removal of a multiplexed local forward (`-O cancel`). A
|
|
20303
|
+
* non-OpenSSH server (Tailscale SSH) attaches the `-L` to the master and it
|
|
20304
|
+
* outlives the spawning client — killing that client does not remove it, so
|
|
20305
|
+
* the forward must be cancelled explicitly or the local port stays forwarded
|
|
20306
|
+
* until the master lapses. A no-op (and unnecessary) without a master, where
|
|
20307
|
+
* the forward dies with its own ssh process.
|
|
20308
|
+
*/
|
|
20309
|
+
cancelForward(localPort, remotePort) {
|
|
20310
|
+
if (!this.hasControlMaster) return Promise.resolve();
|
|
20311
|
+
return new Promise((resolve3) => {
|
|
20312
|
+
const spec = `${localPort}:127.0.0.1:${remotePort}`;
|
|
20313
|
+
const child = spawn4(this.ssh, this.args("-O", "cancel", "-L", spec, this.host), {
|
|
20314
|
+
stdio: "ignore"
|
|
20315
|
+
});
|
|
20316
|
+
child.on("error", () => resolve3());
|
|
20317
|
+
child.on("close", () => resolve3());
|
|
20318
|
+
});
|
|
20319
|
+
}
|
|
20269
20320
|
exec(command, opts = {}) {
|
|
20270
20321
|
return new Promise((resolve3) => {
|
|
20271
20322
|
const child = spawn4(this.ssh, this.args(this.host, "--", `sh -c ${shellQuote(command)}`), {
|
|
@@ -20339,7 +20390,11 @@ async function connectRemote(opts) {
|
|
|
20339
20390
|
const bootstrap = { tarball: opts.tarball, logger };
|
|
20340
20391
|
const endpoint = await ensureDaemon(ssh, bootstrap);
|
|
20341
20392
|
const remotePort = opts.remotePort ?? endpoint.port;
|
|
20342
|
-
const tunnel = await openTunnel(ssh, remotePort, {
|
|
20393
|
+
const tunnel = await openTunnel(ssh, remotePort, {
|
|
20394
|
+
sshBinary: opts.sshBinary,
|
|
20395
|
+
logger,
|
|
20396
|
+
ready: (localPort) => waitForHttp(`http://127.0.0.1:${localPort}/api/version`, 8e3)
|
|
20397
|
+
});
|
|
20343
20398
|
const client = new DaemonClient(tunnel.localPort, endpoint.token);
|
|
20344
20399
|
tunnel.onPortChange((port) => client.setPort(port));
|
|
20345
20400
|
const daemon = await runHandshake({
|
|
@@ -20733,7 +20788,9 @@ async function openTarget(host) {
|
|
|
20733
20788
|
let port = daemonPort;
|
|
20734
20789
|
let tunnel = null;
|
|
20735
20790
|
if (transport instanceof SshTransport) {
|
|
20736
|
-
tunnel = await openTunnel(transport, daemonPort
|
|
20791
|
+
tunnel = await openTunnel(transport, daemonPort, {
|
|
20792
|
+
ready: (localPort) => waitForHttp(`http://127.0.0.1:${localPort}/api/version`, 8e3)
|
|
20793
|
+
});
|
|
20737
20794
|
port = tunnel.localPort;
|
|
20738
20795
|
}
|
|
20739
20796
|
return {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{t as n}from"./jsx-runtime-ffCh33zF.js";import{g as r}from"./api-BJKPJeFZ.js";import{a as i,g as a,i as o,n as s,o as c,t as l}from"./dialog-B43iKmfK.js";import{t as u}from"./utils-B6KiDbIe.js";import{A as d,D as f,E as p,O as m,c as h,j as g,k as _,n as v,r as y,t as b}from"./context-menu-CnHdXMkI.js";import{r as x}from"./dist-fdPAlKke.js";import{$ as S,Pt as C,X as w,Y as T,ft as E,k as D,m as O,nt as k,pt as A,st as j,ut as M}from"./index-wPaOVZ8U.js";var N=e(t(),1),P=n(),F=[{status:`running`,colour:`bg-running`},{status:`waiting_input`,colour:`bg-waiting`},{status:`interrupted`,colour:`bg-interrupted`}];function I({projectId:e}){let t=M(e),n=(0,N.useMemo)(()=>{let e=new Map;for(let n of t.data??[])e.set(n.status,(e.get(n.status)??0)+1);return e},[t.data]),r=(t.data??[]).filter(e=>e.status!==`archived`).length;return(0,P.jsxs)(`div`,{className:`flex items-center gap-3 text-2xs text-fg-muted tabular-nums`,children:[(0,P.jsxs)(`span`,{children:[r,` session`,r===1?``:`s`]}),F.map(({status:e,colour:t})=>{let r=n.get(e)??0;return r===0?null:(0,P.jsxs)(`span`,{className:`flex items-center gap-1`,children:[(0,P.jsx)(`span`,{className:`size-1.5 rounded-full ${t}`}),r,` `,e.replace(`_`,` `)]},e)})]})}function L({icon:e,label:t,onClick:n}){return(0,P.jsx)(`button`,{type:`button`,"aria-label":t,title:t,onClick:e=>{e.preventDefault(),e.stopPropagation(),n()},className:`flex items-center rounded-md p-1.5 text-fg-muted transition-colors hover:bg-ground hover:text-fg`,children:(0,P.jsx)(e,{className:`size-4`})})}function R({project:e,repoPath:t,draggable:n,dragging:f,onDragStart:p,onDragEnd:_,onDragOver:x}){let S=w(),[T,D]=(0,N.useState)(!1),[O,k]=(0,N.useState)(e.name),j=t=>S.mutate({id:e.id,archived:t},{onError:e=>r.error(e.message)}),M=()=>{let t=O.trim();if(!t||t===e.name){D(!1);return}S.mutate({id:e.id,name:t},{onSuccess:()=>D(!1),onError:e=>r.error(e.message)})};return(0,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(b,{children:[(0,P.jsx)(h,{asChild:!0,children:(0,P.jsxs)(`div`,{draggable:n,onDragStart:p,onDragEnd:_,onDragOver:x,className:u(`group relative transition-opacity`,f&&`opacity-50`),children:[(0,P.jsxs)(C,{draggable:!1,to:`/project/${e.id}`,className:`block rounded-lg bg-surface p-4 transition-colors hover:bg-elevated`,children:[(0,P.jsx)(`h2`,{className:`truncate pr-16 text-base font-semibold text-fg group-hover:text-accent`,children:e.name}),(0,P.jsx)(`p`,{className:`mt-1 truncate font-mono text-2xs text-fg-muted`,children:t??`…`}),(0,P.jsx)(`div`,{className:`mt-3`,children:(0,P.jsx)(I,{projectId:e.id})})]}),(0,P.jsx)(`div`,{className:`absolute right-2 top-2 hidden gap-0.5 group-hover:flex has-[[data-state=open]]:flex`,children:e.archived?(0,P.jsx)(L,{icon:g,label:`Reopen project`,onClick:()=>j(!1)}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsx)(L,{icon:m,label:`Rename project`,onClick:()=>{k(e.name),D(!0)}}),(0,P.jsx)(L,{icon:d,label:`Archive project`,onClick:()=>j(!0)})]})})]})}),(0,P.jsx)(v,{children:e.archived?(0,P.jsxs)(y,{onSelect:()=>j(!1),children:[(0,P.jsx)(g,{}),` Reopen`]}):(0,P.jsxs)(P.Fragment,{children:[(0,P.jsxs)(y,{onSelect:()=>{k(e.name),D(!0)},children:[(0,P.jsx)(m,{}),` Rename`]}),(0,P.jsxs)(y,{onSelect:()=>j(!0),children:[(0,P.jsx)(d,{}),` Archive`]})]})})]}),(0,P.jsx)(l,{open:T,onOpenChange:D,children:(0,P.jsxs)(s,{children:[(0,P.jsx)(i,{children:(0,P.jsx)(c,{children:`Rename project`})}),(0,P.jsxs)(`form`,{className:`flex flex-col gap-3`,onSubmit:e=>{e.preventDefault(),M()},children:[(0,P.jsx)(A,{value:O,onChange:e=>k(e.target.value),autoFocus:!0}),(0,P.jsxs)(`div`,{className:`flex flex-col gap-1`,children:[(0,P.jsx)(E,{className:`text-fg-muted`,children:`Path`}),(0,P.jsx)(`p`,{className:`truncate font-mono text-2xs text-fg-muted`,children:t??`…`})]}),(0,P.jsxs)(o,{children:[(0,P.jsx)(a,{type:`button`,variant:`ghost`,onClick:()=>D(!1),children:`Cancel`}),(0,P.jsx)(a,{type:`submit`,disabled:!O.trim()||S.isPending,children:`Rename`})]})]})]})})]})}function z(){let e=D(),t=k(e??void 0),n=j(),r=S(e??void 0),i=T(e??``),[o,s]=(0,N.useState)(!1),[c,l]=(0,N.useState)(null),[m,h]=(0,N.useState)(!1),g=e=>n.data?.find(t=>t.id===e)?.path,v=t.data??[],y=r.data?.projectOrder??[],b=p(v.filter(e=>!e.archived),y),C=v.filter(e=>e.archived),w=(e,t)=>{e!==t&&i.mutate({projectOrder:f(b.map(e=>e.id),e,t)})};return(0,P.jsxs)(`div`,{className:`mx-auto max-w-4xl p-6`,children:[v.length===0&&(0,P.jsxs)(`div`,{className:`mt-24 flex flex-col items-center gap-6 text-center`,children:[(0,P.jsx)(x,{className:`size-8 text-fg-muted`}),(0,P.jsx)(`p`,{className:`text-sm text-fg-secondary`,children:`No projects yet — press ⌘K or create one to get started.`}),(0,P.jsx)(a,{onClick:()=>s(!0),children:`New project`})]}),(0,P.jsx)(`div`,{className:`grid grid-cols-1 gap-3 sm:grid-cols-2`,children:b.map(e=>(0,P.jsx)(R,{project:e,repoPath:g(e.repo_id),draggable:!0,dragging:c===e.id,onDragStart:()=>l(e.id),onDragEnd:()=>l(null),onDragOver:t=>{t.preventDefault(),c&&c!==e.id&&w(c,e.id)}},e.id))}),C.length>0&&(0,P.jsxs)(`div`,{className:`mt-6`,children:[(0,P.jsxs)(`button`,{type:`button`,onClick:()=>h(e=>!e),className:`flex items-center gap-1.5 px-1 py-1.5 text-2xs uppercase tracking-wide text-fg-muted transition-colors hover:text-fg`,children:[(0,P.jsx)(_,{className:u(`size-3 transition-transform`,m&&`rotate-90`)}),(0,P.jsx)(d,{className:`size-3`}),(0,P.jsx)(`span`,{children:`Archived`}),(0,P.jsx)(`span`,{className:`tabular-nums`,children:C.length})]}),m&&(0,P.jsx)(`div`,{className:`mt-2 grid grid-cols-1 gap-3 sm:grid-cols-2`,children:C.map(e=>(0,P.jsx)(R,{project:e,repoPath:g(e.repo_id),draggable:!1,dragging:!1,onDragStart:()=>{},onDragEnd:()=>{},onDragOver:()=>{}},e.id))})]}),e!==null&&(0,P.jsx)(O,{profileId:e,open:o,onOpenChange:s})]})}export{z as Dashboard};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{t as n}from"./jsx-runtime-ffCh33zF.js";import{a as r,g as i,h as a,i as o,n as s,o as c,r as l,t as u}from"./dialog-B43iKmfK.js";import{t as d}from"./createLucideIcon-DyMAgLa3.js";import{l as f,r as p,t as m}from"./buffer-store-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{t as n}from"./jsx-runtime-ffCh33zF.js";import{a as r,g as i,h as a,i as o,n as s,o as c,r as l,t as u}from"./dialog-B43iKmfK.js";import{t as d}from"./createLucideIcon-DyMAgLa3.js";import{l as f,r as p,t as m}from"./buffer-store-BGy4Fj9o.js";import{s as h,t as g}from"./editor-sync-F3Wyveaa.js";var _=d(`circle`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}]]),v=e(t(),1),y=n();function b(e,t){let n=m(e,t);return(0,v.useSyncExternalStore)((0,v.useCallback)(e=>f(n,e),[n]),()=>p(n))}function x({session:e,path:t,kind:n,label:d,onClose:f}){let p=b(e,t)&&n!==`commit`,[m,x]=(0,v.useState)(!1),S=()=>p?x(!0):f();return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(`button`,{onClick:e=>{e.stopPropagation(),S()},className:`flex size-4 items-center justify-center rounded-sm text-fg-muted transition-colors hover:text-fg`,"aria-label":`Close ${d}`,children:p?(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(_,{className:`size-2 fill-current group-hover:hidden`}),(0,y.jsx)(a,{className:`hidden size-3 group-hover:block`})]}):(0,y.jsx)(a,{className:`size-3 opacity-0 group-hover:opacity-100`})}),(0,y.jsx)(u,{open:m,onOpenChange:e=>!e&&x(!1),children:(0,y.jsxs)(s,{children:[(0,y.jsxs)(r,{children:[(0,y.jsxs)(c,{children:[`Discard changes to `,d,`?`]}),(0,y.jsx)(l,{children:`This file has unsaved changes. Closing the tab will discard them.`})]}),(0,y.jsxs)(o,{children:[(0,y.jsx)(i,{variant:`ghost`,onClick:()=>x(!1),children:`Keep editing`}),(0,y.jsx)(i,{variant:`danger`,onClick:()=>{h(e,t),g(e,t),x(!1),f()},children:`Discard`})]})]})})]})}export{x as EditorTabClose};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{a as n,s as r,t as i}from"./buffer-store-
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{a as n,s as r,t as i}from"./buffer-store-BGy4Fj9o.js";var a=e(t(),1);function o({buffers:e}){let t=(0,a.useRef)(new Set);return(0,a.useEffect)(()=>{let a=new Set(e.map(e=>i(e.session,e.path))),o=t.current;for(let e of a)o.has(e)||(r(e),o.add(e));for(let e of[...o])a.has(e)||(n(e),o.delete(e))},[e]),(0,a.useEffect)(()=>()=>{for(let e of t.current)n(e);t.current.clear()},[]),null}export{o as ModelRefcount};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{t as n}from"./jsx-runtime-ffCh33zF.js";import{c as r,d as i,f as a,h as o,m as s,n as c,p as l,r as u,t as d}from"./editor-tabs-DHG9ShX4.js";import{p as f,r as p,t as m}from"./api-DnhKCI2U.js";import{t as h}from"./createLucideIcon-DyMAgLa3.js";import{a as g,o as _,s as v}from"./index-BcSTVRMs.js";import{i as y,r as b}from"./editor.api2-4X9Vh9Zw.js";import{a as x,c as S,d as C,f as w,i as T,l as E,n as ee,o as te,r as D,s as O,t as k,u as A}from"./buffer-store-CJsO7gGE.js";import{a as ne,c as j,i as M,l as N,n as P,o as F,r as I,s as L}from"./editor-sync-F3Wyveaa.js";import{r as R}from"./buffer-logic-BrpRsL1b.js";var z=h(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),B=e(t(),1);function V(e){return!e}function re(e,t){e.addCommand(y.CtrlCmd|b.KeyS,()=>t.onSave()),e.addAction({id:`puddle.toggleWordWrap`,label:`Toggle Word Wrap`,keybindings:[y.Alt|b.KeyZ],run:()=>_({editorWordWrap:V(g().editorWordWrap)})})}function H(e,t,n){let r=k(e,t),i=o(e,t),a=l(e),[s,c]=(0,B.useState)(null),[u,d]=(0,B.useState)(!1),p=(0,B.useRef)(!1),h=(0,B.useRef)(null),g=(0,B.useRef)(null),_=(0,B.useRef)(n);_.current=n;let v=(0,B.useRef)(!1),y=(0,B.useCallback)((e,t)=>{g.current?.cancel(),v.current=!0;try{te(r,e,t)}finally{v.current=!1}},[r]),b=(0,B.useSyncExternalStore)((0,B.useCallback)(e=>E(r,e),[r]),()=>D(r)),x=(0,B.useSyncExternalStore)((0,B.useCallback)(e=>F(r,e),[r]),()=>ne(r));(0,B.useEffect)(()=>{let n=i.data;if(!n||n.binary||n.content===null||p.current)return;p.current=!0;let a=S(r)!==void 0,o=ee(e,t,n.content,n.mtime_ms);c(o),!a&&N(e,t).then(e=>{e&&(e.base_mtime_ms===n.mtime_ms?R(o,e.content)&&d(!0):f(`This file has an unsaved draft from an earlier version.`,{description:`The file changed on disk since the draft was saved.`,duration:12e3,action:{label:`Restore draft`,onClick:()=>{R(o,e.content)&&d(!0)}}}))})},[i.data,r,e,t]),(0,B.useEffect)(()=>{if(!s)return;let n=j(e,t);g.current=n;let i=s.onDidChangeContent(()=>{v.current||(n(s.getValue(),S(r)??0),P(e,t))});return()=>{n.flush(),i.dispose(),g.current=null}},[s,r,e,t]),(0,B.useEffect)(()=>{!s||!x.savedElsewhere||D(r)||i.refetch().then(e=>{D(r)||(e.data&&e.data.content!==null&&y(e.data.content,e.data.mtime_ms),M(r))})},[x.savedElsewhere,s,r]),(0,B.useEffect)(()=>{let e=i.data;!s||!e||e.content===null||!D(r)&&e.mtime_ms!==S(r)&&y(e.content,e.mtime_ms)},[i.data,s,r]);let C=(0,B.useCallback)((n,i)=>{T(r,n,i),g.current?.cancel(),L(e,t),I(e,t,i),d(!1)},[r,e,t]),w=(0,B.useCallback)(()=>{i.refetch().then(n=>{n.data&&n.data.content!==null&&y(n.data.content,n.data.mtime_ms),L(e,t),d(!1)})},[r,e,t,y]),O=(0,B.useCallback)((e,n)=>{a.mutate({path:t,content:e},{onSuccess:e=>C(n,e.mtime_ms),onError:e=>f.error(e instanceof Error?e.message:`Overwrite failed`)})},[a,t,C]),A=(0,B.useCallback)(()=>{if(!s||!D(r))return;let e=s.getValue(),n=s.getAlternativeVersionId();a.mutate({path:t,content:e,expected_mtime_ms:S(r)},{onSuccess:e=>C(n,e.mtime_ms),onError:t=>{t instanceof m&&t.status===409&&t.code===`stale_file`?f.error(`File changed on disk (probably the agent)`,{duration:1/0,action:{label:`Reload`,onClick:()=>w()},cancel:{label:`Overwrite`,onClick:()=>O(e,n)}}):f.error(t instanceof Error?t.message:`Save failed`)}})},[s,r,a,t,C,w,O]),z=(0,B.useRef)(A);z.current=A;let V=(0,B.useCallback)(()=>{let n=h.current,r=_.current;!n||!r||r.session!==e||r.path!==t||(n.revealLineInCenter(r.line),n.setPosition({lineNumber:r.line,column:r.column??1}),n.focus())},[e,t]),H=(0,B.useCallback)(e=>{h.current=e,re(e,{onSave:()=>z.current()}),V()},[V]);(0,B.useEffect)(()=>{V()},[n,s,V]);let U=(0,B.useCallback)(()=>{w()},[w]),W,G=null;i.data?.binary?W=`binary`:i.error?i.error instanceof m&&i.error.status===413?W=`too-large`:(W=`error`,G=i.error instanceof Error?i.error.message:`Failed to load file`):W=s?`ready`:`loading`;let K=b&&x.savedElsewhere?`saved-elsewhere`:b&&x.dirtyElsewhere?`dirty-elsewhere`:null;return{status:W,errorMessage:G,model:s,dirty:b,restoredNotice:u,discardRestore:U,peerBadge:K,save:A,onMount:H}}var U=n();function W({message:e,session:t,path:n,download:i}){return(0,U.jsxs)(`div`,{className:`flex h-full flex-col items-center justify-center gap-3 bg-ground text-sm text-fg-muted`,children:[(0,U.jsx)(`span`,{children:e}),i&&(0,U.jsxs)(`button`,{type:`button`,onClick:()=>void r(t,n).catch(e=>f.error(e instanceof Error?e.message:`Download failed`)),className:`flex items-center gap-1.5 text-fg-secondary transition-colors hover:text-fg`,children:[(0,U.jsx)(z,{className:`size-4`}),`Download`]})]})}function G({session:e,path:t,reveal:n}){let r=v(),i=H(e,t,n),a=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]);if(i.status===`binary`)return(0,U.jsx)(W,{message:`Binary file — use Download`,session:e,path:t,download:!0});if(i.status===`too-large`)return(0,U.jsx)(W,{message:`File too large to edit — use Download`,session:e,path:t,download:!0});if(i.status===`error`)return(0,U.jsx)(W,{message:i.errorMessage??`Failed to load file`,session:e,path:t});let o=i.peerBadge===`saved-elsewhere`?`Saved in another window`:i.peerBadge===`dirty-elsewhere`?`Also being edited in another window`:null;return(0,U.jsxs)(`div`,{className:`flex h-full flex-col bg-ground`,children:[i.restoredNotice&&(0,U.jsxs)(`div`,{className:`flex items-center gap-3 bg-elevated px-3 py-1.5 text-xs text-fg-secondary`,children:[(0,U.jsx)(`span`,{children:`Restored unsaved changes`}),(0,U.jsx)(`button`,{type:`button`,onClick:i.discardRestore,className:`text-fg-muted transition-colors hover:text-fg`,children:`Discard`})]}),o&&(0,U.jsx)(`div`,{className:`bg-surface px-3 py-1 text-xs text-fg-muted`,children:o}),(0,U.jsx)(`div`,{className:`min-h-0 flex-1`,children:i.model&&(0,U.jsx)(C,{path:i.model.uri.toString(),defaultValue:i.model.getValue(),theme:`puddle`,keepCurrentModel:!0,onMount:e=>i.onMount(e),loading:(0,U.jsx)(`div`,{className:`p-3 text-xs text-fg-muted`,children:`…`}),options:{automaticLayout:!0,fontFamily:a,tabSize:r.editorTabSize,wordWrap:r.editorWordWrap?`on`:`off`,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})})]})}function K({children:e}){return(0,U.jsx)(`div`,{className:`flex h-full items-center justify-center px-4 text-xs text-fg-muted`,children:e})}function q(e,t){let n=(0,B.useRef)(t);n.current=t,(0,B.useEffect)(()=>(O(e),()=>{n.current?.(),x(e)}),[e])}function J(e,t,n,r){let i=r.split(`/`).map(encodeURIComponent).join(`/`);return`${e}://${encodeURIComponent(t)}/${encodeURIComponent(n)}/${i}`}function Y({session:e,refName:t,path:n,content:r}){let i=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]);return(0,U.jsx)(C,{path:J(`puddle-view`,e,t,n),defaultValue:r,theme:A,loading:(0,U.jsx)(K,{children:`…`}),options:{readOnly:!0,automaticLayout:!0,fontFamily:i,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})}function X({session:e,against:t,path:n}){let r=a(e,t,n);return r.isPending?(0,U.jsx)(K,{children:`…`}):r.error?(0,U.jsx)(K,{children:r.error instanceof Error?r.error.message:`Failed to load`}):r.data.binary?(0,U.jsx)(K,{children:`Binary file — deleted`}):(0,U.jsxs)(`div`,{className:`flex h-full flex-col`,children:[(0,U.jsx)(`div`,{className:`bg-surface px-4 py-1 text-xs text-fg-muted`,children:`Deleted`}),(0,U.jsx)(`div`,{className:`min-h-0 flex-1`,children:(0,U.jsx)(Y,{session:e,refName:t,path:n,content:r.data.content??``})})]})}function ie({session:e,against:t,path:n,basePath:r}){let i=v(),o=H(e,n,null),s=a(e,t,r),c=(0,B.useRef)(null);q(k(e,n),()=>{let e=c.current;if(!e)return;let t=e.getModel();e.setModel(null),t?.original.dispose(),c.current=null});let l=(0,B.useRef)(o.save);l.current=o.save;let u=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]),d=s.error instanceof m&&s.error.status===404;return o.status===`binary`||s.data?.binary?(0,U.jsx)(K,{children:`Binary file`}):o.status===`too-large`?(0,U.jsx)(K,{children:`File too large to show`}):o.status===`error`?(0,U.jsx)(K,{children:o.errorMessage??`Failed to load file`}):d?(0,U.jsx)(G,{session:e,path:n,reveal:null}):s.isPending||!o.model?(0,U.jsx)(K,{children:`…`}):s.error?(0,U.jsx)(K,{children:s.error instanceof Error?s.error.message:`Failed to load base`}):(0,U.jsx)(w,{modifiedModelPath:o.model.uri.toString(),originalModelPath:J(`puddle-base`,e,t,r),original:s.data.content??``,language:o.model.getLanguageId(),theme:A,keepCurrentModifiedModel:!0,loading:(0,U.jsx)(K,{children:`…`}),onMount:e=>{c.current=e,e.onDidDispose(()=>{c.current=null}),e.getModifiedEditor().addCommand(y.CtrlCmd|b.KeyS,()=>l.current())},options:{readOnly:!1,originalEditable:!1,renderSideBySide:!0,automaticLayout:!0,fontFamily:u,wordWrap:i.editorWordWrap?`on`:`off`,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})}function ae({session:e,path:t}){return q(k(e,t)),(0,U.jsx)(G,{session:e,path:t,reveal:null})}function oe({session:e,against:t,entry:n}){switch(n.status){case`added`:return(0,U.jsx)(ae,{session:e,path:n.path});case`deleted`:return(0,U.jsx)(X,{session:e,against:t,path:n.path});case`modified`:case`renamed`:return(0,U.jsx)(ie,{session:e,against:t,path:n.path,basePath:n.old_path??n.path});default:return null}}function Z({session:e,sha:t,path:n}){let r=a(e,t,n);return r.isPending?(0,U.jsx)(K,{children:`…`}):r.error?(0,U.jsx)(K,{children:r.error instanceof Error?r.error.message:`Failed to load file`}):r.data.binary?(0,U.jsx)(K,{children:`Binary file`}):(0,U.jsx)(Y,{session:e,refName:t,path:n,content:r.data.content??``})}function se({session:e,originalRef:t,originalPath:n,originalContent:r,modifiedRef:i,modifiedPath:a,modifiedContent:o}){let s=(0,B.useRef)(null),c=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]);return(0,B.useEffect)(()=>()=>{let e=s.current;if(!e)return;let t=e.getModel();e.setModel(null),t?.original.dispose(),t?.modified.dispose(),s.current=null},[]),(0,U.jsx)(w,{originalModelPath:J(`puddle-hist-orig`,e,t,n),modifiedModelPath:J(`puddle-hist-mod`,e,i,a),original:r,modified:o,theme:A,keepCurrentOriginalModel:!0,keepCurrentModifiedModel:!0,loading:(0,U.jsx)(K,{children:`…`}),onMount:e=>{s.current=e,e.onDidDispose(()=>{s.current=null})},options:{readOnly:!0,originalEditable:!1,renderSideBySide:!0,automaticLayout:!0,fontFamily:c,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})}function Q({session:e,sha:t,path:n,basePath:r}){let i=a(e,`${t}^`,r),o=a(e,t,n);return i.error instanceof m&&i.error.status===404?(0,U.jsx)(Z,{session:e,sha:t,path:n}):i.isPending||o.isPending?(0,U.jsx)(K,{children:`…`}):i.error?(0,U.jsx)(K,{children:i.error instanceof Error?i.error.message:`Failed to load original`}):o.error?(0,U.jsx)(K,{children:o.error instanceof Error?o.error.message:`Failed to load file`}):i.data.binary||o.data.binary?(0,U.jsx)(K,{children:`Binary file`}):(0,U.jsx)(se,{session:e,originalRef:`${t}^`,originalPath:r,originalContent:i.data.content??``,modifiedRef:t,modifiedPath:n,modifiedContent:o.data.content??``})}function ce({session:e,sha:t,entry:n,isRootCommit:r}){switch(u(n.status,r)){case`added`:return(0,U.jsx)(Z,{session:e,sha:t,path:n.path});case`deleted`:return(0,U.jsx)(X,{session:e,against:`${t}^`,path:n.path});case`modified`:case`renamed`:return(0,U.jsx)(Q,{session:e,sha:t,path:n.path,basePath:n.old_path??n.path});default:return null}}function le({session:e,sha:t,path:n}){let r=i(e,t);if(r.isPending)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:`Loading commit…`});if(r.error)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:r.error instanceof Error?r.error.message:`Failed to load commit`});let a=r.data.files.find(e=>e.path===n);return a?(0,U.jsx)(`div`,{className:`h-full`,children:(0,U.jsx)(ce,{session:e,sha:t,entry:a,isRootCommit:r.data.parents.length===0})}):(0,U.jsxs)(`div`,{className:`flex h-full items-center justify-center px-4 text-center text-xs text-fg-muted`,children:[(0,U.jsx)(`span`,{className:`font-mono`,children:n}),` is not part of `,t.slice(0,7),`.`]})}function ue({session:e,path:t}){let n=s(e);if(n.isPending)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:`Loading diff…`});if(n.error)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:n.error instanceof Error?n.error.message:`Failed to load diff`});let r=n.data.entries.find(e=>e.path===t);return r?(0,U.jsx)(`div`,{className:`h-full`,children:(0,U.jsx)(oe,{session:e,against:n.data.against,entry:r})}):(0,U.jsxs)(`div`,{className:`flex h-full items-center justify-center px-4 text-center text-xs text-fg-muted`,children:[`No changes to `,(0,U.jsx)(`span`,{className:`mx-1 font-mono`,children:t}),` against the base.`]})}var de={png:`image`,jpg:`image`,jpeg:`image`,gif:`image`,webp:`image`,bmp:`image`,ico:`image`,avif:`image`,svg:`image`,mp4:`video`,m4v:`video`,webm:`video`,mov:`video`,mkv:`video`,avi:`video`,mp3:`audio`,wav:`audio`,ogg:`audio`,m4a:`audio`,flac:`audio`,aac:`audio`,pdf:`pdf`};function fe(e){let t=e.split(`/`).pop()??e,n=t.lastIndexOf(`.`);return n<=0?null:de[t.slice(n+1).toLowerCase()]??null}function pe({session:e,path:t,kind:n}){let{url:r,error:i}=he(e,t);return i?(0,U.jsxs)($,{children:[(0,U.jsx)(`p`,{className:`text-sm text-fg-secondary`,children:`Couldn’t load this file.`}),(0,U.jsx)(me,{session:e,path:t})]}):r?n===`image`?(0,U.jsx)($,{children:(0,U.jsx)(`img`,{src:r,alt:t,className:`max-h-full max-w-full object-contain`})}):n===`video`?(0,U.jsx)($,{children:(0,U.jsx)(`video`,{src:r,controls:!0,className:`max-h-full max-w-full`})}):n===`audio`?(0,U.jsx)($,{children:(0,U.jsx)(`audio`,{src:r,controls:!0,className:`w-full max-w-xl`})}):(0,U.jsx)(`iframe`,{src:r,title:t,className:`h-full w-full border-0 bg-ground`}):(0,U.jsx)($,{children:(0,U.jsx)(`span`,{className:`text-xs text-fg-muted`,children:`…`})})}function $({children:e}){return(0,U.jsx)(`div`,{className:`flex h-full w-full flex-col items-center justify-center gap-3 overflow-auto bg-ground p-4`,children:e})}function me({session:e,path:t}){return(0,U.jsx)(`button`,{type:`button`,onClick:()=>void r(e,t).catch(e=>f.error(e instanceof Error?e.message:`Download failed`)),className:`rounded-md bg-elevated px-3 py-1.5 text-sm text-fg transition-colors hover:bg-border/70`,children:`Download`})}function he(e,t){let[n,r]=(0,B.useState)(null),[i,a]=(0,B.useState)(!1);return(0,B.useEffect)(()=>{let n=null,i=!1;return r(null),a(!1),p(`GET`,`/api/worktrees/${e}/media?path=${encodeURIComponent(t)}`).then(e=>e.blob()).then(e=>{i||(n=URL.createObjectURL(e),r(n))}).catch(()=>{i||a(!0)}),()=>{i=!0,n&&URL.revokeObjectURL(n)}},[e,t]),{url:n,error:i}}function ge({tab:e,reveal:t}){let n=c(e);if(n===`diff`)return(0,U.jsx)(ue,{session:e.session,path:e.path},d(e));if(n===`commit`&&e.sha)return(0,U.jsx)(le,{session:e.session,sha:e.sha,path:e.path},d(e));let r=fe(e.path);return r?(0,U.jsx)(pe,{session:e.session,path:e.path,kind:r},d(e)):(0,U.jsx)(G,{session:e.session,path:e.path,reveal:t},d(e))}export{ge as PaneEditorBody};
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{t as n}from"./jsx-runtime-ffCh33zF.js";import{c as r,d as i,f as a,h as o,m as s,n as c,p as l,r as u,t as d}from"./editor-tabs-Cw21CVB0.js";import{g as f,r as p,t as m}from"./api-BJKPJeFZ.js";import{t as h}from"./createLucideIcon-DyMAgLa3.js";import{a as g,o as _,s as v}from"./index-wPaOVZ8U.js";import{i as y,r as b}from"./editor.api2-4X9Vh9Zw.js";import{a as x,c as S,d as C,f as w,i as T,l as E,n as ee,o as te,r as D,s as O,t as k,u as A}from"./buffer-store-BGy4Fj9o.js";import{a as ne,c as j,i as M,l as N,n as P,o as F,r as I,s as L}from"./editor-sync-F3Wyveaa.js";import{r as R}from"./buffer-logic-BrpRsL1b.js";var z=h(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),B=e(t(),1);function V(e){return!e}function re(e,t){e.addCommand(y.CtrlCmd|b.KeyS,()=>t.onSave()),e.addAction({id:`puddle.toggleWordWrap`,label:`Toggle Word Wrap`,keybindings:[y.Alt|b.KeyZ],run:()=>_({editorWordWrap:V(g().editorWordWrap)})})}function H(e,t,n){let r=k(e,t),i=o(e,t),a=l(e),[s,c]=(0,B.useState)(null),[u,d]=(0,B.useState)(!1),p=(0,B.useRef)(!1),h=(0,B.useRef)(null),g=(0,B.useRef)(null),_=(0,B.useRef)(n);_.current=n;let v=(0,B.useRef)(!1),y=(0,B.useCallback)((e,t)=>{g.current?.cancel(),v.current=!0;try{te(r,e,t)}finally{v.current=!1}},[r]),b=(0,B.useSyncExternalStore)((0,B.useCallback)(e=>E(r,e),[r]),()=>D(r)),x=(0,B.useSyncExternalStore)((0,B.useCallback)(e=>F(r,e),[r]),()=>ne(r));(0,B.useEffect)(()=>{let n=i.data;if(!n||n.binary||n.content===null||p.current)return;p.current=!0;let a=S(r)!==void 0,o=ee(e,t,n.content,n.mtime_ms);c(o),!a&&N(e,t).then(e=>{e&&(e.base_mtime_ms===n.mtime_ms?R(o,e.content)&&d(!0):f(`This file has an unsaved draft from an earlier version.`,{description:`The file changed on disk since the draft was saved.`,duration:12e3,action:{label:`Restore draft`,onClick:()=>{R(o,e.content)&&d(!0)}}}))})},[i.data,r,e,t]),(0,B.useEffect)(()=>{if(!s)return;let n=j(e,t);g.current=n;let i=s.onDidChangeContent(()=>{v.current||(n(s.getValue(),S(r)??0),P(e,t))});return()=>{n.flush(),i.dispose(),g.current=null}},[s,r,e,t]),(0,B.useEffect)(()=>{!s||!x.savedElsewhere||D(r)||i.refetch().then(e=>{D(r)||(e.data&&e.data.content!==null&&y(e.data.content,e.data.mtime_ms),M(r))})},[x.savedElsewhere,s,r]),(0,B.useEffect)(()=>{let e=i.data;!s||!e||e.content===null||!D(r)&&e.mtime_ms!==S(r)&&y(e.content,e.mtime_ms)},[i.data,s,r]);let C=(0,B.useCallback)((n,i)=>{T(r,n,i),g.current?.cancel(),L(e,t),I(e,t,i),d(!1)},[r,e,t]),w=(0,B.useCallback)(()=>{i.refetch().then(n=>{n.data&&n.data.content!==null&&y(n.data.content,n.data.mtime_ms),L(e,t),d(!1)})},[r,e,t,y]),O=(0,B.useCallback)((e,n)=>{a.mutate({path:t,content:e},{onSuccess:e=>C(n,e.mtime_ms),onError:e=>f.error(e instanceof Error?e.message:`Overwrite failed`)})},[a,t,C]),A=(0,B.useCallback)(()=>{if(!s||!D(r))return;let e=s.getValue(),n=s.getAlternativeVersionId();a.mutate({path:t,content:e,expected_mtime_ms:S(r)},{onSuccess:e=>C(n,e.mtime_ms),onError:t=>{t instanceof m&&t.status===409&&t.code===`stale_file`?f.error(`File changed on disk (probably the agent)`,{duration:1/0,action:{label:`Reload`,onClick:()=>w()},cancel:{label:`Overwrite`,onClick:()=>O(e,n)}}):f.error(t instanceof Error?t.message:`Save failed`)}})},[s,r,a,t,C,w,O]),z=(0,B.useRef)(A);z.current=A;let V=(0,B.useCallback)(()=>{let n=h.current,r=_.current;!n||!r||r.session!==e||r.path!==t||(n.revealLineInCenter(r.line),n.setPosition({lineNumber:r.line,column:r.column??1}),n.focus())},[e,t]),H=(0,B.useCallback)(e=>{h.current=e,re(e,{onSave:()=>z.current()}),V()},[V]);(0,B.useEffect)(()=>{V()},[n,s,V]);let U=(0,B.useCallback)(()=>{w()},[w]),W,G=null;i.data?.binary?W=`binary`:i.error?i.error instanceof m&&i.error.status===413?W=`too-large`:(W=`error`,G=i.error instanceof Error?i.error.message:`Failed to load file`):W=s?`ready`:`loading`;let K=b&&x.savedElsewhere?`saved-elsewhere`:b&&x.dirtyElsewhere?`dirty-elsewhere`:null;return{status:W,errorMessage:G,model:s,dirty:b,restoredNotice:u,discardRestore:U,peerBadge:K,save:A,onMount:H}}var U=n();function W({message:e,session:t,path:n,download:i}){return(0,U.jsxs)(`div`,{className:`flex h-full flex-col items-center justify-center gap-3 bg-ground text-sm text-fg-muted`,children:[(0,U.jsx)(`span`,{children:e}),i&&(0,U.jsxs)(`button`,{type:`button`,onClick:()=>void r(t,n).catch(e=>f.error(e instanceof Error?e.message:`Download failed`)),className:`flex items-center gap-1.5 text-fg-secondary transition-colors hover:text-fg`,children:[(0,U.jsx)(z,{className:`size-4`}),`Download`]})]})}function G({session:e,path:t,reveal:n}){let r=v(),i=H(e,t,n),a=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]);if(i.status===`binary`)return(0,U.jsx)(W,{message:`Binary file — use Download`,session:e,path:t,download:!0});if(i.status===`too-large`)return(0,U.jsx)(W,{message:`File too large to edit — use Download`,session:e,path:t,download:!0});if(i.status===`error`)return(0,U.jsx)(W,{message:i.errorMessage??`Failed to load file`,session:e,path:t});let o=i.peerBadge===`saved-elsewhere`?`Saved in another window`:i.peerBadge===`dirty-elsewhere`?`Also being edited in another window`:null;return(0,U.jsxs)(`div`,{className:`flex h-full flex-col bg-ground`,children:[i.restoredNotice&&(0,U.jsxs)(`div`,{className:`flex items-center gap-3 bg-elevated px-3 py-1.5 text-xs text-fg-secondary`,children:[(0,U.jsx)(`span`,{children:`Restored unsaved changes`}),(0,U.jsx)(`button`,{type:`button`,onClick:i.discardRestore,className:`text-fg-muted transition-colors hover:text-fg`,children:`Discard`})]}),o&&(0,U.jsx)(`div`,{className:`bg-surface px-3 py-1 text-xs text-fg-muted`,children:o}),(0,U.jsx)(`div`,{className:`min-h-0 flex-1`,children:i.model&&(0,U.jsx)(C,{path:i.model.uri.toString(),defaultValue:i.model.getValue(),theme:`puddle`,keepCurrentModel:!0,onMount:e=>i.onMount(e),loading:(0,U.jsx)(`div`,{className:`p-3 text-xs text-fg-muted`,children:`…`}),options:{automaticLayout:!0,fontFamily:a,tabSize:r.editorTabSize,wordWrap:r.editorWordWrap?`on`:`off`,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})})]})}function K({children:e}){return(0,U.jsx)(`div`,{className:`flex h-full items-center justify-center px-4 text-xs text-fg-muted`,children:e})}function q(e,t){let n=(0,B.useRef)(t);n.current=t,(0,B.useEffect)(()=>(O(e),()=>{n.current?.(),x(e)}),[e])}function J(e,t,n,r){let i=r.split(`/`).map(encodeURIComponent).join(`/`);return`${e}://${encodeURIComponent(t)}/${encodeURIComponent(n)}/${i}`}function Y({session:e,refName:t,path:n,content:r}){let i=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]);return(0,U.jsx)(C,{path:J(`puddle-view`,e,t,n),defaultValue:r,theme:A,loading:(0,U.jsx)(K,{children:`…`}),options:{readOnly:!0,automaticLayout:!0,fontFamily:i,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})}function X({session:e,against:t,path:n}){let r=a(e,t,n);return r.isPending?(0,U.jsx)(K,{children:`…`}):r.error?(0,U.jsx)(K,{children:r.error instanceof Error?r.error.message:`Failed to load`}):r.data.binary?(0,U.jsx)(K,{children:`Binary file — deleted`}):(0,U.jsxs)(`div`,{className:`flex h-full flex-col`,children:[(0,U.jsx)(`div`,{className:`bg-surface px-4 py-1 text-xs text-fg-muted`,children:`Deleted`}),(0,U.jsx)(`div`,{className:`min-h-0 flex-1`,children:(0,U.jsx)(Y,{session:e,refName:t,path:n,content:r.data.content??``})})]})}function ie({session:e,against:t,path:n,basePath:r}){let i=v(),o=H(e,n,null),s=a(e,t,r),c=(0,B.useRef)(null);q(k(e,n),()=>{let e=c.current;if(!e)return;let t=e.getModel();e.setModel(null),t?.original.dispose(),c.current=null});let l=(0,B.useRef)(o.save);l.current=o.save;let u=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]),d=s.error instanceof m&&s.error.status===404;return o.status===`binary`||s.data?.binary?(0,U.jsx)(K,{children:`Binary file`}):o.status===`too-large`?(0,U.jsx)(K,{children:`File too large to show`}):o.status===`error`?(0,U.jsx)(K,{children:o.errorMessage??`Failed to load file`}):d?(0,U.jsx)(G,{session:e,path:n,reveal:null}):s.isPending||!o.model?(0,U.jsx)(K,{children:`…`}):s.error?(0,U.jsx)(K,{children:s.error instanceof Error?s.error.message:`Failed to load base`}):(0,U.jsx)(w,{modifiedModelPath:o.model.uri.toString(),originalModelPath:J(`puddle-base`,e,t,r),original:s.data.content??``,language:o.model.getLanguageId(),theme:A,keepCurrentModifiedModel:!0,loading:(0,U.jsx)(K,{children:`…`}),onMount:e=>{c.current=e,e.onDidDispose(()=>{c.current=null}),e.getModifiedEditor().addCommand(y.CtrlCmd|b.KeyS,()=>l.current())},options:{readOnly:!1,originalEditable:!1,renderSideBySide:!0,automaticLayout:!0,fontFamily:u,wordWrap:i.editorWordWrap?`on`:`off`,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})}function ae({session:e,path:t}){return q(k(e,t)),(0,U.jsx)(G,{session:e,path:t,reveal:null})}function oe({session:e,against:t,entry:n}){switch(n.status){case`added`:return(0,U.jsx)(ae,{session:e,path:n.path});case`deleted`:return(0,U.jsx)(X,{session:e,against:t,path:n.path});case`modified`:case`renamed`:return(0,U.jsx)(ie,{session:e,against:t,path:n.path,basePath:n.old_path??n.path});default:return null}}function Z({session:e,sha:t,path:n}){let r=a(e,t,n);return r.isPending?(0,U.jsx)(K,{children:`…`}):r.error?(0,U.jsx)(K,{children:r.error instanceof Error?r.error.message:`Failed to load file`}):r.data.binary?(0,U.jsx)(K,{children:`Binary file`}):(0,U.jsx)(Y,{session:e,refName:t,path:n,content:r.data.content??``})}function se({session:e,originalRef:t,originalPath:n,originalContent:r,modifiedRef:i,modifiedPath:a,modifiedContent:o}){let s=(0,B.useRef)(null),c=(0,B.useMemo)(()=>getComputedStyle(document.documentElement).getPropertyValue(`--font-mono`).trim()||void 0,[]);return(0,B.useEffect)(()=>()=>{let e=s.current;if(!e)return;let t=e.getModel();e.setModel(null),t?.original.dispose(),t?.modified.dispose(),s.current=null},[]),(0,U.jsx)(w,{originalModelPath:J(`puddle-hist-orig`,e,t,n),modifiedModelPath:J(`puddle-hist-mod`,e,i,a),original:r,modified:o,theme:A,keepCurrentOriginalModel:!0,keepCurrentModifiedModel:!0,loading:(0,U.jsx)(K,{children:`…`}),onMount:e=>{s.current=e,e.onDidDispose(()=>{s.current=null})},options:{readOnly:!0,originalEditable:!1,renderSideBySide:!0,automaticLayout:!0,fontFamily:c,minimap:{enabled:!1},fixedOverflowWidgets:!0,scrollBeyondLastLine:!1}})}function Q({session:e,sha:t,path:n,basePath:r}){let i=a(e,`${t}^`,r),o=a(e,t,n);return i.error instanceof m&&i.error.status===404?(0,U.jsx)(Z,{session:e,sha:t,path:n}):i.isPending||o.isPending?(0,U.jsx)(K,{children:`…`}):i.error?(0,U.jsx)(K,{children:i.error instanceof Error?i.error.message:`Failed to load original`}):o.error?(0,U.jsx)(K,{children:o.error instanceof Error?o.error.message:`Failed to load file`}):i.data.binary||o.data.binary?(0,U.jsx)(K,{children:`Binary file`}):(0,U.jsx)(se,{session:e,originalRef:`${t}^`,originalPath:r,originalContent:i.data.content??``,modifiedRef:t,modifiedPath:n,modifiedContent:o.data.content??``})}function ce({session:e,sha:t,entry:n,isRootCommit:r}){switch(u(n.status,r)){case`added`:return(0,U.jsx)(Z,{session:e,sha:t,path:n.path});case`deleted`:return(0,U.jsx)(X,{session:e,against:`${t}^`,path:n.path});case`modified`:case`renamed`:return(0,U.jsx)(Q,{session:e,sha:t,path:n.path,basePath:n.old_path??n.path});default:return null}}function le({session:e,sha:t,path:n}){let r=i(e,t);if(r.isPending)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:`Loading commit…`});if(r.error)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:r.error instanceof Error?r.error.message:`Failed to load commit`});let a=r.data.files.find(e=>e.path===n);return a?(0,U.jsx)(`div`,{className:`h-full`,children:(0,U.jsx)(ce,{session:e,sha:t,entry:a,isRootCommit:r.data.parents.length===0})}):(0,U.jsxs)(`div`,{className:`flex h-full items-center justify-center px-4 text-center text-xs text-fg-muted`,children:[(0,U.jsx)(`span`,{className:`font-mono`,children:n}),` is not part of `,t.slice(0,7),`.`]})}function ue({session:e,path:t}){let n=s(e);if(n.isPending)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:`Loading diff…`});if(n.error)return(0,U.jsx)(`div`,{className:`px-4 py-3 text-xs text-fg-muted`,children:n.error instanceof Error?n.error.message:`Failed to load diff`});let r=n.data.entries.find(e=>e.path===t);return r?(0,U.jsx)(`div`,{className:`h-full`,children:(0,U.jsx)(oe,{session:e,against:n.data.against,entry:r})}):(0,U.jsxs)(`div`,{className:`flex h-full items-center justify-center px-4 text-center text-xs text-fg-muted`,children:[`No changes to `,(0,U.jsx)(`span`,{className:`mx-1 font-mono`,children:t}),` against the base.`]})}var de={png:`image`,jpg:`image`,jpeg:`image`,gif:`image`,webp:`image`,bmp:`image`,ico:`image`,avif:`image`,svg:`image`,mp4:`video`,m4v:`video`,webm:`video`,mov:`video`,mkv:`video`,avi:`video`,mp3:`audio`,wav:`audio`,ogg:`audio`,m4a:`audio`,flac:`audio`,aac:`audio`,pdf:`pdf`};function fe(e){let t=e.split(`/`).pop()??e,n=t.lastIndexOf(`.`);return n<=0?null:de[t.slice(n+1).toLowerCase()]??null}function pe({session:e,path:t,kind:n}){let{url:r,error:i}=he(e,t);return i?(0,U.jsxs)($,{children:[(0,U.jsx)(`p`,{className:`text-sm text-fg-secondary`,children:`Couldn’t load this file.`}),(0,U.jsx)(me,{session:e,path:t})]}):r?n===`image`?(0,U.jsx)($,{children:(0,U.jsx)(`img`,{src:r,alt:t,className:`max-h-full max-w-full object-contain`})}):n===`video`?(0,U.jsx)($,{children:(0,U.jsx)(`video`,{src:r,controls:!0,className:`max-h-full max-w-full`})}):n===`audio`?(0,U.jsx)($,{children:(0,U.jsx)(`audio`,{src:r,controls:!0,className:`w-full max-w-xl`})}):(0,U.jsx)(`iframe`,{src:r,title:t,className:`h-full w-full border-0 bg-ground`}):(0,U.jsx)($,{children:(0,U.jsx)(`span`,{className:`text-xs text-fg-muted`,children:`…`})})}function $({children:e}){return(0,U.jsx)(`div`,{className:`flex h-full w-full flex-col items-center justify-center gap-3 overflow-auto bg-ground p-4`,children:e})}function me({session:e,path:t}){return(0,U.jsx)(`button`,{type:`button`,onClick:()=>void r(e,t).catch(e=>f.error(e instanceof Error?e.message:`Download failed`)),className:`rounded-md bg-elevated px-3 py-1.5 text-sm text-fg transition-colors hover:bg-border/70`,children:`Download`})}function he(e,t){let[n,r]=(0,B.useState)(null),[i,a]=(0,B.useState)(!1);return(0,B.useEffect)(()=>{let n=null,i=!1;return r(null),a(!1),p(`GET`,`/api/worktrees/${e}/media?path=${encodeURIComponent(t)}`).then(e=>e.blob()).then(e=>{i||(n=URL.createObjectURL(e),r(n))}).catch(()=>{i||a(!0)}),()=>{i=!0,n&&URL.revokeObjectURL(n)}},[e,t]),{url:n,error:i}}function ge({tab:e,reveal:t}){let n=c(e);if(n===`diff`)return(0,U.jsx)(ue,{session:e.session,path:e.path},d(e));if(n===`commit`&&e.sha)return(0,U.jsx)(le,{session:e.session,sha:e.sha,path:e.path},d(e));let r=fe(e.path);return r?(0,U.jsx)(pe,{session:e.session,path:e.path,kind:r},d(e)):(0,U.jsx)(G,{session:e.session,path:e.path,reveal:t},d(e))}export{ge as PaneEditorBody};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{t}from"./react-CZI7_Jkm.js";import{t as n}from"./jsx-runtime-ffCh33zF.js";import{c as r,d as i,g as a,l as o,o as s,s as c,u as l}from"./api-BJKPJeFZ.js";import{a as u,g as d,i as f,n as p,o as m,r as h,t as g}from"./dialog-B43iKmfK.js";import{t as _}from"./utils-B6KiDbIe.js";import{t as v}from"./createLucideIcon-DyMAgLa3.js";import{a as y,c as b,i as x,n as S,o as C,r as w,s as T,t as E}from"./switch-CQITZ7yg.js";import{r as D}from"./dist-fdPAlKke.js";import{$ as O,B as ee,C as k,E as A,F as j,H as te,I as M,J as ne,K as re,M as N,Mt as ie,N as ae,O as oe,Ot as se,R as ce,S as le,T as ue,V as de,W as fe,Y as P,Z as pe,b as me,et as F,f as he,ft as I,g as ge,h as _e,i as ve,k as L,l as R,mt as z,nt as ye,o as B,pt as V,q as H,s as U,st as be,v as W,y as xe}from"./index-wPaOVZ8U.js";var Se=v(`bell`,[[`path`,{d:`M10.268 21a2 2 0 0 0 3.464 0`,key:`vwvbt9`}],[`path`,{d:`M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326`,key:`11g9vi`}]]),G=v(`folder-input`,[[`path`,{d:`M2 9V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-1`,key:`fm4g5t`}],[`path`,{d:`M2 13h10`,key:`pgb2dq`}],[`path`,{d:`m9 16 3-3-3-3`,key:`6m91ic`}]]),Ce=v(`palette`,[[`path`,{d:`M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z`,key:`e79jfc`}],[`circle`,{cx:`13.5`,cy:`6.5`,r:`.5`,fill:`currentColor`,key:`1okk4w`}],[`circle`,{cx:`17.5`,cy:`10.5`,r:`.5`,fill:`currentColor`,key:`f64h9f`}],[`circle`,{cx:`6.5`,cy:`12.5`,r:`.5`,fill:`currentColor`,key:`qy21gx`}],[`circle`,{cx:`8.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`fotxhn`}]]),we=v(`server`,[[`rect`,{width:`20`,height:`8`,x:`2`,y:`2`,rx:`2`,ry:`2`,key:`ngkwjq`}],[`rect`,{width:`20`,height:`8`,x:`2`,y:`14`,rx:`2`,ry:`2`,key:`iecqi9`}],[`line`,{x1:`6`,x2:`6.01`,y1:`6`,y2:`6`,key:`16zg32`}],[`line`,{x1:`6`,x2:`6.01`,y1:`18`,y2:`18`,key:`nzw8ys`}]]),Te=v(`shield-alert`,[[`path`,{d:`M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z`,key:`oel41y`}],[`path`,{d:`M12 8v4`,key:`1got3b`}],[`path`,{d:`M12 16h.01`,key:`1drbdi`}]]),Ee=v(`sliders-horizontal`,[[`path`,{d:`M10 5H3`,key:`1qgfaw`}],[`path`,{d:`M12 19H3`,key:`yhmn1j`}],[`path`,{d:`M14 3v4`,key:`1sua03`}],[`path`,{d:`M16 17v4`,key:`1q0r14`}],[`path`,{d:`M21 12h-9`,key:`1o4lsq`}],[`path`,{d:`M21 19h-5`,key:`1rlt1p`}],[`path`,{d:`M21 5h-7`,key:`1oszz2`}],[`path`,{d:`M8 10v4`,key:`tgpxqk`}],[`path`,{d:`M8 12H3`,key:`a7s4jb`}]]),De=v(`users`,[[`path`,{d:`M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2`,key:`1yyitq`}],[`path`,{d:`M16 3.128a4 4 0 0 1 0 7.744`,key:`16gr8j`}],[`path`,{d:`M22 21v-2a4 4 0 0 0-3-3.87`,key:`kshegd`}],[`circle`,{cx:`9`,cy:`7`,r:`4`,key:`nufk8`}]]),K=e(t(),1),q=n();function J({label:e,description:t,htmlFor:n,children:r,className:i}){return(0,q.jsxs)(`div`,{className:_(`flex items-center gap-4 py-2.5`,i),children:[(0,q.jsxs)(`label`,{htmlFor:n,className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,q.jsx)(`span`,{className:`text-sm text-fg`,children:e}),t&&(0,q.jsx)(`span`,{className:`text-xs text-fg-muted`,children:t})]}),(0,q.jsx)(`div`,{className:`flex shrink-0 items-center gap-2`,children:r})]})}function Y({children:e,note:t}){return(0,q.jsxs)(`div`,{className:`mb-2 flex flex-col gap-0.5`,children:[(0,q.jsx)(`h2`,{className:`text-base font-semibold text-fg`,children:e}),t&&(0,q.jsx)(`span`,{className:`text-xs text-fg-muted`,children:t})]})}function Oe(){let e=U(),[t,n]=(0,K.useState)(ie());return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{note:`This browser only`,children:`Appearance`}),(0,q.jsx)(J,{label:`Theme`,description:`Chrome, terminal, and editor restyle together.`,children:(0,q.jsxs)(S,{value:t,onValueChange:e=>{n(e),se(e)},children:[(0,q.jsx)(y,{className:`w-32`,children:(0,q.jsx)(C,{})}),(0,q.jsxs)(w,{children:[(0,q.jsx)(x,{value:`dark`,children:`Dark`}),(0,q.jsx)(x,{value:`light`,children:`Light`}),(0,q.jsx)(x,{value:`system`,children:`System`})]})]})}),(0,q.jsx)(J,{label:`UI font size`,htmlFor:`ui-font-size`,children:(0,q.jsx)(V,{id:`ui-font-size`,type:`number`,min:12,max:24,step:.5,className:`w-20 tabular-nums`,value:e.uiFontSize,onChange:e=>B({uiFontSize:Number(e.target.value)||ve.uiFontSize})})}),(0,q.jsx)(J,{label:`Terminal font size`,htmlFor:`terminal-font-size`,children:(0,q.jsx)(V,{id:`terminal-font-size`,type:`number`,min:9,max:24,className:`w-20 tabular-nums`,value:e.terminalFontSize,onChange:e=>B({terminalFontSize:Number(e.target.value)||13})})}),(0,q.jsx)(J,{label:`Density`,children:(0,q.jsxs)(S,{value:e.density,onValueChange:e=>B({density:e}),children:[(0,q.jsx)(y,{className:`w-36`,children:(0,q.jsx)(C,{})}),(0,q.jsxs)(w,{children:[(0,q.jsx)(x,{value:`compact`,children:`Compact`}),(0,q.jsx)(x,{value:`comfortable`,children:`Comfortable`})]})]})}),(0,q.jsx)(J,{label:`Reduced motion`,description:`Status ripples become static dots.`,htmlFor:`reduced-motion`,children:(0,q.jsx)(E,{id:`reduced-motion`,checked:e.reducedMotion,onCheckedChange:e=>B({reducedMotion:e})})}),(0,q.jsx)(J,{label:`All projects in the sidebar`,description:`Show every project's sessions in the right sidebar, grouped by project.`,htmlFor:`all-project-sessions`,children:(0,q.jsx)(E,{id:`all-project-sessions`,checked:e.showAllProjectSessions,onCheckedChange:e=>B({showAllProjectSessions:e})})})]})}var X=`__none__`;function ke(){let e=L(),t=F().data?.find(t=>t.id===e),n=N(e??void 0),r=O(e??void 0),i=ne(),o=P(e??``),c=ee(),[l,_]=(0,K.useState)(``),[v,b]=(0,K.useState)(``),[T,E]=(0,K.useState)(!1),[D,k]=(0,K.useState)(``);if((0,K.useEffect)(()=>{t&&(_(t.name),b(t.branch_prefix))},[t]),!t)return null;let A=r.data?.default_account_id;return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{children:`Profile`}),(0,q.jsxs)(J,{label:`Name`,description:`A display label. Letters, digits, dot, underscore and hyphen; must be unique.`,htmlFor:`profile-name`,children:[(0,q.jsx)(V,{id:`profile-name`,value:l,onChange:e=>_(e.target.value),className:`w-44 font-mono`}),(0,q.jsx)(d,{size:`sm`,variant:`secondary`,disabled:l===t.name||l.trim()===``||i.isPending,onClick:()=>i.mutate({id:t.id,name:l},{onError:e=>a.error(e.message)}),children:`Save`})]}),(0,q.jsxs)(J,{label:`Branch prefix`,description:`Session branches become <prefix><slug(title)>.`,htmlFor:`branch-prefix`,children:[(0,q.jsx)(V,{id:`branch-prefix`,value:v,onChange:e=>b(e.target.value),placeholder:s,className:`w-44 font-mono`}),(0,q.jsx)(d,{size:`sm`,variant:`secondary`,disabled:v===t.branch_prefix||i.isPending,onClick:()=>i.mutate({id:t.id,branch_prefix:v},{onError:e=>a.error(e.message)}),children:`Save`})]}),(0,q.jsx)(J,{label:`Default account`,description:`Preselected in the new-session modal.`,children:(0,q.jsxs)(S,{value:typeof A==`number`?String(A):X,onValueChange:e=>o.mutate({default_account_id:e===X?null:Number(e)},{onError:e=>a.error(e.message)}),children:[(0,q.jsx)(y,{className:`w-56`,children:(0,q.jsx)(C,{})}),(0,q.jsxs)(w,{children:[(0,q.jsx)(x,{value:X,children:`First available`}),n.data?.map(e=>(0,q.jsx)(x,{value:String(e.id),children:(0,q.jsxs)(`span`,{className:`font-mono`,children:[e.agent_type,`/`,e.label]})},e.id))]})]})}),(0,q.jsx)(J,{label:`Delete profile`,description:`Removes its projects, accounts (logging them out), and archived history. Non-archived sessions block deletion.`,className:`mt-4`,children:(0,q.jsx)(d,{variant:`danger`,size:`sm`,onClick:()=>E(!0),children:`Delete…`})}),(0,q.jsx)(g,{open:T,onOpenChange:e=>{e||(E(!1),k(``))},children:(0,q.jsxs)(p,{children:[(0,q.jsxs)(u,{children:[(0,q.jsxs)(m,{children:[`Delete profile `,(0,q.jsx)(`span`,{className:`font-mono`,children:t.name}),`?`]}),(0,q.jsx)(h,{children:`Everything this profile owns goes with it: projects, workspace layouts, accounts and their credential directories, and archived session history. Branches, repositories, and terminal logs on disk are untouched. This cannot be undone.`})]}),(0,q.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,q.jsxs)(I,{htmlFor:`delete-confirm-name`,children:[`Type `,(0,q.jsx)(`span`,{className:`font-mono text-fg`,children:t.name}),` to confirm`]}),(0,q.jsx)(V,{id:`delete-confirm-name`,value:D,onChange:e=>k(e.target.value),className:`font-mono`,autoFocus:!0})]}),(0,q.jsxs)(f,{children:[(0,q.jsx)(d,{variant:`ghost`,onClick:()=>{E(!1),k(``)},children:`Cancel`}),(0,q.jsx)(d,{variant:`danger`,disabled:D!==t.name||c.isPending,onClick:()=>c.mutate(t.id,{onSuccess:()=>{W(),oe.set(null)},onError:e=>{E(!1),k(``),a.error(e.message)}}),children:`Delete profile`})]})]})})]})}function Ae({agentId:e,profileId:t,onClose:n}){let r=M(),[i,o]=(0,K.useState)(``),[s,c]=(0,K.useState)(``),l=de(_e(s,150)),_=i.trim()!==``&&(s.startsWith(`/`)||s.startsWith(`~`));return(0,q.jsx)(g,{open:!0,onOpenChange:e=>!e&&n(),children:(0,q.jsxs)(p,{children:[(0,q.jsxs)(u,{children:[(0,q.jsx)(m,{children:`Import an existing account`}),(0,q.jsx)(h,{children:`The directory is copied into a puddle-owned account; the original is never touched. If credentials do not travel (macOS keychain), log in once afterwards.`})]}),(0,q.jsxs)(`div`,{className:`flex flex-col gap-3`,children:[(0,q.jsx)(V,{placeholder:`label, e.g. personal`,value:i,onChange:e=>o(e.target.value),className:`font-mono`}),(0,q.jsx)(ge,{value:s,onValueChange:c,placeholder:`config dir on the host, e.g. ~/.claude`,hints:(l.data?.entries??[]).map(e=>({value:e.path,label:e.name})),className:`font-mono`})]}),(0,q.jsxs)(f,{children:[(0,q.jsx)(d,{variant:`ghost`,onClick:n,children:`Cancel`}),(0,q.jsxs)(d,{disabled:!_||r.isPending,onClick:()=>r.mutate({profile_id:t,agent_type:e,label:i.trim(),import_dir:s.trim()},{onSuccess:e=>{n(),e.logged_in||a.info(`Imported without credentials — press Login to authenticate.`)},onError:e=>a.error(e.message)}),children:[(0,q.jsx)(G,{}),`Import`]})]})]})})}function je({account:e,gateOpen:t}){let n=fe(),r=re(),i=ce(),[o,s]=(0,K.useState)(null),[c,l]=(0,K.useState)(!1),[_,v]=(0,K.useState)(e.label);return(0,K.useEffect)(()=>v(e.label),[e.label]),(0,q.jsxs)(`div`,{className:`flex items-center gap-3 rounded-md bg-surface px-3 py-2`,children:[(0,q.jsxs)(`span`,{className:`min-w-0 flex-1`,children:[(0,q.jsx)(`input`,{value:_,onChange:e=>v(e.target.value),onBlur:()=>{let t=_.trim();if(t!==e.label){if(t===``){v(e.label);return}r.mutate({id:e.id,label:t},{onError:t=>{a.error(t.message),v(e.label)}})}},onKeyDown:t=>{t.key===`Enter`?t.currentTarget.blur():t.key===`Escape`&&(v(e.label),t.currentTarget.blur())},"aria-label":`Account name`,className:`-mx-1 block w-full truncate rounded-sm bg-transparent px-1 py-0.5 font-mono text-sm text-fg transition-colors hover:bg-elevated focus:bg-elevated focus:outline-none`}),(0,q.jsx)(`span`,{className:`text-2xs ${e.logged_in?`text-running`:`text-waiting`}`,children:e.logged_in?`logged in`:`not logged in`})]}),t&&(0,q.jsxs)(`label`,{className:`flex items-center gap-2 text-xs text-fg-secondary`,children:[`skip prompts`,(0,q.jsx)(E,{checked:e.skip_permissions_default,onCheckedChange:t=>r.mutate({id:e.id,skip_permissions_default:t},{onError:e=>a.error(e.message)})})]}),(0,q.jsxs)(d,{size:`sm`,variant:`secondary`,disabled:n.isPending,onClick:()=>n.mutate(e.id,{onSuccess:e=>s(e.stream),onError:e=>a.error(e.message)}),children:[(0,q.jsx)(A,{}),e.logged_in?`Re-login`:`Login`]}),(0,q.jsxs)(d,{size:`icon`,variant:`ghost`,className:`size-7 text-fg-muted hover:text-danger`,onClick:()=>l(!0),children:[(0,q.jsx)(T,{}),(0,q.jsx)(`span`,{className:`sr-only`,children:`Delete account`})]}),o&&(0,q.jsx)(R,{stream:o,label:`${e.agent_type}/${e.label}`,onClose:()=>s(null)}),(0,q.jsx)(g,{open:c,onOpenChange:l,children:(0,q.jsxs)(p,{children:[(0,q.jsxs)(u,{children:[(0,q.jsxs)(m,{children:[`Delete `,(0,q.jsx)(`span`,{className:`font-mono`,children:e.label}),`?`]}),(0,q.jsx)(h,{children:`Removes the account, its archived session history, and its credential directory — this logs the account out. Non-archived sessions block deletion.`})]}),(0,q.jsxs)(f,{children:[(0,q.jsx)(d,{variant:`ghost`,onClick:()=>l(!1),children:`Cancel`}),(0,q.jsx)(d,{variant:`danger`,disabled:i.isPending,onClick:()=>i.mutate(e.id,{onSuccess:()=>l(!1),onError:e=>{l(!1),a.error(e.message)}}),children:`Delete account`})]})]})})]})}function Me(){let e=L(),t=ae(),n=N(e??void 0),r=O(e??void 0),i=M(),o=fe(),[s,c]=(0,K.useState)({}),[l,u]=(0,K.useState)(null),[f,p]=(0,K.useState)(null),m=r.data?.allowSkipPermissions===!0,h=t=>{let n=(s[t]??``).trim();!n||e===null||i.mutate({profile_id:e,agent_type:t,label:n},{onSuccess:e=>{c(e=>({...e,[t]:``})),o.mutate(e.id,{onSuccess:n=>u({stream:n.stream,label:`${t}/${e.label}`})})},onError:e=>a.error(e.message)})};return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{children:`Accounts`}),t.data?.map(e=>{let t=n.data?.filter(t=>t.agent_type===e.id)??[];return(0,q.jsxs)(`div`,{className:`mb-5`,children:[(0,q.jsx)(J,{label:e.display_name,description:`Accounts are isolated config dirs under this profile.`,className:`py-1`,children:(0,q.jsx)(`span`,{className:`font-mono text-2xs text-fg-muted`,children:e.id})}),(0,q.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[t.map(t=>(0,q.jsx)(je,{account:t,gateOpen:m&&e.capabilities.skip_permissions},t.id)),(0,q.jsxs)(`form`,{className:`flex gap-2`,onSubmit:t=>{t.preventDefault(),h(e.id)},children:[(0,q.jsx)(V,{placeholder:`label, e.g. personal`,value:s[e.id]??``,onChange:t=>c(n=>({...n,[e.id]:t.target.value})),className:`w-48 font-mono`}),(0,q.jsxs)(d,{type:`submit`,size:`sm`,variant:`secondary`,disabled:!(s[e.id]??``).trim()||i.isPending,children:[(0,q.jsx)(ue,{}),`Add account`]}),(0,q.jsxs)(d,{type:`button`,size:`sm`,variant:`ghost`,onClick:()=>p(e.id),children:[(0,q.jsx)(G,{}),`Import existing…`]})]})]})]},e.id)}),l&&(0,q.jsx)(R,{stream:l.stream,label:l.label,onClose:()=>u(null)}),f!==null&&e!==null&&(0,q.jsx)(Ae,{agentId:f,profileId:e,onClose:()=>p(null)})]})}function Z({id:e,label:t,description:n,initial:r,defaultText:i,onSave:a,pending:o}){let[s,c]=(0,K.useState)(r);return(0,q.jsxs)(`div`,{className:`mt-3 flex flex-col gap-1.5`,children:[(0,q.jsxs)(`label`,{htmlFor:e,className:`flex flex-col gap-0.5 text-sm text-fg`,children:[t,(0,q.jsx)(`span`,{className:`text-xs text-fg-muted`,children:n})]}),(0,q.jsx)(z,{id:e,value:s,onChange:e=>c(e.target.value),rows:6,className:`font-mono`}),(0,q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,q.jsx)(d,{size:`sm`,variant:`secondary`,disabled:s===r||o,onClick:()=>a(s),children:`Save`}),(0,q.jsx)(d,{size:`sm`,variant:`ghost`,disabled:s===i||o,onClick:()=>{c(i),a(i)},children:`Reset to default`})]})]})}var Ne={id:`4f3c2b1a-7e6d-5c4b-3a2f-1e0d9c8b7a6f`,title:null,agent_title:`Refactor the auth flow`,osc_title:`claude`,branch:`alice/refactor-auth`,worktree_path:`/home/alice/code/app--refactor-auth`,status:`waiting_input`,agent_type:`claude-code`};function Pe({initial:e,onSave:t,pending:n}){let[r,a]=(0,K.useState)(e),o=r.length>0?r:l;return(0,q.jsxs)(`div`,{className:`mt-3 flex flex-col gap-1.5`,children:[(0,q.jsxs)(`label`,{htmlFor:`tab-title-template`,className:`flex flex-col gap-0.5 text-sm text-fg`,children:[`Template`,(0,q.jsx)(`span`,{className:`text-xs text-fg-muted`,children:`Compose the label from the parts below. Empty falls back to the default.`})]}),(0,q.jsx)(V,{id:`tab-title-template`,value:r,onChange:e=>a(e.target.value),className:`font-mono`,placeholder:l}),(0,q.jsxs)(`p`,{className:`text-xs text-fg-muted`,children:[`Preview`,` `,(0,q.jsx)(`span`,{className:`ml-1 font-sans text-fg`,children:he(Ne,o)})]}),(0,q.jsx)(`dl`,{className:`mt-1 flex flex-col gap-0.5 text-xs`,children:i.map(([e,t])=>(0,q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,q.jsx)(`dt`,{className:`w-24 shrink-0 font-mono text-fg-secondary`,children:`\${${e}}`}),(0,q.jsx)(`dd`,{className:`text-fg-muted`,children:t})]},e))}),(0,q.jsxs)(`div`,{className:`flex gap-2`,children:[(0,q.jsx)(d,{size:`sm`,variant:`secondary`,disabled:r===e||n,onClick:()=>t(r),children:`Save`}),(0,q.jsx)(d,{size:`sm`,variant:`ghost`,disabled:r==="${name}"||n,onClick:()=>{a(l),t(l)},children:`Reset to default`})]})]})}function Fe(){let e=L(),t=F().data?.find(t=>t.id===e),n=O(e??void 0),i=P(e??``),[s,l]=(0,K.useState)(!1),[_,v]=(0,K.useState)(``),y=n.data?.allowSkipPermissions===!0,b=e=>i.mutate({allowSkipPermissions:e},{onSuccess:()=>{l(!1),v(``)},onError:e=>a.error(e.message)}),x=e=>t=>i.mutate({[e]:t},{onError:e=>a.error(e.message)});return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{children:`Sessions`}),(0,q.jsx)(`p`,{className:`mb-3 text-xs text-fg-secondary`,children:`Permission prompts are on by default, everywhere. Skipping them requires this profile gate, a per-account opt-in, and a per-session toggle — and the daemon re-checks all three on every launch, resume, and hand-off.`}),(0,q.jsx)(J,{label:`Allow skipping permission prompts`,description:y?`The gate is open: opted-in accounts can start prompt-free sessions.`:`The gate is closed: every session keeps its permission prompts.`,htmlFor:`gate`,children:(0,q.jsx)(E,{id:`gate`,checked:y,onCheckedChange:e=>{e?l(!0):b(!1)}})}),(0,q.jsxs)(`div`,{className:`mt-5`,children:[(0,q.jsx)(Y,{note:`Sent to the agent as its opening message when a session starts. Leave a box empty to send no preamble.`,children:`Launch text`}),n.data&&(0,q.jsxs)(q.Fragment,{children:[(0,q.jsx)(Z,{id:`onboarding-template`,label:`New worktree`,description:`For a freshly created worktree. Use {{rules}} where the repo's onboarding notes should appear.`,initial:n.data.onboardingTemplate??r,defaultText:r,onSave:x(`onboardingTemplate`),pending:i.isPending},`${e}:onboarding`),(0,q.jsx)(Z,{id:`concurrent-template`,label:`Existing / shared worktree`,description:`For a session joining a worktree other agents may already be working in.`,initial:n.data.concurrentTemplate??c,defaultText:c,onSave:x(`concurrentTemplate`),pending:i.isPending},`${e}:concurrent`),(0,q.jsx)(Z,{id:`restart-template`,label:`Resume after restart`,description:`Sent when a session resumes after a daemon restart or machine reboot — its processes are gone.`,initial:n.data.restartTemplate??o,defaultText:o,onSave:x(`restartTemplate`),pending:i.isPending},`${e}:restart`)]})]}),(0,q.jsxs)(`div`,{className:`mt-5`,children:[(0,q.jsx)(Y,{note:`How each session's tab and sidebar label is composed from its parts.`,children:`Tab title`}),n.data&&(0,q.jsx)(Pe,{initial:n.data.tabTitleTemplate??"${name}",onSave:x(`tabTitleTemplate`),pending:i.isPending},`${e}:tab-title`)]}),(0,q.jsx)(g,{open:s,onOpenChange:e=>{e||(l(!1),v(``))},children:(0,q.jsxs)(p,{children:[(0,q.jsxs)(u,{children:[(0,q.jsxs)(m,{className:`flex items-center gap-2`,children:[(0,q.jsx)(Te,{className:`size-5 text-danger`}),`Run agents without permission prompts?`]}),(0,q.jsx)(h,{asChild:!0,children:(0,q.jsxs)(`div`,{className:`flex flex-col gap-2 text-sm`,children:[(0,q.jsx)(`p`,{children:`A prompt-free agent acts unattended, with your credentials, inside its worktree — and nothing technically stops a shell command from reaching outside it. It can:`}),(0,q.jsxs)(`ul`,{className:`list-disc pl-5 text-fg-secondary`,children:[(0,q.jsx)(`li`,{children:`run any shell command without asking first,`}),(0,q.jsx)(`li`,{children:`modify, delete, or exfiltrate files your user can reach,`}),(0,q.jsx)(`li`,{children:`install dependencies and execute the network requests they make,`}),(0,q.jsx)(`li`,{children:`push branches or call remote APIs with the credentials it finds.`})]}),(0,q.jsx)(`p`,{children:`Only open this gate if every account you opt in runs work you would trust a colleague to run unsupervised on this machine.`}),(0,q.jsx)(`p`,{className:`text-fg-muted`,children:`Opening it also records the agent’s own bypass-permissions acceptance for this profile’s accounts, so the skip actually takes effect.`})]})})]}),(0,q.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,q.jsxs)(I,{htmlFor:`confirm-name`,children:[`Type `,(0,q.jsx)(`span`,{className:`font-mono text-fg`,children:t?.name}),` to confirm`]}),(0,q.jsx)(V,{id:`confirm-name`,value:_,onChange:e=>v(e.target.value),className:`font-mono`,autoFocus:!0})]}),(0,q.jsxs)(f,{children:[(0,q.jsx)(d,{variant:`ghost`,onClick:()=>{l(!1),v(``)},children:`Cancel`}),(0,q.jsx)(d,{variant:`danger`,disabled:_!==t?.name||i.isPending,onClick:()=>b(!0),children:`Open the gate`})]})]})})]})}var Ie={desktop:!0,sound:!1,muted_projects:[]};function Le(){let e=L(),t=O(e??void 0),n=P(e??``),r=ye(e??void 0),i={...Ie,...t.data?.notifications??{}},o=e=>n.mutate({notifications:e},{onError:e=>a.error(e.message)});return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{note:`Delivery lands in a later phase`,children:`Notifications`}),(0,q.jsx)(J,{label:`Desktop notification on waiting`,description:`When a session flips to waiting for input.`,htmlFor:`notify-desktop`,children:(0,q.jsx)(E,{id:`notify-desktop`,checked:i.desktop,onCheckedChange:e=>o({...i,desktop:e})})}),(0,q.jsx)(J,{label:`Sound`,htmlFor:`notify-sound`,children:(0,q.jsx)(E,{id:`notify-sound`,checked:i.sound,onCheckedChange:e=>o({...i,sound:e})})}),(0,q.jsx)(J,{label:`Muted projects`,description:`No notifications from these projects.`,children:(0,q.jsxs)(`span`,{className:`text-2xs text-fg-muted tabular-nums`,children:[i.muted_projects.length,` muted`]})}),(0,q.jsx)(`div`,{className:`flex flex-col gap-1`,children:r.data?.map(e=>(0,q.jsxs)(`label`,{className:`flex items-center gap-2 rounded-md px-2 py-1 text-sm text-fg-secondary hover:bg-elevated`,children:[(0,q.jsx)(E,{checked:i.muted_projects.includes(e.id),onCheckedChange:t=>o({...i,muted_projects:t?[...i.muted_projects,e.id]:i.muted_projects.filter(t=>t!==e.id)})}),e.name]},e.id))})]})}function Re(){let e=j(),t=H(),[n,r]=(0,K.useState)(``);return(0,K.useEffect)(()=>{e.data&&r(e.data.agentPath)},[e.data]),(0,q.jsx)(J,{label:`Agent search path (host-wide)`,description:`Colon-separated dirs the daemon prepends to PATH to find agent CLIs like claude (e.g. ~/.local/bin). Applies after the daemon restarts.`,htmlFor:`agent-path`,children:(0,q.jsx)(V,{id:`agent-path`,type:`text`,className:`w-64 font-mono text-2xs`,value:n,onChange:e=>r(e.target.value),onBlur:()=>{e.data&&n!==e.data.agentPath&&t.mutate({agentPath:n})}})})}function ze(){let e=U();return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{note:`This browser only`,children:`Terminal & editor`}),(0,q.jsx)(J,{label:`Terminal scrollback`,description:`Lines kept per terminal.`,htmlFor:`scrollback`,children:(0,q.jsx)(V,{id:`scrollback`,type:`number`,min:500,max:1e5,step:500,className:`w-28 tabular-nums`,value:e.terminalScrollback,onChange:e=>B({terminalScrollback:Number(e.target.value)||5e3})})}),(0,q.jsx)(J,{label:`Editor tab size`,description:`Consumed when the editor lands in Phase 3.`,htmlFor:`tab-size`,children:(0,q.jsx)(V,{id:`tab-size`,type:`number`,min:1,max:8,className:`w-20 tabular-nums`,value:e.editorTabSize,onChange:e=>B({editorTabSize:Number(e.target.value)||2})})}),(0,q.jsx)(J,{label:`Editor word wrap`,htmlFor:`word-wrap`,children:(0,q.jsx)(E,{id:`word-wrap`,checked:e.editorWordWrap,onCheckedChange:e=>B({editorWordWrap:e})})}),(0,q.jsx)(J,{label:`SSH host for editor links`,description:`Used for vscode:// and cursor:// remote links until the CLI supplies it automatically.`,htmlFor:`editor-link-host`,children:(0,q.jsx)(V,{id:`editor-link-host`,type:`text`,placeholder:`user@host`,className:`w-48`,value:e.editorLinkSshHost,onChange:e=>B({editorLinkSshHost:e.target.value})})}),(0,q.jsx)(Re,{})]})}function Be({repo:e}){let t=pe(),n=te(),[r,i]=(0,K.useState)(e.default_base_branch),[o,s]=(0,K.useState)(e.onboarding_notes??``),c=e=>a.error(e.message);return(0,q.jsxs)(`div`,{className:`mb-4 rounded-lg bg-surface p-3`,children:[(0,q.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,q.jsx)(`span`,{className:`min-w-0 flex-1 truncate font-mono text-sm text-fg`,children:e.path}),(0,q.jsx)(`span`,{className:`shrink-0 text-2xs text-fg-muted tabular-nums`,children:e.last_fetched_at?`fetched ${new Date(e.last_fetched_at).toLocaleString()}`:`never fetched`}),(0,q.jsxs)(d,{size:`sm`,variant:`ghost`,disabled:n.isPending,onClick:()=>n.mutate(e.id,{onSuccess:()=>a.success(`Fetched.`),onError:c}),children:[(0,q.jsx)(b,{}),`Fetch now`]})]}),(0,q.jsxs)(J,{label:`Default base branch`,htmlFor:`base-${e.id}`,className:`py-1.5`,children:[(0,q.jsx)(V,{id:`base-${e.id}`,value:r,onChange:e=>i(e.target.value),className:`w-40 font-mono`}),(0,q.jsx)(d,{size:`sm`,variant:`secondary`,disabled:r===e.default_base_branch||t.isPending,onClick:()=>t.mutate({id:e.id,default_base_branch:r},{onError:c}),children:`Save`})]}),(0,q.jsx)(J,{label:`Periodic fetch`,description:`Fetched on open and on the daemon's interval while sessions are active.`,htmlFor:`fetch-${e.id}`,className:`py-1.5`,children:(0,q.jsx)(E,{id:`fetch-${e.id}`,checked:e.fetch_enabled,onCheckedChange:n=>t.mutate({id:e.id,fetch_enabled:n},{onError:c})})}),(0,q.jsxs)(`div`,{className:`mt-1.5 flex flex-col gap-1.5`,children:[(0,q.jsxs)(`label`,{htmlFor:`notes-${e.id}`,className:`flex flex-col gap-0.5 text-sm text-fg`,children:[`Onboarding notes`,(0,q.jsx)(`span`,{className:`text-xs text-fg-muted`,children:`Standing setup rules, injected into every fresh worktree. Agents update them via .puddle/onboarding-notes.md.`})]}),(0,q.jsx)(z,{id:`notes-${e.id}`,value:o,onChange:e=>s(e.target.value),rows:4,className:`font-mono`}),(0,q.jsx)(`div`,{children:(0,q.jsx)(d,{size:`sm`,variant:`secondary`,disabled:o===(e.onboarding_notes??``)||t.isPending,onClick:()=>t.mutate({id:e.id,onboarding_notes:o},{onError:c}),children:`Save notes`})})]}),e.orphan_worktrees.length>0&&(0,q.jsxs)(`div`,{className:`mt-3 rounded-md bg-elevated p-2`,children:[(0,q.jsx)(`p`,{className:`text-xs text-fg-secondary`,children:`Orphan worktrees — directories no session claims. Puddle never deletes these automatically; remove them by hand if unwanted.`}),(0,q.jsx)(`ul`,{className:`mt-1 flex flex-col gap-0.5`,children:e.orphan_worktrees.map(e=>(0,q.jsx)(`li`,{className:`truncate font-mono text-2xs text-fg-muted`,children:e},e))})]})]})}function Ve(){let e=be();return(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{children:`Repositories`}),e.data?.length===0&&(0,q.jsx)(`p`,{className:`text-sm text-fg-muted`,children:`No repositories yet — register one from the new-project flow on the dashboard.`}),e.data?.map(e=>(0,q.jsx)(Be,{repo:e},e.id))]})}function Q({label:e,description:t,field:n,config:r,min:i,step:o=1}){let s=H();return(0,q.jsx)(J,{label:e,description:t,htmlFor:`host-${n}`,children:(0,q.jsx)(V,{id:`host-${n}`,type:`number`,min:i,step:o,className:`w-32 tabular-nums`,defaultValue:r[n],onBlur:e=>{let t=Number(e.target.value);Number.isFinite(t)&&t!==r[n]&&s.mutate({[n]:t},{onError:e=>a.error(e.message)})}})})}function He(){let e=j(),t=H();return e.data?(0,q.jsxs)(`div`,{children:[(0,q.jsx)(Y,{note:`Affects all profiles on this host`,children:`Host`}),(0,q.jsx)(Q,{label:`Fetch interval (minutes)`,description:`Periodic git fetch for repos with active sessions.`,field:`fetchIntervalMinutes`,config:e.data,min:1}),(0,q.jsx)(Q,{label:`Log size cap (bytes)`,description:`Per-terminal on-disk log cap.`,field:`logMaxBytes`,config:e.data,min:65536,step:1048576}),(0,q.jsx)(Q,{label:`Replay size (bytes)`,description:`Log tail sent to a terminal on attach.`,field:`replayBytes`,config:e.data,min:1024,step:65536}),(0,q.jsx)(Q,{label:`UI-state retention (days)`,description:`Stale workspace snapshots are swept at boot.`,field:`uiStateRetentionDays`,config:e.data,min:1}),(0,q.jsx)(J,{label:`Auto-resume`,description:`Resume interrupted sessions automatically after a daemon restart.`,htmlFor:`host-autoResume`,children:(0,q.jsx)(E,{id:`host-autoResume`,checked:e.data.autoResume,onCheckedChange:e=>t.mutate({autoResume:e},{onError:e=>a.error(e.message)})})})]}):null}var $=[{id:`appearance`,label:`Appearance`,icon:Ce,render:()=>(0,q.jsx)(Oe,{})},{id:`profile`,label:`Profile`,icon:le,render:()=>(0,q.jsx)(ke,{})},{id:`accounts`,label:`Accounts`,icon:De,render:()=>(0,q.jsx)(Me,{})},{id:`sessions`,label:`Sessions`,icon:Ee,render:()=>(0,q.jsx)(Fe,{})},{id:`notifications`,label:`Notifications`,icon:Se,render:()=>(0,q.jsx)(Le,{})},{id:`terminal`,label:`Terminal & Editor`,icon:k,render:()=>(0,q.jsx)(ze,{})},{id:`repositories`,label:`Repositories`,icon:D,render:()=>(0,q.jsx)(Ve,{})},{id:`host`,label:`Host`,icon:we,render:()=>(0,q.jsx)(He,{})}];function Ue(){let e=me(),t=$.find(t=>t.id===e)??$[0];return(0,q.jsx)(g,{open:e!==null,onOpenChange:e=>!e&&W(),children:(0,q.jsxs)(p,{wide:!0,className:`grid h-[34rem] grid-cols-[14rem_1fr] gap-0 overflow-hidden p-0`,children:[(0,q.jsx)(m,{className:`sr-only`,children:`Settings`}),(0,q.jsxs)(`nav`,{className:`flex flex-col gap-1 bg-surface p-2.5 pr-6`,children:[(0,q.jsx)(`span`,{className:`px-2.5 py-2 text-2xs font-medium uppercase tracking-wide text-fg-muted`,children:`Settings`}),$.map(({id:e,label:n,icon:r})=>(0,q.jsxs)(`button`,{onClick:()=>xe(e),className:_(`flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors`,e===t.id?`bg-elevated text-fg`:`text-fg-secondary hover:bg-elevated hover:text-fg`),children:[(0,q.jsx)(r,{className:`size-4 shrink-0`}),n]},e))]}),(0,q.jsx)(`div`,{className:`overflow-y-auto p-5`,children:t.render()})]})})}export{Ue as SettingsDialog};
|