From f379aa742e73ce8b90cddaf05573a47495b11ee6 Mon Sep 17 00:00:00 2001 From: Abdo Date: Fri, 25 Jul 2025 14:41:27 +0300 Subject: [PATCH] Implement History button --- proto/anki/frontend.proto | 2 +- qt/aqt/mediasrv.py | 1 + ts/lib/components/icons.ts | 3 + ts/routes/editor/ActionButton.svelte | 3 +- ts/routes/editor/ActionButtons.svelte | 6 +- ts/routes/editor/HistoryButton.svelte | 27 ++++ ts/routes/editor/HistoryModal.svelte | 208 ++++++++++++++++++++++++++ ts/routes/editor/NoteEditor.svelte | 50 ++++++- ts/routes/editor/base.ts | 6 +- ts/routes/editor/types.ts | 5 + 10 files changed, 301 insertions(+), 10 deletions(-) create mode 100644 ts/routes/editor/HistoryButton.svelte create mode 100644 ts/routes/editor/HistoryModal.svelte diff --git a/proto/anki/frontend.proto b/proto/anki/frontend.proto index 17090fe1e..ac439764a 100644 --- a/proto/anki/frontend.proto +++ b/proto/anki/frontend.proto @@ -33,7 +33,7 @@ service FrontendService { // Editor rpc UpdateEditorNote(notes.UpdateNotesRequest) returns (generic.Empty); rpc UpdateEditorNotetype(notetypes.Notetype) returns (generic.Empty); - rpc AddEditorNote(notes.AddNoteRequest) returns (generic.Empty); + rpc AddEditorNote(notes.AddNoteRequest) returns (notes.AddNoteResponse); rpc ConvertPastedImage(ConvertPastedImageRequest) returns (ConvertPastedImageResponse); rpc RetrieveUrl(generic.String) returns (RetrieveUrlResponse); diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index 132160b61..8149ca560 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -922,6 +922,7 @@ exposed_backend_list = [ # CardRenderingService "encode_iri_paths", "decode_iri_paths", + "html_to_text_line", # ConfigService "set_config_json", "get_config_bool", diff --git a/ts/lib/components/icons.ts b/ts/lib/components/icons.ts index ab07cbf17..7bd28070f 100644 --- a/ts/lib/components/icons.ts +++ b/ts/lib/components/icons.ts @@ -149,6 +149,8 @@ import ArrowLeft_ from "bootstrap-icons/icons/arrow-left.svg?component"; import arrowLeft_ from "bootstrap-icons/icons/arrow-left.svg?url"; import ArrowRight_ from "bootstrap-icons/icons/arrow-right.svg?component"; import arrowRight_ from "bootstrap-icons/icons/arrow-right.svg?url"; +import CaretDownFill_ from "bootstrap-icons/icons/caret-down-fill.svg?component"; +import caretDownFill_ from "bootstrap-icons/icons/caret-down-fill.svg?url"; import Minus_ from "bootstrap-icons/icons/dash-lg.svg?component"; import minus_ from "bootstrap-icons/icons/dash-lg.svg?url"; import Eraser_ from "bootstrap-icons/icons/eraser.svg?component"; @@ -285,3 +287,4 @@ export const mdiUngroup = { url: ungroup_, component: Ungroup_ }; export const mdiVectorPolygonVariant = { url: vectorPolygonVariant_, component: VectorPolygonVariant_ }; export const incrementClozeIcon = { url: incrementCloze_, component: IncrementCloze_ }; export const mdiEarth = { url: earth_, component: Earth_ }; +export const caretDownFill = { url: caretDownFill_, component: CaretDownFill_ }; diff --git a/ts/routes/editor/ActionButton.svelte b/ts/routes/editor/ActionButton.svelte index 1e7826132..14e074bee 100644 --- a/ts/routes/editor/ActionButton.svelte +++ b/ts/routes/editor/ActionButton.svelte @@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
@@ -15,6 +15,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {tooltip} --border-left-radius="5px" --border-right-radius="5px" + {disabled} >
{@render children()} diff --git a/ts/routes/editor/ActionButtons.svelte b/ts/routes/editor/ActionButtons.svelte index 30b273ff4..289c04e56 100644 --- a/ts/routes/editor/ActionButtons.svelte +++ b/ts/routes/editor/ActionButtons.svelte @@ -6,11 +6,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import AddButton from "./AddButton.svelte"; import CloseButton from "./CloseButton.svelte"; import HelpButton from "./HelpButton.svelte"; - import type { EditorMode } from "./types"; + import HistoryButton from "./HistoryButton.svelte"; + import type { EditorMode, HistoryEntry } from "./types"; export let mode: EditorMode; export let onClose: () => void; export let onAdd: () => void; + export let onHistory: () => void; + export let history: HistoryEntry[] = [];
@@ -21,6 +24,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {/if} {#if mode === "add"} + {/if}
diff --git a/ts/routes/editor/HistoryButton.svelte b/ts/routes/editor/HistoryButton.svelte new file mode 100644 index 000000000..5a07cc8d6 --- /dev/null +++ b/ts/routes/editor/HistoryButton.svelte @@ -0,0 +1,27 @@ + + + + + {tr.addingHistory()} + + + diff --git a/ts/routes/editor/HistoryModal.svelte b/ts/routes/editor/HistoryModal.svelte new file mode 100644 index 000000000..29f5b71ae --- /dev/null +++ b/ts/routes/editor/HistoryModal.svelte @@ -0,0 +1,208 @@ + + + + + + diff --git a/ts/routes/editor/NoteEditor.svelte b/ts/routes/editor/NoteEditor.svelte index 1416fc115..fa5a55fb7 100644 --- a/ts/routes/editor/NoteEditor.svelte +++ b/ts/routes/editor/NoteEditor.svelte @@ -15,6 +15,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import LabelName from "./LabelName.svelte"; import { EditorState, type EditorMode } from "./types"; import { ContextMenu, Item } from "$lib/context-menu"; + import type Modal from "bootstrap/js/dist/modal"; + import { getContext } from "svelte"; + import { modalsKey } from "$lib/components/context-keys"; export interface NoteEditorAPI { fields: EditorFieldAPI[]; @@ -85,7 +88,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import PlainTextBadge from "./PlainTextBadge.svelte"; import RichTextInput, { editingInputIsRichText } from "./rich-text-input"; import RichTextBadge from "./RichTextBadge.svelte"; - import type { NotetypeIdAndModTime, SessionOptions } from "./types"; + import type { HistoryEntry, NotetypeIdAndModTime, SessionOptions } from "./types"; let contextMenu: ContextMenu; const [onContextMenu, contextMenuItems] = setupContextMenu(); @@ -446,6 +449,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html await addCurrentNote(1n); } + const modals = getContext>(modalsKey); + let modalKey: string; + + let history: HistoryEntry[] = []; + + export async function addNoteToHistory(note: Note) { + let text = ( + await htmlToTextLine({ + text: note.fields.join(", "), + preserveMediaFilenames: true, + }) + ).val; + if (text.length > 30) { + text = `${text.slice(0, 30)}...`; + } + history = [ + ...history, + { + text, + noteId: note.id, + }, + ]; + } + + export function onHistory() { + modals.get(modalKey)!.show(); + } + export function saveOnPageHide() { if (document.visibilityState === "hidden") { // will fire on session close and minimize @@ -501,10 +532,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html if (!(await noteCanBeAdded())) { return; } - await addEditorNote({ - note: note!, - deckId, - }); + const noteId = ( + await addEditorNote({ + note: note!, + deckId, + }) + ).noteId; + note.id = noteId; + addNoteToHistory(note!); lastAddedNote = note; await loadNewNote(); } @@ -638,6 +673,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html updateEditorNotetype, closeAddCards as closeAddCardsBackend, closeEditCurrent as closeEditCurrentBackend, + htmlToTextLine, } from "@generated/backend"; import { wrapInternal } from "@tslib/wrap"; import { getProfileConfig, getMeta, setMeta, getColConfig } from "@tslib/profile"; @@ -662,6 +698,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { setupContextMenu } from "./context-menu.svelte"; import { registerShortcut } from "@tslib/shortcuts"; import ActionButtons from "./ActionButtons.svelte"; + import HistoryModal from "./HistoryModal.svelte"; $: isIOImageLoaded = false; $: ioImageLoadedStore.set(isIOImageLoaded); @@ -1309,7 +1346,8 @@ components and functionality for general note editing. {#if !isLegacy} - + + {/if} {/if} diff --git a/ts/routes/editor/base.ts b/ts/routes/editor/base.ts index 365dc4eaa..1c85a1d8f 100644 --- a/ts/routes/editor/base.ts +++ b/ts/routes/editor/base.ts @@ -29,6 +29,7 @@ declare global { } } +import { modalsKey } from "$lib/components/context-keys"; import { ModuleName } from "@tslib/i18n"; import { mount } from "svelte"; import type { EditorMode } from "./types"; @@ -41,6 +42,7 @@ export const editorModules = [ ModuleName.NOTETYPES, ModuleName.IMPORTING, ModuleName.UNDO, + ModuleName.ADDING, ]; export const components = { @@ -58,6 +60,8 @@ export async function setupEditor(mode: EditorMode, isLegacy = false) { alert("unexpected editor type"); return; } + const context = new Map(); + context.set(modalsKey, new Map()); await setupI18n({ modules: editorModules }); - mount(NoteEditor, { target: document.body, props: { uiResolve, mode, isLegacy } }); + mount(NoteEditor, { target: document.body, props: { uiResolve, mode, isLegacy }, context }); } diff --git a/ts/routes/editor/types.ts b/ts/routes/editor/types.ts index 01aa42855..7c24b0375 100644 --- a/ts/routes/editor/types.ts +++ b/ts/routes/editor/types.ts @@ -29,3 +29,8 @@ export enum EditorState { } export type EditorMode = "add" | "browser" | "current"; + +export type HistoryEntry = { + text: string; + noteId: bigint; +};