@health-samurai/react-components 0.0.0-alpha.2 → 0.0.0-alpha.20

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 (571) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +102 -1
  3. package/dist/bundle.css +2340 -744
  4. package/dist/src/components/button-dropdown.d.ts +10 -0
  5. package/dist/src/components/button-dropdown.d.ts.map +1 -0
  6. package/dist/src/components/button-dropdown.js +70 -0
  7. package/dist/src/components/button-dropdown.js.map +1 -0
  8. package/dist/src/components/button-dropdown.stories.js +48 -0
  9. package/dist/src/components/button-dropdown.stories.js.map +1 -0
  10. package/dist/src/components/code-editor/fhir-autocomplete.d.ts +70 -0
  11. package/dist/src/components/code-editor/fhir-autocomplete.d.ts.map +1 -0
  12. package/dist/src/components/code-editor/fhir-autocomplete.js +1849 -0
  13. package/dist/src/components/code-editor/fhir-autocomplete.js.map +1 -0
  14. package/dist/src/components/code-editor/fhir-autocomplete.test.js +1099 -0
  15. package/dist/src/components/code-editor/fhir-autocomplete.test.js.map +1 -0
  16. package/dist/src/components/code-editor/http/grammar/http.d.ts +3 -0
  17. package/dist/src/components/code-editor/http/grammar/http.d.ts.map +1 -0
  18. package/dist/src/components/code-editor/http/grammar/http.grammar +74 -0
  19. package/dist/src/components/code-editor/http/grammar/http.js +38 -0
  20. package/dist/src/components/code-editor/http/grammar/http.js.map +1 -0
  21. package/dist/src/components/code-editor/http/grammar/http.terms.d.ts +2 -0
  22. package/dist/src/components/code-editor/http/grammar/http.terms.d.ts.map +1 -0
  23. package/dist/src/components/code-editor/http/grammar/http.terms.js +4 -0
  24. package/dist/src/components/code-editor/http/grammar/http.terms.js.map +1 -0
  25. package/dist/src/components/code-editor/http/grammar/http.test.js +80 -0
  26. package/dist/src/components/code-editor/http/grammar/http.test.js.map +1 -0
  27. package/dist/src/components/code-editor/http/index.d.ts +12 -0
  28. package/dist/src/components/code-editor/http/index.d.ts.map +1 -0
  29. package/dist/src/components/code-editor/http/index.js +486 -0
  30. package/dist/src/components/code-editor/http/index.js.map +1 -0
  31. package/dist/src/components/code-editor/index.d.ts +39 -1
  32. package/dist/src/components/code-editor/index.d.ts.map +1 -1
  33. package/dist/src/components/code-editor/index.js +1472 -45
  34. package/dist/src/components/code-editor/index.js.map +1 -1
  35. package/dist/src/components/code-editor/json-ast.d.ts +46 -0
  36. package/dist/src/components/code-editor/json-ast.d.ts.map +1 -0
  37. package/dist/src/components/code-editor/json-ast.js +465 -0
  38. package/dist/src/components/code-editor/json-ast.js.map +1 -0
  39. package/dist/src/components/code-editor/json-ast.test.js +206 -0
  40. package/dist/src/components/code-editor/json-ast.test.js.map +1 -0
  41. package/dist/src/components/code-editor/sql-completion.d.ts +22 -0
  42. package/dist/src/components/code-editor/sql-completion.d.ts.map +1 -0
  43. package/dist/src/components/code-editor/sql-completion.js +895 -0
  44. package/dist/src/components/code-editor/sql-completion.js.map +1 -0
  45. package/dist/src/components/code-editor.stories.js +280 -3
  46. package/dist/src/components/code-editor.stories.js.map +1 -1
  47. package/dist/src/components/copy-icon.d.ts +5 -1
  48. package/dist/src/components/copy-icon.d.ts.map +1 -1
  49. package/dist/src/components/copy-icon.js +41 -3
  50. package/dist/src/components/copy-icon.js.map +1 -1
  51. package/dist/src/components/data-table.d.ts +9 -0
  52. package/dist/src/components/data-table.d.ts.map +1 -0
  53. package/dist/src/components/data-table.js +66 -0
  54. package/dist/src/components/data-table.js.map +1 -0
  55. package/dist/src/components/data-table.stories.js +240 -0
  56. package/dist/src/components/data-table.stories.js.map +1 -0
  57. package/dist/src/components/date-picker-input.d.ts +10 -0
  58. package/dist/src/components/date-picker-input.d.ts.map +1 -0
  59. package/dist/src/components/date-picker-input.js +90 -0
  60. package/dist/src/components/date-picker-input.js.map +1 -0
  61. package/dist/src/components/date-picker-input.stories.js +76 -0
  62. package/dist/src/components/date-picker-input.stories.js.map +1 -0
  63. package/dist/src/components/fhir-structure-view.d.ts +34 -0
  64. package/dist/src/components/fhir-structure-view.d.ts.map +1 -0
  65. package/dist/src/components/fhir-structure-view.js +230 -0
  66. package/dist/src/components/fhir-structure-view.js.map +1 -0
  67. package/dist/src/components/fhir-structure-view.stories.js +447 -0
  68. package/dist/src/components/fhir-structure-view.stories.js.map +1 -0
  69. package/dist/src/components/icon-button.d.ts +12 -0
  70. package/dist/src/components/icon-button.d.ts.map +1 -0
  71. package/dist/src/components/icon-button.js +41 -0
  72. package/dist/src/components/icon-button.js.map +1 -0
  73. package/dist/src/components/icon-button.stories.js +157 -0
  74. package/dist/src/components/icon-button.stories.js.map +1 -0
  75. package/dist/src/components/operation-outcome-view.d.ts +27 -0
  76. package/dist/src/components/operation-outcome-view.d.ts.map +1 -0
  77. package/dist/src/components/operation-outcome-view.js +198 -0
  78. package/dist/src/components/operation-outcome-view.js.map +1 -0
  79. package/dist/src/components/operation-outcome-view.stories.js +207 -0
  80. package/dist/src/components/operation-outcome-view.stories.js.map +1 -0
  81. package/dist/src/components/request-line-editor.d.ts +13 -35
  82. package/dist/src/components/request-line-editor.d.ts.map +1 -1
  83. package/dist/src/components/request-line-editor.js +73 -49
  84. package/dist/src/components/request-line-editor.js.map +1 -1
  85. package/dist/src/components/request-line-editor.stories.js +17 -53
  86. package/dist/src/components/request-line-editor.stories.js.map +1 -1
  87. package/dist/src/components/sandbox.d.ts +13 -0
  88. package/dist/src/components/sandbox.d.ts.map +1 -0
  89. package/dist/src/components/sandbox.js +107 -0
  90. package/dist/src/components/sandbox.js.map +1 -0
  91. package/dist/src/components/sandbox.stories.js +126 -0
  92. package/dist/src/components/sandbox.stories.js.map +1 -0
  93. package/dist/src/components/segment-control.d.ts +13 -0
  94. package/dist/src/components/segment-control.d.ts.map +1 -0
  95. package/dist/src/components/segment-control.js +33 -0
  96. package/dist/src/components/segment-control.js.map +1 -0
  97. package/dist/src/components/segment-control.stories.js +68 -0
  98. package/dist/src/components/segment-control.stories.js.map +1 -0
  99. package/dist/src/components/split-button.d.ts +12 -0
  100. package/dist/src/components/split-button.d.ts.map +1 -0
  101. package/dist/src/components/split-button.js +33 -0
  102. package/dist/src/components/split-button.js.map +1 -0
  103. package/dist/src/components/split-button.stories.js +84 -0
  104. package/dist/src/components/split-button.stories.js.map +1 -0
  105. package/dist/src/components/tag.d.ts +16 -0
  106. package/dist/src/components/tag.d.ts.map +1 -0
  107. package/dist/src/components/tag.js +198 -0
  108. package/dist/src/components/tag.js.map +1 -0
  109. package/dist/src/components/tag.stories.js +459 -0
  110. package/dist/src/components/tag.stories.js.map +1 -0
  111. package/dist/src/components/tile.d.ts +15 -0
  112. package/dist/src/components/tile.d.ts.map +1 -0
  113. package/dist/src/components/tile.js +76 -0
  114. package/dist/src/components/tile.js.map +1 -0
  115. package/dist/src/components/tile.stories.js +167 -0
  116. package/dist/src/components/tile.stories.js.map +1 -0
  117. package/dist/src/components/toolbar.d.ts +18 -0
  118. package/dist/src/components/toolbar.d.ts.map +1 -0
  119. package/dist/src/components/toolbar.js +61 -0
  120. package/dist/src/components/toolbar.js.map +1 -0
  121. package/dist/src/components/toolbar.stories.js +69 -0
  122. package/dist/src/components/toolbar.stories.js.map +1 -0
  123. package/dist/src/components/tree-view.d.ts +47 -0
  124. package/dist/src/components/tree-view.d.ts.map +1 -0
  125. package/dist/src/components/tree-view.js +122 -0
  126. package/dist/src/components/tree-view.js.map +1 -0
  127. package/dist/src/components/tree-view.stories.js +283 -0
  128. package/dist/src/components/tree-view.stories.js.map +1 -0
  129. package/dist/src/icons.d.ts +11 -0
  130. package/dist/src/icons.d.ts.map +1 -0
  131. package/dist/src/icons.js +328 -0
  132. package/dist/src/icons.js.map +1 -0
  133. package/dist/src/index.css +358 -74
  134. package/dist/src/index.d.ts +17 -1
  135. package/dist/src/index.d.ts.map +1 -1
  136. package/dist/src/index.js +17 -1
  137. package/dist/src/index.js.map +1 -1
  138. package/dist/src/shadcn/components/ui/accordion.d.ts +2 -2
  139. package/dist/src/shadcn/components/ui/accordion.d.ts.map +1 -1
  140. package/dist/src/shadcn/components/ui/accordion.js +35 -9
  141. package/dist/src/shadcn/components/ui/accordion.js.map +1 -1
  142. package/dist/src/shadcn/components/ui/alert-dialog.d.ts +12 -4
  143. package/dist/src/shadcn/components/ui/alert-dialog.d.ts.map +1 -1
  144. package/dist/src/shadcn/components/ui/alert-dialog.js +128 -18
  145. package/dist/src/shadcn/components/ui/alert-dialog.js.map +1 -1
  146. package/dist/src/shadcn/components/ui/alert-dialog.stories.js +269 -19
  147. package/dist/src/shadcn/components/ui/alert-dialog.stories.js.map +1 -1
  148. package/dist/src/shadcn/components/ui/alert.d.ts +29 -6
  149. package/dist/src/shadcn/components/ui/alert.d.ts.map +1 -1
  150. package/dist/src/shadcn/components/ui/alert.js +50 -19
  151. package/dist/src/shadcn/components/ui/alert.js.map +1 -1
  152. package/dist/src/shadcn/components/ui/alert.stories.js +140 -36
  153. package/dist/src/shadcn/components/ui/alert.stories.js.map +1 -1
  154. package/dist/src/shadcn/components/ui/aspect-ratio.d.ts.map +1 -1
  155. package/dist/src/shadcn/components/ui/aspect-ratio.js +1 -0
  156. package/dist/src/shadcn/components/ui/aspect-ratio.js.map +1 -1
  157. package/dist/src/shadcn/components/ui/avatar.d.ts.map +1 -1
  158. package/dist/src/shadcn/components/ui/avatar.js +4 -3
  159. package/dist/src/shadcn/components/ui/avatar.js.map +1 -1
  160. package/dist/src/shadcn/components/ui/avatar.stories.js +68 -2
  161. package/dist/src/shadcn/components/ui/avatar.stories.js.map +1 -1
  162. package/dist/src/shadcn/components/ui/badge.d.ts +1 -1
  163. package/dist/src/shadcn/components/ui/badge.d.ts.map +1 -1
  164. package/dist/src/shadcn/components/ui/badge.js +16 -5
  165. package/dist/src/shadcn/components/ui/badge.js.map +1 -1
  166. package/dist/src/shadcn/components/ui/breadcrumb.d.ts +5 -2
  167. package/dist/src/shadcn/components/ui/breadcrumb.d.ts.map +1 -1
  168. package/dist/src/shadcn/components/ui/breadcrumb.js +98 -13
  169. package/dist/src/shadcn/components/ui/breadcrumb.js.map +1 -1
  170. package/dist/src/shadcn/components/ui/breadcrumb.stories.js +205 -45
  171. package/dist/src/shadcn/components/ui/breadcrumb.stories.js.map +1 -1
  172. package/dist/src/shadcn/components/ui/button.d.ts.map +1 -1
  173. package/dist/src/shadcn/components/ui/button.js +65 -11
  174. package/dist/src/shadcn/components/ui/button.js.map +1 -1
  175. package/dist/src/shadcn/components/ui/button.stories.js +99 -17
  176. package/dist/src/shadcn/components/ui/button.stories.js.map +1 -1
  177. package/dist/src/shadcn/components/ui/calendar.d.ts +1 -1
  178. package/dist/src/shadcn/components/ui/calendar.d.ts.map +1 -1
  179. package/dist/src/shadcn/components/ui/calendar.js +1 -0
  180. package/dist/src/shadcn/components/ui/calendar.js.map +1 -1
  181. package/dist/src/shadcn/components/ui/card.d.ts +5 -1
  182. package/dist/src/shadcn/components/ui/card.d.ts.map +1 -1
  183. package/dist/src/shadcn/components/ui/card.js +28 -7
  184. package/dist/src/shadcn/components/ui/card.js.map +1 -1
  185. package/dist/src/shadcn/components/ui/card.stories.js +23 -2
  186. package/dist/src/shadcn/components/ui/card.stories.js.map +1 -1
  187. package/dist/src/shadcn/components/ui/carousel.d.ts +1 -1
  188. package/dist/src/shadcn/components/ui/carousel.d.ts.map +1 -1
  189. package/dist/src/shadcn/components/ui/carousel.js +1 -0
  190. package/dist/src/shadcn/components/ui/carousel.js.map +1 -1
  191. package/dist/src/shadcn/components/ui/chart.d.ts +5 -5
  192. package/dist/src/shadcn/components/ui/chart.d.ts.map +1 -1
  193. package/dist/src/shadcn/components/ui/chart.js +4 -3
  194. package/dist/src/shadcn/components/ui/chart.js.map +1 -1
  195. package/dist/src/shadcn/components/ui/checkbox.d.ts +5 -1
  196. package/dist/src/shadcn/components/ui/checkbox.d.ts.map +1 -1
  197. package/dist/src/shadcn/components/ui/checkbox.js +46 -6
  198. package/dist/src/shadcn/components/ui/checkbox.js.map +1 -1
  199. package/dist/src/shadcn/components/ui/checkbox.stories.js +156 -46
  200. package/dist/src/shadcn/components/ui/checkbox.stories.js.map +1 -1
  201. package/dist/src/shadcn/components/ui/combobox.d.ts +29 -0
  202. package/dist/src/shadcn/components/ui/combobox.d.ts.map +1 -0
  203. package/dist/src/shadcn/components/ui/combobox.js +226 -0
  204. package/dist/src/shadcn/components/ui/combobox.js.map +1 -0
  205. package/dist/src/shadcn/components/ui/combobox.stories.js +167 -0
  206. package/dist/src/shadcn/components/ui/combobox.stories.js.map +1 -0
  207. package/dist/src/shadcn/components/ui/command.d.ts +4 -2
  208. package/dist/src/shadcn/components/ui/command.d.ts.map +1 -1
  209. package/dist/src/shadcn/components/ui/command.js +75 -13
  210. package/dist/src/shadcn/components/ui/command.js.map +1 -1
  211. package/dist/src/shadcn/components/ui/command.stories.js +277 -57
  212. package/dist/src/shadcn/components/ui/command.stories.js.map +1 -1
  213. package/dist/src/shadcn/components/ui/context-menu.d.ts +7 -3
  214. package/dist/src/shadcn/components/ui/context-menu.d.ts.map +1 -1
  215. package/dist/src/shadcn/components/ui/context-menu.js +120 -13
  216. package/dist/src/shadcn/components/ui/context-menu.js.map +1 -1
  217. package/dist/src/shadcn/components/ui/dialog.d.ts.map +1 -1
  218. package/dist/src/shadcn/components/ui/dialog.js +35 -7
  219. package/dist/src/shadcn/components/ui/dialog.js.map +1 -1
  220. package/dist/src/shadcn/components/ui/drawer.d.ts.map +1 -1
  221. package/dist/src/shadcn/components/ui/drawer.js +27 -5
  222. package/dist/src/shadcn/components/ui/drawer.js.map +1 -1
  223. package/dist/src/shadcn/components/ui/dropdown-menu.d.ts +7 -3
  224. package/dist/src/shadcn/components/ui/dropdown-menu.d.ts.map +1 -1
  225. package/dist/src/shadcn/components/ui/dropdown-menu.js +122 -14
  226. package/dist/src/shadcn/components/ui/dropdown-menu.js.map +1 -1
  227. package/dist/src/shadcn/components/ui/dropdown-menu.stories.js +22 -5
  228. package/dist/src/shadcn/components/ui/dropdown-menu.stories.js.map +1 -1
  229. package/dist/src/shadcn/components/ui/form.d.ts +2 -2
  230. package/dist/src/shadcn/components/ui/form.d.ts.map +1 -1
  231. package/dist/src/shadcn/components/ui/form.js +17 -8
  232. package/dist/src/shadcn/components/ui/form.js.map +1 -1
  233. package/dist/src/shadcn/components/ui/hover-card.d.ts.map +1 -1
  234. package/dist/src/shadcn/components/ui/hover-card.js +2 -1
  235. package/dist/src/shadcn/components/ui/hover-card.js.map +1 -1
  236. package/dist/src/shadcn/components/ui/input-otp.d.ts.map +1 -1
  237. package/dist/src/shadcn/components/ui/input-otp.js +1 -0
  238. package/dist/src/shadcn/components/ui/input-otp.js.map +1 -1
  239. package/dist/src/shadcn/components/ui/input.d.ts +3 -1
  240. package/dist/src/shadcn/components/ui/input.d.ts.map +1 -1
  241. package/dist/src/shadcn/components/ui/input.js +126 -17
  242. package/dist/src/shadcn/components/ui/input.js.map +1 -1
  243. package/dist/src/shadcn/components/ui/input.stories.js +218 -29
  244. package/dist/src/shadcn/components/ui/input.stories.js.map +1 -1
  245. package/dist/src/shadcn/components/ui/label.d.ts.map +1 -1
  246. package/dist/src/shadcn/components/ui/label.js +9 -1
  247. package/dist/src/shadcn/components/ui/label.js.map +1 -1
  248. package/dist/src/shadcn/components/ui/menubar.d.ts.map +1 -1
  249. package/dist/src/shadcn/components/ui/menubar.js +35 -13
  250. package/dist/src/shadcn/components/ui/menubar.js.map +1 -1
  251. package/dist/src/shadcn/components/ui/pagination.d.ts +9 -2
  252. package/dist/src/shadcn/components/ui/pagination.d.ts.map +1 -1
  253. package/dist/src/shadcn/components/ui/pagination.js +41 -24
  254. package/dist/src/shadcn/components/ui/pagination.js.map +1 -1
  255. package/dist/src/shadcn/components/ui/pagination.stories.js +44 -37
  256. package/dist/src/shadcn/components/ui/pagination.stories.js.map +1 -1
  257. package/dist/src/shadcn/components/ui/popover.d.ts.map +1 -1
  258. package/dist/src/shadcn/components/ui/popover.js +13 -1
  259. package/dist/src/shadcn/components/ui/popover.js.map +1 -1
  260. package/dist/src/shadcn/components/ui/progress.d.ts.map +1 -1
  261. package/dist/src/shadcn/components/ui/progress.js +6 -2
  262. package/dist/src/shadcn/components/ui/progress.js.map +1 -1
  263. package/dist/src/shadcn/components/ui/radio-button-group.d.ts +21 -0
  264. package/dist/src/shadcn/components/ui/radio-button-group.d.ts.map +1 -0
  265. package/dist/src/shadcn/components/ui/radio-button-group.js +148 -0
  266. package/dist/src/shadcn/components/ui/radio-button-group.js.map +1 -0
  267. package/dist/src/shadcn/components/ui/radio-button-group.stories.js +283 -0
  268. package/dist/src/shadcn/components/ui/radio-button-group.stories.js.map +1 -0
  269. package/dist/src/shadcn/components/ui/radio-group.d.ts +5 -1
  270. package/dist/src/shadcn/components/ui/radio-group.d.ts.map +1 -1
  271. package/dist/src/shadcn/components/ui/radio-group.js +40 -7
  272. package/dist/src/shadcn/components/ui/radio-group.js.map +1 -1
  273. package/dist/src/shadcn/components/ui/radio-group.stories.js +107 -32
  274. package/dist/src/shadcn/components/ui/radio-group.stories.js.map +1 -1
  275. package/dist/src/shadcn/components/ui/resizable.d.ts.map +1 -1
  276. package/dist/src/shadcn/components/ui/resizable.js +2 -1
  277. package/dist/src/shadcn/components/ui/resizable.js.map +1 -1
  278. package/dist/src/shadcn/components/ui/resizable.stories.js +2 -2
  279. package/dist/src/shadcn/components/ui/resizable.stories.js.map +1 -1
  280. package/dist/src/shadcn/components/ui/scroll-area.d.ts.map +1 -1
  281. package/dist/src/shadcn/components/ui/scroll-area.js +10 -3
  282. package/dist/src/shadcn/components/ui/scroll-area.js.map +1 -1
  283. package/dist/src/shadcn/components/ui/select.d.ts +1 -2
  284. package/dist/src/shadcn/components/ui/select.d.ts.map +1 -1
  285. package/dist/src/shadcn/components/ui/select.js +49 -19
  286. package/dist/src/shadcn/components/ui/select.js.map +1 -1
  287. package/dist/src/shadcn/components/ui/select.stories.js +193 -70
  288. package/dist/src/shadcn/components/ui/select.stories.js.map +1 -1
  289. package/dist/src/shadcn/components/ui/separator.d.ts.map +1 -1
  290. package/dist/src/shadcn/components/ui/separator.js +8 -1
  291. package/dist/src/shadcn/components/ui/separator.js.map +1 -1
  292. package/dist/src/shadcn/components/ui/sheet.js +1 -1
  293. package/dist/src/shadcn/components/ui/sheet.js.map +1 -1
  294. package/dist/src/shadcn/components/ui/sidebar.d.ts +4 -4
  295. package/dist/src/shadcn/components/ui/sidebar.d.ts.map +1 -1
  296. package/dist/src/shadcn/components/ui/sidebar.js +21 -6
  297. package/dist/src/shadcn/components/ui/sidebar.js.map +1 -1
  298. package/dist/src/shadcn/components/ui/skeleton.d.ts.map +1 -1
  299. package/dist/src/shadcn/components/ui/skeleton.js +3 -1
  300. package/dist/src/shadcn/components/ui/skeleton.js.map +1 -1
  301. package/dist/src/shadcn/components/ui/slider.d.ts.map +1 -1
  302. package/dist/src/shadcn/components/ui/slider.js +35 -4
  303. package/dist/src/shadcn/components/ui/slider.js.map +1 -1
  304. package/dist/src/shadcn/components/ui/sonner.d.ts +24 -2
  305. package/dist/src/shadcn/components/ui/sonner.d.ts.map +1 -1
  306. package/dist/src/shadcn/components/ui/sonner.js +127 -9
  307. package/dist/src/shadcn/components/ui/sonner.js.map +1 -1
  308. package/dist/src/shadcn/components/ui/sonner.stories.js +251 -12
  309. package/dist/src/shadcn/components/ui/sonner.stories.js.map +1 -1
  310. package/dist/src/shadcn/components/ui/switch.d.ts +7 -1
  311. package/dist/src/shadcn/components/ui/switch.d.ts.map +1 -1
  312. package/dist/src/shadcn/components/ui/switch.js +55 -3
  313. package/dist/src/shadcn/components/ui/switch.js.map +1 -1
  314. package/dist/src/shadcn/components/ui/switch.stories.js +84 -9
  315. package/dist/src/shadcn/components/ui/switch.stories.js.map +1 -1
  316. package/dist/src/shadcn/components/ui/table.d.ts +23 -6
  317. package/dist/src/shadcn/components/ui/table.d.ts.map +1 -1
  318. package/dist/src/shadcn/components/ui/table.js +65 -20
  319. package/dist/src/shadcn/components/ui/table.js.map +1 -1
  320. package/dist/src/shadcn/components/ui/table.stories.js +217 -97
  321. package/dist/src/shadcn/components/ui/table.stories.js.map +1 -1
  322. package/dist/src/shadcn/components/ui/tabs.d.ts +30 -5
  323. package/dist/src/shadcn/components/ui/tabs.d.ts.map +1 -1
  324. package/dist/src/shadcn/components/ui/tabs.js +470 -23
  325. package/dist/src/shadcn/components/ui/tabs.js.map +1 -1
  326. package/dist/src/shadcn/components/ui/tabs.stories.js +405 -181
  327. package/dist/src/shadcn/components/ui/tabs.stories.js.map +1 -1
  328. package/dist/src/shadcn/components/ui/textarea.d.ts +8 -1
  329. package/dist/src/shadcn/components/ui/textarea.d.ts.map +1 -1
  330. package/dist/src/shadcn/components/ui/textarea.js +30 -2
  331. package/dist/src/shadcn/components/ui/textarea.js.map +1 -1
  332. package/dist/src/shadcn/components/ui/textarea.stories.js +85 -4
  333. package/dist/src/shadcn/components/ui/textarea.stories.js.map +1 -1
  334. package/dist/src/shadcn/components/ui/toggle-group.d.ts +3 -3
  335. package/dist/src/shadcn/components/ui/toggle-group.d.ts.map +1 -1
  336. package/dist/src/shadcn/components/ui/toggle-group.js +14 -12
  337. package/dist/src/shadcn/components/ui/toggle-group.js.map +1 -1
  338. package/dist/src/shadcn/components/ui/toggle.d.ts +3 -4
  339. package/dist/src/shadcn/components/ui/toggle.d.ts.map +1 -1
  340. package/dist/src/shadcn/components/ui/toggle.js +44 -16
  341. package/dist/src/shadcn/components/ui/toggle.js.map +1 -1
  342. package/dist/src/shadcn/components/ui/toggle.stories.js +130 -7
  343. package/dist/src/shadcn/components/ui/toggle.stories.js.map +1 -1
  344. package/dist/src/shadcn/components/ui/tooltip.d.ts.map +1 -1
  345. package/dist/src/shadcn/components/ui/tooltip.js +12 -1
  346. package/dist/src/shadcn/components/ui/tooltip.js.map +1 -1
  347. package/dist/src/shadcn/components/ui/tree.d.ts +29 -0
  348. package/dist/src/shadcn/components/ui/tree.d.ts.map +1 -0
  349. package/dist/src/shadcn/components/ui/tree.js +135 -0
  350. package/dist/src/shadcn/components/ui/tree.js.map +1 -0
  351. package/dist/src/shadcn/shadcn.css +4 -4
  352. package/dist/src/tokens.css +31 -14
  353. package/dist/src/typography.css +78 -15
  354. package/package.json +84 -64
  355. package/src/components/button-dropdown.stories.tsx +41 -0
  356. package/src/components/button-dropdown.tsx +97 -0
  357. package/src/components/code-editor/fhir-autocomplete.test.ts +993 -0
  358. package/src/components/code-editor/fhir-autocomplete.ts +2321 -0
  359. package/src/components/code-editor/http/grammar/http.grammar +74 -0
  360. package/src/components/code-editor/http/grammar/http.terms.ts +9 -0
  361. package/src/components/code-editor/http/grammar/http.test.ts +110 -0
  362. package/src/components/code-editor/http/grammar/http.ts +21 -0
  363. package/src/components/code-editor/http/index.ts +424 -0
  364. package/src/components/code-editor/index.tsx +1626 -42
  365. package/src/components/code-editor/json-ast.test.ts +230 -0
  366. package/src/components/code-editor/json-ast.ts +590 -0
  367. package/src/components/code-editor/sql-completion.ts +1105 -0
  368. package/src/components/code-editor.stories.tsx +325 -2
  369. package/src/components/copy-icon.tsx +57 -3
  370. package/src/components/data-table.stories.tsx +91 -0
  371. package/src/components/data-table.tsx +126 -0
  372. package/src/components/date-picker-input.stories.tsx +79 -0
  373. package/src/components/date-picker-input.tsx +104 -0
  374. package/src/components/fhir-structure-view.stories.tsx +439 -0
  375. package/src/components/fhir-structure-view.tsx +233 -0
  376. package/src/components/icon-button.stories.tsx +86 -0
  377. package/src/components/icon-button.tsx +57 -0
  378. package/src/components/operation-outcome-view.stories.tsx +163 -0
  379. package/src/components/operation-outcome-view.tsx +253 -0
  380. package/src/components/request-line-editor.stories.tsx +17 -27
  381. package/src/components/request-line-editor.tsx +103 -61
  382. package/src/components/sandbox.stories.tsx +131 -0
  383. package/src/components/sandbox.tsx +191 -0
  384. package/src/components/segment-control.stories.tsx +61 -0
  385. package/src/components/segment-control.tsx +83 -0
  386. package/src/components/split-button.stories.tsx +68 -0
  387. package/src/components/split-button.tsx +74 -0
  388. package/src/components/tag.stories.tsx +371 -0
  389. package/src/components/tag.tsx +236 -0
  390. package/src/components/tile.stories.tsx +149 -0
  391. package/src/components/tile.tsx +105 -0
  392. package/src/components/toolbar.stories.tsx +64 -0
  393. package/src/components/toolbar.tsx +98 -0
  394. package/src/components/tree-view.stories.tsx +265 -0
  395. package/src/components/tree-view.tsx +246 -0
  396. package/src/icons.tsx +331 -0
  397. package/src/index.css +358 -74
  398. package/src/index.tsx +17 -3
  399. package/src/shadcn/components/ui/accordion.tsx +91 -10
  400. package/src/shadcn/components/ui/alert-dialog.stories.tsx +209 -15
  401. package/src/shadcn/components/ui/alert-dialog.tsx +236 -26
  402. package/src/shadcn/components/ui/alert.stories.tsx +120 -21
  403. package/src/shadcn/components/ui/alert.tsx +125 -28
  404. package/src/shadcn/components/ui/aspect-ratio.tsx +1 -0
  405. package/src/shadcn/components/ui/avatar.stories.tsx +74 -1
  406. package/src/shadcn/components/ui/avatar.tsx +22 -6
  407. package/src/shadcn/components/ui/badge.tsx +67 -18
  408. package/src/shadcn/components/ui/breadcrumb.stories.tsx +161 -41
  409. package/src/shadcn/components/ui/breadcrumb.tsx +172 -23
  410. package/src/shadcn/components/ui/button.stories.tsx +106 -18
  411. package/src/shadcn/components/ui/button.tsx +151 -55
  412. package/src/shadcn/components/ui/calendar.tsx +1 -0
  413. package/src/shadcn/components/ui/card.stories.tsx +17 -3
  414. package/src/shadcn/components/ui/card.tsx +89 -14
  415. package/src/shadcn/components/ui/carousel.tsx +1 -0
  416. package/src/shadcn/components/ui/chart.tsx +9 -5
  417. package/src/shadcn/components/ui/checkbox.stories.tsx +78 -30
  418. package/src/shadcn/components/ui/checkbox.tsx +91 -8
  419. package/src/shadcn/components/ui/combobox.stories.tsx +148 -0
  420. package/src/shadcn/components/ui/combobox.tsx +324 -0
  421. package/src/shadcn/components/ui/command.stories.tsx +184 -39
  422. package/src/shadcn/components/ui/command.tsx +218 -37
  423. package/src/shadcn/components/ui/context-menu.tsx +333 -40
  424. package/src/shadcn/components/ui/dialog.tsx +101 -13
  425. package/src/shadcn/components/ui/drawer.tsx +94 -18
  426. package/src/shadcn/components/ui/dropdown-menu.stories.tsx +18 -2
  427. package/src/shadcn/components/ui/dropdown-menu.tsx +334 -68
  428. package/src/shadcn/components/ui/form.tsx +22 -11
  429. package/src/shadcn/components/ui/hover-card.tsx +2 -1
  430. package/src/shadcn/components/ui/input-otp.tsx +1 -0
  431. package/src/shadcn/components/ui/input.stories.tsx +235 -27
  432. package/src/shadcn/components/ui/input.tsx +400 -29
  433. package/src/shadcn/components/ui/label.tsx +22 -4
  434. package/src/shadcn/components/ui/menubar.tsx +188 -43
  435. package/src/shadcn/components/ui/pagination.stories.tsx +8 -2
  436. package/src/shadcn/components/ui/pagination.tsx +65 -8
  437. package/src/shadcn/components/ui/popover.tsx +36 -4
  438. package/src/shadcn/components/ui/progress.tsx +21 -5
  439. package/src/shadcn/components/ui/radio-button-group.stories.tsx +247 -0
  440. package/src/shadcn/components/ui/radio-button-group.tsx +188 -0
  441. package/src/shadcn/components/ui/radio-group.stories.tsx +70 -14
  442. package/src/shadcn/components/ui/radio-group.tsx +85 -9
  443. package/src/shadcn/components/ui/resizable.stories.tsx +2 -2
  444. package/src/shadcn/components/ui/resizable.tsx +2 -1
  445. package/src/shadcn/components/ui/scroll-area.tsx +34 -5
  446. package/src/shadcn/components/ui/select.stories.tsx +108 -32
  447. package/src/shadcn/components/ui/select.tsx +182 -36
  448. package/src/shadcn/components/ui/separator.tsx +16 -5
  449. package/src/shadcn/components/ui/sheet.tsx +1 -1
  450. package/src/shadcn/components/ui/sidebar.tsx +69 -26
  451. package/src/shadcn/components/ui/skeleton.tsx +4 -1
  452. package/src/shadcn/components/ui/slider.tsx +83 -11
  453. package/src/shadcn/components/ui/sonner.stories.tsx +238 -17
  454. package/src/shadcn/components/ui/sonner.tsx +254 -11
  455. package/src/shadcn/components/ui/switch.stories.tsx +52 -5
  456. package/src/shadcn/components/ui/switch.tsx +92 -7
  457. package/src/shadcn/components/ui/table.stories.tsx +252 -72
  458. package/src/shadcn/components/ui/table.tsx +204 -26
  459. package/src/shadcn/components/ui/tabs.stories.tsx +235 -123
  460. package/src/shadcn/components/ui/tabs.tsx +694 -36
  461. package/src/shadcn/components/ui/textarea.stories.tsx +94 -2
  462. package/src/shadcn/components/ui/textarea.tsx +70 -5
  463. package/src/shadcn/components/ui/toggle-group.tsx +35 -13
  464. package/src/shadcn/components/ui/toggle.stories.tsx +92 -5
  465. package/src/shadcn/components/ui/toggle.tsx +96 -23
  466. package/src/shadcn/components/ui/tooltip.tsx +34 -8
  467. package/src/shadcn/components/ui/tree.tsx +257 -0
  468. package/src/shadcn/shadcn.css +4 -4
  469. package/src/tokens.css +31 -14
  470. package/src/typography.css +78 -15
  471. package/dist/src/components/code-editor.stories.d.ts +0 -7
  472. package/dist/src/components/code-editor.stories.d.ts.map +0 -1
  473. package/dist/src/components/request-line-editor.stories.d.ts +0 -11
  474. package/dist/src/components/request-line-editor.stories.d.ts.map +0 -1
  475. package/dist/src/index.stories.d.ts +0 -14
  476. package/dist/src/index.stories.d.ts.map +0 -1
  477. package/dist/src/index.stories.js +0 -19
  478. package/dist/src/index.stories.js.map +0 -1
  479. package/dist/src/shadcn/components/ui/accordion.stories.d.ts +0 -8
  480. package/dist/src/shadcn/components/ui/accordion.stories.d.ts.map +0 -1
  481. package/dist/src/shadcn/components/ui/alert-dialog.stories.d.ts +0 -8
  482. package/dist/src/shadcn/components/ui/alert-dialog.stories.d.ts.map +0 -1
  483. package/dist/src/shadcn/components/ui/alert.stories.d.ts +0 -8
  484. package/dist/src/shadcn/components/ui/alert.stories.d.ts.map +0 -1
  485. package/dist/src/shadcn/components/ui/aspect-ratio.stories.d.ts +0 -8
  486. package/dist/src/shadcn/components/ui/aspect-ratio.stories.d.ts.map +0 -1
  487. package/dist/src/shadcn/components/ui/avatar.stories.d.ts +0 -8
  488. package/dist/src/shadcn/components/ui/avatar.stories.d.ts.map +0 -1
  489. package/dist/src/shadcn/components/ui/badge.stories.d.ts +0 -8
  490. package/dist/src/shadcn/components/ui/badge.stories.d.ts.map +0 -1
  491. package/dist/src/shadcn/components/ui/breadcrumb.stories.d.ts +0 -8
  492. package/dist/src/shadcn/components/ui/breadcrumb.stories.d.ts.map +0 -1
  493. package/dist/src/shadcn/components/ui/button.stories.d.ts +0 -23
  494. package/dist/src/shadcn/components/ui/button.stories.d.ts.map +0 -1
  495. package/dist/src/shadcn/components/ui/calendar.stories.d.ts +0 -8
  496. package/dist/src/shadcn/components/ui/calendar.stories.d.ts.map +0 -1
  497. package/dist/src/shadcn/components/ui/card.stories.d.ts +0 -8
  498. package/dist/src/shadcn/components/ui/card.stories.d.ts.map +0 -1
  499. package/dist/src/shadcn/components/ui/carousel.stories.d.ts +0 -8
  500. package/dist/src/shadcn/components/ui/carousel.stories.d.ts.map +0 -1
  501. package/dist/src/shadcn/components/ui/chart.stories.d.ts +0 -8
  502. package/dist/src/shadcn/components/ui/chart.stories.d.ts.map +0 -1
  503. package/dist/src/shadcn/components/ui/checkbox.stories.d.ts +0 -8
  504. package/dist/src/shadcn/components/ui/checkbox.stories.d.ts.map +0 -1
  505. package/dist/src/shadcn/components/ui/collapsible.stories.d.ts +0 -8
  506. package/dist/src/shadcn/components/ui/collapsible.stories.d.ts.map +0 -1
  507. package/dist/src/shadcn/components/ui/command.stories.d.ts +0 -8
  508. package/dist/src/shadcn/components/ui/command.stories.d.ts.map +0 -1
  509. package/dist/src/shadcn/components/ui/context-menu.stories.d.ts +0 -8
  510. package/dist/src/shadcn/components/ui/context-menu.stories.d.ts.map +0 -1
  511. package/dist/src/shadcn/components/ui/dialog.stories.d.ts +0 -8
  512. package/dist/src/shadcn/components/ui/dialog.stories.d.ts.map +0 -1
  513. package/dist/src/shadcn/components/ui/drawer.stories.d.ts +0 -8
  514. package/dist/src/shadcn/components/ui/drawer.stories.d.ts.map +0 -1
  515. package/dist/src/shadcn/components/ui/dropdown-menu.stories.d.ts +0 -8
  516. package/dist/src/shadcn/components/ui/dropdown-menu.stories.d.ts.map +0 -1
  517. package/dist/src/shadcn/components/ui/form.stories.d.ts +0 -8
  518. package/dist/src/shadcn/components/ui/form.stories.d.ts.map +0 -1
  519. package/dist/src/shadcn/components/ui/hover-card.stories.d.ts +0 -8
  520. package/dist/src/shadcn/components/ui/hover-card.stories.d.ts.map +0 -1
  521. package/dist/src/shadcn/components/ui/input-otp.stories.d.ts +0 -8
  522. package/dist/src/shadcn/components/ui/input-otp.stories.d.ts.map +0 -1
  523. package/dist/src/shadcn/components/ui/input.stories.d.ts +0 -18
  524. package/dist/src/shadcn/components/ui/input.stories.d.ts.map +0 -1
  525. package/dist/src/shadcn/components/ui/label.stories.d.ts +0 -8
  526. package/dist/src/shadcn/components/ui/label.stories.d.ts.map +0 -1
  527. package/dist/src/shadcn/components/ui/menubar.stories.d.ts +0 -8
  528. package/dist/src/shadcn/components/ui/menubar.stories.d.ts.map +0 -1
  529. package/dist/src/shadcn/components/ui/navigation-menu.stories.d.ts +0 -8
  530. package/dist/src/shadcn/components/ui/navigation-menu.stories.d.ts.map +0 -1
  531. package/dist/src/shadcn/components/ui/pagination.stories.d.ts +0 -8
  532. package/dist/src/shadcn/components/ui/pagination.stories.d.ts.map +0 -1
  533. package/dist/src/shadcn/components/ui/popover.stories.d.ts +0 -8
  534. package/dist/src/shadcn/components/ui/popover.stories.d.ts.map +0 -1
  535. package/dist/src/shadcn/components/ui/progress.stories.d.ts +0 -8
  536. package/dist/src/shadcn/components/ui/progress.stories.d.ts.map +0 -1
  537. package/dist/src/shadcn/components/ui/radio-group.stories.d.ts +0 -8
  538. package/dist/src/shadcn/components/ui/radio-group.stories.d.ts.map +0 -1
  539. package/dist/src/shadcn/components/ui/resizable.stories.d.ts +0 -8
  540. package/dist/src/shadcn/components/ui/resizable.stories.d.ts.map +0 -1
  541. package/dist/src/shadcn/components/ui/scroll-area.stories.d.ts +0 -8
  542. package/dist/src/shadcn/components/ui/scroll-area.stories.d.ts.map +0 -1
  543. package/dist/src/shadcn/components/ui/select.stories.d.ts +0 -11
  544. package/dist/src/shadcn/components/ui/select.stories.d.ts.map +0 -1
  545. package/dist/src/shadcn/components/ui/separator.stories.d.ts +0 -8
  546. package/dist/src/shadcn/components/ui/separator.stories.d.ts.map +0 -1
  547. package/dist/src/shadcn/components/ui/sheet.stories.d.ts +0 -8
  548. package/dist/src/shadcn/components/ui/sheet.stories.d.ts.map +0 -1
  549. package/dist/src/shadcn/components/ui/sidebar.stories.d.ts +0 -11
  550. package/dist/src/shadcn/components/ui/sidebar.stories.d.ts.map +0 -1
  551. package/dist/src/shadcn/components/ui/skeleton.stories.d.ts +0 -8
  552. package/dist/src/shadcn/components/ui/skeleton.stories.d.ts.map +0 -1
  553. package/dist/src/shadcn/components/ui/slider.stories.d.ts +0 -8
  554. package/dist/src/shadcn/components/ui/slider.stories.d.ts.map +0 -1
  555. package/dist/src/shadcn/components/ui/sonner.stories.d.ts +0 -8
  556. package/dist/src/shadcn/components/ui/sonner.stories.d.ts.map +0 -1
  557. package/dist/src/shadcn/components/ui/switch.stories.d.ts +0 -8
  558. package/dist/src/shadcn/components/ui/switch.stories.d.ts.map +0 -1
  559. package/dist/src/shadcn/components/ui/table.stories.d.ts +0 -8
  560. package/dist/src/shadcn/components/ui/table.stories.d.ts.map +0 -1
  561. package/dist/src/shadcn/components/ui/tabs.stories.d.ts +0 -32
  562. package/dist/src/shadcn/components/ui/tabs.stories.d.ts.map +0 -1
  563. package/dist/src/shadcn/components/ui/textarea.stories.d.ts +0 -8
  564. package/dist/src/shadcn/components/ui/textarea.stories.d.ts.map +0 -1
  565. package/dist/src/shadcn/components/ui/toggle-group.stories.d.ts +0 -8
  566. package/dist/src/shadcn/components/ui/toggle-group.stories.d.ts.map +0 -1
  567. package/dist/src/shadcn/components/ui/toggle.stories.d.ts +0 -8
  568. package/dist/src/shadcn/components/ui/toggle.stories.d.ts.map +0 -1
  569. package/dist/src/shadcn/components/ui/tooltip.stories.d.ts +0 -8
  570. package/dist/src/shadcn/components/ui/tooltip.stories.d.ts.map +0 -1
  571. package/src/index.stories.tsx +0 -21
@@ -1,11 +1,17 @@
1
1
  import {
2
+ acceptCompletion,
2
3
  autocompletion,
4
+ type Completion,
3
5
  closeBrackets,
4
6
  closeBracketsKeymap,
5
7
  completionKeymap,
8
+ completionStatus,
9
+ moveCompletionSelection,
6
10
  } from "@codemirror/autocomplete";
7
11
  import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
8
12
  import { json, jsonParseLinter } from "@codemirror/lang-json";
13
+ import { SQLDialect, sql } from "@codemirror/lang-sql";
14
+ import { yaml } from "@codemirror/lang-yaml";
9
15
  import {
10
16
  bracketMatching,
11
17
  foldGutter,
@@ -13,36 +19,441 @@ import {
13
19
  HighlightStyle,
14
20
  indentOnInput,
15
21
  syntaxHighlighting,
22
+ syntaxTree,
16
23
  } from "@codemirror/language";
17
- import { linter, lintGutter, lintKeymap } from "@codemirror/lint";
18
- import { highlightSelectionMatches, searchKeymap } from "@codemirror/search";
19
- import { EditorState } from "@codemirror/state";
24
+ import { linter, lintKeymap } from "@codemirror/lint";
25
+ import {
26
+ closeSearchPanel,
27
+ findNext,
28
+ findPrevious,
29
+ getSearchQuery,
30
+ highlightSelectionMatches,
31
+ SearchQuery,
32
+ search,
33
+ searchKeymap,
34
+ setSearchQuery,
35
+ } from "@codemirror/search";
36
+ import {
37
+ Compartment,
38
+ EditorState,
39
+ type Extension,
40
+ Prec,
41
+ RangeSet,
42
+ StateEffect,
43
+ StateField,
44
+ } from "@codemirror/state";
20
45
  import {
21
46
  crosshairCursor,
47
+ Decoration,
22
48
  drawSelection,
23
49
  dropCursor,
24
50
  EditorView,
25
- highlightActiveLine,
26
- highlightActiveLineGutter,
51
+ GutterMarker,
52
+ gutterLineClass,
27
53
  highlightSpecialChars,
28
54
  keymap,
29
55
  lineNumbers,
30
56
  rectangularSelection,
57
+ type ViewUpdate,
31
58
  } from "@codemirror/view";
32
59
  import { tags } from "@lezer/highlight";
60
+ import { vim } from "@replit/codemirror-vim";
61
+ import {
62
+ ChevronDown,
63
+ ChevronsRight,
64
+ ChevronUp,
65
+ Heading,
66
+ Table2,
67
+ Terminal,
68
+ X,
69
+ } from "lucide-react";
33
70
  import * as React from "react";
71
+ import { flushSync } from "react-dom";
72
+ import { createRoot } from "react-dom/client";
73
+ import {
74
+ ComplexTypeIcon,
75
+ ResourceIcon,
76
+ SquareFunctionIcon,
77
+ TypCodeIcon,
78
+ } from "../../icons";
79
+ import {
80
+ buildFhirCompletionExtension,
81
+ type ExpandValueSet,
82
+ fhirDiagnosticsField,
83
+ type GetStructureDefinitions,
84
+ } from "./fhir-autocomplete";
85
+ import { type GetUrlSuggestions, http } from "./http";
86
+ import {
87
+ buildSqlCompletionExtensions,
88
+ fetchSqlMetadata,
89
+ type SqlConfig,
90
+ } from "./sql-completion";
91
+
92
+ // --- Issue lines: gutter highlighting, line background, hover tooltip ---
93
+
94
+ type IssueLine = { line: number; message?: string };
95
+
96
+ class ErrorLineGutterMarker extends GutterMarker {
97
+ elementClass = "cm-errorLineGutter";
98
+ }
99
+ const errorLineMarker = new ErrorLineGutterMarker();
100
+ const errorLineDecoration = Decoration.line({ class: "cm-errorLine" });
101
+
102
+ const setIssueLinesEffect = StateEffect.define<IssueLine[]>();
103
+
104
+ let errorTooltipEl: HTMLDivElement | null = null;
105
+
106
+ function formatErrorTypeTitle(code: string): string {
107
+ return code
108
+ .split("-")
109
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
110
+ .join(" ");
111
+ }
112
+
113
+ function renderErrorCard(msg: string): HTMLElement {
114
+ const card = document.createElement("div");
115
+ Object.assign(card.style, {
116
+ backgroundColor: "var(--color-bg-primary)",
117
+ border: "1px solid var(--color-border-primary)",
118
+ borderRadius: "var(--radius-md)",
119
+ padding: "6px 10px",
120
+ boxShadow: "0 2px 6px rgba(0, 0, 0, 0.08)",
121
+ });
122
+ const newlineIdx = msg.indexOf("\n");
123
+ if (newlineIdx !== -1) {
124
+ const title = msg.slice(0, newlineIdx);
125
+ const body = msg.slice(newlineIdx + 1);
126
+
127
+ const titleEl = document.createElement("div");
128
+ titleEl.textContent = formatErrorTypeTitle(title);
129
+ Object.assign(titleEl.style, { fontWeight: "600" });
130
+
131
+ const hr = document.createElement("div");
132
+ Object.assign(hr.style, {
133
+ borderTop: "1px solid var(--color-border-primary)",
134
+ margin: "4px 0",
135
+ });
136
+
137
+ const bodyEl = document.createElement("div");
138
+ bodyEl.textContent = body;
139
+ Object.assign(bodyEl.style, { whiteSpace: "pre-wrap" });
140
+
141
+ card.append(titleEl, hr, bodyEl);
142
+ } else {
143
+ card.textContent = msg;
144
+ card.style.whiteSpace = "pre-wrap";
145
+ }
146
+ return card;
147
+ }
148
+
149
+ function showErrorTooltip(message: string, x: number, y: number) {
150
+ hideErrorTooltip();
151
+
152
+ const tooltip = document.createElement("div");
153
+ Object.assign(tooltip.style, {
154
+ position: "fixed",
155
+ fontSize: "12px",
156
+ lineHeight: "1.4",
157
+ color: "var(--color-text-error-primary)",
158
+ fontFamily: "var(--font-family-sans)",
159
+ zIndex: "1000",
160
+ pointerEvents: "none",
161
+ maxWidth: "400px",
162
+ display: "flex",
163
+ flexDirection: "column",
164
+ gap: "6px",
165
+ });
166
+
167
+ const parts = message.split("\n\x00\n");
168
+ for (const part of parts) {
169
+ tooltip.append(renderErrorCard(part ?? ""));
170
+ }
171
+
172
+ document.body.appendChild(tooltip);
173
+ errorTooltipEl = tooltip;
174
+
175
+ const tooltipRect = tooltip.getBoundingClientRect();
176
+ let top = y - tooltipRect.height - 8;
177
+ // If tooltip goes above viewport, show below cursor instead
178
+ if (top < 4) {
179
+ top = y + 20;
180
+ }
181
+ // If it still goes below viewport, clamp to bottom
182
+ if (top + tooltipRect.height > window.innerHeight - 4) {
183
+ top = window.innerHeight - tooltipRect.height - 4;
184
+ }
185
+ // Final clamp to top
186
+ if (top < 4) top = 4;
187
+ tooltip.style.left = `${x}px`;
188
+ tooltip.style.top = `${top}px`;
189
+ }
190
+
191
+ function hideErrorTooltip() {
192
+ errorTooltipEl?.remove();
193
+ errorTooltipEl = null;
194
+ }
195
+
196
+ const issueLinesField = StateField.define<{
197
+ gutterMarkers: RangeSet<GutterMarker>;
198
+ lineDecorations: RangeSet<Decoration>;
199
+ messages: Map<number, string>;
200
+ }>({
201
+ create() {
202
+ return {
203
+ gutterMarkers: RangeSet.empty,
204
+ lineDecorations: Decoration.none,
205
+ messages: new Map(),
206
+ };
207
+ },
208
+ update(state, tr) {
209
+ for (const effect of tr.effects) {
210
+ if (effect.is(setIssueLinesEffect)) {
211
+ const markers: { from: number; to: number; value: GutterMarker }[] = [];
212
+ const lineDecos: {
213
+ from: number;
214
+ to: number;
215
+ value: Decoration;
216
+ }[] = [];
217
+ const messages = new Map<number, string>();
218
+ const doc = tr.state.doc;
219
+
220
+ for (const issue of effect.value) {
221
+ if (issue.line >= 1 && issue.line <= doc.lines) {
222
+ const line = doc.line(issue.line);
223
+ markers.push(errorLineMarker.range(line.from));
224
+ lineDecos.push(errorLineDecoration.range(line.from));
225
+ if (issue.message) {
226
+ messages.set(issue.line, issue.message);
227
+ }
228
+ }
229
+ }
230
+
231
+ return {
232
+ gutterMarkers: RangeSet.of(markers, true),
233
+ lineDecorations: Decoration.set(lineDecos, true),
234
+ messages,
235
+ };
236
+ }
237
+ }
238
+ if (tr.docChanged) {
239
+ try {
240
+ return {
241
+ gutterMarkers: state.gutterMarkers.map(tr.changes),
242
+ lineDecorations: state.lineDecorations.map(tr.changes),
243
+ messages: state.messages,
244
+ };
245
+ } catch {
246
+ return {
247
+ gutterMarkers: RangeSet.empty,
248
+ lineDecorations: Decoration.none,
249
+ messages: new Map(),
250
+ };
251
+ }
252
+ }
253
+ return state;
254
+ },
255
+ provide(field) {
256
+ return [
257
+ gutterLineClass.from(field, (val) => val.gutterMarkers),
258
+ EditorView.decorations.from(field, (val) => val.lineDecorations),
259
+ ];
260
+ },
261
+ });
262
+
263
+ function getErrorMessageForLine(
264
+ view: EditorView,
265
+ lineNo: number,
266
+ ): string | undefined {
267
+ const issueMsg = view.state.field(issueLinesField).messages.get(lineNo);
268
+ if (issueMsg) return issueMsg;
269
+ try {
270
+ return view.state.field(fhirDiagnosticsField).messages.get(lineNo);
271
+ } catch {
272
+ return undefined;
273
+ }
274
+ }
34
275
 
35
- const baseTheme = EditorView.baseTheme({
276
+ function handleErrorTooltipMove(event: Event, view: EditorView) {
277
+ const target = event.target as HTMLElement;
278
+ const mouseEvent = event as MouseEvent;
279
+
280
+ // Check gutter line number
281
+ const gutterEl = target.closest(
282
+ ".cm-lineNumbers .cm-gutterElement",
283
+ ) as HTMLElement | null;
284
+ if (gutterEl) {
285
+ const lineNo = Number.parseInt(gutterEl.textContent ?? "", 10);
286
+ if (!Number.isNaN(lineNo)) {
287
+ const message = getErrorMessageForLine(view, lineNo);
288
+ if (message) {
289
+ showErrorTooltip(message, mouseEvent.clientX, mouseEvent.clientY);
290
+ return false;
291
+ }
292
+ }
293
+ hideErrorTooltip();
294
+ return false;
295
+ }
296
+
297
+ // Check content line (cm-line) — follow cursor
298
+ const lineEl = target.closest(".cm-line") as HTMLElement | null;
299
+ if (lineEl) {
300
+ const pos = view.posAtDOM(lineEl);
301
+ const lineNo = view.state.doc.lineAt(pos).number;
302
+ const message = getErrorMessageForLine(view, lineNo);
303
+ if (message) {
304
+ showErrorTooltip(message, mouseEvent.clientX, mouseEvent.clientY);
305
+ return false;
306
+ }
307
+ }
308
+
309
+ hideErrorTooltip();
310
+ return false;
311
+ }
312
+
313
+ const errorTooltipHandler = EditorView.domEventHandlers({
314
+ mouseover: handleErrorTooltipMove,
315
+ mousemove: handleErrorTooltipMove,
316
+ mouseleave() {
317
+ hideErrorTooltip();
318
+ return false;
319
+ },
320
+ });
321
+
322
+ const baseTheme = EditorView.theme({
36
323
  "&": {
37
324
  backgroundColor: "var(--color-bg-primary)",
38
325
  height: "100%",
39
326
  width: "100%",
40
327
  fontSize: "14px",
328
+ },
329
+ "&.cm-editor": {
330
+ paddingTop: "0 !important",
331
+ paddingBottom: "0 !important",
332
+ },
333
+ ".cm-scroller": {
334
+ overflow: "auto",
41
335
  paddingTop: "8px",
42
336
  paddingBottom: "8px",
43
337
  },
338
+ ".cm-content": {
339
+ fontFamily: "var(--font-family-mono)",
340
+ padding: "0",
341
+ },
342
+ "&.cm-focused": {
343
+ outline: "none",
344
+ },
345
+ ".cm-cursor, .cm-dropCursor": {
346
+ borderLeftColor: "var(--color-text-primary)",
347
+ },
348
+ ".cm-gutter": {
349
+ fontFamily: "var(--font-family-mono)",
350
+ },
351
+ ".cm-gutters": {
352
+ backgroundColor: "transparent",
353
+ border: "none",
354
+ },
355
+ ".cm-lineNumbers": {
356
+ minWidth: "3.5ch",
357
+ },
358
+ ".cm-lineNumbers .cm-gutterElement": {
359
+ minWidth: "3.5ch",
360
+ paddingRight: "4px",
361
+ color: "var(--color-text-quaternary)",
362
+ },
363
+ ".cm-lineNumbers .cm-gutterElement.cm-activeLineGutter": {
364
+ backgroundColor: "var(--color-bg-primary)",
365
+ color: "var(--color-text-secondary)",
366
+ },
367
+ ".cm-activeLineGutter": {
368
+ backgroundColor: "transparent !important",
369
+ },
370
+ ".cm-activeLine": {
371
+ backgroundColor: "transparent !important",
372
+ },
373
+ ".cm-lineNumbers .cm-gutterElement.cm-errorLineGutter": {
374
+ color: "var(--color-text-error-primary)",
375
+ backgroundColor:
376
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
377
+ },
378
+ ".cm-foldGutter .cm-gutterElement.cm-errorLineGutter": {
379
+ color: "var(--color-text-error-primary)",
380
+ backgroundColor:
381
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
382
+ display: "flex",
383
+ alignItems: "center",
384
+ justifyContent: "center",
385
+ },
386
+ ".cm-errorLine": {
387
+ backgroundColor:
388
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
389
+ },
390
+ });
391
+
392
+ const completionTheme = EditorView.theme({
393
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
394
+ maxHeight: "400px",
395
+ },
396
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {
397
+ display: "flex",
398
+ alignItems: "center",
399
+ gap: "8px",
400
+ },
401
+ ".cm-completionLabel": {
402
+ flex: "1",
403
+ minWidth: "0",
404
+ fontFamily: "var(--font-family-mono)",
405
+ fontSize: "var(--font-size-sm)",
406
+ lineHeight: "var(--font-leading-5)",
407
+ },
408
+ ".cm-completionMatchedText": {
409
+ textDecoration: "none",
410
+ fontWeight: "600",
411
+ color: "var(--color-text-link)",
412
+ },
413
+ ".cm-completionDetail": {
414
+ color: "var(--color-text-tertiary)",
415
+ fontSize: "12px",
416
+ marginLeft: "auto",
417
+ whiteSpace: "nowrap",
418
+ },
419
+ ".cm-completionInfo": {
420
+ backgroundColor: "var(--color-bg-primary)",
421
+ border: "1px solid var(--color-border-primary)",
422
+ borderRadius: "var(--radius-md)",
423
+ color: "var(--color-text-secondary)",
424
+ fontFamily: "var(--font-family-mono)",
425
+ fontSize: "14px",
426
+ padding: "8px 12px",
427
+ marginLeft: "8px",
428
+ lineHeight: "1.4",
429
+ whiteSpace: "normal",
430
+ maxWidth: "300px",
431
+ },
432
+ ".cm-completion-icon": {
433
+ display: "flex",
434
+ alignItems: "center",
435
+ justifyContent: "center",
436
+ width: "16px",
437
+ height: "16px",
438
+ flexShrink: "0",
439
+ },
440
+ });
441
+
442
+ const readOnlyTheme = EditorView.theme({
443
+ "&": {
444
+ backgroundColor: "var(--color-bg-secondary)",
445
+ height: "100%",
446
+ width: "100%",
447
+ fontSize: "14px",
448
+ },
449
+ "&.cm-editor": {
450
+ paddingTop: "0 !important",
451
+ paddingBottom: "0 !important",
452
+ },
44
453
  ".cm-scroller": {
45
454
  overflow: "auto",
455
+ paddingTop: "8px",
456
+ paddingBottom: "8px",
46
457
  },
47
458
  ".cm-content": {
48
459
  fontFamily: "var(--font-family-mono)",
@@ -55,89 +466,1262 @@ const baseTheme = EditorView.baseTheme({
55
466
  fontFamily: "var(--font-family-mono)",
56
467
  },
57
468
  ".cm-gutters": {
58
- backgroundColor: "var(--color-bg-primary)",
469
+ backgroundColor: "var(--color-bg-secondary)",
59
470
  border: "none",
60
471
  },
61
472
  ".cm-lineNumbers": {
62
- paddingLeft: "16px",
473
+ minWidth: "3.5ch",
474
+ },
475
+ ".cm-lineNumbers .cm-gutterElement": {
476
+ minWidth: "3.5ch",
477
+ paddingRight: "4px",
478
+ color: "var(--color-text-quaternary)",
479
+ },
480
+ ".cm-lineNumbers .cm-gutterElement.cm-activeLineGutter": {
481
+ backgroundColor: "var(--color-bg-secondary)",
482
+ color: "var(--color-text-secondary)",
63
483
  },
64
484
  ".cm-activeLineGutter": {
65
- backgroundColor: "var(--color-bg-primary)",
66
- color: "var(--color-text-primary)",
485
+ backgroundColor: "transparent !important",
67
486
  },
68
487
  ".cm-activeLine": {
69
- backgroundColor: "rgba(255, 255, 255, 0)",
488
+ backgroundColor: "transparent !important",
489
+ },
490
+ ".cm-lineNumbers .cm-gutterElement.cm-errorLineGutter": {
491
+ color: "var(--color-text-error-primary)",
492
+ backgroundColor:
493
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
494
+ },
495
+ ".cm-foldGutter .cm-gutterElement.cm-errorLineGutter": {
496
+ color: "var(--color-text-error-primary)",
497
+ backgroundColor:
498
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
499
+ display: "flex",
500
+ alignItems: "center",
501
+ justifyContent: "center",
502
+ },
503
+ ".cm-errorLine": {
504
+ backgroundColor:
505
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
70
506
  },
71
507
  });
72
508
 
509
+ const iconButtonStyle: React.CSSProperties = {
510
+ display: "flex",
511
+ alignItems: "center",
512
+ justifyContent: "center",
513
+ width: "28px",
514
+ height: "28px",
515
+ border: "none",
516
+ borderRadius: "var(--radius-sm)",
517
+ background: "transparent",
518
+ color: "var(--color-text-secondary)",
519
+ cursor: "pointer",
520
+ padding: 0,
521
+ };
522
+
523
+ function getMatchInfo(
524
+ state: EditorState,
525
+ searchText: string,
526
+ ): { current: number; total: number } {
527
+ if (!searchText) return { current: 0, total: 0 };
528
+ const doc = state.doc.toString();
529
+ const sel = state.selection.main;
530
+ const lowerDoc = doc.toLowerCase();
531
+ const lowerSearch = searchText.toLowerCase();
532
+ const searchLen = searchText.length;
533
+ let total = 0;
534
+ let current = 0;
535
+ let pos = 0;
536
+ for (;;) {
537
+ const idx = lowerDoc.indexOf(lowerSearch, pos);
538
+ if (idx === -1) break;
539
+ total++;
540
+ if (idx === sel.from && idx + searchLen === sel.to) {
541
+ current = total;
542
+ }
543
+ pos = idx + 1;
544
+ }
545
+ return { current, total };
546
+ }
547
+
548
+ function createSearchPanel(view: EditorView) {
549
+ const dom = document.createElement("div");
550
+ const root = createRoot(dom);
551
+
552
+ const panelRef: {
553
+ setSearch: ((v: string) => void) | null;
554
+ setMatch: ((info: { current: number; total: number }) => void) | null;
555
+ lastSearch: string;
556
+ lastCurrent: number;
557
+ lastTotal: number;
558
+ } = {
559
+ setSearch: null,
560
+ setMatch: null,
561
+ lastSearch: "",
562
+ lastCurrent: 0,
563
+ lastTotal: 0,
564
+ };
565
+
566
+ function Panel() {
567
+ const [value, setValue] = React.useState(
568
+ () => getSearchQuery(view.state).search,
569
+ );
570
+ const [match, setMatchState] = React.useState({ current: 0, total: 0 });
571
+
572
+ panelRef.setSearch = setValue;
573
+ panelRef.setMatch = setMatchState;
574
+
575
+ const handleChange = (newValue: string) => {
576
+ setValue(newValue);
577
+ view.dispatch({
578
+ effects: setSearchQuery.of(new SearchQuery({ search: newValue })),
579
+ });
580
+ };
581
+
582
+ return (
583
+ <div
584
+ style={{
585
+ display: "flex",
586
+ alignItems: "center",
587
+ gap: "2px",
588
+ padding: "6px 8px",
589
+ marginTop: "4px",
590
+ backgroundColor: "var(--color-bg-primary)",
591
+ border: "1px solid var(--color-border-primary)",
592
+ borderRadius: "var(--radius-md)",
593
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.12)",
594
+ }}
595
+ >
596
+ <input
597
+ value={value}
598
+ onChange={(e) => handleChange(e.target.value)}
599
+ onKeyDown={(e) => {
600
+ if (e.key === "Enter") {
601
+ e.preventDefault();
602
+ if (e.shiftKey) findPrevious(view);
603
+ else findNext(view);
604
+ }
605
+ if (e.key === "Escape") {
606
+ e.preventDefault();
607
+ closeSearchPanel(view);
608
+ view.focus();
609
+ }
610
+ }}
611
+ placeholder="Find..."
612
+ style={{
613
+ height: "28px",
614
+ padding: "0 8px",
615
+ border: "1px solid var(--color-border-primary)",
616
+ borderRadius: "var(--radius-md)",
617
+ fontSize: "13px",
618
+ fontFamily: "var(--font-family-sans)",
619
+ backgroundColor: "var(--color-bg-primary)",
620
+ color: "var(--color-text-primary)",
621
+ outline: "none",
622
+ flex: "0 0 200px",
623
+ }}
624
+ />
625
+ <span
626
+ style={{
627
+ fontSize: "12px",
628
+ fontFamily: "var(--font-family-sans)",
629
+ color: "var(--color-text-secondary)",
630
+ whiteSpace: "nowrap",
631
+ minWidth: "70px",
632
+ textAlign: "center",
633
+ visibility: value ? "visible" : "hidden",
634
+ }}
635
+ >
636
+ {value
637
+ ? match.total > 0
638
+ ? `${match.current} of ${match.total}`
639
+ : "No results"
640
+ : "No results"}
641
+ </span>
642
+ <button
643
+ type="button"
644
+ onClick={() => findPrevious(view)}
645
+ title="Previous match"
646
+ style={iconButtonStyle}
647
+ >
648
+ <ChevronUp size={16} />
649
+ </button>
650
+ <button
651
+ type="button"
652
+ onClick={() => findNext(view)}
653
+ title="Next match"
654
+ style={iconButtonStyle}
655
+ >
656
+ <ChevronDown size={16} />
657
+ </button>
658
+ <button
659
+ type="button"
660
+ onClick={() => {
661
+ closeSearchPanel(view);
662
+ view.focus();
663
+ }}
664
+ title="Close"
665
+ style={iconButtonStyle}
666
+ >
667
+ <X size={14} />
668
+ </button>
669
+ </div>
670
+ );
671
+ }
672
+
673
+ flushSync(() => {
674
+ root.render(<Panel />);
675
+ });
676
+
677
+ const input = dom.querySelector("input");
678
+ if (input) input.setAttribute("main-field", "true");
679
+
680
+ // Compute initial match info
681
+ const q = getSearchQuery(view.state);
682
+ panelRef.lastSearch = q.search;
683
+ if (q.search) {
684
+ const info = getMatchInfo(view.state, q.search);
685
+ panelRef.lastCurrent = info.current;
686
+ panelRef.lastTotal = info.total;
687
+ panelRef.setMatch?.(info);
688
+ }
689
+
690
+ return {
691
+ dom,
692
+ top: true,
693
+ mount() {
694
+ const el = dom.querySelector("input");
695
+ if (el) {
696
+ el.focus();
697
+ el.select();
698
+ }
699
+ },
700
+ update(update: ViewUpdate) {
701
+ const query = getSearchQuery(update.state);
702
+
703
+ if (query.search !== panelRef.lastSearch) {
704
+ panelRef.setSearch?.(query.search);
705
+ }
706
+ panelRef.lastSearch = query.search;
707
+
708
+ const info = getMatchInfo(update.state, query.search);
709
+ if (
710
+ info.current !== panelRef.lastCurrent ||
711
+ info.total !== panelRef.lastTotal
712
+ ) {
713
+ panelRef.lastCurrent = info.current;
714
+ panelRef.lastTotal = info.total;
715
+ panelRef.setMatch?.(info);
716
+ }
717
+ },
718
+ destroy() {
719
+ root.unmount();
720
+ panelRef.setSearch = null;
721
+ panelRef.setMatch = null;
722
+ },
723
+ };
724
+ }
725
+
726
+ const searchPanelTheme = EditorView.theme({
727
+ "& .cm-panels-top": {
728
+ position: "absolute",
729
+ top: "8px",
730
+ right: "4px",
731
+ left: "auto",
732
+ zIndex: "10",
733
+ backgroundColor: "transparent",
734
+ border: "none",
735
+ },
736
+ ".cm-searchMatch": {
737
+ backgroundColor: "var(--color-blue-200) !important",
738
+ },
739
+ ".cm-searchMatch-selected": {
740
+ backgroundColor: "var(--color-blue-400) !important",
741
+ },
742
+ ".cm-selectionMatch": {
743
+ backgroundColor: "var(--color-blue-100) !important",
744
+ },
745
+ });
746
+
747
+ const customSearchExtension = [
748
+ search({ createPanel: createSearchPanel }),
749
+ searchPanelTheme,
750
+ ];
751
+
73
752
  const customHighlightStyle = HighlightStyle.define([
74
- { tag: tags.propertyName, color: "#EA4A35" },
75
- { tag: tags.string, color: "#405CBF" },
76
- { tag: tags.number, color: "#00A984" },
77
- { tag: tags.bool, color: "#569cd6" },
78
- { tag: tags.null, color: "#569cd6" },
753
+ { tag: tags.propertyName, color: "var(--hs-syntax-property)" },
754
+ { tag: tags.string, color: "var(--hs-syntax-string)" },
755
+ { tag: tags.number, color: "var(--hs-syntax-number)" },
756
+ { tag: tags.bool, color: "var(--hs-syntax-keyword)" },
757
+ { tag: tags.null, color: "var(--hs-syntax-keyword)" },
758
+ { tag: tags.keyword, color: "var(--hs-syntax-keyword)" },
759
+ { tag: tags.operatorKeyword, color: "var(--hs-syntax-string)" },
760
+ { tag: tags.controlKeyword, color: "var(--hs-syntax-property)" },
761
+ { tag: tags.typeName, color: "var(--hs-syntax-number)" },
762
+ { tag: tags.variableName, color: "var(--hs-syntax-property)" },
763
+ { tag: tags.operator, color: "var(--hs-syntax-string)" },
764
+ { tag: tags.comment, color: "var(--hs-syntax-comment)" },
765
+ { tag: tags.lineComment, color: "var(--hs-syntax-comment)" },
766
+ { tag: tags.blockComment, color: "var(--hs-syntax-comment)" },
79
767
  ]);
80
768
 
769
+ const SQL_KEYWORDS = [
770
+ "select",
771
+ "from",
772
+ "where",
773
+ "and",
774
+ "or",
775
+ "not",
776
+ "in",
777
+ "between",
778
+ "like",
779
+ "insert",
780
+ "update",
781
+ "delete",
782
+ "create",
783
+ "drop",
784
+ "alter",
785
+ "table",
786
+ "index",
787
+ "join",
788
+ "inner",
789
+ "left",
790
+ "right",
791
+ "outer",
792
+ "on",
793
+ "as",
794
+ "order",
795
+ "by",
796
+ "group",
797
+ "having",
798
+ "limit",
799
+ "offset",
800
+ "union",
801
+ "intersect",
802
+ "except",
803
+ "distinct",
804
+ "all",
805
+ "exists",
806
+ "case",
807
+ "when",
808
+ "then",
809
+ "else",
810
+ "end",
811
+ "null",
812
+ "true",
813
+ "false",
814
+ "is",
815
+ "asc",
816
+ "desc",
817
+ ];
818
+
819
+ const SQL_BUILTIN = [
820
+ "varchar",
821
+ "char",
822
+ "text",
823
+ "integer",
824
+ "int",
825
+ "bigint",
826
+ "decimal",
827
+ "numeric",
828
+ "float",
829
+ "real",
830
+ "boolean",
831
+ "date",
832
+ "time",
833
+ "timestamp",
834
+ "uuid",
835
+ "count",
836
+ "sum",
837
+ "avg",
838
+ "min",
839
+ "max",
840
+ "coalesce",
841
+ "concat",
842
+ "substring",
843
+ "upper",
844
+ "lower",
845
+ "trim",
846
+ "length",
847
+ "now",
848
+ "current_date",
849
+ "current_time",
850
+ ];
851
+
852
+ const customSQLDialect = SQLDialect.define({
853
+ keywords: SQL_KEYWORDS.join(" "),
854
+ builtin: SQL_BUILTIN.join(" "),
855
+ });
856
+
857
+ function computeYamlNewlineIndent(lineText: string): string {
858
+ const indent = lineText.match(/^(\s*)/)?.[1] ?? "";
859
+ const trimmed = lineText.trimEnd();
860
+
861
+ if (trimmed.endsWith(":")) {
862
+ // After "key:" with no value — increase indent
863
+ // For " - key:", base indent is at the dash content level
864
+ const dashMatch = trimmed.match(/^(\s*-\s+)/);
865
+ const baseIndent = dashMatch?.[1]
866
+ ? " ".repeat(dashMatch[1].length)
867
+ : indent;
868
+ return `${baseIndent} `;
869
+ }
870
+ if (/^\s*-\s*$/.test(trimmed)) {
871
+ // After bare "- " (array item marker only) — align to content after dash
872
+ const dashMatch = trimmed.match(/^(\s*-\s*)/);
873
+ return dashMatch?.[1] ? " ".repeat(dashMatch[1].length) : indent;
874
+ }
875
+ // Preserve current indent; for " - key: val" align to key level
876
+ const dashKeyMatch = trimmed.match(/^(\s*-\s+)\S/);
877
+ return dashKeyMatch?.[1] ? " ".repeat(dashKeyMatch[1].length) : indent;
878
+ }
879
+
880
+ function yamlEnterKeymap(): Extension {
881
+ return keymap.of([
882
+ {
883
+ key: "Enter",
884
+ run: (view) => {
885
+ const { state } = view;
886
+ const pos = state.selection.main.head;
887
+ const line = state.doc.lineAt(pos);
888
+ const newIndent = computeYamlNewlineIndent(line.text);
889
+
890
+ view.dispatch({
891
+ changes: { from: pos, insert: `\n${newIndent}` },
892
+ selection: { anchor: pos + 1 + newIndent.length },
893
+ });
894
+ return true;
895
+ },
896
+ },
897
+ ]);
898
+ }
899
+
900
+ function httpYamlEnterKeymap(): Extension {
901
+ return keymap.of([
902
+ {
903
+ key: "Enter",
904
+ run: (view) => {
905
+ const { state } = view;
906
+ const pos = state.selection.main.head;
907
+ const doc = state.doc.toString();
908
+
909
+ // Only handle if cursor is in YAML body (after blank line separator)
910
+ const textBeforeCursor = doc.slice(0, pos);
911
+ const blankLineIdx = textBeforeCursor.indexOf("\n\n");
912
+ if (blankLineIdx === -1 || pos <= blankLineIdx + 1) return false;
913
+
914
+ // Check if the body looks like YAML (not JSON)
915
+ const bodyStart = blankLineIdx + 2;
916
+ const bodyPrefix = doc.slice(bodyStart, bodyStart + 20).trimStart();
917
+ if (bodyPrefix.startsWith("{") || bodyPrefix.startsWith("["))
918
+ return false;
919
+
920
+ const line = state.doc.lineAt(pos);
921
+ const newIndent = computeYamlNewlineIndent(line.text);
922
+
923
+ view.dispatch({
924
+ changes: { from: pos, insert: `\n${newIndent}` },
925
+ selection: { anchor: pos + 1 + newIndent.length },
926
+ });
927
+ return true;
928
+ },
929
+ },
930
+ ]);
931
+ }
932
+
933
+ type LanguageMode = "json" | "http" | "sql" | "yaml";
934
+
935
+ function languageExtensions(
936
+ mode: LanguageMode,
937
+ sqlExtraBuiltins?: string[],
938
+ getUrlSuggestions?: GetUrlSuggestions,
939
+ ) {
940
+ if (mode === "http") {
941
+ const jsonLang = json();
942
+ const yamlLang = yaml();
943
+ return [
944
+ http(
945
+ (ct) =>
946
+ ct === "application/json"
947
+ ? jsonLang.language
948
+ : ct === "text/yaml" ||
949
+ ct === "application/yaml" ||
950
+ ct === "application/x-yaml"
951
+ ? yamlLang.language
952
+ : null,
953
+ getUrlSuggestions,
954
+ ),
955
+ syntaxHighlighting(customHighlightStyle),
956
+ jsonAutoExpandBraces(),
957
+ httpYamlEnterKeymap(),
958
+ ];
959
+ } else if (mode === "sql") {
960
+ let dialect = customSQLDialect;
961
+ if (sqlExtraBuiltins && sqlExtraBuiltins.length > 0) {
962
+ dialect = SQLDialect.define({
963
+ keywords: SQL_KEYWORDS.join(" "),
964
+ builtin: [...SQL_BUILTIN, ...sqlExtraBuiltins].join(" "),
965
+ });
966
+ }
967
+ return [sql({ dialect }), syntaxHighlighting(customHighlightStyle)];
968
+ } else if (mode === "yaml") {
969
+ return [
970
+ yaml(),
971
+ syntaxHighlighting(customHighlightStyle),
972
+ yamlEnterKeymap(),
973
+ ];
974
+ } else {
975
+ return [
976
+ json(),
977
+ linter(
978
+ (view) => {
979
+ if (!view.state.doc.toString().trim()) return [];
980
+ return jsonParseLinter()(view);
981
+ },
982
+ { delay: 300 },
983
+ ),
984
+ syntaxHighlighting(customHighlightStyle),
985
+ jsonAutoExpandBraces(),
986
+ ];
987
+ }
988
+ }
989
+
990
+ function jsonAutoExpandBraces(): Extension {
991
+ return EditorState.transactionFilter.of((tr) => {
992
+ if (!tr.docChanged) return tr;
993
+
994
+ let braceFrom = -1;
995
+ let braceTo = -1;
996
+ let changeCount = 0;
997
+
998
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
999
+ changeCount++;
1000
+ if (inserted.toString() === "{}") {
1001
+ braceFrom = fromA;
1002
+ braceTo = toA;
1003
+ }
1004
+ });
1005
+
1006
+ if (changeCount !== 1 || braceFrom === -1) return tr;
1007
+
1008
+ const tree = syntaxTree(tr.startState);
1009
+ const nodeBefore = tree.resolveInner(braceFrom, -1);
1010
+ if (nodeBefore.name === "String" || nodeBefore.parent?.name === "String") {
1011
+ return tr;
1012
+ }
1013
+
1014
+ const line = tr.startState.doc.lineAt(braceFrom);
1015
+ const indent = line.text.match(/^(\s*)/)?.[1] ?? "";
1016
+ const inner = `${indent} `;
1017
+
1018
+ // Check if { is inside an extension array — insert {"url": ""} snippet
1019
+ const docText = tr.startState.doc.toString();
1020
+ const textBefore = docText.slice(0, braceFrom);
1021
+ const isInExtArray =
1022
+ /"(?:extension|modifierExtension)"\s*:\s*\[\s*(?:\{[\s\S]*?\}\s*,?\s*)*$/s.test(
1023
+ textBefore,
1024
+ );
1025
+ if (isInExtArray) {
1026
+ const insert = `{\n${inner}"url": ""\n${indent}}`;
1027
+ return {
1028
+ changes: { from: braceFrom, to: braceTo, insert },
1029
+ selection: { anchor: braceFrom + insert.lastIndexOf('""') + 1 },
1030
+ };
1031
+ }
1032
+
1033
+ return {
1034
+ changes: {
1035
+ from: braceFrom,
1036
+ to: braceTo,
1037
+ insert: `{\n${inner}\n${indent}}`,
1038
+ },
1039
+ selection: { anchor: braceFrom + 2 + inner.length },
1040
+ };
1041
+ });
1042
+ }
1043
+
1044
+ type CodeEditorProps = {
1045
+ readOnly?: boolean;
1046
+ isReadOnlyTheme?: boolean;
1047
+ defaultValue?: string;
1048
+ currentValue?: string;
1049
+ onChange?: (value: string) => void;
1050
+ onUpdate?: (update: ViewUpdate) => void;
1051
+ id?: string;
1052
+ mode?: LanguageMode;
1053
+ viewCallback?: (view: EditorView) => void;
1054
+ additionalExtensions?: Extension[];
1055
+ issueLineNumbers?: { line: number; message?: string }[];
1056
+ foldGutter?: boolean;
1057
+ lineNumbers?: boolean;
1058
+ sql?: SqlConfig;
1059
+ getStructureDefinitions?: GetStructureDefinitions;
1060
+ resourceTypeHint?: string;
1061
+ expandValueSet?: ExpandValueSet;
1062
+ getUrlSuggestions?: GetUrlSuggestions;
1063
+ vimMode?: boolean;
1064
+ };
1065
+
1066
+ export type CodeEditorView = EditorView;
1067
+
1068
+ export type {
1069
+ ExpandValueSet,
1070
+ GetStructureDefinitions,
1071
+ } from "./fhir-autocomplete";
1072
+ export type { GetUrlSuggestions } from "./http";
1073
+ export type {
1074
+ SqlConfig,
1075
+ SqlMetadata,
1076
+ SqlQueryType,
1077
+ } from "./sql-completion";
1078
+
81
1079
  export function CodeEditor({
82
1080
  defaultValue,
1081
+ currentValue,
83
1082
  onChange,
84
- }: {
85
- defaultValue?: string;
86
- onChange?: (value: string) => void;
87
- }) {
88
- const editorRef = React.useRef(null);
1083
+ onUpdate,
1084
+ viewCallback,
1085
+ readOnly = false,
1086
+ id,
1087
+ mode = "json",
1088
+ isReadOnlyTheme = false,
1089
+ additionalExtensions,
1090
+ issueLineNumbers,
1091
+ foldGutter: enableFoldGutter = true,
1092
+ lineNumbers: enableLineNumbers = true,
1093
+ sql,
1094
+ getStructureDefinitions,
1095
+ resourceTypeHint,
1096
+ expandValueSet,
1097
+ getUrlSuggestions,
1098
+ vimMode = false,
1099
+ }: CodeEditorProps) {
1100
+ const domRef = React.useRef(null);
1101
+ const [view, setView] = React.useState<EditorView | null>(null);
1102
+
1103
+ const safeDispatch = React.useCallback(
1104
+ (spec: Parameters<EditorView["dispatch"]>[0]) => {
1105
+ try {
1106
+ view?.dispatch(spec);
1107
+ } catch {
1108
+ // Ignore RangeError from stale decoration positions during reconfigure
1109
+ }
1110
+ },
1111
+ [view],
1112
+ );
1113
+
1114
+ const initialValue = React.useRef(defaultValue ?? "");
1115
+
1116
+ const onChangeComparment = React.useRef(new Compartment());
1117
+ const onUpdateComparment = React.useRef(new Compartment());
1118
+ const languageCompartment = React.useRef(new Compartment());
1119
+ const readOnlyCompartment = React.useRef(new Compartment());
1120
+ const themeCompartment = React.useRef(new Compartment());
1121
+ const additionalExtensionsCompartment = React.useRef(new Compartment());
1122
+ const sqlCompletionCompartment = React.useRef(new Compartment());
1123
+ const fhirCompletionCompartment = React.useRef(new Compartment());
1124
+ const vimCompartment = React.useRef(new Compartment());
1125
+ const [sqlFunctions, setSqlFunctions] = React.useState<
1126
+ string[] | undefined
1127
+ >();
1128
+ const executeSqlRef = React.useRef(sql?.executeSql);
89
1129
 
90
1130
  React.useEffect(() => {
91
- if (!editorRef.current) {
1131
+ if (!domRef.current) {
92
1132
  return;
93
1133
  }
94
1134
 
95
1135
  const view = new EditorView({
96
- parent: editorRef.current,
1136
+ parent: domRef.current,
97
1137
  state: EditorState.create({
98
- doc: defaultValue ?? "",
1138
+ doc: initialValue.current,
99
1139
  extensions: [
100
- lineNumbers(),
101
- foldGutter(),
1140
+ vimCompartment.current.of(vimMode ? vim() : []),
1141
+ EditorView.contentAttributes.of({ "data-gramm": "false" }),
1142
+ readOnlyCompartment.current.of(EditorState.readOnly.of(false)),
1143
+ ...(enableLineNumbers ? [lineNumbers()] : []),
1144
+ ...(enableFoldGutter
1145
+ ? [
1146
+ foldGutter({
1147
+ markerDOM: (open) => {
1148
+ const el = document.createElement("span");
1149
+ el.style.display = "flex";
1150
+ el.style.alignItems = "center";
1151
+ el.style.justifyContent = "center";
1152
+ el.style.width = "100%";
1153
+ el.style.height = "100%";
1154
+ el.innerHTML = open
1155
+ ? '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>'
1156
+ : '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m9 18 6-6-6-6"/></svg>';
1157
+ return el;
1158
+ },
1159
+ }),
1160
+ ]
1161
+ : []),
102
1162
  highlightSpecialChars(),
103
1163
  history(),
104
1164
  drawSelection(),
105
1165
  dropCursor(),
106
1166
  EditorState.allowMultipleSelections.of(true),
107
1167
  indentOnInput(),
108
- json(),
109
- syntaxHighlighting(customHighlightStyle),
1168
+ languageCompartment.current.of([]),
110
1169
  bracketMatching(),
111
1170
  closeBrackets(),
112
- autocompletion(),
1171
+ autocompletion({
1172
+ icons: false,
1173
+ maxRenderedOptions: 1000,
1174
+ defaultKeymap: false,
1175
+ addToOptions: [{ render: renderCompletionIcon, position: 20 }],
1176
+ optionClass: (_completion) =>
1177
+ "!px-2 !py-1 rounded-md aria-selected:!bg-bg-quaternary aria-selected:!text-text-primary hover:!bg-bg-secondary flex items-center gap-2",
1178
+ tooltipClass: (_state) =>
1179
+ "!bg-bg-primary rounded-md p-2 shadow-md !border-border-primary !typo-body",
1180
+ compareCompletions: (a, b) => {
1181
+ const aIsProperty = a.type === "property" ? 0 : 1;
1182
+ const bIsProperty = b.type === "property" ? 0 : 1;
1183
+ return aIsProperty - bIsProperty;
1184
+ },
1185
+ }),
113
1186
  rectangularSelection(),
114
1187
  crosshairCursor(),
115
- highlightActiveLine(),
116
- highlightActiveLineGutter(),
117
1188
  highlightSelectionMatches(),
118
- baseTheme,
1189
+ Prec.highest(
1190
+ keymap.of([
1191
+ {
1192
+ key: "Tab",
1193
+ run: (v) => {
1194
+ if (completionStatus(v.state) === "active") {
1195
+ return moveCompletionSelection(true)(v);
1196
+ }
1197
+ return false;
1198
+ },
1199
+ },
1200
+ {
1201
+ key: "Shift-Tab",
1202
+ run: (v) => {
1203
+ if (completionStatus(v.state) === "active") {
1204
+ return moveCompletionSelection(false)(v);
1205
+ }
1206
+ return false;
1207
+ },
1208
+ },
1209
+ {
1210
+ key: "Enter",
1211
+ run: (v) => {
1212
+ if (completionStatus(v.state) === "active") {
1213
+ return acceptCompletion(v);
1214
+ }
1215
+ return false;
1216
+ },
1217
+ },
1218
+ ]),
1219
+ ),
1220
+ themeCompartment.current.of(baseTheme),
1221
+ completionTheme,
119
1222
  keymap.of([
120
1223
  ...closeBracketsKeymap,
1224
+ ...completionKeymap.filter((b) => b.key !== "Enter"),
121
1225
  ...defaultKeymap,
122
1226
  ...searchKeymap,
123
1227
  ...historyKeymap,
124
1228
  ...foldKeymap,
125
- ...completionKeymap,
126
1229
  ...lintKeymap,
127
1230
  ]),
128
- linter(jsonParseLinter(), { delay: 300 }),
129
- lintGutter(),
130
- EditorView.updateListener.of((update) => {
131
- if (update.docChanged && onChange) {
132
- onChange(update.view.state.doc.toString());
133
- }
1231
+ issueLinesField,
1232
+ errorTooltipHandler,
1233
+ EditorView.exceptionSink.of(() => {}),
1234
+ ...customSearchExtension,
1235
+ onChangeComparment.current.of([]),
1236
+ onUpdateComparment.current.of([]),
1237
+ additionalExtensionsCompartment.current.of([]),
1238
+ sqlCompletionCompartment.current.of([]),
1239
+ fhirCompletionCompartment.current.of([]),
1240
+ ],
1241
+ }),
1242
+ });
1243
+
1244
+ setView(() => view);
1245
+
1246
+ return () => {
1247
+ view.destroy();
1248
+ setView(() => null);
1249
+ };
1250
+ }, [enableFoldGutter, enableLineNumbers, vimMode]);
1251
+
1252
+ React.useEffect(() => {
1253
+ executeSqlRef.current = sql?.executeSql;
1254
+ });
1255
+
1256
+ React.useEffect(() => {
1257
+ if (!view || !sql) {
1258
+ if (view) {
1259
+ safeDispatch({
1260
+ effects: sqlCompletionCompartment.current.reconfigure([]),
1261
+ });
1262
+ }
1263
+ setSqlFunctions(undefined);
1264
+ return;
1265
+ }
1266
+
1267
+ let cancelled = false;
1268
+
1269
+ fetchSqlMetadata(sql.executeSql)
1270
+ .then((metadata) => {
1271
+ if (cancelled) return;
1272
+ setSqlFunctions(metadata.functions);
1273
+ const extensions = buildSqlCompletionExtensions(
1274
+ metadata,
1275
+ (query, type) =>
1276
+ executeSqlRef.current?.(query, type) ?? Promise.resolve([]),
1277
+ );
1278
+ safeDispatch({
1279
+ effects: sqlCompletionCompartment.current.reconfigure(extensions),
1280
+ });
1281
+ })
1282
+ .catch(() => {});
1283
+
1284
+ return () => {
1285
+ cancelled = true;
1286
+ };
1287
+ }, [view, sql, safeDispatch]);
1288
+
1289
+ React.useEffect(() => {
1290
+ if (!view) return;
1291
+ if (getStructureDefinitions) {
1292
+ safeDispatch({
1293
+ effects: fhirCompletionCompartment.current.reconfigure(
1294
+ buildFhirCompletionExtension(
1295
+ getStructureDefinitions,
1296
+ resourceTypeHint,
1297
+ expandValueSet,
1298
+ ),
1299
+ ),
1300
+ });
1301
+ } else {
1302
+ safeDispatch({
1303
+ effects: fhirCompletionCompartment.current.reconfigure([]),
1304
+ });
1305
+ }
1306
+ }, [
1307
+ view,
1308
+ getStructureDefinitions,
1309
+ resourceTypeHint,
1310
+ expandValueSet,
1311
+ safeDispatch,
1312
+ ]);
1313
+
1314
+ React.useEffect(() => {
1315
+ if (viewCallback && view) {
1316
+ viewCallback(view);
1317
+ }
1318
+ }, [view, viewCallback]);
1319
+
1320
+ React.useEffect(() => {
1321
+ safeDispatch({
1322
+ effects: onChangeComparment.current.reconfigure([
1323
+ EditorView.updateListener.of((update) => {
1324
+ if (update.docChanged && onChange) {
1325
+ onChange(update.view.state.doc.toString());
1326
+ }
1327
+ }),
1328
+ ]),
1329
+ });
1330
+ }, [onChange, safeDispatch]);
1331
+
1332
+ React.useEffect(() => {
1333
+ safeDispatch({
1334
+ effects: onUpdateComparment.current.reconfigure([
1335
+ EditorView.updateListener.of((update) => {
1336
+ if (onUpdate) {
1337
+ onUpdate(update);
1338
+ }
1339
+ }),
1340
+ ]),
1341
+ });
1342
+ }, [onUpdate, safeDispatch]);
1343
+
1344
+ // FIXME: it is probably better to have CM manage its state.
1345
+ React.useEffect(() => {
1346
+ if (!view || currentValue === undefined) {
1347
+ return;
1348
+ }
1349
+
1350
+ const currentDoc = view.state.doc.toString();
1351
+ if (currentDoc !== currentValue) {
1352
+ safeDispatch({
1353
+ changes: {
1354
+ from: 0,
1355
+ to: currentDoc.length,
1356
+ insert: currentValue,
1357
+ },
1358
+ });
1359
+ }
1360
+ }, [currentValue, view, safeDispatch]);
1361
+
1362
+ const getUrlSuggestionsRef = React.useRef(getUrlSuggestions);
1363
+ getUrlSuggestionsRef.current = getUrlSuggestions;
1364
+
1365
+ const stableGetUrlSuggestions = React.useMemo(() => {
1366
+ if (!getUrlSuggestions) return undefined;
1367
+ return ((path: string, method: string) =>
1368
+ getUrlSuggestionsRef.current?.(path, method) ?? []) as GetUrlSuggestions;
1369
+ }, [getUrlSuggestions]);
1370
+
1371
+ React.useEffect(() => {
1372
+ if (view === null) {
1373
+ return;
1374
+ }
1375
+ safeDispatch({
1376
+ effects: languageCompartment.current.reconfigure(
1377
+ languageExtensions(mode, sqlFunctions, stableGetUrlSuggestions),
1378
+ ),
1379
+ });
1380
+ }, [mode, view, sqlFunctions, stableGetUrlSuggestions, safeDispatch]);
1381
+
1382
+ React.useEffect(() => {
1383
+ if (view === null) {
1384
+ return;
1385
+ }
1386
+ safeDispatch({
1387
+ effects: [
1388
+ readOnlyCompartment.current.reconfigure(
1389
+ EditorState.readOnly.of(readOnly),
1390
+ ),
1391
+ ],
1392
+ });
1393
+ }, [readOnly, view, safeDispatch]);
1394
+
1395
+ React.useEffect(() => {
1396
+ if (view === null) {
1397
+ return;
1398
+ }
1399
+ safeDispatch({
1400
+ effects: [
1401
+ themeCompartment.current.reconfigure(
1402
+ isReadOnlyTheme ? readOnlyTheme : baseTheme,
1403
+ ),
1404
+ ],
1405
+ });
1406
+ }, [isReadOnlyTheme, view, safeDispatch]);
1407
+
1408
+ React.useEffect(() => {
1409
+ if (view === null) {
1410
+ return;
1411
+ }
1412
+ safeDispatch({
1413
+ effects: [vimCompartment.current.reconfigure(vimMode ? vim() : [])],
1414
+ });
1415
+ }, [vimMode, view, safeDispatch]);
1416
+
1417
+ React.useEffect(() => {
1418
+ if (view === null) {
1419
+ return;
1420
+ }
1421
+ safeDispatch({
1422
+ effects: [
1423
+ additionalExtensionsCompartment.current.reconfigure(
1424
+ additionalExtensions ?? [],
1425
+ ),
1426
+ ],
1427
+ });
1428
+ }, [additionalExtensions, view, safeDispatch]);
1429
+
1430
+ React.useEffect(() => {
1431
+ if (view === null) {
1432
+ return;
1433
+ }
1434
+ safeDispatch({
1435
+ effects: setIssueLinesEffect.of(issueLineNumbers ?? []),
1436
+ });
1437
+ }, [issueLineNumbers, view, safeDispatch]);
1438
+
1439
+ return <div className="h-full w-full" ref={domRef} id={id} />;
1440
+ }
1441
+
1442
+ const editorInputTheme = EditorView.theme({
1443
+ ".cm-content": {
1444
+ backgroundColor: "var(--color-bg-primary)",
1445
+ border: "1px solid var(--color-border-primary)",
1446
+ borderRadius: "var(--radius-md)",
1447
+ fontFamily: "var(--font-family-sans)",
1448
+ fontWeight: "var(--font-weight-normal)",
1449
+ height: "36px",
1450
+ padding: "8px 12px 8px 12px",
1451
+ fontSize: "14px",
1452
+ },
1453
+ ".cm-editor": {
1454
+ fontSize: "var(--font-size-sm)",
1455
+ color: "var(--color-text-primary)",
1456
+ },
1457
+ ".cm-cursor, .cm-dropCursor": {
1458
+ borderLeftColor: "var(--color-text-primary)",
1459
+ },
1460
+ "&.cm-editor.cm-focused": {
1461
+ outline: "none",
1462
+ },
1463
+ "&.cm-editor.cm-focused .cm-content": {
1464
+ border: "1px solid var(--color-border-link)",
1465
+ borderRadius: "var(--radius-md)",
1466
+ },
1467
+ ".cm-line": {
1468
+ padding: "0",
1469
+ },
1470
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
1471
+ maxHeight: "400px",
1472
+ },
1473
+ ".cm-completionInfo": {
1474
+ display: "none",
1475
+ fontFamily: "var(--font-family-sans)",
1476
+ },
1477
+ ".cm-completionLabel": {
1478
+ color: "var(--color-text-link)",
1479
+ fontSize: "14px",
1480
+ },
1481
+ ".cm-completionDetail": {
1482
+ display: "none",
1483
+ },
1484
+ ".cm-completion-icon": {
1485
+ display: "flex",
1486
+ alignItems: "center",
1487
+ justifyContent: "center",
1488
+ width: "16px",
1489
+ height: "16px",
1490
+ flexShrink: "0",
1491
+ },
1492
+ });
1493
+
1494
+ const KeywordIcon = () => <Terminal size={16} color="#717684" />;
1495
+ const OperatorIcon = () => <ChevronsRight size={16} color="#717684" />;
1496
+ const TableIcon = () => <Table2 size={16} color="#717684" />;
1497
+ const HeaderIcon = () => <Heading size={16} color="#717684" />;
1498
+
1499
+ function getCompletionIcon(completion: Completion): React.FC | null {
1500
+ if (completion.type === "function") return SquareFunctionIcon;
1501
+ if (completion.type === "keyword") return KeywordIcon;
1502
+ if (completion.type === "operator") return OperatorIcon;
1503
+ if (completion.type === "table") return TableIcon;
1504
+ if (completion.type === "header") return HeaderIcon;
1505
+ if (completion.type === "text") return TypCodeIcon;
1506
+ if (completion.type === "type") return ResourceIcon;
1507
+ if (completion.type === "search-param") return null;
1508
+ const detail = completion.detail;
1509
+ if (!detail) {
1510
+ if (completion.type === "variable") return SquareFunctionIcon;
1511
+ return TypCodeIcon;
1512
+ }
1513
+ const typeName = detail.replace(/\[\]$/, "");
1514
+ if (!typeName) return TypCodeIcon;
1515
+ // Search param types (TOKEN, REFERENCE) — no icon
1516
+ if (typeName === typeName.toUpperCase()) return null;
1517
+ const firstChar = typeName[0];
1518
+ if (!firstChar) return TypCodeIcon;
1519
+ const isComplex = firstChar === firstChar.toUpperCase();
1520
+ return isComplex ? ComplexTypeIcon : TypCodeIcon;
1521
+ }
1522
+
1523
+ function renderCompletionIcon(completion: Completion): Node {
1524
+ const container = document.createElement("div");
1525
+ container.className = "cm-completion-icon";
1526
+ const Icon = getCompletionIcon(completion);
1527
+ if (Icon) {
1528
+ flushSync(() => {
1529
+ createRoot(container).render(<Icon />);
1530
+ });
1531
+ } else {
1532
+ container.style.display = "none";
1533
+ }
1534
+ return container;
1535
+ }
1536
+
1537
+ let activeTooltip: HTMLDivElement | null = null;
1538
+ let activeRafId: number | null = null;
1539
+
1540
+ function cleanupActiveTooltip() {
1541
+ activeTooltip?.remove();
1542
+ activeTooltip = null;
1543
+ if (activeRafId !== null) {
1544
+ cancelAnimationFrame(activeRafId);
1545
+ activeRafId = null;
1546
+ }
1547
+ }
1548
+
1549
+ function renderCompletionDetail(completion: Completion): Node | null {
1550
+ const detail = completion.detail;
1551
+ if (!detail) return null;
1552
+
1553
+ const anchor = document.createElement("span");
1554
+ anchor.style.display = "none";
1555
+
1556
+ const showTooltip = () => {
1557
+ cleanupActiveTooltip();
1558
+
1559
+ const tooltip = document.createElement("div");
1560
+ tooltip.textContent = detail;
1561
+ Object.assign(tooltip.style, {
1562
+ position: "fixed",
1563
+ backgroundColor: "var(--color-bg-primary)",
1564
+ border: "1px solid var(--color-border-primary)",
1565
+ borderRadius: "var(--radius-md)",
1566
+ padding: "8px 12px",
1567
+ fontSize: "12px",
1568
+ lineHeight: "1.4",
1569
+ color: "var(--color-text-secondary)",
1570
+ fontFamily: "var(--font-family-sans)",
1571
+ whiteSpace: "normal",
1572
+ width: "280px",
1573
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
1574
+ zIndex: "1000",
1575
+ pointerEvents: "none",
1576
+ });
1577
+ document.body.appendChild(tooltip);
1578
+ activeTooltip = tooltip;
1579
+
1580
+ const autocompleteEl = anchor.closest(".cm-tooltip-autocomplete");
1581
+ const anchorRect = autocompleteEl
1582
+ ? autocompleteEl.getBoundingClientRect()
1583
+ : anchor.getBoundingClientRect();
1584
+ tooltip.style.top = `${anchorRect.top}px`;
1585
+ tooltip.style.left = `${anchorRect.right + 8}px`;
1586
+
1587
+ const checkAlive = () => {
1588
+ if (!anchor.isConnected) {
1589
+ cleanupActiveTooltip();
1590
+ return;
1591
+ }
1592
+ activeRafId = requestAnimationFrame(checkAlive);
1593
+ };
1594
+ activeRafId = requestAnimationFrame(checkAlive);
1595
+ };
1596
+
1597
+ requestAnimationFrame(() => {
1598
+ const option = anchor.closest("li");
1599
+ if (!option) return;
1600
+ option.addEventListener("mouseenter", showTooltip);
1601
+ option.addEventListener("mouseleave", cleanupActiveTooltip);
1602
+ });
1603
+
1604
+ return anchor;
1605
+ }
1606
+
1607
+ export function EditorInput({
1608
+ additionalExtensions,
1609
+ id,
1610
+ defaultValue,
1611
+ currentValue,
1612
+ onChange,
1613
+ }: {
1614
+ additionalExtensions?: Extension[];
1615
+ id: string;
1616
+ defaultValue?: string;
1617
+ currentValue?: string;
1618
+ onChange?: (value: string) => void;
1619
+ }) {
1620
+ const domRef = React.useRef(null);
1621
+ const [view, setView] = React.useState<EditorView | null>(null);
1622
+ const additionalExtensionsCompartment = React.useRef(new Compartment());
1623
+ const onChangeCompartment = React.useRef(new Compartment());
1624
+ const initialValue = React.useRef(defaultValue ?? "");
1625
+
1626
+ React.useEffect(() => {
1627
+ if (!domRef.current) {
1628
+ return;
1629
+ }
1630
+
1631
+ const view = new EditorView({
1632
+ parent: domRef.current,
1633
+ state: EditorState.create({
1634
+ doc: initialValue.current,
1635
+ extensions: [
1636
+ autocompletion({
1637
+ icons: false,
1638
+ maxRenderedOptions: 1000,
1639
+ closeOnBlur: false,
1640
+ addToOptions: [
1641
+ { render: renderCompletionIcon, position: 20 },
1642
+ { render: renderCompletionDetail, position: 80 },
1643
+ ],
1644
+ optionClass: (_completion) =>
1645
+ "!px-2 !py-1 rounded-md aria-selected:!bg-bg-quaternary aria-selected:!text-text-primary hover:!bg-bg-secondary grid grid-cols-[16px_1fr] items-center gap-2",
1646
+ tooltipClass: (_state) =>
1647
+ "!bg-bg-primary rounded-md p-2 shadow-md !border-border-primary !typo-body",
1648
+ compareCompletions: (a, b) => {
1649
+ const aIsProperty = a.type === "property" ? 0 : 1;
1650
+ const bIsProperty = b.type === "property" ? 0 : 1;
1651
+ return aIsProperty - bIsProperty;
1652
+ },
134
1653
  }),
1654
+ closeBrackets(),
1655
+ history(),
1656
+ indentOnInput(),
1657
+ editorInputTheme,
1658
+ EditorView.contentAttributes.of({ "data-gramm": "false" }),
1659
+ ...customSearchExtension,
1660
+ additionalExtensionsCompartment.current.of([]),
1661
+ onChangeCompartment.current.of([]),
1662
+ keymap.of([
1663
+ { key: "Tab", preventDefault: true, run: acceptCompletion },
1664
+ ...closeBracketsKeymap,
1665
+ ...defaultKeymap,
1666
+ ...searchKeymap,
1667
+ ...historyKeymap,
1668
+ ...foldKeymap,
1669
+ ...completionKeymap,
1670
+ ...lintKeymap,
1671
+ ]),
135
1672
  ],
136
1673
  }),
137
1674
  });
138
1675
 
139
- return () => view.destroy();
140
- }, [defaultValue, onChange]);
1676
+ setView(() => view);
1677
+
1678
+ return () => {
1679
+ view.destroy();
1680
+ setView(() => null);
1681
+ };
1682
+ }, []);
1683
+
1684
+ React.useEffect(() => {
1685
+ if (view === null) {
1686
+ return;
1687
+ }
1688
+ view.dispatch({
1689
+ effects: [
1690
+ additionalExtensionsCompartment.current.reconfigure(
1691
+ additionalExtensions ?? [],
1692
+ ),
1693
+ ],
1694
+ });
1695
+ }, [additionalExtensions, view]);
1696
+
1697
+ React.useEffect(() => {
1698
+ view?.dispatch({
1699
+ effects: onChangeCompartment.current.reconfigure([
1700
+ EditorView.updateListener.of((update) => {
1701
+ if (update.docChanged && onChange) {
1702
+ onChange(update.view.state.doc.toString());
1703
+ }
1704
+ }),
1705
+ ]),
1706
+ });
1707
+ }, [view, onChange]);
1708
+
1709
+ React.useEffect(() => {
1710
+ if (!view || currentValue === undefined) {
1711
+ return;
1712
+ }
1713
+
1714
+ const currentDoc = view.state.doc.toString();
1715
+ if (currentDoc !== currentValue) {
1716
+ view.dispatch({
1717
+ changes: {
1718
+ from: 0,
1719
+ to: currentDoc.length,
1720
+ insert: currentValue,
1721
+ },
1722
+ });
1723
+ }
1724
+ }, [currentValue, view]);
141
1725
 
142
- return <div className="h-full w-full" ref={editorRef} />;
1726
+ return <div className="h-full w-full" ref={domRef} id={id} />;
143
1727
  }