mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
commit
09cc55b0d3
7 changed files with 84 additions and 36 deletions
|
@ -3,7 +3,7 @@ Copyright: Ankitects Pty Ltd and contributors
|
||||||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount, getContext } from "svelte";
|
import { onMount, onDestroy, getContext, tick } from "svelte";
|
||||||
import { nightModeKey } from "components/context-keys";
|
import { nightModeKey } from "components/context-keys";
|
||||||
import { convertMathjax } from "./mathjax";
|
import { convertMathjax } from "./mathjax";
|
||||||
|
|
||||||
|
@ -22,18 +22,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
let encoded: string;
|
let encoded: string;
|
||||||
let imageHeight: number;
|
let imageHeight: number;
|
||||||
|
|
||||||
$: {
|
$: encoded = encodeURIComponent(converted);
|
||||||
encoded = encodeURIComponent(converted);
|
|
||||||
setTimeout(() => (imageHeight = image.getBoundingClientRect().height));
|
|
||||||
}
|
|
||||||
|
|
||||||
let image: HTMLImageElement;
|
let image: HTMLImageElement;
|
||||||
|
|
||||||
|
const observer = new ResizeObserver(async () => {
|
||||||
|
imageHeight = image.getBoundingClientRect().height;
|
||||||
|
await tick();
|
||||||
|
setTimeout(() => image.dispatchEvent(new Event("resize")));
|
||||||
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
observer.observe(image);
|
||||||
if (autofocus) {
|
if (autofocus) {
|
||||||
image.click();
|
image.click();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
observer.unobserve(image);
|
||||||
|
observer.disconnect();
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
/* eslint
|
||||||
|
@typescript-eslint/no-explicit-any: "off",
|
||||||
|
*/
|
||||||
|
|
||||||
import { mathIcon } from "./icons";
|
import { mathIcon } from "./icons";
|
||||||
|
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
|
@ -36,19 +40,25 @@ export function convertMathjax(
|
||||||
return getEmptyIcon(style);
|
return getEmptyIcon(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
const output = globalThis.MathJax.tex2svg(input);
|
let output: Element;
|
||||||
const svg = output.children[0];
|
try {
|
||||||
|
output = globalThis.MathJax.tex2svg(input);
|
||||||
|
} catch (e) {
|
||||||
|
return ["Mathjax Error", String(e)];
|
||||||
|
}
|
||||||
|
|
||||||
if (svg.viewBox.baseVal.height === 16) {
|
const svg = output.children[0] as SVGElement;
|
||||||
|
|
||||||
|
if ((svg as any).viewBox.baseVal.height === 16) {
|
||||||
return getEmptyIcon(style);
|
return getEmptyIcon(style);
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = "";
|
let title = "";
|
||||||
|
|
||||||
if (svg.innerHTML.includes("data-mjx-error")) {
|
if (svg.innerHTML.includes("data-mjx-error")) {
|
||||||
svg.querySelector("rect").setAttribute("fill", "yellow");
|
svg.querySelector("rect")?.setAttribute("fill", "yellow");
|
||||||
svg.querySelector("text").setAttribute("color", "red");
|
svg.querySelector("text")?.setAttribute("color", "red");
|
||||||
title = svg.querySelector("title").innerHTML;
|
title = svg.querySelector("title")?.innerHTML ?? "";
|
||||||
} else {
|
} else {
|
||||||
svg.insertBefore(style, svg.children[0]);
|
svg.insertBefore(style, svg.children[0]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,27 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
let dropdownApi: any;
|
let dropdownApi: any;
|
||||||
|
|
||||||
|
let removeEventListener: () => void = () => {
|
||||||
|
/* noop */
|
||||||
|
};
|
||||||
|
|
||||||
|
function onImageResize(): void {
|
||||||
|
if (activeImage) {
|
||||||
|
errorMessage = activeImage.title;
|
||||||
|
updateSelection().then(() => dropdownApi.update());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$: if (activeImage) {
|
||||||
|
activeImage.addEventListener("resize", onImageResize);
|
||||||
|
|
||||||
|
const lastImage = activeImage;
|
||||||
|
removeEventListener = () =>
|
||||||
|
lastImage.removeEventListener("resize", onImageResize);
|
||||||
|
} else {
|
||||||
|
removeEventListener();
|
||||||
|
}
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(async () => {
|
const resizeObserver = new ResizeObserver(async () => {
|
||||||
if (activeImage) {
|
if (activeImage) {
|
||||||
await updateSelection();
|
await updateSelection();
|
||||||
|
@ -36,21 +57,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
return image.closest("anki-mathjax")! as HTMLElement;
|
return image.closest("anki-mathjax")! as HTMLElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onEditorUpdate(event: CustomEvent) {
|
function onEditorUpdate(event: CustomEvent): void {
|
||||||
|
/* this updates the image in Mathjax.svelte */
|
||||||
getComponent(activeImage!).dataset.mathjax = event.detail.mathjax;
|
getComponent(activeImage!).dataset.mathjax = event.detail.mathjax;
|
||||||
|
|
||||||
let selectionResolve: (value: void) => void;
|
|
||||||
const afterSelectionUpdate = new Promise((resolve) => {
|
|
||||||
selectionResolve = resolve;
|
|
||||||
});
|
|
||||||
|
|
||||||
setTimeout(async () => {
|
|
||||||
errorMessage = activeImage!.title;
|
|
||||||
await updateSelection();
|
|
||||||
selectionResolve();
|
|
||||||
});
|
|
||||||
|
|
||||||
return afterSelectionUpdate;
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -60,7 +69,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
autoClose={false}
|
autoClose={false}
|
||||||
distance={4}
|
distance={4}
|
||||||
let:createDropdown
|
let:createDropdown
|
||||||
let:dropdownObject
|
|
||||||
>
|
>
|
||||||
{#if activeImage}
|
{#if activeImage}
|
||||||
<HandleSelection
|
<HandleSelection
|
||||||
|
@ -77,10 +85,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<MathjaxHandleEditor
|
<MathjaxHandleEditor
|
||||||
initialValue={getComponent(activeImage).dataset.mathjax ?? ""}
|
initialValue={getComponent(activeImage).dataset.mathjax ?? ""}
|
||||||
on:update={async (event) => {
|
on:update={onEditorUpdate}
|
||||||
await onEditorUpdate(event);
|
|
||||||
dropdownObject.update();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<div class="margin-x">
|
<div class="margin-x">
|
||||||
<ButtonToolbar>
|
<ButtonToolbar>
|
||||||
|
|
|
@ -16,6 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
let codeMirror: CodeMirror.EditorFromTextArea;
|
let codeMirror: CodeMirror.EditorFromTextArea;
|
||||||
const changeTimer = new ChangeTimer();
|
const changeTimer = new ChangeTimer();
|
||||||
|
const dispatch = createEventDispatcher();
|
||||||
|
|
||||||
function onInput() {
|
function onInput() {
|
||||||
changeTimer.schedule(
|
changeTimer.schedule(
|
||||||
|
@ -24,12 +25,16 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onBlur() {
|
||||||
|
changeTimer.fireImmediately();
|
||||||
|
}
|
||||||
|
|
||||||
function openCodemirror(textarea: HTMLTextAreaElement): void {
|
function openCodemirror(textarea: HTMLTextAreaElement): void {
|
||||||
codeMirror = CodeMirror.fromTextArea(textarea, codeMirrorOptions);
|
codeMirror = CodeMirror.fromTextArea(textarea, codeMirrorOptions);
|
||||||
codeMirror.on("change", onInput);
|
codeMirror.on("change", onInput);
|
||||||
|
codeMirror.on("blur", onBlur);
|
||||||
}
|
}
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
let textarea: HTMLTextAreaElement;
|
let textarea: HTMLTextAreaElement;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|
|
@ -3,10 +3,16 @@
|
||||||
|
|
||||||
export class ChangeTimer {
|
export class ChangeTimer {
|
||||||
private value: number | null = null;
|
private value: number | null = null;
|
||||||
|
private action: (() => void) | null = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.fireImmediately = this.fireImmediately.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
schedule(action: () => void, delay: number): void {
|
schedule(action: () => void, delay: number): void {
|
||||||
this.clear();
|
this.clear();
|
||||||
this.value = setTimeout(action, delay);
|
this.action = action;
|
||||||
|
this.value = setTimeout(this.fireImmediately, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(): void {
|
clear(): void {
|
||||||
|
@ -15,4 +21,13 @@ export class ChangeTimer {
|
||||||
this.value = null;
|
this.value = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fireImmediately(): void {
|
||||||
|
if (this.action) {
|
||||||
|
this.action();
|
||||||
|
this.action = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,11 @@ export function saveNow(keepFocus: boolean): void {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
saveFieldTimer.clear();
|
|
||||||
|
|
||||||
if (keepFocus) {
|
if (keepFocus) {
|
||||||
saveField(currentField, "key");
|
saveFieldTimer.fireImmediately();
|
||||||
} else {
|
} else {
|
||||||
// triggers onBlur, which saves
|
// triggers onBlur, which saves
|
||||||
|
saveFieldTimer.clear();
|
||||||
currentField.blur();
|
currentField.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,12 @@ export function wrapInternal(
|
||||||
document.execCommand("inserthtml", false, new_);
|
document.execCommand("inserthtml", false, new_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!span.innerHTML) {
|
if (
|
||||||
|
!span.innerHTML &&
|
||||||
|
/* ugly solution: treat <anki-mathjax> differently than other wraps */ !front.includes(
|
||||||
|
"<anki-mathjax"
|
||||||
|
)
|
||||||
|
) {
|
||||||
moveCursorPastPostfix(selection, back);
|
moveCursorPastPostfix(selection, back);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue