@kedataindo/docflow-plugins 0.0.31 → 0.0.33
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/index.cjs +561 -17
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +541 -5
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -853,7 +853,91 @@ var highlightPlugin = definePlugin16({
|
|
|
853
853
|
import { Extension as Extension2 } from "@tiptap/core";
|
|
854
854
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
855
855
|
import { Decoration, DecorationSet } from "@tiptap/pm/view";
|
|
856
|
+
import { Markdown } from "tiptap-markdown";
|
|
856
857
|
import { definePlugin as definePlugin17 } from "@kedataindo/docflow-core";
|
|
858
|
+
|
|
859
|
+
// src/markdownInsert.ts
|
|
860
|
+
import { createNodeFromContent } from "@tiptap/core";
|
|
861
|
+
import { Fragment } from "@tiptap/pm/model";
|
|
862
|
+
import { Selection } from "@tiptap/pm/state";
|
|
863
|
+
import { ReplaceAroundStep, ReplaceStep } from "@tiptap/pm/transform";
|
|
864
|
+
function mdParser(editor) {
|
|
865
|
+
const storage = editor.storage.markdown;
|
|
866
|
+
if (!storage || typeof storage !== "object") return void 0;
|
|
867
|
+
const parser = storage.parser;
|
|
868
|
+
if (!parser || typeof parser !== "object" || !("parse" in parser)) return void 0;
|
|
869
|
+
return parser;
|
|
870
|
+
}
|
|
871
|
+
function markdownToFragment(editor, markdown, opts) {
|
|
872
|
+
const parser = mdParser(editor);
|
|
873
|
+
if (!parser) return null;
|
|
874
|
+
let html;
|
|
875
|
+
try {
|
|
876
|
+
html = parser.parse(markdown, { inline: opts.inline });
|
|
877
|
+
} catch {
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
if (typeof html !== "string") return null;
|
|
881
|
+
const content = createNodeFromContent(html, editor.schema, {
|
|
882
|
+
slice: true,
|
|
883
|
+
parseOptions: { preserveWhitespace: "full" }
|
|
884
|
+
});
|
|
885
|
+
if (content instanceof Fragment) return content;
|
|
886
|
+
return Fragment.from(content);
|
|
887
|
+
}
|
|
888
|
+
function selectionToInsertionEnd(tr, startLen, bias) {
|
|
889
|
+
const last = tr.steps.length - 1;
|
|
890
|
+
if (last < startLen) return;
|
|
891
|
+
const step = tr.steps[last];
|
|
892
|
+
if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) return;
|
|
893
|
+
const map = tr.mapping.maps[last];
|
|
894
|
+
let end = 0;
|
|
895
|
+
map.forEach((_from, _to, _newFrom, newTo) => {
|
|
896
|
+
if (end === 0) end = newTo;
|
|
897
|
+
});
|
|
898
|
+
tr.setSelection(Selection.near(tr.doc.resolve(end), bias));
|
|
899
|
+
}
|
|
900
|
+
function insertMarkdownBlock(editor, view, from, to, markdown) {
|
|
901
|
+
const fragment = markdownToFragment(editor, markdown, { inline: false });
|
|
902
|
+
if (!fragment) {
|
|
903
|
+
view.dispatch(view.state.tr.insertText(markdown, from, to));
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const tr = view.state.tr;
|
|
907
|
+
let f = from;
|
|
908
|
+
let t = to;
|
|
909
|
+
if (f === t) {
|
|
910
|
+
let onlyBlock = true;
|
|
911
|
+
fragment.forEach((n) => {
|
|
912
|
+
if (!n.isBlock) onlyBlock = false;
|
|
913
|
+
});
|
|
914
|
+
if (onlyBlock) {
|
|
915
|
+
const $pos = tr.doc.resolve(f);
|
|
916
|
+
const parent = $pos.parent;
|
|
917
|
+
if (parent.isTextblock && !parent.type.spec.code && !parent.childCount) {
|
|
918
|
+
f -= 1;
|
|
919
|
+
t += 1;
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
let onlyText = true;
|
|
924
|
+
fragment.forEach((n) => {
|
|
925
|
+
if (!n.isText || n.marks.length > 0) onlyText = false;
|
|
926
|
+
});
|
|
927
|
+
if (onlyText) {
|
|
928
|
+
let text = "";
|
|
929
|
+
fragment.forEach((n) => {
|
|
930
|
+
if (n.isText) text += n.text ?? "";
|
|
931
|
+
});
|
|
932
|
+
tr.insertText(text, f, t);
|
|
933
|
+
} else {
|
|
934
|
+
tr.replaceWith(f, t, fragment);
|
|
935
|
+
}
|
|
936
|
+
selectionToInsertionEnd(tr, 0, -1);
|
|
937
|
+
view.dispatch(tr);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/ai.ts
|
|
857
941
|
var aiPluginKey = new PluginKey("docflow-ai");
|
|
858
942
|
var CONTEXT_CHARS = 1500;
|
|
859
943
|
function getAIPreview(editor) {
|
|
@@ -991,7 +1075,12 @@ var AIExtension = Extension2.create({
|
|
|
991
1075
|
if (!dispatch) return true;
|
|
992
1076
|
const tr = state.tr;
|
|
993
1077
|
if (preview.text.trim()) {
|
|
994
|
-
|
|
1078
|
+
const fragment = markdownToFragment(editor, preview.text, { inline: true });
|
|
1079
|
+
if (fragment) {
|
|
1080
|
+
tr.replaceWith(preview.from, preview.to, fragment);
|
|
1081
|
+
} else {
|
|
1082
|
+
tr.replaceWith(preview.from, preview.to, state.schema.text(preview.text));
|
|
1083
|
+
}
|
|
995
1084
|
}
|
|
996
1085
|
tr.setMeta(aiPluginKey, { type: "clear" });
|
|
997
1086
|
dispatch(tr);
|
|
@@ -1203,7 +1292,15 @@ var AIExtension = Extension2.create({
|
|
|
1203
1292
|
});
|
|
1204
1293
|
var aiPlugin = definePlugin17({
|
|
1205
1294
|
id: "ai",
|
|
1206
|
-
|
|
1295
|
+
// The `tiptap-markdown` `Markdown` extension is registered alongside AI so
|
|
1296
|
+
// that `editor.storage.markdown.parser` is available on every editor that
|
|
1297
|
+
// can stream AI content. It adds a schema-aware markdown↔HTML bridge
|
|
1298
|
+
// (markdown-it under the hood) used by the AI Insert/accept paths to turn
|
|
1299
|
+
// streamed `| col | col |` tables / `# headings` / `**bold**` into real
|
|
1300
|
+
// nodes instead of literal pipe/asterisk text. Its `insertContentAt` /
|
|
1301
|
+
// `setContent` command overrides are intentionally NOT used by the AI
|
|
1302
|
+
// paths (they force `inline:true`); see `markdownInsert.ts`.
|
|
1303
|
+
tiptapExtensions: [AIExtension, Markdown.configure({ html: true, linkify: true, breaks: false })],
|
|
1207
1304
|
slashCommands: [{ name: "AI", description: "Generate text with AI", command: "aiGenerate" }]
|
|
1208
1305
|
});
|
|
1209
1306
|
|
|
@@ -1247,10 +1344,440 @@ var commentPlugin = {
|
|
|
1247
1344
|
commands: {}
|
|
1248
1345
|
};
|
|
1249
1346
|
|
|
1347
|
+
// src/smartElements.ts
|
|
1348
|
+
import { Node as Node5, mergeAttributes as mergeAttributes6 } from "@tiptap/core";
|
|
1349
|
+
import { definePlugin as definePlugin18 } from "@kedataindo/docflow-core";
|
|
1350
|
+
function todayISO() {
|
|
1351
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1352
|
+
}
|
|
1353
|
+
function formatDate(iso) {
|
|
1354
|
+
if (!iso) return "Pick a date";
|
|
1355
|
+
const d = /* @__PURE__ */ new Date(iso + "T00:00:00");
|
|
1356
|
+
if (isNaN(d.getTime())) return "Pick a date";
|
|
1357
|
+
return d.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
|
|
1358
|
+
}
|
|
1359
|
+
function initialOf(name) {
|
|
1360
|
+
return (name || "?").trim().charAt(0).toUpperCase() || "?";
|
|
1361
|
+
}
|
|
1362
|
+
var DEFAULT_STATUSES = ["To Do", "In Progress", "Review", "Approved"];
|
|
1363
|
+
function slugify(s) {
|
|
1364
|
+
return s.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
|
|
1365
|
+
}
|
|
1366
|
+
var DateChipNode = Node5.create({
|
|
1367
|
+
name: "dateChip",
|
|
1368
|
+
group: "inline",
|
|
1369
|
+
inline: true,
|
|
1370
|
+
selectable: true,
|
|
1371
|
+
draggable: false,
|
|
1372
|
+
atom: true,
|
|
1373
|
+
addAttributes() {
|
|
1374
|
+
return {
|
|
1375
|
+
date: {
|
|
1376
|
+
default: todayISO(),
|
|
1377
|
+
parseHTML: (el) => el.getAttribute("data-date") ?? todayISO(),
|
|
1378
|
+
renderHTML: (attrs) => ({ "data-date": attrs.date ?? "" })
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
},
|
|
1382
|
+
parseHTML() {
|
|
1383
|
+
return [{ tag: 'span[data-node-type="date-chip"]' }];
|
|
1384
|
+
},
|
|
1385
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
1386
|
+
return ["span", mergeAttributes6(HTMLAttributes, {
|
|
1387
|
+
"data-node-type": "date-chip",
|
|
1388
|
+
class: "docs-chip docs-chip--date"
|
|
1389
|
+
}), formatDate(node.attrs.date)];
|
|
1390
|
+
},
|
|
1391
|
+
addNodeView() {
|
|
1392
|
+
return (props) => {
|
|
1393
|
+
let currentNode = props.node;
|
|
1394
|
+
const getPos = props.getPos;
|
|
1395
|
+
const view = props.view;
|
|
1396
|
+
const dom = document.createElement("span");
|
|
1397
|
+
dom.className = "docs-chip docs-chip--date";
|
|
1398
|
+
dom.setAttribute("data-node-type", "date-chip");
|
|
1399
|
+
dom.setAttribute("contenteditable", "false");
|
|
1400
|
+
const render = () => {
|
|
1401
|
+
if (dom.querySelector("input")) return;
|
|
1402
|
+
dom.textContent = formatDate(currentNode.attrs.date);
|
|
1403
|
+
dom.setAttribute("data-date", currentNode.attrs.date);
|
|
1404
|
+
};
|
|
1405
|
+
render();
|
|
1406
|
+
dom.addEventListener("click", (e) => {
|
|
1407
|
+
e.preventDefault();
|
|
1408
|
+
e.stopPropagation();
|
|
1409
|
+
if (dom.querySelector("input")) return;
|
|
1410
|
+
const input = document.createElement("input");
|
|
1411
|
+
input.type = "date";
|
|
1412
|
+
input.value = currentNode.attrs.date;
|
|
1413
|
+
input.className = "docs-chip-date-input";
|
|
1414
|
+
dom.replaceChildren(input);
|
|
1415
|
+
input.focus();
|
|
1416
|
+
const commit = () => {
|
|
1417
|
+
const newVal = input.value;
|
|
1418
|
+
const pos = getPos();
|
|
1419
|
+
if (typeof pos === "number") {
|
|
1420
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, void 0, { date: newVal }));
|
|
1421
|
+
}
|
|
1422
|
+
setTimeout(render, 30);
|
|
1423
|
+
};
|
|
1424
|
+
input.addEventListener("change", commit);
|
|
1425
|
+
input.addEventListener("blur", () => setTimeout(render, 30));
|
|
1426
|
+
});
|
|
1427
|
+
return {
|
|
1428
|
+
dom,
|
|
1429
|
+
update(updatedNode) {
|
|
1430
|
+
if (updatedNode.type.name !== "dateChip") return false;
|
|
1431
|
+
currentNode = updatedNode;
|
|
1432
|
+
render();
|
|
1433
|
+
return true;
|
|
1434
|
+
}
|
|
1435
|
+
};
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
});
|
|
1439
|
+
var PeopleChipNode = Node5.create({
|
|
1440
|
+
name: "peopleChip",
|
|
1441
|
+
group: "inline",
|
|
1442
|
+
inline: true,
|
|
1443
|
+
selectable: true,
|
|
1444
|
+
draggable: false,
|
|
1445
|
+
atom: true,
|
|
1446
|
+
addAttributes() {
|
|
1447
|
+
return {
|
|
1448
|
+
userId: {
|
|
1449
|
+
default: "",
|
|
1450
|
+
parseHTML: (el) => el.getAttribute("data-user-id") ?? "",
|
|
1451
|
+
renderHTML: (attrs) => attrs.userId ? { "data-user-id": attrs.userId } : {}
|
|
1452
|
+
},
|
|
1453
|
+
name: {
|
|
1454
|
+
default: "",
|
|
1455
|
+
parseHTML: (el) => el.getAttribute("data-name") ?? "",
|
|
1456
|
+
renderHTML: (attrs) => ({ "data-name": attrs.name ?? "" })
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
},
|
|
1460
|
+
parseHTML() {
|
|
1461
|
+
return [{ tag: 'span[data-node-type="people-chip"]' }];
|
|
1462
|
+
},
|
|
1463
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
1464
|
+
return ["span", mergeAttributes6(HTMLAttributes, {
|
|
1465
|
+
"data-node-type": "people-chip",
|
|
1466
|
+
class: "docs-chip docs-chip--people"
|
|
1467
|
+
}), `@${node.attrs.name || "user"}`];
|
|
1468
|
+
},
|
|
1469
|
+
addNodeView() {
|
|
1470
|
+
return (props) => {
|
|
1471
|
+
let currentNode = props.node;
|
|
1472
|
+
const dom = document.createElement("span");
|
|
1473
|
+
dom.className = "docs-chip docs-chip--people";
|
|
1474
|
+
dom.setAttribute("data-node-type", "people-chip");
|
|
1475
|
+
dom.setAttribute("contenteditable", "false");
|
|
1476
|
+
const render = () => {
|
|
1477
|
+
const name = currentNode.attrs.name || "user";
|
|
1478
|
+
dom.innerHTML = "";
|
|
1479
|
+
const avatar = document.createElement("span");
|
|
1480
|
+
avatar.className = "docs-chip-avatar";
|
|
1481
|
+
avatar.textContent = initialOf(name);
|
|
1482
|
+
dom.appendChild(avatar);
|
|
1483
|
+
const label = document.createElement("span");
|
|
1484
|
+
label.className = "docs-chip-label";
|
|
1485
|
+
label.textContent = "@" + name;
|
|
1486
|
+
dom.appendChild(label);
|
|
1487
|
+
};
|
|
1488
|
+
render();
|
|
1489
|
+
return {
|
|
1490
|
+
dom,
|
|
1491
|
+
update(updatedNode) {
|
|
1492
|
+
if (updatedNode.type.name !== "peopleChip") return false;
|
|
1493
|
+
currentNode = updatedNode;
|
|
1494
|
+
render();
|
|
1495
|
+
return true;
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
};
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
var FileChipNode = Node5.create({
|
|
1502
|
+
name: "fileChip",
|
|
1503
|
+
group: "inline",
|
|
1504
|
+
inline: true,
|
|
1505
|
+
selectable: true,
|
|
1506
|
+
draggable: false,
|
|
1507
|
+
atom: true,
|
|
1508
|
+
addAttributes() {
|
|
1509
|
+
return {
|
|
1510
|
+
fileId: {
|
|
1511
|
+
default: "",
|
|
1512
|
+
parseHTML: (el) => el.getAttribute("data-file-id") ?? "",
|
|
1513
|
+
renderHTML: (attrs) => attrs.fileId ? { "data-file-id": attrs.fileId } : {}
|
|
1514
|
+
},
|
|
1515
|
+
name: {
|
|
1516
|
+
default: "",
|
|
1517
|
+
parseHTML: (el) => el.getAttribute("data-name") ?? "",
|
|
1518
|
+
renderHTML: (attrs) => ({ "data-name": attrs.name ?? "" })
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
},
|
|
1522
|
+
parseHTML() {
|
|
1523
|
+
return [{ tag: 'span[data-node-type="file-chip"]' }];
|
|
1524
|
+
},
|
|
1525
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
1526
|
+
return ["span", mergeAttributes6(HTMLAttributes, {
|
|
1527
|
+
"data-node-type": "file-chip",
|
|
1528
|
+
class: "docs-chip docs-chip--file"
|
|
1529
|
+
}), node.attrs.name || "file"];
|
|
1530
|
+
},
|
|
1531
|
+
addNodeView() {
|
|
1532
|
+
return (props) => {
|
|
1533
|
+
let currentNode = props.node;
|
|
1534
|
+
const dom = document.createElement("span");
|
|
1535
|
+
dom.className = "docs-chip docs-chip--file";
|
|
1536
|
+
dom.setAttribute("data-node-type", "file-chip");
|
|
1537
|
+
dom.setAttribute("contenteditable", "false");
|
|
1538
|
+
const render = () => {
|
|
1539
|
+
const name = currentNode.attrs.name || "Untitled file";
|
|
1540
|
+
dom.innerHTML = "";
|
|
1541
|
+
const icon = document.createElement("span");
|
|
1542
|
+
icon.className = "docs-chip-icon";
|
|
1543
|
+
icon.textContent = "\u{1F5CE}";
|
|
1544
|
+
dom.appendChild(icon);
|
|
1545
|
+
const label = document.createElement("span");
|
|
1546
|
+
label.className = "docs-chip-label";
|
|
1547
|
+
label.textContent = name;
|
|
1548
|
+
dom.appendChild(label);
|
|
1549
|
+
};
|
|
1550
|
+
render();
|
|
1551
|
+
return {
|
|
1552
|
+
dom,
|
|
1553
|
+
update(updatedNode) {
|
|
1554
|
+
if (updatedNode.type.name !== "fileChip") return false;
|
|
1555
|
+
currentNode = updatedNode;
|
|
1556
|
+
render();
|
|
1557
|
+
return true;
|
|
1558
|
+
}
|
|
1559
|
+
};
|
|
1560
|
+
};
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
var DropdownChipNode = Node5.create({
|
|
1564
|
+
name: "dropdownChip",
|
|
1565
|
+
group: "inline",
|
|
1566
|
+
inline: true,
|
|
1567
|
+
selectable: true,
|
|
1568
|
+
draggable: false,
|
|
1569
|
+
atom: true,
|
|
1570
|
+
addAttributes() {
|
|
1571
|
+
return {
|
|
1572
|
+
options: {
|
|
1573
|
+
default: DEFAULT_STATUSES,
|
|
1574
|
+
parseHTML: (el) => {
|
|
1575
|
+
const raw = el.getAttribute("data-options") ?? "";
|
|
1576
|
+
const list = raw ? raw.split("|") : [];
|
|
1577
|
+
return list.length ? list : DEFAULT_STATUSES;
|
|
1578
|
+
},
|
|
1579
|
+
renderHTML: (attrs) => ({
|
|
1580
|
+
"data-options": (attrs.options ?? []).join("|")
|
|
1581
|
+
})
|
|
1582
|
+
},
|
|
1583
|
+
selected: {
|
|
1584
|
+
default: DEFAULT_STATUSES[0],
|
|
1585
|
+
parseHTML: (el) => el.getAttribute("data-selected") ?? DEFAULT_STATUSES[0],
|
|
1586
|
+
renderHTML: (attrs) => ({ "data-selected": attrs.selected ?? "" })
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1589
|
+
},
|
|
1590
|
+
parseHTML() {
|
|
1591
|
+
return [{ tag: 'span[data-node-type="dropdown-chip"]' }];
|
|
1592
|
+
},
|
|
1593
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
1594
|
+
return ["span", mergeAttributes6(HTMLAttributes, {
|
|
1595
|
+
"data-node-type": "dropdown-chip",
|
|
1596
|
+
class: "docs-chip docs-chip--dropdown"
|
|
1597
|
+
}), node.attrs.selected || ""];
|
|
1598
|
+
},
|
|
1599
|
+
addNodeView() {
|
|
1600
|
+
return (props) => {
|
|
1601
|
+
let currentNode = props.node;
|
|
1602
|
+
const getPos = props.getPos;
|
|
1603
|
+
const view = props.view;
|
|
1604
|
+
const dom = document.createElement("span");
|
|
1605
|
+
dom.className = "docs-chip docs-chip--dropdown";
|
|
1606
|
+
dom.setAttribute("data-node-type", "dropdown-chip");
|
|
1607
|
+
dom.setAttribute("contenteditable", "false");
|
|
1608
|
+
const render = () => {
|
|
1609
|
+
const opts = currentNode.attrs.options ?? DEFAULT_STATUSES;
|
|
1610
|
+
const sel = currentNode.attrs.selected ?? (opts[0] ?? "");
|
|
1611
|
+
dom.innerHTML = "";
|
|
1612
|
+
const select = document.createElement("select");
|
|
1613
|
+
select.className = "docs-chip-select";
|
|
1614
|
+
select.addEventListener("mousedown", (e) => e.stopPropagation());
|
|
1615
|
+
for (const opt of opts) {
|
|
1616
|
+
const o = document.createElement("option");
|
|
1617
|
+
o.value = opt;
|
|
1618
|
+
o.textContent = opt;
|
|
1619
|
+
if (opt === sel) o.selected = true;
|
|
1620
|
+
select.appendChild(o);
|
|
1621
|
+
}
|
|
1622
|
+
select.addEventListener("change", () => {
|
|
1623
|
+
const pos = getPos();
|
|
1624
|
+
if (typeof pos === "number") {
|
|
1625
|
+
view.dispatch(view.state.tr.setNodeMarkup(pos, void 0, {
|
|
1626
|
+
...currentNode.attrs,
|
|
1627
|
+
selected: select.value
|
|
1628
|
+
}));
|
|
1629
|
+
}
|
|
1630
|
+
});
|
|
1631
|
+
dom.appendChild(select);
|
|
1632
|
+
};
|
|
1633
|
+
render();
|
|
1634
|
+
return {
|
|
1635
|
+
dom,
|
|
1636
|
+
update(updatedNode) {
|
|
1637
|
+
if (updatedNode.type.name !== "dropdownChip") return false;
|
|
1638
|
+
currentNode = updatedNode;
|
|
1639
|
+
render();
|
|
1640
|
+
return true;
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
});
|
|
1646
|
+
var LocationChipNode = Node5.create({
|
|
1647
|
+
name: "locationChip",
|
|
1648
|
+
group: "inline",
|
|
1649
|
+
inline: true,
|
|
1650
|
+
selectable: true,
|
|
1651
|
+
draggable: false,
|
|
1652
|
+
atom: true,
|
|
1653
|
+
addAttributes() {
|
|
1654
|
+
return {
|
|
1655
|
+
label: {
|
|
1656
|
+
default: "",
|
|
1657
|
+
parseHTML: (el) => el.getAttribute("data-label") ?? "",
|
|
1658
|
+
renderHTML: (attrs) => ({ "data-label": attrs.label ?? "" })
|
|
1659
|
+
},
|
|
1660
|
+
lat: {
|
|
1661
|
+
default: null,
|
|
1662
|
+
parseHTML: (el) => {
|
|
1663
|
+
const v = el.getAttribute("data-lat");
|
|
1664
|
+
return v === null ? null : Number(v);
|
|
1665
|
+
},
|
|
1666
|
+
renderHTML: (attrs) => attrs.lat != null ? { "data-lat": attrs.lat } : {}
|
|
1667
|
+
},
|
|
1668
|
+
lng: {
|
|
1669
|
+
default: null,
|
|
1670
|
+
parseHTML: (el) => {
|
|
1671
|
+
const v = el.getAttribute("data-lng");
|
|
1672
|
+
return v === null ? null : Number(v);
|
|
1673
|
+
},
|
|
1674
|
+
renderHTML: (attrs) => attrs.lng != null ? { "data-lng": attrs.lng } : {}
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
},
|
|
1678
|
+
parseHTML() {
|
|
1679
|
+
return [{ tag: 'span[data-node-type="location-chip"]' }];
|
|
1680
|
+
},
|
|
1681
|
+
renderHTML({ node, HTMLAttributes }) {
|
|
1682
|
+
return ["span", mergeAttributes6(HTMLAttributes, {
|
|
1683
|
+
"data-node-type": "location-chip",
|
|
1684
|
+
class: "docs-chip docs-chip--location"
|
|
1685
|
+
}), node.attrs.label || ""];
|
|
1686
|
+
},
|
|
1687
|
+
addNodeView() {
|
|
1688
|
+
return (props) => {
|
|
1689
|
+
let currentNode = props.node;
|
|
1690
|
+
const dom = document.createElement("span");
|
|
1691
|
+
dom.className = "docs-chip docs-chip--location";
|
|
1692
|
+
dom.setAttribute("data-node-type", "location-chip");
|
|
1693
|
+
dom.setAttribute("contenteditable", "false");
|
|
1694
|
+
const render = () => {
|
|
1695
|
+
const label = currentNode.attrs.label || "Add location";
|
|
1696
|
+
dom.innerHTML = "";
|
|
1697
|
+
const icon = document.createElement("span");
|
|
1698
|
+
icon.className = "docs-chip-icon";
|
|
1699
|
+
icon.textContent = "\u{1F4CD}";
|
|
1700
|
+
dom.appendChild(icon);
|
|
1701
|
+
const lbl = document.createElement("span");
|
|
1702
|
+
lbl.className = "docs-chip-label";
|
|
1703
|
+
lbl.textContent = label;
|
|
1704
|
+
dom.appendChild(lbl);
|
|
1705
|
+
};
|
|
1706
|
+
render();
|
|
1707
|
+
return {
|
|
1708
|
+
dom,
|
|
1709
|
+
update(updatedNode) {
|
|
1710
|
+
if (updatedNode.type.name !== "locationChip") return false;
|
|
1711
|
+
currentNode = updatedNode;
|
|
1712
|
+
render();
|
|
1713
|
+
return true;
|
|
1714
|
+
}
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
var smartElementsPlugin = definePlugin18({
|
|
1720
|
+
id: "smart-elements",
|
|
1721
|
+
tiptapExtensions: [
|
|
1722
|
+
DateChipNode,
|
|
1723
|
+
PeopleChipNode,
|
|
1724
|
+
FileChipNode,
|
|
1725
|
+
DropdownChipNode,
|
|
1726
|
+
LocationChipNode
|
|
1727
|
+
],
|
|
1728
|
+
slashCommands: [
|
|
1729
|
+
{ name: "Date", command: "insertDateChip" },
|
|
1730
|
+
{ name: "People", command: "insertPeopleChip" },
|
|
1731
|
+
{ name: "File", command: "insertFileChip" },
|
|
1732
|
+
{ name: "Dropdown", command: "insertDropdownChip" },
|
|
1733
|
+
{ name: "Location", command: "insertLocationChip" }
|
|
1734
|
+
],
|
|
1735
|
+
commands: {
|
|
1736
|
+
insertDateChip: (editor) => {
|
|
1737
|
+
return editor.chain().focus().insertContent({
|
|
1738
|
+
type: "dateChip",
|
|
1739
|
+
attrs: { date: todayISO() }
|
|
1740
|
+
}).run();
|
|
1741
|
+
},
|
|
1742
|
+
insertPeopleChip: (editor) => {
|
|
1743
|
+
const name = typeof window !== "undefined" && typeof window.prompt === "function" ? window.prompt("Enter user name:", "") ?? "" : "";
|
|
1744
|
+
if (!name.trim()) return false;
|
|
1745
|
+
const trimmed = name.trim();
|
|
1746
|
+
return editor.chain().focus().insertContent({
|
|
1747
|
+
type: "peopleChip",
|
|
1748
|
+
attrs: { userId: slugify(trimmed), name: trimmed }
|
|
1749
|
+
}).run();
|
|
1750
|
+
},
|
|
1751
|
+
insertFileChip: (editor) => {
|
|
1752
|
+
const name = typeof window !== "undefined" && typeof window.prompt === "function" ? window.prompt("Enter file/document name:", "") ?? "" : "";
|
|
1753
|
+
if (!name.trim()) return false;
|
|
1754
|
+
const trimmed = name.trim();
|
|
1755
|
+
return editor.chain().focus().insertContent({
|
|
1756
|
+
type: "fileChip",
|
|
1757
|
+
attrs: { fileId: slugify(trimmed), name: trimmed }
|
|
1758
|
+
}).run();
|
|
1759
|
+
},
|
|
1760
|
+
insertDropdownChip: (editor) => {
|
|
1761
|
+
return editor.chain().focus().insertContent({
|
|
1762
|
+
type: "dropdownChip",
|
|
1763
|
+
attrs: { options: DEFAULT_STATUSES, selected: DEFAULT_STATUSES[0] }
|
|
1764
|
+
}).run();
|
|
1765
|
+
},
|
|
1766
|
+
insertLocationChip: (editor) => {
|
|
1767
|
+
const label = typeof window !== "undefined" && typeof window.prompt === "function" ? window.prompt("Enter location name:", "") ?? "" : "";
|
|
1768
|
+
if (!label.trim()) return false;
|
|
1769
|
+
return editor.chain().focus().insertContent({
|
|
1770
|
+
type: "locationChip",
|
|
1771
|
+
attrs: { label: label.trim() }
|
|
1772
|
+
}).run();
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
});
|
|
1776
|
+
|
|
1250
1777
|
// src/slashMenu.ts
|
|
1251
1778
|
import { Extension as Extension3 } from "@tiptap/core";
|
|
1252
1779
|
import { Plugin as Plugin2, PluginKey as PluginKey2 } from "@tiptap/pm/state";
|
|
1253
|
-
import { definePlugin as
|
|
1780
|
+
import { definePlugin as definePlugin19 } from "@kedataindo/docflow-core";
|
|
1254
1781
|
var slashState = {
|
|
1255
1782
|
open: false,
|
|
1256
1783
|
query: "",
|
|
@@ -1395,7 +1922,7 @@ var SlashMenuExtension = Extension3.create({
|
|
|
1395
1922
|
];
|
|
1396
1923
|
}
|
|
1397
1924
|
});
|
|
1398
|
-
var slashMenuPlugin =
|
|
1925
|
+
var slashMenuPlugin = definePlugin19({
|
|
1399
1926
|
id: "slash-menu",
|
|
1400
1927
|
tiptapExtensions: [SlashMenuExtension],
|
|
1401
1928
|
hooks: {
|
|
@@ -1426,7 +1953,8 @@ var defaultPlugins = [
|
|
|
1426
1953
|
highlightPlugin,
|
|
1427
1954
|
citationPlugin,
|
|
1428
1955
|
aiPlugin,
|
|
1429
|
-
commentPlugin
|
|
1956
|
+
commentPlugin,
|
|
1957
|
+
smartElementsPlugin
|
|
1430
1958
|
];
|
|
1431
1959
|
export {
|
|
1432
1960
|
AIExtension,
|
|
@@ -1438,9 +1966,14 @@ export {
|
|
|
1438
1966
|
CiteEngine,
|
|
1439
1967
|
CommentMark as CommentMarkExtension,
|
|
1440
1968
|
DEFAULT_CSL_STYLE,
|
|
1969
|
+
DateChipNode,
|
|
1970
|
+
DropdownChipNode,
|
|
1971
|
+
FileChipNode,
|
|
1441
1972
|
FontSizeExtension,
|
|
1442
1973
|
FootnoteNode,
|
|
1974
|
+
LocationChipNode,
|
|
1443
1975
|
PageBreak,
|
|
1976
|
+
PeopleChipNode,
|
|
1444
1977
|
SlashMenuExtension,
|
|
1445
1978
|
TocEntryNode,
|
|
1446
1979
|
TocNode,
|
|
@@ -1464,8 +1997,10 @@ export {
|
|
|
1464
1997
|
headingsPlugin,
|
|
1465
1998
|
highlightPlugin,
|
|
1466
1999
|
imagePlugin,
|
|
2000
|
+
insertMarkdownBlock,
|
|
1467
2001
|
linkPlugin,
|
|
1468
2002
|
listsPlugin,
|
|
2003
|
+
markdownToFragment,
|
|
1469
2004
|
nextCitationId,
|
|
1470
2005
|
onSlashStateChange,
|
|
1471
2006
|
pageBreakPlugin,
|
|
@@ -1475,6 +2010,7 @@ export {
|
|
|
1475
2010
|
sanitizeCiteprocHtml,
|
|
1476
2011
|
slashMenuPlugin,
|
|
1477
2012
|
slashState,
|
|
2013
|
+
smartElementsPlugin,
|
|
1478
2014
|
tablePlugin,
|
|
1479
2015
|
textColorPlugin,
|
|
1480
2016
|
tocPlugin
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kedataindo/docflow-plugins",
|
|
3
3
|
"license": "UNLICENSED",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.33",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"@tiptap/extension-highlight": "^2.27.2",
|
|
31
31
|
"@tiptap/pm": "^2.11.0",
|
|
32
32
|
"citeproc": "^2.4.63",
|
|
33
|
-
"
|
|
33
|
+
"tiptap-markdown": "0.8.10",
|
|
34
|
+
"@kedataindo/docflow-core": "0.0.31"
|
|
34
35
|
},
|
|
35
36
|
"peerDependencies": {
|
|
36
37
|
"@tiptap/core": "^2.11.0",
|