This commit is contained in:
jariji 2025-11-03 17:12:00 -08:00 committed by GitHub
commit d92966ddb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 162 additions and 75 deletions

View file

@ -47,6 +47,7 @@ notetypes-toggle-masks = Toggle Masks
notetypes-image-occlusion-name = Image Occlusion notetypes-image-occlusion-name = Image Occlusion
notetypes-hide-all-guess-one = Hide All, Guess One notetypes-hide-all-guess-one = Hide All, Guess One
notetypes-hide-one-guess-one = Hide One, Guess One notetypes-hide-one-guess-one = Hide One, Guess One
notetypes-hide-all-but-one = Hide All But One
notetypes-error-generating-cloze = An error occurred when generating an image occlusion note notetypes-error-generating-cloze = An error occurred when generating an image occlusion note
notetypes-error-getting-imagecloze = An error occurred while fetching an image occlusion note notetypes-error-getting-imagecloze = An error occurred while fetching an image occlusion note
notetypes-error-loading-image-occlusion = Error loading image occlusion. Is your Anki version up to date? notetypes-error-loading-image-occlusion = Error loading image occlusion. Is your Anki version up to date?

View file

@ -69,6 +69,12 @@ message GetImageOcclusionNoteResponse {
uint32 ordinal = 2; uint32 ordinal = 2;
} }
enum OcclusionMode {
HIDE_ONE = 0;
HIDE_ALL = 1;
HIDE_ALL_BUT_ONE = 2;
}
message ImageOcclusionNote { message ImageOcclusionNote {
bytes image_data = 1; bytes image_data = 1;
repeated ImageOcclusion occlusions = 2; repeated ImageOcclusion occlusions = 2;
@ -76,7 +82,7 @@ message GetImageOcclusionNoteResponse {
string back_extra = 4; string back_extra = 4;
repeated string tags = 5; repeated string tags = 5;
string image_file_name = 6; string image_file_name = 6;
bool occlude_inactive = 7; OcclusionMode occlusion_mode = 7;
} }
oneof value { oneof value {

View file

@ -839,4 +839,22 @@ mod test {
let card2_html = reveal_cloze_text(text, 2, true); let card2_html = reveal_cloze_text(text, 2, true);
assert!(card2_html.contains(r#"data-ordinal="1,2""#)); assert!(card2_html.contains(r#"data-ordinal="1,2""#));
} }
#[test]
fn image_occlusion_modes() {
// Mode 1 (HideAll): should include data-occludeinactive="1"
let hide_all = "{{c1::image-occlusion:rect:left=10:top=20:width=30:height=40:oi=1}}";
let html = reveal_cloze_text(hide_all, 1, true);
assert!(html.contains(r#"data-occludeInactive="1""#));
// Mode 2 (HideAllButOne): should include data-occludeinactive="2"
let hide_all_but_one = "{{c1::image-occlusion:rect:left=10:top=20:width=30:height=40:oi=2}}";
let html = reveal_cloze_text(hide_all_but_one, 1, true);
assert!(html.contains(r#"data-occludeInactive="2""#));
// Mode 0 (HideOne): should not include data-occludeinactive attribute
let hide_one = "{{c1::image-occlusion:rect:left=10:top=20:width=30:height=40}}";
let html = reveal_cloze_text(hide_one, 1, true);
assert!(!html.contains("data-occludeInactive"));
}
} }

View file

@ -7,6 +7,7 @@ use std::path::PathBuf;
use anki_io::metadata; use anki_io::metadata;
use anki_io::read_file; use anki_io::read_file;
use anki_proto::image_occlusion::get_image_occlusion_note_response::ImageOcclusionNote; use anki_proto::image_occlusion::get_image_occlusion_note_response::ImageOcclusionNote;
use anki_proto::image_occlusion::get_image_occlusion_note_response::OcclusionMode;
use anki_proto::image_occlusion::get_image_occlusion_note_response::Value; use anki_proto::image_occlusion::get_image_occlusion_note_response::Value;
use anki_proto::image_occlusion::AddImageOcclusionNoteRequest; use anki_proto::image_occlusion::AddImageOcclusionNoteRequest;
use anki_proto::image_occlusion::GetImageForOcclusionResponse; use anki_proto::image_occlusion::GetImageForOcclusionResponse;
@ -97,14 +98,22 @@ impl Collection {
let idxs = nt.get_io_field_indexes()?; let idxs = nt.get_io_field_indexes()?;
cloze_note.occlusions = parse_image_occlusions(fields[idxs.occlusions as usize].as_str()); cloze_note.occlusions = parse_image_occlusions(fields[idxs.occlusions as usize].as_str());
cloze_note.occlude_inactive = cloze_note.occlusions.iter().any(|oc| { cloze_note.occlusion_mode = cloze_note
oc.shapes.iter().any(|sh| { .occlusions
sh.properties .iter()
.iter() .find_map(|oc| {
.find(|p| p.name == "oi") oc.shapes.iter().find_map(|sh| {
.is_some_and(|p| p.value == "1") sh.properties
.iter()
.find(|p| p.name == "oi")
.and_then(|p| match p.value.as_str() {
"1" => Some(OcclusionMode::HideAll as i32),
"2" => Some(OcclusionMode::HideAllButOne as i32),
_ => None,
})
})
}) })
}); .unwrap_or(OcclusionMode::HideOne as i32);
cloze_note.header.clone_from(&fields[idxs.header as usize]); cloze_note.header.clone_from(&fields[idxs.header as usize]);
cloze_note cloze_note
.back_extra .back_extra

View file

@ -423,9 +423,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import type { IOMode } from "../routes/image-occlusion/lib"; import type { IOMode } from "../routes/image-occlusion/lib";
import { exportShapesToClozeDeletions } from "../routes/image-occlusion/shapes/to-cloze"; import { exportShapesToClozeDeletions } from "../routes/image-occlusion/shapes/to-cloze";
import { import {
hideAllGuessOne,
ioImageLoadedStore, ioImageLoadedStore,
ioMaskEditorVisible, ioMaskEditorVisible,
occlusionMode,
} from "../routes/image-occlusion/store"; } from "../routes/image-occlusion/store";
import CollapseLabel from "./CollapseLabel.svelte"; import CollapseLabel from "./CollapseLabel.svelte";
import * as oldEditorAdapter from "./old-editor-adapter"; import * as oldEditorAdapter from "./old-editor-adapter";
@ -477,7 +477,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function saveOcclusions(): void { function saveOcclusions(): void {
if (isImageOcclusion && globalThis.canvas) { if (isImageOcclusion && globalThis.canvas) {
const occlusionsData = exportShapesToClozeDeletions($hideAllGuessOne); const occlusionsData = exportShapesToClozeDeletions($occlusionMode);
fieldStores[ioFields.occlusions].set(occlusionsData.clozes); fieldStores[ioFields.occlusions].set(occlusionsData.clozes);
} }
} }

View file

@ -15,6 +15,8 @@ import AlignVerticalCenter_ from "@mdi/svg/svg/align-vertical-center.svg?compone
import alignVerticalCenter_ from "@mdi/svg/svg/align-vertical-center.svg?url"; import alignVerticalCenter_ from "@mdi/svg/svg/align-vertical-center.svg?url";
import AlignVerticalTop_ from "@mdi/svg/svg/align-vertical-top.svg?component"; import AlignVerticalTop_ from "@mdi/svg/svg/align-vertical-top.svg?component";
import alignVerticalTop_ from "@mdi/svg/svg/align-vertical-top.svg?url"; import alignVerticalTop_ from "@mdi/svg/svg/align-vertical-top.svg?url";
import CheckboxBlankOutline_ from "@mdi/svg/svg/checkbox-blank-outline.svg?component";
import checkboxBlankOutline_ from "@mdi/svg/svg/checkbox-blank-outline.svg?url";
import CheckCircle_ from "@mdi/svg/svg/check-circle.svg?component"; import CheckCircle_ from "@mdi/svg/svg/check-circle.svg?component";
import checkCircle_ from "@mdi/svg/svg/check-circle.svg?url"; import checkCircle_ from "@mdi/svg/svg/check-circle.svg?url";
import ChevronDown_ from "@mdi/svg/svg/chevron-down.svg?component"; import ChevronDown_ from "@mdi/svg/svg/chevron-down.svg?component";
@ -251,6 +253,7 @@ export const underlineIcon = { url: underline_, component: Underline_ };
export const deleteIcon = { url: delete_, component: Delete_ }; export const deleteIcon = { url: delete_, component: Delete_ };
export const inlineIcon = { url: inline_, component: Inline_ }; export const inlineIcon = { url: inline_, component: Inline_ };
export const blockIcon = { url: block_, component: Block_ }; export const blockIcon = { url: block_, component: Block_ };
export const mdiCheckboxBlankOutline = { url: checkboxBlankOutline_, component: CheckboxBlankOutline_ };
export const mdiAlignHorizontalCenter = { url: alignHorizontalCenter_, component: AlignHorizontalCenter_ }; export const mdiAlignHorizontalCenter = { url: alignHorizontalCenter_, component: AlignHorizontalCenter_ };
export const mdiAlignHorizontalLeft = { url: alignHorizontalLeft_, component: AlignHorizontalLeft_ }; export const mdiAlignHorizontalLeft = { url: alignHorizontalLeft_, component: AlignHorizontalLeft_ };
export const mdiAlignHorizontalRight = { url: alignHorizontalRight_, component: AlignHorizontalRight_ }; export const mdiAlignHorizontalRight = { url: alignHorizontalRight_, component: AlignHorizontalRight_ };

View file

@ -16,6 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import Icon from "$lib/components/Icon.svelte"; import Icon from "$lib/components/Icon.svelte";
import IconButton from "$lib/components/IconButton.svelte"; import IconButton from "$lib/components/IconButton.svelte";
import { import {
mdiCheckboxBlankOutline,
mdiEye, mdiEye,
mdiFormatAlignCenter, mdiFormatAlignCenter,
mdiSquare, mdiSquare,
@ -26,11 +27,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import WithFloating from "$lib/components/WithFloating.svelte"; import WithFloating from "$lib/components/WithFloating.svelte";
import { import {
hideAllGuessOne,
ioMaskEditorVisible, ioMaskEditorVisible,
textEditingState, OcclusionMode,
saveNeededStore, occlusionMode,
opacityStateStore, opacityStateStore,
saveNeededStore,
textEditingState,
} from "./store"; } from "./store";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { drawEllipse, drawPolygon, drawRectangle, drawText } from "./tools/index"; import { drawEllipse, drawPolygon, drawRectangle, drawText } from "./tools/index";
@ -228,8 +230,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
disablePan(canvas); disablePan(canvas);
}; };
function changeOcclusionType(occlusionType: "all" | "one"): void { function changeOcclusionType(mode: OcclusionMode): void {
$hideAllGuessOne = occlusionType === "all"; $occlusionMode = mode;
saveNeededStore.set(true); saveNeededStore.set(true);
} }
@ -312,22 +314,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
{iconSize} {iconSize}
on:click={() => (showFloating = !showFloating)} on:click={() => (showFloating = !showFloating)}
> >
<Icon icon={$hideAllGuessOne ? mdiViewDashboard : mdiSquare} /> <Icon
icon={$occlusionMode === OcclusionMode.HideAll
? mdiViewDashboard
: $occlusionMode === OcclusionMode.HideAllButOne
? mdiCheckboxBlankOutline
: mdiSquare}
/>
</IconButton> </IconButton>
<Popover slot="floating"> <Popover slot="floating">
<DropdownItem <DropdownItem
active={$hideAllGuessOne} active={$occlusionMode === OcclusionMode.HideAll}
on:click={() => changeOcclusionType("all")} on:click={() => changeOcclusionType(OcclusionMode.HideAll)}
> >
<span>{tr.notetypesHideAllGuessOne()}</span> <span>{tr.notetypesHideAllGuessOne()}</span>
</DropdownItem> </DropdownItem>
<DropdownItem <DropdownItem
active={!$hideAllGuessOne} active={$occlusionMode === OcclusionMode.HideOne}
on:click={() => changeOcclusionType("one")} on:click={() => changeOcclusionType(OcclusionMode.HideOne)}
> >
<span>{tr.notetypesHideOneGuessOne()}</span> <span>{tr.notetypesHideOneGuessOne()}</span>
</DropdownItem> </DropdownItem>
<DropdownItem
active={$occlusionMode === OcclusionMode.HideAllButOne}
on:click={() => changeOcclusionType(OcclusionMode.HideAllButOne)}
>
<span>{tr.notetypesHideAllButOne()}</span>
</DropdownItem>
</Popover> </Popover>
</WithFloating> </WithFloating>

View file

@ -5,11 +5,11 @@ import { get } from "svelte/store";
import { addOrUpdateNote } from "../add-or-update-note.svelte"; import { addOrUpdateNote } from "../add-or-update-note.svelte";
import type { IOMode } from "../lib"; import type { IOMode } from "../lib";
import { hideAllGuessOne } from "../store"; import { occlusionMode } from "../store";
import type { PageLoad } from "./$types"; import type { PageLoad } from "./$types";
async function save(): Promise<void> { async function save(): Promise<void> {
addOrUpdateNote(globalThis["anki"].imageOcclusion.mode, get(hideAllGuessOne)); addOrUpdateNote(globalThis["anki"].imageOcclusion.mode, get(occlusionMode));
} }
export const load = (async ({ params }) => { export const load = (async ({ params }) => {

View file

@ -9,14 +9,14 @@ import { get } from "svelte/store";
import { mount } from "svelte"; import { mount } from "svelte";
import type { IOAddingMode, IOMode } from "./lib"; import type { IOAddingMode, IOMode } from "./lib";
import { exportShapesToClozeDeletions } from "./shapes/to-cloze"; import { exportShapesToClozeDeletions } from "./shapes/to-cloze";
import { notesDataStore, tagsWritable } from "./store"; import { notesDataStore, OcclusionMode, tagsWritable } from "./store";
import Toast from "./Toast.svelte"; import Toast from "./Toast.svelte";
export const addOrUpdateNote = async function( export const addOrUpdateNote = async function(
mode: IOMode, mode: IOMode,
occludeInactive: boolean, occlusionMode: OcclusionMode,
): Promise<void> { ): Promise<void> {
const { clozes: occlusionCloze, noteCount } = exportShapesToClozeDeletions(occludeInactive); const { clozes: occlusionCloze, noteCount } = exportShapesToClozeDeletions(occlusionMode);
if (noteCount === 0) { if (noteCount === 0) {
return; return;
} }

View file

@ -10,7 +10,7 @@ import { get } from "svelte/store";
import { addOrUpdateNote } from "./add-or-update-note.svelte"; import { addOrUpdateNote } from "./add-or-update-note.svelte";
import ImageOcclusionPage from "./ImageOcclusionPage.svelte"; import ImageOcclusionPage from "./ImageOcclusionPage.svelte";
import type { IOMode } from "./lib"; import type { IOMode } from "./lib";
import { hideAllGuessOne } from "./store"; import { occlusionMode } from "./store";
globalThis.anki = globalThis.anki || {}; globalThis.anki = globalThis.anki || {};
@ -31,7 +31,7 @@ export async function setupImageOcclusion(mode: IOMode, target = document.body):
await i18n; await i18n;
async function addNote(): Promise<void> { async function addNote(): Promise<void> {
addOrUpdateNote(mode, get(hideAllGuessOne)); addOrUpdateNote(mode, get(occlusionMode));
} }
// for adding note from mobile devices // for adding note from mobile devices

View file

@ -9,8 +9,8 @@ import { get } from "svelte/store";
import { optimumCssSizeForCanvas } from "./canvas-scale"; import { optimumCssSizeForCanvas } from "./canvas-scale";
import { import {
hideAllGuessOne,
notesDataStore, notesDataStore,
occlusionMode,
opacityStateStore, opacityStateStore,
saveNeededStore, saveNeededStore,
tagsWritable, tagsWritable,
@ -75,7 +75,7 @@ export const setupMaskEditorForEdit = async (
const clozeNote = clozeNoteResponse.value.value; const clozeNote = clozeNoteResponse.value.value;
const canvas = initCanvas(); const canvas = initCanvas();
hideAllGuessOne.set(clozeNote.occludeInactive); occlusionMode.set(clozeNote.occlusionMode);
// get image width and height // get image width and height
const image = document.getElementById("image") as HTMLImageElement; const image = document.getElementById("image") as HTMLImageElement;

View file

@ -7,6 +7,7 @@ import { ModuleName, setupI18n } from "@tslib/i18n";
import { optimumPixelSizeForCanvas } from "./canvas-scale"; import { optimumPixelSizeForCanvas } from "./canvas-scale";
import { Shape } from "./shapes"; import { Shape } from "./shapes";
import { Ellipse, extractShapesFromRenderedClozes, Polygon, Rectangle, Text } from "./shapes"; import { Ellipse, extractShapesFromRenderedClozes, Polygon, Rectangle, Text } from "./shapes";
import { OcclusionMode } from "./store";
import { SHAPE_MASK_COLOR, TEXT_BACKGROUND_COLOR, TEXT_FONT_FAMILY, TEXT_PADDING } from "./tools/lib"; import { SHAPE_MASK_COLOR, TEXT_BACKGROUND_COLOR, TEXT_FONT_FAMILY, TEXT_PADDING } from "./tools/lib";
import type { Size } from "./types"; import type { Size } from "./types";
@ -168,10 +169,18 @@ async function setupImageOcclusionInner(setupOptions?: SetupImageOcclusionOption
// setup button for toggle image occlusion // setup button for toggle image occlusion
const button = document.getElementById("toggle"); const button = document.getElementById("toggle");
if (button) { if (button) {
if (document.querySelector("[data-occludeinactive=\"1\"]")) { const hasHideAllMode = document.querySelector(`[data-occludeinactive="${OcclusionMode.HideAll}"]`);
button.addEventListener("click", () => toggleMasks(setupOptions)); const hasHideAllButOneMode = document.querySelector(`[data-occludeinactive="${OcclusionMode.HideAllButOne}"]`);
} else { const hasAnyToggleableMode = hasHideAllMode || hasHideAllButOneMode;
const isBackSide = document.querySelectorAll(".cloze-highlight").length > 0;
// Hide button if:
// 1. No shapes with Hide All or Hide All But One modes, OR
// 2. Hide All But One mode on the back side (no shapes to toggle)
if (!hasAnyToggleableMode || (hasHideAllButOneMode && isBackSide)) {
button.style.display = "none"; button.style.display = "none";
} else {
button.addEventListener("click", () => toggleMasks(setupOptions));
} }
} }
@ -202,35 +211,55 @@ function drawShapes(
properties = processed.properties; properties = processed.properties;
} }
for (const shape of activeShapes) { // Determine occlusion mode from the first shape
drawShape({ const occlusionMode = activeShapes[0]?.occlusionMode ?? inactiveShapes[0]?.occlusionMode ?? OcclusionMode.HideOne;
context,
size, // HideOne: Draw active only (front), reveal answer with highlight (back)
shape, // HideAll: Draw both active and inactive (front & back)
fill: properties.activeShapeColor, // HideAllButOne: Draw inactive only (front), draw nothing (back)
stroke: properties.activeBorder.color,
strokeWidth: properties.activeBorder.width, // Check if we're on the back side (highlightShapes only exist on back)
}); const isBackSide = highlightShapes.length > 0;
}
for (const shape of inactiveShapes.filter((s) => s.occludeInactive)) { // For HideAllButOne on the back side, draw nothing (show full unoccluded image)
drawShape({ if (occlusionMode === OcclusionMode.HideAllButOne && isBackSide) {
context, // Don't draw any shapes on the back for "Hide All But One" mode
size, } else {
shape, // Normal drawing logic for all other cases
fill: shape.fill !== SHAPE_MASK_COLOR ? shape.fill : properties.inActiveShapeColor, if (occlusionMode !== OcclusionMode.HideAllButOne) {
stroke: properties.inActiveBorder.color, for (const shape of activeShapes) {
strokeWidth: properties.inActiveBorder.width, drawShape({
}); context,
} size,
for (const shape of highlightShapes) { shape,
drawShape({ fill: properties.activeShapeColor,
context, stroke: properties.activeBorder.color,
size, strokeWidth: properties.activeBorder.width,
shape, });
fill: properties.highlightShapeColor, }
stroke: properties.highlightShapeBorder.color, }
strokeWidth: properties.highlightShapeBorder.width, if (occlusionMode === OcclusionMode.HideAll || occlusionMode === OcclusionMode.HideAllButOne) {
}); for (const shape of inactiveShapes) {
drawShape({
context,
size,
shape,
fill: shape.fill !== SHAPE_MASK_COLOR ? shape.fill : properties.inActiveShapeColor,
stroke: properties.inActiveBorder.color,
strokeWidth: properties.inActiveBorder.width,
});
}
}
for (const shape of highlightShapes) {
drawShape({
context,
size,
shape,
fill: properties.highlightShapeColor,
stroke: properties.highlightShapeBorder.color,
strokeWidth: properties.highlightShapeBorder.width,
});
}
} }
onDidDrawShapes?.({ onDidDrawShapes?.({

View file

@ -20,23 +20,23 @@ export class Shape {
top: number; top: number;
angle?: number; // polygons don't use it angle?: number; // polygons don't use it
fill: string; fill: string;
/** Whether occlusions from other cloze numbers should be shown on the /** Occlusion mode: 0=HideOne, 1=HideAll, 2=HideAllButOne.
* question side. Used only in reviewer code. * Used only in reviewer code.
*/ */
occludeInactive?: boolean; occlusionMode?: number;
/* Cloze ordinal */ /* Cloze ordinal */
ordinal: number | undefined; ordinal: number | undefined;
id: string | undefined; id: string | undefined;
constructor( constructor(
{ left = 0, top = 0, angle = 0, fill = SHAPE_MASK_COLOR, occludeInactive, ordinal = undefined }: { left = 0, top = 0, angle = 0, fill = SHAPE_MASK_COLOR, occlusionMode, ordinal = undefined }:
ConstructorParams<Shape> = {}, ConstructorParams<Shape> = {},
) { ) {
this.left = left; this.left = left;
this.top = top; this.top = top;
this.angle = angle; this.angle = angle;
this.fill = fill; this.fill = fill;
this.occludeInactive = occludeInactive; this.occlusionMode = occlusionMode;
this.ordinal = ordinal; this.ordinal = ordinal;
} }

View file

@ -64,7 +64,7 @@ function extractShapeFromRenderedCloze(cloze: HTMLDivElement): Shape | null {
return null; return null;
} }
const props = { const props = {
occludeInactive: cloze.dataset.occludeinactive === "1", occlusionMode: cloze.dataset.occludeinactive ? parseInt(cloze.dataset.occludeinactive) : undefined,
ordinal: parseInt(cloze.dataset.ordinal!), ordinal: parseInt(cloze.dataset.ordinal!),
left: cloze.dataset.left, left: cloze.dataset.left,
top: cloze.dataset.top, top: cloze.dataset.top,

View file

@ -4,6 +4,7 @@
import { fabric } from "fabric"; import { fabric } from "fabric";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
import { OcclusionMode } from "../store";
import { getBoundingBoxSize } from "../tools/lib"; import { getBoundingBoxSize } from "../tools/lib";
import type { Size } from "../types"; import type { Size } from "../types";
import type { Shape, ShapeOrShapes } from "./base"; import type { Shape, ShapeOrShapes } from "./base";
@ -12,7 +13,7 @@ import { Polygon } from "./polygon";
import { Rectangle } from "./rectangle"; import { Rectangle } from "./rectangle";
import { Text } from "./text"; import { Text } from "./text";
export function exportShapesToClozeDeletions(occludeInactive: boolean): { export function exportShapesToClozeDeletions(mode: OcclusionMode): {
clozes: string; clozes: string;
noteCount: number; noteCount: number;
} { } {
@ -76,7 +77,7 @@ export function exportShapesToClozeDeletions(occludeInactive: boolean): {
clozes += shapeOrShapesToCloze( clozes += shapeOrShapesToCloze(
shapeOrShapes, shapeOrShapes,
ordinal, ordinal,
occludeInactive, mode,
); );
if (!(shapeOrShapes instanceof Text)) { if (!(shapeOrShapes instanceof Text)) {
@ -179,7 +180,7 @@ function fabricObjectToBaseShapeOrShapes(
function shapeOrShapesToCloze( function shapeOrShapesToCloze(
shapeOrShapes: ShapeOrShapes, shapeOrShapes: ShapeOrShapes,
ordinal: number, ordinal: number,
occludeInactive: boolean, mode: OcclusionMode,
): string { ): string {
let text = ""; let text = "";
function addKeyValue(key: string, value: string) { function addKeyValue(key: string, value: string) {
@ -190,7 +191,7 @@ function shapeOrShapesToCloze(
let type: string; let type: string;
if (Array.isArray(shapeOrShapes)) { if (Array.isArray(shapeOrShapes)) {
return shapeOrShapes return shapeOrShapes
.map((shape) => shapeOrShapesToCloze(shape, ordinal, occludeInactive)) .map((shape) => shapeOrShapesToCloze(shape, ordinal, mode))
.join(""); .join("");
} else if (shapeOrShapes instanceof Rectangle) { } else if (shapeOrShapes instanceof Rectangle) {
type = "rect"; type = "rect";
@ -207,8 +208,8 @@ function shapeOrShapesToCloze(
for (const [key, value] of Object.entries(shapeOrShapes.toDataForCloze())) { for (const [key, value] of Object.entries(shapeOrShapes.toDataForCloze())) {
addKeyValue(key, value); addKeyValue(key, value);
} }
if (occludeInactive) { if (mode !== OcclusionMode.HideOne) {
addKeyValue("oi", "1"); addKeyValue("oi", mode.toString());
} }
text = `{{c${ordinal}::image-occlusion:${type}${text}}}<br>`; text = `{{c${ordinal}::image-occlusion:${type}${text}}}<br>`;

View file

@ -3,14 +3,20 @@
import { writable } from "svelte/store"; import { writable } from "svelte/store";
export enum OcclusionMode {
HideOne = 0,
HideAll = 1,
HideAllButOne = 2,
}
// it stores note's data for generate.ts, when function generate() is called it will be used to generate the note // it stores note's data for generate.ts, when function generate() is called it will be used to generate the note
export const notesDataStore = writable({ id: "", title: "", divValue: "", textareaValue: "" }[0]); export const notesDataStore = writable({ id: "", title: "", divValue: "", textareaValue: "" }[0]);
// it stores the tags for the note in note editor // it stores the tags for the note in note editor
export const tagsWritable = writable([""]); export const tagsWritable = writable([""]);
// it stores the visibility of mask editor // it stores the visibility of mask editor
export const ioMaskEditorVisible = writable(true); export const ioMaskEditorVisible = writable(true);
// it store hide all or hide one mode // it stores the occlusion mode (hide one, hide all, or hide all reveal one)
export const hideAllGuessOne = writable(true); export const occlusionMode = writable(OcclusionMode.HideAll);
// ioImageLoadedStore is used to store the image loaded event // ioImageLoadedStore is used to store the image loaded event
export const ioImageLoadedStore = writable(false); export const ioImageLoadedStore = writable(false);
// store opacity state of objects in canvas // store opacity state of objects in canvas