Compare commits

...

2 Commits

Author SHA1 Message Date
Haileyesus
4ab94fce42 chore: upgrade better-sqlite to latest version to support node 25 (#445)
Co-authored-by: Haileyesus <something@gmail.com>
Co-authored-by: viper151 <simosmik@gmail.com>
2026-02-26 15:45:25 +03:00
PaloSP
e3b689214f feat: persist active tab across reloads via localStorage (#414)
* feat: persist active tab across reloads via localStorage (closes #387)

Remember the last active tab in localStorage instead of always resetting
to 'chat'. Also stop force-switching to chat tab on session change,
so users stay on their preferred tab (shell, git, etc.).

* fix: validate localStorage tab value and add try-catch for restricted contexts

Address CodeRabbit feedback: validate stored activeTab against known
tab IDs before using it, and wrap localStorage access in try-catch
to prevent crashes in restricted environments.
2026-02-26 14:46:01 +03:00
3 changed files with 31 additions and 19 deletions

10
package-lock.json generated
View File

@@ -31,7 +31,7 @@
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"bcrypt": "^6.0.0",
"better-sqlite3": "^12.2.0",
"better-sqlite3": "^12.6.2",
"chokidar": "^4.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
@@ -3778,9 +3778,9 @@
"license": "Apache-2.0"
},
"node_modules/better-sqlite3": {
"version": "12.2.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.2.0.tgz",
"integrity": "sha512-eGbYq2CT+tos1fBwLQ/tkBt9J5M3JEHjku4hbvQUePCckkvVf14xWj+1m7dGoK81M/fOjFT7yM9UMeKT/+vFLQ==",
"version": "12.6.2",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz",
"integrity": "sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
@@ -3788,7 +3788,7 @@
"prebuild-install": "^7.1.1"
},
"engines": {
"node": "20.x || 22.x || 23.x || 24.x"
"node": "20.x || 22.x || 23.x || 24.x || 25.x"
}
},
"node_modules/binary-extensions": {

View File

@@ -66,7 +66,7 @@
"@xterm/addon-webgl": "^0.18.0",
"@xterm/xterm": "^5.5.0",
"bcrypt": "^6.0.0",
"better-sqlite3": "^12.2.0",
"better-sqlite3": "^12.6.2",
"chokidar": "^4.0.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",

View File

@@ -101,6 +101,20 @@ const isUpdateAdditive = (
);
};
const VALID_TABS: Set<string> = new Set(['chat', 'files', 'shell', 'git', 'tasks', 'preview']);
const readPersistedTab = (): AppTab => {
try {
const stored = localStorage.getItem('activeTab');
if (stored && VALID_TABS.has(stored)) {
return stored as AppTab;
}
} catch {
// localStorage unavailable
}
return 'chat';
};
export function useProjectsState({
sessionId,
navigate,
@@ -111,7 +125,16 @@ export function useProjectsState({
const [projects, setProjects] = useState<Project[]>([]);
const [selectedProject, setSelectedProject] = useState<Project | null>(null);
const [selectedSession, setSelectedSession] = useState<ProjectSession | null>(null);
const [activeTab, setActiveTab] = useState<AppTab>('chat');
const [activeTab, setActiveTab] = useState<AppTab>(readPersistedTab);
useEffect(() => {
try {
localStorage.setItem('activeTab', activeTab);
} catch {
// Silently ignore storage errors
}
}, [activeTab]);
const [sidebarOpen, setSidebarOpen] = useState(false);
const [isLoadingProjects, setIsLoadingProjects] = useState(true);
const [loadingProgress, setLoadingProgress] = useState<LoadingProgress | null>(null);
@@ -265,8 +288,6 @@ export function useProjectsState({
return;
}
const shouldSwitchTab = !selectedSession || selectedSession.id !== sessionId;
for (const project of projects) {
const claudeSession = project.sessions?.find((session) => session.id === sessionId);
if (claudeSession) {
@@ -280,9 +301,6 @@ export function useProjectsState({
if (shouldUpdateSession) {
setSelectedSession({ ...claudeSession, __provider: 'claude' });
}
if (shouldSwitchTab) {
setActiveTab('chat');
}
return;
}
@@ -298,9 +316,6 @@ export function useProjectsState({
if (shouldUpdateSession) {
setSelectedSession({ ...cursorSession, __provider: 'cursor' });
}
if (shouldSwitchTab) {
setActiveTab('chat');
}
return;
}
@@ -316,9 +331,6 @@ export function useProjectsState({
if (shouldUpdateSession) {
setSelectedSession({ ...codexSession, __provider: 'codex' });
}
if (shouldSwitchTab) {
setActiveTab('chat');
}
return;
}
}
@@ -341,7 +353,7 @@ export function useProjectsState({
(session: ProjectSession) => {
setSelectedSession(session);
if (activeTab !== 'git' && activeTab !== 'preview') {
if (activeTab === 'tasks' || activeTab === 'preview') {
setActiveTab('chat');
}