Revert "Revert "Preserve HTML formatting inside clozes (#3038)""

This reverts commit e911b4b69a.

Trying again now that 24.04 is out.
This commit is contained in:
Damien Elmes 2024-03-31 15:51:03 +07:00
parent 9f55cf26fc
commit 9c733848b8
7 changed files with 95 additions and 19 deletions

View file

@ -1246,7 +1246,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
highest += 1
# must start at 1
highest = max(1, highest)
self.web.eval("wrap('{{c%d::', '}}');" % highest)
self.web.eval("wrapCloze(%d);" % highest)
def setupForegroundButton(self) -> None:
self.fcolour = self.mw.pm.profile.get("lastColour", "#00f")

View file

@ -55,18 +55,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
async function onIncrementCloze(): Promise<void> {
const highestCloze = getCurrentHighestCloze(true);
dispatch("surround", {
prefix: `{{c${highestCloze}::`,
suffix: "}}",
dispatch("cloze", {
n: highestCloze,
});
}
async function onSameCloze(): Promise<void> {
const highestCloze = getCurrentHighestCloze(false);
dispatch("surround", {
prefix: `{{c${highestCloze}::`,
suffix: "}}",
dispatch("cloze", {
n: highestCloze,
});
}

View file

@ -392,7 +392,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { ImageOcclusionFieldIndexes } from "@generated/anki/image_occlusion_pb";
import { getImageOcclusionFields } from "@generated/backend";
import { wrapInternal } from "@tslib/wrap";
import { wrapClozeInternal, wrapInternal } from "@tslib/wrap";
import Shortcut from "$lib/components/Shortcut.svelte";
@ -547,6 +547,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
});
}
function wrapCloze(n: number): void {
if (!$focusedInput || !editingInputIsRichText($focusedInput)) {
return;
}
$focusedInput.element.then((element) => {
wrapClozeInternal(element, n);
});
}
Object.assign(globalThis, {
saveSession,
setFields,
@ -565,6 +575,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setNoteId,
setNotetypeMeta,
wrap,
wrapCloze,
setMathjaxEnabled,
setShrinkImages,
setCloseHTMLTags,

View file

@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import { wrapInternal } from "@tslib/wrap";
import { wrapClozeInternal } from "@tslib/wrap";
import ClozeButtons from "../ClozeButtons.svelte";
import { context as noteEditorContext } from "../NoteEditor.svelte";
@ -13,12 +13,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
$: richTextAPI = $focusedInput as RichTextInputAPI;
async function onSurround({ detail }): Promise<void> {
async function onCloze({ detail }): Promise<void> {
const richText = await richTextAPI.element;
const { prefix, suffix } = detail;
wrapInternal(richText, prefix, suffix, false);
const { n } = detail;
wrapClozeInternal(richText, n);
}
</script>
<ClozeButtons on:surround={onSurround} />
<ClozeButtons on:cloze={onCloze} />

View file

@ -39,7 +39,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
</IconButton>
</ButtonGroup>
<ClozeButtons on:surround alwaysEnabled={true} />
<ClozeButtons on:cloze alwaysEnabled={true} />
<ButtonGroup>
<IconButton

View file

@ -261,12 +261,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
clear();
}
}}
on:surround={async ({ detail }) => {
on:cloze={async ({ detail }) => {
const editor = await mathjaxEditor.editor;
const { prefix, suffix } = detail;
const { n } = detail;
editor.replaceSelection(
prefix + editor.getSelection() + suffix,
`{{c${n}::` + editor.getSelection() + "}}",
);
}}
/>

View file

@ -1,7 +1,9 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import { placeCaretAfter } from "../domlib/place-caret";
import { getRange, getSelection } from "./cross-browser";
import { nodeIsElement } from "./dom";
function wrappedExceptForWhitespace(text: string, front: string, back: string): string {
const match = text.match(/^(\s*)([^]*?)(\s*)$/)!;
@ -53,3 +55,70 @@ export function wrapInternal(
moveCursorInside(selection, back);
}
}
export function wrapClozeInternal(base: Element, n: number): void {
const selection = getSelection(base)!;
const range = getRange(selection);
if (!range) {
return;
}
// Expand the range to include parent nodes whose children are already included.
// This is to work around .extractContents() adding redundant empty elements
let startParent: Node | null = range.startContainer.parentNode;
if (
startParent !== base
&& startParent?.firstChild === range.startContainer && range.startOffset === 0
) {
range.setStartBefore(startParent);
}
let endParent: Node | null = range.endContainer.parentNode;
if (
endParent !== base
&& endParent?.lastChild === range.endContainer && (
(!nodeIsElement(range.endContainer)
&& range.endOffset === range.endContainer.textContent?.length)
|| (nodeIsElement(range.endContainer)
&& range.endOffset === range.endContainer.childNodes.length)
)
) {
range.setEndAfter(endParent);
}
let expand: boolean;
do {
expand = false;
if (
startParent
&& startParent.parentNode !== base && startParent.parentNode?.firstChild === startParent
&& range.isPointInRange(startParent.parentNode, startParent.parentNode?.childNodes.length)
) {
startParent = startParent.parentNode;
range.setStartBefore(startParent);
expand = true;
}
if (
endParent && endParent.parentNode !== base && endParent.parentNode?.lastChild === endParent
&& range.isPointInRange(endParent.parentNode, 0)
) {
endParent = endParent.parentNode;
range.setEndAfter(endParent);
expand = true;
}
if (range.endOffset === 0) {
range.setEndBefore(range.endContainer);
expand = true;
}
} while (expand);
const fragment = range.extractContents();
if (fragment.childNodes.length === 0) {
document.execCommand("inserthtml", false, `{{c${n}::}}`);
} else {
const startNode = document.createTextNode(`{{c${n}::`);
const endNode = document.createTextNode("}}");
range.insertNode(endNode);
range.insertNode(fragment);
range.insertNode(startNode);
placeCaretAfter(endNode);
}
}