Saving these files that should be pretty good.
This commit is contained in:
@@ -19,20 +19,26 @@
|
|||||||
interface Props {
|
interface Props {
|
||||||
entry: ae_JournalEntry;
|
entry: ae_JournalEntry;
|
||||||
journal: ae_Journal;
|
journal: ae_Journal;
|
||||||
|
journals_li?: ae_Journal[];
|
||||||
tmp_entry_obj: any; // Bindable
|
tmp_entry_obj: any; // Bindable
|
||||||
has_changed: boolean;
|
has_changed: boolean;
|
||||||
onSave: () => void;
|
onSave: () => void;
|
||||||
onDecrypt: () => void;
|
onDecrypt: () => void;
|
||||||
|
onDecryptHistory: () => void;
|
||||||
|
onChangeJournal: () => void;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
entry,
|
entry,
|
||||||
journal,
|
journal,
|
||||||
|
journals_li = [],
|
||||||
tmp_entry_obj = $bindable(),
|
tmp_entry_obj = $bindable(),
|
||||||
has_changed,
|
has_changed,
|
||||||
onSave,
|
onSave,
|
||||||
onDecrypt,
|
onDecrypt,
|
||||||
|
onDecryptHistory,
|
||||||
|
onChangeJournal,
|
||||||
log_lvl = 0
|
log_lvl = 0
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
@@ -209,8 +215,11 @@ p-2 md:p-3 rounded-lg shadow-md
|
|||||||
<JournalEntry_SettingsMenu
|
<JournalEntry_SettingsMenu
|
||||||
{entry}
|
{entry}
|
||||||
{journal}
|
{journal}
|
||||||
|
{journals_li}
|
||||||
bind:tmp_entry_obj={tmp_entry_obj}
|
bind:tmp_entry_obj={tmp_entry_obj}
|
||||||
{onSave}
|
{onSave}
|
||||||
|
{onDecryptHistory}
|
||||||
|
{onChangeJournal}
|
||||||
{log_lvl}
|
{log_lvl}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,28 +2,43 @@
|
|||||||
/**
|
/**
|
||||||
* JournalEntry_SettingsMenu.svelte
|
* JournalEntry_SettingsMenu.svelte
|
||||||
* Extracted 2026-01-08 to modularize the Journal Entry Header.
|
* Extracted 2026-01-08 to modularize the Journal Entry Header.
|
||||||
* Manages Category, Priority, Sort, and Deletion.
|
* Manages Category, Flags, Copy/Clone, History, and Sort Order.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
Plus, Minus, ArrowDown10, Flag, FlagOff,
|
Plus, Minus, ArrowDown10, Flag, FlagOff,
|
||||||
Eye, EyeOff, ShieldCheck, ShieldMinus,
|
Eye, EyeOff, ShieldCheck, ShieldMinus,
|
||||||
Clock, X, Trash2
|
Clock, X, Trash2, Settings, Shapes,
|
||||||
|
Copy, RemoveFormatting, CodeXml, TypeOutline,
|
||||||
|
History, Pencil, PenLine, FileX, SquareLibrary
|
||||||
} from '@lucide/svelte';
|
} from '@lucide/svelte';
|
||||||
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
import { ae_loc, ae_api } from '$lib/stores/ae_stores';
|
||||||
import { journals_slct } from '$lib/ae_journals/ae_journals_stores';
|
import { journals_slct, journals_loc, journals_sess } from '$lib/ae_journals/ae_journals_stores';
|
||||||
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
import { journals_func } from '$lib/ae_journals/ae_journals_functions';
|
||||||
import { goto } from '$app/navigation';
|
import { goto } from '$app/navigation';
|
||||||
|
import AE_ObjectFlags from '$lib/ae_elements/AE_ObjectFlags.svelte';
|
||||||
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
import type { ae_JournalEntry, ae_Journal } from '$lib/types/ae_types';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
entry: ae_JournalEntry;
|
entry: ae_JournalEntry;
|
||||||
journal: ae_Journal;
|
journal: ae_Journal;
|
||||||
|
journals_li: ae_Journal[];
|
||||||
tmp_entry_obj: any; // Bindable
|
tmp_entry_obj: any; // Bindable
|
||||||
onSave: () => void;
|
onSave: () => void;
|
||||||
|
onDecryptHistory: () => void;
|
||||||
|
onChangeJournal: () => void;
|
||||||
log_lvl?: number;
|
log_lvl?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { entry, journal, tmp_entry_obj = $bindable(), onSave, log_lvl = 0 }: Props = $props();
|
let {
|
||||||
|
entry,
|
||||||
|
journal,
|
||||||
|
journals_li = [],
|
||||||
|
tmp_entry_obj = $bindable(),
|
||||||
|
onSave,
|
||||||
|
onDecryptHistory,
|
||||||
|
onChangeJournal,
|
||||||
|
log_lvl = 0
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
async function delete_entry() {
|
async function delete_entry() {
|
||||||
if (confirm(`Are you sure you want to delete this journal entry?`)) {
|
if (confirm(`Are you sure you want to delete this journal entry?`)) {
|
||||||
@@ -46,97 +61,133 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyToClipboard(text: string, msg: string) {
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
.then(() => alert(msg))
|
||||||
|
.catch(err => console.error('Copy failed:', err));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function copyRichText() {
|
||||||
|
const element = document.getElementById(`rendered_journal_entry_content_${entry.journal_entry_id}`);
|
||||||
|
if (!element) return;
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.write([
|
||||||
|
new ClipboardItem({ 'text/html': new Blob([element.innerHTML], { type: 'text/html' }) })
|
||||||
|
]);
|
||||||
|
alert('Rich text copied!');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Rich copy failed:', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-4">
|
<div class="space-y-4 min-w-[280px]">
|
||||||
<!-- Category -->
|
<!-- Category selection -->
|
||||||
<div class="flex flex-row items-center justify-evenly gap-2 w-full">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm text-gray-500 hidden sm:inline"> Category: </span>
|
<Shapes size="1.1em" class="text-surface-500" />
|
||||||
<select
|
<select
|
||||||
class="select select-sm preset-tonal-primary w-full"
|
class="select select-sm preset-tonal-primary grow"
|
||||||
bind:value={tmp_entry_obj.category_code}
|
bind:value={tmp_entry_obj.category_code}
|
||||||
onchange={onSave}
|
onchange={onSave}
|
||||||
title="Select a category"
|
|
||||||
>
|
>
|
||||||
<option value="">Select Category</option>
|
<option value="">Select Category</option>
|
||||||
{#each journal?.cfg_json.category_li ?? [] as category}
|
{#each journal?.cfg_json?.category_li ?? [] as category}
|
||||||
<option value={category.code}>{category.name}</option>
|
<option value={category.code}>{category.name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Priority Toggle -->
|
<!-- Flags (Standardized component) -->
|
||||||
<button
|
<AE_ObjectFlags
|
||||||
type="button"
|
bind:obj={tmp_entry_obj}
|
||||||
onclick={() => {
|
onToggle={onSave}
|
||||||
tmp_entry_obj.priority = !entry.priority;
|
hideAlert={journal?.cfg_json?.hide_btn_alert}
|
||||||
onSave();
|
hidePrivate={journal?.cfg_json?.hide_btn_private}
|
||||||
}}
|
hidePublic={journal?.cfg_json?.hide_btn_public}
|
||||||
class="btn w-full mb-2 preset-tonal-tertiary transition hover:preset-filled-tertiary-500"
|
hidePersonal={journal?.cfg_json?.hide_btn_personal}
|
||||||
>
|
hideProfessional={journal?.cfg_json?.hide_btn_professional}
|
||||||
{#if entry.priority}
|
hideTemplate={journal?.cfg_json?.hide_btn_template}
|
||||||
<Flag size="1.25em" class="text-success-500 mr-2" />
|
/>
|
||||||
{:else}
|
|
||||||
<FlagOff size="1.25em" class="opacity-50 mr-2" />
|
|
||||||
{/if}
|
|
||||||
<span>Priority Status</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<!-- Sort Order -->
|
<!-- Copy Actions -->
|
||||||
<div class="w-full flex flex-row items-center justify-center border border-surface-500/30 rounded-lg p-1 bg-surface-500/5">
|
<div class="grid grid-cols-3 gap-1">
|
||||||
<button
|
<button class="btn btn-sm variant-soft-surface" onclick={() => copyToClipboard(tmp_entry_obj.content, 'Markdown copied!')} title="Copy Markdown">
|
||||||
type="button"
|
<RemoveFormatting size="1.2em" />
|
||||||
onclick={() => {
|
|
||||||
tmp_entry_obj.sort = (entry.sort ?? 0) + 1;
|
|
||||||
onSave();
|
|
||||||
}}
|
|
||||||
class="btn-icon btn-icon-sm preset-tonal-surface hover:preset-filled-secondary-500"
|
|
||||||
>
|
|
||||||
<Plus size="1em" />
|
|
||||||
</button>
|
</button>
|
||||||
<span class="mx-4 font-bold text-base min-w-[1.5em] text-center">
|
<button class="btn btn-sm variant-soft-surface" onclick={() => copyToClipboard(entry.content_md_html || '', 'HTML copied!')} title="Copy HTML">
|
||||||
{tmp_entry_obj.sort ?? entry.sort ?? 0}
|
<CodeXml size="1.2em" />
|
||||||
</span>
|
</button>
|
||||||
<button
|
<button class="btn btn-sm variant-soft-surface" onclick={copyRichText} title="Copy Rich Text">
|
||||||
type="button"
|
<TypeOutline size="1.2em" />
|
||||||
onclick={() => {
|
|
||||||
tmp_entry_obj.sort = Math.max(0, (entry.sort ?? 0) - 1);
|
|
||||||
onSave();
|
|
||||||
}}
|
|
||||||
class="btn-icon btn-icon-sm preset-tonal-surface hover:preset-filled-secondary-500"
|
|
||||||
>
|
|
||||||
<Minus size="1em" />
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="divider"></div>
|
<div class="divider !my-1"></div>
|
||||||
|
|
||||||
<!-- Archive On -->
|
<!-- History & Sort Section -->
|
||||||
<div class="flex flex-row items-center justify-between gap-2 border border-surface-500/20 rounded p-1">
|
<div class="flex flex-col gap-2">
|
||||||
<input
|
<!-- History Toggle -->
|
||||||
type="datetime-local"
|
{#if entry.history || entry.history_encrypted}
|
||||||
bind:value={tmp_entry_obj.archive_on}
|
<button
|
||||||
onchange={onSave}
|
class="btn btn-sm w-full { $journals_loc.entry.edit_kv[entry.journal_entry_id] === 'history' ? 'variant-filled-secondary' : 'variant-soft-secondary'}"
|
||||||
class="input input-sm border-none bg-transparent"
|
onclick={onDecryptHistory}
|
||||||
/>
|
>
|
||||||
|
<History size="1.1em" class="mr-2" /> Review History
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Cleaned Sort Controls -->
|
||||||
|
<div class="flex items-center justify-between p-2 bg-surface-500/10 rounded-lg border border-surface-500/20">
|
||||||
|
<span class="text-xs font-bold uppercase tracking-wider opacity-60">Sort Order</span>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
class="btn-icon btn-icon-sm variant-filled-surface border border-surface-500/50"
|
||||||
|
onclick={() => { tmp_entry_obj.sort = (entry.sort ?? 0) + 1; onSave(); }}
|
||||||
|
>
|
||||||
|
<Plus size="1em" />
|
||||||
|
</button>
|
||||||
|
<span class="font-mono font-bold text-sm w-6 text-center">{tmp_entry_obj.sort ?? entry.sort ?? 0}</span>
|
||||||
|
<button
|
||||||
|
class="btn-icon btn-icon-sm variant-filled-surface border border-surface-500/50"
|
||||||
|
onclick={() => { tmp_entry_obj.sort = Math.max(0, (entry.sort ?? 0) - 1); onSave(); }}
|
||||||
|
>
|
||||||
|
<Minus size="1em" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Archive Control -->
|
||||||
|
<div class="flex items-center gap-2 p-1 bg-surface-500/5 rounded border border-surface-500/10">
|
||||||
|
<Clock size="1.1em" class="opacity-50 ml-1" />
|
||||||
|
<input type="datetime-local" bind:value={tmp_entry_obj.archive_on} onchange={onSave} class="input input-sm border-none bg-transparent text-xs grow" />
|
||||||
{#if entry.archive_on}
|
{#if entry.archive_on}
|
||||||
<button onclick={() => { tmp_entry_obj.archive_on = null; onSave(); }} class="text-error-500">
|
<button onclick={() => { tmp_entry_obj.archive_on = null; onSave(); }} class="btn-icon btn-icon-sm text-error-500"><X size="1em"/></button>
|
||||||
<X size="1.2em" />
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<button onclick={() => { tmp_entry_obj.archive_on = new Date().toISOString().slice(0, 16); onSave(); }} class="text-primary-500">
|
|
||||||
<Clock size="1.2em" />
|
|
||||||
</button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Danger Zone: Delete -->
|
<!-- Move to Journal -->
|
||||||
<button
|
{#if $ae_loc.edit_mode && journals_li.length}
|
||||||
type="button"
|
<div class="p-2 border border-surface-500/20 rounded-lg space-y-1">
|
||||||
onclick={delete_entry}
|
<span class="text-[10px] uppercase font-bold opacity-50 flex items-center gap-1">
|
||||||
class="btn variant-filled-error w-full mt-4"
|
<SquareLibrary size="1em" /> Move to Journal
|
||||||
>
|
</span>
|
||||||
<Trash2 size="1.25em" class="mr-2" />
|
<select
|
||||||
Delete Entry
|
class="select select-sm w-full text-xs"
|
||||||
|
bind:value={tmp_entry_obj.journal_id}
|
||||||
|
onchange={() => { if(confirm('Change parent journal?')) onChangeJournal(); }}
|
||||||
|
>
|
||||||
|
<option value="">Select Journal</option>
|
||||||
|
{#each journals_li as j}
|
||||||
|
<option value={j.journal_id}>{j.name}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Deletion -->
|
||||||
|
<button class="btn btn-sm variant-filled-error w-full mt-2" onclick={delete_entry}>
|
||||||
|
<Trash2 size="1.1em" class="mr-2" /> Delete Entry
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user