From d6aa95950d8dcd0160c9137e7ce6db2feaf18507 Mon Sep 17 00:00:00 2001
From: Taylor Obyen <162023405+taylorobyen@users.noreply.github.com>
Date: Wed, 2 Oct 2024 03:19:52 -0400
Subject: [PATCH 1/6] Fix occlusion drift again (#3443)
* Fix occlusion drift
* Fix image editor occasionally not loading fully
* Fix occlusion disassociation when browsing
* Address oversights
* Fix translucent modifier applies to newly created shapes incorrectly
* Fix i-text turns yellow upon immediate note change
* Fix image occlusion hot keys not disabled when typing
* Improve text label creation experience
* Remove redundant functions
* Fix error when adding occlusion (dae)
---
qt/aqt/editor.py | 2 +-
ts/editor/NoteEditor.svelte | 6 +--
.../image-occlusion/ImageOcclusionPage.svelte | 2 +-
ts/routes/image-occlusion/MaskEditor.svelte | 40 +++++++++----------
ts/routes/image-occlusion/Toolbar.svelte | 37 ++++++++++-------
ts/routes/image-occlusion/mask-editor.ts | 28 ++++++++-----
ts/routes/image-occlusion/shapes/base.ts | 2 +-
ts/routes/image-occlusion/shapes/text.ts | 3 +-
ts/routes/image-occlusion/shapes/to-cloze.ts | 8 +---
ts/routes/image-occlusion/store.ts | 2 +
.../image-occlusion/tools/from-shapes.ts | 6 ++-
ts/routes/image-occlusion/tools/lib.ts | 25 +++++++++---
ts/routes/image-occlusion/tools/tool-text.ts | 17 +++-----
.../image-occlusion/tools/tool-undo-redo.ts | 8 ++--
ts/routes/image-occlusion/tools/tool-zoom.ts | 9 +++--
15 files changed, 111 insertions(+), 84 deletions(-)
diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py
index 975780e4eb7..ebd1f50589c 100644
--- a/qt/aqt/editor.py
+++ b/qt/aqt/editor.py
@@ -1125,7 +1125,7 @@ def reset_image_occlusion(self) -> None:
self.web.eval("resetIOImageLoaded()")
def update_occlusions_field(self) -> None:
- self.web.eval("updateOcclusionsField()")
+ self.web.eval("saveOcclusions()")
def _setup_mask_editor(self, io_options: dict):
self.web.eval(
diff --git a/ts/editor/NoteEditor.svelte b/ts/editor/NoteEditor.svelte
index 83ceec32871..885200e07d4 100644
--- a/ts/editor/NoteEditor.svelte
+++ b/ts/editor/NoteEditor.svelte
@@ -459,7 +459,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
globalThis.setImageField = setImageField;
- function updateOcclusionsField(): void {
+ function saveOcclusions(): void {
if (isImageOcclusion && globalThis.canvas) {
const occlusionsData = exportShapesToClozeDeletions($hideAllGuessOne);
fieldStores[ioFields.occlusions].set(occlusionsData.clozes);
@@ -572,7 +572,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
triggerChanges,
setIsImageOcclusion,
setupMaskEditor,
- updateOcclusionsField,
+ saveOcclusions,
...oldEditorAdapter,
});
@@ -637,7 +637,7 @@ the AddCards dialog) should be implemented in the user of this component.
diff --git a/ts/routes/image-occlusion/ImageOcclusionPage.svelte b/ts/routes/image-occlusion/ImageOcclusionPage.svelte
index 4d4d38f3bbd..aad104ad656 100644
--- a/ts/routes/image-occlusion/ImageOcclusionPage.svelte
+++ b/ts/routes/image-occlusion/ImageOcclusionPage.svelte
@@ -40,7 +40,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-
+
diff --git a/ts/routes/image-occlusion/MaskEditor.svelte b/ts/routes/image-occlusion/MaskEditor.svelte
index b6b9d4718e5..9181237533b 100644
--- a/ts/routes/image-occlusion/MaskEditor.svelte
+++ b/ts/routes/image-occlusion/MaskEditor.svelte
@@ -2,15 +2,6 @@
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
-
diff --git a/ts/routes/image-occlusion/Toolbar.svelte b/ts/routes/image-occlusion/Toolbar.svelte
index bf6deafb60f..3031494a884 100644
--- a/ts/routes/image-occlusion/Toolbar.svelte
+++ b/ts/routes/image-occlusion/Toolbar.svelte
@@ -25,8 +25,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import Shortcut from "$lib/components/Shortcut.svelte";
import WithFloating from "$lib/components/WithFloating.svelte";
- import { emitChangeSignal } from "./MaskEditor.svelte";
- import { hideAllGuessOne, ioMaskEditorVisible, textEditingState } from "./store";
+ import {
+ hideAllGuessOne,
+ ioMaskEditorVisible,
+ textEditingState,
+ saveNeededStore,
+ opacityStateStore,
+ } from "./store";
import { drawEllipse, drawPolygon, drawRectangle, drawText } from "./tools/index";
import { makeMaskTransparent } from "./tools/lib";
import { enableSelectable, stopDraw } from "./tools/lib";
@@ -55,7 +60,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let activeTool = "cursor";
let showAlignTools = false;
let leftPos = 82;
- let maksOpacity = false;
+ let maskOpacity = false;
let showFloating = false;
const direction = getContext
>(directionKey);
// handle zoom event when mouse scroll and ctrl key are hold for panzoom
@@ -158,13 +163,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
}
- const handleToolChanges = (activeTool: string) => {
+ const handleToolChanges = (newActiveTool: string) => {
disableFunctions();
enableSelectable(canvas, true);
// remove unfinished polygon when switching to other tools
removeUnfinishedPolygon(canvas);
- switch (activeTool) {
+ switch (newActiveTool) {
case "cursor":
drawCursor(canvas);
break;
@@ -178,9 +183,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
drawPolygon(canvas);
break;
case "draw-text":
- drawText(canvas);
- break;
- default:
+ drawText(canvas, () => {
+ activeTool = "cursor";
+ handleToolChanges(activeTool);
+ });
break;
}
};
@@ -198,10 +204,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function changeOcclusionType(occlusionType: "all" | "one"): void {
$hideAllGuessOne = occlusionType === "all";
- emitChangeSignal();
+ saveNeededStore.set(true);
}
onMount(() => {
+ opacityStateStore.set(maskOpacity);
removeHandlers = singleCallback(
on(document, "click", onClick),
on(window, "mousemove", onMousemove),
@@ -336,8 +343,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
toggleTranslucentKeyCombination,
)})"
on:click={() => {
- maksOpacity = !maksOpacity;
- makeMaskTransparent(canvas, maksOpacity);
+ maskOpacity = !maskOpacity;
+ makeMaskTransparent(canvas, maskOpacity);
}}
>
@@ -346,8 +353,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{
- maksOpacity = !maksOpacity;
- makeMaskTransparent(canvas, maksOpacity);
+ maskOpacity = !maskOpacity;
+ makeMaskTransparent(canvas, maskOpacity);
}}
/>
{/if}
@@ -372,7 +379,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
keyCombination={tool.shortcut}
on:action={() => {
tool.action(canvas);
- emitChangeSignal();
+ saveNeededStore.set(true);
}}
/>
{/if}
@@ -400,7 +407,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
keyCombination={tool.shortcut}
on:action={() => {
tool.action(canvas);
- emitChangeSignal();
+ saveNeededStore.set(true);
}}
/>
{/if}
diff --git a/ts/routes/image-occlusion/mask-editor.ts b/ts/routes/image-occlusion/mask-editor.ts
index f3b24bd546d..f3fe53c8dbd 100644
--- a/ts/routes/image-occlusion/mask-editor.ts
+++ b/ts/routes/image-occlusion/mask-editor.ts
@@ -8,7 +8,7 @@ import { fabric } from "fabric";
import { get } from "svelte/store";
import { optimumCssSizeForCanvas } from "./canvas-scale";
-import { notesDataStore, tagsWritable } from "./store";
+import { notesDataStore, saveNeededStore, tagsWritable, textEditingState } from "./store";
import Toast from "./Toast.svelte";
import { addShapesToCanvasFromCloze } from "./tools/add-from-cloze";
import { enableSelectable, makeShapesRemainInCanvas, moveShapeToCanvasBoundaries } from "./tools/lib";
@@ -24,11 +24,10 @@ export interface ImageLoadedEvent {
export const setupMaskEditor = async (
path: string,
- onChange: () => void,
onImageLoaded: (event: ImageLoadedEvent) => void,
): Promise => {
const imageData = await getImageForOcclusion({ path });
- const canvas = initCanvas(onChange);
+ const canvas = initCanvas();
// get image width and height
const image = document.getElementById("image") as HTMLImageElement;
@@ -46,7 +45,6 @@ export const setupMaskEditor = async (
export const setupMaskEditorForEdit = async (
noteId: number,
- onChange: () => void,
onImageLoaded: (event: ImageLoadedEvent) => void,
): Promise => {
const clozeNoteResponse = await getImageOcclusionNote({ noteId: BigInt(noteId) });
@@ -63,14 +61,17 @@ export const setupMaskEditorForEdit = async (
}
const clozeNote = clozeNoteResponse.value.value;
- const canvas = initCanvas(onChange);
+ const canvas = initCanvas();
// get image width and height
const image = document.getElementById("image") as HTMLImageElement;
image.src = getImageData(clozeNote.imageData!, clozeNote.imageFileName!);
image.onload = async function() {
- const size = optimumCssSizeForCanvas({ width: image.width, height: image.height }, containerSize());
+ const size = optimumCssSizeForCanvas(
+ { width: image.naturalWidth, height: image.naturalHeight },
+ containerSize(),
+ );
setCanvasSize(canvas);
const boundingBox = setupBoundingBox(canvas, size);
addShapesToCanvasFromCloze(canvas, boundingBox, clozeNote.occlusions);
@@ -85,7 +86,7 @@ export const setupMaskEditorForEdit = async (
return canvas;
};
-function initCanvas(onChange: () => void): fabric.Canvas {
+function initCanvas(): fabric.Canvas {
const canvas = new fabric.Canvas("canvas");
tagsWritable.set([]);
globalThis.canvas = canvas;
@@ -110,9 +111,18 @@ function initCanvas(onChange: () => void): fabric.Canvas {
modifiedPolygon(canvas, evt.target);
undoStack.onObjectModified();
}
- onChange();
+ saveNeededStore.set(true);
+ });
+ canvas.on("text:editing:entered", function() {
+ textEditingState.set(true);
+ });
+
+ canvas.on("text:editing:exited", function() {
+ textEditingState.set(false);
+ });
+ canvas.on("object:removed", () => {
+ saveNeededStore.set(true);
});
- canvas.on("object:removed", onChange);
return canvas;
}
diff --git a/ts/routes/image-occlusion/shapes/base.ts b/ts/routes/image-occlusion/shapes/base.ts
index 1e30b479d76..95ea7407c1e 100644
--- a/ts/routes/image-occlusion/shapes/base.ts
+++ b/ts/routes/image-occlusion/shapes/base.ts
@@ -18,7 +18,7 @@ export type ShapeOrShapes = Shape | Shape[];
export class Shape {
left: number;
top: number;
- fill: string = SHAPE_MASK_COLOR;
+ fill: string;
/** Whether occlusions from other cloze numbers should be shown on the
* question side. Used only in reviewer code.
*/
diff --git a/ts/routes/image-occlusion/shapes/text.ts b/ts/routes/image-occlusion/shapes/text.ts
index a8043670a2a..b41a071ecd7 100644
--- a/ts/routes/image-occlusion/shapes/text.ts
+++ b/ts/routes/image-occlusion/shapes/text.ts
@@ -3,7 +3,7 @@
import { fabric } from "fabric";
-import { TEXT_BACKGROUND_COLOR, TEXT_FONT_FAMILY, TEXT_FONT_SIZE, TEXT_PADDING } from "../tools/lib";
+import { TEXT_BACKGROUND_COLOR, TEXT_COLOR, TEXT_FONT_FAMILY, TEXT_FONT_SIZE, TEXT_PADDING } from "../tools/lib";
import type { ConstructorParams, Size } from "../types";
import type { ShapeDataForCloze } from "./base";
import { Shape } from "./base";
@@ -23,6 +23,7 @@ export class Text extends Shape {
...rest
}: ConstructorParams = {}) {
super(rest);
+ this.fill = TEXT_COLOR;
this.text = text;
this.scaleX = scaleX;
this.scaleY = scaleY;
diff --git a/ts/routes/image-occlusion/shapes/to-cloze.ts b/ts/routes/image-occlusion/shapes/to-cloze.ts
index 521666f6002..ca27c6fa538 100644
--- a/ts/routes/image-occlusion/shapes/to-cloze.ts
+++ b/ts/routes/image-occlusion/shapes/to-cloze.ts
@@ -4,7 +4,7 @@
import { fabric } from "fabric";
import { cloneDeep } from "lodash-es";
-import { getBoundingBox } from "../tools/lib";
+import { getBoundingBoxSize } from "../tools/lib";
import type { Size } from "../types";
import type { Shape, ShapeOrShapes } from "./base";
import { Ellipse } from "./ellipse";
@@ -97,7 +97,7 @@ export function baseShapesFromFabric(): ShapeOrShapes[] {
? activeObject
: null;
const objects = canvas.getObjects() as fabric.Object[];
- const boundingBox = getBoundingBox();
+ const boundingBox = getBoundingBoxSize();
// filter transparent rectangles
return objects
.map((object) => {
@@ -170,10 +170,6 @@ function fabricObjectToBaseShapeOrShapes(
shape.top = newPosition.y;
}
- if (size == undefined) {
- size = { width: 0, height: 0 };
- }
-
shape = shape.toNormal(size);
return shape;
}
diff --git a/ts/routes/image-occlusion/store.ts b/ts/routes/image-occlusion/store.ts
index 5847250dd3f..0af7696ebe8 100644
--- a/ts/routes/image-occlusion/store.ts
+++ b/ts/routes/image-occlusion/store.ts
@@ -17,3 +17,5 @@ export const ioImageLoadedStore = writable(false);
export const opacityStateStore = writable(false);
// store state of text editing
export const textEditingState = writable(false);
+// Stores if the canvas shapes data needs to be saved
+export const saveNeededStore = writable(false);
diff --git a/ts/routes/image-occlusion/tools/from-shapes.ts b/ts/routes/image-occlusion/tools/from-shapes.ts
index ab0a3f8b743..851afd04af0 100644
--- a/ts/routes/image-occlusion/tools/from-shapes.ts
+++ b/ts/routes/image-occlusion/tools/from-shapes.ts
@@ -12,9 +12,13 @@ export const addShape = (
shape: Shape,
): void => {
const fabricShape = shape.toFabric(boundingBox.getBoundingRect(true));
- addBorder(fabricShape);
if (fabricShape.type === "i-text") {
enableUniformScaling(canvas, fabricShape);
+ } else {
+ // No border around i-text shapes since it will be interpretted
+ // as character stroke, this is supposed to create an outline
+ // around the entire shape.
+ addBorder(fabricShape);
}
canvas.add(fabricShape);
};
diff --git a/ts/routes/image-occlusion/tools/lib.ts b/ts/routes/image-occlusion/tools/lib.ts
index 7f09ad4f14d..3709add6ae7 100644
--- a/ts/routes/image-occlusion/tools/lib.ts
+++ b/ts/routes/image-occlusion/tools/lib.ts
@@ -5,6 +5,7 @@ import { fabric } from "fabric";
import { get } from "svelte/store";
import { opacityStateStore } from "../store";
+import type { Size } from "../types";
export const SHAPE_MASK_COLOR = "#ffeba2";
export const BORDER_COLOR = "#212121";
@@ -12,6 +13,7 @@ export const TEXT_BACKGROUND_COLOR = "#ffffff";
export const TEXT_FONT_FAMILY = "Arial";
export const TEXT_PADDING = 5;
export const TEXT_FONT_SIZE = 40;
+export const TEXT_COLOR = "#000000";
let _clipboard;
@@ -310,20 +312,31 @@ export const selectAllShapes = (canvas: fabric.Canvas) => {
export const isPointerInBoundingBox = (pointer): boolean => {
const boundingBox = getBoundingBox();
+ if (boundingBox === undefined) {
+ return false;
+ }
boundingBox.selectable = false;
boundingBox.evented = false;
if (
- pointer.x < boundingBox.left
- || pointer.x > boundingBox.left + boundingBox.width
- || pointer.y < boundingBox.top
- || pointer.y > boundingBox.top + boundingBox.height
+ pointer.x < boundingBox.left!
+ || pointer.x > boundingBox.left! + boundingBox.width!
+ || pointer.y < boundingBox.top!
+ || pointer.y > boundingBox.top! + boundingBox.height!
) {
return false;
}
return true;
};
-export const getBoundingBox = () => {
- const canvas = globalThis.canvas;
+export const getBoundingBox = (): fabric.Rect | undefined => {
+ const canvas: fabric.Canvas = globalThis.canvas;
return canvas.getObjects().find((obj) => obj.fill === "transparent");
};
+
+export const getBoundingBoxSize = (): Size => {
+ const boundingBoxSize = getBoundingBox()?.getBoundingRect(true);
+ if (boundingBoxSize) {
+ return { width: boundingBoxSize.width, height: boundingBoxSize.height };
+ }
+ return { width: 0, height: 0 };
+};
diff --git a/ts/routes/image-occlusion/tools/tool-text.ts b/ts/routes/image-occlusion/tools/tool-text.ts
index ee3fb1fb608..76f46318998 100644
--- a/ts/routes/image-occlusion/tools/tool-text.ts
+++ b/ts/routes/image-occlusion/tools/tool-text.ts
@@ -4,7 +4,8 @@
import { fabric } from "fabric";
import { get } from "svelte/store";
-import { opacityStateStore, textEditingState } from "../store";
+import type { Callback } from "@tslib/helpers";
+import { opacityStateStore } from "../store";
import {
enableUniformScaling,
isPointerInBoundingBox,
@@ -16,11 +17,11 @@ import {
import { undoStack } from "./tool-undo-redo";
import { onPinchZoom } from "./tool-zoom";
-export const drawText = (canvas: fabric.Canvas): void => {
+export const drawText = (canvas: fabric.Canvas, onActivated: Callback): void => {
canvas.selectionColor = "rgba(0, 0, 0, 0)";
stopDraw(canvas);
- let text;
+ let text: fabric.IText;
canvas.on("mouse:down", function(o) {
if (o.target) {
@@ -52,7 +53,9 @@ export const drawText = (canvas: fabric.Canvas): void => {
canvas.add(text);
canvas.setActiveObject(text);
undoStack.onObjectAdded(text.id);
+ text.enterEditing();
text.selectAll();
+ onActivated();
});
canvas.on("mouse:move", function(o) {
@@ -62,12 +65,4 @@ export const drawText = (canvas: fabric.Canvas): void => {
return;
}
});
-
- canvas.on("text:editing:entered", function() {
- textEditingState.set(true);
- });
-
- canvas.on("text:editing:exited", function() {
- textEditingState.set(false);
- });
};
diff --git a/ts/routes/image-occlusion/tools/tool-undo-redo.ts b/ts/routes/image-occlusion/tools/tool-undo-redo.ts
index a12d05d9e60..c1754376ecc 100644
--- a/ts/routes/image-occlusion/tools/tool-undo-redo.ts
+++ b/ts/routes/image-occlusion/tools/tool-undo-redo.ts
@@ -7,7 +7,7 @@ import { writable } from "svelte/store";
import { mdiRedo, mdiUndo } from "$lib/components/icons";
-import { emitChangeSignal } from "../MaskEditor.svelte";
+import { saveNeededStore } from "../store";
import { redoKeyCombination, undoKeyCombination } from "./shortcuts";
/**
@@ -84,7 +84,7 @@ class UndoStack {
this.locked = true;
this.canvas?.loadFromJSON(this.stack[this.index], () => {
this.canvas?.renderAll();
- emitChangeSignal();
+ saveNeededStore.set(true);
this.locked = false;
});
// make bounding box unselectable
@@ -100,12 +100,12 @@ class UndoStack {
this.push();
}
this.shapeIds.add(id);
- emitChangeSignal();
+ saveNeededStore.set(true);
}
onObjectModified(): void {
this.push();
- emitChangeSignal();
+ saveNeededStore.set(true);
}
private maybePush(obj: fabric.IEvent): void {
diff --git a/ts/routes/image-occlusion/tools/tool-zoom.ts b/ts/routes/image-occlusion/tools/tool-zoom.ts
index 6f2c1374eec..3d2ef0d6a84 100644
--- a/ts/routes/image-occlusion/tools/tool-zoom.ts
+++ b/ts/routes/image-occlusion/tools/tool-zoom.ts
@@ -9,7 +9,8 @@ import Hammer from "hammerjs";
import { isDesktop } from "$lib/tslib/platform";
-import { getBoundingBox, redraw } from "./lib";
+import type { Size } from "../types";
+import { getBoundingBoxSize, redraw } from "./lib";
const minScale = 0.5;
const maxScale = 5;
@@ -192,7 +193,7 @@ const onMouseUp = () => {
};
export const constrainBoundsAroundBgImage = (canvas: fabric.Canvas) => {
- const boundingBox = getBoundingBox();
+ const boundingBox = getBoundingBoxSize();
const ioImage = document.getElementById("image") as HTMLImageElement;
const width = boundingBox.width * canvas.getZoom();
@@ -217,7 +218,7 @@ export const setCanvasSize = (canvas: fabric.Canvas) => {
};
const fitCanvasVptScale = (canvas: fabric.Canvas) => {
- const boundingBox = getBoundingBox();
+ const boundingBox = getBoundingBoxSize();
const ratio = getScaleRatio(boundingBox);
const vpt = canvas.viewportTransform!;
@@ -237,7 +238,7 @@ const fitCanvasVptScale = (canvas: fabric.Canvas) => {
redraw(canvas);
};
-const getScaleRatio = (boundingBox: fabric.Rect) => {
+const getScaleRatio = (boundingBox: Size) => {
const h1 = boundingBox.height!;
const w1 = boundingBox.width!;
const w2 = innerWidth - 42;
From 159681d9f28b8887d506f2624b91d179ba6d4d01 Mon Sep 17 00:00:00 2001
From: Jarrett Ye
Date: Wed, 2 Oct 2024 16:09:25 +0800
Subject: [PATCH 2/6] Fix/Reschedule doesn't work on cards in filtered deck
(#3441)
* Fix/Reschedule doesn't work on cards in filtered deck
* Update docstring (dae)
---
rslib/src/search/parser.rs | 5 +++--
rslib/src/search/sqlwriter.rs | 7 ++++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/rslib/src/search/parser.rs b/rslib/src/search/parser.rs
index 492c88d937a..93df4ea082c 100644
--- a/rslib/src/search/parser.rs
+++ b/rslib/src/search/parser.rs
@@ -61,10 +61,11 @@ pub enum SearchNode {
EditedInDays(u32),
CardTemplate(TemplateKind),
Deck(String),
- /// Matches cards in a list of decks (original_deck_id is not checked).
+ /// Matches cards in a list of deck ids. Cards are matched even if they are
+ /// in a filtered deck.
DeckIdsWithoutChildren(String),
/// Matches cards in a deck or its children (original_deck_id is not
- /// checked).
+ /// checked, so filtered cards are not matched).
DeckIdWithChildren(DeckId),
IntroducedInDays(u32),
NotetypeId(NotetypeId),
diff --git a/rslib/src/search/sqlwriter.rs b/rslib/src/search/sqlwriter.rs
index 20930b2f5d3..97a6a29a3a3 100644
--- a/rslib/src/search/sqlwriter.rs
+++ b/rslib/src/search/sqlwriter.rs
@@ -161,7 +161,12 @@ impl SqlWriter<'_> {
write!(self.sql, "n.mid = {}", ntid).unwrap();
}
SearchNode::DeckIdsWithoutChildren(dids) => {
- write!(self.sql, "c.did in ({})", dids).unwrap();
+ write!(
+ self.sql,
+ "c.did in ({}) or (c.odid != 0 and c.odid in ({}))",
+ dids, dids
+ )
+ .unwrap();
}
SearchNode::DeckIdWithChildren(did) => self.write_deck_id_with_children(*did)?,
SearchNode::Notetype(notetype) => self.write_notetype(&norm(notetype)),
From cc45db0e22f414ba4777894c6b64cf070b521861 Mon Sep 17 00:00:00 2001
From: Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com>
Date: Wed, 2 Oct 2024 01:20:44 -0700
Subject: [PATCH 3/6] Fix Mnemosyne importer chokes on due dates >= 2038
(#3459)
* Fix integer overflow
* Update CONTRIBUTORS
---
CONTRIBUTORS | 2 +-
rslib/src/import_export/text/import.rs | 4 ++--
rslib/src/import_export/text/mod.rs | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index ea949e7722c..1d6e0f9893d 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -187,7 +187,7 @@ Christian Donat
Asuka Minato
Dillon Baldwin
Voczi
-Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com>
+Ben Nguyen <105088397+bpnguyen107@users.noreply.github.com>
Themis Demetriades
Luke Bartholomew
Gregory Abrasaldo
diff --git a/rslib/src/import_export/text/import.rs b/rslib/src/import_export/text/import.rs
index 6b332a65164..524a0c2391b 100644
--- a/rslib/src/import_export/text/import.rs
+++ b/rslib/src/import_export/text/import.rs
@@ -635,9 +635,9 @@ impl ForeignCard {
}
fn native_due(self, timing: &SchedTimingToday) -> i32 {
- let day_start = timing.next_day_at.0 as i32 - 86_400;
+ let day_start = timing.next_day_at.0 - 86_400;
let due_delta = (self.due - day_start) / 86_400;
- due_delta + timing.days_elapsed as i32
+ due_delta as i32 + timing.days_elapsed as i32
}
}
diff --git a/rslib/src/import_export/text/mod.rs b/rslib/src/import_export/text/mod.rs
index 7dbf55f581f..e9e2da76690 100644
--- a/rslib/src/import_export/text/mod.rs
+++ b/rslib/src/import_export/text/mod.rs
@@ -40,7 +40,7 @@ pub struct ForeignNote {
#[serde(default)]
pub struct ForeignCard {
/// Seconds-based timestamp
- pub due: i32,
+ pub due: i64,
/// In days
pub interval: u32,
pub ease_factor: f32,
From cfd1eba7ec6f5f6678d79c6207df22381f128a39 Mon Sep 17 00:00:00 2001
From: "a.r" <887320+twwn@users.noreply.github.com>
Date: Wed, 2 Oct 2024 10:28:18 +0200
Subject: [PATCH 4/6] typeanswer: hoist strip_expected (#3464)
(renamed from prepare_expected)
Single use makes it likelier to get inlined, too.
---
rslib/src/typeanswer.rs | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)
diff --git a/rslib/src/typeanswer.rs b/rslib/src/typeanswer.rs
index d1975dbdbb5..5277e88ff16 100644
--- a/rslib/src/typeanswer.rs
+++ b/rslib/src/typeanswer.rs
@@ -34,12 +34,12 @@ macro_rules! format_typeans {
// Public API
pub fn compare_answer(expected: &str, typed: &str, combining: bool) -> String {
- if typed.is_empty() {
- format_typeans!(htmlescape::encode_minimal(&prepare_expected(expected)))
- } else if combining {
- Diff::new(expected, typed).to_html()
- } else {
- DiffNonCombining::new(expected, typed).to_html()
+ let stripped = strip_expected(expected);
+
+ match typed.is_empty() {
+ true => format_typeans!(htmlescape::encode_minimal(&stripped)),
+ false if combining => Diff::new(&stripped, typed).to_html(),
+ false => DiffNonCombining::new(&stripped, typed).to_html(),
}
}
@@ -116,7 +116,7 @@ fn slice(chars: &[char], start: usize, end: usize) -> String {
chars[start..end].iter().collect()
}
-fn prepare_expected(expected: &str) -> String {
+fn strip_expected(expected: &str) -> String {
let no_av_tags = strip_av_tags(expected);
let no_linebreaks = LINEBREAKS.replace_all(&no_av_tags, " ");
strip_html(&no_linebreaks).trim().to_string()
@@ -167,7 +167,7 @@ impl DiffTrait for Diff {
fn new(expected: &str, typed: &str) -> Self {
Self {
typed: Self::normalize_typed(typed),
- expected: normalize(&prepare_expected(expected)),
+ expected: normalize(expected),
}
}
fn normalize_typed(typed: &str) -> Vec {
@@ -202,7 +202,7 @@ impl DiffTrait for DiffNonCombining {
let mut expected_stripped = String::new();
// tokenized into "char+combining" for final rendering
let mut expected_split: Vec = Vec::new();
- for c in normalize(&prepare_expected(expected)) {
+ for c in normalize(expected) {
if unicode_normalization::char::is_combining_mark(c) {
if let Some(last) = expected_split.last_mut() {
last.push(c);
@@ -219,7 +219,7 @@ impl DiffTrait for DiffNonCombining {
expected: expected_stripped.chars().collect(),
},
expected_split,
- expected_original: prepare_expected(expected),
+ expected_original: expected.to_string(),
}
}
@@ -340,7 +340,8 @@ mod test {
#[test]
fn html_and_media() {
- let ctx = Diff::new("[sound:foo.mp3]1 2", "1 2");
+ let stripped = strip_expected("[sound:foo.mp3]1 2");
+ let ctx = Diff::new(&stripped, "1 2");
// the spacing is handled by wrapping html output in white-space: pre-wrap
assert_eq!(ctx.to_tokens().expected_tokens, &[good("1 2")]);
}
@@ -387,9 +388,10 @@ mod test {
#[test]
fn tags_removed() {
- assert_eq!(prepare_expected("123
"), "123");
+ let stripped = strip_expected("123
");
+ assert_eq!(stripped, "123");
assert_eq!(
- Diff::new("123
", "123").to_html(),
+ Diff::new(&stripped, "123").to_html(),
"123
"
);
}
From 2ac27585b2c78d7aca0b0913159b2000a7a4725d Mon Sep 17 00:00:00 2001
From: Shirish Pokhrel
Date: Wed, 2 Oct 2024 04:42:41 -0400
Subject: [PATCH 5/6] Fix editor loses focus when toggling list and copy
selects wrong target deck for filtered decks (#3465)
* fix copy selects wrong target deck for filtered decks
---
CONTRIBUTORS | 1 +
qt/aqt/browser/browser.py | 2 +-
qt/aqt/reviewer.py | 2 +-
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 1d6e0f9893d..748b753ad81 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -194,6 +194,7 @@ Gregory Abrasaldo
Taylor Obyen <162023405+taylorobyen@users.noreply.github.com>
Kris Cherven
twwn
+Shirish Pokhrel
********************
diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py
index 3056f96327b..0ddb4737177 100644
--- a/qt/aqt/browser/browser.py
+++ b/qt/aqt/browser/browser.py
@@ -740,7 +740,7 @@ def onHelp(self) -> None:
def on_create_copy(self) -> None:
if note := self.table.get_current_note():
- deck_id = self.table.get_current_card().did
+ deck_id = self.table.get_current_card().current_deck_id()
aqt.dialogs.open("AddCards", self.mw).set_note(note, deck_id)
@no_arg_trigger
diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py
index f64f82208df..c219dfc42e6 100644
--- a/qt/aqt/reviewer.py
+++ b/qt/aqt/reviewer.py
@@ -1155,7 +1155,7 @@ def forget_current_card(self) -> None:
def on_create_copy(self) -> None:
if self.card:
aqt.dialogs.open("AddCards", self.mw).set_note(
- self.card.note(), self.card.did
+ self.card.note(), self.card.current_deck_id()
)
def delete_current_note(self) -> None:
From 9ced6be03ec25afef66452a655c88a16bb5eb607 Mon Sep 17 00:00:00 2001
From: mmjang <752515918@qq.com>
Date: Wed, 2 Oct 2024 17:37:40 +0800
Subject: [PATCH 6/6] Decide if element is bold by getComputedStyle (#2453)
(#2579)
* Decide if element is bold by getComputedStyle (#2453)
* Use getComputedStyle() for italics too (dae)
---
ts/editor/editor-toolbar/BoldButton.svelte | 5 +++--
ts/editor/editor-toolbar/ItalicButton.svelte | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/ts/editor/editor-toolbar/BoldButton.svelte b/ts/editor/editor-toolbar/BoldButton.svelte
index d6b580b5633..215e984302b 100644
--- a/ts/editor/editor-toolbar/BoldButton.svelte
+++ b/ts/editor/editor-toolbar/BoldButton.svelte
@@ -17,8 +17,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return match.remove();
}
- const fontWeight = element.style.fontWeight;
- if (fontWeight === "bold" || Number(fontWeight) >= 700) {
+ const fontWeight = getComputedStyle(element).fontWeight;
+ const threshold = 700;
+ if (fontWeight && Number(fontWeight) >= threshold) {
return match.clear((): void => {
if (
removeStyleProperties(element, "font-weight") &&
diff --git a/ts/editor/editor-toolbar/ItalicButton.svelte b/ts/editor/editor-toolbar/ItalicButton.svelte
index c31b8e93bb3..d5bbb300440 100644
--- a/ts/editor/editor-toolbar/ItalicButton.svelte
+++ b/ts/editor/editor-toolbar/ItalicButton.svelte
@@ -17,7 +17,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return match.remove();
}
- if (["italic", "oblique"].includes(element.style.fontStyle)) {
+ const fontStyle = getComputedStyle(element).fontStyle;
+ if (["italic", "oblique"].includes(fontStyle)) {
return match.clear((): void => {
if (
removeStyleProperties(element, "font-style") &&