@liorian/sdk 0.28.0-alpha.56010ca

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 (362) hide show
  1. package/README.md +483 -0
  2. package/package.json +94 -0
  3. package/schemas/module.schema.json +818 -0
  4. package/src/application/index.ts +3 -0
  5. package/src/application/module-instance.ts +36 -0
  6. package/src/application/service/auth-api-service.ts +291 -0
  7. package/src/application/service/auth-user.service.ts +108 -0
  8. package/src/application/service/index.ts +12 -0
  9. package/src/application/service/module-activation-api.service.ts +105 -0
  10. package/src/application/service/notifications-api-service.ts +38 -0
  11. package/src/application/service/notifications-push.service.ts +299 -0
  12. package/src/application/service/notifications-socket-service.ts +76 -0
  13. package/src/application/service/organizations-api-service.ts +104 -0
  14. package/src/application/service/otp-api-service.ts +11 -0
  15. package/src/application/service/storage-api-service.ts +77 -0
  16. package/src/application/service/storefront-api.service.ts +72 -0
  17. package/src/application/service/user-preferences-api.service.ts +57 -0
  18. package/src/domain/config/app.config.ts +35 -0
  19. package/src/domain/config/auth.config.ts +30 -0
  20. package/src/domain/config/index.ts +4 -0
  21. package/src/domain/config/logo.config.ts +17 -0
  22. package/src/domain/entities/activities.interface.ts +18 -0
  23. package/src/domain/entities/catalog.interface.ts +96 -0
  24. package/src/domain/entities/index.ts +13 -0
  25. package/src/domain/entities/media.ts +22 -0
  26. package/src/domain/entities/module-activation.interface.ts +173 -0
  27. package/src/domain/entities/module.interface.ts +101 -0
  28. package/src/domain/entities/notification.interface.ts +23 -0
  29. package/src/domain/entities/objectable.type.ts +3 -0
  30. package/src/domain/entities/organization.interface.ts +81 -0
  31. package/src/domain/entities/user-auth.interface.ts +17 -0
  32. package/src/domain/entities/user-phone.interface.ts +11 -0
  33. package/src/domain/entities/user-profile.interface.ts +23 -0
  34. package/src/domain/entities/user.interface.ts +79 -0
  35. package/src/domain/enums/coloring.enum.ts +6 -0
  36. package/src/domain/enums/domains.enum.ts +282 -0
  37. package/src/domain/enums/index.ts +8 -0
  38. package/src/domain/enums/module-category.enum.ts +57 -0
  39. package/src/domain/enums/module.enum.ts +97 -0
  40. package/src/domain/enums/routine.enum.ts +12 -0
  41. package/src/domain/enums/user-profile.enum.ts +25 -0
  42. package/src/domain/enums/user-status.enum.ts +7 -0
  43. package/src/domain/index.ts +6 -0
  44. package/src/domain/typing/auth-caches.ts +12 -0
  45. package/src/domain/typing/auth-oauth.types.ts +172 -0
  46. package/src/domain/typing/devise.ts +2 -0
  47. package/src/domain/typing/index.ts +10 -0
  48. package/src/domain/typing/response.ts +30 -0
  49. package/src/domain/typing/routine.types.ts +113 -0
  50. package/src/domain/typing/session.interface.ts +28 -0
  51. package/src/domain/typing/statisticals.ts +11 -0
  52. package/src/domain/typing/user-media-upload.ts +9 -0
  53. package/src/domain/typing/values.ts +6 -0
  54. package/src/index.tsx +168 -0
  55. package/src/infrastructure/capabilities/cache-storage/capability.ts +129 -0
  56. package/src/infrastructure/capabilities/cache-storage/enum.ts +5 -0
  57. package/src/infrastructure/capabilities/cache-storage/index.ts +4 -0
  58. package/src/infrastructure/capabilities/cache-storage/types.ts +13 -0
  59. package/src/infrastructure/capabilities/index.ts +3 -0
  60. package/src/infrastructure/capabilities/stackable/capability.ts +55 -0
  61. package/src/infrastructure/capabilities/stackable/index.ts +3 -0
  62. package/src/infrastructure/capabilities/stackable/types.ts +24 -0
  63. package/src/infrastructure/common-classname.constant.ts +4 -0
  64. package/src/infrastructure/data/countries.ts +208 -0
  65. package/src/infrastructure/data/gender.ts +5 -0
  66. package/src/infrastructure/data/index.ts +3 -0
  67. package/src/infrastructure/dataset/index.ts +3 -0
  68. package/src/infrastructure/dataset/sign-in.dataset.ts +21 -0
  69. package/src/infrastructure/dataset/sign-up.dataset.ts +64 -0
  70. package/src/infrastructure/hooks/index.ts +10 -0
  71. package/src/infrastructure/hooks/use-activity-lock-draft.ts +69 -0
  72. package/src/infrastructure/hooks/use-auth.ts +24 -0
  73. package/src/infrastructure/hooks/use-elastic-interaction.ts +180 -0
  74. package/src/infrastructure/hooks/use-is-desktop.ts +25 -0
  75. package/src/infrastructure/hooks/use-is-tauri.ts +17 -0
  76. package/src/infrastructure/hooks/use-mobile.ts +19 -0
  77. package/src/infrastructure/hooks/use-notifications.ts +128 -0
  78. package/src/infrastructure/hooks/use-recent-colors.ts +113 -0
  79. package/src/infrastructure/hooks/use-recent-modules.ts +76 -0
  80. package/src/infrastructure/index.tsx +11 -0
  81. package/src/infrastructure/library/design-system.ts +95 -0
  82. package/src/infrastructure/library/index.tsx +5 -0
  83. package/src/infrastructure/library/motion-utils.ts +77 -0
  84. package/src/infrastructure/library/motion.tsx +31 -0
  85. package/src/infrastructure/library/scroll-progress.tsx +93 -0
  86. package/src/infrastructure/providers/auth-guard.provider.tsx +47 -0
  87. package/src/infrastructure/providers/auth.provider.tsx +168 -0
  88. package/src/infrastructure/providers/index.tsx +8 -0
  89. package/src/infrastructure/providers/module-usage.provider.tsx +95 -0
  90. package/src/infrastructure/providers/modules-guard.provider.tsx +64 -0
  91. package/src/infrastructure/providers/modules-routines.provider.tsx +33 -0
  92. package/src/infrastructure/providers/modules-switcher.provider.tsx +25 -0
  93. package/src/infrastructure/providers/query.provider.tsx +27 -0
  94. package/src/infrastructure/routines/__tests__/routine.test.ts +368 -0
  95. package/src/infrastructure/routines/index.tsx +3 -0
  96. package/src/infrastructure/routines/routine.hook.ts +121 -0
  97. package/src/infrastructure/routines/routine.ts +283 -0
  98. package/src/infrastructure/routines/routine.tsx +283 -0
  99. package/src/infrastructure/stores/activity-lock.store.ts +22 -0
  100. package/src/infrastructure/stores/auth-user-connected.store.ts +24 -0
  101. package/src/infrastructure/stores/dataset.store.ts +106 -0
  102. package/src/infrastructure/stores/index.ts +6 -0
  103. package/src/infrastructure/stores/module.store.ts +266 -0
  104. package/src/infrastructure/stores/upload.store.ts +198 -0
  105. package/src/infrastructure/utilities/access-label.util.ts +158 -0
  106. package/src/infrastructure/utilities/activities.util.tsx +54 -0
  107. package/src/infrastructure/utilities/activity-lock.service.ts +263 -0
  108. package/src/infrastructure/utilities/activity-tracker.service.ts +205 -0
  109. package/src/infrastructure/utilities/api-service.ts +237 -0
  110. package/src/infrastructure/utilities/auth-logout.util.ts +23 -0
  111. package/src/infrastructure/utilities/classname.util.ts +7 -0
  112. package/src/infrastructure/utilities/encryption.service.ts +125 -0
  113. package/src/infrastructure/utilities/form-snapshot.service.ts +304 -0
  114. package/src/infrastructure/utilities/format.util.ts +40 -0
  115. package/src/infrastructure/utilities/index.tsx +23 -0
  116. package/src/infrastructure/utilities/media-label.service.ts +47 -0
  117. package/src/infrastructure/utilities/modules.ts +11 -0
  118. package/src/infrastructure/utilities/permission.util.ts +22 -0
  119. package/src/infrastructure/utilities/push-permission.util.ts +43 -0
  120. package/src/infrastructure/utilities/route-access.ts +38 -0
  121. package/src/infrastructure/utilities/socket-service.ts +123 -0
  122. package/src/infrastructure/utilities/sso-auth.util.ts +188 -0
  123. package/src/infrastructure/utilities/stringable.util.ts +16 -0
  124. package/src/infrastructure/utilities/strings.util.ts +18 -0
  125. package/src/infrastructure/utilities/tauri.util.ts +15 -0
  126. package/src/infrastructure/utilities/utils.ts +6 -0
  127. package/src/infrastructure/utilities/values.ts +5 -0
  128. package/src/presentation/8starlabs-ui/button.tsx +94 -0
  129. package/src/presentation/8starlabs-ui/flip-clock.tsx +368 -0
  130. package/src/presentation/8starlabs-ui/heatmap.tsx +430 -0
  131. package/src/presentation/8starlabs-ui/hooks/index.tsx +2 -0
  132. package/src/presentation/8starlabs-ui/hooks/use-mobile.ts +21 -0
  133. package/src/presentation/8starlabs-ui/index.tsx +20 -0
  134. package/src/presentation/8starlabs-ui/json-viewer.tsx +972 -0
  135. package/src/presentation/8starlabs-ui/marquee.tsx +134 -0
  136. package/src/presentation/8starlabs-ui/partition-bar.tsx +198 -0
  137. package/src/presentation/8starlabs-ui/scroll-fade.tsx +158 -0
  138. package/src/presentation/8starlabs-ui/status-indicator.tsx +86 -0
  139. package/src/presentation/8starlabs-ui/system-banner.tsx +50 -0
  140. package/src/presentation/8starlabs-ui/timeline.tsx +576 -0
  141. package/src/presentation/8starlabs-ui/transport-badge.tsx +249 -0
  142. package/src/presentation/8starlabs-ui/ui/copy-button.tsx +60 -0
  143. package/src/presentation/8starlabs-ui/ui/index.tsx +2 -0
  144. package/src/presentation/analytics-section.tsx +85 -0
  145. package/src/presentation/app-sidebar.tsx +234 -0
  146. package/src/presentation/charts/area-widget.chart-skeleton.tsx +61 -0
  147. package/src/presentation/charts/area-widget.chart.tsx +128 -0
  148. package/src/presentation/charts/chart-widgets.types.ts +81 -0
  149. package/src/presentation/charts/index.tsx +16 -0
  150. package/src/presentation/charts/line-widget.chart-skeleton.tsx +64 -0
  151. package/src/presentation/charts/line-widget.chart.tsx +105 -0
  152. package/src/presentation/charts/pie-widget.chart-skeleton.tsx +56 -0
  153. package/src/presentation/charts/pie-widget.chart.tsx +168 -0
  154. package/src/presentation/charts/radar-widget.chart-skeleton.tsx +67 -0
  155. package/src/presentation/charts/radar-widget.chart.tsx +80 -0
  156. package/src/presentation/charts/radial-stacked-widget.chart-skeleton.tsx +66 -0
  157. package/src/presentation/charts/radial-stacked-widget.chart.tsx +120 -0
  158. package/src/presentation/charts/radial-widget.chart-skeleton.tsx +61 -0
  159. package/src/presentation/charts/radial-widget.chart.tsx +87 -0
  160. package/src/presentation/charts/tooltip-widget.chart-skeleton.tsx +64 -0
  161. package/src/presentation/charts/tooltip-widget.chart.tsx +89 -0
  162. package/src/presentation/components/activity-actions.tsx +20 -0
  163. package/src/presentation/components/activity-container.tsx +55 -0
  164. package/src/presentation/components/activity-content.tsx +20 -0
  165. package/src/presentation/components/activity-descriptor-parser.tsx +37 -0
  166. package/src/presentation/components/activity-header.tsx +20 -0
  167. package/src/presentation/components/activity-loader.tsx +19 -0
  168. package/src/presentation/components/activity-title.tsx +32 -0
  169. package/src/presentation/components/activity.tsx +22 -0
  170. package/src/presentation/components/auth/index.tsx +4 -0
  171. package/src/presentation/components/auth/sso-callback.view.tsx +44 -0
  172. package/src/presentation/components/auth/sso-disconnect.view.tsx +41 -0
  173. package/src/presentation/components/auth/sso-sign-in.view.tsx +29 -0
  174. package/src/presentation/components/auth-lock-screen.tsx +309 -0
  175. package/src/presentation/components/auth-session.view.tsx +17 -0
  176. package/src/presentation/components/data-table.tsx +814 -0
  177. package/src/presentation/components/dynamic-icon.tsx +16 -0
  178. package/src/presentation/components/floating-upload.tsx +291 -0
  179. package/src/presentation/components/index.tsx +40 -0
  180. package/src/presentation/components/local-time.tsx +170 -0
  181. package/src/presentation/components/markdown-parser.tsx +201 -0
  182. package/src/presentation/components/message-scroller.tsx +131 -0
  183. package/src/presentation/components/module-first-access-config.view.tsx +181 -0
  184. package/src/presentation/components/module-first-access-pending.view.tsx +38 -0
  185. package/src/presentation/components/module-guard.tsx +34 -0
  186. package/src/presentation/components/modules-first-access.guard.tsx +88 -0
  187. package/src/presentation/components/modules-guard-forbidden.tsx +46 -0
  188. package/src/presentation/components/notification-details.modal.tsx +145 -0
  189. package/src/presentation/components/notification-item.tsx +127 -0
  190. package/src/presentation/components/notifications-popover.tsx +128 -0
  191. package/src/presentation/components/push-notifications.provider.tsx +65 -0
  192. package/src/presentation/components/upload-progress-dialog.tsx +140 -0
  193. package/src/presentation/components/waiting-bar.tsx +22 -0
  194. package/src/presentation/components/waiting-section.tsx +15 -0
  195. package/src/presentation/components/waiting.tsx +14 -0
  196. package/src/presentation/data-grid/data-grid-empty.tsx +24 -0
  197. package/src/presentation/data-grid/data-grid-search-engine.tsx +32 -0
  198. package/src/presentation/data-grid/data-grid.tsx +892 -0
  199. package/src/presentation/data-grid/index.tsx +4 -0
  200. package/src/presentation/form-screen.tsx +95 -0
  201. package/src/presentation/icons/custom.tsx +20 -0
  202. package/src/presentation/icons/glyphIcons.tsx +53 -0
  203. package/src/presentation/icons/icons.enum.ts +4 -0
  204. package/src/presentation/icons/icons.util.ts +6 -0
  205. package/src/presentation/icons/index.tsx +8 -0
  206. package/src/presentation/icons/lucide.tsx +17 -0
  207. package/src/presentation/icons/remix.tsx +33 -0
  208. package/src/presentation/icons/types.ts +27 -0
  209. package/src/presentation/index.tsx +45 -0
  210. package/src/presentation/modals/components/ModalBox.tsx +74 -0
  211. package/src/presentation/modals/components/ModalPortal.tsx +31 -0
  212. package/src/presentation/modals/components/ModalStepper.tsx +319 -0
  213. package/src/presentation/modals/components/ModalWrapper.tsx +65 -0
  214. package/src/presentation/modals/components/index.tsx +5 -0
  215. package/src/presentation/modals/hooks/index.ts +2 -0
  216. package/src/presentation/modals/hooks/useModal.ts +38 -0
  217. package/src/presentation/modals/index.tsx +5 -0
  218. package/src/presentation/modals/stores/index.ts +2 -0
  219. package/src/presentation/modals/stores/useModalStore.ts +90 -0
  220. package/src/presentation/modals/types/index.ts +2 -0
  221. package/src/presentation/modals/types/modal.type.ts +33 -0
  222. package/src/presentation/module-menubar.tsx +300 -0
  223. package/src/presentation/module-widget.tsx +233 -0
  224. package/src/presentation/nav-documents.tsx +89 -0
  225. package/src/presentation/nav-main.tsx +62 -0
  226. package/src/presentation/nav-secondary.tsx +41 -0
  227. package/src/presentation/nav-user.tsx +109 -0
  228. package/src/presentation/permission-action-badge.tsx +35 -0
  229. package/src/presentation/reui/index.tsx +3 -0
  230. package/src/presentation/reui/stepper.tsx +508 -0
  231. package/src/presentation/reui/timeline.tsx +226 -0
  232. package/src/presentation/sheets/index.tsx +2 -0
  233. package/src/presentation/sheets/legacy-sheet-header.tsx +0 -0
  234. package/src/presentation/sheets/legacy-sheet.tsx +37 -0
  235. package/src/presentation/size.enum.ts +19 -0
  236. package/src/presentation/system/index.tsx +3 -0
  237. package/src/presentation/system/logo.theme.tsx +21 -0
  238. package/src/presentation/system/types.ts +10 -0
  239. package/src/presentation/theme/index.tsx +6 -0
  240. package/src/presentation/theme/prefer-color-scheme.tsx +50 -0
  241. package/src/presentation/theme/switch-color-scheme.tsx +16 -0
  242. package/src/presentation/theme/theme-switcher-button.tsx +20 -0
  243. package/src/presentation/theme/theme.enum.ts +4 -0
  244. package/src/presentation/theme/theme.store.ts +87 -0
  245. package/src/presentation/themes/index.tsx +2 -0
  246. package/src/presentation/themes/katon/dashboard-layout.tsx +1080 -0
  247. package/src/presentation/themes/katon/edge-section.tsx +19 -0
  248. package/src/presentation/themes/katon/header-menubar.tsx +270 -0
  249. package/src/presentation/themes/katon/header-modules-available.tsx +44 -0
  250. package/src/presentation/themes/katon/header-modules-recents.tsx +52 -0
  251. package/src/presentation/themes/katon/header-modules-store.tsx +25 -0
  252. package/src/presentation/themes/katon/header-options.tsx +13 -0
  253. package/src/presentation/themes/katon/header-organization-select.tsx +146 -0
  254. package/src/presentation/themes/katon/header-routines.tsx +74 -0
  255. package/src/presentation/themes/katon/header-tasks-connected-user.tsx +150 -0
  256. package/src/presentation/themes/katon/index.tsx +16 -0
  257. package/src/presentation/themes/katon/main-wrapper.tsx +34 -0
  258. package/src/presentation/themes/katon/module-item-link.tsx +32 -0
  259. package/src/presentation/themes/katon/view-frame.tsx +25 -0
  260. package/src/presentation/themes/katon/view-helmet.tsx +53 -0
  261. package/src/presentation/themes/katon/view-status.tsx +32 -0
  262. package/src/presentation/themes/katon/view-wrapper.tsx +15 -0
  263. package/src/presentation/themes/katon/view.tsx +42 -0
  264. package/src/presentation/ui/accordion-multiselect.tsx +206 -0
  265. package/src/presentation/ui/accordion.tsx +81 -0
  266. package/src/presentation/ui/alert-dialog.tsx +199 -0
  267. package/src/presentation/ui/alert.tsx +76 -0
  268. package/src/presentation/ui/aspect-ratio.tsx +11 -0
  269. package/src/presentation/ui/attachment.tsx +204 -0
  270. package/src/presentation/ui/avatar.tsx +112 -0
  271. package/src/presentation/ui/badge.tsx +49 -0
  272. package/src/presentation/ui/breadcrumb.tsx +122 -0
  273. package/src/presentation/ui/bubble.tsx +125 -0
  274. package/src/presentation/ui/button-group.tsx +83 -0
  275. package/src/presentation/ui/button.tsx +87 -0
  276. package/src/presentation/ui/calendar.tsx +222 -0
  277. package/src/presentation/ui/card.tsx +106 -0
  278. package/src/presentation/ui/carousel.tsx +242 -0
  279. package/src/presentation/ui/chart.tsx +373 -0
  280. package/src/presentation/ui/checkbox.tsx +33 -0
  281. package/src/presentation/ui/clickable.tsx +58 -0
  282. package/src/presentation/ui/collapsible.tsx +33 -0
  283. package/src/presentation/ui/color-swatch-selector.tsx +81 -0
  284. package/src/presentation/ui/combobox.tsx +299 -0
  285. package/src/presentation/ui/command.tsx +195 -0
  286. package/src/presentation/ui/context-menu.tsx +263 -0
  287. package/src/presentation/ui/dialog.tsx +168 -0
  288. package/src/presentation/ui/direction.tsx +22 -0
  289. package/src/presentation/ui/drawer.tsx +134 -0
  290. package/src/presentation/ui/dropdown-menu.tsx +269 -0
  291. package/src/presentation/ui/empty.tsx +104 -0
  292. package/src/presentation/ui/field.tsx +238 -0
  293. package/src/presentation/ui/granularity-selector.tsx +58 -0
  294. package/src/presentation/ui/hover-card.tsx +44 -0
  295. package/src/presentation/ui/index.tsx +74 -0
  296. package/src/presentation/ui/input-group.tsx +156 -0
  297. package/src/presentation/ui/input-otp.tsx +87 -0
  298. package/src/presentation/ui/input.tsx +19 -0
  299. package/src/presentation/ui/item.tsx +196 -0
  300. package/src/presentation/ui/kpi-card.tsx +203 -0
  301. package/src/presentation/ui/label-selector.tsx +100 -0
  302. package/src/presentation/ui/label.tsx +24 -0
  303. package/src/presentation/ui/legacy-birth-date-input.tsx +43 -0
  304. package/src/presentation/ui/legacy-color-picker/index.tsx +42 -0
  305. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.content.tsx +37 -0
  306. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.context.tsx +42 -0
  307. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.hex-input.tsx +97 -0
  308. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.picker.tsx +266 -0
  309. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.presets.ts +22 -0
  310. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.quick-list.tsx +49 -0
  311. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.recent.tsx +43 -0
  312. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.swatches.tsx +111 -0
  313. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.trigger.tsx +128 -0
  314. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.tsx +110 -0
  315. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.types.ts +1 -0
  316. package/src/presentation/ui/legacy-color-picker/legacy-color-picker.utils.ts +114 -0
  317. package/src/presentation/ui/legacy-country-input.tsx +171 -0
  318. package/src/presentation/ui/legacy-gender-input.tsx +22 -0
  319. package/src/presentation/ui/legacy-input.tsx +106 -0
  320. package/src/presentation/ui/legacy-phone-input.tsx +213 -0
  321. package/src/presentation/ui/legacy-select-input.tsx +114 -0
  322. package/src/presentation/ui/legacy-text-area.tsx +111 -0
  323. package/src/presentation/ui/list-items.tsx +59 -0
  324. package/src/presentation/ui/marker.tsx +69 -0
  325. package/src/presentation/ui/media-player/index.tsx +12 -0
  326. package/src/presentation/ui/media-player/media-player.context.tsx +36 -0
  327. package/src/presentation/ui/media-player/media-player.tsx +167 -0
  328. package/src/presentation/ui/media-player/player-controls.tsx +207 -0
  329. package/src/presentation/ui/media-player/players/audio-player.tsx +78 -0
  330. package/src/presentation/ui/media-player/players/index.tsx +4 -0
  331. package/src/presentation/ui/media-player/players/video-player.tsx +193 -0
  332. package/src/presentation/ui/media-player/players/voice-note-player.tsx +108 -0
  333. package/src/presentation/ui/media-player/use-media-player.hook.ts +263 -0
  334. package/src/presentation/ui/menubar.tsx +281 -0
  335. package/src/presentation/ui/message.tsx +92 -0
  336. package/src/presentation/ui/navigation-menu.tsx +172 -0
  337. package/src/presentation/ui/pagination.tsx +129 -0
  338. package/src/presentation/ui/popover.tsx +89 -0
  339. package/src/presentation/ui/progress.tsx +31 -0
  340. package/src/presentation/ui/resizable.tsx +50 -0
  341. package/src/presentation/ui/select.tsx +192 -0
  342. package/src/presentation/ui/separator.tsx +28 -0
  343. package/src/presentation/ui/sheet.tsx +147 -0
  344. package/src/presentation/ui/sidebar.tsx +702 -0
  345. package/src/presentation/ui/skeleton.tsx +13 -0
  346. package/src/presentation/ui/slide-to-confirm.tsx +129 -0
  347. package/src/presentation/ui/slider.tsx +59 -0
  348. package/src/presentation/ui/sonner.tsx +58 -0
  349. package/src/presentation/ui/switch.tsx +33 -0
  350. package/src/presentation/ui/table.tsx +116 -0
  351. package/src/presentation/ui/tabs.tsx +90 -0
  352. package/src/presentation/ui/text-gradient.tsx +84 -0
  353. package/src/presentation/ui/textarea.tsx +18 -0
  354. package/src/presentation/ui/timeline-steps.tsx +235 -0
  355. package/src/presentation/ui/toggle-group.tsx +89 -0
  356. package/src/presentation/ui/toggle.tsx +47 -0
  357. package/src/presentation/ui/tooltip.tsx +57 -0
  358. package/src/presentation/uploading/index.tsx +3 -0
  359. package/src/presentation/uploading/media-upload-field.tsx +152 -0
  360. package/src/presentation/uploading/storage-media.tsx +210 -0
  361. package/src/styles.css +403 -0
  362. package/src/themes.css +1038 -0
@@ -0,0 +1,972 @@
1
+ "use client";
2
+
3
+ import React, { useMemo, useState } from "react";
4
+ import type { JSX } from "react";
5
+ import Link from "next/link";
6
+ import { cn } from "../../infrastructure/utilities/utils";
7
+ import { useIsMobile } from "./hooks/use-mobile";
8
+ import { Button } from "./button";
9
+ import {
10
+ Collapsible,
11
+ CollapsibleContent
12
+ } from "../ui/collapsible";
13
+ import { Separator } from "../ui/separator";
14
+ import { ChevronRight } from "lucide-react";
15
+ import { CopyButton } from "./ui/copy-button";
16
+
17
+ interface JsonViewerProps {
18
+ data: Record<string, any>;
19
+ className?: string;
20
+ truncation?: Partial<TruncationSettings>;
21
+ showLineNumbers?: boolean;
22
+ showColorIndent?: boolean;
23
+ collapseOn?: "click" | "doubleClick";
24
+ defaultExpanded?: boolean | number;
25
+ title?: string;
26
+ }
27
+
28
+ interface TruncationSettings {
29
+ enabled: boolean;
30
+ itemsPerArray: number;
31
+ }
32
+
33
+ type DataType =
34
+ | "string"
35
+ | "number"
36
+ | "boolean"
37
+ | "null"
38
+ | "object"
39
+ | "array"
40
+ | "unknown";
41
+
42
+ const getDataType = (value: any): DataType => {
43
+ if (value === null) return "null";
44
+ if (Array.isArray(value)) return "array";
45
+ const type = typeof value;
46
+ if (
47
+ type === "string" ||
48
+ type === "number" ||
49
+ type === "boolean" ||
50
+ type === "object"
51
+ ) {
52
+ return type;
53
+ }
54
+ return "unknown";
55
+ };
56
+
57
+ const getTypeStyle = (type: DataType): string => {
58
+ switch (type) {
59
+ case "string":
60
+ return "text-green-600 dark:text-green-400";
61
+ case "number":
62
+ return "text-orange-600 dark:text-orange-400";
63
+ case "boolean":
64
+ return "text-blue-600 dark:text-blue-400";
65
+ case "null":
66
+ return "text-muted-foreground";
67
+ default:
68
+ return "";
69
+ }
70
+ };
71
+
72
+ const formatRelativeTime = (date: Date): string => {
73
+ const now = new Date();
74
+ const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);
75
+
76
+ if (Math.abs(diffInSeconds) < 60) return "just now";
77
+
78
+ const intervals = {
79
+ year: 31536000,
80
+ month: 2592000,
81
+ week: 604800,
82
+ day: 86400,
83
+ hour: 3600,
84
+ minute: 60
85
+ };
86
+
87
+ for (const [unit, seconds] of Object.entries(intervals)) {
88
+ const interval = Math.floor(Math.abs(diffInSeconds) / seconds);
89
+ if (interval >= 1) {
90
+ const suffix = diffInSeconds > 0 ? "ago" : "from now";
91
+ return `${interval} ${unit}${interval !== 1 ? "s" : ""} ${suffix}`;
92
+ }
93
+ }
94
+
95
+ return "just now";
96
+ };
97
+
98
+ const detectDate = (value: any): Date | null => {
99
+ if (typeof value === "string") {
100
+ if (/^\d{4}-\d{2}-\d{2}/.test(value)) {
101
+ const date = new Date(value);
102
+ if (!isNaN(date.getTime())) {
103
+ return date;
104
+ }
105
+ }
106
+ } else if (typeof value === "number") {
107
+ if (value >= 946684800 && value <= 4102444800) {
108
+ return new Date(value * 1000);
109
+ }
110
+ if (value >= 946684800000 && value <= 4102444800000) {
111
+ return new Date(value);
112
+ }
113
+ }
114
+ return null;
115
+ };
116
+
117
+ const SmartValue = React.forwardRef<
118
+ any,
119
+ { value: any; type: DataType } & React.HTMLAttributes<HTMLElement>
120
+ >(({ value, type, ...props }, ref) => {
121
+ const [isExpanded, setIsExpanded] = useState(false);
122
+
123
+ if (type === "string") {
124
+ if (
125
+ /^#([0-9A-F]{3}){1,2}$/i.test(value) ||
126
+ /^rgba?\(/.test(value) ||
127
+ /^hsla?\(/.test(value)
128
+ ) {
129
+ return (
130
+ <span
131
+ ref={ref}
132
+ {...props}
133
+ className={cn(
134
+ "inline-flex items-center gap-1.5 whitespace-nowrap",
135
+ props.className
136
+ )}
137
+ >
138
+ <span
139
+ className="w-3 h-3 rounded-[2px] border border-white/20 shrink-0"
140
+ style={{ backgroundColor: value }}
141
+ />
142
+ <span className="text-green-600 dark:text-green-400">{`'${value}'`}</span>
143
+ </span>
144
+ );
145
+ }
146
+
147
+ if (/^https?:\/\//.test(value)) {
148
+ const isLongUrl = String(value).length > 50;
149
+ const isVeryLongUrl = String(value).length > 180;
150
+
151
+ if (isVeryLongUrl) {
152
+ return (
153
+ <span className="inline-flex flex-col items-start gap-1">
154
+ <Link
155
+ ref={ref}
156
+ href={value}
157
+ target="_blank"
158
+ rel="noopener noreferrer"
159
+ {...(props as any)}
160
+ className={cn(
161
+ "text-green-600 dark:text-green-400 hover:underline hover:text-blue-600 dark:hover:text-blue-400 transition-colors whitespace-pre-wrap break-all",
162
+ !isExpanded && "line-clamp-3",
163
+ props.className
164
+ )}
165
+ style={
166
+ !isExpanded
167
+ ? {
168
+ display: "-webkit-box",
169
+ WebkitLineClamp: 3,
170
+ WebkitBoxOrient: "vertical",
171
+ overflow: "hidden"
172
+ }
173
+ : undefined
174
+ }
175
+ onClick={(e) => {
176
+ e.stopPropagation();
177
+ props.onClick?.(e);
178
+ }}
179
+ >
180
+ {`'${value}'`}
181
+ </Link>
182
+ <Button
183
+ variant="link"
184
+ size="sm"
185
+ onClick={(e) => {
186
+ e.stopPropagation();
187
+ setIsExpanded(!isExpanded);
188
+ }}
189
+ className="h-auto p-0 text-xs text-muted-foreground hover:text-foreground underline select-none"
190
+ >
191
+ {isExpanded ? "Show less" : "Show more"}
192
+ </Button>
193
+ </span>
194
+ );
195
+ }
196
+
197
+ return (
198
+ <Link
199
+ ref={ref}
200
+ href={value}
201
+ target="_blank"
202
+ rel="noopener noreferrer"
203
+ {...(props as any)}
204
+ className={cn(
205
+ "text-green-600 dark:text-green-400 hover:underline hover:text-blue-600 dark:hover:text-blue-400 transition-colors",
206
+ isLongUrl ? "whitespace-pre-wrap break-all" : "whitespace-nowrap",
207
+ props.className
208
+ )}
209
+ onClick={(e) => {
210
+ e.stopPropagation();
211
+ props.onClick?.(e);
212
+ }}
213
+ >
214
+ {`'${value}'`}
215
+ </Link>
216
+ );
217
+ }
218
+ }
219
+
220
+ const typeStyle = getTypeStyle(type);
221
+ if (type === "string") {
222
+ const isLongString = String(value).length > 50;
223
+ const isVeryLongString = String(value).length > 180;
224
+
225
+ if (isVeryLongString) {
226
+ return (
227
+ <span className="inline-flex flex-col items-start gap-1">
228
+ <span
229
+ ref={ref}
230
+ {...props}
231
+ className={cn(
232
+ typeStyle,
233
+ "whitespace-pre-wrap wrap-break-words",
234
+ !isExpanded && "line-clamp-3",
235
+ props.className
236
+ )}
237
+ style={
238
+ !isExpanded
239
+ ? {
240
+ display: "-webkit-box",
241
+ WebkitLineClamp: 3,
242
+ WebkitBoxOrient: "vertical",
243
+ overflow: "hidden"
244
+ }
245
+ : undefined
246
+ }
247
+ >
248
+ {`'${value}'`}
249
+ </span>
250
+ <Button
251
+ variant="link"
252
+ size="sm"
253
+ onClick={(e) => {
254
+ e.stopPropagation();
255
+ setIsExpanded(!isExpanded);
256
+ }}
257
+ className="h-auto p-0 text-xs text-muted-foreground hover:text-foreground underline select-none"
258
+ >
259
+ {isExpanded ? "Show less" : "Show more"}
260
+ </Button>
261
+ </span>
262
+ );
263
+ }
264
+
265
+ return (
266
+ <span
267
+ ref={ref}
268
+ {...props}
269
+ className={cn(
270
+ typeStyle,
271
+ isLongString
272
+ ? "whitespace-pre-wrap wrap-break-words"
273
+ : "whitespace-nowrap",
274
+ props.className
275
+ )}
276
+ >
277
+ {`'${value}'`}
278
+ </span>
279
+ );
280
+ }
281
+ if (type === "null")
282
+ return (
283
+ <span
284
+ ref={ref}
285
+ {...props}
286
+ className={cn(typeStyle, "whitespace-nowrap", props.className)}
287
+ >
288
+ null
289
+ </span>
290
+ );
291
+ return (
292
+ <span
293
+ ref={ref}
294
+ {...props}
295
+ className={cn(typeStyle, "whitespace-nowrap", props.className)}
296
+ >
297
+ {String(value)}
298
+ </span>
299
+ );
300
+ });
301
+ SmartValue.displayName = "SmartValue";
302
+
303
+ const calculateLineCount = (
304
+ data: any,
305
+ expandedPaths: Set<string>,
306
+ path = "root",
307
+ level = 0,
308
+ truncation: TruncationSettings
309
+ ): number => {
310
+ const dataType = getDataType(data);
311
+
312
+ if (dataType === "object") {
313
+ const isOpen = expandedPaths.has(path);
314
+ if (!isOpen) return 1;
315
+ const entries = Object.entries(data);
316
+ if (entries.length === 0) return 2;
317
+ return (
318
+ 2 +
319
+ entries.reduce(
320
+ (acc, [key, value]) =>
321
+ acc +
322
+ calculateLineCount(
323
+ value,
324
+ expandedPaths,
325
+ `${path}.${key}`,
326
+ level + 1,
327
+ truncation
328
+ ),
329
+ 0
330
+ )
331
+ );
332
+ }
333
+
334
+ if (dataType === "array") {
335
+ const isOpen = expandedPaths.has(path);
336
+ if (!isOpen) return 1;
337
+ if (data.length === 0) return 2;
338
+
339
+ if (truncation.enabled && data.length > truncation.itemsPerArray) {
340
+ const visibleItems = data.slice(0, truncation.itemsPerArray);
341
+ return (
342
+ 3 +
343
+ visibleItems.reduce(
344
+ (acc: number, item: any, index: number) =>
345
+ acc +
346
+ calculateLineCount(
347
+ item,
348
+ expandedPaths,
349
+ `${path}[${index}]`,
350
+ level + 1,
351
+ truncation
352
+ ),
353
+ 0
354
+ )
355
+ );
356
+ }
357
+
358
+ return (
359
+ 2 +
360
+ data.reduce(
361
+ (acc: number, item: any, index: number) =>
362
+ acc +
363
+ calculateLineCount(
364
+ item,
365
+ expandedPaths,
366
+ `${path}[${index}]`,
367
+ level + 1,
368
+ truncation
369
+ ),
370
+ 0
371
+ )
372
+ );
373
+ }
374
+
375
+ return 1;
376
+ };
377
+
378
+ const generateAllPaths = (
379
+ data: any,
380
+ maxLevel: number = Infinity,
381
+ currentLevel: number = 0,
382
+ currentPath: string = "root"
383
+ ): Set<string> => {
384
+ const paths = new Set<string>();
385
+ if (currentLevel > maxLevel) return paths;
386
+
387
+ if (typeof data === "object" && data !== null) {
388
+ paths.add(currentPath);
389
+ if (Array.isArray(data)) {
390
+ data.forEach((item, index) => {
391
+ const childPaths = generateAllPaths(
392
+ item,
393
+ maxLevel,
394
+ currentLevel + 1,
395
+ `${currentPath}[${index}]`
396
+ );
397
+ childPaths.forEach((path) => paths.add(path));
398
+ });
399
+ } else {
400
+ Object.entries(data).forEach(([key, value]) => {
401
+ const childPaths = generateAllPaths(
402
+ value,
403
+ maxLevel,
404
+ currentLevel + 1,
405
+ `${currentPath}.${key}`
406
+ );
407
+ childPaths.forEach((path) => paths.add(path));
408
+ });
409
+ }
410
+ }
411
+ return paths;
412
+ };
413
+
414
+ const JsonViewer: React.FC<JsonViewerProps> = ({
415
+ data,
416
+ className,
417
+ truncation: truncationProp,
418
+ showLineNumbers = true,
419
+ showColorIndent = false,
420
+ collapseOn = "click",
421
+ defaultExpanded = false,
422
+ title
423
+ }) => {
424
+ const isMobile = useIsMobile();
425
+
426
+ const [expandedPaths, setExpandedPaths] = React.useState<Set<string>>(() => {
427
+ if (typeof defaultExpanded === "number") {
428
+ return generateAllPaths(data, defaultExpanded);
429
+ }
430
+ if (defaultExpanded === true) {
431
+ return generateAllPaths(data);
432
+ }
433
+ const initialPaths = new Set<string>();
434
+ if (typeof data === "object" && data !== null) {
435
+ initialPaths.add("root");
436
+ }
437
+ return initialPaths;
438
+ });
439
+
440
+ const expandAll = () => {
441
+ setExpandedPaths(generateAllPaths(data));
442
+ };
443
+
444
+ const collapseAll = () => {
445
+ setExpandedPaths(new Set(["root"]));
446
+ };
447
+
448
+ const truncation: TruncationSettings = React.useMemo(
449
+ () => ({
450
+ enabled: isMobile ? false : (truncationProp?.enabled ?? true),
451
+ itemsPerArray: truncationProp?.itemsPerArray ?? 5
452
+ }),
453
+ [truncationProp, isMobile]
454
+ );
455
+
456
+ const toggleNode = (path: string) => {
457
+ setExpandedPaths((prev) => {
458
+ const newPaths = new Set(prev);
459
+ if (newPaths.has(path)) {
460
+ newPaths.delete(path);
461
+ } else {
462
+ newPaths.add(path);
463
+ }
464
+ return newPaths;
465
+ });
466
+ };
467
+
468
+ const lineCount = useMemo(
469
+ () => calculateLineCount(data, expandedPaths, "root", 0, truncation),
470
+ [data, expandedPaths, truncation]
471
+ );
472
+
473
+ return (
474
+ <div
475
+ className={cn(
476
+ "relative font-mono text-[13px] leading-6 w-full text-foreground bg-secondary/10 dark:bg-muted/50 rounded-md border border-border flex flex-col",
477
+ className
478
+ )}
479
+ >
480
+ <div className="flex justify-between items-center p-2 z-10 gap-2">
481
+ <div className="text-xs font-medium text-muted-foreground px-2">
482
+ {title}
483
+ </div>
484
+ <div className="flex items-center rounded-md border bg-muted/50 overflow-hidden">
485
+ <Button
486
+ variant="ghost"
487
+ size="sm"
488
+ onClick={expandAll}
489
+ className="h-7 px-2 text-xs hover:bg-muted rounded-none"
490
+ title="Expand All"
491
+ >
492
+ Expand All
493
+ </Button>
494
+ <Separator orientation="vertical" className="h-4" />
495
+ <Button
496
+ variant="ghost"
497
+ size="sm"
498
+ onClick={collapseAll}
499
+ className="h-7 px-2 text-xs hover:bg-muted rounded-none"
500
+ title="Collapse All"
501
+ >
502
+ Collapse All
503
+ </Button>
504
+ <Separator orientation="vertical" className="h-4" />
505
+ <CopyButton
506
+ value={JSON.stringify(data, null, 2)}
507
+ className="static size-7 bg-transparent hover:bg-muted hover:opacity-100 focus-visible:opacity-100 text-foreground rounded-none"
508
+ />
509
+ </div>
510
+ </div>
511
+ <div className="w-full overflow-auto flex-1 p-4 pt-0">
512
+ <pre className="flex">
513
+ {showLineNumbers && (
514
+ <div className="hidden sm:block">
515
+ <LineNumbers lineCount={lineCount} />
516
+ </div>
517
+ )}
518
+ <code>
519
+ <JsonNode
520
+ data={data}
521
+ path="root"
522
+ expandedPaths={expandedPaths}
523
+ toggleNode={toggleNode}
524
+ truncation={truncation}
525
+ showColorIndent={showColorIndent}
526
+ collapseOn={collapseOn}
527
+ />
528
+ </code>
529
+ </pre>
530
+ </div>
531
+ </div>
532
+ );
533
+ };
534
+
535
+ const LineNumbers: React.FC<{ lineCount: number }> = ({ lineCount }) => {
536
+ return (
537
+ <div className="flex flex-col text-right pr-4 text-muted-foreground select-none border-r border-border mr-4">
538
+ {Array.from({ length: lineCount }, (_, i) => (
539
+ <div key={i} className="h-6 leading-6 text-xs tabular-nums opacity-50">
540
+ {i + 1}
541
+ </div>
542
+ ))}
543
+ </div>
544
+ );
545
+ };
546
+
547
+ interface JsonNodeProps {
548
+ data: any;
549
+ level?: number;
550
+ path: string;
551
+ expandedPaths: Set<string>;
552
+ toggleNode: (path: string) => void;
553
+ showComma?: boolean;
554
+ objectKey?: string;
555
+ truncation: TruncationSettings;
556
+ showColorIndent?: boolean;
557
+ collapseOn?: "click" | "doubleClick";
558
+ }
559
+
560
+ const JsonNode: React.FC<JsonNodeProps> = ({
561
+ data,
562
+ level = 0,
563
+ path,
564
+ expandedPaths,
565
+ toggleNode,
566
+ showComma,
567
+ objectKey,
568
+ truncation,
569
+ showColorIndent,
570
+ collapseOn
571
+ }) => {
572
+ const dataType = getDataType(data);
573
+
574
+ const renderValue = () => {
575
+ let element: JSX.Element | null = null;
576
+ switch (dataType) {
577
+ case "array":
578
+ element = (
579
+ <JsonArray
580
+ data={data}
581
+ level={level}
582
+ path={path}
583
+ expandedPaths={expandedPaths}
584
+ toggleNode={toggleNode}
585
+ showComma={showComma}
586
+ objectKey={objectKey}
587
+ truncation={truncation}
588
+ showColorIndent={showColorIndent}
589
+ collapseOn={collapseOn}
590
+ />
591
+ );
592
+ break;
593
+ case "object":
594
+ element = (
595
+ <JsonObject
596
+ data={data}
597
+ level={level}
598
+ path={path}
599
+ expandedPaths={expandedPaths}
600
+ toggleNode={toggleNode}
601
+ showComma={showComma}
602
+ objectKey={objectKey}
603
+ truncation={truncation}
604
+ showColorIndent={showColorIndent}
605
+ collapseOn={collapseOn}
606
+ />
607
+ );
608
+ break;
609
+ default:
610
+ element = <SmartValue value={data} type={dataType} />;
611
+ break;
612
+ }
613
+
614
+ if (dataType === "object" || dataType === "array") {
615
+ return element;
616
+ }
617
+
618
+ const date = detectDate(data);
619
+ if (date) {
620
+ const timeStr = formatRelativeTime(date);
621
+ return (
622
+ <span className="inline-flex items-center gap-2">
623
+ {element}
624
+ <span className="text-xs text-muted-foreground/60 select-none italic">
625
+ {`// ${timeStr}`}
626
+ </span>
627
+ </span>
628
+ );
629
+ }
630
+
631
+ return element;
632
+ };
633
+
634
+ return (
635
+ <>
636
+ {renderValue()}
637
+ {dataType !== "object" && dataType !== "array" && showComma && (
638
+ <span className="text-muted-foreground">,</span>
639
+ )}
640
+ </>
641
+ );
642
+ };
643
+
644
+ const indentColors = [
645
+ "border-red-300/60 dark:border-red-700/60",
646
+ "border-yellow-300/60 dark:border-yellow-700/60",
647
+ "border-green-300/60 dark:border-green-700/60",
648
+ "border-blue-300/60 dark:border-blue-700/60",
649
+ "border-purple-300/60 dark:border-purple-700/60"
650
+ ];
651
+
652
+ const JsonObject: React.FC<{
653
+ objectKey?: string;
654
+ data: Record<string, any>;
655
+ level: number;
656
+ path: string;
657
+ expandedPaths: Set<string>;
658
+ toggleNode: (path: string) => void;
659
+ showComma?: boolean;
660
+ truncation: TruncationSettings;
661
+ showColorIndent?: boolean;
662
+ collapseOn?: "click" | "doubleClick";
663
+ }> = ({
664
+ data,
665
+ level,
666
+ path,
667
+ expandedPaths,
668
+ toggleNode,
669
+ showComma,
670
+ objectKey,
671
+ truncation,
672
+ showColorIndent,
673
+ collapseOn
674
+ }) => {
675
+ const entries = Object.entries(data);
676
+ const isOpen = expandedPaths.has(path);
677
+
678
+ const trigger = (
679
+ <div
680
+ className={cn(
681
+ "inline-flex items-center text-left h-6 leading-6 group rounded-sm px-1 -ml-1 w-full cursor-pointer select-none",
682
+ isOpen && "hover:bg-muted-foreground/20"
683
+ )}
684
+ onDoubleClick={
685
+ collapseOn === "doubleClick" ? () => toggleNode(path) : undefined
686
+ }
687
+ onClick={
688
+ collapseOn === "doubleClick"
689
+ ? undefined
690
+ : (e) => {
691
+ toggleNode(path);
692
+ }
693
+ }
694
+ >
695
+ {objectKey && (
696
+ <span className="text-purple-600 dark:text-purple-400 inline-flex items-center group font-medium">
697
+ {`'${objectKey}'`}
698
+ <span className="text-muted-foreground mx-1">: </span>
699
+ </span>
700
+ )}
701
+ <Button
702
+ variant="ghost"
703
+ size="icon"
704
+ onClick={(e) => {
705
+ e.stopPropagation();
706
+ toggleNode(path);
707
+ }}
708
+ className="h-4 w-4 p-0 text-muted-foreground hover:text-foreground hover:bg-transparent"
709
+ >
710
+ <ChevronRight
711
+ className={cn(
712
+ "h-4 w-4 transition-transform shrink-0",
713
+ isOpen && "rotate-90"
714
+ )}
715
+ />
716
+ </Button>
717
+ <span className="text-muted-foreground">{"{"}</span>
718
+ {!isOpen && (
719
+ <>
720
+ <span className="text-muted-foreground">...</span>
721
+ <span className="text-muted-foreground">
722
+ {"}"} ({entries.length} {entries.length > 1 ? "items" : "item"})
723
+ </span>
724
+ {showComma && <span className="text-muted-foreground">,</span>}
725
+ </>
726
+ )}
727
+ </div>
728
+ );
729
+
730
+ return (
731
+ <Collapsible open={isOpen} onOpenChange={() => toggleNode(path)} asChild>
732
+ <div>
733
+ {trigger}
734
+ <CollapsibleContent className="transition-all duration-200">
735
+ <div
736
+ className={cn(
737
+ "pl-5 border-l",
738
+ showColorIndent
739
+ ? indentColors[level % indentColors.length]
740
+ : "border-[rgba(0,0,0,0.1)] dark:border-[rgba(255,255,255,0.1)]"
741
+ )}
742
+ >
743
+ {entries.map(([key, value], index) => {
744
+ const childPath = `${path}.${key}`;
745
+ const dataType = getDataType(value);
746
+ const isChildCollapsible =
747
+ dataType === "object" || dataType === "array";
748
+ const isChildOpen =
749
+ isChildCollapsible && expandedPaths.has(childPath);
750
+
751
+ return (
752
+ <div
753
+ key={key}
754
+ className={cn(
755
+ "group rounded-md",
756
+ !isChildCollapsible && "flex items-start min-h-6",
757
+ isChildOpen ? "" : "hover:bg-muted-foreground/20"
758
+ )}
759
+ >
760
+ {isChildCollapsible ? (
761
+ <JsonNode
762
+ data={value}
763
+ level={level + 1}
764
+ path={childPath}
765
+ expandedPaths={expandedPaths}
766
+ toggleNode={toggleNode}
767
+ showComma={index < entries.length - 1}
768
+ objectKey={key}
769
+ truncation={truncation}
770
+ showColorIndent={showColorIndent}
771
+ collapseOn={collapseOn}
772
+ />
773
+ ) : (
774
+ <>
775
+ <span className="text-purple-600 dark:text-purple-400 inline-flex items-center">
776
+ {`'${key}'`}
777
+ </span>
778
+ <span className="text-muted-foreground">: </span>
779
+ <JsonNode
780
+ data={value}
781
+ level={level + 1}
782
+ path={childPath}
783
+ expandedPaths={expandedPaths}
784
+ toggleNode={toggleNode}
785
+ showComma={index < entries.length - 1}
786
+ truncation={truncation}
787
+ showColorIndent={showColorIndent}
788
+ collapseOn={collapseOn}
789
+ />
790
+ </>
791
+ )}
792
+ </div>
793
+ );
794
+ })}
795
+ </div>
796
+ <div>
797
+ <span className="text-muted-foreground">{"}"}</span>
798
+ {showComma && <span className="text-muted-foreground">,</span>}
799
+ </div>
800
+ </CollapsibleContent>
801
+ </div>
802
+ </Collapsible>
803
+ );
804
+ };
805
+
806
+ const JsonArray: React.FC<{
807
+ objectKey?: string;
808
+ data: any[];
809
+ level: number;
810
+ path: string;
811
+ expandedPaths: Set<string>;
812
+ toggleNode: (path: string) => void;
813
+ showComma?: boolean;
814
+ truncation: TruncationSettings;
815
+ showColorIndent?: boolean;
816
+ collapseOn?: "click" | "doubleClick";
817
+ }> = ({
818
+ data,
819
+ level,
820
+ path,
821
+ expandedPaths,
822
+ toggleNode,
823
+ showComma,
824
+ objectKey,
825
+ truncation,
826
+ showColorIndent,
827
+ collapseOn
828
+ }) => {
829
+ const isOpen = expandedPaths.has(path);
830
+ const [showAll, setShowAll] = useState(false);
831
+
832
+ const itemsToShow =
833
+ truncation.enabled && !showAll && data.length > truncation.itemsPerArray
834
+ ? data.slice(0, truncation.itemsPerArray)
835
+ : data;
836
+
837
+ const handleShowMore = () => {
838
+ setShowAll(true);
839
+ };
840
+
841
+ const trigger = (
842
+ <div
843
+ className={cn(
844
+ "inline-flex items-center text-left h-6 leading-6 group rounded-sm px-1 -ml-1 w-full cursor-pointer select-none",
845
+ isOpen && "hover:bg-muted-foreground/20"
846
+ )}
847
+ onDoubleClick={
848
+ collapseOn === "doubleClick" ? () => toggleNode(path) : undefined
849
+ }
850
+ onClick={
851
+ collapseOn === "doubleClick"
852
+ ? undefined
853
+ : (e) => {
854
+ toggleNode(path);
855
+ }
856
+ }
857
+ >
858
+ {objectKey && (
859
+ <span className="text-purple-600 dark:text-purple-400 inline-flex items-center group">
860
+ {`'${objectKey}'`}
861
+ <span className="text-muted-foreground mx-1">: </span>
862
+ </span>
863
+ )}
864
+ <Button
865
+ variant="ghost"
866
+ size="icon"
867
+ onClick={(e) => {
868
+ e.stopPropagation();
869
+ toggleNode(path);
870
+ }}
871
+ className="h-4 w-4 p-0 text-muted-foreground hover:text-foreground hover:bg-transparent"
872
+ >
873
+ <ChevronRight
874
+ className={cn(
875
+ "h-4 w-4 transition-transform shrink-0",
876
+ isOpen && "rotate-90"
877
+ )}
878
+ />
879
+ </Button>
880
+ <span className="text-muted-foreground">{"["}</span>
881
+ {!isOpen && (
882
+ <>
883
+ <span className="text-muted-foreground">...</span>
884
+ <span className="text-muted-foreground">
885
+ {"]"} ({data.length} items)
886
+ </span>
887
+ {showComma && <span className="text-muted-foreground">,</span>}
888
+ </>
889
+ )}
890
+ </div>
891
+ );
892
+
893
+ return (
894
+ <Collapsible open={isOpen} onOpenChange={() => toggleNode(path)} asChild>
895
+ <div>
896
+ {trigger}
897
+ <CollapsibleContent className="transition-all duration-200">
898
+ <div
899
+ className={cn(
900
+ "pl-5 border-l",
901
+ showColorIndent
902
+ ? indentColors[level % indentColors.length]
903
+ : "border-[rgba(0,0,0,0.1)] dark:border-[rgba(255,255,255,0.1)]"
904
+ )}
905
+ >
906
+ {itemsToShow.map((item, index) => {
907
+ const childPath = `${path}[${index}]`;
908
+ const dataType = getDataType(item);
909
+ const isChildCollapsible =
910
+ dataType === "object" || dataType === "array";
911
+ const isChildOpen =
912
+ isChildCollapsible && expandedPaths.has(childPath);
913
+
914
+ return (
915
+ <div
916
+ key={index}
917
+ className={cn(
918
+ "group rounded-md",
919
+ !isChildCollapsible &&
920
+ "flex sm:items-center items-start sm:h-6 h-auto",
921
+ isChildOpen ? "" : "hover:bg-muted-foreground/20"
922
+ )}
923
+ >
924
+ <JsonNode
925
+ data={item}
926
+ level={level + 1}
927
+ path={childPath}
928
+ expandedPaths={expandedPaths}
929
+ toggleNode={toggleNode}
930
+ showComma={index < data.length - 1}
931
+ truncation={truncation}
932
+ showColorIndent={showColorIndent}
933
+ collapseOn={collapseOn}
934
+ />
935
+ </div>
936
+ );
937
+ })}
938
+ {truncation.enabled && data.length > truncation.itemsPerArray && (
939
+ <div className="pl-5">
940
+ {!showAll ? (
941
+ <Button
942
+ variant="secondary"
943
+ size="sm"
944
+ onClick={handleShowMore}
945
+ className="h-auto px-2 py-0.5 text-xs bg-secondary/30 hover:bg-secondary/50 text-muted-foreground hover:text-foreground mt-1"
946
+ >
947
+ Show {data.length - truncation.itemsPerArray} more items...
948
+ </Button>
949
+ ) : (
950
+ <Button
951
+ variant="secondary"
952
+ size="sm"
953
+ onClick={() => setShowAll(false)}
954
+ className="h-auto px-2 py-0.5 text-xs bg-secondary/30 hover:bg-secondary/50 text-muted-foreground hover:text-foreground mt-1"
955
+ >
956
+ Show Less
957
+ </Button>
958
+ )}
959
+ </div>
960
+ )}
961
+ </div>
962
+ <div>
963
+ <span className="text-muted-foreground">]</span>
964
+ {showComma && <span className="text-muted-foreground">,</span>}
965
+ </div>
966
+ </CollapsibleContent>
967
+ </div>
968
+ </Collapsible>
969
+ );
970
+ };
971
+
972
+ export default JsonViewer;