diff --git a/Journal.App/src/lib/backend/ai.ts b/Journal.App/src/lib/backend/ai.ts index 0614287..a0da357 100644 --- a/Journal.App/src/lib/backend/ai.ts +++ b/Journal.App/src/lib/backend/ai.ts @@ -1,8 +1,7 @@ import { sendCommand } from "./client"; import { pickCase } from "./normalize"; -// ── Public types ──────────────────────────────────────────────── - +//#region Public Types export type AiHealthDto = { provider: string; enabled: boolean; @@ -45,9 +44,9 @@ export type CoachSessionPayload = { recentFragments?: string[]; preferences?: CoachPreferencesDto; }; +//#endregion -// ── Raw (PascalCase) variants for normalization ───────────────── - +//#region PascalCase Normalizers type AiHealthDtoRaw = { provider?: string; enabled?: boolean; @@ -93,9 +92,9 @@ type CoachPlanDtoRaw = { Evidence?: CoachEvidenceDtoRaw[]; PatchProposal?: CoachPatchProposalDtoRaw | null; }; +//#endregion -// ── Normalizers ───────────────────────────────────────────────── - +//#region Normalizers function normalizeHealth(raw: AiHealthDtoRaw): AiHealthDto { return { provider: pickCase(raw, "provider", "Provider", ""), @@ -163,9 +162,9 @@ function normalizeCoachPlan(raw: CoachPlanDtoRaw): CoachPlanDto { patchProposal: normalizePatchProposal(patchRaw), }; } +//#endregion -// ── API functions ─────────────────────────────────────────────── - +//#region API Functions export async function aiHealth(): Promise { const data = await sendCommand({ action: "ai.health", @@ -220,3 +219,4 @@ export async function coachWeekly( }); return normalizeCoachPlan(data); } +//#endregion diff --git a/Journal.App/src/lib/backend/conversations.ts b/Journal.App/src/lib/backend/conversations.ts index 95cd9fd..402653a 100644 --- a/Journal.App/src/lib/backend/conversations.ts +++ b/Journal.App/src/lib/backend/conversations.ts @@ -1,8 +1,7 @@ import { sendCommand } from "./client"; import { pickCase } from "./normalize"; -// ── Public types ──────────────────────────────────────────────── - +//#region Public Types export type ConversationDto = { id: string; title: string; @@ -29,9 +28,9 @@ export type ConversationChatResult = { userMessage: ConversationMessageDto; assistantMessage: ConversationMessageDto; }; +//#endregion -// ── Raw (PascalCase) variants ─────────────────────────────────── - +//#region PascalCase Normalizers type ConversationDtoRaw = { id?: string; title?: string; @@ -73,9 +72,9 @@ type ConversationChatResultRaw = { UserMessage?: ConversationMessageDtoRaw; AssistantMessage?: ConversationMessageDtoRaw; }; +//#endregion -// ── Normalizers ───────────────────────────────────────────────── - +//#region Normalizers function normalizeMessage( raw: ConversationMessageDtoRaw, ): ConversationMessageDto { @@ -132,9 +131,9 @@ function normalizeChatResult( assistantMessage: normalizeMessage(assistantRaw), }; } +//#endregion -// ── API functions ─────────────────────────────────────────────── - +//#region API Functions export async function listConversations(): Promise { const data = await sendCommand({ action: "conversations.list", @@ -193,3 +192,4 @@ export async function conversationChat( }); return normalizeChatResult(data); } +//#endregion diff --git a/Journal.App/src/lib/components/SidePanel.svelte b/Journal.App/src/lib/components/SidePanel.svelte index 80d28d2..41378f3 100644 --- a/Journal.App/src/lib/components/SidePanel.svelte +++ b/Journal.App/src/lib/components/SidePanel.svelte @@ -1279,6 +1279,7 @@ {#each $conversationsStore.items as conv}
  • {#if editingConversationId === conv.id} + ({ checking: false, health: null, @@ -52,9 +51,9 @@ export const chatStateStore = writable({ busy: false, messages: [], }); +//#endregion -// ── Actions ───────────────────────────────────────────────────── - +//#region Actions export async function checkAiHealth(): Promise { aiStatusStore.update((s) => ({ ...s, checking: true })); try { @@ -138,3 +137,4 @@ export function clearCoachPlan(): void { export function clearChat(): void { chatStateStore.set({ busy: false, messages: [] }); } +//#endregion diff --git a/Journal.App/src/lib/stores/conversations.ts b/Journal.App/src/lib/stores/conversations.ts index 2133f0d..4a7a85c 100644 --- a/Journal.App/src/lib/stores/conversations.ts +++ b/Journal.App/src/lib/stores/conversations.ts @@ -10,8 +10,7 @@ import { type ConversationMessageDto, } from "$lib/backend/conversations"; -// ── Store shapes ──────────────────────────────────────────────── - +//#region Store Shapes type ConversationsState = { items: ConversationDto[]; busy: boolean; @@ -25,9 +24,9 @@ type ActiveConversationState = { busy: boolean; error: string; }; +//#endregion -// ── Stores ────────────────────────────────────────────────────── - +//#region Stores export const conversationsStore = writable({ items: [], busy: false, @@ -41,9 +40,9 @@ export const activeConversationStore = writable({ busy: false, error: "", }); +//#endregion -// ── Actions ───────────────────────────────────────────────────── - +//#region Actions export async function loadConversations(): Promise { conversationsStore.update((s) => ({ ...s, busy: true, error: "" })); try { @@ -67,7 +66,6 @@ export async function createNewConversation( ...s, items: [conv, ...s.items], })); - // Auto-open the new conversation activeConversationStore.set({ id: conv.id, title: conv.title, @@ -113,7 +111,6 @@ export async function openConversation(id: string): Promise { export async function sendConversationMessage(prompt: string): Promise { const state = get(activeConversationStore); if (!state.id) { - // Auto-create a conversation from the first message const title = prompt.length > 40 ? prompt.slice(0, 37) + "..." : prompt; const convId = await createNewConversation(title); if (!convId) return; @@ -122,7 +119,6 @@ export async function sendConversationMessage(prompt: string): Promise { const currentId = get(activeConversationStore).id; if (!currentId) return; - // Optimistically add user message const tempUserMsg: ConversationMessageDto = { id: `temp-${Date.now()}`, role: "user", @@ -142,14 +138,12 @@ export async function sendConversationMessage(prompt: string): Promise { activeConversationStore.update((s) => ({ ...s, busy: false, - // Replace temp user message + add assistant message messages: [ ...s.messages.filter((m) => m.id !== tempUserMsg.id), result.userMessage, result.assistantMessage, ], })); - // Update conversation list (updated_at changes) void loadConversations(); } catch (error) { const errorMsg: ConversationMessageDto = { @@ -195,7 +189,6 @@ export async function removeConversation(id: string): Promise { ...s, items: s.items.filter((c) => c.id !== id), })); - // If this was the active conversation, clear it const active = get(activeConversationStore); if (active.id === id) { clearActiveConversation(); @@ -217,3 +210,4 @@ export function clearActiveConversation(): void { error: "", }); } +//#endregion diff --git a/Journal.App/src/lib/stores/todos.ts b/Journal.App/src/lib/stores/todos.ts index 2f3af1f..698d300 100644 --- a/Journal.App/src/lib/stores/todos.ts +++ b/Journal.App/src/lib/stores/todos.ts @@ -10,8 +10,6 @@ import { type TodoListDto, } from "$lib/backend/todos"; -// TodoItem keeps a numeric `id` for local array operations (used by EditorPanel) -// plus a `backendId` (guid string) for backend persistence. export type TodoItem = { id: number; text: string; @@ -24,8 +22,7 @@ export const todoListsStore = writable([]); export const todosStore = writable>({}); export const todosBusyStore = writable(false); -// ── ID helpers ─────────────────────────────────────────────────── - +//#region ID Helpers function toStoreId(guid: string): string { return `todos/${guid}`; } @@ -40,9 +37,9 @@ function toBackendId(storeId: string): string | null { export function createTodoId(): number { return Date.now() + Math.floor(Math.random() * 1000); } +//#endregion -// ── DTO mapping ────────────────────────────────────────────────── - +//#region DTO Mapping function dtoToMeta(dto: TodoListDto): TodoListMeta { return { id: toStoreId(dto.id), @@ -59,9 +56,9 @@ function dtoToItems(dto: TodoListDto): TodoItem[] { backendId: item.id, })); } +//#endregion -// ── Hydration ──────────────────────────────────────────────────── - +//#region Hydration export async function hydrateTodos(): Promise { todosBusyStore.set(true); try { @@ -82,9 +79,9 @@ export async function hydrateTodos(): Promise { todosBusyStore.set(false); } } +//#endregion -// ── List CRUD ──────────────────────────────────────────────────── - +//#region List CRUD export async function createTodoListFromLabel( label: string, ): Promise<{ meta: TodoListMeta; items: TodoItem[] }> { @@ -113,9 +110,9 @@ export async function deleteTodoListByStoreId( }); return true; } +//#endregion -// ── Item CRUD (backend-backed) ─────────────────────────────────── - +//#region Item CRUD export async function addTodoItemBackend( storeId: string, text: string, @@ -204,9 +201,9 @@ export async function removeTodoItemBackend( })); return true; } +//#endregion -// ── Pure helpers (used by EditorPanel for local state) ─────────── - +//#region Pure Helpers export function serializeTodoList(title: string, todos: TodoItem[]): string { const heading = title?.trim() ? `# ${title}` : "# To-Do List"; const lines = todos.map( @@ -285,3 +282,4 @@ export function createTodoListDraft(): { items: [], }; } +//#endregion diff --git a/Journal.App/src/lib/utils/markdown.ts b/Journal.App/src/lib/utils/markdown.ts index 8850a75..7f329bc 100644 --- a/Journal.App/src/lib/utils/markdown.ts +++ b/Journal.App/src/lib/utils/markdown.ts @@ -10,7 +10,6 @@ export function escapeHtml(input: string): string { export function parseInline(input: string): string { let value = escapeHtml(input); - // Render tag token groups like [[work, vibe]] as visual chips in preview. value = value.replace(/\[\[([^[\]]+)\]\]/g, (match, rawGroup: string) => { const tags = rawGroup .split(",") @@ -25,7 +24,6 @@ export function parseInline(input: string): string { return `${chips}`; }); - // Render hashtag-style tags (#Work) as chips in preview. value = value.replace( /(^|\s)#([A-Za-z0-9][A-Za-z0-9_-]*)\b/g, (_, leading: string, tag: string) =>