@joinezco/markdown-editor 0.0.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 (168) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +9 -0
  3. package/TEST_README.md +359 -0
  4. package/dist/editor/commands/index.d.ts +2 -0
  5. package/dist/editor/commands/index.js +96 -0
  6. package/dist/editor/extensions/codeblock.d.ts +30 -0
  7. package/dist/editor/extensions/codeblock.js +390 -0
  8. package/dist/editor/extensions/filesystem.d.ts +8 -0
  9. package/dist/editor/extensions/filesystem.js +54 -0
  10. package/dist/editor/extensions/link.d.ts +1 -0
  11. package/dist/editor/extensions/link.js +24 -0
  12. package/dist/editor/extensions/slash-commands.d.ts +17 -0
  13. package/dist/editor/extensions/slash-commands.js +324 -0
  14. package/dist/editor/extensions/taskitem.d.ts +8 -0
  15. package/dist/editor/extensions/taskitem.js +67 -0
  16. package/dist/editor/index.d.ts +19 -0
  17. package/dist/editor/index.js +68 -0
  18. package/dist/editor/styles.d.ts +2 -0
  19. package/dist/editor/styles.js +153 -0
  20. package/dist/index.d.ts +2 -0
  21. package/dist/index.js +2 -0
  22. package/index.html +16 -0
  23. package/package.json +73 -0
  24. package/public/fonts/Source_Serif_4/OFL.txt +91 -0
  25. package/public/fonts/Source_Serif_4/README.txt +128 -0
  26. package/public/fonts/Source_Serif_4/SourceSerif4-Italic-VariableFont_opsz,wght.ttf +0 -0
  27. package/public/fonts/Source_Serif_4/SourceSerif4-VariableFont_opsz,wght.ttf +0 -0
  28. package/public/fonts/Source_Serif_4/static/SourceSerif4-Black.ttf +0 -0
  29. package/public/fonts/Source_Serif_4/static/SourceSerif4-BlackItalic.ttf +0 -0
  30. package/public/fonts/Source_Serif_4/static/SourceSerif4-Bold.ttf +0 -0
  31. package/public/fonts/Source_Serif_4/static/SourceSerif4-BoldItalic.ttf +0 -0
  32. package/public/fonts/Source_Serif_4/static/SourceSerif4-ExtraBold.ttf +0 -0
  33. package/public/fonts/Source_Serif_4/static/SourceSerif4-ExtraBoldItalic.ttf +0 -0
  34. package/public/fonts/Source_Serif_4/static/SourceSerif4-ExtraLight.ttf +0 -0
  35. package/public/fonts/Source_Serif_4/static/SourceSerif4-ExtraLightItalic.ttf +0 -0
  36. package/public/fonts/Source_Serif_4/static/SourceSerif4-Italic.ttf +0 -0
  37. package/public/fonts/Source_Serif_4/static/SourceSerif4-Light.ttf +0 -0
  38. package/public/fonts/Source_Serif_4/static/SourceSerif4-LightItalic.ttf +0 -0
  39. package/public/fonts/Source_Serif_4/static/SourceSerif4-Medium.ttf +0 -0
  40. package/public/fonts/Source_Serif_4/static/SourceSerif4-MediumItalic.ttf +0 -0
  41. package/public/fonts/Source_Serif_4/static/SourceSerif4-Regular.ttf +0 -0
  42. package/public/fonts/Source_Serif_4/static/SourceSerif4-SemiBold.ttf +0 -0
  43. package/public/fonts/Source_Serif_4/static/SourceSerif4-SemiBoldItalic.ttf +0 -0
  44. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-Black.ttf +0 -0
  45. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-BlackItalic.ttf +0 -0
  46. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-Bold.ttf +0 -0
  47. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-BoldItalic.ttf +0 -0
  48. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-ExtraBold.ttf +0 -0
  49. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-ExtraBoldItalic.ttf +0 -0
  50. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-ExtraLight.ttf +0 -0
  51. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-ExtraLightItalic.ttf +0 -0
  52. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-Italic.ttf +0 -0
  53. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-Light.ttf +0 -0
  54. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-LightItalic.ttf +0 -0
  55. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-Medium.ttf +0 -0
  56. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-MediumItalic.ttf +0 -0
  57. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-Regular.ttf +0 -0
  58. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-SemiBold.ttf +0 -0
  59. package/public/fonts/Source_Serif_4/static/SourceSerif4_18pt-SemiBoldItalic.ttf +0 -0
  60. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-Black.ttf +0 -0
  61. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-BlackItalic.ttf +0 -0
  62. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-Bold.ttf +0 -0
  63. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-BoldItalic.ttf +0 -0
  64. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-ExtraBold.ttf +0 -0
  65. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-ExtraBoldItalic.ttf +0 -0
  66. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-ExtraLight.ttf +0 -0
  67. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-ExtraLightItalic.ttf +0 -0
  68. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-Italic.ttf +0 -0
  69. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-Light.ttf +0 -0
  70. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-LightItalic.ttf +0 -0
  71. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-Medium.ttf +0 -0
  72. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-MediumItalic.ttf +0 -0
  73. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-Regular.ttf +0 -0
  74. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-SemiBold.ttf +0 -0
  75. package/public/fonts/Source_Serif_4/static/SourceSerif4_36pt-SemiBoldItalic.ttf +0 -0
  76. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-Black.ttf +0 -0
  77. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-BlackItalic.ttf +0 -0
  78. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-Bold.ttf +0 -0
  79. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-BoldItalic.ttf +0 -0
  80. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-ExtraBold.ttf +0 -0
  81. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-ExtraBoldItalic.ttf +0 -0
  82. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-ExtraLight.ttf +0 -0
  83. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-ExtraLightItalic.ttf +0 -0
  84. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-Italic.ttf +0 -0
  85. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-Light.ttf +0 -0
  86. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-LightItalic.ttf +0 -0
  87. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-Medium.ttf +0 -0
  88. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-MediumItalic.ttf +0 -0
  89. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-Regular.ttf +0 -0
  90. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-SemiBold.ttf +0 -0
  91. package/public/fonts/Source_Serif_4/static/SourceSerif4_48pt-SemiBoldItalic.ttf +0 -0
  92. package/public/snapshot.bin +0 -0
  93. package/src/App.css +236 -0
  94. package/src/App.tsx +73 -0
  95. package/src/index.css +63 -0
  96. package/src/lib/editor/commands/index.ts +110 -0
  97. package/src/lib/editor/extensions/__screenshots__/bullet-to-task.test.ts/BulletToTaskConverter-should-convert-bullet-list-item-to-checked-task-item-when--x--is-typed-1.png +0 -0
  98. package/src/lib/editor/extensions/__screenshots__/bullet-to-task.test.ts/BulletToTaskConverter-should-convert-bullet-list-item-to-task-item-when-----is-typed-1.png +0 -0
  99. package/src/lib/editor/extensions/bullet-to-task.test.ts +38 -0
  100. package/src/lib/editor/extensions/codeblock.ts +447 -0
  101. package/src/lib/editor/extensions/filesystem.ts +68 -0
  102. package/src/lib/editor/extensions/link.ts +31 -0
  103. package/src/lib/editor/extensions/slash-commands.ts +383 -0
  104. package/src/lib/editor/extensions/taskitem.ts +86 -0
  105. package/src/lib/editor/index.ts +82 -0
  106. package/src/lib/editor/styles.ts +166 -0
  107. package/src/lib/index.ts +3 -0
  108. package/src/lib/types/css.d.ts +4 -0
  109. package/src/main.tsx +10 -0
  110. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-be-focusable-1.png +0 -0
  111. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-create-an-editor-instance-1.png +0 -0
  112. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-have-initial-content-1.png +0 -0
  113. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Basic-Editor-Functionality-should-render-in-the-DOM-1.png +0 -0
  114. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Browser-specific-Features-should-handle-copy-and-paste-operations-1.png +0 -0
  115. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Browser-specific-Features-should-handle-undo-and-redo-1.png +0 -0
  116. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Editor-State-and-Updates-should-maintain-state-across-content-changes-1.png +0 -0
  117. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Editor-State-and-Updates-should-trigger-update-callbacks-1.png +0 -0
  118. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Error-Handling-should-handle-invalid-markdown-gracefully-1.png +0 -0
  119. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Error-Handling-should-handle-very-long-content-1.png +0 -0
  120. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Keyboard-Shortcuts-should-handle-Ctrl-B-for-bold-1.png +0 -0
  121. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Keyboard-Shortcuts-should-handle-Ctrl-I-for-italic-1.png +0 -0
  122. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Content-Management-should-get-markdown-content-1.png +0 -0
  123. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Content-Management-should-handle-empty-content-1.png +0 -0
  124. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Content-Management-should-set-markdown-content-1.png +0 -0
  125. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-bold-text-1.png +0 -0
  126. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-code-blocks-1.png +0 -0
  127. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-headings-1.png +0 -0
  128. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-inline-code-1.png +0 -0
  129. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-italic-text-1.png +0 -0
  130. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-lists-1.png +0 -0
  131. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Markdown-Formatting-should-handle-task-lists-1.png +0 -0
  132. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Selection-and-Cursor-Management-should-handle-cursor-positioning-1.png +0 -0
  133. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Selection-and-Cursor-Management-should-set-and-get-selection-1.png +0 -0
  134. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Text-Input-and-Editing-should-handle-line-breaks-1.png +0 -0
  135. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Text-Input-and-Editing-should-handle-typing-at-different-positions-1.png +0 -0
  136. package/src/test/__screenshots__/editor.test.ts/MarkdownEditor-Text-Input-and-Editing-should-insert-text-at-cursor-position-1.png +0 -0
  137. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-handle-code-blocks-without-language-specification-1.png +0 -0
  138. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-handle-different-programming-languages-1.png +0 -0
  139. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-handle-inline-code-1.png +0 -0
  140. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Code-Block-Extension-should-render-code-blocks-with-syntax-highlighting-1.png +0 -0
  141. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Extension-Interactions-should-handle-multiple-extensions-working-together-1.png +0 -0
  142. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Extension-Interactions-should-maintain-editor-state-across-complex-operations-1.png +0 -0
  143. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-File-System-Integration-should-handle-file-references-in-code-blocks-1.png +0 -0
  144. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-File-System-Integration-should-maintain-file-system-state-1.png +0 -0
  145. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Link-Extension-should-auto-detect-URLs-1.png +0 -0
  146. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Link-Extension-should-handle-email-links-1.png +0 -0
  147. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Link-Extension-should-render-links-correctly-1.png +0 -0
  148. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Markdown-Storage-should-provide-markdown-storage-interface-1.png +0 -0
  149. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Markdown-Storage-should-sync-markdown-content-with-storage-1.png +0 -0
  150. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Slash-Commands-Extension-should-handle-heading-commands-1.png +0 -0
  151. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Slash-Commands-Extension-should-trigger-slash-commands-1.png +0 -0
  152. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Table-Extension-should-handle-table-navigation-1.png +0 -0
  153. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Table-Extension-should-render-tables-correctly-1.png +0 -0
  154. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Task-List-Extension-should-handle-nested-task-lists-1.png +0 -0
  155. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Task-List-Extension-should-render-task-lists-correctly-1.png +0 -0
  156. package/src/test/__screenshots__/extensions.test.ts/MarkdownEditor-Extensions-Task-List-Extension-should-toggle-task-completion-1.png +0 -0
  157. package/src/test/editor.test.ts +346 -0
  158. package/src/test/example.ts +92 -0
  159. package/src/test/extensions.test.ts +333 -0
  160. package/src/test/setup.ts +55 -0
  161. package/src/test/utils.ts +212 -0
  162. package/src/vite-env.d.ts +1 -0
  163. package/tsconfig.app.json +29 -0
  164. package/tsconfig.json +7 -0
  165. package/tsconfig.lib.json +17 -0
  166. package/tsconfig.node.json +24 -0
  167. package/vite.config.ts +89 -0
  168. package/vitest.config.ts +65 -0
package/src/App.css ADDED
@@ -0,0 +1,236 @@
1
+ @font-face {
2
+ font-family: "Source Serif 4";
3
+ src: url("/fonts/Source_Serif_4/SourceSerif4-VariableFont_opsz,wght.ttf") format("truetype-variations");
4
+ font-weight: 200 900;
5
+ /* range supported by the variable font */
6
+ font-style: normal;
7
+ }
8
+
9
+ @font-face {
10
+ font-family: "Source Serif 4";
11
+ src: url("/fonts/Source_Serif_4/SourceSerif4-Italic-VariableFont_opsz,wght.ttf") format("truetype-variations");
12
+ font-weight: 200 900;
13
+ font-style: italic;
14
+ }
15
+
16
+ /* TODO: use unique CSS class so it's not likely to conflict with existing page styles */
17
+
18
+ .ProseMirror {
19
+ font-family: 'Source Serif 4', system-ui, Avenir, Helvetica, Arial, serif;
20
+ padding: 2rem;
21
+ min-height: 200px;
22
+ outline: none;
23
+ border: 4px solid #000;
24
+ max-width: min(60ch, 85vw);
25
+ box-shadow: -1rem 1rem black;
26
+
27
+ h1 {
28
+ margin-top: 0;
29
+ }
30
+ }
31
+
32
+ /* Slash Commands Dropdown */
33
+ .slash-commands-dropdown {
34
+ background: white;
35
+ border: 1px solid #e2e8f0;
36
+ border-radius: 8px;
37
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
38
+ max-height: 300px;
39
+ overflow-y: auto;
40
+ padding: 4px;
41
+ min-width: 280px;
42
+ z-index: 1000;
43
+ outline: none;
44
+ }
45
+
46
+ .slash-command-item {
47
+ display: block;
48
+ width: 100%;
49
+ padding: 8px 12px;
50
+ border: none;
51
+ background: none;
52
+ border-radius: 6px;
53
+ cursor: pointer;
54
+ text-align: left;
55
+ transition: background-color 0.15s ease;
56
+ }
57
+
58
+ .slash-command-item:hover,
59
+ .slash-command-item.selected {
60
+ background-color: #f1f5f9;
61
+ }
62
+
63
+ .slash-command-content {
64
+ display: flex;
65
+ align-items: center;
66
+ gap: 12px;
67
+ }
68
+
69
+ .slash-command-icon {
70
+ font-size: 18px;
71
+ width: 24px;
72
+ text-align: center;
73
+ flex-shrink: 0;
74
+ }
75
+
76
+ .slash-command-text {
77
+ flex: 1;
78
+ min-width: 0;
79
+ }
80
+
81
+ .slash-command-title {
82
+ font-weight: 500;
83
+ color: #1e293b;
84
+ font-size: 14px;
85
+ line-height: 1.4;
86
+ }
87
+
88
+ .slash-command-description {
89
+ color: #64748b;
90
+ font-size: 12px;
91
+ line-height: 1.3;
92
+ margin-top: 2px;
93
+ }
94
+
95
+ /* Tippy.js theme for slash commands */
96
+ .tippy-box[data-theme~='slash-commands'] {
97
+ background: transparent;
98
+ border: none;
99
+ box-shadow: none;
100
+ padding: 0;
101
+ }
102
+
103
+ .tippy-box[data-theme~='slash-commands'] .tippy-content {
104
+ padding: 0;
105
+ }
106
+
107
+ /* Settings Modal */
108
+ .settings-modal-overlay {
109
+ position: fixed;
110
+ top: 0;
111
+ left: 0;
112
+ right: 0;
113
+ bottom: 0;
114
+ background: rgba(0, 0, 0, 0.5);
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ z-index: 2000;
119
+ }
120
+
121
+ .settings-modal {
122
+ background: white;
123
+ border-radius: 12px;
124
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
125
+ width: 90%;
126
+ max-width: 500px;
127
+ max-height: 80vh;
128
+ overflow: hidden;
129
+ display: flex;
130
+ flex-direction: column;
131
+ }
132
+
133
+ .settings-header {
134
+ display: flex;
135
+ align-items: center;
136
+ justify-content: space-between;
137
+ padding: 20px 24px;
138
+ border-bottom: 1px solid #e2e8f0;
139
+ }
140
+
141
+ .settings-header h3 {
142
+ margin: 0;
143
+ font-size: 18px;
144
+ font-weight: 600;
145
+ color: #1e293b;
146
+ }
147
+
148
+ .settings-close {
149
+ background: none;
150
+ border: none;
151
+ font-size: 24px;
152
+ cursor: pointer;
153
+ color: #64748b;
154
+ padding: 0;
155
+ width: 32px;
156
+ height: 32px;
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: center;
160
+ border-radius: 6px;
161
+ transition: background-color 0.15s ease;
162
+ }
163
+
164
+ .settings-close:hover {
165
+ background-color: #f1f5f9;
166
+ }
167
+
168
+ .settings-content {
169
+ padding: 24px;
170
+ flex: 1;
171
+ overflow-y: auto;
172
+ }
173
+
174
+ .setting-group {
175
+ margin-bottom: 20px;
176
+ }
177
+
178
+ .setting-group label {
179
+ display: flex;
180
+ align-items: center;
181
+ gap: 8px;
182
+ font-size: 14px;
183
+ color: #374151;
184
+ cursor: pointer;
185
+ }
186
+
187
+ .setting-group input[type="checkbox"] {
188
+ width: 16px;
189
+ height: 16px;
190
+ }
191
+
192
+ .setting-group select {
193
+ margin-left: 8px;
194
+ padding: 4px 8px;
195
+ border: 1px solid #d1d5db;
196
+ border-radius: 4px;
197
+ font-size: 14px;
198
+ }
199
+
200
+ .settings-footer {
201
+ display: flex;
202
+ gap: 12px;
203
+ justify-content: flex-end;
204
+ padding: 20px 24px;
205
+ border-top: 1px solid #e2e8f0;
206
+ }
207
+
208
+ .settings-save,
209
+ .settings-cancel {
210
+ padding: 8px 16px;
211
+ border: 1px solid #d1d5db;
212
+ border-radius: 6px;
213
+ font-size: 14px;
214
+ cursor: pointer;
215
+ transition: all 0.15s ease;
216
+ }
217
+
218
+ .settings-save {
219
+ background: #3b82f6;
220
+ color: white;
221
+ border-color: #3b82f6;
222
+ }
223
+
224
+ .settings-save:hover {
225
+ background: #2563eb;
226
+ border-color: #2563eb;
227
+ }
228
+
229
+ .settings-cancel {
230
+ background: white;
231
+ color: #374151;
232
+ }
233
+
234
+ .settings-cancel:hover {
235
+ background: #f9fafb;
236
+ }
package/src/App.tsx ADDED
@@ -0,0 +1,73 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { createEditor, MarkdownEditor } from './lib/editor';
3
+ import { CodeblockFS, SearchIndex } from '@joinezco/codeblock';
4
+ import './App.css'
5
+ import { file } from './test/example';
6
+
7
+ function App() {
8
+ const [markdownContent, setMarkdownContent] = useState('');
9
+ const [editor, setEditor] = useState<MarkdownEditor | null>(null);
10
+ const ref = useRef(null);
11
+
12
+ async function loadFs() {
13
+ const fs = await CodeblockFS.worker('/snapshot.bin');
14
+
15
+ // Generate or load search index
16
+ try {
17
+ const index = await SearchIndex.get(fs, '.codeblock/index.json');
18
+ console.log('Search index ready with', index, 'documents');
19
+ } catch (error) {
20
+ console.warn('Failed to create search index:', error);
21
+ }
22
+
23
+ return fs;
24
+ }
25
+
26
+ useEffect(() => {
27
+
28
+ let newEditor: MarkdownEditor | null = null;
29
+
30
+ if (ref.current && !editor) {
31
+ loadFs().then(async fs => {
32
+ console.debug('Loaded filesystem', fs);
33
+
34
+ await fs.writeFile('test.md', file);
35
+
36
+ newEditor = createEditor({
37
+ element: ref.current!,
38
+ fs: {
39
+ fs: fs,
40
+ filepath: 'test.md',
41
+ autoSave: false,
42
+ },
43
+ onUpdate: ({ editor }) => {
44
+ const markdown = (editor as MarkdownEditor).storage.markdown.getMarkdown();
45
+ setMarkdownContent(markdown);
46
+ },
47
+ });
48
+ setEditor(newEditor);
49
+ });
50
+ }
51
+
52
+ return () => {
53
+ if (ref.current) {
54
+ newEditor?.destroy();
55
+ }
56
+ };
57
+
58
+ }, [ref.current])
59
+
60
+
61
+ return (
62
+ <div style={{ padding: '20px', maxWidth: '800px', margin: 'auto' }}>
63
+ <div id='md-editor' ref={ref}></div>
64
+ <hr style={{ margin: '20px 0' }} />
65
+ <h2>Live Markdown Output:</h2>
66
+ <pre style={{ whiteSpace: 'pre-wrap', background: '#eee', padding: '10px', border: '1px solid #ccc' }}>
67
+ {markdownContent}
68
+ </pre>
69
+ </div>
70
+ );
71
+ }
72
+
73
+ export default App;
package/src/index.css ADDED
@@ -0,0 +1,63 @@
1
+ :root {
2
+ font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
+ line-height: 1.5;
4
+ font-weight: 400;
5
+
6
+ color-scheme: light dark;
7
+ color: rgba(255, 255, 255, 0.87);
8
+ background-color: #242424;
9
+
10
+ font-synthesis: none;
11
+ text-rendering: optimizeLegibility;
12
+ -webkit-font-smoothing: antialiased;
13
+ -moz-osx-font-smoothing: grayscale;
14
+ }
15
+
16
+ body {
17
+ margin: 0;
18
+ display: flex;
19
+ place-items: center;
20
+ min-width: 320px;
21
+ min-height: 100vh;
22
+ }
23
+
24
+ h1 {
25
+ font-size: 3.2em;
26
+ line-height: 1.1;
27
+ }
28
+
29
+ button {
30
+ border-radius: 8px;
31
+ border: 1px solid transparent;
32
+ padding: 0.6em 1.2em;
33
+ font-size: 1em;
34
+ font-weight: 500;
35
+ font-family: inherit;
36
+ background-color: #1a1a1a;
37
+ cursor: pointer;
38
+ transition: border-color 0.25s;
39
+ }
40
+
41
+ button:hover {
42
+ border-color: #646cff;
43
+ }
44
+
45
+ button:focus,
46
+ button:focus-visible {
47
+ outline: 4px auto -webkit-focus-ring-color;
48
+ }
49
+
50
+ @media (prefers-color-scheme: light) {
51
+ :root {
52
+ color: #213547;
53
+ background-color: #ffffff;
54
+ }
55
+
56
+ a:hover {
57
+ color: #2732fe;
58
+ }
59
+
60
+ button {
61
+ background-color: #f9f9f9;
62
+ }
63
+ }
@@ -0,0 +1,110 @@
1
+ import { SlashCommand } from '../extensions/slash-commands'
2
+
3
+ // Settings modal utility function
4
+ const openSettingsModal = (editor: any) => {
5
+ // Create a simple modal for settings
6
+ const modal = document.createElement('div')
7
+ modal.className = 'settings-modal-overlay'
8
+
9
+ const modalContent = document.createElement('div')
10
+ modalContent.className = 'settings-modal'
11
+
12
+ modalContent.innerHTML = `
13
+ <div class="settings-header">
14
+ <h3>Editor Settings</h3>
15
+ <button class="settings-close">&times;</button>
16
+ </div>
17
+ <div class="settings-content">
18
+ <div class="setting-group">
19
+ <label>
20
+ <input type="checkbox" id="auto-save" ${editor.storage.persistence?.options?.autoSave ? 'checked' : ''}>
21
+ Auto-save documents
22
+ </label>
23
+ </div>
24
+ <div class="setting-group">
25
+ <label>
26
+ <input type="checkbox" id="word-wrap" checked>
27
+ Word wrap
28
+ </label>
29
+ </div>
30
+ <div class="setting-group">
31
+ <label>
32
+ Theme:
33
+ <select id="theme-select">
34
+ <option value="light">Light</option>
35
+ <option value="dark">Dark</option>
36
+ <option value="auto">Auto</option>
37
+ </select>
38
+ </label>
39
+ </div>
40
+ </div>
41
+ <div class="settings-footer">
42
+ <button class="settings-save">Save</button>
43
+ <button class="settings-cancel">Cancel</button>
44
+ </div>
45
+ `
46
+
47
+ modal.appendChild(modalContent)
48
+
49
+ const closeModal = () => {
50
+ if (document.body.contains(modal)) {
51
+ document.body.removeChild(modal)
52
+ }
53
+ }
54
+
55
+ const saveSettings = () => {
56
+ const autoSave = (modal.querySelector('#auto-save') as HTMLInputElement).checked
57
+ const theme = (modal.querySelector('#theme-select') as HTMLSelectElement).value
58
+
59
+ // Apply settings
60
+ if (editor.storage.persistence?.options) {
61
+ editor.storage.persistence.options.autoSave = autoSave
62
+ }
63
+
64
+ // Apply theme (you can extend this based on your theme system)
65
+ document.documentElement.setAttribute('data-theme', theme)
66
+
67
+ console.log('Settings saved:', { autoSave, theme })
68
+ closeModal()
69
+ }
70
+
71
+ // Event listeners
72
+ const closeButton = modal.querySelector('.settings-close')
73
+ const cancelButton = modal.querySelector('.settings-cancel')
74
+ const saveButton = modal.querySelector('.settings-save')
75
+
76
+ if (closeButton) closeButton.addEventListener('click', closeModal)
77
+ if (cancelButton) cancelButton.addEventListener('click', closeModal)
78
+ if (saveButton) saveButton.addEventListener('click', saveSettings)
79
+
80
+ modal.addEventListener('click', (e) => {
81
+ if (e.target === modal) closeModal()
82
+ })
83
+
84
+ // Handle escape key
85
+ const handleEscape = (e: KeyboardEvent) => {
86
+ if (e.key === 'Escape') {
87
+ closeModal()
88
+ document.removeEventListener('keydown', handleEscape)
89
+ }
90
+ }
91
+ document.addEventListener('keydown', handleEscape)
92
+
93
+ document.body.appendChild(modal)
94
+ }
95
+
96
+
97
+ export const defaultSlashCommands: SlashCommand[] = [
98
+ {
99
+ title: 'Settings',
100
+ description: 'Configure editor preferences',
101
+ icon: '⚙️',
102
+ command: ({ editor, range }) => {
103
+ // Remove the slash command text
104
+ editor.chain().focus().deleteRange(range).run()
105
+
106
+ // Open settings modal
107
+ openSettingsModal(editor)
108
+ },
109
+ },
110
+ ]
@@ -0,0 +1,38 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createEditor } from '../index';
3
+
4
+ describe('BulletToTaskConverter', () => {
5
+ it('should convert bullet list item to task item when [ ] is typed', () => {
6
+ const editor = createEditor();
7
+
8
+ // Set initial content with a bullet list
9
+ editor.commands.setContent('* Hello world');
10
+
11
+ // Simulate editing the content to add [ ]
12
+ editor.commands.setTextSelection(2); // Position after "* "
13
+ editor.commands.insertContent('[ ] ');
14
+
15
+ // Check if it converted to a task item
16
+ const json = editor.getJSON();
17
+ expect(json.content?.[0].type).toBe('taskList');
18
+ expect(json.content?.[0].content?.[0].type).toBe('taskItem');
19
+ expect(json.content?.[0].content?.[0].attrs?.checked).toBe(false);
20
+ });
21
+
22
+ it('should convert bullet list item to checked task item when [x] is typed', () => {
23
+ const editor = createEditor();
24
+
25
+ // Set initial content with a bullet list
26
+ editor.commands.setContent('* Hello world');
27
+
28
+ // Simulate editing the content to add [x]
29
+ editor.commands.setTextSelection(2); // Position after "* "
30
+ editor.commands.insertContent('[x] ');
31
+
32
+ // Check if it converted to a checked task item
33
+ const json = editor.getJSON();
34
+ expect(json.content?.[0].type).toBe('taskList');
35
+ expect(json.content?.[0].content?.[0].type).toBe('taskItem');
36
+ expect(json.content?.[0].content?.[0].attrs?.checked).toBe(true);
37
+ });
38
+ });