@refraction-ui/astro 0.8.0 → 0.9.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 (96) hide show
  1. package/dist/analytics/index.ts +5 -0
  2. package/dist/astro-accordion/Accordion.stories.ts +12 -0
  3. package/dist/astro-ai/Ai.stories.ts +12 -0
  4. package/dist/astro-analytics/Analytics.stories.ts +12 -0
  5. package/dist/astro-animated-text/AnimatedText.stories.ts +12 -0
  6. package/dist/astro-app-shell/AppShell.stories.ts +12 -0
  7. package/dist/astro-auth/Auth.stories.ts +12 -0
  8. package/dist/astro-avatar/Avatar.stories.ts +12 -0
  9. package/dist/astro-avatar-group/AvatarGroup.stories.ts +12 -0
  10. package/dist/astro-badge/Badge.stories.ts +16 -0
  11. package/dist/astro-bottom-nav/BottomNav.stories.ts +12 -0
  12. package/dist/astro-breadcrumbs/Breadcrumbs.stories.ts +12 -0
  13. package/dist/astro-button/Button.stories.ts +12 -0
  14. package/dist/astro-calendar/Calendar.stories.ts +12 -0
  15. package/dist/astro-callout/Callout.stories.ts +12 -0
  16. package/dist/astro-card/Card.stories.ts +12 -0
  17. package/dist/astro-card-grid/CardGrid.stories.ts +12 -0
  18. package/dist/astro-carousel/Carousel.stories.ts +12 -0
  19. package/dist/astro-charts/Charts.stories.ts +12 -0
  20. package/dist/astro-checkbox/Checkbox.stories.ts +12 -0
  21. package/dist/astro-code-block/CodeBlock.stories.ts +12 -0
  22. package/dist/astro-code-editor/CodeEditor.stories.ts +12 -0
  23. package/dist/astro-collapsible/Collapsible.stories.ts +12 -0
  24. package/dist/astro-command/Command.stories.ts +12 -0
  25. package/dist/astro-command-input/CommandInput.stories.ts +12 -0
  26. package/dist/astro-content-protection/ContentProtection.stories.ts +12 -0
  27. package/dist/astro-conversation/Chat.astro +208 -0
  28. package/dist/astro-conversation/Conversation.stories.ts +12 -0
  29. package/dist/astro-conversation/index.ts +29 -0
  30. package/dist/astro-cookie-consent/CookieConsent.astro +141 -0
  31. package/dist/astro-cookie-consent/index.ts +12 -0
  32. package/dist/astro-data-table/DataTable.stories.ts +12 -0
  33. package/dist/astro-date-picker/DatePicker.stories.ts +12 -0
  34. package/dist/astro-device-frame/DeviceFrame.stories.ts +12 -0
  35. package/dist/astro-dialog/Dialog.stories.ts +12 -0
  36. package/dist/astro-diff-viewer/DiffViewer.stories.ts +12 -0
  37. package/dist/astro-dropdown-menu/DropdownMenu.stories.ts +12 -0
  38. package/dist/astro-emoji-picker/EmojiPicker.stories.ts +12 -0
  39. package/dist/astro-feedback-dialog/FeedbackDialog.stories.ts +12 -0
  40. package/dist/astro-file-tree/FileTree.stories.ts +12 -0
  41. package/dist/astro-file-upload/FileUpload.stories.ts +12 -0
  42. package/dist/astro-footer/Footer.stories.ts +12 -0
  43. package/dist/astro-icon-system/IconSystem.stories.ts +12 -0
  44. package/dist/astro-inline-editor/InlineEditor.stories.ts +12 -0
  45. package/dist/astro-input/Input.stories.ts +12 -0
  46. package/dist/astro-input-group/InputGroup.stories.ts +12 -0
  47. package/dist/astro-install-prompt/InstallPrompt.stories.ts +12 -0
  48. package/dist/astro-keyboard-shortcut/KeyboardShortcut.stories.ts +12 -0
  49. package/dist/astro-language-selector/LanguageSelector.stories.ts +12 -0
  50. package/dist/astro-link-card/LinkCard.stories.ts +12 -0
  51. package/dist/astro-logger/Logger.stories.ts +12 -0
  52. package/dist/astro-markdown-renderer/MarkdownRenderer.stories.ts +12 -0
  53. package/dist/astro-mobile-nav/MobileNav.stories.ts +12 -0
  54. package/dist/astro-navbar/Navbar.stories.ts +12 -0
  55. package/dist/astro-otp-input/OtpInput.stories.ts +12 -0
  56. package/dist/astro-pagination/Pagination.stories.ts +12 -0
  57. package/dist/astro-payment/Payment.stories.ts +12 -0
  58. package/dist/astro-popover/Popover.stories.ts +12 -0
  59. package/dist/astro-presence-indicator/PresenceIndicator.stories.ts +12 -0
  60. package/dist/astro-progress-display/ProgressDisplay.stories.ts +12 -0
  61. package/dist/astro-radio/Radio.stories.ts +12 -0
  62. package/dist/astro-reaction-bar/ReactionBar.stories.ts +12 -0
  63. package/dist/astro-resizable-layout/ResizableLayout.stories.ts +12 -0
  64. package/dist/astro-rich-editor/RichEditor.stories.ts +12 -0
  65. package/dist/astro-search-bar/SearchBar.stories.ts +12 -0
  66. package/dist/astro-select/Select.stories.ts +12 -0
  67. package/dist/astro-sidebar/Sidebar.stories.ts +12 -0
  68. package/dist/astro-skeleton/Skeleton.stories.ts +12 -0
  69. package/dist/astro-skip-to-content/SkipToContent.stories.ts +12 -0
  70. package/dist/astro-slide-viewer/SlideViewer.stories.ts +12 -0
  71. package/dist/astro-status-indicator/StatusIndicator.stories.ts +12 -0
  72. package/dist/astro-steps/Steps.stories.ts +12 -0
  73. package/dist/astro-switch/Switch.stories.ts +12 -0
  74. package/dist/astro-table-of-contents/TableOfContents.stories.ts +12 -0
  75. package/dist/astro-tabs/Tabs.stories.ts +12 -0
  76. package/dist/astro-textarea/Textarea.stories.ts +12 -0
  77. package/dist/astro-theme/Theme.stories.ts +12 -0
  78. package/dist/astro-thread-view/ThreadView.stories.ts +12 -0
  79. package/dist/astro-toast/Toast.stories.ts +12 -0
  80. package/dist/astro-tooltip/Tooltip.stories.ts +12 -0
  81. package/dist/astro-version-selector/VersionSelector.stories.ts +12 -0
  82. package/dist/astro-video-player/VideoPlayer.stories.ts +12 -0
  83. package/dist/astro-voice-pill/VoicePill.stories.ts +12 -0
  84. package/dist/astro-waveform/Waveform.stories.ts +12 -0
  85. package/dist/conversation/conversation.ts +379 -0
  86. package/dist/conversation/index.ts +30 -0
  87. package/dist/conversation/selectors.ts +58 -0
  88. package/dist/conversation/types.ts +222 -0
  89. package/dist/cookie-consent/cookie-consent.ts +132 -0
  90. package/dist/cookie-consent/index.ts +10 -0
  91. package/dist/cookie-consent/types.ts +72 -0
  92. package/dist/index.ts +2 -0
  93. package/dist/shared/library-origin-error.ts +1 -2
  94. package/dist/shared/types.ts +1 -1
  95. package/dist/theme/theme-script.ts +1 -1
  96. package/package.json +3 -1
@@ -23,6 +23,11 @@ export { SCHEMA_VERSION } from './types.js'
23
23
  // Manager
24
24
  export { createAnalytics } from './analytics-manager.js'
25
25
 
26
+ // Headless no-op (symmetry with @refraction-ui/logger's createNoopTelemetry):
27
+ // a full Analytics whose every method is an empty fn. Used by adapters to
28
+ // degrade gracefully (no-op, never throw) when no provider/context is present.
29
+ export { createNoopAnalytics } from './noop.js'
30
+
26
31
  // Built-in sinks
27
32
  export { createHttpSink } from './http-sink.js'
28
33
  export { createConsoleSink } from './console-sink.js'
@@ -0,0 +1,12 @@
1
+ import Component from './Button.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Accordion',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './AIScript.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Ai',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './AnalyticsScript.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Analytics',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './AnimatedText.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/AnimatedText',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './AppShell.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/AppShell',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './AuthGuard.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Auth',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Avatar.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Avatar',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './AvatarGroup.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/AvatarGroup',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,16 @@
1
+ import Badge from './Badge.astro'
2
+
3
+ const meta = {
4
+ title: 'Astro/Badge',
5
+ component: Badge,
6
+ argTypes: {
7
+ variant: { control: 'select', options: ['default', 'secondary', 'destructive', 'outline'] },
8
+ size: { control: 'select', options: ['sm', 'default', 'lg'] },
9
+ },
10
+ }
11
+
12
+ export default meta
13
+
14
+ export const Default = { args: { variant: 'default' } }
15
+ export const Secondary = { args: { variant: 'secondary' } }
16
+ export const Outline = { args: { variant: 'outline' } }
@@ -0,0 +1,12 @@
1
+ import Component from './BottomNav.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/BottomNav',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Breadcrumbs.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Breadcrumbs',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Button.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Button',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Calendar.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Calendar',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Callout.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Callout',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Card.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Card',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './CardGrid.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/CardGrid',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Button.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Carousel',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Bars.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Charts',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Checkbox.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Checkbox',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './CodeBlock.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/CodeBlock',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './CodeEditor.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/CodeEditor',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Collapsible.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Collapsible',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './Command.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Command',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './CommandInput.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/CommandInput',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,12 @@
1
+ import Component from './ContentProtection.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/ContentProtection',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,208 @@
1
+ ---
2
+ import {
3
+ createConversation,
4
+ selectMainTimeline,
5
+ getReplyCount,
6
+ findMessage,
7
+ type ConversationConfig,
8
+ type ChatMessage,
9
+ } from '../conversation/index.ts'
10
+ import { createMarkdownRenderer } from '../markdown-renderer/index.ts'
11
+ import { formatTimestamp } from '../thread-view/index.ts'
12
+ import { cn } from '../shared/index.ts'
13
+
14
+ interface Props extends astroHTML.JSX.HTMLAttributes {
15
+ /** Seed: conversations, messages, threadingMode, currentUser, … */
16
+ config?: ConversationConfig
17
+ showConversationList?: boolean
18
+ currentUserId?: string
19
+ }
20
+
21
+ const {
22
+ config = {},
23
+ showConversationList = true,
24
+ currentUserId,
25
+ class: className,
26
+ ...rest
27
+ } = Astro.props
28
+
29
+ const store = createConversation(config)
30
+ const state = store.getState()
31
+ const timeline = selectMainTimeline(state.messages, state.threadingMode)
32
+ const activeConv = state.conversations.find((c) => c.id === state.activeConversationId)
33
+
34
+ /** Render markdown to sanitized HTML (mirrors react-markdown-renderer). */
35
+ function renderMarkdown(content: string): string {
36
+ const html = createMarkdownRenderer({ content }).html
37
+ return html
38
+ .replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
39
+ .replace(/\son\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, '')
40
+ .replace(/(href|src)\s*=\s*["']?\s*javascript\s*:[^"'>]*/gi, '$1=""')
41
+ }
42
+
43
+ function isOwn(m: ChatMessage): boolean {
44
+ return currentUserId ? m.author.id === currentUserId : m.role === 'user'
45
+ }
46
+ ---
47
+
48
+ <div
49
+ class={cn('flex h-full min-h-0 overflow-hidden rounded-lg border border-border bg-background', className)}
50
+ data-rfr-chat
51
+ data-threading-mode={state.threadingMode}
52
+ {...rest}
53
+ >
54
+ {showConversationList && (
55
+ <aside class="flex w-56 flex-col gap-1 overflow-y-auto border-r border-border p-2" aria-label="Conversations">
56
+ <button type="button" class="mb-1 w-full rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground" data-rfr-new-conversation>
57
+ + New chat
58
+ </button>
59
+ {state.conversations.map((conv) => (
60
+ <div
61
+ role="button"
62
+ aria-current={conv.id === state.activeConversationId}
63
+ data-rfr-conversation
64
+ data-conversation-id={conv.id}
65
+ class={cn(
66
+ 'group flex items-center justify-between gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent/50',
67
+ conv.id === state.activeConversationId && 'bg-accent font-medium',
68
+ )}
69
+ >
70
+ <span class="truncate">{conv.title}</span>
71
+ <button type="button" class="text-xs text-muted-foreground hover:text-destructive" aria-label={`Delete ${conv.title}`} data-rfr-delete-conversation data-conversation-id={conv.id}>✕</button>
72
+ </div>
73
+ ))}
74
+ </aside>
75
+ )}
76
+
77
+ <div class="flex min-w-0 flex-1 flex-col">
78
+ <div class="flex items-center justify-between border-b border-border px-3 py-2">
79
+ <span class="truncate text-sm font-semibold">{activeConv?.title ?? 'Chat'}</span>
80
+ <span class="text-xs text-muted-foreground">{state.threadingMode === 'panel' ? 'Threads' : 'Inline'}</span>
81
+ </div>
82
+
83
+ <div class="flex-1 overflow-y-auto p-2" role="log" aria-live="polite">
84
+ {timeline.length === 0 ? (
85
+ <div class="flex h-full items-center justify-center p-6 text-sm text-muted-foreground">
86
+ No messages yet. Say hello 👋
87
+ </div>
88
+ ) : (
89
+ timeline.map((message) => {
90
+ const parent = state.threadingMode === 'inline' && message.parentId
91
+ ? findMessage(state.messages, message.parentId)
92
+ : undefined
93
+ const replyCount = getReplyCount(state.messages, message.id)
94
+ return (
95
+ <div class="group flex gap-3 rounded-md px-3 py-2 hover:bg-accent/40" role="article" data-rfr-message data-message-id={message.id}>
96
+ <div class="h-8 w-8 flex-shrink-0 overflow-hidden rounded-full bg-muted flex items-center justify-center text-xs font-medium">
97
+ {message.author.avatarUrl
98
+ ? <img src={message.author.avatarUrl} alt={message.author.name} class="h-full w-full object-cover" />
99
+ : (message.author.name.charAt(0) || '?').toUpperCase()}
100
+ </div>
101
+ <div class="min-w-0 flex-1">
102
+ <div class="flex items-baseline gap-2">
103
+ <span class="text-sm font-semibold">{message.author.name}</span>
104
+ <span class="text-xs text-muted-foreground">{formatTimestamp(message.timestamp)}</span>
105
+ {message.edited && <span class="text-xs text-muted-foreground">(edited)</span>}
106
+ </div>
107
+
108
+ {parent && (
109
+ <button type="button" class="mb-1 flex w-full items-start gap-2 rounded-md border-l-2 border-primary/50 bg-muted/40 px-2 py-1 text-left text-xs text-muted-foreground hover:bg-muted" data-rfr-open-thread data-root-id={parent.id}>
110
+ <span class="font-medium">{parent.author.name}</span>
111
+ <span class="truncate">{parent.content.slice(0, 80)}</span>
112
+ </button>
113
+ )}
114
+
115
+ <div class="prose-sm text-sm" set:html={renderMarkdown(message.content)} />
116
+
117
+ {message.attachments && message.attachments.map((a) => (
118
+ a.type.startsWith('image/')
119
+ ? <img src={a.url} alt={a.name} class="mt-2 max-h-60 rounded-md border border-border object-contain" />
120
+ : <a href={a.url} class="mt-2 inline-flex items-center gap-2 rounded-md border border-border bg-muted/50 px-2 py-1 text-xs">📎 {a.name}</a>
121
+ ))}
122
+
123
+ {message.reactions && message.reactions.length > 0 && (
124
+ <div class="mt-1 flex flex-wrap gap-1">
125
+ {message.reactions.map((r) => (
126
+ <button
127
+ type="button"
128
+ data-rfr-react
129
+ data-message-id={message.id}
130
+ data-emoji={r.emoji}
131
+ class={cn(
132
+ 'inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-xs',
133
+ r.userReacted ? 'border-primary bg-primary/10' : 'border-border',
134
+ )}
135
+ >{r.emoji} {r.count}</button>
136
+ ))}
137
+ </div>
138
+ )}
139
+
140
+ {state.threadingMode === 'panel' && replyCount > 0 && (
141
+ <button type="button" class="mt-1 text-xs font-medium text-primary hover:underline" data-rfr-open-thread data-root-id={message.id}>
142
+ 💬 {replyCount} {replyCount === 1 ? 'reply' : 'replies'}
143
+ </button>
144
+ )}
145
+
146
+ <div class="mt-1 flex items-center gap-3 text-xs text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100">
147
+ <button type="button" class="hover:text-foreground" data-rfr-reply data-message-id={message.id}>Reply</button>
148
+ {isOwn(message) && <button type="button" class="hover:text-destructive" data-rfr-delete-message data-message-id={message.id}>Delete</button>}
149
+ </div>
150
+ </div>
151
+ </div>
152
+ )
153
+ })
154
+ )}
155
+ </div>
156
+
157
+ <form class="flex items-end gap-2 border-t border-border p-3" data-rfr-composer>
158
+ <textarea
159
+ class="flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
160
+ rows="1"
161
+ name="message"
162
+ placeholder="Type a message…"
163
+ aria-label="Message"
164
+ ></textarea>
165
+ <button type="submit" class="rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground">Send</button>
166
+ </form>
167
+ </div>
168
+ </div>
169
+
170
+ <script>
171
+ // Astro renders the initial view from the headless store; interactivity is
172
+ // surfaced as semantic CustomEvents the host app wires to its own backend.
173
+ document.querySelectorAll<HTMLElement>('[data-rfr-chat]').forEach((root) => {
174
+ const fire = (name: string, detail: Record<string, unknown>) =>
175
+ root.dispatchEvent(new CustomEvent(`rfr:${name}`, { detail, bubbles: true }))
176
+
177
+ root.addEventListener('click', (e) => {
178
+ const t = e.target as HTMLElement
179
+ const react = t.closest<HTMLElement>('[data-rfr-react]')
180
+ if (react) return fire('react', { messageId: react.dataset.messageId, emoji: react.dataset.emoji })
181
+ const openThread = t.closest<HTMLElement>('[data-rfr-open-thread]')
182
+ if (openThread) return fire('open-thread', { rootId: openThread.dataset.rootId })
183
+ const reply = t.closest<HTMLElement>('[data-rfr-reply]')
184
+ if (reply) return fire('reply', { messageId: reply.dataset.messageId })
185
+ const del = t.closest<HTMLElement>('[data-rfr-delete-message]')
186
+ if (del) return fire('delete-message', { messageId: del.dataset.messageId })
187
+ const newConv = t.closest<HTMLElement>('[data-rfr-new-conversation]')
188
+ if (newConv) return fire('new-conversation', {})
189
+ const delConv = t.closest<HTMLElement>('[data-rfr-delete-conversation]')
190
+ if (delConv) {
191
+ e.stopPropagation()
192
+ return fire('delete-conversation', { conversationId: delConv.dataset.conversationId })
193
+ }
194
+ const conv = t.closest<HTMLElement>('[data-rfr-conversation]')
195
+ if (conv) return fire('select-conversation', { conversationId: conv.dataset.conversationId })
196
+ })
197
+
198
+ const form = root.querySelector<HTMLFormElement>('[data-rfr-composer]')
199
+ form?.addEventListener('submit', (e) => {
200
+ e.preventDefault()
201
+ const textarea = form.querySelector<HTMLTextAreaElement>('textarea')
202
+ const content = textarea?.value.trim()
203
+ if (!content) return
204
+ fire('send', { content })
205
+ if (textarea) textarea.value = ''
206
+ })
207
+ })
208
+ </script>
@@ -0,0 +1,12 @@
1
+ import Component from './Chat.astro'
2
+
3
+ // Auto-generated baseline story. Renders the component with empty args;
4
+ // components that require props show a render error (enrich by hand as needed).
5
+ const meta = {
6
+ title: 'Astro/Conversation',
7
+ component: Component,
8
+ }
9
+
10
+ export default meta
11
+
12
+ export const Default = { args: {} }
@@ -0,0 +1,29 @@
1
+ export { default as Chat } from './Chat.astro'
2
+
3
+ // Re-export core store, selectors, and types for convenience
4
+ export {
5
+ createConversation,
6
+ findMessage,
7
+ rootIdOf,
8
+ selectRoots,
9
+ selectReplies,
10
+ selectThreadMessages,
11
+ getReplyCount,
12
+ selectMainTimeline,
13
+ selectTimelineFromState,
14
+ type ChatAuthor,
15
+ type ChatRole,
16
+ type MessageStatus,
17
+ type ThreadingMode,
18
+ type MessageReaction,
19
+ type MessageAttachment,
20
+ type ChatMessage,
21
+ type Conversation,
22
+ type SendContext,
23
+ type TransportChunk,
24
+ type ChatTransport,
25
+ type ConversationState,
26
+ type ConversationConfig,
27
+ type SendOptions,
28
+ type ConversationAPI,
29
+ } from '../conversation/index.ts'