@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.
- package/dist/analytics/index.ts +5 -0
- package/dist/astro-accordion/Accordion.stories.ts +12 -0
- package/dist/astro-ai/Ai.stories.ts +12 -0
- package/dist/astro-analytics/Analytics.stories.ts +12 -0
- package/dist/astro-animated-text/AnimatedText.stories.ts +12 -0
- package/dist/astro-app-shell/AppShell.stories.ts +12 -0
- package/dist/astro-auth/Auth.stories.ts +12 -0
- package/dist/astro-avatar/Avatar.stories.ts +12 -0
- package/dist/astro-avatar-group/AvatarGroup.stories.ts +12 -0
- package/dist/astro-badge/Badge.stories.ts +16 -0
- package/dist/astro-bottom-nav/BottomNav.stories.ts +12 -0
- package/dist/astro-breadcrumbs/Breadcrumbs.stories.ts +12 -0
- package/dist/astro-button/Button.stories.ts +12 -0
- package/dist/astro-calendar/Calendar.stories.ts +12 -0
- package/dist/astro-callout/Callout.stories.ts +12 -0
- package/dist/astro-card/Card.stories.ts +12 -0
- package/dist/astro-card-grid/CardGrid.stories.ts +12 -0
- package/dist/astro-carousel/Carousel.stories.ts +12 -0
- package/dist/astro-charts/Charts.stories.ts +12 -0
- package/dist/astro-checkbox/Checkbox.stories.ts +12 -0
- package/dist/astro-code-block/CodeBlock.stories.ts +12 -0
- package/dist/astro-code-editor/CodeEditor.stories.ts +12 -0
- package/dist/astro-collapsible/Collapsible.stories.ts +12 -0
- package/dist/astro-command/Command.stories.ts +12 -0
- package/dist/astro-command-input/CommandInput.stories.ts +12 -0
- package/dist/astro-content-protection/ContentProtection.stories.ts +12 -0
- package/dist/astro-conversation/Chat.astro +208 -0
- package/dist/astro-conversation/Conversation.stories.ts +12 -0
- package/dist/astro-conversation/index.ts +29 -0
- package/dist/astro-cookie-consent/CookieConsent.astro +141 -0
- package/dist/astro-cookie-consent/index.ts +12 -0
- package/dist/astro-data-table/DataTable.stories.ts +12 -0
- package/dist/astro-date-picker/DatePicker.stories.ts +12 -0
- package/dist/astro-device-frame/DeviceFrame.stories.ts +12 -0
- package/dist/astro-dialog/Dialog.stories.ts +12 -0
- package/dist/astro-diff-viewer/DiffViewer.stories.ts +12 -0
- package/dist/astro-dropdown-menu/DropdownMenu.stories.ts +12 -0
- package/dist/astro-emoji-picker/EmojiPicker.stories.ts +12 -0
- package/dist/astro-feedback-dialog/FeedbackDialog.stories.ts +12 -0
- package/dist/astro-file-tree/FileTree.stories.ts +12 -0
- package/dist/astro-file-upload/FileUpload.stories.ts +12 -0
- package/dist/astro-footer/Footer.stories.ts +12 -0
- package/dist/astro-icon-system/IconSystem.stories.ts +12 -0
- package/dist/astro-inline-editor/InlineEditor.stories.ts +12 -0
- package/dist/astro-input/Input.stories.ts +12 -0
- package/dist/astro-input-group/InputGroup.stories.ts +12 -0
- package/dist/astro-install-prompt/InstallPrompt.stories.ts +12 -0
- package/dist/astro-keyboard-shortcut/KeyboardShortcut.stories.ts +12 -0
- package/dist/astro-language-selector/LanguageSelector.stories.ts +12 -0
- package/dist/astro-link-card/LinkCard.stories.ts +12 -0
- package/dist/astro-logger/Logger.stories.ts +12 -0
- package/dist/astro-markdown-renderer/MarkdownRenderer.stories.ts +12 -0
- package/dist/astro-mobile-nav/MobileNav.stories.ts +12 -0
- package/dist/astro-navbar/Navbar.stories.ts +12 -0
- package/dist/astro-otp-input/OtpInput.stories.ts +12 -0
- package/dist/astro-pagination/Pagination.stories.ts +12 -0
- package/dist/astro-payment/Payment.stories.ts +12 -0
- package/dist/astro-popover/Popover.stories.ts +12 -0
- package/dist/astro-presence-indicator/PresenceIndicator.stories.ts +12 -0
- package/dist/astro-progress-display/ProgressDisplay.stories.ts +12 -0
- package/dist/astro-radio/Radio.stories.ts +12 -0
- package/dist/astro-reaction-bar/ReactionBar.stories.ts +12 -0
- package/dist/astro-resizable-layout/ResizableLayout.stories.ts +12 -0
- package/dist/astro-rich-editor/RichEditor.stories.ts +12 -0
- package/dist/astro-search-bar/SearchBar.stories.ts +12 -0
- package/dist/astro-select/Select.stories.ts +12 -0
- package/dist/astro-sidebar/Sidebar.stories.ts +12 -0
- package/dist/astro-skeleton/Skeleton.stories.ts +12 -0
- package/dist/astro-skip-to-content/SkipToContent.stories.ts +12 -0
- package/dist/astro-slide-viewer/SlideViewer.stories.ts +12 -0
- package/dist/astro-status-indicator/StatusIndicator.stories.ts +12 -0
- package/dist/astro-steps/Steps.stories.ts +12 -0
- package/dist/astro-switch/Switch.stories.ts +12 -0
- package/dist/astro-table-of-contents/TableOfContents.stories.ts +12 -0
- package/dist/astro-tabs/Tabs.stories.ts +12 -0
- package/dist/astro-textarea/Textarea.stories.ts +12 -0
- package/dist/astro-theme/Theme.stories.ts +12 -0
- package/dist/astro-thread-view/ThreadView.stories.ts +12 -0
- package/dist/astro-toast/Toast.stories.ts +12 -0
- package/dist/astro-tooltip/Tooltip.stories.ts +12 -0
- package/dist/astro-version-selector/VersionSelector.stories.ts +12 -0
- package/dist/astro-video-player/VideoPlayer.stories.ts +12 -0
- package/dist/astro-voice-pill/VoicePill.stories.ts +12 -0
- package/dist/astro-waveform/Waveform.stories.ts +12 -0
- package/dist/conversation/conversation.ts +379 -0
- package/dist/conversation/index.ts +30 -0
- package/dist/conversation/selectors.ts +58 -0
- package/dist/conversation/types.ts +222 -0
- package/dist/cookie-consent/cookie-consent.ts +132 -0
- package/dist/cookie-consent/index.ts +10 -0
- package/dist/cookie-consent/types.ts +72 -0
- package/dist/index.ts +2 -0
- package/dist/shared/library-origin-error.ts +1 -2
- package/dist/shared/types.ts +1 -1
- package/dist/theme/theme-script.ts +1 -1
- package/package.json +3 -1
package/dist/analytics/index.ts
CHANGED
|
@@ -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'
|