Compare commits

...

29 Commits

Author SHA1 Message Date
Meinzzzz
96154b385f
Merge f8d84814e079bdca892c1c2cac75c53baa96f20b into 22c4fba665567cb37f4fababebdbbc5cfb94a237 2025-11-27 14:23:05 +02:00
Elian Doran
22c4fba665
docs(user): improve documentation on tree keyboard shortcuts
Some checks are pending
Checks / main (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Documentation / Build and Deploy Documentation (push) Waiting to run
Dev / Test development (push) Waiting to run
Dev / Build Docker image (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile) (push) Blocked by required conditions
Dev / Check Docker build (Dockerfile.alpine) (push) Blocked by required conditions
/ Check Docker build (Dockerfile) (push) Waiting to run
/ Check Docker build (Dockerfile.alpine) (push) Waiting to run
/ Build Docker images (Dockerfile, ubuntu-24.04-arm, linux/arm64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.alpine, ubuntu-latest, linux/amd64) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v7) (push) Blocked by required conditions
/ Build Docker images (Dockerfile.legacy, ubuntu-24.04-arm, linux/arm/v8) (push) Blocked by required conditions
/ Merge manifest lists (push) Blocked by required conditions
playwright / E2E tests on linux-arm64 (push) Waiting to run
playwright / E2E tests on linux-x64 (push) Waiting to run
2025-11-27 14:22:45 +02:00
meinzzzz
f8d84814e0 Fix differential d problems 2025-11-26 23:02:34 +01:00
meinzzzz
c46cf41842 Small improvements 2025-11-26 22:48:57 +01:00
meinzzzz
64ab1c4116 Imrovement for Latex 2025-11-26 22:29:29 +01:00
meinzzzz
a6de1041c7 Fix bug in math rendering where old content was not cleared 2025-11-26 21:59:33 +01:00
meinzzzz
c8d34e65ea Improve max window size 2025-11-26 21:49:09 +01:00
meinzzzz
51db729546 Improve and simplify Mathfield integration 2025-11-25 23:27:06 +01:00
meinzzzz
d2052ad236 Disable mathlive sound effects 2025-11-24 21:51:59 +01:00
meinzzzz
9c4301467f Remove unused icons from ckeditor5-math package 2025-11-24 19:46:04 +01:00
meinzzzz
e7355dc0e4 remove gitignore unneccesary changes 2025-11-24 18:43:52 +01:00
meinzzzz
4110fec94f Removed unnecessary declare keyboard 2025-11-24 18:28:59 +01:00
meinzzzz
d5e601eae9 Simpliyfied resize logic for math input form and improved css 2025-11-24 17:56:18 +01:00
meinzzzz
4f044c4a57 Use icons form CKEditor5 icons, instead of testing icons. 2025-11-23 22:43:07 +01:00
meinzzzz
5821c350e1 Fixing class property initialization order 2025-11-23 17:58:51 +01:00
meinzzzz
edba8188fe Fix dark selection colors in MathLive math-field 2025-11-23 13:44:28 +01:00
meinzzzz
1471a72633 refactor: avoid recursive updates in mathLiveInput by normalizing value before updateing 2025-11-23 13:34:22 +01:00
meinzzzz
56834cb88a Improve MathLive and Raw LaTeX input views to propagate mousedown events 2025-11-23 13:29:26 +01:00
meinzzzz
a0f16f9184 Fix typos in mathform.css 2025-11-23 13:09:56 +01:00
meinzzzz
de80eb4806 Improve mathform.css styling for better visual integration 2025-11-22 22:42:34 +01:00
meinzzzz
48a4b81fbe remove automated screenshot files 2025-11-22 21:40:55 +01:00
meinzzzz
e225794f72 Better window focus handling in MathFormView 2025-11-22 21:35:37 +01:00
meinzzzz
4eef30f8b5 Fix names 2025-11-22 00:20:20 +01:00
meinzzzz
569b09609d Remove mathlive dependency and chunking 2025-11-22 00:01:14 +01:00
meinzzzz
39838c25c2 Fixed chaching problems 2025-11-21 23:50:49 +01:00
meinzzzz
49e90c08a9 Better Names for Math UI Components 2025-11-20 22:45:21 +01:00
meinzzzz
e777b06fb8 Math 2025-11-20 18:53:39 +01:00
meinzzzz
497ec2ac74 Merge branch 'main' of https://github.com/Meinzzzz/Trilium-Mathlive 2025-11-20 18:00:18 +01:00
meinzzzz
c5d282d203 Mathlive 2025-11-20 00:09:10 +01:00
18 changed files with 837 additions and 476 deletions

2
.gitignore vendored
View File

@ -48,4 +48,4 @@ upload
.svelte-kit .svelte-kit
# docs # docs
site/ site/

View File

@ -5,69 +5,50 @@
Keyboard shortcuts. Using <code>global:</code> prefix, you can assign a shortcut Keyboard shortcuts. Using <code>global:</code> prefix, you can assign a shortcut
which will work even without Trilium being in focus (requires app restart which will work even without Trilium being in focus (requires app restart
to take effect).</p> to take effect).</p>
<h2>Tree</h2>
<p>See the corresponding section:&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/oPVyFC7WL2Lp/_help_DvdZhoQZY9Yd">Keyboard shortcuts</a>
</p>
<h2>Note navigation</h2> <h2>Note navigation</h2>
<ul> <ul>
<li><kbd><span></span></kbd>, <kbd><span></span></kbd> - go up/down in the <li data-list-item-id="ed8fd9c2ab08ae80ea76c1ae5dc943e90"><kbd>Alt</kbd> + <kbd><span></span></kbd>, <kbd>Alt</kbd> + <kbd><span></span></kbd>
list of notes, <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd><span></span></kbd> and <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd><span></span></kbd> &nbsp;work
also from editor</li>
<li><kbd><span></span></kbd>, <kbd><span></span></kbd> - collapse/expand node</li>
<li><kbd>Alt</kbd> + <kbd><span></span></kbd>, <kbd>Alt</kbd> + <kbd><span></span></kbd> -
go back / forwards in the history</li> go back / forwards in the history</li>
<li><kbd>Ctrl</kbd> + <kbd>J</kbd> - show <a href="#root/_help_MMiBEQljMQh2">"Jump to" dialog</a> <li data-list-item-id="eff3c5760b3d985e7808f1524982fcb9b"><kbd>Ctrl</kbd> + <kbd>J</kbd> show <a href="#root/_help_MMiBEQljMQh2">"Jump to" dialog</a>
</li> </li>
<li><kbd>Ctrl</kbd> + <kbd>.</kbd> - scroll to current note (useful when you <li data-list-item-id="e1fdee08a424868922b0579e78584279c"><kbd>Ctrl</kbd> + <kbd>.</kbd> scroll to current note (useful when you
scroll away from your note or your focus is currently in the editor)</li> scroll away from your note or your focus is currently in the editor)</li>
<li><kbd><span>Backspace</span></kbd> - jumps to parent note</li> <li
<li><kbd>Alt</kbd> + <kbd>C</kbd> - collapse whole note tree</li> data-list-item-id="e9bbc51f19a729c10997010a915779d07"><kbd><span>Backspace</span></kbd> jumps to parent note</li>
<li><kbd>Alt</kbd> + <kbd>-</kbd> (alt with minus sign) - collapse subtree (if <li data-list-item-id="eaaa0f36b2e561d4a24358552e633d924"><kbd>Alt</kbd> + <kbd>C</kbd> collapse whole note tree</li>
some subtree takes too much space on tree pane you can collapse it)</li> <li data-list-item-id="ee625a141f5298dbd52ecb6e56693e647"><kbd>Alt</kbd> + <kbd>-</kbd> (alt with minus sign) collapse subtree (if
<li>you can define a <a href="#root/_help_zEY4DaJG4YT5">label</a> <code>#keyboardShortcut</code> with some subtree takes too much space on tree pane you can collapse it)</li>
e.g. value <kbd>Ctrl</kbd> + <kbd>I</kbd> . Pressing this keyboard combination <li
will then bring you to the note on which it is defined. Note that Trilium data-list-item-id="ec1bf00840a017ea798880470e419f3d9">you can define a <a href="#root/_help_zEY4DaJG4YT5">label</a> <code>#keyboardShortcut</code> with
must be reloaded/restarted (<kbd>Ctrl</kbd> + <kbd>R</kbd> ) for changes to e.g. value <kbd>Ctrl</kbd> + <kbd>I</kbd> . Pressing this keyboard combination
be in effect.</li> will then bring you to the note on which it is defined. Note that Trilium
must be reloaded/restarted (<kbd>Ctrl</kbd> + <kbd>R</kbd> ) for changes to
be in effect.</li>
</ul> </ul>
<p>See demo of some of these features in <a href="#root/_help_MMiBEQljMQh2">note navigation</a>.</p> <p>See demo of some of these features in <a href="#root/_help_MMiBEQljMQh2">note navigation</a>.</p>
<h2>Tabs</h2> <h2>Tabs</h2>
<ul> <ul>
<li><kbd>Ctrl</kbd> + <kbd>🖱 Left click</kbd> - (or middle mouse click) on note <li data-list-item-id="e6a9d460427c0394177000c8a0eecfca4"><kbd>Ctrl</kbd> + <kbd>🖱 Left click</kbd> (or middle mouse click) on note
link opens note in a new tab</li> link opens note in a new tab</li>
</ul> </ul>
<p>Only in desktop (electron build):</p> <p>Only in desktop (electron build):</p>
<ul> <ul>
<li><kbd>Ctrl</kbd> + <kbd>T</kbd> - opens empty tab</li> <li data-list-item-id="e745e6690db1a4bee718faa94b272013e"><kbd>Ctrl</kbd> + <kbd>T</kbd> opens empty tab</li>
<li><kbd>Ctrl</kbd> + <kbd>W</kbd> - closes active tab</li> <li data-list-item-id="ec34c224c2c0dd74367753a6aff4f9721"><kbd>Ctrl</kbd> + <kbd>W</kbd> closes active tab</li>
<li><kbd>Ctrl</kbd> + <kbd>Tab</kbd> - activates next tab</li> <li data-list-item-id="e4b9a2163cccc3ab7dbdcf6fb7f149315"><kbd>Ctrl</kbd> + <kbd>Tab</kbd> activates next tab</li>
<li><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd> - activates previous tab</li> <li data-list-item-id="eeeabd750227ec42c30b282b9b8ab5c8d"><kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd> activates previous tab</li>
</ul> </ul>
<h2>Creating notes</h2> <h2>Creating notes</h2>
<ul> <ul>
<li><code>CTRL+O</code> - creates new note after the current note</li> <li data-list-item-id="e90a7441e67297c921f8bb2145829dd55"><kbd>CTRL</kbd>+<kbd>O</kbd> creates new note after the current note</li>
<li><code>CTRL+P</code> - creates new sub-note into current note</li> <li
<li><code>F2</code> - edit <a href="#root/_help_MMiBEQljMQh2">prefix</a> of current data-list-item-id="e6064f6dd7b09083448cfb52247c94baa"><kbd>CTRL</kbd>+<kbd>P</kbd> creates new sub-note into current note</li>
note clone</li> <li
</ul> data-list-item-id="e191cb0e7231c4439632d70da65dcf800"><kbd>F2</kbd> edit&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/IakOLONlIfGI/_help_TBwsyfadTA18">Branch prefix</a>&nbsp;of
<h2>Moving / cloning notes</h2> current note clone</li>
<ul>
<li><kbd>Ctrl</kbd> + <kbd><span></span></kbd> , Ctrl + <kbd><span></span></kbd> -
move note up/down in the note list</li>
<li><kbd>Ctrl</kbd> + <kbd><span></span></kbd> - move note up in the note tree</li>
<li><kbd>Ctrl</kbd>+<kbd><span></span></kbd> - move note down in the note
tree</li>
<li><kbd>Shift</kbd>+<kbd><span></span></kbd>, <kbd>Shift</kbd><code>+</code><kbd><span></span></kbd> -
multi-select note above/below</li>
<li><kbd>Ctrl</kbd>+<kbd>A</kbd> - select all notes in the current level</li>
<li><kbd>Shift</kbd>+<kbd>🖱 Left click</kbd> - multi select note which you
clicked on</li>
<li><kbd>Ctrl</kbd>+<kbd>C</kbd> - copies current note (or current selection)
into clipboard (used for <a href="#root/_help_IakOLONlIfGI">cloning</a>
</li>
<li><kbd>Ctrl</kbd>+<kbd>X</kbd> - cuts current (or current selection) note
into clipboard (used for moving notes)</li>
<li><kbd>Ctrl</kbd>+<kbd>V</kbd> - pastes note(s) as sub-note into current
note (which is either move or clone depending on whether it was copied
or cut into clipboard)</li>
<li><kbd>Del</kbd> - delete note / sub-tree</li>
</ul> </ul>
<h2>Editing notes</h2> <h2>Editing notes</h2>
<aside class="admonition note"> <aside class="admonition note">
@ -77,30 +58,32 @@
class="reference-link" href="#root/_help_QrtTYPmdd1qq">Markdown-like formatting</a>.</p> class="reference-link" href="#root/_help_QrtTYPmdd1qq">Markdown-like formatting</a>.</p>
</aside> </aside>
<ul> <ul>
<li><kbd>Enter</kbd> in tree pane switches from tree pane into note title. <li data-list-item-id="eea3611b470b2482ccf07e8844e4f66b9"><kbd>Enter</kbd> in tree pane switches from tree pane into note title.
Enter from note title switches focus to text editor. <kbd>Ctrl</kbd>+<kbd>.</kbd> switches Enter from note title switches focus to text editor. <kbd>Ctrl</kbd>+<kbd>.</kbd> switches
back from editor to tree pane.</li> back from editor to tree pane.</li>
<li><kbd>Ctrl</kbd>+<kbd>.</kbd> - jump away from the editor to tree pane and <li data-list-item-id="e5dab831910ac694ccef7bc474b2e67fc"><kbd>Ctrl</kbd>+<kbd>.</kbd> jump away from the editor to tree pane and
scroll to current note</li> scroll to current note</li>
</ul> </ul>
<h2>Runtime shortcuts</h2> <h2>Runtime shortcuts</h2>
<p>These are hooked in Electron to be similar to native browser keyboard <p>These are hooked in Electron to be similar to native browser keyboard
shortcuts.</p> shortcuts.</p>
<ul> <ul>
<li><kbd>F5</kbd>, <kbd>Ctrl</kbd>-<kbd>R</kbd> - reloads Trilium front-end</li> <li data-list-item-id="e1a17ce297e78109362180536b7eabd68"><kbd>F5</kbd>, <kbd>Ctrl</kbd>+<kbd>R</kbd> reloads Trilium front-end</li>
<li><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> - show developer tools</li> <li
<li><kbd>Ctrl</kbd>+<kbd>F</kbd> - show search dialog</li> data-list-item-id="e07d1b25dc46ce0b0bd76a8db13373198"><kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> show developer tools</li>
<li><kbd>Ctrl</kbd>+<kbd>-</kbd> - zoom out</li> <li
<li><kbd>Ctrl</kbd>+<kbd>=</kbd> - zoom in</li> data-list-item-id="e7dc66078ba520c48364952f2ac48aa79"><kbd>Ctrl</kbd>+<kbd>F</kbd> show search dialog</li>
<li data-list-item-id="e63161bd165a51e19fd04dd1223173e68"><kbd>Ctrl</kbd>+<kbd>-</kbd> zoom out</li>
<li data-list-item-id="e961144d0a57b51792830a2182459c8cf"><kbd>Ctrl</kbd>+<kbd>=</kbd> zoom in</li>
</ul> </ul>
<h2>Other</h2> <h2>Other</h2>
<ul> <ul>
<li><kbd>Alt</kbd>+<kbd>O</kbd> - show SQL console (use only if you know what <li data-list-item-id="eb007c4ea2f2cd6eefbc8972d538c724c"><kbd>Alt</kbd> + <kbd>O</kbd> show SQL console (use only if you know what
you're doing)</li> you're doing)</li>
<li><kbd>Alt</kbd>+<kbd>M</kbd> - distraction-free mode - display only note <li data-list-item-id="e9caedb43e6c66fc7c57b4899bd6d5d76"><kbd>Alt</kbd> + <kbd>M</kbd> distraction-free mode - display only note
editor, everything else is hidden</li> editor, everything else is hidden</li>
<li><kbd>F11</kbd> - toggle full screen</li> <li data-list-item-id="ec55bd0e5ff7da0c488a5378c0bdabed5"><kbd>F11</kbd> toggle full screen</li>
<li><kbd>Ctrl</kbd> + <kbd>S</kbd> - toggle <a href="#root/_help_eIg8jdvaoNNd">search</a> form <li data-list-item-id="e8d4ec0d76155371de5d060ad661cb19e"><kbd>Ctrl</kbd> + <kbd>S</kbd> toggle <a href="#root/_help_eIg8jdvaoNNd">search</a> form
in tree pane</li> in tree pane</li>
<li><kbd>Alt</kbd> +<kbd>A</kbd> - show note <a href="#root/_help_zEY4DaJG4YT5">attributes</a> dialog</li> <li data-list-item-id="eb938081f527f44b158be7c3ed4af4925"><kbd>Alt</kbd> +<kbd>A</kbd> show note <a href="#root/_help_zEY4DaJG4YT5">attributes</a> dialog</li>
</ul> </ul>

View File

@ -1,33 +1,53 @@
<p>The&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;comes <p>The&nbsp;<a class="reference-link" href="#root/_help_oPVyFC7WL2Lp">Note Tree</a>&nbsp;comes
with multiple keyboard shortcuts to make editing faster:</p> with multiple keyboard shortcuts to make editing faster:</p>
<h2>Navigation within the tree</h2>
<ul> <ul>
<li>Opening notes: <li data-list-item-id="eede1a5721dab5213153c6bb25bad38c9"><kbd><span></span></kbd> and <kbd><span></span></kbd> to navigate between
<ul> notes.</li>
<li><kbd>Click</kbd> to open the note in the current tab.</li> <li data-list-item-id="ec4c4ab60720b34a95f73e93223ed3628"><kbd><span></span></kbd> to collapse a note with children, or <kbd><span></span></kbd> to
<li><kbd>Ctrl</kbd>+<kbd>Click</kbd> or <kbd>Middle click</kbd> to open the note expand it.</li>
in a new tab.</li> <li data-list-item-id="eb5ad59d78e70611d0233ffbb5ede3b96"><kbd><span></span></kbd> on a note with no children to navigate to its
<li><kbd>Ctrl</kbd>+<kbd>Right click</kbd> to open the note in&nbsp;<a class="reference-link" parent.</li>
href="#root/_help_ZjLYv08Rp3qC">Quick edit</a>.</li> </ul>
</ul> <h2>Opening notes</h2>
</li> <ul>
<li>Navigation within the tree: <li data-list-item-id="e3d92e8c3a1e5b9d6f2efe5067004ef5c"><kbd>Click</kbd> to open the note in the current tab.</li>
<ul> <li data-list-item-id="e8ae44ff429a0da1d529c627874f54908"><kbd>Ctrl</kbd>+<kbd>Click</kbd> or <kbd>Middle click</kbd> to open the note
<li><kbd>Up</kbd> and <kbd>Down</kbd> to navigate between notes.</li> in a new tab.</li>
<li><kbd>Left</kbd> to collapse a note, or <kbd>Right</kbd> to expand it.</li> <li data-list-item-id="e183b1a014e2bd563450dac0c8913c6f0"><kbd>Ctrl</kbd>+<kbd>Right click</kbd> to open the note in&nbsp;<a class="reference-link"
</ul> href="#root/_help_ZjLYv08Rp3qC">Quick edit</a>.</li>
</li> </ul>
<li>Clipboard management: <h2>Clipboard management</h2>
<ul> <ul>
<li><kbd>Ctrl</kbd>+<kbd>C</kbd> to copy a note.</li> <li data-list-item-id="e28fb424bcc0d06b97ddcbcbc5145c350"><kbd>Ctrl</kbd>+<kbd>C</kbd> to copy one or more notes based on selection
<li><kbd>Ctrl</kbd>+<kbd>X</kbd> to cut a note.</li> (see&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/BFs8mudNFgCS/_help_IakOLONlIfGI">Cloning Notes</a>).</li>
<li><kbd>Ctrl</kbd>+<kbd>V</kbd> to paste it somewhere.</li> <li
</ul> data-list-item-id="e11c4c9fcdfeb566a58d9319a340ff893"><kbd>Ctrl</kbd>+<kbd>X</kbd> to cut one or more notes (for moving them).</li>
</li> <li
<li>For&nbsp;<a class="reference-link" href="#root/_help_yTjUdsOi4CIE">Multiple selection</a>: data-list-item-id="e242519cf16b3414242c4d11f20688b5b"><kbd>Ctrl</kbd>+<kbd>V</kbd> to paste them somewhere (which results in
<ul> a copy or move based on the shortcut used).</li>
<li><kbd>Alt</kbd>+<kbd>Click</kbd>to add a single note to the current selection.</li> </ul>
<li><kbd>Shift</kbd>+<kbd>Click</kbd>to select a range of notes, starting <h2>Moving notes</h2>
from the current note (the highlighted one) to the one that is being clicked.</li> <ul>
</ul> <li data-list-item-id="ee2d0e5633e0ab23d0fcde7b968731794"><kbd>Ctrl</kbd> + <kbd><span></span></kbd> , <kbd>Ctrl</kbd> + <kbd><span></span></kbd> -
</li> move note up/down in the note list.</li>
<li data-list-item-id="e14146e7aacfc92aa7daf8215ce4b379c"><kbd>Ctrl</kbd> + <kbd><span></span></kbd> - move note up in the note tree.</li>
<li
data-list-item-id="e21a7f5ab9d62af04b6ab81b0cc4cf8c7"><kbd>Ctrl</kbd>+<kbd><span></span></kbd> - move note down in the note
tree.</li>
<li data-list-item-id="e99f4b0aef9693ad25ca0ce62c5b67418"><kbd>Del</kbd> - deletes note and optionally its subtree (asked in the
dialog).</li>
</ul>
<h2>Multiple selection</h2>
<p>See&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/oPVyFC7WL2Lp/_help_yTjUdsOi4CIE">Multiple selection</a>&nbsp;for
more information about how selection works.</p>
<ul>
<li data-list-item-id="ebe4907d702229ab599e50f753d23cfb6"><kbd>Alt</kbd>+<kbd>Click</kbd> add a single note to the current selection.</li>
<li
data-list-item-id="ecf6bb49f8d993d376382dcab4e4a7ee8"><kbd>Shift</kbd>+<kbd>Click</kbd> select a range of notes, starting from
the current note (the highlighted one) to the one that is being clicked.</li>
<li
data-list-item-id="eb1398c2cf85b53eafc3b7ab7a0974668"><kbd>Shift</kbd>+<kbd><span></span></kbd>, <kbd>Shift</kbd>+<kbd><span></span></kbd>
multi-select not above/below.</li>
<li data-list-item-id="ebc1274f576f67d08a6d665a7318d80a8"><kbd>Ctrl</kbd>+<kbd>A</kbd> select all notes in the current level</li>
</ul> </ul>

View File

@ -12,9 +12,9 @@
as a single continuous document.</p> as a single continuous document.</p>
<h2>Interaction</h2> <h2>Interaction</h2>
<ul> <ul>
<li data-list-item-id="e8a9709d9f325dab560cb24289d5ab231">Each note can be expanded or collapsed by clicking on the arrow to the <li>Each note can be expanded or collapsed by clicking on the arrow to the
left of the title.</li> left of the title.</li>
<li data-list-item-id="e38647a2388fb38e1c6de17b617db9e76">In the&nbsp;<a class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>, <li>In the&nbsp;<a class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>,
in the <em>Collection</em> tab there are options to expand and to collapse in the <em>Collection</em> tab there are options to expand and to collapse
all notes easily.</li> all notes easily.</li>
</ul> </ul>
@ -25,16 +25,15 @@
<p>If exported to PDF within the desktop application, there is additional <p>If exported to PDF within the desktop application, there is additional
functionality:</p> functionality:</p>
<ul> <ul>
<li data-list-item-id="e06095ff0a3876a29bdc11f1a2f18dfdc">The table of contents of the PDF will reflect the structure of the notes.</li> <li>The table of contents of the PDF will reflect the structure of the notes.</li>
<li <li>Reference and inline links to other notes within the same hierarchy will
data-list-item-id="e656526a5aa019d58af324ae5c3d39216">Reference and inline links to other notes within the same hierarchy will
be functional (will jump to the corresponding page). If a link refers to be functional (will jump to the corresponding page). If a link refers to
a note that is not in the printed hierarchy, it will be unlinked.</li> a note that is not in the printed hierarchy, it will be unlinked.</li>
</ul> </ul>
<h2>Expanding and collapsing multiple notes at once</h2> <h2>Expanding and collapsing multiple notes at once</h2>
<p>Apart from individually expanding or collapsing notes, it's also possible <p>Apart from individually expanding or collapsing notes, it's also possible
to expand or collapse them all at once. To do so, go to the <em>Collection Properties</em> tab to expand or collapse them all at once. To do so, go to the <em>Collection Properties</em> tab
in the&nbsp;<a class="reference-link" href="#root/pOsGYCXsbNQG/gh7bpGYxajRS/Vc8PjrjAGuOp/_help_BlN9DFI679QC">Ribbon</a>&nbsp;and in the&nbsp;<a class="reference-link" href="#root/_help_BlN9DFI679QC">Ribbon</a>&nbsp;and
look for the corresponding button.</p> look for the corresponding button.</p>
<p>By default, the <em>Expand</em> button will only expand the direct children <p>By default, the <em>Expand</em> button will only expand the direct children
(first level) of the collection. Starting with v0.100.0, it's possible (first level) of the collection. Starting with v0.100.0, it's possible
@ -45,7 +44,7 @@
<aside class="admonition tip"> <aside class="admonition tip">
<p>By design, the UI provides only a handful of levels of depth for expanding <p>By design, the UI provides only a handful of levels of depth for expanding
notes (direct children, 2-5, all levels). It's also possible to specify notes (direct children, 2-5, all levels). It's also possible to specify
any desired depth by manually setting the <a href="#root/pOsGYCXsbNQG/tC7s2alapj8V/zEY4DaJG4YT5/_help_HI6GBBIduIgv">corresponding label</a>. any desired depth by manually setting the <a href="#root/_help_HI6GBBIduIgv">corresponding label</a>.
For example: <code>#expanded=100</code> to expand up to 100 levels of depth.</p> For example: <code>#expanded=100</code> to expand up to 100 levels of depth.</p>
</aside> </aside>
<aside class="admonition note"> <aside class="admonition note">

View File

@ -1,5 +1,5 @@
# Documentation # Documentation
There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/VUKpkCok5Zh4/Documentation_image.png" width="205" height="162"> There are multiple types of documentation for Trilium:<img class="image-style-align-right" src="api/images/Xtlv3JlumknM/Documentation_image.png" width="205" height="162">
* The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>. * The _User Guide_ represents the user-facing documentation. This documentation can be browsed by users directly from within Trilium, by pressing <kbd>F1</kbd>.
* The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers. * The _Developer's Guide_ represents a set of Markdown documents that present the internals of Trilium, for developers.

View File

@ -2380,13 +2380,6 @@
"isInheritable": false, "isInheritable": false,
"position": 20 "position": 20
}, },
{
"type": "relation",
"name": "internalLink",
"value": "yTjUdsOi4CIE",
"isInheritable": false,
"position": 30
},
{ {
"type": "label", "type": "label",
"name": "iconClass", "name": "iconClass",
@ -2400,6 +2393,20 @@
"value": "keyboard-shortcuts", "value": "keyboard-shortcuts",
"isInheritable": false, "isInheritable": false,
"position": 40 "position": 40
},
{
"type": "relation",
"name": "internalLink",
"value": "IakOLONlIfGI",
"isInheritable": false,
"position": 50
},
{
"type": "relation",
"name": "internalLink",
"value": "yTjUdsOi4CIE",
"isInheritable": false,
"position": 60
} }
], ],
"format": "markdown", "format": "markdown",
@ -5211,13 +5218,6 @@
"isInheritable": false, "isInheritable": false,
"position": 20 "position": 20
}, },
{
"type": "relation",
"name": "internalLink",
"value": "IakOLONlIfGI",
"isInheritable": false,
"position": 30
},
{ {
"type": "relation", "type": "relation",
"name": "internalLink", "name": "internalLink",
@ -5259,6 +5259,20 @@
"value": "bx bxs-keyboard", "value": "bx bxs-keyboard",
"isInheritable": false, "isInheritable": false,
"position": 80 "position": 80
},
{
"type": "relation",
"name": "internalLink",
"value": "DvdZhoQZY9Yd",
"isInheritable": false,
"position": 90
},
{
"type": "relation",
"name": "internalLink",
"value": "TBwsyfadTA18",
"isInheritable": false,
"position": 100
} }
], ],
"format": "markdown", "format": "markdown",
@ -10576,6 +10590,13 @@
"isInheritable": false, "isInheritable": false,
"position": 30 "position": 30
}, },
{
"type": "relation",
"name": "internalLink",
"value": "HI6GBBIduIgv",
"isInheritable": false,
"position": 40
},
{ {
"type": "label", "type": "label",
"name": "iconClass", "name": "iconClass",
@ -10589,13 +10610,6 @@
"value": "list", "value": "list",
"isInheritable": false, "isInheritable": false,
"position": 30 "position": 30
},
{
"type": "relation",
"name": "internalLink",
"value": "HI6GBBIduIgv",
"isInheritable": false,
"position": 40
} }
], ],
"format": "markdown", "format": "markdown",

View File

@ -3,49 +3,38 @@ This is supposed to be a complete list of keyboard shortcuts. Note that some of
It is also possible to configure most keyboard shortcuts in Options -> Keyboard shortcuts. Using `global:` prefix, you can assign a shortcut which will work even without Trilium being in focus (requires app restart to take effect). It is also possible to configure most keyboard shortcuts in Options -> Keyboard shortcuts. Using `global:` prefix, you can assign a shortcut which will work even without Trilium being in focus (requires app restart to take effect).
## Tree
See the corresponding section: <a class="reference-link" href="UI%20Elements/Note%20Tree/Keyboard%20shortcuts.md">Keyboard shortcuts</a>
## Note navigation ## Note navigation
* <kbd><span></span></kbd>, <kbd><span></span></kbd> - go up/down in the list of notes, <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd><span></span></kbd> and <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd><span></span></kbd>  work also from editor * <kbd>Alt</kbd> + <kbd><span></span></kbd>, <kbd>Alt</kbd> + <kbd><span></span></kbd> go back / forwards in the history
* <kbd><span></span></kbd>, <kbd><span></span></kbd> - collapse/expand node * <kbd>Ctrl</kbd> + <kbd>J</kbd> show ["Jump to" dialog](Navigation/Note%20Navigation.md)
* <kbd>Alt</kbd> + <kbd><span></span></kbd>, <kbd>Alt</kbd> + <kbd><span></span></kbd> - go back / forwards in the history * <kbd>Ctrl</kbd> + <kbd>.</kbd> scroll to current note (useful when you scroll away from your note or your focus is currently in the editor)
* <kbd>Ctrl</kbd> + <kbd>J</kbd> - show ["Jump to" dialog](Navigation/Note%20Navigation.md) * <kbd><span>Backspace</span></kbd> jumps to parent note
* <kbd>Ctrl</kbd> + <kbd>.</kbd> - scroll to current note (useful when you scroll away from your note or your focus is currently in the editor) * <kbd>Alt</kbd> + <kbd>C</kbd> collapse whole note tree
* <kbd><span>Backspace</span></kbd> - jumps to parent note * <kbd>Alt</kbd> + <kbd>-</kbd> (alt with minus sign) collapse subtree (if some subtree takes too much space on tree pane you can collapse it)
* <kbd>Alt</kbd> + <kbd>C</kbd> - collapse whole note tree
* <kbd>Alt</kbd> + <kbd>-</kbd> (alt with minus sign) - collapse subtree (if some subtree takes too much space on tree pane you can collapse it)
* you can define a [label](../Advanced%20Usage/Attributes.md) `#keyboardShortcut` with e.g. value <kbd>Ctrl</kbd> + <kbd>I</kbd> . Pressing this keyboard combination will then bring you to the note on which it is defined. Note that Trilium must be reloaded/restarted (<kbd>Ctrl</kbd> + <kbd>R</kbd> ) for changes to be in effect. * you can define a [label](../Advanced%20Usage/Attributes.md) `#keyboardShortcut` with e.g. value <kbd>Ctrl</kbd> + <kbd>I</kbd> . Pressing this keyboard combination will then bring you to the note on which it is defined. Note that Trilium must be reloaded/restarted (<kbd>Ctrl</kbd> + <kbd>R</kbd> ) for changes to be in effect.
See demo of some of these features in [note navigation](Navigation/Note%20Navigation.md). See demo of some of these features in [note navigation](Navigation/Note%20Navigation.md).
## Tabs ## Tabs
* <kbd>Ctrl</kbd> + <kbd>🖱 Left click</kbd> - (or middle mouse click) on note link opens note in a new tab * <kbd>Ctrl</kbd> + <kbd>🖱 Left click</kbd> (or middle mouse click) on note link opens note in a new tab
Only in desktop (electron build): Only in desktop (electron build):
* <kbd>Ctrl</kbd> + <kbd>T</kbd> - opens empty tab * <kbd>Ctrl</kbd> + <kbd>T</kbd> opens empty tab
* <kbd>Ctrl</kbd> + <kbd>W</kbd> - closes active tab * <kbd>Ctrl</kbd> + <kbd>W</kbd> closes active tab
* <kbd>Ctrl</kbd> + <kbd>Tab</kbd> - activates next tab * <kbd>Ctrl</kbd> + <kbd>Tab</kbd> activates next tab
* <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd> - activates previous tab * <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd> activates previous tab
## Creating notes ## Creating notes
* `CTRL+O` - creates new note after the current note * <kbd>CTRL</kbd>+<kbd>O</kbd> creates new note after the current note
* `CTRL+P` - creates new sub-note into current note * <kbd>CTRL</kbd>+<kbd>P</kbd> creates new sub-note into current note
* `F2` - edit [prefix](Navigation/Note%20Navigation.md) of current note clone * <kbd>F2</kbd> edit <a class="reference-link" href="Notes/Cloning%20Notes/Branch%20prefix.md">Branch prefix</a> of current note clone
## Moving / cloning notes
* <kbd>Ctrl</kbd> + <kbd><span></span></kbd> , Ctrl + <kbd><span></span></kbd> - move note up/down in the note list
* <kbd>Ctrl</kbd> + <kbd><span></span></kbd> - move note up in the note tree
* <kbd>Ctrl</kbd>+<kbd><span></span></kbd> - move note down in the note tree
* <kbd>Shift</kbd>+<kbd><span></span></kbd>, <kbd>Shift</kbd>`+`<kbd><span></span></kbd> - multi-select note above/below
* <kbd>Ctrl</kbd>+<kbd>A</kbd> - select all notes in the current level
* <kbd>Shift</kbd>+<kbd>🖱 Left click</kbd> - multi select note which you clicked on
* <kbd>Ctrl</kbd>+<kbd>C</kbd> - copies current note (or current selection) into clipboard (used for [cloning](Notes/Cloning%20Notes.md)
* <kbd>Ctrl</kbd>+<kbd>X</kbd> - cuts current (or current selection) note into clipboard (used for moving notes)
* <kbd>Ctrl</kbd>+<kbd>V</kbd> - pastes note(s) as sub-note into current note (which is either move or clone depending on whether it was copied or cut into clipboard)
* <kbd>Del</kbd> - delete note / sub-tree
## Editing notes ## Editing notes
@ -53,22 +42,22 @@ Only in desktop (electron build):
> For keyboard shortcuts specific to <a class="reference-link" href="../Note%20Types/Text.md">Text</a> notes, refer to <a class="reference-link" href="../Note%20Types/Text/Keyboard%20shortcuts.md">Keyboard shortcuts</a> and <a class="reference-link" href="../Note%20Types/Text/Markdown-like%20formatting.md">Markdown-like formatting</a>. > For keyboard shortcuts specific to <a class="reference-link" href="../Note%20Types/Text.md">Text</a> notes, refer to <a class="reference-link" href="../Note%20Types/Text/Keyboard%20shortcuts.md">Keyboard shortcuts</a> and <a class="reference-link" href="../Note%20Types/Text/Markdown-like%20formatting.md">Markdown-like formatting</a>.
* <kbd>Enter</kbd> in tree pane switches from tree pane into note title. Enter from note title switches focus to text editor. <kbd>Ctrl</kbd>+<kbd>.</kbd> switches back from editor to tree pane. * <kbd>Enter</kbd> in tree pane switches from tree pane into note title. Enter from note title switches focus to text editor. <kbd>Ctrl</kbd>+<kbd>.</kbd> switches back from editor to tree pane.
* <kbd>Ctrl</kbd>+<kbd>.</kbd> - jump away from the editor to tree pane and scroll to current note * <kbd>Ctrl</kbd>+<kbd>.</kbd> jump away from the editor to tree pane and scroll to current note
## Runtime shortcuts ## Runtime shortcuts
These are hooked in Electron to be similar to native browser keyboard shortcuts. These are hooked in Electron to be similar to native browser keyboard shortcuts.
* <kbd>F5</kbd>, <kbd>Ctrl</kbd>\-<kbd>R</kbd> - reloads Trilium front-end * <kbd>F5</kbd>, <kbd>Ctrl</kbd>+<kbd>R</kbd> reloads Trilium front-end
* <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> - show developer tools * <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>I</kbd> show developer tools
* <kbd>Ctrl</kbd>+<kbd>F</kbd> - show search dialog * <kbd>Ctrl</kbd>+<kbd>F</kbd> show search dialog
* <kbd>Ctrl</kbd>+<kbd>-</kbd> - zoom out * <kbd>Ctrl</kbd>+<kbd>-</kbd> zoom out
* <kbd>Ctrl</kbd>+<kbd>=</kbd> - zoom in * <kbd>Ctrl</kbd>+<kbd>=</kbd> zoom in
## Other ## Other
* <kbd>Alt</kbd>+<kbd>O</kbd> - show SQL console (use only if you know what you're doing) * <kbd>Alt</kbd> + <kbd>O</kbd> show SQL console (use only if you know what you're doing)
* <kbd>Alt</kbd>+<kbd>M</kbd> - distraction-free mode - display only note editor, everything else is hidden * <kbd>Alt</kbd> + <kbd>M</kbd> distraction-free mode - display only note editor, everything else is hidden
* <kbd>F11</kbd> - toggle full screen * <kbd>F11</kbd> toggle full screen
* <kbd>Ctrl</kbd> + <kbd>S</kbd> - toggle [search](Navigation/Search.md) form in tree pane * <kbd>Ctrl</kbd> + <kbd>S</kbd> toggle [search](Navigation/Search.md) form in tree pane
* <kbd>Alt</kbd> +<kbd>A</kbd> - show note [attributes](../Advanced%20Usage/Attributes.md) dialog * <kbd>Alt</kbd> +<kbd>A</kbd> show note [attributes](../Advanced%20Usage/Attributes.md) dialog

View File

@ -1,17 +1,36 @@
# Keyboard shortcuts # Keyboard shortcuts
The <a class="reference-link" href="../Note%20Tree.md">Note Tree</a> comes with multiple keyboard shortcuts to make editing faster: The <a class="reference-link" href="../Note%20Tree.md">Note Tree</a> comes with multiple keyboard shortcuts to make editing faster:
* Opening notes: ## Navigation within the tree
* <kbd>Click</kbd> to open the note in the current tab.
* <kbd>Ctrl</kbd>+<kbd>Click</kbd> or <kbd>Middle click</kbd> to open the note in a new tab. * <kbd><span></span></kbd> and <kbd><span></span></kbd> to navigate between notes.
* <kbd>Ctrl</kbd>+<kbd>Right click</kbd> to open the note in <a class="reference-link" href="../../Navigation/Quick%20edit.md">Quick edit</a>. * <kbd><span></span></kbd> to collapse a note with children, or <kbd><span></span></kbd> to expand it.
* Navigation within the tree: * <kbd><span></span></kbd> on a note with no children to navigate to its parent.
* <kbd>Up</kbd> and <kbd>Down</kbd> to navigate between notes.
* <kbd>Left</kbd> to collapse a note, or <kbd>Right</kbd> to expand it. ## Opening notes
* Clipboard management:
* <kbd>Ctrl</kbd>+<kbd>C</kbd> to copy a note. * <kbd>Click</kbd> to open the note in the current tab.
* <kbd>Ctrl</kbd>+<kbd>X</kbd> to cut a note. * <kbd>Ctrl</kbd>+<kbd>Click</kbd> or <kbd>Middle click</kbd> to open the note in a new tab.
* <kbd>Ctrl</kbd>+<kbd>V</kbd> to paste it somewhere. * <kbd>Ctrl</kbd>+<kbd>Right click</kbd> to open the note in <a class="reference-link" href="../../Navigation/Quick%20edit.md">Quick edit</a>.
* For <a class="reference-link" href="Multiple%20selection.md">Multiple selection</a>:
* <kbd>Alt</kbd>+<kbd>Click</kbd>to add a single note to the current selection. ## Clipboard management
* <kbd>Shift</kbd>+<kbd>Click</kbd>to select a range of notes, starting from the current note (the highlighted one) to the one that is being clicked.
* <kbd>Ctrl</kbd>+<kbd>C</kbd> to copy one or more notes based on selection (see <a class="reference-link" href="../../Notes/Cloning%20Notes.md">Cloning Notes</a>).
* <kbd>Ctrl</kbd>+<kbd>X</kbd> to cut one or more notes (for moving them).
* <kbd>Ctrl</kbd>+<kbd>V</kbd> to paste them somewhere (which results in a copy or move based on the shortcut used).
## Moving notes
* <kbd>Ctrl</kbd> + <kbd><span></span></kbd> , <kbd>Ctrl</kbd> + <kbd><span></span></kbd> - move note up/down in the note list.
* <kbd>Ctrl</kbd> + <kbd><span></span></kbd> - move note up in the note tree.
* <kbd>Ctrl</kbd>+<kbd><span></span></kbd> - move note down in the note tree.
* <kbd>Del</kbd> - deletes note and optionally its subtree (asked in the dialog).
## Multiple selection
See <a class="reference-link" href="Multiple%20selection.md">Multiple selection</a> for more information about how selection works.
* <kbd>Alt</kbd>+<kbd>Click</kbd> add a single note to the current selection.
* <kbd>Shift</kbd>+<kbd>Click</kbd> select a range of notes, starting from the current note (the highlighted one) to the one that is being clicked.
* <kbd>Shift</kbd>+<kbd><span></span></kbd>, <kbd>Shift</kbd>+<kbd><span></span></kbd> multi-select not above/below.
* <kbd>Ctrl</kbd>+<kbd>A</kbd> select all notes in the current level

View File

@ -71,6 +71,7 @@
] ]
}, },
"dependencies": { "dependencies": {
"@ckeditor/ckeditor5-icons": "47.2.0" "@ckeditor/ckeditor5-icons": "47.2.0",
"mathlive": "0.108.2"
} }
} }

View File

@ -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';

View File

@ -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 ?? '';

View File

@ -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;
} }
} }

View 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();
}
}

View File

@ -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
); );
} }
} }

View 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();
}
}

View File

@ -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', () => {

View File

@ -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;
} }

36
pnpm-lock.yaml generated
View File

@ -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: