@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 CHANGED
@@ -39,9 +39,14 @@ __export(index_exports, {
39
39
  CiteEngine: () => CiteEngine,
40
40
  CommentMarkExtension: () => CommentMark,
41
41
  DEFAULT_CSL_STYLE: () => DEFAULT_CSL_STYLE,
42
+ DateChipNode: () => DateChipNode,
43
+ DropdownChipNode: () => DropdownChipNode,
44
+ FileChipNode: () => FileChipNode,
42
45
  FontSizeExtension: () => import_docflow_core14.FontSizeExtension,
43
46
  FootnoteNode: () => FootnoteNode,
47
+ LocationChipNode: () => LocationChipNode,
44
48
  PageBreak: () => PageBreak,
49
+ PeopleChipNode: () => PeopleChipNode,
45
50
  SlashMenuExtension: () => SlashMenuExtension,
46
51
  TocEntryNode: () => TocEntryNode,
47
52
  TocNode: () => TocNode,
@@ -65,8 +70,10 @@ __export(index_exports, {
65
70
  headingsPlugin: () => headingsPlugin,
66
71
  highlightPlugin: () => highlightPlugin,
67
72
  imagePlugin: () => imagePlugin,
73
+ insertMarkdownBlock: () => insertMarkdownBlock,
68
74
  linkPlugin: () => linkPlugin,
69
75
  listsPlugin: () => listsPlugin,
76
+ markdownToFragment: () => markdownToFragment,
70
77
  nextCitationId: () => nextCitationId,
71
78
  onSlashStateChange: () => onSlashStateChange,
72
79
  pageBreakPlugin: () => pageBreakPlugin,
@@ -76,6 +83,7 @@ __export(index_exports, {
76
83
  sanitizeCiteprocHtml: () => sanitizeCiteprocHtml,
77
84
  slashMenuPlugin: () => slashMenuPlugin,
78
85
  slashState: () => slashState,
86
+ smartElementsPlugin: () => smartElementsPlugin,
79
87
  tablePlugin: () => tablePlugin,
80
88
  textColorPlugin: () => textColorPlugin,
81
89
  tocPlugin: () => tocPlugin
@@ -2326,11 +2334,95 @@ var citationPlugin = (0, import_docflow_core17.definePlugin)({
2326
2334
  });
2327
2335
 
2328
2336
  // src/ai.ts
2329
- var import_core8 = require("@tiptap/core");
2330
- var import_state = require("@tiptap/pm/state");
2337
+ var import_core9 = require("@tiptap/core");
2338
+ var import_state2 = require("@tiptap/pm/state");
2331
2339
  var import_view = require("@tiptap/pm/view");
2340
+ var import_tiptap_markdown = require("tiptap-markdown");
2332
2341
  var import_docflow_core18 = require("@kedataindo/docflow-core");
2333
- var aiPluginKey = new import_state.PluginKey("docflow-ai");
2342
+
2343
+ // src/markdownInsert.ts
2344
+ var import_core8 = require("@tiptap/core");
2345
+ var import_model = require("@tiptap/pm/model");
2346
+ var import_state = require("@tiptap/pm/state");
2347
+ var import_transform = require("@tiptap/pm/transform");
2348
+ function mdParser(editor) {
2349
+ const storage = editor.storage.markdown;
2350
+ if (!storage || typeof storage !== "object") return void 0;
2351
+ const parser = storage.parser;
2352
+ if (!parser || typeof parser !== "object" || !("parse" in parser)) return void 0;
2353
+ return parser;
2354
+ }
2355
+ function markdownToFragment(editor, markdown, opts) {
2356
+ const parser = mdParser(editor);
2357
+ if (!parser) return null;
2358
+ let html;
2359
+ try {
2360
+ html = parser.parse(markdown, { inline: opts.inline });
2361
+ } catch {
2362
+ return null;
2363
+ }
2364
+ if (typeof html !== "string") return null;
2365
+ const content = (0, import_core8.createNodeFromContent)(html, editor.schema, {
2366
+ slice: true,
2367
+ parseOptions: { preserveWhitespace: "full" }
2368
+ });
2369
+ if (content instanceof import_model.Fragment) return content;
2370
+ return import_model.Fragment.from(content);
2371
+ }
2372
+ function selectionToInsertionEnd(tr, startLen, bias) {
2373
+ const last = tr.steps.length - 1;
2374
+ if (last < startLen) return;
2375
+ const step = tr.steps[last];
2376
+ if (!(step instanceof import_transform.ReplaceStep || step instanceof import_transform.ReplaceAroundStep)) return;
2377
+ const map = tr.mapping.maps[last];
2378
+ let end = 0;
2379
+ map.forEach((_from, _to, _newFrom, newTo) => {
2380
+ if (end === 0) end = newTo;
2381
+ });
2382
+ tr.setSelection(import_state.Selection.near(tr.doc.resolve(end), bias));
2383
+ }
2384
+ function insertMarkdownBlock(editor, view, from, to, markdown) {
2385
+ const fragment = markdownToFragment(editor, markdown, { inline: false });
2386
+ if (!fragment) {
2387
+ view.dispatch(view.state.tr.insertText(markdown, from, to));
2388
+ return;
2389
+ }
2390
+ const tr = view.state.tr;
2391
+ let f = from;
2392
+ let t = to;
2393
+ if (f === t) {
2394
+ let onlyBlock = true;
2395
+ fragment.forEach((n) => {
2396
+ if (!n.isBlock) onlyBlock = false;
2397
+ });
2398
+ if (onlyBlock) {
2399
+ const $pos = tr.doc.resolve(f);
2400
+ const parent = $pos.parent;
2401
+ if (parent.isTextblock && !parent.type.spec.code && !parent.childCount) {
2402
+ f -= 1;
2403
+ t += 1;
2404
+ }
2405
+ }
2406
+ }
2407
+ let onlyText = true;
2408
+ fragment.forEach((n) => {
2409
+ if (!n.isText || n.marks.length > 0) onlyText = false;
2410
+ });
2411
+ if (onlyText) {
2412
+ let text = "";
2413
+ fragment.forEach((n) => {
2414
+ if (n.isText) text += n.text ?? "";
2415
+ });
2416
+ tr.insertText(text, f, t);
2417
+ } else {
2418
+ tr.replaceWith(f, t, fragment);
2419
+ }
2420
+ selectionToInsertionEnd(tr, 0, -1);
2421
+ view.dispatch(tr);
2422
+ }
2423
+
2424
+ // src/ai.ts
2425
+ var aiPluginKey = new import_state2.PluginKey("docflow-ai");
2334
2426
  var CONTEXT_CHARS = 1500;
2335
2427
  function getAIPreview(editor) {
2336
2428
  return editor.storage.ai ? editor.storage.ai.preview ?? null : null;
@@ -2371,7 +2463,7 @@ function stopStream(editor) {
2371
2463
  storage.abort?.abort();
2372
2464
  storage.abort = null;
2373
2465
  }
2374
- var AIExtension = import_core8.Extension.create({
2466
+ var AIExtension = import_core9.Extension.create({
2375
2467
  name: "ai",
2376
2468
  addStorage() {
2377
2469
  return {
@@ -2467,7 +2559,12 @@ var AIExtension = import_core8.Extension.create({
2467
2559
  if (!dispatch) return true;
2468
2560
  const tr = state.tr;
2469
2561
  if (preview.text.trim()) {
2470
- tr.replaceWith(preview.from, preview.to, state.schema.text(preview.text));
2562
+ const fragment = markdownToFragment(editor, preview.text, { inline: true });
2563
+ if (fragment) {
2564
+ tr.replaceWith(preview.from, preview.to, fragment);
2565
+ } else {
2566
+ tr.replaceWith(preview.from, preview.to, state.schema.text(preview.text));
2567
+ }
2471
2568
  }
2472
2569
  tr.setMeta(aiPluginKey, { type: "clear" });
2473
2570
  dispatch(tr);
@@ -2561,7 +2658,7 @@ var AIExtension = import_core8.Extension.create({
2561
2658
  };
2562
2659
  }
2563
2660
  return [
2564
- new import_state.Plugin({
2661
+ new import_state2.Plugin({
2565
2662
  key: aiPluginKey,
2566
2663
  state: {
2567
2664
  init: () => null,
@@ -2679,13 +2776,21 @@ var AIExtension = import_core8.Extension.create({
2679
2776
  });
2680
2777
  var aiPlugin = (0, import_docflow_core18.definePlugin)({
2681
2778
  id: "ai",
2682
- tiptapExtensions: [AIExtension],
2779
+ // The `tiptap-markdown` `Markdown` extension is registered alongside AI so
2780
+ // that `editor.storage.markdown.parser` is available on every editor that
2781
+ // can stream AI content. It adds a schema-aware markdown↔HTML bridge
2782
+ // (markdown-it under the hood) used by the AI Insert/accept paths to turn
2783
+ // streamed `| col | col |` tables / `# headings` / `**bold**` into real
2784
+ // nodes instead of literal pipe/asterisk text. Its `insertContentAt` /
2785
+ // `setContent` command overrides are intentionally NOT used by the AI
2786
+ // paths (they force `inline:true`); see `markdownInsert.ts`.
2787
+ tiptapExtensions: [AIExtension, import_tiptap_markdown.Markdown.configure({ html: true, linkify: true, breaks: false })],
2683
2788
  slashCommands: [{ name: "AI", description: "Generate text with AI", command: "aiGenerate" }]
2684
2789
  });
2685
2790
 
2686
2791
  // src/comment.ts
2687
- var import_core9 = require("@tiptap/core");
2688
- var CommentMark = import_core9.Mark.create({
2792
+ var import_core10 = require("@tiptap/core");
2793
+ var CommentMark = import_core10.Mark.create({
2689
2794
  name: "comment",
2690
2795
  // Comments are inclusive (typing inside an existing comment extends
2691
2796
  // the mark range — that's the typical inline-comment UX). Excluding
@@ -2723,10 +2828,440 @@ var commentPlugin = {
2723
2828
  commands: {}
2724
2829
  };
2725
2830
 
2726
- // src/slashMenu.ts
2727
- var import_core10 = require("@tiptap/core");
2728
- var import_state2 = require("@tiptap/pm/state");
2831
+ // src/smartElements.ts
2832
+ var import_core11 = require("@tiptap/core");
2729
2833
  var import_docflow_core19 = require("@kedataindo/docflow-core");
2834
+ function todayISO() {
2835
+ return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2836
+ }
2837
+ function formatDate(iso) {
2838
+ if (!iso) return "Pick a date";
2839
+ const d = /* @__PURE__ */ new Date(iso + "T00:00:00");
2840
+ if (isNaN(d.getTime())) return "Pick a date";
2841
+ return d.toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
2842
+ }
2843
+ function initialOf(name) {
2844
+ return (name || "?").trim().charAt(0).toUpperCase() || "?";
2845
+ }
2846
+ var DEFAULT_STATUSES = ["To Do", "In Progress", "Review", "Approved"];
2847
+ function slugify(s) {
2848
+ return s.toLowerCase().replace(/\s+/g, "-").replace(/[^a-z0-9-]/g, "");
2849
+ }
2850
+ var DateChipNode = import_core11.Node.create({
2851
+ name: "dateChip",
2852
+ group: "inline",
2853
+ inline: true,
2854
+ selectable: true,
2855
+ draggable: false,
2856
+ atom: true,
2857
+ addAttributes() {
2858
+ return {
2859
+ date: {
2860
+ default: todayISO(),
2861
+ parseHTML: (el) => el.getAttribute("data-date") ?? todayISO(),
2862
+ renderHTML: (attrs) => ({ "data-date": attrs.date ?? "" })
2863
+ }
2864
+ };
2865
+ },
2866
+ parseHTML() {
2867
+ return [{ tag: 'span[data-node-type="date-chip"]' }];
2868
+ },
2869
+ renderHTML({ node, HTMLAttributes }) {
2870
+ return ["span", (0, import_core11.mergeAttributes)(HTMLAttributes, {
2871
+ "data-node-type": "date-chip",
2872
+ class: "docs-chip docs-chip--date"
2873
+ }), formatDate(node.attrs.date)];
2874
+ },
2875
+ addNodeView() {
2876
+ return (props) => {
2877
+ let currentNode = props.node;
2878
+ const getPos = props.getPos;
2879
+ const view = props.view;
2880
+ const dom = document.createElement("span");
2881
+ dom.className = "docs-chip docs-chip--date";
2882
+ dom.setAttribute("data-node-type", "date-chip");
2883
+ dom.setAttribute("contenteditable", "false");
2884
+ const render = () => {
2885
+ if (dom.querySelector("input")) return;
2886
+ dom.textContent = formatDate(currentNode.attrs.date);
2887
+ dom.setAttribute("data-date", currentNode.attrs.date);
2888
+ };
2889
+ render();
2890
+ dom.addEventListener("click", (e) => {
2891
+ e.preventDefault();
2892
+ e.stopPropagation();
2893
+ if (dom.querySelector("input")) return;
2894
+ const input = document.createElement("input");
2895
+ input.type = "date";
2896
+ input.value = currentNode.attrs.date;
2897
+ input.className = "docs-chip-date-input";
2898
+ dom.replaceChildren(input);
2899
+ input.focus();
2900
+ const commit = () => {
2901
+ const newVal = input.value;
2902
+ const pos = getPos();
2903
+ if (typeof pos === "number") {
2904
+ view.dispatch(view.state.tr.setNodeMarkup(pos, void 0, { date: newVal }));
2905
+ }
2906
+ setTimeout(render, 30);
2907
+ };
2908
+ input.addEventListener("change", commit);
2909
+ input.addEventListener("blur", () => setTimeout(render, 30));
2910
+ });
2911
+ return {
2912
+ dom,
2913
+ update(updatedNode) {
2914
+ if (updatedNode.type.name !== "dateChip") return false;
2915
+ currentNode = updatedNode;
2916
+ render();
2917
+ return true;
2918
+ }
2919
+ };
2920
+ };
2921
+ }
2922
+ });
2923
+ var PeopleChipNode = import_core11.Node.create({
2924
+ name: "peopleChip",
2925
+ group: "inline",
2926
+ inline: true,
2927
+ selectable: true,
2928
+ draggable: false,
2929
+ atom: true,
2930
+ addAttributes() {
2931
+ return {
2932
+ userId: {
2933
+ default: "",
2934
+ parseHTML: (el) => el.getAttribute("data-user-id") ?? "",
2935
+ renderHTML: (attrs) => attrs.userId ? { "data-user-id": attrs.userId } : {}
2936
+ },
2937
+ name: {
2938
+ default: "",
2939
+ parseHTML: (el) => el.getAttribute("data-name") ?? "",
2940
+ renderHTML: (attrs) => ({ "data-name": attrs.name ?? "" })
2941
+ }
2942
+ };
2943
+ },
2944
+ parseHTML() {
2945
+ return [{ tag: 'span[data-node-type="people-chip"]' }];
2946
+ },
2947
+ renderHTML({ node, HTMLAttributes }) {
2948
+ return ["span", (0, import_core11.mergeAttributes)(HTMLAttributes, {
2949
+ "data-node-type": "people-chip",
2950
+ class: "docs-chip docs-chip--people"
2951
+ }), `@${node.attrs.name || "user"}`];
2952
+ },
2953
+ addNodeView() {
2954
+ return (props) => {
2955
+ let currentNode = props.node;
2956
+ const dom = document.createElement("span");
2957
+ dom.className = "docs-chip docs-chip--people";
2958
+ dom.setAttribute("data-node-type", "people-chip");
2959
+ dom.setAttribute("contenteditable", "false");
2960
+ const render = () => {
2961
+ const name = currentNode.attrs.name || "user";
2962
+ dom.innerHTML = "";
2963
+ const avatar = document.createElement("span");
2964
+ avatar.className = "docs-chip-avatar";
2965
+ avatar.textContent = initialOf(name);
2966
+ dom.appendChild(avatar);
2967
+ const label = document.createElement("span");
2968
+ label.className = "docs-chip-label";
2969
+ label.textContent = "@" + name;
2970
+ dom.appendChild(label);
2971
+ };
2972
+ render();
2973
+ return {
2974
+ dom,
2975
+ update(updatedNode) {
2976
+ if (updatedNode.type.name !== "peopleChip") return false;
2977
+ currentNode = updatedNode;
2978
+ render();
2979
+ return true;
2980
+ }
2981
+ };
2982
+ };
2983
+ }
2984
+ });
2985
+ var FileChipNode = import_core11.Node.create({
2986
+ name: "fileChip",
2987
+ group: "inline",
2988
+ inline: true,
2989
+ selectable: true,
2990
+ draggable: false,
2991
+ atom: true,
2992
+ addAttributes() {
2993
+ return {
2994
+ fileId: {
2995
+ default: "",
2996
+ parseHTML: (el) => el.getAttribute("data-file-id") ?? "",
2997
+ renderHTML: (attrs) => attrs.fileId ? { "data-file-id": attrs.fileId } : {}
2998
+ },
2999
+ name: {
3000
+ default: "",
3001
+ parseHTML: (el) => el.getAttribute("data-name") ?? "",
3002
+ renderHTML: (attrs) => ({ "data-name": attrs.name ?? "" })
3003
+ }
3004
+ };
3005
+ },
3006
+ parseHTML() {
3007
+ return [{ tag: 'span[data-node-type="file-chip"]' }];
3008
+ },
3009
+ renderHTML({ node, HTMLAttributes }) {
3010
+ return ["span", (0, import_core11.mergeAttributes)(HTMLAttributes, {
3011
+ "data-node-type": "file-chip",
3012
+ class: "docs-chip docs-chip--file"
3013
+ }), node.attrs.name || "file"];
3014
+ },
3015
+ addNodeView() {
3016
+ return (props) => {
3017
+ let currentNode = props.node;
3018
+ const dom = document.createElement("span");
3019
+ dom.className = "docs-chip docs-chip--file";
3020
+ dom.setAttribute("data-node-type", "file-chip");
3021
+ dom.setAttribute("contenteditable", "false");
3022
+ const render = () => {
3023
+ const name = currentNode.attrs.name || "Untitled file";
3024
+ dom.innerHTML = "";
3025
+ const icon = document.createElement("span");
3026
+ icon.className = "docs-chip-icon";
3027
+ icon.textContent = "\u{1F5CE}";
3028
+ dom.appendChild(icon);
3029
+ const label = document.createElement("span");
3030
+ label.className = "docs-chip-label";
3031
+ label.textContent = name;
3032
+ dom.appendChild(label);
3033
+ };
3034
+ render();
3035
+ return {
3036
+ dom,
3037
+ update(updatedNode) {
3038
+ if (updatedNode.type.name !== "fileChip") return false;
3039
+ currentNode = updatedNode;
3040
+ render();
3041
+ return true;
3042
+ }
3043
+ };
3044
+ };
3045
+ }
3046
+ });
3047
+ var DropdownChipNode = import_core11.Node.create({
3048
+ name: "dropdownChip",
3049
+ group: "inline",
3050
+ inline: true,
3051
+ selectable: true,
3052
+ draggable: false,
3053
+ atom: true,
3054
+ addAttributes() {
3055
+ return {
3056
+ options: {
3057
+ default: DEFAULT_STATUSES,
3058
+ parseHTML: (el) => {
3059
+ const raw = el.getAttribute("data-options") ?? "";
3060
+ const list = raw ? raw.split("|") : [];
3061
+ return list.length ? list : DEFAULT_STATUSES;
3062
+ },
3063
+ renderHTML: (attrs) => ({
3064
+ "data-options": (attrs.options ?? []).join("|")
3065
+ })
3066
+ },
3067
+ selected: {
3068
+ default: DEFAULT_STATUSES[0],
3069
+ parseHTML: (el) => el.getAttribute("data-selected") ?? DEFAULT_STATUSES[0],
3070
+ renderHTML: (attrs) => ({ "data-selected": attrs.selected ?? "" })
3071
+ }
3072
+ };
3073
+ },
3074
+ parseHTML() {
3075
+ return [{ tag: 'span[data-node-type="dropdown-chip"]' }];
3076
+ },
3077
+ renderHTML({ node, HTMLAttributes }) {
3078
+ return ["span", (0, import_core11.mergeAttributes)(HTMLAttributes, {
3079
+ "data-node-type": "dropdown-chip",
3080
+ class: "docs-chip docs-chip--dropdown"
3081
+ }), node.attrs.selected || ""];
3082
+ },
3083
+ addNodeView() {
3084
+ return (props) => {
3085
+ let currentNode = props.node;
3086
+ const getPos = props.getPos;
3087
+ const view = props.view;
3088
+ const dom = document.createElement("span");
3089
+ dom.className = "docs-chip docs-chip--dropdown";
3090
+ dom.setAttribute("data-node-type", "dropdown-chip");
3091
+ dom.setAttribute("contenteditable", "false");
3092
+ const render = () => {
3093
+ const opts = currentNode.attrs.options ?? DEFAULT_STATUSES;
3094
+ const sel = currentNode.attrs.selected ?? (opts[0] ?? "");
3095
+ dom.innerHTML = "";
3096
+ const select = document.createElement("select");
3097
+ select.className = "docs-chip-select";
3098
+ select.addEventListener("mousedown", (e) => e.stopPropagation());
3099
+ for (const opt of opts) {
3100
+ const o = document.createElement("option");
3101
+ o.value = opt;
3102
+ o.textContent = opt;
3103
+ if (opt === sel) o.selected = true;
3104
+ select.appendChild(o);
3105
+ }
3106
+ select.addEventListener("change", () => {
3107
+ const pos = getPos();
3108
+ if (typeof pos === "number") {
3109
+ view.dispatch(view.state.tr.setNodeMarkup(pos, void 0, {
3110
+ ...currentNode.attrs,
3111
+ selected: select.value
3112
+ }));
3113
+ }
3114
+ });
3115
+ dom.appendChild(select);
3116
+ };
3117
+ render();
3118
+ return {
3119
+ dom,
3120
+ update(updatedNode) {
3121
+ if (updatedNode.type.name !== "dropdownChip") return false;
3122
+ currentNode = updatedNode;
3123
+ render();
3124
+ return true;
3125
+ }
3126
+ };
3127
+ };
3128
+ }
3129
+ });
3130
+ var LocationChipNode = import_core11.Node.create({
3131
+ name: "locationChip",
3132
+ group: "inline",
3133
+ inline: true,
3134
+ selectable: true,
3135
+ draggable: false,
3136
+ atom: true,
3137
+ addAttributes() {
3138
+ return {
3139
+ label: {
3140
+ default: "",
3141
+ parseHTML: (el) => el.getAttribute("data-label") ?? "",
3142
+ renderHTML: (attrs) => ({ "data-label": attrs.label ?? "" })
3143
+ },
3144
+ lat: {
3145
+ default: null,
3146
+ parseHTML: (el) => {
3147
+ const v = el.getAttribute("data-lat");
3148
+ return v === null ? null : Number(v);
3149
+ },
3150
+ renderHTML: (attrs) => attrs.lat != null ? { "data-lat": attrs.lat } : {}
3151
+ },
3152
+ lng: {
3153
+ default: null,
3154
+ parseHTML: (el) => {
3155
+ const v = el.getAttribute("data-lng");
3156
+ return v === null ? null : Number(v);
3157
+ },
3158
+ renderHTML: (attrs) => attrs.lng != null ? { "data-lng": attrs.lng } : {}
3159
+ }
3160
+ };
3161
+ },
3162
+ parseHTML() {
3163
+ return [{ tag: 'span[data-node-type="location-chip"]' }];
3164
+ },
3165
+ renderHTML({ node, HTMLAttributes }) {
3166
+ return ["span", (0, import_core11.mergeAttributes)(HTMLAttributes, {
3167
+ "data-node-type": "location-chip",
3168
+ class: "docs-chip docs-chip--location"
3169
+ }), node.attrs.label || ""];
3170
+ },
3171
+ addNodeView() {
3172
+ return (props) => {
3173
+ let currentNode = props.node;
3174
+ const dom = document.createElement("span");
3175
+ dom.className = "docs-chip docs-chip--location";
3176
+ dom.setAttribute("data-node-type", "location-chip");
3177
+ dom.setAttribute("contenteditable", "false");
3178
+ const render = () => {
3179
+ const label = currentNode.attrs.label || "Add location";
3180
+ dom.innerHTML = "";
3181
+ const icon = document.createElement("span");
3182
+ icon.className = "docs-chip-icon";
3183
+ icon.textContent = "\u{1F4CD}";
3184
+ dom.appendChild(icon);
3185
+ const lbl = document.createElement("span");
3186
+ lbl.className = "docs-chip-label";
3187
+ lbl.textContent = label;
3188
+ dom.appendChild(lbl);
3189
+ };
3190
+ render();
3191
+ return {
3192
+ dom,
3193
+ update(updatedNode) {
3194
+ if (updatedNode.type.name !== "locationChip") return false;
3195
+ currentNode = updatedNode;
3196
+ render();
3197
+ return true;
3198
+ }
3199
+ };
3200
+ };
3201
+ }
3202
+ });
3203
+ var smartElementsPlugin = (0, import_docflow_core19.definePlugin)({
3204
+ id: "smart-elements",
3205
+ tiptapExtensions: [
3206
+ DateChipNode,
3207
+ PeopleChipNode,
3208
+ FileChipNode,
3209
+ DropdownChipNode,
3210
+ LocationChipNode
3211
+ ],
3212
+ slashCommands: [
3213
+ { name: "Date", command: "insertDateChip" },
3214
+ { name: "People", command: "insertPeopleChip" },
3215
+ { name: "File", command: "insertFileChip" },
3216
+ { name: "Dropdown", command: "insertDropdownChip" },
3217
+ { name: "Location", command: "insertLocationChip" }
3218
+ ],
3219
+ commands: {
3220
+ insertDateChip: (editor) => {
3221
+ return editor.chain().focus().insertContent({
3222
+ type: "dateChip",
3223
+ attrs: { date: todayISO() }
3224
+ }).run();
3225
+ },
3226
+ insertPeopleChip: (editor) => {
3227
+ const name = typeof window !== "undefined" && typeof window.prompt === "function" ? window.prompt("Enter user name:", "") ?? "" : "";
3228
+ if (!name.trim()) return false;
3229
+ const trimmed = name.trim();
3230
+ return editor.chain().focus().insertContent({
3231
+ type: "peopleChip",
3232
+ attrs: { userId: slugify(trimmed), name: trimmed }
3233
+ }).run();
3234
+ },
3235
+ insertFileChip: (editor) => {
3236
+ const name = typeof window !== "undefined" && typeof window.prompt === "function" ? window.prompt("Enter file/document name:", "") ?? "" : "";
3237
+ if (!name.trim()) return false;
3238
+ const trimmed = name.trim();
3239
+ return editor.chain().focus().insertContent({
3240
+ type: "fileChip",
3241
+ attrs: { fileId: slugify(trimmed), name: trimmed }
3242
+ }).run();
3243
+ },
3244
+ insertDropdownChip: (editor) => {
3245
+ return editor.chain().focus().insertContent({
3246
+ type: "dropdownChip",
3247
+ attrs: { options: DEFAULT_STATUSES, selected: DEFAULT_STATUSES[0] }
3248
+ }).run();
3249
+ },
3250
+ insertLocationChip: (editor) => {
3251
+ const label = typeof window !== "undefined" && typeof window.prompt === "function" ? window.prompt("Enter location name:", "") ?? "" : "";
3252
+ if (!label.trim()) return false;
3253
+ return editor.chain().focus().insertContent({
3254
+ type: "locationChip",
3255
+ attrs: { label: label.trim() }
3256
+ }).run();
3257
+ }
3258
+ }
3259
+ });
3260
+
3261
+ // src/slashMenu.ts
3262
+ var import_core12 = require("@tiptap/core");
3263
+ var import_state3 = require("@tiptap/pm/state");
3264
+ var import_docflow_core20 = require("@kedataindo/docflow-core");
2730
3265
  var slashState = {
2731
3266
  open: false,
2732
3267
  query: "",
@@ -2780,12 +3315,12 @@ function getRegisteredCommands() {
2780
3315
  return true;
2781
3316
  });
2782
3317
  }
2783
- var SlashMenuExtension = import_core10.Extension.create({
3318
+ var SlashMenuExtension = import_core12.Extension.create({
2784
3319
  name: "slashMenu",
2785
3320
  addProseMirrorPlugins() {
2786
3321
  return [
2787
- new import_state2.Plugin({
2788
- key: new import_state2.PluginKey("slashMenu"),
3322
+ new import_state3.Plugin({
3323
+ key: new import_state3.PluginKey("slashMenu"),
2789
3324
  props: {
2790
3325
  handleTextInput(view, from, _to, text) {
2791
3326
  if (text === "/") {
@@ -2871,7 +3406,7 @@ var SlashMenuExtension = import_core10.Extension.create({
2871
3406
  ];
2872
3407
  }
2873
3408
  });
2874
- var slashMenuPlugin = (0, import_docflow_core19.definePlugin)({
3409
+ var slashMenuPlugin = (0, import_docflow_core20.definePlugin)({
2875
3410
  id: "slash-menu",
2876
3411
  tiptapExtensions: [SlashMenuExtension],
2877
3412
  hooks: {
@@ -2902,7 +3437,8 @@ var defaultPlugins = [
2902
3437
  highlightPlugin,
2903
3438
  citationPlugin,
2904
3439
  aiPlugin,
2905
- commentPlugin
3440
+ commentPlugin,
3441
+ smartElementsPlugin
2906
3442
  ];
2907
3443
  // Annotate the CommonJS export names for ESM import in node:
2908
3444
  0 && (module.exports = {
@@ -2915,9 +3451,14 @@ var defaultPlugins = [
2915
3451
  CiteEngine,
2916
3452
  CommentMarkExtension,
2917
3453
  DEFAULT_CSL_STYLE,
3454
+ DateChipNode,
3455
+ DropdownChipNode,
3456
+ FileChipNode,
2918
3457
  FontSizeExtension,
2919
3458
  FootnoteNode,
3459
+ LocationChipNode,
2920
3460
  PageBreak,
3461
+ PeopleChipNode,
2921
3462
  SlashMenuExtension,
2922
3463
  TocEntryNode,
2923
3464
  TocNode,
@@ -2941,8 +3482,10 @@ var defaultPlugins = [
2941
3482
  headingsPlugin,
2942
3483
  highlightPlugin,
2943
3484
  imagePlugin,
3485
+ insertMarkdownBlock,
2944
3486
  linkPlugin,
2945
3487
  listsPlugin,
3488
+ markdownToFragment,
2946
3489
  nextCitationId,
2947
3490
  onSlashStateChange,
2948
3491
  pageBreakPlugin,
@@ -2952,6 +3495,7 @@ var defaultPlugins = [
2952
3495
  sanitizeCiteprocHtml,
2953
3496
  slashMenuPlugin,
2954
3497
  slashState,
3498
+ smartElementsPlugin,
2955
3499
  tablePlugin,
2956
3500
  textColorPlugin,
2957
3501
  tocPlugin