- add and expand feature stores for entries, fragments, todos, lists, settings - move CRUD logic into store helpers and simplify component state handling - update SidePanel + button to create section-specific items - switch fragment UX to view-first with edit/create modes - add and update docs for store-based state management - remove deprecated account route
6.2 KiB
Wiring Frontend to the C# Backend
This document explains how to connect the Journal.App frontend to the C# backend in this repository.
Current Backend Reality
In this repo today, the C# backend projects in Journal.slnx are:
Journal.CoreJournal.SidecarJournal.SmokeTests
There is currently no Journal.Api project in the solution file, so the primary integration path is:
- Frontend (Svelte/Tauri) -> Tauri bridge ->
Journal.Sidecar(stdin/stdout JSON protocol)
Command Protocol (C#)
Journal.Core.Entry.HandleCommandAsync accepts a JSON command envelope and returns:
- success:
{ "ok": true, "data": ... } - failure:
{ "ok": false, "error": "..." }
Command model (Journal.Core/Models/Command.cs):
{
"action": "entries.list",
"correlationId": "optional-string",
"id": "optional",
"type": "optional",
"tag": "optional",
"payload": {}
}
Useful actions for frontend wiring:
entries.listentries.loadentries.savesearch.entriesvault.load_allvault.save_current_monthdb.statusdb.hydrate_workspace
Recommended Integration (Sidecar Bridge)
Use a small frontend client that sends commands through one bridge function. The bridge can be backed by:
- a Tauri command that talks to a managed sidecar process, or
- a local HTTP adapter (if you add one).
1. Define shared frontend command/response types
Create Journal.App/src/lib/backend/types.ts:
export type BackendCommand = {
action: string;
correlationId?: string;
id?: string;
type?: string;
tag?: string;
payload?: unknown;
};
export type BackendOk<T> = { ok: true; data: T };
export type BackendErr = { ok: false; error: string };
export type BackendResponse<T> = BackendOk<T> | BackendErr;
2. Create one backend client entrypoint
Create Journal.App/src/lib/backend/client.ts:
import { invoke } from "@tauri-apps/api/core";
import type { BackendCommand, BackendResponse } from "./types";
export async function sendCommand<T>(command: BackendCommand): Promise<T> {
const response = await invoke<BackendResponse<T>>("sidecar_command", { command });
if (!response.ok) {
throw new Error(response.error || "Backend command failed");
}
return response.data;
}
This keeps all UI code backend-agnostic.
3. Build domain helpers (entries example)
Create Journal.App/src/lib/backend/entries.ts:
import { sendCommand } from "./client";
export async function listEntries(dataDirectory?: string) {
return sendCommand<string[]>({
action: "entries.list",
payload: { dataDirectory }
});
}
export async function loadEntry(filePath: string) {
return sendCommand<{ filePath: string; content: string; section?: string }>({
action: "entries.load",
payload: { filePath }
});
}
export async function saveEntry(args: {
filePath?: string;
content: string;
title?: string;
section?: string;
date?: string;
}) {
return sendCommand<{ filePath: string }>({
action: "entries.save",
payload: args
});
}
4. Use client in UI state
In page/component code:
- on panel item click: call
loadEntry(filePath) - on editor save button: call
saveEntry({ filePath, content }) - on app init: call
listEntries()to populate list
Tauri Bridge Notes
Your frontend should not spawn/process-manage the sidecar directly. Keep that in the Tauri layer.
Bridge responsibilities:
- start and keep one sidecar process alive
- write command JSON lines to sidecar stdin
- read stdout lines and map responses by
correlationId - return parsed response to frontend
- restart sidecar if it crashes
If you have not implemented this yet, create one Tauri command such as:
sidecar_command(command)
and route all frontend calls through it.
Vault/Auth Flow
Recommended startup sequence:
- Prompt for vault password in UI.
- Call
vault.load_all(ordb.hydrate_workspace) once. - Backend stores session password (
DatabaseSessionService) for subsequent commands. - Continue with
entries.list,entries.load, etc.
Do not store raw vault password in long-lived frontend state.
Error Handling Pattern
Always normalize backend errors in one place:
- backend client throws
Error(message)whenok: false - UI catches and displays your custom modal
- include
correlationIdon commands for tracing/logging
Optional HTTP Path (If You Add Journal.Api)
If you later add Journal.Api with POST /api/command, keep the same command envelope and swap transport only:
- replace
invoke("sidecar_command", ...)withfetch("/api/command", ...) - keep
sendCommandinterface unchanged
That lets UI code remain identical.
Minimal Next Steps
- Add
src/lib/backend/types.ts,client.ts,entries.ts. - Wire
EditorPanelsave button toentries.save. - Wire
SidePanelitem load toentries.load. - Add vault unlock modal +
vault.load_allon startup. - Keep all backend calls behind
sendCommandonly.
Frontend Store Architecture (Current)
Current frontend uses feature stores in Journal.App/src/lib/stores/:
entries.ts->entriesStorefragments.ts->fragmentsStoretodos.ts->todoListsStore,todosStorelists.ts->listsStoresettings.ts->settingsTags,settingsFragmentTypes
Current pattern is store-first for most feature CRUD and parsing (especially fragments and todos), with UI components invoking store helpers.
State/CRUD Gaps Still Needed
To fully standardize state management:
- Move settings add/edit/remove logic into
settings.tshelper functions (currently in route component code). - Add full CRUD helpers for
entries.tsandlists.ts(update/remove/reorder, not only draft creation). - Make todo list metadata + todo items update atomically through a single store API wrapper.
- Move calendar-created entries out of local component state into a dedicated calendar store.
- Add persistence/hydration strategy between stores and backend (
entries.load/save,vault.load_all, etc.).
Recommended Rule
- Keep all feature data mutations in store helper APIs.
- Keep route/component files focused on view state and command orchestration.
- Keep backend transport (
sendCommand) separate from pure local store mutation helpers, then compose both in thin feature services.