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

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 +2349 -754
  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 +1850 -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 +1792 -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 +897 -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 +50 -20
  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 +2322 -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 +1944 -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 +1112 -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 +254 -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 +50 -20
  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,442 @@ 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
+ Columns2,
66
+ Heading,
67
+ Table2,
68
+ Terminal,
69
+ X,
70
+ } from "lucide-react";
33
71
  import * as React from "react";
72
+ import { flushSync } from "react-dom";
73
+ import { createRoot } from "react-dom/client";
74
+ import {
75
+ ComplexTypeIcon,
76
+ ResourceIcon,
77
+ SquareFunctionIcon,
78
+ TypCodeIcon,
79
+ } from "../../icons";
80
+ import {
81
+ buildFhirCompletionExtension,
82
+ type ExpandValueSet,
83
+ fhirDiagnosticsField,
84
+ type GetStructureDefinitions,
85
+ } from "./fhir-autocomplete";
86
+ import { type GetUrlSuggestions, http } from "./http";
87
+ import {
88
+ buildSqlCompletionExtensions,
89
+ fetchSqlMetadata,
90
+ type SqlConfig,
91
+ } from "./sql-completion";
92
+
93
+ // --- Issue lines: gutter highlighting, line background, hover tooltip ---
94
+
95
+ type IssueLine = { line: number; message?: string };
96
+
97
+ class ErrorLineGutterMarker extends GutterMarker {
98
+ elementClass = "cm-errorLineGutter";
99
+ }
100
+ const errorLineMarker = new ErrorLineGutterMarker();
101
+ const errorLineDecoration = Decoration.line({ class: "cm-errorLine" });
102
+
103
+ const setIssueLinesEffect = StateEffect.define<IssueLine[]>();
104
+
105
+ let errorTooltipEl: HTMLDivElement | null = null;
106
+
107
+ function formatErrorTypeTitle(code: string): string {
108
+ return code
109
+ .split("-")
110
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
111
+ .join(" ");
112
+ }
113
+
114
+ function renderErrorCard(msg: string): HTMLElement {
115
+ const card = document.createElement("div");
116
+ Object.assign(card.style, {
117
+ backgroundColor: "var(--color-bg-primary)",
118
+ border: "1px solid var(--color-border-primary)",
119
+ borderRadius: "var(--radius-md)",
120
+ padding: "6px 10px",
121
+ boxShadow: "0 2px 6px rgba(0, 0, 0, 0.08)",
122
+ });
123
+ const newlineIdx = msg.indexOf("\n");
124
+ if (newlineIdx !== -1) {
125
+ const title = msg.slice(0, newlineIdx);
126
+ const body = msg.slice(newlineIdx + 1);
127
+
128
+ const titleEl = document.createElement("div");
129
+ titleEl.textContent = formatErrorTypeTitle(title);
130
+ Object.assign(titleEl.style, { fontWeight: "600" });
131
+
132
+ const hr = document.createElement("div");
133
+ Object.assign(hr.style, {
134
+ borderTop: "1px solid var(--color-border-primary)",
135
+ margin: "4px 0",
136
+ });
137
+
138
+ const bodyEl = document.createElement("div");
139
+ bodyEl.textContent = body;
140
+ Object.assign(bodyEl.style, { whiteSpace: "pre-wrap" });
141
+
142
+ card.append(titleEl, hr, bodyEl);
143
+ } else {
144
+ card.textContent = msg;
145
+ card.style.whiteSpace = "pre-wrap";
146
+ }
147
+ return card;
148
+ }
149
+
150
+ function showErrorTooltip(message: string, x: number, y: number) {
151
+ hideErrorTooltip();
152
+
153
+ const tooltip = document.createElement("div");
154
+ Object.assign(tooltip.style, {
155
+ position: "fixed",
156
+ fontSize: "12px",
157
+ lineHeight: "1.4",
158
+ color: "var(--color-text-error-primary)",
159
+ fontFamily: "var(--font-family-sans)",
160
+ zIndex: "1000",
161
+ pointerEvents: "none",
162
+ maxWidth: "400px",
163
+ display: "flex",
164
+ flexDirection: "column",
165
+ gap: "6px",
166
+ });
167
+
168
+ const parts = message.split("\n\x00\n");
169
+ for (const part of parts) {
170
+ tooltip.append(renderErrorCard(part ?? ""));
171
+ }
172
+
173
+ document.body.appendChild(tooltip);
174
+ errorTooltipEl = tooltip;
175
+
176
+ const tooltipRect = tooltip.getBoundingClientRect();
177
+ let top = y - tooltipRect.height - 8;
178
+ // If tooltip goes above viewport, show below cursor instead
179
+ if (top < 4) {
180
+ top = y + 20;
181
+ }
182
+ // If it still goes below viewport, clamp to bottom
183
+ if (top + tooltipRect.height > window.innerHeight - 4) {
184
+ top = window.innerHeight - tooltipRect.height - 4;
185
+ }
186
+ // Final clamp to top
187
+ if (top < 4) top = 4;
188
+ tooltip.style.left = `${x}px`;
189
+ tooltip.style.top = `${top}px`;
190
+ }
191
+
192
+ function hideErrorTooltip() {
193
+ errorTooltipEl?.remove();
194
+ errorTooltipEl = null;
195
+ }
196
+
197
+ const issueLinesField = StateField.define<{
198
+ gutterMarkers: RangeSet<GutterMarker>;
199
+ lineDecorations: RangeSet<Decoration>;
200
+ messages: Map<number, string>;
201
+ }>({
202
+ create() {
203
+ return {
204
+ gutterMarkers: RangeSet.empty,
205
+ lineDecorations: Decoration.none,
206
+ messages: new Map(),
207
+ };
208
+ },
209
+ update(state, tr) {
210
+ for (const effect of tr.effects) {
211
+ if (effect.is(setIssueLinesEffect)) {
212
+ const markers: { from: number; to: number; value: GutterMarker }[] = [];
213
+ const lineDecos: {
214
+ from: number;
215
+ to: number;
216
+ value: Decoration;
217
+ }[] = [];
218
+ const messages = new Map<number, string>();
219
+ const doc = tr.state.doc;
220
+
221
+ for (const issue of effect.value) {
222
+ if (issue.line >= 1 && issue.line <= doc.lines) {
223
+ const line = doc.line(issue.line);
224
+ markers.push(errorLineMarker.range(line.from));
225
+ lineDecos.push(errorLineDecoration.range(line.from));
226
+ if (issue.message) {
227
+ messages.set(issue.line, issue.message);
228
+ }
229
+ }
230
+ }
231
+
232
+ return {
233
+ gutterMarkers: RangeSet.of(markers, true),
234
+ lineDecorations: Decoration.set(lineDecos, true),
235
+ messages,
236
+ };
237
+ }
238
+ }
239
+ if (tr.docChanged) {
240
+ try {
241
+ return {
242
+ gutterMarkers: state.gutterMarkers.map(tr.changes),
243
+ lineDecorations: state.lineDecorations.map(tr.changes),
244
+ messages: state.messages,
245
+ };
246
+ } catch {
247
+ return {
248
+ gutterMarkers: RangeSet.empty,
249
+ lineDecorations: Decoration.none,
250
+ messages: new Map(),
251
+ };
252
+ }
253
+ }
254
+ return state;
255
+ },
256
+ provide(field) {
257
+ return [
258
+ gutterLineClass.from(field, (val) => val.gutterMarkers),
259
+ EditorView.decorations.from(field, (val) => val.lineDecorations),
260
+ ];
261
+ },
262
+ });
263
+
264
+ function getErrorMessageForLine(
265
+ view: EditorView,
266
+ lineNo: number,
267
+ ): string | undefined {
268
+ const issueMsg = view.state.field(issueLinesField).messages.get(lineNo);
269
+ if (issueMsg) return issueMsg;
270
+ try {
271
+ return view.state.field(fhirDiagnosticsField).messages.get(lineNo);
272
+ } catch {
273
+ return undefined;
274
+ }
275
+ }
34
276
 
35
- const baseTheme = EditorView.baseTheme({
277
+ function handleErrorTooltipMove(event: Event, view: EditorView) {
278
+ const target = event.target as HTMLElement;
279
+ const mouseEvent = event as MouseEvent;
280
+
281
+ // Check gutter line number
282
+ const gutterEl = target.closest(
283
+ ".cm-lineNumbers .cm-gutterElement",
284
+ ) as HTMLElement | null;
285
+ if (gutterEl) {
286
+ const lineNo = Number.parseInt(gutterEl.textContent ?? "", 10);
287
+ if (!Number.isNaN(lineNo)) {
288
+ const message = getErrorMessageForLine(view, lineNo);
289
+ if (message) {
290
+ showErrorTooltip(message, mouseEvent.clientX, mouseEvent.clientY);
291
+ return false;
292
+ }
293
+ }
294
+ hideErrorTooltip();
295
+ return false;
296
+ }
297
+
298
+ // Check content line (cm-line) — follow cursor
299
+ const lineEl = target.closest(".cm-line") as HTMLElement | null;
300
+ if (lineEl) {
301
+ const pos = view.posAtDOM(lineEl);
302
+ const lineNo = view.state.doc.lineAt(pos).number;
303
+ const message = getErrorMessageForLine(view, lineNo);
304
+ if (message) {
305
+ showErrorTooltip(message, mouseEvent.clientX, mouseEvent.clientY);
306
+ return false;
307
+ }
308
+ }
309
+
310
+ hideErrorTooltip();
311
+ return false;
312
+ }
313
+
314
+ const errorTooltipHandler = EditorView.domEventHandlers({
315
+ mouseover: handleErrorTooltipMove,
316
+ mousemove: handleErrorTooltipMove,
317
+ mouseleave() {
318
+ hideErrorTooltip();
319
+ return false;
320
+ },
321
+ });
322
+
323
+ const baseTheme = EditorView.theme({
36
324
  "&": {
37
325
  backgroundColor: "var(--color-bg-primary)",
38
326
  height: "100%",
39
327
  width: "100%",
40
328
  fontSize: "14px",
329
+ },
330
+ "&.cm-editor": {
331
+ paddingTop: "0 !important",
332
+ paddingBottom: "0 !important",
333
+ },
334
+ ".cm-scroller": {
335
+ overflow: "auto",
41
336
  paddingTop: "8px",
42
337
  paddingBottom: "8px",
43
338
  },
339
+ ".cm-content": {
340
+ fontFamily: "var(--font-family-mono)",
341
+ padding: "0",
342
+ },
343
+ "&.cm-focused": {
344
+ outline: "none",
345
+ },
346
+ ".cm-cursor, .cm-dropCursor": {
347
+ borderLeftColor: "var(--color-text-primary)",
348
+ },
349
+ ".cm-gutter": {
350
+ fontFamily: "var(--font-family-mono)",
351
+ },
352
+ ".cm-gutters": {
353
+ backgroundColor: "transparent",
354
+ border: "none",
355
+ },
356
+ ".cm-lineNumbers": {
357
+ minWidth: "3.5ch",
358
+ },
359
+ ".cm-lineNumbers .cm-gutterElement": {
360
+ minWidth: "3.5ch",
361
+ paddingRight: "4px",
362
+ color: "var(--color-text-quaternary)",
363
+ },
364
+ ".cm-lineNumbers .cm-gutterElement.cm-activeLineGutter": {
365
+ backgroundColor: "var(--color-bg-primary)",
366
+ color: "var(--color-text-secondary)",
367
+ },
368
+ ".cm-activeLineGutter": {
369
+ backgroundColor: "transparent !important",
370
+ },
371
+ ".cm-activeLine": {
372
+ backgroundColor: "transparent !important",
373
+ },
374
+ ".cm-lineNumbers .cm-gutterElement.cm-errorLineGutter": {
375
+ color: "var(--color-text-error-primary)",
376
+ backgroundColor:
377
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
378
+ },
379
+ ".cm-foldGutter .cm-gutterElement.cm-errorLineGutter": {
380
+ color: "var(--color-text-error-primary)",
381
+ backgroundColor:
382
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
383
+ display: "flex",
384
+ alignItems: "center",
385
+ justifyContent: "center",
386
+ },
387
+ ".cm-errorLine": {
388
+ backgroundColor:
389
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
390
+ },
391
+ });
392
+
393
+ const completionTheme = EditorView.theme({
394
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
395
+ maxHeight: "400px",
396
+ },
397
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {
398
+ display: "flex",
399
+ alignItems: "center",
400
+ gap: "8px",
401
+ },
402
+ ".cm-completionLabel": {
403
+ flex: "1",
404
+ minWidth: "0",
405
+ fontFamily: "var(--font-family-mono)",
406
+ fontSize: "var(--font-size-sm)",
407
+ lineHeight: "var(--font-leading-5)",
408
+ },
409
+ ".cm-completionMatchedText": {
410
+ textDecoration: "none",
411
+ fontWeight: "600",
412
+ color: "var(--color-text-link)",
413
+ },
414
+ ".cm-completionDetail": {
415
+ color: "var(--color-text-tertiary)",
416
+ fontSize: "12px",
417
+ marginLeft: "auto",
418
+ whiteSpace: "nowrap",
419
+ },
420
+ ".cm-completionInfo": {
421
+ backgroundColor: "var(--color-bg-primary)",
422
+ border: "1px solid var(--color-border-primary)",
423
+ borderRadius: "var(--radius-md)",
424
+ color: "var(--color-text-secondary)",
425
+ fontFamily: "var(--font-family-mono)",
426
+ fontSize: "14px",
427
+ padding: "8px 12px",
428
+ marginLeft: "8px",
429
+ lineHeight: "1.4",
430
+ whiteSpace: "normal",
431
+ maxWidth: "300px",
432
+ },
433
+ ".cm-completion-icon": {
434
+ display: "flex",
435
+ alignItems: "center",
436
+ justifyContent: "center",
437
+ width: "16px",
438
+ height: "16px",
439
+ flexShrink: "0",
440
+ },
441
+ });
442
+
443
+ const readOnlyTheme = EditorView.theme({
444
+ "&": {
445
+ backgroundColor: "var(--color-bg-secondary)",
446
+ height: "100%",
447
+ width: "100%",
448
+ fontSize: "14px",
449
+ },
450
+ "&.cm-editor": {
451
+ paddingTop: "0 !important",
452
+ paddingBottom: "0 !important",
453
+ },
44
454
  ".cm-scroller": {
45
455
  overflow: "auto",
456
+ paddingTop: "8px",
457
+ paddingBottom: "8px",
46
458
  },
47
459
  ".cm-content": {
48
460
  fontFamily: "var(--font-family-mono)",
@@ -55,89 +467,1579 @@ const baseTheme = EditorView.baseTheme({
55
467
  fontFamily: "var(--font-family-mono)",
56
468
  },
57
469
  ".cm-gutters": {
58
- backgroundColor: "var(--color-bg-primary)",
470
+ backgroundColor: "var(--color-bg-secondary)",
59
471
  border: "none",
60
472
  },
61
473
  ".cm-lineNumbers": {
62
- paddingLeft: "16px",
474
+ minWidth: "3.5ch",
475
+ },
476
+ ".cm-lineNumbers .cm-gutterElement": {
477
+ minWidth: "3.5ch",
478
+ paddingRight: "4px",
479
+ color: "var(--color-text-quaternary)",
480
+ },
481
+ ".cm-lineNumbers .cm-gutterElement.cm-activeLineGutter": {
482
+ backgroundColor: "var(--color-bg-secondary)",
483
+ color: "var(--color-text-secondary)",
63
484
  },
64
485
  ".cm-activeLineGutter": {
65
- backgroundColor: "var(--color-bg-primary)",
66
- color: "var(--color-text-primary)",
486
+ backgroundColor: "transparent !important",
67
487
  },
68
488
  ".cm-activeLine": {
69
- backgroundColor: "rgba(255, 255, 255, 0)",
489
+ backgroundColor: "transparent !important",
490
+ },
491
+ ".cm-lineNumbers .cm-gutterElement.cm-errorLineGutter": {
492
+ color: "var(--color-text-error-primary)",
493
+ backgroundColor:
494
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
495
+ },
496
+ ".cm-foldGutter .cm-gutterElement.cm-errorLineGutter": {
497
+ color: "var(--color-text-error-primary)",
498
+ backgroundColor:
499
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
500
+ display: "flex",
501
+ alignItems: "center",
502
+ justifyContent: "center",
503
+ },
504
+ ".cm-errorLine": {
505
+ backgroundColor:
506
+ "color-mix(in srgb, var(--color-text-error-primary) 7%, transparent)",
70
507
  },
71
508
  });
72
509
 
510
+ const iconButtonStyle: React.CSSProperties = {
511
+ display: "flex",
512
+ alignItems: "center",
513
+ justifyContent: "center",
514
+ width: "28px",
515
+ height: "28px",
516
+ border: "none",
517
+ borderRadius: "var(--radius-sm)",
518
+ background: "transparent",
519
+ color: "var(--color-text-secondary)",
520
+ cursor: "pointer",
521
+ padding: 0,
522
+ };
523
+
524
+ function getMatchInfo(
525
+ state: EditorState,
526
+ searchText: string,
527
+ ): { current: number; total: number } {
528
+ if (!searchText) return { current: 0, total: 0 };
529
+ const doc = state.doc.toString();
530
+ const sel = state.selection.main;
531
+ const lowerDoc = doc.toLowerCase();
532
+ const lowerSearch = searchText.toLowerCase();
533
+ const searchLen = searchText.length;
534
+ let total = 0;
535
+ let current = 0;
536
+ let pos = 0;
537
+ for (;;) {
538
+ const idx = lowerDoc.indexOf(lowerSearch, pos);
539
+ if (idx === -1) break;
540
+ total++;
541
+ if (idx === sel.from && idx + searchLen === sel.to) {
542
+ current = total;
543
+ }
544
+ pos = idx + 1;
545
+ }
546
+ return { current, total };
547
+ }
548
+
549
+ function createSearchPanel(view: EditorView) {
550
+ const dom = document.createElement("div");
551
+ const root = createRoot(dom);
552
+
553
+ const panelRef: {
554
+ setSearch: ((v: string) => void) | null;
555
+ setMatch: ((info: { current: number; total: number }) => void) | null;
556
+ lastSearch: string;
557
+ lastCurrent: number;
558
+ lastTotal: number;
559
+ } = {
560
+ setSearch: null,
561
+ setMatch: null,
562
+ lastSearch: "",
563
+ lastCurrent: 0,
564
+ lastTotal: 0,
565
+ };
566
+
567
+ function Panel() {
568
+ const [value, setValue] = React.useState(
569
+ () => getSearchQuery(view.state).search,
570
+ );
571
+ const [match, setMatchState] = React.useState({ current: 0, total: 0 });
572
+
573
+ panelRef.setSearch = setValue;
574
+ panelRef.setMatch = setMatchState;
575
+
576
+ const handleChange = (newValue: string) => {
577
+ setValue(newValue);
578
+ view.dispatch({
579
+ effects: setSearchQuery.of(new SearchQuery({ search: newValue })),
580
+ });
581
+ };
582
+
583
+ return (
584
+ <div
585
+ style={{
586
+ display: "flex",
587
+ alignItems: "center",
588
+ gap: "2px",
589
+ padding: "6px 8px",
590
+ marginTop: "4px",
591
+ backgroundColor: "var(--color-bg-primary)",
592
+ border: "1px solid var(--color-border-primary)",
593
+ borderRadius: "var(--radius-md)",
594
+ boxShadow: "0 2px 8px rgba(0, 0, 0, 0.12)",
595
+ }}
596
+ >
597
+ <input
598
+ value={value}
599
+ onChange={(e) => handleChange(e.target.value)}
600
+ onKeyDown={(e) => {
601
+ if (e.key === "Enter") {
602
+ e.preventDefault();
603
+ if (e.shiftKey) findPrevious(view);
604
+ else findNext(view);
605
+ }
606
+ if (e.key === "Escape") {
607
+ e.preventDefault();
608
+ closeSearchPanel(view);
609
+ view.focus();
610
+ }
611
+ }}
612
+ placeholder="Find..."
613
+ style={{
614
+ height: "28px",
615
+ padding: "0 8px",
616
+ border: "1px solid var(--color-border-primary)",
617
+ borderRadius: "var(--radius-md)",
618
+ fontSize: "13px",
619
+ fontFamily: "var(--font-family-sans)",
620
+ backgroundColor: "var(--color-bg-primary)",
621
+ color: "var(--color-text-primary)",
622
+ outline: "none",
623
+ flex: "0 0 200px",
624
+ }}
625
+ />
626
+ <span
627
+ style={{
628
+ fontSize: "12px",
629
+ fontFamily: "var(--font-family-sans)",
630
+ color: "var(--color-text-secondary)",
631
+ whiteSpace: "nowrap",
632
+ minWidth: "70px",
633
+ textAlign: "center",
634
+ visibility: value ? "visible" : "hidden",
635
+ }}
636
+ >
637
+ {value
638
+ ? match.total > 0
639
+ ? `${match.current} of ${match.total}`
640
+ : "No results"
641
+ : "No results"}
642
+ </span>
643
+ <button
644
+ type="button"
645
+ onClick={() => findPrevious(view)}
646
+ title="Previous match"
647
+ style={iconButtonStyle}
648
+ >
649
+ <ChevronUp size={16} />
650
+ </button>
651
+ <button
652
+ type="button"
653
+ onClick={() => findNext(view)}
654
+ title="Next match"
655
+ style={iconButtonStyle}
656
+ >
657
+ <ChevronDown size={16} />
658
+ </button>
659
+ <button
660
+ type="button"
661
+ onClick={() => {
662
+ closeSearchPanel(view);
663
+ view.focus();
664
+ }}
665
+ title="Close"
666
+ style={iconButtonStyle}
667
+ >
668
+ <X size={14} />
669
+ </button>
670
+ </div>
671
+ );
672
+ }
673
+
674
+ flushSync(() => {
675
+ root.render(<Panel />);
676
+ });
677
+
678
+ const input = dom.querySelector("input");
679
+ if (input) input.setAttribute("main-field", "true");
680
+
681
+ // Compute initial match info
682
+ const q = getSearchQuery(view.state);
683
+ panelRef.lastSearch = q.search;
684
+ if (q.search) {
685
+ const info = getMatchInfo(view.state, q.search);
686
+ panelRef.lastCurrent = info.current;
687
+ panelRef.lastTotal = info.total;
688
+ panelRef.setMatch?.(info);
689
+ }
690
+
691
+ return {
692
+ dom,
693
+ top: true,
694
+ mount() {
695
+ const el = dom.querySelector("input");
696
+ if (el) {
697
+ el.focus();
698
+ el.select();
699
+ }
700
+ },
701
+ update(update: ViewUpdate) {
702
+ const query = getSearchQuery(update.state);
703
+
704
+ if (query.search !== panelRef.lastSearch) {
705
+ panelRef.setSearch?.(query.search);
706
+ }
707
+ panelRef.lastSearch = query.search;
708
+
709
+ const info = getMatchInfo(update.state, query.search);
710
+ if (
711
+ info.current !== panelRef.lastCurrent ||
712
+ info.total !== panelRef.lastTotal
713
+ ) {
714
+ panelRef.lastCurrent = info.current;
715
+ panelRef.lastTotal = info.total;
716
+ panelRef.setMatch?.(info);
717
+ }
718
+ },
719
+ destroy() {
720
+ root.unmount();
721
+ panelRef.setSearch = null;
722
+ panelRef.setMatch = null;
723
+ },
724
+ };
725
+ }
726
+
727
+ const searchPanelTheme = EditorView.theme({
728
+ "& .cm-panels-top": {
729
+ position: "absolute",
730
+ top: "8px",
731
+ right: "4px",
732
+ left: "auto",
733
+ zIndex: "10",
734
+ backgroundColor: "transparent",
735
+ border: "none",
736
+ },
737
+ ".cm-searchMatch": {
738
+ backgroundColor: "var(--color-blue-200) !important",
739
+ },
740
+ ".cm-searchMatch-selected": {
741
+ backgroundColor: "var(--color-blue-400) !important",
742
+ },
743
+ ".cm-selectionMatch": {
744
+ backgroundColor: "var(--color-blue-100) !important",
745
+ },
746
+ });
747
+
748
+ const customSearchExtension = [
749
+ search({ createPanel: createSearchPanel }),
750
+ searchPanelTheme,
751
+ ];
752
+
73
753
  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" },
754
+ { tag: tags.propertyName, color: "var(--hs-syntax-property)" },
755
+ { tag: tags.string, color: "var(--hs-syntax-string)" },
756
+ { tag: tags.number, color: "var(--hs-syntax-number)" },
757
+ { tag: tags.bool, color: "var(--hs-syntax-keyword)" },
758
+ { tag: tags.null, color: "var(--hs-syntax-keyword)" },
759
+ { tag: tags.keyword, color: "var(--hs-syntax-keyword)" },
760
+ { tag: tags.operatorKeyword, color: "var(--hs-syntax-string)" },
761
+ { tag: tags.controlKeyword, color: "var(--hs-syntax-property)" },
762
+ { tag: tags.typeName, color: "var(--hs-syntax-number)" },
763
+ { tag: tags.variableName, color: "var(--hs-syntax-property)" },
764
+ { tag: tags.operator, color: "var(--hs-syntax-string)" },
765
+ { tag: tags.comment, color: "var(--hs-syntax-comment)" },
766
+ { tag: tags.lineComment, color: "var(--hs-syntax-comment)" },
767
+ { tag: tags.blockComment, color: "var(--hs-syntax-comment)" },
79
768
  ]);
80
769
 
770
+ const SQL_KEYWORDS = [
771
+ "select",
772
+ "from",
773
+ "where",
774
+ "and",
775
+ "or",
776
+ "not",
777
+ "in",
778
+ "between",
779
+ "like",
780
+ "ilike",
781
+ "similar",
782
+ "insert",
783
+ "update",
784
+ "delete",
785
+ "create",
786
+ "drop",
787
+ "alter",
788
+ "table",
789
+ "index",
790
+ "view",
791
+ "materialized",
792
+ "schema",
793
+ "sequence",
794
+ "type",
795
+ "extension",
796
+ "function",
797
+ "procedure",
798
+ "trigger",
799
+ "join",
800
+ "inner",
801
+ "left",
802
+ "right",
803
+ "outer",
804
+ "full",
805
+ "cross",
806
+ "lateral",
807
+ "natural",
808
+ "on",
809
+ "using",
810
+ "as",
811
+ "order",
812
+ "by",
813
+ "group",
814
+ "having",
815
+ "limit",
816
+ "offset",
817
+ "fetch",
818
+ "first",
819
+ "next",
820
+ "rows",
821
+ "only",
822
+ "union",
823
+ "intersect",
824
+ "except",
825
+ "distinct",
826
+ "all",
827
+ "exists",
828
+ "any",
829
+ "some",
830
+ "case",
831
+ "when",
832
+ "then",
833
+ "else",
834
+ "end",
835
+ "null",
836
+ "true",
837
+ "false",
838
+ "is",
839
+ "isnull",
840
+ "notnull",
841
+ "asc",
842
+ "desc",
843
+ "nulls",
844
+ "with",
845
+ "recursive",
846
+ "returning",
847
+ "into",
848
+ "values",
849
+ "set",
850
+ "default",
851
+ "begin",
852
+ "commit",
853
+ "rollback",
854
+ "savepoint",
855
+ "release",
856
+ "transaction",
857
+ "explain",
858
+ "analyze",
859
+ "verbose",
860
+ "costs",
861
+ "buffers",
862
+ "format",
863
+ "grant",
864
+ "revoke",
865
+ "truncate",
866
+ "cascade",
867
+ "restrict",
868
+ "vacuum",
869
+ "reindex",
870
+ "cluster",
871
+ "copy",
872
+ "do",
873
+ "perform",
874
+ "raise",
875
+ "notice",
876
+ "exception",
877
+ "if",
878
+ "elsif",
879
+ "loop",
880
+ "while",
881
+ "for",
882
+ "foreach",
883
+ "return",
884
+ "returns",
885
+ "language",
886
+ "plpgsql",
887
+ "declare",
888
+ "primary",
889
+ "key",
890
+ "foreign",
891
+ "references",
892
+ "unique",
893
+ "check",
894
+ "constraint",
895
+ "not",
896
+ "null",
897
+ "add",
898
+ "column",
899
+ "rename",
900
+ "to",
901
+ "owner",
902
+ "tablespace",
903
+ "temporary",
904
+ "temp",
905
+ "unlogged",
906
+ "if",
907
+ "replace",
908
+ "or",
909
+ "conflict",
910
+ "nothing",
911
+ "window",
912
+ "partition",
913
+ "over",
914
+ "range",
915
+ "unbounded",
916
+ "preceding",
917
+ "following",
918
+ "current",
919
+ "row",
920
+ "groups",
921
+ "exclude",
922
+ "ties",
923
+ "filter",
924
+ "within",
925
+ ];
926
+
927
+ const SQL_BUILTIN = [
928
+ // types
929
+ "varchar",
930
+ "char",
931
+ "text",
932
+ "integer",
933
+ "int",
934
+ "smallint",
935
+ "bigint",
936
+ "decimal",
937
+ "numeric",
938
+ "float",
939
+ "real",
940
+ "double",
941
+ "precision",
942
+ "boolean",
943
+ "bool",
944
+ "date",
945
+ "time",
946
+ "timestamp",
947
+ "timestamptz",
948
+ "interval",
949
+ "uuid",
950
+ "json",
951
+ "jsonb",
952
+ "bytea",
953
+ "serial",
954
+ "bigserial",
955
+ "smallserial",
956
+ "money",
957
+ "inet",
958
+ "cidr",
959
+ "macaddr",
960
+ "point",
961
+ "line",
962
+ "lseg",
963
+ "box",
964
+ "path",
965
+ "polygon",
966
+ "circle",
967
+ "tsquery",
968
+ "tsvector",
969
+ "xml",
970
+ "oid",
971
+ "regclass",
972
+ "regtype",
973
+ // aggregate functions
974
+ "count",
975
+ "sum",
976
+ "avg",
977
+ "min",
978
+ "max",
979
+ "array_agg",
980
+ "string_agg",
981
+ "json_agg",
982
+ "jsonb_agg",
983
+ "json_object_agg",
984
+ "jsonb_object_agg",
985
+ "bool_and",
986
+ "bool_or",
987
+ "every",
988
+ "bit_and",
989
+ "bit_or",
990
+ // window functions
991
+ "row_number",
992
+ "rank",
993
+ "dense_rank",
994
+ "percent_rank",
995
+ "cume_dist",
996
+ "ntile",
997
+ "lag",
998
+ "lead",
999
+ "first_value",
1000
+ "last_value",
1001
+ "nth_value",
1002
+ // string functions
1003
+ "coalesce",
1004
+ "nullif",
1005
+ "greatest",
1006
+ "least",
1007
+ "concat",
1008
+ "concat_ws",
1009
+ "substring",
1010
+ "upper",
1011
+ "lower",
1012
+ "trim",
1013
+ "ltrim",
1014
+ "rtrim",
1015
+ "length",
1016
+ "char_length",
1017
+ "octet_length",
1018
+ "position",
1019
+ "replace",
1020
+ "translate",
1021
+ "left",
1022
+ "right",
1023
+ "repeat",
1024
+ "reverse",
1025
+ "split_part",
1026
+ "regexp_match",
1027
+ "regexp_matches",
1028
+ "regexp_replace",
1029
+ "regexp_split_to_array",
1030
+ "regexp_split_to_table",
1031
+ "format",
1032
+ "encode",
1033
+ "decode",
1034
+ "md5",
1035
+ "starts_with",
1036
+ // date/time functions
1037
+ "now",
1038
+ "current_date",
1039
+ "current_time",
1040
+ "current_timestamp",
1041
+ "localtime",
1042
+ "localtimestamp",
1043
+ "clock_timestamp",
1044
+ "statement_timestamp",
1045
+ "transaction_timestamp",
1046
+ "timeofday",
1047
+ "age",
1048
+ "date_part",
1049
+ "date_trunc",
1050
+ "extract",
1051
+ "make_date",
1052
+ "make_time",
1053
+ "make_timestamp",
1054
+ "make_timestamptz",
1055
+ "make_interval",
1056
+ "to_char",
1057
+ "to_date",
1058
+ "to_timestamp",
1059
+ "to_number",
1060
+ // json/jsonb functions
1061
+ "json_build_object",
1062
+ "jsonb_build_object",
1063
+ "json_build_array",
1064
+ "jsonb_build_array",
1065
+ "json_extract_path",
1066
+ "jsonb_extract_path",
1067
+ "json_extract_path_text",
1068
+ "jsonb_extract_path_text",
1069
+ "jsonb_set",
1070
+ "jsonb_insert",
1071
+ "jsonb_strip_nulls",
1072
+ "jsonb_pretty",
1073
+ "jsonb_typeof",
1074
+ "jsonb_each",
1075
+ "jsonb_each_text",
1076
+ "jsonb_array_elements",
1077
+ "jsonb_array_elements_text",
1078
+ "jsonb_array_length",
1079
+ "jsonb_object_keys",
1080
+ "jsonb_to_record",
1081
+ "jsonb_to_recordset",
1082
+ "jsonb_populate_record",
1083
+ "jsonb_populate_recordset",
1084
+ "jsonb_path_query",
1085
+ "jsonb_path_query_array",
1086
+ "jsonb_path_query_first",
1087
+ "jsonb_path_exists",
1088
+ "to_json",
1089
+ "to_jsonb",
1090
+ "row_to_json",
1091
+ // array functions
1092
+ "array_length",
1093
+ "array_dims",
1094
+ "array_lower",
1095
+ "array_upper",
1096
+ "array_append",
1097
+ "array_prepend",
1098
+ "array_cat",
1099
+ "array_remove",
1100
+ "array_replace",
1101
+ "array_position",
1102
+ "array_positions",
1103
+ "array_to_string",
1104
+ "string_to_array",
1105
+ "unnest",
1106
+ "cardinality",
1107
+ // set-returning functions
1108
+ "generate_series",
1109
+ "generate_subscripts",
1110
+ // math functions
1111
+ "abs",
1112
+ "ceil",
1113
+ "ceiling",
1114
+ "floor",
1115
+ "round",
1116
+ "trunc",
1117
+ "sign",
1118
+ "sqrt",
1119
+ "cbrt",
1120
+ "power",
1121
+ "exp",
1122
+ "ln",
1123
+ "log",
1124
+ "mod",
1125
+ "random",
1126
+ "setseed",
1127
+ "pi",
1128
+ "degrees",
1129
+ "radians",
1130
+ // system functions
1131
+ "pg_typeof",
1132
+ "pg_size_pretty",
1133
+ "pg_table_size",
1134
+ "pg_indexes_size",
1135
+ "pg_total_relation_size",
1136
+ "pg_relation_size",
1137
+ "pg_database_size",
1138
+ "pg_cancel_backend",
1139
+ "pg_terminate_backend",
1140
+ "pg_stat_activity",
1141
+ "pg_stat_statements",
1142
+ "pg_advisory_lock",
1143
+ "pg_advisory_unlock",
1144
+ "pg_try_advisory_lock",
1145
+ // cast/conversion
1146
+ "cast",
1147
+ "pg_get_functiondef",
1148
+ "pg_get_viewdef",
1149
+ "pg_get_indexdef",
1150
+ // misc
1151
+ "exists",
1152
+ "in",
1153
+ "between",
1154
+ "like",
1155
+ "ilike",
1156
+ "similar",
1157
+ "any",
1158
+ "some",
1159
+ "row",
1160
+ "array",
1161
+ "nextval",
1162
+ "currval",
1163
+ "setval",
1164
+ "lastval",
1165
+ "txid_current",
1166
+ ];
1167
+
1168
+ const customSQLDialect = SQLDialect.define({
1169
+ keywords: SQL_KEYWORDS.join(" "),
1170
+ builtin: SQL_BUILTIN.join(" "),
1171
+ });
1172
+
1173
+ function computeYamlNewlineIndent(lineText: string): string {
1174
+ const indent = lineText.match(/^(\s*)/)?.[1] ?? "";
1175
+ const trimmed = lineText.trimEnd();
1176
+
1177
+ if (trimmed.endsWith(":")) {
1178
+ // After "key:" with no value — increase indent
1179
+ // For " - key:", base indent is at the dash content level
1180
+ const dashMatch = trimmed.match(/^(\s*-\s+)/);
1181
+ const baseIndent = dashMatch?.[1]
1182
+ ? " ".repeat(dashMatch[1].length)
1183
+ : indent;
1184
+ return `${baseIndent} `;
1185
+ }
1186
+ if (/^\s*-\s*$/.test(trimmed)) {
1187
+ // After bare "- " (array item marker only) — align to content after dash
1188
+ const dashMatch = trimmed.match(/^(\s*-\s*)/);
1189
+ return dashMatch?.[1] ? " ".repeat(dashMatch[1].length) : indent;
1190
+ }
1191
+ // Preserve current indent; for " - key: val" align to key level
1192
+ const dashKeyMatch = trimmed.match(/^(\s*-\s+)\S/);
1193
+ return dashKeyMatch?.[1] ? " ".repeat(dashKeyMatch[1].length) : indent;
1194
+ }
1195
+
1196
+ function yamlEnterKeymap(): Extension {
1197
+ return keymap.of([
1198
+ {
1199
+ key: "Enter",
1200
+ run: (view) => {
1201
+ const { state } = view;
1202
+ const pos = state.selection.main.head;
1203
+ const line = state.doc.lineAt(pos);
1204
+ const newIndent = computeYamlNewlineIndent(line.text);
1205
+
1206
+ view.dispatch({
1207
+ changes: { from: pos, insert: `\n${newIndent}` },
1208
+ selection: { anchor: pos + 1 + newIndent.length },
1209
+ });
1210
+ return true;
1211
+ },
1212
+ },
1213
+ ]);
1214
+ }
1215
+
1216
+ function httpYamlEnterKeymap(): Extension {
1217
+ return keymap.of([
1218
+ {
1219
+ key: "Enter",
1220
+ run: (view) => {
1221
+ const { state } = view;
1222
+ const pos = state.selection.main.head;
1223
+ const doc = state.doc.toString();
1224
+
1225
+ // Only handle if cursor is in YAML body (after blank line separator)
1226
+ const textBeforeCursor = doc.slice(0, pos);
1227
+ const blankLineIdx = textBeforeCursor.indexOf("\n\n");
1228
+ if (blankLineIdx === -1 || pos <= blankLineIdx + 1) return false;
1229
+
1230
+ // Check if the body looks like YAML (not JSON)
1231
+ const bodyStart = blankLineIdx + 2;
1232
+ const bodyPrefix = doc.slice(bodyStart, bodyStart + 20).trimStart();
1233
+ if (bodyPrefix.startsWith("{") || bodyPrefix.startsWith("["))
1234
+ return false;
1235
+
1236
+ const line = state.doc.lineAt(pos);
1237
+ const newIndent = computeYamlNewlineIndent(line.text);
1238
+
1239
+ view.dispatch({
1240
+ changes: { from: pos, insert: `\n${newIndent}` },
1241
+ selection: { anchor: pos + 1 + newIndent.length },
1242
+ });
1243
+ return true;
1244
+ },
1245
+ },
1246
+ ]);
1247
+ }
1248
+
1249
+ type LanguageMode = "json" | "http" | "sql" | "yaml";
1250
+
1251
+ function languageExtensions(
1252
+ mode: LanguageMode,
1253
+ sqlExtraBuiltins?: string[],
1254
+ getUrlSuggestions?: GetUrlSuggestions,
1255
+ ) {
1256
+ if (mode === "http") {
1257
+ const jsonLang = json();
1258
+ const yamlLang = yaml();
1259
+ return [
1260
+ http(
1261
+ (ct) =>
1262
+ ct === "application/json"
1263
+ ? jsonLang.language
1264
+ : ct === "text/yaml" ||
1265
+ ct === "application/yaml" ||
1266
+ ct === "application/x-yaml"
1267
+ ? yamlLang.language
1268
+ : null,
1269
+ getUrlSuggestions,
1270
+ ),
1271
+ syntaxHighlighting(customHighlightStyle),
1272
+ jsonAutoExpandBraces(),
1273
+ httpYamlEnterKeymap(),
1274
+ ];
1275
+ } else if (mode === "sql") {
1276
+ let dialect = customSQLDialect;
1277
+ if (sqlExtraBuiltins && sqlExtraBuiltins.length > 0) {
1278
+ dialect = SQLDialect.define({
1279
+ keywords: SQL_KEYWORDS.join(" "),
1280
+ builtin: [...SQL_BUILTIN, ...sqlExtraBuiltins].join(" "),
1281
+ });
1282
+ }
1283
+ return [sql({ dialect }), syntaxHighlighting(customHighlightStyle)];
1284
+ } else if (mode === "yaml") {
1285
+ return [
1286
+ yaml(),
1287
+ syntaxHighlighting(customHighlightStyle),
1288
+ yamlEnterKeymap(),
1289
+ ];
1290
+ } else {
1291
+ return [
1292
+ json(),
1293
+ linter(
1294
+ (view) => {
1295
+ if (!view.state.doc.toString().trim()) return [];
1296
+ return jsonParseLinter()(view);
1297
+ },
1298
+ { delay: 300 },
1299
+ ),
1300
+ syntaxHighlighting(customHighlightStyle),
1301
+ jsonAutoExpandBraces(),
1302
+ ];
1303
+ }
1304
+ }
1305
+
1306
+ function jsonAutoExpandBraces(): Extension {
1307
+ return EditorState.transactionFilter.of((tr) => {
1308
+ if (!tr.docChanged) return tr;
1309
+
1310
+ let braceFrom = -1;
1311
+ let braceTo = -1;
1312
+ let changeCount = 0;
1313
+
1314
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
1315
+ changeCount++;
1316
+ if (inserted.toString() === "{}") {
1317
+ braceFrom = fromA;
1318
+ braceTo = toA;
1319
+ }
1320
+ });
1321
+
1322
+ if (changeCount !== 1 || braceFrom === -1) return tr;
1323
+
1324
+ const tree = syntaxTree(tr.startState);
1325
+ const nodeBefore = tree.resolveInner(braceFrom, -1);
1326
+ if (nodeBefore.name === "String" || nodeBefore.parent?.name === "String") {
1327
+ return tr;
1328
+ }
1329
+
1330
+ const line = tr.startState.doc.lineAt(braceFrom);
1331
+ const indent = line.text.match(/^(\s*)/)?.[1] ?? "";
1332
+ const inner = `${indent} `;
1333
+
1334
+ // Check if { is inside an extension array — insert {"url": ""} snippet
1335
+ const docText = tr.startState.doc.toString();
1336
+ const textBefore = docText.slice(0, braceFrom);
1337
+ const isInExtArray =
1338
+ /"(?:extension|modifierExtension)"\s*:\s*\[\s*(?:\{[\s\S]*?\}\s*,?\s*)*$/s.test(
1339
+ textBefore,
1340
+ );
1341
+ if (isInExtArray) {
1342
+ const insert = `{\n${inner}"url": ""\n${indent}}`;
1343
+ return {
1344
+ changes: { from: braceFrom, to: braceTo, insert },
1345
+ selection: { anchor: braceFrom + insert.lastIndexOf('""') + 1 },
1346
+ };
1347
+ }
1348
+
1349
+ return {
1350
+ changes: {
1351
+ from: braceFrom,
1352
+ to: braceTo,
1353
+ insert: `{\n${inner}\n${indent}}`,
1354
+ },
1355
+ selection: { anchor: braceFrom + 2 + inner.length },
1356
+ };
1357
+ });
1358
+ }
1359
+
1360
+ type CodeEditorProps = {
1361
+ readOnly?: boolean;
1362
+ isReadOnlyTheme?: boolean;
1363
+ defaultValue?: string;
1364
+ currentValue?: string;
1365
+ onChange?: (value: string) => void;
1366
+ onUpdate?: (update: ViewUpdate) => void;
1367
+ id?: string;
1368
+ mode?: LanguageMode;
1369
+ viewCallback?: (view: EditorView) => void;
1370
+ additionalExtensions?: Extension[];
1371
+ issueLineNumbers?: { line: number; message?: string }[];
1372
+ foldGutter?: boolean;
1373
+ lineNumbers?: boolean;
1374
+ sql?: SqlConfig;
1375
+ getStructureDefinitions?: GetStructureDefinitions;
1376
+ resourceTypeHint?: string;
1377
+ expandValueSet?: ExpandValueSet;
1378
+ getUrlSuggestions?: GetUrlSuggestions;
1379
+ vimMode?: boolean;
1380
+ };
1381
+
1382
+ export type CodeEditorView = EditorView;
1383
+
1384
+ export type {
1385
+ ExpandValueSet,
1386
+ GetStructureDefinitions,
1387
+ } from "./fhir-autocomplete";
1388
+ export type { GetUrlSuggestions } from "./http";
1389
+ export type {
1390
+ SqlConfig,
1391
+ SqlMetadata,
1392
+ SqlQueryType,
1393
+ } from "./sql-completion";
1394
+
81
1395
  export function CodeEditor({
82
1396
  defaultValue,
1397
+ currentValue,
83
1398
  onChange,
84
- }: {
85
- defaultValue?: string;
86
- onChange?: (value: string) => void;
87
- }) {
88
- const editorRef = React.useRef(null);
1399
+ onUpdate,
1400
+ viewCallback,
1401
+ readOnly = false,
1402
+ id,
1403
+ mode = "json",
1404
+ isReadOnlyTheme = false,
1405
+ additionalExtensions,
1406
+ issueLineNumbers,
1407
+ foldGutter: enableFoldGutter = true,
1408
+ lineNumbers: enableLineNumbers = true,
1409
+ sql,
1410
+ getStructureDefinitions,
1411
+ resourceTypeHint,
1412
+ expandValueSet,
1413
+ getUrlSuggestions,
1414
+ vimMode = false,
1415
+ }: CodeEditorProps) {
1416
+ const domRef = React.useRef(null);
1417
+ const [view, setView] = React.useState<EditorView | null>(null);
1418
+
1419
+ const safeDispatch = React.useCallback(
1420
+ (spec: Parameters<EditorView["dispatch"]>[0]) => {
1421
+ try {
1422
+ view?.dispatch(spec);
1423
+ } catch {
1424
+ // Ignore RangeError from stale decoration positions during reconfigure
1425
+ }
1426
+ },
1427
+ [view],
1428
+ );
1429
+
1430
+ const initialValue = React.useRef(defaultValue ?? "");
1431
+
1432
+ const onChangeComparment = React.useRef(new Compartment());
1433
+ const onUpdateComparment = React.useRef(new Compartment());
1434
+ const languageCompartment = React.useRef(new Compartment());
1435
+ const readOnlyCompartment = React.useRef(new Compartment());
1436
+ const themeCompartment = React.useRef(new Compartment());
1437
+ const additionalExtensionsCompartment = React.useRef(new Compartment());
1438
+ const sqlCompletionCompartment = React.useRef(new Compartment());
1439
+ const fhirCompletionCompartment = React.useRef(new Compartment());
1440
+ const vimCompartment = React.useRef(new Compartment());
1441
+ const [sqlFunctions, setSqlFunctions] = React.useState<
1442
+ string[] | undefined
1443
+ >();
1444
+ const executeSqlRef = React.useRef(sql?.executeSql);
89
1445
 
90
1446
  React.useEffect(() => {
91
- if (!editorRef.current) {
1447
+ if (!domRef.current) {
92
1448
  return;
93
1449
  }
94
1450
 
95
1451
  const view = new EditorView({
96
- parent: editorRef.current,
1452
+ parent: domRef.current,
97
1453
  state: EditorState.create({
98
- doc: defaultValue ?? "",
1454
+ doc: initialValue.current,
99
1455
  extensions: [
100
- lineNumbers(),
101
- foldGutter(),
1456
+ vimCompartment.current.of(vimMode ? vim() : []),
1457
+ EditorView.contentAttributes.of({ "data-gramm": "false" }),
1458
+ readOnlyCompartment.current.of(EditorState.readOnly.of(false)),
1459
+ ...(enableLineNumbers ? [lineNumbers()] : []),
1460
+ ...(enableFoldGutter
1461
+ ? [
1462
+ foldGutter({
1463
+ markerDOM: (open) => {
1464
+ const el = document.createElement("span");
1465
+ el.style.display = "flex";
1466
+ el.style.alignItems = "center";
1467
+ el.style.justifyContent = "center";
1468
+ el.style.width = "100%";
1469
+ el.style.height = "100%";
1470
+ el.innerHTML = open
1471
+ ? '<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>'
1472
+ : '<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>';
1473
+ return el;
1474
+ },
1475
+ }),
1476
+ ]
1477
+ : []),
102
1478
  highlightSpecialChars(),
103
1479
  history(),
104
1480
  drawSelection(),
105
1481
  dropCursor(),
106
1482
  EditorState.allowMultipleSelections.of(true),
107
1483
  indentOnInput(),
108
- json(),
109
- syntaxHighlighting(customHighlightStyle),
1484
+ languageCompartment.current.of([]),
110
1485
  bracketMatching(),
111
1486
  closeBrackets(),
112
- autocompletion(),
1487
+ autocompletion({
1488
+ icons: false,
1489
+ maxRenderedOptions: 1000,
1490
+ defaultKeymap: false,
1491
+ addToOptions: [{ render: renderCompletionIcon, position: 20 }],
1492
+ optionClass: (_completion) =>
1493
+ "!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",
1494
+ tooltipClass: (_state) =>
1495
+ "!bg-bg-primary rounded-md p-2 shadow-md !border-border-primary !typo-body",
1496
+ compareCompletions: (a, b) => {
1497
+ const aIsProperty = a.type === "property" ? 0 : 1;
1498
+ const bIsProperty = b.type === "property" ? 0 : 1;
1499
+ return aIsProperty - bIsProperty;
1500
+ },
1501
+ }),
113
1502
  rectangularSelection(),
114
1503
  crosshairCursor(),
115
- highlightActiveLine(),
116
- highlightActiveLineGutter(),
117
1504
  highlightSelectionMatches(),
118
- baseTheme,
1505
+ Prec.highest(
1506
+ keymap.of([
1507
+ {
1508
+ key: "Tab",
1509
+ run: (v) => {
1510
+ if (completionStatus(v.state) === "active") {
1511
+ return moveCompletionSelection(true)(v);
1512
+ }
1513
+ return false;
1514
+ },
1515
+ },
1516
+ {
1517
+ key: "Shift-Tab",
1518
+ run: (v) => {
1519
+ if (completionStatus(v.state) === "active") {
1520
+ return moveCompletionSelection(false)(v);
1521
+ }
1522
+ return false;
1523
+ },
1524
+ },
1525
+ {
1526
+ key: "Enter",
1527
+ run: (v) => {
1528
+ if (completionStatus(v.state) === "active") {
1529
+ return acceptCompletion(v);
1530
+ }
1531
+ return false;
1532
+ },
1533
+ },
1534
+ ]),
1535
+ ),
1536
+ themeCompartment.current.of(baseTheme),
1537
+ completionTheme,
119
1538
  keymap.of([
120
1539
  ...closeBracketsKeymap,
1540
+ ...completionKeymap.filter((b) => b.key !== "Enter"),
121
1541
  ...defaultKeymap,
122
1542
  ...searchKeymap,
123
1543
  ...historyKeymap,
124
1544
  ...foldKeymap,
125
- ...completionKeymap,
126
1545
  ...lintKeymap,
127
1546
  ]),
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
- }
1547
+ issueLinesField,
1548
+ errorTooltipHandler,
1549
+ EditorView.exceptionSink.of(() => {}),
1550
+ ...customSearchExtension,
1551
+ onChangeComparment.current.of([]),
1552
+ onUpdateComparment.current.of([]),
1553
+ additionalExtensionsCompartment.current.of([]),
1554
+ sqlCompletionCompartment.current.of([]),
1555
+ fhirCompletionCompartment.current.of([]),
1556
+ ],
1557
+ }),
1558
+ });
1559
+
1560
+ setView(() => view);
1561
+
1562
+ return () => {
1563
+ view.destroy();
1564
+ setView(() => null);
1565
+ };
1566
+ }, [enableFoldGutter, enableLineNumbers, vimMode]);
1567
+
1568
+ React.useEffect(() => {
1569
+ executeSqlRef.current = sql?.executeSql;
1570
+ });
1571
+
1572
+ React.useEffect(() => {
1573
+ if (!view || !sql) {
1574
+ if (view) {
1575
+ safeDispatch({
1576
+ effects: sqlCompletionCompartment.current.reconfigure([]),
1577
+ });
1578
+ }
1579
+ setSqlFunctions(undefined);
1580
+ return;
1581
+ }
1582
+
1583
+ let cancelled = false;
1584
+
1585
+ fetchSqlMetadata(sql.executeSql)
1586
+ .then((metadata) => {
1587
+ if (cancelled) return;
1588
+ setSqlFunctions(metadata.functions);
1589
+ const extensions = buildSqlCompletionExtensions(
1590
+ metadata,
1591
+ (query, type) =>
1592
+ executeSqlRef.current?.(query, type) ?? Promise.resolve([]),
1593
+ );
1594
+ safeDispatch({
1595
+ effects: sqlCompletionCompartment.current.reconfigure(extensions),
1596
+ });
1597
+ })
1598
+ .catch(() => {});
1599
+
1600
+ return () => {
1601
+ cancelled = true;
1602
+ };
1603
+ }, [view, sql, safeDispatch]);
1604
+
1605
+ React.useEffect(() => {
1606
+ if (!view) return;
1607
+ if (getStructureDefinitions) {
1608
+ safeDispatch({
1609
+ effects: fhirCompletionCompartment.current.reconfigure(
1610
+ buildFhirCompletionExtension(
1611
+ getStructureDefinitions,
1612
+ resourceTypeHint,
1613
+ expandValueSet,
1614
+ ),
1615
+ ),
1616
+ });
1617
+ } else {
1618
+ safeDispatch({
1619
+ effects: fhirCompletionCompartment.current.reconfigure([]),
1620
+ });
1621
+ }
1622
+ }, [
1623
+ view,
1624
+ getStructureDefinitions,
1625
+ resourceTypeHint,
1626
+ expandValueSet,
1627
+ safeDispatch,
1628
+ ]);
1629
+
1630
+ React.useEffect(() => {
1631
+ if (viewCallback && view) {
1632
+ viewCallback(view);
1633
+ }
1634
+ }, [view, viewCallback]);
1635
+
1636
+ React.useEffect(() => {
1637
+ safeDispatch({
1638
+ effects: onChangeComparment.current.reconfigure([
1639
+ EditorView.updateListener.of((update) => {
1640
+ if (update.docChanged && onChange) {
1641
+ onChange(update.view.state.doc.toString());
1642
+ }
1643
+ }),
1644
+ ]),
1645
+ });
1646
+ }, [onChange, safeDispatch]);
1647
+
1648
+ React.useEffect(() => {
1649
+ safeDispatch({
1650
+ effects: onUpdateComparment.current.reconfigure([
1651
+ EditorView.updateListener.of((update) => {
1652
+ if (onUpdate) {
1653
+ onUpdate(update);
1654
+ }
1655
+ }),
1656
+ ]),
1657
+ });
1658
+ }, [onUpdate, safeDispatch]);
1659
+
1660
+ // FIXME: it is probably better to have CM manage its state.
1661
+ React.useEffect(() => {
1662
+ if (!view || currentValue === undefined) {
1663
+ return;
1664
+ }
1665
+
1666
+ const currentDoc = view.state.doc.toString();
1667
+ if (currentDoc !== currentValue) {
1668
+ safeDispatch({
1669
+ changes: {
1670
+ from: 0,
1671
+ to: currentDoc.length,
1672
+ insert: currentValue,
1673
+ },
1674
+ });
1675
+ }
1676
+ }, [currentValue, view, safeDispatch]);
1677
+
1678
+ const getUrlSuggestionsRef = React.useRef(getUrlSuggestions);
1679
+ getUrlSuggestionsRef.current = getUrlSuggestions;
1680
+
1681
+ const stableGetUrlSuggestions = React.useMemo(() => {
1682
+ if (!getUrlSuggestions) return undefined;
1683
+ return ((path: string, method: string) =>
1684
+ getUrlSuggestionsRef.current?.(path, method) ?? []) as GetUrlSuggestions;
1685
+ }, [getUrlSuggestions]);
1686
+
1687
+ React.useEffect(() => {
1688
+ if (view === null) {
1689
+ return;
1690
+ }
1691
+ safeDispatch({
1692
+ effects: languageCompartment.current.reconfigure(
1693
+ languageExtensions(mode, sqlFunctions, stableGetUrlSuggestions),
1694
+ ),
1695
+ });
1696
+ }, [mode, view, sqlFunctions, stableGetUrlSuggestions, safeDispatch]);
1697
+
1698
+ React.useEffect(() => {
1699
+ if (view === null) {
1700
+ return;
1701
+ }
1702
+ safeDispatch({
1703
+ effects: [
1704
+ readOnlyCompartment.current.reconfigure(
1705
+ EditorState.readOnly.of(readOnly),
1706
+ ),
1707
+ ],
1708
+ });
1709
+ }, [readOnly, view, safeDispatch]);
1710
+
1711
+ React.useEffect(() => {
1712
+ if (view === null) {
1713
+ return;
1714
+ }
1715
+ safeDispatch({
1716
+ effects: [
1717
+ themeCompartment.current.reconfigure(
1718
+ isReadOnlyTheme ? readOnlyTheme : baseTheme,
1719
+ ),
1720
+ ],
1721
+ });
1722
+ }, [isReadOnlyTheme, view, safeDispatch]);
1723
+
1724
+ React.useEffect(() => {
1725
+ if (view === null) {
1726
+ return;
1727
+ }
1728
+ safeDispatch({
1729
+ effects: [vimCompartment.current.reconfigure(vimMode ? vim() : [])],
1730
+ });
1731
+ }, [vimMode, view, safeDispatch]);
1732
+
1733
+ React.useEffect(() => {
1734
+ if (view === null) {
1735
+ return;
1736
+ }
1737
+ safeDispatch({
1738
+ effects: [
1739
+ additionalExtensionsCompartment.current.reconfigure(
1740
+ additionalExtensions ?? [],
1741
+ ),
1742
+ ],
1743
+ });
1744
+ }, [additionalExtensions, view, safeDispatch]);
1745
+
1746
+ React.useEffect(() => {
1747
+ if (view === null) {
1748
+ return;
1749
+ }
1750
+ safeDispatch({
1751
+ effects: setIssueLinesEffect.of(issueLineNumbers ?? []),
1752
+ });
1753
+ }, [issueLineNumbers, view, safeDispatch]);
1754
+
1755
+ return <div className="h-full w-full" ref={domRef} id={id} />;
1756
+ }
1757
+
1758
+ const editorInputTheme = EditorView.theme({
1759
+ ".cm-content": {
1760
+ backgroundColor: "var(--color-bg-primary)",
1761
+ border: "1px solid var(--color-border-primary)",
1762
+ borderRadius: "var(--radius-md)",
1763
+ fontFamily: "var(--font-family-sans)",
1764
+ fontWeight: "var(--font-weight-normal)",
1765
+ height: "36px",
1766
+ padding: "8px 12px 8px 12px",
1767
+ fontSize: "14px",
1768
+ },
1769
+ ".cm-editor": {
1770
+ fontSize: "var(--font-size-sm)",
1771
+ color: "var(--color-text-primary)",
1772
+ },
1773
+ ".cm-cursor, .cm-dropCursor": {
1774
+ borderLeftColor: "var(--color-text-primary)",
1775
+ },
1776
+ "&.cm-editor.cm-focused": {
1777
+ outline: "none",
1778
+ },
1779
+ "&.cm-editor.cm-focused .cm-content": {
1780
+ border: "1px solid var(--color-border-link)",
1781
+ borderRadius: "var(--radius-md)",
1782
+ },
1783
+ ".cm-line": {
1784
+ padding: "0",
1785
+ },
1786
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
1787
+ maxHeight: "400px",
1788
+ },
1789
+ ".cm-completionInfo": {
1790
+ display: "none",
1791
+ fontFamily: "var(--font-family-sans)",
1792
+ },
1793
+ ".cm-completionLabel": {
1794
+ color: "var(--color-text-link)",
1795
+ fontSize: "14px",
1796
+ },
1797
+ ".cm-completionDetail": {
1798
+ display: "none",
1799
+ },
1800
+ ".cm-completion-icon": {
1801
+ display: "flex",
1802
+ alignItems: "center",
1803
+ justifyContent: "center",
1804
+ width: "16px",
1805
+ height: "16px",
1806
+ flexShrink: "0",
1807
+ },
1808
+ });
1809
+
1810
+ const KeywordIcon = () => <Terminal size={16} color="#717684" />;
1811
+ const OperatorIcon = () => <ChevronsRight size={16} color="#717684" />;
1812
+ const TableIcon = () => <Table2 size={16} color="#717684" />;
1813
+ const HeaderIcon = () => <Heading size={16} color="#717684" />;
1814
+ const ColumnIcon = () => <Columns2 size={16} color="#717684" />;
1815
+
1816
+ function getCompletionIcon(completion: Completion): React.FC | null {
1817
+ if (completion.type === "function") return SquareFunctionIcon;
1818
+ if (completion.type === "keyword") return KeywordIcon;
1819
+ if (completion.type === "operator") return OperatorIcon;
1820
+ if (completion.type === "table") return TableIcon;
1821
+ if (completion.type === "header") return HeaderIcon;
1822
+ if (completion.type === "text") return TypCodeIcon;
1823
+ if (completion.type === "type") return ResourceIcon;
1824
+ if (completion.type === "search-param") return null;
1825
+ const detail = completion.detail;
1826
+ if (!detail) {
1827
+ if (completion.type === "variable") return ColumnIcon;
1828
+ return TypCodeIcon;
1829
+ }
1830
+ if (completion.type === "variable") return ColumnIcon;
1831
+ const typeName = detail.replace(/\[\]$/, "");
1832
+ if (!typeName) return TypCodeIcon;
1833
+ // Search param types (TOKEN, REFERENCE) — no icon
1834
+ if (typeName === typeName.toUpperCase()) return null;
1835
+ const firstChar = typeName[0];
1836
+ if (!firstChar) return TypCodeIcon;
1837
+ const isComplex = firstChar === firstChar.toUpperCase();
1838
+ return isComplex ? ComplexTypeIcon : TypCodeIcon;
1839
+ }
1840
+
1841
+ function renderCompletionIcon(completion: Completion): Node {
1842
+ const container = document.createElement("div");
1843
+ container.className = "cm-completion-icon";
1844
+ const Icon = getCompletionIcon(completion);
1845
+ if (Icon) {
1846
+ flushSync(() => {
1847
+ createRoot(container).render(<Icon />);
1848
+ });
1849
+ } else {
1850
+ container.style.display = "none";
1851
+ }
1852
+ return container;
1853
+ }
1854
+
1855
+ let activeTooltip: HTMLDivElement | null = null;
1856
+ let activeRafId: number | null = null;
1857
+
1858
+ function cleanupActiveTooltip() {
1859
+ activeTooltip?.remove();
1860
+ activeTooltip = null;
1861
+ if (activeRafId !== null) {
1862
+ cancelAnimationFrame(activeRafId);
1863
+ activeRafId = null;
1864
+ }
1865
+ }
1866
+
1867
+ function renderCompletionDetail(completion: Completion): Node | null {
1868
+ const detail = completion.detail;
1869
+ if (!detail) return null;
1870
+
1871
+ const anchor = document.createElement("span");
1872
+ anchor.style.display = "none";
1873
+
1874
+ const showTooltip = () => {
1875
+ cleanupActiveTooltip();
1876
+
1877
+ const tooltip = document.createElement("div");
1878
+ tooltip.textContent = detail;
1879
+ Object.assign(tooltip.style, {
1880
+ position: "fixed",
1881
+ backgroundColor: "var(--color-bg-primary)",
1882
+ border: "1px solid var(--color-border-primary)",
1883
+ borderRadius: "var(--radius-md)",
1884
+ padding: "8px 12px",
1885
+ fontSize: "12px",
1886
+ lineHeight: "1.4",
1887
+ color: "var(--color-text-secondary)",
1888
+ fontFamily: "var(--font-family-sans)",
1889
+ whiteSpace: "normal",
1890
+ width: "280px",
1891
+ boxShadow: "0 4px 12px rgba(0, 0, 0, 0.1)",
1892
+ zIndex: "1000",
1893
+ pointerEvents: "none",
1894
+ });
1895
+ document.body.appendChild(tooltip);
1896
+ activeTooltip = tooltip;
1897
+
1898
+ const autocompleteEl = anchor.closest(".cm-tooltip-autocomplete");
1899
+ const anchorRect = autocompleteEl
1900
+ ? autocompleteEl.getBoundingClientRect()
1901
+ : anchor.getBoundingClientRect();
1902
+ tooltip.style.top = `${anchorRect.top}px`;
1903
+ tooltip.style.left = `${anchorRect.right + 8}px`;
1904
+
1905
+ const checkAlive = () => {
1906
+ if (!anchor.isConnected) {
1907
+ cleanupActiveTooltip();
1908
+ return;
1909
+ }
1910
+ activeRafId = requestAnimationFrame(checkAlive);
1911
+ };
1912
+ activeRafId = requestAnimationFrame(checkAlive);
1913
+ };
1914
+
1915
+ requestAnimationFrame(() => {
1916
+ const option = anchor.closest("li");
1917
+ if (!option) return;
1918
+ option.addEventListener("mouseenter", showTooltip);
1919
+ option.addEventListener("mouseleave", cleanupActiveTooltip);
1920
+ });
1921
+
1922
+ return anchor;
1923
+ }
1924
+
1925
+ export function EditorInput({
1926
+ additionalExtensions,
1927
+ id,
1928
+ defaultValue,
1929
+ currentValue,
1930
+ onChange,
1931
+ }: {
1932
+ additionalExtensions?: Extension[];
1933
+ id: string;
1934
+ defaultValue?: string;
1935
+ currentValue?: string;
1936
+ onChange?: (value: string) => void;
1937
+ }) {
1938
+ const domRef = React.useRef(null);
1939
+ const [view, setView] = React.useState<EditorView | null>(null);
1940
+ const additionalExtensionsCompartment = React.useRef(new Compartment());
1941
+ const onChangeCompartment = React.useRef(new Compartment());
1942
+ const initialValue = React.useRef(defaultValue ?? "");
1943
+
1944
+ React.useEffect(() => {
1945
+ if (!domRef.current) {
1946
+ return;
1947
+ }
1948
+
1949
+ const view = new EditorView({
1950
+ parent: domRef.current,
1951
+ state: EditorState.create({
1952
+ doc: initialValue.current,
1953
+ extensions: [
1954
+ autocompletion({
1955
+ icons: false,
1956
+ maxRenderedOptions: 1000,
1957
+ closeOnBlur: false,
1958
+ addToOptions: [
1959
+ { render: renderCompletionIcon, position: 20 },
1960
+ { render: renderCompletionDetail, position: 80 },
1961
+ ],
1962
+ optionClass: (_completion) =>
1963
+ "!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",
1964
+ tooltipClass: (_state) =>
1965
+ "!bg-bg-primary rounded-md p-2 shadow-md !border-border-primary !typo-body",
1966
+ compareCompletions: (a, b) => {
1967
+ const aIsProperty = a.type === "property" ? 0 : 1;
1968
+ const bIsProperty = b.type === "property" ? 0 : 1;
1969
+ return aIsProperty - bIsProperty;
1970
+ },
134
1971
  }),
1972
+ closeBrackets(),
1973
+ history(),
1974
+ indentOnInput(),
1975
+ editorInputTheme,
1976
+ EditorView.contentAttributes.of({ "data-gramm": "false" }),
1977
+ ...customSearchExtension,
1978
+ additionalExtensionsCompartment.current.of([]),
1979
+ onChangeCompartment.current.of([]),
1980
+ keymap.of([
1981
+ { key: "Tab", preventDefault: true, run: acceptCompletion },
1982
+ ...closeBracketsKeymap,
1983
+ ...defaultKeymap,
1984
+ ...searchKeymap,
1985
+ ...historyKeymap,
1986
+ ...foldKeymap,
1987
+ ...completionKeymap,
1988
+ ...lintKeymap,
1989
+ ]),
135
1990
  ],
136
1991
  }),
137
1992
  });
138
1993
 
139
- return () => view.destroy();
140
- }, [defaultValue, onChange]);
1994
+ setView(() => view);
1995
+
1996
+ return () => {
1997
+ view.destroy();
1998
+ setView(() => null);
1999
+ };
2000
+ }, []);
2001
+
2002
+ React.useEffect(() => {
2003
+ if (view === null) {
2004
+ return;
2005
+ }
2006
+ view.dispatch({
2007
+ effects: [
2008
+ additionalExtensionsCompartment.current.reconfigure(
2009
+ additionalExtensions ?? [],
2010
+ ),
2011
+ ],
2012
+ });
2013
+ }, [additionalExtensions, view]);
2014
+
2015
+ React.useEffect(() => {
2016
+ view?.dispatch({
2017
+ effects: onChangeCompartment.current.reconfigure([
2018
+ EditorView.updateListener.of((update) => {
2019
+ if (update.docChanged && onChange) {
2020
+ onChange(update.view.state.doc.toString());
2021
+ }
2022
+ }),
2023
+ ]),
2024
+ });
2025
+ }, [view, onChange]);
2026
+
2027
+ React.useEffect(() => {
2028
+ if (!view || currentValue === undefined) {
2029
+ return;
2030
+ }
2031
+
2032
+ const currentDoc = view.state.doc.toString();
2033
+ if (currentDoc !== currentValue) {
2034
+ view.dispatch({
2035
+ changes: {
2036
+ from: 0,
2037
+ to: currentDoc.length,
2038
+ insert: currentValue,
2039
+ },
2040
+ });
2041
+ }
2042
+ }, [currentValue, view]);
141
2043
 
142
- return <div className="h-full w-full" ref={editorRef} />;
2044
+ return <div className="h-full w-full" ref={domRef} id={id} />;
143
2045
  }