separate window for doll editor

This commit is contained in:
2025-12-22 01:11:38 +08:00
parent 2b368fe556
commit e4fdcd44c6
10 changed files with 412 additions and 105 deletions

View File

@@ -0,0 +1,117 @@
<script lang="ts">
import { onMount } from "svelte";
import { page } from "$app/stores";
import { invoke } from "@tauri-apps/api/core";
import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import DollEditor from "./doll-editor.svelte";
import type { DollDto } from "../../../../types/bindings/DollDto";
import type { CreateDollDto } from "../../../../types/bindings/CreateDollDto";
import type { UpdateDollDto } from "../../../../types/bindings/UpdateDollDto";
let mode: "create" | "edit" = "create";
let dollId: string | null = null;
let loading = true;
let error: string | null = null;
let initialName = "";
let initialBodyColor = "#FFFFFF";
let initialOutlineColor = "#000000";
onMount(async () => {
// Check URL search params for ID
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get("id");
if (id) {
mode = "edit";
dollId = id;
await fetchDoll(id);
} else {
mode = "create";
loading = false;
}
});
async function fetchDoll(id: string) {
loading = true;
try {
const doll: DollDto = await invoke("get_doll", { id });
initialName = doll.name;
initialBodyColor = doll.configuration.colorScheme.body;
initialOutlineColor = doll.configuration.colorScheme.outline;
} catch (e) {
error = (e as Error)?.message ?? String(e);
console.error("Failed to fetch doll:", e);
} finally {
loading = false;
}
}
async function handleSave(
name: string,
bodyColor: string,
outlineColor: string,
) {
try {
if (mode === "create") {
const dto: CreateDollDto = {
name,
configuration: {
colorScheme: {
body: bodyColor,
outline: outlineColor,
},
},
};
await invoke("create_doll", { dto });
} else if (dollId) {
const dto: UpdateDollDto = {
name,
configuration: {
colorScheme: {
body: bodyColor,
outline: outlineColor,
},
},
};
await invoke("update_doll", { id: dollId, dto });
}
// Close window on success
await getCurrentWebviewWindow().close();
} catch (e) {
error = (e as Error)?.message ?? String(e);
console.error("Failed to save doll:", e);
}
}
async function handleCancel() {
await getCurrentWebviewWindow().close();
}
</script>
<div class="w-screen h-screen bg-base-100">
{#if loading}
<div class="flex h-full items-center justify-center">
<span class="loading loading-spinner loading-lg"></span>
</div>
{:else if error}
<div class="flex h-full flex-col items-center justify-center p-4">
<div class="alert alert-error">
<span>{error}</span>
</div>
<button class="btn btn-sm mt-4" on:click={handleCancel}>Close</button>
</div>
{:else}
<DollEditor
isOpen={true}
standalone={true}
{mode}
{initialName}
{initialBodyColor}
{initialOutlineColor}
onSave={handleSave}
onCancel={handleCancel}
/>
{/if}
</div>

View File

@@ -12,6 +12,7 @@
outlineColor: string,
) => void;
export let onCancel: () => void;
export let standalone = false;
let name = initialName;
let bodyColor = initialBodyColor;
@@ -29,9 +30,84 @@
}
</script>
{#if isOpen}
<div class="modal modal-open">
<div class="modal-box">
{#if !standalone}
{#if isOpen}
<div class="modal modal-open">
<div class="modal-box">
<h3 class="font-bold text-lg">
{#if mode === "create"}
Create New Doll
{:else}
Edit Doll
{/if}
</h3>
<div class="form-control w-full mt-4">
<label class="label">
<span class="label-text">Name</span>
</label>
<input
type="text"
placeholder="Doll Name"
class="input input-bordered w-full"
bind:value={name}
/>
</div>
<div class="flex justify-center mt-4">
<DollPreview {bodyColor} {outlineColor} />
</div>
<div class="form-control w-full mt-2">
<label class="label">
<span class="label-text">Body Color</span>
</label>
<div class="flex gap-2">
<input
type="color"
class="input input-bordered w-12 p-1 h-10"
bind:value={bodyColor}
/>
<input
type="text"
class="input input-bordered w-full"
bind:value={bodyColor}
/>
</div>
</div>
<div class="form-control w-full mt-2">
<label class="label">
<span class="label-text">Outline Color</span>
</label>
<div class="flex gap-2">
<input
type="color"
class="input input-bordered w-12 p-1 h-10"
bind:value={outlineColor}
/>
<input
type="text"
class="input input-bordered w-full"
bind:value={outlineColor}
/>
</div>
</div>
<div class="modal-action">
<button class="btn" on:click={onCancel}>Cancel</button>
<button
class="btn btn-primary"
on:click={handleSave}
disabled={!name.trim()}
>
{#if mode === "create"}
Create
{:else}
Save
{/if}
</button>
</div>
</div>
</div>
{/if}
{:else}
<div class="h-full w-full bg-base-100 p-4 flex flex-col">
<h3 class="font-bold text-lg">
{#if mode === "create"}
Create New Doll
@@ -87,7 +163,7 @@
/>
</div>
</div>
<div class="modal-action">
<div class="mt-auto pt-4 flex justify-end gap-2">
<button class="btn" on:click={onCancel}>Cancel</button>
<button
class="btn btn-primary"
@@ -102,5 +178,4 @@
</button>
</div>
</div>
</div>
{/if}
{/if}

View File

@@ -0,0 +1,5 @@
<script lang="ts">
import DollEditorWindow from "../doll-editor-window.svelte";
</script>
<DollEditorWindow />

View File

@@ -1,27 +1,46 @@
<script lang="ts">
import { onMount } from "svelte";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import type { DollDto } from "../../../../types/bindings/DollDto";
import type { UserProfile } from "../../../../types/bindings/UserProfile";
import type { AppData } from "../../../../types/bindings/AppData";
import type { CreateDollDto } from "../../../../types/bindings/CreateDollDto";
import type { UpdateDollDto } from "../../../../types/bindings/UpdateDollDto";
import DollsList from "./dolls-list.svelte";
import DollEditor from "./doll-editor.svelte";
let dolls: DollDto[] = [];
let user: UserProfile | null = null;
let loading = false;
let error: string | null = null;
let isEditorOpen = false;
let editorMode: "create" | "edit" = "create";
let editingDollId: string | null = null;
let editorInitialName = "";
let editorInitialBodyColor = "#FFFFFF";
let editorInitialOutlineColor = "#000000";
// We still keep the focus listener as a fallback, but the websocket events should handle most updates
onMount(() => {
refreshDolls();
// Set up listeners
const unlistenCreated = listen("doll.created", (event) => {
console.log("Received doll.created event", event);
refreshDolls();
});
const unlistenUpdated = listen("doll.updated", (event) => {
console.log("Received doll.updated event", event);
refreshDolls();
});
const unlistenDeleted = listen("doll.deleted", (event) => {
console.log("Received doll.deleted event", event);
refreshDolls();
});
// Listen for focus events to refresh data when returning from editor window
window.addEventListener("focus", refreshDolls);
return async () => {
window.removeEventListener("focus", refreshDolls);
(await unlistenCreated)();
(await unlistenUpdated)();
(await unlistenDeleted)();
};
});
async function refreshDolls() {
@@ -39,86 +58,12 @@
}
}
function openCreateModal() {
editorMode = "create";
editorInitialName = "";
editorInitialBodyColor = "#FFFFFF";
editorInitialOutlineColor = "#000000";
isEditorOpen = true;
async function openCreateModal() {
await invoke("open_doll_editor_window", { dollId: null });
}
function openEditModal(doll: DollDto) {
editorMode = "edit";
editingDollId = doll.id;
editorInitialName = doll.name;
editorInitialBodyColor = doll.configuration.colorScheme.body;
editorInitialOutlineColor = doll.configuration.colorScheme.outline;
isEditorOpen = true;
}
function closeEditor() {
isEditorOpen = false;
editingDollId = null;
}
async function handleSave(
name: string,
bodyColor: string,
outlineColor: string,
) {
if (editorMode === "create") {
await handleCreateDoll(name, bodyColor, outlineColor);
} else {
await handleUpdateDoll(name, bodyColor, outlineColor);
}
}
async function handleCreateDoll(
name: string,
bodyColor: string,
outlineColor: string,
) {
try {
const dto: CreateDollDto = {
name,
configuration: {
colorScheme: {
body: bodyColor,
outline: outlineColor,
},
},
};
await invoke("create_doll", { dto });
closeEditor();
await refreshDolls();
} catch (e) {
error = (e as Error)?.message ?? String(e);
}
}
async function handleUpdateDoll(
name: string,
bodyColor: string,
outlineColor: string,
) {
if (!editingDollId) return;
try {
const dto: UpdateDollDto = {
name,
configuration: {
colorScheme: {
body: bodyColor,
outline: outlineColor,
},
},
};
await invoke("update_doll", { id: editingDollId, dto });
closeEditor();
await refreshDolls();
} catch (e) {
error = (e as Error)?.message ?? String(e);
}
async function openEditModal(doll: DollDto) {
await invoke("open_doll_editor_window", { dollId: doll.id });
}
async function handleSetActiveDoll(dollId: string) {
@@ -157,14 +102,4 @@
onSetActiveDoll={handleSetActiveDoll}
onRemoveActiveDoll={handleRemoveActiveDoll}
/>
<DollEditor
isOpen={isEditorOpen}
mode={editorMode}
initialName={editorInitialName}
initialBodyColor={editorInitialBodyColor}
initialOutlineColor={editorInitialOutlineColor}
onSave={handleSave}
onCancel={closeEditor}
/>
</div>