diff --git a/build/configure/src/aqt.rs b/build/configure/src/aqt.rs index 242c81f57..912a1360c 100644 --- a/build/configure/src/aqt.rs +++ b/build/configure/src/aqt.rs @@ -146,7 +146,10 @@ fn build_css(build: &mut Build) -> Result<()> { }, )?; } - let other_ts_css = build.inputs_with_suffix(inputs![":ts:editor", ":ts:reviewer:reviewer.css"], ".css"); + let other_ts_css = build.inputs_with_suffix( + inputs![":ts:editor", ":ts:editable", ":ts:reviewer:reviewer.css"], + ".css", + ); build.add_action( "qt:aqt:data:web:css", CopyFiles { @@ -187,8 +190,15 @@ fn build_js(build: &mut Build) -> Result<()> { inputs: files, }, )?; - let files_from_ts = - build.inputs_with_suffix(inputs![":ts:editor", ":ts:reviewer:reviewer.js", ":ts:mathjax"], ".js"); + let files_from_ts = build.inputs_with_suffix( + inputs![ + ":ts:editor", + ":ts:editable", + ":ts:reviewer:reviewer.js", + ":ts:mathjax" + ], + ".js", + ); build.add_action( "qt:aqt:data:web:js", CopyFiles { diff --git a/build/configure/src/web.rs b/build/configure/src/web.rs index 9e027fec3..0d952dcc6 100644 --- a/build/configure/src/web.rs +++ b/build/configure/src/web.rs @@ -203,16 +203,23 @@ fn build_and_check_pages(build: &mut Build) -> Result<()> { Ok(()) }; + // we use the generated .css file separately in the legacy editor + build_page( + "editable", + false, + inputs![ + ":ts:lib", + ":ts:components", + ":ts:domlib", + ":ts:sveltelib", + ":sass", + ":sveltekit", + ], + )?; build_page( "congrats", true, - inputs![ - // - ":ts:lib", - ":ts:components", - ":sass", - ":sveltekit" - ], + inputs![":ts:lib", ":ts:components", ":sass", ":sveltekit"], )?; Ok(()) diff --git a/qt/aqt/editcurrent_legacy.py b/qt/aqt/editcurrent_legacy.py index d4e969c21..b74c0801a 100644 --- a/qt/aqt/editcurrent_legacy.py +++ b/qt/aqt/editcurrent_legacy.py @@ -91,4 +91,3 @@ class EditCurrent(QMainWindow): self.editor.call_after_note_saved(callback) onReset = on_operation_did_execute - onReset = on_operation_did_execute diff --git a/qt/aqt/editor_legacy.py b/qt/aqt/editor_legacy.py index 138deed7a..3f196584e 100644 --- a/qt/aqt/editor_legacy.py +++ b/qt/aqt/editor_legacy.py @@ -187,7 +187,7 @@ class Editor: context=self, default_css=False, ) - self.web.eval(f"setupEditor('{mode}')") + self.web.eval(f"setupEditor('{mode}', true)") self.web.show() lefttopbtns: list[str] = [] @@ -563,9 +563,9 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too if not self.note: return - data = [ - (fld, self.mw.col.media.escape_media_filenames(val)) - for fld, val in self.note.items() + field_names = self.note.keys() + field_values = [ + self.mw.col.media.escape_media_filenames(val) for val in self.note.values() ] note_type = self.note_type() @@ -600,7 +600,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too js = f""" saveSession(); - setFields({json.dumps(data)}); + setFields({json.dumps(field_names)}, {json.dumps(field_values)}); setIsImageOcclusion({json.dumps(self.current_notetype_is_image_occlusion())}); setNotetypeMeta({json.dumps(notetype_meta)}); setCollapsed({json.dumps(collapsed)}); @@ -1787,4 +1787,3 @@ gui_hooks.editor_will_use_font_for_field.append(fontMungeHack) gui_hooks.editor_will_munge_html.append(munge_html) # type: ignore gui_hooks.editor_will_munge_html.append(remove_null_bytes) # type: ignore gui_hooks.editor_will_munge_html.append(reverse_url_quoting) # type: ignore -gui_hooks.editor_will_munge_html.append(reverse_url_quoting) # type: ignore diff --git a/ts/lib/editable/index.ts b/ts/lib/editable/index.ts index 00984a718..277588b5a 100644 --- a/ts/lib/editable/index.ts +++ b/ts/lib/editable/index.ts @@ -4,3 +4,6 @@ import "./editable-base.scss"; import "./content-editable.scss"; import "./mathjax.scss"; +/* only imported for the CSS in the legacy editor */ +import "./ContentEditable.svelte"; +import "./Mathjax.svelte"; diff --git a/ts/routes/editor/NoteEditor.svelte b/ts/routes/editor/NoteEditor.svelte index a25bc6d9f..2b7b5d5a8 100644 --- a/ts/routes/editor/NoteEditor.svelte +++ b/ts/routes/editor/NoteEditor.svelte @@ -215,18 +215,25 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } async function toggleStickyAll() { - const values: boolean[] = []; - const notetype = await getNotetype({ ntid: notetypeMeta.id }); - const anySticky = notetype.fields.some((f) => f.config!.sticky); - for (const field of notetype.fields) { - const sticky = field.config!.sticky; - if (!anySticky || sticky) { - field.config!.sticky = !sticky; + if (isLegacy) { + bridgeCommand( + "toggleStickyAll", + (values: boolean[]) => (stickies = values), + ); + } else { + const values: boolean[] = []; + const notetype = await getNotetype({ ntid: notetypeMeta.id }); + const anySticky = notetype.fields.some((f) => f.config!.sticky); + for (const field of notetype.fields) { + const sticky = field.config!.sticky; + if (!anySticky || sticky) { + field.config!.sticky = !sticky; + } + values.push(field.config!.sticky); } - values.push(field.config!.sticky); + await updateEditorNotetype(notetype); + setSticky(values); } - await updateEditorNotetype(notetype); - setSticky(values); } let deregisterSticky: () => void; @@ -262,9 +269,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html await setMeta(tagsCollapsedMetaKey, collapsed); } - let note: Note; - export function setNote(n: Note): void { - note = n; + function clearCodeMirrorHistory() { // TODO this is a hack, because it requires the NoteEditor to know implementation details of the PlainTextInput. // It should be refactored once we work on our own Undo stack for (const pi of plainTextInputs) { @@ -272,6 +277,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } + let noteId: number | null = null; + export function setNoteId(ntid: number): void { + clearCodeMirrorHistory(); + noteId = ntid; + } + + function getNoteId(): number | null { + return noteId; + } + + let note: Note; + export function setNote(n: Note): void { + note = n; + clearCodeMirrorHistory(); + } + let notetypeMeta: NotetypeIdAndModTime; function setNotetypeMeta(notetype: Notetype): void { notetypeMeta = { id: notetype.id, modTime: notetype.mtimeSecs }; @@ -330,13 +351,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html async function transformContentBeforeSave(content: string): Promise { content = content.replace(/ data-editor-shrink="(true|false)"/g, ""); - // misbehaving apps may include a null byte in the text - content = content.replaceAll("\0", ""); - // reverse the url quoting we added to get images to display - content = (await decodeIriPaths({ val: content })).val; + if (!isLegacy) { + // misbehaving apps may include a null byte in the text + content = content.replaceAll("\0", ""); + // reverse the url quoting we added to get images to display + content = (await decodeIriPaths({ val: content })).val; - if (["
", "

"].includes(content)) { - return ""; + if (["
", "

"].includes(content)) { + return ""; + } } return content; } @@ -352,10 +375,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html async function updateField(index: number, content: string): Promise { fieldSave.schedule(async () => { - bridgeCommand(`key:${index}`); - note!.fields[index] = await transformContentBeforeSave(content); - await updateCurrentNote(); - await updateDuplicateDisplay(); + if (isLegacy) { + bridgeCommand( + `key:${index}:${getNoteId()}:${transformContentBeforeSave( + content, + )}`, + ); + } else { + bridgeCommand(`key:${index}`); + note!.fields[index] = await transformContentBeforeSave(content); + await updateCurrentNote(); + await updateDuplicateDisplay(); + } }, 600); } @@ -640,16 +671,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html async function pickIOImage() { imageOcclusionMode = undefined; - const filename = await openFilePickerForImageOcclusion(); - if (!filename) { - return; + if (isLegacy) { + bridgeCommand("addImageForOcclusion"); + } else { + const filename = await openFilePickerForImageOcclusion(); + if (!filename) { + return; + } + setupMaskEditor(filename); } - setupMaskEditor(filename); } async function pickIOImageFromClipboard() { imageOcclusionMode = undefined; - await setupMaskEditorFromClipboard(); + if (isLegacy) { + bridgeCommand("addImageForOcclusionFromClipboard"); + } else { + await setupMaskEditorFromClipboard(); + } } async function handlePickerDrop(event: DragEvent) { @@ -928,6 +967,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html await loadNote(note!.id, notetypeMeta.id, 0, null); } + function checkNonLegacy(value: any): any | undefined { + if (isLegacy) { + return value; + } + return undefined; + } + + function preventDefaultIfNonLegacy(event: Event) { + if (!isLegacy) { + event.preventDefault(); + } + } + $: signalEditorState($editorState); $: $editorState = getEditorState( @@ -976,6 +1028,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html saveNow, closeAddCards, focusIfField, + getNoteId, + setNoteId, setNotetypeMeta, wrap, setMathjaxEnabled, @@ -1036,6 +1090,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let uiResolve: (api: NoteEditorAPI) => void; export let mode: EditorMode; + export let isLegacy: boolean; $: if (noteEditor) { uiResolve(api as NoteEditorAPI); @@ -1044,9 +1099,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html