From 017f77ea6f051c8cca314daf2b66a6f20381b200 Mon Sep 17 00:00:00 2001 From: charlesLoder Date: Thu, 11 Jun 2026 23:57:23 -0400 Subject: [PATCH 1/5] update to use latest schema options --- package-lock.json | 16 +- package.json | 2 +- src/components/remove/Settings.svelte | 101 ++++- src/components/remove/index.svelte | 8 +- .../transliterate/KetivQereEditor.svelte | 96 +++++ .../transliterate/SettingsDialog.svelte | 354 ++++++++++++++++++ src/lib/schemaSerialization.ts | 12 +- 7 files changed, 564 insertions(+), 25 deletions(-) create mode 100644 src/components/transliterate/KetivQereEditor.svelte diff --git a/package-lock.json b/package-lock.json index b3af3fd..11ad101 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "astro": "^6.0.4", "google-auth-library": "^10.6.2", "google-spreadsheet": "^5.2.0", - "hebrew-transliteration": "^2.9.1", + "hebrew-transliteration": "^2.10.0", "svelte-codemirror-editor": "^2.1.0", "tailwindcss": "^4.2.1", "typescript": "^5.9.3" @@ -9087,21 +9087,21 @@ } }, "node_modules/havarotjs": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/havarotjs/-/havarotjs-0.25.2.tgz", - "integrity": "sha512-eh0fZVxJ03Gbropua/v0mqNxHtudU/IKgxlbT71Cfq96FMhLP4UaIRchrVlkfR4e6TuStUPnK0jkbgYB2psakQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/havarotjs/-/havarotjs-0.25.4.tgz", + "integrity": "sha512-DKXQ3GGfNVQxkW2ZY8bYDbTh0GuOX3/hotRiVQCDTksY1k/M283oQjfgBgXg0vO4HvCLl2WDW7EfLWaKUH7S4Q==", "license": "MIT", "engines": { "node": ">=15.0.0" } }, "node_modules/hebrew-transliteration": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/hebrew-transliteration/-/hebrew-transliteration-2.9.1.tgz", - "integrity": "sha512-jb6QlkUoBrTDR9K8gpWmJGfFoqGmi2MMARI+JqnTHEwGBYJBMQ2779yi5NQF+mhD+1Lm4Wabc198c+t5zqNwNA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/hebrew-transliteration/-/hebrew-transliteration-2.10.0.tgz", + "integrity": "sha512-lNdGt1Y9JyulLZCXGC7xEO9vit6XGmkA78UHxI5yIeq0IOPqxH9zYiZM+VkMZ7Ikez5rySGW1CfIb5ZBx3HO0A==", "license": "MIT", "dependencies": { - "havarotjs": "0.25.2" + "havarotjs": "0.25.4" } }, "node_modules/hosted-git-info": { diff --git a/package.json b/package.json index 5cd8b3f..bfa56d6 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "astro": "^6.0.4", "google-auth-library": "^10.6.2", "google-spreadsheet": "^5.2.0", - "hebrew-transliteration": "^2.9.1", + "hebrew-transliteration": "^2.10.0", "svelte-codemirror-editor": "^2.1.0", "tailwindcss": "^4.2.1", "typescript": "^5.9.3" diff --git a/src/components/remove/Settings.svelte b/src/components/remove/Settings.svelte index c9f23b7..6320f13 100644 --- a/src/components/remove/Settings.svelte +++ b/src/components/remove/Settings.svelte @@ -9,6 +9,10 @@ import type { RemoveOptions } from "hebrew-transliteration"; import { getContext } from "svelte"; import type { Context, RemoveState } from "../../types/index"; + import { Switch } from "$lib/components/ui/switch/index.js"; + import { javascript } from "@codemirror/lang-javascript"; + import { EditorView } from "@codemirror/view"; + import CodeMirror from "svelte-codemirror-editor"; import { format_doc_url } from "../../utils/documentation"; import Dialog from "../shared/Dialog.svelte"; @@ -81,15 +85,15 @@ let active_tab = $state("punctuation"); - function handle_option_change(key: keyof RemoveOptions, checked: boolean | "indeterminate") { + function handle_option_change(key: string, checked: boolean | "indeterminate") { remove_state.value.options = { ...remove_state.value.options, [key]: checked === true, }; } - function is_option_checked(key: keyof RemoveOptions): boolean { - return remove_state.value.options[key as keyof RemoveOptions] === true; + function is_option_checked(key: string): boolean { + return (remove_state.value.options as Record)[key] === true; } function handle_dialog_change(open: boolean) { @@ -100,6 +104,65 @@ return format_doc_url("/api/interfaces/removeoptions", key); } + let on_complete_enabled = $state(false); + let on_complete_code = $state(""); + + function load_on_complete() { + const oc = remove_state.value.options.ON_COMPLETE; + if (typeof oc === "function") { + on_complete_enabled = true; + on_complete_code = oc.toString(); + } else { + on_complete_enabled = false; + on_complete_code = ""; + } + } + + function handle_on_complete_toggle(enabled: boolean) { + on_complete_enabled = enabled; + if (!enabled) { + remove_state.value.options = { ...remove_state.value.options, ON_COMPLETE: undefined }; + } else { + const defaultFn = `(result, context) => {\n return result;\n}`; + on_complete_code = defaultFn; + try { + const fn = eval(defaultFn); + remove_state.value.options = { ...remove_state.value.options, ON_COMPLETE: fn }; + } catch { + // ignore parse errors while editing + } + } + } + + $effect(() => { + load_on_complete(); + }); + + $effect(() => { + const code = on_complete_code; + if (on_complete_enabled && code) { + const opt_fn = remove_state.value.options.ON_COMPLETE; + if (typeof opt_fn === "function" && opt_fn.toString() === code) return; + try { + remove_state.value.options = { ...remove_state.value.options, ON_COMPLETE: eval(code) }; + } catch { + // ignore parse errors while editing + } + } + }); + + const on_complete_extensions = [ + javascript(), + EditorView.theme({ + ".cm-gutters": { + backgroundColor: "var(--muted)", + color: "var(--muted-foreground)", + border: "none", + }, + ".cm-gutterElement": { backgroundColor: "var(--muted)", color: "var(--muted-foreground)" }, + }), + ]; + function toggle_all(category: "punctuation" | "taamim" | "vowels") { const items = category === "punctuation" ? punctuation : category === "taamim" ? taamim : vowels; @@ -108,7 +171,7 @@ const new_options = { ...remove_state.value.options }; for (const item of items) { - new_options[item.key as keyof RemoveOptions] = !allChecked; + (new_options as Record)[item.key] = !allChecked; } remove_state.value.options = new_options; } @@ -206,4 +269,34 @@ + +
+ +

+ A callback invoked when removal is complete. Receives + (result, context) where context contains original and + options. +

+
+ + +
+ {#if on_complete_enabled} +
+ +
+ {/if} +
diff --git a/src/components/remove/index.svelte b/src/components/remove/index.svelte index 72f7edd..d42973a 100644 --- a/src/components/remove/index.svelte +++ b/src/components/remove/index.svelte @@ -5,7 +5,9 @@ import { track_removal } from "../../lib/analytics"; import { load_settings, save_settings } from "../../lib/storage"; import { STORAGE_KEYS } from "../../lib/storageKeys"; + import { deserialize_object, serialize_object } from "../../lib/schemaSerialization"; import { performRemoval } from "../../services/removalService"; + import type { RemoveOptions } from "hebrew-transliteration"; import type { AppStatus, Context, RemoveState } from "../../types/index"; import { default_remove_options } from "../../utils/defaults"; import Card from "../shared/Card.svelte"; @@ -13,7 +15,9 @@ import Output from "../shared/Output.svelte"; import Settings from "./Settings.svelte"; - const saved_options = load_settings(STORAGE_KEYS.remove, default_remove_options); + const saved_options = deserialize_object( + load_settings(STORAGE_KEYS.remove, default_remove_options), + ) as RemoveOptions; let remove_state: RemoveState = $state({ dialog_view_state: "close", @@ -24,7 +28,7 @@ }); $effect(() => { - save_settings(STORAGE_KEYS.remove, remove_state.options); + save_settings(STORAGE_KEYS.remove, serialize_object(remove_state.options)); }); setContext>("remove_state", { diff --git a/src/components/transliterate/KetivQereEditor.svelte b/src/components/transliterate/KetivQereEditor.svelte new file mode 100644 index 0000000..3311ecd --- /dev/null +++ b/src/components/transliterate/KetivQereEditor.svelte @@ -0,0 +1,96 @@ + + + { + if (!open) on_cancel(); + }} +> + + + Edit Ketiv/Qere Output Callback + + Edit the callback function for this ketiv/qere pair. Receives (text, input). + + + +
+
+ +
+ +
+

+ Use schema["KEY"] to access schema transliteration values. +

+
+
+ + + + + +
+
diff --git a/src/components/transliterate/SettingsDialog.svelte b/src/components/transliterate/SettingsDialog.svelte index 69c42ba..677828f 100644 --- a/src/components/transliterate/SettingsDialog.svelte +++ b/src/components/transliterate/SettingsDialog.svelte @@ -18,9 +18,12 @@ import { ScrollArea } from "$lib/components/ui/scroll-area/index.js"; import { Switch } from "$lib/components/ui/switch/index.js"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "$lib/components/ui/tabs/index.js"; + import { javascript } from "@codemirror/lang-javascript"; + import { EditorView } from "@codemirror/view"; import IconInfoCircle from "@tabler/icons-svelte/icons/info-circle"; import type { SBL } from "hebrew-transliteration"; import { getContext } from "svelte"; + import CodeMirror from "svelte-codemirror-editor"; import { toast } from "svelte-sonner"; import { track_schema_change } from "../../lib/analytics"; import { @@ -34,6 +37,7 @@ import { get_default_SBL_schema } from "../../utils/schemaDefaults"; import Dialog from "../shared/Dialog.svelte"; import FeatureEditor from "./FeatureEditor.svelte"; + import KetivQereEditor from "./KetivQereEditor.svelte"; const transliteration_state = getContext>("transliteration_state"); @@ -82,6 +86,168 @@ let editing_index = $state(null); let feature_editor_visibility: VisibilityState = $state("hidden"); + // Ketiv/Qere state + interface KetivQerePair { + input: string; + output: string; + is_callback: boolean; + ignoreTaamim: boolean; + captureTaamim: boolean; + } + + let ketiv_qere_pairs = $state([]); + let kq_editor_index = $state(null); + let kq_editor_code = $state(""); + + function load_ketiv_qere_pairs() { + const kq = transliteration_state.value.schema.ketivQeres; + if (kq && Array.isArray(kq)) { + ketiv_qere_pairs = kq.map((item) => ({ + input: + typeof item.input === "string" + ? item.input + : item.input instanceof RegExp + ? `/${item.input.source}/${item.input.flags}` + : "", + output: + typeof item.output === "string" + ? item.output + : typeof item.output === "function" + ? item.output.toString() + : "", + is_callback: typeof item.output === "function", + ignoreTaamim: item.ignoreTaamim ?? true, + captureTaamim: item.captureTaamim ?? false, + })); + } else { + ketiv_qere_pairs = []; + } + } + + function add_ketiv_qere_pair() { + mark_as_custom_if_needed(); + ketiv_qere_pairs = [ + ...ketiv_qere_pairs, + { input: "", output: "", is_callback: false, ignoreTaamim: true, captureTaamim: false }, + ]; + } + + function update_ketiv_qere_pair(index: number, field: string, value: string | boolean) { + mark_as_custom_if_needed(); + const updated = ketiv_qere_pairs.map((pair, i) => + i === index ? { ...pair, [field]: value } : pair, + ); + ketiv_qere_pairs = updated; + commit_ketiv_qere_pairs(updated); + } + + function remove_ketiv_qere_pair(index: number) { + mark_as_custom_if_needed(); + const updated = ketiv_qere_pairs.filter((_, i) => i !== index); + ketiv_qere_pairs = updated; + commit_ketiv_qere_pairs(updated); + } + + function open_kq_callback_editor(index: number) { + kq_editor_index = index; + kq_editor_code = ketiv_qere_pairs[index].output; + } + + function save_kq_callback_editor(code: string) { + const index = kq_editor_index; + if (index === null) return; + update_ketiv_qere_pair(index, "output", code); + kq_editor_index = null; + kq_editor_code = ""; + } + + function cancel_kq_callback_editor() { + kq_editor_index = null; + kq_editor_code = ""; + } + + function toggle_kq_output_type(index: number, is_callback: boolean) { + mark_as_custom_if_needed(); + if (is_callback) { + const defaultFn = `(text, input) => {\n return text.replace(input, "");\n}`; + const updated = ketiv_qere_pairs.map((p, i) => + i === index ? { ...p, is_callback: true, output: defaultFn } : p, + ); + ketiv_qere_pairs = updated; + commit_ketiv_qere_pairs(updated); + } else { + const updated = ketiv_qere_pairs.map((p, i) => + i === index ? { ...p, is_callback: false, output: "" } : p, + ); + ketiv_qere_pairs = updated; + commit_ketiv_qere_pairs(updated); + } + } + + function commit_ketiv_qere_pairs(pairs: KetivQerePair[]) { + const valid = pairs.filter((p) => p.input.trim()); + if (valid.length === 0) { + handle_update_schema("ketivQeres", undefined); + } else { + const ketivQeres = valid.map((p) => { + const input: string | RegExp = + p.input.startsWith("/") && p.input.lastIndexOf("/") > 0 + ? (() => { + try { + const m = p.input.match(/^\/(.*)\/([gimsu]*)$/); + return m ? new RegExp(m[1], m[2]) : p.input; + } catch { + return p.input; + } + })() + : p.input; + const output = p.is_callback + ? (() => { + try { + return eval(p.output); + } catch { + return p.output; + } + })() + : p.output || p.input; + return { input, output, ignoreTaamim: p.ignoreTaamim, captureTaamim: p.captureTaamim }; + }); + handle_update_schema("ketivQeres", ketivQeres); + } + } + + // ON_COMPLETE state + let on_complete_enabled = $state(false); + let on_complete_code = $state(""); + + function load_on_complete() { + const oc = transliteration_state.value.schema.ON_COMPLETE; + if (typeof oc === "function") { + on_complete_enabled = true; + on_complete_code = oc.toString(); + } else { + on_complete_enabled = false; + on_complete_code = ""; + } + } + + function handle_on_complete_toggle(enabled: boolean) { + mark_as_custom_if_needed(); + on_complete_enabled = enabled; + if (!enabled) { + handle_update_schema("ON_COMPLETE", undefined); + } else { + const defaultFn = `(result, context) => {\n return result;\n}`; + on_complete_code = defaultFn; + try { + const fn = eval(defaultFn); + handle_update_schema("ON_COMPLETE", fn); + } catch { + // ignore parse errors while editing + } + } + } + function load_additional_features() { additional_features = transliteration_state.value.schema.ADDITIONAL_FEATURES || []; } @@ -662,7 +828,35 @@ $effect(() => { load_additional_features(); + load_ketiv_qere_pairs(); + load_on_complete(); + }); + + $effect(() => { + const code = on_complete_code; + if (on_complete_enabled && code) { + const schema_fn = transliteration_state.value.schema.ON_COMPLETE; + if (typeof schema_fn === "function" && schema_fn.toString() === code) return; + mark_as_custom_if_needed(); + try { + handle_update_schema("ON_COMPLETE", eval(code)); + } catch { + // ignore parse errors while editing + } + } }); + + const on_complete_extensions = [ + javascript(), + EditorView.theme({ + ".cm-gutters": { + backgroundColor: "var(--muted)", + color: "var(--muted-foreground)", + border: "none", + }, + ".cm-gutterElement": { backgroundColor: "var(--muted)", color: "var(--muted-foreground)" }, + }), + ]; @@ -790,6 +984,128 @@ {/each} + +
+ +

+ Define pairs to replace specific Hebrew text patterns (e.g. ketiv/qere, irregular + spellings). Each pair replaces the input text with the output text prior to + syllabification. +

+ {#each ketiv_qere_pairs as pair, index (index)} +
+
+ + + update_ketiv_qere_pair( + index, + "input", + (e.currentTarget as HTMLInputElement).value, + )} + /> +
+
+ + + toggle_kq_output_type(index, value === "callback")} + > +
+
+ + +
+
+ + +
+
+
+ {#if pair.is_callback} +
+ (callback) + +
+ {:else} + + update_ketiv_qere_pair( + index, + "output", + (e.currentTarget as HTMLInputElement).value, + )} + /> + {/if} +
+
+
+ + update_ketiv_qere_pair( + index, + "ignoreTaamim", + (e.currentTarget as HTMLInputElement).checked, + )} + class="w-3 h-3" + /> + +
+
+ + update_ketiv_qere_pair( + index, + "captureTaamim", + (e.currentTarget as HTMLInputElement).checked, + )} + class="w-3 h-3" + /> + +
+
+ +
+ {/each} + +
+ + {#if kq_editor_index !== null} + + {/if} @@ -989,6 +1305,44 @@ + +
+ +

+ A callback invoked when transliteration is complete. Receives + (result, context) where context contains original, + schema, and text. +

+
+ + +
+ {#if on_complete_enabled} +
+ +
+ {/if} +
+ diff --git a/src/lib/schemaSerialization.ts b/src/lib/schemaSerialization.ts index 9b06ac4..129c39c 100644 --- a/src/lib/schemaSerialization.ts +++ b/src/lib/schemaSerialization.ts @@ -63,17 +63,9 @@ export function serialize_object(obj: unknown): unknown { } export function serialize_schema(schema: Partial): Partial { - if (!schema.ADDITIONAL_FEATURES) return schema; - return { - ...schema, - ADDITIONAL_FEATURES: serialize_object(schema.ADDITIONAL_FEATURES) as SBL["ADDITIONAL_FEATURES"], - }; + return serialize_object(schema) as Partial; } export function deserialize_schema(data: Partial): Partial { - if (!data.ADDITIONAL_FEATURES) return data; - return { - ...data, - ADDITIONAL_FEATURES: deserialize_object(data.ADDITIONAL_FEATURES) as SBL["ADDITIONAL_FEATURES"], - }; + return deserialize_object(data) as Partial; } From 70d463abba102866fd8d5d532d4b8cc73b83d51a Mon Sep 17 00:00:00 2001 From: charlesLoder Date: Thu, 11 Jun 2026 23:57:37 -0400 Subject: [PATCH 2/5] add skill for updating ht --- .../skills/update-hebrew-transliteration.md | 124 ++++++++++++++++++ 1 file changed, 124 insertions(+) create mode 100644 .agents/skills/update-hebrew-transliteration.md diff --git a/.agents/skills/update-hebrew-transliteration.md b/.agents/skills/update-hebrew-transliteration.md new file mode 100644 index 0000000..2e672b7 --- /dev/null +++ b/.agents/skills/update-hebrew-transliteration.md @@ -0,0 +1,124 @@ +# Update `hebrew-transliteration` Package + +Steps to update the `hebrew-transliteration` npm package and sync the application's UI with any new/changed schema options. + +## 1. Update the package version + +```bash +npx npm-check-updates hebrew-transliteration -u +npm install +``` + +This updates `package.json` and installs the new version. + +## 2. Extract updated preset schemas + +```bash +node scripts/extract-schemas.ts +``` + +This re-serializes all 9 preset schemas from the package into `public/schemas/` and updates `public/schemas/manifest.json` with the new version number. + +## 3. Identify changes between versions + +### 3a. Compare type definitions + +```bash +# Download old version for comparison +mkdir -p /tmp/ht-old +npm pack hebrew-transliteration@ --pack-destination /tmp/ht-old +mkdir -p /tmp/ht-old/pkg && tar -xzf /tmp/ht-old/hebrew-transliteration-.tgz -C /tmp/ht-old/pkg + +# Compare Schema types +diff /tmp/ht-old/pkg/package/dist/types/schema.d.ts node_modules/hebrew-transliteration/dist/schema.d.ts + +# Compare Remove types +diff /tmp/ht-old/pkg/package/dist/types/remove.d.ts node_modules/hebrew-transliteration/dist/remove.d.ts + +# Clean up +rm -rf /tmp/ht-old +``` + +### 3b. Check for new/removed preset schemas + +Compare `node_modules/hebrew-transliteration/dist/schemas/` against `public/schemas/manifest.json`: + +```bash +ls node_modules/hebrew-transliteration/dist/schemas/*.d.ts +``` + +### 3c. Check havarotjs changes (transitive dependency) + +The `SylOpts` interface in `node_modules/havarotjs/dist/types/text.d.ts` may have new properties that flow into the Schema type. + +```bash +# Download old havarotjs +npm pack havarotjs@ --pack-destination /tmp/ht-old +# Extract and compare text.d.ts +``` + +## 4. Map new/changed Schema properties to UI + +Check the `SettingsDialog.svelte` component for what's already present: + +**Consonants tab** (`src/components/transliterate/SettingsDialog.svelte`): + +- Array `consonants` (line ~125) — list of all consonant keys +- Compare against Schema type's consonant properties (ALEF through TAV_DAGESH) + +**Vowels tab** (same file): + +- Array `vowels` (line ~162) — list of all vowel keys +- Compare against Schema type's vowel properties (VOCAL_SHEVA through SHUREQ, plus FURTIVE_PATAH, QAMATS_QATAN, and \*HE ligatures) + +**Others tab** (same file): + +- Array `orthographies` (line ~188) — MS_SUFX, DIVINE_NAME, DIVINE_NAME_ELOHIM, SYLLABLE_SEPARATOR, DAGESH, MAQAF, PASEQ, SOF_PASUQ +- `DAGESH_CHAZAQ` — radio group (none/double/custom) +- `STRESS_MARKER` — switch + location/mark/exclude selects +- `ketivQeres` — add/remove pair list +- `ON_COMPLETE` — toggle + textarea + +**Syllabification tab** (same file): + +- Array `syllabification` (line ~199) — boolean/string dropdowns +- Compare against Schema's SylOpts properties + +### Remove Settings (`src/components/remove/Settings.svelte`): + +- Tabs: Punctuation, Taamim, Vowels — each with checkboxes +- `ON_COMPLETE` — toggle + textarea at bottom of dialog + +## 5. Serialization support + +When adding properties that involve functions or RegExps: + +- **`src/lib/schemaSerialization.ts`** — `serialize_schema` and `deserialize_schema` pass the full schema through `serialize_object`/`deserialize_object`, which handles: + - Functions → `{ __function: "..." }` + - RegExps → `{ __regexp: "...", flags: "..." }` +- If adding a new property type that needs special serialization, update `serialize_value`/`deserialize_value` +- For the **remove page**: `src/components/remove/index.svelte` wraps the options with `serialize_object`/`deserialize_object` for localStorage + +## 6. Verify + +```bash +npm run lint +npm run build +npm run test +``` + +## Property categories reference + +| Category | Where it lives | Example properties | +| --------------------- | --------------------------------- | --------------------------------------------------------------------------- | +| Consonants | Schema (required) | ALEF, BET, BET_DAGESH, ..., TAV_DAGESH | +| Vowels | Schema (required) | VOCAL_SHEVA, PATAH, QAMATS, HIRIQ_YOD, PATAH_HE, ... | +| Orthographic features | Schema (optional) | MS_SUFX, DIVINE_NAME, DIVINE_NAME_ELOHIM, SYLLABLE_SEPARATOR | +| Marks | Schema (optional) | DAGESH, MAQAF, PASEQ, SOF_PASUQ | +| Dagesh Chazaq | Schema (optional) | DAGESH_CHAZAQ: boolean \| string | +| Stress Marker | Schema (optional) | STRESS_MARKER: `{ location, mark, exclude? }` | +| Additional Features | Schema (optional) | ADDITIONAL_FEATURES: WordFeature[] \| SyllableFeature[] \| ClusterFeature[] | +| Ketiv/Qere | Schema (optional, SylOpts) | ketivQeres: KetivQere[] | +| ON_COMPLETE | Schema + RemoveOptions (optional) | ON_COMPLETE: `(result, context) => string` | +| Syllabification | Schema (optional, SylOpts) | allowNoNiqqud, article, holemHaser, ketivQeres, longVowels, ... | +| Remove options | RemoveOptions (optional) | ETNAHTA, SHEVA, MAQAF, PUNC_GERESH, ... | From 3f4a843f1c94a6e49f32d947804e17ff0ba77af5 Mon Sep 17 00:00:00 2001 From: charlesLoder Date: Sun, 21 Jun 2026 22:44:35 -0400 Subject: [PATCH 3/5] improve testing --- src/components/remove/Settings.svelte | 73 +-- src/components/remove/index.svelte | 4 +- src/components/shared/Dialog.svelte | 2 +- .../transliterate/SettingsDialog.svelte | 43 +- tests/remove.spec.ts | 49 ++ tests/settings-dialog.spec.ts | 330 ----------- tests/transliterate.spec.ts | 554 ++++++++++++++++++ 7 files changed, 665 insertions(+), 390 deletions(-) create mode 100644 tests/remove.spec.ts delete mode 100644 tests/settings-dialog.spec.ts create mode 100644 tests/transliterate.spec.ts diff --git a/src/components/remove/Settings.svelte b/src/components/remove/Settings.svelte index 6320f13..0ca134e 100644 --- a/src/components/remove/Settings.svelte +++ b/src/components/remove/Settings.svelte @@ -4,15 +4,15 @@ import { DialogDescription, DialogHeader, DialogTitle } from "$lib/components/ui/dialog/index.js"; import { Label } from "$lib/components/ui/label/index.js"; import { ScrollArea } from "$lib/components/ui/scroll-area/index.js"; + import { Switch } from "$lib/components/ui/switch/index.js"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "$lib/components/ui/tabs/index.js"; + import { javascript } from "@codemirror/lang-javascript"; + import { EditorView } from "@codemirror/view"; import IconInfoCircle from "@tabler/icons-svelte/icons/info-circle"; import type { RemoveOptions } from "hebrew-transliteration"; import { getContext } from "svelte"; - import type { Context, RemoveState } from "../../types/index"; - import { Switch } from "$lib/components/ui/switch/index.js"; - import { javascript } from "@codemirror/lang-javascript"; - import { EditorView } from "@codemirror/view"; import CodeMirror from "svelte-codemirror-editor"; + import type { Context, RemoveState } from "../../types/index"; import { format_doc_url } from "../../utils/documentation"; import Dialog from "../shared/Dialog.svelte"; @@ -190,6 +190,7 @@ Punctuation Taamim Vowels + On Complete @@ -268,35 +269,41 @@ - -
- -

- A callback invoked when removal is complete. Receives - (result, context) where context contains original and - options. -

-
- - -
- {#if on_complete_enabled} -
- + +
+ +

+ A callback invoked when removal is complete. Receives + (result, context) where context contains original and + options. +

+
+ + +
+ {#if on_complete_enabled} +
+ +
+ {/if}
- {/if} -
+ +
diff --git a/src/components/remove/index.svelte b/src/components/remove/index.svelte index d42973a..f507bee 100644 --- a/src/components/remove/index.svelte +++ b/src/components/remove/index.svelte @@ -1,13 +1,13 @@