- Add hover titles to all save buttons
- Match warning color scheme across floating, inline, and header save buttons
- Fix floating save button visibility (Tailwind v4 hidden/md:inline-flex conflict)
- Hide floating save when no unsaved changes using {#if}
- Hide Config button when not in admin edit mode
- Remove the mobile/backup explicit Save button from header (redundant)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
164 lines
6.5 KiB
Svelte
164 lines
6.5 KiB
Svelte
<script lang="ts">
|
|
/**
|
|
* ae_comp__journal_entry_editor.svelte
|
|
* Extracted 2026-01-08 to modularize the massive Journal Entry view.
|
|
* Handles: CodeMirror vs Plain vs Rendered HTML for both View and Edit modes.
|
|
*/
|
|
import {
|
|
LockKeyhole, RefreshCcw, Save
|
|
} from '@lucide/svelte';
|
|
import { ae_loc } from '$lib/stores/ae_stores';
|
|
import { journals_loc } from '$lib/ae_journals/ae_journals_stores';
|
|
import AE_Comp_Editor_CodeMirror from '$lib/elements/element_editor_codemirror.svelte';
|
|
import type {
|
|
ae_JournalEntry,
|
|
ae_Journal,
|
|
ae_JournalEntryDraft
|
|
} from '$lib/types/ae_types';
|
|
|
|
interface Props {
|
|
entry: ae_JournalEntry;
|
|
journal: ae_Journal;
|
|
tmp_entry_obj: ae_JournalEntryDraft; // Bindable
|
|
editor_view?: unknown; // Bindable
|
|
has_changed: boolean;
|
|
updated_idb: boolean;
|
|
on_save: () => void;
|
|
on_force_reset?: () => void;
|
|
}
|
|
|
|
let {
|
|
entry,
|
|
journal,
|
|
tmp_entry_obj = $bindable(),
|
|
editor_view = $bindable(),
|
|
has_changed,
|
|
updated_idb,
|
|
on_save,
|
|
on_force_reset
|
|
}: Props = $props();
|
|
|
|
const is_editing = $derived(
|
|
$journals_loc.entry.edit_kv[entry.journal_entry_id] === 'current'
|
|
);
|
|
const preferred_viewer = $derived(
|
|
(journal?.cfg_json?.pref_viewer ?? 'rendered').toLowerCase()
|
|
);
|
|
</script>
|
|
|
|
<div
|
|
class="journal-entry-editor-wrapper flex w-full min-w-0 grow flex-col items-stretch">
|
|
{#if !is_editing}
|
|
<!-- VIEW MODE -->
|
|
<div class="w-full min-w-0">
|
|
{#if preferred_viewer === 'codemirror'}
|
|
<AE_Comp_Editor_CodeMirror
|
|
bind:content={tmp_entry_obj.content}
|
|
bind:editor_view
|
|
theme_mode={$ae_loc.theme_mode}
|
|
language="markdown"
|
|
readonly={true}
|
|
show_toolbar={false}
|
|
class_li="w-full rounded-lg bg-surface-50 shadow-lg dark:bg-surface-800" />
|
|
{:else if preferred_viewer === 'plain'}
|
|
<div
|
|
class="bg-surface-50 dark:bg-surface-800 text-surface-800 dark:text-surface-100 w-full rounded-lg border border-gray-200 p-4 font-mono text-sm wrap-break-word whitespace-pre-wrap shadow-lg dark:border-gray-700">
|
|
{tmp_entry_obj?.content || ''}
|
|
</div>
|
|
{:else}
|
|
<div
|
|
class="prose dark:prose-invert w-full max-w-none overflow-x-auto rounded-lg border border-gray-200 bg-white p-4 shadow-lg dark:border-gray-700 dark:bg-gray-900">
|
|
<!-- The rendered HTML branch is intentionally trusted input from the journal editor pipeline. -->
|
|
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
{@html tmp_entry_obj?.content_md_html || ''}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<!-- EDIT MODE -->
|
|
{#if !tmp_entry_obj?.content && tmp_entry_obj?.content_encrypted}
|
|
<!-- Decryption Required Message -->
|
|
<div
|
|
class="bg-error-100 dark:bg-error-900/30 text-error-900 dark:text-error-100 border-error-500 flex w-full flex-col gap-4 rounded-lg border p-4">
|
|
<div class="space-y-2">
|
|
<div class="flex items-center gap-2 font-bold">
|
|
<LockKeyhole size="1.25em" />
|
|
Decryption Required
|
|
</div>
|
|
<p class="text-sm">
|
|
This entry must be decrypted before it can be edited.
|
|
</p>
|
|
</div>
|
|
|
|
{#if $ae_loc.edit_mode && on_force_reset}
|
|
<div class="border-error-500/20 border-t pt-4">
|
|
<p class="mb-2 text-xs italic opacity-70">
|
|
Passcode lost? You can force a reset to plain text,
|
|
but all currently encrypted data will be permanently
|
|
deleted.
|
|
</p>
|
|
<button
|
|
type="button"
|
|
class="btn btn-sm preset-tonal-error hover:preset-filled-error-500 font-bold"
|
|
onclick={on_force_reset}>
|
|
<RefreshCcw size="1.1em" class="mr-2" /> Force Reset to
|
|
Plain Text
|
|
</button>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<!-- Actual Editor -->
|
|
{#if journal?.cfg_json?.pref_editor == 'codemirror'}
|
|
<AE_Comp_Editor_CodeMirror
|
|
bind:content={tmp_entry_obj.content}
|
|
bind:editor_view
|
|
theme_mode={$ae_loc.theme_mode}
|
|
placeholder="Write using Markdown..."
|
|
class_li="p-2 preset-outlined-warning-300-700 shadow-lg rounded-lg w-full bg-surface-50 dark:bg-surface-800" />
|
|
{:else}
|
|
<textarea
|
|
bind:value={tmp_entry_obj.content}
|
|
class="textarea h-125 w-full grow rounded-lg border-orange-500/30 p-4 font-mono wrap-break-word whitespace-pre-wrap shadow-lg"
|
|
placeholder="Edit content..."></textarea>
|
|
{/if}
|
|
|
|
<!-- Floating Save Button (desktop only) -->
|
|
{#if has_changed}
|
|
<button
|
|
type="button"
|
|
onclick={on_save}
|
|
|
|
class="
|
|
btn btn-sm md:btn-md lg:btn-lg preset-tonal-warning hover:preset-filled-warning-500
|
|
fixed top-84 right-6 z-20
|
|
min-w-32 shadow-xl
|
|
max-sm:hidden
|
|
"
|
|
title="Save changes"
|
|
>
|
|
<Save size="1.2em" class="" />Save
|
|
</button>
|
|
{/if}
|
|
|
|
<!-- Inline Save Button (Mobile/Context) -->
|
|
<button
|
|
type="button"
|
|
onclick={on_save}
|
|
disabled={!has_changed}
|
|
title="Save changes"
|
|
class="btn preset-tonal-warning hover:preset-filled-warning-500 mt-4 w-full max-w-96"
|
|
class:invisible={!has_changed}>
|
|
<Save size="1.2em" class="mr-2" /> Save Changes
|
|
</button>
|
|
|
|
{#if updated_idb}
|
|
<p
|
|
class="text-error-500 mt-2 animate-pulse text-xs font-bold tracking-widest uppercase">
|
|
IDB object updated since last load!
|
|
</p>
|
|
{/if}
|
|
{/if}
|
|
{/if}
|
|
</div>
|