mirror of
https://github.com/zadam/trilium.git
synced 2025-12-05 23:14:24 +01:00
Compare commits
44 Commits
96154b385f
...
e124a3ef28
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e124a3ef28 | ||
|
|
2fb78275f7 | ||
|
|
98f421c697 | ||
|
|
a5572b7d45 | ||
|
|
fdecbaaa6a | ||
|
|
c6afd7fa24 | ||
|
|
5cad522a60 | ||
|
|
82f64677cb | ||
|
|
3ee086a063 | ||
|
|
13da444a69 | ||
|
|
b51ceaaadc | ||
|
|
2024c72209 | ||
|
|
b5959c55e1 | ||
|
|
16f0ac97f4 | ||
|
|
073c02ee0c | ||
|
|
786f0db4bb | ||
|
|
6958e4b74f | ||
|
|
f8d84814e0 | ||
|
|
c46cf41842 | ||
|
|
64ab1c4116 | ||
|
|
a6de1041c7 | ||
|
|
c8d34e65ea | ||
|
|
51db729546 | ||
|
|
d2052ad236 | ||
|
|
9c4301467f | ||
|
|
e7355dc0e4 | ||
|
|
4110fec94f | ||
|
|
d5e601eae9 | ||
|
|
4f044c4a57 | ||
|
|
5821c350e1 | ||
|
|
edba8188fe | ||
|
|
1471a72633 | ||
|
|
56834cb88a | ||
|
|
a0f16f9184 | ||
|
|
de80eb4806 | ||
|
|
48a4b81fbe | ||
|
|
e225794f72 | ||
|
|
4eef30f8b5 | ||
|
|
569b09609d | ||
|
|
39838c25c2 | ||
|
|
49e90c08a9 | ||
|
|
e777b06fb8 | ||
|
|
497ec2ac74 | ||
|
|
c5d282d203 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -48,4 +48,4 @@ upload
|
|||||||
.svelte-kit
|
.svelte-kit
|
||||||
|
|
||||||
# docs
|
# docs
|
||||||
site/
|
site/
|
||||||
@ -58,6 +58,7 @@ function initOnElectron() {
|
|||||||
|
|
||||||
initDarkOrLightMode(style);
|
initDarkOrLightMode(style);
|
||||||
initTransparencyEffects(style, currentWindow);
|
initTransparencyEffects(style, currentWindow);
|
||||||
|
initFullScreenDetection(currentWindow);
|
||||||
|
|
||||||
if (options.get("nativeTitleBarVisible") !== "true") {
|
if (options.get("nativeTitleBarVisible") !== "true") {
|
||||||
initTitleBarButtons(style, currentWindow);
|
initTitleBarButtons(style, currentWindow);
|
||||||
@ -87,6 +88,11 @@ function initTitleBarButtons(style: CSSStyleDeclaration, currentWindow: Electron
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function initFullScreenDetection(currentWindow: Electron.BrowserWindow) {
|
||||||
|
currentWindow.on("enter-full-screen", () => document.body.classList.add("full-screen"));
|
||||||
|
currentWindow.on("leave-full-screen", () => document.body.classList.remove("full-screen"));
|
||||||
|
}
|
||||||
|
|
||||||
function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
|
function initTransparencyEffects(style: CSSStyleDeclaration, currentWindow: Electron.BrowserWindow) {
|
||||||
if (window.glob.platform === "win32") {
|
if (window.glob.platform === "win32") {
|
||||||
const material = style.getPropertyValue("--background-material");
|
const material = style.getPropertyValue("--background-material");
|
||||||
|
|||||||
@ -75,10 +75,7 @@ class ContextMenu {
|
|||||||
if (this.isMobile) {
|
if (this.isMobile) {
|
||||||
this.$cover.on("click", () => this.hide());
|
this.$cover.on("click", () => this.hide());
|
||||||
} else {
|
} else {
|
||||||
$(document).on("click", (e) => {
|
$(document).on("click", (e) => this.hide());
|
||||||
console.log("Hide due to clickus")
|
|
||||||
this.hide()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2009,7 +2009,7 @@ body.electron.platform-darwin:not(.native-titlebar) .tab-row-container {
|
|||||||
-webkit-app-region: drag;
|
-webkit-app-region: drag;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.electron.platform-darwin:not(.native-titlebar) #tab-row-left-spacer {
|
body.electron.platform-darwin:not(.native-titlebar):not(.full-screen) #tab-row-left-spacer {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1702,7 +1702,7 @@
|
|||||||
"paste": "粘贴",
|
"paste": "粘贴",
|
||||||
"paste-as-plain-text": "以纯文本粘贴",
|
"paste-as-plain-text": "以纯文本粘贴",
|
||||||
"search_online": "用 {{searchEngine}} 搜索 \"{{term}}\"",
|
"search_online": "用 {{searchEngine}} 搜索 \"{{term}}\"",
|
||||||
"search_in_trilium": "在 Trilium 中查找搜索 \"{{term}}\""
|
"search_in_trilium": "在 Trilium 中搜索「{{term}}」"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_reference_to_clipboard": "复制引用到剪贴板",
|
"copy_reference_to_clipboard": "复制引用到剪贴板",
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
"link_title_arbitrary": "titulek odkazu může být změněn libovolně"
|
"link_title_arbitrary": "titulek odkazu může být změněn libovolně"
|
||||||
},
|
},
|
||||||
"branch_prefix": {
|
"branch_prefix": {
|
||||||
"prefix": "Prefix: ",
|
"prefix": "Předpona: ",
|
||||||
"save": "Uložit",
|
"save": "Uložit",
|
||||||
"edit_branch_prefix": "Upravit prefix větve",
|
"edit_branch_prefix": "Upravit prefix větve",
|
||||||
"edit_branch_prefix_multiple": "Upravit prefix větve pro {{count}} větví",
|
"edit_branch_prefix_multiple": "Upravit prefix větve pro {{count}} větví",
|
||||||
@ -68,20 +68,54 @@
|
|||||||
},
|
},
|
||||||
"confirm": {
|
"confirm": {
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"ok": "OK"
|
"ok": "OK",
|
||||||
|
"confirmation": "Potvrzení",
|
||||||
|
"are_you_sure_remove_note": "Opravdu chcete odstranit poznámku „{{title}}“ z mapy vztahů?",
|
||||||
|
"if_you_dont_check": "Pokud tuto možnost nezaškrtnete, poznámka bude odstraněna pouze z mapy vztahů.",
|
||||||
|
"also_delete_note": "Odstraňte také poznámku"
|
||||||
},
|
},
|
||||||
"delete_notes": {
|
"delete_notes": {
|
||||||
"cancel": "Zrušit",
|
"cancel": "Zrušit",
|
||||||
"ok": "OK",
|
"ok": "OK",
|
||||||
"close": "Zavřít"
|
"close": "Zavřít",
|
||||||
|
"delete_notes_preview": "Odstranit náhled poznámek",
|
||||||
|
"delete_all_clones_description": "Odstraňte také všechny klony (lze vrátit zpět v nedávných změnách)",
|
||||||
|
"erase_notes_description": "Normální (měkké) smazání pouze označí poznámky jako smazané a lze je během určité doby obnovit (v dialogovém okně posledních změn). Zaškrtnutím této možnosti se poznámky okamžitě vymažou a nebude možné je obnovit.",
|
||||||
|
"erase_notes_warning": "Trvale smažte poznámky (nelze vrátit zpět), včetně všech klonů. Tím se vynutí opětovné načtení aplikace.",
|
||||||
|
"notes_to_be_deleted": "Následující poznámky budou smazány ({{notesCount}})",
|
||||||
|
"no_note_to_delete": "Žádná poznámka nebude smazána (pouze klony).",
|
||||||
|
"broken_relations_to_be_deleted": "Následující vazby budou přerušeny a smazány ({{relationCount}})",
|
||||||
|
"deleted_relation_text": "Poznámka {{- note}} (bude smazána) je odkazována vazbou {{- relation}} pocházející z {{- source}}."
|
||||||
},
|
},
|
||||||
"export": {
|
"export": {
|
||||||
"close": "Zavřít"
|
"close": "Zavřít",
|
||||||
|
"export_note_title": "Exportovat poznámku",
|
||||||
|
"export_type_subtree": "Tato poznámka a všechny její odvozené poznámky",
|
||||||
|
"format_html": "HTML – doporučeno, protože zachovává veškeré formátování",
|
||||||
|
"format_html_zip": "HTML v archivu ZIP – toto se doporučuje, protože se tak zachová veškeré formátování.",
|
||||||
|
"format_markdown": "Markdown – zachovává většinu formátování.",
|
||||||
|
"format_opml": "OPML – formát pro výměnu osnov pouze pro text. Formátování, obrázky a soubory nejsou zahrnuty.",
|
||||||
|
"opml_version_1": "OPML v1.0 – pouze prostý text",
|
||||||
|
"opml_version_2": "OPML v2.0 – umožňuje také HTML",
|
||||||
|
"export_type_single": "Pouze tato poznámka bez jejích potomků",
|
||||||
|
"export": "Exportovat",
|
||||||
|
"choose_export_type": "Nejprve vyberte typ exportu",
|
||||||
|
"export_status": "Stav exportu",
|
||||||
|
"export_in_progress": "Export probíhá: {{progressCount}}",
|
||||||
|
"export_finished_successfully": "Export byl úspěšně dokončen.",
|
||||||
|
"format_pdf": "PDF – pro tisk nebo sdílení.",
|
||||||
|
"share-format": "HTML pro publikování na webu – používá stejný motiv jako sdílené poznámky, ale lze jej publikovat jako statický web."
|
||||||
},
|
},
|
||||||
"clone_to": {
|
"clone_to": {
|
||||||
"clone_notes_to": "Klonovat poznámky do...",
|
"clone_notes_to": "Klonovat poznámky do...",
|
||||||
"help_on_links": "Nápověda k odkazům",
|
"help_on_links": "Nápověda k odkazům",
|
||||||
"notes_to_clone": "Poznámky na klonování",
|
"notes_to_clone": "Poznámky na klonování",
|
||||||
"search_for_note_by_its_name": "hledat poznámku dle jejího názvu"
|
"search_for_note_by_its_name": "hledat poznámku dle jejího názvu",
|
||||||
|
"prefix_optional": "Předpona (volitelná)",
|
||||||
|
"target_parent_note": "Zaměřit rodičovskou poznámku",
|
||||||
|
"cloned_note_prefix_title": "Klonovaná poznámka se zobrazí ve stromu poznámek s danou předponou",
|
||||||
|
"clone_to_selected_note": "Klonovat vybranou poznámku",
|
||||||
|
"no_path_to_clone_to": "Žádná cest pro klonování.",
|
||||||
|
"note_cloned": "Poznámka: „{{clonedTitle}}“ bylo naklonováno do „{{targetTitle}}“"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -769,7 +769,8 @@
|
|||||||
"geo-map": "Weltkarte",
|
"geo-map": "Weltkarte",
|
||||||
"board": "Tafel",
|
"board": "Tafel",
|
||||||
"include_archived_notes": "Zeige archivierte Notizen",
|
"include_archived_notes": "Zeige archivierte Notizen",
|
||||||
"presentation": "Präsentation"
|
"presentation": "Präsentation",
|
||||||
|
"expand_all_levels": "Alle Ebenen erweitern"
|
||||||
},
|
},
|
||||||
"edited_notes": {
|
"edited_notes": {
|
||||||
"no_edited_notes_found": "An diesem Tag wurden noch keine Notizen bearbeitet...",
|
"no_edited_notes_found": "An diesem Tag wurden noch keine Notizen bearbeitet...",
|
||||||
|
|||||||
@ -1727,7 +1727,8 @@
|
|||||||
"refresh-saved-search-results": "Refresh saved search results",
|
"refresh-saved-search-results": "Refresh saved search results",
|
||||||
"create-child-note": "Create child note",
|
"create-child-note": "Create child note",
|
||||||
"unhoist": "Unhoist",
|
"unhoist": "Unhoist",
|
||||||
"toggle-sidebar": "Toggle sidebar"
|
"toggle-sidebar": "Toggle sidebar",
|
||||||
|
"dropping-not-allowed": "Dropping notes into this location is not allowed."
|
||||||
},
|
},
|
||||||
"title_bar_buttons": {
|
"title_bar_buttons": {
|
||||||
"window-on-top": "Keep Window on Top"
|
"window-on-top": "Keep Window on Top"
|
||||||
@ -1830,7 +1831,8 @@
|
|||||||
"duplicate-launcher": "Duplicate launcher <kbd data-command=\"duplicateSubtree\">"
|
"duplicate-launcher": "Duplicate launcher <kbd data-command=\"duplicateSubtree\">"
|
||||||
},
|
},
|
||||||
"editable-text": {
|
"editable-text": {
|
||||||
"auto-detect-language": "Auto-detected"
|
"auto-detect-language": "Auto-detected",
|
||||||
|
"keeps-crashing": "Editing component keeps crashing. Please try restarting Trilium. If problem persists, consider creating a bug report."
|
||||||
},
|
},
|
||||||
"highlighting": {
|
"highlighting": {
|
||||||
"title": "Code Blocks",
|
"title": "Code Blocks",
|
||||||
|
|||||||
@ -541,7 +541,11 @@
|
|||||||
"geo-map": "ジオマップ",
|
"geo-map": "ジオマップ",
|
||||||
"board": "ボード",
|
"board": "ボード",
|
||||||
"include_archived_notes": "アーカイブされたノートを表示",
|
"include_archived_notes": "アーカイブされたノートを表示",
|
||||||
"presentation": "プレゼンテーション"
|
"presentation": "プレゼンテーション",
|
||||||
|
"expand_tooltip": "このコレクションの直下の子(1階層下)を展開します。その他のオプションについては、右側の矢印を押してください。",
|
||||||
|
"expand_first_level": "直下の子を展開",
|
||||||
|
"expand_nth_level": "{{depth}} 階層下まで展開",
|
||||||
|
"expand_all_levels": "すべての階層を展開"
|
||||||
},
|
},
|
||||||
"note_types": {
|
"note_types": {
|
||||||
"geo-map": "ジオマップ",
|
"geo-map": "ジオマップ",
|
||||||
|
|||||||
@ -1661,7 +1661,7 @@
|
|||||||
"paste": "貼上",
|
"paste": "貼上",
|
||||||
"paste-as-plain-text": "以純文字貼上",
|
"paste-as-plain-text": "以純文字貼上",
|
||||||
"search_online": "用 {{searchEngine}} 搜尋 \"{{term}}\"",
|
"search_online": "用 {{searchEngine}} 搜尋 \"{{term}}\"",
|
||||||
"search_in_trilium": "在 Trilium 中搜尋 \"{{term}}\""
|
"search_in_trilium": "在 Trilium 中搜尋「{{term}}」"
|
||||||
},
|
},
|
||||||
"image_context_menu": {
|
"image_context_menu": {
|
||||||
"copy_reference_to_clipboard": "複製引用到剪貼簿",
|
"copy_reference_to_clipboard": "複製引用到剪貼簿",
|
||||||
|
|||||||
@ -508,7 +508,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
(data.hitMode === "over" && node.data.noteType === "search") ||
|
(data.hitMode === "over" && node.data.noteType === "search") ||
|
||||||
(["after", "before"].includes(data.hitMode) && (node.data.noteId === hoistedNoteService.getHoistedNoteId() || node.getParent().data.noteType === "search"))
|
(["after", "before"].includes(data.hitMode) && (node.data.noteId === hoistedNoteService.getHoistedNoteId() || node.getParent().data.noteType === "search"))
|
||||||
) {
|
) {
|
||||||
await dialogService.info("Dropping notes into this location is not allowed.");
|
await dialogService.info(t("note_tree.dropping-not-allowed"));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import TouchBar, { TouchBarButton, TouchBarGroup, TouchBarSegmentedControl } fro
|
|||||||
import { RefObject } from "preact";
|
import { RefObject } from "preact";
|
||||||
import { buildSelectedBackgroundColor } from "../../../components/touch_bar";
|
import { buildSelectedBackgroundColor } from "../../../components/touch_bar";
|
||||||
import { deferred } from "@triliumnext/commons";
|
import { deferred } from "@triliumnext/commons";
|
||||||
|
import { t } from "../../../services/i18n";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The editor can operate into two distinct modes:
|
* The editor can operate into two distinct modes:
|
||||||
@ -279,7 +280,7 @@ function onWatchdogStateChange(watchdog: EditorWatchdog) {
|
|||||||
logError(`CKEditor crash logs: ${JSON.stringify(watchdog.crashes, null, 4)}`);
|
logError(`CKEditor crash logs: ${JSON.stringify(watchdog.crashes, null, 4)}`);
|
||||||
|
|
||||||
if (currentState === "crashedPermanently") {
|
if (currentState === "crashedPermanently") {
|
||||||
dialog.info(`Editing component keeps crashing. Please try restarting Trilium. If problem persists, consider creating a bug report.`);
|
dialog.info(t("editable-text.keeps-crashing"));
|
||||||
watchdog.editor?.enableReadOnlyMode("crashed-editor");
|
watchdog.editor?.enableReadOnlyMode("crashed-editor");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,7 +61,32 @@
|
|||||||
"run-active-note": "Spustit aktivní kód JavaScript (frontend/backend) poznámky",
|
"run-active-note": "Spustit aktivní kód JavaScript (frontend/backend) poznámky",
|
||||||
"open-dev-tools": "Otevřít vývojářské nástroje",
|
"open-dev-tools": "Otevřít vývojářské nástroje",
|
||||||
"zoom-out": "Oddálit",
|
"zoom-out": "Oddálit",
|
||||||
"zoom-in": "Přiblížit"
|
"zoom-in": "Přiblížit",
|
||||||
|
"text-note-operations": "Operace textových poznámek",
|
||||||
|
"show-cheatsheet": "Zobrazit modal s běžnými operacemi klávesnice",
|
||||||
|
"add-new-label": "Vytvořít nový štítek",
|
||||||
|
"insert-date-and-time-to-text": "Vložit do textu současné datum a čas",
|
||||||
|
"dialogs": "Dialogy",
|
||||||
|
"show-revisions": "Ukázat dialog „Revize poznámky“",
|
||||||
|
"add-link-to-text": "Otevřít dialog pro přidání odkazu do textu",
|
||||||
|
"follow-link-under-cursor": "Následujte odkaz, na kterém je umístěna kurzorová šipka",
|
||||||
|
"paste-markdown-into-text": "Vložit Markdown ze schránky do poznámky",
|
||||||
|
"cut-into-note": "Vyjmout výběr z aktuální poznámky a vytvořit podpoznámku s vybraným textem",
|
||||||
|
"add-include-note-to-text": "Otevřít dialog pro vložení poznámky",
|
||||||
|
"attributes-labels-and-relations": "Atributy (štítky a vazby)",
|
||||||
|
"create-new-relation": "Vytvořit novou vazbu",
|
||||||
|
"ribbon-tabs": "Karty pásu záložek",
|
||||||
|
"print-active-note": "Vytiskonout aktiivní poznámku",
|
||||||
|
"render-active-note": "Vykreslit (znovu vykreslit) aktivní poznámku",
|
||||||
|
"open-note-externally": "Otevřít poznámku jako soubor ve výchozí aplikaci",
|
||||||
|
"reload-frontend-app": "Znovu načíst frontend",
|
||||||
|
"unhoist": "Odpojit všude",
|
||||||
|
"note-navigation": "Navigace v poznámce",
|
||||||
|
"reset-zoom-level": "Resetovat úroveň přiblížení",
|
||||||
|
"copy-without-formatting": "Kopírovat vybraný text bez formátování",
|
||||||
|
"force-save-revision": "Vynutit vytvoření / uložení nové revize aktivní poznámky",
|
||||||
|
"export-as-pdf": "Exportovat současnou poznámku jako PDF",
|
||||||
|
"toggle-zen-mode": "Zapnout/vypnout režim zen (minimalistické uživatelské rozhraní pro soustředěnější úpravy)"
|
||||||
},
|
},
|
||||||
"keyboard_action_names": {
|
"keyboard_action_names": {
|
||||||
"jump-to-note": "Přejít na...",
|
"jump-to-note": "Přejít na...",
|
||||||
@ -73,6 +98,15 @@
|
|||||||
"edit-note-title": "Upravit nadpis poznámky",
|
"edit-note-title": "Upravit nadpis poznámky",
|
||||||
"clone-notes-to": "Klonovat poznámku do",
|
"clone-notes-to": "Klonovat poznámku do",
|
||||||
"move-notes-to": "Přemístit poznámku do",
|
"move-notes-to": "Přemístit poznámku do",
|
||||||
"copy-notes-to-clipboard": "Kopírovat poznámky do schránky"
|
"copy-notes-to-clipboard": "Kopírovat poznámky do schránky",
|
||||||
|
"back-in-note-history": "Zpět v historii poznámky",
|
||||||
|
"forward-in-note-history": "Vpřed v historii poznámky",
|
||||||
|
"command-palette": "Paleta příkazů",
|
||||||
|
"scroll-to-active-note": "Posunout na aktivní poznámku",
|
||||||
|
"search-in-subtree": "Hledat v podstromu",
|
||||||
|
"expand-subtree": "Otevřít podstrom",
|
||||||
|
"collapse-tree": "Zavřít strom",
|
||||||
|
"collapse-subtree": "Zavřít podstrom",
|
||||||
|
"sort-child-notes": "Seřadit dceřiné poznámky"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -257,7 +257,8 @@
|
|||||||
"ai-llm-title": "AI/LLM",
|
"ai-llm-title": "AI/LLM",
|
||||||
"localization": "Sprache & Region",
|
"localization": "Sprache & Region",
|
||||||
"inbox-title": "Posteingang",
|
"inbox-title": "Posteingang",
|
||||||
"zen-mode": "Zen-Modus"
|
"zen-mode": "Zen-Modus",
|
||||||
|
"command-palette": "Befehlspalette öffnen"
|
||||||
},
|
},
|
||||||
"notes": {
|
"notes": {
|
||||||
"new-note": "Neue Notiz",
|
"new-note": "Neue Notiz",
|
||||||
|
|||||||
@ -50,6 +50,90 @@
|
|||||||
"canvas_description": "Uspořádejte tvary, obrázky a text na nekonečném plátně pomocí stejné technologie, jaká se používá na webu excalidraw.com. Ideální pro diagramy, náčrtky a vizuální plánování.",
|
"canvas_description": "Uspořádejte tvary, obrázky a text na nekonečném plátně pomocí stejné technologie, jaká se používá na webu excalidraw.com. Ideální pro diagramy, náčrtky a vizuální plánování.",
|
||||||
"mermaid_title": "Mermaid diagramy",
|
"mermaid_title": "Mermaid diagramy",
|
||||||
"mermaid_description": "Vytvářejte diagramy, jako jsou vývojové diagramy, diagramy tříd a sekvencí, Ganttovy diagramy a mnoho dalších, pomocí syntaxe Mermaid.",
|
"mermaid_description": "Vytvářejte diagramy, jako jsou vývojové diagramy, diagramy tříd a sekvencí, Ganttovy diagramy a mnoho dalších, pomocí syntaxe Mermaid.",
|
||||||
"mindmap_title": "Myšlenková mapa"
|
"mindmap_title": "Myšlenková mapa",
|
||||||
|
"mindmap_description": "Zorganizujte si myšlenky vizuálně nebo uspořádejte brainstorming.",
|
||||||
|
"others_list": "a další: <0>mapa poznámek</0>, <1>mapa vztahů</1>, <2>uložená vyhledávání</2>, <3>zobrazení poznámky</3> a <4>webové zobrazení</4>."
|
||||||
|
},
|
||||||
|
"extensibility_benefits": {
|
||||||
|
"title": "Sdílení a rozšiřitelnost",
|
||||||
|
"import_export_title": "Import/export",
|
||||||
|
"import_export_description": "Snadná interakce s jinými aplikacemi pomocí formátů Markdown, ENEX a OML.",
|
||||||
|
"share_title": "Sdílet poznámky na webu",
|
||||||
|
"share_description": "Pokud máte server, můžete jej použít ke sdílení části svých poznámek s ostatními lidmi.",
|
||||||
|
"scripting_title": "Pokročilé skriptování",
|
||||||
|
"scripting_description": "Vytvořte si vlastní integrace v rámci Trilium pomocí přizpůsobených widgetů nebo logiky na straně serveru.",
|
||||||
|
"api_title": "REST API",
|
||||||
|
"api_description": "Komunikujte s Trilium programově pomocí jeho vestavěného REST API."
|
||||||
|
},
|
||||||
|
"collections": {
|
||||||
|
"title": "Kolekce",
|
||||||
|
"calendar_title": "Kalendář",
|
||||||
|
"calendar_description": "Organizujte své osobní nebo pracovní události pomocí kalendáře, který podporuje celodenní i vícedenní události. Zobrazte si své události na první pohled v týdenním, měsíčním a ročním přehledu. Snadná interakce pro přidávání nebo přetahování událostí.",
|
||||||
|
"table_title": "Tabulka",
|
||||||
|
"table_description": "Zobrazujte a upravujte informace o poznámkách v tabulkové struktuře s různými typy sloupců, jako jsou text, čísla, zaškrtávací políčka, datum a čas, odkazy a barvy, a podporou vztahů. Volitelně můžete poznámky zobrazit v hierarchické struktuře stromu uvnitř tabulky.",
|
||||||
|
"board_title": "Kanbanová tabule",
|
||||||
|
"board_description": "Uspořádejte si úkoly nebo stav projektu do tabule Kanban, kde můžete snadno vytvářet nové položky a sloupce a jednoduše měnit jejich stav přetahováním po tabuli.",
|
||||||
|
"geomap_title": "Geomapa",
|
||||||
|
"geomap_description": "Naplánujte si dovolenou nebo si označte místa, která vás zajímají, přímo na geografické mapě pomocí přizpůsobitelných značek. Zobrazte zaznamenané trasy GPX a sledujte itineráře.",
|
||||||
|
"presentation_title": "Prezentace",
|
||||||
|
"presentation_description": "Uspořádejte informace do snímků a prezentujte je na celé obrazovce s plynulými přechody. Snímky lze také exportovat do formátu PDF pro snadné sdílení."
|
||||||
|
},
|
||||||
|
"faq": {
|
||||||
|
"title": "Často kladené otázky",
|
||||||
|
"mobile_question": "Existuje mobilní aplikace?",
|
||||||
|
"mobile_answer": "V současné době neexistuje žádná oficiální mobilní aplikace. Pokud však máte instanci serveru, můžete k ní přistupovat pomocí webového prohlížeče a dokonce ji nainstalovat jako PWA. Pro Android existuje neoficiální aplikace s názvem TriliumDroid, která funguje i offline (stejně jako desktopový klient).",
|
||||||
|
"database_question": "Kde jsou má data uložena?",
|
||||||
|
"database_answer": "Všechny vaše poznámky budou uloženy v databázi SQLite ve složce aplikace. Důvodem, proč Trilium používá databázi namísto prostých textových souborů, je jak výkon, tak i skutečnost, že některé funkce by byly mnohem obtížnější implementovat, například klony (stejná poznámka na více místech ve stromu). Chcete-li najít složku aplikace, stačí přejít do okna O aplikaci.",
|
||||||
|
"server_question": "Potřebuju server pro používání Trilium?",
|
||||||
|
"server_answer": "Ne, server umožňuje přístup přes webový prohlížeč a spravuje synchronizaci, pokud máte více zařízení. Chcete-li začít, stačí si stáhnout desktopovou aplikaci a začít ji používat.",
|
||||||
|
"scaling_question": "Jak dobře se aplikace přizpůsobuje velkému množství poznámek?",
|
||||||
|
"scaling_answer": "V závislosti na použití by aplikace měla být schopna bez problémů zpracovat alespoň 100 000 poznámek. Upozorňujeme, že proces synchronizace může někdy selhat, pokud nahráváte mnoho velkých souborů (1 GB na soubor), protože Trilium je spíše aplikací pro správu znalostí než úložištěm souborů (jako například NextCloud).",
|
||||||
|
"network_share_question": "Mohu sdílet svou databázi přes síťový disk?",
|
||||||
|
"network_share_answer": "Ne, sdílení databáze SQLite přes síťový disk obecně není dobrý nápad. I když to někdy může fungovat, existuje riziko, že se databáze poškodí kvůli nedokonalému zamykání souborů v síti.",
|
||||||
|
"security_question": "Jak jsou má data chráněna?",
|
||||||
|
"security_answer": "Ve výchozím nastavení nejsou poznámky šifrovány a lze je číst přímo z databáze. Jakmile je poznámka označena jako šifrovaná, je zašifrována pomocí AES-128-CBC."
|
||||||
|
},
|
||||||
|
"final_cta": {
|
||||||
|
"title": "Jste připraveni začít používat Trilium Notes?",
|
||||||
|
"description": "Vytvořte si svou osobní znalostní bázi s výkonnými funkcemi a plným soukromím.",
|
||||||
|
"get_started": "Začít"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"link_learn_more": "Zjistit více..."
|
||||||
|
},
|
||||||
|
"download_now": {
|
||||||
|
"text": "Stáhnout nyní ",
|
||||||
|
"platform_big": "v{{version}} pro{{platform}}",
|
||||||
|
"platform_small": "pro {{platform}}",
|
||||||
|
"linux_big": "v{{version}} pro Linux",
|
||||||
|
"linux_small": "pro Linux",
|
||||||
|
"more_platforms": "Další platformy a nastavení serveru"
|
||||||
|
},
|
||||||
|
"header": {
|
||||||
|
"get-started": "Začít",
|
||||||
|
"documentation": "Dokumentace",
|
||||||
|
"support-us": "Podpořte nás"
|
||||||
|
},
|
||||||
|
"footer": {
|
||||||
|
"copyright_and_the": " a ",
|
||||||
|
"copyright_community": "komunita"
|
||||||
|
},
|
||||||
|
"social_buttons": {
|
||||||
|
"github": "GitHub",
|
||||||
|
"github_discussions": "GitHub diskuze",
|
||||||
|
"matrix": "Matrix",
|
||||||
|
"reddit": "Reddit"
|
||||||
|
},
|
||||||
|
"support_us": {
|
||||||
|
"title": "Podpořte nás",
|
||||||
|
"financial_donations_title": "Finanční dary",
|
||||||
|
"financial_donations_description": "Trilium je vyvíjeno a udržováno díky <Link>stovkám hodin práce</Link>. Vaše podpora zajišťuje, že zůstane open-source, vylepšuje jeho funkce a pokrývá náklady, jako je hosting.",
|
||||||
|
"financial_donations_cta": "Zvažte podporu hlavního vývojáře (<Link>eliandoran</Link>) aplikace prostřednictvím:",
|
||||||
|
"github_sponsors": "Sponzoři GitHubu",
|
||||||
|
"paypal": "PayPal",
|
||||||
|
"buy_me_a_coffee": "Buy Me A Coffee"
|
||||||
|
},
|
||||||
|
"contribute": {
|
||||||
|
"title": "Další způsoby, jak přispět"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -71,6 +71,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ckeditor/ckeditor5-icons": "47.2.0"
|
"@ckeditor/ckeditor5-icons": "47.2.0",
|
||||||
|
"mathlive": "0.108.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import ckeditor from './../theme/icons/math.svg?raw';
|
import ckeditor from './../theme/icons/math.svg?raw';
|
||||||
import './augmentation.js';
|
import './augmentation.js';
|
||||||
import "../theme/mathform.css";
|
import "../theme/mathform.css";
|
||||||
|
import 'mathlive';
|
||||||
|
import 'mathlive/fonts.css';
|
||||||
|
import 'mathlive/static.css';
|
||||||
|
|
||||||
export { default as Math } from './math.js';
|
export { default as Math } from './math.js';
|
||||||
export { default as MathUI } from './mathui.js';
|
export { default as MathUI } from './mathui.js';
|
||||||
|
|||||||
@ -56,7 +56,7 @@ export default class MathUI extends Plugin {
|
|||||||
this._balloon.showStack( 'main' );
|
this._balloon.showStack( 'main' );
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
this.formView?.mathInputView.fieldView.element?.focus();
|
this.formView?.mathLiveInputView.focus();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,31 +71,38 @@ export default class MathUI extends Plugin {
|
|||||||
throw new CKEditorError( 'math-command' );
|
throw new CKEditorError( 'math-command' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
const mathConfig = editor.config.get( 'math' )!;
|
const mathConfig = editor.config.get( 'math' )!;
|
||||||
|
|
||||||
const formView = new MainFormView(
|
const formView = new MainFormView(
|
||||||
editor.locale,
|
editor.locale,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
{
|
||||||
mathConfig.engine!,
|
engine: mathConfig.engine!,
|
||||||
mathConfig.lazyLoad,
|
lazyLoad: mathConfig.lazyLoad,
|
||||||
|
previewUid: this._previewUid,
|
||||||
|
previewClassName: mathConfig.previewClassName!,
|
||||||
|
katexRenderOptions: mathConfig.katexRenderOptions!
|
||||||
|
},
|
||||||
mathConfig.enablePreview,
|
mathConfig.enablePreview,
|
||||||
this._previewUid,
|
mathConfig.popupClassName!
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
mathConfig.previewClassName!,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
mathConfig.popupClassName!,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
mathConfig.katexRenderOptions!
|
|
||||||
);
|
);
|
||||||
|
|
||||||
formView.mathInputView.bind( 'value' ).to( mathCommand, 'value' );
|
formView.mathLiveInputView.bind( 'value' ).to( mathCommand, 'value' );
|
||||||
formView.displayButtonView.bind( 'isOn' ).to( mathCommand, 'display' );
|
formView.displayButtonView.bind( 'isOn' ).to( mathCommand, 'display' );
|
||||||
|
|
||||||
// Form elements should be read-only when corresponding commands are disabled.
|
// Form elements should be read-only when corresponding commands are disabled.
|
||||||
formView.mathInputView.bind( 'isReadOnly' ).to( mathCommand, 'isEnabled', value => !value );
|
formView.mathLiveInputView.bind( 'isReadOnly' ).to( mathCommand, 'isEnabled', value => !value );
|
||||||
formView.saveButtonView.bind( 'isEnabled' ).to( mathCommand );
|
formView.saveButtonView.bind( 'isEnabled' ).to(
|
||||||
formView.displayButtonView.bind( 'isEnabled' ).to( mathCommand );
|
mathCommand,
|
||||||
|
'isEnabled',
|
||||||
|
formView.mathLiveInputView,
|
||||||
|
'value',
|
||||||
|
( commandEnabled, equation ) => {
|
||||||
|
const normalizedEquation = ( equation ?? '' ).trim();
|
||||||
|
return commandEnabled && normalizedEquation.length > 0;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
formView.displayButtonView.bind( 'isEnabled' ).to( mathCommand, 'isEnabled' );
|
||||||
|
|
||||||
// Listen to submit button click
|
// Listen to submit button click
|
||||||
this.listenTo( formView, 'submit', () => {
|
this.listenTo( formView, 'submit', () => {
|
||||||
@ -122,18 +129,6 @@ export default class MathUI extends Plugin {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Allow the textarea to be resizable
|
|
||||||
formView.mathInputView.fieldView.once('render', () => {
|
|
||||||
const textarea = formView.mathInputView.fieldView.element;
|
|
||||||
if (!textarea) return;
|
|
||||||
Object.assign(textarea.style, {
|
|
||||||
resize: 'both',
|
|
||||||
height: '100px',
|
|
||||||
width: '400px',
|
|
||||||
minWidth: '100%',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return formView;
|
return formView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,14 +157,14 @@ export default class MathUI extends Plugin {
|
|||||||
} );
|
} );
|
||||||
|
|
||||||
if ( this._balloon.visibleView === this.formView ) {
|
if ( this._balloon.visibleView === this.formView ) {
|
||||||
this.formView.mathInputView.fieldView.element?.select();
|
this.formView.mathLiveInputView.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show preview element
|
// Show preview element
|
||||||
const previewEl = document.getElementById( this._previewUid );
|
const previewEl = document.getElementById( this._previewUid );
|
||||||
if ( previewEl && this.formView.previewEnabled ) {
|
if ( previewEl && this.formView.mathView ) {
|
||||||
// Force refresh preview
|
// Force refresh preview
|
||||||
this.formView.mathView?.updateMath();
|
this.formView.mathView.updateMath();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.formView.equation = mathCommand.value ?? '';
|
this.formView.equation = mathCommand.value ?? '';
|
||||||
|
|||||||
@ -1,270 +1,219 @@
|
|||||||
import { ButtonView, createLabeledTextarea, FocusCycler, LabelView, LabeledFieldView, submitHandler, SwitchButtonView, View, ViewCollection, type TextareaView, type FocusableView, Locale, FocusTracker, KeystrokeHandler } from 'ckeditor5';
|
import {
|
||||||
import IconCheck from "@ckeditor/ckeditor5-icons/theme/icons/check.svg?raw";
|
ButtonView,
|
||||||
import IconCancel from "@ckeditor/ckeditor5-icons/theme/icons/cancel.svg?raw";
|
FocusCycler,
|
||||||
|
LabelView,
|
||||||
|
submitHandler,
|
||||||
|
SwitchButtonView,
|
||||||
|
View,
|
||||||
|
ViewCollection,
|
||||||
|
type FocusableView,
|
||||||
|
Locale,
|
||||||
|
FocusTracker,
|
||||||
|
KeystrokeHandler
|
||||||
|
} from 'ckeditor5';
|
||||||
|
import IconCheck from '@ckeditor/ckeditor5-icons/theme/icons/check.svg?raw';
|
||||||
|
import IconCancel from '@ckeditor/ckeditor5-icons/theme/icons/cancel.svg?raw';
|
||||||
import { extractDelimiters, hasDelimiters } from '../utils.js';
|
import { extractDelimiters, hasDelimiters } from '../utils.js';
|
||||||
import MathView from './mathview.js';
|
import MathView, { type MathViewOptions } from './mathview.js';
|
||||||
|
import MathLiveInputView from './mathliveinputview.js';
|
||||||
|
import RawLatexInputView from './rawlatexinputview.js';
|
||||||
import '../../theme/mathform.css';
|
import '../../theme/mathform.css';
|
||||||
import type { KatexOptions } from '../typings-external.js';
|
|
||||||
|
|
||||||
class MathInputView extends LabeledFieldView<TextareaView> {
|
|
||||||
public value: null | string = null;
|
|
||||||
public isReadOnly = false;
|
|
||||||
|
|
||||||
constructor( locale: Locale ) {
|
|
||||||
super( locale, createLabeledTextarea );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class MainFormView extends View {
|
export default class MainFormView extends View {
|
||||||
public saveButtonView: ButtonView;
|
public saveButtonView: ButtonView;
|
||||||
public mathInputView: MathInputView;
|
|
||||||
public displayButtonView: SwitchButtonView;
|
|
||||||
public cancelButtonView: ButtonView;
|
public cancelButtonView: ButtonView;
|
||||||
public previewEnabled: boolean;
|
public displayButtonView: SwitchButtonView;
|
||||||
public previewLabel?: LabelView;
|
|
||||||
|
public mathLiveInputView: MathLiveInputView;
|
||||||
|
public rawLatexInputView: RawLatexInputView;
|
||||||
public mathView?: MathView;
|
public mathView?: MathView;
|
||||||
public override locale: Locale = new Locale();
|
|
||||||
public lazyLoad: undefined | ( () => Promise<void> );
|
public focusTracker = new FocusTracker();
|
||||||
|
public keystrokes = new KeystrokeHandler();
|
||||||
|
private _focusables = new ViewCollection<FocusableView>();
|
||||||
|
private _focusCycler: FocusCycler;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
locale: Locale,
|
locale: Locale,
|
||||||
engine:
|
mathViewOptions: MathViewOptions,
|
||||||
| 'mathjax'
|
|
||||||
| 'katex'
|
|
||||||
| ( (
|
|
||||||
equation: string,
|
|
||||||
element: HTMLElement,
|
|
||||||
display: boolean,
|
|
||||||
) => void ),
|
|
||||||
lazyLoad: undefined | ( () => Promise<void> ),
|
|
||||||
previewEnabled = false,
|
previewEnabled = false,
|
||||||
previewUid: string,
|
popupClassName: string[] = []
|
||||||
previewClassName: Array<string>,
|
|
||||||
popupClassName: Array<string>,
|
|
||||||
katexRenderOptions: KatexOptions
|
|
||||||
) {
|
) {
|
||||||
super( locale );
|
super( locale );
|
||||||
|
|
||||||
const t = locale.t;
|
const t = locale.t;
|
||||||
|
|
||||||
// Submit button
|
// --- 1. View Initialization ---
|
||||||
this.saveButtonView = this._createButton( t( 'Save' ), IconCheck, 'ck-button-save', null );
|
|
||||||
|
this.mathLiveInputView = new MathLiveInputView( locale );
|
||||||
|
this.rawLatexInputView = new RawLatexInputView( locale );
|
||||||
|
this.rawLatexInputView.label = t( 'LaTeX' );
|
||||||
|
|
||||||
|
this.saveButtonView = this._createButton( t( 'Save' ), IconCheck, 'ck-button-save' );
|
||||||
this.saveButtonView.type = 'submit';
|
this.saveButtonView.type = 'submit';
|
||||||
|
|
||||||
// Equation input
|
this.cancelButtonView = this._createButton( t( 'Cancel' ), IconCancel, 'ck-button-cancel' );
|
||||||
this.mathInputView = this._createMathInput();
|
this.cancelButtonView.delegate( 'execute' ).to( this, 'cancel' );
|
||||||
|
|
||||||
// Display button
|
this.displayButtonView = this._createDisplayButton( t );
|
||||||
this.displayButtonView = this._createDisplayButton();
|
|
||||||
|
|
||||||
// Cancel button
|
// --- 2. Construct Children & Preview ---
|
||||||
this.cancelButtonView = this._createButton( t( 'Cancel' ), IconCancel, 'ck-button-cancel', 'cancel' );
|
|
||||||
|
|
||||||
this.previewEnabled = previewEnabled;
|
const children: View[] = [
|
||||||
|
this.mathLiveInputView,
|
||||||
|
this.rawLatexInputView,
|
||||||
|
this.displayButtonView
|
||||||
|
];
|
||||||
|
|
||||||
let children = [];
|
if ( previewEnabled ) {
|
||||||
if ( this.previewEnabled ) {
|
const previewLabel = new LabelView( locale );
|
||||||
// Preview label
|
previewLabel.text = t( 'Equation preview' );
|
||||||
this.previewLabel = new LabelView( locale );
|
|
||||||
this.previewLabel.text = t( 'Equation preview' );
|
|
||||||
|
|
||||||
// Math element
|
// Clean instantiation using the options object
|
||||||
this.mathView = new MathView( engine, lazyLoad, locale, previewUid, previewClassName, katexRenderOptions );
|
this.mathView = new MathView( locale, mathViewOptions );
|
||||||
|
|
||||||
|
// Bind display mode: When button flips, preview updates automatically
|
||||||
this.mathView.bind( 'display' ).to( this.displayButtonView, 'isOn' );
|
this.mathView.bind( 'display' ).to( this.displayButtonView, 'isOn' );
|
||||||
|
|
||||||
children = [
|
children.push( previewLabel, this.mathView );
|
||||||
this.mathInputView,
|
|
||||||
this.displayButtonView,
|
|
||||||
this.previewLabel,
|
|
||||||
this.mathView
|
|
||||||
];
|
|
||||||
} else {
|
|
||||||
children = [
|
|
||||||
this.mathInputView,
|
|
||||||
this.displayButtonView
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add UI elements to template
|
// --- 3. Sync Logic ---
|
||||||
|
this._setupInputSync( previewEnabled );
|
||||||
|
|
||||||
|
// --- 4. Template Setup ---
|
||||||
this.setTemplate( {
|
this.setTemplate( {
|
||||||
tag: 'form',
|
tag: 'form',
|
||||||
attributes: {
|
attributes: {
|
||||||
class: [
|
class: [ 'ck', 'ck-math-form', ...popupClassName ],
|
||||||
'ck',
|
|
||||||
'ck-math-form',
|
|
||||||
...popupClassName
|
|
||||||
],
|
|
||||||
tabindex: '-1',
|
tabindex: '-1',
|
||||||
spellcheck: 'false'
|
spellcheck: 'false'
|
||||||
},
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
attributes: {
|
attributes: { class: [ 'ck-math-scroll' ] },
|
||||||
class: [
|
children: [ { tag: 'div', attributes: { class: [ 'ck-math-view' ] }, children } ]
|
||||||
'ck-math-view'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
children
|
|
||||||
},
|
},
|
||||||
this.saveButtonView,
|
{
|
||||||
this.cancelButtonView
|
tag: 'div',
|
||||||
|
attributes: { class: [ 'ck-math-button-row' ] },
|
||||||
|
children: [ this.saveButtonView, this.cancelButtonView ]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
// --- 5. Accessibility ---
|
||||||
|
this._focusCycler = new FocusCycler( {
|
||||||
|
focusables: this._focusables,
|
||||||
|
focusTracker: this.focusTracker,
|
||||||
|
keystrokeHandler: this.keystrokes,
|
||||||
|
actions: { focusPrevious: 'shift + tab', focusNext: 'tab' }
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
public override render(): void {
|
public override render(): void {
|
||||||
super.render();
|
super.render();
|
||||||
|
|
||||||
// Prevent default form submit event & trigger custom 'submit'
|
submitHandler( { view: this } );
|
||||||
submitHandler( {
|
|
||||||
view: this
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Register form elements to focusable elements
|
// Register focusables
|
||||||
const childViews = [
|
[
|
||||||
this.mathInputView,
|
this.mathLiveInputView,
|
||||||
|
this.rawLatexInputView,
|
||||||
this.displayButtonView,
|
this.displayButtonView,
|
||||||
this.saveButtonView,
|
this.saveButtonView,
|
||||||
this.cancelButtonView
|
this.cancelButtonView
|
||||||
];
|
].forEach( v => {
|
||||||
|
|
||||||
childViews.forEach( v => {
|
|
||||||
if ( v.element ) {
|
if ( v.element ) {
|
||||||
this._focusables.add( v );
|
this._focusables.add( v );
|
||||||
this.focusTracker.add( v.element );
|
this.focusTracker.add( v.element );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// Listen to keypresses inside form element
|
if ( this.element ) this.keystrokes.listenTo( this.element );
|
||||||
if ( this.element ) {
|
}
|
||||||
this.keystrokes.listenTo( this.element );
|
|
||||||
}
|
public get equation(): string {
|
||||||
|
return this.mathLiveInputView.value ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public set equation( equation: string ) {
|
||||||
|
const norm = equation.trim();
|
||||||
|
// Direct updates to the "source of truth"
|
||||||
|
this.mathLiveInputView.value = norm.length ? norm : null;
|
||||||
|
this.rawLatexInputView.value = norm;
|
||||||
|
if ( this.mathView ) this.mathView.value = norm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public focus(): void {
|
public focus(): void {
|
||||||
this._focusCycler.focusFirst();
|
this._focusCycler.focusFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get equation(): string {
|
/**
|
||||||
return this.mathInputView.fieldView.element?.value ?? '';
|
* Sets up split handlers for synchronization.
|
||||||
}
|
*/
|
||||||
|
private _setupInputSync( previewEnabled: boolean ): void {
|
||||||
|
// Handler 1: MathLive -> Raw LaTeX
|
||||||
|
this.mathLiveInputView.on( 'change:value', () => {
|
||||||
|
let eq = ( this.mathLiveInputView.value ?? '' ).trim();
|
||||||
|
|
||||||
public set equation( equation: string ) {
|
// Delimiter Normalization
|
||||||
if ( this.mathInputView.fieldView.element ) {
|
if ( hasDelimiters( eq ) ) {
|
||||||
this.mathInputView.fieldView.element.value = equation;
|
const params = extractDelimiters( eq );
|
||||||
}
|
eq = params.equation;
|
||||||
if ( this.previewEnabled && this.mathView ) {
|
this.displayButtonView.isOn = params.display;
|
||||||
this.mathView.value = equation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public focusTracker: FocusTracker = new FocusTracker();
|
// UX Fix: If we stripped delimiters, update the source
|
||||||
public keystrokes: KeystrokeHandler = new KeystrokeHandler();
|
// so the visual editor doesn't show them.
|
||||||
private _focusables = new ViewCollection<FocusableView>();
|
if ( this.mathLiveInputView.value !== eq ) {
|
||||||
private _focusCycler: FocusCycler = new FocusCycler( {
|
this.mathLiveInputView.value = eq;
|
||||||
focusables: this._focusables,
|
|
||||||
focusTracker: this.focusTracker,
|
|
||||||
keystrokeHandler: this.keystrokes,
|
|
||||||
actions: {
|
|
||||||
focusPrevious: 'shift + tab',
|
|
||||||
focusNext: 'tab'
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
private _createMathInput() {
|
|
||||||
const t = this.locale.t;
|
|
||||||
|
|
||||||
// Create equation input
|
|
||||||
const mathInput = new MathInputView( this.locale );
|
|
||||||
const fieldView = mathInput.fieldView;
|
|
||||||
mathInput.infoText = t( 'Insert equation in TeX format.' );
|
|
||||||
|
|
||||||
const onInput = () => {
|
|
||||||
if ( fieldView.element != null ) {
|
|
||||||
let equationInput = fieldView.element.value.trim();
|
|
||||||
|
|
||||||
// If input has delimiters
|
|
||||||
if ( hasDelimiters( equationInput ) ) {
|
|
||||||
// Get equation without delimiters
|
|
||||||
const params = extractDelimiters( equationInput );
|
|
||||||
|
|
||||||
// Remove delimiters from input field
|
|
||||||
fieldView.element.value = params.equation;
|
|
||||||
|
|
||||||
equationInput = params.equation;
|
|
||||||
|
|
||||||
// update display button and preview
|
|
||||||
this.displayButtonView.isOn = params.display;
|
|
||||||
}
|
}
|
||||||
if ( this.previewEnabled && this.mathView ) {
|
|
||||||
// Update preview view
|
|
||||||
this.mathView.value = equationInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.saveButtonView.isEnabled = !!equationInput;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
fieldView.on( 'render', onInput );
|
// Sync to Raw LaTeX
|
||||||
fieldView.on( 'input', onInput );
|
if ( this.rawLatexInputView.value !== eq ) {
|
||||||
|
this.rawLatexInputView.value = eq;
|
||||||
|
}
|
||||||
|
|
||||||
return mathInput;
|
// Sync to Preview
|
||||||
|
if ( previewEnabled && this.mathView && this.mathView.value !== eq ) {
|
||||||
|
this.mathView.value = eq;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Handler 2: Raw LaTeX -> MathLive
|
||||||
|
this.rawLatexInputView.on( 'change:value', () => {
|
||||||
|
const eq = ( this.rawLatexInputView.value ?? '' ).trim();
|
||||||
|
const normalized = eq.length ? eq : null;
|
||||||
|
|
||||||
|
// Sync to MathLive
|
||||||
|
if ( this.mathLiveInputView.value !== normalized ) {
|
||||||
|
this.mathLiveInputView.value = normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync to Preview
|
||||||
|
if ( previewEnabled && this.mathView && this.mathView.value !== eq ) {
|
||||||
|
this.mathView.value = eq;
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createButton(
|
private _createButton( label: string, icon: string, className: string ): ButtonView {
|
||||||
label: string,
|
const btn = new ButtonView( this.locale );
|
||||||
icon: string,
|
btn.set( { label, icon, tooltip: true } );
|
||||||
className: string,
|
btn.extendTemplate( { attributes: { class: className } } );
|
||||||
eventName: string | null
|
return btn;
|
||||||
) {
|
|
||||||
const button = new ButtonView( this.locale );
|
|
||||||
|
|
||||||
button.set( {
|
|
||||||
label,
|
|
||||||
icon,
|
|
||||||
tooltip: true
|
|
||||||
} );
|
|
||||||
|
|
||||||
button.extendTemplate( {
|
|
||||||
attributes: {
|
|
||||||
class: className
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
if ( eventName ) {
|
|
||||||
button.delegate( 'execute' ).to( this, eventName );
|
|
||||||
}
|
|
||||||
|
|
||||||
return button;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDisplayButton() {
|
private _createDisplayButton( t: ( str: string ) => string ): SwitchButtonView {
|
||||||
const t = this.locale.t;
|
const btn = new SwitchButtonView( this.locale );
|
||||||
|
btn.set( { label: t( 'Display mode' ), withText: true } );
|
||||||
|
btn.extendTemplate( { attributes: { class: 'ck-button-display-toggle' } } );
|
||||||
|
|
||||||
const switchButton = new SwitchButtonView( this.locale );
|
btn.on( 'execute', () => {
|
||||||
|
btn.isOn = !btn.isOn;
|
||||||
switchButton.set( {
|
// mathView updates automatically via bind()
|
||||||
label: t( 'Display mode' ),
|
|
||||||
withText: true
|
|
||||||
} );
|
} );
|
||||||
|
return btn;
|
||||||
switchButton.extendTemplate( {
|
|
||||||
attributes: {
|
|
||||||
class: 'ck-button-display-toggle'
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
switchButton.on( 'execute', () => {
|
|
||||||
// Toggle state
|
|
||||||
switchButton.isOn = !switchButton.isOn;
|
|
||||||
|
|
||||||
if ( this.previewEnabled && this.mathView ) {
|
|
||||||
// Update preview view
|
|
||||||
this.mathView.display = switchButton.isOn;
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
return switchButton;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
116
packages/ckeditor5-math/src/ui/mathliveinputview.ts
Normal file
116
packages/ckeditor5-math/src/ui/mathliveinputview.ts
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import { View, type Locale } from 'ckeditor5';
|
||||||
|
import 'mathlive'; // Import side-effects only (registers the <math-field> tag)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface describing the custom <math-field> element.
|
||||||
|
*/
|
||||||
|
interface MathFieldElement extends HTMLElement {
|
||||||
|
value: string;
|
||||||
|
readOnly: boolean;
|
||||||
|
mathVirtualKeyboardPolicy: string;
|
||||||
|
// Interface includes the shortcuts property
|
||||||
|
inlineShortcuts: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for the MathLive <math-field> component.
|
||||||
|
*/
|
||||||
|
export default class MathLiveInputView extends View {
|
||||||
|
/**
|
||||||
|
* The current LaTeX value.
|
||||||
|
* @observable
|
||||||
|
*/
|
||||||
|
public declare value: string | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read-only state.
|
||||||
|
* @observable
|
||||||
|
*/
|
||||||
|
public declare isReadOnly: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the DOM element.
|
||||||
|
* Typed as MathFieldElement | null for proper TS support.
|
||||||
|
*/
|
||||||
|
public mathfield: MathFieldElement | null = null;
|
||||||
|
|
||||||
|
constructor( locale: Locale ) {
|
||||||
|
super( locale );
|
||||||
|
|
||||||
|
this.set( 'value', null );
|
||||||
|
this.set( 'isReadOnly', false );
|
||||||
|
|
||||||
|
this.setTemplate( {
|
||||||
|
tag: 'div',
|
||||||
|
attributes: {
|
||||||
|
class: [ 'ck', 'ck-mathlive-input' ]
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override render(): void {
|
||||||
|
super.render();
|
||||||
|
|
||||||
|
// 1. Create element with the specific type
|
||||||
|
const mathfield = document.createElement( 'math-field' ) as MathFieldElement;
|
||||||
|
|
||||||
|
// 2. Configure Options
|
||||||
|
mathfield.mathVirtualKeyboardPolicy = 'manual';
|
||||||
|
|
||||||
|
//Disable differential D
|
||||||
|
mathfield.addEventListener( 'mount', () => {
|
||||||
|
mathfield.inlineShortcuts = {
|
||||||
|
...mathfield.inlineShortcuts, // Safe to read now
|
||||||
|
dx: 'dx',
|
||||||
|
dy: 'dy',
|
||||||
|
dt: 'dt'
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
|
||||||
|
|
||||||
|
// Disable sounds safely
|
||||||
|
const MathfieldConstructor = customElements.get( 'math-field' );
|
||||||
|
if ( MathfieldConstructor ) {
|
||||||
|
const proto = MathfieldConstructor as any;
|
||||||
|
if ( proto.soundsDirectory !== null ) proto.soundsDirectory = null;
|
||||||
|
if ( proto.plonkSound !== null ) proto.plonkSound = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Set Initial State
|
||||||
|
mathfield.value = this.value ?? '';
|
||||||
|
mathfield.readOnly = this.isReadOnly;
|
||||||
|
|
||||||
|
// 4. Bind Events (DOM -> Observable)
|
||||||
|
mathfield.addEventListener( 'input', () => {
|
||||||
|
const val = mathfield.value;
|
||||||
|
this.value = val.length ? val : null;
|
||||||
|
} );
|
||||||
|
|
||||||
|
// 5. Bind Events (Observable -> DOM)
|
||||||
|
this.on( 'change:value', ( _evt, _name, nextValue ) => {
|
||||||
|
if ( mathfield.value !== nextValue ) {
|
||||||
|
mathfield.value = nextValue ?? '';
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
this.on( 'change:isReadOnly', ( _evt, _name, nextValue ) => {
|
||||||
|
mathfield.readOnly = nextValue;
|
||||||
|
} );
|
||||||
|
|
||||||
|
// 6. Mount to the wrapper view
|
||||||
|
this.element?.appendChild( mathfield );
|
||||||
|
this.mathfield = mathfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
public focus(): void {
|
||||||
|
this.mathfield?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override destroy(): void {
|
||||||
|
if ( this.mathfield ) {
|
||||||
|
this.mathfield.remove();
|
||||||
|
this.mathfield = null;
|
||||||
|
}
|
||||||
|
super.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,44 +2,44 @@ import { View, type Locale } from 'ckeditor5';
|
|||||||
import type { KatexOptions } from '../typings-external.js';
|
import type { KatexOptions } from '../typings-external.js';
|
||||||
import { renderEquation } from '../utils.js';
|
import { renderEquation } from '../utils.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options for the MathView.
|
||||||
|
*/
|
||||||
|
export interface MathViewOptions {
|
||||||
|
engine: 'mathjax' | 'katex' | ( ( equation: string, element: HTMLElement, display: boolean ) => void );
|
||||||
|
lazyLoad: undefined | ( () => Promise<void> );
|
||||||
|
previewUid: string;
|
||||||
|
previewClassName: Array<string>;
|
||||||
|
katexRenderOptions: KatexOptions;
|
||||||
|
}
|
||||||
|
|
||||||
export default class MathView extends View {
|
export default class MathView extends View {
|
||||||
|
/**
|
||||||
|
* The LaTeX equation value to render.
|
||||||
|
* @observable
|
||||||
|
*/
|
||||||
public declare value: string;
|
public declare value: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to render in display mode (centered) or inline.
|
||||||
|
* @observable
|
||||||
|
*/
|
||||||
public declare display: boolean;
|
public declare display: boolean;
|
||||||
public previewUid: string;
|
|
||||||
public previewClassName: Array<string>;
|
|
||||||
public katexRenderOptions: KatexOptions;
|
|
||||||
public engine:
|
|
||||||
| 'mathjax'
|
|
||||||
| 'katex'
|
|
||||||
| ( ( equation: string, element: HTMLElement, display: boolean ) => void );
|
|
||||||
public lazyLoad: undefined | ( () => Promise<void> );
|
|
||||||
|
|
||||||
constructor(
|
/**
|
||||||
engine:
|
* Configuration options passed during initialization.
|
||||||
| 'mathjax'
|
*/
|
||||||
| 'katex'
|
private options: MathViewOptions;
|
||||||
| ( (
|
|
||||||
equation: string,
|
constructor( locale: Locale, options: MathViewOptions ) {
|
||||||
element: HTMLElement,
|
|
||||||
display: boolean,
|
|
||||||
) => void ),
|
|
||||||
lazyLoad: undefined | ( () => Promise<void> ),
|
|
||||||
locale: Locale,
|
|
||||||
previewUid: string,
|
|
||||||
previewClassName: Array<string>,
|
|
||||||
katexRenderOptions: KatexOptions
|
|
||||||
) {
|
|
||||||
super( locale );
|
super( locale );
|
||||||
|
this.options = options;
|
||||||
this.engine = engine;
|
|
||||||
this.lazyLoad = lazyLoad;
|
|
||||||
this.previewUid = previewUid;
|
|
||||||
this.katexRenderOptions = katexRenderOptions;
|
|
||||||
this.previewClassName = previewClassName;
|
|
||||||
|
|
||||||
this.set( 'value', '' );
|
this.set( 'value', '' );
|
||||||
this.set( 'display', false );
|
this.set( 'display', false );
|
||||||
|
|
||||||
|
// Update rendering when state changes.
|
||||||
|
// Checking isRendered prevents errors during initialization.
|
||||||
this.on( 'change', () => {
|
this.on( 'change', () => {
|
||||||
if ( this.isRendered ) {
|
if ( this.isRendered ) {
|
||||||
this.updateMath();
|
this.updateMath();
|
||||||
@ -56,16 +56,20 @@ export default class MathView extends View {
|
|||||||
|
|
||||||
public updateMath(): void {
|
public updateMath(): void {
|
||||||
if ( this.element ) {
|
if ( this.element ) {
|
||||||
|
|
||||||
|
// This prevents the new render from appending to the old one.
|
||||||
|
this.element.textContent = '';
|
||||||
|
|
||||||
void renderEquation(
|
void renderEquation(
|
||||||
this.value,
|
this.value,
|
||||||
this.element,
|
this.element,
|
||||||
this.engine,
|
this.options.engine,
|
||||||
this.lazyLoad,
|
this.options.lazyLoad,
|
||||||
this.display,
|
this.display,
|
||||||
true,
|
true, // isPreview
|
||||||
this.previewUid,
|
this.options.previewUid,
|
||||||
this.previewClassName,
|
this.options.previewClassName,
|
||||||
this.katexRenderOptions
|
this.options.katexRenderOptions
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
packages/ckeditor5-math/src/ui/rawlatexinputview.ts
Normal file
54
packages/ckeditor5-math/src/ui/rawlatexinputview.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { LabeledFieldView, createLabeledTextarea, type Locale, type TextareaView } from 'ckeditor5';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A labeled textarea view for direct LaTeX code editing.
|
||||||
|
*/
|
||||||
|
export default class RawLatexInputView extends LabeledFieldView<TextareaView> {
|
||||||
|
/**
|
||||||
|
* The current LaTeX value.
|
||||||
|
* @observable
|
||||||
|
*/
|
||||||
|
public declare value: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the input is in read-only mode.
|
||||||
|
* @observable
|
||||||
|
*/
|
||||||
|
public declare isReadOnly: boolean;
|
||||||
|
|
||||||
|
constructor( locale: Locale ) {
|
||||||
|
super( locale, createLabeledTextarea );
|
||||||
|
|
||||||
|
this.set( 'value', '' );
|
||||||
|
this.set( 'isReadOnly', false );
|
||||||
|
|
||||||
|
const fieldView = this.fieldView;
|
||||||
|
|
||||||
|
// 1. Sync: DOM (Textarea) -> Observable
|
||||||
|
fieldView.on( 'input', () => {
|
||||||
|
// We cast strictly to HTMLTextAreaElement to access '.value' safely
|
||||||
|
const textarea = fieldView.element as HTMLTextAreaElement;
|
||||||
|
if ( textarea ) {
|
||||||
|
this.value = textarea.value;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// 2. Sync: Observable -> DOM (Textarea)
|
||||||
|
this.on( 'change:value', () => {
|
||||||
|
const textarea = fieldView.element as HTMLTextAreaElement;
|
||||||
|
// Check for difference to avoid cursor jumping
|
||||||
|
if ( textarea && textarea.value !== this.value ) {
|
||||||
|
textarea.value = this.value;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
// 3. Sync: ReadOnly State
|
||||||
|
this.on( 'change:isReadOnly', ( _evt, _name, nextValue ) => {
|
||||||
|
fieldView.isReadOnly = nextValue;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public override render(): void {
|
||||||
|
super.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -168,13 +168,13 @@ describe( 'MathUI', () => {
|
|||||||
|
|
||||||
command.isEnabled = true;
|
command.isEnabled = true;
|
||||||
|
|
||||||
expect( formView!.mathInputView.isReadOnly ).to.be.false;
|
expect( formView!.mathLiveInputView.isReadOnly ).to.be.false;
|
||||||
expect( formView!.saveButtonView.isEnabled ).to.be.false;
|
expect( formView!.saveButtonView.isEnabled ).to.be.false;
|
||||||
expect( formView!.cancelButtonView.isEnabled ).to.be.true;
|
expect( formView!.cancelButtonView.isEnabled ).to.be.true;
|
||||||
|
|
||||||
command.isEnabled = false;
|
command.isEnabled = false;
|
||||||
|
|
||||||
expect( formView!.mathInputView.isReadOnly ).to.be.true;
|
expect( formView!.mathLiveInputView.isReadOnly ).to.be.true;
|
||||||
expect( formView!.saveButtonView.isEnabled ).to.be.false;
|
expect( formView!.saveButtonView.isEnabled ).to.be.false;
|
||||||
expect( formView!.cancelButtonView.isEnabled ).to.be.true;
|
expect( formView!.cancelButtonView.isEnabled ).to.be.true;
|
||||||
} );
|
} );
|
||||||
@ -407,22 +407,30 @@ describe( 'MathUI', () => {
|
|||||||
setModelData( editor.model, '<paragraph>f[o]o</paragraph>' );
|
setModelData( editor.model, '<paragraph>f[o]o</paragraph>' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'should bind mainFormView.mathInputView#value to math command value', () => {
|
it( 'should bind mainFormView.mathLiveInputView#value to math command value', () => {
|
||||||
const command = editor.commands.get( 'math' );
|
const command = editor.commands.get( 'math' );
|
||||||
|
|
||||||
expect( formView!.mathInputView.value ).to.null;
|
expect( formView!.mathLiveInputView.value ).to.be.null;
|
||||||
|
|
||||||
command!.value = 'x^2';
|
command!.value = 'x^2';
|
||||||
expect( formView!.mathInputView.value ).to.equal( 'x^2' );
|
expect( formView!.mathLiveInputView.value ).to.equal( 'x^2' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'should execute math command on mainFormView#submit event', () => {
|
it( 'should execute math command on mainFormView#submit event', () => {
|
||||||
const executeSpy = vi.spyOn( editor, 'execute' );
|
const executeSpy = vi.spyOn( editor, 'execute' );
|
||||||
|
|
||||||
formView!.mathInputView.fieldView.element!.value = 'x^2';
|
formView!.mathLiveInputView.value = 'x^2';
|
||||||
formView!.fire( 'submit' );
|
formView!.fire( 'submit' );
|
||||||
|
|
||||||
expect(executeSpy.mock.lastCall?.slice(0, 2)).toMatchObject(['math', 'x^2']);
|
expect( executeSpy.mock.lastCall?.slice( 0, 2 ) ).toMatchObject( [ 'math', 'x^2' ] );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'should sync mathLiveInputView and rawLatexInputView', () => {
|
||||||
|
formView!.mathLiveInputView.value = 'x^2';
|
||||||
|
expect( formView!.rawLatexInputView.value ).to.equal( 'x^2' );
|
||||||
|
|
||||||
|
formView!.rawLatexInputView.value = '\\frac{1}{2}';
|
||||||
|
expect( formView!.mathLiveInputView.value ).to.equal( '\\frac{1}{2}' );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'should hide the balloon on mainFormView#cancel if math command does not have a value', () => {
|
it( 'should hide the balloon on mainFormView#cancel if math command does not have a value', () => {
|
||||||
|
|||||||
@ -1,35 +1,214 @@
|
|||||||
|
/**
|
||||||
|
* Math equation editor dialog styles
|
||||||
|
* Supports MathLive input, raw LaTeX textarea, and equation preview
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Main Dialog Container
|
||||||
|
========================================================================= */
|
||||||
|
|
||||||
.ck.ck-math-form {
|
.ck.ck-math-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
flex-direction: column;
|
||||||
flex-direction: row;
|
padding: var(--ck-spacing-standard);
|
||||||
flex-wrap: nowrap;
|
box-sizing: border-box;
|
||||||
padding: var(--ck-spacing-standard);
|
max-width: 80vw;
|
||||||
|
max-height: 80vh;
|
||||||
@media screen and (max-width: 600px) {
|
height: 100%;
|
||||||
flex-wrap: wrap;
|
overflow-x: hidden;
|
||||||
|
|
||||||
& .ck-math-view {
|
|
||||||
flex-basis: 100%;
|
|
||||||
|
|
||||||
& .ck-labeled-view {
|
|
||||||
flex-basis: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
& .ck-label {
|
|
||||||
flex-basis: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& .ck-button {
|
|
||||||
flex-basis: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck-math-tex.ck-placeholder::before {
|
/* Mobile responsiveness */
|
||||||
display: none !important;
|
@media screen and (max-width: 600px) {
|
||||||
|
.ck.ck-math-form {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck.ck-toolbar-container {
|
/* ============================================================================
|
||||||
z-index: calc(var(--ck-z-panel) + 2);
|
Content Layout
|
||||||
|
========================================================================= */
|
||||||
|
|
||||||
|
.ck-math-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
gap: var(--ck-spacing-standard);
|
||||||
|
min-height: fit-content;
|
||||||
|
min-width: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LaTeX section heading */
|
||||||
|
.ck-math-view > .ck-labeled-field-view::before {
|
||||||
|
content: "LaTeX";
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ck-color-text, #333);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding-left: 2px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Equation preview section heading */
|
||||||
|
.ck-math-view > math-field::before {
|
||||||
|
content: "Equation preview";
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ck-color-text, #333);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
padding-left: 2px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add spacing between preview and action buttons */
|
||||||
|
.ck-math-view > math-field {
|
||||||
|
margin-bottom: var(--ck-spacing-large, 16px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Action buttons row (Save/Cancel) */
|
||||||
|
.ck-math-button-row {
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
gap: var(--ck-spacing-standard);
|
||||||
|
margin-top: var(--ck-spacing-standard);
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 100%;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Shared Styles for Input Fields
|
||||||
|
========================================================================= */
|
||||||
|
|
||||||
|
/* Base styling for both MathLive fields and textareas */
|
||||||
|
.ck.ck-math-form math-field,
|
||||||
|
.ck.ck-math-form textarea {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: var(--ck-spacing-small);
|
||||||
|
background: var(--ck-color-input-background) !important;
|
||||||
|
color: var(--ck-color-input-text, inherit);
|
||||||
|
font-size: var(--ck-font-size-base);
|
||||||
|
border: none !important;
|
||||||
|
border-radius: var(--ck-border-radius, 6px);
|
||||||
|
outline: 3px solid transparent;
|
||||||
|
outline-offset: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MathLive-specific configuration */
|
||||||
|
.ck.ck-math-form math-field {
|
||||||
|
display: block !important;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-x: auto !important;
|
||||||
|
|
||||||
|
/* MathLive theme customization */
|
||||||
|
--selection-background-color: rgba(33, 150, 243, 0.2);
|
||||||
|
--selection-color: inherit;
|
||||||
|
--contains-highlight-background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
MathLive Visual Editor (Top Input)
|
||||||
|
========================================================================= */
|
||||||
|
|
||||||
|
.ck.ck-mathlive-input {
|
||||||
|
display: inline-block;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: fit-content;
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow: auto;
|
||||||
|
padding-bottom: var(--ck-spacing-small);
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure MathLive shadow DOM layout */
|
||||||
|
.ck.ck-math-form math-field::part(container),
|
||||||
|
.ck.ck-math-form math-field::part(content),
|
||||||
|
.ck.ck-math-form math-field::part(field) {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
height: 100%;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Position MathLive UI controls */
|
||||||
|
.ck.ck-math-form math-field::part(virtual-keyboard-toggle),
|
||||||
|
.ck.ck-math-form math-field::part(menu-toggle) {
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck.ck-math-form math-field::part(virtual-keyboard-toggle) {
|
||||||
|
right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ck.ck-math-form math-field::part(menu-toggle) {
|
||||||
|
right: 8px;
|
||||||
|
display: flex !important;
|
||||||
|
visibility: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================================
|
||||||
|
Raw LaTeX Textarea (Middle Input)
|
||||||
|
========================================================================= */
|
||||||
|
|
||||||
|
.ck-math-view .ck-labeled-field-view {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
min-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 60px;
|
||||||
|
max-height: 65vh;
|
||||||
|
resize: both;
|
||||||
|
overflow: auto;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the default label (we use ::before for custom heading) */
|
||||||
|
.ck-math-view .ck-labeled-field-view .ck-label {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textarea wrapper */
|
||||||
|
.ck-math-view .ck-labeled-field-view .ck-labeled-field-view__input-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 100px;
|
||||||
|
height: auto;
|
||||||
|
padding: 0;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Raw LaTeX textarea styling */
|
||||||
|
.ck-math-view .ck-labeled-field-view textarea {
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 140px;
|
||||||
|
resize: none !important;
|
||||||
|
border-radius: 0 !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Textarea hover and focus states */
|
||||||
|
.ck-math-view .ck-labeled-field-view textarea:hover,
|
||||||
|
.ck-math-view .ck-labeled-field-view textarea:focus {
|
||||||
|
background: var(--ck-color-input-background) !important;
|
||||||
|
outline: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,9 +2,14 @@
|
|||||||
* https://github.com/TriliumNext/Trilium/issues/1002
|
* https://github.com/TriliumNext/Trilium/issues/1002
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Command, ModelDocumentSelection, ModelElement, ModelNode, Plugin, ModelRange } from 'ckeditor5';
|
import { Command, ModelDocumentSelection, ModelElement, ModelNode, Plugin, ModelRange, _isMac, Editor } from 'ckeditor5';
|
||||||
export default class MoveBlockUpDownPlugin extends Plugin {
|
|
||||||
|
|
||||||
|
const keyMap = {
|
||||||
|
ArrowUp: 'moveBlockUp',
|
||||||
|
ArrowDown: 'moveBlockDown'
|
||||||
|
};
|
||||||
|
|
||||||
|
export default class MoveBlockUpDownPlugin extends Plugin {
|
||||||
init() {
|
init() {
|
||||||
const editor = this.editor;
|
const editor = this.editor;
|
||||||
|
|
||||||
@ -21,17 +26,14 @@ export default class MoveBlockUpDownPlugin extends Plugin {
|
|||||||
const domRoot = editor.editing.view.getDomRoot();
|
const domRoot = editor.editing.view.getDomRoot();
|
||||||
if (!domRoot) return;
|
if (!domRoot) return;
|
||||||
|
|
||||||
|
const isMac = _isMac(navigator.userAgent.toLowerCase());
|
||||||
const handleKeydown = (e: KeyboardEvent) => {
|
const handleKeydown = (e: KeyboardEvent) => {
|
||||||
const keyMap = {
|
|
||||||
ArrowUp: 'moveBlockUp',
|
|
||||||
ArrowDown: 'moveBlockDown'
|
|
||||||
};
|
|
||||||
|
|
||||||
const command = keyMap[e.key];
|
const command = keyMap[e.key];
|
||||||
const isCtrl = e.ctrlKey || e.metaKey;
|
if (!command) return;
|
||||||
const hasModifier = (isCtrl || e.altKey) && !(isCtrl && e.altKey);
|
const isOnlyMeta = (!e.ctrlKey && !e.altKey && e.metaKey);
|
||||||
|
const isOnlyAlt = (!e.ctrlKey && e.altKey && !e.metaKey);
|
||||||
|
|
||||||
if (command && hasModifier) {
|
if ((!isMac && isOnlyMeta) || isOnlyAlt) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopImmediatePropagation();
|
e.stopImmediatePropagation();
|
||||||
editor.execute(command);
|
editor.execute(command);
|
||||||
@ -100,8 +102,7 @@ abstract class MoveBlockUpDownCommand extends Command {
|
|||||||
}
|
}
|
||||||
writer.setSelection(range);
|
writer.setSelection(range);
|
||||||
this.editor.editing.view.focus();
|
this.editor.editing.view.focus();
|
||||||
|
scrollToSelection(this.editor);
|
||||||
this.scrollToSelection();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,13 +130,6 @@ abstract class MoveBlockUpDownCommand extends Command {
|
|||||||
// Deduplicate adjacent duplicates (e.g., nested selections resolving to same block)
|
// Deduplicate adjacent duplicates (e.g., nested selections resolving to same block)
|
||||||
return resolved.filter((blk, idx) => idx === 0 || blk !== resolved[idx - 1]);
|
return resolved.filter((blk, idx) => idx === 0 || blk !== resolved[idx - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollToSelection() {
|
|
||||||
// Ensure scroll happens in sync with DOM updates
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
this.editor.editing.view.scrollToTheSelection();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MoveBlockUpCommand extends MoveBlockUpDownCommand {
|
class MoveBlockUpCommand extends MoveBlockUpDownCommand {
|
||||||
@ -162,3 +156,10 @@ class MoveBlockDownCommand extends MoveBlockUpDownCommand {
|
|||||||
return "after" as const;
|
return "after" as const;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scrollToSelection(editor: Editor) {
|
||||||
|
// Ensure scroll happens in sync with DOM updates
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
editor.editing.view.scrollToTheSelection();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
36
pnpm-lock.yaml
generated
36
pnpm-lock.yaml
generated
@ -1061,6 +1061,9 @@ importers:
|
|||||||
'@ckeditor/ckeditor5-icons':
|
'@ckeditor/ckeditor5-icons':
|
||||||
specifier: 47.2.0
|
specifier: 47.2.0
|
||||||
version: 47.2.0
|
version: 47.2.0
|
||||||
|
mathlive:
|
||||||
|
specifier: 0.108.2
|
||||||
|
version: 0.108.2
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@ckeditor/ckeditor5-dev-build-tools':
|
'@ckeditor/ckeditor5-dev-build-tools':
|
||||||
specifier: 43.1.0
|
specifier: 43.1.0
|
||||||
@ -2123,6 +2126,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
||||||
engines: {node: '>=0.1.90'}
|
engines: {node: '>=0.1.90'}
|
||||||
|
|
||||||
|
'@cortex-js/compute-engine@0.30.2':
|
||||||
|
resolution: {integrity: sha512-Zx+iisk9WWdbxjm8EYsneIBszvjfUs7BHNwf1jBtSINIgfWGpHrTTq9vW0J59iGCFt6bOFxbmWyxNMRSmksHMA==}
|
||||||
|
engines: {node: '>=21.7.3', npm: '>=10.5.0'}
|
||||||
|
|
||||||
'@cspotcode/source-map-support@0.8.1':
|
'@cspotcode/source-map-support@0.8.1':
|
||||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -6879,6 +6886,10 @@ packages:
|
|||||||
compare-versions@6.1.1:
|
compare-versions@6.1.1:
|
||||||
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
|
resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==}
|
||||||
|
|
||||||
|
complex-esm@2.1.1-esm1:
|
||||||
|
resolution: {integrity: sha512-IShBEWHILB9s7MnfyevqNGxV0A1cfcSnewL/4uPFiSxkcQL4Mm3FxJ0pXMtCXuWLjYz3lRRyk6OfkeDZcjD6nw==}
|
||||||
|
engines: {node: '>=16.14.2', npm: '>=8.5.0'}
|
||||||
|
|
||||||
component-emitter@1.3.1:
|
component-emitter@1.3.1:
|
||||||
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
|
resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==}
|
||||||
|
|
||||||
@ -10192,6 +10203,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
mathlive@0.108.2:
|
||||||
|
resolution: {integrity: sha512-GIZkfprGTxrbHckOvwo92ZmOOxdD018BHDzlrEwYUU+pzR5KabhqI1s43lxe/vqXdF5RLiQKgDcuk5jxEjhkYg==}
|
||||||
|
|
||||||
mathml-tag-names@2.1.3:
|
mathml-tag-names@2.1.3:
|
||||||
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
|
resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==}
|
||||||
|
|
||||||
@ -15623,6 +15637,8 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-core': 47.2.0
|
'@ckeditor/ckeditor5-core': 47.2.0
|
||||||
'@ckeditor/ckeditor5-upload': 47.2.0
|
'@ckeditor/ckeditor5-upload': 47.2.0
|
||||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-ai@47.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
|
'@ckeditor/ckeditor5-ai@47.2.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15769,6 +15785,8 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-core': 47.2.0
|
'@ckeditor/ckeditor5-core': 47.2.0
|
||||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-code-block@47.2.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
'@ckeditor/ckeditor5-code-block@47.2.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -15833,8 +15851,6 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||||
'@ckeditor/ckeditor5-watchdog': 47.2.0
|
'@ckeditor/ckeditor5-watchdog': 47.2.0
|
||||||
es-toolkit: 1.39.5
|
es-toolkit: 1.39.5
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)':
|
'@ckeditor/ckeditor5-dev-build-tools@43.1.0(@swc/helpers@0.5.17)(tslib@2.8.1)(typescript@5.9.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -16029,6 +16045,8 @@ snapshots:
|
|||||||
'@ckeditor/ckeditor5-utils': 47.2.0
|
'@ckeditor/ckeditor5-utils': 47.2.0
|
||||||
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
ckeditor5: 47.2.0(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
|
||||||
es-toolkit: 1.39.5
|
es-toolkit: 1.39.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@ckeditor/ckeditor5-editor-multi-root@47.2.0':
|
'@ckeditor/ckeditor5-editor-multi-root@47.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -16941,6 +16959,11 @@ snapshots:
|
|||||||
|
|
||||||
'@colors/colors@1.5.0': {}
|
'@colors/colors@1.5.0': {}
|
||||||
|
|
||||||
|
'@cortex-js/compute-engine@0.30.2':
|
||||||
|
dependencies:
|
||||||
|
complex-esm: 2.1.1-esm1
|
||||||
|
decimal.js: 10.6.0
|
||||||
|
|
||||||
'@cspotcode/source-map-support@0.8.1':
|
'@cspotcode/source-map-support@0.8.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@jridgewell/trace-mapping': 0.3.9
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
@ -22577,6 +22600,8 @@ snapshots:
|
|||||||
|
|
||||||
compare-versions@6.1.1: {}
|
compare-versions@6.1.1: {}
|
||||||
|
|
||||||
|
complex-esm@2.1.1-esm1: {}
|
||||||
|
|
||||||
component-emitter@1.3.1: {}
|
component-emitter@1.3.1: {}
|
||||||
|
|
||||||
compress-commons@6.0.2:
|
compress-commons@6.0.2:
|
||||||
@ -23360,8 +23385,7 @@ snapshots:
|
|||||||
|
|
||||||
decimal.js@10.5.0: {}
|
decimal.js@10.5.0: {}
|
||||||
|
|
||||||
decimal.js@10.6.0:
|
decimal.js@10.6.0: {}
|
||||||
optional: true
|
|
||||||
|
|
||||||
decko@1.2.0: {}
|
decko@1.2.0: {}
|
||||||
|
|
||||||
@ -26780,6 +26804,10 @@ snapshots:
|
|||||||
|
|
||||||
math-intrinsics@1.1.0: {}
|
math-intrinsics@1.1.0: {}
|
||||||
|
|
||||||
|
mathlive@0.108.2:
|
||||||
|
dependencies:
|
||||||
|
'@cortex-js/compute-engine': 0.30.2
|
||||||
|
|
||||||
mathml-tag-names@2.1.3: {}
|
mathml-tag-names@2.1.3: {}
|
||||||
|
|
||||||
mdast-util-find-and-replace@3.0.2:
|
mdast-util-find-and-replace@3.0.2:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user