Skip to content

feat(desktop): infer project name from folder on import#3605

Merged
Kitenite merged 4 commits intomainfrom
infer-project-name-from-path
Apr 21, 2026
Merged

feat(desktop): infer project name from folder on import#3605
Kitenite merged 4 commits intomainfrom
infer-project-name-from-path

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Apr 21, 2026

Summary

  • v2's folder-first import previously opened a modal asking the user to name a new project whenever the picked folder had no matching cloud project. v1 never did this — it just used the folder basename.
  • Now: when `project.findByPath` returns 0 candidates, we infer the name from `basename(repoPath)` and call `project.create` directly. The naming modal is gone.
  • `useFolderFirstImport` collapses to a single `{ start }` surface; the `no-match` state, `confirmCreateAsNew`, `cancel`, and `FolderFirstImportModal` are removed.

Test plan

  • Drag/select a folder with a git remote that matches an existing cloud project — setup runs, no modal.
  • Drag/select a folder with a git remote that has no cloud match — project is created with the folder basename as the name, no modal shown.
  • Verify rename via project header still works to override the inferred name.
  • Confirm trailing slashes and Windows-style backslashes both produce a sensible basename.

Summary by cubic

Folder-first import now auto-creates a project using the folder’s basename when no cloud match is found, skipping the naming modal (v1 behavior). Name inference handles trailing slashes and Windows/UNC paths; users can still rename after import.

  • Refactors

    • Removed FolderFirstImportModal and the no-match state flow; useFolderFirstImport now returns { start } only and runs import directly.
    • Dropped stale FolderFirstImportModal usage in DashboardSidebarHeader.
    • Extracted getBaseName to renderer/lib/pathBasename, replaced duplicates, added unit tests, and fixed the trailing-separator edge case.
  • Migration

    • Update callers to use useFolderFirstImport().start only.
    • Remove references to FolderFirstImportModal and FolderFirstImportState.

Written for commit cd2d7c3. Summary will update on new commits.

Summary by CodeRabbit

  • Improvements
    • Simplified the folder import workflow by automatically deriving project names from selected folder paths instead of requiring manual entry during import.

…mport

When a folder import has no matching cloud project, infer the project
name from the folder basename and create the project directly instead
of opening a modal asking the user to type a name. Mirrors v1's
upsertProject behavior. Users can still rename after import.

Removes FolderFirstImportModal and its no-match state machine.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 21, 2026

📝 Walkthrough

Walkthrough

This pull request refactors the folder import workflow by removing the modal UI component that collected user input for project naming, simplifying the useFolderFirstImport hook to expose only a start() method, and extracting the getBaseName utility function to a centralized location. Project naming now occurs automatically based on directory paths.

Changes

Cohort / File(s) Summary
Modal UI Removal
FolderFirstImportModal.tsx, FolderFirstImportModal/index.ts
Deleted the entire FolderFirstImportModal component including dialog state management, "no-match" content rendering, and input handling. Removed associated re-exports.
Hook Refactoring
useFolderFirstImport/useFolderFirstImport.ts, useFolderFirstImport/index.ts
Removed modal-driven state machine (FolderFirstImportState, FolderImportCandidate); simplified UseFolderFirstImportResult to expose only start() method. Changed workflow from two-step (setup dialog + naming) to single-step with automatic project naming via getBaseName.
Utility Extraction
pathBasename/pathBasename.ts, pathBasename/index.ts, pathBasename/pathBasename.test.ts
Added new getBaseName utility function that extracts the final path segment from absolute paths, supporting both forward and backslash separators. Includes comprehensive test coverage for POSIX, Windows, and edge-case paths.
Integration Updates
new-item-paths.ts, DashboardSidebarHeader.tsx
Moved getBaseName from new-item-paths.ts to centralized pathBasename library and updated imports. Removed FolderFirstImportModal component mount points while retaining useFolderFirstImport hook initialization and start() trigger.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 The modal bid farewell with grace,
One method now leads the chase,
A path name blooms without a name,
Utility extracted—cleaner game!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: inferring project names from folder basenames during import, replacing the previous modal-based naming flow.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description is well-structured with summary, test plan, and refactoring details, following the template with clear objectives.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch infer-project-name-from-path

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.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 21, 2026

Greptile Summary

This PR removes the modal prompt that appeared whenever a dragged/selected folder had no matching cloud project. Instead, the folder's basename is automatically inferred as the project name (via the new inferProjectName helper) and project.create is called directly. The useFolderFirstImport hook collapses from a multi-state machine to a single async start() function, and the now-redundant FolderFirstImportModal component is deleted.

Key changes:

  • New inferProjectName(repoPath) utility strips trailing separators and splits on the last / or \\, with a fallback to the raw path for degenerate inputs.
  • The no-match state, confirmCreateAsNew, cancel actions, and FolderFirstImportModal are all removed — reducing surface area significantly.
  • AddRepositoryModals.tsx no longer renders FolderFirstImportModal; it now delegates entirely to the onSuccess/onError callbacks.
  • The startRef pattern (storing the latest start reference to avoid re-firing the effect mid-flow) is correctly preserved.
  • Two minor clean-up opportunities exist: a fallback edge-case in inferProjectName when the path consists entirely of separators, and a now-dead .catch on the trigger effect in AddRepositoryModals.tsx.

Confidence Score: 5/5

Safe to merge — the simplification is clean and all error paths are correctly handled.

The logic is straightforward and correct for all realistic inputs. The two flagged items (fallback in inferProjectName for root-only paths, and the dead .catch) are both P2 style suggestions that don't affect the happy path or cause any data-loss/reliability risk.

No files require special attention; the core change in useFolderFirstImport.ts is clean.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts Core logic rewrite: removes the no-match state machine and modal, instead infers project name from the folder basename via inferProjectName and calls project.create directly. Logic is correct; minor fallback edge-case in inferProjectName when path is all separators.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/AddRepositoryModals.tsx Simplified: removes FolderFirstImportModal rendering; the startRef pattern correctly avoids stale-closure issues. The outer .catch on the effect is dead code since start() never rejects.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/components/FolderFirstImportModal/FolderFirstImportModal.tsx Deleted — no longer needed since the naming modal is replaced by automatic basename inference.
apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/index.ts Simplified barrel export: only useFolderFirstImport and UseFolderFirstImportResult are now exported (the old state-machine types are gone).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User triggers folder import] --> B[selectDirectory dialog]
    B --> C{Canceled or no path?}
    C -- Yes --> Z[Return silently]
    C -- No --> D[client.project.findByPath]
    D --> E{Error?}
    E -- Yes --> F[reportError]
    E -- No --> G{candidates.length}
    G -- "> 1" --> H[reportError: Multiple matches]
    G -- "== 1" --> I[client.project.setup.mutate]
    G -- "== 0" --> J[inferProjectName from basename]
    J --> K[client.project.create.mutate]
    I --> L{Error?}
    K --> L
    L -- Yes --> F
    L -- No --> M[reportSuccess → ensureProjectInSidebar + onSuccess toast]
Loading

Comments Outside Diff (1)

  1. apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/AddRepositoryModals.tsx, line 34-39 (link)

    P2 Outer .catch is effectively dead code

    start() is fully self-contained — every error branch inside it calls reportError(...) and returns before re-throwing, so start() never rejects. The .catch here will never fire. This doesn't cause a bug today, but if a future refactor makes start() actually throw, this catch would silently duplicate the error toast (both the inner onError callback and this outer handler would run).

    Consider either removing it (relying entirely on the onError callback) or documenting that it acts as a safety net:

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/AddRepositoryModals.tsx
    Line: 34-39
    
    Comment:
    **Outer `.catch` is effectively dead code**
    
    `start()` is fully self-contained — every error branch inside it calls `reportError(...)` and returns before re-throwing, so `start()` never rejects. The `.catch` here will never fire. This doesn't cause a bug today, but if a future refactor makes `start()` actually throw, this catch would silently duplicate the error toast (both the inner `onError` callback and this outer handler would run).
    
    Consider either removing it (relying entirely on the `onError` callback) or documenting that it acts as a safety net:
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts
Line: 11-16

Comment:
**`inferProjectName` fallback leaks trailing separators**

When `repoPath` consists entirely of separators (e.g. `"/"` or `"\\"`) the regex strips them all, leaving `trimmed = ""`. `lastSep` is then `-1` so `name = trimmed = ""`, and the `name || repoPath` fallback returns the original, unstripped path — meaning a project could be created named `"/"`.

Adding `trimmed` as an intermediate fallback avoids this:

```suggestion
function inferProjectName(repoPath: string): string {
	const trimmed = repoPath.replace(/[\\/]+$/, "");
	const lastSep = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
	const name = lastSep < 0 ? trimmed : trimmed.slice(lastSep + 1);
	return name || trimmed || repoPath;
}
```

In practice the OS dialog won't let the user pick a raw root directory, so this is a very edge-case concern, but the fix is trivial.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/AddRepositoryModals.tsx
Line: 34-39

Comment:
**Outer `.catch` is effectively dead code**

`start()` is fully self-contained — every error branch inside it calls `reportError(...)` and returns before re-throwing, so `start()` never rejects. The `.catch` here will never fire. This doesn't cause a bug today, but if a future refactor makes `start()` actually throw, this catch would silently duplicate the error toast (both the inner `onError` callback and this outer handler would run).

Consider either removing it (relying entirely on the `onError` callback) or documenting that it acts as a safety net:

```suggestion
	useEffect(() => {
		if (folderImportTrigger === 0) return;
		// start() handles all errors internally via onError; this catch is a
		// safety net for unexpected throws only.
		startRef.current().catch((err) => {
			toast.error(
				`Import failed: ${err instanceof Error ? err.message : String(err)}`,
			);
		});
	}, [folderImportTrigger]);
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(desktop): infer project name from f..." | Re-trigger Greptile

Comment on lines +11 to +16
function inferProjectName(repoPath: string): string {
const trimmed = repoPath.replace(/[\\/]+$/, "");
const lastSep = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
const name = lastSep < 0 ? trimmed : trimmed.slice(lastSep + 1);
return name || repoPath;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 inferProjectName fallback leaks trailing separators

When repoPath consists entirely of separators (e.g. "/" or "\\") the regex strips them all, leaving trimmed = "". lastSep is then -1 so name = trimmed = "", and the name || repoPath fallback returns the original, unstripped path — meaning a project could be created named "/".

Adding trimmed as an intermediate fallback avoids this:

Suggested change
function inferProjectName(repoPath: string): string {
const trimmed = repoPath.replace(/[\\/]+$/, "");
const lastSep = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
const name = lastSep < 0 ? trimmed : trimmed.slice(lastSep + 1);
return name || repoPath;
}
function inferProjectName(repoPath: string): string {
const trimmed = repoPath.replace(/[\\/]+$/, "");
const lastSep = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
const name = lastSep < 0 ? trimmed : trimmed.slice(lastSep + 1);
return name || trimmed || repoPath;
}

In practice the OS dialog won't let the user pick a raw root directory, so this is a very edge-case concern, but the fix is trivial.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts
Line: 11-16

Comment:
**`inferProjectName` fallback leaks trailing separators**

When `repoPath` consists entirely of separators (e.g. `"/"` or `"\\"`) the regex strips them all, leaving `trimmed = ""`. `lastSep` is then `-1` so `name = trimmed = ""`, and the `name || repoPath` fallback returns the original, unstripped path — meaning a project could be created named `"/"`.

Adding `trimmed` as an intermediate fallback avoids this:

```suggestion
function inferProjectName(repoPath: string): string {
	const trimmed = repoPath.replace(/[\\/]+$/, "");
	const lastSep = Math.max(trimmed.lastIndexOf("/"), trimmed.lastIndexOf("\\"));
	const name = lastSep < 0 ? trimmed : trimmed.slice(lastSep + 1);
	return name || trimmed || repoPath;
}
```

In practice the OS dialog won't let the user pick a raw root directory, so this is a very edge-case concern, but the fix is trivial.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts">

<violation number="1" location="apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts:12">
P2: Manual path parsing for inferred project names is fragile; root paths resolve to invalid/awkward names (e.g. `C:` or `/`). Use a cross-platform basename utility with an explicit root fallback.

(Based on your team's feedback about using cross-platform path utilities instead of manual parsing.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Promote the inline inferProjectName in useFolderFirstImport and the
duplicate getBaseName in FilesView/utils into a shared helper at
renderer/lib/pathBasename. Fixes the trailing-separator edge case in
the old FilesView impl (/foo/ now returns "foo" instead of "").

Add 27 unit tests covering posix, Windows, UNC, trailing/consecutive
separators, dotfiles, unicode, emoji, and degenerate inputs.
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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts`:
- Around line 82-84: Guard against blank or whitespace-only inferred names
returned by getBaseName(repoPath) before calling client.project.create.mutate in
useFolderFirstImport: call getBaseName(repoPath), trim it, and if the result is
empty replace it with a safe fallback name (e.g., "New Project" or a sanitized
repoPath segment) or abort with a user-visible validation error; then pass the
validated/fallback name to client.project.create.mutate instead of the raw
getBaseName result.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7402f734-d078-4d1d-8b5c-5714e247b4c1

📥 Commits

Reviewing files that changed from the base of the PR and between 664d015 and b1d2d2e.

📒 Files selected for processing (5)
  • apps/desktop/src/renderer/lib/pathBasename/index.ts
  • apps/desktop/src/renderer/lib/pathBasename/pathBasename.test.ts
  • apps/desktop/src/renderer/lib/pathBasename/pathBasename.ts
  • apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts
  • apps/desktop/src/renderer/screens/main/components/WorkspaceView/RightSidebar/FilesView/utils/new-item-paths.ts
✅ Files skipped from review due to trivial changes (3)
  • apps/desktop/src/renderer/lib/pathBasename/pathBasename.ts
  • apps/desktop/src/renderer/lib/pathBasename/index.ts
  • apps/desktop/src/renderer/lib/pathBasename/pathBasename.test.ts

Comment on lines 82 to 84
const result = await client.project.create.mutate({
name,
name: getBaseName(repoPath),
mode: { kind: "importLocal", repoPath },
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 | 🟡 Minor

Guard against blank inferred project names before creating.

getBaseName(repoPath) can produce an unusable project name for degenerate selections such as a filesystem root, or for folders whose basename is whitespace. Since the modal validation path is gone, add a local guard before project.create.

Proposed fix
-			} else {
+			} else {
+				const inferredName = getBaseName(repoPath).trim();
+				if (!inferredName) {
+					reportError("Could not infer a project name from the selected folder");
+					return;
+				}
+
 				const result = await client.project.create.mutate({
-					name: getBaseName(repoPath),
+					name: inferredName,
 					mode: { kind: "importLocal", repoPath },
 				});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/desktop/src/renderer/routes/_authenticated/_dashboard/components/AddRepositoryModals/hooks/useFolderFirstImport/useFolderFirstImport.ts`
around lines 82 - 84, Guard against blank or whitespace-only inferred names
returned by getBaseName(repoPath) before calling client.project.create.mutate in
useFolderFirstImport: call getBaseName(repoPath), trim it, and if the result is
empty replace it with a safe fallback name (e.g., "New Project" or a sanitized
repoPath segment) or abort with a user-visible validation error; then pass the
validated/fallback name to client.project.create.mutate instead of the raw
getBaseName result.

Main added a FolderFirstImportModal reference in DashboardSidebarHeader
after this branch removed the modal. Remove the stale import and the
two renders — the hook now triggers import directly via start().
@Kitenite Kitenite merged commit 1e2302f into main Apr 21, 2026
6 of 7 checks passed
@Kitenite Kitenite deleted the infer-project-name-from-path branch April 21, 2026 18:51
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

Kitenite added a commit that referenced this pull request May 4, 2026
Add v2 project setup section (#3566, #3605, #3606, #3592, #3626, #3632),
scheduled agent runs (#3576), Opus 4.7 (#3579), v1 review comments in pane
(#3596), configurable v2 link-click (#3600), Copy Branch Name (#3635),
safer terminal preset defaults (#3546), and /pricing page (#3639). Expand
bug fixes with v2 git correctness, cross-fork PR misattribution, terminal
paste/Unicode/Shift+Enter, and security bumps.
Kitenite added a commit that referenced this pull request May 6, 2026
…-27) (#3792)

* docs: generate weekly changelog 2026-04-27

* docs: reframe weekly changelog around v2 public beta

Lead with v2 public beta + Settings → Experimental enable, restructure
around the v1→v2 migration story, sidebar overhaul, cross-workspace
terminals, and v2 chat. Pull in ~30 v2 PRs the bot missed and demote
non-v2 items (Hosts page, marketing menu) to a brief "Also this week".

* docs: pull in missed v2 features and bug fixes

Add v2 project setup section (#3566, #3605, #3606, #3592, #3626, #3632),
scheduled agent runs (#3576), Opus 4.7 (#3579), v1 review comments in pane
(#3596), configurable v2 link-click (#3600), Copy Branch Name (#3635),
safer terminal preset defaults (#3546), and /pricing page (#3639). Expand
bug fixes with v2 git correctness, cross-fork PR misattribution, terminal
paste/Unicode/Shift+Enter, and security bumps.

* docs(changelog): add v2 public beta hero screenshot

* docs(changelog): add Settings → Experimental screenshot, compress hero

pngquant compression: v2-public-beta.png 704KB → 166KB (76%),
v2-enable-flag.png 160KB → 36KB (78%). No visible quality loss.

* docs(changelog): tighten v2 launch prose, condense bullet groups

* docs(changelog): reframe cloud-first pillar as remote workspaces

* docs(changelog): cut parallel-agents and honest-state pillars, fold into sub-sections

* docs(changelog): tweak title and lead phrasing

* docs(changelog): rewrite v2 launch lede around Twitter narrative

Pull the launch story (physical limits, 3 ex-CTOs, cloud workspaces)
into the lede, restructure pillars around Remote workspaces, Reimagined
diff view, and Superset CLI, and add v2-remote-workspaces and
v2-changes-pane screenshots to back the new sections.

* docs(changelog): add CLI install snippet and docs link

* docs(changelog): cut narrative lede, match standard changelog tone

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kiet Ho <hoakiet98@gmail.com>
saddlepaddle pushed a commit that referenced this pull request May 6, 2026
…-27) (#3792)

* docs: generate weekly changelog 2026-04-27

* docs: reframe weekly changelog around v2 public beta

Lead with v2 public beta + Settings → Experimental enable, restructure
around the v1→v2 migration story, sidebar overhaul, cross-workspace
terminals, and v2 chat. Pull in ~30 v2 PRs the bot missed and demote
non-v2 items (Hosts page, marketing menu) to a brief "Also this week".

* docs: pull in missed v2 features and bug fixes

Add v2 project setup section (#3566, #3605, #3606, #3592, #3626, #3632),
scheduled agent runs (#3576), Opus 4.7 (#3579), v1 review comments in pane
(#3596), configurable v2 link-click (#3600), Copy Branch Name (#3635),
safer terminal preset defaults (#3546), and /pricing page (#3639). Expand
bug fixes with v2 git correctness, cross-fork PR misattribution, terminal
paste/Unicode/Shift+Enter, and security bumps.

* docs(changelog): add v2 public beta hero screenshot

* docs(changelog): add Settings → Experimental screenshot, compress hero

pngquant compression: v2-public-beta.png 704KB → 166KB (76%),
v2-enable-flag.png 160KB → 36KB (78%). No visible quality loss.

* docs(changelog): tighten v2 launch prose, condense bullet groups

* docs(changelog): reframe cloud-first pillar as remote workspaces

* docs(changelog): cut parallel-agents and honest-state pillars, fold into sub-sections

* docs(changelog): tweak title and lead phrasing

* docs(changelog): rewrite v2 launch lede around Twitter narrative

Pull the launch story (physical limits, 3 ex-CTOs, cloud workspaces)
into the lede, restructure pillars around Remote workspaces, Reimagined
diff view, and Superset CLI, and add v2-remote-workspaces and
v2-changes-pane screenshots to back the new sections.

* docs(changelog): add CLI install snippet and docs link

* docs(changelog): cut narrative lede, match standard changelog tone

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Kiet Ho <hoakiet98@gmail.com>
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