Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/browser/contexts/WorkspaceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import type { FrontendWorkspaceMetadata } from "@/common/types/workspace";
import type { WorkspaceSelection } from "@/browser/components/ProjectSidebar";
import type { RuntimeConfig } from "@/common/types/runtime";
import { deleteWorkspaceStorage } from "@/common/constants/storage";
import { deleteWorkspaceStorage, migrateWorkspaceStorage } from "@/common/constants/storage";
import { usePersistedState } from "@/browser/hooks/usePersistedState";
import { useProjectContext } from "@/browser/contexts/ProjectContext";
import { useWorkspaceStoreRaw } from "@/browser/stores/WorkspaceStore";
Expand Down Expand Up @@ -320,6 +320,11 @@ export function WorkspaceProvider(props: WorkspaceProviderProps) {
try {
const result = await window.api.workspace.rename(workspaceId, newName);
if (result.success) {
const newWorkspaceId = result.data.newWorkspaceId;

// Migrate localStorage keys from old to new workspace ID
migrateWorkspaceStorage(workspaceId, newWorkspaceId);

// Backend has already updated the config - reload projects to get updated state
await refreshProjects();

Expand All @@ -328,8 +333,6 @@ export function WorkspaceProvider(props: WorkspaceProviderProps) {

// Update selected workspace if it was renamed
if (selectedWorkspace?.workspaceId === workspaceId) {
const newWorkspaceId = result.data.newWorkspaceId;

// Get updated workspace metadata from backend
const newMetadata = await window.api.workspace.getInfo(newWorkspaceId);
if (newMetadata) {
Expand Down
17 changes: 5 additions & 12 deletions src/browser/utils/messages/StreamingMessageAggregator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type {
import { isDynamicToolPart } from "@/common/types/toolParts";
import { createDeltaStorage, type DeltaRecordStorage } from "./StreamingTPSCalculator";
import { computeRecencyTimestamp } from "./recency";
import { getStatusUrlKey } from "@/common/constants/storage";

// Maximum number of messages to display in the DOM for performance
// Full history is still maintained internally for token counting and stats
Expand Down Expand Up @@ -125,18 +126,11 @@ export class StreamingMessageAggregator {
this.updateRecency();
}

/** localStorage key for persisting lastStatusUrl. Only call when workspaceId is defined. */
private getStatusUrlKey(): string | undefined {
if (!this.workspaceId) return undefined;
return `mux:workspace:${this.workspaceId}:lastStatusUrl`;
}

/** Load lastStatusUrl from localStorage */
private loadLastStatusUrl(): string | undefined {
const key = this.getStatusUrlKey();
if (!key) return undefined;
if (!this.workspaceId) return undefined;
try {
const stored = localStorage.getItem(key);
const stored = localStorage.getItem(getStatusUrlKey(this.workspaceId));
return stored ?? undefined;
} catch {
return undefined;
Expand All @@ -148,10 +142,9 @@ export class StreamingMessageAggregator {
* Once set, the URL can only be replaced with a new URL, never deleted.
*/
private saveLastStatusUrl(url: string): void {
const key = this.getStatusUrlKey();
if (!key) return;
if (!this.workspaceId) return;
try {
localStorage.setItem(key, url);
localStorage.setItem(getStatusUrlKey(this.workspaceId), url);
} catch {
// Ignore localStorage errors
}
Expand Down
19 changes: 19 additions & 0 deletions src/common/constants/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,15 @@ export function getFileTreeExpandStateKey(workspaceId: string): string {
return `fileTreeExpandState:${workspaceId}`;
}

/**
* Get the localStorage key for persisted status URL for a workspace
* Stores the last URL set via status_set tool (survives compaction)
* Format: "statusUrl:{workspaceId}"
*/
export function getStatusUrlKey(workspaceId: string): string {
return `statusUrl:${workspaceId}`;
}

/**
* Get the localStorage key for unified Review search state per workspace
* Stores: { input: string, useRegex: boolean, matchCase: boolean }
Expand Down Expand Up @@ -202,6 +211,7 @@ const PERSISTENT_WORKSPACE_KEY_FUNCTIONS: Array<(workspaceId: string) => string>
getFileTreeExpandStateKey,
getReviewSearchStateKey,
getAutoCompactionEnabledKey,
getStatusUrlKey,
// Note: getAutoCompactionThresholdKey is per-model, not per-workspace
];

Expand Down Expand Up @@ -242,3 +252,12 @@ export function deleteWorkspaceStorage(workspaceId: string): void {
localStorage.removeItem(key);
}
}

/**
* Migrate all workspace-specific localStorage keys from old to new workspace ID
* Should be called when a workspace is renamed to preserve settings
*/
export function migrateWorkspaceStorage(oldWorkspaceId: string, newWorkspaceId: string): void {
copyWorkspaceStorage(oldWorkspaceId, newWorkspaceId);
deleteWorkspaceStorage(oldWorkspaceId);
}