Feat: share GPT (#502)
* may share GPT * add ai assistant from shared gpt
This commit is contained in:
@@ -454,6 +454,10 @@
|
||||
"shareStory": "Share story",
|
||||
"sharedStory": "Shared a story",
|
||||
"areYouSureToShareThisStoryToCommunity": "Are you sure to share this story to community?",
|
||||
"shareGpt": "Share GPT",
|
||||
"sharedGpt": "Shared a GPT",
|
||||
"areYouSureToShareThisGptToCommunity": "Are you sure to share this GPT to community?",
|
||||
"saveAiAssistant": "Save this AI assistant",
|
||||
"addToLibary": "Add to library",
|
||||
"areYouSureToAddThisVideoToYourLibrary": "Are you sure to add this video to library?",
|
||||
"areYouSureToAddThisAudioToYourLibrary": "Are you sure to add this audio to library?",
|
||||
|
||||
@@ -453,6 +453,10 @@
|
||||
"shareStory": "分享文章",
|
||||
"sharedStory": "分享了一篇文章",
|
||||
"areYouSureToShareThisStoryToCommunity": "您确定要分享此文章到社区吗?",
|
||||
"shareGpt": "分享智能助手",
|
||||
"sharedGpt": "分享了一个智能助手",
|
||||
"areYouSureToShareThisGptToCommunity": "您确定要将这个智能助手分享到社区吗?",
|
||||
"saveAiAssistant": "保存智能助手",
|
||||
"addToLibary": "添加到资源库",
|
||||
"areYouSureToAddThisVideoToYourLibrary": "您确定要添加此视频到资料库吗?",
|
||||
"areYouSureToAddThisAudioToYourLibrary": "您确定要添加此音频到资料库吗?",
|
||||
|
||||
@@ -28,13 +28,14 @@ import {
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
Textarea,
|
||||
toast,
|
||||
} from "@renderer/components/ui";
|
||||
import { useState, useEffect, useContext } from "react";
|
||||
import {
|
||||
AppSettingsProviderContext,
|
||||
AISettingsProviderContext,
|
||||
} from "@renderer/context";
|
||||
import { LoaderIcon } from "lucide-react";
|
||||
import { LoaderIcon, Share2Icon } from "lucide-react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const conversationFormSchema = z.object({
|
||||
@@ -125,20 +126,20 @@ export const ConversationForm = (props: {
|
||||
// @ts-ignore
|
||||
values: conversation?.id
|
||||
? {
|
||||
name: conversation.name,
|
||||
engine: conversation.engine,
|
||||
configuration: {
|
||||
type: conversation.configuration.type || "gpt",
|
||||
...conversation.configuration,
|
||||
},
|
||||
}
|
||||
: {
|
||||
name: defaultConfig.name,
|
||||
engine: defaultConfig.engine,
|
||||
configuration: {
|
||||
...defaultConfig.configuration,
|
||||
},
|
||||
name: conversation.name,
|
||||
engine: conversation.engine,
|
||||
configuration: {
|
||||
type: conversation.configuration.type || "gpt",
|
||||
...conversation.configuration,
|
||||
},
|
||||
}
|
||||
: {
|
||||
name: defaultConfig.name,
|
||||
engine: defaultConfig.engine,
|
||||
configuration: {
|
||||
...defaultConfig.configuration,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit = async (data: z.infer<typeof conversationFormSchema>) => {
|
||||
@@ -203,8 +204,11 @@ export const ConversationForm = (props: {
|
||||
className="h-full flex flex-col pt-6"
|
||||
data-testid="conversation-form"
|
||||
>
|
||||
<div className="mb-4 px-6 text-lg font-bold">
|
||||
{conversation.id ? t("editConversation") : t("startConversation")}
|
||||
<div className="mb-4 px-6 flex items-center space-x-4">
|
||||
<div className="text-lg font-bold">
|
||||
{conversation.id ? t("editConversation") : t("startConversation")}
|
||||
</div>
|
||||
<GPTShareButton conversation={conversation} />
|
||||
</div>
|
||||
<ScrollArea className="flex-1 px-4">
|
||||
<div className="space-y-4 px-2 mb-6">
|
||||
@@ -342,160 +346,160 @@ export const ConversationForm = (props: {
|
||||
{LLM_PROVIDERS[form.watch("engine")]?.configurable.includes(
|
||||
"temperature"
|
||||
) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.temperature"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.temperature")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.1"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
field.onChange(
|
||||
event.target.value
|
||||
? parseFloat(event.target.value)
|
||||
: 0.0
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.temperatureDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.temperature"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.temperature")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.1"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
field.onChange(
|
||||
event.target.value
|
||||
? parseFloat(event.target.value)
|
||||
: 0.0
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.temperatureDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{LLM_PROVIDERS[form.watch("engine")]?.configurable.includes(
|
||||
"maxTokens"
|
||||
) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.maxTokens"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.maxTokens")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
if (!event.target.value) return;
|
||||
field.onChange(parseInt(event.target.value));
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.maxTokensDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.maxTokens"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.maxTokens")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="0"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
if (!event.target.value) return;
|
||||
field.onChange(parseInt(event.target.value));
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.maxTokensDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{LLM_PROVIDERS[form.watch("engine")]?.configurable.includes(
|
||||
"presencePenalty"
|
||||
) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.presencePenalty"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.presencePenalty")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="-2"
|
||||
step="0.1"
|
||||
max="2"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
if (!event.target.value) return;
|
||||
field.onChange(parseInt(event.target.value));
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.presencePenaltyDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.presencePenalty"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.presencePenalty")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="-2"
|
||||
step="0.1"
|
||||
max="2"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
if (!event.target.value) return;
|
||||
field.onChange(parseInt(event.target.value));
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.presencePenaltyDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{LLM_PROVIDERS[form.watch("engine")]?.configurable.includes(
|
||||
"frequencyPenalty"
|
||||
) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.frequencyPenalty"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.frequencyPenalty")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="-2"
|
||||
step="0.1"
|
||||
max="2"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
if (!event.target.value) return;
|
||||
field.onChange(parseInt(event.target.value));
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.frequencyPenaltyDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.frequencyPenalty"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.frequencyPenalty")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="-2"
|
||||
step="0.1"
|
||||
max="2"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
if (!event.target.value) return;
|
||||
field.onChange(parseInt(event.target.value));
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.frequencyPenaltyDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{LLM_PROVIDERS[form.watch("engine")]?.configurable.includes(
|
||||
"numberOfChoices"
|
||||
) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.numberOfChoices"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.numberOfChoices")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
step="1.0"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
field.onChange(
|
||||
event.target.value
|
||||
? parseInt(event.target.value)
|
||||
: 1.0
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.numberOfChoicesDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.numberOfChoices"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.numberOfChoices")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
step="1.0"
|
||||
value={field.value}
|
||||
onChange={(event) => {
|
||||
field.onChange(
|
||||
event.target.value
|
||||
? parseInt(event.target.value)
|
||||
: 1.0
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<FormDescription>
|
||||
{t("models.conversation.numberOfChoicesDescription")}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -530,25 +534,25 @@ export const ConversationForm = (props: {
|
||||
{LLM_PROVIDERS[form.watch("engine")]?.configurable.includes(
|
||||
"baseUrl"
|
||||
) && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.baseUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.baseUrl")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t(
|
||||
"models.conversation.baseUrlDescription"
|
||||
)}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.baseUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
{t("models.conversation.baseUrl")}
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t(
|
||||
"models.conversation.baseUrlDescription"
|
||||
)}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -584,95 +588,95 @@ export const ConversationForm = (props: {
|
||||
{TTS_PROVIDERS[
|
||||
form.watch("configuration.tts.engine")
|
||||
]?.configurable.includes("model") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.model"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsModel")}</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("selectTtsModel")} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{(
|
||||
TTS_PROVIDERS[form.watch("configuration.tts.engine")]
|
||||
?.models || []
|
||||
).map((model: string) => (
|
||||
<SelectItem key={model} value={model}>
|
||||
{model}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.model"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsModel")}</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("selectTtsModel")} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{(
|
||||
TTS_PROVIDERS[form.watch("configuration.tts.engine")]
|
||||
?.models || []
|
||||
).map((model: string) => (
|
||||
<SelectItem key={model} value={model}>
|
||||
{model}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{TTS_PROVIDERS[
|
||||
form.watch("configuration.tts.engine")
|
||||
]?.configurable.includes("voice") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.voice"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsVoice")}</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("selectTtsVoice")} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{(
|
||||
TTS_PROVIDERS[form.watch("configuration.tts.engine")]
|
||||
?.voices || []
|
||||
).map((voice: string) => (
|
||||
<SelectItem key={voice} value={voice}>
|
||||
<span className="capitalize">{voice}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.voice"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsVoice")}</FormLabel>
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder={t("selectTtsVoice")} />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{(
|
||||
TTS_PROVIDERS[form.watch("configuration.tts.engine")]
|
||||
?.voices || []
|
||||
).map((voice: string) => (
|
||||
<SelectItem key={voice} value={voice}>
|
||||
<span className="capitalize">{voice}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{TTS_PROVIDERS[
|
||||
form.watch("configuration.tts.engine")
|
||||
]?.configurable.includes("baseUrl") && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.baseUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsBaseUrl")}</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t(
|
||||
"models.conversation.ttsBaseUrlDescription"
|
||||
)}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="configuration.tts.baseUrl"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t("models.conversation.ttsBaseUrl")}</FormLabel>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder={t(
|
||||
"models.conversation.ttsBaseUrlDescription"
|
||||
)}
|
||||
/>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
@@ -838,3 +842,80 @@ export const TTS_PROVIDERS: { [key: string]: any } = {
|
||||
configurable: ["model", "voice", "baseUrl"],
|
||||
},
|
||||
};
|
||||
|
||||
const GPTShareButton = (props: {
|
||||
conversation: Partial<ConversationType>;
|
||||
}) => {
|
||||
const { conversation } = props;
|
||||
const { webApi } = useContext(AppSettingsProviderContext);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleShare = () => {
|
||||
const { configuration } = conversation;
|
||||
delete configuration.baseUrl
|
||||
delete configuration?.tts?.baseUrl
|
||||
|
||||
if (!configuration.roleDefinition) {
|
||||
toast.error('shareFailed');
|
||||
return;
|
||||
}
|
||||
|
||||
webApi
|
||||
.createPost({
|
||||
metadata: {
|
||||
type: "gpt",
|
||||
content: {
|
||||
name: conversation.name,
|
||||
engine: conversation.engine,
|
||||
configuration,
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(t("sharedSuccessfully"), {
|
||||
description: t("sharedGpt"),
|
||||
action: {
|
||||
label: t("view"),
|
||||
onClick: () => {
|
||||
navigate("/community");
|
||||
},
|
||||
},
|
||||
actionButtonStyle: {
|
||||
backgroundColor: "var(--primary)",
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(t("shareFailed"), { description: err.message });
|
||||
});
|
||||
}
|
||||
|
||||
if (!conversation.id) return null;
|
||||
if (conversation.type !== "gpt") return null;
|
||||
|
||||
return (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button variant="link" size="icon" className="rounded-full p-0 w-6 h-6">
|
||||
<Share2Icon className="w-4 h-4 text-muted-foreground" />
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{t("shareGpt")}</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t("areYouSureToShareThisGptToCommunity")}
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>{t("cancel")}</AlertDialogCancel>
|
||||
<AlertDialogAction asChild>
|
||||
<Button variant="default" onClick={handleShare}>
|
||||
{t("share")}
|
||||
</Button>
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
@@ -143,6 +143,7 @@ export const PostActions = (props: { post: PostType }) => {
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{post.metadata?.type === "prompt" && (
|
||||
<ConversationShortcuts
|
||||
open={asking}
|
||||
@@ -166,6 +167,47 @@ export const PostActions = (props: { post: PostType }) => {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
|
||||
{post.metadata?.type === "gpt" && (
|
||||
<>
|
||||
<Button
|
||||
data-tooltip-id="global-tooltip"
|
||||
data-tooltip-content={t("copy")}
|
||||
data-tooltip-place="bottom"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="px-1.5 rounded-full"
|
||||
>
|
||||
{copied ? (
|
||||
<CheckIcon className="w-5 h-5 text-green-500" />
|
||||
) : (
|
||||
<CopyPlusIcon
|
||||
className="w-5 h-5 text-muted-foreground hover:text-primary"
|
||||
onClick={() => {
|
||||
copyToClipboard((post.metadata.content as { [key: string]: any }).configuration.roleDefinition as string);
|
||||
setCopied(true);
|
||||
setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 3000);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
|
||||
<Link to={`/conversations?postId=${post.id}`}>
|
||||
<Button
|
||||
data-tooltip-id="global-tooltip"
|
||||
data-tooltip-content={t("saveAiAssistant")}
|
||||
data-tooltip-place="bottom"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="px-1.5 rounded-full"
|
||||
>
|
||||
<PlusCircleIcon className="w-5 h-5 text-muted-foreground hover:text-primary" />
|
||||
</Button>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{aiReplies.length > 0 && <AIReplies replies={aiReplies} />}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { formatDateTime } from "@renderer/lib/utils";
|
||||
import { t } from "i18next";
|
||||
import Markdown from "react-markdown";
|
||||
import { Link } from "react-router-dom";
|
||||
import { BotIcon } from "lucide-react";
|
||||
|
||||
export const PostCard = (props: {
|
||||
post: PostType;
|
||||
@@ -50,12 +51,30 @@ export const PostCard = (props: {
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{t("sharedPrompt")}
|
||||
</div>
|
||||
<Markdown className="prose prose-slate prose-pre:whitespace-normal select-text">
|
||||
<Markdown className="prose prose-slate prose-pre:whitespace-pre-line select-text">
|
||||
{"```prompt\n" + post.metadata.content + "\n```"}
|
||||
</Markdown>
|
||||
</>
|
||||
)}
|
||||
|
||||
{post.metadata?.type === "gpt" && (
|
||||
<>
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{t("sharedGpt")}
|
||||
</div>
|
||||
<div className="text-sm">
|
||||
{t('models.conversation.roleDefinition')}:
|
||||
</div>
|
||||
<div className="prose prose-stone prose-pre:whitespace-pre-line select-text">
|
||||
<blockquote className="not-italic whitespace-pre-line">
|
||||
<Markdown>
|
||||
{(post.metadata.content as { [key: string]: any }).configuration?.roleDefinition}
|
||||
</Markdown>
|
||||
</blockquote>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{post.targetType == "Medium" && (
|
||||
<PostMedium medium={post.target as MediumType} />
|
||||
)}
|
||||
|
||||
@@ -213,7 +213,7 @@ export default () => {
|
||||
inputRef.current.focus();
|
||||
|
||||
return () => {
|
||||
inputRef.current?.removeEventListener("keypress", () => {});
|
||||
inputRef.current?.removeEventListener("keypress", () => { });
|
||||
autosize.destroy(inputRef.current);
|
||||
};
|
||||
}, [id, inputRef.current]);
|
||||
@@ -319,7 +319,7 @@ export default () => {
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
placeholder={t("pressEnterToSend")}
|
||||
data-testid="conversation-page-input"
|
||||
className="text-base px-4 py-0 shadow-none border-none focus-visible:outline-0 focus-visible:ring-0 border-none bg-muted focus:bg-background min-h-[1rem] max-h-[70vh] scrollbar-thin scrollbar-thumb-sky-500 !overflow-x-hidden"
|
||||
className="text-base px-4 py-0 shadow-none focus-visible:outline-0 focus-visible:ring-0 border-none bg-muted focus:bg-background min-h-[1rem] max-h-[70vh] scrollbar-thin scrollbar-thumb-sky-500 !overflow-x-hidden"
|
||||
/>
|
||||
<div className="h-12 py-1">
|
||||
<Button
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { ConversationForm } from "@renderer/components";
|
||||
import { useState, useEffect, useContext, useReducer } from "react";
|
||||
import { ChevronLeftIcon, MessageCircleIcon, SpeechIcon } from "lucide-react";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import {
|
||||
DbProviderContext,
|
||||
AppSettingsProviderContext,
|
||||
@@ -24,10 +24,11 @@ import dayjs from "dayjs";
|
||||
import { CONVERSATION_PRESETS } from "@/constants";
|
||||
|
||||
export default () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [creating, setCreating] = useState<boolean>(false);
|
||||
const [preset, setPreset] = useState<any>({});
|
||||
const { addDblistener, removeDbListener } = useContext(DbProviderContext);
|
||||
const { EnjoyApp } = useContext(AppSettingsProviderContext);
|
||||
const { EnjoyApp, webApi } = useContext(AppSettingsProviderContext);
|
||||
const { currentEngine } = useContext(AISettingsProviderContext);
|
||||
const [conversations, dispatchConversations] = useReducer(
|
||||
conversationsReducer,
|
||||
@@ -44,6 +45,21 @@ export default () => {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const postId = searchParams.get('postId');
|
||||
if (!postId) return;
|
||||
|
||||
webApi.post(postId).then((post) => {
|
||||
const preset: any = post.metadata.content;
|
||||
if (!preset?.configuration?.roleDefinition) {
|
||||
return;
|
||||
}
|
||||
|
||||
setPreset(preset);
|
||||
setCreating(true);
|
||||
})
|
||||
}, [searchParams.get('postId')])
|
||||
|
||||
const fetchConversations = async () => {
|
||||
const _conversations = await EnjoyApp.conversations.findAll({});
|
||||
|
||||
|
||||
2
enjoy/src/types/post.d.ts
vendored
2
enjoy/src/types/post.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
type PostType = {
|
||||
id: string;
|
||||
metadata: {
|
||||
type: 'text' | 'prompt' | 'llm_configuration';
|
||||
type: "text" | "prompt" | "gpt";
|
||||
content:
|
||||
| string
|
||||
| {
|
||||
|
||||
Reference in New Issue
Block a user