diff --git a/GEMINI.md b/GEMINI.md index 736e1ab1..6fd9bfb1 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -11,10 +11,11 @@ This project is the frontend UI/UX for the Aether (AE) system, built with Svelte ## 🏗️ Architecture & Standards -### Svelte v5 (Runes) -- **State Management:** Use `$state` and `$derived`. Props are passed via `$props()` and can be `$bindable`. -- **Event Handling:** Use lowercase `onevent` attributes (e.g., `onclick`). Handle `preventDefault` inside functions. -- **Dexie Integration:** `liveQuery` results are observables and require the `$` prefix (e.g., `$lq__obj`) in templates. Subscribe in `onMount` to avoid SSR issues. +### Reactivity & Stores +- **Svelte 5 Runes:** Use `$state`, `$derived`, and `$effect` for component-level reactivity. +- **Dexie LiveQuery:** Results from `liveQuery` are observables. You **MUST** use the `$` prefix (e.g., `$lq__obj`) in templates and logic to subscribe to the live data. Failure to do so will result in accessing the observable object instead of its data. +- **Persistence:** Use `svelte-persisted-store` for data that must survive page refreshes (e.g., `ae_loc`, `journals_loc`). +- **Initialization:** Always initialize reactive state (`$state`) outside of `$props()` destructuring. Use `untrack` inside `$effect` when synchronizing props to local state to prevent infinite loops. ### ID Convention (Triple-ID Pattern) - **Standard:** Use `id`, `[obj_type]_id`, and `[obj_type]_id_random` consistently. diff --git a/TODO.md b/TODO.md index 150541e9..f8725b9a 100644 --- a/TODO.md +++ b/TODO.md @@ -31,6 +31,7 @@ This is a list of tasks to be completed before the next event/show/conference. - [ ] **Phase 2: UI/UX Excellence** - [x] Implement "Quick Add" for high-velocity entry. - [x] Add rapid append/prepend functionality to existing entries. + - [x] Standardize Module, Journal, and Entry configuration modals (Aether Orange pattern). - [ ] Ensure full cross-platform responsiveness (Mobile -> Workstation). - [ ] **Phase 3: Content Portability** - [x] Implement Structured Markdown import (with metadata). diff --git a/documentation/AE_UI_Journals_module_update_2026.md b/documentation/AE_UI_Journals_module_update_2026.md index 065c3f89..5a28e69f 100644 --- a/documentation/AE_UI_Journals_module_update_2026.md +++ b/documentation/AE_UI_Journals_module_update_2026.md @@ -71,8 +71,9 @@ This document outlines the modernization of the Journals module UI in the Svelte ### Phase 4: Polish & Security (ACTIVE) - [x] Implement Auto-Save toggle and visual status indicators. - [x] Extract decryption workflow to non-reactive helper. -- [ ] Solidify E2EE passcode system for Journals and Entries. +- [x] **Standardize Configuration Modals:** Refactored Module, Journal, and Entry configuration into a unified tabbed UI. - [x] **RESOLVED:** Decryption workflow stability (Fixed via dependency isolation). +- [ ] Solidify E2EE passcode system for Journals and Entries. - [ ] Audit encryption flow for Quick Added and Imported entries. - [ ] Integrate Outbound Email sharing. @@ -80,14 +81,25 @@ This document outlines the modernization of the Journals module UI in the Svelte ## 🧠 Lessons Learned: Solving the Svelte 5 Reactivity Hang -During the implementation of the Privacy/Decryption toggle, we encountered a critical browser hang caused by an infinite reactivity loop. Here is how we resolved it and the patterns we should follow in the future: +During the implementation of the Privacy/Decryption toggle and the new Configuration Modals, we encountered critical browser hangs caused by infinite reactivity loops. Here is how we resolved them: ### 1. Rigorous Dependency Isolation (`untrack`) Svelte 5 runes (`$effect`, `$derived`) automatically track **every** reactive variable read inside them. * **The Problem:** An effect would read `save_status` or `tmp_entry_obj.content` to decide if it should sync, but the act of syncing would update those same variables, re-triggering the effect. -* **The Fix:** Wrap any "check-only" state or store reads in `untrack(() => ... )`. This allows the effect to use the value without becoming a dependency of it. +* **The Fix:** Wrap any "check-only" state or store reads in `untrack(() => ... )`. This allows the effect to use the value without becoming a dependency of it. This is **CRITICAL** when initializing local state from props inside an effect. -### 2. Manual Deep Copying vs. Proxies +### 2. Standardized Modal UI ("Aether Orange") +We have established a unified design language for configuration interfaces: +* **Header/Footer:** Use `bg-orange-100 dark:bg-orange-900` with consistent borders. +* **Tabs:** Center-aligned tabbed navigation using Skeleton UI `btn` and `variant-filled-primary` / `variant-soft-surface`. +* **Icons:** Every tab and primary action should have a Lucide icon for better scannability. +* **Explicit Persistence:** All configuration modals must follow an "Edit working copy -> Save Changes" pattern to prevent accidental store/API churn. + +### 3. Dexie LiveQuery Subscriptions +* **The Problem:** Accessing `liveQuery` observables directly in templates results in `[object Object]` or `undefined` property errors. +* **The Mandate:** ALWAYS use the `$` prefix (e.g. `$lq__obj`) when passing or using data from a Dexie `liveQuery`. + +### 4. Manual Deep Copying vs. Proxies Svelte 5 state is backed by Proxies. * **The Problem:** Using `JSON.parse(JSON.stringify(proxy))` can sometimes trigger unexpected behavior or loops when used inside a reactive context. * **The Fix:** Implement a manual `deep_copy` helper or selective property assignment when syncing "Original" vs "Temporary" state. This ensures `orig_entry_obj` is a plain JS object, making the `has_unsaved_changes` check stable. diff --git a/src/routes/journals/JournalEntry_SettingsMenu.svelte b/src/routes/journals/JournalEntry_SettingsMenu.svelte deleted file mode 100644 index 765cae9c..00000000 --- a/src/routes/journals/JournalEntry_SettingsMenu.svelte +++ /dev/null @@ -1,227 +0,0 @@ - - -
- -
- - -
- - - - - -
- - -
- - - - - -
- - - -
- -
- - -
- - {#if entry.history || entry.history_encrypted} - - {/if} - - -
- Sort Order -
- - {tmp_entry_obj.sort ?? entry.sort ?? 0} - -
-
-
- - -
- - - {#if entry.archive_on} - - {/if} -
- - - {#if $ae_loc.edit_mode && journals_li.length} -
- - Move to Journal - - -
- {/if} - - - -
\ No newline at end of file diff --git a/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte b/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte index 2e0cea14..c5d91719 100644 --- a/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte +++ b/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte @@ -357,7 +357,7 @@
- {#if $lq__journal_entry_obj} + {#if $lq__journal_entry_obj && $lq__journal_obj} { - if (show && lq__journal_obj && !tmp__journal_obj.journal_id) { - tmp__journal_obj = JSON.parse(JSON.stringify(lq__journal_obj)); + if (show && lq__journal_obj) { + const source_id = lq__journal_obj.journal_id; + untrack(() => { + if (!tmp__journal_obj.journal_id || tmp__journal_obj.journal_id !== source_id) { + tmp__journal_obj = JSON.parse(JSON.stringify(lq__journal_obj)); + // Ensure cfg_json exists + if (!tmp__journal_obj.cfg_json) tmp__journal_obj.cfg_json = {}; + } + }); } }); @@ -69,26 +88,10 @@ } try { - const data_kv = { - name: tmp__journal_obj.name, - description: tmp__journal_obj.description ?? '', - type_code: tmp__journal_obj.type_code, - passcode: tmp__journal_obj.passcode, - private_passcode: tmp__journal_obj.private_passcode, - passcode_timeout: tmp__journal_obj.passcode_timeout, - auth_key: tmp__journal_obj.auth_key, - cfg_json: tmp__journal_obj.cfg_json, - group: tmp__journal_obj.group, - sort: tmp__journal_obj.sort, - hide: tmp__journal_obj.hide, - enable: tmp__journal_obj.enable, - priority: tmp__journal_obj.priority - }; - await journals_func.update_ae_obj__journal({ api_cfg: $ae_api, journal_id: lq__journal_obj?.journal_id, - data_kv: data_kv, + data_kv: tmp__journal_obj, log_lvl: log_lvl }); show = false; @@ -128,11 +131,11 @@ {#snippet header()}

- Edit Journal: {lq__journal_obj?.name ?? '--'} + Edit Journal: {$lq__journal_obj?.name ?? '--'}

{/snippet} -
+
+
+ +
{:else if tab === 'ui'} @@ -262,9 +267,9 @@

- Editor & Viewer + Default View Modes

-
+
+ + +
+
+ + +
+

+ + Entry Visibility (Global) +

+
+ + + +
+
+ + +
+

+ + Entry Feature Buttons +

+
+ + + + + + + +

- - Visual Theme + + List Interactions

- +
+ +
+ + +
+
diff --git a/src/routes/journals/modal_journal_entry_config.svelte b/src/routes/journals/modal_journal_entry_config.svelte index 2662c619..25941534 100644 --- a/src/routes/journals/modal_journal_entry_config.svelte +++ b/src/routes/journals/modal_journal_entry_config.svelte @@ -80,16 +80,16 @@
diff --git a/src/routes/journals/modal_journals_config.svelte b/src/routes/journals/modal_journals_config.svelte index e96c4dfa..cfd28bc1 100644 --- a/src/routes/journals/modal_journals_config.svelte +++ b/src/routes/journals/modal_journals_config.svelte @@ -1,21 +1,26 @@ {#snippet header()}

@@ -57,7 +82,7 @@
-
+