@refraction-ui/react 0.19.1 → 0.19.2
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/chunk-533MCMSU.js +2355 -0
- package/dist/chunk-533MCMSU.js.map +7 -0
- package/dist/chunk-AMK4VCBG.js +250 -0
- package/dist/chunk-AMK4VCBG.js.map +7 -0
- package/dist/chunk-BOLFYVI6.js +90 -0
- package/dist/chunk-BOLFYVI6.js.map +7 -0
- package/dist/chunk-F7B64KT3.js +187 -0
- package/dist/chunk-F7B64KT3.js.map +7 -0
- package/dist/chunk-FD37DFCC.js +77 -0
- package/dist/chunk-FD37DFCC.js.map +7 -0
- package/dist/chunk-GFXGXZDB.js +23543 -0
- package/dist/chunk-GFXGXZDB.js.map +7 -0
- package/dist/chunk-H54FLNAF.js +132 -0
- package/dist/chunk-H54FLNAF.js.map +7 -0
- package/dist/chunk-INP46354.js +664 -0
- package/dist/chunk-INP46354.js.map +7 -0
- package/dist/chunk-O4R4W2KY.js +6460 -0
- package/dist/chunk-O4R4W2KY.js.map +7 -0
- package/dist/chunk-ONACRMG4.js +116 -0
- package/dist/chunk-ONACRMG4.js.map +7 -0
- package/dist/chunk-SDZ3LROZ.js +7 -0
- package/dist/chunk-SDZ3LROZ.js.map +7 -0
- package/dist/chunk-UJ6BBWQM.js +154 -0
- package/dist/chunk-UJ6BBWQM.js.map +7 -0
- package/dist/chunk-UNEM2TRI.js +225 -0
- package/dist/chunk-UNEM2TRI.js.map +7 -0
- package/dist/chunk-VK3EQ34F.js +92 -0
- package/dist/chunk-VK3EQ34F.js.map +7 -0
- package/dist/chunk-WK2IIUFO.js +316 -0
- package/dist/chunk-WK2IIUFO.js.map +7 -0
- package/dist/chunk-XWP763SH.js +3 -2
- package/dist/chunk-XWP763SH.js.map +7 -1
- package/dist/chunk-Y2FNNAIL.js +80 -0
- package/dist/chunk-Y2FNNAIL.js.map +1 -0
- package/dist/{chunk-4A4BW3GR.js → chunk-ZF3OHCGV.js} +28 -5
- package/dist/chunk-ZF3OHCGV.js.map +7 -0
- package/dist/form.cjs +5 -11
- package/dist/form.cjs.map +1 -1
- package/dist/form.js +6 -12
- package/dist/form.js.map +1 -1
- package/dist/index.cjs +320 -538
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +125 -28780
- package/dist/internal/react-app-shell/index.d.cts +2 -22
- package/dist/internal/react-app-shell/index.d.ts +2 -22
- package/dist/internal/react-collapsible/index.d.cts +0 -3
- package/dist/internal/react-collapsible/index.d.ts +0 -3
- package/dist/internal/react-combobox/index.d.cts +0 -3
- package/dist/internal/react-combobox/index.d.ts +0 -3
- package/dist/internal/react-command/index.d.cts +0 -3
- package/dist/internal/react-command/index.d.ts +0 -3
- package/dist/internal/react-data-table/index.d.cts +0 -3
- package/dist/internal/react-data-table/index.d.ts +0 -3
- package/dist/internal/react-dialog/index.d.cts +0 -3
- package/dist/internal/react-dialog/index.d.ts +0 -3
- package/dist/internal/react-dropdown-menu/index.d.cts +0 -3
- package/dist/internal/react-dropdown-menu/index.d.ts +0 -3
- package/dist/internal/react-feedback-dialog/index.d.cts +0 -3
- package/dist/internal/react-feedback-dialog/index.d.ts +0 -3
- package/dist/internal/react-keyboard-shortcut/index.d.cts +0 -6
- package/dist/internal/react-keyboard-shortcut/index.d.ts +0 -6
- package/dist/internal/react-popover/index.d.cts +0 -3
- package/dist/internal/react-popover/index.d.ts +0 -3
- package/dist/internal/react-sheet/index.d.cts +0 -3
- package/dist/internal/react-sheet/index.d.ts +0 -3
- package/dist/internal/react-toast/index.d.cts +0 -3
- package/dist/internal/react-toast/index.d.ts +0 -3
- package/dist/internal/react-tooltip/index.d.cts +0 -3
- package/dist/internal/react-tooltip/index.d.ts +0 -3
- package/dist/react-accordion.js +125 -0
- package/dist/react-accordion.js.map +7 -0
- package/dist/react-ai.js +257 -0
- package/dist/react-ai.js.map +7 -0
- package/dist/react-analytics.js +1698 -0
- package/dist/react-analytics.js.map +7 -0
- package/dist/react-animated-text.js +164 -0
- package/dist/react-animated-text.js.map +7 -0
- package/dist/react-app-shell.js +631 -0
- package/dist/react-app-shell.js.map +7 -0
- package/dist/react-audience-feature-card.js +53 -0
- package/dist/react-audience-feature-card.js.map +7 -0
- package/dist/react-audio-room.js +166 -0
- package/dist/react-audio-room.js.map +7 -0
- package/dist/react-auth.js +211 -0
- package/dist/react-auth.js.map +7 -0
- package/dist/react-avatar-group.js +154 -0
- package/dist/react-avatar-group.js.map +7 -0
- package/dist/react-avatar.js +168 -0
- package/dist/react-avatar.js.map +7 -0
- package/dist/react-badge.js +70 -0
- package/dist/react-badge.js.map +7 -0
- package/dist/react-bottom-nav.js +67 -0
- package/dist/react-bottom-nav.js.map +7 -0
- package/dist/react-brand-network-cell.js +92 -0
- package/dist/react-brand-network-cell.js.map +7 -0
- package/dist/react-breadcrumbs.js +104 -0
- package/dist/react-breadcrumbs.js.map +7 -0
- package/dist/react-browser-chrome-mock.js +89 -0
- package/dist/react-browser-chrome-mock.js.map +7 -0
- package/dist/react-button.js +13 -0
- package/dist/react-button.js.map +7 -0
- package/dist/react-calendar.js +301 -0
- package/dist/react-calendar.js.map +7 -0
- package/dist/react-call-controls.js +113 -0
- package/dist/react-call-controls.js.map +7 -0
- package/dist/react-callout.js +136 -0
- package/dist/react-callout.js.map +7 -0
- package/dist/react-card-grid.js +34 -0
- package/dist/react-card-grid.js.map +7 -0
- package/dist/react-card.js +184 -0
- package/dist/react-card.js.map +7 -0
- package/dist/react-carousel.js +132 -0
- package/dist/react-carousel.js.map +7 -0
- package/dist/react-charts.js +509 -0
- package/dist/react-charts.js.map +7 -0
- package/dist/react-checkbox.js +156 -0
- package/dist/react-checkbox.js.map +7 -0
- package/dist/react-checklist.js +168 -0
- package/dist/react-checklist.js.map +7 -0
- package/dist/react-code-block.js +78 -0
- package/dist/react-code-block.js.map +7 -0
- package/dist/react-code-editor.js +184 -0
- package/dist/react-code-editor.js.map +7 -0
- package/dist/react-collapsible.js +192 -0
- package/dist/react-collapsible.js.map +7 -0
- package/dist/react-combobox.js +609 -0
- package/dist/react-combobox.js.map +7 -0
- package/dist/react-command-input.js +202 -0
- package/dist/react-command-input.js.map +7 -0
- package/dist/react-command.js +364 -0
- package/dist/react-command.js.map +7 -0
- package/dist/react-composer.js +2036 -0
- package/dist/react-composer.js.map +7 -0
- package/dist/react-content-protection.js +133 -0
- package/dist/react-content-protection.js.map +7 -0
- package/dist/react-conversation.js +1226 -0
- package/dist/react-conversation.js.map +7 -0
- package/dist/react-cookie-consent.js +291 -0
- package/dist/react-cookie-consent.js.map +7 -0
- package/dist/react-data-table.js +310 -0
- package/dist/react-data-table.js.map +7 -0
- package/dist/react-date-picker.js +95 -0
- package/dist/react-date-picker.js.map +7 -0
- package/dist/react-device-frame.js +137 -0
- package/dist/react-device-frame.js.map +7 -0
- package/dist/react-dialog.js +218 -0
- package/dist/react-dialog.js.map +7 -0
- package/dist/react-diff-viewer.js +460 -0
- package/dist/react-diff-viewer.js.map +7 -0
- package/dist/react-dropdown-menu.js +292 -0
- package/dist/react-dropdown-menu.js.map +7 -0
- package/dist/react-editor-status-bar.js +148 -0
- package/dist/react-editor-status-bar.js.map +7 -0
- package/dist/react-editor-tabs.js +159 -0
- package/dist/react-editor-tabs.js.map +7 -0
- package/dist/react-emoji-picker.js +238 -0
- package/dist/react-emoji-picker.js.map +7 -0
- package/dist/react-empty-state.js +90 -0
- package/dist/react-empty-state.js.map +7 -0
- package/dist/react-feedback-dialog.js +275 -0
- package/dist/react-feedback-dialog.js.map +7 -0
- package/dist/react-file-tree.js +376 -0
- package/dist/react-file-tree.js.map +7 -0
- package/dist/react-file-upload.js +360 -0
- package/dist/react-file-upload.js.map +7 -0
- package/dist/react-floating-reactions.js +124 -0
- package/dist/react-floating-reactions.js.map +7 -0
- package/dist/react-flow-editor.js +198 -0
- package/dist/react-flow-editor.js.map +7 -0
- package/dist/react-footer.js +67 -0
- package/dist/react-footer.js.map +7 -0
- package/dist/react-graph-view.js +197 -0
- package/dist/react-graph-view.js.map +7 -0
- package/dist/react-icon-system.js +9 -0
- package/dist/react-icon-system.js.map +7 -0
- package/dist/react-infinite-canvas.js +8 -0
- package/dist/react-infinite-canvas.js.map +7 -0
- package/dist/react-inline-editor.js +216 -0
- package/dist/react-inline-editor.js.map +7 -0
- package/dist/react-input-group.js +150 -0
- package/dist/react-input-group.js.map +7 -0
- package/dist/react-input.js +10 -0
- package/dist/react-input.js.map +7 -0
- package/dist/react-install-prompt.js +132 -0
- package/dist/react-install-prompt.js.map +7 -0
- package/dist/react-kanban-board.js +198 -0
- package/dist/react-kanban-board.js.map +7 -0
- package/dist/react-keyboard-shortcut.js +36 -0
- package/dist/react-keyboard-shortcut.js.map +7 -0
- package/dist/react-language-selector.js +311 -0
- package/dist/react-language-selector.js.map +7 -0
- package/dist/react-link-card.js +34 -0
- package/dist/react-link-card.js.map +7 -0
- package/dist/react-live-captions.js +92 -0
- package/dist/react-live-captions.js.map +7 -0
- package/dist/react-live-cursors.js +145 -0
- package/dist/react-live-cursors.js.map +7 -0
- package/dist/react-live-transcript.js +117 -0
- package/dist/react-live-transcript.js.map +7 -0
- package/dist/react-location-selector.js +252 -0
- package/dist/react-location-selector.js.map +7 -0
- package/dist/react-logger.js +415 -0
- package/dist/react-logger.js.map +7 -0
- package/dist/react-markdown-renderer.js +12 -0
- package/dist/react-markdown-renderer.js.map +7 -0
- package/dist/react-marquee-strip.js +62 -0
- package/dist/react-marquee-strip.js.map +7 -0
- package/dist/react-mascot.js +367 -0
- package/dist/react-mascot.js.map +7 -0
- package/dist/react-mastery-bar.js +97 -0
- package/dist/react-mastery-bar.js.map +7 -0
- package/dist/react-mini-map.js +207 -0
- package/dist/react-mini-map.js.map +7 -0
- package/dist/react-mobile-nav.js +216 -0
- package/dist/react-mobile-nav.js.map +7 -0
- package/dist/react-navbar.js +96 -0
- package/dist/react-navbar.js.map +7 -0
- package/dist/react-numbered-steps.js +67 -0
- package/dist/react-numbered-steps.js.map +7 -0
- package/dist/react-otp-input.js +318 -0
- package/dist/react-otp-input.js.map +7 -0
- package/dist/react-pagination.js +199 -0
- package/dist/react-pagination.js.map +7 -0
- package/dist/react-password-input.js +104 -0
- package/dist/react-password-input.js.map +7 -0
- package/dist/react-payment.js +81 -0
- package/dist/react-payment.js.map +7 -0
- package/dist/react-popover.js +176 -0
- package/dist/react-popover.js.map +7 -0
- package/dist/react-pre-call-lobby.js +309 -0
- package/dist/react-pre-call-lobby.js.map +7 -0
- package/dist/react-presence-indicator.js +86 -0
- package/dist/react-presence-indicator.js.map +7 -0
- package/dist/react-pricing-card.js +142 -0
- package/dist/react-pricing-card.js.map +7 -0
- package/dist/react-progress-display.js +188 -0
- package/dist/react-progress-display.js.map +7 -0
- package/dist/react-radial-gauge.js +193 -0
- package/dist/react-radial-gauge.js.map +7 -0
- package/dist/react-radio.js +169 -0
- package/dist/react-radio.js.map +7 -0
- package/dist/react-rating-scale.js +171 -0
- package/dist/react-rating-scale.js.map +7 -0
- package/dist/react-reaction-bar.js +111 -0
- package/dist/react-reaction-bar.js.map +7 -0
- package/dist/react-resizable-layout.js +298 -0
- package/dist/react-resizable-layout.js.map +7 -0
- package/dist/react-rich-editor.js +1 -0
- package/dist/react-rich-editor.js.map +7 -0
- package/dist/react-search-bar.js +285 -0
- package/dist/react-search-bar.js.map +7 -0
- package/dist/react-section-head.js +65 -0
- package/dist/react-section-head.js.map +7 -0
- package/dist/react-segmented-control.js +215 -0
- package/dist/react-segmented-control.js.map +7 -0
- package/dist/react-select.js +389 -0
- package/dist/react-select.js.map +7 -0
- package/dist/react-separator.js +86 -0
- package/dist/react-separator.js.map +7 -0
- package/dist/react-sheet.js +310 -0
- package/dist/react-sheet.js.map +7 -0
- package/dist/react-sidebar.js +100 -0
- package/dist/react-sidebar.js.map +7 -0
- package/dist/react-skeleton.js +91 -0
- package/dist/react-skeleton.js.map +7 -0
- package/dist/react-skip-to-content.js +38 -0
- package/dist/react-skip-to-content.js.map +7 -0
- package/dist/react-slide-viewer.js +264 -0
- package/dist/react-slide-viewer.js.map +7 -0
- package/dist/react-slider.js +105 -0
- package/dist/react-slider.js.map +7 -0
- package/dist/react-slot-picker.js +184 -0
- package/dist/react-slot-picker.js.map +7 -0
- package/dist/react-social-auth-button.js +169 -0
- package/dist/react-social-auth-button.js.map +7 -0
- package/dist/react-sortable-list.js +204 -0
- package/dist/react-sortable-list.js.map +7 -0
- package/dist/react-stat-grid.js +69 -0
- package/dist/react-stat-grid.js.map +7 -0
- package/dist/react-status-indicator.js +117 -0
- package/dist/react-status-indicator.js.map +7 -0
- package/dist/react-steps.js +167 -0
- package/dist/react-steps.js.map +7 -0
- package/dist/react-sticky-note.js +131 -0
- package/dist/react-sticky-note.js.map +7 -0
- package/dist/react-switch.js +139 -0
- package/dist/react-switch.js.map +7 -0
- package/dist/react-table-of-contents.js +80 -0
- package/dist/react-table-of-contents.js.map +7 -0
- package/dist/react-tabs.js +230 -0
- package/dist/react-tabs.js.map +7 -0
- package/dist/react-terminal.js +101 -0
- package/dist/react-terminal.js.map +7 -0
- package/dist/react-test-results.js +158 -0
- package/dist/react-test-results.js.map +7 -0
- package/dist/react-textarea.js +89 -0
- package/dist/react-textarea.js.map +7 -0
- package/dist/react-thread-view.js +139 -0
- package/dist/react-thread-view.js.map +7 -0
- package/dist/react-timeline.js +153 -0
- package/dist/react-timeline.js.map +7 -0
- package/dist/react-toast.js +253 -0
- package/dist/react-toast.js.map +7 -0
- package/dist/react-tooltip.js +221 -0
- package/dist/react-tooltip.js.map +7 -0
- package/dist/react-version-selector.js +270 -0
- package/dist/react-version-selector.js.map +7 -0
- package/dist/react-video-grid.js +129 -0
- package/dist/react-video-grid.js.map +7 -0
- package/dist/react-video-player.js +206 -0
- package/dist/react-video-player.js.map +7 -0
- package/dist/react-video-tile.js +8 -0
- package/dist/react-video-tile.js.map +7 -0
- package/dist/react-voice-pill.js +314 -0
- package/dist/react-voice-pill.js.map +7 -0
- package/dist/react-waveform.js +558 -0
- package/dist/react-waveform.js.map +7 -0
- package/dist/react-wizard.js +337 -0
- package/dist/react-wizard.js.map +7 -0
- package/dist/theme.js +1 -1
- package/package.json +2 -2
- package/dist/chunk-4A4BW3GR.js.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
generateId
|
|
3
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
4
|
+
|
|
5
|
+
// ../thread-view/dist/index.js
|
|
6
|
+
function formatTimestamp(date) {
|
|
7
|
+
const hours = date.getHours();
|
|
8
|
+
const minutes = date.getMinutes();
|
|
9
|
+
const ampm = hours >= 12 ? "PM" : "AM";
|
|
10
|
+
const displayHours = hours % 12 || 12;
|
|
11
|
+
const displayMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
|
|
12
|
+
return `${displayHours}:${displayMinutes} ${ampm}`;
|
|
13
|
+
}
|
|
14
|
+
function formatRelativeTime(date, now) {
|
|
15
|
+
const reference = now ?? /* @__PURE__ */ new Date();
|
|
16
|
+
const diffMs = reference.getTime() - date.getTime();
|
|
17
|
+
const diffSeconds = Math.floor(diffMs / 1e3);
|
|
18
|
+
const diffMinutes = Math.floor(diffSeconds / 60);
|
|
19
|
+
const diffHours = Math.floor(diffMinutes / 60);
|
|
20
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
21
|
+
if (diffSeconds < 60) return "just now";
|
|
22
|
+
if (diffMinutes < 60) return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`;
|
|
23
|
+
if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
|
|
24
|
+
if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
|
|
25
|
+
return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
|
|
26
|
+
}
|
|
27
|
+
function createThreadView(props) {
|
|
28
|
+
const { messages, onReply, onReact, currentUserId } = props;
|
|
29
|
+
let replyingTo = null;
|
|
30
|
+
const threadId = generateId("rfr-thread");
|
|
31
|
+
const labelId = generateId("rfr-thread-label");
|
|
32
|
+
function startReply(messageId) {
|
|
33
|
+
replyingTo = messageId;
|
|
34
|
+
}
|
|
35
|
+
function cancelReply() {
|
|
36
|
+
replyingTo = null;
|
|
37
|
+
}
|
|
38
|
+
function reply(messageId, content) {
|
|
39
|
+
onReply?.(messageId, content);
|
|
40
|
+
replyingTo = null;
|
|
41
|
+
}
|
|
42
|
+
function react(messageId, emoji) {
|
|
43
|
+
onReact?.(messageId, emoji);
|
|
44
|
+
}
|
|
45
|
+
const ariaProps = {
|
|
46
|
+
role: "log",
|
|
47
|
+
"aria-label": "Message thread",
|
|
48
|
+
"aria-live": "polite",
|
|
49
|
+
id: threadId
|
|
50
|
+
};
|
|
51
|
+
function getMessageAriaProps(message) {
|
|
52
|
+
const isOwn = currentUserId && message.author.id === currentUserId;
|
|
53
|
+
return {
|
|
54
|
+
role: "article",
|
|
55
|
+
"aria-label": `Message from ${message.author.name}${isOwn ? " (you)" : ""} at ${formatTimestamp(message.timestamp)}`
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function getReplyButtonAriaProps(_messageId) {
|
|
59
|
+
return {
|
|
60
|
+
role: "button",
|
|
61
|
+
"aria-label": `Reply to message`
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
state: {
|
|
66
|
+
messages,
|
|
67
|
+
get replyingTo() {
|
|
68
|
+
return replyingTo;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
startReply,
|
|
72
|
+
cancelReply,
|
|
73
|
+
reply,
|
|
74
|
+
react,
|
|
75
|
+
formatTimestamp,
|
|
76
|
+
formatRelativeTime,
|
|
77
|
+
ariaProps,
|
|
78
|
+
getMessageAriaProps,
|
|
79
|
+
getReplyButtonAriaProps,
|
|
80
|
+
ids: {
|
|
81
|
+
thread: threadId,
|
|
82
|
+
label: labelId
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
var threadContainerStyles = "flex flex-col gap-1";
|
|
87
|
+
var threadMessageStyles = "flex gap-3 px-4 py-2 hover:bg-accent/50 rounded-md transition-colors group";
|
|
88
|
+
var threadAvatarStyles = "h-9 w-9 rounded-full bg-muted flex items-center justify-center text-sm font-medium overflow-hidden flex-shrink-0";
|
|
89
|
+
var threadContentStyles = "flex-1 min-w-0";
|
|
90
|
+
var threadAuthorStyles = "font-semibold text-sm";
|
|
91
|
+
var threadTimestampStyles = "text-xs text-muted-foreground ml-2";
|
|
92
|
+
var threadBodyStyles = "text-sm mt-0.5 whitespace-pre-wrap break-words";
|
|
93
|
+
var threadReactionsStyles = "flex flex-wrap gap-1 mt-1";
|
|
94
|
+
var threadReplyIndicatorStyles = "flex items-center gap-1 mt-1 text-xs text-primary cursor-pointer hover:underline";
|
|
95
|
+
var threadActionsStyles = "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity";
|
|
96
|
+
var threadAttachmentStyles = "flex items-center gap-2 mt-1 p-2 rounded border text-xs bg-muted/50";
|
|
97
|
+
var threadEditedStyles = "text-xs text-muted-foreground ml-1";
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
formatTimestamp,
|
|
101
|
+
formatRelativeTime,
|
|
102
|
+
createThreadView,
|
|
103
|
+
threadContainerStyles,
|
|
104
|
+
threadMessageStyles,
|
|
105
|
+
threadAvatarStyles,
|
|
106
|
+
threadContentStyles,
|
|
107
|
+
threadAuthorStyles,
|
|
108
|
+
threadTimestampStyles,
|
|
109
|
+
threadBodyStyles,
|
|
110
|
+
threadReactionsStyles,
|
|
111
|
+
threadReplyIndicatorStyles,
|
|
112
|
+
threadActionsStyles,
|
|
113
|
+
threadAttachmentStyles,
|
|
114
|
+
threadEditedStyles
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=chunk-ONACRMG4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../thread-view/src/thread-view.ts", "../../thread-view/src/thread-view.styles.ts"],
|
|
4
|
+
"sourcesContent": ["import { generateId } from '@refraction-ui/shared'\n\nexport interface MessageReaction {\n emoji: string\n count: number\n userReacted: boolean\n}\n\nexport interface MessageAttachment {\n id: string\n name: string\n url: string\n type: string\n size?: number\n}\n\nexport interface MessageData {\n /** Unique message ID */\n id: string\n /** Author display info */\n author: {\n id: string\n name: string\n avatarUrl?: string\n }\n /** Message content (can contain markdown) */\n content: string\n /** Message timestamp */\n timestamp: Date\n /** Reactions on this message */\n reactions?: MessageReaction[]\n /** Threaded replies */\n replies?: MessageData[]\n /** File attachments */\n attachments?: MessageAttachment[]\n /** Whether this message has been edited */\n edited?: boolean\n}\n\nexport interface ThreadViewProps {\n /** Messages to display */\n messages: MessageData[]\n /** Callback when a reply is sent */\n onReply?: (messageId: string, content: string) => void\n /** Callback when a reaction is toggled */\n onReact?: (messageId: string, emoji: string) => void\n /** Current user ID (for highlighting own messages) */\n currentUserId?: string\n}\n\nexport interface ThreadViewState {\n messages: MessageData[]\n replyingTo: string | null\n}\n\nexport interface ThreadViewAPI {\n /** Current state */\n state: ThreadViewState\n /** Start replying to a message */\n startReply(messageId: string): void\n /** Cancel reply */\n cancelReply(): void\n /** Send a reply */\n reply(messageId: string, content: string): void\n /** Toggle a reaction on a message */\n react(messageId: string, emoji: string): void\n /** Format a timestamp for display */\n formatTimestamp(date: Date): string\n /** Format relative time (e.g., \"2 minutes ago\") */\n formatRelativeTime(date: Date): string\n /** ARIA props for the thread container */\n ariaProps: Record<string, string | number | boolean>\n /** Get ARIA props for a message */\n getMessageAriaProps(message: MessageData): Record<string, string | number | boolean>\n /** Get ARIA props for a reply button */\n getReplyButtonAriaProps(messageId: string): Record<string, string | number | boolean>\n /** Generated IDs */\n ids: {\n thread: string\n label: string\n }\n}\n\nexport function formatTimestamp(date: Date): string {\n const hours = date.getHours()\n const minutes = date.getMinutes()\n const ampm = hours >= 12 ? 'PM' : 'AM'\n const displayHours = hours % 12 || 12\n const displayMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`\n return `${displayHours}:${displayMinutes} ${ampm}`\n}\n\nexport function formatRelativeTime(date: Date, now?: Date): string {\n const reference = now ?? new Date()\n const diffMs = reference.getTime() - date.getTime()\n const diffSeconds = Math.floor(diffMs / 1000)\n const diffMinutes = Math.floor(diffSeconds / 60)\n const diffHours = Math.floor(diffMinutes / 60)\n const diffDays = Math.floor(diffHours / 24)\n\n if (diffSeconds < 60) return 'just now'\n if (diffMinutes < 60) return `${diffMinutes} minute${diffMinutes === 1 ? '' : 's'} ago`\n if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? '' : 's'} ago`\n if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? '' : 's'} ago`\n\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\nexport function createThreadView(props: ThreadViewProps): ThreadViewAPI {\n const { messages, onReply, onReact, currentUserId } = props\n\n let replyingTo: string | null = null\n\n const threadId = generateId('rfr-thread')\n const labelId = generateId('rfr-thread-label')\n\n function startReply(messageId: string): void {\n replyingTo = messageId\n }\n\n function cancelReply(): void {\n replyingTo = null\n }\n\n function reply(messageId: string, content: string): void {\n onReply?.(messageId, content)\n replyingTo = null\n }\n\n function react(messageId: string, emoji: string): void {\n onReact?.(messageId, emoji)\n }\n\n const ariaProps: Record<string, string | number | boolean> = {\n role: 'log',\n 'aria-label': 'Message thread',\n 'aria-live': 'polite',\n id: threadId,\n }\n\n function getMessageAriaProps(message: MessageData): Record<string, string | number | boolean> {\n const isOwn = currentUserId && message.author.id === currentUserId\n return {\n role: 'article',\n 'aria-label': `Message from ${message.author.name}${isOwn ? ' (you)' : ''} at ${formatTimestamp(message.timestamp)}`,\n }\n }\n\n function getReplyButtonAriaProps(_messageId: string): Record<string, string | number | boolean> {\n return {\n role: 'button',\n 'aria-label': `Reply to message`,\n }\n }\n\n return {\n state: {\n messages,\n get replyingTo() { return replyingTo },\n },\n startReply,\n cancelReply,\n reply,\n react,\n formatTimestamp,\n formatRelativeTime,\n ariaProps,\n getMessageAriaProps,\n getReplyButtonAriaProps,\n ids: {\n thread: threadId,\n label: labelId,\n },\n }\n}\n", "export const threadContainerStyles =\n 'flex flex-col gap-1'\n\nexport const threadMessageStyles =\n 'flex gap-3 px-4 py-2 hover:bg-accent/50 rounded-md transition-colors group'\n\nexport const threadAvatarStyles =\n 'h-9 w-9 rounded-full bg-muted flex items-center justify-center text-sm font-medium overflow-hidden flex-shrink-0'\n\nexport const threadContentStyles =\n 'flex-1 min-w-0'\n\nexport const threadAuthorStyles =\n 'font-semibold text-sm'\n\nexport const threadTimestampStyles =\n 'text-xs text-muted-foreground ml-2'\n\nexport const threadBodyStyles =\n 'text-sm mt-0.5 whitespace-pre-wrap break-words'\n\nexport const threadReactionsStyles =\n 'flex flex-wrap gap-1 mt-1'\n\nexport const threadReplyIndicatorStyles =\n 'flex items-center gap-1 mt-1 text-xs text-primary cursor-pointer hover:underline'\n\nexport const threadActionsStyles =\n 'flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity'\n\nexport const threadAttachmentStyles =\n 'flex items-center gap-2 mt-1 p-2 rounded border text-xs bg-muted/50'\n\nexport const threadEditedStyles =\n 'text-xs text-muted-foreground ml-1'\n"],
|
|
5
|
+
"mappings": ";;;;;AAmFO,SAAS,gBAAgB,MAAoB;AAClD,QAAM,QAAQ,KAAK,SAAA;AACnB,QAAM,UAAU,KAAK,WAAA;AACrB,QAAM,OAAO,SAAS,KAAK,OAAO;AAClC,QAAM,eAAe,QAAQ,MAAM;AACnC,QAAM,iBAAiB,UAAU,KAAK,IAAI,OAAO,KAAK,GAAG,OAAO;AAChE,SAAO,GAAG,YAAY,IAAI,cAAc,IAAI,IAAI;AAClD;AAEO,SAAS,mBAAmB,MAAY,KAAoB;AACjE,QAAM,YAAY,OAAO,oBAAI,KAAA;AAC7B,QAAM,SAAS,UAAU,QAAA,IAAY,KAAK,QAAA;AAC1C,QAAM,cAAc,KAAK,MAAM,SAAS,GAAI;AAC5C,QAAM,cAAc,KAAK,MAAM,cAAc,EAAE;AAC/C,QAAM,YAAY,KAAK,MAAM,cAAc,EAAE;AAC7C,QAAM,WAAW,KAAK,MAAM,YAAY,EAAE;AAE1C,MAAI,cAAc,GAAI,QAAO;AAC7B,MAAI,cAAc,GAAI,QAAO,GAAG,WAAW,UAAU,gBAAgB,IAAI,KAAK,GAAG;AACjF,MAAI,YAAY,GAAI,QAAO,GAAG,SAAS,QAAQ,cAAc,IAAI,KAAK,GAAG;AACzE,MAAI,WAAW,EAAG,QAAO,GAAG,QAAQ,OAAO,aAAa,IAAI,KAAK,GAAG;AAEpE,SAAO,KAAK,mBAAmB,SAAS,EAAE,OAAO,SAAS,KAAK,WAAW,MAAM,UAAA,CAAW;AAC7F;AAEO,SAAS,iBAAiB,OAAuC;AACtE,QAAM,EAAE,UAAU,SAAS,SAAS,cAAA,IAAkB;AAEtD,MAAI,aAA4B;AAEhC,QAAM,WAAW,WAAW,YAAY;AACxC,QAAM,UAAU,WAAW,kBAAkB;AAE7C,WAAS,WAAW,WAAyB;AAC3C,iBAAa;EACf;AAEA,WAAS,cAAoB;AAC3B,iBAAa;EACf;AAEA,WAAS,MAAM,WAAmB,SAAuB;AACvD,cAAU,WAAW,OAAO;AAC5B,iBAAa;EACf;AAEA,WAAS,MAAM,WAAmB,OAAqB;AACrD,cAAU,WAAW,KAAK;EAC5B;AAEA,QAAM,YAAuD;IAC3D,MAAM;IACN,cAAc;IACd,aAAa;IACb,IAAI;EAAA;AAGN,WAAS,oBAAoB,SAAiE;AAC5F,UAAM,QAAQ,iBAAiB,QAAQ,OAAO,OAAO;AACrD,WAAO;MACL,MAAM;MACN,cAAc,gBAAgB,QAAQ,OAAO,IAAI,GAAG,QAAQ,WAAW,EAAE,OAAO,gBAAgB,QAAQ,SAAS,CAAC;IAAA;EAEtH;AAEA,WAAS,wBAAwB,YAA+D;AAC9F,WAAO;MACL,MAAM;MACN,cAAc;IAAA;EAElB;AAEA,SAAO;IACL,OAAO;MACL;MACA,IAAI,aAAa;AAAE,eAAO;MAAW;IAAA;IAEvC;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,KAAK;MACH,QAAQ;MACR,OAAO;IAAA;EACT;AAEJ;AC9KO,IAAM,wBACX;AAEK,IAAM,sBACX;AAEK,IAAM,qBACX;AAEK,IAAM,sBACX;AAEK,IAAM,qBACX;AAEK,IAAM,wBACX;AAEK,IAAM,mBACX;AAEK,IAAM,wBACX;AAEK,IAAM,6BACX;AAEK,IAAM,sBACX;AAEK,IAAM,yBACX;AAEK,IAAM,qBACX;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn,
|
|
3
|
+
cva
|
|
4
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
5
|
+
|
|
6
|
+
// ../react-input/dist/index.js
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
// ../input/dist/index.js
|
|
10
|
+
function resolveAriaInvalid(ariaInvalid, validationState) {
|
|
11
|
+
if (ariaInvalid !== void 0) {
|
|
12
|
+
return ariaInvalid;
|
|
13
|
+
}
|
|
14
|
+
if (validationState === "invalid") {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
if (validationState === "valid") {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
function createInput(props = {}) {
|
|
23
|
+
const {
|
|
24
|
+
disabled = false,
|
|
25
|
+
readOnly = false,
|
|
26
|
+
required = false,
|
|
27
|
+
"aria-invalid": ariaInvalid,
|
|
28
|
+
validationState
|
|
29
|
+
} = props;
|
|
30
|
+
const resolvedInvalid = resolveAriaInvalid(ariaInvalid, validationState);
|
|
31
|
+
const ariaProps = {};
|
|
32
|
+
if (disabled) {
|
|
33
|
+
ariaProps["aria-disabled"] = true;
|
|
34
|
+
}
|
|
35
|
+
if (resolvedInvalid !== void 0) {
|
|
36
|
+
ariaProps["aria-invalid"] = resolvedInvalid;
|
|
37
|
+
}
|
|
38
|
+
if (required) {
|
|
39
|
+
ariaProps["aria-required"] = true;
|
|
40
|
+
}
|
|
41
|
+
const dataAttributes = {};
|
|
42
|
+
if (disabled) {
|
|
43
|
+
dataAttributes["data-disabled"] = "";
|
|
44
|
+
}
|
|
45
|
+
if (readOnly) {
|
|
46
|
+
dataAttributes["data-readonly"] = "";
|
|
47
|
+
}
|
|
48
|
+
if (resolvedInvalid === true) {
|
|
49
|
+
dataAttributes["data-invalid"] = "";
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
ariaProps,
|
|
53
|
+
dataAttributes
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
var inputVariants = cva({
|
|
57
|
+
base: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
58
|
+
variants: {
|
|
59
|
+
size: {
|
|
60
|
+
sm: "h-8 text-xs",
|
|
61
|
+
default: "h-9",
|
|
62
|
+
lg: "h-10 text-base"
|
|
63
|
+
},
|
|
64
|
+
validationState: {
|
|
65
|
+
valid: "border-green-500 focus-visible:ring-green-500",
|
|
66
|
+
invalid: "border-destructive focus-visible:ring-destructive"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
defaultVariants: {
|
|
70
|
+
size: "default"
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// ../react-input/dist/index.js
|
|
75
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
76
|
+
var CheckIcon = /* @__PURE__ */ jsx(
|
|
77
|
+
"svg",
|
|
78
|
+
{
|
|
79
|
+
"aria-hidden": "true",
|
|
80
|
+
viewBox: "0 0 24 24",
|
|
81
|
+
fill: "none",
|
|
82
|
+
stroke: "currentColor",
|
|
83
|
+
strokeWidth: "2",
|
|
84
|
+
strokeLinecap: "round",
|
|
85
|
+
strokeLinejoin: "round",
|
|
86
|
+
className: "pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-green-500",
|
|
87
|
+
children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
var Input = React.forwardRef(
|
|
91
|
+
function Input2({
|
|
92
|
+
type = "text",
|
|
93
|
+
size,
|
|
94
|
+
className,
|
|
95
|
+
disabled,
|
|
96
|
+
readOnly,
|
|
97
|
+
required,
|
|
98
|
+
validationState,
|
|
99
|
+
leadingIcon,
|
|
100
|
+
"aria-invalid": ariaInvalid,
|
|
101
|
+
...props
|
|
102
|
+
}, ref) {
|
|
103
|
+
const api = createInput({
|
|
104
|
+
type,
|
|
105
|
+
disabled,
|
|
106
|
+
readOnly,
|
|
107
|
+
required,
|
|
108
|
+
"aria-invalid": ariaInvalid === void 0 ? void 0 : ariaInvalid === true || ariaInvalid === "true",
|
|
109
|
+
validationState
|
|
110
|
+
});
|
|
111
|
+
const hasWrapper = leadingIcon != null || validationState != null;
|
|
112
|
+
const classes = cn(
|
|
113
|
+
inputVariants({ size, validationState }),
|
|
114
|
+
leadingIcon != null && "pl-9",
|
|
115
|
+
validationState === "valid" && "pr-9",
|
|
116
|
+
className
|
|
117
|
+
);
|
|
118
|
+
const inputEl = /* @__PURE__ */ jsx(
|
|
119
|
+
"input",
|
|
120
|
+
{
|
|
121
|
+
ref,
|
|
122
|
+
type,
|
|
123
|
+
className: classes,
|
|
124
|
+
disabled,
|
|
125
|
+
readOnly,
|
|
126
|
+
required,
|
|
127
|
+
...api.ariaProps,
|
|
128
|
+
...api.dataAttributes,
|
|
129
|
+
...props
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
if (!hasWrapper) {
|
|
133
|
+
return inputEl;
|
|
134
|
+
}
|
|
135
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
136
|
+
leadingIcon != null && /* @__PURE__ */ jsx(
|
|
137
|
+
"span",
|
|
138
|
+
{
|
|
139
|
+
"aria-hidden": "true",
|
|
140
|
+
className: "pointer-events-none absolute left-3 top-1/2 flex size-4 -translate-y-1/2 items-center justify-center text-muted-foreground",
|
|
141
|
+
children: leadingIcon
|
|
142
|
+
}
|
|
143
|
+
),
|
|
144
|
+
inputEl,
|
|
145
|
+
validationState === "valid" && CheckIcon
|
|
146
|
+
] });
|
|
147
|
+
}
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
export {
|
|
151
|
+
inputVariants,
|
|
152
|
+
Input
|
|
153
|
+
};
|
|
154
|
+
//# sourceMappingURL=chunk-UJ6BBWQM.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../input/src/input.ts", "../../input/src/input.styles.ts", "../../react-input/src/input.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { AccessibilityProps } from '@refraction-ui/shared'\n\nexport type InputType = 'text' | 'password' | 'email' | 'number' | 'tel' | 'url' | 'search'\n\n/** High-level validation affordance for an input. */\nexport type InputValidationState = 'valid' | 'invalid'\n\nexport interface InputProps {\n type?: InputType\n disabled?: boolean\n readOnly?: boolean\n required?: boolean\n placeholder?: string\n 'aria-invalid'?: boolean\n /**\n * Validation affordance. The core derives `aria-invalid` from this:\n * - `'valid'` → `aria-invalid={false}`\n * - `'invalid'` → `aria-invalid={true}`\n *\n * An explicit `aria-invalid` prop always takes precedence.\n */\n validationState?: InputValidationState\n}\n\nexport interface InputAPI {\n /** ARIA attributes to spread on the element */\n ariaProps: Partial<AccessibilityProps> & { 'aria-invalid'?: boolean; 'aria-required'?: boolean }\n /** Data attributes for CSS styling hooks */\n dataAttributes: Record<string, string>\n}\n\n/**\n * Resolve the effective `aria-invalid` value from an explicit `aria-invalid`\n * prop and a high-level `validationState`. The explicit prop always wins;\n * otherwise `validationState` maps `'invalid' → true`, `'valid' → false`, and\n * an absent state leaves `aria-invalid` undefined (legacy behavior).\n */\nexport function resolveAriaInvalid(\n ariaInvalid: boolean | undefined,\n validationState: InputValidationState | undefined,\n): boolean | undefined {\n if (ariaInvalid !== undefined) {\n return ariaInvalid\n }\n if (validationState === 'invalid') {\n return true\n }\n if (validationState === 'valid') {\n return false\n }\n return undefined\n}\n\nexport function createInput(props: InputProps = {}): InputAPI {\n const {\n disabled = false,\n readOnly = false,\n required = false,\n 'aria-invalid': ariaInvalid,\n validationState,\n } = props\n\n const resolvedInvalid = resolveAriaInvalid(ariaInvalid, validationState)\n\n const ariaProps: InputAPI['ariaProps'] = {}\n\n if (disabled) {\n ariaProps['aria-disabled'] = true\n }\n\n if (resolvedInvalid !== undefined) {\n ariaProps['aria-invalid'] = resolvedInvalid\n }\n\n if (required) {\n ariaProps['aria-required'] = true\n }\n\n const dataAttributes: Record<string, string> = {}\n\n if (disabled) {\n dataAttributes['data-disabled'] = ''\n }\n\n if (readOnly) {\n dataAttributes['data-readonly'] = ''\n }\n\n if (resolvedInvalid === true) {\n dataAttributes['data-invalid'] = ''\n }\n\n return {\n ariaProps,\n dataAttributes,\n }\n}\n\nexport function getInputAriaProps(state: {\n disabled?: boolean\n readOnly?: boolean\n required?: boolean\n invalid?: boolean\n}): Partial<AccessibilityProps> & { 'aria-invalid'?: boolean; 'aria-required'?: boolean } {\n const ariaProps: Partial<AccessibilityProps> & { 'aria-invalid'?: boolean; 'aria-required'?: boolean } = {}\n\n if (state.disabled) {\n ariaProps['aria-disabled'] = true\n }\n\n if (state.invalid) {\n ariaProps['aria-invalid'] = true\n }\n\n if (state.required) {\n ariaProps['aria-required'] = true\n }\n\n return ariaProps\n}\n", "import { cva } from '@refraction-ui/shared'\n\nexport const inputVariants = cva({\n base: 'flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50',\n variants: {\n size: {\n sm: 'h-8 text-xs',\n default: 'h-9',\n lg: 'h-10 text-base',\n },\n validationState: {\n valid: 'border-green-500 focus-visible:ring-green-500',\n invalid: 'border-destructive focus-visible:ring-destructive',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n})\n", "import * as React from 'react'\nimport {\n createInput,\n inputVariants,\n type InputValidationState,\n} from '@refraction-ui/input'\nimport { cn } from '@refraction-ui/shared'\n\nexport interface InputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'size'> {\n /**\n * Visual size of the input.\n *\n * Note: this prop intentionally **shadows** the native HTML `size`\n * attribute (which would otherwise control the visible character width\n * of the `<input>` element). We re-purpose the name for our visual\n * size variants (`'sm' | 'default' | 'lg'`) because in practice almost\n * no one uses the native character-width `size` attribute, while\n * visual sizing is a near-universal need.\n *\n * If you need the native character-width behavior, set it via\n * `htmlSize` on the underlying DOM through a `ref`, or render a plain\n * `<input>` instead.\n *\n * See the package README's \"Sizing\" section for more.\n */\n size?: 'sm' | 'default' | 'lg'\n /**\n * Validation affordance. Tints the border and wires `aria-invalid`:\n * - `'valid'` → green border, trailing check icon, `aria-invalid={false}`\n * - `'invalid'` → destructive border, `aria-invalid={true}`\n *\n * When omitted the input is rendered without a wrapper (identical to the\n * legacy bare `<input>` markup), unless `leadingIcon` is set.\n */\n validationState?: InputValidationState\n /**\n * Optional icon rendered inside the input's left padding. When provided\n * (or when `validationState` is set) the input is wrapped in a relative\n * `<div>` and gains left padding to make room for the icon.\n */\n leadingIcon?: React.ReactNode\n}\n\nconst CheckIcon = (\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className=\"pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-green-500\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n)\n\n/**\n * Input component — renders a styled input with size support.\n *\n * Uses the headless @refraction-ui/input core for state, ARIA, and data attributes.\n * Styling via Tailwind utility classes (no external CSS-in-JS).\n */\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n function Input(\n {\n type = 'text',\n size,\n className,\n disabled,\n readOnly,\n required,\n validationState,\n leadingIcon,\n 'aria-invalid': ariaInvalid,\n ...props\n },\n ref,\n ) {\n // The core owns the `aria-invalid` derivation and the validation border\n // tokens; the React layer only does DOM composition (wrapper + icons).\n const api = createInput({\n type: type as import('@refraction-ui/input').InputType,\n disabled,\n readOnly,\n required,\n 'aria-invalid': ariaInvalid === undefined ? undefined : ariaInvalid === true || ariaInvalid === 'true',\n validationState,\n })\n\n const hasWrapper = leadingIcon != null || validationState != null\n\n const classes = cn(\n inputVariants({ size, validationState }),\n leadingIcon != null && 'pl-9',\n validationState === 'valid' && 'pr-9',\n className,\n )\n\n const inputEl = (\n <input\n ref={ref}\n type={type}\n className={classes}\n disabled={disabled}\n readOnly={readOnly}\n required={required}\n {...api.ariaProps}\n {...api.dataAttributes}\n {...props}\n />\n )\n\n if (!hasWrapper) {\n return inputEl\n }\n\n return (\n <div className=\"relative\">\n {leadingIcon != null && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute left-3 top-1/2 flex size-4 -translate-y-1/2 items-center justify-center text-muted-foreground\"\n >\n {leadingIcon}\n </span>\n )}\n {inputEl}\n {validationState === 'valid' && CheckIcon}\n </div>\n )\n },\n)\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;AAqCO,SAAS,mBACd,aACA,iBACqB;AACrB,MAAI,gBAAgB,QAAW;AAC7B,WAAO;EACT;AACA,MAAI,oBAAoB,WAAW;AACjC,WAAO;EACT;AACA,MAAI,oBAAoB,SAAS;AAC/B,WAAO;EACT;AACA,SAAO;AACT;AAEO,SAAS,YAAY,QAAoB,CAAA,GAAc;AAC5D,QAAM;IACJ,WAAW;IACX,WAAW;IACX,WAAW;IACX,gBAAgB;IAChB;EAAA,IACE;AAEJ,QAAM,kBAAkB,mBAAmB,aAAa,eAAe;AAEvE,QAAM,YAAmC,CAAA;AAEzC,MAAI,UAAU;AACZ,cAAU,eAAe,IAAI;EAC/B;AAEA,MAAI,oBAAoB,QAAW;AACjC,cAAU,cAAc,IAAI;EAC9B;AAEA,MAAI,UAAU;AACZ,cAAU,eAAe,IAAI;EAC/B;AAEA,QAAM,iBAAyC,CAAA;AAE/C,MAAI,UAAU;AACZ,mBAAe,eAAe,IAAI;EACpC;AAEA,MAAI,UAAU;AACZ,mBAAe,eAAe,IAAI;EACpC;AAEA,MAAI,oBAAoB,MAAM;AAC5B,mBAAe,cAAc,IAAI;EACnC;AAEA,SAAO;IACL;IACA;EAAA;AAEJ;AC9FO,IAAM,gBAAgB,IAAI;EAC/B,MAAM;EACN,UAAU;IACR,MAAM;MACJ,IAAI;MACJ,SAAS;MACT,IAAI;IAAA;IAEN,iBAAiB;MACf,OAAO;MACP,SAAS;IAAA;EACX;EAEF,iBAAiB;IACf,MAAM;EAAA;AAEV,CAAC;;;;ACyBD,IAAM,YACJ;EAAC;EAAA;IACC,eAAY;IACZ,SAAQ;IACR,MAAK;IACL,QAAO;IACP,aAAY;IACZ,eAAc;IACd,gBAAe;IACf,WAAU;IAEV,UAAA,oBAAC,YAAA,EAAS,QAAO,iBAAA,CAAiB;EAAA;AACpC;AASK,IAAM,QAAc;EACzB,SAASA,OACP;IACE,OAAO;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA,gBAAgB;IAChB,GAAG;EAAA,GAEL,KACA;AAGA,UAAM,MAAM,YAAY;MACtB;MACA;MACA;MACA;MACA,gBAAgB,gBAAgB,SAAY,SAAY,gBAAgB,QAAQ,gBAAgB;MAChG;IAAA,CACD;AAED,UAAM,aAAa,eAAe,QAAQ,mBAAmB;AAE7D,UAAM,UAAU;MACd,cAAc,EAAE,MAAM,gBAAA,CAAiB;MACvC,eAAe,QAAQ;MACvB,oBAAoB,WAAW;MAC/B;IAAA;AAGF,UAAM,UACJ;MAAC;MAAA;QACC;QACA;QACA,WAAW;QACX;QACA;QACA;QACC,GAAG,IAAI;QACP,GAAG,IAAI;QACP,GAAG;MAAA;IAAA;AAIR,QAAI,CAAC,YAAY;AACf,aAAO;IACT;AAEA,WACE,qBAAC,OAAA,EAAI,WAAU,YACZ,UAAA;MAAA,eAAe,QACd;QAAC;QAAA;UACC,eAAY;UACZ,WAAU;UAET,UAAA;QAAA;MAAA;MAGJ;MACA,oBAAoB,WAAW;IAAA,EAAA,CAClC;EAEJ;AACF;",
|
|
6
|
+
"names": ["Input"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import {
|
|
2
|
+
cn,
|
|
3
|
+
cva
|
|
4
|
+
} from "./chunk-ZF3OHCGV.js";
|
|
5
|
+
|
|
6
|
+
// ../react-markdown-renderer/dist/index.js
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
|
|
9
|
+
// ../markdown-renderer/dist/index.js
|
|
10
|
+
function escapeHtml(text) {
|
|
11
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
12
|
+
}
|
|
13
|
+
function parseInline(text, linkResolver) {
|
|
14
|
+
let result = escapeHtml(text);
|
|
15
|
+
result = result.replace(/`([^`]+)`/g, "<code>$1</code>");
|
|
16
|
+
result = result.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
17
|
+
result = result.replace(/__([^_]+)__/g, "<strong>$1</strong>");
|
|
18
|
+
result = result.replace(/\*([^*]+)\*/g, "<em>$1</em>");
|
|
19
|
+
result = result.replace(/_([^_]+)_/g, "<em>$1</em>");
|
|
20
|
+
result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text2, url) => {
|
|
21
|
+
if (/^\s*javascript\s*:/i.test(url)) {
|
|
22
|
+
return text2;
|
|
23
|
+
}
|
|
24
|
+
const resolvedUrl = linkResolver ? linkResolver(url) : url;
|
|
25
|
+
return `<a href="${resolvedUrl}">${text2}</a>`;
|
|
26
|
+
});
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
function parseMarkdown(content, linkResolver) {
|
|
30
|
+
const lines = content.split("\n");
|
|
31
|
+
const outputLines = [];
|
|
32
|
+
let inCodeBlock = false;
|
|
33
|
+
let codeBlockContent = [];
|
|
34
|
+
let codeBlockLang = "";
|
|
35
|
+
let inList = null;
|
|
36
|
+
let inBlockquote = false;
|
|
37
|
+
function closeList() {
|
|
38
|
+
if (inList) {
|
|
39
|
+
outputLines.push(inList === "ul" ? "</ul>" : "</ol>");
|
|
40
|
+
inList = null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function closeBlockquote() {
|
|
44
|
+
if (inBlockquote) {
|
|
45
|
+
outputLines.push("</blockquote>");
|
|
46
|
+
inBlockquote = false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (let i = 0; i < lines.length; i++) {
|
|
50
|
+
const line = lines[i];
|
|
51
|
+
if (line.trimStart().startsWith("```")) {
|
|
52
|
+
if (inCodeBlock) {
|
|
53
|
+
outputLines.push(`<pre><code${codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : ""}>${escapeHtml(codeBlockContent.join("\n"))}</code></pre>`);
|
|
54
|
+
codeBlockContent = [];
|
|
55
|
+
codeBlockLang = "";
|
|
56
|
+
inCodeBlock = false;
|
|
57
|
+
} else {
|
|
58
|
+
closeList();
|
|
59
|
+
closeBlockquote();
|
|
60
|
+
inCodeBlock = true;
|
|
61
|
+
codeBlockLang = line.trimStart().slice(3).trim();
|
|
62
|
+
}
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (inCodeBlock) {
|
|
66
|
+
codeBlockContent.push(line);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (/^(\s*[-*_]\s*){3,}$/.test(line)) {
|
|
70
|
+
closeList();
|
|
71
|
+
closeBlockquote();
|
|
72
|
+
outputLines.push("<hr />");
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
|
|
76
|
+
if (headingMatch) {
|
|
77
|
+
closeList();
|
|
78
|
+
closeBlockquote();
|
|
79
|
+
const level = headingMatch[1].length;
|
|
80
|
+
const text = parseInline(headingMatch[2], linkResolver);
|
|
81
|
+
outputLines.push(`<h${level}>${text}</h${level}>`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const blockquoteMatch = line.match(/^>\s?(.*)$/);
|
|
85
|
+
if (blockquoteMatch) {
|
|
86
|
+
closeList();
|
|
87
|
+
if (!inBlockquote) {
|
|
88
|
+
inBlockquote = true;
|
|
89
|
+
outputLines.push("<blockquote>");
|
|
90
|
+
}
|
|
91
|
+
const text = blockquoteMatch[1].trim();
|
|
92
|
+
if (text) {
|
|
93
|
+
outputLines.push(`<p>${parseInline(text, linkResolver)}</p>`);
|
|
94
|
+
}
|
|
95
|
+
continue;
|
|
96
|
+
} else if (inBlockquote) {
|
|
97
|
+
closeBlockquote();
|
|
98
|
+
}
|
|
99
|
+
const ulMatch = line.match(/^[\s]*[-*+]\s+(.+)$/);
|
|
100
|
+
if (ulMatch) {
|
|
101
|
+
closeBlockquote();
|
|
102
|
+
if (inList !== "ul") {
|
|
103
|
+
closeList();
|
|
104
|
+
inList = "ul";
|
|
105
|
+
outputLines.push("<ul>");
|
|
106
|
+
}
|
|
107
|
+
outputLines.push(`<li>${parseInline(ulMatch[1], linkResolver)}</li>`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const olMatch = line.match(/^[\s]*\d+\.\s+(.+)$/);
|
|
111
|
+
if (olMatch) {
|
|
112
|
+
closeBlockquote();
|
|
113
|
+
if (inList !== "ol") {
|
|
114
|
+
closeList();
|
|
115
|
+
inList = "ol";
|
|
116
|
+
outputLines.push("<ol>");
|
|
117
|
+
}
|
|
118
|
+
outputLines.push(`<li>${parseInline(olMatch[1], linkResolver)}</li>`);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (inList) {
|
|
122
|
+
closeList();
|
|
123
|
+
}
|
|
124
|
+
if (line.trim() === "") {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
outputLines.push(`<p>${parseInline(line, linkResolver)}</p>`);
|
|
128
|
+
}
|
|
129
|
+
if (inCodeBlock) {
|
|
130
|
+
outputLines.push(`<pre><code${codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : ""}>${escapeHtml(codeBlockContent.join("\n"))}</code></pre>`);
|
|
131
|
+
}
|
|
132
|
+
closeList();
|
|
133
|
+
closeBlockquote();
|
|
134
|
+
return outputLines.join("\n");
|
|
135
|
+
}
|
|
136
|
+
function extractComponents(content, components) {
|
|
137
|
+
if (!components) return [];
|
|
138
|
+
const extracted = [];
|
|
139
|
+
for (const [name, def] of Object.entries(components)) {
|
|
140
|
+
const matches = Array.from(content.matchAll(def.pattern));
|
|
141
|
+
matches.forEach(() => {
|
|
142
|
+
extracted.push({
|
|
143
|
+
name,
|
|
144
|
+
props: { ...def.props }
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return extracted;
|
|
149
|
+
}
|
|
150
|
+
function createMarkdownRenderer(props) {
|
|
151
|
+
const { content, components, linkResolver } = props;
|
|
152
|
+
const html = parseMarkdown(content, linkResolver);
|
|
153
|
+
const extractedComponents = extractComponents(content, components);
|
|
154
|
+
const ariaProps = {
|
|
155
|
+
role: "document",
|
|
156
|
+
"aria-label": "Rendered markdown content"
|
|
157
|
+
};
|
|
158
|
+
return {
|
|
159
|
+
html,
|
|
160
|
+
components: extractedComponents,
|
|
161
|
+
ariaProps
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
var markdownRendererTokens = {
|
|
165
|
+
name: "markdown-renderer",
|
|
166
|
+
tokens: {
|
|
167
|
+
bg: { variable: "--rfr-markdown-bg", fallback: "hsl(var(--background))" },
|
|
168
|
+
fg: { variable: "--rfr-markdown-fg", fallback: "hsl(var(--foreground))" },
|
|
169
|
+
codeBg: { variable: "--rfr-markdown-code-bg", fallback: "hsl(var(--muted))" },
|
|
170
|
+
linkColor: { variable: "--rfr-markdown-link", fallback: "hsl(var(--primary))" },
|
|
171
|
+
borderColor: { variable: "--rfr-markdown-border", fallback: "hsl(var(--border))" }
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
var proseVariants = cva({
|
|
175
|
+
base: "prose max-w-none text-foreground leading-relaxed",
|
|
176
|
+
variants: {
|
|
177
|
+
size: {
|
|
178
|
+
sm: "prose-sm text-sm",
|
|
179
|
+
default: "prose-base text-base",
|
|
180
|
+
lg: "prose-lg text-lg"
|
|
181
|
+
},
|
|
182
|
+
theme: {
|
|
183
|
+
light: "bg-white text-gray-900",
|
|
184
|
+
dark: "bg-gray-900 text-gray-100"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
defaultVariants: {
|
|
188
|
+
size: "default"
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// ../react-markdown-renderer/dist/index.js
|
|
193
|
+
import { jsx } from "react/jsx-runtime";
|
|
194
|
+
function sanitizeHtml(html) {
|
|
195
|
+
let sanitized = html;
|
|
196
|
+
sanitized = sanitized.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
|
|
197
|
+
sanitized = sanitized.replace(/<\/?script[^>]*>/gi, "");
|
|
198
|
+
sanitized = sanitized.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
|
|
199
|
+
sanitized = sanitized.replace(/(href|src)\s*=\s*["']?\s*javascript\s*:[^"'>]*/gi, '$1=""');
|
|
200
|
+
return sanitized;
|
|
201
|
+
}
|
|
202
|
+
var MarkdownRenderer = React.forwardRef(
|
|
203
|
+
function MarkdownRenderer2({ content, components, linkResolver, className, size }, ref) {
|
|
204
|
+
const coreProps = { content, components, linkResolver };
|
|
205
|
+
const api = createMarkdownRenderer(coreProps);
|
|
206
|
+
const classes = cn(proseVariants({ size }), className);
|
|
207
|
+
const sanitizedHtml = sanitizeHtml(api.html);
|
|
208
|
+
return /* @__PURE__ */ jsx(
|
|
209
|
+
"div",
|
|
210
|
+
{
|
|
211
|
+
ref,
|
|
212
|
+
className: classes,
|
|
213
|
+
...api.ariaProps,
|
|
214
|
+
dangerouslySetInnerHTML: { __html: sanitizedHtml }
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
export {
|
|
221
|
+
markdownRendererTokens,
|
|
222
|
+
proseVariants,
|
|
223
|
+
MarkdownRenderer
|
|
224
|
+
};
|
|
225
|
+
//# sourceMappingURL=chunk-UNEM2TRI.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../markdown-renderer/src/markdown-renderer.ts", "../../markdown-renderer/src/markdown-renderer.styles.ts", "../../react-markdown-renderer/src/MarkdownRenderer.tsx"],
|
|
4
|
+
"sourcesContent": ["import type { AccessibilityProps } from '@refraction-ui/shared'\n\nexport interface ComponentDef {\n type: string\n pattern: RegExp\n props: Record<string, string>\n}\n\nexport interface MarkdownRendererProps {\n content: string\n components?: Record<string, ComponentDef>\n linkResolver?: (url: string) => string\n}\n\nexport interface MarkdownRendererAPI {\n /** Rendered HTML string */\n html: string\n /** Extracted component slots from custom component definitions */\n components: Array<{ name: string; props: Record<string, string> }>\n /** ARIA attributes for the rendered content container */\n ariaProps: Partial<AccessibilityProps>\n}\n\n/**\n * Escape HTML entities to prevent injection in non-code contexts.\n */\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n}\n\n/**\n * Parse inline markdown elements: bold, italic, code, links.\n */\nfunction parseInline(text: string, linkResolver?: (url: string) => string): string {\n let result = escapeHtml(text)\n\n // Inline code (backticks) — must come before bold/italic to avoid conflicts\n result = result.replace(/`([^`]+)`/g, '<code>$1</code>')\n\n // Bold (**text** or __text__)\n result = result.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n result = result.replace(/__([^_]+)__/g, '<strong>$1</strong>')\n\n // Italic (*text* or _text_)\n result = result.replace(/\\*([^*]+)\\*/g, '<em>$1</em>')\n result = result.replace(/_([^_]+)_/g, '<em>$1</em>')\n\n // Links [text](url) — reject javascript: URLs for XSS safety\n result = result.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, (_match, text, url) => {\n if (/^\\s*javascript\\s*:/i.test(url)) {\n return text\n }\n const resolvedUrl = linkResolver ? linkResolver(url) : url\n return `<a href=\"${resolvedUrl}\">${text}</a>`\n })\n\n return result\n}\n\n/**\n * Parse a full markdown string into HTML.\n * Handles: headings, bold, italic, links, code (inline & block),\n * lists (unordered & ordered), blockquotes, horizontal rules.\n */\nfunction parseMarkdown(content: string, linkResolver?: (url: string) => string): string {\n const lines = content.split('\\n')\n const outputLines: string[] = []\n let inCodeBlock = false\n let codeBlockContent: string[] = []\n let codeBlockLang = ''\n let inList: 'ul' | 'ol' | null = null\n let inBlockquote = false\n\n function closeList() {\n if (inList) {\n outputLines.push(inList === 'ul' ? '</ul>' : '</ol>')\n inList = null\n }\n }\n\n function closeBlockquote() {\n if (inBlockquote) {\n outputLines.push('</blockquote>')\n inBlockquote = false\n }\n }\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]\n\n // Code blocks (fenced with ```)\n if (line.trimStart().startsWith('```')) {\n if (inCodeBlock) {\n // Closing code block\n outputLines.push(`<pre><code${codeBlockLang ? ` class=\"language-${escapeHtml(codeBlockLang)}\"` : ''}>${escapeHtml(codeBlockContent.join('\\n'))}</code></pre>`)\n codeBlockContent = []\n codeBlockLang = ''\n inCodeBlock = false\n } else {\n // Opening code block\n closeList()\n closeBlockquote()\n inCodeBlock = true\n codeBlockLang = line.trimStart().slice(3).trim()\n }\n continue\n }\n\n if (inCodeBlock) {\n codeBlockContent.push(line)\n continue\n }\n\n // Horizontal rule (--- or ___ or ***)\n if (/^(\\s*[-*_]\\s*){3,}$/.test(line)) {\n closeList()\n closeBlockquote()\n outputLines.push('<hr />')\n continue\n }\n\n // Headings (# through ######)\n const headingMatch = line.match(/^(#{1,6})\\s+(.+)$/)\n if (headingMatch) {\n closeList()\n closeBlockquote()\n const level = headingMatch[1].length\n const text = parseInline(headingMatch[2], linkResolver)\n outputLines.push(`<h${level}>${text}</h${level}>`)\n continue\n }\n\n // Blockquotes (> text)\n const blockquoteMatch = line.match(/^>\\s?(.*)$/)\n if (blockquoteMatch) {\n closeList()\n if (!inBlockquote) {\n inBlockquote = true\n outputLines.push('<blockquote>')\n }\n const text = blockquoteMatch[1].trim()\n if (text) {\n outputLines.push(`<p>${parseInline(text, linkResolver)}</p>`)\n }\n continue\n } else if (inBlockquote) {\n closeBlockquote()\n }\n\n // Unordered list items (- or * at start)\n const ulMatch = line.match(/^[\\s]*[-*+]\\s+(.+)$/)\n if (ulMatch) {\n closeBlockquote()\n if (inList !== 'ul') {\n closeList()\n inList = 'ul'\n outputLines.push('<ul>')\n }\n outputLines.push(`<li>${parseInline(ulMatch[1], linkResolver)}</li>`)\n continue\n }\n\n // Ordered list items (1. 2. etc.)\n const olMatch = line.match(/^[\\s]*\\d+\\.\\s+(.+)$/)\n if (olMatch) {\n closeBlockquote()\n if (inList !== 'ol') {\n closeList()\n inList = 'ol'\n outputLines.push('<ol>')\n }\n outputLines.push(`<li>${parseInline(olMatch[1], linkResolver)}</li>`)\n continue\n }\n\n // If we were in a list and hit a non-list line, close the list\n if (inList) {\n closeList()\n }\n\n // Empty line\n if (line.trim() === '') {\n continue\n }\n\n // Regular paragraph\n outputLines.push(`<p>${parseInline(line, linkResolver)}</p>`)\n }\n\n // Close any open blocks\n if (inCodeBlock) {\n outputLines.push(`<pre><code${codeBlockLang ? ` class=\"language-${escapeHtml(codeBlockLang)}\"` : ''}>${escapeHtml(codeBlockContent.join('\\n'))}</code></pre>`)\n }\n closeList()\n closeBlockquote()\n\n return outputLines.join('\\n')\n}\n\n/**\n * Extract custom component slots from content based on component definitions.\n */\nfunction extractComponents(\n content: string,\n components?: Record<string, ComponentDef>,\n): Array<{ name: string; props: Record<string, string> }> {\n if (!components) return []\n\n const extracted: Array<{ name: string; props: Record<string, string> }> = []\n\n for (const [name, def] of Object.entries(components)) {\n const matches = Array.from(content.matchAll(def.pattern))\n matches.forEach(() => {\n extracted.push({\n name,\n props: { ...def.props },\n })\n })\n }\n\n return extracted\n}\n\n/**\n * Create a headless markdown renderer.\n *\n * Uses the headless pattern — returns an API object with rendered HTML,\n * extracted component slots, and ARIA props for the container.\n */\nexport function createMarkdownRenderer(props: MarkdownRendererProps): MarkdownRendererAPI {\n const { content, components, linkResolver } = props\n\n const html = parseMarkdown(content, linkResolver)\n const extractedComponents = extractComponents(content, components)\n\n const ariaProps: Partial<AccessibilityProps> = {\n role: 'document',\n 'aria-label': 'Rendered markdown content',\n }\n\n return {\n html,\n components: extractedComponents,\n ariaProps,\n }\n}\n", "import { cva } from '@refraction-ui/shared'\nimport type { TokenContract } from '@refraction-ui/shared'\n\nexport const markdownRendererTokens: TokenContract = {\n name: 'markdown-renderer',\n tokens: {\n bg: { variable: '--rfr-markdown-bg', fallback: 'hsl(var(--background))' },\n fg: { variable: '--rfr-markdown-fg', fallback: 'hsl(var(--foreground))' },\n codeBg: { variable: '--rfr-markdown-code-bg', fallback: 'hsl(var(--muted))' },\n linkColor: { variable: '--rfr-markdown-link', fallback: 'hsl(var(--primary))' },\n borderColor: { variable: '--rfr-markdown-border', fallback: 'hsl(var(--border))' },\n },\n}\n\nexport const proseVariants = cva({\n base: 'prose max-w-none text-foreground leading-relaxed',\n variants: {\n size: {\n sm: 'prose-sm text-sm',\n default: 'prose-base text-base',\n lg: 'prose-lg text-lg',\n },\n theme: {\n light: 'bg-white text-gray-900',\n dark: 'bg-gray-900 text-gray-100',\n },\n },\n defaultVariants: {\n size: 'default',\n },\n})\n", "import * as React from 'react'\nimport {\n createMarkdownRenderer,\n proseVariants,\n type MarkdownRendererProps as CoreProps,\n type ComponentDef,\n} from '@refraction-ui/markdown-renderer'\nimport { cn } from '@refraction-ui/shared'\n\nexport type { ComponentDef }\n\nexport interface MarkdownRendererProps {\n content: string\n components?: Record<string, ComponentDef>\n linkResolver?: (url: string) => string\n className?: string\n size?: 'sm' | 'default' | 'lg'\n}\n\n/**\n * Sanitize HTML to prevent XSS attacks.\n * Strips script tags, on* event attributes, and javascript: URLs.\n */\nfunction sanitizeHtml(html: string): string {\n let sanitized = html\n\n // Remove <script> tags and their contents\n sanitized = sanitized.replace(/<script\\b[^<]*(?:(?!<\\/script>)<[^<]*)*<\\/script>/gi, '')\n\n // Remove standalone <script> opening/closing tags\n sanitized = sanitized.replace(/<\\/?script[^>]*>/gi, '')\n\n // Remove on* event handler attributes\n sanitized = sanitized.replace(/\\s+on\\w+\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^\\s>]+)/gi, '')\n\n // Remove javascript: URLs from href and src attributes\n sanitized = sanitized.replace(/(href|src)\\s*=\\s*[\"']?\\s*javascript\\s*:[^\"'>]*/gi, '$1=\"\"')\n\n return sanitized\n}\n\n/**\n * MarkdownRenderer component — renders markdown content as sanitized HTML.\n *\n * Uses the headless @refraction-ui/markdown-renderer core for parsing.\n * XSS sanitization is applied before rendering via dangerouslySetInnerHTML.\n */\nexport const MarkdownRenderer = React.forwardRef<HTMLDivElement, MarkdownRendererProps>(\n function MarkdownRenderer({ content, components, linkResolver, className, size }, ref) {\n const coreProps: CoreProps = { content, components, linkResolver }\n const api = createMarkdownRenderer(coreProps)\n const classes = cn(proseVariants({ size }), className)\n const sanitizedHtml = sanitizeHtml(api.html)\n\n return (\n <div\n ref={ref}\n className={classes}\n {...api.ariaProps}\n dangerouslySetInnerHTML={{ __html: sanitizedHtml }}\n />\n )\n },\n)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["text", "MarkdownRenderer"]
|
|
7
|
+
}
|