@puddle-code/cli 0.0.2 → 0.0.4
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 +5 -36
- package/README.md +34 -0
- package/dist/index.js +99 -42
- package/dist/public/assets/Dashboard-CQ4RItpD.js +1 -0
- package/dist/public/assets/{EditorTabClose-Dal7HKgg.js → EditorTabClose-BEGz1662.js} +1 -1
- package/dist/public/assets/{ModelRefcount-B12_Xdv2.js → ModelRefcount-BAuJllX7.js} +1 -1
- package/dist/public/assets/{PaneEditorBody-D1npLlye.js → PaneEditorBody-LFOhdgSr.js} +1 -1
- package/dist/public/assets/SettingsDialog-C90fZfP_.js +1 -0
- package/dist/public/assets/{Terminal-D03wYX9q.js → Terminal-Ds2WkC4V.js} +2 -2
- package/dist/public/assets/Workspace-Czvc4bTn.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-RgxVkmfL.js} +2 -2
- package/dist/public/assets/context-menu-x5cflO6b.js +1 -0
- package/dist/public/assets/{cssMode-D1iXtOo8.js → cssMode-B3OYxT7p.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-D3wKh961.js} +1 -1
- package/dist/public/assets/index-B-ZBqMzE.js +10 -0
- package/dist/public/assets/index-y2Zzy0lJ.css +2 -0
- package/dist/public/assets/{jsonMode-CpQhQE0R.js → jsonMode-BRqU2DEx.js} +1 -1
- package/dist/public/assets/ssh-mode-CyuzrKTN.js +1 -0
- package/dist/public/assets/switch-DlRshCm6.js +1 -0
- package/dist/public/assets/{tsMode-ET2kHbb0.js → tsMode-M7R2IO25.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,14 @@ Past releases: see docs/changelogs/.
|
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
|
10
|
-
## [0.0.
|
|
11
|
-
|
|
12
|
-
### Added
|
|
13
|
-
|
|
14
|
-
- The npm page for **`@puddle-code/cli`** now renders this repository's `README.md` and links back to the repo — the package gains `homepage`, `repository`, and `bugs` fields, and the build copies the root README into the package at publish time (npm only renders a README that sits in the package directory). The README gains an npm version badge and a licence badge.
|
|
15
|
-
- Profiles can now be **renamed** in Settings → Profile — the Name field is editable (previously read-only). The name is a display label that keys nothing on disk (directories are id-keyed), so a rename touches no files; it stays letters/digits/dot/underscore/hyphen and must be unique (a clash is rejected). `PATCH /api/profiles/:id` now accepts an optional `name` alongside `branch_prefix` (additive; `PROTOCOL_VERSION` minor bump) — SPEC §6.
|
|
16
|
-
- The right sidebar now shows **every project's sessions**, grouped by project (a header per project when expanded, a divider between groups on the collapsed rail), with the project order inheriting the homescreen's — clicking a session jumps into its own project. A per-browser toggle (Settings → Appearance, on by default) reverts to the current-project-only view. The list scrolls without a visible scrollbar so a long list still works while the new-terminal/new-session controls stay fixed, and a collapsed dot's tooltip now shows the session name over its branch. New query `GET /api/sessions?profile=…` (SPEC §12).
|
|
17
|
-
- Homescreen projects can now be **renamed**, **archived**, and **reordered**. Hover a card (or right-click it) for rename/archive; archiving is a reversible hide — the project drops into a collapsed "Archived" disclosure at the bottom, keeping every session and worktree, and reopening restores it. Drag cards to reorder (newest first until dragged); the order persists per profile (`profileSettings.projectOrder`) and the cross-project session sidebar inherits it. New endpoint `PATCH /api/projects/:id {name?, archived?}` + `projects.archived` column (migration 011; additive `PROTOCOL_VERSION` minor bump) — SPEC §11.
|
|
18
|
-
- The workspace centre is now a **free-form tiling area** (SPEC §8): editor tabs (files, diffs, media) and agent terminals share one space — drag any tab onto a pane to reorder it, or onto a pane's edge to split it into an arbitrary grid of resizable panes (VSCode editor-groups style). Terminals stay mounted and their DOM is adopted into whichever pane shows them, so moving one never drops its PTY/scrollback; the same file may open in two panes sharing one editor model. The layout persists as a recursive tree in `ui_state.layout_tree` (legacy layouts migrate automatically; additive `PROTOCOL_VERSION` minor bump).
|
|
19
|
-
- Cmd/Ctrl+click now opens more terminal paths: extensionless directories and files with path structure (`.worktrees/hil-demos`, `~/.claude/prompts/up-fix`, `/home/bios/waddle`), not just paths ending in a recognised extension. A leading `@` (Claude Code's file-ref syntax) is stripped. The daemon's `/resolve` check still gates underlining (SPEC §7).
|
|
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).
|
|
10
|
+
## [0.0.4] — 2026-07-16
|
|
28
11
|
|
|
29
12
|
### Changed
|
|
30
13
|
|
|
31
|
-
- **
|
|
32
|
-
- GitHub release notes are now taken from the release's changelog (`docs/changelogs/CHANGELOG-v<version>.md` via `scripts/extract-changelog.mjs`) instead of being auto-derived from commits, and `@puddle-code/cli` now ships that same changelog as its `CHANGELOG.md` — one changelog drives the release notes and the npm package.
|
|
33
|
-
- The Worktrees navigator's status badges are terser, matching VSCode: a dirty worktree shows a yellow **M** (the explorer's modified-decoration colour) instead of "uncommitted", and a worktree with live sessions shows a green count (e.g. **3**) instead of "3 running". Both keep the full text on hover.
|
|
34
|
-
- The top bar is slimmer — a shorter header with a smaller mark sized inline with the title — reclaiming vertical space for the workspace.
|
|
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.
|
|
14
|
+
- The **resume banner** (interrupted/exited sessions) and the **ports strip** now sit at the **bottom** of the editor view instead of the top.
|
|
38
15
|
|
|
39
16
|
### Fixed
|
|
40
17
|
|
|
41
|
-
-
|
|
42
|
-
- Clicking a
|
|
43
|
-
-
|
|
44
|
-
- A remote `puddle connect` no longer risks making a later local `puddle start` report a spurious port conflict. Its UI server was told to avoid the *remote* daemon's port when auto-picking, but the port a local `start` actually probes is *this* machine's daemon port — the two coincide only when the remote daemon runs on the default 7434. A connect cockpit's proxy could therefore land on the local daemon's port and be mistaken for it. The avoided port is now discovered from this machine the same way `start` finds its own daemon, so they can never collide (SPEC §10).
|
|
45
|
-
- "Skip permission prompts" now actually skips them for Claude Code. The `--dangerously-skip-permissions` flag was delivered correctly but Claude 2.1.x silently downgraded it to normal prompts because its bypass-mode disclaimer had never been accepted in puddle's managed config dir (that acceptance is only offered interactively, which a non-interactive PTY can't do). Opening the profile skip gate — the existing typed-confirmation dialogue in Settings → Sessions — now records that acceptance (`bypassPermissionsModeAccepted`) for the profile's skip-capable accounts, as does opting an individual account in afterwards, so the flag takes effect. Verified against Claude Code 2.1.210.
|
|
46
|
-
- Renaming a session now sticks. The `.puddle/session-title` marker sync used to re-apply the agent's on-disk title over a UI rename on the next `.puddle` change; that title mechanism is gone, so a rename persists.
|
|
47
|
-
- Proxied apps with absolute asset paths (any Vite/CRA-style build) no longer render a blank page through `/proxy/<sid>/<port>/`. Their `/assets/…` and `fetch('/api/…')` requests used to escape the proxy prefix and receive puddle's own `index.html` (the "Failed to load module script … MIME type of text/html" console error); the cockpit origin now 307-redirects any stray request whose `Referer` is a proxied page back under that page's prefix, and the static handler 404s missing assets instead of SPA-falling-back to HTML. WS handshakes and `no-referrer` apps still need the per-port `ssh -L` fallback (SPEC §9, §15.5 resolved).
|
|
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.
|
|
18
|
+
- The **Settings** dialog now opens on click. The open state was tied to `window.location.hash`, and setting the fragment inside the click handler stopped the open from triggering its own re-render (any later, unrelated re-render then revealed the dialog). Opening is now plain React state; the `#settings/<section>` deep link is kept, but written from an effect after the render (via `replaceState`) so it never sits on the open's critical path — deep links and back/forward still work.
|
|
19
|
+
- Clicking a session tab from **another project** in the editor now switches to that session's own project (the URL and left file tree follow it), instead of keeping the current project and showing an empty tree.
|
|
20
|
+
- A stored profile id that no longer matches a real profile (e.g. after a daemon change) now falls back to the **profile picker** instead of rendering a homepage bound to a nameless, non-existent profile.
|
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.4";
|
|
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-x5cflO6b.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-B-ZBqMzE.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-RgxVkmfL.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-RgxVkmfL.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-B-ZBqMzE.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-RgxVkmfL.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-DlRshCm6.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-B-ZBqMzE.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};
|