@refraction-ui/react 0.9.3 → 0.11.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/index.js CHANGED
@@ -2680,238 +2680,1888 @@ var CommandSeparator = React11.forwardRef(
2680
2680
  );
2681
2681
  CommandSeparator.displayName = "CommandSeparator";
2682
2682
 
2683
- // ../content-protection/dist/index.js
2684
- function createContentProtection(props = {}) {
2685
- const {
2686
- enabled = true,
2687
- disableCopy = true,
2688
- disableContextMenu = true,
2689
- watermarkText
2690
- } = props;
2691
- const eventHandlers = {};
2692
- if (enabled) {
2693
- if (disableCopy) {
2694
- const prevent = (e) => e.preventDefault();
2695
- eventHandlers.onCopy = prevent;
2696
- eventHandlers.onCut = prevent;
2697
- eventHandlers.onSelectStart = prevent;
2698
- }
2699
- if (disableContextMenu) {
2700
- eventHandlers.onContextMenu = (e) => e.preventDefault();
2701
- }
2702
- }
2703
- const watermarkConfig = watermarkText ? { text: watermarkText, opacity: 0.08, angle: -45 } : null;
2704
- const dataAttributes = {};
2705
- if (enabled) {
2706
- dataAttributes["data-protected"] = "true";
2707
- }
2708
- return {
2709
- eventHandlers,
2710
- watermarkConfig,
2711
- dataAttributes
2712
- };
2713
- }
2714
- var contentProtectionVariants = cva({
2715
- base: "relative select-none"
2716
- });
2717
- var watermarkVariants = cva({
2718
- base: "pointer-events-none absolute inset-0 z-50 overflow-hidden"
2719
- });
2720
- var ContentProtection = React11.forwardRef(
2721
- ({
2722
- enabled,
2723
- disableCopy,
2724
- disableContextMenu,
2725
- watermarkText,
2726
- className,
2727
- children,
2728
- ...props
2729
- }, ref) => {
2730
- const api = createContentProtection({
2731
- enabled,
2732
- disableCopy,
2733
- disableContextMenu,
2734
- watermarkText
2735
- });
2736
- const classes = cn(contentProtectionVariants(), className);
2737
- return /* @__PURE__ */ jsxs(
2738
- "div",
2739
- {
2740
- ref,
2741
- className: classes,
2742
- ...api.dataAttributes,
2743
- onCopy: api.eventHandlers.onCopy,
2744
- onCut: api.eventHandlers.onCut,
2745
- onContextMenu: api.eventHandlers.onContextMenu,
2746
- onSelect: api.eventHandlers.onSelectStart,
2747
- ...props,
2748
- children: [
2749
- children,
2750
- api.watermarkConfig && /* @__PURE__ */ jsx(
2751
- "div",
2752
- {
2753
- className: watermarkVariants(),
2754
- "aria-hidden": "true",
2755
- style: {
2756
- opacity: api.watermarkConfig.opacity,
2757
- transform: `rotate(${api.watermarkConfig.angle}deg)`,
2758
- backgroundImage: `repeating-linear-gradient(
2759
- ${api.watermarkConfig.angle}deg,
2760
- transparent,
2761
- transparent 80px,
2762
- currentColor 80px,
2763
- currentColor 80px
2764
- )`
2765
- },
2766
- children: /* @__PURE__ */ jsxs("svg", { width: "100%", height: "100%", children: [
2767
- /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
2768
- "pattern",
2769
- {
2770
- id: "rfr-watermark",
2771
- x: "0",
2772
- y: "0",
2773
- width: "200",
2774
- height: "200",
2775
- patternUnits: "userSpaceOnUse",
2776
- patternTransform: `rotate(${api.watermarkConfig.angle})`,
2777
- children: /* @__PURE__ */ jsx(
2778
- "text",
2779
- {
2780
- x: "0",
2781
- y: "100",
2782
- fill: "currentColor",
2783
- fontSize: "16",
2784
- fontFamily: "sans-serif",
2785
- children: api.watermarkConfig.text
2786
- }
2787
- )
2788
- }
2789
- ) }),
2790
- /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: "url(#rfr-watermark)" })
2791
- ] })
2792
- }
2793
- )
2794
- ]
2795
- }
2796
- );
2797
- }
2798
- );
2799
- ContentProtection.displayName = "ContentProtection";
2800
-
2801
- // ../data-table/dist/index.js
2802
- function createDataTable(props) {
2803
- const {
2804
- columns,
2805
- data,
2806
- sortBy: initialSortBy,
2807
- sortDir: initialSortDir = "asc",
2808
- onSort,
2809
- filters: initialFilters
2810
- } = props;
2811
- let sortBy = initialSortBy ?? null;
2812
- let sortDir = initialSortDir;
2813
- let filters = { ...initialFilters ?? {} };
2814
- function getFilteredData() {
2815
- let result = [...data];
2816
- for (const [columnId, filterValue] of Object.entries(filters)) {
2817
- if (!filterValue) continue;
2818
- const col = columns.find((c) => c.id === columnId);
2819
- if (!col) continue;
2820
- const lowerFilter = filterValue.toLowerCase();
2821
- result = result.filter((row) => {
2822
- const cellValue = col.accessor(row);
2823
- return String(cellValue ?? "").toLowerCase().includes(lowerFilter);
2824
- });
2825
- }
2826
- return result;
2683
+ // ../conversation/dist/index.js
2684
+ function byTime(a, b) {
2685
+ return a.timestamp.getTime() - b.timestamp.getTime();
2686
+ }
2687
+ function findMessage(messages, id) {
2688
+ return messages.find((m) => m.id === id);
2689
+ }
2690
+ function rootIdOf(messages, id) {
2691
+ const m = findMessage(messages, id);
2692
+ if (!m) return id;
2693
+ return m.parentId ?? m.id;
2694
+ }
2695
+ function selectRoots(messages) {
2696
+ return messages.filter((m) => !m.parentId).sort(byTime);
2697
+ }
2698
+ function selectReplies(messages, rootId) {
2699
+ return messages.filter((m) => m.parentId === rootId).sort(byTime);
2700
+ }
2701
+ function selectThreadMessages(messages, rootId) {
2702
+ const root = findMessage(messages, rootId);
2703
+ const replies = selectReplies(messages, rootId);
2704
+ return root ? [root, ...replies] : replies;
2705
+ }
2706
+ function getReplyCount(messages, rootId) {
2707
+ return messages.reduce((n, m) => m.parentId === rootId ? n + 1 : n, 0);
2708
+ }
2709
+ function selectMainTimeline(messages, mode) {
2710
+ if (mode === "panel") return selectRoots(messages);
2711
+ return [...messages].sort(byTime);
2712
+ }
2713
+ function selectTimelineFromState(state) {
2714
+ return selectMainTimeline(state.messages, state.threadingMode);
2715
+ }
2716
+ var DEFAULT_ASSISTANT = { id: "assistant", name: "Assistant" };
2717
+ var DEFAULT_USER = { id: "user", name: "You" };
2718
+ var DEFAULT_TITLE = "New conversation";
2719
+ function defaultGenerateTitle(firstMessage) {
2720
+ const firstLine = firstMessage.split("\n", 1)[0].trim();
2721
+ if (firstLine.length <= 48) return firstLine || DEFAULT_TITLE;
2722
+ return `${firstLine.slice(0, 47).trimEnd()}\u2026`;
2723
+ }
2724
+ function createConversation(config = {}) {
2725
+ const assistant = config.assistant ?? DEFAULT_ASSISTANT;
2726
+ const currentUser = config.currentUser ?? DEFAULT_USER;
2727
+ const generateTitle = config.generateTitle ?? defaultGenerateTitle;
2728
+ let transport = config.transport;
2729
+ let threadingMode = config.threadingMode ?? "inline";
2730
+ const conversations = /* @__PURE__ */ new Map();
2731
+ const messagesByConv = /* @__PURE__ */ new Map();
2732
+ let activeConversationId = config.activeConversationId ?? null;
2733
+ let openThreadRootId = null;
2734
+ let replyTarget = null;
2735
+ let status = "idle";
2736
+ let error = null;
2737
+ let abortController = null;
2738
+ for (const c of config.conversations ?? []) {
2739
+ conversations.set(c.id, c);
2740
+ messagesByConv.set(c.id, []);
2741
+ }
2742
+ for (const [conversationId, msgs] of Object.entries(config.messages ?? {})) {
2743
+ messagesByConv.set(conversationId, [...msgs]);
2744
+ }
2745
+ if (activeConversationId === null && config.conversations?.length) {
2746
+ activeConversationId = config.conversations[0].id;
2827
2747
  }
2828
- function getSortedData() {
2829
- const filtered = getFilteredData();
2830
- if (!sortBy) return filtered;
2831
- const col = columns.find((c) => c.id === sortBy);
2832
- if (!col) return filtered;
2833
- return [...filtered].sort((a, b) => {
2834
- const aVal = col.accessor(a);
2835
- const bVal = col.accessor(b);
2836
- const aStr = String(aVal ?? "");
2837
- const bStr = String(bVal ?? "");
2838
- const cmp = aStr.localeCompare(bStr, void 0, { numeric: true });
2839
- return sortDir === "asc" ? cmp : -cmp;
2840
- });
2748
+ const listeners = /* @__PURE__ */ new Set();
2749
+ let snapshot = buildSnapshot();
2750
+ function buildSnapshot() {
2751
+ return {
2752
+ conversations: orderedConversations(),
2753
+ activeConversationId,
2754
+ messages: activeConversationId ? messagesByConv.get(activeConversationId) ?? [] : [],
2755
+ openThreadRootId,
2756
+ replyTarget,
2757
+ threadingMode,
2758
+ status,
2759
+ error
2760
+ };
2841
2761
  }
2842
- function sort(columnId) {
2843
- const col = columns.find((c) => c.id === columnId);
2844
- if (!col?.sortable) return;
2845
- if (sortBy === columnId) {
2846
- sortDir = sortDir === "asc" ? "desc" : "asc";
2847
- } else {
2848
- sortBy = columnId;
2849
- sortDir = "asc";
2850
- }
2851
- onSort?.(sortBy, sortDir);
2762
+ function emit() {
2763
+ snapshot = buildSnapshot();
2764
+ for (const l of listeners) l();
2852
2765
  }
2853
- function setFilter(columnId, value) {
2854
- filters = { ...filters, [columnId]: value };
2766
+ function orderedConversations() {
2767
+ return [...conversations.values()].sort(
2768
+ (a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
2769
+ );
2855
2770
  }
2856
- function getHeaderProps(col) {
2857
- const props2 = {
2858
- role: "columnheader"
2771
+ function touch(conversationId) {
2772
+ const c = conversations.get(conversationId);
2773
+ if (c) conversations.set(conversationId, { ...c, updatedAt: /* @__PURE__ */ new Date() });
2774
+ }
2775
+ function ensureActiveConversation(opts) {
2776
+ if (opts?.conversationId) {
2777
+ if (!conversations.has(opts.conversationId)) createConversationInternal({}, opts.conversationId);
2778
+ return opts.conversationId;
2779
+ }
2780
+ if (activeConversationId && conversations.has(activeConversationId)) return activeConversationId;
2781
+ return createConversationInternal({}).id;
2782
+ }
2783
+ function createConversationInternal(opts, id) {
2784
+ const now = /* @__PURE__ */ new Date();
2785
+ const conversation = {
2786
+ id: id ?? generateId("rfr-conv"),
2787
+ title: opts.title ?? DEFAULT_TITLE,
2788
+ createdAt: now,
2789
+ updatedAt: now,
2790
+ metadata: opts.metadata
2859
2791
  };
2860
- if (col.sortable) {
2861
- if (sortBy === col.id) {
2862
- props2["aria-sort"] = sortDir === "asc" ? "ascending" : "descending";
2792
+ conversations.set(conversation.id, conversation);
2793
+ messagesByConv.set(conversation.id, []);
2794
+ activeConversationId = conversation.id;
2795
+ return conversation;
2796
+ }
2797
+ async function streamReply(conversationId, assistantMsg, userMsg, history) {
2798
+ abortController = new AbortController();
2799
+ status = "streaming";
2800
+ emit();
2801
+ try {
2802
+ const stream = transport.send({
2803
+ conversationId,
2804
+ message: userMsg,
2805
+ history,
2806
+ parentId: assistantMsg.parentId,
2807
+ signal: abortController.signal
2808
+ });
2809
+ for await (const chunk of stream) {
2810
+ if (chunk.content !== void 0) assistantMsg.content = chunk.content;
2811
+ if (chunk.delta) assistantMsg.content += chunk.delta;
2812
+ if (chunk.metadata) {
2813
+ assistantMsg.metadata = { ...assistantMsg.metadata, ...chunk.metadata };
2814
+ }
2815
+ emit();
2816
+ if (abortController.signal.aborted) break;
2817
+ }
2818
+ assistantMsg.status = "sent";
2819
+ status = "idle";
2820
+ error = null;
2821
+ } catch (err) {
2822
+ if (abortController.signal.aborted) {
2823
+ assistantMsg.status = "sent";
2824
+ status = "idle";
2863
2825
  } else {
2864
- props2["aria-sort"] = "none";
2826
+ const message = err instanceof Error ? err.message : String(err);
2827
+ assistantMsg.status = "error";
2828
+ assistantMsg.error = message;
2829
+ status = "error";
2830
+ error = message;
2865
2831
  }
2832
+ } finally {
2833
+ abortController = null;
2834
+ touch(conversationId);
2835
+ emit();
2866
2836
  }
2867
- return props2;
2868
- }
2869
- function getCellProps(col, _row) {
2870
- return {
2871
- role: "cell",
2872
- "data-column": col.id
2873
- };
2874
2837
  }
2875
- function getRowProps(_row, index) {
2876
- return {
2877
- role: "row",
2878
- "data-row-index": index
2879
- };
2838
+ function historyFor(list, parentId) {
2839
+ const scope = parentId ? selectThreadMessages(list, parentId) : selectRoots(list);
2840
+ return scope.filter((m) => m.status !== "error");
2880
2841
  }
2881
- return {
2882
- get state() {
2883
- return {
2884
- get sortedData() {
2885
- return getSortedData();
2886
- },
2887
- get sortBy() {
2888
- return sortBy;
2889
- },
2890
- get sortDir() {
2891
- return sortDir;
2892
- },
2893
- get filters() {
2894
- return { ...filters };
2842
+ const api = {
2843
+ getState() {
2844
+ return snapshot;
2845
+ },
2846
+ subscribe(listener) {
2847
+ listeners.add(listener);
2848
+ return () => listeners.delete(listener);
2849
+ },
2850
+ newConversation(opts) {
2851
+ const conversation = createConversationInternal(opts ?? {});
2852
+ openThreadRootId = null;
2853
+ replyTarget = null;
2854
+ emit();
2855
+ return conversation;
2856
+ },
2857
+ selectConversation(conversationId) {
2858
+ if (!conversations.has(conversationId) || activeConversationId === conversationId) return;
2859
+ activeConversationId = conversationId;
2860
+ openThreadRootId = null;
2861
+ replyTarget = null;
2862
+ emit();
2863
+ },
2864
+ deleteConversation(conversationId) {
2865
+ if (!conversations.has(conversationId)) return;
2866
+ conversations.delete(conversationId);
2867
+ messagesByConv.delete(conversationId);
2868
+ if (activeConversationId === conversationId) {
2869
+ activeConversationId = orderedConversations()[0]?.id ?? null;
2870
+ openThreadRootId = null;
2871
+ replyTarget = null;
2872
+ }
2873
+ emit();
2874
+ },
2875
+ renameConversation(conversationId, title) {
2876
+ const c = conversations.get(conversationId);
2877
+ if (!c) return;
2878
+ conversations.set(conversationId, { ...c, title });
2879
+ emit();
2880
+ },
2881
+ appendMessage(message) {
2882
+ const list = messagesByConv.get(message.conversationId);
2883
+ if (!list) return;
2884
+ list.push(message);
2885
+ touch(message.conversationId);
2886
+ emit();
2887
+ },
2888
+ editMessage(messageId, content) {
2889
+ if (!activeConversationId) return;
2890
+ const list = messagesByConv.get(activeConversationId);
2891
+ const msg = list.find((m) => m.id === messageId);
2892
+ if (!msg) return;
2893
+ msg.content = content;
2894
+ msg.edited = true;
2895
+ emit();
2896
+ },
2897
+ deleteMessage(messageId) {
2898
+ if (!activeConversationId) return;
2899
+ const list = messagesByConv.get(activeConversationId);
2900
+ const msg = list.find((m) => m.id === messageId);
2901
+ if (!msg) return;
2902
+ const removeIds = /* @__PURE__ */ new Set([messageId]);
2903
+ if (!msg.parentId) {
2904
+ for (const r of list) if (r.parentId === messageId) removeIds.add(r.id);
2905
+ }
2906
+ const next = list.filter((m) => !removeIds.has(m.id));
2907
+ messagesByConv.set(activeConversationId, next);
2908
+ if (openThreadRootId && removeIds.has(openThreadRootId)) openThreadRootId = null;
2909
+ if (replyTarget && removeIds.has(replyTarget)) replyTarget = openThreadRootId;
2910
+ emit();
2911
+ },
2912
+ react(messageId, emoji) {
2913
+ if (!activeConversationId) return;
2914
+ const list = messagesByConv.get(activeConversationId);
2915
+ const msg = list.find((m) => m.id === messageId);
2916
+ if (!msg) return;
2917
+ const reactions = [...msg.reactions ?? []];
2918
+ const idx = reactions.findIndex((r) => r.emoji === emoji);
2919
+ if (idx === -1) {
2920
+ reactions.push({ emoji, count: 1, userReacted: true });
2921
+ } else {
2922
+ const r = reactions[idx];
2923
+ if (r.userReacted) {
2924
+ const count = r.count - 1;
2925
+ if (count <= 0) reactions.splice(idx, 1);
2926
+ else reactions[idx] = { ...r, count, userReacted: false };
2927
+ } else {
2928
+ reactions[idx] = { ...r, count: r.count + 1, userReacted: true };
2895
2929
  }
2930
+ }
2931
+ msg.reactions = reactions;
2932
+ emit();
2933
+ },
2934
+ async sendMessage(content, opts) {
2935
+ const trimmed = content.trim();
2936
+ if (!trimmed && !opts?.attachments?.length) return;
2937
+ const conversationId = ensureActiveConversation(opts);
2938
+ const list = messagesByConv.get(conversationId);
2939
+ const parentId = opts?.replyTo ? rootIdOf(list, opts.replyTo) : void 0;
2940
+ const replyToId = opts?.replyTo;
2941
+ const isFirstRoot = !parentId && selectRoots(list).length === 0;
2942
+ const userMsg = {
2943
+ id: generateId("rfr-msg"),
2944
+ conversationId,
2945
+ role: "user",
2946
+ author: currentUser,
2947
+ content: trimmed,
2948
+ timestamp: /* @__PURE__ */ new Date(),
2949
+ status: "sent",
2950
+ parentId,
2951
+ replyToId,
2952
+ attachments: opts?.attachments,
2953
+ metadata: opts?.metadata
2954
+ };
2955
+ list.push(userMsg);
2956
+ const conversation = conversations.get(conversationId);
2957
+ if (isFirstRoot && conversation.title === DEFAULT_TITLE) {
2958
+ conversations.set(conversationId, { ...conversation, title: generateTitle(trimmed) });
2959
+ }
2960
+ touch(conversationId);
2961
+ if (!transport) {
2962
+ status = "idle";
2963
+ emit();
2964
+ return;
2965
+ }
2966
+ const history = historyFor(list, parentId).filter((m) => m.id !== userMsg.id);
2967
+ const assistantMsg = {
2968
+ id: generateId("rfr-msg"),
2969
+ conversationId,
2970
+ role: "assistant",
2971
+ author: assistant,
2972
+ content: "",
2973
+ timestamp: /* @__PURE__ */ new Date(),
2974
+ status: "streaming",
2975
+ parentId
2896
2976
  };
2977
+ list.push(assistantMsg);
2978
+ await streamReply(conversationId, assistantMsg, userMsg, history);
2897
2979
  },
2898
- sort,
2899
- setFilter,
2900
- getHeaderProps,
2901
- getCellProps,
2902
- getRowProps
2980
+ async retryLast() {
2981
+ if (!activeConversationId || !transport) return;
2982
+ const list = messagesByConv.get(activeConversationId);
2983
+ const last = list[list.length - 1];
2984
+ const parentId = last?.parentId;
2985
+ if (last && last.role === "assistant" && last.status === "error") list.pop();
2986
+ const scope = parentId ? selectThreadMessages(list, parentId) : selectRoots(list);
2987
+ const lastUser = [...scope].reverse().find((m) => m.role === "user");
2988
+ if (!lastUser) return;
2989
+ const history = historyFor(list, parentId);
2990
+ const assistantMsg = {
2991
+ id: generateId("rfr-msg"),
2992
+ conversationId: activeConversationId,
2993
+ role: "assistant",
2994
+ author: assistant,
2995
+ content: "",
2996
+ timestamp: /* @__PURE__ */ new Date(),
2997
+ status: "streaming",
2998
+ parentId
2999
+ };
3000
+ list.push(assistantMsg);
3001
+ await streamReply(activeConversationId, assistantMsg, lastUser, history);
3002
+ },
3003
+ stop() {
3004
+ abortController?.abort();
3005
+ },
3006
+ openThread(rootId) {
3007
+ openThreadRootId = rootId;
3008
+ replyTarget = rootId;
3009
+ emit();
3010
+ },
3011
+ replyTo(messageId) {
3012
+ if (!activeConversationId) return;
3013
+ const list = messagesByConv.get(activeConversationId);
3014
+ openThreadRootId = rootIdOf(list, messageId);
3015
+ replyTarget = messageId;
3016
+ emit();
3017
+ },
3018
+ closeThread() {
3019
+ if (openThreadRootId === null && replyTarget === null) return;
3020
+ openThreadRootId = null;
3021
+ replyTarget = null;
3022
+ emit();
3023
+ },
3024
+ setThreadingMode(mode) {
3025
+ if (threadingMode === mode) return;
3026
+ threadingMode = mode;
3027
+ emit();
3028
+ },
3029
+ setTransport(next) {
3030
+ transport = next;
3031
+ }
2903
3032
  };
3033
+ return api;
2904
3034
  }
2905
- var tableVariants = cva({
2906
- base: "w-full caption-bottom text-sm border-collapse",
2907
- variants: {
2908
- size: {
2909
- sm: "text-xs",
2910
- md: "text-sm",
2911
- lg: "text-base"
2912
- }
2913
- },
2914
- defaultVariants: {
3035
+
3036
+ // ../markdown-renderer/dist/index.js
3037
+ function escapeHtml(text) {
3038
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
3039
+ }
3040
+ function parseInline(text, linkResolver) {
3041
+ let result = escapeHtml(text);
3042
+ result = result.replace(/`([^`]+)`/g, "<code>$1</code>");
3043
+ result = result.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
3044
+ result = result.replace(/__([^_]+)__/g, "<strong>$1</strong>");
3045
+ result = result.replace(/\*([^*]+)\*/g, "<em>$1</em>");
3046
+ result = result.replace(/_([^_]+)_/g, "<em>$1</em>");
3047
+ result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text2, url) => {
3048
+ if (/^\s*javascript\s*:/i.test(url)) {
3049
+ return text2;
3050
+ }
3051
+ const resolvedUrl = linkResolver ? linkResolver(url) : url;
3052
+ return `<a href="${resolvedUrl}">${text2}</a>`;
3053
+ });
3054
+ return result;
3055
+ }
3056
+ function parseMarkdown(content, linkResolver) {
3057
+ const lines = content.split("\n");
3058
+ const outputLines = [];
3059
+ let inCodeBlock = false;
3060
+ let codeBlockContent = [];
3061
+ let codeBlockLang = "";
3062
+ let inList = null;
3063
+ let inBlockquote = false;
3064
+ function closeList() {
3065
+ if (inList) {
3066
+ outputLines.push(inList === "ul" ? "</ul>" : "</ol>");
3067
+ inList = null;
3068
+ }
3069
+ }
3070
+ function closeBlockquote() {
3071
+ if (inBlockquote) {
3072
+ outputLines.push("</blockquote>");
3073
+ inBlockquote = false;
3074
+ }
3075
+ }
3076
+ for (let i = 0; i < lines.length; i++) {
3077
+ const line = lines[i];
3078
+ if (line.trimStart().startsWith("```")) {
3079
+ if (inCodeBlock) {
3080
+ outputLines.push(`<pre><code${codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : ""}>${escapeHtml(codeBlockContent.join("\n"))}</code></pre>`);
3081
+ codeBlockContent = [];
3082
+ codeBlockLang = "";
3083
+ inCodeBlock = false;
3084
+ } else {
3085
+ closeList();
3086
+ closeBlockquote();
3087
+ inCodeBlock = true;
3088
+ codeBlockLang = line.trimStart().slice(3).trim();
3089
+ }
3090
+ continue;
3091
+ }
3092
+ if (inCodeBlock) {
3093
+ codeBlockContent.push(line);
3094
+ continue;
3095
+ }
3096
+ if (/^(\s*[-*_]\s*){3,}$/.test(line)) {
3097
+ closeList();
3098
+ closeBlockquote();
3099
+ outputLines.push("<hr />");
3100
+ continue;
3101
+ }
3102
+ const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
3103
+ if (headingMatch) {
3104
+ closeList();
3105
+ closeBlockquote();
3106
+ const level = headingMatch[1].length;
3107
+ const text = parseInline(headingMatch[2], linkResolver);
3108
+ outputLines.push(`<h${level}>${text}</h${level}>`);
3109
+ continue;
3110
+ }
3111
+ const blockquoteMatch = line.match(/^>\s?(.*)$/);
3112
+ if (blockquoteMatch) {
3113
+ closeList();
3114
+ if (!inBlockquote) {
3115
+ inBlockquote = true;
3116
+ outputLines.push("<blockquote>");
3117
+ }
3118
+ const text = blockquoteMatch[1].trim();
3119
+ if (text) {
3120
+ outputLines.push(`<p>${parseInline(text, linkResolver)}</p>`);
3121
+ }
3122
+ continue;
3123
+ } else if (inBlockquote) {
3124
+ closeBlockquote();
3125
+ }
3126
+ const ulMatch = line.match(/^[\s]*[-*+]\s+(.+)$/);
3127
+ if (ulMatch) {
3128
+ closeBlockquote();
3129
+ if (inList !== "ul") {
3130
+ closeList();
3131
+ inList = "ul";
3132
+ outputLines.push("<ul>");
3133
+ }
3134
+ outputLines.push(`<li>${parseInline(ulMatch[1], linkResolver)}</li>`);
3135
+ continue;
3136
+ }
3137
+ const olMatch = line.match(/^[\s]*\d+\.\s+(.+)$/);
3138
+ if (olMatch) {
3139
+ closeBlockquote();
3140
+ if (inList !== "ol") {
3141
+ closeList();
3142
+ inList = "ol";
3143
+ outputLines.push("<ol>");
3144
+ }
3145
+ outputLines.push(`<li>${parseInline(olMatch[1], linkResolver)}</li>`);
3146
+ continue;
3147
+ }
3148
+ if (inList) {
3149
+ closeList();
3150
+ }
3151
+ if (line.trim() === "") {
3152
+ continue;
3153
+ }
3154
+ outputLines.push(`<p>${parseInline(line, linkResolver)}</p>`);
3155
+ }
3156
+ if (inCodeBlock) {
3157
+ outputLines.push(`<pre><code${codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : ""}>${escapeHtml(codeBlockContent.join("\n"))}</code></pre>`);
3158
+ }
3159
+ closeList();
3160
+ closeBlockquote();
3161
+ return outputLines.join("\n");
3162
+ }
3163
+ function extractComponents(content, components) {
3164
+ if (!components) return [];
3165
+ const extracted = [];
3166
+ for (const [name, def] of Object.entries(components)) {
3167
+ const matches = Array.from(content.matchAll(def.pattern));
3168
+ matches.forEach(() => {
3169
+ extracted.push({
3170
+ name,
3171
+ props: { ...def.props }
3172
+ });
3173
+ });
3174
+ }
3175
+ return extracted;
3176
+ }
3177
+ function createMarkdownRenderer(props) {
3178
+ const { content, components, linkResolver } = props;
3179
+ const html = parseMarkdown(content, linkResolver);
3180
+ const extractedComponents = extractComponents(content, components);
3181
+ const ariaProps = {
3182
+ role: "document",
3183
+ "aria-label": "Rendered markdown content"
3184
+ };
3185
+ return {
3186
+ html,
3187
+ components: extractedComponents,
3188
+ ariaProps
3189
+ };
3190
+ }
3191
+ var markdownRendererTokens = {
3192
+ name: "markdown-renderer",
3193
+ tokens: {
3194
+ bg: { variable: "--rfr-markdown-bg", fallback: "hsl(var(--background))" },
3195
+ fg: { variable: "--rfr-markdown-fg", fallback: "hsl(var(--foreground))" },
3196
+ codeBg: { variable: "--rfr-markdown-code-bg", fallback: "hsl(var(--muted))" },
3197
+ linkColor: { variable: "--rfr-markdown-link", fallback: "hsl(var(--primary))" },
3198
+ borderColor: { variable: "--rfr-markdown-border", fallback: "hsl(var(--border))" }
3199
+ }
3200
+ };
3201
+ var proseVariants = cva({
3202
+ base: "prose max-w-none text-foreground leading-relaxed",
3203
+ variants: {
3204
+ size: {
3205
+ sm: "prose-sm text-sm",
3206
+ default: "prose-base text-base",
3207
+ lg: "prose-lg text-lg"
3208
+ },
3209
+ theme: {
3210
+ light: "bg-white text-gray-900",
3211
+ dark: "bg-gray-900 text-gray-100"
3212
+ }
3213
+ },
3214
+ defaultVariants: {
3215
+ size: "default"
3216
+ }
3217
+ });
3218
+ function sanitizeHtml(html) {
3219
+ let sanitized = html;
3220
+ sanitized = sanitized.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
3221
+ sanitized = sanitized.replace(/<\/?script[^>]*>/gi, "");
3222
+ sanitized = sanitized.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
3223
+ sanitized = sanitized.replace(/(href|src)\s*=\s*["']?\s*javascript\s*:[^"'>]*/gi, '$1=""');
3224
+ return sanitized;
3225
+ }
3226
+ var MarkdownRenderer = React11.forwardRef(
3227
+ ({ content, components, linkResolver, className, size }, ref) => {
3228
+ const coreProps = { content, components, linkResolver };
3229
+ const api = createMarkdownRenderer(coreProps);
3230
+ const classes = cn(proseVariants({ size }), className);
3231
+ const sanitizedHtml = sanitizeHtml(api.html);
3232
+ return /* @__PURE__ */ jsx(
3233
+ "div",
3234
+ {
3235
+ ref,
3236
+ className: classes,
3237
+ ...api.ariaProps,
3238
+ dangerouslySetInnerHTML: { __html: sanitizedHtml }
3239
+ }
3240
+ );
3241
+ }
3242
+ );
3243
+ MarkdownRenderer.displayName = "MarkdownRenderer";
3244
+
3245
+ // ../thread-view/dist/index.js
3246
+ function formatTimestamp(date) {
3247
+ const hours = date.getHours();
3248
+ const minutes = date.getMinutes();
3249
+ const ampm = hours >= 12 ? "PM" : "AM";
3250
+ const displayHours = hours % 12 || 12;
3251
+ const displayMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
3252
+ return `${displayHours}:${displayMinutes} ${ampm}`;
3253
+ }
3254
+ function formatRelativeTime(date, now) {
3255
+ const reference = now ?? /* @__PURE__ */ new Date();
3256
+ const diffMs = reference.getTime() - date.getTime();
3257
+ const diffSeconds = Math.floor(diffMs / 1e3);
3258
+ const diffMinutes = Math.floor(diffSeconds / 60);
3259
+ const diffHours = Math.floor(diffMinutes / 60);
3260
+ const diffDays = Math.floor(diffHours / 24);
3261
+ if (diffSeconds < 60) return "just now";
3262
+ if (diffMinutes < 60) return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`;
3263
+ if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
3264
+ if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
3265
+ return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
3266
+ }
3267
+ function createThreadView(props) {
3268
+ const { messages, onReply, onReact, currentUserId } = props;
3269
+ let replyingTo = null;
3270
+ const threadId = generateId("rfr-thread");
3271
+ const labelId = generateId("rfr-thread-label");
3272
+ function startReply(messageId) {
3273
+ replyingTo = messageId;
3274
+ }
3275
+ function cancelReply() {
3276
+ replyingTo = null;
3277
+ }
3278
+ function reply(messageId, content) {
3279
+ onReply?.(messageId, content);
3280
+ replyingTo = null;
3281
+ }
3282
+ function react(messageId, emoji) {
3283
+ onReact?.(messageId, emoji);
3284
+ }
3285
+ const ariaProps = {
3286
+ role: "log",
3287
+ "aria-label": "Message thread",
3288
+ "aria-live": "polite",
3289
+ id: threadId
3290
+ };
3291
+ function getMessageAriaProps(message) {
3292
+ const isOwn = currentUserId && message.author.id === currentUserId;
3293
+ return {
3294
+ role: "article",
3295
+ "aria-label": `Message from ${message.author.name}${isOwn ? " (you)" : ""} at ${formatTimestamp(message.timestamp)}`
3296
+ };
3297
+ }
3298
+ function getReplyButtonAriaProps(_messageId) {
3299
+ return {
3300
+ role: "button",
3301
+ "aria-label": `Reply to message`
3302
+ };
3303
+ }
3304
+ return {
3305
+ state: {
3306
+ messages,
3307
+ get replyingTo() {
3308
+ return replyingTo;
3309
+ }
3310
+ },
3311
+ startReply,
3312
+ cancelReply,
3313
+ reply,
3314
+ react,
3315
+ formatTimestamp,
3316
+ formatRelativeTime,
3317
+ ariaProps,
3318
+ getMessageAriaProps,
3319
+ getReplyButtonAriaProps,
3320
+ ids: {
3321
+ thread: threadId,
3322
+ label: labelId
3323
+ }
3324
+ };
3325
+ }
3326
+ var threadContainerStyles = "flex flex-col gap-1";
3327
+ var threadMessageStyles = "flex gap-3 px-4 py-2 hover:bg-accent/50 rounded-md transition-colors group";
3328
+ var threadAvatarStyles = "h-9 w-9 rounded-full bg-muted flex items-center justify-center text-sm font-medium overflow-hidden flex-shrink-0";
3329
+ var threadContentStyles = "flex-1 min-w-0";
3330
+ var threadAuthorStyles = "font-semibold text-sm";
3331
+ var threadTimestampStyles = "text-xs text-muted-foreground ml-2";
3332
+ var threadBodyStyles = "text-sm mt-0.5 whitespace-pre-wrap break-words";
3333
+ var threadReactionsStyles = "flex flex-wrap gap-1 mt-1";
3334
+ var threadReplyIndicatorStyles = "flex items-center gap-1 mt-1 text-xs text-primary cursor-pointer hover:underline";
3335
+ var threadActionsStyles = "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity";
3336
+ var threadAttachmentStyles = "flex items-center gap-2 mt-1 p-2 rounded border text-xs bg-muted/50";
3337
+ var threadEditedStyles = "text-xs text-muted-foreground ml-1";
3338
+
3339
+ // ../react-conversation/dist/index.js
3340
+ function useConversation(config) {
3341
+ const apiRef = React11.useRef(null);
3342
+ if (apiRef.current === null) {
3343
+ apiRef.current = createConversation(config);
3344
+ }
3345
+ const api = apiRef.current;
3346
+ const state = React11.useSyncExternalStore(api.subscribe, api.getState, api.getState);
3347
+ const transport = config?.transport;
3348
+ React11.useEffect(() => {
3349
+ if (transport) api.setTransport(transport);
3350
+ }, [api, transport]);
3351
+ return {
3352
+ state,
3353
+ api,
3354
+ sendMessage: api.sendMessage,
3355
+ newConversation: api.newConversation,
3356
+ selectConversation: api.selectConversation,
3357
+ deleteConversation: api.deleteConversation,
3358
+ renameConversation: api.renameConversation,
3359
+ editMessage: api.editMessage,
3360
+ deleteMessage: api.deleteMessage,
3361
+ react: api.react,
3362
+ retryLast: api.retryLast,
3363
+ stop: api.stop,
3364
+ openThread: api.openThread,
3365
+ replyTo: api.replyTo,
3366
+ closeThread: api.closeThread,
3367
+ setThreadingMode: api.setThreadingMode
3368
+ };
3369
+ }
3370
+ var h = React11.createElement;
3371
+ var EMOJI = {
3372
+ smile: "\u{1F604}",
3373
+ grin: "\u{1F601}",
3374
+ joy: "\u{1F602}",
3375
+ rofl: "\u{1F923}",
3376
+ wink: "\u{1F609}",
3377
+ heart_eyes: "\u{1F60D}",
3378
+ thinking: "\u{1F914}",
3379
+ neutral: "\u{1F610}",
3380
+ sob: "\u{1F62D}",
3381
+ scream: "\u{1F631}",
3382
+ tada: "\u{1F389}",
3383
+ fire: "\u{1F525}",
3384
+ heart: "\u2764\uFE0F",
3385
+ thumbsup: "\u{1F44D}",
3386
+ thumbsdown: "\u{1F44E}",
3387
+ clap: "\u{1F44F}",
3388
+ pray: "\u{1F64F}",
3389
+ eyes: "\u{1F440}",
3390
+ rocket: "\u{1F680}",
3391
+ sparkles: "\u2728",
3392
+ star: "\u2B50",
3393
+ check: "\u2705",
3394
+ x: "\u274C",
3395
+ warning: "\u26A0\uFE0F",
3396
+ bulb: "\u{1F4A1}",
3397
+ bug: "\u{1F41B}",
3398
+ wave: "\u{1F44B}",
3399
+ ok_hand: "\u{1F44C}",
3400
+ muscle: "\u{1F4AA}",
3401
+ "100": "\u{1F4AF}",
3402
+ poop: "\u{1F4A9}",
3403
+ ghost: "\u{1F47B}",
3404
+ robot: "\u{1F916}",
3405
+ cat: "\u{1F431}",
3406
+ dog: "\u{1F436}",
3407
+ coffee: "\u2615",
3408
+ pizza: "\u{1F355}",
3409
+ beer: "\u{1F37A}",
3410
+ sun: "\u2600\uFE0F",
3411
+ moon: "\u{1F319}",
3412
+ zap: "\u26A1"
3413
+ };
3414
+ function detectTrigger(text, caret) {
3415
+ const before = text.slice(0, caret);
3416
+ const m = before.match(/(?:^|\s)([/@:])([\w+-]*)$/);
3417
+ if (!m) return null;
3418
+ const type = m[1];
3419
+ const query = m[2] ?? "";
3420
+ return { type, query, start: caret - query.length - 1, end: caret };
3421
+ }
3422
+ function Composer({
3423
+ placeholder = "Type a message\u2026 (/ commands, @ mentions, : emoji)",
3424
+ busy = false,
3425
+ slashCommands = [],
3426
+ mentions,
3427
+ toolbar = true,
3428
+ emoji = true,
3429
+ attachments = true,
3430
+ onSubmit,
3431
+ onStop,
3432
+ onSlashCommand,
3433
+ autoFocus
3434
+ }) {
3435
+ const [value, setValue] = React11.useState("");
3436
+ const [pending, setPending] = React11.useState([]);
3437
+ const [trigger, setTrigger] = React11.useState(null);
3438
+ const [active, setActive] = React11.useState(0);
3439
+ const [mentionItems, setMentionItems] = React11.useState([]);
3440
+ const ref = React11.useRef(null);
3441
+ const fileRef = React11.useRef(null);
3442
+ React11.useEffect(() => {
3443
+ if (trigger?.type !== "@" || !mentions) {
3444
+ setMentionItems([]);
3445
+ return;
3446
+ }
3447
+ if (Array.isArray(mentions)) {
3448
+ const q = trigger.query.toLowerCase();
3449
+ setMentionItems(mentions.filter((m) => m.label.toLowerCase().includes(q)).slice(0, 8));
3450
+ return;
3451
+ }
3452
+ let cancelled = false;
3453
+ Promise.resolve(mentions(trigger.query)).then((res) => {
3454
+ if (!cancelled) setMentionItems(res.slice(0, 8));
3455
+ });
3456
+ return () => {
3457
+ cancelled = true;
3458
+ };
3459
+ }, [trigger, mentions]);
3460
+ const items = React11.useMemo(() => {
3461
+ if (!trigger) return [];
3462
+ const q = trigger.query.toLowerCase();
3463
+ if (trigger.type === "/") {
3464
+ return slashCommands.filter((c) => c.label.toLowerCase().includes(q) || c.id.toLowerCase().includes(q)).slice(0, 8).map((c) => ({ key: c.id, primary: c.label, secondary: c.description, icon: c.icon, apply: c.insertText ?? "", runCmd: c }));
3465
+ }
3466
+ if (trigger.type === "@") {
3467
+ return mentionItems.map((m) => ({ key: m.id, primary: m.label, icon: m.avatarUrl ? "" : "@", apply: `@${m.label} ` }));
3468
+ }
3469
+ if (trigger.type === ":" && emoji) {
3470
+ return Object.entries(EMOJI).filter(([name]) => name.includes(q)).slice(0, 8).map(([name, char]) => ({ key: name, primary: `${char} :${name}:`, apply: char }));
3471
+ }
3472
+ return [];
3473
+ }, [trigger, slashCommands, mentionItems, emoji]);
3474
+ const menuOpen = trigger !== null && items.length > 0;
3475
+ React11.useEffect(() => setActive(0), [trigger?.type, trigger?.query]);
3476
+ function syncFromTextarea(el) {
3477
+ setValue(el.value);
3478
+ setTrigger(detectTrigger(el.value, el.selectionStart ?? el.value.length));
3479
+ }
3480
+ function selectItem(i) {
3481
+ const item = items[i];
3482
+ if (!item || !trigger) return;
3483
+ if (trigger.type === "/" && !item.apply && item.runCmd) {
3484
+ const next2 = value.slice(0, trigger.start) + value.slice(trigger.end);
3485
+ setValue(next2);
3486
+ setTrigger(null);
3487
+ onSlashCommand?.(item.runCmd);
3488
+ queueCaret(trigger.start);
3489
+ return;
3490
+ }
3491
+ const next = value.slice(0, trigger.start) + item.apply + value.slice(trigger.end);
3492
+ setValue(next);
3493
+ setTrigger(null);
3494
+ queueCaret(trigger.start + item.apply.length);
3495
+ }
3496
+ function queueCaret(pos) {
3497
+ requestAnimationFrame(() => {
3498
+ const el = ref.current;
3499
+ if (!el) return;
3500
+ el.focus();
3501
+ el.setSelectionRange(pos, pos);
3502
+ });
3503
+ }
3504
+ function format(kind) {
3505
+ const el = ref.current;
3506
+ if (!el) return;
3507
+ const start = el.selectionStart ?? 0;
3508
+ const end = el.selectionEnd ?? 0;
3509
+ const sel = value.slice(start, end);
3510
+ let replacement = sel;
3511
+ let caretOffset = 0;
3512
+ if (kind === "bold") replacement = `**${sel || "bold"}**`;
3513
+ else if (kind === "italic") replacement = `*${sel || "italic"}*`;
3514
+ else if (kind === "code") replacement = sel.includes("\n") ? `
3515
+ \`\`\`
3516
+ ${sel}
3517
+ \`\`\`
3518
+ ` : `\`${sel || "code"}\``;
3519
+ else if (kind === "link") {
3520
+ replacement = `[${sel || "text"}](url)`;
3521
+ caretOffset = replacement.length - 4;
3522
+ } else if (kind === "quote" || kind === "ul" || kind === "ol") {
3523
+ const prefix = kind === "quote" ? "> " : kind === "ul" ? "- " : "1. ";
3524
+ const block = (sel || "item").split("\n").map((l) => prefix + l).join("\n");
3525
+ replacement = (start > 0 && value[start - 1] !== "\n" ? "\n" : "") + block;
3526
+ }
3527
+ const next = value.slice(0, start) + replacement + value.slice(end);
3528
+ setValue(next);
3529
+ const caret = caretOffset ? start + caretOffset : start + replacement.length;
3530
+ queueCaret(caret);
3531
+ }
3532
+ function submit() {
3533
+ const text = value.trim();
3534
+ if (!text && pending.length === 0 || busy) return;
3535
+ onSubmit(text, pending.length ? pending : void 0);
3536
+ setValue("");
3537
+ setPending([]);
3538
+ setTrigger(null);
3539
+ }
3540
+ function onFiles(files) {
3541
+ if (!files) return;
3542
+ setPending((p) => [
3543
+ ...p,
3544
+ ...Array.from(files).map((f) => ({
3545
+ id: `${f.name}-${f.size}-${f.lastModified}`,
3546
+ name: f.name,
3547
+ url: URL.createObjectURL(f),
3548
+ type: f.type || "application/octet-stream",
3549
+ size: f.size
3550
+ }))
3551
+ ]);
3552
+ }
3553
+ function onKeyDown(e) {
3554
+ if (menuOpen) {
3555
+ if (e.key === "ArrowDown") return e.preventDefault(), setActive((a) => (a + 1) % items.length);
3556
+ if (e.key === "ArrowUp") return e.preventDefault(), setActive((a) => (a - 1 + items.length) % items.length);
3557
+ if (e.key === "Enter" || e.key === "Tab") return e.preventDefault(), selectItem(active);
3558
+ if (e.key === "Escape") return e.preventDefault(), setTrigger(null);
3559
+ }
3560
+ const mod = e.metaKey || e.ctrlKey;
3561
+ if (mod && e.key.toLowerCase() === "b") return e.preventDefault(), format("bold");
3562
+ if (mod && e.key.toLowerCase() === "i") return e.preventDefault(), format("italic");
3563
+ if (mod && e.key.toLowerCase() === "e") return e.preventDefault(), format("code");
3564
+ if (mod && e.key.toLowerCase() === "k") return e.preventDefault(), format("link");
3565
+ if (e.key === "Enter" && !e.shiftKey) {
3566
+ e.preventDefault();
3567
+ submit();
3568
+ }
3569
+ }
3570
+ const toolbarBtn = (label, title, kind) => h(
3571
+ "button",
3572
+ {
3573
+ key: kind,
3574
+ type: "button",
3575
+ title,
3576
+ className: "rounded px-1.5 py-0.5 text-xs text-muted-foreground hover:bg-accent hover:text-foreground",
3577
+ onMouseDown: (e) => e.preventDefault(),
3578
+ // keep textarea selection
3579
+ onClick: () => format(kind)
3580
+ },
3581
+ label
3582
+ );
3583
+ return h(
3584
+ "div",
3585
+ { className: "border-t border-border" },
3586
+ // attachment chips
3587
+ pending.length > 0 ? h(
3588
+ "div",
3589
+ { className: "flex flex-wrap gap-2 px-3 pt-2" },
3590
+ ...pending.map(
3591
+ (a) => h(
3592
+ "span",
3593
+ { key: a.id, className: "inline-flex items-center gap-1 rounded bg-muted px-2 py-0.5 text-xs" },
3594
+ a.name,
3595
+ h(
3596
+ "button",
3597
+ {
3598
+ type: "button",
3599
+ className: "text-muted-foreground hover:text-destructive",
3600
+ onClick: () => setPending((p) => p.filter((x) => x.id !== a.id))
3601
+ },
3602
+ "\u2715"
3603
+ )
3604
+ )
3605
+ )
3606
+ ) : null,
3607
+ // toolbar
3608
+ toolbar ? h(
3609
+ "div",
3610
+ { className: "flex items-center gap-0.5 px-2 pt-2" },
3611
+ toolbarBtn("B", "Bold (\u2318B)", "bold"),
3612
+ toolbarBtn("\u{1D456}", "Italic (\u2318I)", "italic"),
3613
+ toolbarBtn("</>", "Code (\u2318E)", "code"),
3614
+ toolbarBtn("\u{1F517}", "Link (\u2318K)", "link"),
3615
+ toolbarBtn("\u275D", "Quote", "quote"),
3616
+ toolbarBtn("\u2022", "Bulleted list", "ul"),
3617
+ toolbarBtn("1.", "Numbered list", "ol")
3618
+ ) : null,
3619
+ // input row (relative for the popup menu)
3620
+ h(
3621
+ "div",
3622
+ { className: "relative flex items-end gap-2 p-3" },
3623
+ menuOpen ? h(
3624
+ "div",
3625
+ {
3626
+ className: "absolute bottom-full left-3 z-20 mb-1 w-72 overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
3627
+ role: "listbox"
3628
+ },
3629
+ h(
3630
+ "div",
3631
+ { className: "border-b border-border px-2 py-1 text-[10px] uppercase tracking-wide text-muted-foreground" },
3632
+ trigger?.type === "/" ? "Commands" : trigger?.type === "@" ? "Mentions" : "Emoji"
3633
+ ),
3634
+ ...items.map(
3635
+ (it, i) => h(
3636
+ "button",
3637
+ {
3638
+ key: it.key,
3639
+ type: "button",
3640
+ role: "option",
3641
+ "aria-selected": i === active,
3642
+ className: cn(
3643
+ "flex w-full items-center gap-2 px-2 py-1.5 text-left text-sm",
3644
+ i === active ? "bg-accent" : "hover:bg-accent/50"
3645
+ ),
3646
+ onMouseEnter: () => setActive(i),
3647
+ onMouseDown: (e) => e.preventDefault(),
3648
+ onClick: () => selectItem(i)
3649
+ },
3650
+ it.icon ? h("span", { className: "w-4 text-center text-muted-foreground" }, it.icon) : null,
3651
+ h("span", { className: "flex-1 truncate" }, it.primary),
3652
+ it.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it.secondary) : null
3653
+ )
3654
+ )
3655
+ ) : null,
3656
+ attachments ? h(
3657
+ React11.Fragment,
3658
+ null,
3659
+ h("input", {
3660
+ ref: fileRef,
3661
+ type: "file",
3662
+ accept: "image/*",
3663
+ multiple: true,
3664
+ className: "hidden",
3665
+ onChange: (e) => {
3666
+ onFiles(e.target.files);
3667
+ e.target.value = "";
3668
+ }
3669
+ }),
3670
+ h(
3671
+ "button",
3672
+ {
3673
+ type: "button",
3674
+ className: "rounded-md border border-border px-2 py-2 text-sm hover:bg-accent",
3675
+ "aria-label": "Attach image or GIF",
3676
+ onClick: () => fileRef.current?.click()
3677
+ },
3678
+ "\u{1F4CE}"
3679
+ )
3680
+ ) : null,
3681
+ h("textarea", {
3682
+ ref,
3683
+ className: "max-h-40 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",
3684
+ rows: 1,
3685
+ value,
3686
+ placeholder,
3687
+ autoFocus,
3688
+ "aria-label": "Message",
3689
+ onChange: (e) => syncFromTextarea(e.target),
3690
+ onClick: (e) => syncFromTextarea(e.currentTarget),
3691
+ onKeyUp: (e) => syncFromTextarea(e.currentTarget),
3692
+ onKeyDown,
3693
+ onBlur: () => setTimeout(() => setTrigger(null), 120)
3694
+ }),
3695
+ busy ? h(
3696
+ "button",
3697
+ {
3698
+ type: "button",
3699
+ className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground",
3700
+ onClick: () => onStop?.()
3701
+ },
3702
+ "Stop"
3703
+ ) : h(
3704
+ "button",
3705
+ {
3706
+ type: "button",
3707
+ className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50",
3708
+ disabled: !value.trim() && pending.length === 0,
3709
+ onClick: submit
3710
+ },
3711
+ "Send"
3712
+ )
3713
+ )
3714
+ );
3715
+ }
3716
+ var h2 = React11.createElement;
3717
+ var QUICK_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F389}", "\u{1F440}", "\u{1F64F}"];
3718
+ var chatMd = cn(
3719
+ "max-w-none text-sm",
3720
+ "[&_p]:my-1 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0",
3721
+ "[&_pre]:my-2 [&_pre]:rounded-md [&_pre]:text-xs [&_pre]:leading-relaxed",
3722
+ "[&_ul]:my-1 [&_ol]:my-1 [&_li]:my-0.5",
3723
+ "[&_h1]:text-base [&_h1]:font-semibold [&_h1]:mt-2 [&_h1]:mb-1",
3724
+ "[&_h2]:text-sm [&_h2]:font-semibold [&_h2]:mt-2 [&_h2]:mb-1",
3725
+ "[&_h3]:text-sm [&_h3]:font-semibold",
3726
+ "[&_blockquote]:my-1 [&_blockquote]:border-l-2 [&_blockquote]:pl-2 [&_blockquote]:not-italic [&_blockquote]:text-muted-foreground",
3727
+ "[&_img]:my-1 [&_img]:max-h-60 [&_img]:rounded-md",
3728
+ "[&_code]:text-[0.85em]"
3729
+ );
3730
+ function Avatar({ name, avatarUrl, size = 8 }) {
3731
+ return h2(
3732
+ "div",
3733
+ {
3734
+ className: cn(
3735
+ "flex-shrink-0 overflow-hidden rounded-full bg-muted flex items-center justify-center text-xs font-medium",
3736
+ size === 8 ? "h-8 w-8" : "h-7 w-7"
3737
+ )
3738
+ },
3739
+ avatarUrl ? h2("img", { src: avatarUrl, alt: name, className: "h-full w-full object-cover" }) : (name.charAt(0) || "?").toUpperCase()
3740
+ );
3741
+ }
3742
+ function TypingDots() {
3743
+ return h2(
3744
+ "div",
3745
+ { className: "flex items-center gap-1 py-1.5", "aria-label": "Assistant is typing" },
3746
+ ...[0, 150, 300].map(
3747
+ (delay) => h2("span", {
3748
+ key: delay,
3749
+ className: "h-1.5 w-1.5 rounded-full bg-muted-foreground/60 animate-bounce",
3750
+ style: { animationDelay: `${delay}ms` }
3751
+ })
3752
+ )
3753
+ );
3754
+ }
3755
+ function Attachments({ attachments }) {
3756
+ return h2(
3757
+ "div",
3758
+ { className: "mt-2 flex flex-wrap gap-2" },
3759
+ ...attachments.map(
3760
+ (a) => a.type.startsWith("image/") ? h2("img", { key: a.id, src: a.url, alt: a.name, className: "max-h-60 rounded-md border border-border object-contain" }) : h2(
3761
+ "a",
3762
+ {
3763
+ key: a.id,
3764
+ href: a.url,
3765
+ target: "_blank",
3766
+ rel: "noreferrer",
3767
+ className: "inline-flex items-center gap-2 rounded-md border border-border bg-background/60 px-2 py-1 text-xs"
3768
+ },
3769
+ "\u{1F4CE} ",
3770
+ a.name
3771
+ )
3772
+ )
3773
+ );
3774
+ }
3775
+ function MessageBody({ message }) {
3776
+ if (message.status === "streaming" && message.content === "") return h2(TypingDots);
3777
+ return h2(
3778
+ React11.Fragment,
3779
+ null,
3780
+ message.content ? h2(MarkdownRenderer, { content: message.content, size: "sm", className: chatMd }) : null,
3781
+ message.attachments && message.attachments.length > 0 ? h2(Attachments, { attachments: message.attachments }) : null,
3782
+ message.status === "error" ? h2("div", { className: "mt-1 text-xs text-destructive", role: "alert" }, message.error ?? "Failed to send.") : null
3783
+ );
3784
+ }
3785
+ function Reactions({ message, onReact, align }) {
3786
+ if (!message.reactions || message.reactions.length === 0) return null;
3787
+ return h2(
3788
+ "div",
3789
+ { className: cn("mt-1 flex flex-wrap gap-1", align === "end" && "justify-end") },
3790
+ ...message.reactions.map(
3791
+ (r) => h2(
3792
+ "button",
3793
+ {
3794
+ key: r.emoji,
3795
+ type: "button",
3796
+ onClick: () => onReact(r.emoji),
3797
+ className: cn(
3798
+ "inline-flex items-center gap-1 rounded-full border px-1.5 py-0.5 text-xs",
3799
+ r.userReacted ? "border-primary bg-primary/10" : "border-border bg-background"
3800
+ )
3801
+ },
3802
+ `${r.emoji} ${r.count}`
3803
+ )
3804
+ )
3805
+ );
3806
+ }
3807
+ function QuotedParent({ parent, onClick }) {
3808
+ const snippet = parent.content.length > 80 ? `${parent.content.slice(0, 80)}\u2026` : parent.content;
3809
+ return h2(
3810
+ "button",
3811
+ {
3812
+ type: "button",
3813
+ onClick,
3814
+ className: "mb-1 flex max-w-full items-start gap-2 rounded-md border-l-2 border-primary/50 bg-muted/50 px-2 py-1 text-left text-xs text-muted-foreground hover:bg-muted"
3815
+ },
3816
+ h2("span", { className: "font-medium" }, parent.author.name),
3817
+ h2("span", { className: "truncate" }, snippet)
3818
+ );
3819
+ }
3820
+ function HoverActions({
3821
+ message,
3822
+ conversation,
3823
+ isOwn,
3824
+ onEdit,
3825
+ onToggleEmojis,
3826
+ align
3827
+ }) {
3828
+ const { replyTo, deleteMessage } = conversation;
3829
+ return h2(
3830
+ "div",
3831
+ {
3832
+ className: cn(
3833
+ "mt-1 flex items-center gap-3 text-xs text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100",
3834
+ align === "end" && "justify-end"
3835
+ )
3836
+ },
3837
+ // Reply targets this specific message but groups under the originating root.
3838
+ h2("button", { type: "button", className: "hover:text-foreground", onClick: () => replyTo(message.id) }, "Reply"),
3839
+ h2("button", { type: "button", className: "hover:text-foreground", onClick: onToggleEmojis }, "React"),
3840
+ isOwn ? h2("button", { type: "button", className: "hover:text-foreground", onClick: onEdit }, "Edit") : null,
3841
+ isOwn ? h2("button", { type: "button", className: "hover:text-destructive", onClick: () => deleteMessage(message.id) }, "Delete") : null
3842
+ );
3843
+ }
3844
+ function EmojiRow({ onPick, align }) {
3845
+ return h2(
3846
+ "div",
3847
+ { className: cn("mt-1 flex gap-1", align === "end" && "justify-end") },
3848
+ ...QUICK_EMOJIS.map(
3849
+ (emoji) => h2("button", { key: emoji, type: "button", className: "rounded px-1 text-base hover:bg-accent", onClick: () => onPick(emoji) }, emoji)
3850
+ )
3851
+ );
3852
+ }
3853
+ function EditField({ message, conversation, onDone }) {
3854
+ const [draft, setDraft] = React11.useState(message.content);
3855
+ function save() {
3856
+ const t = draft.trim();
3857
+ if (t && t !== message.content) conversation.editMessage(message.id, t);
3858
+ onDone();
3859
+ }
3860
+ return h2(
3861
+ "div",
3862
+ { className: "mt-1" },
3863
+ h2("textarea", {
3864
+ className: "w-full resize-none rounded-md border border-border bg-background px-2 py-1 text-sm",
3865
+ value: draft,
3866
+ autoFocus: true,
3867
+ onChange: (e) => setDraft(e.target.value),
3868
+ onKeyDown: (e) => {
3869
+ if (e.key === "Enter" && !e.shiftKey) {
3870
+ e.preventDefault();
3871
+ save();
3872
+ }
3873
+ if (e.key === "Escape") onDone();
3874
+ }
3875
+ }),
3876
+ h2(
3877
+ "div",
3878
+ { className: "mt-1 flex gap-2 text-xs" },
3879
+ h2("button", { type: "button", className: "text-primary", onClick: save }, "Save"),
3880
+ h2("button", { type: "button", className: "text-muted-foreground", onClick: onDone }, "Cancel")
3881
+ )
3882
+ );
3883
+ }
3884
+ function MessageRow({
3885
+ message,
3886
+ conversation,
3887
+ currentUserId,
3888
+ showThreadAffordance,
3889
+ quotedParent
3890
+ }) {
3891
+ const { state, react, openThread } = conversation;
3892
+ const [showEmojis, setShowEmojis] = React11.useState(false);
3893
+ const [editing, setEditing] = React11.useState(false);
3894
+ const isUser = currentUserId ? message.author.id === currentUserId : message.role === "user";
3895
+ const replyCount = getReplyCount(state.messages, message.id);
3896
+ const align = isUser ? "end" : "start";
3897
+ const inner = h2(
3898
+ React11.Fragment,
3899
+ null,
3900
+ quotedParent ? h2(QuotedParent, { parent: quotedParent, onClick: () => openThread(rootIdOf(state.messages, quotedParent.id)) }) : null,
3901
+ editing ? h2(EditField, { message, conversation, onDone: () => setEditing(false) }) : isUser ? h2("div", { className: "inline-block rounded-2xl rounded-br-sm bg-primary/10 px-3 py-2 text-left" }, h2(MessageBody, { message })) : h2(MessageBody, { message }),
3902
+ h2(Reactions, { message, onReact: (e) => react(message.id, e), align }),
3903
+ showThreadAffordance && replyCount > 0 ? h2(
3904
+ "button",
3905
+ { type: "button", className: cn("mt-1 text-xs font-medium text-primary hover:underline", align === "end" && "self-end"), onClick: () => openThread(message.id) },
3906
+ `\u{1F4AC} ${replyCount} ${replyCount === 1 ? "reply" : "replies"}`
3907
+ ) : null,
3908
+ h2(HoverActions, { message, conversation, isOwn: isUser, onEdit: () => setEditing(true), onToggleEmojis: () => setShowEmojis((v) => !v), align }),
3909
+ showEmojis ? h2(EmojiRow, {
3910
+ onPick: (e) => {
3911
+ react(message.id, e);
3912
+ setShowEmojis(false);
3913
+ },
3914
+ align
3915
+ }) : null
3916
+ );
3917
+ if (!isUser) {
3918
+ return h2(
3919
+ "div",
3920
+ { className: "group flex gap-3 rounded-md px-3 py-2 hover:bg-accent/30", role: "article", "aria-label": `Message from ${message.author.name}`, "data-message-id": message.id },
3921
+ h2(Avatar, { name: message.author.name, avatarUrl: message.author.avatarUrl }),
3922
+ h2(
3923
+ "div",
3924
+ { className: "min-w-0 flex-1" },
3925
+ h2(
3926
+ "div",
3927
+ { className: "flex items-baseline gap-2" },
3928
+ h2("span", { className: "text-sm font-semibold" }, message.author.name),
3929
+ h2("span", { className: "text-xs text-muted-foreground", title: message.timestamp.toLocaleString() }, formatTimestamp(message.timestamp)),
3930
+ message.edited ? h2("span", { className: "text-xs text-muted-foreground" }, "(edited)") : null
3931
+ ),
3932
+ inner
3933
+ )
3934
+ );
3935
+ }
3936
+ return h2(
3937
+ "div",
3938
+ { className: "group flex flex-col items-end px-3 py-1.5", role: "article", "aria-label": `Message from ${message.author.name}`, "data-message-id": message.id },
3939
+ h2(
3940
+ "div",
3941
+ { className: "flex items-baseline gap-2" },
3942
+ message.edited ? h2("span", { className: "text-xs text-muted-foreground" }, "(edited)") : null,
3943
+ h2("span", { className: "text-xs text-muted-foreground", title: message.timestamp.toLocaleString() }, formatTimestamp(message.timestamp)),
3944
+ h2("span", { className: "text-sm font-semibold" }, message.author.name)
3945
+ ),
3946
+ h2("div", { className: "mt-0.5 flex max-w-[80%] flex-col items-end" }, inner)
3947
+ );
3948
+ }
3949
+ function ConversationSidebar({ conversation }) {
3950
+ const { state, newConversation, selectConversation, deleteConversation } = conversation;
3951
+ return h2(
3952
+ "aside",
3953
+ { className: "flex w-56 flex-col gap-1 overflow-y-auto border-r border-border bg-muted/20 p-2", "aria-label": "Conversations" },
3954
+ h2(
3955
+ "button",
3956
+ { type: "button", className: "mb-1 w-full rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground hover:opacity-90", onClick: () => newConversation() },
3957
+ "+ New chat"
3958
+ ),
3959
+ ...state.conversations.map(
3960
+ (conv) => h2(
3961
+ "div",
3962
+ {
3963
+ key: conv.id,
3964
+ role: "button",
3965
+ "aria-current": conv.id === state.activeConversationId,
3966
+ onClick: () => selectConversation(conv.id),
3967
+ className: cn(
3968
+ "group flex items-center justify-between gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent/60",
3969
+ conv.id === state.activeConversationId && "bg-accent font-medium"
3970
+ )
3971
+ },
3972
+ h2(
3973
+ "div",
3974
+ { className: "min-w-0" },
3975
+ h2("div", { className: "truncate" }, conv.title),
3976
+ h2("div", { className: "truncate text-xs text-muted-foreground" }, formatRelativeTime(conv.updatedAt))
3977
+ ),
3978
+ h2(
3979
+ "button",
3980
+ {
3981
+ type: "button",
3982
+ className: "text-xs text-muted-foreground opacity-0 hover:text-destructive group-hover:opacity-100",
3983
+ "aria-label": `Delete ${conv.title}`,
3984
+ onClick: (e) => {
3985
+ e.stopPropagation();
3986
+ deleteConversation(conv.id);
3987
+ }
3988
+ },
3989
+ "\u2715"
3990
+ )
3991
+ )
3992
+ )
3993
+ );
3994
+ }
3995
+ function ThreadPanel({ conversation, currentUserId, composer }) {
3996
+ const { state } = conversation;
3997
+ const rootId = state.openThreadRootId;
3998
+ if (!rootId) return null;
3999
+ const messages = selectThreadMessages(state.messages, rootId);
4000
+ const target = state.replyTarget && state.replyTarget !== rootId ? findMessage(state.messages, state.replyTarget) : void 0;
4001
+ return h2(
4002
+ "aside",
4003
+ { className: "flex w-80 flex-col border-l border-border", "aria-label": "Thread" },
4004
+ h2(
4005
+ "div",
4006
+ { className: "flex items-center justify-between border-b border-border px-3 py-2" },
4007
+ h2("span", { className: "text-sm font-semibold" }, `Thread \xB7 ${messages.length - 1} ${messages.length - 1 === 1 ? "reply" : "replies"}`),
4008
+ h2("button", { type: "button", className: "text-muted-foreground hover:text-foreground", "aria-label": "Close thread", onClick: () => conversation.closeThread() }, "\u2715")
4009
+ ),
4010
+ h2(
4011
+ "div",
4012
+ { className: "flex-1 overflow-y-auto p-1" },
4013
+ ...messages.map((m) => h2(MessageRow, { key: m.id, message: m, conversation, currentUserId, showThreadAffordance: false }))
4014
+ ),
4015
+ target ? h2(
4016
+ "div",
4017
+ { className: "flex items-center justify-between gap-2 border-t border-border bg-muted/40 px-3 py-1 text-xs text-muted-foreground" },
4018
+ h2("span", { className: "truncate" }, `\u21B3 Replying to ${target.author.name}`),
4019
+ h2("button", { type: "button", className: "hover:text-foreground", onClick: () => conversation.openThread(rootId) }, "Reply to thread instead")
4020
+ ) : null,
4021
+ composer
4022
+ );
4023
+ }
4024
+ function ModeToggle({ conversation }) {
4025
+ const { state, setThreadingMode } = conversation;
4026
+ const opt = (mode, label) => h2(
4027
+ "button",
4028
+ {
4029
+ type: "button",
4030
+ onClick: () => setThreadingMode(mode),
4031
+ className: cn("rounded px-2 py-0.5 text-xs", state.threadingMode === mode ? "bg-background shadow-sm" : "text-muted-foreground")
4032
+ },
4033
+ label
4034
+ );
4035
+ return h2(
4036
+ "div",
4037
+ { className: "inline-flex rounded-md bg-muted p-0.5", role: "group", "aria-label": "Threading mode" },
4038
+ opt("inline", "Inline"),
4039
+ opt("panel", "Threads")
4040
+ );
4041
+ }
4042
+ function Chat({
4043
+ conversation,
4044
+ showConversationList = true,
4045
+ showModeToggle = true,
4046
+ placeholder,
4047
+ currentUserId,
4048
+ emptyState,
4049
+ className,
4050
+ slashCommands,
4051
+ mentions,
4052
+ onSlashCommand,
4053
+ composerToolbar = true
4054
+ }) {
4055
+ const { state, sendMessage } = conversation;
4056
+ const timeline = selectMainTimeline(state.messages, state.threadingMode);
4057
+ const activeConv = state.conversations.find((c) => c.id === state.activeConversationId);
4058
+ const busy = state.status === "sending" || state.status === "streaming";
4059
+ const mainComposer = h2(Composer, {
4060
+ placeholder,
4061
+ busy,
4062
+ slashCommands,
4063
+ mentions,
4064
+ onSlashCommand,
4065
+ toolbar: composerToolbar,
4066
+ onSubmit: (content, atts) => void sendMessage(content, { attachments: atts }),
4067
+ onStop: () => conversation.stop()
4068
+ });
4069
+ const threadComposer = state.openThreadRootId ? h2(Composer, {
4070
+ placeholder: "Reply\u2026",
4071
+ busy,
4072
+ slashCommands,
4073
+ mentions,
4074
+ onSlashCommand,
4075
+ toolbar: composerToolbar,
4076
+ onSubmit: (content, atts) => void sendMessage(content, { replyTo: state.replyTarget ?? state.openThreadRootId, attachments: atts }),
4077
+ onStop: () => conversation.stop()
4078
+ }) : null;
4079
+ const body = timeline.length === 0 ? h2("div", { className: "flex flex-1 items-center justify-center p-6 text-sm text-muted-foreground" }, emptyState ?? "No messages yet. Say hello \u{1F44B}") : h2(
4080
+ "div",
4081
+ { className: "flex-1 space-y-0.5 overflow-y-auto p-2" },
4082
+ ...timeline.map(
4083
+ (m) => h2(MessageRow, {
4084
+ key: m.id,
4085
+ message: m,
4086
+ conversation,
4087
+ currentUserId,
4088
+ // Show the "N replies" count on originating messages in BOTH modes.
4089
+ showThreadAffordance: true,
4090
+ // Inline: quote the specific message replied to (falls back to the root).
4091
+ quotedParent: state.threadingMode === "inline" && m.parentId ? findMessage(state.messages, m.replyToId ?? m.parentId) : void 0
4092
+ })
4093
+ )
4094
+ );
4095
+ return h2(
4096
+ "div",
4097
+ { className: cn("flex h-full min-h-0 overflow-hidden rounded-xl border border-border bg-background", className) },
4098
+ showConversationList ? h2(ConversationSidebar, { conversation }) : null,
4099
+ h2(
4100
+ "div",
4101
+ { className: "flex min-w-0 flex-1 flex-col" },
4102
+ h2(
4103
+ "div",
4104
+ { className: "flex items-center justify-between border-b border-border px-3 py-2" },
4105
+ h2("span", { className: "truncate text-sm font-semibold" }, activeConv?.title ?? "Chat"),
4106
+ showModeToggle ? h2(ModeToggle, { conversation }) : null
4107
+ ),
4108
+ body,
4109
+ mainComposer
4110
+ ),
4111
+ h2(ThreadPanel, { conversation, currentUserId, composer: threadComposer })
4112
+ );
4113
+ }
4114
+
4115
+ // ../content-protection/dist/index.js
4116
+ function createContentProtection(props = {}) {
4117
+ const {
4118
+ enabled = true,
4119
+ disableCopy = true,
4120
+ disableContextMenu = true,
4121
+ watermarkText
4122
+ } = props;
4123
+ const eventHandlers = {};
4124
+ if (enabled) {
4125
+ if (disableCopy) {
4126
+ const prevent = (e) => e.preventDefault();
4127
+ eventHandlers.onCopy = prevent;
4128
+ eventHandlers.onCut = prevent;
4129
+ eventHandlers.onSelectStart = prevent;
4130
+ }
4131
+ if (disableContextMenu) {
4132
+ eventHandlers.onContextMenu = (e) => e.preventDefault();
4133
+ }
4134
+ }
4135
+ const watermarkConfig = watermarkText ? { text: watermarkText, opacity: 0.08, angle: -45 } : null;
4136
+ const dataAttributes = {};
4137
+ if (enabled) {
4138
+ dataAttributes["data-protected"] = "true";
4139
+ }
4140
+ return {
4141
+ eventHandlers,
4142
+ watermarkConfig,
4143
+ dataAttributes
4144
+ };
4145
+ }
4146
+ var contentProtectionVariants = cva({
4147
+ base: "relative select-none"
4148
+ });
4149
+ var watermarkVariants = cva({
4150
+ base: "pointer-events-none absolute inset-0 z-50 overflow-hidden"
4151
+ });
4152
+ var ContentProtection = React11.forwardRef(
4153
+ ({
4154
+ enabled,
4155
+ disableCopy,
4156
+ disableContextMenu,
4157
+ watermarkText,
4158
+ className,
4159
+ children,
4160
+ ...props
4161
+ }, ref) => {
4162
+ const api = createContentProtection({
4163
+ enabled,
4164
+ disableCopy,
4165
+ disableContextMenu,
4166
+ watermarkText
4167
+ });
4168
+ const classes = cn(contentProtectionVariants(), className);
4169
+ return /* @__PURE__ */ jsxs(
4170
+ "div",
4171
+ {
4172
+ ref,
4173
+ className: classes,
4174
+ ...api.dataAttributes,
4175
+ onCopy: api.eventHandlers.onCopy,
4176
+ onCut: api.eventHandlers.onCut,
4177
+ onContextMenu: api.eventHandlers.onContextMenu,
4178
+ onSelect: api.eventHandlers.onSelectStart,
4179
+ ...props,
4180
+ children: [
4181
+ children,
4182
+ api.watermarkConfig && /* @__PURE__ */ jsx(
4183
+ "div",
4184
+ {
4185
+ className: watermarkVariants(),
4186
+ "aria-hidden": "true",
4187
+ style: {
4188
+ opacity: api.watermarkConfig.opacity,
4189
+ transform: `rotate(${api.watermarkConfig.angle}deg)`,
4190
+ backgroundImage: `repeating-linear-gradient(
4191
+ ${api.watermarkConfig.angle}deg,
4192
+ transparent,
4193
+ transparent 80px,
4194
+ currentColor 80px,
4195
+ currentColor 80px
4196
+ )`
4197
+ },
4198
+ children: /* @__PURE__ */ jsxs("svg", { width: "100%", height: "100%", children: [
4199
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
4200
+ "pattern",
4201
+ {
4202
+ id: "rfr-watermark",
4203
+ x: "0",
4204
+ y: "0",
4205
+ width: "200",
4206
+ height: "200",
4207
+ patternUnits: "userSpaceOnUse",
4208
+ patternTransform: `rotate(${api.watermarkConfig.angle})`,
4209
+ children: /* @__PURE__ */ jsx(
4210
+ "text",
4211
+ {
4212
+ x: "0",
4213
+ y: "100",
4214
+ fill: "currentColor",
4215
+ fontSize: "16",
4216
+ fontFamily: "sans-serif",
4217
+ children: api.watermarkConfig.text
4218
+ }
4219
+ )
4220
+ }
4221
+ ) }),
4222
+ /* @__PURE__ */ jsx("rect", { width: "100%", height: "100%", fill: "url(#rfr-watermark)" })
4223
+ ] })
4224
+ }
4225
+ )
4226
+ ]
4227
+ }
4228
+ );
4229
+ }
4230
+ );
4231
+ ContentProtection.displayName = "ContentProtection";
4232
+
4233
+ // ../cookie-consent/dist/index.js
4234
+ var DEFAULT_KEY = "rfr-cookie-consent";
4235
+ var DEFAULT_CATEGORIES = [
4236
+ { id: "necessary", label: "Strictly necessary", description: "Required for the site to function. Always on.", required: true },
4237
+ { id: "preferences", label: "Preferences", description: "Remembers your settings and choices." },
4238
+ { id: "analytics", label: "Analytics", description: "Helps us understand how the site is used." },
4239
+ { id: "marketing", label: "Marketing", description: "Used to personalize ads and measure campaigns." }
4240
+ ];
4241
+ function baseline(categories) {
4242
+ const p = {};
4243
+ for (const c of categories) p[c.id] = !!c.required;
4244
+ return p;
4245
+ }
4246
+ function createCookieConsent(config = {}) {
4247
+ const categories = config.categories ?? DEFAULT_CATEGORIES;
4248
+ const storage = config.storage;
4249
+ const key = config.storageKey ?? DEFAULT_KEY;
4250
+ const version = config.version;
4251
+ let preferences = baseline(categories);
4252
+ let consented = false;
4253
+ const raw = storage?.get(key) ?? null;
4254
+ if (raw) {
4255
+ try {
4256
+ const parsed = JSON.parse(raw);
4257
+ if ((parsed.version ?? void 0) === version) {
4258
+ preferences = { ...baseline(categories), ...parsed.preferences };
4259
+ for (const c of categories) if (c.required) preferences[c.id] = true;
4260
+ consented = true;
4261
+ }
4262
+ } catch {
4263
+ }
4264
+ }
4265
+ let open = !consented;
4266
+ const listeners = /* @__PURE__ */ new Set();
4267
+ let snapshot = build();
4268
+ function build() {
4269
+ return { consented, preferences: { ...preferences }, open, categories };
4270
+ }
4271
+ function emit() {
4272
+ snapshot = build();
4273
+ for (const l of listeners) l();
4274
+ }
4275
+ function persist() {
4276
+ storage?.set(key, JSON.stringify({ version, preferences }));
4277
+ config.onChange?.({ ...preferences });
4278
+ }
4279
+ function save(next) {
4280
+ const merged = { ...baseline(categories), ...next };
4281
+ for (const c of categories) if (c.required) merged[c.id] = true;
4282
+ preferences = merged;
4283
+ consented = true;
4284
+ open = false;
4285
+ persist();
4286
+ emit();
4287
+ }
4288
+ return {
4289
+ getState() {
4290
+ return snapshot;
4291
+ },
4292
+ subscribe(listener) {
4293
+ listeners.add(listener);
4294
+ return () => listeners.delete(listener);
4295
+ },
4296
+ acceptAll() {
4297
+ const all = {};
4298
+ for (const c of categories) all[c.id] = true;
4299
+ save(all);
4300
+ },
4301
+ rejectAll() {
4302
+ save(baseline(categories));
4303
+ },
4304
+ savePreferences(prefs) {
4305
+ save(prefs);
4306
+ },
4307
+ setPreference(id, value) {
4308
+ const cat = categories.find((c) => c.id === id);
4309
+ if (!cat || cat.required) return;
4310
+ preferences = { ...preferences, [id]: value };
4311
+ emit();
4312
+ },
4313
+ reset() {
4314
+ storage?.remove(key);
4315
+ preferences = baseline(categories);
4316
+ consented = false;
4317
+ open = true;
4318
+ emit();
4319
+ },
4320
+ openSettings() {
4321
+ if (open) return;
4322
+ open = true;
4323
+ emit();
4324
+ },
4325
+ close() {
4326
+ if (!open) return;
4327
+ open = false;
4328
+ emit();
4329
+ }
4330
+ };
4331
+ }
4332
+
4333
+ // ../react-cookie-consent/dist/index.js
4334
+ var localStorageAdapter = {
4335
+ get(k) {
4336
+ try {
4337
+ return typeof localStorage !== "undefined" ? localStorage.getItem(k) : null;
4338
+ } catch {
4339
+ return null;
4340
+ }
4341
+ },
4342
+ set(k, v) {
4343
+ try {
4344
+ localStorage?.setItem(k, v);
4345
+ } catch {
4346
+ }
4347
+ },
4348
+ remove(k) {
4349
+ try {
4350
+ localStorage?.removeItem(k);
4351
+ } catch {
4352
+ }
4353
+ }
4354
+ };
4355
+ function useCookieConsent(config) {
4356
+ const apiRef = React11.useRef(null);
4357
+ if (apiRef.current === null) {
4358
+ apiRef.current = createCookieConsent({ storage: localStorageAdapter, ...config });
4359
+ }
4360
+ const api = apiRef.current;
4361
+ const state = React11.useSyncExternalStore(api.subscribe, api.getState, api.getState);
4362
+ return {
4363
+ state,
4364
+ api,
4365
+ acceptAll: api.acceptAll,
4366
+ rejectAll: api.rejectAll,
4367
+ savePreferences: api.savePreferences,
4368
+ setPreference: api.setPreference,
4369
+ reset: api.reset,
4370
+ openSettings: api.openSettings,
4371
+ close: api.close
4372
+ };
4373
+ }
4374
+ var h3 = React11.createElement;
4375
+ var btnPrimary = "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:opacity-90";
4376
+ var btnGhost = "rounded-md border border-border px-3 py-1.5 text-sm hover:bg-accent";
4377
+ var btnLink = "text-sm text-muted-foreground underline hover:text-foreground";
4378
+ function CookieConsent({
4379
+ consent,
4380
+ position = "bottom",
4381
+ title = "We use cookies",
4382
+ description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which categories to allow.",
4383
+ policyUrl,
4384
+ policyLabel = "Cookie policy",
4385
+ className
4386
+ }) {
4387
+ const { state, acceptAll, rejectAll, savePreferences, setPreference } = consent;
4388
+ const [settings, setSettings] = React11.useState(false);
4389
+ if (!state.open) return null;
4390
+ const wrapper = cn(
4391
+ "fixed inset-x-0 z-50 p-4",
4392
+ position === "bottom" ? "bottom-0" : "top-0",
4393
+ className
4394
+ );
4395
+ const panel = "mx-auto max-w-3xl rounded-xl border border-border bg-background p-4 shadow-lg";
4396
+ const header = h3(
4397
+ "div",
4398
+ null,
4399
+ h3("h2", { className: "text-base font-semibold" }, title),
4400
+ h3("p", { className: "mt-1 text-sm text-muted-foreground" }, description),
4401
+ policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "mt-1 inline-block") }, policyLabel) : null
4402
+ );
4403
+ const promptActions = h3(
4404
+ "div",
4405
+ { className: "mt-3 flex flex-wrap items-center gap-2" },
4406
+ h3("button", { type: "button", className: btnPrimary, onClick: () => acceptAll() }, "Accept all"),
4407
+ h3("button", { type: "button", className: btnGhost, onClick: () => rejectAll() }, "Reject all"),
4408
+ h3("button", { type: "button", className: cn(btnGhost, "ml-auto"), onClick: () => setSettings(true) }, "Manage preferences")
4409
+ );
4410
+ const settingsView = h3(
4411
+ "div",
4412
+ { className: "mt-3 space-y-2" },
4413
+ ...state.categories.map(
4414
+ (cat) => h3(
4415
+ "label",
4416
+ {
4417
+ key: cat.id,
4418
+ className: "flex items-start justify-between gap-3 rounded-md border border-border p-3"
4419
+ },
4420
+ h3(
4421
+ "span",
4422
+ { className: "min-w-0" },
4423
+ h3("span", { className: "block text-sm font-medium" }, cat.label, cat.required ? " (required)" : ""),
4424
+ cat.description ? h3("span", { className: "block text-xs text-muted-foreground" }, cat.description) : null
4425
+ ),
4426
+ h3("input", {
4427
+ type: "checkbox",
4428
+ className: "mt-0.5 h-4 w-4 accent-[hsl(var(--primary))]",
4429
+ checked: !!state.preferences[cat.id],
4430
+ disabled: cat.required,
4431
+ "aria-label": cat.label,
4432
+ onChange: (e) => setPreference(cat.id, e.target.checked)
4433
+ })
4434
+ )
4435
+ ),
4436
+ h3(
4437
+ "div",
4438
+ { className: "flex flex-wrap items-center gap-2 pt-1" },
4439
+ h3("button", { type: "button", className: btnPrimary, onClick: () => savePreferences(state.preferences) }, "Save preferences"),
4440
+ h3("button", { type: "button", className: btnGhost, onClick: () => acceptAll() }, "Accept all"),
4441
+ h3("button", { type: "button", className: cn(btnLink, "ml-auto"), onClick: () => setSettings(false) }, "Back")
4442
+ )
4443
+ );
4444
+ return h3(
4445
+ "div",
4446
+ { className: wrapper, role: "dialog", "aria-label": "Cookie consent", "aria-modal": false },
4447
+ h3("div", { className: panel }, header, settings ? settingsView : promptActions)
4448
+ );
4449
+ }
4450
+
4451
+ // ../data-table/dist/index.js
4452
+ function createDataTable(props) {
4453
+ const {
4454
+ columns,
4455
+ data,
4456
+ sortBy: initialSortBy,
4457
+ sortDir: initialSortDir = "asc",
4458
+ onSort,
4459
+ filters: initialFilters
4460
+ } = props;
4461
+ let sortBy = initialSortBy ?? null;
4462
+ let sortDir = initialSortDir;
4463
+ let filters = { ...initialFilters ?? {} };
4464
+ function getFilteredData() {
4465
+ let result = [...data];
4466
+ for (const [columnId, filterValue] of Object.entries(filters)) {
4467
+ if (!filterValue) continue;
4468
+ const col = columns.find((c) => c.id === columnId);
4469
+ if (!col) continue;
4470
+ const lowerFilter = filterValue.toLowerCase();
4471
+ result = result.filter((row) => {
4472
+ const cellValue = col.accessor(row);
4473
+ return String(cellValue ?? "").toLowerCase().includes(lowerFilter);
4474
+ });
4475
+ }
4476
+ return result;
4477
+ }
4478
+ function getSortedData() {
4479
+ const filtered = getFilteredData();
4480
+ if (!sortBy) return filtered;
4481
+ const col = columns.find((c) => c.id === sortBy);
4482
+ if (!col) return filtered;
4483
+ return [...filtered].sort((a, b) => {
4484
+ const aVal = col.accessor(a);
4485
+ const bVal = col.accessor(b);
4486
+ const aStr = String(aVal ?? "");
4487
+ const bStr = String(bVal ?? "");
4488
+ const cmp = aStr.localeCompare(bStr, void 0, { numeric: true });
4489
+ return sortDir === "asc" ? cmp : -cmp;
4490
+ });
4491
+ }
4492
+ function sort(columnId) {
4493
+ const col = columns.find((c) => c.id === columnId);
4494
+ if (!col?.sortable) return;
4495
+ if (sortBy === columnId) {
4496
+ sortDir = sortDir === "asc" ? "desc" : "asc";
4497
+ } else {
4498
+ sortBy = columnId;
4499
+ sortDir = "asc";
4500
+ }
4501
+ onSort?.(sortBy, sortDir);
4502
+ }
4503
+ function setFilter(columnId, value) {
4504
+ filters = { ...filters, [columnId]: value };
4505
+ }
4506
+ function getHeaderProps(col) {
4507
+ const props2 = {
4508
+ role: "columnheader"
4509
+ };
4510
+ if (col.sortable) {
4511
+ if (sortBy === col.id) {
4512
+ props2["aria-sort"] = sortDir === "asc" ? "ascending" : "descending";
4513
+ } else {
4514
+ props2["aria-sort"] = "none";
4515
+ }
4516
+ }
4517
+ return props2;
4518
+ }
4519
+ function getCellProps(col, _row) {
4520
+ return {
4521
+ role: "cell",
4522
+ "data-column": col.id
4523
+ };
4524
+ }
4525
+ function getRowProps(_row, index) {
4526
+ return {
4527
+ role: "row",
4528
+ "data-row-index": index
4529
+ };
4530
+ }
4531
+ return {
4532
+ get state() {
4533
+ return {
4534
+ get sortedData() {
4535
+ return getSortedData();
4536
+ },
4537
+ get sortBy() {
4538
+ return sortBy;
4539
+ },
4540
+ get sortDir() {
4541
+ return sortDir;
4542
+ },
4543
+ get filters() {
4544
+ return { ...filters };
4545
+ }
4546
+ };
4547
+ },
4548
+ sort,
4549
+ setFilter,
4550
+ getHeaderProps,
4551
+ getCellProps,
4552
+ getRowProps
4553
+ };
4554
+ }
4555
+ var tableVariants = cva({
4556
+ base: "w-full caption-bottom text-sm border-collapse",
4557
+ variants: {
4558
+ size: {
4559
+ sm: "text-xs",
4560
+ md: "text-sm",
4561
+ lg: "text-base"
4562
+ }
4563
+ },
4564
+ defaultVariants: {
2915
4565
  size: "md"
2916
4566
  }
2917
4567
  });
@@ -4322,460 +5972,251 @@ Input.displayName = "Input";
4322
5972
  function createInputGroup(props = {}) {
4323
5973
  const { orientation = "horizontal" } = props;
4324
5974
  const id = props.id ?? generateId("rfr-input-group");
4325
- const ariaProps = {
4326
- role: "group"
4327
- };
4328
- if (props["aria-label"]) {
4329
- ariaProps["aria-label"] = props["aria-label"];
4330
- }
4331
- if (props["aria-labelledby"]) {
4332
- ariaProps["aria-labelledby"] = props["aria-labelledby"];
4333
- }
4334
- const dataAttributes = {
4335
- "data-orientation": orientation,
4336
- id
4337
- };
4338
- return {
4339
- ariaProps,
4340
- dataAttributes
4341
- };
4342
- }
4343
- var inputGroupTokens = {
4344
- name: "input-group",
4345
- tokens: {
4346
- bg: { variable: "--rfr-input-group-bg", fallback: "hsl(var(--background))" },
4347
- border: { variable: "--rfr-input-group-border", fallback: "hsl(var(--border))" },
4348
- radius: { variable: "--rfr-input-group-radius", fallback: "var(--radius)" }
4349
- }
4350
- };
4351
- var inputGroupVariants = cva({
4352
- base: "flex items-stretch [&>*:not(:first-child):not(:last-child)]:rounded-none",
4353
- variants: {
4354
- orientation: {
4355
- horizontal: "flex-row [&>*:first-child]:rounded-r-none [&>*:last-child]:rounded-l-none",
4356
- vertical: "flex-col [&>*:first-child]:rounded-b-none [&>*:last-child]:rounded-t-none"
4357
- }
4358
- },
4359
- defaultVariants: {
4360
- orientation: "horizontal"
4361
- }
4362
- });
4363
- var inputGroupAddonVariants = cva({
4364
- base: "flex items-center justify-center border bg-muted px-3 text-sm text-muted-foreground",
4365
- variants: {
4366
- orientation: {
4367
- horizontal: "border-y border-x first:border-r-0 last:border-l-0",
4368
- vertical: "border-x border-y first:border-b-0 last:border-t-0"
4369
- }
4370
- },
4371
- defaultVariants: {
4372
- orientation: "horizontal"
4373
- }
4374
- });
4375
- var inputGroupButtonVariants = cva({
4376
- base: "relative inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
4377
- variants: {
4378
- orientation: {
4379
- horizontal: "",
4380
- vertical: ""
4381
- }
4382
- },
4383
- defaultVariants: {
4384
- orientation: "horizontal"
4385
- }
4386
- });
4387
- var InputGroup = React11.forwardRef(
4388
- ({ orientation = "horizontal", className, children, ...props }, ref) => {
4389
- const api = createInputGroup({
4390
- orientation,
4391
- id: props.id,
4392
- "aria-label": props["aria-label"],
4393
- "aria-labelledby": props["aria-labelledby"]
4394
- });
4395
- return /* @__PURE__ */ jsx(
4396
- "div",
4397
- {
4398
- ref,
4399
- className: cn(inputGroupVariants({ orientation }), className),
4400
- ...api.ariaProps,
4401
- ...api.dataAttributes,
4402
- ...props,
4403
- children
4404
- }
4405
- );
4406
- }
4407
- );
4408
- InputGroup.displayName = "InputGroup";
4409
- var InputGroupAddon = React11.forwardRef(
4410
- ({ orientation = "horizontal", className, children, ...props }, ref) => {
4411
- return /* @__PURE__ */ jsx(
4412
- "div",
4413
- {
4414
- ref,
4415
- className: cn(inputGroupAddonVariants({ orientation }), className),
4416
- ...props,
4417
- children
4418
- }
4419
- );
4420
- }
4421
- );
4422
- InputGroupAddon.displayName = "InputGroupAddon";
4423
- var InputGroupText = React11.forwardRef(
4424
- ({ className, children, ...props }, ref) => {
4425
- return /* @__PURE__ */ jsx(
4426
- "span",
4427
- {
4428
- ref,
4429
- className: cn("flex items-center px-3 text-sm text-muted-foreground", className),
4430
- ...props,
4431
- children
4432
- }
4433
- );
4434
- }
4435
- );
4436
- InputGroupText.displayName = "InputGroupText";
4437
- var InputGroupButton = React11.forwardRef(
4438
- ({ orientation = "horizontal", className, children, ...props }, ref) => {
4439
- return /* @__PURE__ */ jsx(
4440
- "button",
4441
- {
4442
- ref,
4443
- type: props.type ?? "button",
4444
- className: cn(inputGroupButtonVariants({ orientation }), className),
4445
- ...props,
4446
- children
4447
- }
4448
- );
4449
- }
4450
- );
4451
- InputGroupButton.displayName = "InputGroupButton";
4452
-
4453
- // ../install-prompt/dist/index.js
4454
- function createInstallPrompt(props = {}, storage) {
4455
- const { storageKey = "rfr-install-dismissed" } = props;
4456
- const dismissed = storage?.get(storageKey) === "true";
4457
- const state = {
4458
- canShow: false,
4459
- isDismissed: dismissed
4460
- };
4461
- return {
4462
- state,
4463
- show() {
4464
- if (!state.isDismissed) {
4465
- state.canShow = true;
4466
- }
4467
- },
4468
- dismiss() {
4469
- state.canShow = false;
4470
- state.isDismissed = true;
4471
- storage?.set(storageKey, "true");
4472
- },
4473
- install(promptEvent) {
4474
- promptEvent?.prompt();
4475
- state.canShow = false;
4476
- },
4477
- ariaProps: {
4478
- role: "banner",
4479
- "aria-label": "Install application"
4480
- }
4481
- };
4482
- }
4483
- var installPromptVariants = cva({
4484
- base: "fixed bottom-0 left-0 right-0 z-50 flex items-center justify-between gap-4 border-t bg-background px-4 py-3 shadow-lg"
4485
- });
4486
- function createLocalStorage() {
4487
- return {
4488
- get(key) {
4489
- try {
4490
- return localStorage.getItem(key);
4491
- } catch {
4492
- return null;
4493
- }
4494
- },
4495
- set(key, value) {
4496
- try {
4497
- localStorage.setItem(key, value);
4498
- } catch {
4499
- }
4500
- }
4501
- };
4502
- }
4503
- var InstallPrompt = React11.forwardRef(
4504
- ({
4505
- delay = 3e3,
4506
- storageKey,
4507
- installLabel = "Install",
4508
- dismissLabel = "Dismiss",
4509
- message = "Install this app for a better experience",
4510
- className,
4511
- ...props
4512
- }, ref) => {
4513
- const storageRef = React11.useRef(void 0);
4514
- if (typeof window !== "undefined" && !storageRef.current) {
4515
- storageRef.current = createLocalStorage();
4516
- }
4517
- const api = createInstallPrompt({ storageKey }, storageRef.current);
4518
- const [visible, setVisible] = React11.useState(false);
4519
- const promptEventRef = React11.useRef(null);
4520
- React11.useEffect(() => {
4521
- if (api.state.isDismissed) return;
4522
- const handleBeforeInstall = (e) => {
4523
- e.preventDefault();
4524
- promptEventRef.current = e;
4525
- };
4526
- window.addEventListener("beforeinstallprompt", handleBeforeInstall);
4527
- const timer = setTimeout(() => {
4528
- if (promptEventRef.current && !api.state.isDismissed) {
4529
- api.show();
4530
- setVisible(true);
4531
- }
4532
- }, delay);
4533
- return () => {
4534
- window.removeEventListener("beforeinstallprompt", handleBeforeInstall);
4535
- clearTimeout(timer);
4536
- };
4537
- }, [delay, api]);
4538
- if (!visible) return null;
4539
- const classes = cn(installPromptVariants(), className);
4540
- return /* @__PURE__ */ jsxs("div", { ref, className: classes, ...api.ariaProps, ...props, children: [
4541
- /* @__PURE__ */ jsx("span", { children: message }),
4542
- /* @__PURE__ */ jsxs("div", { children: [
4543
- /* @__PURE__ */ jsx(
4544
- "button",
4545
- {
4546
- type: "button",
4547
- onClick: () => {
4548
- api.install(promptEventRef.current ?? void 0);
4549
- setVisible(false);
4550
- },
4551
- children: installLabel
4552
- }
4553
- ),
4554
- /* @__PURE__ */ jsx(
4555
- "button",
4556
- {
4557
- type: "button",
4558
- onClick: () => {
4559
- api.dismiss();
4560
- setVisible(false);
4561
- },
4562
- children: dismissLabel
4563
- }
4564
- )
4565
- ] })
4566
- ] });
5975
+ const ariaProps = {
5976
+ role: "group"
5977
+ };
5978
+ if (props["aria-label"]) {
5979
+ ariaProps["aria-label"] = props["aria-label"];
4567
5980
  }
4568
- );
4569
- InstallPrompt.displayName = "InstallPrompt";
4570
-
4571
- // ../markdown-renderer/dist/index.js
4572
- function escapeHtml(text) {
4573
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
4574
- }
4575
- function parseInline(text, linkResolver) {
4576
- let result = escapeHtml(text);
4577
- result = result.replace(/`([^`]+)`/g, "<code>$1</code>");
4578
- result = result.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
4579
- result = result.replace(/__([^_]+)__/g, "<strong>$1</strong>");
4580
- result = result.replace(/\*([^*]+)\*/g, "<em>$1</em>");
4581
- result = result.replace(/_([^_]+)_/g, "<em>$1</em>");
4582
- result = result.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_match, text2, url) => {
4583
- if (/^\s*javascript\s*:/i.test(url)) {
4584
- return text2;
4585
- }
4586
- const resolvedUrl = linkResolver ? linkResolver(url) : url;
4587
- return `<a href="${resolvedUrl}">${text2}</a>`;
4588
- });
4589
- return result;
5981
+ if (props["aria-labelledby"]) {
5982
+ ariaProps["aria-labelledby"] = props["aria-labelledby"];
5983
+ }
5984
+ const dataAttributes = {
5985
+ "data-orientation": orientation,
5986
+ id
5987
+ };
5988
+ return {
5989
+ ariaProps,
5990
+ dataAttributes
5991
+ };
4590
5992
  }
4591
- function parseMarkdown(content, linkResolver) {
4592
- const lines = content.split("\n");
4593
- const outputLines = [];
4594
- let inCodeBlock = false;
4595
- let codeBlockContent = [];
4596
- let codeBlockLang = "";
4597
- let inList = null;
4598
- let inBlockquote = false;
4599
- function closeList() {
4600
- if (inList) {
4601
- outputLines.push(inList === "ul" ? "</ul>" : "</ol>");
4602
- inList = null;
4603
- }
5993
+ var inputGroupTokens = {
5994
+ name: "input-group",
5995
+ tokens: {
5996
+ bg: { variable: "--rfr-input-group-bg", fallback: "hsl(var(--background))" },
5997
+ border: { variable: "--rfr-input-group-border", fallback: "hsl(var(--border))" },
5998
+ radius: { variable: "--rfr-input-group-radius", fallback: "var(--radius)" }
4604
5999
  }
4605
- function closeBlockquote() {
4606
- if (inBlockquote) {
4607
- outputLines.push("</blockquote>");
4608
- inBlockquote = false;
6000
+ };
6001
+ var inputGroupVariants = cva({
6002
+ base: "flex items-stretch [&>*:not(:first-child):not(:last-child)]:rounded-none",
6003
+ variants: {
6004
+ orientation: {
6005
+ horizontal: "flex-row [&>*:first-child]:rounded-r-none [&>*:last-child]:rounded-l-none",
6006
+ vertical: "flex-col [&>*:first-child]:rounded-b-none [&>*:last-child]:rounded-t-none"
4609
6007
  }
6008
+ },
6009
+ defaultVariants: {
6010
+ orientation: "horizontal"
4610
6011
  }
4611
- for (let i = 0; i < lines.length; i++) {
4612
- const line = lines[i];
4613
- if (line.trimStart().startsWith("```")) {
4614
- if (inCodeBlock) {
4615
- outputLines.push(`<pre><code${codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : ""}>${escapeHtml(codeBlockContent.join("\n"))}</code></pre>`);
4616
- codeBlockContent = [];
4617
- codeBlockLang = "";
4618
- inCodeBlock = false;
4619
- } else {
4620
- closeList();
4621
- closeBlockquote();
4622
- inCodeBlock = true;
4623
- codeBlockLang = line.trimStart().slice(3).trim();
4624
- }
4625
- continue;
4626
- }
4627
- if (inCodeBlock) {
4628
- codeBlockContent.push(line);
4629
- continue;
4630
- }
4631
- if (/^(\s*[-*_]\s*){3,}$/.test(line)) {
4632
- closeList();
4633
- closeBlockquote();
4634
- outputLines.push("<hr />");
4635
- continue;
4636
- }
4637
- const headingMatch = line.match(/^(#{1,6})\s+(.+)$/);
4638
- if (headingMatch) {
4639
- closeList();
4640
- closeBlockquote();
4641
- const level = headingMatch[1].length;
4642
- const text = parseInline(headingMatch[2], linkResolver);
4643
- outputLines.push(`<h${level}>${text}</h${level}>`);
4644
- continue;
6012
+ });
6013
+ var inputGroupAddonVariants = cva({
6014
+ base: "flex items-center justify-center border bg-muted px-3 text-sm text-muted-foreground",
6015
+ variants: {
6016
+ orientation: {
6017
+ horizontal: "border-y border-x first:border-r-0 last:border-l-0",
6018
+ vertical: "border-x border-y first:border-b-0 last:border-t-0"
4645
6019
  }
4646
- const blockquoteMatch = line.match(/^>\s?(.*)$/);
4647
- if (blockquoteMatch) {
4648
- closeList();
4649
- if (!inBlockquote) {
4650
- inBlockquote = true;
4651
- outputLines.push("<blockquote>");
4652
- }
4653
- const text = blockquoteMatch[1].trim();
4654
- if (text) {
4655
- outputLines.push(`<p>${parseInline(text, linkResolver)}</p>`);
4656
- }
4657
- continue;
4658
- } else if (inBlockquote) {
4659
- closeBlockquote();
6020
+ },
6021
+ defaultVariants: {
6022
+ orientation: "horizontal"
6023
+ }
6024
+ });
6025
+ var inputGroupButtonVariants = cva({
6026
+ base: "relative inline-flex items-center justify-center whitespace-nowrap text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
6027
+ variants: {
6028
+ orientation: {
6029
+ horizontal: "",
6030
+ vertical: ""
4660
6031
  }
4661
- const ulMatch = line.match(/^[\s]*[-*+]\s+(.+)$/);
4662
- if (ulMatch) {
4663
- closeBlockquote();
4664
- if (inList !== "ul") {
4665
- closeList();
4666
- inList = "ul";
4667
- outputLines.push("<ul>");
6032
+ },
6033
+ defaultVariants: {
6034
+ orientation: "horizontal"
6035
+ }
6036
+ });
6037
+ var InputGroup = React11.forwardRef(
6038
+ ({ orientation = "horizontal", className, children, ...props }, ref) => {
6039
+ const api = createInputGroup({
6040
+ orientation,
6041
+ id: props.id,
6042
+ "aria-label": props["aria-label"],
6043
+ "aria-labelledby": props["aria-labelledby"]
6044
+ });
6045
+ return /* @__PURE__ */ jsx(
6046
+ "div",
6047
+ {
6048
+ ref,
6049
+ className: cn(inputGroupVariants({ orientation }), className),
6050
+ ...api.ariaProps,
6051
+ ...api.dataAttributes,
6052
+ ...props,
6053
+ children
4668
6054
  }
4669
- outputLines.push(`<li>${parseInline(ulMatch[1], linkResolver)}</li>`);
4670
- continue;
4671
- }
4672
- const olMatch = line.match(/^[\s]*\d+\.\s+(.+)$/);
4673
- if (olMatch) {
4674
- closeBlockquote();
4675
- if (inList !== "ol") {
4676
- closeList();
4677
- inList = "ol";
4678
- outputLines.push("<ol>");
6055
+ );
6056
+ }
6057
+ );
6058
+ InputGroup.displayName = "InputGroup";
6059
+ var InputGroupAddon = React11.forwardRef(
6060
+ ({ orientation = "horizontal", className, children, ...props }, ref) => {
6061
+ return /* @__PURE__ */ jsx(
6062
+ "div",
6063
+ {
6064
+ ref,
6065
+ className: cn(inputGroupAddonVariants({ orientation }), className),
6066
+ ...props,
6067
+ children
4679
6068
  }
4680
- outputLines.push(`<li>${parseInline(olMatch[1], linkResolver)}</li>`);
4681
- continue;
4682
- }
4683
- if (inList) {
4684
- closeList();
4685
- }
4686
- if (line.trim() === "") {
4687
- continue;
4688
- }
4689
- outputLines.push(`<p>${parseInline(line, linkResolver)}</p>`);
6069
+ );
4690
6070
  }
4691
- if (inCodeBlock) {
4692
- outputLines.push(`<pre><code${codeBlockLang ? ` class="language-${escapeHtml(codeBlockLang)}"` : ""}>${escapeHtml(codeBlockContent.join("\n"))}</code></pre>`);
6071
+ );
6072
+ InputGroupAddon.displayName = "InputGroupAddon";
6073
+ var InputGroupText = React11.forwardRef(
6074
+ ({ className, children, ...props }, ref) => {
6075
+ return /* @__PURE__ */ jsx(
6076
+ "span",
6077
+ {
6078
+ ref,
6079
+ className: cn("flex items-center px-3 text-sm text-muted-foreground", className),
6080
+ ...props,
6081
+ children
6082
+ }
6083
+ );
4693
6084
  }
4694
- closeList();
4695
- closeBlockquote();
4696
- return outputLines.join("\n");
4697
- }
4698
- function extractComponents(content, components) {
4699
- if (!components) return [];
4700
- const extracted = [];
4701
- for (const [name, def] of Object.entries(components)) {
4702
- const matches = Array.from(content.matchAll(def.pattern));
4703
- matches.forEach(() => {
4704
- extracted.push({
4705
- name,
4706
- props: { ...def.props }
4707
- });
4708
- });
6085
+ );
6086
+ InputGroupText.displayName = "InputGroupText";
6087
+ var InputGroupButton = React11.forwardRef(
6088
+ ({ orientation = "horizontal", className, children, ...props }, ref) => {
6089
+ return /* @__PURE__ */ jsx(
6090
+ "button",
6091
+ {
6092
+ ref,
6093
+ type: props.type ?? "button",
6094
+ className: cn(inputGroupButtonVariants({ orientation }), className),
6095
+ ...props,
6096
+ children
6097
+ }
6098
+ );
4709
6099
  }
4710
- return extracted;
4711
- }
4712
- function createMarkdownRenderer(props) {
4713
- const { content, components, linkResolver } = props;
4714
- const html = parseMarkdown(content, linkResolver);
4715
- const extractedComponents = extractComponents(content, components);
4716
- const ariaProps = {
4717
- role: "document",
4718
- "aria-label": "Rendered markdown content"
6100
+ );
6101
+ InputGroupButton.displayName = "InputGroupButton";
6102
+
6103
+ // ../install-prompt/dist/index.js
6104
+ function createInstallPrompt(props = {}, storage) {
6105
+ const { storageKey = "rfr-install-dismissed" } = props;
6106
+ const dismissed = storage?.get(storageKey) === "true";
6107
+ const state = {
6108
+ canShow: false,
6109
+ isDismissed: dismissed
4719
6110
  };
4720
6111
  return {
4721
- html,
4722
- components: extractedComponents,
4723
- ariaProps
6112
+ state,
6113
+ show() {
6114
+ if (!state.isDismissed) {
6115
+ state.canShow = true;
6116
+ }
6117
+ },
6118
+ dismiss() {
6119
+ state.canShow = false;
6120
+ state.isDismissed = true;
6121
+ storage?.set(storageKey, "true");
6122
+ },
6123
+ install(promptEvent) {
6124
+ promptEvent?.prompt();
6125
+ state.canShow = false;
6126
+ },
6127
+ ariaProps: {
6128
+ role: "banner",
6129
+ "aria-label": "Install application"
6130
+ }
4724
6131
  };
4725
6132
  }
4726
- var markdownRendererTokens = {
4727
- name: "markdown-renderer",
4728
- tokens: {
4729
- bg: { variable: "--rfr-markdown-bg", fallback: "hsl(var(--background))" },
4730
- fg: { variable: "--rfr-markdown-fg", fallback: "hsl(var(--foreground))" },
4731
- codeBg: { variable: "--rfr-markdown-code-bg", fallback: "hsl(var(--muted))" },
4732
- linkColor: { variable: "--rfr-markdown-link", fallback: "hsl(var(--primary))" },
4733
- borderColor: { variable: "--rfr-markdown-border", fallback: "hsl(var(--border))" }
4734
- }
4735
- };
4736
- var proseVariants = cva({
4737
- base: "prose max-w-none text-foreground leading-relaxed",
4738
- variants: {
4739
- size: {
4740
- sm: "prose-sm text-sm",
4741
- default: "prose-base text-base",
4742
- lg: "prose-lg text-lg"
6133
+ var installPromptVariants = cva({
6134
+ base: "fixed bottom-0 left-0 right-0 z-50 flex items-center justify-between gap-4 border-t bg-background px-4 py-3 shadow-lg"
6135
+ });
6136
+ function createLocalStorage() {
6137
+ return {
6138
+ get(key) {
6139
+ try {
6140
+ return localStorage.getItem(key);
6141
+ } catch {
6142
+ return null;
6143
+ }
4743
6144
  },
4744
- theme: {
4745
- light: "bg-white text-gray-900",
4746
- dark: "bg-gray-900 text-gray-100"
6145
+ set(key, value) {
6146
+ try {
6147
+ localStorage.setItem(key, value);
6148
+ } catch {
6149
+ }
4747
6150
  }
4748
- },
4749
- defaultVariants: {
4750
- size: "default"
4751
- }
4752
- });
4753
- function sanitizeHtml(html) {
4754
- let sanitized = html;
4755
- sanitized = sanitized.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, "");
4756
- sanitized = sanitized.replace(/<\/?script[^>]*>/gi, "");
4757
- sanitized = sanitized.replace(/\s+on\w+\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/gi, "");
4758
- sanitized = sanitized.replace(/(href|src)\s*=\s*["']?\s*javascript\s*:[^"'>]*/gi, '$1=""');
4759
- return sanitized;
6151
+ };
4760
6152
  }
4761
- var MarkdownRenderer = React11.forwardRef(
4762
- ({ content, components, linkResolver, className, size }, ref) => {
4763
- const coreProps = { content, components, linkResolver };
4764
- const api = createMarkdownRenderer(coreProps);
4765
- const classes = cn(proseVariants({ size }), className);
4766
- const sanitizedHtml = sanitizeHtml(api.html);
4767
- return /* @__PURE__ */ jsx(
4768
- "div",
4769
- {
4770
- ref,
4771
- className: classes,
4772
- ...api.ariaProps,
4773
- dangerouslySetInnerHTML: { __html: sanitizedHtml }
4774
- }
4775
- );
6153
+ var InstallPrompt = React11.forwardRef(
6154
+ ({
6155
+ delay = 3e3,
6156
+ storageKey,
6157
+ installLabel = "Install",
6158
+ dismissLabel = "Dismiss",
6159
+ message = "Install this app for a better experience",
6160
+ className,
6161
+ ...props
6162
+ }, ref) => {
6163
+ const storageRef = React11.useRef(void 0);
6164
+ if (typeof window !== "undefined" && !storageRef.current) {
6165
+ storageRef.current = createLocalStorage();
6166
+ }
6167
+ const api = createInstallPrompt({ storageKey }, storageRef.current);
6168
+ const [visible, setVisible] = React11.useState(false);
6169
+ const promptEventRef = React11.useRef(null);
6170
+ React11.useEffect(() => {
6171
+ if (api.state.isDismissed) return;
6172
+ const handleBeforeInstall = (e) => {
6173
+ e.preventDefault();
6174
+ promptEventRef.current = e;
6175
+ };
6176
+ window.addEventListener("beforeinstallprompt", handleBeforeInstall);
6177
+ const timer = setTimeout(() => {
6178
+ if (promptEventRef.current && !api.state.isDismissed) {
6179
+ api.show();
6180
+ setVisible(true);
6181
+ }
6182
+ }, delay);
6183
+ return () => {
6184
+ window.removeEventListener("beforeinstallprompt", handleBeforeInstall);
6185
+ clearTimeout(timer);
6186
+ };
6187
+ }, [delay, api]);
6188
+ if (!visible) return null;
6189
+ const classes = cn(installPromptVariants(), className);
6190
+ return /* @__PURE__ */ jsxs("div", { ref, className: classes, ...api.ariaProps, ...props, children: [
6191
+ /* @__PURE__ */ jsx("span", { children: message }),
6192
+ /* @__PURE__ */ jsxs("div", { children: [
6193
+ /* @__PURE__ */ jsx(
6194
+ "button",
6195
+ {
6196
+ type: "button",
6197
+ onClick: () => {
6198
+ api.install(promptEventRef.current ?? void 0);
6199
+ setVisible(false);
6200
+ },
6201
+ children: installLabel
6202
+ }
6203
+ ),
6204
+ /* @__PURE__ */ jsx(
6205
+ "button",
6206
+ {
6207
+ type: "button",
6208
+ onClick: () => {
6209
+ api.dismiss();
6210
+ setVisible(false);
6211
+ },
6212
+ children: dismissLabel
6213
+ }
6214
+ )
6215
+ ] })
6216
+ ] });
4776
6217
  }
4777
6218
  );
4778
- MarkdownRenderer.displayName = "MarkdownRenderer";
6219
+ InstallPrompt.displayName = "InstallPrompt";
4779
6220
 
4780
6221
  // ../mobile-nav/dist/index.js
4781
6222
  function createMobileNav(props = {}) {
@@ -9593,7 +11034,7 @@ var AvatarContext = React11.createContext({
9593
11034
  setImageError: () => {
9594
11035
  }
9595
11036
  });
9596
- var Avatar = React11.forwardRef(
11037
+ var Avatar2 = React11.forwardRef(
9597
11038
  ({ size = "md", className, children, ...props }, ref) => {
9598
11039
  const [imageLoaded, setImageLoaded] = React11.useState(false);
9599
11040
  const [imageError, setImageError] = React11.useState(false);
@@ -9611,7 +11052,7 @@ var Avatar = React11.forwardRef(
9611
11052
  ) });
9612
11053
  }
9613
11054
  );
9614
- Avatar.displayName = "Avatar";
11055
+ Avatar2.displayName = "Avatar";
9615
11056
  var AvatarImage = React11.forwardRef(
9616
11057
  ({ className, src, alt = "", onLoad, onError, ...props }, ref) => {
9617
11058
  const { setImageLoaded, setImageError } = React11.useContext(AvatarContext);
@@ -10204,10 +11645,10 @@ function createDatePicker(props = {}) {
10204
11645
  closePicker();
10205
11646
  }
10206
11647
  }
10207
- function setHours(h) {
10208
- if (h < 0 || h > 23) return;
11648
+ function setHours(h4) {
11649
+ if (h4 < 0 || h4 > 23) return;
10209
11650
  const newDate = value ? new Date(value) : /* @__PURE__ */ new Date();
10210
- newDate.setHours(h);
11651
+ newDate.setHours(h4);
10211
11652
  onChange?.(newDate);
10212
11653
  }
10213
11654
  function setMinutes(m) {
@@ -10399,10 +11840,10 @@ function DatePicker({
10399
11840
  }
10400
11841
  };
10401
11842
  const handleHoursChange = (e) => {
10402
- const h = parseInt(e.target.value, 10);
10403
- if (isNaN(h) || h < 0 || h > 23) return;
11843
+ const h4 = parseInt(e.target.value, 10);
11844
+ if (isNaN(h4) || h4 < 0 || h4 > 23) return;
10404
11845
  const newDate = value ? new Date(value) : /* @__PURE__ */ new Date();
10405
- newDate.setHours(h);
11846
+ newDate.setHours(h4);
10406
11847
  onChange?.(newDate);
10407
11848
  };
10408
11849
  const handleMinutesChange = (e) => {
@@ -12576,102 +14017,6 @@ var Switch = React11.forwardRef(
12576
14017
  }
12577
14018
  );
12578
14019
  Switch.displayName = "Switch";
12579
-
12580
- // ../thread-view/dist/index.js
12581
- function formatTimestamp(date) {
12582
- const hours = date.getHours();
12583
- const minutes = date.getMinutes();
12584
- const ampm = hours >= 12 ? "PM" : "AM";
12585
- const displayHours = hours % 12 || 12;
12586
- const displayMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`;
12587
- return `${displayHours}:${displayMinutes} ${ampm}`;
12588
- }
12589
- function formatRelativeTime(date, now) {
12590
- const reference = now ?? /* @__PURE__ */ new Date();
12591
- const diffMs = reference.getTime() - date.getTime();
12592
- const diffSeconds = Math.floor(diffMs / 1e3);
12593
- const diffMinutes = Math.floor(diffSeconds / 60);
12594
- const diffHours = Math.floor(diffMinutes / 60);
12595
- const diffDays = Math.floor(diffHours / 24);
12596
- if (diffSeconds < 60) return "just now";
12597
- if (diffMinutes < 60) return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`;
12598
- if (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
12599
- if (diffDays < 7) return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
12600
- return date.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
12601
- }
12602
- function createThreadView(props) {
12603
- const { messages, onReply, onReact, currentUserId } = props;
12604
- let replyingTo = null;
12605
- const threadId = generateId("rfr-thread");
12606
- const labelId = generateId("rfr-thread-label");
12607
- function startReply(messageId) {
12608
- replyingTo = messageId;
12609
- }
12610
- function cancelReply() {
12611
- replyingTo = null;
12612
- }
12613
- function reply(messageId, content) {
12614
- onReply?.(messageId, content);
12615
- replyingTo = null;
12616
- }
12617
- function react(messageId, emoji) {
12618
- onReact?.(messageId, emoji);
12619
- }
12620
- const ariaProps = {
12621
- role: "log",
12622
- "aria-label": "Message thread",
12623
- "aria-live": "polite",
12624
- id: threadId
12625
- };
12626
- function getMessageAriaProps(message) {
12627
- const isOwn = currentUserId && message.author.id === currentUserId;
12628
- return {
12629
- role: "article",
12630
- "aria-label": `Message from ${message.author.name}${isOwn ? " (you)" : ""} at ${formatTimestamp(message.timestamp)}`
12631
- };
12632
- }
12633
- function getReplyButtonAriaProps(_messageId) {
12634
- return {
12635
- role: "button",
12636
- "aria-label": `Reply to message`
12637
- };
12638
- }
12639
- return {
12640
- state: {
12641
- messages,
12642
- get replyingTo() {
12643
- return replyingTo;
12644
- }
12645
- },
12646
- startReply,
12647
- cancelReply,
12648
- reply,
12649
- react,
12650
- formatTimestamp,
12651
- formatRelativeTime,
12652
- ariaProps,
12653
- getMessageAriaProps,
12654
- getReplyButtonAriaProps,
12655
- ids: {
12656
- thread: threadId,
12657
- label: labelId
12658
- }
12659
- };
12660
- }
12661
- var threadContainerStyles = "flex flex-col gap-1";
12662
- var threadMessageStyles = "flex gap-3 px-4 py-2 hover:bg-accent/50 rounded-md transition-colors group";
12663
- var threadAvatarStyles = "h-9 w-9 rounded-full bg-muted flex items-center justify-center text-sm font-medium overflow-hidden flex-shrink-0";
12664
- var threadContentStyles = "flex-1 min-w-0";
12665
- var threadAuthorStyles = "font-semibold text-sm";
12666
- var threadTimestampStyles = "text-xs text-muted-foreground ml-2";
12667
- var threadBodyStyles = "text-sm mt-0.5 whitespace-pre-wrap break-words";
12668
- var threadReactionsStyles = "flex flex-wrap gap-1 mt-1";
12669
- var threadReplyIndicatorStyles = "flex items-center gap-1 mt-1 text-xs text-primary cursor-pointer hover:underline";
12670
- var threadActionsStyles = "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity";
12671
- var threadAttachmentStyles = "flex items-center gap-2 mt-1 p-2 rounded border text-xs bg-muted/50";
12672
- var threadEditedStyles = "text-xs text-muted-foreground ml-1";
12673
-
12674
- // ../react-thread-view/dist/index.js
12675
14020
  function MessageComponent({
12676
14021
  message,
12677
14022
  api
@@ -12783,11 +14128,11 @@ ThreadView.displayName = "ThreadView";
12783
14128
  // ../table-of-contents/dist/index.js
12784
14129
  function parseHeadings(container, selectors = "h2, h3, h4") {
12785
14130
  const headings = Array.from(container.querySelectorAll(selectors));
12786
- return headings.map((h) => ({
12787
- id: h.id || h.textContent?.toLowerCase().replace(/\s+/g, "-") || "",
12788
- text: h.textContent || "",
12789
- level: parseInt(h.tagName.charAt(1), 10)
12790
- })).filter((h) => h.id !== "");
14131
+ return headings.map((h4) => ({
14132
+ id: h4.id || h4.textContent?.toLowerCase().replace(/\s+/g, "-") || "",
14133
+ text: h4.textContent || "",
14134
+ level: parseInt(h4.tagName.charAt(1), 10)
14135
+ })).filter((h4) => h4.id !== "");
12791
14136
  }
12792
14137
  function observeHeadings(headingIds, callback, options) {
12793
14138
  const observer = new IntersectionObserver((entries) => {
@@ -12814,7 +14159,7 @@ var TableOfContents = React11.forwardRef(
12814
14159
  const parsedHeadings = parseHeadings(container, selectors);
12815
14160
  setHeadings(parsedHeadings);
12816
14161
  if (parsedHeadings.length === 0) return;
12817
- const disconnect = observeHeadings(parsedHeadings.map((h) => h.id), (id) => {
14162
+ const disconnect = observeHeadings(parsedHeadings.map((h4) => h4.id), (id) => {
12818
14163
  setActiveId(id);
12819
14164
  onActiveIdChange?.(id);
12820
14165
  });
@@ -13944,7 +15289,7 @@ function resolveStorage(override) {
13944
15289
  return createMemoryStorage();
13945
15290
  }
13946
15291
  var DEFAULT_SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
13947
- var DEFAULT_KEY = "rfx:analytics:session";
15292
+ var DEFAULT_KEY2 = "rfx:analytics:session";
13948
15293
  var CAMPAIGN_PARAMS = [
13949
15294
  "utm_source",
13950
15295
  "utm_medium",
@@ -13975,7 +15320,7 @@ function campaignFingerprint(search) {
13975
15320
  }
13976
15321
  function createSession(config, now = () => Date.now()) {
13977
15322
  const storage = resolveStorage(config?.storage);
13978
- const key = config?.storageKey ?? DEFAULT_KEY;
15323
+ const key = config?.storageKey ?? DEFAULT_KEY2;
13979
15324
  const timeoutMs = config?.timeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;
13980
15325
  const resetOnCampaign = config?.resetOnCampaign ?? true;
13981
15326
  function read() {
@@ -14044,10 +15389,10 @@ function createSession(config, now = () => Date.now()) {
14044
15389
  }
14045
15390
  };
14046
15391
  }
14047
- var DEFAULT_KEY2 = "rfx:analytics:anon";
15392
+ var DEFAULT_KEY22 = "rfx:analytics:anon";
14048
15393
  function createIdentity(config) {
14049
15394
  const storage = resolveStorage(config?.storage);
14050
- const key = config?.storageKey ?? DEFAULT_KEY2;
15395
+ const key = config?.storageKey ?? DEFAULT_KEY22;
14051
15396
  let userId;
14052
15397
  function loadOrMintAnon() {
14053
15398
  const existing = storage.get(key);
@@ -15516,6 +16861,6 @@ function useTrackEvent(options) {
15516
16861
  );
15517
16862
  }
15518
16863
 
15519
- export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AltHintState, AnalyticsProvider, AnimatedText, AppShell, AuthGuard, AuthProvider, Avatar, AvatarFallback, AvatarGroup, AvatarImage, Badge, BadgeDisplay, BottomNav, Breadcrumbs, Button, CATEGORY_LABELS, Calendar, CalendarHeader, Callout, CalloutContent, CalloutDescription, CalloutIcon, CalloutTitle, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselTrigger, Checkbox, CodeBlock, CodeBlockContent, CodeBlockHeader, CodeEditor, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxTrigger, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, ContentProtection, DEFAULT_VOICE_PILL_POSITION, DEFAULT_VOICE_PILL_SPEAKER, DEFAULT_WAVEFORM_BAR_COUNT, DEFAULT_WAVEFORM_COLOR, DEFAULT_WAVEFORM_HEIGHT, DEFAULT_WAVEFORM_INTENSITY, DEFAULT_WAVEFORM_SMOOTHING, DataTable, DatePicker, DeviceFrame, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, EMOJI_CATEGORIES, EMOJI_DATA, EmojiPicker, FeedbackButton, FeedbackDialog, FileTree, FileUpload, Footer, IconSystem, InlineEditor, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InstallPrompt, KeyboardShortcut, LanguageSelector, LinkCard, MarkdownRenderer, MobileNav, MobileNavContent, MobileNavLink, MobileNavTrigger, Navbar, OtpInput, STATUS_COLORS as PRESENCE_STATUS_COLORS, STATUS_LABELS as PRESENCE_STATUS_LABELS, Pagination, Payment, Popover, PopoverClose, PopoverContent, PopoverTrigger, PresenceIndicator, ProgressBar, RadioGroup, RadioItem, ReactionBar, ResizableDivider, ResizableLayout, ResizablePane, SANE_DEFAULTS, STATUS_INDICATOR_COLORS as STATUS_COLORS, STATUS_INDICATOR_LABELS as STATUS_LABELS, SearchBar, SearchResultItem, SearchResults, Select, SelectContent, SelectItem, SelectTrigger, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetTitle, SheetTrigger, ShortcutBadge, ShortcutContext, ShortcutHint, ShortcutProvider, ShortcutRegistry, Sidebar, Skeleton, SkeletonText, SkipToContent, SlideViewer, StatsGrid, StatusIndicator, Step, StepContent, StepDescription, StepIndicator, StepTitle, Steps, Switch, TableOfContents, Tabs, TabsContent, TabsList, TabsTrigger, TelemetryErrorBoundary, TelemetryProvider, Textarea, ThreadView, Toast, ToastProvider, Toaster, Tooltip, TooltipContent, TooltipTrigger, TypewriterText, VersionSelector, VideoPlayer, VoicePill, Waveform, altHintState, animatedTextVariants, avatarFallbackVariants, avatarImageVariants, avatarTokens, avatarVariants, badgeGridVariants, badgeItemVariants, badgeVariants, bottomNavTabVariants, bottomNavVariants, breadcrumbItemVariants, breadcrumbSeparatorStyles, breadcrumbsVariants, buttonTokens, buttonVariants, calendarVariants, canAccessAdmin, canAccessReviewer, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardTokens, cardVariants, cellVariants, checkIconPath, checkboxTokens, checkboxVariants, clampVoicePillIntensity, codeEditorTokens, codeEditorVariants, collapsibleContentVariants, comboboxContentVariants, comboboxEmptyVariants, comboboxInputVariants, comboboxItemVariants, comboboxListVariants, comboboxTriggerVariants, commandGroupVariants, commandInputVariants, commandItemVariants, commandVariants, contentProtectionVariants, controlsVariants, createAnalytics, createAppInsightsSink, createConsoleSink, createGA4ClientSdkSink, createGA4HttpSink, createGA4Sink, createIntensitySamples, createPostHogClientSdkSink, createPostHogHttpSink, createPostHogSink, createSilentSamples, createTelemetry, createVoicePill, createWaveform, dayVariants, deviceFrameVariants, dialogContentVariants, drawWaveform, editorVariants, emojiPickerContainerStyles, emojiPickerEmojiButtonStyles, emojiPickerGridStyles, feedbackDialogVariants, fileUploadDropZoneVariants, fileUploadFileItemStyles, fileUploadFileListStyles, footerVariants, formatFileSize, formatRelativeTime, formatShortcut, formatTimestamp, getAssignableRoles, getDefaultPortal, getInitials, getVoicePillAriaLabel, getVoicePillInitials, getVoicePillPosition, getVoicePillPulseStyle, getVoicePillSpeakerKey, getVoicePillSpeakerLabel, getWaveformPeak, globalShortcutRegistry, hasAllRoles, hasAnyRole, hasRole, headerVariants, indeterminateIconPath, inputGroupAddonVariants, inputGroupButtonVariants, inputGroupTokens, inputGroupVariants, inputVariants, installPromptVariants, isWaveformSampleInput, latestBadgeVariants, markdownRendererTokens, menuContentVariants, menuItemVariants, mobileNavContentVariants, mobileNavLinkVariants, mobileNavTokens, mobileNavTriggerVariants, mobileNavVariants, navLinkVariants, navbarVariants, normalizeBarCount, normalizeIntensity, normalizeSmoothing, normalizeWaveformConfig, normalizeWaveformSamples, optionVariants, otpInputContainerVariants, otpInputSlotVariants, otpInputTokens, overlayStyles, overlayVariants, playerVariants, popoverContentVariants, prepareWaveformCanvas, previewVariants, progressBarVariants, proseVariants, radioCircleVariants, radioGroupVariants, radioItemVariants, reactionAddButtonStyles, reactionBarStyles, reactionCountStyles, reactionEmojiStyles, reactionPillVariants, resampleWaveformSamples, resizableDividerVariants, resizableLayoutTokens, resizableLayoutVariants, resizablePaneVariants, rowVariants, scaleWaveformSamples, searchBarVariants, searchResultVariants, selectContentVariants, selectItemVariants, selectTokens, selectTriggerVariants, selectorVariants, sheetContentVariants, sheetOverlayStyles, shortcutBadgeStyles, shortcutKeyStyles, shortcutSeparatorStyles, sidebarItemVariants, sidebarVariants, skeletonVariants, slideTypeBadgeVariants, slideViewerProgressBarVariants, slideViewerTokens, slideViewerVariants, smoothWaveformSamples, startSessionReplay, statCardVariants, statsGridVariants, statusContainerStyles, statusDotVariants, statusLabelStyles, statusPulseVariants, switchThumbVariants, switchTokens, switchVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, threadAuthorStyles, threadAvatarStyles, threadBodyStyles, threadContainerStyles, threadContentStyles, threadMessageStyles, threadReactionsStyles, threadTimestampStyles, toCssDimension, toastVariants, toolbarVariants, tooltipContentVariants, typewriterVariants, useAnalytics, useAuth, useLogger, useShortcut, useSpan, useTelemetry, useToast, useTrackEvent, versionSelectorOptionVariants, versionSelectorVariants, voicePillAvatarStyles, voicePillAvatarWrapStyles, voicePillLabelStyles, voicePillMuteButtonStyles, voicePillPositionVariants, voicePillPulseRingStyles, voicePillRootStyles, voicePillSpeakerStyles, voicePillSubStyles, voicePillTextStyles, voicePillTokens, watermarkVariants, waveformCanvasVariants, waveformVariants };
16864
+ export { Accordion, AccordionContent, AccordionItem, AccordionTrigger, AltHintState, AnalyticsProvider, AnimatedText, AppShell, AuthGuard, AuthProvider, Avatar2 as Avatar, AvatarFallback, AvatarGroup, AvatarImage, Badge, BadgeDisplay, BottomNav, Breadcrumbs, Button, CATEGORY_LABELS, Calendar, CalendarHeader, Callout, CalloutContent, CalloutDescription, CalloutIcon, CalloutTitle, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselTrigger, Chat, Checkbox, CodeBlock, CodeBlockContent, CodeBlockHeader, CodeEditor, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, ComboboxTrigger, Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, Composer, ContentProtection, CookieConsent, DEFAULT_CATEGORIES, DEFAULT_VOICE_PILL_POSITION, DEFAULT_VOICE_PILL_SPEAKER, DEFAULT_WAVEFORM_BAR_COUNT, DEFAULT_WAVEFORM_COLOR, DEFAULT_WAVEFORM_HEIGHT, DEFAULT_WAVEFORM_INTENSITY, DEFAULT_WAVEFORM_SMOOTHING, DataTable, DatePicker, DeviceFrame, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, EMOJI_CATEGORIES, EMOJI_DATA, EmojiPicker, FeedbackButton, FeedbackDialog, FileTree, FileUpload, Footer, IconSystem, InlineEditor, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InstallPrompt, KeyboardShortcut, LanguageSelector, LinkCard, MarkdownRenderer, MobileNav, MobileNavContent, MobileNavLink, MobileNavTrigger, Navbar, OtpInput, STATUS_COLORS as PRESENCE_STATUS_COLORS, STATUS_LABELS as PRESENCE_STATUS_LABELS, Pagination, Payment, Popover, PopoverClose, PopoverContent, PopoverTrigger, PresenceIndicator, ProgressBar, RadioGroup, RadioItem, ReactionBar, ResizableDivider, ResizableLayout, ResizablePane, SANE_DEFAULTS, STATUS_INDICATOR_COLORS as STATUS_COLORS, STATUS_INDICATOR_LABELS as STATUS_LABELS, SearchBar, SearchResultItem, SearchResults, Select, SelectContent, SelectItem, SelectTrigger, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetTitle, SheetTrigger, ShortcutBadge, ShortcutContext, ShortcutHint, ShortcutProvider, ShortcutRegistry, Sidebar, Skeleton, SkeletonText, SkipToContent, SlideViewer, StatsGrid, StatusIndicator, Step, StepContent, StepDescription, StepIndicator, StepTitle, Steps, Switch, TableOfContents, Tabs, TabsContent, TabsList, TabsTrigger, TelemetryErrorBoundary, TelemetryProvider, Textarea, ThreadView, Toast, ToastProvider, Toaster, Tooltip, TooltipContent, TooltipTrigger, TypewriterText, VersionSelector, VideoPlayer, VoicePill, Waveform, altHintState, animatedTextVariants, avatarFallbackVariants, avatarImageVariants, avatarTokens, avatarVariants, badgeGridVariants, badgeItemVariants, badgeVariants, bottomNavTabVariants, bottomNavVariants, breadcrumbItemVariants, breadcrumbSeparatorStyles, breadcrumbsVariants, buttonTokens, buttonVariants, calendarVariants, canAccessAdmin, canAccessReviewer, cardContentVariants, cardDescriptionVariants, cardFooterVariants, cardHeaderVariants, cardTitleVariants, cardTokens, cardVariants, cellVariants, checkIconPath, checkboxTokens, checkboxVariants, clampVoicePillIntensity, codeEditorTokens, codeEditorVariants, collapsibleContentVariants, comboboxContentVariants, comboboxEmptyVariants, comboboxInputVariants, comboboxItemVariants, comboboxListVariants, comboboxTriggerVariants, commandGroupVariants, commandInputVariants, commandItemVariants, commandVariants, contentProtectionVariants, controlsVariants, createAnalytics, createAppInsightsSink, createConsoleSink, createConversation, createCookieConsent, createGA4ClientSdkSink, createGA4HttpSink, createGA4Sink, createIntensitySamples, createPostHogClientSdkSink, createPostHogHttpSink, createPostHogSink, createSilentSamples, createTelemetry, createVoicePill, createWaveform, dayVariants, deviceFrameVariants, dialogContentVariants, drawWaveform, editorVariants, emojiPickerContainerStyles, emojiPickerEmojiButtonStyles, emojiPickerGridStyles, feedbackDialogVariants, fileUploadDropZoneVariants, fileUploadFileItemStyles, fileUploadFileListStyles, findMessage, footerVariants, formatFileSize, formatRelativeTime, formatShortcut, formatTimestamp, getAssignableRoles, getDefaultPortal, getInitials, getReplyCount, getVoicePillAriaLabel, getVoicePillInitials, getVoicePillPosition, getVoicePillPulseStyle, getVoicePillSpeakerKey, getVoicePillSpeakerLabel, getWaveformPeak, globalShortcutRegistry, hasAllRoles, hasAnyRole, hasRole, headerVariants, indeterminateIconPath, inputGroupAddonVariants, inputGroupButtonVariants, inputGroupTokens, inputGroupVariants, inputVariants, installPromptVariants, isWaveformSampleInput, latestBadgeVariants, markdownRendererTokens, menuContentVariants, menuItemVariants, mobileNavContentVariants, mobileNavLinkVariants, mobileNavTokens, mobileNavTriggerVariants, mobileNavVariants, navLinkVariants, navbarVariants, normalizeBarCount, normalizeIntensity, normalizeSmoothing, normalizeWaveformConfig, normalizeWaveformSamples, optionVariants, otpInputContainerVariants, otpInputSlotVariants, otpInputTokens, overlayStyles, overlayVariants, playerVariants, popoverContentVariants, prepareWaveformCanvas, previewVariants, progressBarVariants, proseVariants, radioCircleVariants, radioGroupVariants, radioItemVariants, reactionAddButtonStyles, reactionBarStyles, reactionCountStyles, reactionEmojiStyles, reactionPillVariants, resampleWaveformSamples, resizableDividerVariants, resizableLayoutTokens, resizableLayoutVariants, resizablePaneVariants, rootIdOf, rowVariants, scaleWaveformSamples, searchBarVariants, searchResultVariants, selectContentVariants, selectItemVariants, selectMainTimeline, selectReplies, selectRoots, selectThreadMessages, selectTimelineFromState, selectTokens, selectTriggerVariants, selectorVariants, sheetContentVariants, sheetOverlayStyles, shortcutBadgeStyles, shortcutKeyStyles, shortcutSeparatorStyles, sidebarItemVariants, sidebarVariants, skeletonVariants, slideTypeBadgeVariants, slideViewerProgressBarVariants, slideViewerTokens, slideViewerVariants, smoothWaveformSamples, startSessionReplay, statCardVariants, statsGridVariants, statusContainerStyles, statusDotVariants, statusLabelStyles, statusPulseVariants, switchThumbVariants, switchTokens, switchVariants, tableVariants, tabsListVariants, tabsTriggerVariants, textareaVariants, threadAuthorStyles, threadAvatarStyles, threadBodyStyles, threadContainerStyles, threadContentStyles, threadMessageStyles, threadReactionsStyles, threadTimestampStyles, toCssDimension, toastVariants, toolbarVariants, tooltipContentVariants, typewriterVariants, useAnalytics, useAuth, useConversation, useCookieConsent, useLogger, useShortcut, useSpan, useTelemetry, useToast, useTrackEvent, versionSelectorOptionVariants, versionSelectorVariants, voicePillAvatarStyles, voicePillAvatarWrapStyles, voicePillLabelStyles, voicePillMuteButtonStyles, voicePillPositionVariants, voicePillPulseRingStyles, voicePillRootStyles, voicePillSpeakerStyles, voicePillSubStyles, voicePillTextStyles, voicePillTokens, watermarkVariants, waveformCanvasVariants, waveformVariants };
15520
16865
  //# sourceMappingURL=index.js.map
15521
16866
  //# sourceMappingURL=index.js.map