Skip to content

feat(desktop): split auto-apply preset into workspace creation and new tab settings#1320

Merged
Kitenite merged 10 commits into
mainfrom
kitenite/allow-preset-to-run-on-worktree-created-vs-new-tab
Feb 9, 2026
Merged

feat(desktop): split auto-apply preset into workspace creation and new tab settings#1320
Kitenite merged 10 commits into
mainfrom
kitenite/allow-preset-to-run-on-worktree-created-vs-new-tab

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Feb 8, 2026

Summary

  • Split the single autoApplyDefaultPreset toggle into two independent settings: one for workspace/worktree creation and one for new tabs/panes/splits
  • Users can now disable preset auto-apply on new tabs while keeping it on workspace creation (or vice versa)
  • Explicit preset launches (via hotkey/menu) are never gated by either setting

Changes

Database (packages/local-db)

  • Added apply_preset_on_new_tab boolean column to the settings table

Shared constants (apps/desktop/src/shared/constants.ts)

  • Added DEFAULT_APPLY_PRESET_ON_NEW_TAB = true default

tRPC settings router

  • Added getApplyPresetOnNewTab query and setApplyPresetOnNewTab mutation

Tab store (useTabsWithPresets.ts)

  • Queries the new setting and gates defaultPresetOptions, shouldUseParallelMode, and tab rename behind it
  • openPreset() (explicit user action) remains ungated

Settings UI (TerminalSettings.tsx)

  • Renamed existing toggle: "Apply preset on workspace creation"
  • Added new toggle: "Apply preset on new tab" with optimistic updates

Settings search (settings-search.ts)

  • Added TERMINAL_APPLY_PRESET_ON_NEW_TAB search entry
  • Updated existing entry title to "Apply Preset on Workspace Creation"

Test Plan

  • Toggle "Apply preset on workspace creation" OFF → create a new workspace → no preset should run
  • Toggle "Apply preset on new tab" OFF → open a new tab/pane/split → empty terminal (no preset commands)
  • Toggle both ON → both behaviors work as they do today
  • openPreset() (explicit preset launch via hotkey/menu) always works regardless of settings
  • Settings search finds both toggles by relevant keywords
  • bun run typecheck passes

Summary by CodeRabbit

  • New Features

    • Auto-apply controls for terminal presets (apply on workspace creation or on new tabs)
    • Terminal session management UI for live monitoring, killing sessions, daemon restart, and history clearing
    • Link behavior control to choose how file links open
  • Improvements

    • Redesigned terminal settings into modular, independently rendered sections with improved presets management (quick-add, edit, reorder) and clearer UX
  • API

    • New preset retrieval/apply endpoints integrated for workspace creation and new-tab flows

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds terminal preset auto-apply flags and TRPC endpoints, extends preset schema, refactors TerminalSettings into modular sections, and updates hooks to use new preset selection for workspace creation and new tabs. New UI components manage auto-apply, link behavior, presets CRUD/reorder, and daemon sessions.

Changes

Cohort / File(s) Summary
TRPC Settings Router
apps/desktop/src/lib/trpc/routers/settings/index.ts
Added setPresetAutoApply mutation and getWorkspaceCreationPreset / getNewTabPreset queries to manage and retrieve presets by applyOnWorkspaceCreated / applyOnNewTab.
Schema & Workspace Init
packages/local-db/src/schema/zod.ts, apps/desktop/src/lib/trpc/routers/workspaces/procedures/init.ts
Added optional booleans applyOnWorkspaceCreated and applyOnNewTab to TerminalPreset schema; getDefaultPreset now prefers applyOnWorkspaceCreated, then isDefault.
Terminal Settings Refactor
apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/TerminalSettings.tsx
Replaced monolithic settings UI with SectionList and per-section components; removed in-file preset management and delegated to new components.
New Settings Components
apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/.../{AutoApplyPresetSetting,LinkBehaviorSetting,PresetsSection,SessionsSection}.tsx
Added components: AutoApplyPresetSetting (toggle), LinkBehaviorSetting (select), PresetsSection (templates, CRUD, reorder, auto-apply), SessionsSection (daemon session management and actions).
Preset Row & UI
apps/desktop/src/renderer/.../PresetRow/PresetRow.tsx
Replaced onSetDefault with onToggleAutoApply(presetId, field); added AutoApplyField type; updated icons, tooltips, aria-labels, and DnD init via useEffect.
Presets Hooks & Queries
apps/desktop/src/renderer/react-query/presets/index.ts, apps/desktop/src/renderer/stores/tabs/useTabsWithPresets.ts
Added useSetPresetAutoApply hook and exposed via usePresets; useTabsWithPresets switched to electronTrpc.settings.getNewTabPreset.useQuery and renamed defaultPresetnewTabPreset (affects command/CWD/executionMode logic).
Settings Search Copy
apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts
Capitalization and description tweak for TERMINAL_AUTO_APPLY_PRESET to reference "workspace creation preset".

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as Settings UI
    participant TRPC as TRPC Server
    participant DB as Settings Store

    User->>UI: Toggle auto-apply for workspace creation
    UI->>TRPC: setPresetAutoApply({ id, field: "applyOnWorkspaceCreated" })
    TRPC->>DB: Upsert terminalPresets with updated flag
    DB-->>TRPC: Success
    TRPC-->>UI: { success: true }
    UI->>TRPC: getWorkspaceCreationPreset()
    TRPC->>DB: Query presets (applyOnWorkspaceCreated first)
    DB-->>TRPC: Matching TerminalPreset | null
    TRPC-->>UI: TerminalPreset | null
    UI->>User: Reflect updated auto-apply state
Loading
sequenceDiagram
    participant System as Workspace Creator
    participant Init as Workspace Init
    participant TRPC as TRPC Server
    participant DB as Settings Store
    participant Tab as Tab Creator

    System->>Init: Create new workspace
    Init->>TRPC: getDefaultPreset()
    TRPC->>DB: Query presets (applyOnWorkspaceCreated → isDefault)
    DB-->>TRPC: Matching TerminalPreset | null
    TRPC-->>Init: TerminalPreset | null
    Init->>Tab: Create tab with preset (commands, cwd, exec mode)
    Tab->>User: Terminal created with applied preset
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 I hopped through presets, flags in paw,
Set apply-on-workspace and new-tab law,
Sections sprouted, tidy and neat,
Presets dance, sessions keep the beat—
A carrot for code, a celebratory hop! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: splitting auto-apply preset into separate workspace creation and new tab settings.
Description check ✅ Passed The PR description is comprehensive and well-structured with clear sections covering summary, changes across multiple modules, and a detailed test plan.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kitenite/allow-preset-to-run-on-worktree-created-vs-new-tab

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 8, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Fly.io Electric (Fly.io) View App
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/TerminalSettings.tsx (1)

692-722: ⚠️ Potential issue | 🟡 Minor

Update settings-search entry title to match UI label capitalization.

The settings-search entry for TERMINAL_AUTO_APPLY_PRESET has title "Apply Preset on Workspace Creation" (Title Case) at line 409 of apps/desktop/src/renderer/routes/_authenticated/settings/utils/settings-search/settings-search.ts, but the UI label at line 705 uses "Apply preset on workspace creation" (sentence case). Update the settings-search title to use sentence case for consistency so search results match the visible UI.

🧹 Nitpick comments (1)
apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetRow/PresetRow.tsx (1)

128-131: Consider adding cleanup to disconnect DnD connectors.

When the connector functions (preview, drop, drag) change, the effect re-runs and re-attaches, but the previous connectors aren't explicitly disconnected. Passing null on cleanup is the defensive pattern recommended by react-dnd to avoid stale connector bindings.

🛡️ Suggested cleanup
 	useEffect(() => {
 		preview(drop(rowRef));
 		drag(dragHandleRef);
+		return () => {
+			preview(null);
+			drop(null);
+			drag(null);
+		};
 	}, [preview, drop, drag]);

…w tab settings

Give users independent control over when the default preset is applied:
one toggle for workspace/worktree creation and another for new tabs,
panes, and splits. Explicit preset launches via hotkey/menu are never gated.

Refactors TerminalSettings from a monolithic component into isolated
sub-components (PresetsSection, SessionsSection, individual setting
components), each owning their own queries. This prevents query
resolutions in one section from re-rendering others, fixing the
"Maximum update depth exceeded" error caused by Radix UI ref
composition cascading through react-dnd connectors on re-renders.
… global toggle

Replace the global "apply preset on new tab" setting with per-preset
flags (applyOnWorkspaceCreated, applyOnNewTab) that give users granular
control over when each preset auto-applies. Each preset now shows two
toggle icons in the row: folder+ for workspace creation and document+
for new tab.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetRow/PresetRow.tsx`:
- Around line 135-140: The toggle UI fails to turn off legacy presets because
setPresetAutoApply doesn't clear isDefault when explicitly setting
applyOnWorkspaceCreated/applyOnNewTab; update the mutation logic in
setPresetAutoApply to map over presets and for the preset being changed set
[input.field] = input.id === p.id ? true : undefined AND clear isDefault for
that preset (isDefault = input.id === p.id ? undefined : p.isDefault), so legacy
presets with only isDefault can be transitioned off; verify related UI booleans
isWorkspaceCreation and isNewTab still derive from
preset.applyOnWorkspaceCreated/applyOnNewTab || (!preset.applyOnNewTab &&
preset.isDefault) etc.
🧹 Nitpick comments (5)
apps/desktop/src/renderer/stores/tabs/useTabsWithPresets.ts (1)

167-175: Aliased return name may confuse consumers.

defaultPreset: newTabPreset at line 174 aliases the new tab preset as defaultPreset for backward compatibility. This works, but the semantics have changed — consumers may assume defaultPreset is the isDefault preset. Consider renaming to newTabPreset in consumers or adding a brief inline comment explaining the alias.

apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/TerminalSettings.tsx (1)

21-35: SectionList key extraction relies on unsafe cast.

Line 27 casts every child to React.ReactElement to access .key, but after filter(Boolean), children could still include non-element ReactNode types (strings, numbers). In practice, callers only pass JSX elements so this works, but the cast is fragile.

A safer approach would use React.isValidElement:

♻️ Suggested improvement
-				<div
-					key={(child as React.ReactElement).key ?? i}
-					className={i > 0 ? "pt-6 border-t mt-6" : ""}
-				>
+				<div
+					key={React.isValidElement(child) ? child.key ?? i : i}
+					className={i > 0 ? "pt-6 border-t mt-6" : ""}
+				>
apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetsSection.tsx (3)

247-258: Deleted row remains visible until server round-trip completes.

handleDeleteRow fires deletePreset.mutate but returns currentLocal unchanged, so the row stays rendered until serverPresets updates via invalidation and the sync useEffect runs. This creates a brief flash where the user could click delete again (double-delete).

Consider optimistically removing the row from local state immediately:

♻️ Suggested fix
 const handleDeleteRow = useCallback(
   (rowIndex: number) => {
     setLocalPresets((currentLocal) => {
       const preset = currentLocal[rowIndex];
       if (preset) {
         deletePreset.mutate({ id: preset.id });
       }
-      return currentLocal;
+      return currentLocal.filter((_, i) => i !== rowIndex);
     });
   },
   [deletePreset],
 );

143-162: State setter used as a read-only accessor is unconventional.

handleCellBlur, handleCommandsBlur, and handleDeleteRow all call setLocalPresets(currentLocal => { ...; return currentLocal; }) purely to read currentLocal without modifying state. While React does bail out on same-reference returns, this pattern is non-obvious and may confuse future maintainers.

A cleaner alternative would be to use a ref (e.g., localPresetsRef) to access the latest local state in these callbacks, or restructure so the blur handler receives the preset ID/data directly.


260-268: No optimistic update for auto-apply toggle.

handleToggleAutoApply fires the mutation but doesn't optimistically update localPresets. The UI will appear unresponsive until the server round-trip completes and the useEffect syncs server state. Given that the PresetRow reads preset.applyOnWorkspaceCreated/applyOnNewTab directly, the toggle button won't reflect the change until the next render cycle after invalidation.

Consider optimistically toggling the field in localPresets alongside the mutation for a snappier UX, consistent with how handleExecutionModeChange (line 210) already does local + server updates together.

Comment on lines +135 to +140
const isWorkspaceCreation =
preset.applyOnWorkspaceCreated ||
(!preset.applyOnNewTab && preset.isDefault);
const isNewTab =
preset.applyOnNewTab ||
(!preset.applyOnWorkspaceCreated && preset.isDefault);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for setPresetAutoApply function definition
rg -n 'setPresetAutoApply|setPreset.*AutoApply' --type=ts --type=tsx -B2 -A30 | head -100

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Search for setPresetAutoApply function definition without file type
rg -n 'setPresetAutoApply|setPreset.*AutoApply' -B2 -A30 | head -150

Repository: superset-sh/superset

Length of output: 17150


🏁 Script executed:

#!/bin/bash
# Get the complete setPresetAutoApply mutation implementation
rg -n 'setPresetAutoApply: publicProcedure' -A50 'apps/desktop/src/lib/trpc/routers/settings/index.ts'

Repository: superset-sh/superset

Length of output: 1591


🏁 Script executed:

#!/bin/bash
# Check PresetRow toggle logic around lines 186-243
sed -n '186,243p' 'apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetRow/PresetRow.tsx'

Repository: superset-sh/superset

Length of output: 1897


🏁 Script executed:

#!/bin/bash
# Check if there are any places where isDefault is cleared or modified
rg -n 'isDefault' 'apps/desktop/src/lib/trpc/routers/settings/index.ts' -B3 -A3

Repository: superset-sh/superset

Length of output: 902


🏁 Script executed:

#!/bin/bash
# Verify the complete flow: check what PresetRow sends when toggling each scenario
sed -n '135,243p' 'apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetRow/PresetRow.tsx' | grep -n 'onToggleAutoApply\|isWorkspaceCreation\|isNewTab'

Repository: superset-sh/superset

Length of output: 709


Server mutation doesn't clear isDefault when explicitly setting applyOn* fields, causing toggle-off to silently fail for legacy presets.

When a preset has only isDefault: true (both applyOnWorkspaceCreated and applyOnNewTab undefined), both buttons render active. Clicking an active button sends id: null to toggle off, but the server mutation treats null like any other non-matching ID and sets the field to undefined—which is already its current state. This leaves the preset unchanged, so both buttons remain active and cannot be toggled off.

To align with the migration strategy, setPresetAutoApply should clear isDefault when explicitly enabling one of the applyOn* fields (similar to how setDefaultPreset mutually clears other presets' isDefault):

const updatedPresets = presets.map((p) => ({
  ...p,
  [input.field]: input.id === p.id ? true : undefined,
  isDefault: input.id === p.id ? undefined : p.isDefault, // Clear isDefault when explicitly setting
}));

This ensures users can transition legacy presets out of the isDefault-only state via the toggle UI.

🤖 Prompt for AI Agents
In
`@apps/desktop/src/renderer/routes/_authenticated/settings/terminal/components/TerminalSettings/components/PresetRow/PresetRow.tsx`
around lines 135 - 140, The toggle UI fails to turn off legacy presets because
setPresetAutoApply doesn't clear isDefault when explicitly setting
applyOnWorkspaceCreated/applyOnNewTab; update the mutation logic in
setPresetAutoApply to map over presets and for the preset being changed set
[input.field] = input.id === p.id ? true : undefined AND clear isDefault for
that preset (isDefault = input.id === p.id ? undefined : p.isDefault), so legacy
presets with only isDefault can be transitioned off; verify related UI booleans
isWorkspaceCreation and isNewTab still derive from
preset.applyOnWorkspaceCreated/applyOnNewTab || (!preset.applyOnNewTab &&
preset.isDefault) etc.

Replace icon buttons with clickable WS/Tab badges that toggle
independently. Multiple presets can now be tagged for the same trigger -
all tagged presets fire when a workspace is created or a new tab opens.

- setPresetAutoApply toggles per-preset (no longer mutually exclusive)
- getWorkspaceCreationPresets/getNewTabPresets return arrays
- WorkspaceInitEffects applies all workspace-tagged presets
- useTabsWithPresets opens a tab for each new-tab-tagged preset
@Kitenite Kitenite merged commit 06055a8 into main Feb 9, 2026
13 checks passed
@Kitenite Kitenite deleted the kitenite/allow-preset-to-run-on-worktree-created-vs-new-tab branch February 9, 2026 05:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant