mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 08:46:37 -04:00
Make indent outdent only work for list items
+ make paragraph show its active state
This commit is contained in:
parent
9803bb19ca
commit
893028b2df
9 changed files with 96 additions and 71 deletions
|
@ -44,7 +44,6 @@ ts_library(
|
||||||
deps = [
|
deps = [
|
||||||
"//ts/lib",
|
"//ts/lib",
|
||||||
"//ts/lib:backend_proto",
|
"//ts/lib:backend_proto",
|
||||||
"//ts:image_module_support",
|
|
||||||
"//ts/sveltelib",
|
"//ts/sveltelib",
|
||||||
"@npm//@popperjs/core",
|
"@npm//@popperjs/core",
|
||||||
"@npm//@types/bootstrap",
|
"@npm//@types/bootstrap",
|
||||||
|
|
8
ts/editor-toolbar/CommandIconButton.d.ts
vendored
8
ts/editor-toolbar/CommandIconButton.d.ts
vendored
|
@ -5,6 +5,12 @@ export interface CommandIconButtonProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
tooltip: string;
|
tooltip: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
|
|
||||||
command: string;
|
command: string;
|
||||||
activatable?: boolean;
|
onClick: (event: MouseEvent) => void;
|
||||||
|
|
||||||
|
onUpdate: (event: Event) => boolean;
|
||||||
|
|
||||||
|
disables?: boolean;
|
||||||
|
dropdownToggle?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
type ActiveMap = Map<string, boolean>;
|
type ActiveMap = Map<string, boolean>;
|
||||||
|
|
||||||
const updateMap = new Map() as UpdateMap;
|
const updateMap = new Map() as UpdateMap;
|
||||||
const activeMap = writable(new Map() as ActiveMap);
|
const activeMap = new Map() as ActiveMap;
|
||||||
|
const activeStore = writable(activeMap);
|
||||||
|
|
||||||
function updateButton(key: string, event: MouseEvent): void {
|
function updateButton(key: string, event: MouseEvent): void {
|
||||||
activeMap.update(
|
activeStore.update(
|
||||||
(map: ActiveMap): ActiveMap =>
|
(map: ActiveMap): ActiveMap =>
|
||||||
new Map([...map, [key, updateMap.get(key)(event)]])
|
new Map([...map, [key, updateMap.get(key)(event)]])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateButtons(callback: (key: string) => boolean): void {
|
function updateButtons(callback: (key: string) => boolean): void {
|
||||||
activeMap.update(
|
activeStore.update(
|
||||||
(map: ActiveMap): ActiveMap => {
|
(map: ActiveMap): ActiveMap => {
|
||||||
const newMap = new Map() as ActiveMap;
|
const newMap = new Map() as ActiveMap;
|
||||||
|
|
||||||
|
@ -50,7 +51,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
export let icon: string;
|
export let icon: string;
|
||||||
|
|
||||||
export let command: string;
|
export let command: string;
|
||||||
export let onClick = () => {
|
export let onClick = (_event: MouseEvent) => {
|
||||||
document.execCommand(command);
|
document.execCommand(command);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,19 +60,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
updateButton(command, event);
|
updateButton(command, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
export let activatable = true;
|
|
||||||
export let onUpdate = (_event: Event) => document.queryCommandState(command);
|
export let onUpdate = (_event: Event) => document.queryCommandState(command);
|
||||||
|
|
||||||
updateMap.set(command, onUpdate);
|
updateMap.set(command, onUpdate);
|
||||||
|
|
||||||
let active = false;
|
let active = false;
|
||||||
|
|
||||||
if (activatable) {
|
activeStore.subscribe((map: ActiveMap): (() => void) => {
|
||||||
activeMap.subscribe((map: ActiveMap): (() => void) => {
|
active = Boolean(map.get(command));
|
||||||
active = Boolean(map.get(command));
|
return () => map.delete(command);
|
||||||
return () => map.delete(command);
|
});
|
||||||
});
|
activeMap.set(command, active);
|
||||||
}
|
|
||||||
|
|
||||||
export let disables = true;
|
export let disables = true;
|
||||||
export let dropdownToggle = false;
|
export let dropdownToggle = false;
|
||||||
|
|
|
@ -8,11 +8,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
export let id: string;
|
export let id: string;
|
||||||
export let className = "";
|
export let className = "";
|
||||||
export let tooltip: string;
|
export let tooltip: string;
|
||||||
|
export let icon: string;
|
||||||
|
|
||||||
|
export let onClick: (event: MouseEvent) => void;
|
||||||
|
|
||||||
export let disables = true;
|
export let disables = true;
|
||||||
export let dropdownToggle = false;
|
export let dropdownToggle = false;
|
||||||
|
|
||||||
export let icon = "";
|
|
||||||
export let onClick: (event: MouseEvent) => void;
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SquareButton {id} {className} {tooltip} {onClick} {disables} {dropdownToggle} on:mount>
|
<SquareButton {id} {className} {tooltip} {onClick} {disables} {dropdownToggle} on:mount>
|
||||||
|
|
|
@ -12,9 +12,13 @@ import type { CommandIconButtonProps } from "editor-toolbar/CommandIconButton";
|
||||||
import IconButton from "editor-toolbar/IconButton.svelte";
|
import IconButton from "editor-toolbar/IconButton.svelte";
|
||||||
import type { IconButtonProps } from "editor-toolbar/IconButton";
|
import type { IconButtonProps } from "editor-toolbar/IconButton";
|
||||||
|
|
||||||
|
import type { EditingArea } from "./editingArea";
|
||||||
|
|
||||||
import { DynamicSvelteComponent, dynamicComponent } from "sveltelib/dynamicComponent";
|
import { DynamicSvelteComponent, dynamicComponent } from "sveltelib/dynamicComponent";
|
||||||
import * as tr from "anki/i18n";
|
import * as tr from "anki/i18n";
|
||||||
|
|
||||||
|
import { getListItem, getParagraph } from "./helpers";
|
||||||
|
|
||||||
import paragraphIcon from "./paragraph.svg";
|
import paragraphIcon from "./paragraph.svg";
|
||||||
import ulIcon from "./list-ul.svg";
|
import ulIcon from "./list-ul.svg";
|
||||||
import olIcon from "./list-ol.svg";
|
import olIcon from "./list-ol.svg";
|
||||||
|
@ -33,6 +37,8 @@ const commandIconButton = dynamicComponent<
|
||||||
CommandIconButtonProps
|
CommandIconButtonProps
|
||||||
>(CommandIconButton);
|
>(CommandIconButton);
|
||||||
|
|
||||||
|
const iconButton = dynamicComponent<typeof IconButton, IconButtonProps>(IconButton);
|
||||||
|
|
||||||
const buttonGroup = dynamicComponent<typeof ButtonGroup, ButtonGroupProps>(ButtonGroup);
|
const buttonGroup = dynamicComponent<typeof ButtonGroup, ButtonGroupProps>(ButtonGroup);
|
||||||
const buttonDropdown = dynamicComponent<typeof ButtonDropdown, ButtonDropdownProps>(
|
const buttonDropdown = dynamicComponent<typeof ButtonDropdown, ButtonDropdownProps>(
|
||||||
ButtonDropdown
|
ButtonDropdown
|
||||||
|
@ -43,6 +49,25 @@ const withDropdownMenu = dynamicComponent<
|
||||||
WithDropdownMenuProps
|
WithDropdownMenuProps
|
||||||
>(WithDropdownMenu);
|
>(WithDropdownMenu);
|
||||||
|
|
||||||
|
const outdentListItem = () => {
|
||||||
|
const currentField = document.activeElement as EditingArea;
|
||||||
|
if (getListItem(currentField.shadowRoot!)) {
|
||||||
|
document.execCommand("outdent");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const indentListItem = () => {
|
||||||
|
const currentField = document.activeElement as EditingArea;
|
||||||
|
if (getListItem(currentField.shadowRoot!)) {
|
||||||
|
document.execCommand("indent");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkForParagraph = (): boolean => {
|
||||||
|
const currentField = document.activeElement as EditingArea;
|
||||||
|
return Boolean(getParagraph(currentField.shadowRoot!));
|
||||||
|
};
|
||||||
|
|
||||||
export function getFormatBlockMenus(): (DynamicSvelteComponent<typeof ButtonDropdown> &
|
export function getFormatBlockMenus(): (DynamicSvelteComponent<typeof ButtonDropdown> &
|
||||||
ButtonDropdownProps)[] {
|
ButtonDropdownProps)[] {
|
||||||
const justifyLeftButton = commandIconButton({
|
const justifyLeftButton = commandIconButton({
|
||||||
|
@ -79,18 +104,16 @@ export function getFormatBlockMenus(): (DynamicSvelteComponent<typeof ButtonDrop
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const outdentButton = commandIconButton({
|
const outdentButton = iconButton({
|
||||||
icon: outdentIcon,
|
icon: outdentIcon,
|
||||||
command: "outdent",
|
onClick: outdentListItem,
|
||||||
tooltip: tr.editingOutdent(),
|
tooltip: tr.editingOutdent(),
|
||||||
activatable: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const indentButton = commandIconButton({
|
const indentButton = iconButton({
|
||||||
icon: indentIcon,
|
icon: indentIcon,
|
||||||
command: "indent",
|
onClick: indentListItem,
|
||||||
tooltip: tr.editingIndent(),
|
tooltip: tr.editingIndent(),
|
||||||
activatable: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const indentationGroup = buttonGroup({
|
const indentationGroup = buttonGroup({
|
||||||
|
@ -106,8 +129,6 @@ export function getFormatBlockMenus(): (DynamicSvelteComponent<typeof ButtonDrop
|
||||||
return [formattingOptions];
|
return [formattingOptions];
|
||||||
}
|
}
|
||||||
|
|
||||||
const iconButton = dynamicComponent<typeof IconButton, IconButtonProps>(IconButton);
|
|
||||||
|
|
||||||
export function getFormatBlockGroup(): DynamicSvelteComponent<typeof ButtonGroup> &
|
export function getFormatBlockGroup(): DynamicSvelteComponent<typeof ButtonGroup> &
|
||||||
ButtonGroupProps {
|
ButtonGroupProps {
|
||||||
const paragraphButton = commandIconButton({
|
const paragraphButton = commandIconButton({
|
||||||
|
@ -116,8 +137,8 @@ export function getFormatBlockGroup(): DynamicSvelteComponent<typeof ButtonGroup
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
document.execCommand("formatBlock", false, "p");
|
document.execCommand("formatBlock", false, "p");
|
||||||
},
|
},
|
||||||
|
onUpdate: checkForParagraph,
|
||||||
tooltip: tr.editingUnorderedList(),
|
tooltip: tr.editingUnorderedList(),
|
||||||
activatable: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const ulButton = commandIconButton({
|
const ulButton = commandIconButton({
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
import CommandIconButton from "editor-toolbar/CommandIconButton.svelte";
|
import CommandIconButton from "editor-toolbar/CommandIconButton.svelte";
|
||||||
import type { CommandIconButtonProps } from "editor-toolbar/CommandIconButton";
|
import type { CommandIconButtonProps } from "editor-toolbar/CommandIconButton";
|
||||||
|
import IconButton from "editor-toolbar/IconButton.svelte";
|
||||||
|
import type { IconButtonProps } from "editor-toolbar/IconButton";
|
||||||
import ButtonGroup from "editor-toolbar/ButtonGroup.svelte";
|
import ButtonGroup from "editor-toolbar/ButtonGroup.svelte";
|
||||||
import type { ButtonGroupProps } from "editor-toolbar/ButtonGroup";
|
import type { ButtonGroupProps } from "editor-toolbar/ButtonGroup";
|
||||||
|
|
||||||
|
@ -19,45 +21,47 @@ const commandIconButton = dynamicComponent<
|
||||||
typeof CommandIconButton,
|
typeof CommandIconButton,
|
||||||
CommandIconButtonProps
|
CommandIconButtonProps
|
||||||
>(CommandIconButton);
|
>(CommandIconButton);
|
||||||
|
const iconButton = dynamicComponent<typeof IconButton, IconButtonProps>(IconButton);
|
||||||
const buttonGroup = dynamicComponent<typeof ButtonGroup, ButtonGroupProps>(ButtonGroup);
|
const buttonGroup = dynamicComponent<typeof ButtonGroup, ButtonGroupProps>(ButtonGroup);
|
||||||
|
|
||||||
export function getFormatInlineGroup(): DynamicSvelteComponent<typeof ButtonGroup> &
|
export function getFormatInlineGroup(): DynamicSvelteComponent<typeof ButtonGroup> &
|
||||||
ButtonGroupProps {
|
ButtonGroupProps {
|
||||||
const boldButton = commandIconButton({
|
const boldButton = commandIconButton({
|
||||||
icon: boldIcon,
|
icon: boldIcon,
|
||||||
command: "bold",
|
|
||||||
tooltip: tr.editingBoldTextCtrlandb(),
|
tooltip: tr.editingBoldTextCtrlandb(),
|
||||||
|
command: "bold",
|
||||||
});
|
});
|
||||||
|
|
||||||
const italicButton = commandIconButton({
|
const italicButton = commandIconButton({
|
||||||
icon: italicIcon,
|
icon: italicIcon,
|
||||||
command: "italic",
|
|
||||||
tooltip: tr.editingItalicTextCtrlandi(),
|
tooltip: tr.editingItalicTextCtrlandi(),
|
||||||
|
command: "italic",
|
||||||
});
|
});
|
||||||
|
|
||||||
const underlineButton = commandIconButton({
|
const underlineButton = commandIconButton({
|
||||||
icon: underlineIcon,
|
icon: underlineIcon,
|
||||||
command: "underline",
|
|
||||||
tooltip: tr.editingUnderlineTextCtrlandu(),
|
tooltip: tr.editingUnderlineTextCtrlandu(),
|
||||||
|
command: "underline",
|
||||||
});
|
});
|
||||||
|
|
||||||
const superscriptButton = commandIconButton({
|
const superscriptButton = commandIconButton({
|
||||||
icon: superscriptIcon,
|
icon: superscriptIcon,
|
||||||
command: "superscript",
|
|
||||||
tooltip: tr.editingSuperscriptCtrlandand(),
|
tooltip: tr.editingSuperscriptCtrlandand(),
|
||||||
|
command: "superscript",
|
||||||
});
|
});
|
||||||
|
|
||||||
const subscriptButton = commandIconButton({
|
const subscriptButton = commandIconButton({
|
||||||
icon: subscriptIcon,
|
icon: subscriptIcon,
|
||||||
command: "subscript",
|
|
||||||
tooltip: tr.editingSubscriptCtrland(),
|
tooltip: tr.editingSubscriptCtrland(),
|
||||||
|
command: "subscript",
|
||||||
});
|
});
|
||||||
|
|
||||||
const removeFormatButton = commandIconButton({
|
const removeFormatButton = iconButton({
|
||||||
icon: eraserIcon,
|
icon: eraserIcon,
|
||||||
command: "removeFormat",
|
|
||||||
activatable: false,
|
|
||||||
tooltip: tr.editingRemoveFormattingCtrlandr(),
|
tooltip: tr.editingRemoveFormattingCtrlandr(),
|
||||||
|
onClick: () => {
|
||||||
|
document.execCommand("removeFormat");
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return buttonGroup({
|
return buttonGroup({
|
||||||
|
|
|
@ -77,3 +77,31 @@ export function caretToEnd(currentField: EditingArea): void {
|
||||||
selection.removeAllRanges();
|
selection.removeAllRanges();
|
||||||
selection.addRange(range);
|
selection.addRange(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getAnchorParent = <T extends Element>(
|
||||||
|
predicate: (element: Element) => element is T
|
||||||
|
) => (currentField: DocumentOrShadowRoot): T | null => {
|
||||||
|
const anchor = currentField.getSelection()?.anchorNode;
|
||||||
|
|
||||||
|
if (!anchor) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let anchorParent: T | null = null;
|
||||||
|
let element = nodeIsElement(anchor) ? anchor : anchor.parentElement;
|
||||||
|
|
||||||
|
while (element) {
|
||||||
|
anchorParent = anchorParent || (predicate(element) ? element : null);
|
||||||
|
element = element.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
return anchorParent;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isListItem = (element: Element): element is HTMLLIElement =>
|
||||||
|
window.getComputedStyle(element).display === "list-item";
|
||||||
|
const isParagraph = (element: Element): element is HTMLParamElement =>
|
||||||
|
element.tagName === "P";
|
||||||
|
|
||||||
|
export const getListItem = getAnchorParent(isListItem);
|
||||||
|
export const getParagraph = getAnchorParent(isParagraph);
|
||||||
|
|
|
@ -3,38 +3,9 @@
|
||||||
|
|
||||||
import { updateActiveButtons } from "editor-toolbar";
|
import { updateActiveButtons } from "editor-toolbar";
|
||||||
import { EditingArea } from "./editingArea";
|
import { EditingArea } from "./editingArea";
|
||||||
import { caretToEnd, nodeIsElement } from "./helpers";
|
import { caretToEnd, nodeIsElement, getListItem, getParagraph } from "./helpers";
|
||||||
import { triggerChangeTimer } from "./changeTimer";
|
import { triggerChangeTimer } from "./changeTimer";
|
||||||
|
|
||||||
const getAnchorParent = <T extends Element>(
|
|
||||||
predicate: (element: Element) => element is T
|
|
||||||
) => (currentField: EditingArea): T | null => {
|
|
||||||
const anchor = currentField.getSelection()?.anchorNode;
|
|
||||||
|
|
||||||
if (!anchor) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let anchorParent: T | null = null;
|
|
||||||
let element = nodeIsElement(anchor) ? anchor : anchor.parentElement;
|
|
||||||
|
|
||||||
while (element) {
|
|
||||||
anchorParent = anchorParent || (predicate(element) ? element : null);
|
|
||||||
element = element.parentElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
return anchorParent;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getListItem = getAnchorParent(
|
|
||||||
(element: Element): element is HTMLLIElement =>
|
|
||||||
window.getComputedStyle(element).display === "list-item"
|
|
||||||
);
|
|
||||||
|
|
||||||
const getParagraph = getAnchorParent(
|
|
||||||
(element: Element): element is HTMLParamElement => element.tagName === "P"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function onInput(event: Event): void {
|
export function onInput(event: Event): void {
|
||||||
// make sure IME changes get saved
|
// make sure IME changes get saved
|
||||||
triggerChangeTimer(event.currentTarget as EditingArea);
|
triggerChangeTimer(event.currentTarget as EditingArea);
|
||||||
|
@ -53,8 +24,8 @@ export function onKey(evt: KeyboardEvent): void {
|
||||||
// prefer <br> instead of <div></div>
|
// prefer <br> instead of <div></div>
|
||||||
if (
|
if (
|
||||||
evt.code === "Enter" &&
|
evt.code === "Enter" &&
|
||||||
!getListItem(currentField) &&
|
!getListItem(currentField.shadowRoot!) &&
|
||||||
!getParagraph(currentField)
|
!getParagraph(currentField.shadowRoot!)
|
||||||
) {
|
) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
document.execCommand("insertLineBreak");
|
document.execCommand("insertLineBreak");
|
||||||
|
|
|
@ -15,6 +15,8 @@ import { bridgeCommand } from "anki/bridgecommand";
|
||||||
import { DynamicSvelteComponent, dynamicComponent } from "sveltelib/dynamicComponent";
|
import { DynamicSvelteComponent, dynamicComponent } from "sveltelib/dynamicComponent";
|
||||||
import * as tr from "anki/i18n";
|
import * as tr from "anki/i18n";
|
||||||
|
|
||||||
|
import { wrap } from "./wrap";
|
||||||
|
|
||||||
import paperclipIcon from "./paperclip.svg";
|
import paperclipIcon from "./paperclip.svg";
|
||||||
import micIcon from "./mic.svg";
|
import micIcon from "./mic.svg";
|
||||||
import functionIcon from "./function-variant.svg";
|
import functionIcon from "./function-variant.svg";
|
||||||
|
@ -95,19 +97,16 @@ export function getTemplateMenus(): (DynamicSvelteComponent<typeof DropdownMenu>
|
||||||
DropdownMenuProps)[] {
|
DropdownMenuProps)[] {
|
||||||
const mathjaxMenuItems = [
|
const mathjaxMenuItems = [
|
||||||
dropdownItem({
|
dropdownItem({
|
||||||
// @ts-expect-error
|
|
||||||
onClick: () => wrap("\\(", "\\)"),
|
onClick: () => wrap("\\(", "\\)"),
|
||||||
label: tr.editingMathjaxInline(),
|
label: tr.editingMathjaxInline(),
|
||||||
endLabel: "Ctrl+M, M",
|
endLabel: "Ctrl+M, M",
|
||||||
}),
|
}),
|
||||||
dropdownItem({
|
dropdownItem({
|
||||||
// @ts-expect-error
|
|
||||||
onClick: () => wrap("\\[", "\\]"),
|
onClick: () => wrap("\\[", "\\]"),
|
||||||
label: tr.editingMathjaxBlock(),
|
label: tr.editingMathjaxBlock(),
|
||||||
endLabel: "Ctrl+M, E",
|
endLabel: "Ctrl+M, E",
|
||||||
}),
|
}),
|
||||||
dropdownItem({
|
dropdownItem({
|
||||||
// @ts-expect-error
|
|
||||||
onClick: () => wrap("\\(\\ce{", "}\\)"),
|
onClick: () => wrap("\\(\\ce{", "}\\)"),
|
||||||
label: tr.editingMathjaxChemistry(),
|
label: tr.editingMathjaxChemistry(),
|
||||||
endLabel: "Ctrl+M, C",
|
endLabel: "Ctrl+M, C",
|
||||||
|
@ -116,19 +115,16 @@ export function getTemplateMenus(): (DynamicSvelteComponent<typeof DropdownMenu>
|
||||||
|
|
||||||
const latexMenuItems = [
|
const latexMenuItems = [
|
||||||
dropdownItem({
|
dropdownItem({
|
||||||
// @ts-expect-error
|
|
||||||
onClick: () => wrap("[latex]", "[/latex]"),
|
onClick: () => wrap("[latex]", "[/latex]"),
|
||||||
label: tr.editingLatex(),
|
label: tr.editingLatex(),
|
||||||
endLabel: "Ctrl+T, T",
|
endLabel: "Ctrl+T, T",
|
||||||
}),
|
}),
|
||||||
dropdownItem({
|
dropdownItem({
|
||||||
// @ts-expect-error
|
|
||||||
onClick: () => wrap("[$]", "[/$]"),
|
onClick: () => wrap("[$]", "[/$]"),
|
||||||
label: tr.editingLatexEquation(),
|
label: tr.editingLatexEquation(),
|
||||||
endLabel: "Ctrl+T, E",
|
endLabel: "Ctrl+T, E",
|
||||||
}),
|
}),
|
||||||
dropdownItem({
|
dropdownItem({
|
||||||
// @ts-expect-error
|
|
||||||
onClick: () => wrap("[$$]", "[/$$]"),
|
onClick: () => wrap("[$$]", "[/$$]"),
|
||||||
label: tr.editingLatexMathEnv(),
|
label: tr.editingLatexMathEnv(),
|
||||||
endLabel: "Ctrl+T, M",
|
endLabel: "Ctrl+T, M",
|
||||||
|
|
Loading…
Reference in a new issue