@cratis/components 4.22.1 → 4.23.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 (153) hide show
  1. package/compat-manifest.json +11 -11
  2. package/dist/esm/MarkdownEditor/CaretPosition.d.ts +12 -0
  3. package/dist/esm/MarkdownEditor/CaretPosition.d.ts.map +1 -0
  4. package/dist/esm/MarkdownEditor/CaretPosition.js +4 -0
  5. package/dist/esm/MarkdownEditor/CaretPosition.js.map +1 -0
  6. package/dist/esm/MarkdownEditor/MarkdownCompletion.d.ts +63 -0
  7. package/dist/esm/MarkdownEditor/MarkdownCompletion.d.ts.map +1 -0
  8. package/dist/esm/MarkdownEditor/MarkdownCompletion.js +4 -0
  9. package/dist/esm/MarkdownEditor/MarkdownCompletion.js.map +1 -0
  10. package/dist/esm/MarkdownEditor/MarkdownCompletionContext.d.ts +11 -0
  11. package/dist/esm/MarkdownEditor/MarkdownCompletionContext.d.ts.map +1 -0
  12. package/dist/esm/MarkdownEditor/MarkdownCompletionContext.js +4 -0
  13. package/dist/esm/MarkdownEditor/MarkdownCompletionContext.js.map +1 -0
  14. package/dist/esm/MarkdownEditor/MarkdownCompletionMatch.d.ts +17 -0
  15. package/dist/esm/MarkdownEditor/MarkdownCompletionMatch.d.ts.map +1 -0
  16. package/dist/esm/MarkdownEditor/MarkdownCompletionMatch.js +4 -0
  17. package/dist/esm/MarkdownEditor/MarkdownCompletionMatch.js.map +1 -0
  18. package/dist/esm/MarkdownEditor/MarkdownEditor.d.ts +86 -0
  19. package/dist/esm/MarkdownEditor/MarkdownEditor.d.ts.map +1 -0
  20. package/dist/esm/MarkdownEditor/MarkdownEditor.js +100 -0
  21. package/dist/esm/MarkdownEditor/MarkdownEditor.js.map +1 -0
  22. package/dist/esm/MarkdownEditor/MarkdownEditorLabels.d.ts +47 -0
  23. package/dist/esm/MarkdownEditor/MarkdownEditorLabels.d.ts.map +1 -0
  24. package/dist/esm/MarkdownEditor/MarkdownEditorLabels.js +4 -0
  25. package/dist/esm/MarkdownEditor/MarkdownEditorLabels.js.map +1 -0
  26. package/dist/esm/MarkdownEditor/MarkdownEditorMode.d.ts +11 -0
  27. package/dist/esm/MarkdownEditor/MarkdownEditorMode.d.ts.map +1 -0
  28. package/dist/esm/MarkdownEditor/MarkdownEditorMode.js +8 -0
  29. package/dist/esm/MarkdownEditor/MarkdownEditorMode.js.map +1 -0
  30. package/dist/esm/MarkdownEditor/MarkdownEditorParts.d.ts +33 -0
  31. package/dist/esm/MarkdownEditor/MarkdownEditorParts.d.ts.map +1 -0
  32. package/dist/esm/MarkdownEditor/MarkdownEditorParts.js +4 -0
  33. package/dist/esm/MarkdownEditor/MarkdownEditorParts.js.map +1 -0
  34. package/dist/esm/MarkdownEditor/MarkdownFormat.d.ts +29 -0
  35. package/dist/esm/MarkdownEditor/MarkdownFormat.d.ts.map +1 -0
  36. package/dist/esm/MarkdownEditor/MarkdownFormat.js +17 -0
  37. package/dist/esm/MarkdownEditor/MarkdownFormat.js.map +1 -0
  38. package/dist/esm/MarkdownEditor/MarkdownLineBlock.d.ts +12 -0
  39. package/dist/esm/MarkdownEditor/MarkdownLineBlock.d.ts.map +1 -0
  40. package/dist/esm/MarkdownEditor/MarkdownLineBlock.js +4 -0
  41. package/dist/esm/MarkdownEditor/MarkdownLineBlock.js.map +1 -0
  42. package/dist/esm/MarkdownEditor/MarkdownModeToggle.d.ts +22 -0
  43. package/dist/esm/MarkdownEditor/MarkdownModeToggle.d.ts.map +1 -0
  44. package/dist/esm/MarkdownEditor/MarkdownModeToggle.js +14 -0
  45. package/dist/esm/MarkdownEditor/MarkdownModeToggle.js.map +1 -0
  46. package/dist/esm/MarkdownEditor/MarkdownSuggestion.d.ts +18 -0
  47. package/dist/esm/MarkdownEditor/MarkdownSuggestion.d.ts.map +1 -0
  48. package/dist/esm/MarkdownEditor/MarkdownSuggestion.js +4 -0
  49. package/dist/esm/MarkdownEditor/MarkdownSuggestion.js.map +1 -0
  50. package/dist/esm/MarkdownEditor/MarkdownSuggestionList.d.ts +41 -0
  51. package/dist/esm/MarkdownEditor/MarkdownSuggestionList.d.ts.map +1 -0
  52. package/dist/esm/MarkdownEditor/MarkdownSuggestionList.js +19 -0
  53. package/dist/esm/MarkdownEditor/MarkdownSuggestionList.js.map +1 -0
  54. package/dist/esm/MarkdownEditor/MarkdownTextEdit.d.ts +12 -0
  55. package/dist/esm/MarkdownEditor/MarkdownTextEdit.d.ts.map +1 -0
  56. package/dist/esm/MarkdownEditor/MarkdownTextEdit.js +4 -0
  57. package/dist/esm/MarkdownEditor/MarkdownTextEdit.js.map +1 -0
  58. package/dist/esm/MarkdownEditor/MarkdownToolbar.d.ts +23 -0
  59. package/dist/esm/MarkdownEditor/MarkdownToolbar.d.ts.map +1 -0
  60. package/dist/esm/MarkdownEditor/MarkdownToolbar.js +43 -0
  61. package/dist/esm/MarkdownEditor/MarkdownToolbar.js.map +1 -0
  62. package/dist/esm/MarkdownEditor/MarkdownWritingArea.d.ts +37 -0
  63. package/dist/esm/MarkdownEditor/MarkdownWritingArea.d.ts.map +1 -0
  64. package/dist/esm/MarkdownEditor/MarkdownWritingArea.js +65 -0
  65. package/dist/esm/MarkdownEditor/MarkdownWritingArea.js.map +1 -0
  66. package/dist/esm/MarkdownEditor/applyMarkdownFormat.d.ts +13 -0
  67. package/dist/esm/MarkdownEditor/applyMarkdownFormat.d.ts.map +1 -0
  68. package/dist/esm/MarkdownEditor/applyMarkdownFormat.js +42 -0
  69. package/dist/esm/MarkdownEditor/applyMarkdownFormat.js.map +1 -0
  70. package/dist/esm/MarkdownEditor/caretPositionIn.d.ts +13 -0
  71. package/dist/esm/MarkdownEditor/caretPositionIn.d.ts.map +1 -0
  72. package/dist/esm/MarkdownEditor/caretPositionIn.js +54 -0
  73. package/dist/esm/MarkdownEditor/caretPositionIn.js.map +1 -0
  74. package/dist/esm/MarkdownEditor/defaultMarkdownEditorLabels.d.ts +4 -0
  75. package/dist/esm/MarkdownEditor/defaultMarkdownEditorLabels.d.ts.map +1 -0
  76. package/dist/esm/MarkdownEditor/defaultMarkdownEditorLabels.js +23 -0
  77. package/dist/esm/MarkdownEditor/defaultMarkdownEditorLabels.js.map +1 -0
  78. package/dist/esm/MarkdownEditor/defaultMarkdownFormats.d.ts +7 -0
  79. package/dist/esm/MarkdownEditor/defaultMarkdownFormats.d.ts.map +1 -0
  80. package/dist/esm/MarkdownEditor/defaultMarkdownFormats.js +10 -0
  81. package/dist/esm/MarkdownEditor/defaultMarkdownFormats.js.map +1 -0
  82. package/dist/esm/MarkdownEditor/filesIn.d.ts +7 -0
  83. package/dist/esm/MarkdownEditor/filesIn.d.ts.map +1 -0
  84. package/dist/esm/MarkdownEditor/filesIn.js +7 -0
  85. package/dist/esm/MarkdownEditor/filesIn.js.map +1 -0
  86. package/dist/esm/MarkdownEditor/findCompletionMatch.d.ts +12 -0
  87. package/dist/esm/MarkdownEditor/findCompletionMatch.d.ts.map +1 -0
  88. package/dist/esm/MarkdownEditor/findCompletionMatch.js +42 -0
  89. package/dist/esm/MarkdownEditor/findCompletionMatch.js.map +1 -0
  90. package/dist/esm/MarkdownEditor/formatIcons.d.ts +5 -0
  91. package/dist/esm/MarkdownEditor/formatIcons.d.ts.map +1 -0
  92. package/dist/esm/MarkdownEditor/formatIcons.js +20 -0
  93. package/dist/esm/MarkdownEditor/formatIcons.js.map +1 -0
  94. package/dist/esm/MarkdownEditor/formatShortcuts.d.ts +4 -0
  95. package/dist/esm/MarkdownEditor/formatShortcuts.d.ts.map +1 -0
  96. package/dist/esm/MarkdownEditor/formatShortcuts.js +10 -0
  97. package/dist/esm/MarkdownEditor/formatShortcuts.js.map +1 -0
  98. package/dist/esm/MarkdownEditor/index.d.ts +14 -0
  99. package/dist/esm/MarkdownEditor/index.d.ts.map +1 -0
  100. package/dist/esm/MarkdownEditor/index.js +7 -0
  101. package/dist/esm/MarkdownEditor/index.js.map +1 -0
  102. package/dist/esm/MarkdownEditor/insertLink.d.ts +12 -0
  103. package/dist/esm/MarkdownEditor/insertLink.d.ts.map +1 -0
  104. package/dist/esm/MarkdownEditor/insertLink.js +26 -0
  105. package/dist/esm/MarkdownEditor/insertLink.js.map +1 -0
  106. package/dist/esm/MarkdownEditor/lineBlockAround.d.ts +11 -0
  107. package/dist/esm/MarkdownEditor/lineBlockAround.d.ts.map +1 -0
  108. package/dist/esm/MarkdownEditor/lineBlockAround.js +14 -0
  109. package/dist/esm/MarkdownEditor/lineBlockAround.js.map +1 -0
  110. package/dist/esm/MarkdownEditor/nextMarkdownEditorMode.d.ts +8 -0
  111. package/dist/esm/MarkdownEditor/nextMarkdownEditorMode.d.ts.map +1 -0
  112. package/dist/esm/MarkdownEditor/nextMarkdownEditorMode.js +6 -0
  113. package/dist/esm/MarkdownEditor/nextMarkdownEditorMode.js.map +1 -0
  114. package/dist/esm/MarkdownEditor/replacePlaceholder.d.ts +14 -0
  115. package/dist/esm/MarkdownEditor/replacePlaceholder.d.ts.map +1 -0
  116. package/dist/esm/MarkdownEditor/replacePlaceholder.js +4 -0
  117. package/dist/esm/MarkdownEditor/replacePlaceholder.js.map +1 -0
  118. package/dist/esm/MarkdownEditor/replaceRange.d.ts +12 -0
  119. package/dist/esm/MarkdownEditor/replaceRange.d.ts.map +1 -0
  120. package/dist/esm/MarkdownEditor/replaceRange.js +11 -0
  121. package/dist/esm/MarkdownEditor/replaceRange.js.map +1 -0
  122. package/dist/esm/MarkdownEditor/toggleCodeBlock.d.ts +11 -0
  123. package/dist/esm/MarkdownEditor/toggleCodeBlock.d.ts.map +1 -0
  124. package/dist/esm/MarkdownEditor/toggleCodeBlock.js +30 -0
  125. package/dist/esm/MarkdownEditor/toggleCodeBlock.js.map +1 -0
  126. package/dist/esm/MarkdownEditor/toggleInlineMarker.d.ts +13 -0
  127. package/dist/esm/MarkdownEditor/toggleInlineMarker.d.ts.map +1 -0
  128. package/dist/esm/MarkdownEditor/toggleInlineMarker.js +29 -0
  129. package/dist/esm/MarkdownEditor/toggleInlineMarker.js.map +1 -0
  130. package/dist/esm/MarkdownEditor/toggleLinePrefix.d.ts +14 -0
  131. package/dist/esm/MarkdownEditor/toggleLinePrefix.d.ts.map +1 -0
  132. package/dist/esm/MarkdownEditor/toggleLinePrefix.js +24 -0
  133. package/dist/esm/MarkdownEditor/toggleLinePrefix.js.map +1 -0
  134. package/dist/esm/MarkdownEditor/uploadPlaceholderFor.d.ts +11 -0
  135. package/dist/esm/MarkdownEditor/uploadPlaceholderFor.d.ts.map +1 -0
  136. package/dist/esm/MarkdownEditor/uploadPlaceholderFor.js +4 -0
  137. package/dist/esm/MarkdownEditor/uploadPlaceholderFor.js.map +1 -0
  138. package/dist/esm/MarkdownEditor/useMarkdownCompletion.d.ts +44 -0
  139. package/dist/esm/MarkdownEditor/useMarkdownCompletion.d.ts.map +1 -0
  140. package/dist/esm/MarkdownEditor/useMarkdownCompletion.js +92 -0
  141. package/dist/esm/MarkdownEditor/useMarkdownCompletion.js.map +1 -0
  142. package/dist/esm/MarkdownEditor/useMarkdownUploads.d.ts +38 -0
  143. package/dist/esm/MarkdownEditor/useMarkdownUploads.d.ts.map +1 -0
  144. package/dist/esm/MarkdownEditor/useMarkdownUploads.js +55 -0
  145. package/dist/esm/MarkdownEditor/useMarkdownUploads.js.map +1 -0
  146. package/dist/esm/styles.MarkdownEditor.css +229 -0
  147. package/dist/esm/styles.base.css +2 -0
  148. package/dist/esm/styles.css +228 -0
  149. package/dist/esm/types/parts.d.ts +12 -0
  150. package/dist/esm/types/parts.d.ts.map +1 -1
  151. package/dist/esm/types/parts.js +12 -0
  152. package/dist/esm/types/parts.js.map +1 -1
  153. package/package.json +7 -2
@@ -0,0 +1,11 @@
1
+ import type { MarkdownTextEdit } from './MarkdownTextEdit.js';
2
+ /**
3
+ * Fences the lines the selection touches in a code block, or takes the fences off when those lines
4
+ * already open and close one.
5
+ * @param value The markdown being edited.
6
+ * @param start Where the selection starts.
7
+ * @param end Where the selection ends.
8
+ * @returns The edited markdown and the selection after it.
9
+ */
10
+ export declare const toggleCodeBlock: (value: string, start: number, end: number) => MarkdownTextEdit;
11
+ //# sourceMappingURL=toggleCodeBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleCodeBlock.d.ts","sourceRoot":"","sources":["../../../MarkdownEditor/toggleCodeBlock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAK3D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,UAAW,MAAM,SAAS,MAAM,OAAO,MAAM,KAAG,gBAyB3E,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { lineBlockAround } from './lineBlockAround.js';
2
+
3
+ const fence = '```';
4
+ const toggleCodeBlock = (value, start, end) => {
5
+ const block = lineBlockAround(value, start, end);
6
+ const before = value.slice(0, block.start);
7
+ const after = value.slice(block.end);
8
+ const lines = block.lines;
9
+ const fenced = lines.length >= 2 && lines[0].startsWith(fence) && lines[lines.length - 1].trim() === fence;
10
+ if (fenced) {
11
+ const inner = lines.slice(1, -1).join('\n');
12
+ return {
13
+ value: `${before}${inner}${after}`,
14
+ selectionStart: block.start,
15
+ selectionEnd: block.start + inner.length,
16
+ };
17
+ }
18
+ const inner = lines.join('\n');
19
+ const opening = `${fence}\n`;
20
+ const contentStart = block.start + opening.length;
21
+ const caretOffset = start === end ? start - block.start : 0;
22
+ return {
23
+ value: `${before}${opening}${inner}\n${fence}${after}`,
24
+ selectionStart: contentStart + caretOffset,
25
+ selectionEnd: start === end ? contentStart + caretOffset : contentStart + inner.length,
26
+ };
27
+ };
28
+
29
+ export { toggleCodeBlock };
30
+ //# sourceMappingURL=toggleCodeBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleCodeBlock.js","sources":["../../../MarkdownEditor/toggleCodeBlock.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type { MarkdownTextEdit } from './MarkdownTextEdit';\nimport { lineBlockAround } from './lineBlockAround';\n\nconst fence = '```';\n\n/**\n * Fences the lines the selection touches in a code block, or takes the fences off when those lines\n * already open and close one.\n * @param value The markdown being edited.\n * @param start Where the selection starts.\n * @param end Where the selection ends.\n * @returns The edited markdown and the selection after it.\n */\nexport const toggleCodeBlock = (value: string, start: number, end: number): MarkdownTextEdit => {\n const block = lineBlockAround(value, start, end);\n const before = value.slice(0, block.start);\n const after = value.slice(block.end);\n const lines = block.lines;\n const fenced = lines.length >= 2 && lines[0].startsWith(fence) && lines[lines.length - 1].trim() === fence;\n\n if (fenced) {\n const inner = lines.slice(1, -1).join('\\n');\n return {\n value: `${before}${inner}${after}`,\n selectionStart: block.start,\n selectionEnd: block.start + inner.length,\n };\n }\n\n const inner = lines.join('\\n');\n const opening = `${fence}\\n`;\n const contentStart = block.start + opening.length;\n const caretOffset = start === end ? start - block.start : 0;\n return {\n value: `${before}${opening}${inner}\\n${fence}${after}`,\n selectionStart: contentStart + caretOffset,\n selectionEnd: start === end ? contentStart + caretOffset : contentStart + inner.length,\n };\n};\n"],"names":[],"mappings":";;AAMA,MAAM,KAAK,GAAG,KAAK;AAUZ,MAAM,eAAe,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW,KAAsB;IAC3F,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;AAChD,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;IAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;AACpC,IAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK;AACzB,IAAA,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,KAAK;IAE1G,IAAI,MAAM,EAAE;AACR,QAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3C,OAAO;AACH,YAAA,KAAK,EAAE,CAAA,EAAG,MAAM,GAAG,KAAK,CAAA,EAAG,KAAK,CAAA,CAAE;YAClC,cAAc,EAAE,KAAK,CAAC,KAAK;AAC3B,YAAA,YAAY,EAAE,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM;SAC3C;IACL;IAEA,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAC9B,IAAA,MAAM,OAAO,GAAG,CAAA,EAAG,KAAK,IAAI;IAC5B,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC,MAAM;AACjD,IAAA,MAAM,WAAW,GAAG,KAAK,KAAK,GAAG,GAAG,KAAK,GAAG,KAAK,CAAC,KAAK,GAAG,CAAC;IAC3D,OAAO;QACH,KAAK,EAAE,CAAA,EAAG,MAAM,CAAA,EAAG,OAAO,CAAA,EAAG,KAAK,CAAA,EAAA,EAAK,KAAK,CAAA,EAAG,KAAK,CAAA,CAAE;QACtD,cAAc,EAAE,YAAY,GAAG,WAAW;AAC1C,QAAA,YAAY,EAAE,KAAK,KAAK,GAAG,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,KAAK,CAAC,MAAM;KACzF;AACL;;"}
@@ -0,0 +1,13 @@
1
+ import type { MarkdownTextEdit } from './MarkdownTextEdit.js';
2
+ /**
3
+ * Wraps the selection in an inline marker such as `**` or a backtick, or takes the marker off when
4
+ * the selection already carries it - either just outside the selection or as its first and last
5
+ * characters. The selection stays on the same text, so a second format can be applied straight after.
6
+ * @param value The markdown being edited.
7
+ * @param start Where the selection starts.
8
+ * @param end Where the selection ends.
9
+ * @param marker The marker to toggle.
10
+ * @returns The edited markdown and the selection after it.
11
+ */
12
+ export declare const toggleInlineMarker: (value: string, start: number, end: number, marker: string) => MarkdownTextEdit;
13
+ //# sourceMappingURL=toggleInlineMarker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleInlineMarker.d.ts","sourceRoot":"","sources":["../../../MarkdownEditor/toggleInlineMarker.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAE3D;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,UAAW,MAAM,SAAS,MAAM,OAAO,MAAM,UAAU,MAAM,KAAG,gBA8B9F,CAAC"}
@@ -0,0 +1,29 @@
1
+ const toggleInlineMarker = (value, start, end, marker) => {
2
+ const length = marker.length;
3
+ const selected = value.slice(start, end);
4
+ const markedOutside = value.slice(start - length, start) === marker && value.slice(end, end + length) === marker;
5
+ if (markedOutside) {
6
+ return {
7
+ value: `${value.slice(0, start - length)}${selected}${value.slice(end + length)}`,
8
+ selectionStart: start - length,
9
+ selectionEnd: end - length,
10
+ };
11
+ }
12
+ const markedInside = selected.length >= length * 2 && selected.startsWith(marker) && selected.endsWith(marker);
13
+ if (markedInside) {
14
+ const inner = selected.slice(length, selected.length - length);
15
+ return {
16
+ value: `${value.slice(0, start)}${inner}${value.slice(end)}`,
17
+ selectionStart: start,
18
+ selectionEnd: start + inner.length,
19
+ };
20
+ }
21
+ return {
22
+ value: `${value.slice(0, start)}${marker}${selected}${marker}${value.slice(end)}`,
23
+ selectionStart: start + length,
24
+ selectionEnd: end + length,
25
+ };
26
+ };
27
+
28
+ export { toggleInlineMarker };
29
+ //# sourceMappingURL=toggleInlineMarker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleInlineMarker.js","sources":["../../../MarkdownEditor/toggleInlineMarker.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type { MarkdownTextEdit } from './MarkdownTextEdit';\n\n/**\n * Wraps the selection in an inline marker such as `**` or a backtick, or takes the marker off when\n * the selection already carries it - either just outside the selection or as its first and last\n * characters. The selection stays on the same text, so a second format can be applied straight after.\n * @param value The markdown being edited.\n * @param start Where the selection starts.\n * @param end Where the selection ends.\n * @param marker The marker to toggle.\n * @returns The edited markdown and the selection after it.\n */\nexport const toggleInlineMarker = (value: string, start: number, end: number, marker: string): MarkdownTextEdit => {\n const length = marker.length;\n const selected = value.slice(start, end);\n\n const markedOutside =\n value.slice(start - length, start) === marker && value.slice(end, end + length) === marker;\n if (markedOutside) {\n return {\n value: `${value.slice(0, start - length)}${selected}${value.slice(end + length)}`,\n selectionStart: start - length,\n selectionEnd: end - length,\n };\n }\n\n const markedInside =\n selected.length >= length * 2 && selected.startsWith(marker) && selected.endsWith(marker);\n if (markedInside) {\n const inner = selected.slice(length, selected.length - length);\n return {\n value: `${value.slice(0, start)}${inner}${value.slice(end)}`,\n selectionStart: start,\n selectionEnd: start + inner.length,\n };\n }\n\n return {\n value: `${value.slice(0, start)}${marker}${selected}${marker}${value.slice(end)}`,\n selectionStart: start + length,\n selectionEnd: end + length,\n };\n};\n"],"names":[],"mappings":"AAeO,MAAM,kBAAkB,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,GAAW,EAAE,MAAc,KAAsB;AAC9G,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM;IAC5B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC;IAExC,MAAM,aAAa,GACf,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,EAAE,KAAK,CAAC,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAAC,KAAK,MAAM;IAC9F,IAAI,aAAa,EAAE;QACf,OAAO;YACH,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,CAAA,EAAG,QAAQ,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,CAAA,CAAE;YACjF,cAAc,EAAE,KAAK,GAAG,MAAM;YAC9B,YAAY,EAAE,GAAG,GAAG,MAAM;SAC7B;IACL;IAEA,MAAM,YAAY,GACd,QAAQ,CAAC,MAAM,IAAI,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC7F,IAAI,YAAY,EAAE;AACd,QAAA,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC;QAC9D,OAAO;AACH,YAAA,KAAK,EAAE,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA,EAAG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,CAAE;AAC5D,YAAA,cAAc,EAAE,KAAK;AACrB,YAAA,YAAY,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM;SACrC;IACL;IAEA,OAAO;QACH,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,EAAG,QAAQ,CAAA,EAAG,MAAM,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,CAAE;QACjF,cAAc,EAAE,KAAK,GAAG,MAAM;QAC9B,YAAY,EAAE,GAAG,GAAG,MAAM;KAC7B;AACL;;"}
@@ -0,0 +1,14 @@
1
+ import type { MarkdownTextEdit } from './MarkdownTextEdit.js';
2
+ /**
3
+ * Puts a prefix such as `> ` or `- ` in front of every line the selection touches, or takes it off
4
+ * when every one of those lines already has it. A caret keeps its place in the text; a selection grows
5
+ * to cover the edited lines.
6
+ * @param value The markdown being edited.
7
+ * @param start Where the selection starts.
8
+ * @param end Where the selection ends.
9
+ * @param prefixFor Gives the prefix for the line at a position within the touched lines.
10
+ * @param existing Recognizes a line that already carries the prefix, and matches the prefix itself.
11
+ * @returns The edited markdown and the selection after it.
12
+ */
13
+ export declare const toggleLinePrefix: (value: string, start: number, end: number, prefixFor: (lineIndex: number) => string, existing: RegExp) => MarkdownTextEdit;
14
+ //# sourceMappingURL=toggleLinePrefix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleLinePrefix.d.ts","sourceRoot":"","sources":["../../../MarkdownEditor/toggleLinePrefix.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAG3D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,UAClB,MAAM,SACN,MAAM,OACR,MAAM,aACA,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,YAC9B,MAAM,KACjB,gBAoBF,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { lineBlockAround } from './lineBlockAround.js';
2
+
3
+ const toggleLinePrefix = (value, start, end, prefixFor, existing) => {
4
+ const block = lineBlockAround(value, start, end);
5
+ const everyLinePrefixed = block.lines.every(line => existing.test(line));
6
+ const edited = everyLinePrefixed
7
+ ? block.lines.map(line => line.replace(existing, ''))
8
+ : block.lines.map((line, lineIndex) => `${prefixFor(lineIndex)}${line}`);
9
+ const editedBlock = edited.join('\n');
10
+ const result = `${value.slice(0, block.start)}${editedBlock}${value.slice(block.end)}`;
11
+ if (start === end) {
12
+ const shift = edited[0].length - block.lines[0].length;
13
+ const caret = Math.max(block.start, start + shift);
14
+ return { value: result, selectionStart: caret, selectionEnd: caret };
15
+ }
16
+ return {
17
+ value: result,
18
+ selectionStart: block.start,
19
+ selectionEnd: block.start + editedBlock.length,
20
+ };
21
+ };
22
+
23
+ export { toggleLinePrefix };
24
+ //# sourceMappingURL=toggleLinePrefix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggleLinePrefix.js","sources":["../../../MarkdownEditor/toggleLinePrefix.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport type { MarkdownTextEdit } from './MarkdownTextEdit';\nimport { lineBlockAround } from './lineBlockAround';\n\n/**\n * Puts a prefix such as `> ` or `- ` in front of every line the selection touches, or takes it off\n * when every one of those lines already has it. A caret keeps its place in the text; a selection grows\n * to cover the edited lines.\n * @param value The markdown being edited.\n * @param start Where the selection starts.\n * @param end Where the selection ends.\n * @param prefixFor Gives the prefix for the line at a position within the touched lines.\n * @param existing Recognizes a line that already carries the prefix, and matches the prefix itself.\n * @returns The edited markdown and the selection after it.\n */\nexport const toggleLinePrefix = (\n value: string,\n start: number,\n end: number,\n prefixFor: (lineIndex: number) => string,\n existing: RegExp,\n): MarkdownTextEdit => {\n const block = lineBlockAround(value, start, end);\n const everyLinePrefixed = block.lines.every(line => existing.test(line));\n const edited = everyLinePrefixed\n ? block.lines.map(line => line.replace(existing, ''))\n : block.lines.map((line, lineIndex) => `${prefixFor(lineIndex)}${line}`);\n const editedBlock = edited.join('\\n');\n const result = `${value.slice(0, block.start)}${editedBlock}${value.slice(block.end)}`;\n\n if (start === end) {\n const shift = edited[0].length - block.lines[0].length;\n const caret = Math.max(block.start, start + shift);\n return { value: result, selectionStart: caret, selectionEnd: caret };\n }\n\n return {\n value: result,\n selectionStart: block.start,\n selectionEnd: block.start + editedBlock.length,\n };\n};\n"],"names":[],"mappings":";;AAiBO,MAAM,gBAAgB,GAAG,CAC5B,KAAa,EACb,KAAa,EACb,GAAW,EACX,SAAwC,EACxC,QAAgB,KACE;IAClB,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC;AAChD,IAAA,MAAM,iBAAiB,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG;AACX,UAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;UAClD,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,SAAS,KAAK,CAAA,EAAG,SAAS,CAAC,SAAS,CAAC,CAAA,EAAG,IAAI,CAAA,CAAE,CAAC;IAC5E,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA,EAAG,WAAW,CAAA,EAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA,CAAE;AAEtF,IAAA,IAAI,KAAK,KAAK,GAAG,EAAE;AACf,QAAA,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;AACtD,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAAC;AAClD,QAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;IACxE;IAEA,OAAO;AACH,QAAA,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,KAAK,CAAC,KAAK;AAC3B,QAAA,YAAY,EAAE,KAAK,CAAC,KAAK,GAAG,WAAW,CAAC,MAAM;KACjD;AACL;;"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The placeholder that stands where a file is going while it uploads.
3
+ *
4
+ * The text lands at the caret the moment a file is pasted or dropped, so the editor shows it took the
5
+ * file, keeps the position while several upload at once, and leaves something to point at when one
6
+ * fails.
7
+ * @param fileName The name of the file being uploaded.
8
+ * @returns The placeholder text.
9
+ */
10
+ export declare const uploadPlaceholderFor: (fileName: string) => string;
11
+ //# sourceMappingURL=uploadPlaceholderFor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadPlaceholderFor.d.ts","sourceRoot":"","sources":["../../../MarkdownEditor/uploadPlaceholderFor.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,aAAc,MAAM,KAAG,MAAuC,CAAC"}
@@ -0,0 +1,4 @@
1
+ const uploadPlaceholderFor = (fileName) => `![Uploading ${fileName}…]()`;
2
+
3
+ export { uploadPlaceholderFor };
4
+ //# sourceMappingURL=uploadPlaceholderFor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uploadPlaceholderFor.js","sources":["../../../MarkdownEditor/uploadPlaceholderFor.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\n/**\n * The placeholder that stands where a file is going while it uploads.\n *\n * The text lands at the caret the moment a file is pasted or dropped, so the editor shows it took the\n * file, keeps the position while several upload at once, and leaves something to point at when one\n * fails.\n * @param fileName The name of the file being uploaded.\n * @returns The placeholder text.\n */\nexport const uploadPlaceholderFor = (fileName: string): string => `![Uploading ${fileName}…]()`;\n"],"names":[],"mappings":"AAYO,MAAM,oBAAoB,GAAG,CAAC,QAAgB,KAAa,CAAA,YAAA,EAAe,QAAQ,CAAA,IAAA;;"}
@@ -0,0 +1,44 @@
1
+ import { type KeyboardEvent } from 'react';
2
+ import type { MarkdownCompletion } from './MarkdownCompletion.js';
3
+ import type { MarkdownCompletionMatch } from './MarkdownCompletionMatch.js';
4
+ import type { MarkdownSuggestion } from './MarkdownSuggestion.js';
5
+ import type { MarkdownTextEdit } from './MarkdownTextEdit.js';
6
+ /**
7
+ * The state of the completion being typed at the caret, as {@link useMarkdownCompletion} reports it.
8
+ */
9
+ export interface MarkdownCompletionState {
10
+ /** The completion being typed, when there is one and it has not been dismissed. */
11
+ match?: MarkdownCompletionMatch;
12
+ /** The suggestions on offer. Empty while none are known. */
13
+ suggestions: readonly MarkdownSuggestion[];
14
+ /** Whether the suggestion list is open. */
15
+ isOpen: boolean;
16
+ /** The index of the highlighted suggestion, which Enter or Tab picks. */
17
+ activeIndex: number;
18
+ /** Highlights a suggestion, as the pointer moving onto it does. */
19
+ highlight: (index: number) => void;
20
+ /**
21
+ * Works out the markdown after picking a suggestion, and closes the list.
22
+ * @param suggestion The picked suggestion.
23
+ * @returns The edit to apply, or undefined when nothing is being completed.
24
+ */
25
+ choose: (suggestion: MarkdownSuggestion) => MarkdownTextEdit | undefined;
26
+ /**
27
+ * Handles the keys the open list owns: the arrows move the highlight, Enter and Tab pick, Escape
28
+ * dismisses the list until the person starts another completion.
29
+ * @param event The key event from the writing area.
30
+ * @returns The edit to apply when a suggestion was picked.
31
+ */
32
+ handleKeyDown: (event: KeyboardEvent<HTMLTextAreaElement>) => MarkdownTextEdit | undefined;
33
+ }
34
+ /**
35
+ * Tracks the completion being typed at the caret, asks it for suggestions once typing pauses, and
36
+ * drops answers that arrive for a query the person has already typed past.
37
+ * @param value The markdown being edited.
38
+ * @param selectionStart Where the selection starts.
39
+ * @param selectionEnd Where the selection ends. Completion only runs on a plain caret.
40
+ * @param completions The configured completions.
41
+ * @returns The {@link MarkdownCompletionState}.
42
+ */
43
+ export declare const useMarkdownCompletion: (value: string, selectionStart: number, selectionEnd: number, completions: readonly MarkdownCompletion[]) => MarkdownCompletionState;
44
+ //# sourceMappingURL=useMarkdownCompletion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMarkdownCompletion.d.ts","sourceRoot":"","sources":["../../../MarkdownEditor/useMarkdownCompletion.ts"],"names":[],"mappings":"AAGA,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,OAAO,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA2B,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAoB,CAAC;AAW3D;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,mFAAmF;IACnF,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAEhC,4DAA4D;IAC5D,WAAW,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAE3C,2CAA2C;IAC3C,MAAM,EAAE,OAAO,CAAC;IAEhB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;IAEpB,mEAAmE;IACnE,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;;;OAIG;IACH,MAAM,EAAE,CAAC,UAAU,EAAE,kBAAkB,KAAK,gBAAgB,GAAG,SAAS,CAAC;IAEzE;;;;;OAKG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,mBAAmB,CAAC,KAAK,gBAAgB,GAAG,SAAS,CAAC;CAC9F;AAID;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,UACvB,MAAM,kBACG,MAAM,gBACR,MAAM,eACP,SAAS,kBAAkB,EAAE,KAC3C,uBA+FF,CAAC"}
@@ -0,0 +1,92 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { findCompletionMatch } from './findCompletionMatch.js';
3
+ import { replaceRange } from './replaceRange.js';
4
+
5
+ const defaultDebounce = 150;
6
+ const sessionOf = (match) => `${match.completionIndex}:${match.start}`;
7
+ const useMarkdownCompletion = (value, selectionStart, selectionEnd, completions) => {
8
+ const [answer, setAnswer] = useState();
9
+ const [activeIndex, setActiveIndex] = useState(0);
10
+ const [dismissedSession, setDismissedSession] = useState();
11
+ const found = completions.length > 0 && selectionStart === selectionEnd
12
+ ? findCompletionMatch(value, selectionStart, completions)
13
+ : undefined;
14
+ const session = found ? sessionOf(found) : undefined;
15
+ const match = found && session !== dismissedSession ? found : undefined;
16
+ const request = match ? `${sessionOf(match)}:${match.query}` : undefined;
17
+ const matchRef = useRef(match);
18
+ matchRef.current = match;
19
+ useEffect(() => {
20
+ const current = matchRef.current;
21
+ if (!current)
22
+ return;
23
+ const controller = new AbortController();
24
+ const requestSession = sessionOf(current);
25
+ const timeout = window.setTimeout(() => {
26
+ Promise.resolve()
27
+ .then(() => current.completion.suggest(current.query, { signal: controller.signal }))
28
+ .then(suggestions => {
29
+ if (!controller.signal.aborted)
30
+ setAnswer({ session: requestSession, suggestions });
31
+ }, () => {
32
+ if (!controller.signal.aborted)
33
+ setAnswer({ session: requestSession, suggestions: [] });
34
+ });
35
+ }, current.completion.debounce ?? defaultDebounce);
36
+ return () => {
37
+ window.clearTimeout(timeout);
38
+ controller.abort();
39
+ };
40
+ }, [request]);
41
+ useEffect(() => {
42
+ if (session === undefined)
43
+ setDismissedSession(undefined);
44
+ }, [session]);
45
+ const suggestions = match && answer?.session === sessionOf(match) ? answer.suggestions : [];
46
+ const isOpen = suggestions.length > 0;
47
+ const highlighted = Math.min(activeIndex, Math.max(suggestions.length - 1, 0));
48
+ useEffect(() => setActiveIndex(0), [answer]);
49
+ const choose = (suggestion) => {
50
+ if (!match)
51
+ return undefined;
52
+ setAnswer(undefined);
53
+ return replaceRange(value, match.start, match.end, suggestion.insertText);
54
+ };
55
+ const handleKeyDown = (event) => {
56
+ if (!isOpen || !match)
57
+ return undefined;
58
+ switch (event.key) {
59
+ case 'ArrowDown':
60
+ event.preventDefault();
61
+ setActiveIndex((highlighted + 1) % suggestions.length);
62
+ return undefined;
63
+ case 'ArrowUp':
64
+ event.preventDefault();
65
+ setActiveIndex((highlighted - 1 + suggestions.length) % suggestions.length);
66
+ return undefined;
67
+ case 'Enter':
68
+ case 'Tab':
69
+ event.preventDefault();
70
+ return choose(suggestions[highlighted]);
71
+ case 'Escape':
72
+ event.preventDefault();
73
+ event.stopPropagation();
74
+ setDismissedSession(sessionOf(match));
75
+ return undefined;
76
+ default:
77
+ return undefined;
78
+ }
79
+ };
80
+ return {
81
+ match,
82
+ suggestions,
83
+ isOpen,
84
+ activeIndex: highlighted,
85
+ highlight: setActiveIndex,
86
+ choose,
87
+ handleKeyDown,
88
+ };
89
+ };
90
+
91
+ export { useMarkdownCompletion };
92
+ //# sourceMappingURL=useMarkdownCompletion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMarkdownCompletion.js","sources":["../../../MarkdownEditor/useMarkdownCompletion.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useEffect, useRef, useState, type KeyboardEvent } from 'react';\nimport type { MarkdownCompletion } from './MarkdownCompletion';\nimport type { MarkdownCompletionMatch } from './MarkdownCompletionMatch';\nimport type { MarkdownSuggestion } from './MarkdownSuggestion';\nimport type { MarkdownTextEdit } from './MarkdownTextEdit';\nimport { findCompletionMatch } from './findCompletionMatch';\nimport { replaceRange } from './replaceRange';\n\nconst defaultDebounce = 150;\n\ninterface Answer {\n session: string;\n suggestions: readonly MarkdownSuggestion[];\n}\n\n/**\n * The state of the completion being typed at the caret, as {@link useMarkdownCompletion} reports it.\n */\nexport interface MarkdownCompletionState {\n /** The completion being typed, when there is one and it has not been dismissed. */\n match?: MarkdownCompletionMatch;\n\n /** The suggestions on offer. Empty while none are known. */\n suggestions: readonly MarkdownSuggestion[];\n\n /** Whether the suggestion list is open. */\n isOpen: boolean;\n\n /** The index of the highlighted suggestion, which Enter or Tab picks. */\n activeIndex: number;\n\n /** Highlights a suggestion, as the pointer moving onto it does. */\n highlight: (index: number) => void;\n\n /**\n * Works out the markdown after picking a suggestion, and closes the list.\n * @param suggestion The picked suggestion.\n * @returns The edit to apply, or undefined when nothing is being completed.\n */\n choose: (suggestion: MarkdownSuggestion) => MarkdownTextEdit | undefined;\n\n /**\n * Handles the keys the open list owns: the arrows move the highlight, Enter and Tab pick, Escape\n * dismisses the list until the person starts another completion.\n * @param event The key event from the writing area.\n * @returns The edit to apply when a suggestion was picked.\n */\n handleKeyDown: (event: KeyboardEvent<HTMLTextAreaElement>) => MarkdownTextEdit | undefined;\n}\n\nconst sessionOf = (match: MarkdownCompletionMatch) => `${match.completionIndex}:${match.start}`;\n\n/**\n * Tracks the completion being typed at the caret, asks it for suggestions once typing pauses, and\n * drops answers that arrive for a query the person has already typed past.\n * @param value The markdown being edited.\n * @param selectionStart Where the selection starts.\n * @param selectionEnd Where the selection ends. Completion only runs on a plain caret.\n * @param completions The configured completions.\n * @returns The {@link MarkdownCompletionState}.\n */\nexport const useMarkdownCompletion = (\n value: string,\n selectionStart: number,\n selectionEnd: number,\n completions: readonly MarkdownCompletion[],\n): MarkdownCompletionState => {\n const [answer, setAnswer] = useState<Answer>();\n const [activeIndex, setActiveIndex] = useState(0);\n const [dismissedSession, setDismissedSession] = useState<string>();\n\n const found =\n completions.length > 0 && selectionStart === selectionEnd\n ? findCompletionMatch(value, selectionStart, completions)\n : undefined;\n const session = found ? sessionOf(found) : undefined;\n const match = found && session !== dismissedSession ? found : undefined;\n const request = match ? `${sessionOf(match)}:${match.query}` : undefined;\n\n // The completions are usually recreated on every render of the host; only the request decides\n // when to ask again, and the ask reads whichever completion is current.\n const matchRef = useRef(match);\n matchRef.current = match;\n\n useEffect(() => {\n const current = matchRef.current;\n if (!current) return;\n const controller = new AbortController();\n const requestSession = sessionOf(current);\n const timeout = window.setTimeout(() => {\n Promise.resolve()\n .then(() => current.completion.suggest(current.query, { signal: controller.signal }))\n .then(\n suggestions => {\n if (!controller.signal.aborted) setAnswer({ session: requestSession, suggestions });\n },\n () => {\n if (!controller.signal.aborted) setAnswer({ session: requestSession, suggestions: [] });\n },\n );\n }, current.completion.debounce ?? defaultDebounce);\n\n return () => {\n window.clearTimeout(timeout);\n controller.abort();\n };\n }, [request]);\n\n // A dismissal lasts until the trigger it dismissed is gone - typing it again starts afresh.\n useEffect(() => {\n if (session === undefined) setDismissedSession(undefined);\n }, [session]);\n\n // An answer from the same session stays on screen while the next one is asked for, so the list\n // does not flicker on every keystroke.\n const suggestions = match && answer?.session === sessionOf(match) ? answer.suggestions : [];\n const isOpen = suggestions.length > 0;\n const highlighted = Math.min(activeIndex, Math.max(suggestions.length - 1, 0));\n\n useEffect(() => setActiveIndex(0), [answer]);\n\n const choose = (suggestion: MarkdownSuggestion) => {\n if (!match) return undefined;\n setAnswer(undefined);\n return replaceRange(value, match.start, match.end, suggestion.insertText);\n };\n\n const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {\n if (!isOpen || !match) return undefined;\n switch (event.key) {\n case 'ArrowDown':\n event.preventDefault();\n setActiveIndex((highlighted + 1) % suggestions.length);\n return undefined;\n case 'ArrowUp':\n event.preventDefault();\n setActiveIndex((highlighted - 1 + suggestions.length) % suggestions.length);\n return undefined;\n case 'Enter':\n case 'Tab':\n event.preventDefault();\n return choose(suggestions[highlighted]);\n case 'Escape':\n event.preventDefault();\n event.stopPropagation();\n setDismissedSession(sessionOf(match));\n return undefined;\n default:\n return undefined;\n }\n };\n\n return {\n match,\n suggestions,\n isOpen,\n activeIndex: highlighted,\n highlight: setActiveIndex,\n choose,\n handleKeyDown,\n };\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * What {@link useMarkdownUploads} needs from the editor it uploads for.
3
+ */
4
+ export interface MarkdownUploadsOptions {
5
+ /** Uploads one file and returns the markdown that stands for it. Omit to turn uploads off. */
6
+ uploadFile?: (file: File) => Promise<string>;
7
+ /** Told about a file whose upload failed; its placeholder has already been taken out again. */
8
+ onUploadFailed?: (file: File, error: unknown) => void;
9
+ /** Reads the markdown as it is right now, not as it was when an upload started. */
10
+ currentValue: () => string;
11
+ /** Puts text in at the caret. */
12
+ insertAtCaret: (text: string) => void;
13
+ /** Replaces the whole markdown. */
14
+ replaceValue: (value: string) => void;
15
+ }
16
+ /**
17
+ * The state of the uploads started from an editor, as {@link useMarkdownUploads} reports it.
18
+ */
19
+ export interface MarkdownUploadsState {
20
+ /** Whether files can be uploaded at all. */
21
+ isEnabled: boolean;
22
+ /** The names of the files uploading right now, in the order they were added. */
23
+ uploading: readonly string[];
24
+ /**
25
+ * Uploads files, each behind a placeholder put in at the caret straight away.
26
+ * @param files The files to upload.
27
+ */
28
+ upload: (files: readonly File[]) => void;
29
+ }
30
+ /**
31
+ * Uploads pasted and dropped files. Every file gets its placeholder at the caret first, so several
32
+ * added at once keep their order wherever the caret wanders, and each placeholder is resolved by value
33
+ * when its upload answers - replaced by the returned markdown, or taken out when the upload fails.
34
+ * @param options The {@link MarkdownUploadsOptions}.
35
+ * @returns The {@link MarkdownUploadsState}.
36
+ */
37
+ export declare const useMarkdownUploads: (options: MarkdownUploadsOptions) => MarkdownUploadsState;
38
+ //# sourceMappingURL=useMarkdownUploads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMarkdownUploads.d.ts","sourceRoot":"","sources":["../../../MarkdownEditor/useMarkdownUploads.ts"],"names":[],"mappings":"AAOA;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,8FAA8F;IAC9F,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7C,+FAA+F;IAC/F,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAEtD,mFAAmF;IACnF,YAAY,EAAE,MAAM,MAAM,CAAC;IAE3B,iCAAiC;IACjC,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAEtC,mCAAmC;IACnC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,4CAA4C;IAC5C,SAAS,EAAE,OAAO,CAAC;IAEnB,gFAAgF;IAChF,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAE7B;;;OAGG;IACH,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,CAAC;CAC5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,YAAa,sBAAsB,KAAG,oBAkDpE,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { useState, useRef, useEffect } from 'react';
2
+ import { replacePlaceholder } from './replacePlaceholder.js';
3
+ import { uploadPlaceholderFor } from './uploadPlaceholderFor.js';
4
+
5
+ const useMarkdownUploads = (options) => {
6
+ const [uploading, setUploading] = useState([]);
7
+ const optionsRef = useRef(options);
8
+ const mounted = useRef(true);
9
+ useEffect(() => {
10
+ optionsRef.current = options;
11
+ });
12
+ useEffect(() => {
13
+ mounted.current = true;
14
+ return () => {
15
+ mounted.current = false;
16
+ };
17
+ }, []);
18
+ const uploadOne = async (file, uploadFile) => {
19
+ const placeholder = uploadPlaceholderFor(file.name);
20
+ optionsRef.current.insertAtCaret(`${placeholder}\n`);
21
+ setUploading(current => [...current, file.name]);
22
+ try {
23
+ const markup = await uploadFile(file);
24
+ if (mounted.current) {
25
+ optionsRef.current.replaceValue(replacePlaceholder(optionsRef.current.currentValue(), placeholder, markup));
26
+ }
27
+ }
28
+ catch (error) {
29
+ if (mounted.current) {
30
+ const current = optionsRef.current.currentValue();
31
+ const inserted = current.includes(`${placeholder}\n`) ? `${placeholder}\n` : placeholder;
32
+ optionsRef.current.replaceValue(replacePlaceholder(current, inserted, ''));
33
+ }
34
+ optionsRef.current.onUploadFailed?.(file, error);
35
+ }
36
+ finally {
37
+ if (mounted.current) {
38
+ setUploading(current => {
39
+ const index = current.indexOf(file.name);
40
+ return index === -1 ? current : [...current.slice(0, index), ...current.slice(index + 1)];
41
+ });
42
+ }
43
+ }
44
+ };
45
+ const upload = (files) => {
46
+ const uploadFile = optionsRef.current.uploadFile;
47
+ if (!uploadFile)
48
+ return;
49
+ void Promise.all(files.map(file => uploadOne(file, uploadFile)));
50
+ };
51
+ return { isEnabled: options.uploadFile !== undefined, uploading, upload };
52
+ };
53
+
54
+ export { useMarkdownUploads };
55
+ //# sourceMappingURL=useMarkdownUploads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMarkdownUploads.js","sources":["../../../MarkdownEditor/useMarkdownUploads.ts"],"sourcesContent":["// Copyright (c) Cratis. All rights reserved.\n// Licensed under the MIT license. See LICENSE file in the project root for full license information.\n\nimport { useEffect, useRef, useState } from 'react';\nimport { replacePlaceholder } from './replacePlaceholder';\nimport { uploadPlaceholderFor } from './uploadPlaceholderFor';\n\n/**\n * What {@link useMarkdownUploads} needs from the editor it uploads for.\n */\nexport interface MarkdownUploadsOptions {\n /** Uploads one file and returns the markdown that stands for it. Omit to turn uploads off. */\n uploadFile?: (file: File) => Promise<string>;\n\n /** Told about a file whose upload failed; its placeholder has already been taken out again. */\n onUploadFailed?: (file: File, error: unknown) => void;\n\n /** Reads the markdown as it is right now, not as it was when an upload started. */\n currentValue: () => string;\n\n /** Puts text in at the caret. */\n insertAtCaret: (text: string) => void;\n\n /** Replaces the whole markdown. */\n replaceValue: (value: string) => void;\n}\n\n/**\n * The state of the uploads started from an editor, as {@link useMarkdownUploads} reports it.\n */\nexport interface MarkdownUploadsState {\n /** Whether files can be uploaded at all. */\n isEnabled: boolean;\n\n /** The names of the files uploading right now, in the order they were added. */\n uploading: readonly string[];\n\n /**\n * Uploads files, each behind a placeholder put in at the caret straight away.\n * @param files The files to upload.\n */\n upload: (files: readonly File[]) => void;\n}\n\n/**\n * Uploads pasted and dropped files. Every file gets its placeholder at the caret first, so several\n * added at once keep their order wherever the caret wanders, and each placeholder is resolved by value\n * when its upload answers - replaced by the returned markdown, or taken out when the upload fails.\n * @param options The {@link MarkdownUploadsOptions}.\n * @returns The {@link MarkdownUploadsState}.\n */\nexport const useMarkdownUploads = (options: MarkdownUploadsOptions): MarkdownUploadsState => {\n const [uploading, setUploading] = useState<readonly string[]>([]);\n const optionsRef = useRef(options);\n const mounted = useRef(true);\n\n useEffect(() => {\n optionsRef.current = options;\n });\n\n useEffect(() => {\n mounted.current = true;\n return () => {\n mounted.current = false;\n };\n }, []);\n\n const uploadOne = async (file: File, uploadFile: (file: File) => Promise<string>) => {\n const placeholder = uploadPlaceholderFor(file.name);\n optionsRef.current.insertAtCaret(`${placeholder}\\n`);\n setUploading(current => [...current, file.name]);\n\n try {\n const markup = await uploadFile(file);\n if (mounted.current) {\n optionsRef.current.replaceValue(replacePlaceholder(optionsRef.current.currentValue(), placeholder, markup));\n }\n } catch (error) {\n if (mounted.current) {\n const current = optionsRef.current.currentValue();\n const inserted = current.includes(`${placeholder}\\n`) ? `${placeholder}\\n` : placeholder;\n optionsRef.current.replaceValue(replacePlaceholder(current, inserted, ''));\n }\n optionsRef.current.onUploadFailed?.(file, error);\n } finally {\n if (mounted.current) {\n setUploading(current => {\n const index = current.indexOf(file.name);\n return index === -1 ? current : [...current.slice(0, index), ...current.slice(index + 1)];\n });\n }\n }\n };\n\n const upload = (files: readonly File[]) => {\n const uploadFile = optionsRef.current.uploadFile;\n if (!uploadFile) return;\n void Promise.all(files.map(file => uploadOne(file, uploadFile)));\n };\n\n return { isEnabled: options.uploadFile !== undefined, uploading, upload };\n};\n"],"names":[],"mappings":";;;;AAmDO,MAAM,kBAAkB,GAAG,CAAC,OAA+B,KAA0B;IACxF,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAoB,EAAE,CAAC;AACjE,IAAA,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC;AAClC,IAAA,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC;IAE5B,SAAS,CAAC,MAAK;AACX,QAAA,UAAU,CAAC,OAAO,GAAG,OAAO;AAChC,IAAA,CAAC,CAAC;IAEF,SAAS,CAAC,MAAK;AACX,QAAA,OAAO,CAAC,OAAO,GAAG,IAAI;AACtB,QAAA,OAAO,MAAK;AACR,YAAA,OAAO,CAAC,OAAO,GAAG,KAAK;AAC3B,QAAA,CAAC;IACL,CAAC,EAAE,EAAE,CAAC;IAEN,MAAM,SAAS,GAAG,OAAO,IAAU,EAAE,UAA2C,KAAI;QAChF,MAAM,WAAW,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;QACnD,UAAU,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA,EAAG,WAAW,CAAA,EAAA,CAAI,CAAC;AACpD,QAAA,YAAY,CAAC,OAAO,IAAI,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;AAEhD,QAAA,IAAI;AACA,YAAA,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC;AACrC,YAAA,IAAI,OAAO,CAAC,OAAO,EAAE;AACjB,gBAAA,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;YAC/G;QACJ;QAAE,OAAO,KAAK,EAAE;AACZ,YAAA,IAAI,OAAO,CAAC,OAAO,EAAE;gBACjB,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE;gBACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA,EAAG,WAAW,CAAA,EAAA,CAAI,CAAC,GAAG,GAAG,WAAW,CAAA,EAAA,CAAI,GAAG,WAAW;AACxF,gBAAA,UAAU,CAAC,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;YAC9E;YACA,UAAU,CAAC,OAAO,CAAC,cAAc,GAAG,IAAI,EAAE,KAAK,CAAC;QACpD;gBAAU;AACN,YAAA,IAAI,OAAO,CAAC,OAAO,EAAE;gBACjB,YAAY,CAAC,OAAO,IAAG;oBACnB,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;AACxC,oBAAA,OAAO,KAAK,KAAK,EAAE,GAAG,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AAC7F,gBAAA,CAAC,CAAC;YACN;QACJ;AACJ,IAAA,CAAC;AAED,IAAA,MAAM,MAAM,GAAG,CAAC,KAAsB,KAAI;AACtC,QAAA,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU;AAChD,QAAA,IAAI,CAAC,UAAU;YAAE;QACjB,KAAK,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;AACpE,IAAA,CAAC;AAED,IAAA,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE;AAC7E;;"}