chore(upstream): PR8 - 新規upstream 8件取り込み#149
Merged
Conversation
* feat(desktop): git decoration in v2 "All files" tree
Adds VS Code-style decoration to the v2 workspace file tree:
- Modified files get a colored name + single-letter badge (M/A/U/D/R/C)
- Ancestor folders of any change get a colored dot roll-up
- Gitignored paths (node_modules, dist, .turbo, etc.) render muted
Backend: git.getStatus gains an ignoredPaths field populated by
`git ls-files --others --ignored --exclude-standard --directory`, run
inside the existing Promise.all so it adds zero wall-clock latency.
Client: new useGitStatusMap hook is a pure derivation over the shared
git.getStatus query cache (same key as useChangesTab → React Query
dedupes → zero extra subscriptions). TreeNode resolves decoration +
isMuted once per node and passes primitives down so WorkspaceFilesTreeItem
can React.memo cleanly — after a refresh, only rows whose state actually
flipped re-render.
Also unifies the server-side dirty signal: GitWatcher now watches both
.git/ and the worktree (via @superset/workspace-fs's multiplexed watcher
manager, so zero duplicate native watchers) and coalesces both sources
into one debounced git:changed per workspace. useChangesTab drops its
fs:events subscription and client-side 300ms debounce — git:changed is
now the single refetch trigger.
* fix(desktop): add right padding to file tree status badge
* fix(desktop): file tree polish — padding, indent, z-index, decoration tweaks
- Bump row right padding to pr-4 for breathing room on status badges
- Reduce TREE_INDENT from 16px to 10px so deep paths stay on screen
- Shift paddingLeft to (depth-1)*indent so root items aren't over-indented
- Fix sticky folder z-index going negative at depth > 10 (was 10-depth,
now 50-depth) — folders at depth 11+ were rendering behind the
scroll container background
- Use LuCircle icon at opacity-50 for folder status dots instead of
the tiny bullet character
- Skip deleted files when rolling up status to ancestor folders —
expanding the folder wouldn't show the deleted file anyway
- Remove "Loading files..." placeholder
* refactor(desktop): split git status fetching from derivation + Changes tab UI
Three single-purpose hooks instead of one tangled one:
- useGitStatus(workspaceId) — fetches + subscribes + invalidates. Single
owner of the git.getStatus query and git:changed subscription. Called
once at the WorkspaceSidebar level.
- useGitStatusMap(statusData) — pure useMemo over the data, returns
{ fileStatusByPath, folderStatusByPath, ignoredPaths }. No fetching,
no subscription, no dependency on CollectionsProvider.
- useChangesTab({ workspaceId, gitStatus, ... }) — now just the Changes
tab UI. Accepts git status as a param instead of fetching internally.
Still owns its Changes-tab-specific queries (listCommits, listBranches,
getCommitFiles) and filter state.
WorkspaceSidebar calls useGitStatus once and passes the query result to
useChangesTab and the query data to FilesTab. Single query, single
subscription, explicit data flow — no more React Query dedup tricks or
"two hooks with the same key happen to share a cache entry."
Also:
- Renamed `statusByPath` → `fileStatusByPath` and `worstStatusByFolder`
→ `folderStatusByPath` for symmetry.
- Dropped the unused `changedAncestors` set (value-equivalent to
folderStatusByPath.keys()).
* refactor(desktop): drop custom --diff-* tokens for standard Tailwind colors
Removed 15 lines of custom CSS variables (--diff-added, --diff-modified,
etc.) from globals.css. These were only used in two places — ChangesFileList
and WorkspaceFilesTreeItem — and encouraged a pattern of adding bespoke
tokens to globals.css for every semantic color.
Replaced with standard Tailwind colors + dark: variants, leveraging the
existing @custom-variant dark (&:is(.dark *)) in packages/ui/src/globals.css
and the .dark class the theme store sets on the root element.
* fix(desktop): tighten diff status colors to match old OKLCH tokens
* refactor(desktop): use yellow instead of amber for modified status
* fix(desktop): address PR review feedback
- useGitStatus: stop invalidating listCommits on git:changed. With the
unified watcher, git:changed now fires on ordinary file edits, so
refetching commit history every save was wasteful. listCommits now
refreshes on window focus / mutations only.
- FilesTab: gate the "No files found" empty state on !isLoadingRoot so
it doesn't flash during the initial root fetch.
- git-watcher: reorder initialization so disposeWorktreeWatch is captured
in the .git watcher's error handler closure — fixes a race where the
error could fire before this.watched.set ran, leaking the worktree
watch.
- useGitStatusMap: swap EMPTY_RESULT singleton for an emptyResult()
factory so consumers can't accidentally mutate a module-level Map/Set.
- WorkspaceFilesTreeItem: clamp sticky folder z-index with
Math.max(1, ...) so depths past 50 don't underflow into the scroll
container.
…sh#3332) Each row in Settings > Agents is collapsible but had no visual affordance, so users didn't realize they could expand it to configure the agent. Adds a rotating ChevronDownIcon next to the enable switch.
…erset-sh#3334) * fix(desktop): pass file attachments through PromptInput onSubmit callback handlePromptSubmit was ignoring the message argument from PromptInput, causing file attachments to be lost when submitting via Enter. Now converts the already-available FileUIParts and passes them directly to handleCreate, skipping the redundant takeFiles() path. * style(desktop): format PromptGroup with biome
…h#3318) * fix(cli): switch CLI auth to OAuth code + PKCE + loopback The CLI's device flow always landed multi-org users on the wrong org. Better Auth's `deviceAuthorization` mints a fresh session with `activeOrganizationId = null`, and `customSession` then falls back to `allMemberships[0]` — the org picked on the `/device` consent page only ever wrote to the *web* session, never the new CLI session. Replaces the device flow with RFC 8252 authorization_code + PKCE + loopback redirect, matching what gcloud/stripe/vercel do. Consent- time `customAccessTokenClaims` bakes `organizationId`, `email`, and `plan` into the JWT, so the CLI's bearer carries the picked org (and plan, for billing gates) with zero session lookups. Server: - Narrow `protectedProcedure`'s session to `AuthSession` = `{user:{id,email}, session:{activeOrganizationId, plan}}` — the complete set of fields any tRPC route actually reads today, plus `plan` for imminent billing gates. `customSession` still returns the full enriched shape for cookie callers. - Rewrite `apps/api/src/trpc/context.ts` to resolve the session from one of three sources, bearer-authoritative: (1) OAuth JWT via `verifyAccessToken`+JWKS, (2) `sk_live_*` API key via `verifyApiKey` with org from metadata, (3) cookie session via `getSession`. A malformed or invalid bearer returns null (→ 401); we never silently fall through to cookie auth on failure. - Hydrate `user.me` from the DB so callers see consistent fields regardless of auth method. - Seed the `superset-cli` OAuth client at API startup via a new `seedSupersetCliOAuthClient` helper called from Next.js's `instrumentation.ts:register()` hook. Uses a hardcoded public `client_id` instead of per-install dynamic registration, matching every major CLI's pattern (gh/gcloud/stripe). Loopback ports 51789-51793 are seeded as `redirect_uris`. - Add the `authTime` column `@better-auth/oauth-provider@1.5.6` expects on `oauthRefreshTokens` (migration 0033). - Extend `customAccessTokenClaims` to also return `email` and `plan` so the JWT carries everything the narrowed session needs. - Extract `resolvePlanForOrganization` shared between `customSession` and `customAccessTokenClaims`. - Extract `looksLikeJwt`/`parseApiKeyMetadata`/etc. into a shared `apps/api/src/lib/auth-utils.ts` used by both the MCP auth-flow and the new tRPC context builder. - Delete `deviceAuthorization` plugin + `apps/web/src/app/device/`. CLI: - `packages/cli/src/lib/auth.ts` — `authorizationCodeAuth` does: generate PKCE S256 + random state, bind a one-shot loopback HTTP server on the first free candidate port, open the browser to `/oauth2/authorize` with `prompt=consent` (always show the picker so re-running login can switch orgs), verify the state on the callback (CSRF), exchange the code at `/oauth2/token` with `resource` in the POST body (required for JWT mint). `refreshAccessToken` uses the same hardcoded `client_id`. - `packages/cli/src/lib/config.ts` — new auth shape `{accessToken, refreshToken, expiresAt}`, dropped `activeOrg` and `clientIds`. Fix 0600 file mode bug (both on write and repair on read for pre-0.1.x installs). - `packages/cli/src/lib/api-client.ts` — explicit `bearer` option, no more `config.auth` peek. - `packages/cli/src/lib/resolve-auth.ts` — shared bearer resolution used by the middleware, `auth check`, and host commands. Order: `--api-key` flag → `SUPERSET_API_KEY` env → stored OAuth token (pre-emptively refreshed if within 5 min of expiry). - `packages/cli/src/lib/active-org.ts` — derive the active org from the bearer (JWT claim for OAuth, API call for API keys). - `packages/cli/src/commands/middleware.ts` — use `resolveAuth`, pass bearer + authSource on ctx. - `packages/cli/src/bin.ts` + `run-static.ts` — new `--api-key` global flag (`.env("SUPERSET_API_KEY")`). - `auth whoami` → `auth check`; new output reports auth source and OAuth token expiry. - Delete `org switch` — the JWT is pinned to the org at mint time, so switching means re-running `auth login` and picking a different org on the consent screen. `prompt=consent` on the authorize URL ensures the picker always shows. - `host start/stop/status` derive the active org via `getActiveOrgId` instead of the dropped `config.activeOrg`. Verified locally against a multi-org user: logout + login + pick a different org swaps the JWT organizationId, user.me returns the right account, token refresh advances `iat`/`exp` in the decoded payload, and sending `Authorization: Bearer <invalid>` with a browser cookie attached returns 401 instead of silently falling through to the cookie user. * fix: remove oauth client seed, insert row directly into prod/dev * fix(cli): replace JWT auth with session tokens Reverts the oauthProvider JWT infrastructure (context.ts rewrite, AuthSession narrowing, auth-utils, plan helper, user.me hydration) and replaces it with normal Better Auth sessions — the same token format the desktop and web app use. New flow: CLI opens browser to /cli/authorize (org picker page), user picks an org, page calls /api/cli/create-code (stores a one-time code in Redis keyed to userId:orgId, 5min TTL), redirects to the CLI's loopback callback with the code. CLI exchanges the code at /api/cli/exchange, which creates a session via internalAdapter.createSession with activeOrganizationId set correctly. CLI stores the session token and uses it as Authorization: Bearer — getSession + bearer() + customSession handles the rest. Context.ts untouched. Net -548 lines server-side. CLI auth.ts 160 lines (was 435). * refactor(cli): framework-owned dev/build, fix native packaging cli-framework now exposes a bin with `dev` and `build` subcommands. Consumer writes cli.config.ts (defineConfig) and commands/; zero glue files. No bin.ts, no commands/index.ts stub, no runtime plugin() registration. `cli-framework dev` scans the commands directory at runtime; `cli-framework build` generates a temp entry in .cache/cli-framework and calls Bun.build() with createCommandsPlugin, which now has an onResolve hook so no filesystem stub is needed. createCommand<CliContext>() gives every command typed ctx. Group-level middleware skip markers are gone — commands that don't need middleware declare `skipMiddleware: true`. active-org.ts and run-static.ts are deleted; the CLI is now one code path. Also fixes three pre-existing packaging gaps surfaced while testing the staged tarball end-to-end on darwin-arm64: 1. libsql native binding never shipped. It's transitively pulled by mastra; now listed in NATIVE_PACKAGES with platform subpackages (@libsql/darwin-arm64 etc.) in TARGET_NATIVE_PACKAGES. 2. findPackagePath couldn't locate transitive packages in Bun's isolated store. Added a `.bun/<encoded>@*/node_modules/<name>` fallback so transitives (not just direct host-service deps) are walkable. 3. Native ABI mismatch. Desktop's root install:deps runs electron-rebuild on every `bun install`, clobbering the hoisted build/Release/*.node binaries with Electron-ABI (143) builds that can't load under Node 22 (ABI 127). fixNativeBinariesForNode now runs after the copy: downloads the Node-ABI better-sqlite3 prebuild straight from GitHub releases, deletes node-pty/build/ so the `bindings` loader falls through to N-API prebuilds/, and @parcel/watcher-<target> is listed so the platform subpackage (untouched by electron-rebuild) is copied and preferred at runtime over the clobbered main package build. Verified end-to-end on darwin-arm64: bun run build:dist produces a tarball whose bin/superset-host starts, runs migrations via better-sqlite3, and listens on Hono. * fix(cli): PR review followups on framework refactor Seven independent fixes against comments on 311067d: - plugin.ts: conditionally import root middleware. dev.ts treats commandsDir/middleware.ts as optional, but the build plugin used to emit an unconditional import — a consumer without a root middleware would work in dev and crash at build. - plugin.ts: JSON.stringify import specifiers in the generated module. Matches the pattern already used in build.ts. Raw interpolation was fragile on Windows paths or repo paths with special characters. - runner.ts: thread opts.name through handleError instead of hardcoding "superset auth login" in the UNAUTHORIZED branch. run() is a framework-level API. - runner.ts: leading global flags no longer break routing. `superset --json auth check` used to print root help because routeCommand stops at the first `-` segment. Added a splitArgsForRouting helper that peels known globals from argv before routing and concatenates them back into remainingArgs for the command parser. Applied to both the help path and main routing. - runner.ts: reject surplus positionals. Non-variadic commands previously silently dropped trailing positional arguments, so typos like `tasks get ABC extra` ran successfully. - runner.ts: require middleware to invoke next() before running typed commands. A short-circuiting middleware used to leave ctx as {} while cmd.run received it typed as the consumer's CliContext, turning a middleware bug into a runtime undefined access. Now throws CLIError with a clear message. - build-dist.ts: error on ambiguous .bun store matches. If the isolated store contains multiple versions of the same package, readdirSync previously returned whichever one the filesystem yielded first. Now collects all matches and throws unless exactly one is found.
…ment (superset-sh#3345) * docs: add design doc for v2 host project paths Outlines the approach for allowing users to import existing local repos or clone to a chosen location instead of auto-cloning to a fixed path. Covers local-only storage decision, throw-on-create pattern, and phased implementation plan. * feat(host-service): add project.setup endpoint for importing/cloning repos Adds a `project.setup` mutation that lets users either import an existing local repo or clone to a chosen directory. Validates git remotes against the cloud project's GitHub repo (handles SSH + HTTPS). Upserts the local projects table so re-running handles re-pointing. * fix(host-service): address PR review feedback on project.setup - Reuse existing parseGitHubRemote helper instead of custom regex, adds ssh:// URI support and credential-safe URL storage - Validate parentDir is a directory in cloneRepo, not just exists - Wrap simpleGit().clone() in try/catch for actionable error messages - Return remotes from importExistingRepo to avoid double git remote -v - Use path.resolve for absolute clone paths - Fix design doc: git rev-parse instead of .git folder check * fix(host-service): replace non-null assertions with explicit guards * fix(host-service): clean up cloned directory on post-clone validation failure * fix(host-service): add cleanup on all cloneRepo error paths
…t-sh#3302) * feat(desktop): clone V1 new-workspace composer onto V2 modal Replaces the tab-based V2 create-workspace modal with a clone of the battle-tested V1 composer, rewiring only the backend boundaries. Backend boundary changes (V1 → V2): - Project list: electronTrpc.projects.getRecents → v2Projects + githubRepositories collections - Branch list: electronTrpc.projects.getBranches* → workspaceCreation.searchBranches on host-service - Create action: 4 V1 mutations (create/createFromPr/openTracked/ openExternal) → single workspaceCreation.create on host-service - GitHub issues/PRs list + content: electronTrpc.projects.{listIssues, listPullRequests, searchPullRequests, getIssueContent} → workspaceCreation.{searchGitHubIssues, searchPullRequests, getGitHubIssueContent} on host-service (Octokit via ctx.github()) - Navigation: navigateToWorkspace → navigateToV2Workspace V2 additions: - DevicePicker in the composer footer for host target selection; on host change, compareBaseBranch resets - hostTarget field in the draft context Intentionally dropped for Phase 1 (deferred to Phase 2): - Branch prefix feature (projects.get, getGitAuthor, settings. getBranchPrefix, settings.getGitInfo, resolveBranchPrefix) — crosses V2 host boundary, needs host-aware prefix in Phase 2 - Worktree preflight UI (getExternalWorktrees, getWorktreesByProject, resolveOpenableWorktrees, worktree badges/filter tab) — host-service workspaceCreation.create handles tracked/external/adopt server-side Host-service endpoints added: - workspaceCreation.getContext — project + default branch - workspaceCreation.searchBranches — git branches with hasWorkspace - workspaceCreation.create — semantic create with outcome resolution (created_workspace / opened_existing_workspace / opened_worktree / adopted_external_worktree) and path-traversal guard on branchName - workspaceCreation.searchGitHubIssues — Octokit issue list/search - workspaceCreation.searchPullRequests — Octokit PR list/search - workspaceCreation.getGitHubIssueContent — Octokit issue body fetch * fix: pass organizationId to cloud API calls in workspaceCreation router After merging main, v2Project.get and v2Workspace.create were converted to jwtProcedure and now require organizationId in their input. The host-service workspace.create endpoint was already updated in main, but our new workspaceCreation router still had four call sites missing it. * chore: add instrumentation to workspace creation flow Temporary console.log at key points to debug "workspace created" toast showing but workspace not appearing: - useCreateDashboardWorkspace: log input + result - PromptGroup: log create result + navigation - workspaceCreation.create: log resolved names, each outcome path * Update docs * docs: clean up plan files, keep only final decisions + scenario analysis Remove superseded docs (v2-create-fix-plan, v2-create-decisions, v1-workspace-creation-logic). Keep v1-create-scenario-analysis as V1 behavior reference and v2-create-decisions-final as the 13 implementation decisions. * feat: implement v2 create decisions — always create, never collide Host-service workspaceCreation.create rewrite: - Strip collision detection (no opened_existing_workspace / opened_worktree / adopted_external_worktree). Create always creates. - Sanitize + deduplicate branch name server-side. If branch exists, append -2, -3, etc. Renderer sends best-effort name. - Simplified return: { workspace, warnings } — no outcome field. - Add setup script execution (runs .superset/setup.sh in worktree, blocks until done, non-fatal on failure). - Remove source and behavior fields from input schema. - Add sanitize-branch utils (copied from shared/utils/branch.ts). Renderer PromptGroup rewrite: - Remove AI branch gen (electronTrpc.workspaces.generateBranchName) — boundary violation, needs V1 project ID. - Renderer computes branch name: user-typed > prompt slug > random UUID. - Renderer computes workspace name: user-typed > prompt > branch name. - Single pending phase: "creating" (no "generating-branch" or "preparing"). - Remove buildLaunchRequest, hostUrl resolution, agentConfigsById — dead code after removing AI gen and collision paths. Per decisions in apps/desktop/plans/v2-create-decisions-final.md. * feat: draft stash for failure recovery On create submit: snapshot the draft into a zustand atom before closing the modal. On success: clear the stash. On failure: restore the stash and reopen the modal so the user can retry with their prompt, attachments, and linked context intact. - Added stashedDraft / stashDraft / clearStashedDraft / restoreStashedDraft to the new-workspace-modal zustand store - PromptGroup.handleCreate stashes before closeAndResetDraft, restores on catch - DashboardNewWorkspaceModalContent applies stash on reopen via useEffect that reads from the zustand store - Replaced runAsyncAction with direct try/catch for clearer success/failure handling * chore: add logging + error handling to cloud API calls in create The create flow was hanging silently when ensureV2Host or v2Workspace.create failed (e.g. Neon DB schema mismatch). Now both calls log before + catch errors explicitly, rollback the worktree on failure, and throw with a descriptive message instead of hanging. * docs: finalize v2 workspace creation status + pending workspace design - pendingWorkspaces local collection (localStorage-backed via @tanstack/react-db) holds full draft data for retry on failure - Attachments stored as raw blobs in IndexedDB (no compression — files are already compressed, IndexedDB has no size limit) - EventBus workspace:creating events for live step-by-step progress - /v2-workspace/pending/$pendingId route shows progress, error + retry - Sidebar renders pending workspaces as clickable skeletons - Multiple concurrent creates supported - Host-service returns initialCommands, setup dispatched to terminal pane * docs: switch from EventBus to polling for create progress - Host-service writes to in-memory Map during create mutation - New getProgress query endpoint, pending page polls every 500ms - PromptGroup owns the create promise (fire-and-forget, survives unmount), updates pendingWorkspaces collection on resolve/reject - Pending page is purely a progress viewer, not an owner - Sidebar reads collection via useLiveQuery, no polling - Removed all EventBus references from the design * docs: add TODO for cleaning up stale createProgress map entries * chore: add TODO to migrate chat pane uploads to IndexedDB blob pattern * chore: move IndexedDB migration TODO to ChatLaunchConfig where base64 blobs are stored * feat: add create progress infrastructure Host-service: - In-memory progress map with TTL sweep for stale entries - getProgress query endpoint (polled by pending page at 500ms) - Create mutation writes step progress (ensuring_repo → creating_worktree → registering) - Clears progress on success or failure - Returns initialCommands instead of running execSync (setup dispatched to terminal pane) - Accepts pendingId for progress correlation Renderer: - pendingWorkspaces local collection (localStorage-backed via @tanstack/react-db) with full draft data for retry on failure - pending-attachment-store.ts: IndexedDB wrapper for attachment blobs (store/load/clear keyed by pendingId) - useCreateDashboardWorkspace accepts + forwards pendingId * feat: rewrite PromptGroup submit to use pendingWorkspaces collection + IndexedDB - Insert full draft into pendingWorkspaces collection before closing modal - Store attachment blobs in IndexedDB (keyed by pendingId/blobUUID) - Navigate to /v2-workspace/pending/$pendingId immediately - Fire-and-forget createWorkspace — closure survives modal unmount - On success: update collection row to succeeded, clear IndexedDB blobs - On failure: update collection row to failed (draft preserved for retry) - Removed zustand stashedDraft/pendingWorkspace hooks (replaced by collection) - Removed inline convertBlobUrlToDataUrl (moved to pending-attachment-store) * feat: add pending workspace page with live progress polling New route: /v2-workspace/pending/$pendingId - Reads pending workspace from pendingWorkspaces collection via useLiveQuery - Polls workspaceCreation.getProgress every 500ms for step-by-step progress - Shows: workspace name, branch name, step checklist (ensuring_repo → creating_worktree → registering) - On succeeded: auto-navigates to /v2-workspace/$workspaceId, cleans up pending row - On failed: shows error message with Retry + Dismiss buttons - Retry resets status to creating (full re-fire is a follow-up TODO) - Dismiss deletes pending row + clears IndexedDB attachments * feat: sidebar renders pending workspaces from collection, clickable - Replace single zustand pendingWorkspace with useLiveQuery on pendingWorkspaces collection (supports multiple concurrent creates) - Pending sidebar items are now clickable — navigate to /v2-workspace/pending/$pendingId to view progress - Add "failed" status to creationStatus type across sidebar components (types, icon, collapsed button, status text helper) - Succeeded pending workspaces are filtered out (replaced by real workspace from Electric sync) * refactor: split PromptGroup into focused files PromptGroup.tsx: 890 → 441 lines (UI render only) Extracted: - components/AttachmentButtons/ (74 lines) - components/ProjectPickerPill/ (79 lines) - components/CompareBaseBranchPicker/ (134 lines) - hooks/useHandleCreate/ (181 lines) — full create orchestration - types.ts (15 lines) — shared types + constants Existing sub-components unchanged: - GitHubIssueLinkCommand, LinkedGitHubIssuePill, LinkedPRPill, PRLinkCommand * fix: sidebar pending workspace visual states - Failed: red triangle icon + red "Failed" text - Creating: keeps spinner + gray "Creating..." text * fix: center pending workspace page layout * fix: pending page centered on page, left-aligned text * fix: pending page takes full width of content area * refactor: host-service defines step labels, renderer just renders Host-service getProgress now returns fully resolved steps with id, label, and status (pending/active/done). The renderer maps over the array directly — no STEP_LABELS, no STEP_ORDER, no string matching. If the host-service adds/changes steps, the UI updates automatically. * fix: pending page top-aligned with padding, update dev seed scripts * feat: elapsed timer + staleness detection on pending page * fix: coerce createdAt to timestamp regardless of string/Date type * fix: timer inline before status text * fix: formatRelativeTime shows seconds under 1 minute Was returning "now" for everything under 60s. Now returns "now" for <5s, then "5s", "30s", etc. Pending page consumes it for the elapsed timer instead of a custom formatter. * chore: remove dev seed files and dead stash restore code - Delete dev-seed-pending-workspace.ts and DevSeedPendingWorkspace.tsx - Remove stash restore effect from DashboardNewWorkspaceModalContent (draft recovery now handled by pending page retry, not modal reopen) - Remove DevSeedPendingWorkspace from layout - Leave zustand store untouched (V1 still uses it) * fix: move pending page out of v2-workspace layout The pending page was under /v2-workspace/pending/$pendingId which caused the v2-workspace layout to render bare <Outlet /> during route transitions. This removed the WorkspaceTrpcProvider from the tree while TerminalPane was still mounted → crash. Moved to /_dashboard/pending/$pendingId — completely outside the v2-workspace layout. The layout reverts to main's version unchanged. * docs: add comment explaining why pending page lives outside v2-workspace * chore: remove instrumentation console.logs from workspaceCreation.create * fix: always show dismiss on creating page, add TODO on v2-workspace layout bug - Dismiss button always visible on pending page (not just when stale) - Added TODO comment on v2-workspace layout explaining the bare Outlet bug that strips WorkspaceTrpcProvider during transitions - Removed instrumentation console.logs * fix: always create new branch, never try checkout existing Create flow uses `git worktree add -b` directly. No try/catch fallback to checkout. If branch exists despite dedup, the git command errors and the create fails cleanly. Checking out existing branches is a separate intent (createFromPr). Updated decisions doc (decision #8). * feat: use friendly two-word names for fallback branch/workspace names Replace workspace-${uuid} with friendly-words pattern (e.g. "cheerful-umbrella"). Generate once, use for both branch name and workspace name when user typed neither. New shared util at shared/utils/friendly-branch-name.ts. * feat: wire up retry from pending page, move timer to end - Extract useRetryCreate hook that re-fires createWorkspace with draft data from the pending collection row + attachments from IndexedDB - Retry button calls the hook instead of inline logic - Move elapsed timer to right-aligned end of status line * refactor: rename compareBaseBranch to baseBranch in V2 create flow V1 uses "compareBaseBranch" because it serves double duty as the git fork point AND the diff comparison base. For V2's create flow it's just the fork point — "baseBranch" is clearer. The workspace view can derive its own compare base independently. Renamed in: draft context, PromptGroup, useHandleCreate, useCreateDashboardWorkspace, host-service create input schema, pendingWorkspace collection schema, pending page retry. V1 code untouched. * refactor: split branch name handling into slugifyForBranch + sanitizeUserBranchName Two clearly different operations: - slugifyForBranch: turns arbitrary text (prompts) into a branch slug. Lowercases, strips special chars, collapses spaces to dashes. - sanitizeUserBranchName: strips only what git forbids from a user-typed branch name. Preserves case, slashes, underscores. Host-service no longer sanitizes — it only validates (non-empty) and deduplicates. The renderer owns all sanitization/slugification. Removed sanitizeBranchNameWithMaxLength from host-service utils. * refactor: rename useHandleCreate → useSubmitWorkspace, extract pure functions - resolveNames(draft) — pure. Computes branch + workspace names from draft state. User-typed → sanitizeUserBranchName, prompt → slugifyForBranch, empty → friendlyBranchName. - mapLinkedContext(draft) — pure. Maps linked issues/PR to API payload shape. - useSubmitWorkspace(projectId) — the hook. Orchestrates resolve → store → insert → close → navigate → fire create. Calls the pure functions. Dependency array simplified: just draft object instead of 12 individual fields. * fix: dedup suffix can no longer exceed max branch length Truncates base name upfront to 94 chars (reserving 6 for suffix like -99999) before appending dedup suffixes. Strips trailing .- after truncation. Last-resort fallback uses base36 timestamp instead of decimal for shorter output. * lint
…#3347) * feat(desktop): add local v1/v2 version toggle in top bar When the v2 feature flag is enabled remotely, a segmented toggle appears in the top bar (next to resource consumption) that lets you switch between v1 and v2 locally without changing the PostHog flag. The preference persists in localStorage across app restarts. * lint
…ence (superset-sh#3346) * feat(desktop): port browser pane to v2 workspaces with global persistence - Port v1 browser pane (webview, URL toolbar, history, error overlay, new-window handler) into v2 workspace tree as a native implementation. Uses the v2 Workspace component's renderToolbar slot for a Chrome-style URL bar in the pane header. - Float-over webview: each <webview> lives at document.body under a fixed #browser-runtime-root, positioned via ResizeObserver on a per-pane placeholder div. Elements never reparent, so Electron's guest WebContents survives tab switches, pane drags, and workspace route remounts — no page reloads. - Keep-alive tab rendering in @superset/panes Workspace.tsx: tabs that have been visited stay mounted behind visibility:hidden so their React state (and persistent DOM like webviews) survives tab switches. Also benefits terminal scrollback, chat subscriptions, editor state. - BrowserRuntimeRegistry: module-level singleton keyed by paneId, modeled on terminalRuntimeRegistry. Owns webview lifecycle, state subscriptions via useSyncExternalStore, drag-passthrough listeners, equality-guarded setState to deduplicate webview event spam. - Single-pane-browser tabs show the page favicon + live page title in the tab bar via a new optional renderTabLabel prop on the panes library. - BrowserPane subscribes to the workspace store for activeTabId to drive webview visibility, so inactive-tab webviews don't paint on top of the active tab's content. - Destroy path: diff the set of browser pane ids across all tabs each render, destroying any that disappeared. Correctly handles tab closes, intra-tab split closes, and pane drags between tabs (moved panes stay in the set). Known limitation: dropping panes onto another browser pane's body doesn't register — the webview compositor layer intercepts drag events even with pointer-events:none/visibility:hidden. Drops on pane headers work. Deferred. * refactor(desktop): co-locate browser pane integration, remove keep-alive - Float-over webviews already preserve state across tab switches via the registry's attach/detach pair, so keep-alive tab rendering was redundant. Revert packages/panes Workspace.tsx to rendering only the active tab. - Move all browser-specific wiring from page.tsx into a new useBrowserPaneIntegration hook co-located with BrowserPane. page.tsx now consumes a single hook call instead of owning tab title derivation, tab label rendering, and the destroy diff effect. - Registry attach takes over the visibility flip directly; setVisibility method is no longer needed. Drop the rAF deferral — React useEffect fires after layout commit so getBoundingClientRect reads correct values synchronously. - BrowserPane no longer subscribes to the workspace store for isTabActive. * refactor(desktop): simplify browser pane integration, add onRemoved hook - Add PaneDefinition.onRemoved(pane) lifecycle hook to @superset/panes. Workspace.tsx tracks a flat snapshot of panes across all tabs and fires onRemoved with the pre-removal Pane object when an id disappears. Pane moves between tabs keep the same id in the current set so moves correctly don't fire onRemoved. - Wire browser cleanup declaratively via the new hook in usePaneRegistry.tsx: `onRemoved: (pane) => browserRuntimeRegistry.destroy(pane.id)`. Removes the need for useBrowserPaneDestroy in userland. - Persist faviconUrl on BrowserPaneData alongside pageTitle. The registry's persist callback fires from handleDidStopLoading AND handlePageFaviconUpdated via a shared firePersist helper. Favicons now survive app restart. - Replace renderTabLabel with renderTabIcon in @superset/panes. TabItem renders {icon}{title}{accessory} — icon leading, accessory trailing, native gap-2 from the parent button. Drop redundant `| null` from the ReactNode types. - Fix titleOverride precedence in Workspace.tsx: `tab.titleOverride ?? getTabTitle(tab) ?? tab.id`. Renaming a browser tab now actually sticks. - Empty tab rename clears titleOverride instead of being swallowed; thread `string | undefined` through TabItem → TabBar → Workspace so the derived title takes back over on clear. - Collapse useBrowserPaneIntegration.tsx and BrowserTabLabel component into BrowserPane.tsx as pure functions (getBrowserTabTitle, renderBrowserTabIcon). Favicon is now a plain <img> reading from persisted pane data — no more subscribing component just for one field. - page.tsx loses browser-specific imports, refs, effects, and callbacks. Just two pure helpers passed as props. * refactor(desktop): use native webview back/forward + tighten sanitizeUrl - Drop the custom history array and isHistoryNavigation flag. goBack/goForward now call webview.goBack()/goForward() directly, preserving scroll position, form state, and BFCache on back/forward navigation. - Add refreshNavState helper that reads webview.canGoBack()/canGoForward() and writes them into runtime state. Called from did-navigate, did-navigate-in-page, did-stop-loading, and on re-attach of existing entries. - The isHistoryNavigation guard was dead code: did-navigate fires before did-stop-loading, so the flag was always false by the time the guard ran, causing a redundant browserHistory.upsert IPC call on every back/forward nav. - Clear faviconUrl in handleDidStartLoading so stale favicons don't persist when navigating to a page with no favicon. - Tighten sanitizeUrl: reject dotted strings containing whitespace (e.g. "release v2.1") as URLs, trim input, tighten the localhost/127.0.0.1 regex to accept optional port and path. Dotted search queries now correctly fall through to Google search.
|
Important Review skippedToo many files! This PR contains 190 files, which is 40 over the limit of 150. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (190)
You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
MocA-Love
added a commit
that referenced
this pull request
Apr 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
upstream に新たに追加された8コミットを取り込み。fork 危険領域との overlap 0、全て v2/CLI/host-service の新機能・修正。
取り込みコミット
23b53ed37(superset-sh#3320)87b6ffff9(superset-sh#3332)d7eed7872(superset-sh#3334)1bdd70022(superset-sh#3318)bf07410ca(superset-sh#3345)ec1baccac(superset-sh#3302)a44108af9(superset-sh#3347)2c6736416(superset-sh#3346)コンフリクト解決
3件の軽微な「両方追加」型衝突を解決:
_dashboard/layout.tsx: fork 独自 import + upstream の useIsV2CloudEnabled_authenticated/layout.tsx: fork 独自 Provider + upstream の V2 Providerv2-workspace/$workspaceId/types.ts: fork 独自 type + upstream の BrowserPaneData検証
bun install→ 変更なしbun run typecheck→ 新規エラーなしTest plan