feat: added terminal tabs and resilient session storage (#265)

- Add per-directory tabbed terminal UI with separate buffers
- Persist and restore active tab per directory across sessions
- Fallback to in-memory storage when sessionStorage is unavailable
This commit is contained in:
Bohdan Triapitsyn
2026-02-01 19:28:05 +02:00
committed by GitHub
parent 0981194eed
commit 3e145bdbd6
3 changed files with 861 additions and 282 deletions
@@ -1,4 +1,5 @@
let safeStorageInstance: Storage | null = null;
let safeSessionStorageInstance: Storage | null = null;
const createInMemoryStorage = (): Storage => {
const store = new Map<string, string>();
@@ -119,3 +120,101 @@ export const getSafeStorage = (): Storage => {
return safeStorageInstance;
};
const createSafeSessionStorage = (): Storage => {
if (typeof window === 'undefined' || !window.sessionStorage) {
return createInMemoryStorage();
}
const baseStorage = window.sessionStorage;
const fallback = createInMemoryStorage();
let storageAvailable = true;
const disableStorage = () => {
storageAvailable = false;
};
const safeGet = (key: string): string | null => {
if (storageAvailable) {
try {
const value = baseStorage.getItem(key);
if (value !== null) {
return value;
}
} catch {
disableStorage();
}
}
return fallback.getItem(key);
};
const safeSet = (key: string, value: string) => {
if (storageAvailable) {
try {
baseStorage.setItem(key, value);
fallback.removeItem(key);
return;
} catch {
disableStorage();
}
}
fallback.setItem(key, value);
};
const safeRemove = (key: string) => {
if (storageAvailable) {
try {
baseStorage.removeItem(key);
} catch {
disableStorage();
}
}
fallback.removeItem(key);
};
const safeClear = () => {
if (storageAvailable) {
try {
baseStorage.clear();
} catch {
disableStorage();
}
}
fallback.clear();
};
const safeKey = (index: number): string | null => {
if (storageAvailable) {
try {
return baseStorage.key(index);
} catch {
disableStorage();
}
}
return fallback.key(index);
};
return {
getItem: safeGet,
setItem: safeSet,
removeItem: safeRemove,
clear: safeClear,
key: safeKey,
get length() {
if (storageAvailable) {
try {
return baseStorage.length + fallback.length;
} catch {
disableStorage();
}
}
return fallback.length;
},
} as Storage;
};
export const getSafeSessionStorage = (): Storage => {
if (!safeSessionStorageInstance) {
safeSessionStorageInstance = createSafeSessionStorage();
}
return safeSessionStorageInstance;
};