Skip to content

Commit 55ebb4c

Browse files
prplxprplx
authored andcommitted
feat(writer): inline scene management and reliable export downloads
1 parent 31c1e44 commit 55ebb4c

10 files changed

Lines changed: 1403 additions & 364 deletions

File tree

electron/main.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { app, BrowserWindow, ipcMain, dialog } from "electron";
22
import path from "path";
33
import { existsSync } from "fs";
4+
import { writeFile } from "fs/promises";
45
import { pathToFileURL } from "url";
56

67
const isDev = !app.isPackaged;
@@ -213,6 +214,29 @@ ipcMain.handle("window:getPlatform", () => {
213214
return process.platform;
214215
});
215216

217+
ipcMain.handle("file:save", async (_event, payload: { filename?: unknown; base64Data?: unknown }) => {
218+
if (!payload || typeof payload !== "object") {
219+
return { ok: false, canceled: true };
220+
}
221+
const filename = String(payload.filename || "export.txt").trim() || "export.txt";
222+
const base64Data = String(payload.base64Data || "").trim();
223+
if (!base64Data) {
224+
throw new Error("Missing file payload");
225+
}
226+
227+
const result = await dialog.showSaveDialog(mainWindow ?? undefined, {
228+
defaultPath: filename,
229+
buttonLabel: "Save"
230+
});
231+
if (result.canceled || !result.filePath) {
232+
return { ok: false, canceled: true };
233+
}
234+
235+
const buffer = Buffer.from(base64Data, "base64");
236+
await writeFile(result.filePath, buffer);
237+
return { ok: true, canceled: false, filePath: result.filePath };
238+
});
239+
216240
app.on("second-instance", () => {
217241
if (mainWindow) {
218242
if (mainWindow.isMinimized()) mainWindow.restore();

electron/preload.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ contextBridge.exposeInMainWorld("electronAPI", {
66
close: () => ipcRenderer.invoke("window:close"),
77
isMaximized: () => ipcRenderer.invoke("window:isMaximized"),
88
getPlatform: () => ipcRenderer.invoke("window:getPlatform"),
9+
saveFile: (filename: string, base64Data: string) => ipcRenderer.invoke("file:save", { filename, base64Data }),
910
onMaximizedChange: (callback: (maximized: boolean) => void) => {
1011
ipcRenderer.on("window:maximized", (_event, maximized: boolean) => {
1112
callback(maximized);

package-lock.json

Lines changed: 72 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "vellium",
33
"private": true,
4-
"version": "0.3.1",
4+
"version": "0.3.5",
55
"type": "module",
66
"main": "dist-electron/main.cjs",
77
"scripts": {
@@ -28,6 +28,7 @@
2828
"better-sqlite3": "^12.6.2",
2929
"clsx": "^2.1.1",
3030
"cors": "^2.8.5",
31+
"docx": "^9.5.3",
3132
"express": "^4.21.0",
3233
"mammoth": "^1.11.0",
3334
"marked": "^17.0.3",

0 commit comments

Comments
 (0)