@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.
- package/lib/components/AIToolbar/components/completion/AssistantAvatar.d.ts +12 -0
- package/lib/components/AIToolbar/components/completion/AssistantAvatar.d.ts.map +1 -0
- package/lib/components/AIToolbar/components/completion/AssistantAvatar.js +36 -0
- package/lib/components/AIToolbar/components/completion/AssistantAvatar.js.map +1 -0
- package/lib/components/AIToolbar/components/completion/AssistantSenderBadge.d.ts +15 -0
- package/lib/components/AIToolbar/components/completion/AssistantSenderBadge.d.ts.map +1 -0
- package/lib/components/AIToolbar/components/completion/CharacterPicker.d.ts +3 -0
- package/lib/components/AIToolbar/components/completion/CharacterPicker.d.ts.map +1 -0
- package/lib/components/AIToolbar/components/completion/CharacterPicker.js +90 -0
- package/lib/components/AIToolbar/components/completion/CharacterPicker.js.map +1 -0
- package/lib/components/AIToolbar/components/completion/Input.d.ts.map +1 -1
- package/lib/components/AIToolbar/components/completion/Input.js +103 -53
- package/lib/components/AIToolbar/components/completion/Input.js.map +1 -1
- package/lib/components/AIToolbar/components/completion/MessageHistory.d.ts.map +1 -1
- package/lib/components/AIToolbar/components/completion/MessageHistory.js +59 -20
- package/lib/components/AIToolbar/components/completion/MessageHistory.js.map +1 -1
- package/lib/components/AIToolbar/components/speech/ResultsSection.d.ts.map +1 -1
- package/lib/components/AIToolbar/components/speech/ResultsSection.js +14 -4
- package/lib/components/AIToolbar/components/speech/ResultsSection.js.map +1 -1
- package/lib/components/AIToolbar/hooks/useActiveCharacter.d.ts +33 -0
- package/lib/components/AIToolbar/hooks/useActiveCharacter.d.ts.map +1 -0
- package/lib/components/AIToolbar/hooks/useActiveCharacter.js +257 -0
- package/lib/components/AIToolbar/hooks/useActiveCharacter.js.map +1 -0
- package/lib/components/AIToolbar/hooks/useChatBackgroundImage.d.ts +10 -0
- package/lib/components/AIToolbar/hooks/useChatBackgroundImage.d.ts.map +1 -0
- package/lib/components/AIToolbar/hooks/useChatBackgroundImage.js +126 -0
- package/lib/components/AIToolbar/hooks/useChatBackgroundImage.js.map +1 -0
- 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 @@
|
|
|
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;
|
|
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}` :
|
|
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(
|
|
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
|
|
81
|
-
|
|
82
|
-
|
|
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-
|
|
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:
|
|
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' : '
|
|
145
|
-
children:
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
})
|
|
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:
|
|
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:
|
|
170
|
-
className: "
|
|
171
|
-
children: [jsx(
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}),
|
|
175
|
-
className: "
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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;
|
|
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:
|
|
28
|
-
children: [
|
|
29
|
-
|
|
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 =>
|
|
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:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
})
|
|
83
|
-
|
|
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":"
|
|
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;
|
|
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(
|
|
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:
|
|
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' :
|
|
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":"
|
|
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.
|
|
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": "
|
|
99
|
+
"gitHead": "134adf9207d6cad519b1fd25f4c39615b3682354"
|
|
100
100
|
}
|