@admin-layout/tailwind-ui 12.2.4-alpha.55 → 12.2.4-alpha.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/lib/components/AIToolbar/components/completion/AssistantAvatar.d.ts +12 -0
  2. package/lib/components/AIToolbar/components/completion/AssistantAvatar.d.ts.map +1 -0
  3. package/lib/components/AIToolbar/components/completion/AssistantAvatar.js +36 -0
  4. package/lib/components/AIToolbar/components/completion/AssistantAvatar.js.map +1 -0
  5. package/lib/components/AIToolbar/components/completion/AssistantSenderBadge.d.ts +15 -0
  6. package/lib/components/AIToolbar/components/completion/AssistantSenderBadge.d.ts.map +1 -0
  7. package/lib/components/AIToolbar/components/completion/CharacterPicker.d.ts +3 -0
  8. package/lib/components/AIToolbar/components/completion/CharacterPicker.d.ts.map +1 -0
  9. package/lib/components/AIToolbar/components/completion/CharacterPicker.js +90 -0
  10. package/lib/components/AIToolbar/components/completion/CharacterPicker.js.map +1 -0
  11. package/lib/components/AIToolbar/components/completion/Input.d.ts.map +1 -1
  12. package/lib/components/AIToolbar/components/completion/Input.js +103 -53
  13. package/lib/components/AIToolbar/components/completion/Input.js.map +1 -1
  14. package/lib/components/AIToolbar/components/completion/MessageHistory.d.ts.map +1 -1
  15. package/lib/components/AIToolbar/components/completion/MessageHistory.js +59 -20
  16. package/lib/components/AIToolbar/components/completion/MessageHistory.js.map +1 -1
  17. package/lib/components/AIToolbar/components/speech/ResultsSection.d.ts.map +1 -1
  18. package/lib/components/AIToolbar/components/speech/ResultsSection.js +14 -4
  19. package/lib/components/AIToolbar/components/speech/ResultsSection.js.map +1 -1
  20. package/lib/components/AIToolbar/hooks/useActiveCharacter.d.ts +33 -0
  21. package/lib/components/AIToolbar/hooks/useActiveCharacter.d.ts.map +1 -0
  22. package/lib/components/AIToolbar/hooks/useActiveCharacter.js +257 -0
  23. package/lib/components/AIToolbar/hooks/useActiveCharacter.js.map +1 -0
  24. package/lib/components/AIToolbar/hooks/useChatBackgroundImage.d.ts +10 -0
  25. package/lib/components/AIToolbar/hooks/useChatBackgroundImage.d.ts.map +1 -0
  26. package/lib/components/AIToolbar/hooks/useChatBackgroundImage.js +126 -0
  27. package/lib/components/AIToolbar/hooks/useChatBackgroundImage.js.map +1 -0
  28. package/package.json +2 -2
@@ -0,0 +1,12 @@
1
+ interface AssistantAvatarProps {
2
+ avatarSrc: string | null;
3
+ /** Optional message sequence number rendered as "#N" beneath the avatar. */
4
+ index?: number;
5
+ /** Avatar diameter in px. */
6
+ size?: number;
7
+ /** Tailwind classes applied to the column wrapper. */
8
+ className?: string;
9
+ }
10
+ export declare function AssistantAvatar({ avatarSrc, index, size, className }: AssistantAvatarProps): import("react/jsx-runtime").JSX.Element;
11
+ export default AssistantAvatar;
12
+ //# sourceMappingURL=AssistantAvatar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantAvatar.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/AssistantAvatar.tsx"],"names":[],"mappings":"AASA,UAAU,oBAAoB;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,IAAS,EAAE,SAAS,EAAE,EAAE,oBAAoB,2CA6B/F;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,36 @@
1
+ import {jsxs,jsx}from'react/jsx-runtime';import'../../../../utils/isBrowser/index.js';import {cn}from'../../../../utils/util.js';import'react';import'fast-deep-equal/react.js';function AssistantAvatar({
2
+ avatarSrc,
3
+ index,
4
+ size = 44,
5
+ className
6
+ }) {
7
+ const hasNumber = typeof index === 'number';
8
+ return jsxs("div", {
9
+ className: cn('flex shrink-0 flex-col items-center gap-1 pt-0.5', className),
10
+ "aria-hidden": !hasNumber,
11
+ children: [avatarSrc ? jsx("img", {
12
+ src: avatarSrc,
13
+ alt: "",
14
+ "aria-hidden": "true",
15
+ draggable: false,
16
+ width: size,
17
+ height: size,
18
+ className: "rounded-full object-cover ring-1 ring-border/60 shadow-sm",
19
+ style: {
20
+ width: size,
21
+ height: size
22
+ }
23
+ }) : jsx("div", {
24
+ "aria-hidden": "true",
25
+ className: "flex items-center justify-center rounded-full bg-muted text-[10px] font-semibold text-muted-foreground ring-1 ring-border/60",
26
+ style: {
27
+ width: size,
28
+ height: size
29
+ },
30
+ children: "AI"
31
+ }), hasNumber ? jsxs("span", {
32
+ className: "text-[10px] font-medium leading-none text-muted-foreground",
33
+ children: ["#", index]
34
+ }) : null]
35
+ });
36
+ }export{AssistantAvatar};//# sourceMappingURL=AssistantAvatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantAvatar.js","sources":["../../../../../src/components/AIToolbar/components/completion/AssistantAvatar.tsx"],"sourcesContent":[null],"names":["_jsxs"],"mappings":"gLAWI,SAAA,eAAA,CAAA;WACM;OACN;MACA,GAAK,EAAE;;;AAGV,EAAA,MAAA,SAAA,GAAA,OAAA,KAAA,KAAA,QAAA;AAED,EAAA,OAAAA,IAAA,CAAA,KAAA,EAAA;AA+BA,IAAA,SAAA,EAAe,qDAAgB,EAAA,SAAA,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,15 @@
1
+ interface AssistantSenderBadgeProps {
2
+ name: string;
3
+ avatarSrc: string | null;
4
+ /** Tailwind override on the wrapping span (e.g. scale text down inside dense lists). */
5
+ className?: string;
6
+ /** Avatar square size in px; defaults to 44 — matches SillyTavern's chat-header portrait. */
7
+ avatarSize?: number;
8
+ /** Optional message sequence number — rendered as "#N" beneath the avatar (SillyTavern style). */
9
+ index?: number;
10
+ /** Tailwind classes applied to the name `<span>`. Override when reusing inside denser surfaces. */
11
+ nameClassName?: string;
12
+ }
13
+ export declare function AssistantSenderBadge({ name, avatarSrc, className, avatarSize, index, nameClassName, }: AssistantSenderBadgeProps): import("react/jsx-runtime").JSX.Element;
14
+ export default AssistantSenderBadge;
15
+ //# sourceMappingURL=AssistantSenderBadge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssistantSenderBadge.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/AssistantSenderBadge.tsx"],"names":[],"mappings":"AAUA,UAAU,yBAAyB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,wFAAwF;IACxF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,kGAAkG;IAClG,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mGAAmG;IACnG,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,oBAAoB,CAAC,EACjC,IAAI,EACJ,SAAS,EACT,SAAS,EACT,UAAe,EACf,KAAK,EACL,aAAa,GAChB,EAAE,yBAAyB,2CA0B3B;AAED,eAAe,oBAAoB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export declare function CharacterPicker(): import("react/jsx-runtime").JSX.Element;
2
+ export default CharacterPicker;
3
+ //# sourceMappingURL=CharacterPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CharacterPicker.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/CharacterPicker.tsx"],"names":[],"mappings":"AAgDA,wBAAgB,eAAe,4CAiD9B;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,90 @@
1
+ import {jsxs,jsx}from'react/jsx-runtime';import*as React from'react';import {ChevronDownIcon,CheckIcon}from'lucide-react';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import {Button}from'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import {Popover,PopoverTrigger,PopoverContent}from'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import {cn}from'../../../../utils/util.js';import'fast-deep-equal/react.js';import'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {useCharacterPicker}from'../../hooks/useActiveCharacter.js';function AvatarBadge({
2
+ entry,
3
+ size
4
+ }) {
5
+ if (entry?.avatarSrc) {
6
+ return jsx("img", {
7
+ src: entry.avatarSrc,
8
+ alt: "",
9
+ "aria-hidden": "true",
10
+ draggable: false,
11
+ width: size,
12
+ height: size,
13
+ className: "rounded-full object-cover ring-1 ring-border/60",
14
+ style: {
15
+ width: size,
16
+ height: size
17
+ }
18
+ });
19
+ }
20
+ return jsx("div", {
21
+ "aria-hidden": "true",
22
+ className: "flex items-center justify-center rounded-full bg-muted text-[9px] font-semibold text-muted-foreground ring-1 ring-border/60",
23
+ style: {
24
+ width: size,
25
+ height: size
26
+ },
27
+ children: "AI"
28
+ });
29
+ }
30
+ // main component
31
+ function CharacterPicker() {
32
+ const {
33
+ characters,
34
+ activeName,
35
+ setActive
36
+ } = useCharacterPicker();
37
+ const [open, setOpen] = React.useState(false);
38
+ if (characters.length < 2) return null;
39
+ const active = characters.find(c => c.name === activeName) ?? characters[0] ?? null;
40
+ return jsxs(Popover, {
41
+ open: open,
42
+ onOpenChange: setOpen,
43
+ children: [jsx(PopoverTrigger, {
44
+ asChild: true,
45
+ children: jsxs(Button, {
46
+ size: "sm",
47
+ variant: "ghost",
48
+ className: "h-7 gap-1.5 rounded-full border border-border/50 bg-muted/30 px-1.5 py-0 text-[11px] font-medium",
49
+ title: "Switch character",
50
+ children: [jsx(AvatarBadge, {
51
+ entry: active,
52
+ size: 20
53
+ }), jsx("span", {
54
+ className: "max-w-[8rem] truncate",
55
+ children: active?.name ?? 'AI'
56
+ }), jsx(ChevronDownIcon, {
57
+ className: "h-3 w-3 opacity-60"
58
+ })]
59
+ })
60
+ }), jsx(PopoverContent, {
61
+ align: "start",
62
+ side: "bottom",
63
+ sideOffset: 6,
64
+ className: "z-[210] w-56 p-1",
65
+ children: jsx("div", {
66
+ className: "max-h-64 overflow-y-auto",
67
+ children: characters.map(c => {
68
+ const isActive = c.name === active?.name;
69
+ return jsxs("button", {
70
+ type: "button",
71
+ onClick: () => {
72
+ setActive(c.name);
73
+ setOpen(false);
74
+ },
75
+ className: cn('flex w-full items-center gap-2 rounded px-2 py-1.5 text-left text-xs hover:bg-muted', isActive ? 'bg-muted/60' : ''),
76
+ children: [jsx(AvatarBadge, {
77
+ entry: c,
78
+ size: 24
79
+ }), jsx("span", {
80
+ className: "flex-1 truncate",
81
+ children: c.name
82
+ }), isActive ? jsx(CheckIcon, {
83
+ className: "h-3.5 w-3.5 opacity-70"
84
+ }) : null]
85
+ }, c.name);
86
+ })
87
+ })
88
+ })]
89
+ });
90
+ }export{CharacterPicker};//# sourceMappingURL=CharacterPicker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CharacterPicker.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/Input.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAI/C,eAAO,MAAM,KAAK,GAAI,sTAsBnB,mBAAmB,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAA;CAAE,4CAuP7C,CAAC"}
1
+ {"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/Input.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAQ/C,eAAO,MAAM,KAAK,GAAI,sTAsBnB,mBAAmB,GAAG;IACrB,QAAQ,EAAE,OAAO,CAAC;CACrB,4CAyTA,CAAC"}
@@ -1,4 +1,4 @@
1
- import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}from'react';import {AIToolbarInputSurface}from'../AIToolbarPrimitives.js';import {XIcon,Loader2}from'lucide-react';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import {Button}from'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import {Input as Input$1}from'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import {Popover,PopoverContent,PopoverTrigger}from'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import {cn}from'../../../../utils/util.js';import'fast-deep-equal/react.js';import'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import {Switch}from'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {Markdown}from'../Markdown/index.js';import {AssistantMessageActions}from'./AssistantMessageActions.js';import {CopyButton}from'../Markdown/copy-button.js';import {MessageHistory}from'./MessageHistory.js';const Input = ({
1
+ import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}from'react';import {AIToolbarInputSurface}from'../AIToolbarPrimitives.js';import {XIcon,Loader2}from'lucide-react';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import {Button}from'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import {Input as Input$1}from'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import {Popover,PopoverContent,PopoverTrigger}from'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import {cn}from'../../../../utils/util.js';import'fast-deep-equal/react.js';import'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import {Switch}from'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {Markdown}from'../Markdown/index.js';import {AssistantMessageActions}from'./AssistantMessageActions.js';import {CopyButton}from'../Markdown/copy-button.js';import {MessageHistory}from'./MessageHistory.js';import {useActiveCharacter}from'../../hooks/useActiveCharacter.js';import {useChatBackgroundImage}from'../../hooks/useChatBackgroundImage.js';import {AssistantAvatar}from'./AssistantAvatar.js';import {CharacterPicker}from'./CharacterPicker.js';const Input = ({
2
2
  isPopoverOpen,
3
3
  isLoading,
4
4
  reset,
@@ -22,6 +22,9 @@ import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}
22
22
  regenerateAssistantMessage
23
23
  }) => {
24
24
  const conversationEndRef = useRef(null);
25
+ const character = useActiveCharacter();
26
+ const assistantName = character?.name ?? 'AI';
27
+ const chatBgImage = useChatBackgroundImage();
25
28
  const sortedAsc = useMemo(() => [...conversationHistory].sort((a, b) => (a.timestamp ?? 0) - (b.timestamp ?? 0)), [conversationHistory]);
26
29
  /** In-flight assistant bubble: hide when history already contains the same assistant text (avoids double render if `response` lingers). */
27
30
  const streamingAssistantVisible = useMemo(() => {
@@ -51,13 +54,13 @@ import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}
51
54
  return () => cancelAnimationFrame(outer);
52
55
  }, [isPopoverOpen, sortedAsc, response, isLoading, error, streamingAssistantVisible]);
53
56
  const copyPayload = useMemo(() => {
54
- const lines = sortedAsc.map(m => m.role === 'user' ? `You:\n${m.content}` : `AI:\n${m.content}`);
57
+ const lines = sortedAsc.map(m => m.role === 'user' ? `You:\n${m.content}` : `${assistantName}:\n${m.content}`);
55
58
  const last = sortedAsc[sortedAsc.length - 1];
56
59
  if (response && !(last?.role === 'assistant' && last.content === response)) {
57
- lines.push(`AI:\n${response}`);
60
+ lines.push(`${assistantName}:\n${response}`);
58
61
  }
59
62
  return lines.join('\n\n');
60
- }, [sortedAsc, response]);
63
+ }, [sortedAsc, response, assistantName]);
61
64
  return jsx("div", {
62
65
  className: "relative flex-1",
63
66
  children: jsxs(Popover, {
@@ -77,15 +80,28 @@ import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}
77
80
  left: 8,
78
81
  right: 8
79
82
  },
80
- className: cn('z-[200] flex h-[min(90vh,calc(100dvh-1rem))] flex-col overflow-hidden border bg-popover p-0 text-popover-foreground shadow-lg', 'w-[calc(100vw-16px)] max-w-[calc(100vw-16px)] min-w-0 rounded-md'),
81
- children: [jsxs("div", {
82
- className: "flex shrink-0 items-center justify-between border-b bg-muted/30 px-4 py-2",
83
+ className: cn('relative z-[200] flex h-[min(90vh,calc(100dvh-1rem))] flex-col overflow-hidden border p-0 text-popover-foreground shadow-lg',
84
+ // Solid base when no wallpaper; translucent so the wallpaper bleeds through when one is selected.
85
+ chatBgImage ? 'bg-popover/70' : 'bg-popover', 'w-[calc(100vw-16px)] max-w-[calc(100vw-16px)] min-w-0 rounded-md'),
86
+ children: [chatBgImage ? jsxs(Fragment, {
87
+ children: [jsx("div", {
88
+ "aria-hidden": true,
89
+ className: "pointer-events-none absolute inset-0 bg-cover bg-center",
90
+ style: {
91
+ backgroundImage: `url(${chatBgImage})`
92
+ }
93
+ }), jsx("div", {
94
+ "aria-hidden": true,
95
+ className: "pointer-events-none absolute inset-0 bg-background/30 backdrop-blur-[2px]"
96
+ })]
97
+ }) : null, jsxs("div", {
98
+ className: "relative z-10 flex shrink-0 items-center justify-between border-b bg-muted/30 px-4 py-2",
83
99
  children: [jsxs("div", {
84
- className: "flex flex-row items-center gap-1",
100
+ className: "flex flex-row items-center gap-2",
85
101
  children: [jsx("h3", {
86
102
  className: "select-none text-xs font-semibold",
87
103
  children: keepEngaged ? 'Conversation Mode' : 'AI Response'
88
- }), jsx("div", {
104
+ }), jsx(CharacterPicker, {}), jsx("div", {
89
105
  className: "text-[10px] text-muted-foreground/70",
90
106
  children: "(Use arrow keys to scroll)"
91
107
  })]
@@ -130,7 +146,7 @@ import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}
130
146
  })]
131
147
  }), jsx("div", {
132
148
  ref: scrollAreaRef,
133
- className: cn('min-h-0 flex-1 basis-0 overflow-x-hidden overflow-y-scroll overscroll-contain', 'pr-1 [scrollbar-gutter:stable]'),
149
+ className: cn('relative z-10 min-h-0 flex-1 basis-0 overflow-x-hidden overflow-y-scroll overscroll-contain', 'pr-1 [scrollbar-gutter:stable]'),
134
150
  children: jsxs("div", {
135
151
  className: "space-y-3 p-4 pb-8",
136
152
  children: [error ? jsxs("div", {
@@ -138,54 +154,88 @@ import {jsx,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useLayoutEffect}
138
154
  children: [jsx("strong", {
139
155
  children: "Error:"
140
156
  }), " ", error]
141
- }) : null, sortedAsc.map(msg => jsx("div", {
142
- className: cn('flex w-full', msg.role === 'user' ? 'justify-end' : 'justify-start'),
143
- children: jsxs("div", {
144
- className: cn('rounded-xl px-3 py-2 text-sm shadow-sm', msg.role === 'user' ? 'max-w-[min(100%,40rem)] border border-border/60 bg-muted/70 text-foreground' : 'w-full min-w-0 max-w-full border border-border/60 bg-muted/70'),
145
- children: [jsxs("div", {
146
- className: "mb-1.5 flex items-center gap-2",
147
- children: [jsx("span", {
148
- className: "text-[10px] font-semibold uppercase text-muted-foreground",
149
- children: msg.role === 'user' ? 'You' : 'AI'
150
- }), jsx("span", {
151
- className: "text-[10px] text-muted-foreground",
152
- children: new Date(msg.timestamp).toLocaleTimeString([], {
153
- hour: '2-digit',
154
- minute: '2-digit'
155
- })
157
+ }) : null, sortedAsc.map((msg, idx) => jsx("div", {
158
+ className: cn('flex w-full gap-2', msg.role === 'user' ? 'justify-end' : 'justify-start'),
159
+ children: jsx("div", {
160
+ className: cn('rounded-xl px-3 py-2 text-sm shadow-sm', msg.role === 'user' ? 'max-w-[min(100%,40rem)] border border-border/60 bg-muted/70 text-foreground' : 'min-w-0 max-w-full flex-1 border border-border/60 bg-muted/70'),
161
+ children: msg.role === 'user' ? jsxs(Fragment, {
162
+ children: [jsxs("div", {
163
+ className: "mb-1.5 flex items-center gap-2",
164
+ children: [jsx("span", {
165
+ className: "text-[10px] font-semibold uppercase text-muted-foreground",
166
+ children: "You"
167
+ }), jsx("span", {
168
+ className: "text-[11px] text-muted-foreground",
169
+ children: new Date(msg.timestamp).toLocaleTimeString([], {
170
+ hour: '2-digit',
171
+ minute: '2-digit'
172
+ })
173
+ })]
174
+ }), jsx(Markdown, {
175
+ children: msg.content
176
+ })]
177
+ }) : jsxs("div", {
178
+ className: "flex w-full gap-3",
179
+ children: [jsx(AssistantAvatar, {
180
+ avatarSrc: character?.avatarSrc ?? null,
181
+ index: idx + 1
182
+ }), jsxs("div", {
183
+ className: "min-w-0 flex-1",
184
+ children: [jsxs("div", {
185
+ className: "mb-1 flex items-baseline gap-2",
186
+ children: [jsx("span", {
187
+ className: "text-sm font-semibold text-foreground",
188
+ children: assistantName
189
+ }), jsx("span", {
190
+ className: "text-[11px] text-muted-foreground",
191
+ children: new Date(msg.timestamp).toLocaleTimeString([], {
192
+ hour: '2-digit',
193
+ minute: '2-digit'
194
+ })
195
+ })]
196
+ }), jsx(Markdown, {
197
+ children: msg.content
198
+ }), jsx(AssistantMessageActions, {
199
+ messageId: msg.id,
200
+ content: msg.content,
201
+ onRegenerate: regenerateAssistantMessage ? id => void regenerateAssistantMessage(id) : undefined
202
+ })]
156
203
  })]
157
- }), jsx(Markdown, {
158
- children: msg.content
159
- }), msg.role === 'assistant' ? jsx(AssistantMessageActions, {
160
- messageId: msg.id,
161
- content: msg.content,
162
- onRegenerate: regenerateAssistantMessage ? id => void regenerateAssistantMessage(id) : undefined
163
- }) : null]
204
+ })
164
205
  })
165
206
  }, msg.id)), streamingAssistantVisible ? jsx("div", {
166
207
  className: "flex w-full justify-start",
167
- children: jsxs("div", {
208
+ children: jsx("div", {
168
209
  className: "w-full min-w-0 max-w-full rounded-xl border border-border/60 bg-muted/70 px-3 py-2 text-sm shadow-sm",
169
- children: [jsxs("div", {
170
- className: "mb-1.5 flex items-center gap-2",
171
- children: [jsx("span", {
172
- className: "text-[10px] font-semibold uppercase text-muted-foreground",
173
- children: "AI"
174
- }), isLoading && !response ? jsx(Loader2, {
175
- className: "h-3 w-3 animate-spin text-muted-foreground"
176
- }) : null]
177
- }), response ? jsx(Markdown, {
178
- children: response
179
- }) : isLoading ? jsx("p", {
180
- className: "text-sm text-muted-foreground",
181
- children: "Generating response\u2026"
182
- }) : null, response ? jsx(AssistantMessageActions, {
183
- messageId: "streaming",
184
- content: response,
185
- visibility: {
186
- regenerate: false
187
- }
188
- }) : null]
210
+ children: jsxs("div", {
211
+ className: "flex w-full gap-3",
212
+ children: [jsx(AssistantAvatar, {
213
+ avatarSrc: character?.avatarSrc ?? null,
214
+ index: sortedAsc.length + 1
215
+ }), jsxs("div", {
216
+ className: "min-w-0 flex-1",
217
+ children: [jsxs("div", {
218
+ className: "mb-1 flex items-baseline gap-2",
219
+ children: [jsx("span", {
220
+ className: "text-sm font-semibold text-foreground",
221
+ children: assistantName
222
+ }), isLoading && !response ? jsx(Loader2, {
223
+ className: "h-3.5 w-3.5 animate-spin text-muted-foreground"
224
+ }) : null]
225
+ }), response ? jsx(Markdown, {
226
+ children: response
227
+ }) : isLoading ? jsx("p", {
228
+ className: "text-sm text-muted-foreground",
229
+ children: "Generating response\u2026"
230
+ }) : null, response ? jsx(AssistantMessageActions, {
231
+ messageId: "streaming",
232
+ content: response,
233
+ visibility: {
234
+ regenerate: false
235
+ }
236
+ }) : null]
237
+ })]
238
+ })
189
239
  })
190
240
  }) : null, jsx("div", {
191
241
  ref: conversationEndRef,
@@ -1 +1 @@
1
- {"version":3,"file":"Input.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"Input.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHistory.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/MessageHistory.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,UAAU,mBAAmB;IACzB,mBAAmB,EAAE,WAAW,EAAE,CAAC;IACnC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD;AAED,eAAO,MAAM,cAAc,GAAI,6FAK5B,mBAAmB,4CA2FrB,CAAC"}
1
+ {"version":3,"file":"MessageHistory.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/completion/MessageHistory.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAM5C,UAAU,mBAAmB;IACzB,mBAAmB,EAAE,WAAW,EAAE,CAAC;IACnC,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,sBAAsB,EAAE,MAAM,IAAI,CAAC;IACnC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,qBAAqB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAClD;AAED,eAAO,MAAM,cAAc,GAAI,6FAK5B,mBAAmB,4CAuIrB,CAAC"}
@@ -1,9 +1,12 @@
1
- import {jsxs,jsx}from'react/jsx-runtime';import {MessageSquareText,ChevronUp,ChevronDown}from'lucide-react';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import {Button}from'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import {Popover,PopoverTrigger,PopoverContent}from'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import'clsx';import'tailwind-merge';import'react';import'fast-deep-equal/react.js';import {ScrollArea}from'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {Markdown}from'../Markdown/index.js';const MessageHistory = ({
1
+ import {jsxs,jsx,Fragment}from'react/jsx-runtime';import {MessageSquareText,ChevronUp,ChevronDown}from'lucide-react';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import {Button}from'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import {Popover,PopoverTrigger,PopoverContent}from'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import'clsx';import'tailwind-merge';import'react';import'fast-deep-equal/react.js';import {ScrollArea}from'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {Markdown}from'../Markdown/index.js';import {useActiveCharacter}from'../../hooks/useActiveCharacter.js';import {useChatBackgroundImage}from'../../hooks/useChatBackgroundImage.js';import {AssistantAvatar}from'./AssistantAvatar.js';const MessageHistory = ({
2
2
  conversationHistory,
3
3
  onStartNewConversation,
4
4
  messageHistoryOpen,
5
5
  setMessageHistoryOpen
6
6
  }) => {
7
+ const character = useActiveCharacter();
8
+ const assistantName = character?.name ?? 'AI';
9
+ const chatBgImage = useChatBackgroundImage();
7
10
  return jsxs(Popover, {
8
11
  open: messageHistoryOpen,
9
12
  onOpenChange: setMessageHistoryOpen,
@@ -24,9 +27,20 @@ import {jsxs,jsx}from'react/jsx-runtime';import {MessageSquareText,ChevronUp,Che
24
27
  }), jsxs(PopoverContent, {
25
28
  align: "end",
26
29
  side: "bottom",
27
- className: "z-[200] select-none w-screen max-h-[min(90vh,calc(100vh-1rem))] p-0 mt-3 border overflow-hidden border-input/50",
28
- children: [jsx("div", {
29
- className: "border-b border-input/50 p-4",
30
+ className: `relative z-[200] select-none w-screen max-h-[min(90vh,calc(100vh-1rem))] p-0 mt-3 border overflow-hidden border-input/50 ${chatBgImage ? 'bg-popover/70' : ''}`,
31
+ children: [chatBgImage ? jsxs(Fragment, {
32
+ children: [jsx("div", {
33
+ "aria-hidden": true,
34
+ className: "pointer-events-none absolute inset-0 bg-cover bg-center",
35
+ style: {
36
+ backgroundImage: `url(${chatBgImage})`
37
+ }
38
+ }), jsx("div", {
39
+ "aria-hidden": true,
40
+ className: "pointer-events-none absolute inset-0 bg-background/30 backdrop-blur-[2px]"
41
+ })]
42
+ }) : null, jsx("div", {
43
+ className: "relative z-10 border-b border-input/50 p-4",
30
44
  children: jsxs("div", {
31
45
  className: "flex items-center justify-between",
32
46
  children: [jsxs("div", {
@@ -62,26 +76,51 @@ import {jsxs,jsx}from'react/jsx-runtime';import {MessageSquareText,ChevronUp,Che
62
76
  })]
63
77
  })
64
78
  }), jsx(ScrollArea, {
65
- className: "h-[calc(100vh-10rem)]",
79
+ className: "relative z-10 h-[calc(100vh-10rem)]",
66
80
  children: jsx("div", {
67
81
  className: "p-4 space-y-4",
68
- children: conversationHistory.sort((a, b) => b?.timestamp - a?.timestamp).map(message => jsxs("div", {
82
+ children: conversationHistory.sort((a, b) => b?.timestamp - a?.timestamp).map((message, descIdx, arr) => jsx("div", {
69
83
  className: `p-3 rounded-lg ${message.role === 'user' ? 'bg-primary/10 border-l-4 border-primary' : 'bg-muted/50'}`,
70
- children: [jsxs("div", {
71
- className: "flex items-center gap-2 mb-2",
72
- children: [jsx("span", {
73
- className: "text-xs font-medium text-muted-foreground uppercase",
74
- children: message.role === 'user' ? 'You' : 'AI'
75
- }), jsx("span", {
76
- className: "text-xs text-muted-foreground",
77
- children: new Date(message.timestamp).toLocaleTimeString([], {
78
- hour: '2-digit',
79
- minute: '2-digit'
80
- })
84
+ children: message.role === 'user' ? jsxs(Fragment, {
85
+ children: [jsxs("div", {
86
+ className: "mb-2 flex items-center gap-2",
87
+ children: [jsx("span", {
88
+ className: "text-xs font-medium uppercase text-muted-foreground",
89
+ children: "You"
90
+ }), jsx("span", {
91
+ className: "text-xs text-muted-foreground",
92
+ children: new Date(message.timestamp).toLocaleTimeString([], {
93
+ hour: '2-digit',
94
+ minute: '2-digit'
95
+ })
96
+ })]
97
+ }), jsx(Markdown, {
98
+ children: message.content
81
99
  })]
82
- }), jsx(Markdown, {
83
- children: message.content
84
- })]
100
+ }) : jsxs("div", {
101
+ className: "flex w-full gap-3",
102
+ children: [jsx(AssistantAvatar, {
103
+ avatarSrc: character?.avatarSrc ?? null,
104
+ index: arr.length - descIdx
105
+ }), jsxs("div", {
106
+ className: "min-w-0 flex-1",
107
+ children: [jsxs("div", {
108
+ className: "mb-1 flex items-baseline gap-2",
109
+ children: [jsx("span", {
110
+ className: "text-sm font-semibold text-foreground",
111
+ children: assistantName
112
+ }), jsx("span", {
113
+ className: "text-xs text-muted-foreground",
114
+ children: new Date(message.timestamp).toLocaleTimeString([], {
115
+ hour: '2-digit',
116
+ minute: '2-digit'
117
+ })
118
+ })]
119
+ }), jsx(Markdown, {
120
+ children: message.content
121
+ })]
122
+ })]
123
+ })
85
124
  }, message.id))
86
125
  })
87
126
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"MessageHistory.js","sources":["../../../../../src/components/AIToolbar/components/completion/MessageHistory.tsx"],"sourcesContent":[null],"names":[],"mappings":"uoEAQI,MAAA,cAAsB,GAAE,CAAA;qBACxB;AACA,EAAA,sBAAA;AACH,EAAA,kBAAA;AAED,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"MessageHistory.js","sources":["../../../../../src/components/AIToolbar/components/completion/MessageHistory.tsx"],"sourcesContent":[null],"names":[],"mappings":"i1EAcC,MAAA,cAAA,GAAA,CAAA;AAED,EAAA,mBAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ResultsSection.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/speech/ResultsSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAOjD,KAAK,KAAK,GAAG;IACT,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,6GAO5B,KAAK,4CAmIP,CAAC"}
1
+ {"version":3,"file":"ResultsSection.d.ts","sourceRoot":"","sources":["../../../../../src/components/AIToolbar/components/speech/ResultsSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAQjD,KAAK,KAAK,GAAG;IACT,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,gBAAgB,CAAC;IAC/B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mBAAmB,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,eAAO,MAAM,cAAc,GAAI,6GAO5B,KAAK,4CAiJP,CAAC"}
@@ -1,4 +1,4 @@
1
- import {jsxs,jsx}from'react/jsx-runtime';import {Markdown}from'../Markdown/index.js';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import {SparklesIcon,Loader2,BotIcon,HeadphonesIcon}from'lucide-react';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import {cn}from'../../../../utils/util.js';import'react';import'fast-deep-equal/react.js';import'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import {Switch}from'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {CopyButton}from'../Markdown/copy-button.js';const ResultsSection = ({
1
+ import {jsxs,jsx}from'react/jsx-runtime';import {Markdown}from'../Markdown/index.js';import'../../../../shardui/accordion.js';import'../../../../shardui/alert.js';import'../../../../shardui/alert-dialog.js';import'@radix-ui/react-aspect-ratio';import'../../../../shardui/avatar.js';import'../../../../shardui/badge.js';import'../../../../shardui/breadcrumb.js';import'../../../../shardui/button.js';import'../../../../shardui/calendar.js';import'../../../../shardui/card.js';import'../../../../shardui/carousel.js';import'../../../../shardui/chart.js';import'../../../../shardui/checkbox.js';import'@radix-ui/react-collapsible';import'../../../../shardui/command.js';import'../../../../shardui/context-menu.js';import'../../../../shardui/dialog.js';import'../../../../shardui/drawer.js';import'../../../../shardui/dropdown-menu.js';import'../../../../shardui/form.js';import'../../../../shardui/hover-card.js';import'../../../../shardui/input.js';import'../../../../shardui/input-otp.js';import'../../../../shardui/label.js';import'../../../../shardui/menubar.js';import'../../../../shardui/navigation-menu.js';import'../../../../shardui/pagination.js';import'../../../../shardui/popover.js';import'../../../../shardui/progress.js';import'../../../../shardui/radio-group.js';import {SparklesIcon,Loader2,BotIcon,HeadphonesIcon}from'lucide-react';import'react-resizable-panels';import'../../../../utils/isBrowser/index.js';import {cn}from'../../../../utils/util.js';import'react';import'fast-deep-equal/react.js';import'../../../../shardui/scroll-area.js';import'../../../../shardui/select.js';import'../../../../shardui/separator.js';import'../../../../shardui/sheet.js';import'../../../../shardui/sidebar.js';import'../../../../shardui/slider.js';import'../../../ThemeProvider/ThemeProvider.js';import'sonner';import {Switch}from'../../../../shardui/switch.js';import'../../../../shardui/table.js';import'../../../../shardui/tabs.js';import'../../../../shardui/textarea.js';import'../../../../shardui/toast.js';import'../../../../shardui/toggle.js';import'../../../../shardui/toggle-group.js';import'../../../../shardui/tooltip.js';import {CopyButton}from'../Markdown/copy-button.js';import {useActiveCharacter}from'../../hooks/useActiveCharacter.js';const ResultsSection = ({
2
2
  lastTranscription,
3
3
  lastAIResponse,
4
4
  isAIProcessing,
@@ -6,6 +6,8 @@ import {jsxs,jsx}from'react/jsx-runtime';import {Markdown}from'../Markdown/index
6
6
  conversationMode,
7
7
  setConversationMode
8
8
  }) => {
9
+ const character = useActiveCharacter();
10
+ const assistantName = character?.name ?? 'AI';
9
11
  const hasResponse = lastAIResponse || isAIProcessing;
10
12
  const hasHistory = conversation.messages.length > 2;
11
13
  if (!hasResponse && !lastTranscription) {
@@ -70,11 +72,19 @@ import {jsxs,jsx}from'react/jsx-runtime';import {Markdown}from'../Markdown/index
70
72
  className: "rounded-md bg-background/50 p-2.5",
71
73
  children: [jsxs("div", {
72
74
  className: "flex items-center gap-1.5 mb-1",
73
- children: [jsx(BotIcon, {
75
+ children: [character?.avatarSrc ? jsx("img", {
76
+ src: character.avatarSrc,
77
+ alt: "",
78
+ "aria-hidden": "true",
79
+ draggable: false,
80
+ width: 14,
81
+ height: 14,
82
+ className: "h-3.5 w-3.5 rounded-full object-cover ring-1 ring-border/60"
83
+ }) : jsx(BotIcon, {
74
84
  className: "h-3 w-3 text-muted-foreground"
75
85
  }), jsx("span", {
76
86
  className: "text-[9px] font-medium text-muted-foreground uppercase tracking-wide",
77
- children: "AI"
87
+ children: assistantName
78
88
  })]
79
89
  }), isAIProcessing && !lastAIResponse ? jsxs("div", {
80
90
  className: "flex items-center gap-2",
@@ -117,7 +127,7 @@ import {jsxs,jsx}from'react/jsx-runtime';import {Markdown}from'../Markdown/index
117
127
  className: cn('p-2 rounded-md text-[11px]', message.role === 'user' ? 'bg-primary/5 border-l-2 border-primary/30' : 'bg-background/50'),
118
128
  children: [jsx("span", {
119
129
  className: "text-[8px] font-medium text-muted-foreground uppercase",
120
- children: message.role === 'user' ? 'System' : 'AI'
130
+ children: message.role === 'user' ? 'System' : assistantName
121
131
  }), jsx("div", {
122
132
  className: "text-muted-foreground leading-relaxed mt-0.5",
123
133
  children: jsx(Markdown, {
@@ -1 +1 @@
1
- {"version":3,"file":"ResultsSection.js","sources":["../../../../../src/components/AIToolbar/components/speech/ResultsSection.tsx"],"sourcesContent":[null],"names":[],"mappings":"yoEAYI,MAAA,iBAA0B,CAAA;AAC1B,EAAA,iBAAA;EACF,cAAA;AAEF,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"ResultsSection.js","sources":["../../../../../src/components/AIToolbar/components/speech/ResultsSection.tsx"],"sourcesContent":[null],"names":[],"mappings":"4sEAcI,MAAA,cAAsB,GAAI;EAC5B,iBAAA;AAEF,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,33 @@
1
+ export interface ActiveCharacter {
2
+ /** Manifest `name` (or filesystem stem) — e.g. "Seraphina". */
3
+ name: string;
4
+ /** PNG card filename without extension — used to fetch avatar / sprites. */
5
+ fileName?: string;
6
+ /** Pre-baked `data:` URL for the avatar PNG, or null if not yet loaded. */
7
+ avatarSrc: string | null;
8
+ }
9
+ export interface CharacterListEntry {
10
+ name: string;
11
+ fileName?: string;
12
+ avatarSrc: string | null;
13
+ }
14
+ export interface CharacterPicker {
15
+ /** All installed characters with resolved avatars. */
16
+ characters: CharacterListEntry[];
17
+ /** Currently active character name, or null if none picked / none installed. */
18
+ activeName: string | null;
19
+ /** Switch the active character by name. Pass null to clear (revert to first). */
20
+ setActive: (name: string | null) => void;
21
+ }
22
+ /**
23
+ * Return the active character (name + avatar) or null when none is installed
24
+ * / we're not running under Tauri. Selection persists via localStorage and
25
+ * follows changes made by `useCharacterPicker`.
26
+ */
27
+ export declare function useActiveCharacter(): ActiveCharacter | null;
28
+ /**
29
+ * Enumerate installed characters and switch the active one. Empty list and
30
+ * `activeName === null` outside Tauri / when nothing is installed.
31
+ */
32
+ export declare function useCharacterPicker(): CharacterPicker;
33
+ //# sourceMappingURL=useActiveCharacter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActiveCharacter.d.ts","sourceRoot":"","sources":["../../../../src/components/AIToolbar/hooks/useActiveCharacter.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,eAAe;IAC5B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC5B,sDAAsD;IACtD,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,gFAAgF;IAChF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,iFAAiF;IACjF,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CAC5C;AA2GD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,IAAI,CA8D3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAqFpD"}
@@ -0,0 +1,257 @@
1
+ import*as React from'react';// Active-character lookup for the AIToolbar.
2
+ //
3
+ // Surfaces the currently-selected character (name + avatar data URL) so the
4
+ // completion popover and message history can show "Seraphina" with her avatar
5
+ // instead of a generic "AI" label. Backed by the desktop Tauri commands
6
+ // `character_list` and `character_get_avatar` (see
7
+ // `yantra-app/portable-devices/desktop/src-tauri/src/features/character_feature/`).
8
+ //
9
+ // Degrades gracefully:
10
+ // - Outside Tauri (regular browser, SSR): returns null synchronously.
11
+ // - Tauri without any character-contributing extension installed: returns null.
12
+ // - Tauri with an extension but no avatar: returns name-only (avatarSrc = null).
13
+ //
14
+ // Re-fetches on the `yantra:character-refresh` Tauri event + the
15
+ // `yantra:extensions-changed` window event (marketplace install/uninstall).
16
+ //
17
+ // When multiple characters are installed, the active one is chosen by name
18
+ // from localStorage (`yantra:active-character-name`). Use `useCharacterPicker`
19
+ // to enumerate and switch.
20
+ // constants
21
+ const CHARACTER_REFRESH_EVENT = 'yantra:character-refresh';
22
+ /**
23
+ * Window event dispatched by `yantra-app/portable-devices/desktop/src/utils/marketplaceInstall.ts`
24
+ * after the marketplace install / uninstall path triggers `character_refresh`.
25
+ * Listening here lets the avatar swap in without an app restart.
26
+ */
27
+ const EXTENSIONS_CHANGED_EVENT = 'yantra:extensions-changed';
28
+ /** Window event dispatched by `useCharacterPicker().setActive(...)` to notify subscribers. */
29
+ const ACTIVE_CHARACTER_CHANGED_EVENT = 'yantra:active-character-changed';
30
+ const ACTIVE_CHARACTER_STORAGE_KEY = 'yantra:active-character-name';
31
+ /** Manifest contribution key emitted by extensions that ship character cards. */
32
+ const CHARACTER_CONTRIBUTION = 'characters';
33
+ // helpers
34
+ function isTauri() {
35
+ if (typeof window === 'undefined') return false;
36
+ const w = window;
37
+ return Boolean(w.__TAURI_INTERNALS__ ?? w.__TAURI__);
38
+ }
39
+ async function safeInvoke(cmd, args) {
40
+ if (!isTauri()) return null;
41
+ try {
42
+ // @ts-ignore — optional dynamic import, present as a peer dep in Tauri builds.
43
+ const mod = await import('@tauri-apps/api/core');
44
+ return await mod.invoke(cmd, args);
45
+ } catch {
46
+ return null;
47
+ }
48
+ }
49
+ async function safeListen(event, cb) {
50
+ if (!isTauri()) return () => {};
51
+ try {
52
+ // @ts-ignore — see safeInvoke note.
53
+ const mod = await import('@tauri-apps/api/event');
54
+ const unlisten = await mod.listen(event, e => cb(e.payload));
55
+ return unlisten;
56
+ } catch {
57
+ return () => {};
58
+ }
59
+ }
60
+ function readStoredActiveName() {
61
+ if (typeof window === 'undefined') return null;
62
+ try {
63
+ return window.localStorage.getItem(ACTIVE_CHARACTER_STORAGE_KEY);
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+ /**
69
+ * Decide whether an `extensions-changed` event is relevant to characters.
70
+ * Detail with no `contributes` array is treated as relevant (defensive
71
+ * reload) so we never miss a real change.
72
+ */
73
+ function isCharacterRelevant(event) {
74
+ const detail = event.detail;
75
+ if (!detail || !Array.isArray(detail.contributes)) return true;
76
+ return detail.contributes.includes(CHARACTER_CONTRIBUTION);
77
+ }
78
+ function writeStoredActiveName(name) {
79
+ if (typeof window === 'undefined') return;
80
+ try {
81
+ if (name == null) window.localStorage.removeItem(ACTIVE_CHARACTER_STORAGE_KEY);else window.localStorage.setItem(ACTIVE_CHARACTER_STORAGE_KEY, name);
82
+ } catch {
83
+ // ignore quota / privacy-mode errors
84
+ }
85
+ }
86
+ async function fetchAvatarSrc(fileName) {
87
+ if (!fileName) return null;
88
+ const dto = await safeInvoke('character_get_avatar', {
89
+ fileName
90
+ });
91
+ if (!dto || !dto.base64 || dto.base64.length <= 100) return null;
92
+ return `data:${dto.mime_type || 'image/png'};base64,${dto.base64}`;
93
+ }
94
+ function pickActive(list, desiredName) {
95
+ if (list.length === 0) return null;
96
+ if (desiredName) {
97
+ const match = list.find(c => c.name === desiredName);
98
+ if (match) return match;
99
+ }
100
+ return list[0];
101
+ }
102
+ // main API
103
+ /**
104
+ * Return the active character (name + avatar) or null when none is installed
105
+ * / we're not running under Tauri. Selection persists via localStorage and
106
+ * follows changes made by `useCharacterPicker`.
107
+ */
108
+ function useActiveCharacter() {
109
+ const [list, setList] = React.useState([]);
110
+ const [activeName, setActiveName] = React.useState(() => readStoredActiveName());
111
+ const [avatarSrc, setAvatarSrc] = React.useState(null);
112
+ const reload = React.useCallback(async () => {
113
+ const next = await safeInvoke('character_list', {
114
+ shallow: false
115
+ });
116
+ setList(next ?? []);
117
+ }, []);
118
+ React.useEffect(() => {
119
+ void reload();
120
+ }, [reload]);
121
+ React.useEffect(() => {
122
+ let unlisten = null;
123
+ let cancelled = false;
124
+ safeListen(CHARACTER_REFRESH_EVENT, () => {
125
+ void reload();
126
+ }).then(fn => {
127
+ if (cancelled) fn();else unlisten = fn;
128
+ });
129
+ function onExtensionsChanged(e) {
130
+ if (!isCharacterRelevant(e)) return;
131
+ void reload();
132
+ }
133
+ function onActiveChanged() {
134
+ setActiveName(readStoredActiveName());
135
+ }
136
+ if (typeof window !== 'undefined') {
137
+ window.addEventListener(EXTENSIONS_CHANGED_EVENT, onExtensionsChanged);
138
+ window.addEventListener(ACTIVE_CHARACTER_CHANGED_EVENT, onActiveChanged);
139
+ }
140
+ return () => {
141
+ cancelled = true;
142
+ if (unlisten) unlisten();
143
+ if (typeof window !== 'undefined') {
144
+ window.removeEventListener(EXTENSIONS_CHANGED_EVENT, onExtensionsChanged);
145
+ window.removeEventListener(ACTIVE_CHARACTER_CHANGED_EVENT, onActiveChanged);
146
+ }
147
+ };
148
+ }, [reload]);
149
+ const active = React.useMemo(() => pickActive(list, activeName), [list, activeName]);
150
+ React.useEffect(() => {
151
+ if (!active?.file_name) {
152
+ setAvatarSrc(null);
153
+ return;
154
+ }
155
+ let alive = true;
156
+ fetchAvatarSrc(active.file_name).then(src => {
157
+ if (alive) setAvatarSrc(src);
158
+ });
159
+ return () => {
160
+ alive = false;
161
+ };
162
+ }, [active?.file_name]);
163
+ if (!active) return null;
164
+ return {
165
+ name: active.name,
166
+ fileName: active.file_name,
167
+ avatarSrc
168
+ };
169
+ }
170
+ /**
171
+ * Enumerate installed characters and switch the active one. Empty list and
172
+ * `activeName === null` outside Tauri / when nothing is installed.
173
+ */
174
+ function useCharacterPicker() {
175
+ const [list, setList] = React.useState([]);
176
+ const [avatars, setAvatars] = React.useState({});
177
+ const [activeName, setActiveNameState] = React.useState(() => readStoredActiveName());
178
+ const reload = React.useCallback(async () => {
179
+ const next = await safeInvoke('character_list', {
180
+ shallow: false
181
+ });
182
+ setList(next ?? []);
183
+ }, []);
184
+ React.useEffect(() => {
185
+ void reload();
186
+ }, [reload]);
187
+ React.useEffect(() => {
188
+ let unlisten = null;
189
+ let cancelled = false;
190
+ safeListen(CHARACTER_REFRESH_EVENT, () => {
191
+ void reload();
192
+ }).then(fn => {
193
+ if (cancelled) fn();else unlisten = fn;
194
+ });
195
+ function onExtensionsChanged(e) {
196
+ if (!isCharacterRelevant(e)) return;
197
+ void reload();
198
+ }
199
+ function onActiveChanged() {
200
+ setActiveNameState(readStoredActiveName());
201
+ }
202
+ if (typeof window !== 'undefined') {
203
+ window.addEventListener(EXTENSIONS_CHANGED_EVENT, onExtensionsChanged);
204
+ window.addEventListener(ACTIVE_CHARACTER_CHANGED_EVENT, onActiveChanged);
205
+ }
206
+ return () => {
207
+ cancelled = true;
208
+ if (unlisten) unlisten();
209
+ if (typeof window !== 'undefined') {
210
+ window.removeEventListener(EXTENSIONS_CHANGED_EVENT, onExtensionsChanged);
211
+ window.removeEventListener(ACTIVE_CHARACTER_CHANGED_EVENT, onActiveChanged);
212
+ }
213
+ };
214
+ }, [reload]);
215
+ // Resolve avatars for every character in the list (cached by file_name).
216
+ React.useEffect(() => {
217
+ let alive = true;
218
+ async function loadAll() {
219
+ const pending = list.filter(c => c.file_name && !(c.file_name in avatars));
220
+ if (pending.length === 0) return;
221
+ const entries = await Promise.all(pending.map(async c => [c.file_name, await fetchAvatarSrc(c.file_name)]));
222
+ if (!alive) return;
223
+ setAvatars(prev => {
224
+ const next = {
225
+ ...prev
226
+ };
227
+ for (const [key, src] of entries) next[key] = src;
228
+ return next;
229
+ });
230
+ }
231
+ void loadAll();
232
+ return () => {
233
+ alive = false;
234
+ };
235
+ }, [list, avatars]);
236
+ const characters = React.useMemo(() => list.map(c => ({
237
+ name: c.name,
238
+ fileName: c.file_name,
239
+ avatarSrc: c.file_name ? avatars[c.file_name] ?? null : null
240
+ })), [list, avatars]);
241
+ const setActive = React.useCallback(name => {
242
+ writeStoredActiveName(name);
243
+ setActiveNameState(name);
244
+ if (typeof window !== 'undefined') {
245
+ window.dispatchEvent(new CustomEvent(ACTIVE_CHARACTER_CHANGED_EVENT, {
246
+ detail: {
247
+ name
248
+ }
249
+ }));
250
+ }
251
+ }, []);
252
+ return {
253
+ characters,
254
+ activeName,
255
+ setActive
256
+ };
257
+ }export{useActiveCharacter,useCharacterPicker};//# sourceMappingURL=useActiveCharacter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useActiveCharacter.js","sources":["../../../../src/components/AIToolbar/hooks/useActiveCharacter.ts"],"sourcesContent":[null],"names":[],"mappings":"4BAwBA;;;;;;AAMI;AACH;AAED;;;AAGI;AACH;AAED;;;;AAII;;AAGH;AA2GD,MAAA,uBAAA,GAAA,0BAAA;;;;AAIG;AACH;AAgEA,MAAA,wBAAA,GAAA,2BAAA;;;AAGG,MAAA,4BAAA,GAAA,8BAAA;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Returns the current chat-background image as a `data:` URL, or `null` when
3
+ * no wallpaper is selected / we are not running under Tauri.
4
+ *
5
+ * Re-fetches whenever the user picks a different wallpaper, regardless of
6
+ * which webview the picker lives in.
7
+ */
8
+ export declare function useChatBackgroundImage(): string | null;
9
+ export default useChatBackgroundImage;
10
+ //# sourceMappingURL=useChatBackgroundImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatBackgroundImage.d.ts","sourceRoot":"","sources":["../../../../src/components/AIToolbar/hooks/useChatBackgroundImage.ts"],"names":[],"mappings":"AA4EA;;;;;;GAMG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,GAAG,IAAI,CA+DtD;AAED,eAAe,sBAAsB,CAAC"}
@@ -0,0 +1,126 @@
1
+ import*as React from'react';// Chat-background image lookup for AIToolbar surfaces.
2
+ //
3
+ // The desktop app lets users pick a wallpaper that paints behind the `/chat`
4
+ // route. The toolbar's Conversation popover and Message History popover render
5
+ // in Radix portals (outside the chat tree), so the wallpaper does not bleed
6
+ // through to them by default. This hook re-subscribes to the same contract
7
+ // `yantra-app/packages-modules/account/browser/src/hooks/useChatBackground.tsx`
8
+ // already publishes:
9
+ //
10
+ // - localStorage key `yantra:chat-background:selected-id` → currently-selected background ID
11
+ // - cross-webview event `yantra:chat-background-change` → fan-out when the picker mutates the selection
12
+ // - Tauri command `background_get` { id } → returns { mime_type, base64 }
13
+ //
14
+ // Reusing the same key/event lets the popovers stay in lock-step with the
15
+ // `/chat` overlay without yantra-app having to inject anything through props.
16
+ //
17
+ // Degrades gracefully:
18
+ // - Outside Tauri (regular browser, SSR): returns null synchronously.
19
+ // - No selection / unknown ID: returns null.
20
+ // - Selection changes mid-session: re-fetches automatically.
21
+ // constants
22
+ const SELECTED_BG_STORAGE_KEY = 'yantra:chat-background:selected-id';
23
+ const BG_CHANGE_EVENT = 'yantra:chat-background-change';
24
+ // helpers
25
+ function isTauri() {
26
+ if (typeof window === 'undefined') return false;
27
+ const w = window;
28
+ return Boolean(w.__TAURI_INTERNALS__ ?? w.__TAURI__);
29
+ }
30
+ async function safeInvoke(cmd, args) {
31
+ if (!isTauri()) return null;
32
+ try {
33
+ // @ts-ignore — optional dynamic import, present as a peer dep in Tauri builds.
34
+ const mod = await import('@tauri-apps/api/core');
35
+ return await mod.invoke(cmd, args);
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ async function safeListen(event, cb) {
41
+ if (!isTauri()) return () => {};
42
+ try {
43
+ // @ts-ignore — see safeInvoke note.
44
+ const mod = await import('@tauri-apps/api/event');
45
+ const unlisten = await mod.listen(event, e => cb(e.payload));
46
+ return unlisten;
47
+ } catch {
48
+ return () => {};
49
+ }
50
+ }
51
+ function readSelectedId() {
52
+ if (typeof window === 'undefined') return null;
53
+ try {
54
+ return window.localStorage.getItem(SELECTED_BG_STORAGE_KEY);
55
+ } catch {
56
+ return null;
57
+ }
58
+ }
59
+ // main API
60
+ /**
61
+ * Returns the current chat-background image as a `data:` URL, or `null` when
62
+ * no wallpaper is selected / we are not running under Tauri.
63
+ *
64
+ * Re-fetches whenever the user picks a different wallpaper, regardless of
65
+ * which webview the picker lives in.
66
+ */
67
+ function useChatBackgroundImage() {
68
+ const [selectedId, setSelectedId] = React.useState(() => readSelectedId());
69
+ const [imageUrl, setImageUrl] = React.useState(null);
70
+ // Subscribe to selection changes from three possible sources:
71
+ // (a) same-window `dispatchEvent` (picker on this webview).
72
+ // (b) cross-tab `storage` event (rare in the Tauri shell but free).
73
+ // (c) Tauri `listen` (picker on the *other* webview — desktop-bar vs dashboard).
74
+ React.useEffect(() => {
75
+ function localHandler(e) {
76
+ const detail = e.detail;
77
+ setSelectedId(detail?.id ?? null);
78
+ }
79
+ function storageHandler(e) {
80
+ if (e.key !== SELECTED_BG_STORAGE_KEY) return;
81
+ setSelectedId(e.newValue);
82
+ }
83
+ if (typeof window !== 'undefined') {
84
+ window.addEventListener(BG_CHANGE_EVENT, localHandler);
85
+ window.addEventListener('storage', storageHandler);
86
+ }
87
+ let unlisten = null;
88
+ let cancelled = false;
89
+ safeListen(BG_CHANGE_EVENT, payload => {
90
+ setSelectedId(payload?.id ?? null);
91
+ }).then(fn => {
92
+ if (cancelled) fn();else unlisten = fn;
93
+ });
94
+ return () => {
95
+ cancelled = true;
96
+ if (typeof window !== 'undefined') {
97
+ window.removeEventListener(BG_CHANGE_EVENT, localHandler);
98
+ window.removeEventListener('storage', storageHandler);
99
+ }
100
+ if (unlisten) unlisten();
101
+ };
102
+ }, []);
103
+ // Resolve ID → data URL. Tauri returns base64 — we only build the data URL
104
+ // once per ID change to avoid churning the popover background.
105
+ React.useEffect(() => {
106
+ if (!selectedId) {
107
+ setImageUrl(null);
108
+ return;
109
+ }
110
+ let alive = true;
111
+ safeInvoke('background_get', {
112
+ id: selectedId
113
+ }).then(dto => {
114
+ if (!alive) return;
115
+ if (dto && dto.base64) {
116
+ setImageUrl(`data:${dto.mime_type || 'image/png'};base64,${dto.base64}`);
117
+ } else {
118
+ setImageUrl(null);
119
+ }
120
+ });
121
+ return () => {
122
+ alive = false;
123
+ };
124
+ }, [selectedId]);
125
+ return imageUrl;
126
+ }export{useChatBackgroundImage};//# sourceMappingURL=useChatBackgroundImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useChatBackgroundImage.js","sources":["../../../../src/components/AIToolbar/hooks/useChatBackgroundImage.ts"],"sourcesContent":[null],"names":[],"mappings":"4BA4EA;;;;;;AAMG;AACH;AAiEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@admin-layout/tailwind-ui",
3
- "version": "12.2.4-alpha.55",
3
+ "version": "12.2.4-alpha.57",
4
4
  "description": "Sample core for higher packages to depend on",
5
5
  "license": "ISC",
6
6
  "author": "CDMBase LLC",
@@ -96,5 +96,5 @@
96
96
  "typescript": {
97
97
  "definition": "lib/index.d.ts"
98
98
  },
99
- "gitHead": "eae3d37b08da2262ab728666c34176bd1798262e"
99
+ "gitHead": "134adf9207d6cad519b1fd25f4c39615b3682354"
100
100
  }