@milkdown/crepe 7.20.0 → 7.21.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.
Files changed (173) hide show
  1. package/lib/cjs/builder.js +1 -0
  2. package/lib/cjs/builder.js.map +1 -1
  3. package/lib/cjs/feature/ai/index.js +1492 -0
  4. package/lib/cjs/feature/ai/index.js.map +1 -0
  5. package/lib/cjs/feature/block-edit/index.js +1 -0
  6. package/lib/cjs/feature/block-edit/index.js.map +1 -1
  7. package/lib/cjs/feature/code-mirror/index.js +1 -0
  8. package/lib/cjs/feature/code-mirror/index.js.map +1 -1
  9. package/lib/cjs/feature/cursor/index.js +1 -0
  10. package/lib/cjs/feature/cursor/index.js.map +1 -1
  11. package/lib/cjs/feature/image-block/index.js +1 -0
  12. package/lib/cjs/feature/image-block/index.js.map +1 -1
  13. package/lib/cjs/feature/latex/index.js +2 -0
  14. package/lib/cjs/feature/latex/index.js.map +1 -1
  15. package/lib/cjs/feature/link-tooltip/index.js +1 -0
  16. package/lib/cjs/feature/link-tooltip/index.js.map +1 -1
  17. package/lib/cjs/feature/list-item/index.js +1 -0
  18. package/lib/cjs/feature/list-item/index.js.map +1 -1
  19. package/lib/cjs/feature/placeholder/index.js +1 -0
  20. package/lib/cjs/feature/placeholder/index.js.map +1 -1
  21. package/lib/cjs/feature/table/index.js +1 -0
  22. package/lib/cjs/feature/table/index.js.map +1 -1
  23. package/lib/cjs/feature/toolbar/index.js +488 -3
  24. package/lib/cjs/feature/toolbar/index.js.map +1 -1
  25. package/lib/cjs/feature/top-bar/index.js +1 -0
  26. package/lib/cjs/feature/top-bar/index.js.map +1 -1
  27. package/lib/cjs/index.js +1424 -25
  28. package/lib/cjs/index.js.map +1 -1
  29. package/lib/cjs/llm-providers/anthropic/index.js +152 -0
  30. package/lib/cjs/llm-providers/anthropic/index.js.map +1 -0
  31. package/lib/cjs/llm-providers/openai/index.js +143 -0
  32. package/lib/cjs/llm-providers/openai/index.js.map +1 -0
  33. package/lib/esm/builder.js +1 -0
  34. package/lib/esm/builder.js.map +1 -1
  35. package/lib/esm/feature/ai/index.js +1487 -0
  36. package/lib/esm/feature/ai/index.js.map +1 -0
  37. package/lib/esm/feature/block-edit/index.js +1 -0
  38. package/lib/esm/feature/block-edit/index.js.map +1 -1
  39. package/lib/esm/feature/code-mirror/index.js +1 -0
  40. package/lib/esm/feature/code-mirror/index.js.map +1 -1
  41. package/lib/esm/feature/cursor/index.js +1 -0
  42. package/lib/esm/feature/cursor/index.js.map +1 -1
  43. package/lib/esm/feature/image-block/index.js +1 -0
  44. package/lib/esm/feature/image-block/index.js.map +1 -1
  45. package/lib/esm/feature/latex/index.js +2 -0
  46. package/lib/esm/feature/latex/index.js.map +1 -1
  47. package/lib/esm/feature/link-tooltip/index.js +1 -0
  48. package/lib/esm/feature/link-tooltip/index.js.map +1 -1
  49. package/lib/esm/feature/list-item/index.js +1 -0
  50. package/lib/esm/feature/list-item/index.js.map +1 -1
  51. package/lib/esm/feature/placeholder/index.js +1 -0
  52. package/lib/esm/feature/placeholder/index.js.map +1 -1
  53. package/lib/esm/feature/table/index.js +1 -0
  54. package/lib/esm/feature/table/index.js.map +1 -1
  55. package/lib/esm/feature/toolbar/index.js +490 -5
  56. package/lib/esm/feature/toolbar/index.js.map +1 -1
  57. package/lib/esm/feature/top-bar/index.js +1 -0
  58. package/lib/esm/feature/top-bar/index.js.map +1 -1
  59. package/lib/esm/index.js +1414 -15
  60. package/lib/esm/index.js.map +1 -1
  61. package/lib/esm/llm-providers/anthropic/index.js +150 -0
  62. package/lib/esm/llm-providers/anthropic/index.js.map +1 -0
  63. package/lib/esm/llm-providers/openai/index.js +141 -0
  64. package/lib/esm/llm-providers/openai/index.js.map +1 -0
  65. package/lib/theme/common/ai.css +446 -0
  66. package/lib/theme/common/code-mirror.css +14 -0
  67. package/lib/theme/common/diff.css +177 -0
  68. package/lib/theme/common/style.css +2 -0
  69. package/lib/tsconfig.tsbuildinfo +1 -1
  70. package/lib/types/feature/ai/ai.spec.d.ts +2 -0
  71. package/lib/types/feature/ai/ai.spec.d.ts.map +1 -0
  72. package/lib/types/feature/ai/commands.d.ts +24 -0
  73. package/lib/types/feature/ai/commands.d.ts.map +1 -0
  74. package/lib/types/feature/ai/context.d.ts +4 -0
  75. package/lib/types/feature/ai/context.d.ts.map +1 -0
  76. package/lib/types/feature/ai/diff-actions/index.d.ts +12 -0
  77. package/lib/types/feature/ai/diff-actions/index.d.ts.map +1 -0
  78. package/lib/types/feature/ai/diff-actions/view.d.ts +21 -0
  79. package/lib/types/feature/ai/diff-actions/view.d.ts.map +1 -0
  80. package/lib/types/feature/ai/index.d.ts +7 -0
  81. package/lib/types/feature/ai/index.d.ts.map +1 -0
  82. package/lib/types/feature/ai/instruction-tooltip/component.d.ts +26 -0
  83. package/lib/types/feature/ai/instruction-tooltip/component.d.ts.map +1 -0
  84. package/lib/types/feature/ai/instruction-tooltip/index.d.ts +17 -0
  85. package/lib/types/feature/ai/instruction-tooltip/index.d.ts.map +1 -0
  86. package/lib/types/feature/ai/instruction-tooltip/suggestions.d.ts +50 -0
  87. package/lib/types/feature/ai/instruction-tooltip/suggestions.d.ts.map +1 -0
  88. package/lib/types/feature/ai/instruction-tooltip/view.d.ts +19 -0
  89. package/lib/types/feature/ai/instruction-tooltip/view.d.ts.map +1 -0
  90. package/lib/types/feature/ai/streaming-indicator.d.ts +9 -0
  91. package/lib/types/feature/ai/streaming-indicator.d.ts.map +1 -0
  92. package/lib/types/feature/ai/types.d.ts +58 -0
  93. package/lib/types/feature/ai/types.d.ts.map +1 -0
  94. package/lib/types/feature/index.d.ts +4 -1
  95. package/lib/types/feature/index.d.ts.map +1 -1
  96. package/lib/types/feature/latex/inline-tooltip/inline-tooltip.spec.d.ts +2 -0
  97. package/lib/types/feature/latex/inline-tooltip/inline-tooltip.spec.d.ts.map +1 -0
  98. package/lib/types/feature/latex/inline-tooltip/view.d.ts.map +1 -1
  99. package/lib/types/feature/loader.d.ts.map +1 -1
  100. package/lib/types/feature/toolbar/config.d.ts.map +1 -1
  101. package/lib/types/feature/toolbar/index.d.ts +1 -0
  102. package/lib/types/feature/toolbar/index.d.ts.map +1 -1
  103. package/lib/types/icons/ai.d.ts +2 -0
  104. package/lib/types/icons/ai.d.ts.map +1 -0
  105. package/lib/types/icons/chevron-left.d.ts +2 -0
  106. package/lib/types/icons/chevron-left.d.ts.map +1 -0
  107. package/lib/types/icons/chevron-right.d.ts +2 -0
  108. package/lib/types/icons/chevron-right.d.ts.map +1 -0
  109. package/lib/types/icons/enter-key.d.ts +2 -0
  110. package/lib/types/icons/enter-key.d.ts.map +1 -0
  111. package/lib/types/icons/grammar-check.d.ts +2 -0
  112. package/lib/types/icons/grammar-check.d.ts.map +1 -0
  113. package/lib/types/icons/index.d.ts +11 -0
  114. package/lib/types/icons/index.d.ts.map +1 -1
  115. package/lib/types/icons/longer.d.ts +2 -0
  116. package/lib/types/icons/longer.d.ts.map +1 -0
  117. package/lib/types/icons/retry.d.ts +2 -0
  118. package/lib/types/icons/retry.d.ts.map +1 -0
  119. package/lib/types/icons/send-prompt.d.ts +2 -0
  120. package/lib/types/icons/send-prompt.d.ts.map +1 -0
  121. package/lib/types/icons/send.d.ts +2 -0
  122. package/lib/types/icons/send.d.ts.map +1 -0
  123. package/lib/types/icons/shorter.d.ts +2 -0
  124. package/lib/types/icons/shorter.d.ts.map +1 -0
  125. package/lib/types/icons/translate.d.ts +2 -0
  126. package/lib/types/icons/translate.d.ts.map +1 -0
  127. package/lib/types/llm-providers/anthropic/index.d.ts +21 -0
  128. package/lib/types/llm-providers/anthropic/index.d.ts.map +1 -0
  129. package/lib/types/llm-providers/openai/index.d.ts +15 -0
  130. package/lib/types/llm-providers/openai/index.d.ts.map +1 -0
  131. package/lib/types/llm-providers/providers.spec.d.ts +2 -0
  132. package/lib/types/llm-providers/providers.spec.d.ts.map +1 -0
  133. package/lib/types/llm-providers/shared.d.ts +17 -0
  134. package/lib/types/llm-providers/shared.d.ts.map +1 -0
  135. package/package.json +18 -2
  136. package/src/feature/ai/ai.spec.ts +742 -0
  137. package/src/feature/ai/commands.ts +257 -0
  138. package/src/feature/ai/context.ts +45 -0
  139. package/src/feature/ai/diff-actions/index.ts +95 -0
  140. package/src/feature/ai/diff-actions/view.ts +237 -0
  141. package/src/feature/ai/index.ts +118 -0
  142. package/src/feature/ai/instruction-tooltip/component.tsx +414 -0
  143. package/src/feature/ai/instruction-tooltip/index.ts +101 -0
  144. package/src/feature/ai/instruction-tooltip/suggestions.ts +249 -0
  145. package/src/feature/ai/instruction-tooltip/view.ts +159 -0
  146. package/src/feature/ai/streaming-indicator.ts +183 -0
  147. package/src/feature/ai/types.ts +178 -0
  148. package/src/feature/index.ts +8 -2
  149. package/src/feature/latex/inline-tooltip/inline-tooltip.spec.ts +81 -0
  150. package/src/feature/latex/inline-tooltip/view.ts +2 -0
  151. package/src/feature/loader.ts +4 -0
  152. package/src/feature/toolbar/config.ts +27 -1
  153. package/src/feature/toolbar/index.ts +1 -0
  154. package/src/icons/ai.ts +14 -0
  155. package/src/icons/chevron-left.ts +15 -0
  156. package/src/icons/chevron-right.ts +15 -0
  157. package/src/icons/enter-key.ts +13 -0
  158. package/src/icons/grammar-check.ts +13 -0
  159. package/src/icons/index.ts +11 -0
  160. package/src/icons/longer.ts +13 -0
  161. package/src/icons/retry.ts +13 -0
  162. package/src/icons/send-prompt.ts +13 -0
  163. package/src/icons/send.ts +13 -0
  164. package/src/icons/shorter.ts +13 -0
  165. package/src/icons/translate.ts +13 -0
  166. package/src/llm-providers/anthropic/index.ts +133 -0
  167. package/src/llm-providers/openai/index.ts +110 -0
  168. package/src/llm-providers/providers.spec.ts +472 -0
  169. package/src/llm-providers/shared.ts +170 -0
  170. package/src/theme/common/ai.css +430 -0
  171. package/src/theme/common/code-mirror.css +14 -0
  172. package/src/theme/common/diff.css +196 -0
  173. package/src/theme/common/style.css +2 -0
package/lib/cjs/index.js CHANGED
@@ -3,31 +3,36 @@
3
3
  var lodashEs = require('lodash-es');
4
4
  var languageData = require('@codemirror/language-data');
5
5
  var themeOneDark = require('@codemirror/theme-one-dark');
6
- var block = require('@milkdown/kit/plugin/block');
6
+ var diff$1 = require('@milkdown/kit/component/diff');
7
+ var diff = require('@milkdown/kit/plugin/diff');
8
+ var streaming = require('@milkdown/kit/plugin/streaming');
7
9
  var ctx = require('@milkdown/kit/ctx');
8
10
  var core = require('@milkdown/kit/core');
9
- var commonmark = require('@milkdown/kit/preset/commonmark');
10
- var prose = require('@milkdown/kit/prose');
11
+ var exception = require('@milkdown/kit/exception');
12
+ var utils = require('@milkdown/kit/utils');
11
13
  var state = require('@milkdown/kit/prose/state');
14
+ var DOMPurify = require('dompurify');
15
+ var tooltip = require('@milkdown/kit/plugin/tooltip');
16
+ var prose = require('@milkdown/kit/prose');
12
17
  var vue = require('vue');
13
- var slash = require('@milkdown/kit/plugin/slash');
14
- var utils = require('@milkdown/kit/utils');
15
18
  var component = require('@milkdown/kit/component');
19
+ var view = require('@milkdown/kit/prose/view');
20
+ var block = require('@milkdown/kit/plugin/block');
21
+ var commonmark = require('@milkdown/kit/preset/commonmark');
22
+ var slash = require('@milkdown/kit/plugin/slash');
16
23
  var imageBlock$1 = require('@milkdown/kit/component/image-block');
17
24
  var gfm = require('@milkdown/kit/preset/gfm');
18
25
  var commands = require('@codemirror/commands');
19
- var view = require('@codemirror/view');
26
+ var view$1 = require('@codemirror/view');
20
27
  var codeBlock = require('@milkdown/kit/component/code-block');
21
28
  var codemirror = require('codemirror');
22
29
  var cursor$1 = require('@milkdown/kit/plugin/cursor');
23
30
  var prosemirrorVirtualCursor = require('prosemirror-virtual-cursor');
24
31
  var imageInline = require('@milkdown/kit/component/image-inline');
25
32
  var katex = require('katex');
26
- var tooltip = require('@milkdown/kit/plugin/tooltip');
27
33
  var history = require('@milkdown/kit/prose/history');
28
34
  var keymap = require('@milkdown/kit/prose/keymap');
29
35
  var model = require('@milkdown/kit/prose/model');
30
- var view$1 = require('@milkdown/kit/prose/view');
31
36
  var inputrules = require('@milkdown/kit/prose/inputrules');
32
37
  var remarkMath = require('remark-math');
33
38
  var unistUtilVisit = require('unist-util-visit');
@@ -54,6 +59,7 @@ var CrepeFeature = /* @__PURE__ */ ((CrepeFeature2) => {
54
59
  CrepeFeature2["Table"] = "table";
55
60
  CrepeFeature2["Latex"] = "latex";
56
61
  CrepeFeature2["TopBar"] = "top-bar";
62
+ CrepeFeature2["AI"] = "ai";
57
63
  return CrepeFeature2;
58
64
  })(CrepeFeature || {});
59
65
  const defaultFeatures = {
@@ -67,9 +73,25 @@ const defaultFeatures = {
67
73
  ["code-mirror" /* CodeMirror */]: true,
68
74
  ["table" /* Table */]: true,
69
75
  ["latex" /* Latex */]: true,
70
- ["top-bar" /* TopBar */]: false
76
+ ["top-bar" /* TopBar */]: false,
77
+ ["ai" /* AI */]: false
71
78
  };
72
79
 
80
+ const aiIcon = `
81
+ <svg
82
+ xmlns="http://www.w3.org/2000/svg"
83
+ width="24"
84
+ height="24"
85
+ viewBox="0 0 24 24"
86
+ >
87
+ <path
88
+ fill="currentColor"
89
+ d="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25zm0 14l1.25-2.75L23 19l-2.75-1.25L19 15l-1.25 2.75L15 19l2.75 1.25zM9 20l3-6.5L18 10l-6-3.5L9 0L6 6.5L0 10l6 3.5z"
90
+ transform="translate(3 3) scale(0.75)"
91
+ />
92
+ </svg>
93
+ `;
94
+
73
95
  const alignCenterIcon = `
74
96
  <svg
75
97
  xmlns="http://www.w3.org/2000/svg"
@@ -231,6 +253,38 @@ const chevronDownIcon = `
231
253
  </svg>
232
254
  `;
233
255
 
256
+ const chevronLeftIcon = `
257
+ <svg
258
+ xmlns="http://www.w3.org/2000/svg"
259
+ fill="none"
260
+ viewBox="0 0 24 24"
261
+ stroke-width="1.5"
262
+ stroke="currentColor"
263
+ >
264
+ <path
265
+ stroke-linecap="round"
266
+ stroke-linejoin="round"
267
+ d="M15.75 19.5L8.25 12l7.5-7.5"
268
+ />
269
+ </svg>
270
+ `;
271
+
272
+ const chevronRightIcon = `
273
+ <svg
274
+ xmlns="http://www.w3.org/2000/svg"
275
+ fill="none"
276
+ viewBox="0 0 24 24"
277
+ stroke-width="1.5"
278
+ stroke="currentColor"
279
+ >
280
+ <path
281
+ stroke-linecap="round"
282
+ stroke-linejoin="round"
283
+ d="M8.25 4.5l7.5 7.5-7.5 7.5"
284
+ />
285
+ </svg>
286
+ `;
287
+
234
288
  const clearIcon = `
235
289
  <svg
236
290
  xmlns="http://www.w3.org/2000/svg"
@@ -375,6 +429,34 @@ const editIcon = `
375
429
  </svg>
376
430
  `;
377
431
 
432
+ const enterKeyIcon = `
433
+ <svg
434
+ xmlns="http://www.w3.org/2000/svg"
435
+ width="24"
436
+ height="24"
437
+ viewBox="0 0 24 24"
438
+ >
439
+ <path
440
+ fill="currentColor"
441
+ d="M9 16l-5-5l5-5l1.4 1.4L7.825 10H18V6h2v6H7.825l2.575 2.6z"
442
+ />
443
+ </svg>
444
+ `;
445
+
446
+ const grammarCheckIcon = `
447
+ <svg
448
+ xmlns="http://www.w3.org/2000/svg"
449
+ width="24"
450
+ height="24"
451
+ viewBox="0 0 24 24"
452
+ >
453
+ <path
454
+ fill="currentColor"
455
+ d="M9.55 17.575L4.225 12.25l1.4-1.425L9.55 14.75l8.825-8.825l1.4 1.425z"
456
+ />
457
+ </svg>
458
+ `;
459
+
378
460
  const h1Icon = `
379
461
  <svg
380
462
  xmlns="http://www.w3.org/2000/svg"
@@ -541,6 +623,20 @@ const linkIcon = `
541
623
  </svg>
542
624
  `;
543
625
 
626
+ const longerIcon = `
627
+ <svg
628
+ xmlns="http://www.w3.org/2000/svg"
629
+ width="24"
630
+ height="24"
631
+ viewBox="0 0 24 24"
632
+ >
633
+ <path
634
+ fill="currentColor"
635
+ d="M3 18v-2h13v2zm0-5v-2h18v2zm0-5V6h18v2zm15 13v-3h-3v-2h3v-3h2v3h3v2h-3v3z"
636
+ />
637
+ </svg>
638
+ `;
639
+
544
640
  const menuIcon = `
545
641
  <svg
546
642
  xmlns="http://www.w3.org/2000/svg"
@@ -634,6 +730,20 @@ const removeIcon = `
634
730
  </svg>
635
731
  `;
636
732
 
733
+ const retryIcon = `
734
+ <svg
735
+ xmlns="http://www.w3.org/2000/svg"
736
+ width="24"
737
+ height="24"
738
+ viewBox="0 0 24 24"
739
+ >
740
+ <path
741
+ fill="currentColor"
742
+ d="M12 20q-3.35 0-5.675-2.325T4 12q0-3.35 2.325-5.675T12 4q1.725 0 3.3.713T18 6.75V4h2v7h-7V9h4.2q-.8-1.4-2.187-2.2T12 6Q9.5 6 7.75 7.75T6 12q0 2.5 1.75 4.25T12 18q1.925 0 3.475-1.1T17.65 14h2.1q-.7 2.65-2.85 4.325T12 20"
743
+ />
744
+ </svg>
745
+ `;
746
+
637
747
  const searchIcon = `
638
748
  <svg
639
749
  xmlns="http://www.w3.org/2000/svg"
@@ -651,6 +761,48 @@ const searchIcon = `
651
761
  </svg>
652
762
  `;
653
763
 
764
+ const sendIcon = `
765
+ <svg
766
+ xmlns="http://www.w3.org/2000/svg"
767
+ width="24"
768
+ height="24"
769
+ viewBox="0 0 24 24"
770
+ >
771
+ <path
772
+ fill="currentColor"
773
+ d="M11 20V7.825l-5.6 5.6L4 12l8-8l8 8l-1.4 1.425l-5.6-5.6V20z"
774
+ />
775
+ </svg>
776
+ `;
777
+
778
+ const sendPromptIcon = `
779
+ <svg
780
+ xmlns="http://www.w3.org/2000/svg"
781
+ width="24"
782
+ height="24"
783
+ viewBox="0 0 24 24"
784
+ >
785
+ <path
786
+ fill="currentColor"
787
+ d="M3 20v-6l8-2l-8-2V4l19 8z"
788
+ />
789
+ </svg>
790
+ `;
791
+
792
+ const shorterIcon = `
793
+ <svg
794
+ xmlns="http://www.w3.org/2000/svg"
795
+ width="24"
796
+ height="24"
797
+ viewBox="0 0 24 24"
798
+ >
799
+ <path
800
+ fill="currentColor"
801
+ d="M5 13v-2h14v2z"
802
+ />
803
+ </svg>
804
+ `;
805
+
654
806
  const strikethroughIcon = `
655
807
  <svg
656
808
  xmlns="http://www.w3.org/2000/svg"
@@ -717,6 +869,20 @@ const todoListIcon = `
717
869
  </svg>
718
870
  `;
719
871
 
872
+ const translateIcon = `
873
+ <svg
874
+ xmlns="http://www.w3.org/2000/svg"
875
+ width="24"
876
+ height="24"
877
+ viewBox="0 0 24 24"
878
+ >
879
+ <path
880
+ fill="currentColor"
881
+ d="m12.9 16l-2.55-2.5l.05-.05q1.45-1.6 2.262-3.117T13.95 7H16V5h-6V3H8v2H2v2h9.1q-.5 1.2-1.225 2.387T8 11.7q-.6-.6-1.187-1.412T5.85 8.6H3.85q.5 1.4 1.3 2.7T6.95 13.7L2.85 17.75L4.25 19.15L8 15.4L10.55 17.95zM18.5 22h-2L21 10h2l4.5 12h-2l-1.05-3h-4.9zm1.6-5h3.6l-1.8-5.1z"
882
+ />
883
+ </svg>
884
+ `;
885
+
720
886
  const functionsIcon = `
721
887
  <svg
722
888
  xmlns="http://www.w3.org/2000/svg"
@@ -776,6 +942,1223 @@ function crepeFeatureConfig(feature) {
776
942
  };
777
943
  }
778
944
 
945
+ function defaultBuildContext(ctx, instruction) {
946
+ var _a;
947
+ const view = ctx.get(core.editorViewCtx);
948
+ const serializer = ctx.get(core.serializerCtx);
949
+ const { state } = view;
950
+ const document = serializer(state.doc);
951
+ let selection = "";
952
+ if (!state.selection.empty) {
953
+ const { from, to } = state.selection;
954
+ const slice = state.doc.slice(from, to);
955
+ const { schema } = state.doc.type;
956
+ let wrapper = schema.topNodeType.createAndFill(null, slice.content);
957
+ if (!wrapper) {
958
+ const paragraph = (_a = schema.nodes.paragraph) == null ? void 0 : _a.createAndFill(
959
+ null,
960
+ slice.content
961
+ );
962
+ if (paragraph) wrapper = schema.topNodeType.createAndFill(null, paragraph);
963
+ }
964
+ selection = wrapper ? serializer(wrapper) : state.doc.textBetween(from, to);
965
+ }
966
+ return { document, selection, instruction };
967
+ }
968
+
969
+ const aiProviderConfig = utils.$ctx(
970
+ {
971
+ provider: void 0,
972
+ buildContext: void 0,
973
+ diffReviewOnEnd: true,
974
+ onError: (error) => {
975
+ console.error(`[milkdown/ai] [${error.code}]`, error);
976
+ },
977
+ aiIcon: void 0
978
+ },
979
+ "aiProviderConfig"
980
+ );
981
+ const aiSessionCtx = utils.$ctx(
982
+ {
983
+ abortController: null,
984
+ label: "",
985
+ lastInstruction: "",
986
+ lastLabel: void 0,
987
+ lastFrom: -1,
988
+ lastTo: -1,
989
+ diffOwnedByAI: false
990
+ },
991
+ "aiSession"
992
+ );
993
+ function emitAIError(ctx, error) {
994
+ const config = ctx.get(aiProviderConfig.key);
995
+ try {
996
+ config.onError(error);
997
+ } catch (handlerError) {
998
+ console.error("[milkdown/ai] onError handler failed:", handlerError);
999
+ }
1000
+ }
1001
+ function clearActiveSession(ctx) {
1002
+ const current = ctx.get(aiSessionCtx.key);
1003
+ ctx.set(aiSessionCtx.key, {
1004
+ ...current,
1005
+ abortController: null,
1006
+ label: ""
1007
+ });
1008
+ }
1009
+ async function runProvider(ctx, provider, promptContext, abortController) {
1010
+ try {
1011
+ const iterable = provider(promptContext, abortController.signal);
1012
+ const commands = ctx.get(core.commandsCtx);
1013
+ for await (const chunk of iterable) {
1014
+ if (abortController.signal.aborted) break;
1015
+ commands.call(streaming.pushChunkCmd.key, chunk);
1016
+ }
1017
+ if (abortController.signal.aborted) return;
1018
+ const config = ctx.get(aiProviderConfig.key);
1019
+ if (config.diffReviewOnEnd) {
1020
+ const cur = ctx.get(aiSessionCtx.key);
1021
+ ctx.set(aiSessionCtx.key, { ...cur, diffOwnedByAI: true });
1022
+ }
1023
+ const dispatched = commands.call(streaming.endStreamingCmd.key, {
1024
+ diffReview: config.diffReviewOnEnd
1025
+ });
1026
+ if (config.diffReviewOnEnd && !dispatched) {
1027
+ const cur = ctx.get(aiSessionCtx.key);
1028
+ ctx.set(aiSessionCtx.key, { ...cur, diffOwnedByAI: false });
1029
+ }
1030
+ } catch (error) {
1031
+ if (abortController.signal.aborted) return;
1032
+ const milkdownError = exception.aiProviderError(error);
1033
+ emitAIError(ctx, milkdownError);
1034
+ const commands = ctx.get(core.commandsCtx);
1035
+ commands.call(streaming.abortStreamingCmd.key, { keep: false });
1036
+ } finally {
1037
+ const current = ctx.get(aiSessionCtx.key);
1038
+ if (current.abortController === abortController) {
1039
+ clearActiveSession(ctx);
1040
+ }
1041
+ }
1042
+ }
1043
+ const runAICmd = utils.$command("RunAI", (ctx) => {
1044
+ return (options) => (state, dispatch) => {
1045
+ var _a, _b, _c, _d;
1046
+ if (!(options == null ? void 0 : options.instruction)) return false;
1047
+ const config = ctx.get(aiProviderConfig.key);
1048
+ if (!config.provider) return false;
1049
+ const session = ctx.get(aiSessionCtx.key);
1050
+ if (session.abortController) return false;
1051
+ if ((_a = streaming.streamingPluginKey.getState(state)) == null ? void 0 : _a.active) return false;
1052
+ if ((_b = diff.diffPluginKey.getState(state)) == null ? void 0 : _b.active) return false;
1053
+ if (!dispatch) return true;
1054
+ const abortController = new AbortController();
1055
+ const { from, to } = state.selection;
1056
+ ctx.set(aiSessionCtx.key, {
1057
+ abortController,
1058
+ label: (_c = options.label) != null ? _c : "",
1059
+ lastInstruction: options.instruction,
1060
+ lastLabel: options.label,
1061
+ lastFrom: from,
1062
+ lastTo: to,
1063
+ // Reset every run; only the success path that hands off to diff
1064
+ // review flips it back on.
1065
+ diffOwnedByAI: false
1066
+ });
1067
+ const commands = ctx.get(core.commandsCtx);
1068
+ const insertAt = state.selection.empty ? "cursor" : "selection";
1069
+ if (!commands.call(streaming.startStreamingCmd.key, { insertAt })) {
1070
+ clearActiveSession(ctx);
1071
+ return false;
1072
+ }
1073
+ let promptContext;
1074
+ try {
1075
+ const buildContext = (_d = config.buildContext) != null ? _d : defaultBuildContext;
1076
+ promptContext = buildContext(ctx, options.instruction);
1077
+ } catch (error) {
1078
+ const milkdownError = exception.aiBuildContextError(error);
1079
+ emitAIError(ctx, milkdownError);
1080
+ commands.call(streaming.abortStreamingCmd.key, { keep: false });
1081
+ clearActiveSession(ctx);
1082
+ return false;
1083
+ }
1084
+ void runProvider(ctx, config.provider, promptContext, abortController);
1085
+ return true;
1086
+ };
1087
+ });
1088
+ const abortAICmd = utils.$command("AbortAI", (ctx) => {
1089
+ return (options) => (state, dispatch) => {
1090
+ var _a;
1091
+ const session = ctx.get(aiSessionCtx.key);
1092
+ if (!dispatch) return !!session.abortController;
1093
+ if (!session.abortController) return false;
1094
+ session.abortController.abort();
1095
+ clearActiveSession(ctx);
1096
+ if ((_a = streaming.streamingPluginKey.getState(state)) == null ? void 0 : _a.active) {
1097
+ const commands = ctx.get(core.commandsCtx);
1098
+ commands.call(streaming.abortStreamingCmd.key, options);
1099
+ }
1100
+ return true;
1101
+ };
1102
+ });
1103
+
1104
+ var __typeError$a = (msg) => {
1105
+ throw TypeError(msg);
1106
+ };
1107
+ var __accessCheck$a = (obj, member, msg) => member.has(obj) || __typeError$a("Cannot " + msg);
1108
+ var __privateGet$a = (obj, member, getter) => (__accessCheck$a(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
1109
+ var __privateAdd$a = (obj, member, value) => member.has(obj) ? __typeError$a("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1110
+ var __privateSet$a = (obj, member, value, setter) => (__accessCheck$a(obj, member, "write to private field"), member.set(obj, value), value);
1111
+ var __privateMethod = (obj, member, method) => (__accessCheck$a(obj, member, "access private method"), method);
1112
+ var _panel, _host, _retryBtn, _config, _visible, _diffActive, _diffStartDoc, _ownedByAI, _DiffActionsPanelView_instances, findHost_fn, makeButton_fn, makeShortcutChip_fn, _retry, canRetry_fn, _rejectAll, _acceptAll;
1113
+ const PANEL_CLASS = "milkdown-ai-diff-actions";
1114
+ function setSanitizedIcon(host, svg) {
1115
+ host.innerHTML = DOMPurify.sanitize(svg.trim());
1116
+ }
1117
+ function createIcon(svg) {
1118
+ const span = document.createElement("span");
1119
+ span.className = `${PANEL_CLASS}-icon`;
1120
+ setSanitizedIcon(span, svg);
1121
+ return span;
1122
+ }
1123
+ class DiffActionsPanelView {
1124
+ constructor(ctx, view, config) {
1125
+ this.ctx = ctx;
1126
+ __privateAdd$a(this, _DiffActionsPanelView_instances);
1127
+ __privateAdd$a(this, _panel);
1128
+ __privateAdd$a(this, _host);
1129
+ __privateAdd$a(this, _retryBtn);
1130
+ __privateAdd$a(this, _config);
1131
+ __privateAdd$a(this, _visible, false);
1132
+ /// Tracks the diff plugin's `active` flag across transactions so we
1133
+ /// can detect false→true / true→false edges independently of whether
1134
+ /// the panel is actually being shown.
1135
+ __privateAdd$a(this, _diffActive, false);
1136
+ /// Doc snapshot at the moment diff review activated. If the live doc
1137
+ /// drifts from this snapshot the user has accepted some per-change
1138
+ /// diffs and the stored `lastFrom`/`lastTo` no longer point at the
1139
+ /// original range — Retry is unsafe at that point.
1140
+ __privateAdd$a(this, _diffStartDoc, null);
1141
+ /// Whether the active diff review came from this AI session's
1142
+ /// streaming hand-off (vs being started manually via
1143
+ /// `startDiffReviewCmd`). Captured at the false→true transition.
1144
+ /// The panel only renders when this is true so it doesn't take over
1145
+ /// non-AI diff flows that exist independently of the AI feature.
1146
+ __privateAdd$a(this, _ownedByAI, false);
1147
+ __privateAdd$a(this, _retry, () => {
1148
+ const session = this.ctx.get(aiSessionCtx.key);
1149
+ if (!session.lastInstruction) return;
1150
+ if (!__privateGet$a(this, _ownedByAI) || !__privateMethod(this, _DiffActionsPanelView_instances, canRetry_fn).call(this)) return;
1151
+ const commands = this.ctx.get(core.commandsCtx);
1152
+ commands.call(diff.clearDiffReviewCmd.key);
1153
+ const editorView = this.ctx.get(core.editorViewCtx);
1154
+ const { doc } = editorView.state;
1155
+ const from = Math.min(Math.max(session.lastFrom, 0), doc.content.size);
1156
+ const to = Math.min(Math.max(session.lastTo, 0), doc.content.size);
1157
+ editorView.dispatch(
1158
+ editorView.state.tr.setSelection(
1159
+ state.TextSelection.create(editorView.state.doc, from, to)
1160
+ )
1161
+ );
1162
+ commands.call(runAICmd.key, {
1163
+ instruction: session.lastInstruction,
1164
+ label: session.lastLabel
1165
+ });
1166
+ });
1167
+ __privateAdd$a(this, _rejectAll, () => {
1168
+ this.ctx.get(core.commandsCtx).call(diff.clearDiffReviewCmd.key);
1169
+ });
1170
+ __privateAdd$a(this, _acceptAll, () => {
1171
+ this.ctx.get(core.commandsCtx).call(diff.acceptAllDiffsCmd.key);
1172
+ });
1173
+ __privateSet$a(this, _config, config);
1174
+ __privateSet$a(this, _host, __privateMethod(this, _DiffActionsPanelView_instances, findHost_fn).call(this, view));
1175
+ const panel = document.createElement("div");
1176
+ panel.className = PANEL_CLASS;
1177
+ panel.dataset.show = "false";
1178
+ __privateSet$a(this, _retryBtn, __privateMethod(this, _DiffActionsPanelView_instances, makeButton_fn).call(this, "retry", config.retryIcon, config.retryLabel, __privateGet$a(this, _retry)));
1179
+ panel.appendChild(__privateGet$a(this, _retryBtn));
1180
+ panel.appendChild(
1181
+ __privateMethod(this, _DiffActionsPanelView_instances, makeButton_fn).call(this, "reject", config.rejectIcon, config.rejectAllLabel, __privateGet$a(this, _rejectAll))
1182
+ );
1183
+ const acceptBtn = __privateMethod(this, _DiffActionsPanelView_instances, makeButton_fn).call(this, "accept", config.acceptIcon, config.acceptAllLabel, __privateGet$a(this, _acceptAll));
1184
+ acceptBtn.appendChild(__privateMethod(this, _DiffActionsPanelView_instances, makeShortcutChip_fn).call(this));
1185
+ panel.appendChild(acceptBtn);
1186
+ __privateSet$a(this, _panel, panel);
1187
+ __privateGet$a(this, _host).appendChild(panel);
1188
+ this.update(view);
1189
+ }
1190
+ update(view) {
1191
+ var _a;
1192
+ const diffActive = !!((_a = diff.diffPluginKey.getState(view.state)) == null ? void 0 : _a.active);
1193
+ if (diffActive !== __privateGet$a(this, _diffActive)) {
1194
+ __privateSet$a(this, _diffActive, diffActive);
1195
+ if (diffActive) {
1196
+ const session = this.ctx.get(aiSessionCtx.key);
1197
+ __privateSet$a(this, _ownedByAI, session.diffOwnedByAI);
1198
+ __privateSet$a(this, _diffStartDoc, view.state.doc);
1199
+ } else {
1200
+ __privateSet$a(this, _ownedByAI, false);
1201
+ __privateSet$a(this, _diffStartDoc, null);
1202
+ const session = this.ctx.get(aiSessionCtx.key);
1203
+ if (session.diffOwnedByAI) {
1204
+ this.ctx.set(aiSessionCtx.key, {
1205
+ ...session,
1206
+ diffOwnedByAI: false
1207
+ });
1208
+ }
1209
+ }
1210
+ }
1211
+ const shouldShow = diffActive && __privateGet$a(this, _ownedByAI);
1212
+ if (shouldShow !== __privateGet$a(this, _visible)) {
1213
+ __privateSet$a(this, _visible, shouldShow);
1214
+ __privateGet$a(this, _panel).dataset.show = shouldShow ? "true" : "false";
1215
+ }
1216
+ if (shouldShow) {
1217
+ const session = this.ctx.get(aiSessionCtx.key);
1218
+ const docUntouched = !!__privateGet$a(this, _diffStartDoc) && view.state.doc.eq(__privateGet$a(this, _diffStartDoc));
1219
+ __privateGet$a(this, _retryBtn).disabled = !session.lastInstruction || !docUntouched;
1220
+ }
1221
+ }
1222
+ destroy() {
1223
+ __privateGet$a(this, _panel).remove();
1224
+ }
1225
+ }
1226
+ _panel = new WeakMap();
1227
+ _host = new WeakMap();
1228
+ _retryBtn = new WeakMap();
1229
+ _config = new WeakMap();
1230
+ _visible = new WeakMap();
1231
+ _diffActive = new WeakMap();
1232
+ _diffStartDoc = new WeakMap();
1233
+ _ownedByAI = new WeakMap();
1234
+ _DiffActionsPanelView_instances = new WeakSet();
1235
+ findHost_fn = function(view) {
1236
+ var _a;
1237
+ return (_a = view.dom.closest(".milkdown")) != null ? _a : document.body;
1238
+ };
1239
+ makeButton_fn = function(variant, icon, label, onClick) {
1240
+ const btn = document.createElement("button");
1241
+ btn.type = "button";
1242
+ btn.className = `${PANEL_CLASS}-btn ${PANEL_CLASS}-btn-${variant}`;
1243
+ btn.appendChild(createIcon(icon));
1244
+ const text = document.createElement("span");
1245
+ text.textContent = label;
1246
+ btn.appendChild(text);
1247
+ btn.addEventListener("mousedown", (e) => e.preventDefault());
1248
+ btn.addEventListener("click", (e) => {
1249
+ e.preventDefault();
1250
+ e.stopPropagation();
1251
+ onClick();
1252
+ });
1253
+ return btn;
1254
+ };
1255
+ makeShortcutChip_fn = function() {
1256
+ const shortcut = document.createElement("span");
1257
+ shortcut.className = `${PANEL_CLASS}-shortcut`;
1258
+ const cmd = document.createElement("span");
1259
+ cmd.textContent = __privateGet$a(this, _config).modSymbol;
1260
+ const enter = document.createElement("span");
1261
+ enter.className = `${PANEL_CLASS}-shortcut-icon`;
1262
+ setSanitizedIcon(enter, __privateGet$a(this, _config).enterKeyIcon);
1263
+ shortcut.append(cmd, enter);
1264
+ return shortcut;
1265
+ };
1266
+ _retry = new WeakMap();
1267
+ canRetry_fn = function() {
1268
+ if (!__privateGet$a(this, _diffStartDoc)) return false;
1269
+ const editorView = this.ctx.get(core.editorViewCtx);
1270
+ return editorView.state.doc.eq(__privateGet$a(this, _diffStartDoc));
1271
+ };
1272
+ _rejectAll = new WeakMap();
1273
+ _acceptAll = new WeakMap();
1274
+
1275
+ const diffActionsPanelKey = new state.PluginKey("CREPE_AI_DIFF_ACTIONS_PANEL");
1276
+ const DEFAULT_DIFF_ACTIONS_RETRY_LABEL = "Retry";
1277
+ const DEFAULT_DIFF_ACTIONS_REJECT_ALL_LABEL = "Reject all";
1278
+ const DEFAULT_DIFF_ACTIONS_ACCEPT_ALL_LABEL = "Accept all";
1279
+ function detectModSymbol() {
1280
+ var _a, _b, _c, _d;
1281
+ if (typeof navigator === "undefined") return "\u2318";
1282
+ const ua = navigator;
1283
+ const platform = (_d = (_c = (_b = (_a = ua.userAgentData) == null ? void 0 : _a.platform) != null ? _b : ua.platform) != null ? _c : ua.userAgent) != null ? _d : "";
1284
+ return /mac|iphone|ipad|ipod/i.test(platform) ? "\u2318" : "Ctrl";
1285
+ }
1286
+ const DEFAULT_DIFF_ACTIONS_MOD_SYMBOL = detectModSymbol();
1287
+ function resolveDiffActionsConfig(options) {
1288
+ var _a, _b, _c, _d, _e, _f, _g;
1289
+ const { config, enterKeyIcon: enterKeyIcon$1 } = options;
1290
+ return {
1291
+ retryLabel: (_a = config == null ? void 0 : config.retryLabel) != null ? _a : DEFAULT_DIFF_ACTIONS_RETRY_LABEL,
1292
+ rejectAllLabel: (_b = config == null ? void 0 : config.rejectAllLabel) != null ? _b : DEFAULT_DIFF_ACTIONS_REJECT_ALL_LABEL,
1293
+ acceptAllLabel: (_c = config == null ? void 0 : config.acceptAllLabel) != null ? _c : DEFAULT_DIFF_ACTIONS_ACCEPT_ALL_LABEL,
1294
+ retryIcon: (_d = config == null ? void 0 : config.retryIcon) != null ? _d : retryIcon,
1295
+ rejectIcon: (_e = config == null ? void 0 : config.rejectIcon) != null ? _e : clearIcon,
1296
+ acceptIcon: (_f = config == null ? void 0 : config.acceptIcon) != null ? _f : confirmIcon,
1297
+ enterKeyIcon: enterKeyIcon$1 != null ? enterKeyIcon$1 : enterKeyIcon,
1298
+ modSymbol: (_g = config == null ? void 0 : config.modSymbol) != null ? _g : DEFAULT_DIFF_ACTIONS_MOD_SYMBOL
1299
+ };
1300
+ }
1301
+ function diffActionsPanelPlugin(options = {}) {
1302
+ const resolved = resolveDiffActionsConfig(options);
1303
+ return utils.$prose((ctx) => {
1304
+ return new state.Plugin({
1305
+ key: diffActionsPanelKey,
1306
+ view(view) {
1307
+ return new DiffActionsPanelView(ctx, view, resolved);
1308
+ },
1309
+ props: {
1310
+ handleKeyDown(view, event) {
1311
+ var _a;
1312
+ if (event.key !== "Enter") return false;
1313
+ if (!(event.metaKey || event.ctrlKey)) return false;
1314
+ if (!((_a = diff.diffPluginKey.getState(view.state)) == null ? void 0 : _a.active)) return false;
1315
+ if (!ctx.get(aiSessionCtx.key).diffOwnedByAI) return false;
1316
+ event.preventDefault();
1317
+ const commands = ctx.get(core.commandsCtx);
1318
+ commands.call(diff.acceptAllDiffsCmd.key);
1319
+ return true;
1320
+ }
1321
+ }
1322
+ });
1323
+ });
1324
+ }
1325
+
1326
+ var __typeError$9 = (msg) => {
1327
+ throw TypeError(msg);
1328
+ };
1329
+ var __accessCheck$9 = (obj, member, msg) => member.has(obj) || __typeError$9("Cannot " + msg);
1330
+ var __privateGet$9 = (obj, member, getter) => (__accessCheck$9(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
1331
+ var __privateAdd$9 = (obj, member, value) => member.has(obj) ? __typeError$9("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1332
+ var __privateSet$9 = (obj, member, value, setter) => (__accessCheck$9(obj, member, "write to private field"), member.set(obj, value), value);
1333
+ var _nodes, _removeById;
1334
+ function createSubmenuBuilder(node) {
1335
+ const builder = {
1336
+ addItem: (id, item) => {
1337
+ node.items.set(id, item);
1338
+ return builder;
1339
+ },
1340
+ removeItem: (id) => {
1341
+ node.items.delete(id);
1342
+ return builder;
1343
+ },
1344
+ getItem: (id) => node.items.get(id),
1345
+ clear: () => {
1346
+ node.items.clear();
1347
+ return builder;
1348
+ }
1349
+ };
1350
+ return builder;
1351
+ }
1352
+ class AISuggestionsBuilder {
1353
+ constructor() {
1354
+ __privateAdd$9(this, _nodes, []);
1355
+ this.addItem = (id, item) => {
1356
+ __privateGet$9(this, _removeById).call(this, id);
1357
+ __privateGet$9(this, _nodes).push({ kind: "item", id, item });
1358
+ return this;
1359
+ };
1360
+ /// Add a submenu. Populate items via the optional `build` callback,
1361
+ /// or call `getSubmenu(id)` afterward. Returns `this` so calls can be
1362
+ /// chained at the parent level alongside `addItem`.
1363
+ this.addSubmenu = (id, def, build) => {
1364
+ __privateGet$9(this, _removeById).call(this, id);
1365
+ const node = { def, items: /* @__PURE__ */ new Map() };
1366
+ if (build) build(createSubmenuBuilder(node));
1367
+ __privateGet$9(this, _nodes).push({ kind: "submenu", id, node });
1368
+ return this;
1369
+ };
1370
+ this.removeItem = (id) => {
1371
+ __privateGet$9(this, _removeById).call(this, id);
1372
+ return this;
1373
+ };
1374
+ this.getItem = (id) => {
1375
+ const node = __privateGet$9(this, _nodes).find((n) => n.kind === "item" && n.id === id);
1376
+ return (node == null ? void 0 : node.kind) === "item" ? node.item : void 0;
1377
+ };
1378
+ /// Return a builder that mutates the submenu's items in place.
1379
+ /// Multiple calls return distinct builder objects backed by the same
1380
+ /// underlying node, so changes are always visible.
1381
+ this.getSubmenu = (id) => {
1382
+ const node = __privateGet$9(this, _nodes).find((n) => n.kind === "submenu" && n.id === id);
1383
+ return (node == null ? void 0 : node.kind) === "submenu" ? createSubmenuBuilder(node.node) : void 0;
1384
+ };
1385
+ this.clear = () => {
1386
+ __privateSet$9(this, _nodes, []);
1387
+ return this;
1388
+ };
1389
+ this.build = () => {
1390
+ const main = [];
1391
+ const submenus = {};
1392
+ for (const node of __privateGet$9(this, _nodes)) {
1393
+ if (node.kind === "item") {
1394
+ main.push({ kind: "item", id: node.id, item: node.item });
1395
+ } else {
1396
+ main.push({ kind: "submenu", id: node.id, def: node.node.def });
1397
+ submenus[node.id] = {
1398
+ def: node.node.def,
1399
+ items: Array.from(node.node.items.entries()).map(([id, item]) => ({
1400
+ id,
1401
+ item
1402
+ }))
1403
+ };
1404
+ }
1405
+ }
1406
+ return { main, submenus };
1407
+ };
1408
+ __privateAdd$9(this, _removeById, (id) => {
1409
+ __privateSet$9(this, _nodes, __privateGet$9(this, _nodes).filter((n) => n.id !== id));
1410
+ });
1411
+ }
1412
+ }
1413
+ _nodes = new WeakMap();
1414
+ _removeById = new WeakMap();
1415
+ function applyDefaultSuggestions(builder) {
1416
+ builder.addItem("improve", {
1417
+ icon: aiIcon,
1418
+ label: "Improve writing",
1419
+ streamingLabel: "Improving writing",
1420
+ prompt: "Improve the writing while preserving the original meaning."
1421
+ }).addItem("grammar", {
1422
+ icon: grammarCheckIcon,
1423
+ label: "Fix grammar & spelling",
1424
+ streamingLabel: "Fixing grammar & spelling",
1425
+ prompt: "Fix any grammar and spelling errors without changing the meaning."
1426
+ }).addItem("shorter", {
1427
+ icon: shorterIcon,
1428
+ label: "Make shorter",
1429
+ streamingLabel: "Making shorter",
1430
+ prompt: "Make this shorter while preserving the key information."
1431
+ }).addItem("longer", {
1432
+ icon: longerIcon,
1433
+ label: "Make longer",
1434
+ streamingLabel: "Expanding",
1435
+ prompt: "Expand this with more detail and examples."
1436
+ });
1437
+ builder.addSubmenu(
1438
+ "tone",
1439
+ {
1440
+ icon: editIcon,
1441
+ label: "Change tone\u2026",
1442
+ title: "Change tone",
1443
+ searchPlaceholder: "Search tones\u2026"
1444
+ },
1445
+ (sub) => {
1446
+ const tones = [
1447
+ ["professional", "Professional"],
1448
+ ["casual", "Casual"],
1449
+ ["confident", "Confident"],
1450
+ ["friendly", "Friendly"],
1451
+ ["direct", "Direct"],
1452
+ ["formal", "Formal"]
1453
+ ];
1454
+ for (const [id, label] of tones) {
1455
+ sub.addItem(id, {
1456
+ icon: editIcon,
1457
+ label,
1458
+ streamingLabel: "Adjusting tone",
1459
+ prompt: `Rewrite this in a ${label.toLowerCase()} tone.`
1460
+ });
1461
+ }
1462
+ }
1463
+ );
1464
+ builder.addSubmenu(
1465
+ "translate",
1466
+ {
1467
+ icon: translateIcon,
1468
+ label: "Translate\u2026",
1469
+ title: "Translate",
1470
+ searchPlaceholder: "Search languages\u2026"
1471
+ },
1472
+ (sub) => {
1473
+ const languages = [
1474
+ ["english", "English", "English"],
1475
+ ["chinese", "Chinese", "Chinese (Simplified)"],
1476
+ ["japanese", "Japanese", "Japanese"],
1477
+ ["korean", "Korean", "Korean"],
1478
+ ["spanish", "Spanish", "Spanish"],
1479
+ ["french", "French", "French"],
1480
+ ["german", "German", "German"]
1481
+ ];
1482
+ for (const [id, label, promptName] of languages) {
1483
+ sub.addItem(id, {
1484
+ icon: translateIcon,
1485
+ label,
1486
+ streamingLabel: `Translating to ${label}`,
1487
+ prompt: `Translate this to ${promptName}.`
1488
+ });
1489
+ }
1490
+ }
1491
+ );
1492
+ }
1493
+
1494
+ function keepAlive(..._args) {
1495
+ }
1496
+
1497
+ keepAlive(vue.h);
1498
+ function renderHighlighted(label, query) {
1499
+ const q = query.trim();
1500
+ if (!q) return [label];
1501
+ const lower = label.toLowerCase();
1502
+ const lq = q.toLowerCase();
1503
+ const idx = lower.indexOf(lq);
1504
+ if (idx === -1) return [label];
1505
+ return [
1506
+ label.slice(0, idx),
1507
+ vue.h("mark", null, label.slice(idx, idx + q.length)),
1508
+ label.slice(idx + q.length)
1509
+ ];
1510
+ }
1511
+ const AIInstructionInput = vue.defineComponent({
1512
+ props: {
1513
+ placeholder: { type: Object, required: true },
1514
+ resetSignal: { type: Object, required: true },
1515
+ suggestions: { type: Object, required: true },
1516
+ chrome: { type: Object, required: true },
1517
+ onConfirm: { type: Function, required: true },
1518
+ onCancel: { type: Function, required: true }
1519
+ },
1520
+ setup({
1521
+ placeholder,
1522
+ resetSignal,
1523
+ suggestions,
1524
+ chrome,
1525
+ onConfirm,
1526
+ onCancel
1527
+ }) {
1528
+ const inputValue = vue.ref("");
1529
+ const view = vue.ref({ kind: "main" });
1530
+ const selectedIndex = vue.ref(0);
1531
+ const inputRef = vue.ref(null);
1532
+ const listRef = vue.ref(null);
1533
+ const listboxId = `ai-instruction-list-${Math.random().toString(36).slice(2, 9)}`;
1534
+ const optionId = (idx) => `${listboxId}-opt-${idx}`;
1535
+ vue.watch(resetSignal, () => {
1536
+ inputValue.value = "";
1537
+ view.value = { kind: "main" };
1538
+ selectedIndex.value = 0;
1539
+ });
1540
+ const allItems = vue.computed(() => {
1541
+ if (view.value.kind === "submenu") {
1542
+ const submenu = suggestions.submenus[view.value.id];
1543
+ if (!submenu) return [];
1544
+ return submenu.items.map(({ id, item }) => ({
1545
+ id,
1546
+ icon: item.icon,
1547
+ label: item.label,
1548
+ hasSubmenu: false,
1549
+ prompt: { text: item.prompt, streamingLabel: item.streamingLabel }
1550
+ }));
1551
+ }
1552
+ return suggestions.main.map((entry) => {
1553
+ if (entry.kind === "item") {
1554
+ return {
1555
+ id: entry.id,
1556
+ icon: entry.item.icon,
1557
+ label: entry.item.label,
1558
+ hasSubmenu: false,
1559
+ prompt: {
1560
+ text: entry.item.prompt,
1561
+ streamingLabel: entry.item.streamingLabel
1562
+ }
1563
+ };
1564
+ }
1565
+ return {
1566
+ id: entry.id,
1567
+ icon: entry.def.icon,
1568
+ label: entry.def.label,
1569
+ hasSubmenu: true
1570
+ };
1571
+ });
1572
+ });
1573
+ const currentSubmenuDef = vue.computed(() => {
1574
+ var _a, _b;
1575
+ if (view.value.kind !== "submenu") return null;
1576
+ return (_b = (_a = suggestions.submenus[view.value.id]) == null ? void 0 : _a.def) != null ? _b : null;
1577
+ });
1578
+ const filteredItems = vue.computed(() => {
1579
+ const q = inputValue.value.trim().toLowerCase();
1580
+ if (!q) return allItems.value;
1581
+ return allItems.value.filter(
1582
+ (item) => item.label.toLowerCase().includes(q)
1583
+ );
1584
+ });
1585
+ const showSendAsPrompt = vue.computed(() => inputValue.value.trim().length > 0);
1586
+ const totalItems = vue.computed(
1587
+ () => filteredItems.value.length + (showSendAsPrompt.value ? 1 : 0)
1588
+ );
1589
+ vue.watch([filteredItems, view, showSendAsPrompt], () => {
1590
+ selectedIndex.value = showSendAsPrompt.value ? filteredItems.value.length : 0;
1591
+ });
1592
+ const focusInput = () => {
1593
+ void vue.nextTick(() => {
1594
+ var _a;
1595
+ return (_a = inputRef.value) == null ? void 0 : _a.focus();
1596
+ });
1597
+ };
1598
+ const enterSubmenu = (id) => {
1599
+ view.value = { kind: "submenu", id };
1600
+ inputValue.value = "";
1601
+ selectedIndex.value = 0;
1602
+ focusInput();
1603
+ };
1604
+ const exitSubmenu = () => {
1605
+ view.value = { kind: "main" };
1606
+ inputValue.value = "";
1607
+ selectedIndex.value = 0;
1608
+ focusInput();
1609
+ };
1610
+ const runItem = (item) => {
1611
+ if (item.hasSubmenu) {
1612
+ enterSubmenu(item.id);
1613
+ } else if (item.prompt) {
1614
+ onConfirm(item.prompt.text, item.prompt.streamingLabel);
1615
+ inputValue.value = "";
1616
+ }
1617
+ };
1618
+ const submitRaw = () => {
1619
+ const v = inputValue.value.trim();
1620
+ if (!v) return;
1621
+ onConfirm(v);
1622
+ inputValue.value = "";
1623
+ };
1624
+ const onSelectCurrent = () => {
1625
+ const idx = selectedIndex.value;
1626
+ const items = filteredItems.value;
1627
+ if (idx < items.length) {
1628
+ runItem(items[idx]);
1629
+ } else if (showSendAsPrompt.value) {
1630
+ submitRaw();
1631
+ }
1632
+ };
1633
+ const scrollToSelected = () => {
1634
+ void vue.nextTick(() => {
1635
+ const list = listRef.value;
1636
+ if (!list) return;
1637
+ const el = list.querySelector(
1638
+ `[data-index="${selectedIndex.value}"]`
1639
+ );
1640
+ el == null ? void 0 : el.scrollIntoView({ block: "nearest" });
1641
+ });
1642
+ };
1643
+ const onKeydown = (e) => {
1644
+ e.stopPropagation();
1645
+ if (e.key === "ArrowDown") {
1646
+ e.preventDefault();
1647
+ if (totalItems.value === 0) return;
1648
+ selectedIndex.value = (selectedIndex.value + 1) % totalItems.value;
1649
+ scrollToSelected();
1650
+ return;
1651
+ }
1652
+ if (e.key === "ArrowUp") {
1653
+ e.preventDefault();
1654
+ if (totalItems.value === 0) return;
1655
+ selectedIndex.value = (selectedIndex.value - 1 + totalItems.value) % totalItems.value;
1656
+ scrollToSelected();
1657
+ return;
1658
+ }
1659
+ if (e.key === "Enter") {
1660
+ e.preventDefault();
1661
+ onSelectCurrent();
1662
+ return;
1663
+ }
1664
+ if (e.key === "Escape") {
1665
+ e.preventDefault();
1666
+ if (view.value.kind === "submenu") exitSubmenu();
1667
+ else onCancel();
1668
+ return;
1669
+ }
1670
+ if (e.key === "Backspace" && inputValue.value === "" && view.value.kind === "submenu") {
1671
+ e.preventDefault();
1672
+ exitSubmenu();
1673
+ }
1674
+ };
1675
+ const onItemPointerDown = (e) => {
1676
+ e.preventDefault();
1677
+ };
1678
+ return () => {
1679
+ const items = filteredItems.value;
1680
+ const showPrompt = showSendAsPrompt.value;
1681
+ const submenuDef = currentSubmenuDef.value;
1682
+ return /* @__PURE__ */ vue.h("div", { class: "ai-instruction" }, /* @__PURE__ */ vue.h("div", { class: "ai-instruction-input" }, /* @__PURE__ */ vue.h("span", { class: "ai-instruction-input-prefix" }, /* @__PURE__ */ vue.h(component.Icon, { icon: chrome.aiIcon })), /* @__PURE__ */ vue.h(
1683
+ "input",
1684
+ {
1685
+ ref: inputRef,
1686
+ class: "ai-instruction-input-field",
1687
+ role: "combobox",
1688
+ "aria-expanded": "true",
1689
+ "aria-autocomplete": "list",
1690
+ "aria-controls": listboxId,
1691
+ "aria-activedescendant": totalItems.value > 0 ? optionId(selectedIndex.value) : void 0,
1692
+ placeholder: submenuDef ? submenuDef.searchPlaceholder : placeholder.value,
1693
+ value: inputValue.value,
1694
+ onInput: (e) => {
1695
+ inputValue.value = e.target.value;
1696
+ },
1697
+ onKeydown
1698
+ }
1699
+ ), /* @__PURE__ */ vue.h(
1700
+ "button",
1701
+ {
1702
+ type: "button",
1703
+ class: "ai-instruction-submit",
1704
+ "aria-label": chrome.submitButtonLabel,
1705
+ disabled: !showPrompt,
1706
+ onMousedown: onItemPointerDown,
1707
+ onClick: submitRaw
1708
+ },
1709
+ /* @__PURE__ */ vue.h(component.Icon, { icon: chrome.sendIcon })
1710
+ )), /* @__PURE__ */ vue.h(
1711
+ "div",
1712
+ {
1713
+ class: "ai-instruction-list",
1714
+ ref: listRef,
1715
+ id: listboxId,
1716
+ role: "listbox",
1717
+ "aria-label": chrome.listboxLabel
1718
+ },
1719
+ submenuDef && /* @__PURE__ */ vue.h(
1720
+ "button",
1721
+ {
1722
+ type: "button",
1723
+ class: "ai-instruction-back",
1724
+ onMousedown: onItemPointerDown,
1725
+ onClick: exitSubmenu
1726
+ },
1727
+ /* @__PURE__ */ vue.h("span", { class: "ai-instruction-back-icon", "aria-hidden": "true" }, /* @__PURE__ */ vue.h(component.Icon, { icon: chrome.chevronLeftIcon })),
1728
+ /* @__PURE__ */ vue.h("span", null, submenuDef.title)
1729
+ ),
1730
+ items.length > 0 && /* @__PURE__ */ vue.h("div", { class: "ai-instruction-section" }, /* @__PURE__ */ vue.h("div", { class: "ai-instruction-section-header" }, chrome.suggestionsHeaderLabel), items.map((item, idx) => /* @__PURE__ */ vue.h(
1731
+ "div",
1732
+ {
1733
+ key: item.id,
1734
+ id: optionId(idx),
1735
+ "data-index": idx,
1736
+ role: "option",
1737
+ "aria-selected": idx === selectedIndex.value,
1738
+ class: [
1739
+ "ai-instruction-item",
1740
+ idx === selectedIndex.value ? "active" : ""
1741
+ ],
1742
+ onMousedown: onItemPointerDown,
1743
+ onClick: () => runItem(item),
1744
+ onPointerenter: () => {
1745
+ selectedIndex.value = idx;
1746
+ }
1747
+ },
1748
+ /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-icon" }, /* @__PURE__ */ vue.h(component.Icon, { icon: item.icon })),
1749
+ /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-label" }, renderHighlighted(item.label, inputValue.value)),
1750
+ item.hasSubmenu && /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-arrow" }, /* @__PURE__ */ vue.h(component.Icon, { icon: chrome.chevronRightIcon }))
1751
+ ))),
1752
+ showPrompt && /* @__PURE__ */ vue.h("div", { class: "ai-instruction-section" }, /* @__PURE__ */ vue.h("div", { class: "ai-instruction-section-header" }, chrome.sendAsPromptHeaderLabel), /* @__PURE__ */ vue.h(
1753
+ "div",
1754
+ {
1755
+ id: optionId(items.length),
1756
+ "data-index": items.length,
1757
+ role: "option",
1758
+ "aria-selected": selectedIndex.value === items.length,
1759
+ class: [
1760
+ "ai-instruction-item",
1761
+ "ai-instruction-item-prompt",
1762
+ selectedIndex.value === items.length ? "active" : ""
1763
+ ],
1764
+ onMousedown: onItemPointerDown,
1765
+ onClick: submitRaw,
1766
+ onPointerenter: () => {
1767
+ selectedIndex.value = items.length;
1768
+ }
1769
+ },
1770
+ /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-icon" }, /* @__PURE__ */ vue.h(component.Icon, { icon: chrome.sendPromptIcon })),
1771
+ /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-label" }, chrome.sendAsPromptLabel, " ", /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-quote" }, '"', inputValue.value, '"')),
1772
+ /* @__PURE__ */ vue.h("span", { class: "ai-instruction-item-shortcut" }, /* @__PURE__ */ vue.h(component.Icon, { icon: chrome.enterKeyIcon }))
1773
+ ))
1774
+ ));
1775
+ };
1776
+ }
1777
+ });
1778
+
1779
+ var __typeError$8 = (msg) => {
1780
+ throw TypeError(msg);
1781
+ };
1782
+ var __accessCheck$8 = (obj, member, msg) => member.has(obj) || __typeError$8("Cannot " + msg);
1783
+ var __privateGet$8 = (obj, member, getter) => (__accessCheck$8(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
1784
+ var __privateAdd$8 = (obj, member, value) => member.has(obj) ? __typeError$8("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1785
+ var __privateSet$8 = (obj, member, value, setter) => (__accessCheck$8(obj, member, "write to private field"), member.set(obj, value), value);
1786
+ var _content$4, _provider$2, _app$5, _placeholder, _resetSignal, _from, _to, _wantsShow, _onConfirm, _onCancel;
1787
+ class AIInstructionTooltipView {
1788
+ constructor(ctx, view, config) {
1789
+ this.ctx = ctx;
1790
+ __privateAdd$8(this, _content$4);
1791
+ __privateAdd$8(this, _provider$2);
1792
+ __privateAdd$8(this, _app$5);
1793
+ __privateAdd$8(this, _placeholder);
1794
+ __privateAdd$8(this, _resetSignal);
1795
+ __privateAdd$8(this, _from, -1);
1796
+ __privateAdd$8(this, _to, -1);
1797
+ /// Source of truth for "should the palette currently be showing".
1798
+ /// `TooltipProvider.shouldShow` reads this so that forwarding `update()`
1799
+ /// to the provider on every editor transition (needed to keep the
1800
+ /// floating position in sync with layout changes) doesn't dismiss the
1801
+ /// palette behind our back.
1802
+ __privateAdd$8(this, _wantsShow, false);
1803
+ __privateAdd$8(this, _onConfirm, (instruction, label) => {
1804
+ if (!instruction.trim()) return;
1805
+ const commands = this.ctx.get(core.commandsCtx);
1806
+ const accepted = commands.call(runAICmd.key, { instruction, label });
1807
+ if (!accepted) return;
1808
+ __privateSet$8(this, _wantsShow, false);
1809
+ __privateGet$8(this, _provider$2).hide();
1810
+ });
1811
+ __privateAdd$8(this, _onCancel, () => {
1812
+ __privateSet$8(this, _wantsShow, false);
1813
+ __privateGet$8(this, _provider$2).hide();
1814
+ });
1815
+ this.show = (from, to) => {
1816
+ __privateSet$8(this, _from, from);
1817
+ __privateSet$8(this, _to, to);
1818
+ __privateGet$8(this, _resetSignal).value++;
1819
+ __privateSet$8(this, _wantsShow, true);
1820
+ const view = this.ctx.get(core.editorViewCtx);
1821
+ __privateGet$8(this, _provider$2).show(
1822
+ { getBoundingClientRect: () => prose.posToDOMRect(view, from, to) },
1823
+ view
1824
+ );
1825
+ requestAnimationFrame(() => {
1826
+ var _a;
1827
+ (_a = __privateGet$8(this, _content$4).querySelector("input")) == null ? void 0 : _a.focus();
1828
+ });
1829
+ };
1830
+ this.update = (view, prevState) => {
1831
+ const { selection } = view.state;
1832
+ const isTextSelection = selection instanceof state.TextSelection;
1833
+ const movedRange = selection.from !== __privateGet$8(this, _from) || selection.to !== __privateGet$8(this, _to);
1834
+ if (!isTextSelection || movedRange) {
1835
+ __privateSet$8(this, _wantsShow, false);
1836
+ __privateGet$8(this, _provider$2).hide();
1837
+ return;
1838
+ }
1839
+ __privateGet$8(this, _provider$2).update(view, prevState);
1840
+ };
1841
+ this.destroy = () => {
1842
+ __privateGet$8(this, _app$5).unmount();
1843
+ __privateGet$8(this, _provider$2).destroy();
1844
+ __privateGet$8(this, _content$4).remove();
1845
+ };
1846
+ __privateSet$8(this, _placeholder, vue.ref(config.placeholder));
1847
+ __privateSet$8(this, _resetSignal, vue.ref(0));
1848
+ const content = document.createElement("div");
1849
+ content.className = "milkdown-ai-instruction";
1850
+ const app = vue.createApp(AIInstructionInput, {
1851
+ placeholder: __privateGet$8(this, _placeholder),
1852
+ resetSignal: __privateGet$8(this, _resetSignal),
1853
+ suggestions: config.suggestions,
1854
+ chrome: config.chrome,
1855
+ onConfirm: __privateGet$8(this, _onConfirm),
1856
+ onCancel: __privateGet$8(this, _onCancel)
1857
+ });
1858
+ app.mount(content);
1859
+ __privateSet$8(this, _app$5, app);
1860
+ __privateSet$8(this, _content$4, content);
1861
+ __privateSet$8(this, _provider$2, new tooltip.TooltipProvider({
1862
+ content,
1863
+ debounce: 0,
1864
+ offset: 10,
1865
+ shouldShow: () => __privateGet$8(this, _wantsShow),
1866
+ floatingUIOptions: {
1867
+ placement: "bottom"
1868
+ }
1869
+ }));
1870
+ __privateGet$8(this, _provider$2).onHide = () => {
1871
+ __privateSet$8(this, _wantsShow, false);
1872
+ requestAnimationFrame(() => {
1873
+ try {
1874
+ const root = __privateGet$8(this, _content$4).getRootNode();
1875
+ const active = root.activeElement;
1876
+ if (!active || !__privateGet$8(this, _content$4).contains(active)) return;
1877
+ const v = this.ctx.get(core.editorViewCtx);
1878
+ v.dom.focus({ preventScroll: true });
1879
+ } catch (e) {
1880
+ }
1881
+ });
1882
+ };
1883
+ __privateGet$8(this, _provider$2).update(view);
1884
+ }
1885
+ }
1886
+ _content$4 = new WeakMap();
1887
+ _provider$2 = new WeakMap();
1888
+ _app$5 = new WeakMap();
1889
+ _placeholder = new WeakMap();
1890
+ _resetSignal = new WeakMap();
1891
+ _from = new WeakMap();
1892
+ _to = new WeakMap();
1893
+ _wantsShow = new WeakMap();
1894
+ _onConfirm = new WeakMap();
1895
+ _onCancel = new WeakMap();
1896
+
1897
+ const defaultAPI = {
1898
+ show: () => {
1899
+ }
1900
+ };
1901
+ const aiInstructionTooltipAPI = utils.$ctx(
1902
+ { ...defaultAPI },
1903
+ "aiInstructionTooltipAPI"
1904
+ );
1905
+ const aiInstructionTooltip = tooltip.tooltipFactory("CREPE_AI_INSTRUCTION");
1906
+ const DEFAULT_SUGGESTIONS_HEADER_LABEL = "SUGGESTIONS";
1907
+ const DEFAULT_SEND_AS_PROMPT_HEADER_LABEL = "SEND AS PROMPT";
1908
+ const DEFAULT_SEND_AS_PROMPT_LABEL = "Ask AI:";
1909
+ const DEFAULT_SUBMIT_BUTTON_LABEL = "Send prompt";
1910
+ const DEFAULT_LISTBOX_LABEL = "AI suggestions";
1911
+ const DEFAULT_INSTRUCTION_PLACEHOLDER = "Tell AI what to do with the selection\u2026";
1912
+ function resolveChrome(config) {
1913
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
1914
+ return {
1915
+ aiIcon: (_a = config == null ? void 0 : config.aiIcon) != null ? _a : aiIcon,
1916
+ sendIcon: (_b = config == null ? void 0 : config.sendIcon) != null ? _b : sendIcon,
1917
+ sendPromptIcon: (_c = config == null ? void 0 : config.sendPromptIcon) != null ? _c : sendPromptIcon,
1918
+ enterKeyIcon: (_d = config == null ? void 0 : config.enterKeyIcon) != null ? _d : enterKeyIcon,
1919
+ chevronLeftIcon: (_e = config == null ? void 0 : config.chevronLeftIcon) != null ? _e : chevronLeftIcon,
1920
+ chevronRightIcon: (_f = config == null ? void 0 : config.chevronRightIcon) != null ? _f : chevronRightIcon,
1921
+ suggestionsHeaderLabel: (_g = config == null ? void 0 : config.suggestionsHeaderLabel) != null ? _g : DEFAULT_SUGGESTIONS_HEADER_LABEL,
1922
+ sendAsPromptHeaderLabel: (_h = config == null ? void 0 : config.sendAsPromptHeaderLabel) != null ? _h : DEFAULT_SEND_AS_PROMPT_HEADER_LABEL,
1923
+ sendAsPromptLabel: (_i = config == null ? void 0 : config.sendAsPromptLabel) != null ? _i : DEFAULT_SEND_AS_PROMPT_LABEL,
1924
+ submitButtonLabel: (_j = config == null ? void 0 : config.submitButtonLabel) != null ? _j : DEFAULT_SUBMIT_BUTTON_LABEL,
1925
+ listboxLabel: (_k = config == null ? void 0 : config.listboxLabel) != null ? _k : DEFAULT_LISTBOX_LABEL
1926
+ };
1927
+ }
1928
+ function resolveViewConfig(config) {
1929
+ var _a, _b;
1930
+ const builder = new AISuggestionsBuilder();
1931
+ applyDefaultSuggestions(builder);
1932
+ (_a = config == null ? void 0 : config.buildAISuggestions) == null ? void 0 : _a.call(config, builder);
1933
+ return {
1934
+ placeholder: (_b = config == null ? void 0 : config.instructionPlaceholder) != null ? _b : DEFAULT_INSTRUCTION_PLACEHOLDER,
1935
+ chrome: resolveChrome(config),
1936
+ suggestions: builder.build()
1937
+ };
1938
+ }
1939
+ function configureAIInstructionTooltip(config) {
1940
+ return (ctx) => {
1941
+ const viewConfig = resolveViewConfig(config);
1942
+ let tooltipView = null;
1943
+ ctx.update(aiInstructionTooltipAPI.key, (api) => ({
1944
+ ...api,
1945
+ show: (from, to) => {
1946
+ tooltipView == null ? void 0 : tooltipView.show(from, to);
1947
+ }
1948
+ }));
1949
+ ctx.set(aiInstructionTooltip.key, {
1950
+ view: (view) => {
1951
+ tooltipView = new AIInstructionTooltipView(ctx, view, viewConfig);
1952
+ return tooltipView;
1953
+ }
1954
+ });
1955
+ };
1956
+ }
1957
+
1958
+ var __typeError$7 = (msg) => {
1959
+ throw TypeError(msg);
1960
+ };
1961
+ var __accessCheck$7 = (obj, member, msg) => member.has(obj) || __typeError$7("Cannot " + msg);
1962
+ var __privateGet$7 = (obj, member, getter) => (__accessCheck$7(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
1963
+ var __privateAdd$7 = (obj, member, value) => member.has(obj) ? __typeError$7("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
1964
+ var __privateSet$7 = (obj, member, value, setter) => (__accessCheck$7(obj, member, "write to private field"), member.set(obj, value), value);
1965
+ var _spinner, _label, _fallbackLabel, _start, _lastLabelText, _rafId, _tick;
1966
+ const CLASS_PREFIX = "milkdown-ai-streaming";
1967
+ const SPINNER_PERIOD_MS = 800;
1968
+ const DEFAULT_STREAMING_FALLBACK_LABEL = "Generating";
1969
+ const DEFAULT_STREAMING_CANCEL_HINT = "Esc to cancel";
1970
+ const indicatorKey = new state.PluginKey(
1971
+ "CREPE_AI_STREAMING_INDICATOR"
1972
+ );
1973
+ class IndicatorWidget {
1974
+ constructor(ctx, fallbackLabel, cancelHint) {
1975
+ __privateAdd$7(this, _spinner);
1976
+ __privateAdd$7(this, _label);
1977
+ __privateAdd$7(this, _fallbackLabel);
1978
+ __privateAdd$7(this, _start, performance.now());
1979
+ __privateAdd$7(this, _lastLabelText, "");
1980
+ __privateAdd$7(this, _rafId, 0);
1981
+ __privateAdd$7(this, _tick, () => {
1982
+ const elapsed = performance.now() - __privateGet$7(this, _start);
1983
+ const angle = elapsed / SPINNER_PERIOD_MS * 360;
1984
+ __privateGet$7(this, _spinner).style.transform = `rotate(${angle}deg)`;
1985
+ __privateSet$7(this, _rafId, requestAnimationFrame(__privateGet$7(this, _tick)));
1986
+ });
1987
+ __privateSet$7(this, _fallbackLabel, fallbackLabel);
1988
+ const dom = document.createElement("span");
1989
+ dom.className = `${CLASS_PREFIX}-indicator`;
1990
+ dom.contentEditable = "false";
1991
+ dom.setAttribute("role", "status");
1992
+ dom.setAttribute("aria-live", "polite");
1993
+ const spinner = document.createElement("span");
1994
+ spinner.className = `${CLASS_PREFIX}-spinner`;
1995
+ spinner.setAttribute("aria-hidden", "true");
1996
+ dom.appendChild(spinner);
1997
+ const label = document.createElement("span");
1998
+ label.className = `${CLASS_PREFIX}-label`;
1999
+ dom.appendChild(label);
2000
+ const escHint = document.createElement("span");
2001
+ escHint.className = `${CLASS_PREFIX}-esc`;
2002
+ escHint.textContent = cancelHint;
2003
+ dom.appendChild(escHint);
2004
+ this.dom = dom;
2005
+ __privateSet$7(this, _spinner, spinner);
2006
+ __privateSet$7(this, _label, label);
2007
+ this.setLabel(ctx);
2008
+ __privateGet$7(this, _tick).call(this);
2009
+ }
2010
+ setLabel(ctx) {
2011
+ const session = ctx.get(aiSessionCtx.key);
2012
+ const text = `${session.label || __privateGet$7(this, _fallbackLabel)}\u2026`;
2013
+ if (text === __privateGet$7(this, _lastLabelText)) return;
2014
+ __privateSet$7(this, _lastLabelText, text);
2015
+ __privateGet$7(this, _label).textContent = text;
2016
+ }
2017
+ destroy() {
2018
+ if (__privateGet$7(this, _rafId)) cancelAnimationFrame(__privateGet$7(this, _rafId));
2019
+ __privateSet$7(this, _rafId, 0);
2020
+ }
2021
+ }
2022
+ _spinner = new WeakMap();
2023
+ _label = new WeakMap();
2024
+ _fallbackLabel = new WeakMap();
2025
+ _start = new WeakMap();
2026
+ _lastLabelText = new WeakMap();
2027
+ _rafId = new WeakMap();
2028
+ _tick = new WeakMap();
2029
+ function streamingIndicatorPlugin(options = {}) {
2030
+ var _a, _b;
2031
+ const { config } = options;
2032
+ const fallbackLabel = (_a = config == null ? void 0 : config.fallbackLabel) != null ? _a : DEFAULT_STREAMING_FALLBACK_LABEL;
2033
+ const cancelHint = (_b = config == null ? void 0 : config.cancelHint) != null ? _b : DEFAULT_STREAMING_CANCEL_HINT;
2034
+ return utils.$prose((ctx) => {
2035
+ let widget = null;
2036
+ function ensureWidget() {
2037
+ if (!widget) widget = new IndicatorWidget(ctx, fallbackLabel, cancelHint);
2038
+ else widget.setLabel(ctx);
2039
+ return widget;
2040
+ }
2041
+ function dropWidget() {
2042
+ if (widget) {
2043
+ widget.destroy();
2044
+ widget = null;
2045
+ }
2046
+ }
2047
+ function buildSet(doc, insertEndPos) {
2048
+ const pos = Math.min(insertEndPos, doc.content.size);
2049
+ const decoration = view.Decoration.widget(pos, ensureWidget().dom, {
2050
+ side: 1,
2051
+ key: "ai-streaming-indicator"
2052
+ });
2053
+ return view.DecorationSet.create(doc, [decoration]);
2054
+ }
2055
+ return new state.Plugin({
2056
+ key: indicatorKey,
2057
+ state: {
2058
+ init: () => view.DecorationSet.empty,
2059
+ apply(tr, decorations, _oldState, newState) {
2060
+ const streaming$1 = streaming.streamingPluginKey.getState(newState);
2061
+ if (!(streaming$1 == null ? void 0 : streaming$1.active) || streaming$1.insertEndPos == null) {
2062
+ dropWidget();
2063
+ return view.DecorationSet.empty;
2064
+ }
2065
+ if (tr.getMeta(streaming.streamingPluginKey) || tr.docChanged) {
2066
+ return buildSet(newState.doc, streaming$1.insertEndPos);
2067
+ }
2068
+ return decorations.map(tr.mapping, tr.doc);
2069
+ }
2070
+ },
2071
+ view() {
2072
+ return {
2073
+ destroy() {
2074
+ dropWidget();
2075
+ }
2076
+ };
2077
+ },
2078
+ props: {
2079
+ decorations(state) {
2080
+ var _a2;
2081
+ return (_a2 = indicatorKey.getState(state)) != null ? _a2 : view.DecorationSet.empty;
2082
+ },
2083
+ handleKeyDown(view, event) {
2084
+ var _a2;
2085
+ if (event.key !== "Escape") return false;
2086
+ const commands = ctx.get(core.commandsCtx);
2087
+ if (ctx.get(aiSessionCtx.key).abortController) {
2088
+ event.preventDefault();
2089
+ commands.call(abortAICmd.key, { keep: true });
2090
+ return true;
2091
+ }
2092
+ if ((_a2 = streaming.streamingPluginKey.getState(view.state)) == null ? void 0 : _a2.active) {
2093
+ event.preventDefault();
2094
+ commands.call(streaming.abortStreamingCmd.key, { keep: true });
2095
+ return true;
2096
+ }
2097
+ return false;
2098
+ }
2099
+ }
2100
+ });
2101
+ });
2102
+ }
2103
+
2104
+ const CREPE_CUSTOM_BLOCK_TYPES = ["table", "image-block", "code_block"];
2105
+ const CREPE_IGNORE_ATTRS = { heading: ["id"] };
2106
+ const ai = (editor, config) => {
2107
+ var _a, _b;
2108
+ const diffCfg = (_a = config == null ? void 0 : config.diff) != null ? _a : {};
2109
+ const streamingCfg = (_b = config == null ? void 0 : config.streaming) != null ? _b : {};
2110
+ editor.config(crepeFeatureConfig(CrepeFeature.AI)).config((ctx) => {
2111
+ ctx.update(diff.diffConfig.key, (prev) => {
2112
+ var _a2;
2113
+ return {
2114
+ ...prev,
2115
+ ignoreAttrs: (_a2 = diffCfg.ignoreAttrs) != null ? _a2 : CREPE_IGNORE_ATTRS
2116
+ };
2117
+ });
2118
+ const { ignoreAttrs: _, ...componentConfig } = diffCfg;
2119
+ ctx.update(diff$1.diffComponentConfig.key, (prev) => {
2120
+ var _a2;
2121
+ return {
2122
+ ...prev,
2123
+ customBlockTypes: (_a2 = componentConfig.customBlockTypes) != null ? _a2 : CREPE_CUSTOM_BLOCK_TYPES,
2124
+ ...componentConfig
2125
+ };
2126
+ });
2127
+ }).use(diff.diff).use(diff$1.diffComponent).config((ctx) => {
2128
+ ctx.update(streaming.streamingConfig.key, (prev) => {
2129
+ var _a2;
2130
+ return {
2131
+ ...prev,
2132
+ ...streamingCfg,
2133
+ ignoreAttrs: (_a2 = streamingCfg.ignoreAttrs) != null ? _a2 : CREPE_IGNORE_ATTRS,
2134
+ // Wire diffReviewOnEnd into the streaming plugin so manual
2135
+ // endStreamingCmd calls (outside runAICmd) also respect it.
2136
+ // Only override if the user explicitly set it — otherwise
2137
+ // keep the streaming plugin's own default so tests and
2138
+ // manual-streaming use cases aren't surprised.
2139
+ ...(config == null ? void 0 : config.diffReviewOnEnd) !== void 0 ? { diffReviewOnEnd: config.diffReviewOnEnd } : {}
2140
+ };
2141
+ });
2142
+ }).use(streaming.streaming).config((ctx) => {
2143
+ ctx.update(aiProviderConfig.key, (prev) => {
2144
+ var _a2;
2145
+ return {
2146
+ ...prev,
2147
+ ...(config == null ? void 0 : config.provider) !== void 0 ? { provider: config.provider } : {},
2148
+ ...(config == null ? void 0 : config.buildContext) !== void 0 ? { buildContext: config.buildContext } : {},
2149
+ diffReviewOnEnd: (_a2 = config == null ? void 0 : config.diffReviewOnEnd) != null ? _a2 : prev.diffReviewOnEnd,
2150
+ ...(config == null ? void 0 : config.onError) !== void 0 ? { onError: config.onError } : {},
2151
+ ...(config == null ? void 0 : config.aiIcon) !== void 0 ? { aiIcon: config.aiIcon } : {}
2152
+ };
2153
+ });
2154
+ }).use(aiProviderConfig).use(aiSessionCtx).use(runAICmd).use(abortAICmd).config(configureAIInstructionTooltip(config)).use(aiInstructionTooltipAPI).use(aiInstructionTooltip).use(streamingIndicatorPlugin({ config: config == null ? void 0 : config.streamingIndicator })).use(
2155
+ diffActionsPanelPlugin({
2156
+ config: config == null ? void 0 : config.diffActions,
2157
+ enterKeyIcon: config == null ? void 0 : config.enterKeyIcon
2158
+ })
2159
+ );
2160
+ };
2161
+
779
2162
  function isInCodeBlock(selection) {
780
2163
  const type = selection.$from.parent.type;
781
2164
  return type.name === "code_block";
@@ -839,9 +2222,6 @@ class GroupBuilder {
839
2222
  _groups = new WeakMap();
840
2223
  _getGroupInstance = new WeakMap();
841
2224
 
842
- function keepAlive(..._args) {
843
- }
844
-
845
2225
  function getGroups$2(filter, config, ctx) {
846
2226
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P, _Q, _R, _S, _T, _U, _V, _W, _X, _Y, _Z, __, _$, _aa, _ba, _ca, _da, _ea, _fa, _ga, _ha, _ia, _ja, _ka, _la, _ma, _na, _oa, _pa, _qa, _ra, _sa, _ta, _ua, _va, _wa, _xa, _ya, _za, _Aa, _Ba, _Ca, _Da, _Ea, _Fa, _Ga, _Ha, _Ia, _Ja, _Ka, _La, _Ma, _Na, _Oa, _Pa, _Qa, _Ra, _Sa, _Ta, _Ua, _Va, _Wa, _Xa, _Ya, _Za, __a, _$a, _ab, _bb, _cb, _db, _eb, _fb, _gb, _hb, _ib, _jb, _kb;
847
2227
  const flags = ctx && useCrepeFeatures(ctx).get();
@@ -1570,7 +2950,7 @@ const codeMirror = (editor, config = {}) => {
1570
2950
  editor.config(crepeFeatureConfig(CrepeFeature.CodeMirror)).config((ctx) => {
1571
2951
  const { languages = [], theme } = config;
1572
2952
  const extensions = [
1573
- view.keymap.of(commands.defaultKeymap.concat(commands.indentWithTab)),
2953
+ view$1.keymap.of(commands.defaultKeymap.concat(commands.indentWithTab)),
1574
2954
  codemirror.basicSetup
1575
2955
  ];
1576
2956
  if (theme) {
@@ -1815,7 +3195,7 @@ var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$3("Ca
1815
3195
  var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), member.set(obj, value), value);
1816
3196
  var _content$1, _provider, _dom, _innerView, _updateValue, _app$2, _onHide, _shouldShow;
1817
3197
  class LatexInlineTooltip {
1818
- constructor(ctx, view, config) {
3198
+ constructor(ctx, view$1, config) {
1819
3199
  this.ctx = ctx;
1820
3200
  __privateAdd$3(this, _content$1);
1821
3201
  __privateAdd$3(this, _provider);
@@ -1830,9 +3210,10 @@ class LatexInlineTooltip {
1830
3210
  __privateGet$3(this, _innerView).value = null;
1831
3211
  }
1832
3212
  });
1833
- __privateAdd$3(this, _shouldShow, (view) => {
3213
+ __privateAdd$3(this, _shouldShow, (view$1) => {
1834
3214
  const shouldShow = () => {
1835
- const { selection, schema } = view.state;
3215
+ if (!view$1.editable) return false;
3216
+ const { selection, schema } = view$1.state;
1836
3217
  if (selection.empty) return false;
1837
3218
  if (!(selection instanceof state.NodeSelection)) return false;
1838
3219
  const node = selection.node;
@@ -1842,7 +3223,7 @@ class LatexInlineTooltip {
1842
3223
  null,
1843
3224
  schema.text(node.attrs.value)
1844
3225
  );
1845
- const innerView = new view$1.EditorView(__privateGet$3(this, _dom), {
3226
+ const innerView = new view.EditorView(__privateGet$3(this, _dom), {
1846
3227
  state: state.EditorState.create({
1847
3228
  doc: paragraph,
1848
3229
  schema: new model.Schema({
@@ -1878,11 +3259,11 @@ class LatexInlineTooltip {
1878
3259
  });
1879
3260
  __privateGet$3(this, _innerView).value = innerView;
1880
3261
  __privateGet$3(this, _updateValue).value = () => {
1881
- const { tr } = view.state;
3262
+ const { tr } = view$1.state;
1882
3263
  tr.setNodeAttribute(textFrom, "value", innerView.state.doc.textContent);
1883
- view.dispatch(tr);
3264
+ view$1.dispatch(tr);
1884
3265
  requestAnimationFrame(() => {
1885
- view.focus();
3266
+ view$1.focus();
1886
3267
  });
1887
3268
  };
1888
3269
  return true;
@@ -1917,7 +3298,7 @@ class LatexInlineTooltip {
1917
3298
  placement: "bottom"
1918
3299
  }
1919
3300
  }));
1920
- __privateGet$3(this, _provider).update(view);
3301
+ __privateGet$3(this, _provider).update(view$1);
1921
3302
  __privateSet$3(this, _dom, document.createElement("div"));
1922
3303
  }
1923
3304
  }
@@ -2055,7 +3436,7 @@ function createPlaceholderDecoration(state, placeholderText) {
2055
3436
  const inTable = prose.findParent((node2) => node2.type.name === "table")($pos);
2056
3437
  if (inTable) return null;
2057
3438
  const before = $pos.before();
2058
- return view$1.Decoration.node(before, before + node.nodeSize, {
3439
+ return view.Decoration.node(before, before + node.nodeSize, {
2059
3440
  class: "crepe-placeholder",
2060
3441
  "data-placeholder": placeholderText
2061
3442
  });
@@ -2082,7 +3463,7 @@ const placeholderPlugin = utils.$prose((ctx) => {
2082
3463
  const placeholderText = (_a = config.text) != null ? _a : "Please enter...";
2083
3464
  const deco = createPlaceholderDecoration(state, placeholderText);
2084
3465
  if (!deco) return null;
2085
- return view$1.DecorationSet.create(state.doc, [deco]);
3466
+ return view.DecorationSet.create(state.doc, [deco]);
2086
3467
  }
2087
3468
  }
2088
3469
  });
@@ -2132,7 +3513,7 @@ const table = (editor, config) => {
2132
3513
  };
2133
3514
 
2134
3515
  function getGroups$1(config, ctx) {
2135
- var _a, _b, _c, _d, _e, _f, _g;
3516
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
2136
3517
  const groupBuilder = new GroupBuilder();
2137
3518
  groupBuilder.addGroup("formatting", "Formatting").addItem("bold", {
2138
3519
  icon: (_a = config == null ? void 0 : config.boldIcon) != null ? _a : boldIcon,
@@ -2215,7 +3596,22 @@ function getGroups$1(config, ctx) {
2215
3596
  commands.call(linkTooltip$1.toggleLinkCommand.key);
2216
3597
  }
2217
3598
  });
2218
- (_g = config == null ? void 0 : config.buildToolbar) == null ? void 0 : _g.call(config, groupBuilder);
3599
+ if (ctx && (flags == null ? void 0 : flags.includes(CrepeFeature.AI))) {
3600
+ const aiCfg = ctx.get(aiProviderConfig.key);
3601
+ if (aiCfg.provider) {
3602
+ functionGroup.addItem("ai", {
3603
+ icon: (_h = (_g = config == null ? void 0 : config.aiIcon) != null ? _g : aiCfg.aiIcon) != null ? _h : aiIcon,
3604
+ active: () => false,
3605
+ onRun: (ctx2) => {
3606
+ const api = ctx2.get(aiInstructionTooltipAPI.key);
3607
+ const view = ctx2.get(core.editorViewCtx);
3608
+ const { from, to } = view.state.selection;
3609
+ api.show(from, to);
3610
+ }
3611
+ });
3612
+ }
3613
+ }
3614
+ (_i = config == null ? void 0 : config.buildToolbar) == null ? void 0 : _i.call(config, groupBuilder);
2219
3615
  return groupBuilder.build();
2220
3616
  }
2221
3617
 
@@ -2838,6 +4234,9 @@ function loadFeature(feature, editor, config) {
2838
4234
  case CrepeFeature.TopBar: {
2839
4235
  return topBar(editor, config);
2840
4236
  }
4237
+ case CrepeFeature.AI: {
4238
+ return ai(editor, config);
4239
+ }
2841
4240
  }
2842
4241
  }
2843
4242