diff --git a/services/vault-service.js b/services/vault-service.js index 974bfde..2fbfee6 100644 --- a/services/vault-service.js +++ b/services/vault-service.js @@ -78,16 +78,17 @@ export const vaultService = { if (window.__vaultConfig) { window.__vaultConfig.id = newName; } + + history.replaceState(null, "", "/?vault=" + encodeURIComponent(newName)); } return this.listVaults(); }, async deleteVault(id) { - await fetchJson( - API_BASE + "/remove?vault=" + encodeURIComponent(id), - { method: "DELETE" }, - ); + await fetchJson(API_BASE + "/remove?vault=" + encodeURIComponent(id), { + method: "DELETE", + }); const wasCurrentVault = id === this.getCurrentVaultId(); diff --git a/shims/electron/remote/dialog.js b/shims/electron/remote/dialog.js index bb9da0f..aae3013 100644 --- a/shims/electron/remote/dialog.js +++ b/shims/electron/remote/dialog.js @@ -1,3 +1,9 @@ +import { + showMessageDialog, + showConfirmDialog, + showPromptDialog, +} from "../../../ui/bootstrap.js"; + export const dialogShim = { async showOpenDialog(browserWindow, options) { // TODO: implement custom modal with server-side file listing @@ -5,21 +11,28 @@ export const dialogShim = { return { canceled: true, filePaths: [] }; }, - // TODO: replace prompt() with a styled modal (matching vault manager style) async showSaveDialog(browserWindow, options) { if (typeof browserWindow === "object" && !options) { options = browserWindow; } + const defaultName = - options?.defaultPath?.split(/[/\\]/).pop() || "download"; - const name = prompt("Save as:", defaultName); + options?.defaultPath?.split(/[\/\\]/).pop() || "download"; + const name = await showPromptDialog( + "Save File", + "Save as:", + "filename", + defaultName, + "Save", + ); + if (!name) { return { canceled: true, filePath: undefined }; } + return { canceled: false, filePath: "/downloads/" + name }; }, - // TODO: replace alert() with a styled modal (matching vault manager style) async showMessageBox(browserWindow, options) { if (typeof browserWindow === "object" && !options) { options = browserWindow; @@ -30,21 +43,20 @@ export const dialogShim = { const message = options.message || ""; const detail = options.detail || ""; const buttons = options.buttons || ["OK"]; + const fullMessage = message + (detail ? "\n\n" + detail : ""); if (buttons.length <= 1) { - alert(message + (detail ? "\n\n" + detail : "")); + await showMessageDialog(options.title || "Message", fullMessage); return { response: 0, checkboxChecked: false }; } - const result = confirm( - message + - (detail ? "\n\n" + detail : "") + - '\n\n[OK] = "' + - buttons[0] + - '", [Cancel] = "' + - buttons[1] + - '"', + const result = await showConfirmDialog( + options.title || "Confirm", + message, + detail, + buttons[0], ); + return { response: result ? 0 : 1, checkboxChecked: false, @@ -53,6 +65,6 @@ export const dialogShim = { showErrorBox(title, content) { console.error("[shim:dialog] Error:", title, content); - alert(title + "\n\n" + content); + showMessageDialog(title, content); }, }; diff --git a/ui/bootstrap.js b/ui/bootstrap.js index bb12d90..0028722 100644 --- a/ui/bootstrap.js +++ b/ui/bootstrap.js @@ -9,3 +9,66 @@ export function showVaultManager() { props: { vaultService }, }); } + +export function showMessageDialog(title, message) { + return new Promise((resolve) => { + const dialog = new window.IgnisUI.MessageDialog({ + target: document.body, + props: { title, message }, + }); + + dialog.$on("confirm", () => { + dialog.$destroy(); + resolve(); + }); + }); +} + +export function showConfirmDialog( + title, + message, + description, + confirmText = "OK", +) { + return new Promise((resolve) => { + const dialog = new window.IgnisUI.ConfirmDialog({ + target: document.body, + props: { title, message, description, confirmText }, + }); + + dialog.$on("confirm", () => { + dialog.$destroy(); + resolve(true); + }); + + dialog.$on("cancel", () => { + dialog.$destroy(); + resolve(false); + }); + }); +} + +export function showPromptDialog( + title, + label, + placeholder = "", + value = "", + confirmText = "OK", +) { + return new Promise((resolve) => { + const dialog = new window.IgnisUI.PromptDialog({ + target: document.body, + props: { title, label, placeholder, value, confirmText }, + }); + + dialog.$on("confirm", (event) => { + dialog.$destroy(); + resolve(event.detail); + }); + + dialog.$on("cancel", () => { + dialog.$destroy(); + resolve(null); + }); + }); +} diff --git a/ui/components/layout/MessageDialog.svelte b/ui/components/layout/MessageDialog.svelte new file mode 100644 index 0000000..0386dab --- /dev/null +++ b/ui/components/layout/MessageDialog.svelte @@ -0,0 +1,69 @@ + + + + + + + +
+

{message}

+
+ + + + +
+ + diff --git a/ui/index.js b/ui/index.js index 1a15fc5..11fd0d4 100644 --- a/ui/index.js +++ b/ui/index.js @@ -1 +1,4 @@ export { default as VaultManager } from "./views/VaultManager.svelte"; +export { default as MessageDialog } from "./components/layout/MessageDialog.svelte"; +export { default as ConfirmDialog } from "./components/layout/ConfirmDialog.svelte"; +export { default as PromptDialog } from "./components/layout/PromptDialog.svelte"; diff --git a/ui/views/VaultManager.svelte b/ui/views/VaultManager.svelte index 1f7c85c..ffe8589 100644 --- a/ui/views/VaultManager.svelte +++ b/ui/views/VaultManager.svelte @@ -12,6 +12,7 @@ import Modal from "../components/layout/Modal.svelte"; import PromptDialog from "../components/layout/PromptDialog.svelte"; import ConfirmDialog from "../components/layout/ConfirmDialog.svelte"; + import MessageDialog from "../components/layout/MessageDialog.svelte"; import SearchInput from "../components/input/SearchInput.svelte"; import Button from "../components/input/Button.svelte"; import ListItem from "../components/display/ListItem.svelte"; @@ -27,6 +28,8 @@ let activeDialog = null; let targetVault = null; let dialogValue = ""; + let errorMessage = ""; + let pendingReload = false; const menuItems = [ { id: "rename", label: "Rename" }, @@ -108,12 +111,11 @@ try { vaults = await vaultService.createVault(name); + closeDialog(); } catch (err) { - alert("Failed to create vault: " + err.message); - return; + errorMessage = "Failed to create vault: " + err.message; + activeDialog = "error"; } - - closeDialog(); } async function onRenameConfirm(e) { @@ -128,15 +130,15 @@ try { vaults = await vaultService.renameVault(targetVault.id, trimmed); + closeDialog(); + + if (wasCurrentVault) { + currentVaultId = vaultService.getCurrentVaultId(); + pendingReload = true; + } } catch (err) { - alert("Failed to rename vault: " + err.message); - return; - } - - closeDialog(); - - if (wasCurrentVault) { - currentVaultId = vaultService.getCurrentVaultId(); + errorMessage = "Failed to rename vault: " + err.message; + activeDialog = "error"; } } @@ -154,7 +156,14 @@ vaultService.openVault(""); } } catch (err) { - alert("Failed to delete vault: " + err.message); + errorMessage = "Failed to delete vault: " + err.message; + activeDialog = "error"; + } + } + + function onModalClose() { + if (pendingReload) { + window.location.reload(); } } @@ -176,6 +185,7 @@ width="600px" bind:this={modalRef} on:escape={onEscape} + on:close={onModalClose} closeOnOverlayClick={false} > @@ -270,7 +280,7 @@ {/if} +{#if activeDialog === "error"} + { + activeDialog = null; + errorMessage = ""; + }} + /> +{/if} +