magnaui 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/HextaUI-MIT.txt +21 -0
- package/README.md +178 -14
- package/THIRD_PARTY_NOTICES.md +14 -0
- package/dist/components/interactions/border-beam.js +1 -1
- package/dist/components/interactions/image-generation.js +6 -5
- package/dist/components/interactions/liquid.js +1 -1
- package/dist/components/interactions/metal-fx.js +6 -5
- package/dist/components/interactions/voice-beam.js +1 -1
- package/dist/components/magna/app-icon.js +3 -2
- package/dist/components/magna/brand-logo.d.ts +12 -0
- package/dist/components/magna/brand-logo.js +9 -0
- package/dist/components/magna/chat-activity.js +5 -4
- package/dist/components/magna/chat-attachment.d.ts +14 -0
- package/dist/components/magna/chat-attachment.js +35 -0
- package/dist/components/magna/chat-composer.d.ts +7 -0
- package/dist/components/magna/chat-composer.js +30 -12
- package/dist/components/magna/chat-composer.module.css +7 -14
- package/dist/components/magna/chat-conversation.d.ts +19 -0
- package/dist/components/magna/chat-conversation.js +158 -0
- package/dist/components/magna/chat-message.d.ts +17 -0
- package/dist/components/magna/chat-message.js +76 -0
- package/dist/components/magna/chat-response.d.ts +50 -0
- package/dist/components/magna/chat-response.js +64 -0
- package/dist/components/magna/desktop-button.js +5 -4
- package/dist/components/magna/desktop-switch.js +2 -1
- package/dist/components/magna/file-tile.js +3 -2
- package/dist/components/magna/index.d.ts +6 -0
- package/dist/components/magna/index.js +5 -0
- package/dist/components/magna/logo.js +6 -5
- package/dist/components/magna/navigation.d.ts +10 -3
- package/dist/components/magna/navigation.js +3 -2
- package/dist/components/magna/project-context.js +2 -1
- package/dist/components/magna/voice-orb.js +4 -3
- package/dist/components/magna/voice-orb.module.css +25 -44
- package/dist/components/theme-switch.js +2 -1
- package/dist/components/ui/accordion.js +6 -5
- package/dist/components/ui/activity-feed.d.ts +1 -1
- package/dist/components/ui/activity-feed.js +9 -8
- package/dist/components/ui/alert.d.ts +1 -1
- package/dist/components/ui/alert.js +6 -5
- package/dist/components/ui/avatar-empty-icons.js +3 -2
- package/dist/components/ui/avatar-group-compact.js +7 -6
- package/dist/components/ui/avatar-group.js +5 -4
- package/dist/components/ui/avatar.d.ts +1 -1
- package/dist/components/ui/avatar.js +18 -17
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/components/ui/badge.js +6 -5
- package/dist/components/ui/banner.js +3 -2
- package/dist/components/ui/breadcrumb.js +6 -5
- package/dist/components/ui/button-group.d.ts +1 -1
- package/dist/components/ui/button-group.js +8 -7
- package/dist/components/ui/button.d.ts +3 -2
- package/dist/components/ui/button.js +10 -5
- package/dist/components/ui/chart-legend.js +3 -2
- package/dist/components/ui/checkbox.js +8 -7
- package/dist/components/ui/color-picker.js +13 -9
- package/dist/components/ui/combobox.d.ts +25 -0
- package/dist/components/ui/combobox.js +72 -0
- package/dist/components/ui/command-menu.d.ts +1 -1
- package/dist/components/ui/command-menu.js +9 -8
- package/dist/components/ui/compact-button.d.ts +3 -2
- package/dist/components/ui/compact-button.js +10 -5
- package/dist/components/ui/content-card.js +3 -2
- package/dist/components/ui/content-label.js +2 -1
- package/dist/components/ui/counter-input.js +5 -4
- package/dist/components/ui/datepicker.d.ts +20 -2
- package/dist/components/ui/datepicker.js +121 -34
- package/dist/components/ui/digit-input.d.ts +14 -2
- package/dist/components/ui/digit-input.js +10 -4
- package/dist/components/ui/direction-provider.d.ts +19 -0
- package/dist/components/ui/direction-provider.js +41 -0
- package/dist/components/ui/display-asset.js +2 -1
- package/dist/components/ui/divider.js +7 -5
- package/dist/components/ui/dot-stepper.js +3 -2
- package/dist/components/ui/drawer.js +8 -5
- package/dist/components/ui/dropdown.js +16 -12
- package/dist/components/ui/fancy-button.d.ts +1 -1
- package/dist/components/ui/fancy-button.js +5 -4
- package/dist/components/ui/file-format-icon.js +4 -3
- package/dist/components/ui/file-upload-compositions.js +7 -6
- package/dist/components/ui/file-upload.js +4 -3
- package/dist/components/ui/filter.js +11 -10
- package/dist/components/ui/form-asset.js +3 -2
- package/dist/components/ui/highlight.d.ts +15 -0
- package/dist/components/ui/highlight.js +20 -0
- package/dist/components/ui/hint.d.ts +1 -1
- package/dist/components/ui/hint.js +5 -4
- package/dist/components/ui/hit-area.d.ts +6 -0
- package/dist/components/ui/hit-area.js +2 -0
- package/dist/components/ui/horizontal-stepper.js +6 -5
- package/dist/components/ui/inline-input.js +4 -3
- package/dist/components/ui/input.js +8 -7
- package/dist/components/ui/kbd.js +2 -1
- package/dist/components/ui/key-icon.js +3 -2
- package/dist/components/ui/label.js +4 -3
- package/dist/components/ui/link-button.d.ts +1 -1
- package/dist/components/ui/link-button.js +5 -4
- package/dist/components/ui/magna-recipe.js +3 -1
- package/dist/components/ui/modal.d.ts +14 -0
- package/dist/components/ui/modal.js +28 -9
- package/dist/components/ui/notification-feed.js +8 -7
- package/dist/components/ui/notification.d.ts +1 -1
- package/dist/components/ui/notification.js +10 -3
- package/dist/components/ui/pagination.js +10 -9
- package/dist/components/ui/panel.d.ts +10 -0
- package/dist/components/ui/panel.js +12 -0
- package/dist/components/ui/password-strength.js +3 -2
- package/dist/components/ui/payment-icon.js +2 -1
- package/dist/components/ui/popover.js +7 -4
- package/dist/components/ui/progress-bar.d.ts +4 -2
- package/dist/components/ui/progress-bar.js +16 -11
- package/dist/components/ui/progress-circle.d.ts +2 -1
- package/dist/components/ui/progress-circle.js +8 -6
- package/dist/components/ui/quote.d.ts +6 -0
- package/dist/components/ui/quote.js +14 -0
- package/dist/components/ui/radio.js +7 -6
- package/dist/components/ui/rating.js +11 -10
- package/dist/components/ui/resizable.d.ts +53 -0
- package/dist/components/ui/resizable.js +200 -0
- package/dist/components/ui/rich-editor.js +17 -16
- package/dist/components/ui/rich-editor.module.css +17 -28
- package/dist/components/ui/scroll-area.js +6 -5
- package/dist/components/ui/segmented-control.js +4 -3
- package/dist/components/ui/select.js +29 -23
- package/dist/components/ui/skeleton.d.ts +10 -0
- package/dist/components/ui/skeleton.js +12 -0
- package/dist/components/ui/slider.js +3 -2
- package/dist/components/ui/social-button.d.ts +1 -1
- package/dist/components/ui/social-button.js +5 -4
- package/dist/components/ui/status-badge.d.ts +1 -1
- package/dist/components/ui/status-badge.js +6 -5
- package/dist/components/ui/status-modal.d.ts +1 -0
- package/dist/components/ui/status-modal.js +3 -2
- package/dist/components/ui/svg-rating-icons.js +11 -10
- package/dist/components/ui/switch-compositions.d.ts +1 -1
- package/dist/components/ui/switch-compositions.js +10 -9
- package/dist/components/ui/switch.js +4 -3
- package/dist/components/ui/tab-menu-horizontal.js +19 -10
- package/dist/components/ui/tab-menu-vertical.js +5 -4
- package/dist/components/ui/table.d.ts +4 -1
- package/dist/components/ui/table.js +19 -11
- package/dist/components/ui/tag-input.js +5 -4
- package/dist/components/ui/tag.d.ts +1 -1
- package/dist/components/ui/tag.js +8 -7
- package/dist/components/ui/textarea.js +7 -7
- package/dist/components/ui/time-picker.js +9 -8
- package/dist/components/ui/tooltip.js +4 -1
- package/dist/components/ui/vertical-stepper.js +7 -6
- package/dist/core.d.ts +99 -0
- package/dist/core.js +97 -0
- package/dist/design/theme.js +9 -9
- package/dist/fonts/README.md +6 -1
- package/dist/hooks/use-tab-observer.js +9 -0
- package/dist/magna-classes.js +47 -0
- package/dist/magnaui.d.ts +4 -76
- package/dist/magnaui.js +4 -76
- package/dist/styles.css +1208 -2441
- package/dist/utils/cn.js +2 -1
- package/dist/utils/magna-theme.d.ts +20 -0
- package/dist/utils/magna-theme.js +42 -0
- package/dist/utils/tv.js +3 -2
- package/docs/implementation-provenance.md +37 -7
- package/package.json +104 -34
|
@@ -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,26 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
React components, theme tokens, and original Magna artwork for authorized Magna AI applications.
|
|
4
4
|
|
|
5
|
+
MagnaUI `0.3.0` includes AI conversation components, React 18.3 and React 19 support, RTL direction support, and isolated styles for Tailwind 4 applications.
|
|
6
|
+
|
|
5
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
|
|
|
9
|
+
Internal teams may use MagnaUI in Magna AI projects covered by the owner’s authorization. Keep that approval with your project records; public npm access or organization membership alone does not extend permission to unrelated projects.
|
|
10
|
+
|
|
7
11
|
## Install
|
|
8
12
|
|
|
9
13
|
```sh
|
|
10
|
-
npm install magnaui
|
|
14
|
+
npm install magnaui@^0.3.0
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
The package ships ESM JavaScript, TypeScript declarations, compiled CSS, fonts, and artwork. Use
|
|
17
|
+
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
18
|
|
|
15
|
-
Import the styles once
|
|
19
|
+
Import the styles once in your application entry point. In Next.js App Router, import them from the root layout:
|
|
16
20
|
|
|
17
21
|
```tsx
|
|
18
22
|
import 'magnaui/styles.css';
|
|
23
|
+
import './globals.css'; // Include your Tailwind 4 setup here, if used.
|
|
19
24
|
```
|
|
20
25
|
|
|
26
|
+
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.
|
|
27
|
+
|
|
21
28
|
```tsx
|
|
22
29
|
'use client';
|
|
23
30
|
|
|
24
|
-
import { Button, Checkbox, MagnaLogo } from 'magnaui';
|
|
31
|
+
import { Button, Checkbox, MagnaLogo } from 'magnaui/core';
|
|
25
32
|
|
|
26
33
|
export function Workspace() {
|
|
27
34
|
return (
|
|
@@ -36,7 +43,7 @@ export function Workspace() {
|
|
|
36
43
|
}
|
|
37
44
|
```
|
|
38
45
|
|
|
39
|
-
The stylesheet includes
|
|
46
|
+
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
47
|
|
|
41
48
|
For a narrower import, component modules also have subpaths:
|
|
42
49
|
|
|
@@ -46,27 +53,117 @@ import { MagnaLogo } from 'magnaui/magna';
|
|
|
46
53
|
import { BorderBeam } from 'magnaui/interactions';
|
|
47
54
|
```
|
|
48
55
|
|
|
56
|
+
## Optional editor and effects
|
|
57
|
+
|
|
58
|
+
Starting in 0.3.0, editor and animation engines are optional peers. A basic install does not download Tiptap, Three.js, or the effects packages. Use `magnaui/core` for base UI, branding, providers, and AI message components, or `magnaui/ui/*` and `magnaui/magna/*` for individual modules.
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { Button, DirectionProvider, MagnaLogo } from 'magnaui/core';
|
|
62
|
+
import { ChatMessage } from 'magnaui/magna/chat-message';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Install only the engines required by the feature you import:
|
|
66
|
+
|
|
67
|
+
| Feature/subpath | Additional packages |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `magnaui/ui/rich-editor` | All Tiptap packages in the command below |
|
|
70
|
+
| `magnaui/magna/chat-composer` | `border-beam@1.4.1 voice-glow@0.2.1` |
|
|
71
|
+
| `magnaui/magna/chat-activity` | `thinking-orbs@0.3.2` |
|
|
72
|
+
| `magnaui/interactions/border-beam` | `border-beam@1.4.1` |
|
|
73
|
+
| `magnaui/interactions/thinking-orb` | `thinking-orbs@0.3.2` |
|
|
74
|
+
| `magnaui/interactions/bot-avatar` | `bot-avatars@0.1.1` |
|
|
75
|
+
| `magnaui/interactions/liquid` | `liquid-gooey@0.2.2` |
|
|
76
|
+
| `magnaui/interactions/voice-beam` | `voice-glow@0.2.1` |
|
|
77
|
+
| `magnaui/interactions/metal-fx` | `metal-fx@2.0.11` |
|
|
78
|
+
| `magnaui/interactions/image-generation` | `img-fx@0.5.1 three@0.149.0` |
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
# Rich editor
|
|
82
|
+
npm install @tiptap/core@3.31.3 @tiptap/extension-bubble-menu@3.31.3 @tiptap/extension-floating-menu@3.31.3 @tiptap/extension-text-align@3.31.3 @tiptap/extension-text-style@3.31.3 @tiptap/extension-underline@3.31.3 @tiptap/pm@3.31.3 @tiptap/react@3.31.3 @tiptap/starter-kit@3.31.3
|
|
83
|
+
|
|
84
|
+
# Animated chat composer and activity
|
|
85
|
+
npm install border-beam@1.4.1 voice-glow@0.2.1 thinking-orbs@0.3.2
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The existing full `magnaui`, `magnaui/magna`, and `magnaui/interactions` barrels retain their exports. Full-root examples in this guide require all optional engines because bundlers and TypeScript can traverse re-exports even when only one symbol is imported. To preserve an existing full-root application, install all engines once:
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
npm install @tiptap/core@3.31.3 @tiptap/extension-bubble-menu@3.31.3 @tiptap/extension-floating-menu@3.31.3 @tiptap/extension-text-align@3.31.3 @tiptap/extension-text-style@3.31.3 @tiptap/extension-underline@3.31.3 @tiptap/pm@3.31.3 @tiptap/react@3.31.3 @tiptap/starter-kit@3.31.3 border-beam@1.4.1 bot-avatars@0.1.1 img-fx@0.5.1 liquid-gooey@0.2.2 metal-fx@2.0.11 thinking-orbs@0.3.2 three@0.149.0 voice-glow@0.2.1
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
For a smaller installation, switch ordinary UI imports to `magnaui/core` and feature imports to the individual subpaths above. Setting `interactions={false}` hides effects but does not change a module's dependency requirements. All component designs and existing prop defaults are preserved.
|
|
95
|
+
|
|
49
96
|
## Included components
|
|
50
97
|
|
|
51
|
-
|
|
98
|
+
MagnaUI `0.3.0` includes base component families, Magna workspace components, interaction adapters, and AI conversation components, with their public component parts and types.
|
|
52
99
|
|
|
53
100
|
| Group | Public component families |
|
|
54
101
|
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
55
102
|
| Actions | `Button`, `ButtonGroup`, `CompactButton`, `FancyButton`, `LinkButton`, `SocialButton` |
|
|
56
|
-
| Forms and input | `Checkbox`, `ColorPicker`, `CounterInput`, `DatePicker`, `DigitInput`, `FileUpload`, `Filter`, `Hint`, `InlineInput`, `Input`, `Label`, `PasswordStrength`, `Radio`, `Rating`, `RichEditor`, `Select`, `Slider`, `Switch`, `TagInput`, `Textarea`, `TimePicker` |
|
|
103
|
+
| Forms and input | `Checkbox`, `Combobox`, `ColorPicker`, `CounterInput`, `DatePicker`, `DigitInput`, `FileUpload`, `Filter`, `Hint`, `InlineInput`, `Input`, `Label`, `PasswordStrength`, `Radio`, `Rating`, `RichEditor`, `Select`, `Slider`, `Switch`, `TagInput`, `Textarea`, `TimePicker` |
|
|
57
104
|
| Navigation | `Accordion`, `Breadcrumb`, `CommandMenu`, `DotStepper`, `HorizontalStepper`, `Pagination`, `SegmentedControl`, `TabMenuHorizontal`, `TabMenuVertical`, `VerticalStepper` |
|
|
58
|
-
| Feedback | `Alert`, `Banner`, `Notification`, `NotificationFeed`, `ProgressBar`, `ProgressCircle`, `StatusBadge`, `Tooltip` |
|
|
105
|
+
| Feedback | `Alert`, `Banner`, `Notification`, `NotificationFeed`, `ProgressBar`, `ProgressCircle`, `Skeleton`, `StatusBadge`, `Tooltip` |
|
|
59
106
|
| Overlays | `Drawer`, `Dropdown`, `Modal`, `Popover`, `StatusModal` |
|
|
60
|
-
| Data and content | `ActivityFeed`, `ChartLegend`, `ContentCard`, `ContentLabel`, `Divider`, `Kbd`, `ScrollArea`, `Table`, `Tag` |
|
|
107
|
+
| Data and content | `ActivityFeed`, `ChartLegend`, `ContentCard`, `ContentLabel`, `Divider`, `Highlight`, `Kbd`, `Panel`, `Quote`, `Resizable`, `ScrollArea`, `Table`, `Tag` |
|
|
61
108
|
| Identity and icons | `Avatar`, `AvatarEmptyIcons`, `AvatarGroup`, `AvatarGroupCompact`, `Badge`, `FileFormatIcon`, `KeyIcon`, `PaymentIcon`, `RatingIcons` |
|
|
62
109
|
|
|
63
|
-
- **Magna workspace:** `AppIcon`, `AppLauncher`, `MagnaLogo`, `ChatComposer`, `ChatActivity`, `DesktopButton`, `DesktopSwitch`, `FileTile`, `MagnaNavigation`, and `ProjectContext`.
|
|
110
|
+
- **Magna workspace:** `AppIcon`, `AppLauncher`, `BrandLogo`, `MagnaLogo`, `ChatComposer`, `ChatActivity`, `DesktopButton`, `DesktopSwitch`, `FileTile`, `MagnaNavigation`, and `ProjectContext`.
|
|
111
|
+
- **AI conversations:** `ChatMessage`, `ChatConversation`, `ChatAttachment`, `ChatReasoning`, `ChatToolCall`, `ChatSources`, and `ChatSuggestions`.
|
|
64
112
|
- **Interactions:** `BorderBeam`, `ThinkingOrb`, `BotAvatar`, `Liquid`, `VoiceBeam`, `MetalFx`, and `ImageGeneration`.
|
|
65
|
-
- **Providers and helpers:** `ThemeProvider`, `useTheme`, `ThemeSwitch`, `TooltipProvider`, `NotificationProvider`, `notification`, `useNotification`, `useTabObserver`, and application, navigation, and logo metadata.
|
|
66
|
-
- **Styles and assets:** compiled component CSS, light/dark themes, five accents, two neutral palettes, 265 artwork files (including 40 application icons and 24 logo variants), and 11 self-hosted font files.
|
|
113
|
+
- **Providers and helpers:** `ThemeProvider`, `useTheme`, `ThemeSwitch`, `TooltipProvider`, `NotificationProvider`, `notification`, `useNotification`, `useTabObserver`, `DirectionProvider`, `useDirection`, `createMagnaTheme`, and application, navigation, and logo metadata.
|
|
114
|
+
- **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.
|
|
67
115
|
|
|
68
116
|
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.
|
|
69
117
|
|
|
118
|
+
## Compose an AI conversation
|
|
119
|
+
|
|
120
|
+
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.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
'use client';
|
|
124
|
+
|
|
125
|
+
import { useState } from 'react';
|
|
126
|
+
import {
|
|
127
|
+
ChatComposer,
|
|
128
|
+
ChatConversation,
|
|
129
|
+
ChatMessage,
|
|
130
|
+
ChatSuggestions,
|
|
131
|
+
} from 'magnaui';
|
|
132
|
+
|
|
133
|
+
export function Conversation() {
|
|
134
|
+
const [draft, setDraft] = useState('');
|
|
135
|
+
const [messages, setMessages] = useState<string[]>([]);
|
|
136
|
+
|
|
137
|
+
return (
|
|
138
|
+
<div style={{ display: 'grid', gap: 16, maxWidth: 700 }}>
|
|
139
|
+
<ChatConversation emptyState={<p>What would you like to work on?</p>}>
|
|
140
|
+
{messages.map((message, index) => (
|
|
141
|
+
<ChatMessage key={index} role='user'>
|
|
142
|
+
{message}
|
|
143
|
+
</ChatMessage>
|
|
144
|
+
))}
|
|
145
|
+
</ChatConversation>
|
|
146
|
+
<ChatSuggestions
|
|
147
|
+
suggestions={['Summarize a document', 'Plan the next steps']}
|
|
148
|
+
onSelect={setDraft}
|
|
149
|
+
/>
|
|
150
|
+
<ChatComposer
|
|
151
|
+
value={draft}
|
|
152
|
+
onValueChange={setDraft}
|
|
153
|
+
onSend={(message) => {
|
|
154
|
+
setMessages((current) => [...current, message]);
|
|
155
|
+
setDraft('');
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
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.
|
|
164
|
+
|
|
165
|
+
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`.
|
|
166
|
+
|
|
70
167
|
## Themes and providers
|
|
71
168
|
|
|
72
169
|
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.
|
|
@@ -87,8 +184,75 @@ export function Providers({ children }: { children: ReactNode }) {
|
|
|
87
184
|
}
|
|
88
185
|
```
|
|
89
186
|
|
|
90
|
-
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.
|
|
187
|
+
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.
|
|
91
188
|
|
|
92
189
|
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.
|
|
93
190
|
|
|
94
|
-
The documentation and full source are maintained in the MagnaUI repository. Registry visibility does not change access to that repository.
|
|
191
|
+
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.
|
|
192
|
+
|
|
193
|
+
## Arabic and RTL
|
|
194
|
+
|
|
195
|
+
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.
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
'use client';
|
|
199
|
+
|
|
200
|
+
import { DirectionProvider, Button } from 'magnaui';
|
|
201
|
+
|
|
202
|
+
export function ArabicWorkspace() {
|
|
203
|
+
return (
|
|
204
|
+
<DirectionProvider dir='rtl' lang='ar'>
|
|
205
|
+
<Button.Root>إنشاء مساحة عمل</Button.Root>
|
|
206
|
+
</DirectionProvider>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
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`.
|
|
212
|
+
|
|
213
|
+
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:
|
|
214
|
+
|
|
215
|
+
```css
|
|
216
|
+
@font-face {
|
|
217
|
+
font-family: 'Thmanyah Sans';
|
|
218
|
+
src: url('/fonts/ThmanyahSans.woff2') format('woff2');
|
|
219
|
+
font-weight: 100 900;
|
|
220
|
+
font-style: normal;
|
|
221
|
+
font-display: swap;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
:root {
|
|
225
|
+
--magna-font-sans: 'Magna Inter', sans-serif;
|
|
226
|
+
--magna-font-arabic: 'Thmanyah Sans', sans-serif;
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
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.
|
|
231
|
+
|
|
232
|
+
## Custom brand colors
|
|
233
|
+
|
|
234
|
+
`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:
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { createMagnaTheme } from 'magnaui';
|
|
238
|
+
|
|
239
|
+
const magnaTheme = createMagnaTheme({
|
|
240
|
+
primary: '#8a1538',
|
|
241
|
+
darkPrimary: '#b54162',
|
|
242
|
+
fontSans: '"Magna Inter", sans-serif',
|
|
243
|
+
fontArabic: '"Thmanyah Sans", sans-serif',
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
<html lang='ar' dir='rtl' data-accent='custom' style={magnaTheme}>
|
|
247
|
+
<body>{children}</body>
|
|
248
|
+
</html>;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
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.
|
|
252
|
+
|
|
253
|
+
## Upgrading from 0.1.x
|
|
254
|
+
|
|
255
|
+
- 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.
|
|
256
|
+
- 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`.
|
|
257
|
+
- 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.
|
|
258
|
+
- Apply `DirectionProvider dir="rtl" lang="ar"` for Arabic keyboard behavior and fonts. Load your authorized Thmanyah font through the consuming application.
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -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 ??
|
|
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 =
|
|
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:
|
|
48
|
-
color:
|
|
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
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from "react";
|
|
2
|
+
export type BrandLogoProps = Omit<ComponentPropsWithoutRef<'span'>, 'children'> & {
|
|
3
|
+
src: string;
|
|
4
|
+
darkSrc?: string;
|
|
5
|
+
/** Organisation name, or an empty string when the enclosing link names it. */
|
|
6
|
+
alt: string;
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
imageClassName?: string;
|
|
10
|
+
};
|
|
11
|
+
/** Tenant artwork for a header or navigation rail, with one accessible name. */
|
|
12
|
+
export declare function BrandLogo({ src, darkSrc, alt, width, height, className, imageClassName, style, ...props }: BrandLogoProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { magnaClasses as __magnaClasses_1 } from "../../magna-classes.js";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
/** Tenant artwork for a header or navigation rail, with one accessible name. */
|
|
5
|
+
export function BrandLogo({ src, darkSrc, alt, width = 32, height = 32, className, imageClassName, style, ...props }) {
|
|
6
|
+
return (_jsxs("span", { ...props, role: alt ? 'img' : undefined, "aria-label": alt || undefined, "aria-hidden": alt ? undefined : true, className: __magnaClasses_1(__magnaClasses_1(cn('inline-flex shrink-0 items-center justify-center', className), className), className), style: { width, height, ...style }, "data-magnaui": '', children: [_jsx("img", { src: src, alt: '', width: width, height: height, className: __magnaClasses_1(__magnaClasses_1(cn('h-full w-full object-contain', darkSrc ? 'block dark:hidden' : 'block', imageClassName), imageClassName), imageClassName), "data-magnaui": '' }), darkSrc && (
|
|
7
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
8
|
+
_jsx("img", { src: darkSrc, alt: '', width: width, height: height, className: __magnaClasses_1(__magnaClasses_1(cn('hidden h-full w-full object-contain dark:block', imageClassName), imageClassName), imageClassName), "data-magnaui": '' }))] }));
|
|
9
|
+
}
|
|
@@ -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
|
|
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. */
|