@threadlabs/looma 0.29.5 → 0.29.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/editor/code-language-picker.d.ts +29 -0
- package/editor/extensions/table-commands.d.ts +1 -0
- package/editor/table-ui.d.ts +35 -0
- package/editor/toolbar-tooltip.d.ts +25 -0
- package/package.json +1 -1
- package/vue/editor/LoomaEditor.d.ts +1 -1
- package/vue/editor/compact/index.js +1 -1
- package/vue/editor/index.js +206 -118
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Editor } from "@tiptap/core";
|
|
2
|
+
/** The code block the selection lies in: its position, saved language (`auto` when none), text, and element. */
|
|
3
|
+
export interface LoomaCodeBlockAtSelection {
|
|
4
|
+
readonly pos: number;
|
|
5
|
+
readonly language: string;
|
|
6
|
+
readonly text: string;
|
|
7
|
+
readonly element: HTMLElement;
|
|
8
|
+
}
|
|
9
|
+
/** The code block holding the whole selection, or null when the selection is elsewhere or spans blocks. */
|
|
10
|
+
export declare function codeBlockAtSelection(editor: Editor): LoomaCodeBlockAtSelection | null;
|
|
11
|
+
/**
|
|
12
|
+
* The picker's choices: Auto, naming the detected language when there is one; a saved language the
|
|
13
|
+
* catalog lacks, marked unavailable, so it stays selected; then every catalog language.
|
|
14
|
+
*/
|
|
15
|
+
export declare function codeLanguageChoices(language: string, detected: string | null, names: readonly string[]): Array<{
|
|
16
|
+
value: string;
|
|
17
|
+
label: string;
|
|
18
|
+
}>;
|
|
19
|
+
/** Centers the picker over the code block, above it when there is room and below it otherwise. */
|
|
20
|
+
export declare function codeLanguagePickerPlacement(block: DOMRect): {
|
|
21
|
+
top: string;
|
|
22
|
+
left: string;
|
|
23
|
+
transform: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Saves `value` as the language of the code block at `pos`, as its own undo step; `auto` clears it.
|
|
27
|
+
* @failure Returns false, and changes nothing, for a language outside `names` or no code block at `pos`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function setCodeBlockLanguage(editor: Editor, pos: number, value: string, names: readonly string[]): boolean;
|
|
@@ -53,6 +53,7 @@ export interface ActiveTableUiState {
|
|
|
53
53
|
headerColumn: boolean;
|
|
54
54
|
capabilities: TableActionCapabilities;
|
|
55
55
|
}
|
|
56
|
+
export declare const NO_TABLE_CAPABILITIES: TableActionCapabilities;
|
|
56
57
|
/**
|
|
57
58
|
* Text formatting belongs to actual text selections, not table CellSelections.
|
|
58
59
|
*
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ActiveTableUiState, type TableActionCapabilities } from "./extensions/table-commands.js";
|
|
2
|
+
import type { TableCellAlignment, TableCellBackground } from "./extensions/table-formatting.js";
|
|
3
|
+
/** What the table's controls show: the toolbar and overlay, and the selected cells' formatting and actions. */
|
|
4
|
+
export interface LoomaTableControls {
|
|
5
|
+
readonly toolbarOpen: boolean;
|
|
6
|
+
readonly overlayOpen: boolean;
|
|
7
|
+
readonly alignment: TableCellAlignment;
|
|
8
|
+
readonly background: TableCellBackground;
|
|
9
|
+
readonly headerRow: boolean;
|
|
10
|
+
readonly headerColumn: boolean;
|
|
11
|
+
readonly capabilities: TableActionCapabilities;
|
|
12
|
+
}
|
|
13
|
+
export declare const LOOMA_TABLE_CONTROLS_CLOSED: LoomaTableControls;
|
|
14
|
+
/**
|
|
15
|
+
* The controls for the table shown: its overlay on wider screens, and the toolbar and the selection's
|
|
16
|
+
* formatting only while the selection is in that table. A table that is only hovered shows its overlay
|
|
17
|
+
* with nothing selected; narrow screens use the editing dock instead of the toolbar and overlay.
|
|
18
|
+
*/
|
|
19
|
+
export declare function tableControls(state: ActiveTableUiState, selectionInTable: boolean, narrow: boolean): LoomaTableControls;
|
|
20
|
+
/**
|
|
21
|
+
* Centers the table toolbar on the table, at most 420px wide and 12px inside the viewport's sides,
|
|
22
|
+
* above the table when there is room and below it otherwise.
|
|
23
|
+
*/
|
|
24
|
+
export declare function tableToolbarPlacement(table: DOMRect, viewportWidth: number): {
|
|
25
|
+
top: string;
|
|
26
|
+
left: string;
|
|
27
|
+
maxWidth: string;
|
|
28
|
+
};
|
|
29
|
+
/** The overlay covers the table exactly. */
|
|
30
|
+
export declare const tableOverlayPlacement: (table: DOMRect) => {
|
|
31
|
+
top: string;
|
|
32
|
+
left: string;
|
|
33
|
+
width: string;
|
|
34
|
+
height: string;
|
|
35
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The toolbar's one tooltip: the button it describes, its label and shortcut, and whether it shows. */
|
|
2
|
+
export interface LoomaToolbarTooltipState {
|
|
3
|
+
readonly for: string;
|
|
4
|
+
readonly label: string;
|
|
5
|
+
readonly shortcut: string;
|
|
6
|
+
readonly open: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** How long, in milliseconds, the first button waits before its tooltip shows. */
|
|
9
|
+
export declare const LOOMA_TOOLBAR_TOOLTIP_DELAY = 400;
|
|
10
|
+
/**
|
|
11
|
+
* One tooltip that follows a toolbar row: the first button waits, and moving along the row while it
|
|
12
|
+
* shows is immediate, which the native `title` attribute cannot do.
|
|
13
|
+
*
|
|
14
|
+
* @ownership The host renders the tooltip from the state and calls `show` and `hide` from its buttons.
|
|
15
|
+
* @lifecycle A pending delay runs until `hide()` or `destroy()`.
|
|
16
|
+
*/
|
|
17
|
+
export interface LoomaToolbarTooltip {
|
|
18
|
+
readonly state: LoomaToolbarTooltipState;
|
|
19
|
+
/** Describes the button with `id`; `immediate` skips the delay, as keyboard focus does. */
|
|
20
|
+
show(id: string, label: string, immediate: boolean, shortcut?: string): void;
|
|
21
|
+
hide(): void;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
}
|
|
24
|
+
/** Calls `onChange` after every state change. */
|
|
25
|
+
export declare function createToolbarTooltip(onChange: (state: LoomaToolbarTooltipState) => void): LoomaToolbarTooltip;
|
package/package.json
CHANGED
|
@@ -384,6 +384,7 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
384
384
|
linkBaseUrl: string;
|
|
385
385
|
disableHighlight: boolean;
|
|
386
386
|
codeLanguages: Readonly<Record<string, import("highlight.js").LanguageFn>> | null;
|
|
387
|
+
toolbarOpen: boolean;
|
|
387
388
|
modelValue: JSONContent;
|
|
388
389
|
slashCommands: LoomaEditorSlashCommands | undefined;
|
|
389
390
|
mentionQuery: LoomaMentionQuery | undefined;
|
|
@@ -401,5 +402,4 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
401
402
|
selectionActions: LoomaEditorSelectionAction[];
|
|
402
403
|
rangeActions: LoomaEditorRangeAction[];
|
|
403
404
|
toolbarTriggerId: string;
|
|
404
|
-
toolbarOpen: boolean;
|
|
405
405
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { defineComponent, h, onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
|
|
2
2
|
import { Extension, Mark, Node, markInputRule, markPasteRule, mergeAttributes } from "@tiptap/core";
|
|
3
3
|
import { Editor as Editor$1, EditorContent } from "@tiptap/vue-3";
|
|
4
|
-
import { closeHistory, history, redo, undo } from "@tiptap/pm/history";
|
|
5
4
|
import { Plugin } from "@tiptap/pm/state";
|
|
5
|
+
import { closeHistory, history, redo, undo } from "@tiptap/pm/history";
|
|
6
6
|
//#region ../../node_modules/.pnpm/@tiptap+extension-document@2.27.2_@tiptap+core@2.27.2_@tiptap+pm@2.27.2_/node_modules/@tiptap/extension-document/dist/index.js
|
|
7
7
|
/**
|
|
8
8
|
* The default document node which represents the top level node of the editor.
|
package/vue/editor/index.js
CHANGED
|
@@ -3,12 +3,12 @@ import { DEFAULT_MENTION_RESULT_LIMIT, LOOMA_ICONS, createLoomaMentionExtension,
|
|
|
3
3
|
import { Button, Callout, Card, Checkbox, Cluster, Disclosure, EditorBlockActions, EditorChipEditor, EditorCodeLanguage, EditorImageToolbar, EditorInsertTableGrid, EditorLinkCard, EditorLinkForm, EditorMentionMenu, EditorMentionMenuItem, EditorSlashMenu, EditorSlashMenuGroup, EditorSlashMenuItem, EditorSurface, EditorTableContextMenu, EditorTableOverlay, EditorTableToolbar, EditorToolbar, FormField, Icon, IconButton, Image, Input, List, Menu, MenuItem, Popover, Separator, Stack, StatusMessage, Text, Tooltip } from "@threadlabs/looma/vue";
|
|
4
4
|
import { Editor, Extension, Node as Node$1, getText, getTextSerializersFromSchema, isNodeSelection, mergeAttributes, nodeInputRule, posToDOMRect } from "@tiptap/core";
|
|
5
5
|
import { Editor as Editor$1 } from "@tiptap/vue-3";
|
|
6
|
-
import { closeHistory } from "@tiptap/pm/history";
|
|
7
6
|
import { AllSelection, NodeSelection, Plugin, PluginKey, TextSelection } from "@tiptap/pm/state";
|
|
8
7
|
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
9
8
|
import { createLowlight } from "lowlight";
|
|
10
9
|
import "@tiptap/pm/tables";
|
|
11
10
|
import "@tiptap/pm/transform";
|
|
11
|
+
import { closeHistory } from "@tiptap/pm/history";
|
|
12
12
|
import { DOMParser, DOMSerializer, Fragment as Fragment$1 } from "@tiptap/pm/model";
|
|
13
13
|
export * from "@threadlabs/looma/editor";
|
|
14
14
|
//#region src/vue/editor/primitives.ts
|
|
@@ -1562,6 +1562,23 @@ function selectedTableCellElement(editor) {
|
|
|
1562
1562
|
}
|
|
1563
1563
|
//#endregion
|
|
1564
1564
|
//#region src/editor/extensions/table-commands.ts
|
|
1565
|
+
var NO_TABLE_CAPABILITIES = {
|
|
1566
|
+
canToggleHeaderRow: false,
|
|
1567
|
+
canToggleHeaderColumn: false,
|
|
1568
|
+
canMoveRowUp: false,
|
|
1569
|
+
canMoveRowDown: false,
|
|
1570
|
+
canMoveColumnLeft: false,
|
|
1571
|
+
canMoveColumnRight: false,
|
|
1572
|
+
canAddRowBefore: false,
|
|
1573
|
+
canAddRowAfter: false,
|
|
1574
|
+
canAddColumnBefore: false,
|
|
1575
|
+
canAddColumnAfter: false,
|
|
1576
|
+
canDeleteRow: false,
|
|
1577
|
+
canDeleteColumn: false,
|
|
1578
|
+
canDeleteTable: false,
|
|
1579
|
+
canMergeCells: false,
|
|
1580
|
+
canSplitCell: false
|
|
1581
|
+
};
|
|
1565
1582
|
/**
|
|
1566
1583
|
* Moves the caret to the cell an overlay action names when the action comes from a table other
|
|
1567
1584
|
* than the selected one, so table commands act on that table. A cell menu also selects its cell,
|
|
@@ -1879,6 +1896,59 @@ function selectionActionDetail(state, id) {
|
|
|
1879
1896
|
after: doc.textBetween(to, Math.min(doc.content.size, to + CONTEXT), "\n")
|
|
1880
1897
|
};
|
|
1881
1898
|
}
|
|
1899
|
+
//#endregion
|
|
1900
|
+
//#region src/editor/table-ui.ts
|
|
1901
|
+
var LOOMA_TABLE_CONTROLS_CLOSED = Object.freeze({
|
|
1902
|
+
toolbarOpen: false,
|
|
1903
|
+
overlayOpen: false,
|
|
1904
|
+
alignment: "left",
|
|
1905
|
+
background: null,
|
|
1906
|
+
headerRow: false,
|
|
1907
|
+
headerColumn: false,
|
|
1908
|
+
capabilities: Object.freeze({ ...NO_TABLE_CAPABILITIES })
|
|
1909
|
+
});
|
|
1910
|
+
/**
|
|
1911
|
+
* The controls for the table shown: its overlay on wider screens, and the toolbar and the selection's
|
|
1912
|
+
* formatting only while the selection is in that table. A table that is only hovered shows its overlay
|
|
1913
|
+
* with nothing selected; narrow screens use the editing dock instead of the toolbar and overlay.
|
|
1914
|
+
*/
|
|
1915
|
+
function tableControls(state, selectionInTable, narrow) {
|
|
1916
|
+
if (!selectionInTable) return {
|
|
1917
|
+
...LOOMA_TABLE_CONTROLS_CLOSED,
|
|
1918
|
+
overlayOpen: !narrow
|
|
1919
|
+
};
|
|
1920
|
+
return {
|
|
1921
|
+
toolbarOpen: state.showToolbar && !narrow,
|
|
1922
|
+
overlayOpen: !narrow,
|
|
1923
|
+
alignment: state.cellAlignment,
|
|
1924
|
+
background: state.cellBackground,
|
|
1925
|
+
headerRow: state.headerRow,
|
|
1926
|
+
headerColumn: state.headerColumn,
|
|
1927
|
+
capabilities: state.capabilities
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
/** Space, in pixels, the toolbar needs above a table; with less it sits below. */
|
|
1931
|
+
var ROOM_ABOVE$1 = 76;
|
|
1932
|
+
/**
|
|
1933
|
+
* Centers the table toolbar on the table, at most 420px wide and 12px inside the viewport's sides,
|
|
1934
|
+
* above the table when there is room and below it otherwise.
|
|
1935
|
+
*/
|
|
1936
|
+
function tableToolbarPlacement(table, viewportWidth) {
|
|
1937
|
+
const width = Math.min(420, viewportWidth - 24);
|
|
1938
|
+
const left = Math.min(Math.max(12, table.left + (table.width - width) / 2), Math.max(12, viewportWidth - width - 12));
|
|
1939
|
+
return {
|
|
1940
|
+
top: `${table.top > ROOM_ABOVE$1 ? table.top - 52 : table.bottom + 12}px`,
|
|
1941
|
+
left: `${left}px`,
|
|
1942
|
+
maxWidth: `${width}px`
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
/** The overlay covers the table exactly. */
|
|
1946
|
+
var tableOverlayPlacement = (table) => ({
|
|
1947
|
+
top: `${table.top}px`,
|
|
1948
|
+
left: `${table.left}px`,
|
|
1949
|
+
width: `${table.width}px`,
|
|
1950
|
+
height: `${table.height}px`
|
|
1951
|
+
});
|
|
1882
1952
|
/** Whether a viewport this wide gets the editing dock rather than floating tools. */
|
|
1883
1953
|
var usesMobileDock = (viewportWidth) => viewportWidth <= 767;
|
|
1884
1954
|
/**
|
|
@@ -2439,6 +2509,116 @@ function formatEditorShortcut(keys, apple) {
|
|
|
2439
2509
|
].filter(Boolean).join("+");
|
|
2440
2510
|
}
|
|
2441
2511
|
var isApplePlatform = () => typeof navigator !== "undefined" && /mac|iphone|ipad|ipod/i.test(navigator.userAgentData?.platform || navigator.platform || navigator.userAgent);
|
|
2512
|
+
/** Calls `onChange` after every state change. */
|
|
2513
|
+
function createToolbarTooltip(onChange) {
|
|
2514
|
+
let state = {
|
|
2515
|
+
for: "",
|
|
2516
|
+
label: "",
|
|
2517
|
+
shortcut: "",
|
|
2518
|
+
open: false
|
|
2519
|
+
};
|
|
2520
|
+
let timer;
|
|
2521
|
+
const set = (patch) => {
|
|
2522
|
+
state = {
|
|
2523
|
+
...state,
|
|
2524
|
+
...patch
|
|
2525
|
+
};
|
|
2526
|
+
onChange(state);
|
|
2527
|
+
};
|
|
2528
|
+
const clear = () => {
|
|
2529
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
2530
|
+
timer = void 0;
|
|
2531
|
+
};
|
|
2532
|
+
return {
|
|
2533
|
+
get state() {
|
|
2534
|
+
return state;
|
|
2535
|
+
},
|
|
2536
|
+
show(id, label, immediate, shortcut = "") {
|
|
2537
|
+
clear();
|
|
2538
|
+
const open = immediate || state.open;
|
|
2539
|
+
set({
|
|
2540
|
+
for: id,
|
|
2541
|
+
label,
|
|
2542
|
+
shortcut: shortcut ? formatEditorShortcut(shortcut, isApplePlatform()) : "",
|
|
2543
|
+
open
|
|
2544
|
+
});
|
|
2545
|
+
if (!open) timer = setTimeout(() => {
|
|
2546
|
+
timer = void 0;
|
|
2547
|
+
set({ open: true });
|
|
2548
|
+
}, 400);
|
|
2549
|
+
},
|
|
2550
|
+
hide() {
|
|
2551
|
+
clear();
|
|
2552
|
+
if (state.open) set({ open: false });
|
|
2553
|
+
},
|
|
2554
|
+
destroy: clear
|
|
2555
|
+
};
|
|
2556
|
+
}
|
|
2557
|
+
//#endregion
|
|
2558
|
+
//#region src/editor/code-language-picker.ts
|
|
2559
|
+
/** The code block holding the whole selection, or null when the selection is elsewhere or spans blocks. */
|
|
2560
|
+
function codeBlockAtSelection(editor) {
|
|
2561
|
+
const { $from, $to } = editor.state.selection;
|
|
2562
|
+
const block = $from.parent;
|
|
2563
|
+
if (block.type.name !== "codeBlock" || !$from.sameParent($to)) return null;
|
|
2564
|
+
const pos = $from.before();
|
|
2565
|
+
const element = editor.view.nodeDOM(pos);
|
|
2566
|
+
if (!(element instanceof HTMLElement)) return null;
|
|
2567
|
+
return {
|
|
2568
|
+
pos,
|
|
2569
|
+
language: typeof block.attrs.language === "string" && block.attrs.language ? block.attrs.language : "auto",
|
|
2570
|
+
text: block.textContent,
|
|
2571
|
+
element
|
|
2572
|
+
};
|
|
2573
|
+
}
|
|
2574
|
+
/**
|
|
2575
|
+
* The picker's choices: Auto, naming the detected language when there is one; a saved language the
|
|
2576
|
+
* catalog lacks, marked unavailable, so it stays selected; then every catalog language.
|
|
2577
|
+
*/
|
|
2578
|
+
function codeLanguageChoices(language, detected, names) {
|
|
2579
|
+
return [
|
|
2580
|
+
{
|
|
2581
|
+
value: "auto",
|
|
2582
|
+
label: detected ? `Auto (${detected.toUpperCase()})` : "Auto"
|
|
2583
|
+
},
|
|
2584
|
+
...language !== "auto" && !names.includes(language) ? [{
|
|
2585
|
+
value: language,
|
|
2586
|
+
label: `${language.toUpperCase()} (unavailable)`
|
|
2587
|
+
}] : [],
|
|
2588
|
+
...names.map((name) => ({
|
|
2589
|
+
value: name,
|
|
2590
|
+
label: name.toUpperCase()
|
|
2591
|
+
}))
|
|
2592
|
+
];
|
|
2593
|
+
}
|
|
2594
|
+
/** Space, in pixels, the picker needs above a code block; with less it sits below. */
|
|
2595
|
+
var ROOM_ABOVE = 76;
|
|
2596
|
+
/** Centers the picker over the code block, above it when there is room and below it otherwise. */
|
|
2597
|
+
function codeLanguagePickerPlacement(block) {
|
|
2598
|
+
const above = block.top > ROOM_ABOVE;
|
|
2599
|
+
return {
|
|
2600
|
+
top: `${above ? block.top - 8 : block.bottom + 8}px`,
|
|
2601
|
+
left: `${block.left + block.width / 2}px`,
|
|
2602
|
+
transform: `translate(-50%, ${above ? "-100%" : "0"})`
|
|
2603
|
+
};
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* Saves `value` as the language of the code block at `pos`, as its own undo step; `auto` clears it.
|
|
2607
|
+
* @failure Returns false, and changes nothing, for a language outside `names` or no code block at `pos`.
|
|
2608
|
+
*/
|
|
2609
|
+
function setCodeBlockLanguage(editor, pos, value, names) {
|
|
2610
|
+
const language = value === "auto" ? null : value;
|
|
2611
|
+
if (language !== null && !names.includes(language)) return false;
|
|
2612
|
+
const node = editor.state.doc.nodeAt(pos);
|
|
2613
|
+
if (node?.type.name !== "codeBlock") return false;
|
|
2614
|
+
editor.view.dispatch(closeHistory(editor.state.tr));
|
|
2615
|
+
editor.view.dispatch(editor.state.tr.setNodeMarkup(pos, void 0, {
|
|
2616
|
+
...node.attrs,
|
|
2617
|
+
language
|
|
2618
|
+
}));
|
|
2619
|
+
editor.view.dispatch(closeHistory(editor.state.tr));
|
|
2620
|
+
return true;
|
|
2621
|
+
}
|
|
2442
2622
|
//#endregion
|
|
2443
2623
|
//#region src/editor/extensions/expand.ts
|
|
2444
2624
|
/**
|
|
@@ -3344,23 +3524,6 @@ var EMPTY_DOCUMENT = {
|
|
|
3344
3524
|
};
|
|
3345
3525
|
var uploadProgressText = (progress) => progress && progress.total > 0 ? `Uploading image… ${Math.round(Math.min(1, progress.loaded / progress.total) * 100)}%` : "Uploading image…";
|
|
3346
3526
|
var editorInstanceSequence = 0;
|
|
3347
|
-
var EMPTY_CAPABILITIES = {
|
|
3348
|
-
canToggleHeaderRow: false,
|
|
3349
|
-
canToggleHeaderColumn: false,
|
|
3350
|
-
canMoveRowUp: false,
|
|
3351
|
-
canMoveRowDown: false,
|
|
3352
|
-
canMoveColumnLeft: false,
|
|
3353
|
-
canMoveColumnRight: false,
|
|
3354
|
-
canAddRowBefore: false,
|
|
3355
|
-
canAddRowAfter: false,
|
|
3356
|
-
canAddColumnBefore: false,
|
|
3357
|
-
canAddColumnAfter: false,
|
|
3358
|
-
canDeleteRow: false,
|
|
3359
|
-
canDeleteColumn: false,
|
|
3360
|
-
canDeleteTable: false,
|
|
3361
|
-
canMergeCells: false,
|
|
3362
|
-
canSplitCell: false
|
|
3363
|
-
};
|
|
3364
3527
|
function slashMenuRows(items) {
|
|
3365
3528
|
const groups = [];
|
|
3366
3529
|
for (const [index, item] of items.entries()) {
|
|
@@ -3573,35 +3736,15 @@ var LoomaEditor = defineComponent({
|
|
|
3573
3736
|
let chipEditing;
|
|
3574
3737
|
const chipState = shallowRef(LOOMA_CHIP_EDITING_IDLE);
|
|
3575
3738
|
const toolbarScope = `looma-editor-tool-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
3576
|
-
const
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
const
|
|
3739
|
+
const toolbarTooltip = createToolbarTooltip((state) => {
|
|
3740
|
+
tooltip.value = state;
|
|
3741
|
+
});
|
|
3742
|
+
const tooltip = shallowRef(toolbarTooltip.state);
|
|
3580
3743
|
const resizeHintFor = ref("");
|
|
3581
3744
|
let resizeHintSequence = 0;
|
|
3582
|
-
|
|
3583
|
-
const
|
|
3584
|
-
|
|
3585
|
-
tooltipTimer = 0;
|
|
3586
|
-
};
|
|
3587
|
-
const showTool = (id, label, immediate, shortcut = "") => {
|
|
3588
|
-
clearTooltipTimer();
|
|
3589
|
-
tooltipFor.value = id;
|
|
3590
|
-
tooltipLabel.value = label;
|
|
3591
|
-
tooltipShortcut.value = shortcut ? formatEditorShortcut(shortcut, isApplePlatform()) : "";
|
|
3592
|
-
if (immediate || tooltipOpen.value) {
|
|
3593
|
-
tooltipOpen.value = true;
|
|
3594
|
-
return;
|
|
3595
|
-
}
|
|
3596
|
-
tooltipTimer = window.setTimeout(() => {
|
|
3597
|
-
tooltipOpen.value = true;
|
|
3598
|
-
}, 400);
|
|
3599
|
-
};
|
|
3600
|
-
const hideTool = () => {
|
|
3601
|
-
clearTooltipTimer();
|
|
3602
|
-
tooltipOpen.value = false;
|
|
3603
|
-
};
|
|
3604
|
-
onBeforeUnmount(clearTooltipTimer);
|
|
3745
|
+
const showTool = toolbarTooltip.show;
|
|
3746
|
+
const hideTool = toolbarTooltip.hide;
|
|
3747
|
+
onBeforeUnmount(toolbarTooltip.destroy);
|
|
3605
3748
|
const tableToolbarShell = ref(null);
|
|
3606
3749
|
const tableOverlayShell = ref(null);
|
|
3607
3750
|
const tableMenuShell = ref(null);
|
|
@@ -3670,7 +3813,7 @@ var LoomaEditor = defineComponent({
|
|
|
3670
3813
|
toolbarStyle: {},
|
|
3671
3814
|
overlayStyle: {},
|
|
3672
3815
|
menuStyle: {},
|
|
3673
|
-
capabilities:
|
|
3816
|
+
capabilities: LOOMA_TABLE_CONTROLS_CLOSED.capabilities
|
|
3674
3817
|
});
|
|
3675
3818
|
const openChipEditor = (_instance, position) => {
|
|
3676
3819
|
if (!chipEditing?.open(position)) return;
|
|
@@ -4089,16 +4232,10 @@ var LoomaEditor = defineComponent({
|
|
|
4089
4232
|
if (session.setContent(value, lastFocusedSelection) && wasFocused) rememberSelection(instance);
|
|
4090
4233
|
}, { deep: true });
|
|
4091
4234
|
const closeTableUi = () => {
|
|
4092
|
-
tableUi
|
|
4093
|
-
tableUi.overlayOpen = false;
|
|
4235
|
+
Object.assign(tableUi, LOOMA_TABLE_CONTROLS_CLOSED);
|
|
4094
4236
|
tableUi.menuOpen = false;
|
|
4095
4237
|
tableUi.menuScope = "cell";
|
|
4096
|
-
tableUi.alignment = "left";
|
|
4097
|
-
tableUi.background = null;
|
|
4098
|
-
tableUi.headerRow = false;
|
|
4099
|
-
tableUi.headerColumn = false;
|
|
4100
4238
|
tableUi.geometry = null;
|
|
4101
|
-
tableUi.capabilities = { ...EMPTY_CAPABILITIES };
|
|
4102
4239
|
overlayTableElement = null;
|
|
4103
4240
|
mobileDock.tableInactive();
|
|
4104
4241
|
};
|
|
@@ -4120,63 +4257,31 @@ var LoomaEditor = defineComponent({
|
|
|
4120
4257
|
let codeBlurTimer;
|
|
4121
4258
|
const updateCodeUi = (focusSettled = false) => {
|
|
4122
4259
|
const instance = editor.value?.isDestroyed ? void 0 : editor.value;
|
|
4123
|
-
const
|
|
4124
|
-
|
|
4125
|
-
const dom = instance && block?.type.name === "codeBlock" && selection.$from.sameParent(selection.$to) ? instance.view.nodeDOM(selection.$from.before()) : null;
|
|
4126
|
-
if (!instance || !props.editable || !codeLanguageNames.length || !(dom instanceof HTMLElement)) {
|
|
4260
|
+
const block = instance ? codeBlockAtSelection(instance) : null;
|
|
4261
|
+
if (!instance || !props.editable || !codeLanguageNames.length || !block) {
|
|
4127
4262
|
codeUi.open = false;
|
|
4128
4263
|
return;
|
|
4129
4264
|
}
|
|
4130
|
-
loadForCodeBlock(instance, block.
|
|
4265
|
+
loadForCodeBlock(instance, block.language === "auto" ? null : block.language, block.text);
|
|
4131
4266
|
if (!instance.isFocused && !codeLanguageShell.value?.contains(document.activeElement)) {
|
|
4132
4267
|
clearTimeout(codeBlurTimer);
|
|
4133
4268
|
if (codeUi.open && !focusSettled) codeBlurTimer = setTimeout(() => updateCodeUi(true), 0);
|
|
4134
4269
|
else codeUi.open = false;
|
|
4135
4270
|
return;
|
|
4136
4271
|
}
|
|
4137
|
-
const rect = dom.getBoundingClientRect();
|
|
4138
|
-
const above = rect.top > 76;
|
|
4139
4272
|
codeUi.open = true;
|
|
4140
|
-
codeUi.pos =
|
|
4141
|
-
codeUi.language =
|
|
4142
|
-
codeUi.text = block.
|
|
4143
|
-
codeUi.style =
|
|
4144
|
-
top: `${above ? rect.top - 8 : rect.bottom + 8}px`,
|
|
4145
|
-
left: `${rect.left + rect.width / 2}px`,
|
|
4146
|
-
transform: `translate(-50%, ${above ? "-100%" : "0"})`
|
|
4147
|
-
};
|
|
4273
|
+
codeUi.pos = block.pos;
|
|
4274
|
+
codeUi.language = block.language;
|
|
4275
|
+
codeUi.text = block.text;
|
|
4276
|
+
codeUi.style = codeLanguagePickerPlacement(block.element.getBoundingClientRect());
|
|
4148
4277
|
};
|
|
4149
4278
|
const codeLanguageOptions = computed(() => {
|
|
4150
4279
|
codeUi.grammarVersion;
|
|
4151
4280
|
const detected = codeUi.open && codeUi.text.trim() ? codeHighlighting.lowlight.highlightAuto(codeUi.text).data?.language : null;
|
|
4152
|
-
return
|
|
4153
|
-
{
|
|
4154
|
-
value: "auto",
|
|
4155
|
-
label: typeof detected === "string" ? `Auto (${detected.toUpperCase()})` : "Auto"
|
|
4156
|
-
},
|
|
4157
|
-
...codeUi.language !== "auto" && !codeLanguageNames.includes(codeUi.language) ? [{
|
|
4158
|
-
value: codeUi.language,
|
|
4159
|
-
label: `${codeUi.language.toUpperCase()} (unavailable)`
|
|
4160
|
-
}] : [],
|
|
4161
|
-
...codeLanguageNames.map((name) => ({
|
|
4162
|
-
value: name,
|
|
4163
|
-
label: name.toUpperCase()
|
|
4164
|
-
}))
|
|
4165
|
-
];
|
|
4281
|
+
return codeLanguageChoices(codeUi.language, typeof detected === "string" ? detected : null, codeLanguageNames);
|
|
4166
4282
|
});
|
|
4167
4283
|
const chooseCodeLanguage = (value) => {
|
|
4168
|
-
|
|
4169
|
-
if (!instance) return;
|
|
4170
|
-
const language = value === "auto" ? null : value;
|
|
4171
|
-
if (language !== null && !codeLanguageNames.includes(language)) return;
|
|
4172
|
-
const node = instance.state.doc.nodeAt(codeUi.pos);
|
|
4173
|
-
if (node?.type.name !== "codeBlock") return;
|
|
4174
|
-
instance.view.dispatch(closeHistory(instance.state.tr));
|
|
4175
|
-
instance.view.dispatch(instance.state.tr.setNodeMarkup(codeUi.pos, void 0, {
|
|
4176
|
-
...node.attrs,
|
|
4177
|
-
language
|
|
4178
|
-
}));
|
|
4179
|
-
instance.view.dispatch(closeHistory(instance.state.tr));
|
|
4284
|
+
if (editor.value) setCodeBlockLanguage(editor.value, codeUi.pos, value, codeLanguageNames);
|
|
4180
4285
|
};
|
|
4181
4286
|
const onCodeTransaction = () => updateCodeUi();
|
|
4182
4287
|
watch(() => props.editable, () => nextTick(onCodeTransaction));
|
|
@@ -4205,31 +4310,14 @@ var LoomaEditor = defineComponent({
|
|
|
4205
4310
|
overlayTableElement = table;
|
|
4206
4311
|
if (selectedTableIsActive) mobileDock.tableActive();
|
|
4207
4312
|
const rect = table.getBoundingClientRect();
|
|
4208
|
-
|
|
4209
|
-
const toolbarLeft = Math.min(Math.max(12, rect.left + (rect.width - maxToolbarWidth) / 2), Math.max(12, window.innerWidth - maxToolbarWidth - 12));
|
|
4210
|
-
tableUi.toolbarOpen = selectedTableIsActive && state.showToolbar && !mobile.value;
|
|
4211
|
-
tableUi.overlayOpen = !mobile.value;
|
|
4212
|
-
tableUi.alignment = selectedTableIsActive ? state.cellAlignment : "left";
|
|
4213
|
-
tableUi.background = selectedTableIsActive ? state.cellBackground : null;
|
|
4214
|
-
tableUi.headerRow = selectedTableIsActive && state.headerRow;
|
|
4215
|
-
tableUi.headerColumn = selectedTableIsActive && state.headerColumn;
|
|
4216
|
-
tableUi.capabilities = selectedTableIsActive ? state.capabilities : { ...EMPTY_CAPABILITIES };
|
|
4313
|
+
Object.assign(tableUi, tableControls(state, selectedTableIsActive, mobile.value));
|
|
4217
4314
|
const selectedCell = selectedTableIsActive ? selectedTableCellElement(instance) : null;
|
|
4218
4315
|
const geometry = measureTableOverlayGeometry(table, selectedCell, hoveredTableCell && table.contains(hoveredTableCell) ? hoveredTableCell : null);
|
|
4219
4316
|
tableUi.rows = Math.max(0, geometry.rowBoundaries.length - 1);
|
|
4220
4317
|
tableUi.cols = Math.max(0, geometry.columnBoundaries.length - 1);
|
|
4221
4318
|
tableUi.geometry = geometry;
|
|
4222
|
-
tableUi.toolbarStyle =
|
|
4223
|
-
|
|
4224
|
-
left: `${toolbarLeft}px`,
|
|
4225
|
-
maxWidth: `${maxToolbarWidth}px`
|
|
4226
|
-
};
|
|
4227
|
-
tableUi.overlayStyle = {
|
|
4228
|
-
top: `${rect.top}px`,
|
|
4229
|
-
left: `${rect.left}px`,
|
|
4230
|
-
width: `${rect.width}px`,
|
|
4231
|
-
height: `${rect.height}px`
|
|
4232
|
-
};
|
|
4319
|
+
tableUi.toolbarStyle = tableToolbarPlacement(rect, window.innerWidth);
|
|
4320
|
+
tableUi.overlayStyle = tableOverlayPlacement(rect);
|
|
4233
4321
|
};
|
|
4234
4322
|
const onEditorFocus = () => {
|
|
4235
4323
|
editorFocused.value = true;
|
|
@@ -4810,13 +4898,13 @@ var LoomaEditor = defineComponent({
|
|
|
4810
4898
|
const actions = full ? [] : renderSelectionActions(instance);
|
|
4811
4899
|
return h(EditorToolbar, { floating }, () => [...mobile.value ? [...actions, ...buttons] : [...buttons, ...actions], h(Tooltip, {
|
|
4812
4900
|
class: "looma-editor__toolbar-tip",
|
|
4813
|
-
for:
|
|
4814
|
-
open:
|
|
4901
|
+
for: tooltip.value.for,
|
|
4902
|
+
open: tooltip.value.open,
|
|
4815
4903
|
placement: floating ? "top" : "bottom",
|
|
4816
4904
|
trigger: "focus"
|
|
4817
4905
|
}, {
|
|
4818
|
-
default: () =>
|
|
4819
|
-
shortcut: () =>
|
|
4906
|
+
default: () => tooltip.value.label,
|
|
4907
|
+
shortcut: () => tooltip.value.shortcut ? h("kbd", tooltip.value.shortcut) : null
|
|
4820
4908
|
})]);
|
|
4821
4909
|
};
|
|
4822
4910
|
const focus = (position = "start") => {
|