@riducms/admin 0.2.2 → 0.3.0

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 (352) hide show
  1. package/THIRD_PARTY_NOTICES.md +60 -37
  2. package/package.json +27 -15
  3. package/src/app/admin-core-view-route.svelte +38 -53
  4. package/src/app/admin-extension-route.svelte +19 -0
  5. package/src/app/admin-layout.scss +175 -0
  6. package/src/app/admin-layout.svelte +144 -410
  7. package/src/app/admin-navigation-progress.svelte +11 -77
  8. package/src/app/admin-page-title.svelte +17 -85
  9. package/src/app/admin-page-title.ts +136 -0
  10. package/src/app/admin-route-error.svelte +24 -0
  11. package/src/app/admin-router-provider.svelte +34 -0
  12. package/src/app/admin-router-root.svelte +41 -0
  13. package/src/app/admin-routes.svelte +56 -19
  14. package/src/app/loading-boundary.svelte +3 -1
  15. package/src/app-meta.ts +13 -0
  16. package/src/app.css +138 -496
  17. package/src/app.svelte +60 -26
  18. package/src/components/brand/ridu-logo.svelte +19 -5
  19. package/src/components/data/list-table.scss +63 -0
  20. package/src/components/icons/LICENSE.md +24 -0
  21. package/src/components/icons/chevron.svelte +8 -0
  22. package/src/components/icons/plus.svelte +12 -0
  23. package/src/components/icons/search.svelte +12 -0
  24. package/src/components/icons/x.svelte +8 -0
  25. package/src/components/json-tree/json-tree-node.svelte +61 -67
  26. package/src/components/json-tree/json-viewer.scss +153 -0
  27. package/src/components/json-tree/json-viewer.svelte +57 -96
  28. package/src/components/ui/banner/banner.scss +28 -0
  29. package/src/components/ui/banner/banner.svelte +9 -8
  30. package/src/components/ui/banner/index.ts +1 -5
  31. package/src/components/ui/code-editor/code-editor.scss +20 -0
  32. package/src/components/ui/code-editor/code-editor.svelte +71 -0
  33. package/src/components/ui/code-editor/code-editor.ts +151 -0
  34. package/src/components/ui/code-editor/code-languages.ts +29 -0
  35. package/src/components/ui/collapsible-panel/collapsible-panel.scss +38 -0
  36. package/src/components/ui/collapsible-panel/collapsible-panel.svelte +13 -0
  37. package/src/components/ui/collapsible-panel/index.ts +1 -0
  38. package/src/components/ui/combobox/combobox.scss +163 -0
  39. package/src/components/ui/combobox/index.ts +22 -0
  40. package/src/components/ui/date-value-control/date-value-calendar.svelte +128 -16
  41. package/src/components/ui/date-value-control/date-value-control-loader.scss +26 -0
  42. package/src/components/ui/date-value-control/date-value-control-loader.svelte +35 -13
  43. package/src/components/ui/date-value-control/date-value-control.scss +223 -0
  44. package/src/components/ui/date-value-control/date-value-control.svelte +62 -44
  45. package/src/components/ui/drawer/drawer-depth.ts +8 -0
  46. package/src/components/ui/dropdown-menu/dropdown-menu-content.svelte +2 -5
  47. package/src/components/ui/dropdown-menu/dropdown-menu-item.svelte +2 -5
  48. package/src/components/ui/dropdown-menu/dropdown-menu-separator.svelte +2 -2
  49. package/src/components/ui/dropdown-menu/dropdown-menu.scss +62 -0
  50. package/src/components/ui/kbd/kbd.scss +17 -0
  51. package/src/components/ui/kbd/kbd.svelte +3 -10
  52. package/src/components/ui/navigation-drawer/navigation-drawer.scss +40 -0
  53. package/src/components/ui/navigation-drawer/navigation-drawer.svelte +38 -0
  54. package/src/components/ui/popover/popover-content.svelte +3 -5
  55. package/src/components/ui/popover/popover.scss +18 -0
  56. package/src/components/ui/radio-group/index.ts +0 -1
  57. package/src/components/ui/radio-group/radio-card-item.svelte +7 -12
  58. package/src/components/ui/radio-group/radio-group.svelte +2 -2
  59. package/src/components/ui/radio-group/radio.scss +45 -0
  60. package/src/components/ui/select/select-content.svelte +5 -6
  61. package/src/components/ui/select/select-item.svelte +8 -8
  62. package/src/components/ui/select/select-scroll-down-button.svelte +3 -5
  63. package/src/components/ui/select/select-scroll-up-button.svelte +3 -5
  64. package/src/components/ui/select/select-trigger.svelte +6 -7
  65. package/src/components/ui/select/select.scss +126 -0
  66. package/src/components/ui/sheet/sheet-content.svelte +6 -34
  67. package/src/components/ui/sheet/sheet-description.svelte +2 -2
  68. package/src/components/ui/sheet/sheet-overlay.svelte +2 -21
  69. package/src/components/ui/sheet/sheet-title.svelte +2 -2
  70. package/src/components/ui/sheet/sheet.scss +221 -0
  71. package/src/components/ui/skeleton/skeleton.scss +20 -0
  72. package/src/components/ui/skeleton/skeleton.svelte +4 -16
  73. package/src/components/ui/sonner/sonner.scss +121 -0
  74. package/src/components/ui/sonner/sonner.svelte +11 -10
  75. package/src/components/ui/status-indicator/status-indicator.scss +48 -0
  76. package/src/components/ui/status-indicator/status-indicator.svelte +14 -14
  77. package/src/components/ui/tabs/tabs-content.svelte +2 -2
  78. package/src/components/ui/tabs/tabs-list.svelte +7 -7
  79. package/src/components/ui/tabs/tabs-trigger.svelte +2 -8
  80. package/src/components/ui/tabs/tabs.scss +86 -0
  81. package/src/components/ui/tabs/tabs.svelte +2 -2
  82. package/src/components/ui/timezone-picker/timezone-picker.svelte +152 -0
  83. package/src/core/api/admin-client.ts +2 -2
  84. package/src/core/bootstrap/admin-bootstrap.ts +396 -0
  85. package/src/core/bootstrap/admin-prepared-state.ts +213 -0
  86. package/src/core/bootstrap/admin-route-modules.ts +66 -0
  87. package/src/core/clipboard/copy-feedback.svelte.ts +43 -0
  88. package/src/core/forms/embedded-fields.ts +4 -8
  89. package/src/core/forms/field-editor-authoring.ts +8 -0
  90. package/src/core/forms/field-issue-focus.ts +4 -4
  91. package/src/core/forms/field-occurrence.ts +4 -7
  92. package/src/core/forms/form-controller.svelte.ts +14 -21
  93. package/src/core/forms/form-draft-recovery.ts +17 -4
  94. package/src/core/forms/form-issue-correlation.ts +13 -13
  95. package/src/core/forms/form-path.ts +17 -0
  96. package/src/core/forms/form-schema.ts +12 -43
  97. package/src/core/forms/form-validation.ts +7 -46
  98. package/src/core/forms/plugin-field-authoring.ts +6 -2
  99. package/src/core/forms/plugin-field-write.ts +2 -6
  100. package/src/core/i18n/admin-i18n.svelte.ts +18 -0
  101. package/src/core/i18n/format-admin-date-time.ts +19 -0
  102. package/src/core/i18n/humanize-identifier.ts +5 -0
  103. package/src/core/i18n/time-zone-label.ts +20 -0
  104. package/src/core/loaders/admin-view-host.svelte +72 -0
  105. package/src/core/loaders/admin-view-loader.svelte.ts +90 -0
  106. package/src/core/notifications/validation-toast.svelte +2 -2
  107. package/src/core/plugins/field-registry.ts +7 -8
  108. package/src/core/routing/admin-paths.ts +53 -2
  109. package/src/core/routing/admin-scroll.svelte.ts +74 -0
  110. package/src/core/routing/admin-view-selection.ts +78 -0
  111. package/src/core/runtime/admin-runtime.svelte.ts +121 -124
  112. package/src/core/schema/field-default.ts +33 -0
  113. package/src/core/schema/field-localization.ts +15 -0
  114. package/src/core/schema/read-document-path.ts +11 -0
  115. package/src/features/account/account-menu.scss +37 -0
  116. package/src/features/account/account-menu.svelte +115 -0
  117. package/src/features/{auth → account}/account-navigation.svelte +16 -19
  118. package/src/features/{auth → account}/account-route.svelte +206 -49
  119. package/src/features/account/account-security-controller.svelte.ts +157 -0
  120. package/src/features/{auth → account}/account-security-route.svelte +140 -42
  121. package/src/features/api-reference/api-reference-code.svelte +64 -0
  122. package/src/features/api-reference/api-reference-content.svelte +258 -0
  123. package/src/features/api-reference/api-reference-drawer.scss +74 -0
  124. package/src/features/api-reference/api-reference-examples.ts +381 -0
  125. package/src/features/api-reference/api-reference-schema.ts +332 -0
  126. package/src/features/api-reference/api-reference.scss +190 -0
  127. package/src/features/api-reference/api-reference.svelte +74 -0
  128. package/src/features/auth/auth-brand.svelte +5 -3
  129. package/src/features/auth/auth-field.svelte +92 -0
  130. package/src/features/auth/auth-frame.svelte +5 -8
  131. package/src/features/auth/auth.scss +122 -0
  132. package/src/features/auth/create-first-user-controller.svelte.ts +20 -51
  133. package/src/features/auth/create-first-user-route.svelte +83 -122
  134. package/src/features/auth/forgot-password-route.svelte +31 -23
  135. package/src/features/auth/login-route.svelte +68 -116
  136. package/src/features/auth/logout-button.scss +34 -0
  137. package/src/features/auth/logout-button.svelte +52 -0
  138. package/src/features/auth/request-verification-route.svelte +27 -22
  139. package/src/features/auth/reset-password-route.svelte +108 -41
  140. package/src/features/auth/verify-email-route.svelte +49 -15
  141. package/src/features/bulk-edit/bulk-edit-fields.ts +23 -0
  142. package/src/features/bulk-edit/bulk-edit.scss +152 -0
  143. package/src/features/bulk-edit/field-picker.svelte +176 -0
  144. package/src/features/collections/bulk/bulk-actions.svelte +148 -0
  145. package/src/features/collections/bulk/bulk-confirmation.svelte +153 -0
  146. package/src/features/collections/bulk/bulk-editor-loader.svelte +95 -0
  147. package/src/features/collections/bulk/bulk-editor.svelte +186 -0
  148. package/src/features/collections/collection-list-cell-values.ts +144 -0
  149. package/src/features/collections/collection-list-controller.svelte.ts +268 -379
  150. package/src/features/collections/collection-list-preferences-controller.svelte.ts +54 -40
  151. package/src/features/collections/collection-list-query.ts +223 -0
  152. package/src/features/collections/collection-list-results.svelte +205 -329
  153. package/src/features/collections/collection-list-route.svelte +143 -884
  154. package/src/features/collections/collection-list.scss +261 -0
  155. package/src/features/collections/collection-list.svelte.ts +362 -0
  156. package/src/features/collections/controls/column-picker.svelte +110 -0
  157. package/src/features/collections/controls/column-pill.svelte +62 -0
  158. package/src/features/collections/controls/filter-builder.svelte +193 -0
  159. package/src/features/collections/controls/filter-row.svelte +142 -0
  160. package/src/features/collections/controls/list-controls.scss +196 -0
  161. package/src/features/collections/controls/list-options.svelte +203 -0
  162. package/src/features/collections/controls/list-pagination.scss +78 -0
  163. package/src/features/collections/controls/list-pagination.svelte +115 -0
  164. package/src/features/collections/controls/list-toolbar.svelte +132 -0
  165. package/src/features/collections/controls/sort-heading.scss +52 -0
  166. package/src/features/collections/controls/sort-heading.svelte +78 -0
  167. package/src/features/collections/list-columns.ts +43 -0
  168. package/src/features/collections/list-workspace.ts +155 -54
  169. package/src/features/dashboard/dashboard-route.svelte +82 -86
  170. package/src/features/dashboard/dashboard.scss +131 -0
  171. package/src/features/development/development-tools.svelte +20 -36
  172. package/src/features/documents/document-action-bar.svelte +438 -0
  173. package/src/features/documents/document-api-view-loader.svelte +17 -6
  174. package/src/features/documents/document-api-view.scss +120 -0
  175. package/src/features/documents/document-api-view.svelte +169 -454
  176. package/src/features/documents/document-controller.svelte.ts +526 -613
  177. package/src/features/documents/document-credentials.scss +37 -0
  178. package/src/features/documents/document-credentials.svelte +72 -0
  179. package/src/features/documents/{document-route-dirty.ts → document-credentials.ts} +25 -0
  180. package/src/features/documents/document-field-sections.scss +39 -0
  181. package/src/features/documents/document-field-sections.svelte +4 -13
  182. package/src/features/documents/document-loading.scss +24 -0
  183. package/src/features/documents/document-lock-controller.svelte.ts +187 -0
  184. package/src/features/documents/document-operation-dialogs.svelte +110 -0
  185. package/src/features/documents/document-route-loader.svelte +17 -5
  186. package/src/features/documents/document-route.svelte +305 -811
  187. package/src/features/documents/document-schedule.scss +127 -0
  188. package/src/features/documents/document-schedule.svelte +323 -0
  189. package/src/features/documents/document-title.ts +25 -0
  190. package/src/features/documents/document.scss +274 -0
  191. package/src/features/documents/live-preview-panel.scss +117 -0
  192. package/src/features/documents/live-preview-panel.svelte +167 -168
  193. package/src/features/documents/live-preview.ts +2 -10
  194. package/src/features/documents/upload-document-preview-loader.svelte +26 -23
  195. package/src/features/localization/content-locale-switcher.scss +43 -0
  196. package/src/features/localization/content-locale-switcher.svelte +24 -17
  197. package/src/features/navigation/admin-breadcrumbs.scss +56 -0
  198. package/src/features/navigation/admin-breadcrumbs.svelte +102 -37
  199. package/src/features/navigation/admin-command-menu.svelte +38 -15
  200. package/src/features/navigation/admin-navigation.scss +133 -0
  201. package/src/features/navigation/admin-navigation.svelte +169 -0
  202. package/src/features/navigation/breadcrumb-context.svelte.ts +15 -0
  203. package/src/features/navigation/navigation-preference.svelte.ts +70 -0
  204. package/src/features/reference-browser/reference-browser-editor.svelte +180 -0
  205. package/src/features/reference-browser/reference-browser-list.scss +113 -0
  206. package/src/features/reference-browser/reference-browser-list.svelte +370 -0
  207. package/src/features/reference-browser/reference-browser-loader.svelte +26 -8
  208. package/src/features/reference-browser/reference-browser-workflow.svelte.ts +335 -236
  209. package/src/features/reference-browser/reference-browser.scss +217 -0
  210. package/src/features/reference-browser/reference-browser.svelte +154 -797
  211. package/src/features/reference-browser/reference-collection-picker.svelte +116 -0
  212. package/src/features/reference-browser/reference-list-query.ts +35 -0
  213. package/src/features/uploads/bulk-upload-controller.svelte.ts +604 -0
  214. package/src/features/uploads/bulk-upload-edit-all.svelte +92 -0
  215. package/src/features/uploads/bulk-upload-editor.svelte +191 -0
  216. package/src/features/uploads/bulk-upload-file-picker.svelte +70 -0
  217. package/src/features/uploads/bulk-upload-remote.svelte +100 -0
  218. package/src/features/uploads/bulk-upload-route-loader.svelte +54 -0
  219. package/src/features/uploads/bulk-upload-route.svelte +172 -592
  220. package/src/features/uploads/bulk-upload-sidebar.svelte +125 -0
  221. package/src/features/uploads/bulk-upload.scss +526 -0
  222. package/src/features/uploads/bulk-upload.ts +105 -14
  223. package/src/features/uploads/crop-surface.ts +150 -0
  224. package/src/features/uploads/image-editor.svelte +231 -0
  225. package/src/features/uploads/rendition-preview.svelte +64 -0
  226. package/src/features/uploads/upload-control.svelte +340 -0
  227. package/src/features/uploads/upload-document-contracts.ts +30 -0
  228. package/src/features/uploads/upload-draft.svelte.ts +294 -0
  229. package/src/features/uploads/upload-metadata-fields.scss +32 -0
  230. package/src/features/uploads/upload-metadata-fields.svelte +8 -7
  231. package/src/features/uploads/upload-thumbnail.svelte +47 -0
  232. package/src/features/uploads/upload.scss +361 -0
  233. package/src/features/versions/version-comparison.svelte +361 -0
  234. package/src/features/versions/version-diff-fields.svelte +136 -0
  235. package/src/features/versions/version-diff.ts +296 -41
  236. package/src/features/versions/version-history-controller.svelte.ts +311 -0
  237. package/src/features/versions/version-history-route-loader.svelte +32 -0
  238. package/src/features/versions/version-history-route.svelte +157 -522
  239. package/src/features/versions/version-history.scss +444 -0
  240. package/src/features/versions/version-history.ts +45 -0
  241. package/src/features/versions/version-table.svelte +91 -0
  242. package/src/fields/embedded-schema-draft-editor.scss +48 -0
  243. package/src/fields/embedded-schema-draft-editor.svelte +24 -16
  244. package/src/fields/field-collapsible.svelte +14 -8
  245. package/src/fields/field-layout.scss +171 -0
  246. package/src/fields/field-layout.svelte +3 -5
  247. package/src/fields/field-renderer.svelte +17 -29
  248. package/src/fields/field-shell.svelte +1 -1
  249. package/src/fields/field-tab-layout.svelte +25 -19
  250. package/src/fields/join/join-field-controller.svelte.ts +513 -0
  251. package/src/fields/join/join-field-values.ts +111 -0
  252. package/src/fields/join/join-field.scss +189 -0
  253. package/src/fields/join/join-field.svelte +164 -332
  254. package/src/fields/live-validation-feedback.svelte +4 -3
  255. package/src/fields/local-field-editor.svelte +2 -2
  256. package/src/fields/nested/block-name-input.svelte +11 -12
  257. package/src/fields/nested/block-picker.scss +128 -0
  258. package/src/fields/nested/block-picker.svelte +81 -0
  259. package/src/fields/nested/block-placeholder.svg +2 -0
  260. package/src/fields/nested/nested-field.scss +331 -0
  261. package/src/fields/nested/nested-field.svelte +265 -245
  262. package/src/fields/nested/sortable-row.svelte +9 -7
  263. package/src/fields/plugin-field.svelte +4 -6
  264. package/src/fields/point/point-field.svelte +51 -38
  265. package/src/fields/primitive-list/primitive-list-field.svelte +63 -19
  266. package/src/fields/primitive-list/primitive-list.scss +109 -0
  267. package/src/fields/radio/radio-field.svelte +2 -1
  268. package/src/fields/relationship/relationship-field-controller.svelte.ts +81 -41
  269. package/src/fields/relationship/relationship-field.svelte +144 -175
  270. package/src/fields/relationship/relationship-lookup-controller.svelte.ts +58 -30
  271. package/src/fields/relationship/relationship-query.ts +2 -10
  272. package/src/fields/relationship/relationship-quick-picker-controller.svelte.ts +36 -62
  273. package/src/fields/relationship/relationship-quick-picker.svelte +178 -227
  274. package/src/fields/relationship/relationship-selection.svelte +120 -0
  275. package/src/fields/relationship/relationship-value.ts +46 -7
  276. package/src/fields/relationship/relationship.scss +182 -0
  277. package/src/fields/scalar/code-field.svelte +101 -0
  278. package/src/fields/scalar/date-control-value.ts +13 -0
  279. package/src/fields/scalar/scalar-field.svelte +25 -67
  280. package/src/fields/select/select-field.svelte +132 -94
  281. package/src/fields/text/slug-field.scss +28 -0
  282. package/src/fields/text/slug-field.svelte +19 -15
  283. package/src/fields/text/text-field.svelte +1 -2
  284. package/src/fields/ui/ui-field.svelte +7 -4
  285. package/src/fields/virtual/virtual-field.svelte +2 -5
  286. package/src/index.ts +1 -2
  287. package/src/routing.ts +2 -0
  288. package/src/app/deferred-redirect.svelte +0 -23
  289. package/src/components/code-block/code-block.svelte +0 -107
  290. package/src/components/ui/bulk-action-bar/bulk-action-bar.svelte +0 -50
  291. package/src/components/ui/bulk-action-bar/index.ts +0 -1
  292. package/src/components/ui/button/index.ts +0 -8
  293. package/src/components/ui/calendar/calendar-caption.svelte +0 -72
  294. package/src/components/ui/calendar/calendar-cell.svelte +0 -20
  295. package/src/components/ui/calendar/calendar-day.svelte +0 -27
  296. package/src/components/ui/calendar/calendar-grid-row.svelte +0 -13
  297. package/src/components/ui/calendar/calendar-grid.svelte +0 -17
  298. package/src/components/ui/calendar/calendar-head-cell.svelte +0 -20
  299. package/src/components/ui/calendar/calendar-header.svelte +0 -20
  300. package/src/components/ui/calendar/calendar-heading.svelte +0 -17
  301. package/src/components/ui/calendar/calendar-month-select.svelte +0 -45
  302. package/src/components/ui/calendar/calendar-month.svelte +0 -16
  303. package/src/components/ui/calendar/calendar-months.svelte +0 -20
  304. package/src/components/ui/calendar/calendar-nav.svelte +0 -20
  305. package/src/components/ui/calendar/calendar-next-button.svelte +0 -30
  306. package/src/components/ui/calendar/calendar-prev-button.svelte +0 -30
  307. package/src/components/ui/calendar/calendar-year-select.svelte +0 -44
  308. package/src/components/ui/calendar/calendar.svelte +0 -118
  309. package/src/components/ui/calendar/index.ts +0 -1
  310. package/src/components/ui/checkbox/checkbox.svelte +0 -35
  311. package/src/components/ui/checkbox/index.ts +0 -1
  312. package/src/components/ui/dialog/dialog-close.svelte +0 -11
  313. package/src/components/ui/dialog/dialog-content.svelte +0 -52
  314. package/src/components/ui/dialog/dialog-description.svelte +0 -20
  315. package/src/components/ui/dialog/dialog-footer.svelte +0 -20
  316. package/src/components/ui/dialog/dialog-header.svelte +0 -20
  317. package/src/components/ui/dialog/dialog-overlay.svelte +0 -20
  318. package/src/components/ui/dialog/dialog-title.svelte +0 -17
  319. package/src/components/ui/dialog/dialog-trigger.svelte +0 -11
  320. package/src/components/ui/dialog/dialog.svelte +0 -7
  321. package/src/components/ui/dialog/index.ts +0 -8
  322. package/src/components/ui/empty-state/empty-state.svelte +0 -34
  323. package/src/components/ui/empty-state/index.ts +0 -1
  324. package/src/components/ui/input/index.ts +0 -1
  325. package/src/components/ui/pagination/index.ts +0 -1
  326. package/src/components/ui/pagination/pagination.svelte +0 -88
  327. package/src/components/ui/radio-group/radio-group-item.svelte +0 -17
  328. package/src/components/ui/slider/index.ts +0 -3
  329. package/src/components/ui/slider/slider-content.svelte +0 -59
  330. package/src/components/ui/slider/slider.svelte +0 -35
  331. package/src/components/ui/table/index.ts +0 -6
  332. package/src/components/ui/table/table-body.svelte +0 -20
  333. package/src/components/ui/table/table-cell.svelte +0 -23
  334. package/src/components/ui/table/table-head.svelte +0 -23
  335. package/src/components/ui/table/table-header.svelte +0 -20
  336. package/src/components/ui/table/table-row.svelte +0 -23
  337. package/src/components/ui/table/table.svelte +0 -22
  338. package/src/components/ui/textarea/index.ts +0 -1
  339. package/src/components/ui/tooltip/index.ts +0 -4
  340. package/src/components/ui/tooltip/tooltip-content.svelte +0 -50
  341. package/src/components/ui/tooltip/tooltip-provider.svelte +0 -7
  342. package/src/components/ui/tooltip/tooltip-trigger.svelte +0 -7
  343. package/src/components/ui/tooltip/tooltip.svelte +0 -7
  344. package/src/features/auth/account-menu.svelte +0 -159
  345. package/src/features/auth/account-security-controller.svelte.ts +0 -119
  346. package/src/features/collections/collection-list-bulk-actions.svelte +0 -143
  347. package/src/features/collections/collection-list-bulk-editor.svelte +0 -157
  348. package/src/features/collections/collection-list-filter-popover.svelte +0 -238
  349. package/src/features/collections/collection-list-workspace-toolbar.svelte +0 -409
  350. package/src/features/collections/collection-table-skeleton.svelte +0 -44
  351. package/src/features/documents/document-heading.ts +0 -20
  352. package/src/features/documents/upload-document-preview.svelte +0 -525
@@ -1,60 +1,67 @@
1
1
  <script lang="ts">
2
- import { useNavigate } from "@hvniel/svelte-router";
3
2
  import KeyIcon from "~icons/lucide/key-round";
4
3
  import LaptopIcon from "~icons/lucide/laptop";
5
4
  import LockIcon from "~icons/lucide/lock-keyhole";
6
5
  import ShieldIcon from "~icons/lucide/shield-check";
7
6
 
8
7
  import { Banner } from "@admin/components/ui/banner";
9
- import { Button } from "@admin/components/ui/button";
10
- import { DateValueControl } from "@admin/components/ui/date-value-control";
11
- import { Input } from "@admin/components/ui/input";
12
8
  import {
9
+ Button,
10
+ Input,
13
11
  Dialog,
14
12
  DialogContent,
15
13
  DialogDescription,
16
14
  DialogFooter,
17
15
  DialogHeader,
18
16
  DialogTitle,
19
- } from "@admin/components/ui/dialog";
17
+ } from "@riducms/ui";
18
+ import { DateValueControl } from "@admin/components/ui/date-value-control";
19
+
20
20
  import { adminRoutePatterns } from "@admin/core/routing/admin-paths";
21
21
  import { getNotificationCenter } from "@admin/core/notifications/notification-center.svelte";
22
22
  import { getAdminRuntime } from "@admin/core/runtime/admin-runtime.svelte";
23
- import { AccountSecurityController } from "@admin/features/auth/account-security-controller.svelte";
24
- import AccountNavigation from "@admin/features/auth/account-navigation.svelte";
23
+ import { AccountSecurityController } from "@admin/features/account/account-security-controller.svelte";
24
+ import AccountNavigation from "@admin/features/account/account-navigation.svelte";
25
+ import { getAdminBootstrapCoordinator } from "@admin/core/bootstrap/admin-bootstrap";
26
+ import { useLocation } from "@hvniel/svelte-router";
25
27
 
26
28
  const runtime = getAdminRuntime();
29
+ const bootstrap = getAdminBootstrapCoordinator();
30
+ const location = useLocation();
27
31
  const notifications = getNotificationCenter();
28
- const navigate = useNavigate();
29
- // The authenticated route table is remounted when the session changes, so this
30
- // manifest capability is an intentional controller-lifetime snapshot.
32
+
33
+ // The manifest capability is stable for this mounted route. Session identity is
34
+ // synchronized separately because valid session replacements retain the route.
31
35
  const controller = new AccountSecurityController(
32
36
  runtime.client,
33
37
  runtime.authCollection?.authSettings?.apiKeys === true,
34
38
  runtime.i18n.t
35
39
  );
40
+
41
+ let hostRequest: AbortController | undefined;
42
+
43
+ const sessionID = $derived(runtime.session?.id);
36
44
  const { status, error, operation, sessions, apiKeys, createdAPIKey, apiKeysEnabled } =
37
45
  $derived(controller);
46
+
38
47
  let currentPassword = $state("");
39
48
  let nextPassword = $state("");
40
49
  let confirmPassword = $state("");
50
+
41
51
  let apiKeyName = $state("");
42
52
  let apiKeyExpiry = $state("");
53
+
43
54
  let destructiveAction = $state<{ kind: "session" | "api-key" | "logout-all"; id?: string }>();
44
- const accountComponents = $derived(
45
- runtime.accountComponents.filter((component) => component.surface === "security")
46
- );
47
- const replacement = $derived(
48
- accountComponents.find((component) => component.position === "replace")
49
- );
50
- const beforeComponents = $derived(
51
- accountComponents.filter((component) => component.position === "before")
55
+
56
+ const accountComponents = runtime.config.extensions.account.filter(
57
+ (component) => component.surface === "security"
52
58
  );
53
- const afterComponents = $derived(
54
- accountComponents.filter(
55
- (component) => component.position === undefined || component.position === "after"
56
- )
59
+ const replacement = accountComponents.find((component) => component.position === "replace");
60
+ const beforeComponents = accountComponents.filter((component) => component.position === "before");
61
+ const afterComponents = accountComponents.filter(
62
+ (component) => component.position === undefined || component.position === "after"
57
63
  );
64
+
58
65
  const host = {
59
66
  refreshUser,
60
67
  logout,
@@ -62,9 +69,20 @@
62
69
  notifications[tone]({ title, message }),
63
70
  };
64
71
 
65
- $effect(() => {
66
- controller.load();
67
- return () => controller.destroy();
72
+ let activeSessionID: string | undefined;
73
+
74
+ $effect.pre(() => {
75
+ const owner = sessionID;
76
+ if (owner === undefined || owner === activeSessionID) return;
77
+
78
+ activeSessionID = owner;
79
+ const route = bootstrap.routeData(location.current.pathname, location.current.search);
80
+ controller.load(route?.kind === "security" ? route.security : undefined);
81
+ });
82
+
83
+ $effect(() => () => {
84
+ hostRequest?.abort();
85
+ controller.destroy();
68
86
  });
69
87
 
70
88
  async function changePassword(event: SubmitEvent) {
@@ -73,64 +91,138 @@
73
91
  notifications.error({ title: runtime.i18n.t("account:passwordMismatch") });
74
92
  return;
75
93
  }
76
- if (!(await controller.changePassword(currentPassword, nextPassword))) return;
77
- runtime.session = undefined;
94
+
95
+ const owner = runtime.session?.id;
96
+ if (
97
+ owner === undefined ||
98
+ !(await controller.changePassword(currentPassword, nextPassword)) ||
99
+ runtime.session?.id !== owner
100
+ )
101
+ return;
102
+
78
103
  notifications.success({ title: runtime.i18n.t("account:passwordUpdated") });
79
- navigate(adminRoutePatterns.login, { replace: true });
104
+ await runtime.hardNavigate(adminRoutePatterns.login);
80
105
  }
81
106
 
82
107
  async function createAPIKey(event: SubmitEvent) {
83
108
  event.preventDefault();
109
+ const owner = runtime.session?.id;
110
+ if (owner === undefined) return;
111
+
84
112
  const expiry = apiKeyExpiry === "" ? undefined : new Date(apiKeyExpiry).toISOString();
85
- if (!(await controller.createAPIKey(apiKeyName, expiry))) return;
113
+ if (!(await controller.createAPIKey(apiKeyName, expiry)) || runtime.session?.id !== owner)
114
+ return;
115
+
86
116
  apiKeyName = "";
87
117
  apiKeyExpiry = "";
88
118
  notifications.success({ title: runtime.i18n.t("account:apiKeyCreated") });
89
119
  }
90
120
 
91
121
  async function revokeSession(id: string) {
92
- if (!(await controller.revokeSession(id))) return;
122
+ const owner = runtime.session?.id;
123
+ if (
124
+ owner === undefined ||
125
+ !(await controller.revokeSession(id)) ||
126
+ runtime.session?.id !== owner
127
+ )
128
+ return;
129
+
93
130
  notifications.success({ title: runtime.i18n.t("account:sessionRevoked") });
94
131
  }
95
132
 
96
133
  async function revokeAPIKey(id: string) {
97
- if (!(await controller.revokeAPIKey(id))) return;
134
+ const owner = runtime.session?.id;
135
+ if (
136
+ owner === undefined ||
137
+ !(await controller.revokeAPIKey(id)) ||
138
+ runtime.session?.id !== owner
139
+ )
140
+ return;
141
+
98
142
  notifications.success({ title: runtime.i18n.t("account:apiKeyRevoked") });
99
143
  }
100
144
 
101
145
  async function logoutAll() {
102
- if (!(await controller.logoutAll())) return;
103
- runtime.session = undefined;
146
+ const owner = runtime.session?.id;
147
+ if (owner === undefined || !(await controller.logoutAll()) || runtime.session?.id !== owner)
148
+ return;
149
+
104
150
  notifications.success({ title: runtime.i18n.t("account:signedOutAll") });
105
- navigate(adminRoutePatterns.login, { replace: true });
151
+ await runtime.hardNavigate(adminRoutePatterns.login);
106
152
  }
107
153
 
108
154
  async function refreshUser() {
109
155
  const collection = runtime.authCollection;
110
156
  const user = runtime.session?.user;
111
157
  if (collection === undefined || user === undefined) return undefined;
112
- const document = await runtime.client.find(collection.slug, user.id);
113
- if (runtime.session !== undefined) runtime.session = { ...runtime.session, user: document };
114
- return document;
158
+
159
+ const owner = runtime.session?.id;
160
+ if (owner === undefined) return undefined;
161
+
162
+ const request = new AbortController();
163
+ hostRequest?.abort();
164
+ hostRequest = request;
165
+
166
+ try {
167
+ const document = await runtime.client.find(collection.slug, user.id, {
168
+ signal: request.signal,
169
+ });
170
+ if (request.signal.aborted || runtime.session?.id !== owner) return undefined;
171
+
172
+ const activeSession = runtime.session;
173
+ if (activeSession === undefined) return undefined;
174
+
175
+ runtime.session = { ...activeSession, user: document };
176
+
177
+ return document;
178
+ } catch (cause) {
179
+ if (request.signal.aborted || runtime.session?.id !== owner) return undefined;
180
+
181
+ throw cause;
182
+ } finally {
183
+ if (hostRequest === request) hostRequest = undefined;
184
+ }
115
185
  }
116
186
 
117
187
  async function logout() {
118
- await runtime.client.logout();
119
- runtime.session = undefined;
120
- navigate(adminRoutePatterns.login, { replace: true });
188
+ const user = runtime.session?.user;
189
+ const owner = runtime.session?.id;
190
+ if (user === undefined || owner === undefined) return;
191
+
192
+ const request = new AbortController();
193
+ hostRequest?.abort();
194
+ hostRequest = request;
195
+
196
+ try {
197
+ await runtime.client.logout({ signal: request.signal });
198
+ if (request.signal.aborted || runtime.session?.id !== owner) return;
199
+
200
+ await runtime.hardNavigate(adminRoutePatterns.login);
201
+ } catch (cause) {
202
+ if (request.signal.aborted || runtime.session?.id !== owner) return;
203
+
204
+ throw cause;
205
+ } finally {
206
+ if (hostRequest === request) hostRequest = undefined;
207
+ }
121
208
  }
122
209
 
123
210
  async function confirmDestructiveAction() {
124
211
  const action = destructiveAction;
125
212
  if (action === undefined) return;
213
+
126
214
  if (action.kind === "session" && action.id !== undefined) await revokeSession(action.id);
215
+
127
216
  if (action.kind === "api-key" && action.id !== undefined) await revokeAPIKey(action.id);
217
+
128
218
  if (action.kind === "logout-all") await logoutAll();
219
+
129
220
  destructiveAction = undefined;
130
221
  }
131
222
 
132
223
  async function copyAPIKey() {
133
224
  if (createdAPIKey === undefined) return;
225
+
134
226
  try {
135
227
  await navigator.clipboard.writeText(createdAPIKey.key);
136
228
  notifications.success({ title: runtime.i18n.t("account:apiKeyCopied") });
@@ -141,6 +233,7 @@
141
233
 
142
234
  function formatDate(value: string | undefined) {
143
235
  if (value === undefined || value === "") return runtime.i18n.t("account:never");
236
+
144
237
  return runtime.i18n.formatDate(value, { dateStyle: "medium", timeStyle: "short" });
145
238
  }
146
239
  </script>
@@ -264,7 +357,12 @@
264
357
  )}
265
358
 
266
359
  {#if sessionMessage !== undefined}
267
- <p class="px-5 py-6 text-[13px] text-foreground-muted">{sessionMessage}</p>
360
+ <p
361
+ class="px-5 py-6 text-[13px] text-foreground-muted"
362
+ data-ridu-loading-surface={status === "loading" ? "security" : undefined}
363
+ >
364
+ {sessionMessage}
365
+ </p>
268
366
  {:else}
269
367
  {#each sessions as session (session.id)}
270
368
  <div
@@ -426,7 +524,7 @@
426
524
  }
427
525
  }
428
526
  >
429
- <DialogContent>
527
+ <DialogContent closeLabel={runtime.i18n.t("general:close")}>
430
528
  <DialogHeader>
431
529
  <p class="font-mono text-[10px] tracking-[0.13em] text-destructive uppercase">
432
530
  {runtime.i18n.t("account:securityAction")}
@@ -0,0 +1,64 @@
1
+ <script lang="ts" module>
2
+ import { createHighlighterCoreSync } from "shiki/core";
3
+ import { createJavaScriptRegexEngine } from "shiki/engine/javascript";
4
+ import typescript from "shiki/langs/typescript.mjs";
5
+ import shell from "shiki/langs/shellscript.mjs";
6
+ import go from "shiki/langs/go.mjs";
7
+ import { riduCodeTheme } from "@riducms/ui/code-theme";
8
+ import "@fontsource-variable/martian-mono";
9
+
10
+ // This module belongs to the drawer's lazy chunk. Reuse one tokenizer across examples.
11
+ const highlighter = createHighlighterCoreSync({
12
+ engine: createJavaScriptRegexEngine(),
13
+ themes: [riduCodeTheme],
14
+ langs: [typescript, shell, go],
15
+ });
16
+ </script>
17
+
18
+ <script lang="ts">
19
+ import type { ReferenceLanguage } from "@admin/features/api-reference/api-reference-examples";
20
+
21
+ let { code, language }: { code: string; language: ReferenceLanguage } = $props();
22
+
23
+ const highlighted = $derived(
24
+ highlighter.codeToTokens(code, {
25
+ lang: language === "curl" ? "shellscript" : language,
26
+ theme: riduCodeTheme.name,
27
+ })
28
+ );
29
+ </script>
30
+
31
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex (Scrollable examples need a keyboard scroll target.) -->
32
+ <pre
33
+ class="ridu-api-reference__code"
34
+ dir="ltr"
35
+ tabindex="0"
36
+ style:--code-block-foreground={highlighted.fg}><code>{#each highlighted.tokens as line, index}{#if index > 0}{"\n"}{/if}{#each line as token}<span
37
+ style:color={token.color}
38
+ style:font-style={token.fontStyle && token.fontStyle & 1 ? "italic" : undefined}
39
+ style:font-weight={token.fontStyle && token.fontStyle & 2
40
+ ? "700"
41
+ : undefined}>{token.content}</span>{/each}{/each}</code></pre>
42
+
43
+ <style lang="scss">
44
+ @layer ridu.components {
45
+ .ridu-api-reference__code {
46
+ margin: 0;
47
+ padding: 18px 20px;
48
+ max-height: 430px;
49
+ overflow: auto;
50
+ color: var(--code-block-foreground);
51
+ border: 1px solid var(--border);
52
+ border-radius: var(--radius-sm);
53
+ font-family: "Martian Mono Variable", "Martian Mono", ui-monospace, monospace;
54
+ font-size: 14px;
55
+ font-variant-ligatures: none;
56
+ line-height: 1.56;
57
+ tab-size: 2;
58
+
59
+ code {
60
+ font: inherit;
61
+ }
62
+ }
63
+ }
64
+ </style>
@@ -0,0 +1,258 @@
1
+ <script lang="ts">
2
+ import type { SchemaCollection, SchemaManifest } from "@riducms/protocol";
3
+ import { getAdminI18n } from "@riducms/plugin";
4
+ import { Button } from "@riducms/ui";
5
+ import CopyIcon from "~icons/lucide/copy";
6
+ import CheckIcon from "~icons/lucide/check";
7
+
8
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from "@admin/components/ui/tabs";
9
+ import APIReferenceCode from "@admin/features/api-reference/api-reference-code.svelte";
10
+ import JsonViewer from "@admin/components/json-tree/json-viewer.svelte";
11
+ import { CopyFeedback } from "@admin/core/clipboard/copy-feedback.svelte";
12
+ import { collectionReferenceSchema } from "@admin/features/api-reference/api-reference-schema";
13
+ import {
14
+ referenceCode,
15
+ referenceOperations,
16
+ referenceParameters,
17
+ referenceRequest,
18
+ referenceResponses,
19
+ type ReferenceLanguage,
20
+ type ReferenceOperation,
21
+ } from "@admin/features/api-reference/api-reference-examples";
22
+ import "@admin/features/api-reference/api-reference.scss";
23
+
24
+ let {
25
+ collection,
26
+ manifest,
27
+ documentID,
28
+ }: { collection: SchemaCollection; manifest: SchemaManifest; documentID?: string } = $props();
29
+
30
+ const i18n = getAdminI18n();
31
+ const languages: { value: ReferenceLanguage; label: string }[] = [
32
+ { value: "typescript", label: "TypeScript SDK" },
33
+ { value: "curl", label: "cURL" },
34
+ { value: "go", label: "Go local API" },
35
+ ];
36
+
37
+ let operation = $state<ReferenceOperation>("list");
38
+ let language = $state<ReferenceLanguage>("typescript");
39
+ let response = $state("success");
40
+ // svelte-ignore non_reactive_update
41
+ let body: HTMLDivElement;
42
+ const copyFeedback = new CopyFeedback(2_000);
43
+
44
+ const schema = $derived(collectionReferenceSchema(collection, manifest, i18n));
45
+ const context = $derived({
46
+ collection,
47
+ manifest,
48
+ schema,
49
+ documentID,
50
+ origin: window.location.origin,
51
+ });
52
+ const request = $derived(referenceRequest(context, operation));
53
+ const code = $derived(referenceCode(context, operation, language));
54
+ const responses = $derived(referenceResponses(context, operation));
55
+ const parameters = $derived(referenceParameters(context, operation, i18n));
56
+ const mutation = $derived(operation === "create" || operation === "update");
57
+ const fields = $derived(schema.fields.filter((field) => !mutation || field.writable));
58
+ const currentCopy = $derived(
59
+ copyFeedback.result?.source === code ? copyFeedback.result : undefined
60
+ );
61
+ const selectedResponse = $derived(
62
+ response === "success"
63
+ ? responses[0]
64
+ : (responses.find((item) => String(item.status) === response) ?? responses[0])
65
+ );
66
+
67
+ function selectOperation(next: ReferenceOperation) {
68
+ operation = next;
69
+ response = "success";
70
+ body?.scrollTo({ top: 0 });
71
+ }
72
+ </script>
73
+
74
+ <div class="ridu-api-reference">
75
+ <nav class="ridu-api-reference__operations" aria-label={i18n.t("apiReference:operations")}>
76
+ {#each referenceOperations as item (item)}
77
+ <button
78
+ type="button"
79
+ aria-current={operation === item ? "page" : undefined}
80
+ onclick={() => selectOperation(item)}
81
+ >
82
+ {i18n.t(`apiReference:${item}`)}
83
+ </button>
84
+ {/each}
85
+ </nav>
86
+
87
+ <div class="ridu-api-reference__body" bind:this={body}>
88
+ <section class="ridu-api-reference__section">
89
+ <h2>
90
+ {i18n.t(`apiReference:${operation}`)}
91
+ <span>({collection.slug})</span>
92
+ </h2>
93
+ <div class="ridu-api-reference__endpoint" dir="ltr">
94
+ <strong>{request.method}</strong>
95
+ <code>{request.url}</code>
96
+ </div>
97
+ <p>{i18n.t("apiReference:guidance")}</p>
98
+ {#if operation === "delete"}
99
+ <p>
100
+ {i18n.t(
101
+ collection.capabilities.trash ? "apiReference:trashNote" : "apiReference:deleteNote"
102
+ )}
103
+ </p>
104
+ {/if}
105
+ {#if operation === "update"}
106
+ <p>{i18n.t("apiReference:updateNote")}</p>
107
+ {/if}
108
+ {#if mutation && collection.capabilities.upload}
109
+ <p>
110
+ {i18n.t("apiReference:uploadNote")}
111
+ </p>
112
+ {/if}
113
+ {#if operation === "create" && collection.authSettings}
114
+ <p>
115
+ {i18n.t("apiReference:authNote", { value: collection.authSettings.passwordMinLength })}
116
+ </p>
117
+ {/if}
118
+ </section>
119
+
120
+ <section class="ridu-api-reference__section" aria-label={i18n.t("apiReference:examples")}>
121
+ <Tabs bind:value={language}>
122
+ <div class="ridu-api-reference__code-toolbar">
123
+ <TabsList variant="line" aria-label={i18n.t("apiReference:languages")}>
124
+ {#each languages as item}
125
+ <TabsTrigger value={item.value}>
126
+ {item.label}
127
+ </TabsTrigger>
128
+ {/each}
129
+ </TabsList>
130
+ <Button
131
+ variant="ghost"
132
+ size="icon-sm"
133
+ onclick={() => copyFeedback.copy(code)}
134
+ aria-label={i18n.t("apiReference:copy")}
135
+ >
136
+ {#if currentCopy?.copied}
137
+ <CheckIcon />
138
+ {:else}
139
+ <CopyIcon />
140
+ {/if}
141
+ </Button>
142
+ </div>
143
+ <TabsContent value={language}>
144
+ <APIReferenceCode {code} {language} />
145
+ </TabsContent>
146
+ </Tabs>
147
+ <span class="ridu-api-reference__copy-status" role="status">
148
+ {currentCopy
149
+ ? i18n.t(currentCopy.copied ? "general:copied" : "general:copyUnavailable")
150
+ : ""}
151
+ </span>
152
+ {#if language !== "curl"}
153
+ <p>
154
+ {i18n.t(language === "go" ? "apiReference:goOptions" : "apiReference:credentials")}
155
+ </p>
156
+ {/if}
157
+ {#if mutation && schema.omitted.length}
158
+ <p class="ridu-api-reference__notice">
159
+ {i18n.t("apiReference:omitted", { fields: schema.omitted.join(", ") })}
160
+ </p>
161
+ {/if}
162
+ </section>
163
+
164
+ {#if parameters.length}
165
+ <section class="ridu-api-reference__section">
166
+ <h3>{i18n.t("apiReference:query")}</h3>
167
+ <dl class="ridu-api-reference__parameters">
168
+ {#each parameters as parameter (parameter.name)}
169
+ <div>
170
+ <dt><code>{parameter.name}</code></dt>
171
+ <dd>{parameter.description}</dd>
172
+ </div>
173
+ {/each}
174
+ </dl>
175
+ </section>
176
+ {/if}
177
+
178
+ {#if operation !== "delete"}
179
+ <section class="ridu-api-reference__section">
180
+ <h3>{i18n.t("apiReference:fields")}</h3>
181
+ <p>{i18n.t("apiReference:metadata")}</p>
182
+ {#if fields.length}
183
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex (Wide schema tables must be keyboard-scrollable.) -->
184
+ <div
185
+ class="ridu-api-reference__table-scroll"
186
+ tabindex="0"
187
+ role="region"
188
+ aria-label={i18n.t("apiReference:fields")}
189
+ >
190
+ <table>
191
+ <thead>
192
+ <tr>
193
+ <th>{i18n.t("apiReference:field")}</th>
194
+ <th>{i18n.t("apiReference:type")}</th>
195
+ <th>{i18n.t("apiReference:details")}</th>
196
+ </tr>
197
+ </thead>
198
+ <tbody>
199
+ {#each fields as field (field.path)}
200
+ <tr>
201
+ <td><code>{field.path}</code></td>
202
+ <td><code>{mutation ? field.type : field.outputType}</code></td>
203
+ <td>
204
+ <div class="ridu-api-reference__badges">
205
+ {#if field.requiredOnCreate && field.writable}
206
+ <span>
207
+ {i18n.t("apiReference:required")}
208
+ </span>
209
+ {/if}
210
+ {#if !field.writable}
211
+ <span>{i18n.t("apiReference:readOnly")}</span>
212
+ {/if}
213
+ {#if field.localized}
214
+ <span>{i18n.t("apiReference:localized")}</span>
215
+ {/if}
216
+ </div>
217
+ {#if field.description}
218
+ <p>{field.description}</p>
219
+ {/if}
220
+ {#each field.constraints as constraint}
221
+ <p>{constraint}</p>
222
+ {/each}
223
+ {#if field.jsonSchema !== undefined}
224
+ <details>
225
+ <summary>{i18n.t("apiReference:jsonSchema")}</summary>
226
+ <pre dir="ltr">{JSON.stringify(field.jsonSchema, null, 2)}</pre>
227
+ </details>
228
+ {/if}
229
+ </td>
230
+ </tr>
231
+ {/each}
232
+ </tbody>
233
+ </table>
234
+ </div>
235
+ {:else}
236
+ <p>{i18n.t("apiReference:noFields")}</p>
237
+ {/if}
238
+ </section>
239
+ {/if}
240
+
241
+ <section class="ridu-api-reference__section">
242
+ <h3>{i18n.t("apiReference:responses")}</h3>
243
+ <p>{i18n.t("apiReference:responseNote")}</p>
244
+ <Tabs bind:value={response}>
245
+ <TabsList variant="line" aria-label={i18n.t("apiReference:responses")}>
246
+ {#each responses as item, index}
247
+ <TabsTrigger value={index === 0 ? "success" : String(item.status)}>
248
+ {item.status}
249
+ </TabsTrigger>
250
+ {/each}
251
+ </TabsList>
252
+ <TabsContent value={response}>
253
+ <JsonViewer value={selectedResponse?.value} />
254
+ </TabsContent>
255
+ </Tabs>
256
+ </section>
257
+ </div>
258
+ </div>
@@ -0,0 +1,74 @@
1
+ @layer ridu.components {
2
+ .ridu-api-reference-overlay {
3
+ position: fixed;
4
+ inset: 0;
5
+ z-index: 50;
6
+ background: var(--media-overlay);
7
+ }
8
+
9
+ .ridu-api-reference-drawer {
10
+ position: fixed;
11
+ inset-block: 0;
12
+ inset-inline-end: 0;
13
+ z-index: 50;
14
+ display: flex;
15
+ flex-direction: column;
16
+ width: min(1100px, 100vw);
17
+ background: var(--background);
18
+ color: var(--foreground);
19
+ border-inline-start: 1px solid var(--border);
20
+ font-size: var(--font-size-body);
21
+ line-height: var(--line-height-body);
22
+ outline: none;
23
+ animation: ridu-api-reference-in 200ms ease-out;
24
+
25
+ &[dir="rtl"] {
26
+ --api-reference-slide: -40px;
27
+ }
28
+ &__header {
29
+ display: flex;
30
+ justify-content: space-between;
31
+ align-items: flex-start;
32
+ gap: 20px;
33
+ padding: 24px 30px;
34
+ border-bottom: 1px solid var(--border);
35
+ .ridu-api-reference-drawer__title {
36
+ margin: 0 0 8px;
37
+ font-size: 24px;
38
+ font-weight: 500;
39
+ }
40
+ code {
41
+ margin-inline-start: 8px;
42
+ font-size: 15px;
43
+ color: var(--muted-foreground);
44
+ }
45
+ .ridu-api-reference-drawer__description {
46
+ margin: 0;
47
+ color: var(--muted-foreground);
48
+ }
49
+ svg {
50
+ width: 20px;
51
+ height: 20px;
52
+ }
53
+ }
54
+ &__message {
55
+ padding: 30px;
56
+ }
57
+ }
58
+
59
+ @keyframes ridu-api-reference-in {
60
+ from {
61
+ opacity: 0;
62
+ transform: translateX(var(--api-reference-slide, 40px));
63
+ }
64
+ to {
65
+ opacity: 1;
66
+ transform: translateX(0);
67
+ }
68
+ }
69
+ @media (prefers-reduced-motion: reduce) {
70
+ .ridu-api-reference-drawer {
71
+ animation: none;
72
+ }
73
+ }
74
+ }