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

View File

@ -5,23 +5,24 @@
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>
<li data-list-item-id="ee625a141f5298dbd52ecb6e56693e647"><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)</li> some subtree takes too much space on tree pane you can collapse it)</li>
<li>you can define a <a href="#root/_help_zEY4DaJG4YT5">label</a> <code>#keyboardShortcut</code> with <li
data-list-item-id="ec1bf00840a017ea798880470e419f3d9">you can define a <a href="#root/_help_zEY4DaJG4YT5">label</a> <code>#keyboardShortcut</code> with
e.g. value <kbd>Ctrl</kbd> + <kbd>I</kbd> . Pressing this keyboard combination 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 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 must be reloaded/restarted (<kbd>Ctrl</kbd> + <kbd>R</kbd> ) for changes to
@ -30,44 +31,24 @@
<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,
let children = []; this.rawLatexInputView,
if ( this.previewEnabled ) {
// Preview label
this.previewLabel = new LabelView( locale );
this.previewLabel.text = t( 'Equation preview' );
// Math element
this.mathView = new MathView( engine, lazyLoad, locale, previewUid, previewClassName, katexRenderOptions );
this.mathView.bind( 'display' ).to( this.displayButtonView, 'isOn' );
children = [
this.mathInputView,
this.displayButtonView,
this.previewLabel,
this.mathView
];
} else {
children = [
this.mathInputView,
this.displayButtonView this.displayButtonView
]; ];
if ( previewEnabled ) {
const previewLabel = new LabelView( locale );
previewLabel.text = t( 'Equation preview' );
// Clean instantiation using the options object
this.mathView = new MathView( locale, mathViewOptions );
// Bind display mode: When button flips, preview updates automatically
this.mathView.bind( 'display' ).to( this.displayButtonView, 'isOn' );
children.push( previewLabel, this.mathView );
} }
// 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 {
}, tag: 'div',
this.saveButtonView, attributes: { class: [ 'ck-math-button-row' ] },
this.cancelButtonView 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.mathView.value = equation;
}
}
public focusTracker: FocusTracker = new FocusTracker();
public keystrokes: KeystrokeHandler = new KeystrokeHandler();
private _focusables = new ViewCollection<FocusableView>();
private _focusCycler: FocusCycler = new FocusCycler( {
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; this.displayButtonView.isOn = params.display;
// UX Fix: If we stripped delimiters, update the source
// so the visual editor doesn't show them.
if ( this.mathLiveInputView.value !== eq ) {
this.mathLiveInputView.value = eq;
} }
if ( this.previewEnabled && this.mathView ) {
// Update preview view
this.mathView.value = equationInput;
} }
this.saveButtonView.isEnabled = !!equationInput; // Sync to Raw LaTeX
} if ( this.rawLatexInputView.value !== eq ) {
}; this.rawLatexInputView.value = eq;
fieldView.on( 'render', onInput );
fieldView.on( 'input', onInput );
return mathInput;
} }
private _createButton( // Sync to Preview
label: string, if ( previewEnabled && this.mathView && this.mathView.value !== eq ) {
icon: string, this.mathView.value = eq;
className: string,
eventName: string | null
) {
const button = new ButtonView( this.locale );
button.set( {
label,
icon,
tooltip: true
} );
button.extendTemplate( {
attributes: {
class: className
} }
} ); } );
if ( eventName ) { // Handler 2: Raw LaTeX -> MathLive
button.delegate( 'execute' ).to( this, eventName ); 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;
} }
return button; // Sync to Preview
} if ( previewEnabled && this.mathView && this.mathView.value !== eq ) {
this.mathView.value = eq;
private _createDisplayButton() {
const t = this.locale.t;
const switchButton = new SwitchButtonView( this.locale );
switchButton.set( {
label: t( 'Display mode' ),
withText: true
} );
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; private _createButton( label: string, icon: string, className: string ): ButtonView {
const btn = new ButtonView( this.locale );
btn.set( { label, icon, tooltip: true } );
btn.extendTemplate( { attributes: { class: className } } );
return btn;
}
private _createDisplayButton( t: ( str: string ) => string ): SwitchButtonView {
const btn = new SwitchButtonView( this.locale );
btn.set( { label: t( 'Display mode' ), withText: true } );
btn.extendTemplate( { attributes: { class: 'ck-button-display-toggle' } } );
btn.on( 'execute', () => {
btn.isOn = !btn.isOn;
// mathView updates automatically via bind()
} );
return btn;
} }
} }

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;
flex-wrap: nowrap;
padding: var(--ck-spacing-standard); padding: var(--ck-spacing-standard);
box-sizing: border-box;
max-width: 80vw;
max-height: 80vh;
height: 100%;
overflow-x: hidden;
}
@media screen and (max-width: 600px) { /* Mobile responsiveness */
@media screen and (max-width: 600px) {
.ck.ck-math-form {
flex-wrap: wrap; flex-wrap: wrap;
& .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 { /* ============================================================================
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; display: none !important;
} }
.ck.ck-toolbar-container { /* Textarea wrapper */
z-index: calc(var(--ck-z-panel) + 2); .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: