@threadlabs/looma 0.31.5 → 0.32.1
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/chunks/extensions.js +56 -9
- package/editor/chunks/reference-display.js +6 -1
- package/editor/expressmark.js +1 -1
- package/editor/extensions/slash-command.d.ts +1 -1
- package/editor/file-uploads.d.ts +31 -0
- package/editor/icons.d.ts +1 -0
- package/editor/index.js +2 -1
- package/markdown/fixtures.js +1 -1
- package/markdown/index.d.ts +5 -0
- package/markdown/index.js +6 -1
- package/markdown/markdown-it.js +36 -6
- package/package.json +1 -1
- package/vue/editor/LoomaEditor.d.ts +18 -2
- package/vue/editor/index.d.ts +2 -1
- package/vue/editor/index.js +119 -5
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as TABLE_CELL_BACKGROUND_PRESETS } from "./table-backgrounds.js";
|
|
2
|
-
import { _ as
|
|
2
|
+
import { _ as normalizeLoomaChipTone, b as readLoomaAttributes, c as LOOMA_CONTAINER_NAMES, d as assignLoomaHeadingIds, f as formatLoomaAttributes, g as normalizeLoomaCalloutTone, h as loomaHeadingSlug, i as referenceDisplay, l as LOOMA_MENTION_HANDLE, m as loomaContainerMarker, n as LoomaReferencePresentation, p as loomaCodeFence, r as getReferenceDisplay, s as LOOMA_CHIP_TONES, t as LoomaReference, u as applyLoomaCodeNotation, v as parseLoomaAttributes, x as splitLoomaFrontmatter, y as parseLoomaFenceInfo } from "./reference-display.js";
|
|
3
3
|
import { Extension, InputRule, Mark, Node as Node$1, PasteRule, callOrReturn, canInsertNode, combineTransactionSteps, escapeForRegEx, findChildren, findChildrenInRange, findParentNodeClosestToPos, getAttributes, getChangedRanges, getExtensionField, getMarksBetween, getText, getTextSerializersFromSchema, isNodeEmpty, isNodeSelection, markInputRule, markPasteRule, mergeAttributes, nodeInputRule, textInputRule, textblockTypeInputRule, wrappingInputRule } from "@tiptap/core";
|
|
4
4
|
import { NodeSelection, Plugin, PluginKey, Selection, TextSelection } from "@tiptap/pm/state";
|
|
5
5
|
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
@@ -10072,7 +10072,7 @@ function taskLists(state) {
|
|
|
10072
10072
|
function directive(state, silent) {
|
|
10073
10073
|
const { src, pos } = state;
|
|
10074
10074
|
if (src.charCodeAt(pos) !== 58) return false;
|
|
10075
|
-
const name = /^:(chip
|
|
10075
|
+
const name = /^:(chip)\[/.exec(src.slice(pos, pos + 10))?.[1];
|
|
10076
10076
|
if (!name) return false;
|
|
10077
10077
|
let label = "";
|
|
10078
10078
|
let index = pos + name.length + 2;
|
|
@@ -10086,7 +10086,6 @@ function directive(state, silent) {
|
|
|
10086
10086
|
end: index + 1
|
|
10087
10087
|
};
|
|
10088
10088
|
if (!read || read.end > state.posMax) return false;
|
|
10089
|
-
if (name === "mention" && typeof read.attributes.id !== "string") return false;
|
|
10090
10089
|
if (!silent) {
|
|
10091
10090
|
const token = state.push(`looma_${name}`, "span", 0);
|
|
10092
10091
|
token.meta = {
|
|
@@ -10097,6 +10096,37 @@ function directive(state, silent) {
|
|
|
10097
10096
|
state.pos = read.end;
|
|
10098
10097
|
return true;
|
|
10099
10098
|
}
|
|
10099
|
+
function mention(state, silent) {
|
|
10100
|
+
const { src, pos } = state;
|
|
10101
|
+
if (src.charCodeAt(pos) !== 64 || pos > 0 && !/\s/u.test(src[pos - 1])) return false;
|
|
10102
|
+
let index = pos + 1;
|
|
10103
|
+
let label = "";
|
|
10104
|
+
if (src[index] === "[") {
|
|
10105
|
+
for (index++; index < state.posMax && src[index] !== "]"; index++) {
|
|
10106
|
+
if (src[index] === "\\" && index + 1 < state.posMax) index++;
|
|
10107
|
+
label += src[index];
|
|
10108
|
+
}
|
|
10109
|
+
if (src[index] !== "]" || !label) return false;
|
|
10110
|
+
index++;
|
|
10111
|
+
} else {
|
|
10112
|
+
label = LOOMA_MENTION_HANDLE.exec(src.slice(index, state.posMax))?.[0] ?? "";
|
|
10113
|
+
if (!label) return false;
|
|
10114
|
+
index += label.length;
|
|
10115
|
+
}
|
|
10116
|
+
let ref = src[index] === "#" ? LOOMA_MENTION_HANDLE.exec(src.slice(index + 1, state.posMax))?.[0] : void 0;
|
|
10117
|
+
if (ref) index += ref.length + 1;
|
|
10118
|
+
const read = src[index] === "{" ? readLoomaAttributes(src, index) : null;
|
|
10119
|
+
if (read && read.end <= state.posMax) {
|
|
10120
|
+
if (typeof read.attributes.ref === "string") ref ??= read.attributes.ref;
|
|
10121
|
+
index = read.end;
|
|
10122
|
+
}
|
|
10123
|
+
if (!silent) state.push("looma_mention", "span", 0).meta = {
|
|
10124
|
+
label,
|
|
10125
|
+
ref
|
|
10126
|
+
};
|
|
10127
|
+
state.pos = index;
|
|
10128
|
+
return true;
|
|
10129
|
+
}
|
|
10100
10130
|
function inlineText(token) {
|
|
10101
10131
|
return (token?.children ?? []).map((child) => child.type === "image" ? inlineText(child) : child.content).join("");
|
|
10102
10132
|
}
|
|
@@ -10318,6 +10348,7 @@ function loomaMarkdown$1(md, { codeNotation = "apply" } = {}) {
|
|
|
10318
10348
|
"list"
|
|
10319
10349
|
] });
|
|
10320
10350
|
md.inline.ruler.before("link", "looma_directive", directive);
|
|
10351
|
+
md.inline.ruler.before("link", "looma_mention", mention);
|
|
10321
10352
|
md.inline.ruler.before("link", "looma_footnote_ref", footnoteReference);
|
|
10322
10353
|
md.core.ruler.after("block", "looma_task_lists", taskLists);
|
|
10323
10354
|
md.core.ruler.before("inline", "looma_alerts", alerts);
|
|
@@ -10367,9 +10398,8 @@ function loomaMarkdown$1(md, { codeNotation = "apply" } = {}) {
|
|
|
10367
10398
|
return `<span data-looma-chip="" data-label="${escape(label)}" data-color="${tone}"${label ? "" : " data-placeholder=\"\""}>${escape(label)}</span>`;
|
|
10368
10399
|
};
|
|
10369
10400
|
rules.looma_mention = (tokens, index) => {
|
|
10370
|
-
const { label,
|
|
10371
|
-
|
|
10372
|
-
return `<span data-type="mention" class="looma-mention" data-id="${escape(id)}" data-label="${escape(label || id)}">@${escape(label || id)}</span>`;
|
|
10401
|
+
const { label, ref } = tokens[index].meta;
|
|
10402
|
+
return `<span data-type="mention" class="looma-mention"${ref === void 0 ? "" : ` data-id="${escape(ref)}"`} data-label="${escape(label)}">@${escape(label)}</span>`;
|
|
10373
10403
|
};
|
|
10374
10404
|
const code = (content, info) => {
|
|
10375
10405
|
const { language, meta } = parseLoomaFenceInfo(info);
|
|
@@ -10525,8 +10555,9 @@ var NODES = {
|
|
|
10525
10555
|
state.write(`:chip[${label(String(node.attrs.label ?? ""))}]${formatLoomaAttributes({ tone: tone !== "neutral" ? tone : null })}`);
|
|
10526
10556
|
},
|
|
10527
10557
|
mention: (state, node) => {
|
|
10528
|
-
const id =
|
|
10529
|
-
|
|
10558
|
+
const id = node.attrs.id == null ? "" : String(node.attrs.id);
|
|
10559
|
+
const reference = !id ? "" : LOOMA_MENTION_HANDLE.exec(id)?.[0] === id ? `#${id}` : formatLoomaAttributes({ ref: id });
|
|
10560
|
+
state.write(`@[${label(String(node.attrs.label || id))}]${reference}`);
|
|
10530
10561
|
},
|
|
10531
10562
|
loomaReference: (state, node) => {
|
|
10532
10563
|
const wrapper = document.createElement("span");
|
|
@@ -11955,7 +11986,7 @@ var CALLOUT_COMMANDS = [
|
|
|
11955
11986
|
* @ownership Returned command objects belong to the caller and are recreated
|
|
11956
11987
|
* per call so one editor cannot mutate another editor's command inventory.
|
|
11957
11988
|
*/
|
|
11958
|
-
function getDefaultSlashCommands(onOpenImagePicker, onOpenChipEditor, onOpenLinkEditor) {
|
|
11989
|
+
function getDefaultSlashCommands(onOpenImagePicker, onOpenChipEditor, onOpenLinkEditor, onOpenFilePicker) {
|
|
11959
11990
|
return [
|
|
11960
11991
|
{
|
|
11961
11992
|
title: "Text",
|
|
@@ -12178,6 +12209,21 @@ function getDefaultSlashCommands(onOpenImagePicker, onOpenChipEditor, onOpenLink
|
|
|
12178
12209
|
onOpenLinkEditor();
|
|
12179
12210
|
}
|
|
12180
12211
|
}] : [],
|
|
12212
|
+
...onOpenFilePicker ? [{
|
|
12213
|
+
title: "File attachment",
|
|
12214
|
+
description: "Attach a file",
|
|
12215
|
+
icon: "paperclip",
|
|
12216
|
+
keywords: [
|
|
12217
|
+
"file",
|
|
12218
|
+
"attachment",
|
|
12219
|
+
"upload",
|
|
12220
|
+
"pdf"
|
|
12221
|
+
],
|
|
12222
|
+
command: ({ editor, range }) => {
|
|
12223
|
+
editor.chain().focus().deleteRange(range).run();
|
|
12224
|
+
onOpenFilePicker();
|
|
12225
|
+
}
|
|
12226
|
+
}] : [],
|
|
12181
12227
|
...onOpenImagePicker ? [{
|
|
12182
12228
|
title: "Image",
|
|
12183
12229
|
description: "Upload an image",
|
|
@@ -12197,6 +12243,7 @@ function getDefaultSlashCommands(onOpenImagePicker, onOpenChipEditor, onOpenLink
|
|
|
12197
12243
|
...command,
|
|
12198
12244
|
id: command.title.toLowerCase().replaceAll(" ", "-"),
|
|
12199
12245
|
group: CALLOUT_COMMANDS.some((callout) => callout.title === command.title) ? "Callouts" : [
|
|
12246
|
+
"File attachment",
|
|
12200
12247
|
"Chip",
|
|
12201
12248
|
"Inline code",
|
|
12202
12249
|
"Link",
|
|
@@ -44,6 +44,11 @@ function normalizeLoomaChipTone(value) {
|
|
|
44
44
|
return LOOMA_CHIP_TONES.includes(value) ? value : "neutral";
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
+
* A mention handle, as `@handle` and the `#ref` of `@[Label]#ref` spell it: a letter, number or
|
|
48
|
+
* underscore, then letters, marks, numbers, underscores and hyphens.
|
|
49
|
+
*/
|
|
50
|
+
var LOOMA_MENTION_HANDLE = /^[\p{L}\p{N}_][\p{L}\p{M}\p{N}_-]*/u;
|
|
51
|
+
/**
|
|
47
52
|
* Names that open a `:::` container, matched without regard to case. A container with any other
|
|
48
53
|
* name is ordinary text.
|
|
49
54
|
*/
|
|
@@ -445,4 +450,4 @@ var LoomaReferencePresentation = Extension.create({
|
|
|
445
450
|
}
|
|
446
451
|
});
|
|
447
452
|
//#endregion
|
|
448
|
-
export {
|
|
453
|
+
export { normalizeLoomaChipTone as _, setReferenceDisplay as a, readLoomaAttributes as b, LOOMA_CONTAINER_NAMES as c, assignLoomaHeadingIds as d, formatLoomaAttributes as f, normalizeLoomaCalloutTone as g, loomaHeadingSlug as h, referenceDisplay as i, LOOMA_MENTION_HANDLE as l, loomaContainerMarker as m, LoomaReferencePresentation as n, LOOMA_CALLOUT_TONES as o, loomaCodeFence as p, getReferenceDisplay as r, LOOMA_CHIP_TONES as s, LoomaReference as t, applyLoomaCodeNotation as u, parseLoomaAttributes as v, splitLoomaFrontmatter as x, parseLoomaFenceInfo as y };
|
package/editor/expressmark.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { h as loomaHeadingSlug, o as LOOMA_CALLOUT_TONES, r as getReferenceDisplay, s as LOOMA_CHIP_TONES } from "./chunks/reference-display.js";
|
|
2
2
|
import { parse, stringify } from "expressmark";
|
|
3
3
|
//#region src/editor/expressmark.ts
|
|
4
4
|
/**
|
|
@@ -79,7 +79,7 @@ declare module "@tiptap/core" {
|
|
|
79
79
|
* @ownership Returned command objects belong to the caller and are recreated
|
|
80
80
|
* per call so one editor cannot mutate another editor's command inventory.
|
|
81
81
|
*/
|
|
82
|
-
export declare function getDefaultSlashCommands(onOpenImagePicker?: () => void, onOpenChipEditor?: (editor: Editor, position: number) => void, onOpenLinkEditor?: () => void): LoomaSlashCommand[];
|
|
82
|
+
export declare function getDefaultSlashCommands(onOpenImagePicker?: () => void, onOpenChipEditor?: (editor: Editor, position: number) => void, onOpenLinkEditor?: () => void, onOpenFilePicker?: () => void): LoomaSlashCommand[];
|
|
83
83
|
/**
|
|
84
84
|
* Ranks exact names/aliases before partial matches, then matching categories.
|
|
85
85
|
* Other ties retain inventory order.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Editor } from '@tiptap/core';
|
|
2
|
+
/** A retained file, represented as a readable ordinary link in the document. */
|
|
3
|
+
export interface LoomaFileDescriptor {
|
|
4
|
+
href: string;
|
|
5
|
+
name: string;
|
|
6
|
+
size: number;
|
|
7
|
+
contentType: string;
|
|
8
|
+
}
|
|
9
|
+
/** The application retains the bytes and returns their authorized download URL. */
|
|
10
|
+
export type LoomaFileUpload = (file: File, init: {
|
|
11
|
+
signal: AbortSignal;
|
|
12
|
+
progress: (loaded: number, total: number) => void;
|
|
13
|
+
}) => PromiseLike<LoomaFileDescriptor | null> | LoomaFileDescriptor | null;
|
|
14
|
+
/** File insertion status, independent of the document and its undo history. */
|
|
15
|
+
export interface LoomaFileUploadsState {
|
|
16
|
+
uploading: boolean;
|
|
17
|
+
progress: {
|
|
18
|
+
loaded: number;
|
|
19
|
+
total: number;
|
|
20
|
+
} | null;
|
|
21
|
+
}
|
|
22
|
+
/** Captures the caret before asynchronous transport; later transactions map that caret. */
|
|
23
|
+
export declare function createFileUploads(options: {
|
|
24
|
+
editor: Editor;
|
|
25
|
+
upload: () => LoomaFileUpload | undefined;
|
|
26
|
+
onError: (error: unknown, file: File) => void;
|
|
27
|
+
onState?: (state: LoomaFileUploadsState) => void;
|
|
28
|
+
}): {
|
|
29
|
+
cancel(): void;
|
|
30
|
+
insert(file: File): Promise<void>;
|
|
31
|
+
};
|
package/editor/icons.d.ts
CHANGED
package/editor/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { n as TABLE_CELL_BACKGROUND_PRESETS, t as TABLE_CELL_BACKGROUND_OPTIONS
|
|
|
2
2
|
import { n as resolveTableCellAt, t as measureTableOverlayGeometry } from "./chunks/ui.js";
|
|
3
3
|
import { $ as LOOMA_ACTIVE_BLOCK_BLUR_GRACE_MS, A as filterLoomaMentionItems, B as LOOMA_FOOTNOTE_LABEL, C as loomaMarkdownToHtml, D as createLoomaMentionExtension, E as LoomaMentionSuggestionPluginKey, F as LoomaTableOfContents, G as LoomaFigcaption, H as LoomaFootnoteReference, I as getTableOfContentsEntries, J as revealLoomaExpandAt, K as LoomaFigure, L as normalizeTableOfContentsConfig, M as LoomaImage, N as normalizeImageDimension, O as DEFAULT_MENTION_RESULT_LIMIT, P as normalizeImagePlacement, Q as LoomaCallout, R as LoomaTab, S as loomaMarkdownFromDoc, T as siteRelativeHref, U as LoomaFootnotes, V as LoomaFootnote, W as findLoomaFootnote, X as LoomaChip, Y as LOOMA_CHIP_COLORS, Z as normalizeLoomaChipColor, _ as getActiveTableCellAlignment, a as getActiveTableUiState, b as setActiveTableCellBackground, c as insertTableAtRange, d as LoomaTableKit, et as LOOMA_ACTIVE_BLOCK_CLASS, f as getDefaultEditorExtensions, g as LoomaTableHeader, h as LoomaTableCell, i as getDefaultSlashCommands, j as normalizeMentionResultLimit, k as MAX_MENTION_RESULT_LIMIT, l as normalizeActiveTableColumnWidths, m as LoomaTable, n as createLoomaSlashCommandExtension, nt as LoomaActiveBlock, o as handleTableAction, p as getLoomaTableExtensions, q as LoomaExpand, r as filterLoomaSlashCommands, rt as LoomaActiveBlockPluginKey, s as handleTableOverlayAction, t as LoomaSlashCommand, tt as LOOMA_ACTIVE_BLOCK_TYPING_IDLE_MS, u as shouldShowTextFormattingToolbar, v as getActiveTableCellBackground, w as createSiteRelativeLinks, x as LoomaSmartPaste, y as setActiveTableCellAlignment, z as LoomaTabs } from "./chunks/extensions.js";
|
|
4
4
|
import { a as setReferenceDisplay, i as referenceDisplay, n as LoomaReferencePresentation, o as LOOMA_CALLOUT_TONES, r as getReferenceDisplay, t as LoomaReference } from "./chunks/reference-display.js";
|
|
5
|
-
import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Superscript, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
|
|
5
|
+
import { AlignCenter, AlignLeft, AlignRight, ArrowLeft, Bell, Bold, BookUser, Braces, Calculator, Check, ChevronDown, ChevronLeft, ChevronRight, CircleCheck, CircleDot, CircleQuestionMark, CircleX, CodeXml, Columns3, Compass, CreditCard, Ellipsis, Eraser, ExternalLink, FileText, Folder, GripHorizontal, GripVertical, Heading1, Heading2, Heading3, Highlighter, History, Image, Info, Italic, LayoutDashboard, Lightbulb, Link, List, ListOrdered, ListTodo, LoaderCircle, Merge, MessageSquareWarning, Minus, NotebookPen, PaintBucket, PanelBottom, PanelLeft, PanelRight, PanelTop, Paperclip, Pencil, Pilcrow, Plus, Quote, Receipt, Redo2, Rows3, Search, Settings, Split, Strikethrough, Superscript, Table2, Tag, Trash2, TriangleAlert, Truck, Underline, Undo2, Unlink, UserRound, Users, X } from "lucide";
|
|
6
6
|
//#region src/editor/icons.ts
|
|
7
7
|
/** The opinionated Lucide set used by Looma's shipped interaction surfaces. */
|
|
8
8
|
var LOOMA_ICONS = {
|
|
@@ -43,6 +43,7 @@ var LOOMA_ICONS = {
|
|
|
43
43
|
highlighter: Highlighter,
|
|
44
44
|
history: History,
|
|
45
45
|
image: Image,
|
|
46
|
+
paperclip: Paperclip,
|
|
46
47
|
info: Info,
|
|
47
48
|
italic: Italic,
|
|
48
49
|
"layout-dashboard": LayoutDashboard,
|
package/markdown/fixtures.js
CHANGED
|
@@ -96,7 +96,7 @@ var files = /* #__PURE__ */ Object.assign({
|
|
|
96
96
|
"../../markdown-fixtures/lists/blocks.html": "<ul>\n<li>One\n<ul>\n<li>Nested</li>\n</ul>\n</li>\n<li>Two</li>\n</ul>\n<ol start=\"3\">\n<li>Three</li>\n<li>Four</li>\n</ol>\n<blockquote>\n<p>Quoted</p>\n</blockquote>\n<hr>\n",
|
|
97
97
|
"../../markdown-fixtures/lists/blocks.md": "- One\n - Nested\n- Two\n\n3. Three\n4. Four\n\n> Quoted\n\n---\n",
|
|
98
98
|
"../../markdown-fixtures/mentions/person.html": "<p>Thanks, <span data-type=\"mention\" class=\"looma-mention\" data-id=\"u1\" data-label=\"Ada Lovelace\">@Ada Lovelace</span>.</p>\n",
|
|
99
|
-
"../../markdown-fixtures/mentions/person.md": "Thanks,
|
|
99
|
+
"../../markdown-fixtures/mentions/person.md": "Thanks, @[Ada Lovelace]#u1.\n",
|
|
100
100
|
"../../markdown-fixtures/tables/gfm.html": "<table>\n<thead>\n<tr>\n<th>Package</th>\n<th style=\"text-align:right\">Version</th>\n<th style=\"text-align:center\">Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>looma</td>\n<td style=\"text-align:right\">0.28</td>\n<td style=\"text-align:center\">Components</td>\n</tr>\n</tbody>\n</table>\n",
|
|
101
101
|
"../../markdown-fixtures/tables/gfm.md": "| Package | Version | Notes |\n| --- | ---: | :---: |\n| looma | 0.28 | Components |\n",
|
|
102
102
|
"../../markdown-fixtures/tables/left-aligned.html": "<table>\n<thead>\n<tr>\n<th style=\"text-align:left\">Name</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:left\">a | b</td>\n<td><code>x | y</code></td>\n</tr>\n</tbody>\n</table>\n",
|
package/markdown/index.d.ts
CHANGED
|
@@ -32,6 +32,11 @@ export type LoomaChipTone = (typeof LOOMA_CHIP_TONES)[number];
|
|
|
32
32
|
* @failure Unknown values become `neutral`.
|
|
33
33
|
*/
|
|
34
34
|
export declare function normalizeLoomaChipTone(value: unknown): LoomaChipTone;
|
|
35
|
+
/**
|
|
36
|
+
* A mention handle, as `@handle` and the `#ref` of `@[Label]#ref` spell it: a letter, number or
|
|
37
|
+
* underscore, then letters, marks, numbers, underscores and hyphens.
|
|
38
|
+
*/
|
|
39
|
+
export declare const LOOMA_MENTION_HANDLE: RegExp;
|
|
35
40
|
/**
|
|
36
41
|
* Names that open a `:::` container, matched without regard to case. A container with any other
|
|
37
42
|
* name is ordinary text.
|
package/markdown/index.js
CHANGED
|
@@ -66,6 +66,11 @@ function normalizeLoomaChipTone(value) {
|
|
|
66
66
|
return LOOMA_CHIP_TONES.includes(value) ? value : "neutral";
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
|
+
* A mention handle, as `@handle` and the `#ref` of `@[Label]#ref` spell it: a letter, number or
|
|
70
|
+
* underscore, then letters, marks, numbers, underscores and hyphens.
|
|
71
|
+
*/
|
|
72
|
+
var LOOMA_MENTION_HANDLE = /^[\p{L}\p{N}_][\p{L}\p{M}\p{N}_-]*/u;
|
|
73
|
+
/**
|
|
69
74
|
* Names that open a `:::` container, matched without regard to case. A container with any other
|
|
70
75
|
* name is ordinary text.
|
|
71
76
|
*/
|
|
@@ -263,4 +268,4 @@ function loomaCodeFence(code) {
|
|
|
263
268
|
return "`".repeat(Math.max(3, longest + 1));
|
|
264
269
|
}
|
|
265
270
|
//#endregion
|
|
266
|
-
export { LOOMA_BADGE_TONES, LOOMA_CALLOUT_TONES, LOOMA_CALLOUT_TONE_ALIASES, LOOMA_CHIP_TONES, LOOMA_CONTAINER_NAMES, LOOMA_MEANING_TONES, applyLoomaCodeNotation, assignLoomaHeadingIds, formatLoomaAttributes, loomaCodeFence, loomaContainerMarker, loomaHeadingSlug, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter };
|
|
271
|
+
export { LOOMA_BADGE_TONES, LOOMA_CALLOUT_TONES, LOOMA_CALLOUT_TONE_ALIASES, LOOMA_CHIP_TONES, LOOMA_CONTAINER_NAMES, LOOMA_MEANING_TONES, LOOMA_MENTION_HANDLE, applyLoomaCodeNotation, assignLoomaHeadingIds, formatLoomaAttributes, loomaCodeFence, loomaContainerMarker, loomaHeadingSlug, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter };
|
package/markdown/markdown-it.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LOOMA_CONTAINER_NAMES, applyLoomaCodeNotation, assignLoomaHeadingIds, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter } from "./index.js";
|
|
1
|
+
import { LOOMA_CONTAINER_NAMES, LOOMA_MENTION_HANDLE, applyLoomaCodeNotation, assignLoomaHeadingIds, normalizeLoomaCalloutTone, normalizeLoomaChipTone, parseLoomaAttributes, parseLoomaFenceInfo, readLoomaAttributes, splitLoomaFrontmatter } from "./index.js";
|
|
2
2
|
import MarkdownIt from "markdown-it";
|
|
3
3
|
//#region src/markdown/markdown-it.ts
|
|
4
4
|
var CONTAINER_OPEN = /^(:{3,})[ \t]*([A-Za-z][\w-]*)(?:[ \t]+(.*?))?[ \t]*$/;
|
|
@@ -210,7 +210,7 @@ function taskLists(state) {
|
|
|
210
210
|
function directive(state, silent) {
|
|
211
211
|
const { src, pos } = state;
|
|
212
212
|
if (src.charCodeAt(pos) !== 58) return false;
|
|
213
|
-
const name = /^:(chip
|
|
213
|
+
const name = /^:(chip)\[/.exec(src.slice(pos, pos + 10))?.[1];
|
|
214
214
|
if (!name) return false;
|
|
215
215
|
let label = "";
|
|
216
216
|
let index = pos + name.length + 2;
|
|
@@ -224,7 +224,6 @@ function directive(state, silent) {
|
|
|
224
224
|
end: index + 1
|
|
225
225
|
};
|
|
226
226
|
if (!read || read.end > state.posMax) return false;
|
|
227
|
-
if (name === "mention" && typeof read.attributes.id !== "string") return false;
|
|
228
227
|
if (!silent) {
|
|
229
228
|
const token = state.push(`looma_${name}`, "span", 0);
|
|
230
229
|
token.meta = {
|
|
@@ -235,6 +234,37 @@ function directive(state, silent) {
|
|
|
235
234
|
state.pos = read.end;
|
|
236
235
|
return true;
|
|
237
236
|
}
|
|
237
|
+
function mention(state, silent) {
|
|
238
|
+
const { src, pos } = state;
|
|
239
|
+
if (src.charCodeAt(pos) !== 64 || pos > 0 && !/\s/u.test(src[pos - 1])) return false;
|
|
240
|
+
let index = pos + 1;
|
|
241
|
+
let label = "";
|
|
242
|
+
if (src[index] === "[") {
|
|
243
|
+
for (index++; index < state.posMax && src[index] !== "]"; index++) {
|
|
244
|
+
if (src[index] === "\\" && index + 1 < state.posMax) index++;
|
|
245
|
+
label += src[index];
|
|
246
|
+
}
|
|
247
|
+
if (src[index] !== "]" || !label) return false;
|
|
248
|
+
index++;
|
|
249
|
+
} else {
|
|
250
|
+
label = LOOMA_MENTION_HANDLE.exec(src.slice(index, state.posMax))?.[0] ?? "";
|
|
251
|
+
if (!label) return false;
|
|
252
|
+
index += label.length;
|
|
253
|
+
}
|
|
254
|
+
let ref = src[index] === "#" ? LOOMA_MENTION_HANDLE.exec(src.slice(index + 1, state.posMax))?.[0] : void 0;
|
|
255
|
+
if (ref) index += ref.length + 1;
|
|
256
|
+
const read = src[index] === "{" ? readLoomaAttributes(src, index) : null;
|
|
257
|
+
if (read && read.end <= state.posMax) {
|
|
258
|
+
if (typeof read.attributes.ref === "string") ref ??= read.attributes.ref;
|
|
259
|
+
index = read.end;
|
|
260
|
+
}
|
|
261
|
+
if (!silent) state.push("looma_mention", "span", 0).meta = {
|
|
262
|
+
label,
|
|
263
|
+
ref
|
|
264
|
+
};
|
|
265
|
+
state.pos = index;
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
238
268
|
function inlineText(token) {
|
|
239
269
|
return (token?.children ?? []).map((child) => child.type === "image" ? inlineText(child) : child.content).join("");
|
|
240
270
|
}
|
|
@@ -456,6 +486,7 @@ function loomaMarkdown(md, { codeNotation = "apply" } = {}) {
|
|
|
456
486
|
"list"
|
|
457
487
|
] });
|
|
458
488
|
md.inline.ruler.before("link", "looma_directive", directive);
|
|
489
|
+
md.inline.ruler.before("link", "looma_mention", mention);
|
|
459
490
|
md.inline.ruler.before("link", "looma_footnote_ref", footnoteReference);
|
|
460
491
|
md.core.ruler.after("block", "looma_task_lists", taskLists);
|
|
461
492
|
md.core.ruler.before("inline", "looma_alerts", alerts);
|
|
@@ -505,9 +536,8 @@ function loomaMarkdown(md, { codeNotation = "apply" } = {}) {
|
|
|
505
536
|
return `<span data-looma-chip="" data-label="${escape(label)}" data-color="${tone}"${label ? "" : " data-placeholder=\"\""}>${escape(label)}</span>`;
|
|
506
537
|
};
|
|
507
538
|
rules.looma_mention = (tokens, index) => {
|
|
508
|
-
const { label,
|
|
509
|
-
|
|
510
|
-
return `<span data-type="mention" class="looma-mention" data-id="${escape(id)}" data-label="${escape(label || id)}">@${escape(label || id)}</span>`;
|
|
539
|
+
const { label, ref } = tokens[index].meta;
|
|
540
|
+
return `<span data-type="mention" class="looma-mention"${ref === void 0 ? "" : ` data-id="${escape(ref)}"`} data-label="${escape(label)}">@${escape(label)}</span>`;
|
|
511
541
|
};
|
|
512
542
|
const code = (content, info) => {
|
|
513
543
|
const { language, meta } = parseLoomaFenceInfo(info);
|
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type LoomaFileDescriptor } from "../../editor/file-uploads.js";
|
|
1
2
|
import "../../components/ui-editor-surface/ui-editor-surface.css";
|
|
2
3
|
import "./looma-editor.css";
|
|
3
4
|
import { type AnyExtension, type Editor, type JSONContent } from "@tiptap/core";
|
|
@@ -77,6 +78,10 @@ export type LoomaMentionQuery = LoomaResponder<LoomaMentionQueryDetail, LoomaMen
|
|
|
77
78
|
* @failure A rejection leaves the document unchanged and emits `uploadError`.
|
|
78
79
|
*/
|
|
79
80
|
export type LoomaImageUploader = LoomaResponder<LoomaImageUploadDetail, LoomaImageDescriptor>;
|
|
81
|
+
/** Uploads a file and returns its retained link; rejection leaves authored content unchanged. */
|
|
82
|
+
export type LoomaFileUploader = LoomaResponder<{
|
|
83
|
+
file: File;
|
|
84
|
+
}, LoomaFileDescriptor>;
|
|
80
85
|
/**
|
|
81
86
|
* Turnkey Vue editor that composes Looma's framework-neutral Tiptap extensions
|
|
82
87
|
* with declarative menus, image delivery, and table interaction chrome.
|
|
@@ -174,7 +179,12 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
174
179
|
type: StringConstructor;
|
|
175
180
|
default: string;
|
|
176
181
|
};
|
|
177
|
-
/** Answers `
|
|
182
|
+
/** Answers `file-upload` with a retained ordinary file link. */
|
|
183
|
+
fileUpload: {
|
|
184
|
+
type: PropType<LoomaFileUploader | undefined>;
|
|
185
|
+
default: undefined;
|
|
186
|
+
};
|
|
187
|
+
/** Answers `image-upload` with the image to insert. */
|
|
178
188
|
imageUpload: {
|
|
179
189
|
type: PropType<LoomaImageUploader | undefined>;
|
|
180
190
|
default: undefined;
|
|
@@ -324,7 +334,12 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
324
334
|
type: StringConstructor;
|
|
325
335
|
default: string;
|
|
326
336
|
};
|
|
327
|
-
/** Answers `
|
|
337
|
+
/** Answers `file-upload` with a retained ordinary file link. */
|
|
338
|
+
fileUpload: {
|
|
339
|
+
type: PropType<LoomaFileUploader | undefined>;
|
|
340
|
+
default: undefined;
|
|
341
|
+
};
|
|
342
|
+
/** Answers `image-upload` with the image to insert. */
|
|
328
343
|
imageUpload: {
|
|
329
344
|
type: PropType<LoomaImageUploader | undefined>;
|
|
330
345
|
default: undefined;
|
|
@@ -402,6 +417,7 @@ export declare const LoomaEditor: import("vue").DefineComponent<import("vue").Ex
|
|
|
402
417
|
linkSearch: LoomaLinkSearch | undefined;
|
|
403
418
|
linkResolve: LoomaLinkResolve | undefined;
|
|
404
419
|
imageUpload: LoomaImageUploader | undefined;
|
|
420
|
+
fileUpload: LoomaFileUploader | undefined;
|
|
405
421
|
modelValue: JSONContent;
|
|
406
422
|
contentDensity: "comfortable" | "compact";
|
|
407
423
|
mentionItems: LoomaMentionItem[];
|
package/vue/editor/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export * from "@threadlabs/looma/editor";
|
|
2
2
|
export * from "./primitives.js";
|
|
3
|
-
export { LoomaEditor, type LoomaEditorToolbarMode, type LoomaEditorSelectionAction, type LoomaEditorSelectionActionDetail, type LoomaEditorRangeAction, type LoomaEditorRangeActionDetail, type LoomaEditorExtensions, type LoomaEditorSlashCommands, type LoomaSlashCommandsDetail, type LoomaLinkTarget, type LoomaLinkSearch, type LoomaLinkSearchDetail, type LoomaLinkResolve, type LoomaLinkResolveDetail, type LoomaMentionQuery, type LoomaMentionQueryDetail, type LoomaImageUploader, type LoomaImageUploadDetail, type LoomaRequestInit, type LoomaResponder, } from "./LoomaEditor.js";
|
|
3
|
+
export { LoomaEditor, type LoomaEditorToolbarMode, type LoomaEditorSelectionAction, type LoomaEditorSelectionActionDetail, type LoomaEditorRangeAction, type LoomaEditorRangeActionDetail, type LoomaEditorExtensions, type LoomaEditorSlashCommands, type LoomaSlashCommandsDetail, type LoomaLinkTarget, type LoomaLinkSearch, type LoomaLinkSearchDetail, type LoomaLinkResolve, type LoomaLinkResolveDetail, type LoomaMentionQuery, type LoomaMentionQueryDetail, type LoomaFileUploader, type LoomaImageUploader, type LoomaImageUploadDetail, type LoomaRequestInit, type LoomaResponder, } from "./LoomaEditor.js";
|
|
4
4
|
export type { LoomaImageActivationDetail, LoomaImageActivationTrigger, LoomaImageAttributeResolver, LoomaImageSourceResolver, LoomaImageDescriptor, LoomaImageRenditionErrorDetail, LoomaImageRenditionAttributes, } from "../../editor/image-delivery.js";
|
|
5
|
+
export type { LoomaFileDescriptor } from "../../editor/file-uploads.js";
|
package/vue/editor/index.js
CHANGED
|
@@ -47,6 +47,73 @@ var EDITOR_ADAPTER_COMPONENT_TAG_MAP = {
|
|
|
47
47
|
EditorFootnoteEditor: "ui-editor-footnote-editor"
|
|
48
48
|
};
|
|
49
49
|
//#endregion
|
|
50
|
+
//#region src/editor/file-uploads.ts
|
|
51
|
+
/** Captures the caret before asynchronous transport; later transactions map that caret. */
|
|
52
|
+
function createFileUploads(options) {
|
|
53
|
+
let active;
|
|
54
|
+
const state = (uploading, progress = null) => options.onState?.({
|
|
55
|
+
uploading,
|
|
56
|
+
progress
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
cancel() {
|
|
60
|
+
active?.abort();
|
|
61
|
+
state(false);
|
|
62
|
+
},
|
|
63
|
+
async insert(file) {
|
|
64
|
+
const upload = options.upload();
|
|
65
|
+
if (!upload || options.editor.isDestroyed) return;
|
|
66
|
+
active?.abort();
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
active = controller;
|
|
69
|
+
const { editor } = options;
|
|
70
|
+
let position = editor.state.selection.from;
|
|
71
|
+
const map = ({ transaction }) => {
|
|
72
|
+
position = transaction.mapping.map(position);
|
|
73
|
+
};
|
|
74
|
+
editor.on("transaction", map);
|
|
75
|
+
state(true);
|
|
76
|
+
try {
|
|
77
|
+
const answer = await upload(file, {
|
|
78
|
+
signal: controller.signal,
|
|
79
|
+
progress: (loaded, total) => {
|
|
80
|
+
if (!controller.signal.aborted) state(true, {
|
|
81
|
+
loaded,
|
|
82
|
+
total
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
if (!answer || controller.signal.aborted || editor.isDestroyed || !editor.isEditable) return;
|
|
87
|
+
if (!/^(?:https?:\/\/|\/(?!\/))/.test(answer.href) || !Number.isSafeInteger(answer.size) || answer.size < 0) throw new Error("Invalid file descriptor");
|
|
88
|
+
const size = answer.size < 1e3 ? `${answer.size} B` : answer.size < 1e6 ? `${Math.ceil(answer.size / 1e3)} KB` : `${Number((answer.size / 1e6).toFixed(1))} MB`;
|
|
89
|
+
editor.chain().focus().insertContentAt(position, [{
|
|
90
|
+
type: "text",
|
|
91
|
+
text: answer.name || file.name,
|
|
92
|
+
marks: [{
|
|
93
|
+
type: "link",
|
|
94
|
+
attrs: {
|
|
95
|
+
href: answer.href,
|
|
96
|
+
target: "_blank",
|
|
97
|
+
rel: "noopener noreferrer"
|
|
98
|
+
}
|
|
99
|
+
}]
|
|
100
|
+
}, {
|
|
101
|
+
type: "text",
|
|
102
|
+
text: ` (${answer.contentType || "application/octet-stream"}, ${size}) `
|
|
103
|
+
}]).run();
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (!controller.signal.aborted) options.onError(error, file);
|
|
106
|
+
} finally {
|
|
107
|
+
editor.off("transaction", map);
|
|
108
|
+
if (active === controller) {
|
|
109
|
+
active = void 0;
|
|
110
|
+
state(false);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
50
117
|
//#region src/components/shared/overlay.js
|
|
51
118
|
function stateFor$1(document) {
|
|
52
119
|
const key = Symbol.for("@threadlabs/looma/overlay-stack/v1");
|
|
@@ -14546,7 +14613,8 @@ var EDITOR_REQUESTS = {
|
|
|
14546
14613
|
"mention-query": "mentionQuery",
|
|
14547
14614
|
"link-search": "linkSearch",
|
|
14548
14615
|
"link-resolve": "linkResolve",
|
|
14549
|
-
"image-upload": "imageUpload"
|
|
14616
|
+
"image-upload": "imageUpload",
|
|
14617
|
+
"file-upload": "fileUpload"
|
|
14550
14618
|
};
|
|
14551
14619
|
var EDITOR_REQUEST_NAMES = Object.keys(EDITOR_REQUESTS);
|
|
14552
14620
|
var EMPTY_DOCUMENT = {
|
|
@@ -14680,7 +14748,12 @@ var LoomaEditor = defineComponent({
|
|
|
14680
14748
|
type: String,
|
|
14681
14749
|
default: "Item"
|
|
14682
14750
|
},
|
|
14683
|
-
/** Answers `
|
|
14751
|
+
/** Answers `file-upload` with a retained ordinary file link. */
|
|
14752
|
+
fileUpload: {
|
|
14753
|
+
type: Function,
|
|
14754
|
+
default: void 0
|
|
14755
|
+
},
|
|
14756
|
+
/** Answers `image-upload` with the image to insert. */
|
|
14684
14757
|
imageUpload: {
|
|
14685
14758
|
type: Function,
|
|
14686
14759
|
default: void 0
|
|
@@ -14750,6 +14823,12 @@ var LoomaEditor = defineComponent({
|
|
|
14750
14823
|
const formatAnchorId = `looma-editor-format-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
14751
14824
|
const formatOpen = ref(false);
|
|
14752
14825
|
const fileInput = ref(null);
|
|
14826
|
+
const attachmentInput = ref(null);
|
|
14827
|
+
const fileUploadState = shallowRef({
|
|
14828
|
+
uploading: false,
|
|
14829
|
+
progress: null
|
|
14830
|
+
});
|
|
14831
|
+
let attachmentUploads;
|
|
14753
14832
|
const tablePickerAnchorId = `looma-editor-table-picker-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
14754
14833
|
const blockActionAnchorId = `ui-block-tools-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
14755
14834
|
const linkAnchorId = `looma-editor-link-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
|
|
@@ -14918,9 +14997,11 @@ var LoomaEditor = defineComponent({
|
|
|
14918
14997
|
let slashDefaults;
|
|
14919
14998
|
const defaultSlashCommands = () => {
|
|
14920
14999
|
const uploads = answers("image-upload");
|
|
14921
|
-
|
|
15000
|
+
const files = answers("file-upload");
|
|
15001
|
+
if (slashDefaults?.uploads !== uploads || slashDefaults?.files !== files) slashDefaults = {
|
|
14922
15002
|
uploads,
|
|
14923
|
-
|
|
15003
|
+
files,
|
|
15004
|
+
commands: getDefaultSlashCommands(uploads ? () => fileInput.value?.click() : void 0, openChipEditor, () => openLinkEditor(), files ? () => attachmentInput.value?.click() : void 0)
|
|
14924
15005
|
};
|
|
14925
15006
|
return slashDefaults.commands;
|
|
14926
15007
|
};
|
|
@@ -14946,7 +15027,7 @@ var LoomaEditor = defineComponent({
|
|
|
14946
15027
|
const current = new Set(EDITOR_REQUEST_NAMES.filter(answers));
|
|
14947
15028
|
const arrived = (name) => current.has(name) && !responders.has(name);
|
|
14948
15029
|
const extensionsArrived = arrived("extensions");
|
|
14949
|
-
const slashChanged = arrived("slash-commands") || current.has("image-upload") !== responders.has("image-upload");
|
|
15030
|
+
const slashChanged = arrived("slash-commands") || current.has("image-upload") !== responders.has("image-upload") || current.has("file-upload") !== responders.has("file-upload");
|
|
14950
15031
|
responders = current;
|
|
14951
15032
|
if (extensionsArrived) askExtensions();
|
|
14952
15033
|
if (slashChanged) askSlashCommands();
|
|
@@ -15122,6 +15203,14 @@ var LoomaEditor = defineComponent({
|
|
|
15122
15203
|
uploadState.value = imageUploads.state;
|
|
15123
15204
|
});
|
|
15124
15205
|
uploads = imageUploads;
|
|
15206
|
+
attachmentUploads = createFileUploads({
|
|
15207
|
+
editor: session.editor,
|
|
15208
|
+
upload: () => (file, init) => ask("file-upload", { file }, init),
|
|
15209
|
+
onError: (error, file) => emit("uploadError", error, file),
|
|
15210
|
+
onState: (value) => {
|
|
15211
|
+
fileUploadState.value = value;
|
|
15212
|
+
}
|
|
15213
|
+
});
|
|
15125
15214
|
const instance = session.editor;
|
|
15126
15215
|
formatting = createFormattingSelection({
|
|
15127
15216
|
editor: instance,
|
|
@@ -15142,6 +15231,7 @@ var LoomaEditor = defineComponent({
|
|
|
15142
15231
|
footnoteEditing?.close();
|
|
15143
15232
|
formatting?.destroy();
|
|
15144
15233
|
uploads?.cancel();
|
|
15234
|
+
attachmentUploads?.cancel();
|
|
15145
15235
|
previous.destroy();
|
|
15146
15236
|
build(content);
|
|
15147
15237
|
};
|
|
@@ -15167,6 +15257,7 @@ var LoomaEditor = defineComponent({
|
|
|
15167
15257
|
onBeforeUnmount(() => {
|
|
15168
15258
|
const element = session?.editor.options.element;
|
|
15169
15259
|
element?.parentNode?.replaceChild(element.cloneNode(true), element);
|
|
15260
|
+
attachmentUploads?.cancel();
|
|
15170
15261
|
connection.abort();
|
|
15171
15262
|
stopAnnouncements?.();
|
|
15172
15263
|
linkEditing?.destroy();
|
|
@@ -15712,6 +15803,12 @@ var LoomaEditor = defineComponent({
|
|
|
15712
15803
|
if (dragLeaveTimer) clearTimeout(dragLeaveTimer);
|
|
15713
15804
|
if (tablePointerFrame !== null) cancelAnimationFrame(tablePointerFrame);
|
|
15714
15805
|
});
|
|
15806
|
+
const onAttachmentChange = async (event) => {
|
|
15807
|
+
const input = event.target;
|
|
15808
|
+
const file = input.files?.[0];
|
|
15809
|
+
input.value = "";
|
|
15810
|
+
if (file) await attachmentUploads?.insert(file);
|
|
15811
|
+
};
|
|
15715
15812
|
const onFileChange = async (event) => {
|
|
15716
15813
|
const input = event.target;
|
|
15717
15814
|
const files = Array.from(input.files ?? []);
|
|
@@ -15954,6 +16051,7 @@ var LoomaEditor = defineComponent({
|
|
|
15954
16051
|
variant: tablePickerOpen.value ? "solid" : "ghost",
|
|
15955
16052
|
"aria-expanded": tablePickerOpen.value ? "true" : "false"
|
|
15956
16053
|
}, () => loomaIcon("table")),
|
|
16054
|
+
commandButton("Attach file", "paperclip", false, fileUploadState.value.uploading || !answers("file-upload"), () => attachmentInput.value?.click()),
|
|
15957
16055
|
commandButton(uploadState.value.uploading ? "Uploading image" : "Insert image", "image", false, uploadState.value.uploading || !answers("image-upload"), () => fileInput.value?.click()),
|
|
15958
16056
|
h("span", {
|
|
15959
16057
|
class: "divider",
|
|
@@ -16143,6 +16241,22 @@ var LoomaEditor = defineComponent({
|
|
|
16143
16241
|
"aria-hidden": "true",
|
|
16144
16242
|
onChange: onFileChange
|
|
16145
16243
|
}),
|
|
16244
|
+
h("input", {
|
|
16245
|
+
ref: attachmentInput,
|
|
16246
|
+
class: "looma-editor__file-input",
|
|
16247
|
+
type: "file",
|
|
16248
|
+
tabindex: -1,
|
|
16249
|
+
"aria-hidden": "true",
|
|
16250
|
+
onChange: onAttachmentChange
|
|
16251
|
+
}),
|
|
16252
|
+
fileUploadState.value.uploading ? h(StatusMessage, { kind: "loading" }, {
|
|
16253
|
+
default: () => "Attaching file…",
|
|
16254
|
+
action: () => h(Button, {
|
|
16255
|
+
size: "sm",
|
|
16256
|
+
variant: "ghost",
|
|
16257
|
+
onClick: () => attachmentUploads?.cancel()
|
|
16258
|
+
}, () => "Cancel")
|
|
16259
|
+
}) : null,
|
|
16146
16260
|
uploadState.value.uploading ? h(StatusMessage, { kind: "loading" }, {
|
|
16147
16261
|
default: () => uploadProgressText(uploadState.value.progress),
|
|
16148
16262
|
action: () => h(Button, {
|