(chore) Introduced regions
This commit is contained in:
parent
ae70fbdae9
commit
b0cd38e54d
@ -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<AiHealthDto> {
|
||||
const data = await sendCommand<AiHealthDtoRaw>({
|
||||
action: "ai.health",
|
||||
@ -220,3 +219,4 @@ export async function coachWeekly(
|
||||
});
|
||||
return normalizeCoachPlan(data);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -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<ConversationDto[]> {
|
||||
const data = await sendCommand<ConversationDtoRaw[]>({
|
||||
action: "conversations.list",
|
||||
@ -193,3 +192,4 @@ export async function conversationChat(
|
||||
});
|
||||
return normalizeChatResult(data);
|
||||
}
|
||||
//#endregion
|
||||
|
||||
@ -1279,6 +1279,7 @@
|
||||
{#each $conversationsStore.items as conv}
|
||||
<li class:is-active={conv.id === $activeConversationStore.id}>
|
||||
{#if editingConversationId === conv.id}
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<input
|
||||
type="text"
|
||||
class="rename-input"
|
||||
|
||||
@ -10,8 +10,7 @@ import {
|
||||
type CoachSessionPayload,
|
||||
} from "$lib/backend/ai";
|
||||
|
||||
// ── Store shapes ────────────────────────────────────────────────
|
||||
|
||||
//#region Store Shapes
|
||||
type AiStatusState = {
|
||||
checking: boolean;
|
||||
health: AiHealthDto | null;
|
||||
@ -33,9 +32,9 @@ type ChatState = {
|
||||
busy: boolean;
|
||||
messages: ChatMessage[];
|
||||
};
|
||||
//#endregion
|
||||
|
||||
// ── Stores ──────────────────────────────────────────────────────
|
||||
|
||||
//#region Stores
|
||||
export const aiStatusStore = writable<AiStatusState>({
|
||||
checking: false,
|
||||
health: null,
|
||||
@ -52,9 +51,9 @@ export const chatStateStore = writable<ChatState>({
|
||||
busy: false,
|
||||
messages: [],
|
||||
});
|
||||
//#endregion
|
||||
|
||||
// ── Actions ─────────────────────────────────────────────────────
|
||||
|
||||
//#region Actions
|
||||
export async function checkAiHealth(): Promise<void> {
|
||||
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
|
||||
|
||||
@ -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<ConversationsState>({
|
||||
items: [],
|
||||
busy: false,
|
||||
@ -41,9 +40,9 @@ export const activeConversationStore = writable<ActiveConversationState>({
|
||||
busy: false,
|
||||
error: "",
|
||||
});
|
||||
//#endregion
|
||||
|
||||
// ── Actions ─────────────────────────────────────────────────────
|
||||
|
||||
//#region Actions
|
||||
export async function loadConversations(): Promise<void> {
|
||||
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<void> {
|
||||
export async function sendConversationMessage(prompt: string): Promise<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
...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
|
||||
|
||||
@ -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<TodoListMeta[]>([]);
|
||||
export const todosStore = writable<Record<string, TodoItem[]>>({});
|
||||
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<void> {
|
||||
todosBusyStore.set(true);
|
||||
try {
|
||||
@ -82,9 +79,9 @@ export async function hydrateTodos(): Promise<void> {
|
||||
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
|
||||
|
||||
@ -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 `<span class="markdown-tag-list">${chips}</span>`;
|
||||
});
|
||||
|
||||
// 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) =>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user