feat: add tabbed Settings, 10 themes with custom accent/text color pickers, main project directory selector, and export filename/directory controls

This commit is contained in:
Schwitzz
2026-10-30 14:06:46 -05:00
parent fa22a755ee
commit 1d209fedd2
8 changed files with 1660 additions and 648 deletions
+109 -1
View File
@@ -373,6 +373,7 @@ export const api = {
port: number;
activePort?: number;
startOnBoot: boolean;
projectDirectory?: string;
}> => {
const endpoints = [
"/api/system/server-config",
@@ -388,6 +389,7 @@ export const api = {
port: number;
activePort?: number;
startOnBoot: boolean;
projectDirectory?: string;
};
return {
online: true,
@@ -395,6 +397,7 @@ export const api = {
port: data.port,
...(data.activePort !== undefined ? { activePort: data.activePort } : {}),
startOnBoot: data.startOnBoot,
...(data.projectDirectory ? { projectDirectory: data.projectDirectory } : {}),
};
}
} catch {
@@ -410,7 +413,12 @@ export const api = {
},
updateServerConfig: async (
patch: { enabled?: boolean; port?: number; startOnBoot?: boolean },
patch: {
enabled?: boolean;
port?: number;
startOnBoot?: boolean;
projectDirectory?: string;
},
portHint?: number,
): Promise<{
online: boolean;
@@ -418,6 +426,7 @@ export const api = {
port: number;
activePort?: number;
startOnBoot: boolean;
projectDirectory?: string;
}> => {
const endpoints = [
"/api/system/server-config",
@@ -438,6 +447,7 @@ export const api = {
port: number;
activePort?: number;
startOnBoot: boolean;
projectDirectory?: string;
};
return {
online: true,
@@ -445,6 +455,7 @@ export const api = {
port: data.port,
...(data.activePort !== undefined ? { activePort: data.activePort } : {}),
startOnBoot: data.startOnBoot,
...(data.projectDirectory ? { projectDirectory: data.projectDirectory } : {}),
};
}
} catch {
@@ -456,6 +467,103 @@ export const api = {
enabled: patch.enabled ?? true,
port: patch.port ?? portHint ?? 8081,
startOnBoot: patch.startOnBoot ?? false,
...(patch.projectDirectory ? { projectDirectory: patch.projectDirectory } : {}),
};
},
listSystemDirectories: async (
dirPath?: string,
portHint?: number,
): Promise<{
online: boolean;
currentPath: string;
parentPath: string | null;
homePath: string;
directories: { name: string; path: string }[];
}> => {
const query = dirPath ? `?path=${encodeURIComponent(dirPath)}` : "";
const endpoints = [
`/api/system/list-dirs${query}`,
...(portHint ? [`http://localhost:${portHint}/api/system/list-dirs${query}`] : []),
];
for (const url of endpoints) {
try {
const res = await fetch(url, { credentials: "include" });
const ct = res.headers.get("content-type") ?? "";
if (res.ok && ct.includes("application/json")) {
const data = (await res.json()) as {
currentPath: string;
parentPath: string | null;
homePath: string;
directories: { name: string; path: string }[];
};
return {
online: true,
currentPath: data.currentPath,
parentPath: data.parentPath,
homePath: data.homePath,
directories: data.directories,
};
}
} catch {
/* try next */
}
}
return {
online: false,
currentPath: dirPath || "~/Documents/PubScript",
parentPath: null,
homePath: "~",
directories: [],
};
},
saveExportToDirectory: async (
params: {
directory: string;
fileName: string;
data: Uint8Array | string;
},
portHint?: number,
): Promise<{ savedOnServer: boolean; savedPath?: string }> => {
const bytes =
typeof params.data === "string"
? new TextEncoder().encode(params.data)
: params.data;
let binary = "";
const chunkSize = 0x8000;
for (let i = 0; i < bytes.length; i += chunkSize) {
binary += String.fromCharCode(...bytes.subarray(i, i + chunkSize));
}
const base64Data = btoa(binary);
const endpoints = [
"/api/system/save-export",
...(portHint ? [`http://localhost:${portHint}/api/system/save-export`] : []),
];
for (const url of endpoints) {
try {
const res = await fetch(url, {
method: "POST",
credentials: "include",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
directory: params.directory,
fileName: params.fileName,
base64Data,
}),
});
const ct = res.headers.get("content-type") ?? "";
if (res.ok && ct.includes("application/json")) {
const out = (await res.json()) as { ok?: boolean; savedPath?: string };
if (out.ok && out.savedPath) {
return { savedOnServer: true, savedPath: out.savedPath };
}
}
} catch {
/* try next */
}
}
return { savedOnServer: false };
},
};