Add config.json onboarding flow for workspace setup scripts#224
Add config.json onboarding flow for workspace setup scripts#224saddlepaddle merged 3 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughThis PR introduces a configuration management system for projects with a new TRPC config router, SetupConfigModal UI component, and OpenInButton component for opening project paths. It migrates configuration references from Changes
Sequence DiagramsequenceDiagram
participant User
participant UI as React UI
participant TRPC
participant DB as Database
participant FS as File System
User->>UI: Create workspace
UI->>TRPC: Create workspace (mutation)
TRPC->>DB: Save workspace & project
alt Has setup commands
TRPC-->>UI: Return with projectId
UI->>User: Show success
else No setup commands
TRPC-->>UI: Return with projectId
UI->>User: Show toast "No setup script configured"
User->>UI: Click "Configure"
UI->>TRPC: shouldShowConfigToast
TRPC->>DB: Check project & dismissal state
TRPC-->>UI: Return true (should show)
UI->>UI: Open SetupConfigModal
UI->>TRPC: getConfigFilePath
TRPC->>FS: Check .superset/config.json exists
FS-->>TRPC: Not found
TRPC->>FS: Create .superset/ & config.json with template
FS-->>TRPC: Success
TRPC-->>UI: Return path
UI->>User: Display modal with config template
User->>UI: Click "Done" or "Dismiss"
UI->>TRPC: dismissConfigToast (mutation)
TRPC->>DB: Mark configToastDismissed: true
TRPC-->>UI: Success
UI->>User: Close modal
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (2)
🧰 Additional context used📓 Path-based instructions (4)apps/desktop/**/*.{ts,tsx,js,jsx}📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
Files:
apps/desktop/**/*.{ts,tsx}📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
Files:
**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
apps/desktop/src/renderer/**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (2)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (6)
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts (1)
5-7: Config path rename looks correct; consider updating log wordingThe switch to
.superset/config.jsonmatches the new config flow and the tests. Optionally, you might update the error message to say"config.json"or "workspace config" instead of "setup config" to avoid confusion when debugging.apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts (1)
13-15: Path change is correct; consider reusing the shared loaderThe
.superset/config.jsonpath matches the setup side and tests. To avoid future drift, you could import and reuseloadSetupConfigfromsetup.tshere instead of maintaining a second copy of the same logic, and optionally update the log message to refer to "config.json" or "workspace config" rather than "setup config".apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (1)
28-53: Minor cleanup:handleCreateWorkspacedoesn’t need to beasync
handleCreateWorkspacedoesn’tawaitanything; it just callstoast.promisewith the mutation promise. You can simplify by droppingasync(and the implicit unusedPromise<void>):- const handleCreateWorkspace = async (projectId: string) => { + const handleCreateWorkspace = (projectId: string) => {Behavior is unchanged and avoids an unnecessary async wrapper.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsx (1)
8-10: Windows path separator handling.The path splitting uses forward slashes only. If
worktreePathcan contain Windows-style backslashes, the folder name extraction may fail. Consider using a cross-platform approach similar tonormalizeSeparatorsin StartView:const folderName = worktreePath - ? worktreePath.split("/").filter(Boolean).pop() || worktreePath + ? worktreePath.replace(/\\/g, "/").split("/").filter(Boolean).pop() || worktreePath : null;apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsx (1)
29-37: Queries fire with empty string when projectId is null.Although
enabled: !!projectIdprevents execution when projectId is falsy, the query inputprojectId ?? ""passes an empty string. This is fine since enabled guards it, but for clarity you could use a non-null assertion or type guard pattern.apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx (1)
39-40: Replace hardcoded array index with explicit fallback.Using
APP_OPTIONS[1]as a fallback is fragile—if the array order changes, the fallback silently breaks.Replace with an explicit fallback:
const getAppOption = (id: ExternalApp) => - APP_OPTIONS.find((app) => app.id === id) ?? APP_OPTIONS[1]; + APP_OPTIONS.find((app) => app.id === id) ?? APP_OPTIONS.find((app) => app.id === "cursor")!;Or define a constant:
const DEFAULT_APP: ExternalApp = "cursor"; const getAppOption = (id: ExternalApp) => APP_OPTIONS.find((app) => app.id === id) ?? APP_OPTIONS.find((app) => app.id === DEFAULT_APP)!;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (7)
apps/desktop/src/renderer/assets/app-icons/cursor.svgis excluded by!**/*.svgapps/desktop/src/renderer/assets/app-icons/finder.pngis excluded by!**/*.pngapps/desktop/src/renderer/assets/app-icons/iterm.pngis excluded by!**/*.pngapps/desktop/src/renderer/assets/app-icons/terminal.pngis excluded by!**/*.pngapps/desktop/src/renderer/assets/app-icons/vscode.svgis excluded by!**/*.svgapps/desktop/src/renderer/assets/app-icons/warp.pngis excluded by!**/*.pngapps/desktop/src/renderer/assets/app-icons/xcode.svgis excluded by!**/*.svg
📒 Files selected for processing (23)
.env.example(1 hunks).gitignore(1 hunks)README.md(1 hunks)apps/desktop/src/lib/trpc/routers/config/config.ts(1 hunks)apps/desktop/src/lib/trpc/routers/config/index.ts(1 hunks)apps/desktop/src/lib/trpc/routers/index.ts(2 hunks)apps/desktop/src/lib/trpc/routers/projects/projects.ts(1 hunks)apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts(3 hunks)apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts(1 hunks)apps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.ts(1 hunks)apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts(1 hunks)apps/desktop/src/main/lib/db/schemas.ts(1 hunks)apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx(1 hunks)apps/desktop/src/renderer/components/OpenInButton/index.ts(1 hunks)apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsx(1 hunks)apps/desktop/src/renderer/components/SetupConfigModal/index.ts(1 hunks)apps/desktop/src/renderer/screens/main/components/StartView/index.tsx(3 hunks)apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx(2 hunks)apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsx(1 hunks)apps/desktop/src/renderer/screens/main/index.tsx(2 hunks)apps/desktop/src/renderer/stores/config-modal.ts(1 hunks)apps/desktop/src/shared/constants.ts(1 hunks)apps/website/src/app/scripts/page.tsx(1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
apps/desktop/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
For Electron interprocess communication, ALWAYS use tRPC as defined in
src/lib/trpc
Files:
apps/desktop/src/renderer/components/OpenInButton/index.tsapps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/lib/trpc/routers/workspaces/workspaces.tsapps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsxapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.tsapps/desktop/src/renderer/components/SetupConfigModal/index.tsapps/desktop/src/lib/trpc/routers/config/index.tsapps/desktop/src/renderer/stores/config-modal.tsapps/desktop/src/shared/constants.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.tsapps/desktop/src/renderer/screens/main/components/StartView/index.tsxapps/desktop/src/main/lib/db/schemas.tsapps/desktop/src/lib/trpc/routers/config/config.tsapps/desktop/src/renderer/screens/main/index.tsxapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsxapps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
apps/desktop/**/*.{ts,tsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.{ts,tsx}: Please use alias as defined intsconfig.jsonwhen possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary
Files:
apps/desktop/src/renderer/components/OpenInButton/index.tsapps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/lib/trpc/routers/workspaces/workspaces.tsapps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsxapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.tsapps/desktop/src/renderer/components/SetupConfigModal/index.tsapps/desktop/src/lib/trpc/routers/config/index.tsapps/desktop/src/renderer/stores/config-modal.tsapps/desktop/src/shared/constants.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.tsapps/desktop/src/renderer/screens/main/components/StartView/index.tsxapps/desktop/src/main/lib/db/schemas.tsapps/desktop/src/lib/trpc/routers/config/config.tsapps/desktop/src/renderer/screens/main/index.tsxapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsxapps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Avoid usinganytype - use explicit types instead for type safety
Use camelCase for variable and function names following existing codebase patterns
Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Follow existing patterns and match the codebase style when writing new code
Files:
apps/desktop/src/renderer/components/OpenInButton/index.tsapps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/lib/trpc/routers/workspaces/workspaces.tsapps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsxapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.tsapps/desktop/src/renderer/components/SetupConfigModal/index.tsapps/website/src/app/scripts/page.tsxapps/desktop/src/lib/trpc/routers/config/index.tsapps/desktop/src/renderer/stores/config-modal.tsapps/desktop/src/shared/constants.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.tsapps/desktop/src/renderer/screens/main/components/StartView/index.tsxapps/desktop/src/main/lib/db/schemas.tsapps/desktop/src/lib/trpc/routers/config/config.tsapps/desktop/src/renderer/screens/main/index.tsxapps/desktop/src/lib/trpc/routers/index.tsapps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsxapps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment only
Files:
apps/desktop/src/renderer/components/OpenInButton/index.tsapps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsxapps/desktop/src/renderer/components/SetupConfigModal/index.tsapps/desktop/src/renderer/stores/config-modal.tsapps/desktop/src/renderer/screens/main/components/StartView/index.tsxapps/desktop/src/renderer/screens/main/index.tsxapps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsxapps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
apps/desktop/src/lib/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Never import Node.js modules in shared code like
src/lib/electron-router-dom.ts- this code runs in both main and renderer processes
Files:
apps/desktop/src/lib/trpc/routers/projects/projects.tsapps/desktop/src/lib/trpc/routers/workspaces/workspaces.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/teardown.tsapps/desktop/src/lib/trpc/routers/config/index.tsapps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.tsapps/desktop/src/lib/trpc/routers/config/config.tsapps/desktop/src/lib/trpc/routers/index.ts
**/components/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
**/components/**/*.tsx: Create one folder per component with structure:ComponentName/ComponentName.tsx+index.tsfor barrel export
Co-locate tests next to the component file they test (e.g.,ComponentName.test.tsx)
Co-locate dependencies (utils, hooks, constants, config, stories) next to the file using them
Use nestedcomponents/subdirectory within a parent component only if a sub-component is used 2+ times within that parent; otherwise keep it in the parent'scomponents/folder
One component per file - avoid multi-component files
Files:
apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsxapps/desktop/src/renderer/screens/main/components/StartView/index.tsxapps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsxapps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
apps/desktop/src/renderer/**/*.tsx
📄 CodeRabbit inference engine (AGENTS.md)
Call IPC methods using
window.ipcRenderer.invoke()with object parameters - TypeScript will infer the exact response type automatically
Files:
apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsxapps/desktop/src/renderer/screens/main/components/StartView/index.tsxapps/desktop/src/renderer/screens/main/index.tsxapps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsxapps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsxapps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
apps/desktop/**/*.test.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)
apps/desktop/**/*.test.{ts,tsx,js,jsx}: Tests should have one assert per test
Tests should be readable
Tests should be fast
Tests should be independent
Tests should be repeatable
Files:
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts
apps/desktop/src/main/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Node.js modules (fs, path, os, net, etc.) can be used in main process code only
Files:
apps/desktop/src/main/lib/db/schemas.ts
🧠 Learnings (7)
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary
Applied to files:
apps/desktop/src/renderer/stores/config-modal.ts
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.test.{ts,tsx,js,jsx} : Tests should be repeatable
Applied to files:
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.test.{ts,tsx,js,jsx} : Tests should be independent
Applied to files:
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.test.{ts,tsx,js,jsx} : Tests should be readable
Applied to files:
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to **/*.{ts,tsx} : Keep diffs minimal with targeted edits only - avoid unnecessary changes when making modifications
Applied to files:
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts
📚 Learning: 2025-11-28T01:03:47.963Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-28T01:03:47.963Z
Learning: Applies to apps/desktop/src/lib/**/*.ts : Never import Node.js modules in shared code like `src/lib/electron-router-dom.ts` - this code runs in both main and renderer processes
Applied to files:
apps/desktop/src/lib/trpc/routers/index.ts
📚 Learning: 2025-11-24T21:33:13.267Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-11-24T21:33:13.267Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Please use alias as defined in `tsconfig.json` when possible
Applied to files:
apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx
🧬 Code graph analysis (8)
apps/desktop/src/lib/trpc/routers/projects/projects.ts (2)
apps/desktop/src/main/lib/db/schemas.ts (1)
Project(1-10)apps/desktop/src/main/lib/db/index.ts (1)
db(18-25)
apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsx (5)
apps/desktop/src/renderer/stores/config-modal.ts (3)
useConfigModalOpen(30-31)useConfigModalProjectId(32-33)useCloseConfigModal(36-37)apps/desktop/src/shared/constants.ts (1)
WEBSITE_URL(27-27)packages/ui/src/components/dialog.tsx (5)
Dialog(132-132)DialogContent(134-134)DialogHeader(137-137)DialogTitle(140-140)DialogDescription(135-135)apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx (1)
OpenInButton(50-149)packages/ui/src/components/button.tsx (1)
Button(61-61)
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts (1)
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.ts (1)
loadSetupConfig(5-27)
apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (2)
apps/desktop/src/renderer/stores/config-modal.ts (1)
useOpenConfigModal(34-35)packages/ui/src/components/sonner.tsx (1)
toast(30-30)
apps/desktop/src/lib/trpc/routers/config/config.ts (2)
apps/desktop/src/lib/trpc/routers/config/index.ts (2)
createConfigRouter(2-2)ConfigRouter(1-1)apps/desktop/src/lib/trpc/index.ts (1)
router(15-15)
apps/desktop/src/renderer/screens/main/index.tsx (2)
apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsx (1)
SetupConfigModal(24-90)apps/desktop/src/renderer/components/SetupConfigModal/index.ts (1)
SetupConfigModal(1-1)
apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsx (1)
apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx (1)
OpenInButton(50-149)
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx (2)
apps/desktop/src/renderer/stores/config-modal.ts (1)
useOpenConfigModal(34-35)packages/ui/src/components/sonner.tsx (1)
toast(30-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build
🔇 Additional comments (30)
.env.example (1)
3-3: LGTM!The WEBSITE_URL environment variable is appropriately added for local development and aligns with the new documentation page feature.
.gitignore (1)
58-58: LGTM!The gitignore exception correctly updates to track
config.jsoninstead ofsetup.json, aligning with the naming migration throughout the PR.apps/desktop/src/main/lib/db/schemas.ts (1)
9-9: LGTM!The optional
configToastDismissedfield is well-named and appropriately typed for tracking per-project toast dismissal state.README.md (1)
53-53: LGTM!The documentation correctly updates to reference
.superset/config.jsonand fixes the path format, aligning with the naming migration throughout the PR.apps/desktop/src/lib/trpc/routers/workspaces/workspaces.ts (1)
121-121: LGTM!Returning
projectIdin the workspace creation response enables the caller to associate the workspace with its project, which is necessary for the per-project toast notification feature.apps/desktop/src/shared/constants.ts (1)
26-27: LGTM!The WEBSITE_URL constant with environment override follows the existing pattern in this file and provides appropriate defaults for production and development.
apps/website/src/app/scripts/page.tsx (1)
1-21: LGTM!The imports and example constants are well-structured. The JSON examples provide clear, practical guidance for users configuring their setup and teardown scripts.
apps/desktop/src/renderer/screens/main/index.tsx (2)
6-6: LGTM!The import follows the established alias pattern for the renderer directory as per coding guidelines.
157-157: LGTM!The SetupConfigModal is correctly mounted at the top level of the MainScreen component, allowing it to overlay the entire application when opened. The modal manages its own state via Zustand, which is the preferred state management approach per coding guidelines.
apps/desktop/src/lib/trpc/routers/workspaces/utils/setup.test.ts (1)
22-59: Tests correctly target.superset/config.jsonand remain well‑isolatedThe updated tests consistently use
.superset/config.jsonand still cover the key paths (missing file, valid config, invalid JSON, invalidsetuptype) with one assertion per test and proper setup/teardown of the temp directory.apps/desktop/src/renderer/components/OpenInButton/index.ts (1)
1-2: Barrel export matches component structure guidelinesRe-exporting
OpenInButtonand its props from the index keeps consumers clean and follows the one‑component‑per‑folder + barrel pattern.apps/desktop/src/renderer/components/SetupConfigModal/index.ts (1)
1-1: SetupConfigModal barrel export looks goodThe index re-export keeps the component tree organized and makes imports consistent with other components.
apps/desktop/src/lib/trpc/routers/index.ts (1)
3-26: Config router wiring is consistent with existing router compositionAdding
config: createConfigRouter()alongside the other domain routers cleanly exposes the new config procedures onAppRouter. Ensure any generated TRPC client types are refreshed sotrpc.config.*is available in the renderer.apps/desktop/src/lib/trpc/routers/projects/projects.ts (1)
83-90: Newprojects.getquery is straightforward and consistentThe
getprocedure’s schema and implementation are simple and match existing router style, returningProject | nullwithout side effects. This should integrate cleanly with the rest of the TRPC API surface.apps/desktop/src/renderer/screens/main/components/StartView/index.tsx (2)
1-1: LGTM on imports and hook setup.The new imports for
toast,useOpenConfigModal, and thedismissConfigToastmutation are correctly set up and follow the established patterns in the codebase.Also applies to: 9-9, 59-60
89-94: Consistent onSuccess callback usage.The
showConfigToastIfNeededcallback is correctly wired to both workspace creation flows, ensuring users see the configuration prompt regardless of how they open a project.Also applies to: 108-108
apps/desktop/src/lib/trpc/routers/config/index.ts (1)
1-2: LGTM!Clean barrel export following tRPC router conventions.
apps/desktop/src/renderer/screens/main/components/WorkspaceView/WorkspaceHeader/WorkspaceHeader.tsx (1)
1-1: Good refactor to reusable component.Delegating to
OpenInButtonreduces code duplication and centralizes the "open in app" logic. The component correctly passes the derived label and enables keyboard shortcuts.Also applies to: 14-18
apps/desktop/src/renderer/components/SetupConfigModal/SetupConfigModal.tsx (2)
19-22: Static template shown regardless of existing config.The
CONFIG_TEMPLATEconstant is always displayed, even if aconfig.jsonfile already exists with different content. If this is intentional (showing the expected format), consider adding a note in the UI. Otherwise, you may want to fetch and display the actual file content when it exists.
45-89: LGTM on modal structure.The Dialog implementation correctly uses the UI primitives, handles close via
onOpenChange, and provides a clean UX with the config path display and "Learn more" external link.apps/desktop/src/renderer/stores/config-modal.ts (2)
1-27: LGTM!Clean Zustand store implementation with devtools middleware. The state management follows the codebase patterns and avoids unnecessary effects. Based on learnings, this correctly uses Zustand for state management.
29-37: Well-structured convenience hooks.The individual selector hooks provide efficient subscriptions to specific state slices, preventing unnecessary re-renders when unrelated state changes.
apps/desktop/src/renderer/components/OpenInButton/OpenInButton.tsx (3)
50-84: Component logic is well-structured.The tRPC integration, state management, and action guards are correctly implemented. The handlers properly check for path existence before executing mutations, and the invalidation pattern ensures the UI stays in sync.
85-149: UI rendering is clean and accessible.The button group structure, dropdown menu implementation, and conditional rendering logic are well-organized. Proper use of disabled states and ARIA-compatible components from @superset/ui.
10-10: Move ExternalApp type to shared location.The renderer process is importing a type from
main/lib/db/schemas, which violates the separation between main and renderer processes. Even though it's just a type import, this creates coupling to main-process code.Consider moving the
ExternalApptype to a shared location (e.g.,src/lib/types/external.tsor similar) that both main and renderer can import from. This maintains proper process boundaries in the Electron architecture.As per coding guidelines, renderer code should remain browser-environment compatible and not depend on main process modules.
⛔ Skipped due to learnings
Learnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-28T01:03:47.963Z Learning: Applies to apps/desktop/src/lib/**/*.ts : Never import Node.js modules in shared code like `src/lib/electron-router-dom.ts` - this code runs in both main and renderer processesLearnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-28T01:03:47.963Z Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net, etc.) in renderer process code - browser environment onlyLearnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-28T01:03:47.963Z Learning: Applies to apps/desktop/src/main/**/*.{ts,tsx} : Node.js modules (fs, path, os, net, etc.) can be used in main process code onlyLearnt from: CR Repo: superset-sh/superset PR: 0 File: apps/desktop/AGENTS.md:0-0 Timestamp: 2025-11-24T21:33:13.267Z Learning: Applies to apps/desktop/**/*.{ts,tsx,js,jsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`Learnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-28T01:03:47.963Z Learning: Applies to apps/desktop/src/main/lib/*-ipcs.ts : IPC handlers must accept a single object parameter, not positional parameters, to match type-safe renderer callsLearnt from: CR Repo: superset-sh/superset PR: 0 File: AGENTS.md:0-0 Timestamp: 2025-11-28T01:03:47.963Z Learning: Applies to packages/db/src/**/*.ts : Define all database schema in `packages/db/src/` using Drizzle ORMapps/desktop/src/lib/trpc/routers/config/config.ts (5)
1-10: File system utilities are correctly scoped.The Node.js imports (
node:fs,node:path) are appropriate here since tRPC routers run in the main process. TheconfigExistshelper is a straightforward existence check.
12-16: Config template is clear and functional.The template provides a valid starting structure for the configuration file with empty arrays for setup and teardown commands.
40-55: Toast visibility logic is sound.The procedure correctly guards against missing projects, respects the dismissal flag, and accurately determines toast visibility based on config file existence. The early returns make the logic easy to follow.
57-68: Dismissal mutation is implemented correctly.The procedure properly updates the database to mark the toast as dismissed, uses async/await appropriately, and returns a success indicator.
70-80: Query with side effects—verify this is intentional.The
getConfigFilePathquery callsensureConfigExists, which creates directories and files as a side effect. tRPC queries are typically read-only operations, while mutations handle state changes.If this auto-creation behavior is intentional (e.g., to ensure config always exists when accessed), consider:
- Documenting this behavior in a comment
- Alternatively, splitting into a separate mutation like
ensureAndGetConfigFilePathto make the side effect explicitExample documentation:
// Get the config file path (creates it if it doesn't exist) // Note: This query has side effects—it will create .superset/config.json // if it doesn't already exist. This ensures the path is always valid. getConfigFilePath: publicProcedureVerify this design aligns with your team's tRPC patterns for side-effecting operations.
| function ensureConfigExists(mainRepoPath: string): string { | ||
| const configPath = getConfigPath(mainRepoPath); | ||
| const supersetDir = join(mainRepoPath, ".superset"); | ||
|
|
||
| if (!existsSync(configPath)) { | ||
| // Create .superset directory if it doesn't exist | ||
| if (!existsSync(supersetDir)) { | ||
| mkdirSync(supersetDir, { recursive: true }); | ||
| } | ||
| // Create config.json with template | ||
| writeFileSync(configPath, CONFIG_TEMPLATE, "utf-8"); | ||
| } | ||
|
|
||
| return configPath; | ||
| } |
There was a problem hiding this comment.
Add error handling for file system operations.
The ensureConfigExists function performs file system operations (mkdirSync, writeFileSync) without error handling. Operations can fail due to permissions, disk space, or other I/O issues, potentially causing unhandled exceptions.
Consider wrapping in try-catch to provide better error feedback:
function ensureConfigExists(mainRepoPath: string): string {
const configPath = getConfigPath(mainRepoPath);
const supersetDir = join(mainRepoPath, ".superset");
try {
if (!existsSync(configPath)) {
if (!existsSync(supersetDir)) {
mkdirSync(supersetDir, { recursive: true });
}
writeFileSync(configPath, CONFIG_TEMPLATE, "utf-8");
}
return configPath;
} catch (error) {
throw new Error(`Failed to create config file: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}This ensures failures are caught and reported with context rather than causing silent crashes.
🤖 Prompt for AI Agents
In apps/desktop/src/lib/trpc/routers/config/config.ts around lines 22 to 36, the
ensureConfigExists function performs filesystem operations (mkdirSync,
writeFileSync) without error handling; wrap the block that checks/creates the
.superset directory and writes config.json in a try-catch, on error throw or
rethrow a new Error with contextual message including the original error message
(safely checking error instanceof Error), so callers receive a clear, readable
failure reason instead of an unhandled exception.
| const showConfigToastIfNeeded = (data: { | ||
| initialCommands: string[] | null; | ||
| projectId: string; | ||
| }) => { | ||
| if (!data.initialCommands || data.initialCommands.length === 0) { | ||
| toast.info("No setup script configured", { | ||
| description: "Automate workspace setup with a config.json file", | ||
| action: { | ||
| label: "Configure", | ||
| onClick: () => openConfigModal(data.projectId), | ||
| }, | ||
| onDismiss: () => { | ||
| dismissConfigToast.mutate({ projectId: data.projectId }); | ||
| }, | ||
| }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for shouldShowConfigToast usage
rg "shouldShowConfigToast" --type ts --type tsx -A 3 -B 3Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Search for config-related TRPC routes to understand the API
fd "config" --type f | grep -E "(trpc|router)" | head -20Repository: superset-sh/superset
Length of output: 114
🏁 Script executed:
#!/bin/bash
# Check the StartView file to see current imports and usage
cat -n apps/desktop/src/renderer/screens/main/components/StartView/index.tsx | head -100Repository: superset-sh/superset
Length of output: 4331
🏁 Script executed:
#!/bin/bash
# Search for dismissConfigToast to understand how dismissal is implemented
rg "dismissConfigToast" --type ts --type tsx -B 5 -A 5Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
#!/bin/bash
# Read the config TRPC router to see all available queries and mutations
cat apps/desktop/src/lib/trpc/routers/config/config.tsRepository: superset-sh/superset
Length of output: 2424
🏁 Script executed:
#!/bin/bash
# Search for shouldShowConfigToast with correct file types
rg "shouldShowConfigToast" -t tsRepository: superset-sh/superset
Length of output: 155
🏁 Script executed:
#!/bin/bash
# Search for dismissal-related code in config
rg "dismissConfig" -t ts -A 5 -B 5Repository: superset-sh/superset
Length of output: 6087
🏁 Script executed:
#!/bin/bash
# Find all config-related files
fd "config" --type f -E node_modules | grep -v ".git"Repository: superset-sh/superset
Length of output: 853
Add shouldShowConfigToast query check before displaying the toast.
The showConfigToastIfNeeded function shows the toast whenever initialCommands is empty, but doesn't check if the user previously dismissed it for this project. While dismissConfigToast.mutate() persists the dismissal server-side via project.configToastDismissed, the client-side check is missing. The shouldShowConfigToast query is available in the TRPC config router and already handles this dismissal check—use it before showing the toast to prevent repeated prompts after dismissal.
| const openConfigModal = useOpenConfigModal(); | ||
| const dismissConfigToast = trpc.config.dismissConfigToast.useMutation(); | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd WorkspaceDropdown.tsxRepository: superset-sh/superset
Length of output: 156
🏁 Script executed:
cat -n apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsxRepository: superset-sh/superset
Length of output: 4760
🏁 Script executed:
rg "shouldShowConfigToast" --type ts --type tsxRepository: superset-sh/superset
Length of output: 91
🏁 Script executed:
rg "dismissConfigToast" --type ts --type tsx -A 3 -B 3Repository: superset-sh/superset
Length of output: 91
🏁 Script executed:
fd "config" apps/desktop/src --type d | head -20Repository: superset-sh/superset
Length of output: 160
🏁 Script executed:
rg "shouldShowConfigToast" -t tsRepository: superset-sh/superset
Length of output: 155
🏁 Script executed:
rg "dismissConfigToast" -t ts -A 3 -B 3Repository: superset-sh/superset
Length of output: 3977
🏁 Script executed:
cat -n apps/desktop/src/lib/trpc/routers/config/index.tsRepository: superset-sh/superset
Length of output: 170
🏁 Script executed:
cat -n apps/desktop/src/lib/trpc/routers/config/config.tsRepository: superset-sh/superset
Length of output: 3005
🏁 Script executed:
sed -n '1,150p' apps/desktop/src/renderer/screens/main/components/StartView/index.tsx | grep -A 30 "dismissConfigToast"Repository: superset-sh/superset
Length of output: 1448
Call trpc.config.shouldShowConfigToast before showing the config toast
The toast is shown whenever there are no initialCommands, but the dismissal state is never checked before display. The dismissConfigToast mutation persists dismissal to the database (setting configToastDismissed flag), but shouldShowConfigToast is never queried to gate the toast. This means the toast will reappear on every workspace creation without initialCommands, even after the user has dismissed it for that project.
Add a check:
const shouldShow = await trpc.config.shouldShowConfigToast.useQuery({ projectId });
if (shouldShow.data) {
// show toast
}
🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/screens/main/components/TopBar/WorkspaceTabs/WorkspaceDropdown.tsx
around lines 25 to 27, the config toast is shown without checking persisted
dismissal state; add a top-level TRPC query using
trpc.config.shouldShowConfigToast.useQuery({ projectId }) (not an imperative
mutation) and use its returned data to gate showing the toast: only show the
toast when there are no initialCommands AND shouldShowConfigToast.data is
truthy; ensure the hook is called unconditionally at component top-level and
handle loading/undefined safely before deciding to show the toast.
| Add <code className="text-amber-400">.superset/</code> to your{" "} | ||
| <code className="text-amber-400">.gitignore</code> if you | ||
| don't want to share configs |
There was a problem hiding this comment.
Clarify the gitignore advice to align with tracked config.
The tip suggests adding .superset/ to .gitignore, but this PR specifically tracks .superset/config.json in version control. Consider revising this tip to clarify that users can ignore the directory while still tracking specific files, or remove this tip to avoid confusion with the project's established pattern.
Apply this diff to clarify the guidance:
<li>
- Add <code className="text-amber-400">.superset/</code> to your{" "}
- <code className="text-amber-400">.gitignore</code> if you
- don't want to share configs
+ The config file can be committed to share workspace setup with your team,
+ or kept local if you prefer different setups per developer
</li>
- <li>Or commit it to share workspace setup with your team</li>Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/website/src/app/scripts/page.tsx around lines 163 to 165, the current
tip tells users to add ".superset/" to .gitignore which conflicts with this repo
tracking ".superset/config.json"; update the copy to clarify that you can ignore
the .superset/ directory while still tracking specific files (e.g., keep
.superset/config.json in source control) or remove the tip entirely to avoid
confusion; adjust the sentence to explicitly mention preserving
.superset/config.json if the ignore rule is recommended.
- Show toast notification when workspace is created without setup commands - Add modal to configure .superset/config.json with setup/teardown scripts - Create reusable OpenInButton component for opening files in various apps - Add /scripts documentation page to website explaining config.json usage - Migrate from setup.json to config.json naming convention - Track toast dismissal per-project to avoid repeated prompts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
f9a7b39 to
d819940
Compare
- Add "Setup & Teardown Scripts" section to WorkspaceSettings - Show config.json template and path with OpenInButton for easy editing - Add "Learn how to use scripts" link to documentation - Merge PR #224 changes (config router, OpenInButton component) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Summary
.superset/config.jsonwith setup/teardown scriptsOpenInButtoncomponent for opening files in various apps (Finder, Cursor, VS Code, etc.)/scriptsdocumentation page to website explaining config.json usagesetup.jsontoconfig.jsonnaming conventionTest plan
.superset/config.json- should see toast/scriptson website - should show documentation🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Updates
✏️ Tip: You can customize this high-level summary in your review settings.