{# One project row. Rendered by the dashboard grid loop AND by GET /api/projects/{name}/row for reactive insertion (no full-page reload). `idx` is the row's position for the search/cap visibility gate (0 from the fragment route, so a freshly created project is always shown). The `:style` below reorders rows VISUALLY for a non-name sort via CSS `order` — it does NOT change DOM/tab order (focus + screen-reader order stay A–Z), the deliberate trade for an opt-in cosmetic sort. Don't turn this into a DOM reorder (the anti-pattern the Active zone avoids). WCAG 2.4.3 note: visual order diverges from DOM/tab order only on opt-in non-default sorts; all interactive controls are self-contained within each row so keyboard navigation is coherent regardless of visual position. #}
{# DES-07: responsive name cap — clamp scales the truncation width with the viewport (narrow phone → ~10rem, wide desktop → 28rem) instead of a fixed 16rem; min-width:0 lets text-truncate shrink inside the flex row. #} {{ p.name }} {% if p.is_git_repo %}Git{% endif %} {% if p.has_claude_md %}CLAUDE.md{% endif %} {# FE-578: surface a launch precondition INSIDE the popover; Run stays ENABLED (disabling it would hide the spawn picker / trust confirm that resolve it). On open we coerce a non-git+worktree default to a valid mode; the note below the Spawn picker explains it. #} {# Dismissing the popover (outside-click / Escape) also clears any pending trust/bypass gate (#686) so a hidden gate never lingers — reopening starts a fresh launch. mopen is reset too so More options collapses on reopen. #}
How do you want to drive it?

Connectable from claude.ai / Desktop — converse with it, and Stop it here when done.

{# A

Required — an unregistered Background session can't be given a prompt later. Check "also register on claude.ai" to start without one.

{# Option text + effect tooltip both come from the canonical permission_labels map (#685) — never a hand-maintained copy. bypassPermissions is offered only where the per-project ceiling allows it and only for the Desktop/bridge launch (the typed-confirm footgun guard); the "(Desktop only)" caveat is a contextual hint appended here, not baked into the canonical label. #} {# Plain-language effect of the SELECTED mode, inline (not just title=) so it's visible on touch — the riskiest per-launch choice shouldn't hide in a tooltip. #}

{# "More options" — a single disclosure (#686) holding the ADVANCED choices for the Desktop/bridge launch: the Mode selector (Server Mode / Interactive Session) AND Spawn. Mode is an advanced choice, tucked away by default; the safe Desktop default works with this closed. Only the Desktop launch exposes these (browser/background ignore spawn + resume mode). #}
{% if pty_supported %} {# Mode (Server Mode / Interactive Session) — the advanced launch-shape choice, tucked inside More options. Drives whether Spawn applies (standard) or the single-session note shows (pty). #}
{% endif %} {# Custom bridge/session display name (#780) — Server Mode (standard) only: it becomes `claude remote-control --name`. The Interactive Session (pty) flag form has no equivalent flag, so this is hidden there rather than silently ignored (avoids a name typed here from looking like it applies when Mode is flipped to pty). Blank keeps today's default (the project name); the server validates length + control characters before any spawn side effect. #}
{#

Leave blank to use the project name ({{ p.name }}).

{# Sandbox toggle (#780) removed for 1.0 (#1037): `--sandbox` never propagated to the server-mode session worker, so the control silently did nothing. It returns behind dependency preflight + platform gating in #1046. #} {# Spawn applies to BOTH modes (#779): standard maps to `claude remote-control --spawn`; an Interactive Session honors same-dir/worktree (worktree = `claude --worktree`, its own isolated git worktree that survives Resume). Only `session` is standard-only — it stays visible but disabled in pty mode (coercePtySpawn resets it). #}
{# #578: surface (don't disable) — a non-git dir can't worktree, so note it here and coerceSpawnMode() falls back to same-dir on open. x-text (auto-escaped, never x-html); no d-* class on this x-show el. #}

{% if pty_supported %} {# Resume picker (#303) — pty/Interactive Session only: fork a picked past conversation into the NEW session (`--resume --fork-session`; the original conversation is never clobbered). Options come from the transcripts API (non-live, non-empty), loaded on popover open; labels are server-redacted first prompts rendered via x-text (never x-html). "" = fresh conversation (the default, re-coerced on every popover open). #}

Forking a previous conversation resumes it in a new session — the original stays untouched.

{# Keep d-flex OFF this x-show element — `.d-flex{display:flex!important}` overrides x-show's inline display:none and would pin this hint visible even in standard mode (same bug as the live-tail banners). Flex goes on the inner wrapper. #}

Runs as one interactive session — several can run per project.

{% if p.is_git_repo %} {# No-worktree collision warning (#779): warn, never block — the launch proceeds; the server echoes the same advisory (warnings[], #778). No d-* class on the x-show element (Tabler !important, cf. #538). #}

Sessions sharing the project directory can make conflicting edits — choose Spawn: worktree to isolate this one.

{% endif %}
{% endif %}
{# Trust-on-start + bypassPermissions gates fold INSIDE the popover (#686) — no separate below-the-row alerts. Both are hidden until the Run button routes a Desktop launch into start(), which opens the matching gate; resolving or cancelling either one dismisses the popover (via the launch-pop-close event). #} {# When a gate opens INSIDE the popover the Run button hides, so move focus into the gate's first control (WCAG 2.4.3 / 4.1.3) — else focus is orphaned on and a keyboard/SR user gets no signal a confirm now blocks launch. x-effect re-runs when confirmTrust[name] flips; $nextTick waits for x-show. #}

Trust the files in {{ p.name }}? Launching a session here lets it run tools and execute any code in CLAUDE.md / .claude in this directory.

bypassPermissions lets sessions in {{ p.name }} run tools without asking. Type the project name to confirm.

{# #851: interactive-launch MCP-approval SOFT-BLOCK. An Interactive Session with unapproved committed .mcp.json servers deterministically hangs at claude's enable prompt (the read-only terminal can't answer it), so block by default with an error-styled confirm rather than the passive WARN. Two actions: resolve (a real BUTTON, not a link — #851 feedback) or the explicit "Launch anyway" override (guards the #850 false-positive case). #}

This Interactive Session will hang at claude's MCP approval prompt and won't connect — the read-only terminal can't answer it. Resolve the unapproved server(s) first.

{# Primary Run action. Hidden while a trust/bypass/MCP gate is showing INSIDE the popover so the gate's own confirm button is the single next step (no competing primary). On a clean launch (no gate pending) launchRun() returns true and the popover closes; when it opens a gate it returns false and the popover stays. #} {# Semantic end-of-popover sentinel: a stable structural boundary for tests (so they don't couple to template whitespace/nesting). Renders nothing. #}
Readiness checks for {{ p.name }} before launch
{# The trust-on-start + bypassPermissions confirm gates moved INSIDE the launch popover (#686) — they no longer render as separate below-the-row alerts. The `data-test="inline-error"` hook is load-bearing: three `.alert-danger.mb-0` blocks now share this row (the in-popover bypass-confirm, this transient-action error, and the spawn-failure detail below), so a bare `.alert-danger.mb-0` selector is ambiguous — tests must target this stable hook. #}