144 lines
3.8 KiB
TypeScript
144 lines
3.8 KiB
TypeScript
import type { BackendCommand } from "$lib/backend/types";
|
|
|
|
type InvokeArgs = Record<string, unknown> | undefined;
|
|
|
|
type WindowWithTauri = Window & {
|
|
__TAURI_INTERNALS__?: unknown;
|
|
};
|
|
|
|
type UiSettingsPayload = {
|
|
tags?: string[];
|
|
fragmentTypes?: string[];
|
|
};
|
|
|
|
type FetchJsonOptions = {
|
|
keepalive?: boolean;
|
|
};
|
|
|
|
const UI_SETTINGS_KEY = "journal.ui.settings";
|
|
|
|
function normalizedApiBase(): string {
|
|
const configured = import.meta.env.VITE_JOURNAL_API_BASE?.trim();
|
|
if (!configured) {
|
|
return "/api";
|
|
}
|
|
|
|
return configured.endsWith("/") ? configured.slice(0, -1) : configured;
|
|
}
|
|
|
|
export function isTauriRuntime(): boolean {
|
|
if (typeof window === "undefined") {
|
|
return false;
|
|
}
|
|
|
|
return Object.prototype.hasOwnProperty.call(window as WindowWithTauri, "__TAURI_INTERNALS__");
|
|
}
|
|
|
|
function readUiSettingsFromLocalStorage(): UiSettingsPayload {
|
|
if (typeof window === "undefined") {
|
|
return {};
|
|
}
|
|
|
|
const raw = window.localStorage.getItem(UI_SETTINGS_KEY);
|
|
if (!raw) {
|
|
return {};
|
|
}
|
|
|
|
try {
|
|
const parsed = JSON.parse(raw) as UiSettingsPayload;
|
|
return {
|
|
tags: Array.isArray(parsed.tags) ? parsed.tags : undefined,
|
|
fragmentTypes: Array.isArray(parsed.fragmentTypes) ? parsed.fragmentTypes : undefined
|
|
};
|
|
} catch {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function writeUiSettingsToLocalStorage(payload: UiSettingsPayload): void {
|
|
if (typeof window === "undefined") {
|
|
return;
|
|
}
|
|
|
|
const safePayload: UiSettingsPayload = {
|
|
tags: Array.isArray(payload.tags) ? payload.tags : undefined,
|
|
fragmentTypes: Array.isArray(payload.fragmentTypes) ? payload.fragmentTypes : undefined
|
|
};
|
|
|
|
window.localStorage.setItem(UI_SETTINGS_KEY, JSON.stringify(safePayload));
|
|
}
|
|
|
|
async function fetchJson<T>(path: string, init: RequestInit = {}, options: FetchJsonOptions = {}): Promise<T> {
|
|
const response = await fetch(`${normalizedApiBase()}${path}`, {
|
|
...init,
|
|
keepalive: options.keepalive === true,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
...(init.headers ?? {})
|
|
}
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
throw new Error(text || `Request failed (${response.status})`);
|
|
}
|
|
|
|
if (response.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
|
|
return (await response.json()) as T;
|
|
}
|
|
|
|
export async function invoke<T>(command: string, args?: InvokeArgs): Promise<T> {
|
|
if (isTauriRuntime()) {
|
|
const tauriCore = await import("@tauri-apps/api/core");
|
|
return tauriCore.invoke<T>(command, args);
|
|
}
|
|
|
|
switch (command) {
|
|
case "sidecar_command": {
|
|
const envelope = args?.command;
|
|
if (!envelope || typeof envelope !== "object") {
|
|
throw new Error("Missing command payload.");
|
|
}
|
|
|
|
const keepalive = args?.keepalive === true;
|
|
|
|
return fetchJson<T>(
|
|
"/command",
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify(envelope as BackendCommand)
|
|
},
|
|
{ keepalive }
|
|
);
|
|
}
|
|
case "get_sidecar_root":
|
|
return fetchJson<T>("/sidecar/root");
|
|
case "set_sidecar_root": {
|
|
const path = typeof args?.path === "string" ? args.path : "";
|
|
return fetchJson<T>("/sidecar/root", {
|
|
method: "POST",
|
|
body: JSON.stringify({ path })
|
|
});
|
|
}
|
|
case "get_ui_settings":
|
|
return readUiSettingsFromLocalStorage() as T;
|
|
case "set_ui_settings": {
|
|
const tags = Array.isArray(args?.tags) ? (args?.tags as string[]) : undefined;
|
|
const fragmentTypes =
|
|
Array.isArray(args?.fragmentTypes) ? (args?.fragmentTypes as string[]) :
|
|
Array.isArray(args?.fragment_types) ? (args?.fragment_types as string[]) :
|
|
undefined;
|
|
|
|
writeUiSettingsToLocalStorage({ tags, fragmentTypes });
|
|
return undefined as T;
|
|
}
|
|
case "shutdown":
|
|
return undefined as T;
|
|
default:
|
|
throw new Error(`Unsupported command in web runtime: ${command}`);
|
|
}
|
|
}
|