magnaui 0.1.0 → 0.2.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 (138) hide show
  1. package/LICENSES/HextaUI-MIT.txt +21 -0
  2. package/README.md +154 -11
  3. package/THIRD_PARTY_NOTICES.md +14 -0
  4. package/dist/components/interactions/border-beam.js +1 -1
  5. package/dist/components/interactions/image-generation.js +6 -5
  6. package/dist/components/interactions/liquid.js +1 -1
  7. package/dist/components/interactions/metal-fx.js +6 -5
  8. package/dist/components/interactions/voice-beam.js +1 -1
  9. package/dist/components/magna/app-icon.js +3 -2
  10. package/dist/components/magna/chat-activity.js +5 -4
  11. package/dist/components/magna/chat-attachment.d.ts +14 -0
  12. package/dist/components/magna/chat-attachment.js +35 -0
  13. package/dist/components/magna/chat-composer.d.ts +7 -0
  14. package/dist/components/magna/chat-composer.js +30 -12
  15. package/dist/components/magna/chat-composer.module.css +7 -14
  16. package/dist/components/magna/chat-conversation.d.ts +19 -0
  17. package/dist/components/magna/chat-conversation.js +158 -0
  18. package/dist/components/magna/chat-message.d.ts +17 -0
  19. package/dist/components/magna/chat-message.js +76 -0
  20. package/dist/components/magna/chat-response.d.ts +50 -0
  21. package/dist/components/magna/chat-response.js +64 -0
  22. package/dist/components/magna/desktop-button.js +5 -4
  23. package/dist/components/magna/desktop-switch.js +2 -1
  24. package/dist/components/magna/file-tile.js +3 -2
  25. package/dist/components/magna/index.d.ts +4 -0
  26. package/dist/components/magna/index.js +4 -0
  27. package/dist/components/magna/logo.js +6 -5
  28. package/dist/components/magna/navigation.js +2 -1
  29. package/dist/components/magna/project-context.js +2 -1
  30. package/dist/components/magna/voice-orb.js +4 -3
  31. package/dist/components/magna/voice-orb.module.css +25 -44
  32. package/dist/components/theme-switch.js +2 -1
  33. package/dist/components/ui/accordion.js +6 -5
  34. package/dist/components/ui/activity-feed.js +9 -8
  35. package/dist/components/ui/alert.d.ts +1 -1
  36. package/dist/components/ui/alert.js +6 -5
  37. package/dist/components/ui/avatar-empty-icons.js +3 -2
  38. package/dist/components/ui/avatar-group-compact.js +7 -6
  39. package/dist/components/ui/avatar-group.js +5 -4
  40. package/dist/components/ui/avatar.d.ts +1 -1
  41. package/dist/components/ui/avatar.js +18 -17
  42. package/dist/components/ui/badge.d.ts +1 -1
  43. package/dist/components/ui/badge.js +6 -5
  44. package/dist/components/ui/banner.js +3 -2
  45. package/dist/components/ui/breadcrumb.js +6 -5
  46. package/dist/components/ui/button-group.d.ts +1 -1
  47. package/dist/components/ui/button-group.js +8 -7
  48. package/dist/components/ui/button.d.ts +1 -1
  49. package/dist/components/ui/button.js +5 -4
  50. package/dist/components/ui/chart-legend.js +3 -2
  51. package/dist/components/ui/checkbox.js +8 -7
  52. package/dist/components/ui/color-picker.js +13 -9
  53. package/dist/components/ui/command-menu.d.ts +1 -1
  54. package/dist/components/ui/command-menu.js +9 -8
  55. package/dist/components/ui/compact-button.d.ts +1 -1
  56. package/dist/components/ui/compact-button.js +5 -4
  57. package/dist/components/ui/content-card.js +2 -1
  58. package/dist/components/ui/content-label.js +2 -1
  59. package/dist/components/ui/counter-input.js +5 -4
  60. package/dist/components/ui/datepicker.d.ts +20 -2
  61. package/dist/components/ui/datepicker.js +121 -34
  62. package/dist/components/ui/digit-input.d.ts +14 -2
  63. package/dist/components/ui/digit-input.js +10 -4
  64. package/dist/components/ui/direction-provider.d.ts +19 -0
  65. package/dist/components/ui/direction-provider.js +41 -0
  66. package/dist/components/ui/display-asset.js +2 -1
  67. package/dist/components/ui/divider.js +7 -5
  68. package/dist/components/ui/dot-stepper.js +3 -2
  69. package/dist/components/ui/drawer.js +8 -5
  70. package/dist/components/ui/dropdown.js +16 -12
  71. package/dist/components/ui/fancy-button.d.ts +1 -1
  72. package/dist/components/ui/fancy-button.js +5 -4
  73. package/dist/components/ui/file-format-icon.js +4 -3
  74. package/dist/components/ui/file-upload-compositions.js +7 -6
  75. package/dist/components/ui/file-upload.js +4 -3
  76. package/dist/components/ui/filter.js +11 -10
  77. package/dist/components/ui/form-asset.js +3 -2
  78. package/dist/components/ui/hint.d.ts +1 -1
  79. package/dist/components/ui/hint.js +5 -4
  80. package/dist/components/ui/horizontal-stepper.js +6 -5
  81. package/dist/components/ui/inline-input.js +4 -3
  82. package/dist/components/ui/input.js +8 -7
  83. package/dist/components/ui/kbd.js +2 -1
  84. package/dist/components/ui/key-icon.js +3 -2
  85. package/dist/components/ui/label.js +4 -3
  86. package/dist/components/ui/link-button.d.ts +1 -1
  87. package/dist/components/ui/link-button.js +5 -4
  88. package/dist/components/ui/magna-recipe.js +3 -1
  89. package/dist/components/ui/modal.js +11 -8
  90. package/dist/components/ui/notification-feed.js +8 -7
  91. package/dist/components/ui/notification.d.ts +1 -1
  92. package/dist/components/ui/notification.js +10 -3
  93. package/dist/components/ui/pagination.js +10 -9
  94. package/dist/components/ui/password-strength.js +3 -2
  95. package/dist/components/ui/payment-icon.js +2 -1
  96. package/dist/components/ui/popover.js +7 -4
  97. package/dist/components/ui/progress-bar.js +7 -6
  98. package/dist/components/ui/progress-circle.js +5 -4
  99. package/dist/components/ui/radio.js +7 -6
  100. package/dist/components/ui/rating.js +11 -10
  101. package/dist/components/ui/rich-editor.js +17 -16
  102. package/dist/components/ui/rich-editor.module.css +17 -28
  103. package/dist/components/ui/scroll-area.js +6 -5
  104. package/dist/components/ui/segmented-control.js +4 -3
  105. package/dist/components/ui/select.js +29 -23
  106. package/dist/components/ui/slider.js +3 -2
  107. package/dist/components/ui/social-button.d.ts +1 -1
  108. package/dist/components/ui/social-button.js +5 -4
  109. package/dist/components/ui/status-badge.d.ts +1 -1
  110. package/dist/components/ui/status-badge.js +6 -5
  111. package/dist/components/ui/status-modal.js +3 -2
  112. package/dist/components/ui/svg-rating-icons.js +11 -10
  113. package/dist/components/ui/switch-compositions.d.ts +1 -1
  114. package/dist/components/ui/switch-compositions.js +10 -9
  115. package/dist/components/ui/switch.js +4 -3
  116. package/dist/components/ui/tab-menu-horizontal.js +19 -10
  117. package/dist/components/ui/tab-menu-vertical.js +5 -4
  118. package/dist/components/ui/table.js +10 -9
  119. package/dist/components/ui/tag-input.js +5 -4
  120. package/dist/components/ui/tag.d.ts +1 -1
  121. package/dist/components/ui/tag.js +8 -7
  122. package/dist/components/ui/textarea.js +7 -7
  123. package/dist/components/ui/time-picker.js +9 -8
  124. package/dist/components/ui/tooltip.js +4 -1
  125. package/dist/components/ui/vertical-stepper.js +7 -6
  126. package/dist/design/theme.js +9 -9
  127. package/dist/fonts/README.md +6 -1
  128. package/dist/hooks/use-tab-observer.js +9 -0
  129. package/dist/magna-classes.js +47 -0
  130. package/dist/magnaui.d.ts +4 -0
  131. package/dist/magnaui.js +2 -0
  132. package/dist/styles.css +1208 -2441
  133. package/dist/utils/cn.js +2 -1
  134. package/dist/utils/magna-theme.d.ts +20 -0
  135. package/dist/utils/magna-theme.js +42 -0
  136. package/dist/utils/tv.js +3 -2
  137. package/docs/implementation-provenance.md +37 -7
  138. package/package.json +24 -22
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Preet Suthar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2,22 +2,27 @@
2
2
 
3
3
  React components, theme tokens, and original Magna artwork for authorized Magna AI applications.
4
4
 
5
- **Usage terms:** Public package visibility permits downloading the distribution; it does not grant an open-source license to Magna-owned materials. Use is restricted to the owner and expressly authorized users under [LICENSE](LICENSE). Third-party materials retain their own terms in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
5
+ MagnaUI `0.2.0` includes AI conversation components, React 18.3 and React 19 support, RTL direction support, and isolated styles for Tailwind 4 applications.
6
+
7
+ **Usage terms:** Public package visibility permits downloading the distribution; it does not grant an open-source license to Magna-owned materials. Use is restricted to the owner and expressly authorized users under `LICENSE` in the installed package. Third-party materials retain their own terms in `THIRD_PARTY_NOTICES.md`.
6
8
 
7
9
  ## Install
8
10
 
9
11
  ```sh
10
- npm install magnaui react@^18.3.1 react-dom@^18.3.1
12
+ npm install magnaui@^0.2.0
11
13
  ```
12
14
 
13
- The package ships ESM JavaScript, TypeScript declarations, compiled CSS, fonts, and artwork. Use a modern React bundler such as Next.js or Vite that supports CSS Modules and static `new URL(..., import.meta.url)` assets. React 18.3 is the supported peer version. The package does not require Next.js or a Tailwind build in your application.
15
+ Use matching `react` and `react-dom` versions: `^18.3.1` or `^19.0.0`. Next.js 15 applications can keep React 19. The package ships ESM JavaScript, TypeScript declarations, compiled CSS, fonts, and artwork. Use Next.js, Vite, or another modern bundler that supports CSS Modules and static package assets. Next.js itself is not a library dependency.
14
16
 
15
- Import the styles once at your application entry point. In Next.js App Router, import them from the root layout.
17
+ Import the styles once in your application entry point. In Next.js App Router, import them from the root layout:
16
18
 
17
19
  ```tsx
18
20
  import 'magnaui/styles.css';
21
+ import './globals.css'; // Include your Tailwind 4 setup here, if used.
19
22
  ```
20
23
 
24
+ No Tailwind plugin, `@source` scan, or Tailwind 3 configuration is required for a package install. MagnaUI namespaces its compiled component classes, places styles in the `components.magnaui` cascade layer, and limits its reset to library elements. Normal Tailwind utility overrides remain above component defaults even if the bundler changes stylesheet order. Your application owns additional layout utilities and styles. Interactive MagnaUI modules preserve their `"use client"` boundary; add `"use client"` to your own component when using hooks or event handlers.
25
+
21
26
  ```tsx
22
27
  'use client';
23
28
 
@@ -25,9 +30,9 @@ import { Button, Checkbox, MagnaLogo } from 'magnaui';
25
30
 
26
31
  export function Workspace() {
27
32
  return (
28
- <main>
29
- <MagnaLogo variant="horizontal" label="Magna" />
30
- <Checkbox.Label label="Send workspace updates" defaultChecked />
33
+ <main style={{ display: 'grid', gap: 16, padding: 24 }}>
34
+ <MagnaLogo variant='horizontal' label='Magna' />
35
+ <Checkbox.Label label='Send workspace updates' defaultChecked />
31
36
  <Button.Root onClick={() => console.log('Create workspace')}>
32
37
  Create workspace
33
38
  </Button.Root>
@@ -36,7 +41,7 @@ export function Workspace() {
36
41
  }
37
42
  ```
38
43
 
39
- The stylesheet includes the existing reset, semantic utilities, Inter, DM Mono, and Magna themes. Artwork URLs are resolved from the installed package by your bundler; there is no public-folder copy step or CDN dependency. CSS Modules remain next to their component files for the consuming bundler to process.
44
+ The stylesheet includes scoped component styles, Inter, DM Mono, and Magna themes. Thmanyah font binaries are supplied by your application. Artwork URLs are resolved from the installed package by your bundler; there is no public-folder copy step or CDN dependency. CSS Modules remain next to their component files for the consuming bundler to process.
40
45
 
41
46
  For a narrower import, component modules also have subpaths:
42
47
 
@@ -46,6 +51,77 @@ import { MagnaLogo } from 'magnaui/magna';
46
51
  import { BorderBeam } from 'magnaui/interactions';
47
52
  ```
48
53
 
54
+ ## Included components
55
+
56
+ MagnaUI `0.2.0` includes **68 base component families**, **10 Magna workspace components**, **7 interaction adapters**, and **7 AI conversation components**, with their public component parts and types.
57
+
58
+ | Group | Public component families |
59
+ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
60
+ | Actions | `Button`, `ButtonGroup`, `CompactButton`, `FancyButton`, `LinkButton`, `SocialButton` |
61
+ | Forms and input | `Checkbox`, `ColorPicker`, `CounterInput`, `DatePicker`, `DigitInput`, `FileUpload`, `Filter`, `Hint`, `InlineInput`, `Input`, `Label`, `PasswordStrength`, `Radio`, `Rating`, `RichEditor`, `Select`, `Slider`, `Switch`, `TagInput`, `Textarea`, `TimePicker` |
62
+ | Navigation | `Accordion`, `Breadcrumb`, `CommandMenu`, `DotStepper`, `HorizontalStepper`, `Pagination`, `SegmentedControl`, `TabMenuHorizontal`, `TabMenuVertical`, `VerticalStepper` |
63
+ | Feedback | `Alert`, `Banner`, `Notification`, `NotificationFeed`, `ProgressBar`, `ProgressCircle`, `StatusBadge`, `Tooltip` |
64
+ | Overlays | `Drawer`, `Dropdown`, `Modal`, `Popover`, `StatusModal` |
65
+ | Data and content | `ActivityFeed`, `ChartLegend`, `ContentCard`, `ContentLabel`, `Divider`, `Kbd`, `ScrollArea`, `Table`, `Tag` |
66
+ | Identity and icons | `Avatar`, `AvatarEmptyIcons`, `AvatarGroup`, `AvatarGroupCompact`, `Badge`, `FileFormatIcon`, `KeyIcon`, `PaymentIcon`, `RatingIcons` |
67
+
68
+ - **Magna workspace:** `AppIcon`, `AppLauncher`, `MagnaLogo`, `ChatComposer`, `ChatActivity`, `DesktopButton`, `DesktopSwitch`, `FileTile`, `MagnaNavigation`, and `ProjectContext`.
69
+ - **AI conversations:** `ChatMessage`, `ChatConversation`, `ChatAttachment`, `ChatReasoning`, `ChatToolCall`, `ChatSources`, and `ChatSuggestions`.
70
+ - **Interactions:** `BorderBeam`, `ThinkingOrb`, `BotAvatar`, `Liquid`, `VoiceBeam`, `MetalFx`, and `ImageGeneration`.
71
+ - **Providers and helpers:** `ThemeProvider`, `useTheme`, `ThemeSwitch`, `TooltipProvider`, `NotificationProvider`, `notification`, `useNotification`, `useTabObserver`, `DirectionProvider`, `useDirection`, `createMagnaTheme`, and application, navigation, and logo metadata.
72
+ - **Styles and assets:** compiled component CSS, light/dark themes, five preset accents, custom brand colors, two neutral palettes, 265 artwork files (including 40 application icons and 24 logo variants), and 11 self-hosted font files.
73
+
74
+ Compound parts such as `Checkbox.Card`, `Switch.Integration`, `RichEditor.Toolbar`, and `FileUpload.Root` are included. Internal render helpers are bundled with the components that use them. The documentation app, gallery demos, and full Figma product screens are separate from the reusable package. Package coverage confirms availability; it does not certify every Figma variant visually.
75
+
76
+ ## Compose an AI conversation
77
+
78
+ Use the new components with the existing `ChatComposer` and `ChatActivity`. `ChatMessage` supplies streaming, error, copy, retry, and feedback states; `ChatAttachment` displays upload progress and file actions. Add `ChatToolCall` for explicit approval and results, `ChatSources` for numbered citations, and `ChatReasoning` for app-provided progress summaries.
79
+
80
+ ```tsx
81
+ 'use client';
82
+
83
+ import { useState } from 'react';
84
+ import {
85
+ ChatComposer,
86
+ ChatConversation,
87
+ ChatMessage,
88
+ ChatSuggestions,
89
+ } from 'magnaui';
90
+
91
+ export function Conversation() {
92
+ const [draft, setDraft] = useState('');
93
+ const [messages, setMessages] = useState<string[]>([]);
94
+
95
+ return (
96
+ <div style={{ display: 'grid', gap: 16, maxWidth: 700 }}>
97
+ <ChatConversation emptyState={<p>What would you like to work on?</p>}>
98
+ {messages.map((message, index) => (
99
+ <ChatMessage key={index} role='user'>
100
+ {message}
101
+ </ChatMessage>
102
+ ))}
103
+ </ChatConversation>
104
+ <ChatSuggestions
105
+ suggestions={['Summarize a document', 'Plan the next steps']}
106
+ onSelect={setDraft}
107
+ />
108
+ <ChatComposer
109
+ value={draft}
110
+ onValueChange={setDraft}
111
+ onSend={(message) => {
112
+ setMessages((current) => [...current, message]);
113
+ setDraft('');
114
+ }}
115
+ />
116
+ </div>
117
+ );
118
+ }
119
+ ```
120
+
121
+ This example stores messages locally. Your application provides responses, streaming, file uploads, tool authorization, and audio services. Response steps are public progress summaries supplied by your app; `ChatReasoning` does not retrieve hidden model reasoning. Place `ChatActivity` in the conversation so the composer keeps its stable text and layout across thinking, recording, and voice modes.
122
+
123
+ Run the repository documentation and open [AI Chat](http://localhost:3000/docs#ai-chat) for the integrated example, controls, and API references. The seven new components also export their named `Props` types; tool status and citations use `ChatToolCallStatus` and `ChatSource`.
124
+
49
125
  ## Themes and providers
50
126
 
51
127
  Wrap theme controls with `ThemeProvider`, tooltips with `TooltipProvider`, and mount the notification host once when using notifications. Ordinary buttons and fields do not need a provider.
@@ -58,7 +134,7 @@ import type { ReactNode } from 'react';
58
134
 
59
135
  export function Providers({ children }: { children: ReactNode }) {
60
136
  return (
61
- <ThemeProvider attribute="class">
137
+ <ThemeProvider attribute='class'>
62
138
  <TooltipProvider>{children}</TooltipProvider>
63
139
  <NotificationProvider />
64
140
  </ThemeProvider>
@@ -66,8 +142,75 @@ export function Providers({ children }: { children: ReactNode }) {
66
142
  }
67
143
  ```
68
144
 
69
- The `dark` class selects dark mode. `data-primary` and `data-neutral` on the document element select palettes. Keep those attributes on the document element so portaled controls inherit the theme. Font families can be overridden through `--font-sans` and `--font-dm-mono`.
145
+ The `dark` class selects dark mode. `data-accent` and `data-neutral` on the document element select palettes. Keep those attributes on the document element so portaled controls inherit the theme. Override the Latin and Arabic families with `--magna-font-sans` and `--magna-font-arabic`; `--magna-font-dm-mono` controls monospace text in the npm package. Existing direct token overrides must use the `--magna-` prefix; source-checkout CSS retains its original token names.
70
146
 
71
147
  Components keep their existing API: `Button.Root`, `Input.Input`, `Checkbox.Label`, and the other compound members. Your application supplies persistence, uploads, chat, and voice services through the component callbacks.
72
148
 
73
- The documentation and full source are maintained in the MagnaUI repository. Registry visibility does not change access to that repository.
149
+ The documentation and full source are maintained in the [company MagnaUI repository](https://github.com/trend-mgai/magnaui). Registry visibility does not change access to that repository.
150
+
151
+ ## Arabic and RTL
152
+
153
+ Wrap localized UI with `DirectionProvider`. It applies `dir` to the DOM and Radix context, keeping arrow-key navigation, slider progression, and layout consistent. `lang` also follows MagnaUI menus, dialogs, popovers, tooltips, and notifications across portals.
154
+
155
+ ```tsx
156
+ 'use client';
157
+
158
+ import { DirectionProvider, Button } from 'magnaui';
159
+
160
+ export function ArabicWorkspace() {
161
+ return (
162
+ <DirectionProvider dir='rtl' lang='ar'>
163
+ <Button.Root>إنشاء مساحة عمل</Button.Root>
164
+ </DirectionProvider>
165
+ );
166
+ }
167
+ ```
168
+
169
+ For a fully Arabic application, also set `<html dir="rtl" lang="ar">`. For bilingual sections, nest another provider with its own `dir` and `lang`. `asChild` applies those attributes to an existing element instead of adding a wrapper. OTP codes keep left-to-right character order intentionally; chat inherits its surrounding direction unless you pass an explicit `dir`.
170
+
171
+ Supply Thmanyah Sans through your application’s authorized font files. This example assumes that **your application** places a variable WOFF2 font at the given path; adjust its weight declaration to match your actual file:
172
+
173
+ ```css
174
+ @font-face {
175
+ font-family: 'Thmanyah Sans';
176
+ src: url('/fonts/ThmanyahSans.woff2') format('woff2');
177
+ font-weight: 100 900;
178
+ font-style: normal;
179
+ font-display: swap;
180
+ }
181
+
182
+ :root {
183
+ --magna-font-sans: 'Magna Inter', sans-serif;
184
+ --magna-font-arabic: 'Thmanyah Sans', sans-serif;
185
+ }
186
+ ```
187
+
188
+ Alternatively use `next/font/local` and assign its family variable to `--magna-font-arabic`. Arabic `lang` selects this family; direction alone does not identify a language. MagnaUI includes Inter and DM Mono, but does not bundle or download Thmanyah font files.
189
+
190
+ ## Custom brand colors
191
+
192
+ `createMagnaTheme` accepts a `primary` color in `#RGB` or `#RRGGBB` format, optional `darkPrimary` in the same format, `fontSans`, and `fontArabic`. Apply its style object and `data-accent="custom"` to the document root so portaled components share the palette:
193
+
194
+ ```tsx
195
+ import { createMagnaTheme } from 'magnaui';
196
+
197
+ const magnaTheme = createMagnaTheme({
198
+ primary: '#8a1538',
199
+ darkPrimary: '#b54162',
200
+ fontSans: '"Magna Inter", sans-serif',
201
+ fontArabic: '"Thmanyah Sans", sans-serif',
202
+ });
203
+
204
+ <html lang='ar' dir='rtl' data-accent='custom' style={magnaTheme}>
205
+ <body>{children}</body>
206
+ </html>;
207
+ ```
208
+
209
+ The helper derives light/dark semantic accent values and focus treatments while preserving Magna geometry. `darkPrimary` defaults to `primary` when omitted. Filled controls retain white text, so choose accents with sufficient white-text contrast and review both modes. Keep `ThemeProvider attribute="class"` when switching modes. Use your app’s CSS or Tailwind utilities for custom layouts, and semantic CSS variables such as `var(--magna-primary-base)` for custom colored surfaces; the package’s compiled utility names are internal.
210
+
211
+ ## Upgrading from 0.1.x
212
+
213
+ - Keep the `magnaui/styles.css` import alongside your application stylesheet. The nested `components.magnaui` layer preserves the priority of normal Tailwind utilities independently of stylesheet order. MagnaUI no longer supplies global resets or generic utility classes to unrelated application elements. Add any layout/reset your application previously relied on.
214
+ - React and React DOM must use matching major versions. Both React 18.3 and React 19 are supported; do not suppress peer dependency errors with `--legacy-peer-deps`.
215
+ - The calendar now uses React DayPicker 9. Standard selection and range props remain available. Advanced custom renderers should use DayPicker 9 slot signatures, including `Chevron`. Legacy `IconLeft`, `IconRight`, and `IconDropdown` adapters and legacy class-name mappings remain available. Set `classNamesVersion={9}` when using the v9 `day` cell class; a lone `day` override retains the v8 day-button interpretation unless this is set. `classNamesVersion={8}` explicitly requests legacy mapping.
216
+ - Apply `DirectionProvider dir="rtl" lang="ar"` for Arabic keyboard behavior and fonts. Load your authorized Thmanyah font through the consuming application.
@@ -70,3 +70,17 @@ The RichEditor component uses Tiptap 3.31.3 and its ProseMirror dependencies. Th
70
70
  Tiptap packages retain their [MIT license](LICENSES/Tiptap-MIT.txt), copyright
71
71
  (c) 2025 Tiptap GmbH. Dependency notices remain in their distributed packages.
72
72
  MagnaUI supplies the Figma toolbar presentation, public API, and documentation.
73
+
74
+ ## HextaUI conversation patterns
75
+
76
+ MagnaUI's chat message, attachment, and conversation components adapt patterns
77
+ from [HextaUI](https://github.com/preetsuthar17/hextaui) by Preet Suthar,
78
+ revision `c4e4caf60ea920f1c7759dda7ec77d54e42da065`. The referenced modules are
79
+ `components/ui/message.tsx`, `bubble.tsx`, `attachment.tsx`, and
80
+ `message-scroller.tsx`. Their [complete MIT license](LICENSES/HextaUI-MIT.txt),
81
+ copyright (c) 2026 Preet Suthar, is retained.
82
+
83
+ The MagnaUI adaptation uses Magna tokens, React 18, existing Magna primitives,
84
+ and native browser scrolling. It does not require HextaUI's application, Base UI,
85
+ or its shadcn message-scroller dependency. Response details, sources, suggestions,
86
+ and local examples are integrated into the MagnaUI public API.
@@ -10,6 +10,6 @@ export const BorderBeam = React.forwardRef(function BorderBeam({ theme = 'auto',
10
10
  React.useEffect(() => setMounted(true), []);
11
11
  // The upstream style element must mount in the browser: SSR escapes its CSS text.
12
12
  if (!mounted)
13
- return _jsx("div", { ...props, ref: ref });
13
+ return _jsx("div", { ...props, ref: ref, "data-magnaui": '' });
14
14
  return (_jsx(Primitive, { ...props, ref: ref, theme: theme === 'auto' ? preferences.theme : theme, active: active && !preferences.reducedMotion, size: size, colorVariant: colorVariant, staticColors: staticColors, duration: duration, borderRadius: borderRadius, brightness: brightness, saturation: saturation, glowSize: glowSize, hueRange: hueRange, strength: strength, css: css, onActivate: onActivate, onDeactivate: onDeactivate }));
15
15
  });
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { magnaClasses as __magnaClasses_1 } from "../../magna-classes.js";
3
4
  import * as React from "react";
4
5
  import { useInteractionPreferences } from "./preferences.js";
5
6
  const ImageGenerationPrimitive = React.lazy(async () => ({
@@ -68,19 +69,19 @@ export const ImageGeneration = React.forwardRef(function ImageGeneration({ child
68
69
  const firstImage = typeof props.images === 'string' ? props.images : props.images?.[0];
69
70
  const htmlProps = Object.fromEntries(Object.entries(props).filter(([key]) => !effectKeys.has(key)));
70
71
  const resolvedTheme = theme === 'auto' ? preferences.theme : theme;
71
- const staticContent = (_jsx("div", { ...htmlProps, ref: fallbackRef, className: className, "data-image-generation-fallback": '', "data-theme": resolvedTheme, style: {
72
+ const staticContent = (_jsx("div", { ...htmlProps, ref: fallbackRef, className: __magnaClasses_1(className, className), "data-image-generation-fallback": '', "data-theme": resolvedTheme, style: {
72
73
  position: 'relative',
73
74
  display: 'inline-block',
74
75
  overflow: 'hidden',
75
76
  verticalAlign: 'top',
76
- background: props.cardBg ?? 'var(--bg-weak-50)',
77
+ background: props.cardBg ?? "var(--magna-bg-weak-50)",
77
78
  borderRadius: props.borderRadius ?? 20,
78
79
  ...style,
79
- }, children: fallback ?? (_jsxs(_Fragment, { children: [children, firstImage && failedImage !== firstImage && (
80
+ }, "data-magnaui": '', children: fallback ?? (_jsxs(_Fragment, { children: [children, firstImage && failedImage !== firstImage && (
80
81
  // Keep a native image: these are caller-owned local or data URLs.
81
82
  // eslint-disable-next-line @next/next/no-img-element
82
- _jsx("img", { src: firstImage, alt: fallbackAlt, className: 'pointer-events-none absolute inset-0 size-full object-cover', onError: () => setFailedImage(firstImage) }))] })) }));
83
+ _jsx("img", { src: firstImage, alt: fallbackAlt, className: __magnaClasses_1('pointer-events-none absolute inset-0 size-full object-cover'), onError: () => setFailedImage(firstImage), "data-magnaui": '' }))] })) }));
83
84
  if (!supported || preferences.reducedMotion)
84
85
  return staticContent;
85
- return (_jsx(ImageEffectBoundary, { fallback: staticContent, children: _jsx(React.Suspense, { fallback: staticContent, children: _jsx(ImageGenerationPrimitive, { ...props, ref: effectRef, theme: resolvedTheme, className: className, cardBg: props.cardBg ?? (resolvedTheme === 'dark' ? '#171717' : '#ffffff'), style: style, children: children }) }) }, props.preset));
86
+ return (_jsx(ImageEffectBoundary, { fallback: staticContent, children: _jsx(React.Suspense, { fallback: staticContent, children: _jsx(ImageGenerationPrimitive, { ...props, ref: effectRef, theme: resolvedTheme, className: __magnaClasses_1(className, className), cardBg: props.cardBg ?? (resolvedTheme === 'dark' ? '#171717' : '#ffffff'), style: style, "data-magnaui": '', children: children }) }) }, props.preset));
86
87
  });
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { Liquid as LiquidPrimitive, } from "liquid-gooey";
5
5
  import { useInteractionPreferences } from "./preferences.js";
6
- const LiquidRoot = React.forwardRef(function Liquid({ fill = 'var(--primary-base)', waviness, ...props }, ref) {
6
+ const LiquidRoot = React.forwardRef(function Liquid({ fill = "var(--magna-primary-base)", waviness, ...props }, ref) {
7
7
  const { reducedMotion } = useInteractionPreferences();
8
8
  return (_jsx(LiquidPrimitive, { ...props, ref: ref, fill: fill, waviness: reducedMotion ? 0 : waviness }));
9
9
  });
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { magnaClasses as __magnaClasses_1 } from "../../magna-classes.js";
3
4
  import * as React from "react";
4
5
  import { useInteractionPreferences } from "./preferences.js";
5
6
  const MetalFxPrimitive = React.lazy(async () => ({
@@ -38,14 +39,14 @@ export const MetalFx = React.forwardRef(function MetalFx({ children, theme = 'au
38
39
  const [mounted, setMounted] = React.useState(false);
39
40
  React.useEffect(() => setMounted(true), []);
40
41
  const htmlProps = Object.fromEntries(Object.entries(props).filter(([key]) => !effectKeys.has(key)));
41
- const staticContent = (_jsx("div", { ...htmlProps, ref: ref, className: className, style: { display: 'inline-flex', ...style }, children: children }));
42
+ const staticContent = (_jsx("div", { ...htmlProps, ref: ref, className: __magnaClasses_1(className, className), style: { display: 'inline-flex', ...style }, "data-magnaui": '', children: children }));
42
43
  // The upstream capability probe caches its answer. Mount it only in the
43
44
  // browser, so server rendering cannot cache an unsupported result.
44
45
  if (!mounted)
45
46
  return staticContent;
46
- return (_jsx(MetalEffectBoundary, { fallback: staticContent, children: _jsx(React.Suspense, { fallback: staticContent, children: _jsx(MetalFxPrimitive, { ...props, ref: ref, className: className, theme: theme === 'auto' ? preferences.theme : theme, paused: paused || preferences.reducedMotion, normalizeHostStyles: normalizeHostStyles, style: {
47
- background: 'var(--bg-white-0)',
48
- color: 'var(--text-strong-950)',
47
+ return (_jsx(MetalEffectBoundary, { fallback: staticContent, children: _jsx(React.Suspense, { fallback: staticContent, children: _jsx(MetalFxPrimitive, { ...props, ref: ref, className: __magnaClasses_1(className, className), theme: theme === 'auto' ? preferences.theme : theme, paused: paused || preferences.reducedMotion, normalizeHostStyles: normalizeHostStyles, style: {
48
+ background: "var(--magna-bg-white-0)",
49
+ color: "var(--magna-text-strong-950)",
49
50
  ...style,
50
- }, children: children }) }) }));
51
+ }, "data-magnaui": '', children: children }) }) }));
51
52
  });
@@ -27,7 +27,7 @@ export const VoiceBeam = React.forwardRef(function VoiceBeam({ theme, paused, st
27
27
  // hydration so its CSS and feature-dependent markup never disagree with SSR.
28
28
  if (!mounted) {
29
29
  const htmlProps = Object.fromEntries(Object.entries(props).filter(([key]) => !effectPropNames.has(key)));
30
- return (_jsx("div", { ...htmlProps, ref: ref, children: props.children }));
30
+ return (_jsx("div", { ...htmlProps, ref: ref, "data-magnaui": '', children: props.children }));
31
31
  }
32
32
  return (_jsx(VoiceBeamPrimitive, { ...props, ref: ref, theme: theme === undefined || theme === 'auto' ? preferences.theme : theme, paused: preferences.reducedMotion || paused, staticColors: preferences.reducedMotion || staticColors, processing: preferences.reducedMotion ? false : processing, flow: preferences.reducedMotion ? 0 : flow }));
33
33
  });
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { magnaClasses as __magnaClasses_1 } from "../../magna-classes.js";
3
4
  import { cn } from "../../utils/cn.js";
4
5
  import { magnaApps } from "./apps.js";
5
6
  /** Transparent 2x PNG exports of the original Figma icon components. */
@@ -8,8 +9,8 @@ export function AppIcon({ app, size = 48, className, decorative = false, }) {
8
9
  return (
9
10
  // Keep the exported artwork usable outside Next.js as a source component.
10
11
  // eslint-disable-next-line @next/next/no-img-element
11
- _jsx("img", { src: asset.src, alt: decorative ? '' : asset.name, width: size, height: size, className: cn('shrink-0 object-contain', className), style: { width: size, height: size } }));
12
+ _jsx("img", { src: asset.src, alt: decorative ? '' : asset.name, width: size, height: size, className: __magnaClasses_1(__magnaClasses_1(cn('shrink-0 object-contain', className), className), className), style: { width: size, height: size }, "data-magnaui": '' }));
12
13
  }
13
14
  export function AppLauncher({ apps = magnaApps.map((a) => a.id), onLaunch, className, }) {
14
- return (_jsx("div", { className: cn('grid grid-cols-3 gap-5 sm:grid-cols-5 lg:grid-cols-8', className), children: apps.map((id) => (_jsxs("button", { type: 'button', onClick: () => onLaunch(id), className: 'flex min-w-0 flex-col items-center gap-2 rounded-2xl p-2 transition hover:bg-bg-weak-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-base', children: [_jsx(AppIcon, { app: id, size: 52, decorative: true }), _jsx("span", { className: 'w-full truncate text-center text-label-xs', children: magnaApps.find((a) => a.id === id)?.name })] }, id))) }));
15
+ return (_jsx("div", { className: __magnaClasses_1(__magnaClasses_1(cn('grid grid-cols-3 gap-5 sm:grid-cols-5 lg:grid-cols-8', className), className), className), "data-magnaui": '', children: apps.map((id) => (_jsxs("button", { type: 'button', onClick: () => onLaunch(id), className: __magnaClasses_1('flex min-w-0 flex-col items-center gap-2 rounded-2xl p-2 transition hover:bg-bg-weak-50 focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary-base'), "data-magnaui": '', children: [_jsx(AppIcon, { app: id, size: 52, decorative: true }), _jsx("span", { className: __magnaClasses_1('w-full truncate text-center text-label-xs'), "data-magnaui": '', children: magnaApps.find((a) => a.id === id)?.name })] }, id))) }));
15
16
  }
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { magnaClasses as __magnaClasses_1 } from "../../magna-classes.js";
3
4
  import { ThinkingOrb } from "../interactions/thinking-orb.js";
4
5
  import { cn } from "../../utils/cn.js";
5
6
  import { VoiceOrb } from "./voice-orb.js";
6
7
  import styles from "./chat-composer.module.css";
7
8
  /** Render in the message area, separately from the persistent ChatComposer. */
8
- export function ChatActivity({ status = 'idle', voiceLevel = 0, recordingSeconds = 0, interactions = true, disabled = false, dir = 'ltr', className, }) {
9
+ export function ChatActivity({ status = 'idle', voiceLevel = 0, recordingSeconds = 0, interactions = true, disabled = false, dir, className, }) {
9
10
  if (status === 'idle')
10
11
  return null;
11
12
  const voice = status === 'listening' || status === 'speaking';
@@ -20,10 +21,10 @@ export function ChatActivity({ status = 'idle', voiceLevel = 0, recordingSeconds
20
21
  const elapsed = `${Math.floor(seconds / 60)
21
22
  .toString()
22
23
  .padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
23
- return (_jsxs("div", { role: 'group', "aria-label": 'Message activity', dir: dir, className: cn('w-full text-text-sub-600', voice
24
+ return (_jsxs("div", { role: 'group', "aria-label": 'Message activity', dir: dir, className: __magnaClasses_1(__magnaClasses_1(cn('w-full text-text-sub-600', voice
24
25
  ? 'flex flex-col items-center gap-4 py-6 text-center'
25
- : 'space-y-3 py-2', className), children: [voice && interactions && (_jsx(VoiceOrb, { state: status, level: voiceLevel, paused: disabled })), _jsxs("div", { className: 'flex items-center gap-2 text-label-sm', children: [status === 'thinking' && interactions && (_jsx("span", { "aria-hidden": 'true', className: 'shrink-0', children: _jsx(ThinkingOrb, { state: 'weaving', size: 20, paused: disabled }) })), _jsx("span", { children: label })] }), status === 'recording' && (_jsxs("div", { className: 'flex max-w-[280px] items-center gap-4', children: [interactions && (_jsx("span", { "aria-hidden": 'true', className: cn(styles.waveform, 'min-w-0 flex-1 text-success-base'), "data-paused": disabled || undefined, children: [
26
+ : 'space-y-3 py-2', className), className), className), "data-magnaui": '', children: [voice && interactions && (_jsx(VoiceOrb, { state: status, level: voiceLevel, paused: disabled })), _jsxs("div", { className: __magnaClasses_1('flex items-center gap-2 text-label-sm'), "data-magnaui": '', children: [status === 'thinking' && interactions && (_jsx("span", { "aria-hidden": 'true', className: __magnaClasses_1('shrink-0'), "data-magnaui": '', children: _jsx(ThinkingOrb, { state: 'weaving', size: 20, paused: disabled }) })), _jsx("span", { "data-magnaui": '', children: label })] }), status === 'recording' && (_jsxs("div", { className: __magnaClasses_1('flex max-w-[280px] items-center gap-4'), "data-magnaui": '', children: [interactions && (_jsx("span", { "aria-hidden": 'true', className: __magnaClasses_1(__magnaClasses_1(cn(styles.waveform, 'min-w-0 flex-1 text-success-base'))), "data-paused": disabled || undefined, "data-magnaui": '', children: [
26
27
  8, 14, 23, 17, 28, 20, 12, 24, 32, 18, 27, 15, 22, 10, 18, 26,
27
28
  12,
28
- ].map((height, index) => (_jsx("span", { style: { height, animationDelay: `${index * -0.11}s` } }, index))) })), _jsx("time", { dateTime: `PT${seconds}S`, "aria-label": `Recording duration ${Math.floor(seconds / 60)} minutes ${seconds % 60} seconds`, "aria-live": 'off', dir: 'ltr', className: 'shrink-0 font-mono text-label-sm tabular-nums', children: elapsed })] }))] }));
29
+ ].map((height, index) => (_jsx("span", { style: { height, animationDelay: `${index * -0.11}s` }, "data-magnaui": '' }, index))) })), _jsx("time", { dateTime: `PT${seconds}S`, "aria-label": `Recording duration ${Math.floor(seconds / 60)} minutes ${seconds % 60} seconds`, "aria-live": 'off', dir: 'ltr', className: __magnaClasses_1('shrink-0 font-mono text-label-sm tabular-nums'), "data-magnaui": '', children: elapsed })] }))] }));
29
30
  }
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ export interface ChatAttachmentProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {
3
+ name: string;
4
+ description?: string;
5
+ status?: 'ready' | 'uploading' | 'processing' | 'error';
6
+ /** Upload percentage from 0 to 100. Omit for indeterminate progress. */
7
+ progress?: number;
8
+ onRemove?: () => void;
9
+ onRetry?: () => void;
10
+ onOpen?: () => void;
11
+ disabled?: boolean;
12
+ }
13
+ /** A controlled file preview; uploading and opening files are app responsibilities. */
14
+ export declare const ChatAttachment: React.ForwardRefExoticComponent<ChatAttachmentProps & React.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { magnaClasses as __magnaClasses_1 } from "../../magna-classes.js";
4
+ import * as React from "react";
5
+ import { RiCloseLine, RiErrorWarningLine, RiFileTextLine, RiLoader4Line, RiRefreshLine, } from "@remixicon/react";
6
+ import * as CompactButton from "../ui/compact-button.js";
7
+ import { cn } from "../../utils/cn.js";
8
+ /** A controlled file preview; uploading and opening files are app responsibilities. */
9
+ export const ChatAttachment = React.forwardRef(function ChatAttachment({ name, description, status = 'ready', progress, onRemove, onRetry, onOpen, disabled = false, className, ...props }, ref) {
10
+ const titleId = React.useId();
11
+ const statusId = React.useId();
12
+ const uploading = status === 'uploading';
13
+ const processing = status === 'processing';
14
+ const failed = status === 'error';
15
+ const value = progress === undefined
16
+ ? undefined
17
+ : Number.isFinite(progress)
18
+ ? Math.min(100, Math.max(0, progress))
19
+ : 0;
20
+ const statusLabel = uploading
21
+ ? value === undefined
22
+ ? 'Uploading…'
23
+ : `Uploading ${Math.round(value)}%`
24
+ : processing
25
+ ? 'Processing…'
26
+ : failed
27
+ ? 'Upload failed'
28
+ : 'Ready';
29
+ const dot = name.lastIndexOf('.');
30
+ const hasExtension = dot > 0 && dot < name.length - 1 && name.length - dot <= 8;
31
+ const title = (_jsxs("span", { id: titleId, title: name, className: __magnaClasses_1('flex min-w-0 text-label-sm text-text-strong-950'), "data-magnaui": '', children: [_jsx("span", { className: __magnaClasses_1('truncate'), "data-magnaui": '', children: hasExtension ? name.slice(0, dot) : name }), hasExtension && _jsx("span", { className: __magnaClasses_1('shrink-0'), "data-magnaui": '', children: name.slice(dot) })] }));
32
+ const details = (_jsxs(_Fragment, { children: [title, description && (_jsx("span", { className: __magnaClasses_1('mt-0.5 block truncate text-paragraph-xs text-text-soft-400'), "data-magnaui": '', children: description }))] }));
33
+ return (_jsxs("div", { ref: ref, "data-slot": 'chat-attachment', "data-state": status, className: __magnaClasses_1(__magnaClasses_1(cn('relative flex w-72 min-w-0 max-w-full items-center gap-2.5 rounded-2xl border border-stroke-soft-200 bg-bg-white-0 p-2.5 text-start shadow-regular-xs', failed && 'border-error-base/30', disabled && 'opacity-60', className), className), className), ...props, "data-magnaui": '', children: [_jsx("span", { "aria-hidden": 'true', className: __magnaClasses_1(__magnaClasses_1(cn('flex size-10 shrink-0 items-center justify-center rounded-xl bg-bg-weak-50 text-text-sub-600', status === 'ready' && 'bg-success-lighter text-success-dark', failed && 'bg-error-lighter text-error-base'))), "data-magnaui": '', children: failed ? (_jsx(RiErrorWarningLine, { className: __magnaClasses_1('size-5'), "data-magnaui": '' })) : uploading || processing ? (_jsx(RiLoader4Line, { className: __magnaClasses_1('size-5 motion-safe:animate-spin'), "data-magnaui": '' })) : (_jsx(RiFileTextLine, { className: __magnaClasses_1('size-5'), "data-magnaui": '' })) }), _jsxs("div", { className: __magnaClasses_1('min-w-0 flex-1'), "data-magnaui": '', children: [onOpen ? (_jsx("button", { type: 'button', onClick: onOpen, disabled: disabled || status !== 'ready', "aria-label": `Open ${name}`, "aria-describedby": statusId, className: __magnaClasses_1('block w-full min-w-0 rounded-md text-start outline-none hover:underline focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-base disabled:cursor-default disabled:no-underline'), "data-magnaui": '', children: details })) : (details), _jsx("span", { id: statusId, role: 'status', "aria-live": 'polite', "aria-atomic": 'true', className: __magnaClasses_1(__magnaClasses_1(cn('mt-0.5 block text-paragraph-xs text-text-sub-600', failed && 'text-error-base', status === 'ready' && description && 'sr-only'))), "data-magnaui": '', children: statusLabel })] }), (onRemove || (failed && onRetry)) && (_jsxs("div", { className: __magnaClasses_1('flex shrink-0 items-center gap-0.5'), "data-magnaui": '', children: [failed && onRetry && (_jsx(CompactButton.Root, { type: 'button', variant: 'ghost', className: __magnaClasses_1('size-8'), "aria-label": `Retry ${name}`, title: 'Retry upload', onClick: onRetry, disabled: disabled, "data-magnaui": '', children: _jsx(CompactButton.Icon, { as: RiRefreshLine, "aria-hidden": 'true', className: __magnaClasses_1('size-4'), "data-magnaui": '' }) })), onRemove && (_jsx(CompactButton.Root, { type: 'button', variant: 'ghost', className: __magnaClasses_1('size-8'), "aria-label": `Remove ${name}`, title: 'Remove attachment', onClick: onRemove, disabled: disabled, "data-magnaui": '', children: _jsx(CompactButton.Icon, { as: RiCloseLine, "aria-hidden": 'true', className: __magnaClasses_1('size-4'), "data-magnaui": '' }) }))] })), uploading && (_jsx("div", { role: 'progressbar', "aria-labelledby": titleId, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": value, "aria-valuetext": statusLabel, className: __magnaClasses_1('pointer-events-none absolute inset-x-3 bottom-0 h-0.5 overflow-hidden rounded-full bg-bg-soft-200'), "data-magnaui": '', children: value !== undefined && (_jsx("span", { className: __magnaClasses_1('block h-full rounded-full bg-success-base'), style: { width: `${value}%` }, "data-magnaui": '' })) }))] }));
34
+ });
35
+ ChatAttachment.displayName = 'ChatAttachment';
@@ -11,6 +11,13 @@ export interface ChatComposerProps {
11
11
  /** Controlled draft; omit to keep the draft inside the composer. */
12
12
  value?: string;
13
13
  onValueChange?: (value: string) => void;
14
+ /** File chips or other selected context, controlled by the consuming app. */
15
+ attachments?: React.ReactNode;
16
+ /** Prevent sending while attachments or another prerequisite are pending. */
17
+ sendDisabled?: boolean;
18
+ /** Optional validation or service error; the consuming app owns recovery. */
19
+ error?: string;
20
+ maxLength?: number;
14
21
  /** Controlled activity state. The consuming app supplies voice and AI services. */
15
22
  status?: ChatComposerStatus;
16
23
  /** A manual audio level from 0 to 1, or a getter sampled by the voice effect. */