@aspect-ops/exon-ui 0.1.0 → 0.2.1

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 (52) hide show
  1. package/README.md +341 -16
  2. package/dist/components/Accordion/Accordion.svelte +2 -2
  3. package/dist/components/Accordion/AccordionItem.svelte +2 -2
  4. package/dist/components/Chatbot/ChatMessage.svelte +143 -0
  5. package/dist/components/Chatbot/ChatMessage.svelte.d.ts +8 -0
  6. package/dist/components/Chatbot/Chatbot.svelte +640 -0
  7. package/dist/components/Chatbot/Chatbot.svelte.d.ts +22 -0
  8. package/dist/components/Chatbot/index.d.ts +3 -0
  9. package/dist/components/Chatbot/index.js +2 -0
  10. package/dist/components/Chatbot/types.d.ts +48 -0
  11. package/dist/components/Chatbot/types.js +2 -0
  12. package/dist/components/ContactForm/ContactForm.svelte +564 -0
  13. package/dist/components/ContactForm/ContactForm.svelte.d.ts +44 -0
  14. package/dist/components/ContactForm/index.d.ts +1 -0
  15. package/dist/components/ContactForm/index.js +1 -0
  16. package/dist/components/DoughnutChart/DoughnutChart.svelte +372 -0
  17. package/dist/components/DoughnutChart/DoughnutChart.svelte.d.ts +25 -0
  18. package/dist/components/DoughnutChart/index.d.ts +1 -0
  19. package/dist/components/DoughnutChart/index.js +1 -0
  20. package/dist/components/FAB/FAB.svelte +5 -1
  21. package/dist/components/FAB/FABGroup.svelte +10 -2
  22. package/dist/components/FileUpload/FileUpload.svelte +12 -12
  23. package/dist/components/Mermaid/Mermaid.svelte +206 -0
  24. package/dist/components/Mermaid/Mermaid.svelte.d.ts +28 -0
  25. package/dist/components/Mermaid/index.d.ts +1 -0
  26. package/dist/components/Mermaid/index.js +1 -0
  27. package/dist/components/Mermaid/mermaid.d.ts +21 -0
  28. package/dist/components/NumberInput/NumberInput.svelte +293 -0
  29. package/dist/components/NumberInput/NumberInput.svelte.d.ts +16 -0
  30. package/dist/components/NumberInput/index.d.ts +1 -0
  31. package/dist/components/NumberInput/index.js +1 -0
  32. package/dist/components/Pagination/Pagination.svelte +243 -0
  33. package/dist/components/Pagination/Pagination.svelte.d.ts +10 -0
  34. package/dist/components/Pagination/index.d.ts +1 -0
  35. package/dist/components/Pagination/index.js +1 -0
  36. package/dist/components/Popover/PopoverTrigger.svelte +1 -3
  37. package/dist/components/ToggleGroup/ToggleGroup.svelte +91 -0
  38. package/dist/components/ToggleGroup/ToggleGroup.svelte.d.ts +13 -0
  39. package/dist/components/ToggleGroup/ToggleGroupItem.svelte +158 -0
  40. package/dist/components/ToggleGroup/ToggleGroupItem.svelte.d.ts +9 -0
  41. package/dist/components/ToggleGroup/index.d.ts +3 -0
  42. package/dist/components/ToggleGroup/index.js +2 -0
  43. package/dist/components/ViewCounter/ViewCounter.svelte +157 -0
  44. package/dist/components/ViewCounter/ViewCounter.svelte.d.ts +17 -0
  45. package/dist/components/ViewCounter/index.d.ts +1 -0
  46. package/dist/components/ViewCounter/index.js +1 -0
  47. package/dist/index.d.ts +13 -1
  48. package/dist/index.js +12 -0
  49. package/dist/styles/tokens.css +2 -1
  50. package/dist/types/index.d.ts +1 -1
  51. package/dist/types/input.d.ts +35 -0
  52. package/package.json +2 -1
package/README.md CHANGED
@@ -46,22 +46,24 @@ npm install @aspect-ops/exon-ui
46
46
 
47
47
  ### Form Components
48
48
 
49
- | Component | Description |
50
- | --------------- | ---------------------------------------- |
51
- | `TextInput` | Text input with validation states |
52
- | `Textarea` | Multi-line input with auto-resize |
53
- | `Select` | Dropdown select with keyboard navigation |
54
- | `Checkbox` | Single checkbox with indeterminate state |
55
- | `CheckboxGroup` | Group of checkboxes with shared state |
56
- | `Radio` | Single radio button |
57
- | `RadioGroup` | Radio button group with orientation |
58
- | `Switch` | Toggle switch component |
59
- | `FormField` | Label wrapper with helper/error text |
60
- | `SearchInput` | Search with autocomplete suggestions |
61
- | `DatePicker` | Date selection with calendar popup |
62
- | `TimePicker` | Time selection with hour/minute picker |
63
- | `FileUpload` | Drag-drop file upload with previews |
64
- | `OTPInput` | One-time password input |
49
+ | Component | Description |
50
+ | --------------- | ------------------------------------------ |
51
+ | `TextInput` | Text input with validation states |
52
+ | `Textarea` | Multi-line input with auto-resize |
53
+ | `Select` | Dropdown select with keyboard navigation |
54
+ | `Checkbox` | Single checkbox with indeterminate state |
55
+ | `CheckboxGroup` | Group of checkboxes with shared state |
56
+ | `Radio` | Single radio button |
57
+ | `RadioGroup` | Radio button group with orientation |
58
+ | `Switch` | Toggle switch component |
59
+ | `FormField` | Label wrapper with helper/error text |
60
+ | `SearchInput` | Search with autocomplete suggestions |
61
+ | `DatePicker` | Date selection with calendar popup |
62
+ | `TimePicker` | Time selection with hour/minute picker |
63
+ | `FileUpload` | Drag-drop file upload with previews |
64
+ | `OTPInput` | One-time password input |
65
+ | `NumberInput` | Number input with +/- buttons and keyboard |
66
+ | `ToggleGroup` | Single/multi select button group |
65
67
 
66
68
  ### Navigation Components
67
69
 
@@ -74,6 +76,7 @@ npm install @aspect-ops/exon-ui
74
76
  | `Navbar`, `NavItem` | Responsive header with mobile menu |
75
77
  | `Sidebar`, `SidebarItem`, `SidebarGroup` | Collapsible sidebar navigation |
76
78
  | `Stepper`, `StepperStep` | Multi-step progress indicator |
79
+ | `Pagination` | Page navigation with ellipsis |
77
80
 
78
81
  ### Data Display Components
79
82
 
@@ -492,6 +495,128 @@ One-time password input with auto-focus and paste support.
492
495
  <OTPInput bind:value={otp} length={6} oncomplete={(code) => console.log('OTP:', code)} />
493
496
  ```
494
497
 
498
+ ### NumberInput
499
+
500
+ Number input with increment/decrement buttons and keyboard support.
501
+
502
+ **Props:**
503
+
504
+ | Prop | Type | Default | Description |
505
+ | --------------- | ------------------------- | ------- | --------------------------- |
506
+ | `value` | `number \| null` | `null` | Bindable number value |
507
+ | `min` | `number` | - | Minimum allowed value |
508
+ | `max` | `number` | - | Maximum allowed value |
509
+ | `step` | `number` | `1` | Increment/decrement step |
510
+ | `disabled` | `boolean` | `false` | Disabled state |
511
+ | `placeholder` | `string` | - | Input placeholder |
512
+ | `error` | `boolean` | `false` | Error state styling |
513
+ | `onValueChange` | `(value: number) => void` | - | Callback when value changes |
514
+
515
+ **Usage:**
516
+
517
+ ```svelte
518
+ <script>
519
+ import { NumberInput } from '@aspect-ops/exon-ui';
520
+
521
+ let quantity = $state(1);
522
+ </script>
523
+
524
+ <NumberInput bind:value={quantity} min={1} max={99} step={1} />
525
+
526
+ <!-- With error state -->
527
+ <NumberInput bind:value={quantity} min={0} max={10} error={quantity > 10} />
528
+ ```
529
+
530
+ ### ToggleGroup
531
+
532
+ Button group for single or multiple selection.
533
+
534
+ **Props (ToggleGroup):**
535
+
536
+ | Prop | Type | Default | Description |
537
+ | --------------- | ---------------------------- | -------------- | --------------------------- |
538
+ | `type` | `'single' \| 'multiple'` | `'single'` | Selection mode |
539
+ | `value` | `string \| string[]` | `'' \| []` | Bindable selected value(s) |
540
+ | `onValueChange` | `(value) => void` | - | Callback when value changes |
541
+ | `disabled` | `boolean` | `false` | Disabled state |
542
+ | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction |
543
+
544
+ **Props (ToggleGroupItem):**
545
+
546
+ | Prop | Type | Default | Description |
547
+ | ---------- | --------- | ------- | --------------------- |
548
+ | `value` | `string` | - | Item value (required) |
549
+ | `disabled` | `boolean` | `false` | Disabled state |
550
+
551
+ **Usage:**
552
+
553
+ ```svelte
554
+ <script>
555
+ import { ToggleGroup, ToggleGroupItem } from '@aspect-ops/exon-ui';
556
+
557
+ let alignment = $state('left');
558
+ let formats = $state([]);
559
+ </script>
560
+
561
+ <!-- Single selection (radio-like) -->
562
+ <ToggleGroup bind:value={alignment} type="single">
563
+ {#snippet children()}
564
+ <ToggleGroupItem value="left">Left</ToggleGroupItem>
565
+ <ToggleGroupItem value="center">Center</ToggleGroupItem>
566
+ <ToggleGroupItem value="right">Right</ToggleGroupItem>
567
+ {/snippet}
568
+ </ToggleGroup>
569
+
570
+ <!-- Multiple selection (checkbox-like) -->
571
+ <ToggleGroup bind:value={formats} type="multiple">
572
+ {#snippet children()}
573
+ <ToggleGroupItem value="bold">B</ToggleGroupItem>
574
+ <ToggleGroupItem value="italic">I</ToggleGroupItem>
575
+ <ToggleGroupItem value="underline">U</ToggleGroupItem>
576
+ {/snippet}
577
+ </ToggleGroup>
578
+ ```
579
+
580
+ ### Pagination
581
+
582
+ Page navigation with ellipsis for large page ranges.
583
+
584
+ **Props:**
585
+
586
+ | Prop | Type | Default | Description |
587
+ | -------------- | ------------------------ | ------- | --------------------------------- |
588
+ | `currentPage` | `number` | - | Current page number (required) |
589
+ | `totalPages` | `number` | - | Total number of pages (required) |
590
+ | `siblingCount` | `number` | `1` | Pages to show around current page |
591
+ | `onPageChange` | `(page: number) => void` | - | Callback when page changes |
592
+
593
+ **Usage:**
594
+
595
+ ```svelte
596
+ <script>
597
+ import { Pagination } from '@aspect-ops/exon-ui';
598
+
599
+ let currentPage = $state(1);
600
+ const totalPages = 20;
601
+ </script>
602
+
603
+ <Pagination {currentPage} {totalPages} onPageChange={(page) => (currentPage = page)} />
604
+
605
+ <!-- With more visible siblings -->
606
+ <Pagination
607
+ {currentPage}
608
+ {totalPages}
609
+ siblingCount={2}
610
+ onPageChange={(page) => (currentPage = page)}
611
+ />
612
+ ```
613
+
614
+ **Keyboard Navigation:**
615
+
616
+ - `←` / `→`: Previous / Next page
617
+ - `Home`: First page
618
+ - `End`: Last page
619
+
495
620
  ## Data Display Components
496
621
 
497
622
  ### Accordion
@@ -922,6 +1047,206 @@ Components optimized for Capacitor mobile apps with gesture support, haptic feed
922
1047
 
923
1048
  - `edges` - Array of edges: `'top'` | `'right'` | `'bottom'` | `'left'` (default: all)
924
1049
 
1050
+ ## Website Components
1051
+
1052
+ ### Chatbot
1053
+
1054
+ AI-ready chat widget with session management, typing indicators, and lead capture.
1055
+
1056
+ ```svelte
1057
+ <script>
1058
+ import { Chatbot } from '@aspect-ops/exon-ui';
1059
+
1060
+ async function handleMessage(message, sessionId) {
1061
+ // Call your AI backend (OpenAI, Claude, etc.)
1062
+ const response = await fetch('/api/chat', {
1063
+ method: 'POST',
1064
+ body: JSON.stringify({ message, sessionId })
1065
+ });
1066
+ return (await response.json()).reply;
1067
+ }
1068
+
1069
+ async function handleLeadCapture(data, sessionId) {
1070
+ // Send to your CRM
1071
+ await fetch('/api/leads', {
1072
+ method: 'POST',
1073
+ body: JSON.stringify({ ...data, sessionId })
1074
+ });
1075
+ }
1076
+ </script>
1077
+
1078
+ <Chatbot
1079
+ title="Support Chat"
1080
+ subtitle="We typically reply within minutes"
1081
+ welcomeMessage="Hello! How can I help you today?"
1082
+ onSendMessage={handleMessage}
1083
+ onLeadCapture={handleLeadCapture}
1084
+ position="bottom-right"
1085
+ />
1086
+ ```
1087
+
1088
+ **Props:**
1089
+
1090
+ | Prop | Type | Default | Description |
1091
+ | ------------------- | ---------- | ---------------- | ----------------------------------------- |
1092
+ | `title` | `string` | `'Chat Support'` | Chat header title |
1093
+ | `subtitle` | `string` | - | Chat header subtitle |
1094
+ | `welcomeMessage` | `string` | - | Initial welcome message |
1095
+ | `placeholder` | `string` | `'Type...'` | Input placeholder text |
1096
+ | `defaultOpen` | `boolean` | `false` | Open chat on mount |
1097
+ | `position` | `string` | `'bottom-right'` | Position: bottom-right, bottom-left |
1098
+ | `inactivityTimeout` | `number` | `40000` | Auto-open after inactivity (ms), 0=off |
1099
+ | `onSendMessage` | `function` | - | Callback: `(msg, sessionId) => reply` |
1100
+ | `onLeadCapture` | `function` | - | Callback: `(data, sessionId) => void` |
1101
+ | `onEscalate` | `function` | - | Callback: `(sessionId, messages) => void` |
1102
+
1103
+ ### ContactForm
1104
+
1105
+ Customizable contact form with real-time validation and UTM tracking.
1106
+
1107
+ ```svelte
1108
+ <script>
1109
+ import { ContactForm } from '@aspect-ops/exon-ui';
1110
+
1111
+ async function handleSubmit(data) {
1112
+ const response = await fetch('/api/contact', {
1113
+ method: 'POST',
1114
+ body: JSON.stringify(data)
1115
+ });
1116
+ return { success: response.ok };
1117
+ }
1118
+ </script>
1119
+
1120
+ <ContactForm
1121
+ submitText="Send Message"
1122
+ successMessage="Thank you! We'll be in touch soon."
1123
+ onSubmit={handleSubmit}
1124
+ />
1125
+ ```
1126
+
1127
+ **Props:**
1128
+
1129
+ | Prop | Type | Default | Description |
1130
+ | ------------------ | --------------- | -------------- | ------------------------------------- |
1131
+ | `fields` | `FieldConfig[]` | Default fields | Array of field configurations |
1132
+ | `initialValues` | `object` | `{}` | Initial form values |
1133
+ | `submitText` | `string` | `'Submit'` | Submit button text |
1134
+ | `successMessage` | `string` | `'Thank you!'` | Success message after submit |
1135
+ | `privacyNotice` | `string` | - | Privacy policy text |
1136
+ | `extractUtmParams` | `boolean` | `true` | Extract UTM params from URL |
1137
+ | `onSubmit` | `function` | - | Callback: `(data) => { success }` |
1138
+ | `onValidate` | `function` | - | Callback: `(isValid, errors) => void` |
1139
+
1140
+ **Default fields:** firstName, lastName, email, phone, company, message
1141
+
1142
+ ### ViewCounter
1143
+
1144
+ Track and display page views with session-based deduplication.
1145
+
1146
+ ```svelte
1147
+ <script>
1148
+ import { ViewCounter } from '@aspect-ops/exon-ui';
1149
+
1150
+ async function getCount(slug) {
1151
+ const res = await fetch(`/api/views/${slug}`);
1152
+ return (await res.json()).count;
1153
+ }
1154
+
1155
+ async function trackView(slug) {
1156
+ const res = await fetch(`/api/views/${slug}`, { method: 'POST' });
1157
+ return (await res.json()).count;
1158
+ }
1159
+ </script>
1160
+
1161
+ <ViewCounter slug="blog-post-123" onGetCount={getCount} onTrackView={trackView} />
1162
+ ```
1163
+
1164
+ **Props:**
1165
+
1166
+ | Prop | Type | Default | Description |
1167
+ | ------------- | ---------- | ------- | ---------------------------- |
1168
+ | `slug` | `string` | - | Unique content identifier |
1169
+ | `showLabel` | `boolean` | `true` | Show "views" label |
1170
+ | `trackView` | `boolean` | `true` | Track view (or just display) |
1171
+ | `onGetCount` | `function` | - | Callback: `(slug) => count` |
1172
+ | `onTrackView` | `function` | - | Callback: `(slug) => count` |
1173
+
1174
+ ### DoughnutChart
1175
+
1176
+ Pure SVG doughnut chart with interactive legend (no external dependencies).
1177
+
1178
+ ```svelte
1179
+ <script>
1180
+ import { DoughnutChart } from '@aspect-ops/exon-ui';
1181
+
1182
+ const data = [
1183
+ { label: 'Direct', value: 45000 },
1184
+ { label: 'Referral', value: 28000 },
1185
+ { label: 'Organic', value: 32000 },
1186
+ { label: 'Social', value: 15000 }
1187
+ ];
1188
+ </script>
1189
+
1190
+ <DoughnutChart title="Traffic Sources" {data} size={200} showLegend showTotal />
1191
+ ```
1192
+
1193
+ **Props:**
1194
+
1195
+ | Prop | Type | Default | Description |
1196
+ | ------------ | ---------- | --------------- | ------------------------- |
1197
+ | `title` | `string` | - | Chart title |
1198
+ | `data` | `array` | `[]` | `[{ label, value }, ...]` |
1199
+ | `size` | `number` | `200` | Chart size in pixels |
1200
+ | `thickness` | `number` | `0.6` | Doughnut thickness (0-1) |
1201
+ | `colors` | `string[]` | Default palette | Custom color array |
1202
+ | `showLegend` | `boolean` | `true` | Show legend |
1203
+ | `showTotal` | `boolean` | `true` | Show center total |
1204
+
1205
+ ### Mermaid
1206
+
1207
+ Render Mermaid diagrams with customizable theming.
1208
+
1209
+ **Note:** Requires `mermaid` package: `npm install mermaid`
1210
+
1211
+ ```svelte
1212
+ <script>
1213
+ import { Mermaid } from '@aspect-ops/exon-ui';
1214
+
1215
+ const diagram = `flowchart TD
1216
+ A[Start] --> B{Decision}
1217
+ B -->|Yes| C[Action 1]
1218
+ B -->|No| D[Action 2]
1219
+ C --> E[End]
1220
+ D --> E`;
1221
+ </script>
1222
+
1223
+ <!-- Using code prop -->
1224
+ <Mermaid code={diagram} />
1225
+
1226
+ <!-- Using slot -->
1227
+ <Mermaid>flowchart LR A --> B --> C</Mermaid>
1228
+ ```
1229
+
1230
+ **Props:**
1231
+
1232
+ | Prop | Type | Default | Description |
1233
+ | ------- | -------- | ------------- | ---------------------- |
1234
+ | `code` | `string` | - | Mermaid diagram code |
1235
+ | `theme` | `object` | ExonPro theme | Custom theme variables |
1236
+
1237
+ **Theme variables:**
1238
+
1239
+ ```javascript
1240
+ const theme = {
1241
+ primaryColor: '#4654A3',
1242
+ primaryTextColor: '#270949',
1243
+ primaryBorderColor: '#4654A3',
1244
+ lineColor: '#270949',
1245
+ secondaryColor: '#FF3131',
1246
+ tertiaryColor: '#f3f4f6'
1247
+ };
1248
+ ```
1249
+
925
1250
  ## Theming & Customization
926
1251
 
927
1252
  ### Quick Setup
@@ -25,8 +25,8 @@
25
25
  return value ? [value] : [];
26
26
  });
27
27
 
28
- // Context key
29
- const ACCORDION_KEY = Symbol('accordion');
28
+ // Context key - must match AccordionItem
29
+ const ACCORDION_KEY = 'exon-accordion-context';
30
30
 
31
31
  // Context API
32
32
  const accordionContext = {
@@ -11,8 +11,8 @@
11
11
 
12
12
  let { value, disabled = false, class: className = '', header, children }: Props = $props();
13
13
 
14
- // Context key (must match parent)
15
- const ACCORDION_KEY = Symbol('accordion');
14
+ // Context key (must match parent Accordion)
15
+ const ACCORDION_KEY = 'exon-accordion-context';
16
16
 
17
17
  // Get context from parent Accordion
18
18
  const ctx = getContext<{
@@ -0,0 +1,143 @@
1
+ <script lang="ts">
2
+ import type { ChatMessage } from './types.js';
3
+
4
+ interface Props {
5
+ message: ChatMessage;
6
+ class?: string;
7
+ }
8
+
9
+ let { message, class: className = '' }: Props = $props();
10
+
11
+ // Format message content (bold, links)
12
+ function formatContent(content: string): string {
13
+ // Convert **text** to bold
14
+ let formatted = content.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
15
+ // Convert [text](url) to links
16
+ formatted = formatted.replace(
17
+ /\[([^\]]+)\]\(([^)]+)\)/g,
18
+ '<a href="$2" target="_blank" rel="noopener noreferrer">$1</a>'
19
+ );
20
+ return formatted;
21
+ }
22
+
23
+ const formattedContent = $derived(formatContent(message.content));
24
+ const isUser = $derived(message.role === 'user');
25
+ const isSystem = $derived(message.role === 'system');
26
+ </script>
27
+
28
+ <div
29
+ class="chat-message chat-message--{message.role} {className}"
30
+ class:chat-message--user={isUser}
31
+ class:chat-message--assistant={!isUser && !isSystem}
32
+ class:chat-message--system={isSystem}
33
+ >
34
+ {#if !isUser && !isSystem}
35
+ <div class="chat-message__avatar">
36
+ <svg
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ width="20"
39
+ height="20"
40
+ viewBox="0 0 24 24"
41
+ fill="none"
42
+ stroke="currentColor"
43
+ stroke-width="2"
44
+ stroke-linecap="round"
45
+ stroke-linejoin="round"
46
+ >
47
+ <path d="M12 8V4H8" />
48
+ <rect width="16" height="12" x="4" y="8" rx="2" />
49
+ <path d="M2 14h2" />
50
+ <path d="M20 14h2" />
51
+ <path d="M15 13v2" />
52
+ <path d="M9 13v2" />
53
+ </svg>
54
+ </div>
55
+ {/if}
56
+ <div class="chat-message__bubble">
57
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
58
+ {@html formattedContent}
59
+ </div>
60
+ {#if message.timestamp}
61
+ <div class="chat-message__time">
62
+ {new Date(message.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
63
+ </div>
64
+ {/if}
65
+ </div>
66
+
67
+ <style>
68
+ .chat-message {
69
+ display: flex;
70
+ gap: var(--space-sm, 0.5rem);
71
+ max-width: 85%;
72
+ margin-bottom: var(--space-sm, 0.5rem);
73
+ }
74
+
75
+ .chat-message--user {
76
+ margin-left: auto;
77
+ flex-direction: row-reverse;
78
+ }
79
+
80
+ .chat-message--assistant {
81
+ margin-right: auto;
82
+ }
83
+
84
+ .chat-message--system {
85
+ margin: var(--space-sm, 0.5rem) auto;
86
+ max-width: 90%;
87
+ }
88
+
89
+ .chat-message__avatar {
90
+ flex-shrink: 0;
91
+ width: 2rem;
92
+ height: 2rem;
93
+ display: flex;
94
+ align-items: center;
95
+ justify-content: center;
96
+ border-radius: var(--radius-full, 9999px);
97
+ background: var(--color-primary, #3b82f6);
98
+ color: var(--color-text-inverse, #ffffff);
99
+ }
100
+
101
+ .chat-message__bubble {
102
+ padding: var(--space-sm, 0.5rem) var(--space-md, 1rem);
103
+ border-radius: var(--radius-lg, 0.75rem);
104
+ font-size: var(--text-sm, 0.875rem);
105
+ line-height: var(--leading-relaxed, 1.625);
106
+ word-wrap: break-word;
107
+ }
108
+
109
+ .chat-message--user .chat-message__bubble {
110
+ background: var(--color-primary, #3b82f6);
111
+ color: var(--color-text-inverse, #ffffff);
112
+ border-bottom-right-radius: var(--radius-sm, 0.25rem);
113
+ }
114
+
115
+ .chat-message--assistant .chat-message__bubble {
116
+ background: var(--color-bg-muted, #f3f4f6);
117
+ color: var(--color-text, #1f2937);
118
+ border-bottom-left-radius: var(--radius-sm, 0.25rem);
119
+ }
120
+
121
+ .chat-message--system .chat-message__bubble {
122
+ background: var(--color-bg-elevated, #ffffff);
123
+ border: 1px solid var(--color-border, #e5e7eb);
124
+ color: var(--color-text-muted, #6b7280);
125
+ font-style: italic;
126
+ text-align: center;
127
+ }
128
+
129
+ .chat-message__bubble :global(a) {
130
+ color: inherit;
131
+ text-decoration: underline;
132
+ }
133
+
134
+ .chat-message__bubble :global(strong) {
135
+ font-weight: var(--font-semibold, 600);
136
+ }
137
+
138
+ .chat-message__time {
139
+ align-self: flex-end;
140
+ font-size: var(--text-xs, 0.75rem);
141
+ color: var(--color-text-subtle, #9ca3af);
142
+ }
143
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { ChatMessage } from './types.js';
2
+ interface Props {
3
+ message: ChatMessage;
4
+ class?: string;
5
+ }
6
+ declare const ChatMessage: import("svelte").Component<Props, {}, "">;
7
+ type ChatMessage = ReturnType<typeof ChatMessage>;
8
+ export default ChatMessage;