diff --git a/src/lib/client/ui/modal/UnsavedChangesModal.svelte b/src/lib/client/ui/modal/UnsavedChangesModal.svelte
new file mode 100644
index 0000000..f3c0e66
--- /dev/null
+++ b/src/lib/client/ui/modal/UnsavedChangesModal.svelte
@@ -0,0 +1,31 @@
+
+
+ unsavedChanges.confirmDiscard()}
+ on:cancel={() => unsavedChanges.cancelDiscard()}
+/>
diff --git a/src/lib/client/utils/unsavedChanges.svelte.ts b/src/lib/client/utils/unsavedChanges.svelte.ts
new file mode 100644
index 0000000..2c02f64
--- /dev/null
+++ b/src/lib/client/utils/unsavedChanges.svelte.ts
@@ -0,0 +1,78 @@
+/**
+ * Utility for detecting and handling unsaved changes
+ */
+
+let hasUnsavedChanges = $state(false);
+let showWarningModal = $state(false);
+let resolveNavigation: ((value: boolean) => void) | null = null;
+
+export function useUnsavedChanges() {
+ return {
+ /**
+ * Mark the page as having unsaved changes
+ */
+ markDirty() {
+ hasUnsavedChanges = true;
+ },
+
+ /**
+ * Mark the page as clean (changes saved)
+ */
+ markClean() {
+ hasUnsavedChanges = false;
+ },
+
+ /**
+ * Check if there are unsaved changes
+ */
+ get isDirty() {
+ return hasUnsavedChanges;
+ },
+
+ /**
+ * Get modal state
+ */
+ get showModal() {
+ return showWarningModal;
+ },
+
+ /**
+ * Request navigation confirmation
+ * Returns a promise that resolves to true if navigation should proceed
+ */
+ confirmNavigation(): Promise {
+ if (!hasUnsavedChanges) {
+ return Promise.resolve(true);
+ }
+
+ showWarningModal = true;
+
+ return new Promise((resolve) => {
+ resolveNavigation = resolve;
+ });
+ },
+
+ /**
+ * User confirmed navigation (discard changes)
+ */
+ confirmDiscard() {
+ showWarningModal = false;
+ hasUnsavedChanges = false;
+ if (resolveNavigation) {
+ resolveNavigation(true);
+ resolveNavigation = null;
+ }
+ },
+
+ /**
+ * User cancelled navigation (stay on page)
+ */
+ cancelDiscard() {
+ showWarningModal = false;
+ if (resolveNavigation) {
+ resolveNavigation(false);
+ resolveNavigation = null;
+ }
+ }
+ };
+}