@agent-native/core 0.43.0 → 0.44.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/dist/chat-threads/store.d.ts.map +1 -1
- package/dist/chat-threads/store.js +71 -10
- package/dist/chat-threads/store.js.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +1 -1
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/cli/recap.d.ts +23 -0
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +177 -13
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +67 -20
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +76 -18
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/blocks/index.d.ts +0 -2
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +0 -2
- package/dist/client/blocks/index.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +22 -9
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/ApiEndpointBlock.js +113 -13
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/DiffBlock.js +63 -35
- package/dist/client/blocks/library/DiffBlock.js.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/FileTreeBlock.js +4 -0
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -1
- package/dist/client/blocks/library/MermaidBlock.js +22 -3
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
- package/dist/client/blocks/library/annotation-rail.d.ts +85 -19
- package/dist/client/blocks/library/annotation-rail.d.ts.map +1 -1
- package/dist/client/blocks/library/annotation-rail.js +149 -27
- package/dist/client/blocks/library/annotation-rail.js.map +1 -1
- package/dist/client/blocks/library/code-tabs.js +1 -1
- package/dist/client/blocks/library/code-tabs.js.map +1 -1
- package/dist/client/blocks/library/diagram.d.ts +17 -0
- package/dist/client/blocks/library/diagram.d.ts.map +1 -1
- package/dist/client/blocks/library/diagram.js +47 -2
- package/dist/client/blocks/library/diagram.js.map +1 -1
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
- package/dist/client/blocks/library/server-specs.js +0 -10
- package/dist/client/blocks/library/server-specs.js.map +1 -1
- package/dist/client/blocks/library/specs.d.ts.map +1 -1
- package/dist/client/blocks/library/specs.js +0 -2
- package/dist/client/blocks/library/specs.js.map +1 -1
- package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -1
- package/dist/client/blocks/library/wireframe.config.js +19 -2
- package/dist/client/blocks/library/wireframe.config.js.map +1 -1
- package/dist/client/blocks/mdx.d.ts.map +1 -1
- package/dist/client/blocks/mdx.js +11 -0
- package/dist/client/blocks/mdx.js.map +1 -1
- package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/client/composer/TiptapComposer.js +13 -8
- package/dist/client/composer/TiptapComposer.js.map +1 -1
- package/dist/client/composer/pasted-text.d.ts +25 -0
- package/dist/client/composer/pasted-text.d.ts.map +1 -1
- package/dist/client/composer/pasted-text.js +86 -4
- package/dist/client/composer/pasted-text.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.js +35 -72
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +1 -1
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +9 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +3 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/extensions.d.ts +13 -1
- package/dist/client/rich-markdown-editor/extensions.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/extensions.js +4 -2
- package/dist/client/rich-markdown-editor/extensions.js.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/dist/db/migrations.d.ts +10 -0
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +32 -0
- package/dist/db/migrations.js.map +1 -1
- package/dist/server/og-fonts-data.d.ts +3 -0
- package/dist/server/og-fonts-data.d.ts.map +1 -0
- package/dist/server/og-fonts-data.js +9 -0
- package/dist/server/og-fonts-data.js.map +1 -0
- package/dist/server/og-fonts.d.ts +10 -0
- package/dist/server/og-fonts.d.ts.map +1 -0
- package/dist/server/og-fonts.js +58 -0
- package/dist/server/og-fonts.js.map +1 -0
- package/dist/server/poll.d.ts.map +1 -1
- package/dist/server/poll.js +30 -14
- package/dist/server/poll.js.map +1 -1
- package/dist/server/social-og-image.d.ts.map +1 -1
- package/dist/server/social-og-image.js +16 -5
- package/dist/server/social-og-image.js.map +1 -1
- package/dist/styles/blocks.css +121 -2
- package/dist/templates/default/.agents/skills/storing-data/SKILL.md +2 -0
- package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +141 -0
- package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +2 -0
- package/dist/usage/store.d.ts +12 -0
- package/dist/usage/store.d.ts.map +1 -1
- package/dist/usage/store.js +35 -5
- package/dist/usage/store.js.map +1 -1
- package/package.json +1 -1
- package/src/templates/default/.agents/skills/storing-data/SKILL.md +2 -0
- package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +141 -0
- package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +2 -0
- package/dist/client/blocks/library/decision.config.d.ts +0 -37
- package/dist/client/blocks/library/decision.config.d.ts.map +0 -1
- package/dist/client/blocks/library/decision.config.js +0 -32
- package/dist/client/blocks/library/decision.config.js.map +0 -1
- package/dist/client/blocks/library/decision.d.ts +0 -19
- package/dist/client/blocks/library/decision.d.ts.map +0 -1
- package/dist/client/blocks/library/decision.js +0 -119
- package/dist/client/blocks/library/decision.js.map +0 -1
|
@@ -1,4 +1,29 @@
|
|
|
1
|
+
/** The clipboard flavors we care about for a paste. */
|
|
2
|
+
export interface ClipboardPaste {
|
|
3
|
+
/** `text/plain` flavor — used for size heuristics and as the default body. */
|
|
4
|
+
text: string;
|
|
5
|
+
/** `text/html` flavor when the source provided one. */
|
|
6
|
+
html?: string;
|
|
7
|
+
}
|
|
8
|
+
/** Read the relevant clipboard flavors from a paste/drop DataTransfer. */
|
|
9
|
+
export declare function readClipboardPaste(data: {
|
|
10
|
+
getData(type: string): string;
|
|
11
|
+
} | null | undefined): ClipboardPaste;
|
|
1
12
|
export declare function shouldConvertPasteToAttachment(text: string): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Whether a clipboard paste is large enough to become a `Pasted text`
|
|
15
|
+
* attachment chip. Mirrors `shouldConvertPasteToAttachment` but evaluates the
|
|
16
|
+
* representation we'd actually store, so an HTML-only paste (empty text/plain)
|
|
17
|
+
* still converts on the strength of its markup.
|
|
18
|
+
*/
|
|
19
|
+
export declare function shouldConvertClipboardToAttachment(paste: ClipboardPaste): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Build the attachment File for a page-sized paste, preserving HTML markup when
|
|
22
|
+
* the pasted content is an HTML document so it travels the same rail as an
|
|
23
|
+
* uploaded .html file.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createPastedAttachmentFile(paste: ClipboardPaste): File;
|
|
26
|
+
/** Back-compat helper for callers that only have plain text. */
|
|
2
27
|
export declare function createPastedTextFile(text: string): File;
|
|
3
28
|
export declare function isPastedTextAttachmentName(name: string | undefined): boolean;
|
|
4
29
|
export declare function unwrapAttachmentEnvelope(text: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pasted-text.d.ts","sourceRoot":"","sources":["../../../src/client/composer/pasted-text.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pasted-text.d.ts","sourceRoot":"","sources":["../../../src/client/composer/pasted-text.ts"],"names":[],"mappings":"AA4CA,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,0EAA0E;AAC1E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE;IAAE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GACzD,cAAc,CAIhB;AAmCD,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAWpE;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,cAAc,GACpB,OAAO,CAET;AAQD;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,CAGtE;AAED,gEAAgE;AAChE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE5E;AAKD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C"}
|
|
@@ -4,6 +4,67 @@
|
|
|
4
4
|
const PASTED_TEXT_MIN_CHARS = 3200;
|
|
5
5
|
const PASTED_TEXT_MIN_LINES = 24;
|
|
6
6
|
const PASTED_TEXT_FILENAME_PREFIX = "pasted-text-";
|
|
7
|
+
// A copied HTML document/source is recognizable from its markup: a closing tag
|
|
8
|
+
// (`</div>`), a doctype, or a common structural/element tag. We key off the
|
|
9
|
+
// *content* the user actually pasted, not the clipboard's `text/html` flavor —
|
|
10
|
+
// editors (VS Code) and rich-text apps (Google Docs) populate `text/html` with
|
|
11
|
+
// syntax-highlight spans or formatting wrappers even when the real content is
|
|
12
|
+
// plain code/prose, so trusting `text/html` blindly would mangle those pastes.
|
|
13
|
+
const HTML_SOURCE_SIGNAL = /<!doctype\s+html|<html[\s>]|<\/[a-z][a-z0-9-]*\s*>|<(?:body|head|div|span|section|main|header|footer|nav|article|aside|ul|ol|li|table|thead|tbody|tr|td|th|h[1-6]|p|a|img|button|input|textarea|select|form|label|script|style|link|meta|svg|canvas|template)\b/i;
|
|
14
|
+
// A real HTML *document* announces itself with a doctype / html / head / body.
|
|
15
|
+
// When one of these is present we keep the HTML classification even if an inline
|
|
16
|
+
// <script> contains JS keywords — it's a genuine page.
|
|
17
|
+
const HTML_DOCUMENT_SIGNAL = /<!doctype\s+html|<html[\s>]|<head[\s>]|<body[\s>]/i;
|
|
18
|
+
// JSX/TSX source contains the same `</div>`/`<span>` tags as an HTML document,
|
|
19
|
+
// so the HTML tag signal alone misfiles a pasted React/TS component (even a bare
|
|
20
|
+
// function component) as an `.html` artifact — the agent then mishandles it as a
|
|
21
|
+
// hostable document instead of source. These markers appear in JS/TS/JSX source
|
|
22
|
+
// but not in a plain HTML *fragment*: `className=` (JSX uses it; HTML uses
|
|
23
|
+
// `class=`), ES module import/export, arrow functions, TS type/React annotations,
|
|
24
|
+
// React hooks, and the basic JS declaration/return keywords that make up a
|
|
25
|
+
// component body.
|
|
26
|
+
const CODE_SOURCE_SIGNAL = /\bclassName=|\bimport\b|\bexport\b|=>|:\s*React\.|\buse[A-Z]\w*\(|\b(?:function|const|let|var|return|class|interface|type|enum)\b/;
|
|
27
|
+
function looksLikeHtml(value) {
|
|
28
|
+
if (!value || !HTML_SOURCE_SIGNAL.test(value))
|
|
29
|
+
return false;
|
|
30
|
+
// A self-announcing HTML document stays HTML even if it embeds a <script>.
|
|
31
|
+
if (HTML_DOCUMENT_SIGNAL.test(value))
|
|
32
|
+
return true;
|
|
33
|
+
// Otherwise it's a bare fragment — if it carries JS/TS/JSX code signals,
|
|
34
|
+
// treat it as source code, not an HTML attachment.
|
|
35
|
+
if (CODE_SOURCE_SIGNAL.test(value))
|
|
36
|
+
return false;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/** Read the relevant clipboard flavors from a paste/drop DataTransfer. */
|
|
40
|
+
export function readClipboardPaste(data) {
|
|
41
|
+
const text = data?.getData("text/plain") ?? "";
|
|
42
|
+
const html = data?.getData("text/html") ?? "";
|
|
43
|
+
return { text, html: html.trim() ? html : undefined };
|
|
44
|
+
}
|
|
45
|
+
// Decide what to actually store for a paste. Preserving HTML markup means a
|
|
46
|
+
// pasted HTML document behaves exactly like uploading that .html file: the agent
|
|
47
|
+
// recognizes it as a hostable artifact and reads it verbatim via
|
|
48
|
+
// `contentFromAttachment` instead of retyping it inline (which cuts off
|
|
49
|
+
// mid-stream on large files and triggers a continuation loop / "spin").
|
|
50
|
+
function selectPasteBody(paste) {
|
|
51
|
+
const plain = paste.text ?? "";
|
|
52
|
+
const html = paste.html ?? "";
|
|
53
|
+
// 1) The pasted text is itself HTML source (copied from an editor, a file, or
|
|
54
|
+
// view-source). The plain text *is* the markup, so keep it as .html.
|
|
55
|
+
if (plain.trim() && looksLikeHtml(plain)) {
|
|
56
|
+
return { body: plain, ext: "html", type: "text/html" };
|
|
57
|
+
}
|
|
58
|
+
// 2) No usable plain text, but a real HTML flavor exists (some apps only
|
|
59
|
+
// expose text/html). Fall back to the markup so nothing is dropped.
|
|
60
|
+
if (!plain.trim() && looksLikeHtml(html)) {
|
|
61
|
+
return { body: html, ext: "html", type: "text/html" };
|
|
62
|
+
}
|
|
63
|
+
// 3) Default: keep the clean plain text. Avoids the syntax-highlight /
|
|
64
|
+
// rich-text noise that lives in text/html when the plain text is already
|
|
65
|
+
// the real content (code from an editor, prose from a doc).
|
|
66
|
+
return { body: plain, ext: "txt", type: "text/plain" };
|
|
67
|
+
}
|
|
7
68
|
export function shouldConvertPasteToAttachment(text) {
|
|
8
69
|
if (!text)
|
|
9
70
|
return false;
|
|
@@ -19,11 +80,32 @@ export function shouldConvertPasteToAttachment(text) {
|
|
|
19
80
|
}
|
|
20
81
|
return false;
|
|
21
82
|
}
|
|
22
|
-
|
|
23
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Whether a clipboard paste is large enough to become a `Pasted text`
|
|
85
|
+
* attachment chip. Mirrors `shouldConvertPasteToAttachment` but evaluates the
|
|
86
|
+
* representation we'd actually store, so an HTML-only paste (empty text/plain)
|
|
87
|
+
* still converts on the strength of its markup.
|
|
88
|
+
*/
|
|
89
|
+
export function shouldConvertClipboardToAttachment(paste) {
|
|
90
|
+
return shouldConvertPasteToAttachment(selectPasteBody(paste).body);
|
|
91
|
+
}
|
|
92
|
+
function pastedAttachmentName(ext) {
|
|
93
|
+
return `${PASTED_TEXT_FILENAME_PREFIX}${Date.now()}-${Math.random()
|
|
24
94
|
.toString(36)
|
|
25
|
-
.slice(2, 8)}
|
|
26
|
-
|
|
95
|
+
.slice(2, 8)}.${ext}`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Build the attachment File for a page-sized paste, preserving HTML markup when
|
|
99
|
+
* the pasted content is an HTML document so it travels the same rail as an
|
|
100
|
+
* uploaded .html file.
|
|
101
|
+
*/
|
|
102
|
+
export function createPastedAttachmentFile(paste) {
|
|
103
|
+
const { body, ext, type } = selectPasteBody(paste);
|
|
104
|
+
return new File([body], pastedAttachmentName(ext), { type });
|
|
105
|
+
}
|
|
106
|
+
/** Back-compat helper for callers that only have plain text. */
|
|
107
|
+
export function createPastedTextFile(text) {
|
|
108
|
+
return createPastedAttachmentFile({ text });
|
|
27
109
|
}
|
|
28
110
|
export function isPastedTextAttachmentName(name) {
|
|
29
111
|
return !!name && name.startsWith(PASTED_TEXT_FILENAME_PREFIX);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pasted-text.js","sourceRoot":"","sources":["../../../src/client/composer/pasted-text.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,0EAA0E;AAC1E,+DAA+D;AAC/D,MAAM,qBAAqB,GAAG,IAAI,CAAC;AACnC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,MAAM,2BAA2B,GAAG,cAAc,CAAC;AAEnD,MAAM,UAAU,8BAA8B,CAAC,IAAY;IACzD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,IAAI,IAAI,CAAC,MAAM,IAAI,qBAAqB;QAAE,OAAO,IAAI,CAAC;IACtD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;YAC9B,KAAK,EAAE,CAAC;YACR,IAAI,KAAK,IAAI,qBAAqB;gBAAE,OAAO,IAAI,CAAC;QAClD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,MAAM,IAAI,GAAG,GAAG,2BAA2B,GAAG,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE;SACtE,QAAQ,CAAC,EAAE,CAAC;SACZ,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC;IACrB,OAAO,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,IAAwB;IACjE,OAAO,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,2BAA2B,CAAC,CAAC;AAChE,CAAC;AAED,yEAAyE;AACzE,+EAA+E;AAC/E,2BAA2B;AAC3B,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAC7E,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE;YAAE,KAAK,EAAE,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["// Page-sized pastes turn into a `Pasted text` attachment chip instead of being\n// dumped into the editor. Short paragraphs and everyday lists should stay\n// inline so the composer still feels like a normal text field.\nconst PASTED_TEXT_MIN_CHARS = 3200;\nconst PASTED_TEXT_MIN_LINES = 24;\n\nconst PASTED_TEXT_FILENAME_PREFIX = \"pasted-text-\";\n\nexport function shouldConvertPasteToAttachment(text: string): boolean {\n if (!text) return false;\n if (text.length >= PASTED_TEXT_MIN_CHARS) return true;\n let lines = 1;\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) === 10) {\n lines++;\n if (lines >= PASTED_TEXT_MIN_LINES) return true;\n }\n }\n return false;\n}\n\nexport function createPastedTextFile(text: string): File {\n const name = `${PASTED_TEXT_FILENAME_PREFIX}${Date.now()}-${Math.random()\n .toString(36)\n .slice(2, 8)}.txt`;\n return new File([text], name, { type: \"text/plain\" });\n}\n\nexport function isPastedTextAttachmentName(name: string | undefined): boolean {\n return !!name && name.startsWith(PASTED_TEXT_FILENAME_PREFIX);\n}\n\n// Strips the `<attachment name=...>\\n` / `\\n</attachment>` envelope that\n// SimpleTextAttachmentAdapter wraps the file body in when sending. Returns the\n// raw body for previewing.\nexport function unwrapAttachmentEnvelope(text: string): string {\n const match = text.match(/^<attachment\\b[^>]*>\\n([\\s\\S]*)\\n<\\/attachment>$/);\n return match ? match[1] : text;\n}\n\nexport function countLines(text: string): number {\n if (!text) return 0;\n let lines = 1;\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) === 10) lines++;\n }\n return lines;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pasted-text.js","sourceRoot":"","sources":["../../../src/client/composer/pasted-text.ts"],"names":[],"mappings":"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","sourcesContent":["// Page-sized pastes turn into a `Pasted text` attachment chip instead of being\n// dumped into the editor. Short paragraphs and everyday lists should stay\n// inline so the composer still feels like a normal text field.\nconst PASTED_TEXT_MIN_CHARS = 3200;\nconst PASTED_TEXT_MIN_LINES = 24;\n\nconst PASTED_TEXT_FILENAME_PREFIX = \"pasted-text-\";\n\n// A copied HTML document/source is recognizable from its markup: a closing tag\n// (`</div>`), a doctype, or a common structural/element tag. We key off the\n// *content* the user actually pasted, not the clipboard's `text/html` flavor —\n// editors (VS Code) and rich-text apps (Google Docs) populate `text/html` with\n// syntax-highlight spans or formatting wrappers even when the real content is\n// plain code/prose, so trusting `text/html` blindly would mangle those pastes.\nconst HTML_SOURCE_SIGNAL =\n /<!doctype\\s+html|<html[\\s>]|<\\/[a-z][a-z0-9-]*\\s*>|<(?:body|head|div|span|section|main|header|footer|nav|article|aside|ul|ol|li|table|thead|tbody|tr|td|th|h[1-6]|p|a|img|button|input|textarea|select|form|label|script|style|link|meta|svg|canvas|template)\\b/i;\n\n// A real HTML *document* announces itself with a doctype / html / head / body.\n// When one of these is present we keep the HTML classification even if an inline\n// <script> contains JS keywords — it's a genuine page.\nconst HTML_DOCUMENT_SIGNAL =\n /<!doctype\\s+html|<html[\\s>]|<head[\\s>]|<body[\\s>]/i;\n\n// JSX/TSX source contains the same `</div>`/`<span>` tags as an HTML document,\n// so the HTML tag signal alone misfiles a pasted React/TS component (even a bare\n// function component) as an `.html` artifact — the agent then mishandles it as a\n// hostable document instead of source. These markers appear in JS/TS/JSX source\n// but not in a plain HTML *fragment*: `className=` (JSX uses it; HTML uses\n// `class=`), ES module import/export, arrow functions, TS type/React annotations,\n// React hooks, and the basic JS declaration/return keywords that make up a\n// component body.\nconst CODE_SOURCE_SIGNAL =\n /\\bclassName=|\\bimport\\b|\\bexport\\b|=>|:\\s*React\\.|\\buse[A-Z]\\w*\\(|\\b(?:function|const|let|var|return|class|interface|type|enum)\\b/;\n\nfunction looksLikeHtml(value: string): boolean {\n if (!value || !HTML_SOURCE_SIGNAL.test(value)) return false;\n // A self-announcing HTML document stays HTML even if it embeds a <script>.\n if (HTML_DOCUMENT_SIGNAL.test(value)) return true;\n // Otherwise it's a bare fragment — if it carries JS/TS/JSX code signals,\n // treat it as source code, not an HTML attachment.\n if (CODE_SOURCE_SIGNAL.test(value)) return false;\n return true;\n}\n\n/** The clipboard flavors we care about for a paste. */\nexport interface ClipboardPaste {\n /** `text/plain` flavor — used for size heuristics and as the default body. */\n text: string;\n /** `text/html` flavor when the source provided one. */\n html?: string;\n}\n\n/** Read the relevant clipboard flavors from a paste/drop DataTransfer. */\nexport function readClipboardPaste(\n data: { getData(type: string): string } | null | undefined,\n): ClipboardPaste {\n const text = data?.getData(\"text/plain\") ?? \"\";\n const html = data?.getData(\"text/html\") ?? \"\";\n return { text, html: html.trim() ? html : undefined };\n}\n\ninterface SelectedPasteBody {\n body: string;\n ext: \"html\" | \"txt\";\n type: \"text/html\" | \"text/plain\";\n}\n\n// Decide what to actually store for a paste. Preserving HTML markup means a\n// pasted HTML document behaves exactly like uploading that .html file: the agent\n// recognizes it as a hostable artifact and reads it verbatim via\n// `contentFromAttachment` instead of retyping it inline (which cuts off\n// mid-stream on large files and triggers a continuation loop / \"spin\").\nfunction selectPasteBody(paste: ClipboardPaste): SelectedPasteBody {\n const plain = paste.text ?? \"\";\n const html = paste.html ?? \"\";\n\n // 1) The pasted text is itself HTML source (copied from an editor, a file, or\n // view-source). The plain text *is* the markup, so keep it as .html.\n if (plain.trim() && looksLikeHtml(plain)) {\n return { body: plain, ext: \"html\", type: \"text/html\" };\n }\n\n // 2) No usable plain text, but a real HTML flavor exists (some apps only\n // expose text/html). Fall back to the markup so nothing is dropped.\n if (!plain.trim() && looksLikeHtml(html)) {\n return { body: html, ext: \"html\", type: \"text/html\" };\n }\n\n // 3) Default: keep the clean plain text. Avoids the syntax-highlight /\n // rich-text noise that lives in text/html when the plain text is already\n // the real content (code from an editor, prose from a doc).\n return { body: plain, ext: \"txt\", type: \"text/plain\" };\n}\n\nexport function shouldConvertPasteToAttachment(text: string): boolean {\n if (!text) return false;\n if (text.length >= PASTED_TEXT_MIN_CHARS) return true;\n let lines = 1;\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) === 10) {\n lines++;\n if (lines >= PASTED_TEXT_MIN_LINES) return true;\n }\n }\n return false;\n}\n\n/**\n * Whether a clipboard paste is large enough to become a `Pasted text`\n * attachment chip. Mirrors `shouldConvertPasteToAttachment` but evaluates the\n * representation we'd actually store, so an HTML-only paste (empty text/plain)\n * still converts on the strength of its markup.\n */\nexport function shouldConvertClipboardToAttachment(\n paste: ClipboardPaste,\n): boolean {\n return shouldConvertPasteToAttachment(selectPasteBody(paste).body);\n}\n\nfunction pastedAttachmentName(ext: \"html\" | \"txt\"): string {\n return `${PASTED_TEXT_FILENAME_PREFIX}${Date.now()}-${Math.random()\n .toString(36)\n .slice(2, 8)}.${ext}`;\n}\n\n/**\n * Build the attachment File for a page-sized paste, preserving HTML markup when\n * the pasted content is an HTML document so it travels the same rail as an\n * uploaded .html file.\n */\nexport function createPastedAttachmentFile(paste: ClipboardPaste): File {\n const { body, ext, type } = selectPasteBody(paste);\n return new File([body], pastedAttachmentName(ext), { type });\n}\n\n/** Back-compat helper for callers that only have plain text. */\nexport function createPastedTextFile(text: string): File {\n return createPastedAttachmentFile({ text });\n}\n\nexport function isPastedTextAttachmentName(name: string | undefined): boolean {\n return !!name && name.startsWith(PASTED_TEXT_FILENAME_PREFIX);\n}\n\n// Strips the `<attachment name=...>\\n` / `\\n</attachment>` envelope that\n// SimpleTextAttachmentAdapter wraps the file body in when sending. Returns the\n// raw body for previewing.\nexport function unwrapAttachmentEnvelope(text: string): string {\n const match = text.match(/^<attachment\\b[^>]*>\\n([\\s\\S]*)\\n<\\/attachment>$/);\n return match ? match[1] : text;\n}\n\nexport function countLines(text: string): number {\n if (!text) return 0;\n let lines = 1;\n for (let i = 0; i < text.length; i++) {\n if (text.charCodeAt(i) === 10) lines++;\n }\n return lines;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DragHandle.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/DragHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQzC,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,oCAAoC,2BAA2B,CAAC;AAE7E,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;OAQG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC9B,IAAI,EAAE,UAAU,CAAC;QACjB,IAAI,EAAE,eAAe,CAAC;QACtB,GAAG,EAAE,MAAM,CAAC;KACb,KAAK,OAAO,CAAC;IACd;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,CACxB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE;QACP,IAAI,EAAE,UAAU,CAAC;QACjB,IAAI,EAAE,eAAe,CAAC;QACtB,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,UAAU,CAAC;KACxB,KACE,IAAI,CAAC;IACV;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC;CACzE;
|
|
1
|
+
{"version":3,"file":"DragHandle.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/DragHandle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAQzC,OAAO,KAAK,EAAE,IAAI,IAAI,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,oCAAoC,2BAA2B,CAAC;AAE7E,MAAM,WAAW,iBAAiB;IAChC;;;;;;;;OAQG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC9B,IAAI,EAAE,UAAU,CAAC;QACjB,IAAI,EAAE,eAAe,CAAC;QACtB,GAAG,EAAE,MAAM,CAAC;KACb,KAAK,OAAO,CAAC;IACd;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,CACxB,IAAI,EAAE,OAAO,EACb,OAAO,EAAE;QACP,IAAI,EAAE,UAAU,CAAC;QACjB,IAAI,EAAE,eAAe,CAAC;QACtB,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,UAAU,CAAC;KACxB,KACE,IAAI,CAAC;IACV;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC;CACzE;AA8DD,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAE5E,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,eAAe,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,uBAAuB,CAAC;CACpC,CAAC;AAuSF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,UAAU,mCA48BrB,CAAC"}
|
|
@@ -33,6 +33,20 @@ const DRAG_HANDLE_MENU_STYLE_ID = "an-rich-md-drag-menu-styles";
|
|
|
33
33
|
const DRAG_HANDLE_MENU_WIDTH = 220;
|
|
34
34
|
const DRAG_HANDLE_MENU_GAP = 6;
|
|
35
35
|
const DRAG_HANDLE_MENU_VIEWPORT_PADDING = 8;
|
|
36
|
+
/**
|
|
37
|
+
* Wraps Tabler outline icon path data in the standard 24×24 stroke SVG so the
|
|
38
|
+
* DOM-based block menu renders the same icons the React UI uses (Tabler is the
|
|
39
|
+
* framework-wide icon set). The editor is plain DOM, not React, so we inline the
|
|
40
|
+
* markup instead of importing `@tabler/icons-react` components.
|
|
41
|
+
*/
|
|
42
|
+
const tablerIconSvg = (paths) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">${paths}</svg>`;
|
|
43
|
+
// Tabler `copy`, `trash`, and `plus` (outline). Path data copied verbatim from
|
|
44
|
+
// @tabler/icons so the glyphs stay pixel-identical to the React icon set.
|
|
45
|
+
const DRAG_HANDLE_MENU_ICON_DUPLICATE = tablerIconSvg('<path d="M7 9.667a2.667 2.667 0 0 1 2.667 -2.667h8.666a2.667 2.667 0 0 1 2.667 2.667v8.666a2.667 2.667 0 0 1 -2.667 2.667h-8.666a2.667 2.667 0 0 1 -2.667 -2.667l0 -8.666" /><path d="M4.012 16.737a2.005 2.005 0 0 1 -1.012 -1.737v-10c0 -1.1 .9 -2 2 -2h10c.75 0 1.158 .385 1.5 1" />');
|
|
46
|
+
const DRAG_HANDLE_MENU_ICON_DELETE = tablerIconSvg('<path d="M4 7l16 0" /><path d="M10 11l0 6" /><path d="M14 11l0 6" /><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12" /><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3" />');
|
|
47
|
+
const DRAG_HANDLE_MENU_ICON_INSERT = tablerIconSvg('<path d="M12 5l0 14" /><path d="M5 12l14 0" />');
|
|
48
|
+
// Tabler `grip-vertical` (outline) for the left-margin drag grip.
|
|
49
|
+
const DRAG_HANDLE_GRIP_ICON = tablerIconSvg('<path d="M8 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M8 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M8 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M14 5a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M14 12a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /><path d="M14 19a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />');
|
|
36
50
|
const dragHandleRegistrations = new Set();
|
|
37
51
|
let dragHandleGlobalHoverListeners = 0;
|
|
38
52
|
let activeDragRegistration = null;
|
|
@@ -223,8 +237,10 @@ const ensureDragHandleMenuStyles = () => {
|
|
|
223
237
|
}
|
|
224
238
|
|
|
225
239
|
.an-rich-md-drag-menu__icon {
|
|
226
|
-
|
|
240
|
+
display: flex;
|
|
227
241
|
flex: 0 0 auto;
|
|
242
|
+
align-items: center;
|
|
243
|
+
justify-content: center;
|
|
228
244
|
width: 18px;
|
|
229
245
|
height: 18px;
|
|
230
246
|
color: hsl(var(--muted-foreground, 215.4 16.3% 46.9%));
|
|
@@ -234,69 +250,9 @@ const ensureDragHandleMenuStyles = () => {
|
|
|
234
250
|
color: currentColor;
|
|
235
251
|
}
|
|
236
252
|
|
|
237
|
-
.an-rich-md-drag-menu__icon
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
position: absolute;
|
|
241
|
-
box-sizing: border-box;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.an-rich-md-drag-menu__icon--duplicate::before,
|
|
245
|
-
.an-rich-md-drag-menu__icon--duplicate::after {
|
|
246
|
-
width: 11px;
|
|
247
|
-
height: 11px;
|
|
248
|
-
border: 1.5px solid currentColor;
|
|
249
|
-
border-radius: 2px;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.an-rich-md-drag-menu__icon--duplicate::before {
|
|
253
|
-
left: 6px;
|
|
254
|
-
top: 2px;
|
|
255
|
-
opacity: 0.55;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
.an-rich-md-drag-menu__icon--duplicate::after {
|
|
259
|
-
left: 2px;
|
|
260
|
-
top: 6px;
|
|
261
|
-
background: hsl(var(--popover, 0 0% 100%));
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.an-rich-md-drag-menu__icon--insert::before {
|
|
265
|
-
left: 3px;
|
|
266
|
-
top: 8px;
|
|
267
|
-
width: 12px;
|
|
268
|
-
height: 1.5px;
|
|
269
|
-
border-radius: 999px;
|
|
270
|
-
background: currentColor;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
.an-rich-md-drag-menu__icon--insert::after {
|
|
274
|
-
left: 8px;
|
|
275
|
-
top: 3px;
|
|
276
|
-
width: 1.5px;
|
|
277
|
-
height: 12px;
|
|
278
|
-
border-radius: 999px;
|
|
279
|
-
background: currentColor;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.an-rich-md-drag-menu__icon--delete::before {
|
|
283
|
-
left: 4px;
|
|
284
|
-
top: 7px;
|
|
285
|
-
width: 10px;
|
|
286
|
-
height: 11px;
|
|
287
|
-
border: 1.5px solid currentColor;
|
|
288
|
-
border-top: 0;
|
|
289
|
-
border-radius: 0 0 2px 2px;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
.an-rich-md-drag-menu__icon--delete::after {
|
|
293
|
-
left: 3px;
|
|
294
|
-
top: 4px;
|
|
295
|
-
width: 12px;
|
|
296
|
-
height: 1.5px;
|
|
297
|
-
border-radius: 999px;
|
|
298
|
-
background: currentColor;
|
|
299
|
-
box-shadow: 3px -2.5px 0 -0.4px currentColor;
|
|
253
|
+
.an-rich-md-drag-menu__icon svg {
|
|
254
|
+
width: 17px;
|
|
255
|
+
height: 17px;
|
|
300
256
|
}
|
|
301
257
|
|
|
302
258
|
.an-rich-md-drag-menu__label {
|
|
@@ -587,7 +543,7 @@ export const DragHandle = Extension.create({
|
|
|
587
543
|
DRAG_HANDLE_MENU_VIEWPORT_PADDING)}px`;
|
|
588
544
|
menu.style.top = `${clamp(anchorRect.top - 4, DRAG_HANDLE_MENU_VIEWPORT_PADDING, viewportHeight - menuHeight - DRAG_HANDLE_MENU_VIEWPORT_PADDING)}px`;
|
|
589
545
|
};
|
|
590
|
-
const createMenuItem = (label,
|
|
546
|
+
const createMenuItem = (label, iconSvg, action, options = {}) => {
|
|
591
547
|
const button = document.createElement("button");
|
|
592
548
|
button.type = "button";
|
|
593
549
|
button.className = "an-rich-md-drag-menu__item";
|
|
@@ -596,8 +552,9 @@ export const DragHandle = Extension.create({
|
|
|
596
552
|
if (options.danger)
|
|
597
553
|
button.setAttribute("data-danger", "true");
|
|
598
554
|
const icon = document.createElement("span");
|
|
599
|
-
icon.className =
|
|
555
|
+
icon.className = "an-rich-md-drag-menu__icon";
|
|
600
556
|
icon.setAttribute("aria-hidden", "true");
|
|
557
|
+
icon.innerHTML = iconSvg;
|
|
601
558
|
const labelElement = document.createElement("span");
|
|
602
559
|
labelElement.className = "an-rich-md-drag-menu__label";
|
|
603
560
|
labelElement.textContent = label;
|
|
@@ -628,7 +585,9 @@ export const DragHandle = Extension.create({
|
|
|
628
585
|
el.setAttribute("role", "menu");
|
|
629
586
|
el.setAttribute("aria-label", "Block actions");
|
|
630
587
|
el.setAttribute("data-plan-interactive", "true");
|
|
631
|
-
el.append(createMenuItem("Duplicate",
|
|
588
|
+
el.append(createMenuItem("Duplicate", DRAG_HANDLE_MENU_ICON_DUPLICATE, duplicateBlock), createMenuItem("Delete", DRAG_HANDLE_MENU_ICON_DELETE, deleteBlock, {
|
|
589
|
+
danger: true,
|
|
590
|
+
}), createMenuItem("Insert block below", DRAG_HANDLE_MENU_ICON_INSERT, insertParagraphBelow));
|
|
632
591
|
menu = el;
|
|
633
592
|
menuContext = {
|
|
634
593
|
view: resolved.view,
|
|
@@ -796,11 +755,15 @@ export const DragHandle = Extension.create({
|
|
|
796
755
|
// grip DIV — so a press on the icon gets swallowed and the block can't be
|
|
797
756
|
// dragged out of / between columns. `pointer-events:none` makes every
|
|
798
757
|
// press in the grip area resolve to the DIV instead.
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
758
|
+
// Tabler `grip-vertical` (the framework-wide icon set). `pointer-events:none`
|
|
759
|
+
// keeps every press in the grip area resolving to the DIV, not the SVG.
|
|
760
|
+
el.innerHTML = DRAG_HANDLE_GRIP_ICON;
|
|
761
|
+
const gripSvg = el.querySelector("svg");
|
|
762
|
+
if (gripSvg) {
|
|
763
|
+
gripSvg.setAttribute("width", "16");
|
|
764
|
+
gripSvg.setAttribute("height", "16");
|
|
765
|
+
gripSvg.style.pointerEvents = "none";
|
|
766
|
+
}
|
|
804
767
|
return el;
|
|
805
768
|
};
|
|
806
769
|
const hideHandle = () => {
|