123 lines
4.0 KiB
TypeScript
123 lines
4.0 KiB
TypeScript
export interface ChatViewPreferences {
|
|
showThinking: boolean;
|
|
showTimestamps: boolean;
|
|
/** Include reasoning, tool summaries, and execution narration in exports. */
|
|
includeExecution: boolean;
|
|
}
|
|
|
|
type ChatViewPreferencePatch = Partial<ChatViewPreferences>;
|
|
type StoredChatViewPreferences = Record<string, ChatViewPreferencePatch>;
|
|
|
|
export const CHAT_VIEW_PREFERENCES_STORAGE_KEY = 'makelore.chat-view-preferences.v1';
|
|
export const DEFAULT_CHAT_VIEW_PREFERENCES: ChatViewPreferences = {
|
|
showThinking: true,
|
|
showTimestamps: true,
|
|
includeExecution: false,
|
|
};
|
|
|
|
function browserStorage(): Storage | undefined {
|
|
try {
|
|
return typeof window === 'undefined' ? undefined : window.localStorage;
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
function readStoredPreferences(storage: Storage | undefined): StoredChatViewPreferences {
|
|
const preferences = Object.create(null) as StoredChatViewPreferences;
|
|
if (!storage) return preferences;
|
|
|
|
try {
|
|
const parsed = JSON.parse(storage.getItem(CHAT_VIEW_PREFERENCES_STORAGE_KEY) ?? '{}');
|
|
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return preferences;
|
|
|
|
for (const [sessionId, candidate] of Object.entries(parsed as Record<string, unknown>)) {
|
|
if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) continue;
|
|
const record = candidate as Record<string, unknown>;
|
|
preferences[sessionId] = {
|
|
...(typeof record.showThinking === 'boolean'
|
|
? { showThinking: record.showThinking }
|
|
: {}),
|
|
...(typeof record.showTimestamps === 'boolean'
|
|
? { showTimestamps: record.showTimestamps }
|
|
: {}),
|
|
...(typeof record.includeExecution === 'boolean'
|
|
? { includeExecution: record.includeExecution }
|
|
: {}),
|
|
};
|
|
}
|
|
} catch {
|
|
return preferences;
|
|
}
|
|
|
|
return preferences;
|
|
}
|
|
|
|
export function loadChatViewPreferences(
|
|
sessionId: string,
|
|
storage: Storage | undefined = browserStorage(),
|
|
): ChatViewPreferences {
|
|
const candidate = readStoredPreferences(storage)[sessionId];
|
|
return {
|
|
showThinking: typeof candidate?.showThinking === 'boolean'
|
|
? candidate.showThinking
|
|
: DEFAULT_CHAT_VIEW_PREFERENCES.showThinking,
|
|
showTimestamps: typeof candidate?.showTimestamps === 'boolean'
|
|
? candidate.showTimestamps
|
|
: DEFAULT_CHAT_VIEW_PREFERENCES.showTimestamps,
|
|
includeExecution: typeof candidate?.includeExecution === 'boolean'
|
|
? candidate.includeExecution
|
|
: DEFAULT_CHAT_VIEW_PREFERENCES.includeExecution,
|
|
};
|
|
}
|
|
|
|
export function saveChatViewPreferences(
|
|
sessionId: string,
|
|
patch: ChatViewPreferencePatch,
|
|
storage: Storage | undefined = browserStorage(),
|
|
): ChatViewPreferences {
|
|
const current = loadChatViewPreferences(sessionId, storage);
|
|
const next = {
|
|
showThinking: typeof patch.showThinking === 'boolean'
|
|
? patch.showThinking
|
|
: current.showThinking,
|
|
showTimestamps: typeof patch.showTimestamps === 'boolean'
|
|
? patch.showTimestamps
|
|
: current.showTimestamps,
|
|
includeExecution: typeof patch.includeExecution === 'boolean'
|
|
? patch.includeExecution
|
|
: current.includeExecution,
|
|
};
|
|
if (!storage) return next;
|
|
|
|
try {
|
|
const all = readStoredPreferences(storage);
|
|
all[sessionId] = next;
|
|
storage.setItem(CHAT_VIEW_PREFERENCES_STORAGE_KEY, JSON.stringify(all));
|
|
} catch {
|
|
// View preferences are best-effort and must never block the chat UI.
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
export function deleteChatViewPreferences(
|
|
sessionId: string,
|
|
storage: Storage | undefined = browserStorage(),
|
|
): void {
|
|
if (!storage) return;
|
|
|
|
try {
|
|
const all = readStoredPreferences(storage);
|
|
if (!Object.prototype.hasOwnProperty.call(all, sessionId)) return;
|
|
delete all[sessionId];
|
|
if (Object.keys(all).length === 0) {
|
|
storage.removeItem(CHAT_VIEW_PREFERENCES_STORAGE_KEY);
|
|
} else {
|
|
storage.setItem(CHAT_VIEW_PREFERENCES_STORAGE_KEY, JSON.stringify(all));
|
|
}
|
|
} catch {
|
|
// Host deletion already succeeded; unavailable storage must not undo it.
|
|
}
|
|
}
|