Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions scripts/rtl-baseline.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
{
"src/components/AGGrid/EnhancedCellRenderers.tsx": 2,
"src/components/AI/AIChatModal.tsx": 7,
"src/components/AI/HeyOzwell/OzwellSettingsMenu.tsx": 1,
"src/components/AI/HeyOzwell/VisitScribe.tsx": 2,
"src/components/AI/HeyOzwell/VoiceManager.tsx": 1,
"src/components/AI/HeyOzwell/VoiceSetup.tsx": 1,
"src/components/AI/Reconciliation.tsx": 1,
"src/components/AddContactModal/AddContactModal.tsx": 2,
"src/components/AdditionalFields/AdditionalFields.tsx": 2,
"src/components/Alert/Alert.tsx": 4,
Expand Down Expand Up @@ -62,12 +56,6 @@
"src/components/MediaEditor/ScriptPanel.tsx": 2,
"src/components/MediaEditor/SpeedMarkerMenu.tsx": 2,
"src/components/MedicationList/MedicationList.tsx": 1,
"src/components/Messaging/AttachmentPicker.tsx": 3,
"src/components/Messaging/ConversationHeader.tsx": 4,
"src/components/Messaging/MessageBubble.tsx": 7,
"src/components/Messaging/MessageComposer.tsx": 7,
"src/components/Messaging/MessageList.tsx": 4,
"src/components/Messaging/MessageThread.tsx": 2,
"src/components/OnboardingWizard/OnboardingWizard.tsx": 6,
"src/components/OrderCard/OrderCard.tsx": 2,
"src/components/OrderConfirmationWizard/OrderConfirmationWizard.tsx": 5,
Expand Down
41 changes: 30 additions & 11 deletions src/components/AI/AIChatModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,16 @@ export interface AIChatTriggerProps {
pulse?: boolean;
/** Badge count for notifications */
badge?: number;
/** Position of the button */
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
/** Position of the button. `start`/`end` aliases are direction-aware (RTL). */
position?:
| 'bottom-right'
| 'bottom-left'
| 'top-right'
| 'top-left'
| 'bottom-end'
| 'bottom-start'
| 'top-end'
| 'top-start';
/** Additional class name */
className?: string;
}
Expand All @@ -40,10 +48,14 @@ export function AIChatTrigger({
className,
}: AIChatTriggerProps) {
const positionClasses = {
'bottom-right': 'bottom-4 right-4',
'bottom-left': 'bottom-4 left-4',
'top-right': 'top-4 right-4',
'top-left': 'top-4 left-4',
'bottom-right': 'bottom-4 end-4',
'bottom-left': 'bottom-4 start-4',
'top-right': 'top-4 end-4',
'top-left': 'top-4 start-4',
'bottom-end': 'bottom-4 end-4',
'bottom-start': 'bottom-4 start-4',
'top-end': 'top-4 end-4',
'top-start': 'top-4 start-4',
};

return (
Expand Down Expand Up @@ -72,7 +84,7 @@ export function AIChatTrigger({
{badge && badge > 0 && (
<span
data-slot="ai-chat-trigger-badge"
className="absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs font-bold"
className="absolute -end-1 -top-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs font-bold"
>
{badge > 9 ? '9+' : badge}
</span>
Expand All @@ -96,8 +108,13 @@ export interface AIChatModalProps extends Omit<
open: boolean;
/** Callback when the modal should close */
onOpenChange: (open: boolean) => void;
/** Position of the modal */
position?: 'bottom-right' | 'bottom-left' | 'center';
/** Position of the modal. `start`/`end` aliases are direction-aware (RTL). */
position?:
| 'bottom-right'
| 'bottom-left'
| 'bottom-end'
| 'bottom-start'
| 'center';
/** Width of the modal */
width?: string | number;
/** Height of the modal */
Expand Down Expand Up @@ -129,8 +146,10 @@ export function AIChatModal({
if (!open) return null;

const positionClasses = {
'bottom-right': 'bottom-20 right-4',
'bottom-left': 'bottom-20 left-4',
'bottom-right': 'bottom-20 end-4',
'bottom-left': 'bottom-20 start-4',
'bottom-end': 'bottom-20 end-4',
'bottom-start': 'bottom-20 start-4',
center: 'top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2',
};

Expand Down
2 changes: 1 addition & 1 deletion src/components/AI/HeyOzwell/OzwellSettingsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function ItemLabel({ label, sub }: { label: string; sub: string }) {

/** DropdownItem's default-variant classes, reused by the custom collapsible row for visual parity. */
const itemClasses = cn(
'flex w-full items-center gap-3 rounded-lg px-3 py-2 text-left text-sm',
'flex w-full items-center gap-3 rounded-lg px-3 py-2 text-start text-sm',
'transition-colors duration-150 focus:outline-none',
'text-neutral-700 dark:text-neutral-300',
'hover:bg-neutral-100 dark:hover:bg-neutral-700',
Expand Down
4 changes: 2 additions & 2 deletions src/components/AI/HeyOzwell/VisitScribe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export function VisitScribe({
{recording ? (
<Button variant="danger" onClick={scribe.stop}>
<span
className="mr-2 inline-block h-2.5 w-2.5 animate-pulse rounded-full bg-white"
className="me-2 inline-block h-2.5 w-2.5 animate-pulse rounded-full bg-white"
aria-hidden="true"
/>
Stop · {mmss(elapsedMs)}
Expand Down Expand Up @@ -312,7 +312,7 @@ export function VisitScribe({
>
{recording && (
<span
className="mr-2 inline-block h-2 w-2 animate-pulse rounded-full bg-red-500 align-middle"
className="me-2 inline-block h-2 w-2 animate-pulse rounded-full bg-red-500 align-middle"
aria-hidden="true"
/>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/AI/HeyOzwell/VoiceManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function VoiceManager({ logoSrc }: VoiceManagerProps) {
{voices.map((v) => (
<div
key={v.id}
className="border-border bg-card flex items-center gap-3 rounded-2xl border px-4 py-3 text-left"
className="border-border bg-card flex items-center gap-3 rounded-2xl border px-4 py-3 text-start"
>
<div className="min-w-0 flex-1">
{editingId === v.id ? (
Expand Down
2 changes: 1 addition & 1 deletion src/components/AI/HeyOzwell/VoiceSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export function VoiceSetup({
<Button
variant="ghost"
size="sm"
className="text-muted-foreground absolute top-4 left-4"
className="text-muted-foreground absolute start-4 top-4"
onClick={() => {
oz.cancel();
onCancel();
Expand Down
2 changes: 1 addition & 1 deletion src/components/AI/Reconciliation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,7 @@ function ReconciliationProposalRow({
{proposal.label}
{proposal.required && (
<span
className="text-muted-foreground ml-1 text-xs font-normal"
className="text-muted-foreground ms-1 text-xs font-normal"
aria-label="required"
>
(required)
Expand Down
4 changes: 2 additions & 2 deletions src/components/Messaging/AttachmentPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ function AttachmentPreviewItem({
type="button"
onClick={onRemove}
className={cn(
'absolute -top-1 -right-1 z-10',
'absolute -end-1 -top-1 z-10',
'rounded-full p-1',
'bg-neutral-900 text-white',
'opacity-0 group-hover:opacity-100',
Expand Down Expand Up @@ -259,7 +259,7 @@ function AttachmentPreviewItem({
{/* File name tooltip on hover */}
<div
className={cn(
'absolute right-0 bottom-0 left-0',
'absolute start-0 end-0 bottom-0',
'bg-black/70 px-1 py-0.5',
'opacity-0 group-hover:opacity-100',
'transition-opacity'
Expand Down
8 changes: 4 additions & 4 deletions src/components/Messaging/ConversationHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const ConversationHeader = React.forwardRef<
type="button"
onClick={onBack}
className={cn(
'-ml-2 rounded-full p-2',
'-ms-2 rounded-full p-2',
'text-neutral-500 hover:text-neutral-700',
'dark:text-neutral-400 dark:hover:text-neutral-200',
'hover:bg-neutral-100 dark:hover:bg-neutral-800',
Expand Down Expand Up @@ -227,7 +227,7 @@ const ConversationHeader = React.forwardRef<
{showOnlineStatus && isOnline && (
<span
className={cn(
'absolute right-0 bottom-0',
'absolute end-0 bottom-0',
'h-3 w-3 rounded-full',
'bg-green-500 ring-2 ring-white dark:ring-neutral-900'
)}
Expand Down Expand Up @@ -334,7 +334,7 @@ const ConversationListItem = React.forwardRef<
data-slot="conversation-list-item"
className={cn(
'flex w-full items-center gap-3 px-4 py-3',
'text-left transition-colors',
'text-start transition-colors',
isSelected
? 'bg-primary-50 dark:bg-primary-900/20'
: 'hover:bg-neutral-50 dark:hover:bg-neutral-800/50',
Expand Down Expand Up @@ -368,7 +368,7 @@ const ConversationListItem = React.forwardRef<
{participant?.isOnline && (
<span
className={cn(
'absolute right-0 bottom-0',
'absolute end-0 bottom-0',
'h-3 w-3 rounded-full',
'bg-green-500 ring-2 ring-white dark:ring-neutral-900'
)}
Expand Down
15 changes: 9 additions & 6 deletions src/components/Messaging/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,10 @@ function ReadReceiptIndicator({

return (
<div
className={cn('flex items-center -space-x-1', className)}
className={cn(
'flex items-center -space-x-1 rtl:space-x-reverse',
className
)}
aria-label={`Read by ${receipts.map((r) => r.participant.name).join(', ')}`}
>
{visibleReceipts.map((receipt) => (
Expand Down Expand Up @@ -337,7 +340,7 @@ function AttachmentPreview({
/>
</svg>
</div>
<div className="min-w-0 flex-1 text-left">
<div className="min-w-0 flex-1 text-start">
<p className="truncate text-sm font-medium">{attachment.filename}</p>
<p className="text-xs opacity-70">{formatFileSize(attachment.size)}</p>
</div>
Expand Down Expand Up @@ -367,12 +370,12 @@ const bubbleVariants = cva(
{
variants: {
variant: {
outgoing: ['bg-primary-800 text-white', 'rounded-br-md', 'ml-auto'],
outgoing: ['bg-primary-800 text-white', 'rounded-ee-md', 'ms-auto'],
incoming: [
'bg-neutral-100 text-neutral-900',
'dark:bg-neutral-800 dark:text-neutral-100',
'rounded-bl-md',
'mr-auto',
'rounded-es-md',
'me-auto',
],
system: [
'mx-auto max-w-none',
Expand Down Expand Up @@ -598,7 +601,7 @@ const MessageBubble = React.forwardRef<HTMLDivElement, MessageBubbleProps>(

{/* Edited indicator */}
{message.isEdited && !message.isDeleted && (
<span className="ml-1 text-xs opacity-60">(edited)</span>
<span className="ms-1 text-xs opacity-60">(edited)</span>
)}
</div>

Expand Down
12 changes: 6 additions & 6 deletions src/components/Messaging/MessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ const MessageComposer = React.forwardRef<
className={cn(
'flex items-center gap-2 px-4 py-2',
'bg-neutral-50 dark:bg-neutral-800/50',
'border-primary-500 border-l-4'
'border-primary-500 border-s-4'
)}
>
<div className="min-w-0 flex-1">
Expand Down Expand Up @@ -755,7 +755,7 @@ const MessageComposer = React.forwardRef<
}}
onMouseEnter={() => setMentionHighlight(i)}
className={cn(
'flex w-full items-center gap-2 px-3 py-1.5 text-left text-sm',
'flex w-full items-center gap-2 px-3 py-1.5 text-start text-sm',
i === clampedMentionHighlight
? 'bg-primary-100 text-primary-900 dark:bg-primary-900/40 dark:text-primary-100'
: 'text-neutral-700 dark:text-neutral-200'
Expand All @@ -765,7 +765,7 @@ const MessageComposer = React.forwardRef<
<span className="min-w-0 flex-1 truncate">
<span className="font-medium">{option.label}</span>
{option.description && (
<span className="ml-1 text-xs text-neutral-400">
<span className="ms-1 text-xs text-neutral-400">
{option.description}
</span>
)}
Expand Down Expand Up @@ -803,7 +803,7 @@ const MessageComposer = React.forwardRef<
rows={1}
className={cn(
'w-full resize-none rounded-2xl py-2.5',
inputTrailing ? 'pr-10 pl-4' : 'px-4',
inputTrailing ? 'ps-4 pe-10' : 'px-4',
'bg-neutral-100 dark:bg-neutral-800',
'text-neutral-900 dark:text-neutral-100',
'placeholder:text-neutral-400 dark:placeholder:text-neutral-500',
Expand All @@ -829,7 +829,7 @@ const MessageComposer = React.forwardRef<
{inputTrailing && (
<div
data-slot="composer-input-trailing"
className="pointer-events-none absolute top-0 right-1 flex h-[44px] items-center [&>*]:pointer-events-auto"
className="pointer-events-none absolute end-1 top-0 flex h-[44px] items-center [&>*]:pointer-events-auto"
>
{inputTrailing}
</div>
Expand All @@ -840,7 +840,7 @@ const MessageComposer = React.forwardRef<
<div
data-slot="composer-char-count"
id="char-count"
className="absolute right-3 bottom-1.5"
className="absolute end-3 bottom-1.5"
>
<CharacterCounter current={content.length} max={maxLength} />
</div>
Expand Down
8 changes: 4 additions & 4 deletions src/components/Messaging/MessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,8 @@ function SkeletonMessage({
className={cn(
'animate-pulse rounded-2xl',
isOutgoing
? 'bg-primary-800/30 rounded-br-md'
: 'rounded-bl-md bg-neutral-200 dark:bg-neutral-700',
? 'bg-primary-800/30 rounded-ee-md'
: 'rounded-es-md bg-neutral-200 dark:bg-neutral-700',
'h-10 w-48'
)}
/>
Expand Down Expand Up @@ -176,7 +176,7 @@ function TypingIndicator({ typingState, className }: TypingIndicatorProps) {
aria-label={typingText}
>
{/* Typing bubble animation */}
<div className="flex items-center gap-1 rounded-2xl rounded-bl-md bg-neutral-200 px-4 py-3 dark:bg-neutral-700">
<div className="flex items-center gap-1 rounded-2xl rounded-es-md bg-neutral-200 px-4 py-3 dark:bg-neutral-700">
<span
className="h-2 w-2 animate-bounce rounded-full bg-neutral-500"
style={{ animationDelay: '0ms' }}
Expand Down Expand Up @@ -619,7 +619,7 @@ const MessageList = React.forwardRef<HTMLDivElement, MessageListProps>(
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
}}
className={cn(
'fixed right-4 bottom-24 z-10',
'fixed end-4 bottom-24 z-10',
'rounded-full p-3 shadow-lg',
'bg-white dark:bg-neutral-800',
'border border-neutral-200 dark:border-neutral-700',
Expand Down
4 changes: 2 additions & 2 deletions src/components/Messaging/MessageThread.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function LightboxModal({ attachment, onClose }: LightboxModalProps) {
type="button"
onClick={onClose}
className={cn(
'absolute top-4 right-4 z-10',
'absolute end-4 top-4 z-10',
'rounded-full p-2',
'bg-white/10 text-white',
'hover:bg-white/20',
Expand Down Expand Up @@ -436,7 +436,7 @@ function MessagingSplitView({
data-slot="messaging-list-pane"
className={cn(
'h-full w-full flex-shrink-0',
'border-r border-neutral-200 dark:border-neutral-700',
'border-e border-neutral-200 dark:border-neutral-700',
hideMobileClasses[mobileBreakpoint]
)}
style={{
Expand Down
12 changes: 12 additions & 0 deletions src/tailwind-preset.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ module.exports = {
'translate-x-0',
'-translate-x-full',
'[&:has([role=checkbox])]:pe-0',
// RTL messaging+AI batch: bubble tails, composer borders/paddings, corner
// pins (AIChatModal/AttachmentPicker/MessageList), read-receipt avatar stack
'end-1',
'end-3',
'end-4',
'start-4',
'me-2',
'pe-10',
'border-s-4',
'rounded-ee-md',
'rounded-es-md',
'rtl:space-x-reverse',
// MediaEditor media-surface height cap: the small-screen `dvh` cap, and the
// md variants that hand desktop back its original percentage cap. Purging
// these uncaps the player on a phone, which is the bug the cap exists for.
Expand Down
12 changes: 12 additions & 0 deletions src/tailwind-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,18 @@ export const miewebUISafelist = [
'translate-x-0',
'-translate-x-full',
'[&:has([role=checkbox])]:pe-0',
// RTL messaging+AI batch: bubble tails, composer borders/paddings, corner
// pins (AIChatModal/AttachmentPicker/MessageList), read-receipt avatar stack
'end-1',
'end-3',
'end-4',
'start-4',
'me-2',
'pe-10',
'border-s-4',
'rounded-ee-md',
'rounded-es-md',
'rtl:space-x-reverse',
// SchedulePicker / overflow handling
'overflow-x-auto',
'overflow-hidden',
Expand Down
Loading