@pilotiq/pilotiq 0.24.1 → 0.24.3

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 (518) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/boost/guidelines.md +571 -0
  3. package/boost/skills/pilotiq-actions/SKILL.md +49 -0
  4. package/boost/skills/pilotiq-actions/rules/dispatch-modes.md +177 -0
  5. package/boost/skills/pilotiq-actions/rules/factories.md +130 -0
  6. package/boost/skills/pilotiq-actions/rules/visibility-and-authorization.md +125 -0
  7. package/boost/skills/pilotiq-fields/SKILL.md +47 -0
  8. package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
  9. package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
  10. package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
  11. package/boost/skills/pilotiq-relations/SKILL.md +47 -0
  12. package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
  13. package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
  14. package/boost/skills/pilotiq-resource/SKILL.md +61 -0
  15. package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
  16. package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
  17. package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
  18. package/dist/Pilotiq.d.ts +31 -0
  19. package/dist/Pilotiq.d.ts.map +1 -1
  20. package/dist/Pilotiq.js +3 -1
  21. package/dist/Pilotiq.js.map +1 -1
  22. package/dist/PilotiqRegistry.d.ts +13 -0
  23. package/dist/PilotiqRegistry.d.ts.map +1 -1
  24. package/dist/PilotiqRegistry.js +15 -0
  25. package/dist/PilotiqRegistry.js.map +1 -1
  26. package/dist/pageData/misc.d.ts.map +1 -1
  27. package/dist/pageData/misc.js +6 -0
  28. package/dist/pageData/misc.js.map +1 -1
  29. package/dist/pageData/navigation.d.ts +1 -0
  30. package/dist/pageData/navigation.d.ts.map +1 -1
  31. package/dist/pageData/navigation.js +3 -0
  32. package/dist/pageData/navigation.js.map +1 -1
  33. package/dist/pageData/relationPages.d.ts.map +1 -1
  34. package/dist/pageData/relationPages.js +3 -0
  35. package/dist/pageData/relationPages.js.map +1 -1
  36. package/dist/pageData/resourcePages.d.ts.map +1 -1
  37. package/dist/pageData/resourcePages.js +8 -0
  38. package/dist/pageData/resourcePages.js.map +1 -1
  39. package/dist/react/AppShell.d.ts +8 -0
  40. package/dist/react/AppShell.d.ts.map +1 -1
  41. package/dist/react/AppShell.js.map +1 -1
  42. package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
  43. package/dist/react/layouts/SidebarLayout.js +10 -2
  44. package/dist/react/layouts/SidebarLayout.js.map +1 -1
  45. package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -1
  46. package/dist/react/widgets/StatsOverviewRenderer.js +32 -18
  47. package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -1
  48. package/dist/routes/relations.d.ts.map +1 -1
  49. package/dist/routes/relations.js +25 -18
  50. package/dist/routes/relations.js.map +1 -1
  51. package/dist/routes/resources.js.map +1 -1
  52. package/package.json +10 -5
  53. package/.turbo/turbo-build.log +0 -8
  54. package/CLAUDE.md +0 -265
  55. package/src/Cluster.test.ts +0 -283
  56. package/src/Cluster.ts +0 -83
  57. package/src/Column.test.ts +0 -199
  58. package/src/Column.ts +0 -710
  59. package/src/Global.test.ts +0 -367
  60. package/src/Global.ts +0 -169
  61. package/src/Page.test.ts +0 -114
  62. package/src/Page.ts +0 -208
  63. package/src/Pilotiq.perf.test.ts +0 -252
  64. package/src/Pilotiq.test.ts +0 -129
  65. package/src/Pilotiq.ts +0 -1158
  66. package/src/PilotiqRegistry.ts +0 -36
  67. package/src/PilotiqServiceProvider.ts +0 -121
  68. package/src/RelationManager.test.ts +0 -400
  69. package/src/RelationManager.ts +0 -527
  70. package/src/RenderHook.test.ts +0 -252
  71. package/src/RenderHook.ts +0 -242
  72. package/src/Resource.test.ts +0 -284
  73. package/src/Resource.ts +0 -526
  74. package/src/RightPanel.test.ts +0 -202
  75. package/src/RightPanel.ts +0 -132
  76. package/src/Tab.test.ts +0 -91
  77. package/src/Tab.ts +0 -156
  78. package/src/UserMenuItem.ts +0 -145
  79. package/src/actions/Action.test.ts +0 -2526
  80. package/src/actions/Action.ts +0 -1515
  81. package/src/actions/ActionGroup.test.ts +0 -112
  82. package/src/actions/ActionGroup.ts +0 -173
  83. package/src/actions/attachFactory.ts +0 -172
  84. package/src/actions/bulkFactories.ts +0 -168
  85. package/src/actions/crudFactories.ts +0 -220
  86. package/src/actions/exportFactory.ts +0 -225
  87. package/src/actions/factoryHelpers.ts +0 -177
  88. package/src/actions/importFactory.ts +0 -243
  89. package/src/actions/index.ts +0 -17
  90. package/src/actions/m2mFactories.ts +0 -193
  91. package/src/actions/relationFactories.ts +0 -372
  92. package/src/applyPageHooks.test.ts +0 -463
  93. package/src/applyPageHooks.ts +0 -330
  94. package/src/authorization.test.ts +0 -483
  95. package/src/breadcrumbs.test.ts +0 -238
  96. package/src/cells/coerce.test.ts +0 -85
  97. package/src/cells/coerce.ts +0 -84
  98. package/src/clusterPaths.ts +0 -35
  99. package/src/columns/BadgeColumn.test.ts +0 -54
  100. package/src/columns/BadgeColumn.ts +0 -32
  101. package/src/columns/BooleanColumn.test.ts +0 -41
  102. package/src/columns/BooleanColumn.ts +0 -18
  103. package/src/columns/ColorColumn.test.ts +0 -37
  104. package/src/columns/ColorColumn.ts +0 -38
  105. package/src/columns/IconColumn.test.ts +0 -54
  106. package/src/columns/IconColumn.ts +0 -37
  107. package/src/columns/ImageColumn.test.ts +0 -41
  108. package/src/columns/ImageColumn.ts +0 -28
  109. package/src/columns/SelectColumn.ts +0 -98
  110. package/src/columns/TextColumn.test.ts +0 -190
  111. package/src/columns/TextColumn.ts +0 -20
  112. package/src/columns/TextInputColumn.ts +0 -68
  113. package/src/columns/ToggleColumn.ts +0 -46
  114. package/src/columns/editableColumns.test.ts +0 -238
  115. package/src/columns/index.ts +0 -9
  116. package/src/defaultGlobalPages.ts +0 -95
  117. package/src/defaultPages.test.ts +0 -634
  118. package/src/defaultPages.ts +0 -617
  119. package/src/defaultViewPage.test.ts +0 -147
  120. package/src/elements/Form.test.ts +0 -223
  121. package/src/elements/Form.ts +0 -416
  122. package/src/elements/ListTabs.ts +0 -28
  123. package/src/elements/Table.test.ts +0 -422
  124. package/src/elements/Table.ts +0 -850
  125. package/src/elements/TableGroup.test.ts +0 -260
  126. package/src/elements/TableGroup.ts +0 -334
  127. package/src/elements/dispatchAction.test.ts +0 -463
  128. package/src/elements/dispatchAction.ts +0 -355
  129. package/src/elements/dispatchForm.test.ts +0 -477
  130. package/src/elements/dispatchForm.ts +0 -1993
  131. package/src/elements/dispatchTable.test.ts +0 -1514
  132. package/src/elements/dispatchTable.ts +0 -745
  133. package/src/elements/index.ts +0 -21
  134. package/src/entries/BadgeEntry.ts +0 -39
  135. package/src/entries/CodeEntry.test.ts +0 -40
  136. package/src/entries/CodeEntry.ts +0 -52
  137. package/src/entries/ColorEntry.ts +0 -63
  138. package/src/entries/ComponentEntry.test.ts +0 -173
  139. package/src/entries/ComponentEntry.ts +0 -95
  140. package/src/entries/Entry.ts +0 -304
  141. package/src/entries/IconEntry.ts +0 -49
  142. package/src/entries/ImageEntry.ts +0 -61
  143. package/src/entries/KeyValueEntry.ts +0 -47
  144. package/src/entries/RepeatableEntry.test.ts +0 -239
  145. package/src/entries/RepeatableEntry.ts +0 -173
  146. package/src/entries/TextEntry.test.ts +0 -394
  147. package/src/entries/TextEntry.ts +0 -60
  148. package/src/entries/index.ts +0 -12
  149. package/src/entries/leaves.test.ts +0 -306
  150. package/src/entries/registry.ts +0 -54
  151. package/src/fields/BuilderField.test.ts +0 -1188
  152. package/src/fields/BuilderField.ts +0 -605
  153. package/src/fields/BuilderRelationship.test.ts +0 -811
  154. package/src/fields/CheckboxField.test.ts +0 -44
  155. package/src/fields/CheckboxField.ts +0 -27
  156. package/src/fields/CheckboxListField.test.ts +0 -99
  157. package/src/fields/CheckboxListField.ts +0 -66
  158. package/src/fields/ColorPickerField.test.ts +0 -33
  159. package/src/fields/ColorPickerField.ts +0 -25
  160. package/src/fields/DateField.ts +0 -54
  161. package/src/fields/DateTimeField.test.ts +0 -55
  162. package/src/fields/EmailField.ts +0 -16
  163. package/src/fields/Field.test.ts +0 -654
  164. package/src/fields/Field.ts +0 -817
  165. package/src/fields/FileUploadField.test.ts +0 -143
  166. package/src/fields/FileUploadField.ts +0 -159
  167. package/src/fields/HiddenField.test.ts +0 -27
  168. package/src/fields/HiddenField.ts +0 -28
  169. package/src/fields/KeyValueField.test.ts +0 -105
  170. package/src/fields/KeyValueField.ts +0 -55
  171. package/src/fields/MarkdownField.test.ts +0 -167
  172. package/src/fields/MarkdownField.ts +0 -162
  173. package/src/fields/NumberField.ts +0 -33
  174. package/src/fields/RadioField.test.ts +0 -94
  175. package/src/fields/RadioField.ts +0 -67
  176. package/src/fields/RepeaterField.test.ts +0 -1806
  177. package/src/fields/RepeaterField.ts +0 -939
  178. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  179. package/src/fields/RepeaterSimple.test.ts +0 -248
  180. package/src/fields/RowButton.test.ts +0 -219
  181. package/src/fields/RowButton.ts +0 -135
  182. package/src/fields/SelectField.test.ts +0 -192
  183. package/src/fields/SelectField.ts +0 -235
  184. package/src/fields/SliderField.test.ts +0 -50
  185. package/src/fields/SliderField.ts +0 -53
  186. package/src/fields/SlugField.ts +0 -24
  187. package/src/fields/TagsInputField.test.ts +0 -154
  188. package/src/fields/TagsInputField.ts +0 -133
  189. package/src/fields/TextField.test.ts +0 -213
  190. package/src/fields/TextField.ts +0 -177
  191. package/src/fields/TextareaField.test.ts +0 -58
  192. package/src/fields/TextareaField.ts +0 -59
  193. package/src/fields/ToggleButtonsField.test.ts +0 -106
  194. package/src/fields/ToggleButtonsField.ts +0 -59
  195. package/src/fields/ToggleField.ts +0 -16
  196. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  197. package/src/fields/optionsResolver.ts +0 -95
  198. package/src/fields/resolveField.ts +0 -28
  199. package/src/filters/BooleanFilter.ts +0 -35
  200. package/src/filters/DateRangeFilter.test.ts +0 -194
  201. package/src/filters/DateRangeFilter.ts +0 -148
  202. package/src/filters/Filter.test.ts +0 -268
  203. package/src/filters/Filter.ts +0 -184
  204. package/src/filters/FormFilter.test.ts +0 -238
  205. package/src/filters/FormFilter.ts +0 -215
  206. package/src/filters/MultiSelectFilter.test.ts +0 -119
  207. package/src/filters/MultiSelectFilter.ts +0 -78
  208. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  209. package/src/filters/QueryBuilderFilter.ts +0 -398
  210. package/src/filters/SelectFilter.ts +0 -46
  211. package/src/filters/TernaryFilter.test.ts +0 -160
  212. package/src/filters/TernaryFilter.ts +0 -72
  213. package/src/filters/TrashedFilter.test.ts +0 -149
  214. package/src/filters/TrashedFilter.ts +0 -55
  215. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  216. package/src/filters/queryBuilder/Constraint.ts +0 -115
  217. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  218. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  219. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  220. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  221. package/src/filters/queryBuilder/index.ts +0 -12
  222. package/src/icons/index.ts +0 -2
  223. package/src/icons/lucide.ts +0 -204
  224. package/src/icons/registry.test.ts +0 -56
  225. package/src/icons/registry.ts +0 -41
  226. package/src/icons/types.ts +0 -47
  227. package/src/index.ts +0 -525
  228. package/src/io/csv.test.ts +0 -142
  229. package/src/io/csv.ts +0 -170
  230. package/src/nestedRelationManagerData.test.ts +0 -547
  231. package/src/notifications/Notification.test.ts +0 -210
  232. package/src/notifications/Notification.ts +0 -354
  233. package/src/notifications/broadcast.test.ts +0 -110
  234. package/src/notifications/broadcast.ts +0 -95
  235. package/src/notifications/database.test.ts +0 -383
  236. package/src/notifications/database.ts +0 -398
  237. package/src/notifications/databaseNotifications.test.ts +0 -187
  238. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  239. package/src/notifications/dispatchNotificationAction.ts +0 -142
  240. package/src/notifications/flash.test.ts +0 -89
  241. package/src/notifications/flash.ts +0 -71
  242. package/src/notifications/index.ts +0 -45
  243. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  244. package/src/notifications/registerBroadcastAuth.ts +0 -100
  245. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  246. package/src/notifications/resolveSavedNotification.ts +0 -59
  247. package/src/notifications/types.ts +0 -93
  248. package/src/orm/m2mAccessor.ts +0 -66
  249. package/src/orm/modelDefaults.test.ts +0 -633
  250. package/src/orm/modelDefaults.ts +0 -666
  251. package/src/pageData/breadcrumbs.ts +0 -288
  252. package/src/pageData/forms.ts +0 -578
  253. package/src/pageData/helpers.ts +0 -857
  254. package/src/pageData/misc.ts +0 -347
  255. package/src/pageData/navigation.ts +0 -842
  256. package/src/pageData/relationPages.ts +0 -1248
  257. package/src/pageData/relationTabs.ts +0 -286
  258. package/src/pageData/resourcePages.ts +0 -609
  259. package/src/pageData.test.ts +0 -1545
  260. package/src/pageData.ts +0 -341
  261. package/src/plugins/index.ts +0 -8
  262. package/src/plugins/themeEditor.test.ts +0 -36
  263. package/src/plugins/themeEditor.ts +0 -45
  264. package/src/react/AppShell.tsx +0 -251
  265. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  266. package/src/react/CollabRoomContext.ts +0 -98
  267. package/src/react/CollabTextRendererRegistry.ts +0 -102
  268. package/src/react/CommandPalette.tsx +0 -375
  269. package/src/react/CurrentUserContext.tsx +0 -50
  270. package/src/react/CustomPageWrapperGate.tsx +0 -69
  271. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  272. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  273. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  274. package/src/react/FieldPresenceRegistry.ts +0 -46
  275. package/src/react/FormCollabBindingRegistry.ts +0 -242
  276. package/src/react/FormStateContext.tsx +0 -591
  277. package/src/react/HeadHooks.tsx +0 -126
  278. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  279. package/src/react/MarkdownEditorRegistry.ts +0 -107
  280. package/src/react/NotificationActionStrip.tsx +0 -263
  281. package/src/react/NotificationBell.tsx +0 -426
  282. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  283. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  284. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  285. package/src/react/PendingSuggestionsContext.tsx +0 -172
  286. package/src/react/RecordWrapperGate.tsx +0 -58
  287. package/src/react/RecordWrapperRegistry.ts +0 -39
  288. package/src/react/RenderHookSlot.tsx +0 -32
  289. package/src/react/RightSidebar.tsx +0 -257
  290. package/src/react/RightSidebarContext.tsx +0 -234
  291. package/src/react/RightSidebarTrigger.tsx +0 -53
  292. package/src/react/RowCoordsContext.tsx +0 -23
  293. package/src/react/SchemaRenderer.tsx +0 -549
  294. package/src/react/SearchTrigger.tsx +0 -46
  295. package/src/react/ThemeProvider.tsx +0 -93
  296. package/src/react/ThemeSettingsPage.tsx +0 -579
  297. package/src/react/ThemeToggle.tsx +0 -20
  298. package/src/react/Toaster.tsx +0 -158
  299. package/src/react/UserMenu.tsx +0 -196
  300. package/src/react/WidgetDataContext.tsx +0 -157
  301. package/src/react/cells/EditableCell.tsx +0 -389
  302. package/src/react/component-slots.test.ts +0 -103
  303. package/src/react/component-slots.ts +0 -116
  304. package/src/react/fieldJsHandler.test.ts +0 -166
  305. package/src/react/fieldJsHandler.ts +0 -79
  306. package/src/react/fields/BuilderInput.tsx +0 -1078
  307. package/src/react/fields/CheckboxInput.tsx +0 -39
  308. package/src/react/fields/CheckboxListInput.tsx +0 -102
  309. package/src/react/fields/ColorInput.tsx +0 -71
  310. package/src/react/fields/DateFieldInput.tsx +0 -70
  311. package/src/react/fields/DateTimeInput.tsx +0 -62
  312. package/src/react/fields/FieldShell.tsx +0 -348
  313. package/src/react/fields/FileUploadInput.tsx +0 -639
  314. package/src/react/fields/HiddenInput.tsx +0 -17
  315. package/src/react/fields/KeyValueInput.tsx +0 -230
  316. package/src/react/fields/MarkdownInput.tsx +0 -560
  317. package/src/react/fields/RadioInput.tsx +0 -81
  318. package/src/react/fields/RepeaterInput.test.ts +0 -116
  319. package/src/react/fields/RepeaterInput.tsx +0 -1420
  320. package/src/react/fields/SelectFieldInput.tsx +0 -280
  321. package/src/react/fields/SliderInput.tsx +0 -81
  322. package/src/react/fields/TagsInput.tsx +0 -283
  323. package/src/react/fields/TextLikeInput.tsx +0 -256
  324. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  325. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  326. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  327. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  328. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  329. package/src/react/fields/repeaterReconcile.ts +0 -104
  330. package/src/react/fields/rowChromeButton.tsx +0 -336
  331. package/src/react/fields/rowState.ts +0 -106
  332. package/src/react/fields/syncRowGates.test.ts +0 -202
  333. package/src/react/fields/syncRowGates.ts +0 -66
  334. package/src/react/fields/textInputControls.tsx +0 -238
  335. package/src/react/fields/useRowReorderDnd.ts +0 -78
  336. package/src/react/formStateHelpers.test.ts +0 -508
  337. package/src/react/formStateHelpers.ts +0 -381
  338. package/src/react/hooks/use-mobile.ts +0 -19
  339. package/src/react/icon-context.tsx +0 -60
  340. package/src/react/index.ts +0 -194
  341. package/src/react/layouts/SidebarLayout.tsx +0 -250
  342. package/src/react/layouts/TopbarLayout.tsx +0 -258
  343. package/src/react/navigate.tsx +0 -37
  344. package/src/react/onProviderSynced.test.ts +0 -90
  345. package/src/react/parseRecordEditUrl.test.ts +0 -122
  346. package/src/react/parseRecordEditUrl.ts +0 -94
  347. package/src/react/persistedState.ts +0 -40
  348. package/src/react/registry.ts +0 -48
  349. package/src/react/right-panel-registry.tsx +0 -47
  350. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  351. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  352. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  353. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  354. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  355. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  356. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  357. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  358. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  359. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  360. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  361. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  362. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  363. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  364. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  365. package/src/react/schemaRenderer/constants.ts +0 -50
  366. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  367. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  368. package/src/react/schemaRenderer/helpers.tsx +0 -81
  369. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  370. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  371. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  372. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  373. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  374. package/src/react/schemaRenderer/table/links.tsx +0 -112
  375. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  376. package/src/react/schemaRenderer/table/url.tsx +0 -143
  377. package/src/react/theme-preview/apply.ts +0 -99
  378. package/src/react/theme-preview/build-html.ts +0 -436
  379. package/src/react/ui/button.tsx +0 -51
  380. package/src/react/ui/calendar.tsx +0 -67
  381. package/src/react/ui/checkbox.tsx +0 -29
  382. package/src/react/ui/dialog.tsx +0 -108
  383. package/src/react/ui/dropdown-menu.tsx +0 -97
  384. package/src/react/ui/input.tsx +0 -20
  385. package/src/react/ui/label.tsx +0 -21
  386. package/src/react/ui/popover.tsx +0 -50
  387. package/src/react/ui/select.tsx +0 -169
  388. package/src/react/ui/separator.tsx +0 -25
  389. package/src/react/ui/sheet.tsx +0 -136
  390. package/src/react/ui/sidebar.tsx +0 -723
  391. package/src/react/ui/skeleton.tsx +0 -13
  392. package/src/react/ui/slider.tsx +0 -34
  393. package/src/react/ui/switch.tsx +0 -28
  394. package/src/react/ui/table.tsx +0 -105
  395. package/src/react/ui/tabs.tsx +0 -63
  396. package/src/react/ui/textarea.tsx +0 -18
  397. package/src/react/ui/tooltip.tsx +0 -64
  398. package/src/react/useResizableWidth.ts +0 -139
  399. package/src/react/utils.ts +0 -6
  400. package/src/react/widgetRegistry.test.ts +0 -43
  401. package/src/react/widgetRegistry.ts +0 -50
  402. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  403. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  404. package/src/react/widgets/ViewRenderer.tsx +0 -71
  405. package/src/relationManagerData.test.ts +0 -1595
  406. package/src/richtext/index.ts +0 -8
  407. package/src/richtext/registry.ts +0 -89
  408. package/src/routes/globals.ts +0 -148
  409. package/src/routes/guard.test.ts +0 -325
  410. package/src/routes/helpers.ts +0 -704
  411. package/src/routes/pages.ts +0 -175
  412. package/src/routes/panel.ts +0 -204
  413. package/src/routes/relations.ts +0 -1243
  414. package/src/routes/resources.ts +0 -781
  415. package/src/routes/theme.ts +0 -91
  416. package/src/routes-nested-relations.test.ts +0 -676
  417. package/src/routes-relations.test.ts +0 -972
  418. package/src/routes.test.ts +0 -2027
  419. package/src/routes.ts +0 -303
  420. package/src/schema/Alert.test.ts +0 -109
  421. package/src/schema/Alert.ts +0 -131
  422. package/src/schema/Block.ts +0 -169
  423. package/src/schema/Breadcrumbs.ts +0 -40
  424. package/src/schema/Card.ts +0 -35
  425. package/src/schema/Divider.ts +0 -20
  426. package/src/schema/Element.ts +0 -219
  427. package/src/schema/EmptyState.test.ts +0 -37
  428. package/src/schema/EmptyState.ts +0 -63
  429. package/src/schema/Fieldset.ts +0 -43
  430. package/src/schema/Grid.ts +0 -43
  431. package/src/schema/Group.ts +0 -30
  432. package/src/schema/Heading.ts +0 -39
  433. package/src/schema/Html.ts +0 -67
  434. package/src/schema/Icon.ts +0 -54
  435. package/src/schema/Image.ts +0 -57
  436. package/src/schema/LinkTag.ts +0 -41
  437. package/src/schema/Markdown.ts +0 -85
  438. package/src/schema/MetaTag.ts +0 -41
  439. package/src/schema/RelationTabs.ts +0 -71
  440. package/src/schema/ScriptTag.ts +0 -55
  441. package/src/schema/Section.ts +0 -160
  442. package/src/schema/ServerDataElement.test.ts +0 -140
  443. package/src/schema/ServerDataElement.ts +0 -156
  444. package/src/schema/SlotComponent.test.ts +0 -77
  445. package/src/schema/SlotComponent.ts +0 -71
  446. package/src/schema/Split.ts +0 -50
  447. package/src/schema/Stat.test.ts +0 -118
  448. package/src/schema/Stat.ts +0 -154
  449. package/src/schema/StatsOverview.test.ts +0 -141
  450. package/src/schema/StatsOverview.ts +0 -119
  451. package/src/schema/StyleTag.ts +0 -35
  452. package/src/schema/TableWidget.test.ts +0 -297
  453. package/src/schema/TableWidget.ts +0 -289
  454. package/src/schema/Tabs.ts +0 -79
  455. package/src/schema/Text.ts +0 -58
  456. package/src/schema/UnorderedList.ts +0 -49
  457. package/src/schema/View.test.ts +0 -111
  458. package/src/schema/View.ts +0 -127
  459. package/src/schema/Wizard.ts +0 -220
  460. package/src/schema/containers.test.ts +0 -564
  461. package/src/schema/headTags.test.ts +0 -134
  462. package/src/schema/index.ts +0 -40
  463. package/src/schema/primes.test.ts +0 -269
  464. package/src/schema/resolveSchema.test.ts +0 -379
  465. package/src/schema/resolveSchema.ts +0 -917
  466. package/src/schema/sanitize.ts +0 -58
  467. package/src/search.test.ts +0 -446
  468. package/src/search.ts +0 -178
  469. package/src/sessionFilters.test.ts +0 -375
  470. package/src/sessionFilters.ts +0 -143
  471. package/src/slot-components/index.ts +0 -10
  472. package/src/slot-components/registry.ts +0 -56
  473. package/src/styles/file-upload.css +0 -13
  474. package/src/summarizers/Summarizer.test.ts +0 -84
  475. package/src/summarizers/Summarizer.ts +0 -123
  476. package/src/summarizers/index.ts +0 -11
  477. package/src/theme/base-colors.ts +0 -68
  478. package/src/theme/chart-colors.ts +0 -50
  479. package/src/theme/colors.ts +0 -447
  480. package/src/theme/generate-css.test.ts +0 -139
  481. package/src/theme/generate-css.ts +0 -44
  482. package/src/theme/generate-scale.test.ts +0 -106
  483. package/src/theme/generate-scale.ts +0 -97
  484. package/src/theme/icon-map.ts +0 -42
  485. package/src/theme/index.ts +0 -34
  486. package/src/theme/migrate.test.ts +0 -178
  487. package/src/theme/migrate.ts +0 -81
  488. package/src/theme/presets.ts +0 -135
  489. package/src/theme/radius.ts +0 -18
  490. package/src/theme/resolve.test.ts +0 -238
  491. package/src/theme/resolve.ts +0 -96
  492. package/src/theme/spacing.ts +0 -18
  493. package/src/theme/storage.test.ts +0 -126
  494. package/src/theme/storage.ts +0 -106
  495. package/src/theme/theme-colors.ts +0 -88
  496. package/src/theme/types.ts +0 -125
  497. package/src/uploads/UploadAdapter.ts +0 -35
  498. package/src/uploads/index.ts +0 -2
  499. package/src/uploads/localUpload.test.ts +0 -70
  500. package/src/uploads/localUpload.ts +0 -84
  501. package/src/validation/Validator.ts +0 -49
  502. package/src/validation/index.ts +0 -28
  503. package/src/validation/rules.ts +0 -78
  504. package/src/validation/runValidators.ts +0 -435
  505. package/src/validation/uniqueValidator.test.ts +0 -196
  506. package/src/validation/uniqueValidator.ts +0 -133
  507. package/src/validation/validators.test.ts +0 -268
  508. package/src/vite.test.ts +0 -184
  509. package/src/vite.ts +0 -787
  510. package/src/widgets/index.ts +0 -10
  511. package/src/widgets/registry.ts +0 -45
  512. package/src/widgets.test.ts +0 -592
  513. package/tsconfig.build.json +0 -11
  514. package/tsconfig.json +0 -4
  515. package/tsconfig.test.json +0 -10
  516. package/views/react/Dashboard.tsx +0 -27
  517. package/views/react/Resources/Form.tsx +0 -102
  518. package/views/react/Resources/Index.tsx +0 -49
@@ -1,426 +0,0 @@
1
- 'use client'
2
-
3
- import React from 'react'
4
- import {
5
- DropdownMenu,
6
- DropdownMenuContent,
7
- DropdownMenuTrigger,
8
- } from './ui/dropdown-menu.js'
9
- import { useNavigate } from './navigate.js'
10
- import { useIconFor } from './icon-context.js'
11
- import { cn } from './utils.js'
12
- import type { DatabaseNotificationsMeta } from '../pageData.js'
13
- import type { DatabaseNotificationMeta } from '../notifications/database.js'
14
- import type { NavigationBadgeColor } from '../Resource.js'
15
- import type { NotificationMeta as NotificationMetaImport } from '../notifications/Notification.js'
16
- import { NotificationActionStrip } from './NotificationActionStrip.js'
17
- import { useToast } from './Toaster.js'
18
-
19
- const BADGE_COLOR: Record<NavigationBadgeColor, string> = {
20
- default: 'bg-muted text-muted-foreground',
21
- primary: 'bg-primary text-primary-foreground',
22
- success: 'bg-emerald-500 text-white',
23
- warning: 'bg-amber-500 text-white',
24
- destructive: 'bg-red-500 text-white',
25
- info: 'bg-sky-500 text-white',
26
- }
27
-
28
- const TYPE_DOT: Record<NonNullable<DatabaseNotificationMeta['type']>, string> = {
29
- info: 'bg-sky-500',
30
- success: 'bg-emerald-500',
31
- warning: 'bg-amber-500',
32
- error: 'bg-red-500',
33
- }
34
-
35
- /**
36
- * Bell-icon dropdown for `Pilotiq.databaseNotifications()`. Renders
37
- * nothing when `meta` is absent — `panelInfo()` only ships the meta
38
- * when the panel opted in AND a user resolved.
39
- *
40
- * Lifecycle:
41
- * - First mount: fetch the current list via `meta.listUrl`.
42
- * - Polling: when `meta.polling > 0`, refetch every N seconds.
43
- * Pauses while `document.visibilityState !== 'visible'` so a
44
- * backgrounded tab doesn't keep tickling the server.
45
- * - Mark-read: optimistic — flips `readAt` immediately, refetches
46
- * after the POST so the unread count rebases against the server.
47
- * - Click-through: when a row carries a `url`, the click marks-read
48
- * + SPA-navigates in the same step.
49
- */
50
- export function NotificationBell({ meta }: { meta?: DatabaseNotificationsMeta }) {
51
- if (!meta) return null
52
- const navigate = useNavigate()
53
-
54
- const [open, setOpen] = React.useState(false)
55
- const [data, setData] = React.useState<{
56
- notifications: DatabaseNotificationMeta[]
57
- unreadCount: number
58
- } | null>(null)
59
- const [loading, setLoading] = React.useState(false)
60
-
61
- // ── Fetch helpers ─────────────────────────────────────
62
- // Latest-wins seq tracking — same pattern as the global-search
63
- // palette / live-form state. A slow first request can't clobber a
64
- // fresh second one.
65
- const seqRef = React.useRef(0)
66
- const latestRef = React.useRef(0)
67
- const refetch = React.useCallback(async () => {
68
- const seq = ++seqRef.current
69
- setLoading(true)
70
- try {
71
- const r = await fetch(meta.listUrl, {
72
- headers: { Accept: 'application/json' },
73
- credentials: 'same-origin',
74
- })
75
- if (!r.ok) return
76
- const json = await r.json() as {
77
- notifications?: DatabaseNotificationMeta[]
78
- unreadCount?: number
79
- }
80
- if (seq < latestRef.current) return
81
- latestRef.current = seq
82
- setData({
83
- notifications: json.notifications ?? [],
84
- unreadCount: json.unreadCount ?? 0,
85
- })
86
- } catch { /* network-flake; the next poll retries */ }
87
- finally {
88
- if (seq >= latestRef.current) setLoading(false)
89
- }
90
- }, [meta.listUrl])
91
-
92
- // ── First mount + polling ────────────────────────────
93
- React.useEffect(() => {
94
- void refetch()
95
- }, [refetch])
96
-
97
- React.useEffect(() => {
98
- if (meta.polling === null || meta.polling === 0) return
99
- const ms = meta.polling * 1000
100
- const tick = () => {
101
- if (typeof document !== 'undefined' && document.visibilityState !== 'visible') return
102
- void refetch()
103
- }
104
- const id = window.setInterval(tick, ms)
105
- return () => window.clearInterval(id)
106
- }, [meta.polling, refetch])
107
-
108
- // ── Phase 2 broadcast subscription ───────────────────
109
- // When `meta.broadcast` is present, connect to the panel's WebSocket
110
- // and listen for `notification.created` on the user's private channel.
111
- // Triggers the same `refetch()` already wired for polling — broadcast
112
- // is "low-latency refetch hint", not a payload-pushing transport.
113
- //
114
- // RudderSocket loads via dynamic import so the bell doesn't bundle
115
- // broadcast for apps that don't enable it. Failures (package missing,
116
- // wsUrl unreachable, auth rejected) silently fall back to polling.
117
- React.useEffect(() => {
118
- if (!meta.broadcast) return
119
- if (typeof window === 'undefined') return
120
- let cancelled = false
121
- let socket: { disconnect(): void } | null = null
122
-
123
- const wsUrl = meta.broadcast.wsUrl || sameOriginWsUrl()
124
- const channelName = meta.broadcast.channel
125
- const eventName = meta.broadcast.event
126
-
127
- void (async () => {
128
- try {
129
- const RudderSocket = await loadRudderSocket()
130
- if (!RudderSocket || cancelled) return
131
- const s = new RudderSocket(wsUrl)
132
- socket = s
133
- // Strip the `private-` prefix — `RudderSocket.private(name)`
134
- // re-adds it. We pass an empty token because the auth callback
135
- // we registered server-side reads the upgrade request's cookies,
136
- // not a per-message bearer token.
137
- const bareName = channelName.replace(/^private-/, '')
138
- s.private(bareName, '').on(eventName, () => {
139
- if (cancelled) return
140
- void refetch()
141
- })
142
- } catch { /* package not vendored / connect failed — polling covers it */ }
143
- })()
144
-
145
- return () => {
146
- cancelled = true
147
- socket?.disconnect()
148
- }
149
- }, [meta.broadcast?.wsUrl, meta.broadcast?.channel, meta.broadcast?.event, refetch])
150
-
151
- // ── Mutations ────────────────────────────────────────
152
- const markRead = React.useCallback(async (id: string) => {
153
- // Optimistic flip — the bell stays responsive even on slow networks.
154
- setData(prev => prev && {
155
- notifications: prev.notifications.map(n =>
156
- n.id === id ? { ...n, readAt: n.readAt ?? new Date().toISOString() } : n,
157
- ),
158
- unreadCount: Math.max(0, prev.unreadCount - 1),
159
- })
160
- try {
161
- await fetch(meta.readUrl.replace(':id', encodeURIComponent(id)), {
162
- method: 'POST',
163
- headers: { Accept: 'application/json' },
164
- credentials: 'same-origin',
165
- })
166
- } catch { /* swallow — next poll resyncs */ }
167
- void refetch()
168
- }, [meta.readUrl, refetch])
169
-
170
- const markAllRead = React.useCallback(async () => {
171
- setData(prev => prev && {
172
- notifications: prev.notifications.map(n =>
173
- n.readAt ? n : { ...n, readAt: new Date().toISOString() }),
174
- unreadCount: 0,
175
- })
176
- try {
177
- await fetch(meta.readAllUrl, {
178
- method: 'POST',
179
- headers: { Accept: 'application/json' },
180
- credentials: 'same-origin',
181
- })
182
- } catch { /* swallow */ }
183
- void refetch()
184
- }, [meta.readAllUrl, refetch])
185
-
186
- const unreadCount = data?.unreadCount ?? 0
187
- const items = data?.notifications ?? []
188
- const toast = useToast()
189
-
190
- return (
191
- <DropdownMenu open={open} onOpenChange={setOpen}>
192
- <DropdownMenuTrigger
193
- className="relative inline-flex items-center justify-center size-9 rounded-md hover:bg-accent hover:text-accent-foreground transition-colors focus:outline-none"
194
- aria-label={meta.trigger?.label ?? 'Notifications'}
195
- >
196
- <BellIcon icon={meta.trigger?.icon} />
197
- {unreadCount > 0 && (
198
- <span
199
- className={cn(
200
- 'absolute -top-0.5 -end-0.5 inline-flex min-w-[1rem] h-4 items-center justify-center rounded-full px-1 text-[10px] font-semibold leading-none',
201
- BADGE_COLOR[meta.badgeColor],
202
- )}
203
- aria-label={`${unreadCount} unread`}
204
- >
205
- {unreadCount > 99 ? '99+' : unreadCount}
206
- </span>
207
- )}
208
- </DropdownMenuTrigger>
209
-
210
- <DropdownMenuContent align="end" className="w-80 p-0">
211
- <header className="flex items-center justify-between px-3 py-2 border-b">
212
- <span className="text-sm font-medium">Notifications</span>
213
- {unreadCount > 0 && (
214
- <button
215
- type="button"
216
- className="text-xs text-muted-foreground hover:text-foreground transition-colors"
217
- onClick={() => { void markAllRead() }}
218
- >
219
- Mark all as read
220
- </button>
221
- )}
222
- </header>
223
-
224
- <div className="max-h-96 overflow-y-auto">
225
- {items.length === 0
226
- ? <EmptyState loading={loading} />
227
- : items.map(n => (
228
- <NotificationRow
229
- key={n.id}
230
- notification={n}
231
- actionUrlTemplate={meta.actionUrl}
232
- onMarkRead={markRead}
233
- onNavigate={(href) => { setOpen(false); navigate(href) }}
234
- onNotify={(notifs) => notifs.forEach(t => toast.notify(t))}
235
- onAfterClick={() => setOpen(false)}
236
- />
237
- ))
238
- }
239
- </div>
240
- </DropdownMenuContent>
241
- </DropdownMenu>
242
- )
243
- }
244
-
245
- function NotificationRow({
246
- notification: n,
247
- actionUrlTemplate,
248
- onMarkRead,
249
- onNavigate,
250
- onNotify,
251
- onAfterClick,
252
- }: {
253
- notification: DatabaseNotificationMeta
254
- actionUrlTemplate?: string
255
- onMarkRead: (id: string) => void
256
- onNavigate: (url: string) => void
257
- onNotify: (notifs: NotificationMetaImport[]) => void
258
- onAfterClick: () => void
259
- }) {
260
- const RowIcon = useIconFor(n.icon)
261
- const unread = !n.readAt
262
-
263
- const onClick = (e: React.MouseEvent) => {
264
- // Modified clicks (cmd/ctrl/middle) preserve native semantics.
265
- if (e.defaultPrevented || e.metaKey || e.ctrlKey || e.shiftKey) return
266
- e.preventDefault()
267
- if (unread) onMarkRead(n.id)
268
- if (n.url) onNavigate(n.url)
269
- }
270
-
271
- // Body wrapper is `<a>` when the row has a click-through url, plain
272
- // `<button>` otherwise. Form-mode + handler-mode action buttons in
273
- // the strip can't validly nest inside an `<a>`/`<button>`, so the
274
- // strip lives as a sibling under a shared chrome `<div>`.
275
- const Tag: 'a' | 'button' = n.url ? 'a' : 'button'
276
- const tagProps: React.AnchorHTMLAttributes<HTMLAnchorElement> & React.ButtonHTMLAttributes<HTMLButtonElement> = {
277
- onClick,
278
- className: 'w-full text-start flex items-start gap-2 focus:outline-none',
279
- }
280
- if (Tag === 'a') {
281
- tagProps.href = n.url!
282
- } else {
283
- tagProps.type = 'button'
284
- }
285
-
286
- return (
287
- <div
288
- className={cn(
289
- 'px-3 py-2 hover:bg-accent transition-colors border-b last:border-b-0',
290
- unread && 'bg-primary/5',
291
- )}
292
- >
293
- <Tag {...(tagProps as React.HTMLAttributes<HTMLElement>)}>
294
- <span
295
- className={cn(
296
- 'mt-1.5 inline-block size-2 rounded-full shrink-0',
297
- n.type ? TYPE_DOT[n.type] : 'bg-muted-foreground/40',
298
- )}
299
- aria-hidden="true"
300
- />
301
- <span className="flex-1 min-w-0">
302
- <span className="flex items-center gap-1.5">
303
- {RowIcon && <RowIcon className="size-3.5 text-muted-foreground" aria-hidden="true" />}
304
- <span className={cn('text-sm leading-tight', unread ? 'font-medium' : '')}>
305
- {n.title || 'Notification'}
306
- </span>
307
- </span>
308
- {n.body && (
309
- <span className="block text-xs text-muted-foreground mt-0.5 line-clamp-2">
310
- {n.body}
311
- </span>
312
- )}
313
- <span className="block text-[11px] text-muted-foreground/70 mt-1">
314
- {formatRelative(n.createdAt)}
315
- </span>
316
- </span>
317
- </Tag>
318
- {n.actions && n.actions.length > 0 && (
319
- <div className="ms-4">
320
- <NotificationActionStrip
321
- actions={n.actions}
322
- {...(actionUrlTemplate !== undefined ? { actionUrlTemplate } : {})}
323
- notificationId={n.id}
324
- onMarkAsRead={onMarkRead}
325
- onNotify={onNotify}
326
- onAfterClick={onAfterClick}
327
- />
328
- </div>
329
- )}
330
- </div>
331
- )
332
- }
333
-
334
- function EmptyState({ loading }: { loading: boolean }) {
335
- return (
336
- <div className="px-3 py-8 text-center text-sm text-muted-foreground">
337
- {loading ? 'Loading…' : 'No notifications'}
338
- </div>
339
- )
340
- }
341
-
342
- function BellIcon({ icon }: { icon: string | undefined }) {
343
- // Honor `meta.trigger.icon` when set (string registry name); fall
344
- // through to the bundled bell glyph otherwise. The registry-resolved
345
- // hook returns `null` when no entry is registered, so a misspelt name
346
- // still shows the default bell — better than a silent blank.
347
- const Icon = useIconFor(icon)
348
- if (Icon) return <Icon className="size-5" aria-hidden="true" />
349
- return (
350
- <svg
351
- xmlns="http://www.w3.org/2000/svg"
352
- width="20" height="20" viewBox="0 0 24 24" fill="none"
353
- stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"
354
- aria-hidden="true"
355
- >
356
- <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" />
357
- <path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
358
- </svg>
359
- )
360
- }
361
-
362
- /** Soft-load `RudderSocket` from whichever path the consuming app
363
- * vendored it to. `@rudderjs/broadcast` ships the client as
364
- * `client/RudderSocket.ts`, intended for vendor:publish copy into the
365
- * app's `src/`. We try a stack of common locations in order, returning
366
- * the first that resolves; falls through to `null` when nothing's
367
- * reachable so the bell silently continues with polling.
368
- *
369
- * Apps that vendor the file under a custom path can register their
370
- * own loader by setting `window.__pilotiqRudderSocket` to the class
371
- * constructor before the bell mounts. */
372
- type RudderSocketCtor = new (url: string) => {
373
- private(name: string, token: string): {
374
- on(event: string, handler: (data: unknown) => void): unknown
375
- }
376
- disconnect(): void
377
- }
378
-
379
- async function loadRudderSocket(): Promise<RudderSocketCtor | null> {
380
- // Allow apps to register a custom-path RudderSocket without us
381
- // having to guess at the import path.
382
- const w = typeof window !== 'undefined'
383
- ? (window as unknown as { __pilotiqRudderSocket?: RudderSocketCtor })
384
- : null
385
- if (w?.__pilotiqRudderSocket) return w.__pilotiqRudderSocket
386
-
387
- // Apps that vendor:publish the broadcast client typically end up
388
- // with `src/RudderSocket.ts` (or `.js`); try common shapes via the
389
- // app's import-map / module resolution. The dynamic import is
390
- // wrapped in a try/catch so missing modules don't surface.
391
- const candidates = [
392
- '@rudderjs/broadcast/client/RudderSocket.js',
393
- '@rudderjs/broadcast/client/RudderSocket',
394
- ]
395
- for (const id of candidates) {
396
- try {
397
- const mod = await import(/* @vite-ignore */ id) as { RudderSocket?: RudderSocketCtor }
398
- if (mod.RudderSocket) return mod.RudderSocket
399
- } catch { /* try next */ }
400
- }
401
- return null
402
- }
403
-
404
- /** Same-origin `ws://…/ws` (or `wss://` on https). Used when
405
- * `meta.broadcast.wsUrl` is empty — the default for apps that boot
406
- * `BroadcastingProvider` with no custom path. */
407
- function sameOriginWsUrl(): string {
408
- if (typeof window === 'undefined') return ''
409
- const proto = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
410
- return `${proto}//${window.location.host}/ws`
411
- }
412
-
413
- function formatRelative(ts: number): string {
414
- const now = Date.now()
415
- const diff = Math.max(0, now - ts)
416
- const sec = Math.floor(diff / 1000)
417
- if (sec < 45) return 'Just now'
418
- if (sec < 90) return '1 minute ago'
419
- const min = Math.floor(sec / 60)
420
- if (min < 60) return `${min} minutes ago`
421
- const hr = Math.floor(min / 60)
422
- if (hr < 24) return `${hr} hour${hr === 1 ? '' : 's'} ago`
423
- const day = Math.floor(hr / 24)
424
- if (day < 7) return `${day} day${day === 1 ? '' : 's'} ago`
425
- return new Date(ts).toLocaleDateString()
426
- }
@@ -1,97 +0,0 @@
1
- import { describe, it, beforeEach } from 'node:test'
2
- import assert from 'node:assert/strict'
3
-
4
- import {
5
- registerPendingSuggestionApplier,
6
- getPendingSuggestionApplier,
7
- _clearAppliersForTests,
8
- } from './PendingSuggestionApplierRegistry.js'
9
-
10
- describe('PendingSuggestionApplierRegistry', () => {
11
- beforeEach(() => { _clearAppliersForTests() })
12
-
13
- it('returns the scoped applier when both formId and fieldName match', () => {
14
- const apply = (): void => {}
15
- registerPendingSuggestionApplier('form-1', 'bio', apply)
16
- assert.equal(getPendingSuggestionApplier('form-1', 'bio'), apply)
17
- })
18
-
19
- it('routes scoped lookups to the matching scoped entry across multi-form pages', () => {
20
- // Two editors in two forms register under the same field name but
21
- // different formIds — the routing has to disambiguate.
22
- const applyA = (): void => {}
23
- const applyB = (): void => {}
24
- registerPendingSuggestionApplier('form-A', 'summary', applyA)
25
- registerPendingSuggestionApplier('form-B', 'summary', applyB)
26
- assert.equal(getPendingSuggestionApplier('form-A', 'summary'), applyA)
27
- assert.equal(getPendingSuggestionApplier('form-B', 'summary'), applyB)
28
- })
29
-
30
- it('falls through to the wildcard slot when no scoped match exists', () => {
31
- const wild = (): void => {}
32
- registerPendingSuggestionApplier(undefined, 'bio', wild)
33
- // formId provided but the registry only has a wildcard entry —
34
- // the lookup should still resolve so historic single-form pages keep
35
- // working even when consumers thread a formId.
36
- assert.equal(getPendingSuggestionApplier('form-1', 'bio'), wild)
37
- })
38
-
39
- it('global producer + scoped consumer: undefined-formId lookup finds the scoped entry', () => {
40
- // Regression guard for the multi-form fix: after threading `formId`
41
- // through the Tiptap adapter hooks, every editor registers scoped
42
- // by its surrounding `FormRenderer`'s id. A global producer (no
43
- // `formId` stamped on the suggestion) calls
44
- // `getPendingSuggestionApplier(undefined, fieldName)` — without the
45
- // fallback, the wildcard slot is empty and the suggestion silently
46
- // never applies. The fallback returns any matching scoped entry.
47
- const scopedApply = (): void => {}
48
- registerPendingSuggestionApplier('form-edit', 'bio', scopedApply)
49
- assert.equal(getPendingSuggestionApplier(undefined, 'bio'), scopedApply)
50
- })
51
-
52
- it('global lookup prefers the explicit wildcard slot over scoped fallback', () => {
53
- const wild = (): void => {}
54
- const scoped = (): void => {}
55
- registerPendingSuggestionApplier('form-edit', 'bio', scoped)
56
- registerPendingSuggestionApplier(undefined, 'bio', wild)
57
- // Wildcard wins — it was the intent of the original API ("formId
58
- // defaults to '*'") and a deliberately-registered wildcard applier
59
- // is presumed authoritative.
60
- assert.equal(getPendingSuggestionApplier(undefined, 'bio'), wild)
61
- })
62
-
63
- it('scoped lookup prefers exact match over wildcard slot', () => {
64
- const wild = (): void => {}
65
- const scoped = (): void => {}
66
- registerPendingSuggestionApplier(undefined, 'bio', wild)
67
- registerPendingSuggestionApplier('form-edit', 'bio', scoped)
68
- assert.equal(getPendingSuggestionApplier('form-edit', 'bio'), scoped)
69
- })
70
-
71
- it('returns undefined when no entry matches the fieldName at all', () => {
72
- registerPendingSuggestionApplier('form-A', 'bio', () => {})
73
- assert.equal(getPendingSuggestionApplier('form-A', 'subtitle'), undefined)
74
- assert.equal(getPendingSuggestionApplier(undefined, 'subtitle'), undefined)
75
- })
76
-
77
- it('unregister cleanup drops the entry', () => {
78
- const apply = (): void => {}
79
- const unregister = registerPendingSuggestionApplier('form-1', 'bio', apply)
80
- assert.equal(getPendingSuggestionApplier('form-1', 'bio'), apply)
81
- unregister()
82
- assert.equal(getPendingSuggestionApplier('form-1', 'bio'), undefined)
83
- })
84
-
85
- it('re-registering replaces the previous entry and its unregister no-ops', () => {
86
- const first = (): void => {}
87
- const second = (): void => {}
88
- const off1 = registerPendingSuggestionApplier('form-1', 'bio', first)
89
- registerPendingSuggestionApplier('form-1', 'bio', second) // wins
90
- assert.equal(getPendingSuggestionApplier('form-1', 'bio'), second)
91
- // First's unregister must NOT delete the second's entry — the
92
- // registry tracks identity to defend against unmount-after-remount
93
- // racing the cleanup of the just-replaced entry.
94
- off1()
95
- assert.equal(getPendingSuggestionApplier('form-1', 'bio'), second)
96
- })
97
- })
@@ -1,98 +0,0 @@
1
- import type { PendingSuggestion } from './PendingSuggestionsContext.js'
2
-
3
- /**
4
- * A function that applies a `PendingSuggestion` to its target field —
5
- * registered by the field renderer (or editor adapter) on mount, looked
6
- * up by aggregate consumers (e.g. a chat-sidebar pending-pill's
7
- * "Approve all" button) that live outside the form's React tree.
8
- *
9
- * The applier is responsible for the apply *only*. Dismissing the
10
- * suggestion from the queue is the caller's job — the apply path is
11
- * decoupled from the queue-side bookkeeping so a future Phase that
12
- * mirrors approvals to a server can do both via different code paths.
13
- */
14
- export type PendingSuggestionApplier = (suggestion: PendingSuggestion) => void
15
-
16
- interface RegistryEntry {
17
- formId: string | undefined
18
- fieldName: string
19
- apply: PendingSuggestionApplier
20
- }
21
-
22
- const _entries = new Map<string, RegistryEntry>()
23
-
24
- /**
25
- * Compose the registry key. `formId` defaults to `'*'` (global form
26
- * scope) so renderers in non-multi-form pages don't have to thread an
27
- * id. Form-scoped registrations always win over the wildcard when both
28
- * exist for the same field name.
29
- */
30
- function keyFor(formId: string | undefined, fieldName: string): string {
31
- return `${formId ?? '*'}::${fieldName}`
32
- }
33
-
34
- /**
35
- * Register an applier for `(formId, fieldName)`. Returns an unregister
36
- * function for `useEffect` cleanup. Re-registering with the same key
37
- * replaces the previous entry — the most recently mounted renderer
38
- * wins (typical in multi-instance form scenarios where an old form
39
- * unmounts after a new one mounts during navigation).
40
- */
41
- export function registerPendingSuggestionApplier(
42
- formId: string | undefined,
43
- fieldName: string,
44
- apply: PendingSuggestionApplier,
45
- ): () => void {
46
- const key = keyFor(formId, fieldName)
47
- const entry: RegistryEntry = { formId, fieldName, apply }
48
- _entries.set(key, entry)
49
- return () => {
50
- // Only delete if this entry is still the one we registered — a
51
- // re-register from another instance may have replaced us.
52
- if (_entries.get(key) === entry) _entries.delete(key)
53
- }
54
- }
55
-
56
- /**
57
- * Look up an applier for `(formId, fieldName)`. Tries the form-scoped
58
- * key first; falls back to the wildcard form ('*') so a producer that
59
- * pushed a suggestion without `formId` still resolves an applier from
60
- * a single-form page.
61
- *
62
- * Global-producer fallback: when the lookup `formId` is `undefined` AND
63
- * no wildcard entry is registered, return any single scoped entry
64
- * matching `fieldName`. This mirrors the consumer-side filter in
65
- * `usePendingSuggestionsForField` which lets undefined formId on either
66
- * side pass-through. Editors today register scoped by their surrounding
67
- * `FormRenderer`'s id (`useFormId()`), so the wildcard slot is almost
68
- * always empty — without this fallback, a producer that pushes without
69
- * a formId on a single-form page would silently fail to resolve any
70
- * applier. We pick the first scoped match (Map insertion order); when
71
- * the page genuinely has multiple forms with the same field name,
72
- * producers SHOULD stamp `formId` to disambiguate.
73
- */
74
- export function getPendingSuggestionApplier(
75
- formId: string | undefined,
76
- fieldName: string,
77
- ): PendingSuggestionApplier | undefined {
78
- if (formId !== undefined) {
79
- const scoped = _entries.get(keyFor(formId, fieldName))
80
- if (scoped) return scoped.apply
81
- }
82
- const wild = _entries.get(keyFor(undefined, fieldName))
83
- if (wild) return wild.apply
84
- if (formId === undefined) {
85
- for (const entry of _entries.values()) {
86
- if (entry.fieldName === fieldName) return entry.apply
87
- }
88
- }
89
- return undefined
90
- }
91
-
92
- /**
93
- * Test seam — clear the registry between tests. Not part of the public
94
- * API.
95
- */
96
- export function _clearAppliersForTests(): void {
97
- _entries.clear()
98
- }
@@ -1,54 +0,0 @@
1
- import type { ComponentType } from 'react'
2
- import type { PendingSuggestion } from './PendingSuggestionsContext.js'
3
- import type { ElementMeta } from '../schema/Element.js'
4
-
5
- /**
6
- * Props the per-field overlay component receives from `FieldShell` when
7
- * one or more pending suggestions target the field.
8
- *
9
- * The overlay is responsible for both rendering AND applying the
10
- * suggestion. On Approve, call `onApprove()` (which dismisses the
11
- * suggestion from the queue) AFTER mutating the form's field value to
12
- * `suggestion.suggestedValue` — the queue is just a notification surface,
13
- * applying is the renderer's job.
14
- *
15
- * The Tiptap `RichTextField` renderer skips this slot entirely — it
16
- * mirrors suggestions into the editor's inline AiSuggestion extension
17
- * instead. Other field types (Text / Textarea / Select / Number / …)
18
- * render the registered overlay below their input.
19
- */
20
- export interface PendingSuggestionOverlayProps {
21
- /** First suggestion targeting this field. Aggregate UIs handle stacks. */
22
- suggestion: PendingSuggestion
23
- /** Drop from queue (callable after applying). */
24
- onApprove: () => void
25
- /** Drop from queue without applying. */
26
- onReject: () => void
27
- /** Field type string (`text` / `select` / `toggle` / `slider` / `color` /
28
- * …) so per-fieldType overlay renderers can branch. Sparse — older
29
- * hosts may omit. Phase C of ai-review-mode. */
30
- fieldType?: string
31
- /** Resolved field meta — gives the overlay access to per-field config
32
- * (`options` for Select, `min/max` for Slider, etc.) needed to render
33
- * human-friendly comparisons rather than raw values. Sparse — older
34
- * hosts may omit. */
35
- el?: ElementMeta
36
- }
37
-
38
- let _component: ComponentType<PendingSuggestionOverlayProps> | null = null
39
-
40
- /**
41
- * Register a component to render below any `FieldShell` whose field has at
42
- * least one matching pending suggestion in the
43
- * `<PendingSuggestionsContext>` queue. Called once at boot by a plugin
44
- * (e.g. `@pilotiq-pro/ai`). No-op when no plugin registers — `FieldShell`
45
- * skips the overlay slot.
46
- */
47
- export function registerPendingSuggestionOverlay(C: ComponentType<PendingSuggestionOverlayProps>): void {
48
- _component = C
49
- }
50
-
51
- /** Returns the registered overlay component, or `null`. */
52
- export function getPendingSuggestionOverlay(): ComponentType<PendingSuggestionOverlayProps> | null {
53
- return _component
54
- }