feat: journal dashboard widgets, agent polish, and edit-mode live previews
CI / checks (push) Failing after 2m7s
CI / build (push) Successful in 4m36s

Journal dashboard widgets and quick-add; rich-text quick-add dialogs.

Dashboard draft sync for live edit previews; assistant bubble + API tools.

Journal UX: stress slider, mood grid, query cap fix.
This commit is contained in:
ginnoir
2026-07-04 22:03:45 -05:00
parent 4a924a4107
commit a09747c314
76 changed files with 4594 additions and 611 deletions
@@ -0,0 +1,54 @@
export type AssistantChatMessage = {
role: "user" | "assistant";
content: string;
};
const STORAGE_VERSION = "v1";
const MAX_MESSAGES = 40;
function storageKey(userId: string) {
return `assistant-chat:${STORAGE_VERSION}:${userId}`;
}
function isValidMessage(value: unknown): value is AssistantChatMessage {
if (!value || typeof value !== "object") return false;
const row = value as Record<string, unknown>;
return (
(row.role === "user" || row.role === "assistant") &&
typeof row.content === "string" &&
row.content.trim().length > 0
);
}
export function loadAssistantChat(userId: string): AssistantChatMessage[] {
try {
const raw = localStorage.getItem(storageKey(userId));
if (!raw) return [];
const parsed: unknown = JSON.parse(raw);
if (!Array.isArray(parsed)) return [];
return parsed.filter(isValidMessage).slice(-MAX_MESSAGES);
} catch {
return [];
}
}
export function saveAssistantChat(userId: string, messages: AssistantChatMessage[]): void {
try {
const trimmed = messages.slice(-MAX_MESSAGES);
if (trimmed.length === 0) {
localStorage.removeItem(storageKey(userId));
return;
}
localStorage.setItem(storageKey(userId), JSON.stringify(trimmed));
} catch {
// Private browsing, quota exceeded, or disabled storage.
}
}
export function clearAssistantChat(userId: string): void {
try {
localStorage.removeItem(storageKey(userId));
} catch {
// Ignore storage errors.
}
}