@carlonicora/nextjs-jsonapi 3.3.9 → 3.4.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 (175) hide show
  1. package/dist/{BlockNoteEditor-BYMRWUVG.mjs → BlockNoteEditor-3D5D7YL7.mjs} +2 -2
  2. package/dist/{BlockNoteEditor-USRMFZGP.js → BlockNoteEditor-F6TGKMTW.js} +9 -9
  3. package/dist/{BlockNoteEditor-USRMFZGP.js.map → BlockNoteEditor-F6TGKMTW.js.map} +1 -1
  4. package/dist/billing/index.js +347 -347
  5. package/dist/billing/index.js.map +1 -1
  6. package/dist/billing/index.mjs +27 -27
  7. package/dist/billing/index.mjs.map +1 -1
  8. package/dist/{chunk-CFZW6GU4.mjs → chunk-G5LSXG5K.mjs} +2572 -2482
  9. package/dist/chunk-G5LSXG5K.mjs.map +1 -0
  10. package/dist/{chunk-5WFYQE22.js → chunk-KT4CTWCV.js} +825 -735
  11. package/dist/chunk-KT4CTWCV.js.map +1 -0
  12. package/dist/client/index.d.mts +5 -0
  13. package/dist/client/index.d.ts +5 -0
  14. package/dist/client/index.js +2 -2
  15. package/dist/client/index.mjs +1 -1
  16. package/dist/components/index.d.mts +12 -6
  17. package/dist/components/index.d.ts +12 -6
  18. package/dist/components/index.js +2 -2
  19. package/dist/components/index.mjs +1 -1
  20. package/dist/contexts/index.d.mts +13 -1
  21. package/dist/contexts/index.d.ts +13 -1
  22. package/dist/contexts/index.js +6 -2
  23. package/dist/contexts/index.js.map +1 -1
  24. package/dist/contexts/index.mjs +5 -1
  25. package/dist/features/help/index.js +35 -35
  26. package/dist/features/help/index.js.map +1 -1
  27. package/dist/features/help/index.mjs +6 -6
  28. package/dist/features/help/index.mjs.map +1 -1
  29. package/dist/features/tokenusage/index.js +135 -129
  30. package/dist/features/tokenusage/index.js.map +1 -1
  31. package/dist/features/tokenusage/index.mjs +74 -68
  32. package/dist/features/tokenusage/index.mjs.map +1 -1
  33. package/package.json +2 -2
  34. package/scripts/check-rtl-classes.mjs +60 -0
  35. package/scripts/check-rtl-classes.test.ts +25 -0
  36. package/src/components/TableCellAvatar.tsx +1 -1
  37. package/src/components/containers/PageContainer.tsx +1 -1
  38. package/src/components/containers/ReactMarkdownContainer.tsx +20 -4
  39. package/src/components/containers/RoundPageContainer.tsx +7 -7
  40. package/src/components/contents/AttributeElement.tsx +1 -1
  41. package/src/components/editors/BlockNoteDiffInlineContent.tsx +2 -2
  42. package/src/components/editors/BlockNoteEditorMentionHoverCard.tsx +1 -0
  43. package/src/components/forms/CommonDeleter.tsx +1 -1
  44. package/src/components/forms/CommonEditorButtons.tsx +1 -1
  45. package/src/components/forms/DatePickerPopover.tsx +2 -2
  46. package/src/components/forms/DateRangeSelector.tsx +2 -2
  47. package/src/components/forms/EditorSheet.tsx +18 -9
  48. package/src/components/forms/EntityMultiSelector.tsx +7 -7
  49. package/src/components/forms/EntitySelector.tsx +8 -8
  50. package/src/components/forms/FileUploader.tsx +1 -0
  51. package/src/components/forms/FormCheckbox.tsx +3 -3
  52. package/src/components/forms/FormDateTime.tsx +3 -3
  53. package/src/components/forms/FormFieldWrapper.tsx +1 -1
  54. package/src/components/forms/FormPlaceAutocomplete.tsx +2 -2
  55. package/src/components/forms/GdprConsentCheckbox.tsx +1 -1
  56. package/src/components/forms/MultiFileUploader.tsx +1 -1
  57. package/src/components/forms/PasswordInput.tsx +1 -1
  58. package/src/components/forms/__tests__/FormCheckbox.test.tsx +4 -4
  59. package/src/components/navigations/Header.tsx +2 -2
  60. package/src/components/navigations/PageSection.tsx +1 -1
  61. package/src/components/pages/PageContainerContentDetails.tsx +1 -1
  62. package/src/components/pages/PageContentContainer.tsx +5 -2
  63. package/src/components/tables/ContentListTable.tsx +4 -4
  64. package/src/components/tables/ContentTableSearch.tsx +5 -5
  65. package/src/contexts/DirectionContext.tsx +25 -0
  66. package/src/contexts/__tests__/DirectionContext.test.tsx +22 -0
  67. package/src/contexts/index.ts +1 -0
  68. package/src/features/ai-connection/components/lists/AiConnectionTypeCard.tsx +1 -1
  69. package/src/features/assistant/components/containers/AssistantPageContainer.tsx +1 -1
  70. package/src/features/assistant/components/parts/AssistantBlockNoteComposer.tsx +2 -2
  71. package/src/features/assistant/components/parts/AssistantComposer.tsx +2 -2
  72. package/src/features/assistant/components/parts/AssistantEmptyState.tsx +1 -1
  73. package/src/features/assistant/components/parts/AssistantSidebar.tsx +3 -3
  74. package/src/features/assistant-action/components/ApprovalActionCard.tsx +1 -1
  75. package/src/features/assistant-action/components/AssistantContainerWithApprovals.tsx +1 -1
  76. package/src/features/assistant-message/components/MessageItem.tsx +3 -3
  77. package/src/features/assistant-message/components/__tests__/MessageItem.spec.tsx +1 -0
  78. package/src/features/assistant-message/components/parts/MessageSourcesPanel.tsx +2 -2
  79. package/src/features/assistant-message/components/parts/RelevanceMeter.tsx +1 -1
  80. package/src/features/assistant-message/components/parts/tabs/SuggestedQuestionsTab.tsx +1 -1
  81. package/src/features/auth/components/buttons/GoogleSignInButton.tsx +1 -1
  82. package/src/features/auth/components/forms/Register.tsx +2 -2
  83. package/src/features/auth/components/two-factor/BackupCodesDialog.tsx +3 -3
  84. package/src/features/auth/utils/__tests__/clearClientStorage.spec.ts +4 -4
  85. package/src/features/billing/components/cards/BillingUsageSummaryCard.tsx +1 -1
  86. package/src/features/billing/components/cards/CustomerInfoCard.tsx +1 -1
  87. package/src/features/billing/components/cards/InvoicesSummaryCard.tsx +1 -1
  88. package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +1 -1
  89. package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +1 -1
  90. package/src/features/billing/components/containers/BillingDashboardContainer.tsx +1 -1
  91. package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +1 -1
  92. package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +6 -6
  93. package/src/features/billing/stripe-invoice/components/lists/InvoicesList.tsx +2 -2
  94. package/src/features/billing/stripe-price/hooks/useStripePriceTableStructure.tsx +3 -3
  95. package/src/features/billing/stripe-product/hooks/useStripeProductTableStructure.tsx +2 -2
  96. package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +4 -4
  97. package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +2 -2
  98. package/src/features/billing/stripe-subscription/components/widgets/SubscriptionConfirmation.tsx +1 -1
  99. package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +2 -2
  100. package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +2 -2
  101. package/src/features/company/components/forms/CompanyDeleter.tsx +4 -4
  102. package/src/features/content/hooks/useContentTableStructure.tsx +1 -1
  103. package/src/features/feature/components/forms/FormFeatures.tsx +2 -2
  104. package/src/features/help/components/HelpAssistantSheet.tsx +3 -2
  105. package/src/features/help/components/HelpHint.tsx +2 -2
  106. package/src/features/help/components/HelpTOC.tsx +1 -1
  107. package/src/features/how-to/components/containers/HowToCommand.tsx +2 -2
  108. package/src/features/how-to/components/containers/HowToCommandViewer.tsx +5 -5
  109. package/src/features/how-to/components/forms/HowToEditor.tsx +3 -3
  110. package/src/features/how-to/components/forms/HowToSelector.tsx +5 -5
  111. package/src/features/notification/components/lists/NotificationsList.tsx +2 -2
  112. package/src/features/notification/components/modals/NotificationModal.tsx +1 -1
  113. package/src/features/oauth/components/OAuthClientCard.tsx +2 -2
  114. package/src/features/oauth/components/OAuthClientDetail.tsx +2 -2
  115. package/src/features/oauth/components/OAuthClientList.tsx +3 -3
  116. package/src/features/oauth/components/OAuthRedirectUriInput.tsx +1 -1
  117. package/src/features/oauth/components/OAuthScopeSelector.tsx +2 -2
  118. package/src/features/onboarding/components/OnboardingCard.tsx +1 -7
  119. package/src/features/rbac/components/RbacByRoleContainer.tsx +3 -3
  120. package/src/features/rbac/components/RbacContainer.tsx +3 -3
  121. package/src/features/referral/components/ReferralWidget.tsx +1 -0
  122. package/src/features/role/components/forms/FormRoles.tsx +1 -1
  123. package/src/features/role/components/forms/RemoveUserFromRole.tsx +1 -1
  124. package/src/features/role/components/forms/UserRoleAdd.tsx +1 -1
  125. package/src/features/tokenusage/components/TokenUsageBreakdownTable.tsx +9 -9
  126. package/src/features/tokenusage/components/TokenUsageRankedBar.tsx +8 -3
  127. package/src/features/tokenusage/components/TokenUsageTimelineChart.tsx +5 -1
  128. package/src/features/user/components/details/UserStandaloneDetails.tsx +1 -1
  129. package/src/features/user/components/forms/UserReactivator.tsx +2 -2
  130. package/src/features/user/components/forms/UserResentInvitationEmail.tsx +1 -1
  131. package/src/features/user/components/forms/UserSelector.tsx +7 -7
  132. package/src/features/user/components/lists/ContributorsList.tsx +1 -1
  133. package/src/features/user/components/lists/UserListInAdd.tsx +1 -1
  134. package/src/features/user/components/widgets/UserSearchPopover.tsx +5 -5
  135. package/src/features/user/hooks/useUserTableStructure.tsx +1 -1
  136. package/src/features/waitlist/components/forms/WaitlistQuestionnaireRenderer.tsx +1 -1
  137. package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +1 -1
  138. package/src/hooks/useCustomD3Graph.tsx +16 -0
  139. package/src/lib/__tests__/direction.test.ts +18 -0
  140. package/src/lib/direction.ts +8 -0
  141. package/src/shadcnui/custom/multi-select.tsx +6 -6
  142. package/src/shadcnui/custom/multiple-selector.tsx +6 -2
  143. package/src/shadcnui/ui/accordion.tsx +1 -1
  144. package/src/shadcnui/ui/alert-dialog.tsx +2 -1
  145. package/src/shadcnui/ui/alert.tsx +2 -2
  146. package/src/shadcnui/ui/avatar.tsx +1 -1
  147. package/src/shadcnui/ui/badge.tsx +1 -1
  148. package/src/shadcnui/ui/breadcrumb.tsx +1 -1
  149. package/src/shadcnui/ui/button.tsx +4 -4
  150. package/src/shadcnui/ui/calendar.tsx +9 -8
  151. package/src/shadcnui/ui/carousel.tsx +18 -11
  152. package/src/shadcnui/ui/combobox.tsx +3 -3
  153. package/src/shadcnui/ui/command.tsx +2 -2
  154. package/src/shadcnui/ui/context-menu.tsx +15 -11
  155. package/src/shadcnui/ui/dialog.tsx +2 -1
  156. package/src/shadcnui/ui/drawer.tsx +19 -3
  157. package/src/shadcnui/ui/dropdown-menu.tsx +12 -10
  158. package/src/shadcnui/ui/field.tsx +2 -2
  159. package/src/shadcnui/ui/input-group.tsx +3 -3
  160. package/src/shadcnui/ui/input-otp.tsx +2 -0
  161. package/src/shadcnui/ui/navigation-menu.tsx +2 -2
  162. package/src/shadcnui/ui/progress.tsx +1 -1
  163. package/src/shadcnui/ui/radio-group.tsx +1 -0
  164. package/src/shadcnui/ui/resizable.tsx +1 -0
  165. package/src/shadcnui/ui/scroll-area.tsx +1 -1
  166. package/src/shadcnui/ui/select.tsx +2 -2
  167. package/src/shadcnui/ui/sheet.tsx +11 -4
  168. package/src/shadcnui/ui/sidebar.tsx +37 -20
  169. package/src/shadcnui/ui/sonner.tsx +6 -1
  170. package/src/shadcnui/ui/switch.tsx +1 -1
  171. package/src/shadcnui/ui/table.tsx +2 -2
  172. package/src/shadcnui/ui/tabs.tsx +1 -1
  173. package/dist/chunk-5WFYQE22.js.map +0 -1
  174. package/dist/chunk-CFZW6GU4.mjs.map +0 -1
  175. /package/dist/{BlockNoteEditor-BYMRWUVG.mjs.map → BlockNoteEditor-3D5D7YL7.mjs.map} +0 -0
@@ -49,9 +49,9 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
49
49
  header: t("billing.admin.prices.fields.amount"),
50
50
  cell: ({ row }: { row: TableContent<StripePriceInterface> }) => {
51
51
  const price: StripePriceInterface = row.original.jsonApiData;
52
- // Typography role 15 (numeric): tabular-nums, right-aligned. NEVER font-mono.
52
+ // Typography role 15 (numeric): tabular-nums, end-aligned. NEVER font-mono.
53
53
  return (
54
- <span className="block text-xs tabular-nums text-right">
54
+ <span className="block text-xs tabular-nums text-end">
55
55
  {formatCurrency(price.unitAmount, price.currency)}
56
56
  </span>
57
57
  );
@@ -77,7 +77,7 @@ export const useStripePriceTableStructure: UseTableStructureHook<StripePriceInte
77
77
  header: t("billing.admin.prices.fields.token"),
78
78
  cell: ({ row }: { row: TableContent<StripePriceInterface> }) => {
79
79
  const price: StripePriceInterface = row.original.jsonApiData;
80
- return <span className="block text-xs tabular-nums text-right">{price.token ?? ""}</span>;
80
+ return <span className="block text-xs tabular-nums text-end">{price.token ?? ""}</span>;
81
81
  },
82
82
  enableSorting: false,
83
83
  enableHiding: false,
@@ -59,9 +59,9 @@ export const useStripeProductTableStructure: UseTableStructureHook<StripeProduct
59
59
  header: t("billing.admin.products.fields.prices"),
60
60
  cell: ({ row }: { row: TableContent<StripeProductInterface> }) => {
61
61
  const product: StripeProductInterface = row.original.jsonApiData;
62
- // Typography role 15 (numeric): tabular-nums, right-aligned. NEVER
62
+ // Typography role 15 (numeric): tabular-nums, end-aligned. NEVER
63
63
  // font-mono — no monospace font is loaded in these apps.
64
- return <span className="block text-xs tabular-nums text-right">{product.stripePrices.length}</span>;
64
+ return <span className="block text-xs tabular-nums text-end">{product.stripePrices.length}</span>;
65
65
  },
66
66
  enableSorting: false,
67
67
  enableHiding: false,
@@ -60,8 +60,8 @@ export function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChan
60
60
  <TableHead>Status</TableHead>
61
61
  <TableHead>Plan</TableHead>
62
62
  <TableHead>Period</TableHead>
63
- <TableHead className="text-right">Amount</TableHead>
64
- <TableHead className="text-right">Actions</TableHead>
63
+ <TableHead className="text-end">Amount</TableHead>
64
+ <TableHead className="text-end">Actions</TableHead>
65
65
  </TableRow>
66
66
  </TableHeader>
67
67
  <TableBody>
@@ -84,8 +84,8 @@ export function SubscriptionsList({ subscriptions, onSubscriptionsChange, onChan
84
84
  </TableCell>
85
85
  <TableCell className="font-medium">{formatPlanName(price)}</TableCell>
86
86
  <TableCell className="text-muted-foreground text-sm">{period}</TableCell>
87
- <TableCell className="text-right font-medium">{amount}</TableCell>
88
- <TableCell className="text-right">
87
+ <TableCell className="text-end font-medium">{amount}</TableCell>
88
+ <TableCell className="text-end">
89
89
  {(subscription.status === SubscriptionStatus.ACTIVE ||
90
90
  subscription.status === SubscriptionStatus.TRIALING) &&
91
91
  onChangePlan && (
@@ -61,7 +61,7 @@ export function PricingCard({
61
61
  )}
62
62
  >
63
63
  {isCurrentPlan && (
64
- <Badge variant="secondary" className="absolute top-2 right-2">
64
+ <Badge variant="secondary" className="absolute top-2 end-2">
65
65
  Current
66
66
  </Badge>
67
67
  )}
@@ -73,7 +73,7 @@ export function PricingCard({
73
73
  <CardContent className="pb-4 grow">
74
74
  <div className="mb-4">
75
75
  <span className="text-3xl font-bold">{formattedPrice}</span>
76
- <span className="text-muted-foreground ml-1">{interval}</span>
76
+ <span className="text-muted-foreground ms-1">{interval}</span>
77
77
  </div>
78
78
 
79
79
  {features.length > 0 && (
@@ -54,7 +54,7 @@ export function SubscriptionConfirmation({ price, isLoading, onConfirm, onCancel
54
54
  <Button onClick={onConfirm} disabled={isLoading}>
55
55
  {isLoading ? (
56
56
  <>
57
- <Loader2 className="h-4 w-4 animate-spin mr-2" />
57
+ <Loader2 className="h-4 w-4 animate-spin me-2" />
58
58
  Processing...
59
59
  </>
60
60
  ) : (
@@ -127,7 +127,7 @@ export function WizardStepReview({
127
127
  <p className="font-medium">{selectedPrice.product?.name}</p>
128
128
  {selectedPrice.nickname && <p className="text-sm text-muted-foreground">{selectedPrice.nickname}</p>}
129
129
  </div>
130
- <div className="text-right">
130
+ <div className="text-end">
131
131
  {discountedPrice !== null ? (
132
132
  <>
133
133
  <p className="text-sm text-muted-foreground line-through">
@@ -168,7 +168,7 @@ export function WizardStepReview({
168
168
  <span className={`font-medium ${isTrialUpgrade ? "text-warning" : "text-foreground"}`}>
169
169
  {discountedImmediateCharge !== null ? (
170
170
  <>
171
- <span className="line-through text-muted-foreground mr-2">
171
+ <span className="line-through text-muted-foreground me-2">
172
172
  {formatCurrency(prorationPreview.immediateCharge, prorationPreview.currency)}
173
173
  </span>
174
174
  <span className="text-success">
@@ -47,7 +47,7 @@ export function UsageHistoryTable({ usageRecords }: UsageHistoryTableProps) {
47
47
  <TableRow>
48
48
  <TableHead>Date & Time</TableHead>
49
49
  <TableHead>Meter Event</TableHead>
50
- <TableHead className="text-right">Quantity</TableHead>
50
+ <TableHead className="text-end">Quantity</TableHead>
51
51
  <TableHead>Event ID</TableHead>
52
52
  </TableRow>
53
53
  </TableHeader>
@@ -60,7 +60,7 @@ export function UsageHistoryTable({ usageRecords }: UsageHistoryTableProps) {
60
60
  <TableRow key={record.id}>
61
61
  <TableCell className="font-medium">{dateTime}</TableCell>
62
62
  <TableCell className="text-muted-foreground">{record.meterEventName}</TableCell>
63
- <TableCell className="text-right font-medium">{quantity}</TableCell>
63
+ <TableCell className="text-end font-medium">{quantity}</TableCell>
64
64
  <TableCell className="text-muted-foreground text-sm tabular-nums">{record.stripeEventId}</TableCell>
65
65
  </TableRow>
66
66
  );
@@ -99,7 +99,7 @@ function CompanyDeleterInternal({ company, isAdministrator }: CompanyDeleterInte
99
99
  <div className="flex w-full flex-col">
100
100
  <Label className="flex items-center">
101
101
  {t(`company.fields.name.label`)}
102
- <span className="ml-1 text-destructive">*</span>
102
+ <span className="ms-1 text-destructive">*</span>
103
103
  </Label>
104
104
  <Input
105
105
  className={`w-full`}
@@ -109,7 +109,7 @@ function CompanyDeleterInternal({ company, isAdministrator }: CompanyDeleterInte
109
109
  </div>
110
110
  </div>
111
111
  <div className="flex justify-end">
112
- <Button className="mr-2" variant={"outline"} type={`button`} onClick={() => setOpen(false)}>
112
+ <Button className="me-2" variant={"outline"} type={`button`} onClick={() => setOpen(false)}>
113
113
  {t(`ui.buttons.cancel`)}
114
114
  </Button>
115
115
  <Button
@@ -136,7 +136,7 @@ function CompanyDeleterInternal({ company, isAdministrator }: CompanyDeleterInte
136
136
  </AlertDialogHeader>
137
137
  <div className="bg-destructive/10 border-destructive text-destructive rounded-md border p-4 text-sm">
138
138
  <p className="mb-3 font-semibold">{t(`common.delete.finalWarning.description`)}</p>
139
- <ul className="list-disc space-y-1 pl-5">
139
+ <ul className="list-disc space-y-1 ps-5">
140
140
  <li>{t(`common.delete.finalWarning.bullet1`)}</li>
141
141
  <li>{t(`common.delete.finalWarning.bullet2`)}</li>
142
142
  <li>{t(`common.delete.finalWarning.bullet3`)}</li>
@@ -145,7 +145,7 @@ function CompanyDeleterInternal({ company, isAdministrator }: CompanyDeleterInte
145
145
  </ul>
146
146
  </div>
147
147
  <div className="flex justify-end">
148
- <Button className="mr-2" variant={"outline"} type={`button`} onClick={handleGoBack} disabled={isDeleting}>
148
+ <Button className="me-2" variant={"outline"} type={`button`} onClick={handleGoBack} disabled={isDeleting}>
149
149
  {t(`ui.buttons.go_back`)}
150
150
  </Button>
151
151
  <Button
@@ -75,7 +75,7 @@ export const useContentTableStructure = <U extends string = ContentFields>(
75
75
  return (
76
76
  <div className="relative flex h-5 w-20 items-center justify-center overflow-clip rounded border text-center">
77
77
  <div
78
- className={`bg-accent absolute top-0 left-0 h-full opacity-${Math.round(content.relevance)}`}
78
+ className={`bg-accent absolute top-0 start-0 h-full opacity-${Math.round(content.relevance)}`}
79
79
  style={{ width: `${content.relevance}%` }}
80
80
  ></div>
81
81
  <span
@@ -37,7 +37,7 @@ export function FormFeatures({ form, name, features, featureField = "featureIds"
37
37
  </SectionHeader>
38
38
  )}
39
39
  <ScrollArea className="h-[40vh]">
40
- <div className="flex flex-col gap-y-2 pr-4">
40
+ <div className="flex flex-col gap-y-2 pe-4">
41
41
  {features
42
42
  .filter((feature) => !feature.isCore)
43
43
  .map((feature) => (
@@ -49,7 +49,7 @@ export function FormFeatures({ form, name, features, featureField = "featureIds"
49
49
  toggleFeature(feature, val === true);
50
50
  }}
51
51
  />
52
- <Label htmlFor={feature.id} className="ml-3 cursor-pointer font-normal">
52
+ <Label htmlFor={feature.id} className="ms-3 cursor-pointer font-normal">
53
53
  {feature.name}
54
54
  </Label>
55
55
  </div>
@@ -10,8 +10,9 @@ export function HelpAssistantSheet({ open, onOpenChange }: { open: boolean; onOp
10
10
  return (
11
11
  <Sheet open={open} onOpenChange={onOpenChange}>
12
12
  <SheetContent
13
- side="right"
14
- className="flex w-full flex-col data-[side=right]:sm:max-w-2xl data-[side=right]:lg:max-w-3xl"
13
+ side="end"
14
+ /* rtl-ok: keyed to the resolved physical side ("right" in LTR, "left" in RTL) */
15
+ className="flex w-full flex-col data-[side=right]:sm:max-w-2xl data-[side=right]:lg:max-w-3xl data-[side=left]:sm:max-w-2xl data-[side=left]:lg:max-w-3xl"
15
16
  >
16
17
  <AssistantProvider manageUrl={false}>
17
18
  <HelpAssistantSheetBody />
@@ -52,7 +52,7 @@ export function HelpHint({ contextKey }: { contextKey: string }) {
52
52
  <SheetTrigger render={<Button variant="ghost" size="icon-sm" aria-label={t("help.hint.trigger")} />}>
53
53
  <HelpCircleIcon className="h-4 w-4" />
54
54
  </SheetTrigger>
55
- <SheetContent side="right" className="w-full max-w-md sm:max-w-lg">
55
+ <SheetContent side="end" className="w-full max-w-md sm:max-w-lg">
56
56
  <SheetHeader>
57
57
  <SheetTitle>{active ? active.name : t("help.hint.pickArticle")}</SheetTitle>
58
58
  <SheetDescription>{active?.summary ?? ""}</SheetDescription>
@@ -71,7 +71,7 @@ export function HelpHint({ contextKey }: { contextKey: string }) {
71
71
  <button
72
72
  type="button"
73
73
  onClick={() => setPicked(a)}
74
- className="hover:bg-muted block w-full rounded px-2 py-1 text-left text-sm"
74
+ className="hover:bg-muted block w-full rounded px-2 py-1 text-start text-sm"
75
75
  >
76
76
  <div className="font-medium">{a.name}</div>
77
77
  <div className="text-muted-foreground text-xs">{a.summary}</div>
@@ -30,7 +30,7 @@ export function HelpTOC({ headings }: { headings: readonly HelpHeading[] }) {
30
30
  <MicroLabel className="mb-2">{t("help.toc.title")}</MicroLabel>
31
31
  <ul className="space-y-1 text-sm">
32
32
  {headings.map((h) => (
33
- <li key={h.slug} style={{ paddingLeft: `${(h.depth - 2) * 0.75}rem` }}>
33
+ <li key={h.slug} style={{ paddingInlineStart: `${(h.depth - 2) * 0.75}rem` }}>
34
34
  <a
35
35
  href={`#${h.slug}`}
36
36
  className={
@@ -184,7 +184,7 @@ export default function HowToCommand({ pathname, extraGroups, onStartChat }: How
184
184
  <CommandGroup heading={t(`howto.command.relevant`)}>
185
185
  {relevantHowTos.map((howTo: HowToInterface) => (
186
186
  <CommandItem key={howTo.id} onSelect={() => setSelectedHowTo(howTo)} className="cursor-pointer">
187
- <ArrowRight className="h-4 w-4" />
187
+ <ArrowRight className="h-4 w-4 rtl:rotate-180" />
188
188
  <span>{howTo.name}</span>
189
189
  </CommandItem>
190
190
  ))}
@@ -196,7 +196,7 @@ export default function HowToCommand({ pathname, extraGroups, onStartChat }: How
196
196
  <CommandGroup heading={searchTerm ? undefined : t(`howto.command.all`)}>
197
197
  {otherHowTos.map((howTo: HowToInterface) => (
198
198
  <CommandItem key={howTo.id} onSelect={() => setSelectedHowTo(howTo)} className="cursor-pointer">
199
- <ArrowRight className="h-4 w-4" />
199
+ <ArrowRight className="h-4 w-4 rtl:rotate-180" />
200
200
  <span>{howTo.name}</span>
201
201
  </CommandItem>
202
202
  ))}
@@ -27,8 +27,8 @@ export default function HowToCommandViewer({ howTo, onBack, onStartChat }: HowTo
27
27
  {/* Header with back button, title, and reading time */}
28
28
  <div className="flex items-center gap-3 border-b px-4 py-3">
29
29
  <Button variant="ghost" size="sm" onClick={onBack} className="h-8 px-2">
30
- <ArrowLeft className="h-4 w-4" />
31
- <span className="ml-1">{t("howto.command.back")}</span>
30
+ <ArrowLeft className="h-4 w-4 rtl:rotate-180" />
31
+ <span className="ms-1">{t("howto.command.back")}</span>
32
32
  </Button>
33
33
  <SectionHeader level={2} className="flex-1 truncate">
34
34
  {howTo.name}
@@ -43,14 +43,14 @@ export default function HowToCommandViewer({ howTo, onBack, onStartChat }: HowTo
43
43
  <div className="flex min-h-0 flex-1">
44
44
  {/* Left sidebar - table of contents */}
45
45
  {headings.length > 0 && (
46
- <div className="w-50 shrink-0 overflow-y-auto border-r p-4">
46
+ <div className="w-50 shrink-0 overflow-y-auto border-e p-4">
47
47
  <nav className="space-y-1">
48
48
  {headings.map((heading) => (
49
49
  <a
50
50
  key={heading.id}
51
51
  href={`#${heading.id}`}
52
52
  className="text-muted-foreground hover:text-foreground block truncate text-sm"
53
- style={{ paddingLeft: `${(heading.level - 1) * 0.75}rem` }}
53
+ style={{ paddingInlineStart: `${(heading.level - 1) * 0.75}rem` }}
54
54
  >
55
55
  {heading.text}
56
56
  </a>
@@ -69,7 +69,7 @@ export default function HowToCommandViewer({ howTo, onBack, onStartChat }: HowTo
69
69
  {onStartChat && (
70
70
  <div className="flex items-center justify-end gap-2 border-t px-4 py-3">
71
71
  <Button onClick={onStartChat} variant="default" size="sm">
72
- <MessageSquare className="mr-2 h-4 w-4" />
72
+ <MessageSquare className="me-2 h-4 w-4" />
73
73
  {t("howto.command.chat_button")}
74
74
  </Button>
75
75
  </div>
@@ -77,11 +77,11 @@ function PageSelector({
77
77
  <PopoverContent align="start" className="w-(--anchor-width) p-0">
78
78
  <Command shouldFilter={false}>
79
79
  <div className="relative w-full border-b">
80
- <SearchIcon className="text-muted-foreground absolute top-2.5 left-2.5 h-4 w-4" />
80
+ <SearchIcon className="text-muted-foreground absolute top-2.5 start-2.5 h-4 w-4" />
81
81
  <Input
82
82
  placeholder={placeholder}
83
83
  type="text"
84
- className="rounded-none border-0 pl-8 focus-visible:ring-0"
84
+ className="rounded-none border-0 ps-8 focus-visible:ring-0"
85
85
  onChange={(e) => setSearch(e.target.value)}
86
86
  value={search}
87
87
  />
@@ -339,7 +339,7 @@ function HowToEditorInternal({
339
339
  />
340
340
  ))}
341
341
  <Button type="button" variant="outline" size="sm" onClick={addPage}>
342
- <PlusIcon className="mr-2 h-4 w-4" />
342
+ <PlusIcon className="me-2 h-4 w-4" />
343
343
  {t(`howto.fields.pages.add`)}
344
344
  </Button>
345
345
  </div>
@@ -111,7 +111,7 @@ export default function HowToSelector({
111
111
  </PopoverTrigger>
112
112
  {field.value && (
113
113
  <CircleX
114
- className="text-muted hover:text-destructive ml-2 h-4 w-4 shrink-0 cursor-pointer"
114
+ className="text-muted hover:text-destructive ms-2 h-4 w-4 shrink-0 cursor-pointer"
115
115
  onClick={() => setHowTo()}
116
116
  />
117
117
  )}
@@ -119,19 +119,19 @@ export default function HowToSelector({
119
119
  <PopoverContent align="start" className="w-(--anchor-width)">
120
120
  <Command shouldFilter={false}>
121
121
  <div className="relative mb-2 w-full">
122
- <SearchIcon className="text-muted-foreground absolute top-2.5 left-2.5 h-4 w-4" />
122
+ <SearchIcon className="text-muted-foreground absolute top-2.5 start-2.5 h-4 w-4" />
123
123
  <Input
124
124
  placeholder={t(`generic.search.placeholder`, { type: t(`entities.howtos`, { count: 1 }) })}
125
125
  type="text"
126
- className="w-full pr-8 pl-8"
126
+ className="w-full pe-8 ps-8"
127
127
  onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearchTerm(e.target.value)}
128
128
  value={searchTerm}
129
129
  />
130
130
  {isSearching ? (
131
- <RefreshCwIcon className="text-muted-foreground absolute top-2.5 right-2.5 h-4 w-4 animate-spin" />
131
+ <RefreshCwIcon className="text-muted-foreground absolute top-2.5 end-2.5 h-4 w-4 animate-spin" />
132
132
  ) : searchTermRef.current ? (
133
133
  <XIcon
134
- className={`absolute top-2.5 right-2.5 h-4 w-4 ${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}`}
134
+ className={`absolute top-2.5 end-2.5 h-4 w-4 ${searchTermRef.current ? "cursor-pointer" : "text-muted-foreground"}`}
135
135
  onClick={() => {
136
136
  setSearchTerm("");
137
137
  search("");
@@ -44,7 +44,7 @@ export function NotificationsList({ archived }: NotificationsListProps) {
44
44
  <Card key={i}>
45
45
  <CardContent className="p-2">
46
46
  <div className="flex w-full flex-row items-center">
47
- <Skeleton className="mr-4 h-8 w-8 rounded-full" />
47
+ <Skeleton className="me-4 h-8 w-8 rounded-full" />
48
48
  <div className="flex-1 space-y-2">
49
49
  <Skeleton className="h-4 w-3/4" />
50
50
  <Skeleton className="h-3 w-1/2" />
@@ -108,7 +108,7 @@ export function NotificationsList({ archived }: NotificationsListProps) {
108
108
  e.stopPropagation();
109
109
  archiveNotification(notification);
110
110
  }}
111
- className="text-muted-foreground hover:text-destructive ml-2"
111
+ className="text-muted-foreground hover:text-destructive ms-2"
112
112
  >
113
113
  <ArchiveIcon className="h-4 w-4 cursor-pointer" />
114
114
  </Button>
@@ -200,7 +200,7 @@ function NotificationModalContent({ isOpen, setIsOpen }: NotificationModalProps)
200
200
  {t(`entities.notifications`, { count: 2 })}
201
201
  </SidebarMenuButton>
202
202
  </PopoverTrigger>
203
- <PopoverContent className="relative left-10 w-80 border-0 p-0 shadow-none">
203
+ <PopoverContent className="relative start-10 w-80 border-0 p-0 shadow-none">
204
204
  <Card>
205
205
  <CardHeader className="p-4">
206
206
  <CardTitle>{t(`entities.notifications`, { count: 2 })}</CardTitle>
@@ -68,7 +68,7 @@ export function OAuthClientCard({ client, onClick, onEdit, onDelete }: OAuthClie
68
68
  onEdit();
69
69
  }}
70
70
  >
71
- <Pencil className="h-4 w-4 mr-2" />
71
+ <Pencil className="h-4 w-4 me-2" />
72
72
  Edit
73
73
  </DropdownMenuItem>
74
74
  )}
@@ -80,7 +80,7 @@ export function OAuthClientCard({ client, onClick, onEdit, onDelete }: OAuthClie
80
80
  }}
81
81
  className="text-destructive"
82
82
  >
83
- <Trash2 className="h-4 w-4 mr-2" />
83
+ <Trash2 className="h-4 w-4 me-2" />
84
84
  Delete
85
85
  </DropdownMenuItem>
86
86
  )}
@@ -194,13 +194,13 @@ export function OAuthClientDetail({
194
194
  <div className="flex gap-3 pt-4">
195
195
  {onEdit && (
196
196
  <Button variant="outline" onClick={onEdit} disabled={isLoading}>
197
- <Pencil className="h-4 w-4 mr-2" />
197
+ <Pencil className="h-4 w-4 me-2" />
198
198
  Edit
199
199
  </Button>
200
200
  )}
201
201
  {onDelete && (
202
202
  <Button variant="destructive" onClick={() => setShowDeleteConfirm(true)} disabled={isLoading}>
203
- <Trash2 className="h-4 w-4 mr-2" />
203
+ <Trash2 className="h-4 w-4 me-2" />
204
204
  Delete
205
205
  </Button>
206
206
  )}
@@ -80,7 +80,7 @@ export function OAuthClientList({
80
80
  <SectionHeader level={2}>{title}</SectionHeader>
81
81
  {onCreateClick && (
82
82
  <Button onClick={onCreateClick}>
83
- <Plus className="h-4 w-4 mr-2" />
83
+ <Plus className="h-4 w-4 me-2" />
84
84
  New App
85
85
  </Button>
86
86
  )}
@@ -91,7 +91,7 @@ export function OAuthClientList({
91
91
  <p className="text-muted-foreground mb-4">{emptyStateMessage}</p>
92
92
  {onCreateClick && (
93
93
  <Button onClick={onCreateClick}>
94
- <Plus className="h-4 w-4 mr-2" />
94
+ <Plus className="h-4 w-4 me-2" />
95
95
  Create Application
96
96
  </Button>
97
97
  )}
@@ -107,7 +107,7 @@ export function OAuthClientList({
107
107
  <SectionHeader level={2}>{title}</SectionHeader>
108
108
  {onCreateClick && (
109
109
  <Button onClick={onCreateClick}>
110
- <Plus className="h-4 w-4 mr-2" />
110
+ <Plus className="h-4 w-4 me-2" />
111
111
  New App
112
112
  </Button>
113
113
  )}
@@ -135,7 +135,7 @@ export function OAuthRedirectUriInput({
135
135
  </div>
136
136
 
137
137
  <Button type="button" variant="outline" size="sm" onClick={handleAdd} disabled={disabled} className="mt-2">
138
- <Plus className="h-4 w-4 mr-2" />
138
+ <Plus className="h-4 w-4 me-2" />
139
139
  Add Redirect URI
140
140
  </Button>
141
141
 
@@ -78,7 +78,7 @@ export function OAuthScopeSelector({
78
78
  {Object.entries(groupedScopes).map(([groupName, scopes]) => (
79
79
  <div key={groupName} className="space-y-2">
80
80
  <h4 className="text-sm font-medium capitalize">{groupName}</h4>
81
- <div className="grid grid-cols-1 md:grid-cols-2 gap-2 pl-2">
81
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-2 ps-2">
82
82
  {scopes.map((scopeInfo) => {
83
83
  const isChecked = value.includes(scopeInfo.scope);
84
84
  const isAdmin = scopeInfo.scope === "admin";
@@ -99,7 +99,7 @@ export function OAuthScopeSelector({
99
99
  <div className="flex-1">
100
100
  <Label htmlFor={`scope-${scopeInfo.scope}`} className="cursor-pointer">
101
101
  {scopeInfo.name}
102
- {isAdmin && <span className="ml-2 text-xs text-destructive">(Dangerous)</span>}
102
+ {isAdmin && <span className="ms-2 text-xs text-destructive">(Dangerous)</span>}
103
103
  </Label>
104
104
  <p className="text-xs text-muted-foreground">{scopeInfo.description}</p>
105
105
  </div>
@@ -18,13 +18,7 @@ export function OnboardingCard({
18
18
  }: OnboardingCardRenderProps) {
19
19
  return (
20
20
  <Card className="w-[320px] relative" data-shepherd-card>
21
- <Button
22
- variant="ghost"
23
- size="icon"
24
- className="absolute right-2 top-2"
25
- onClick={onClose}
26
- aria-label={labels.close}
27
- >
21
+ <Button variant="ghost" size="icon" className="absolute end-2 top-2" onClick={onClose} aria-label={labels.close}>
28
22
  <X className="h-4 w-4" />
29
23
  </Button>
30
24
  <CardHeader>
@@ -156,7 +156,7 @@ export default function RbacByRoleContainer() {
156
156
  return (
157
157
  <RoundPageContainer fullWidth forceHeader>
158
158
  <div className="flex h-full w-full">
159
- <aside className="w-60 shrink-0 overflow-y-auto border-r bg-muted/20">
159
+ <aside className="w-60 shrink-0 overflow-y-auto border-e bg-muted/20">
160
160
  <ul className="py-1">
161
161
  {sortedRoleIds.map((id) => (
162
162
  <li key={id}>
@@ -165,7 +165,7 @@ export default function RbacByRoleContainer() {
165
165
  onClick={() => handleSelectRole(id)}
166
166
  aria-current={id === selectedRoleId ? "true" : undefined}
167
167
  className={cn(
168
- "block w-full px-4 py-1.5 text-left text-sm hover:bg-muted",
168
+ "block w-full px-4 py-1.5 text-start text-sm hover:bg-muted",
169
169
  id === selectedRoleId && "bg-muted font-medium text-foreground",
170
170
  id !== selectedRoleId && "text-muted-foreground",
171
171
  )}
@@ -184,7 +184,7 @@ export default function RbacByRoleContainer() {
184
184
  <table className="w-full text-sm">
185
185
  <thead className="sticky top-0 z-10">
186
186
  <tr className="border-b bg-muted/80 backdrop-blur-sm">
187
- <th className="w-40 px-4 py-2 text-left text-xs font-medium text-muted-foreground">
187
+ <th className="w-40 px-4 py-2 text-start text-xs font-medium text-muted-foreground">
188
188
  {t("rbac.module")}
189
189
  </th>
190
190
  {ACTION_TYPES.map((action) => (
@@ -118,7 +118,7 @@ const ModuleEditor = memo(function ModuleEditor({
118
118
  <table className="w-full text-sm">
119
119
  <thead>
120
120
  <tr className="border-b bg-muted/50">
121
- <th className="w-40 px-4 py-2 text-left text-xs font-medium text-muted-foreground">{t("rbac.role")}</th>
121
+ <th className="w-40 px-4 py-2 text-start text-xs font-medium text-muted-foreground">{t("rbac.role")}</th>
122
122
  {ACTION_TYPES.map((action) => (
123
123
  <th key={action} className="min-w-28 px-2 py-2 text-center text-xs font-medium text-muted-foreground">
124
124
  {ACTION_LABELS[action]}
@@ -295,7 +295,7 @@ export default function RbacContainer() {
295
295
  <RoundPageContainer fullWidth forceHeader>
296
296
  <div className="flex h-full w-full">
297
297
  {/* Sidebar: module list */}
298
- <aside className="w-60 shrink-0 overflow-y-auto border-r bg-muted/20">
298
+ <aside className="w-60 shrink-0 overflow-y-auto border-e bg-muted/20">
299
299
  <ul className="py-1">
300
300
  {sortedModuleIds.map((id) => (
301
301
  <li key={id}>
@@ -304,7 +304,7 @@ export default function RbacContainer() {
304
304
  onClick={() => handleSelectModule(id)}
305
305
  aria-current={id === selectedModuleId ? "true" : undefined}
306
306
  className={cn(
307
- "block w-full px-4 py-1.5 text-left text-sm hover:bg-muted",
307
+ "block w-full px-4 py-1.5 text-start text-sm hover:bg-muted",
308
308
  id === selectedModuleId && "bg-muted font-medium text-foreground",
309
309
  id !== selectedModuleId && "text-muted-foreground",
310
310
  )}
@@ -110,6 +110,7 @@ async function copyToClipboard(text: string): Promise<boolean> {
110
110
  const textArea = document.createElement("textarea");
111
111
  textArea.value = text;
112
112
  textArea.style.position = "fixed";
113
+ // rtl-ok: off-screen clipboard fallback, not layout — physical offset is deliberate
113
114
  textArea.style.left = "-999999px";
114
115
  textArea.style.top = "-999999px";
115
116
  document.body.appendChild(textArea);
@@ -47,7 +47,7 @@ export function FormRoles({ form, id, name, roles }: FormRolesProps) {
47
47
  />
48
48
  <Tooltip>
49
49
  <TooltipTrigger>
50
- <FieldLabel htmlFor={`role-${role.id}`} className="ml-3 cursor-pointer font-normal">
50
+ <FieldLabel htmlFor={`role-${role.id}`} className="ms-3 cursor-pointer font-normal">
51
51
  {t(`role.roles`, { role: role.id.replaceAll(`-`, ``) })}
52
52
  </FieldLabel>
53
53
  </TooltipTrigger>
@@ -84,7 +84,7 @@ export function RemoveUserFromRole({ role, user, refresh }: RemoveUserFromRolePr
84
84
  <>
85
85
  {t(`role.remove_user.description_allowed`, { role: roleName, user: user.name })}
86
86
  <div className="flex justify-end">
87
- <Button className="mr-2" variant={"outline"} type={`button`} onClick={() => setOpen(false)}>
87
+ <Button className="me-2" variant={"outline"} type={`button`} onClick={() => setOpen(false)}>
88
88
  {t(`ui.buttons.cancel`)}
89
89
  </Button>
90
90
  <Button
@@ -82,7 +82,7 @@ export function UserRoleAdd({ user, refresh }: UserRoleAddProps) {
82
82
  return (
83
83
  <>
84
84
  <Button size="sm" onClick={() => setOpen(true)}>
85
- <PlusCircle className="mr-3 h-3.5 w-3.5" />
85
+ <PlusCircle className="me-3 h-3.5 w-3.5" />
86
86
  {t(`common.association.label`, {
87
87
  source: t(`entities.roles`, { count: 1 }),
88
88
  destination: t(`entities.users`, { count: 1 }),
@@ -104,7 +104,7 @@ export function TokenUsageBreakdownTable({ rows, metric }: Props) {
104
104
  <TableHeader>
105
105
  <TableRow>
106
106
  {columns.map((column) => (
107
- <TableHead key={column.key} className={cn(column.numeric && "text-right")}>
107
+ <TableHead key={column.key} className={cn(column.numeric && "text-end")}>
108
108
  <button
109
109
  type="button"
110
110
  onClick={() => toggleSort(column.key)}
@@ -137,19 +137,19 @@ export function TokenUsageBreakdownTable({ rows, metric }: Props) {
137
137
  </TableCell>
138
138
  <TableCell className="text-muted-foreground text-xs">{row.sublabel ?? ""}</TableCell>
139
139
  {showActiveUsers && (
140
- <TableCell className="text-right text-xs tabular-nums">
140
+ <TableCell className="text-end text-xs tabular-nums">
141
141
  {row.activeUsers === undefined ? "" : decimal(row.activeUsers, 0)}
142
142
  </TableCell>
143
143
  )}
144
- <TableCell className="text-right text-xs tabular-nums">{decimal(row.calls, 0)}</TableCell>
145
- <TableCell className="text-right text-xs tabular-nums">{decimal(row.tokensIn, 0)}</TableCell>
146
- <TableCell className="text-right text-xs tabular-nums">{decimal(row.tokensOut, 0)}</TableCell>
147
- <TableCell className="text-right text-xs tabular-nums">
144
+ <TableCell className="text-end text-xs tabular-nums">{decimal(row.calls, 0)}</TableCell>
145
+ <TableCell className="text-end text-xs tabular-nums">{decimal(row.tokensIn, 0)}</TableCell>
146
+ <TableCell className="text-end text-xs tabular-nums">{decimal(row.tokensOut, 0)}</TableCell>
147
+ <TableCell className="text-end text-xs tabular-nums">
148
148
  {percent(cacheHitPercentage(row.cached, row.tokensIn))}
149
149
  </TableCell>
150
- <TableCell className="text-right text-xs tabular-nums">{formatValue(row.cost, "cost")}</TableCell>
151
- <TableCell className="text-right text-xs tabular-nums">{formatValue(row.credits, "credits")}</TableCell>
152
- <TableCell className="text-right text-xs tabular-nums">{percent(share)}</TableCell>
150
+ <TableCell className="text-end text-xs tabular-nums">{formatValue(row.cost, "cost")}</TableCell>
151
+ <TableCell className="text-end text-xs tabular-nums">{formatValue(row.credits, "credits")}</TableCell>
152
+ <TableCell className="text-end text-xs tabular-nums">{percent(share)}</TableCell>
153
153
  </TableRow>
154
154
  );
155
155
  })}