rcgit

/ karakeep

Commit 03e938a9

SHA 03e938a903f520ff4c6976d873355a0f44278a9e
Author MohamedBassem <me at mbassem dot com>
Author Date 2024-10-05 15:17 +0000
Committer MohamedBassem <me at mbassem dot com>
Commit Date 2024-10-05 17:15 +0000
Parent(s) 1f7da9ae38a6 (diff)
Tree c78a33599035

patch snapshot

fix(web): Simplify the language for inference settings
File + - Graph
A apps/web/app/dashboard/settings/ai/page.tsx +325 -0
M apps/web/app/dashboard/settings/page.tsx +5 -5
D apps/web/app/dashboard/settings/prompts/page.tsx +0 -325
3 file(s) changed, 330 insertions(+), 330 deletions(-)

apps/web/app/dashboard/settings/ai/page.tsx

diff --git a/apps/web/app/dashboard/settings/ai/page.tsx b/apps/web/app/dashboard/settings/ai/page.tsx
new file mode 100644
index 00000000..009919a0
--- /dev/null
+++ b/apps/web/app/dashboard/settings/ai/page.tsx
@@ -0,0 +1,325 @@
+"use client";
+
+import { ActionButton } from "@/components/ui/action-button";
+import {
+  Form,
+  FormControl,
+  FormField,
+  FormItem,
+  FormMessage,
+} from "@/components/ui/form";
+import { FullPageSpinner } from "@/components/ui/full-page-spinner";
+import { Input } from "@/components/ui/input";
+import {
+  Select,
+  SelectContent,
+  SelectGroup,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from "@/components/ui/select";
+import { toast } from "@/components/ui/use-toast";
+import { useClientConfig } from "@/lib/clientConfig";
+import { api } from "@/lib/trpc";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { Plus, Save, Trash2 } from "lucide-react";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { buildImagePrompt, buildTextPrompt } from "@hoarder/shared/prompts";
+import {
+  zNewPromptSchema,
+  ZPrompt,
+  zUpdatePromptSchema,
+} from "@hoarder/shared/types/prompts";
+
+export function PromptEditor() {
+  const apiUtils = api.useUtils();
+
+  const form = useForm<z.infer<typeof zNewPromptSchema>>({
+    resolver: zodResolver(zNewPromptSchema),
+    defaultValues: {
+      text: "",
+      appliesTo: "all",
+    },
+  });
+
+  const { mutateAsync: createPrompt, isPending: isCreating } =
+    api.prompts.create.useMutation({
+      onSuccess: () => {
+        toast({
+          description: "Prompt has been created!",
+        });
+        apiUtils.prompts.list.invalidate();
+      },
+    });
+
+  return (
+    <Form {...form}>
+      <form
+        className="flex gap-2"
+        onSubmit={form.handleSubmit(async (value) => {
+          await createPrompt(value);
+          form.resetField("text");
+        })}
+      >
+        <FormField
+          control={form.control}
+          name="text"
+          render={({ field }) => {
+            return (
+              <FormItem className="flex-1">
+                <FormControl>
+                  <Input
+                    placeholder="Add a custom prompt"
+                    type="text"
+                    {...field}
+                  />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            );
+          }}
+        />
+
+        <FormField
+          control={form.control}
+          name="appliesTo"
+          render={({ field }) => {
+            return (
+              <FormItem className="flex-0">
+                <FormControl>
+                  <Select
+                    onValueChange={field.onChange}
+                    defaultValue={field.value}
+                  >
+                    <SelectTrigger>
+                      <SelectValue placeholder="Applies To" />
+                    </SelectTrigger>
+                    <SelectContent>
+                      <SelectGroup>
+                        <SelectItem value="all">All</SelectItem>
+                        <SelectItem value="text">Text</SelectItem>
+                        <SelectItem value="images">Images</SelectItem>
+                      </SelectGroup>
+                    </SelectContent>
+                  </Select>
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            );
+          }}
+        />
+        <ActionButton
+          type="submit"
+          loading={isCreating}
+          variant="default"
+          className="items-center"
+        >
+          <Plus className="mr-2 size-4" />
+          Add
+        </ActionButton>
+      </form>
+    </Form>
+  );
+}
+
+export function PromptRow({ prompt }: { prompt: ZPrompt }) {
+  const apiUtils = api.useUtils();
+  const { mutateAsync: updatePrompt, isPending: isUpdating } =
+    api.prompts.update.useMutation({
+      onSuccess: () => {
+        toast({
+          description: "Prompt has been updated!",
+        });
+        apiUtils.prompts.list.invalidate();
+      },
+    });
+  const { mutate: deletePrompt, isPending: isDeleting } =
+    api.prompts.delete.useMutation({
+      onSuccess: () => {
+        toast({
+          description: "Prompt has been deleted!",
+        });
+        apiUtils.prompts.list.invalidate();
+      },
+    });
+
+  const form = useForm<z.infer<typeof zUpdatePromptSchema>>({
+    resolver: zodResolver(zUpdatePromptSchema),
+    defaultValues: {
+      promptId: prompt.id,
+      text: prompt.text,
+      appliesTo: prompt.appliesTo,
+    },
+  });
+
+  return (
+    <Form {...form}>
+      <form
+        className="flex gap-2"
+        onSubmit={form.handleSubmit(async (value) => {
+          await updatePrompt(value);
+        })}
+      >
+        <FormField
+          control={form.control}
+          name="promptId"
+          render={({ field }) => {
+            return (
+              <FormItem className="hidden">
+                <FormControl>
+                  <Input
+                    placeholder="Add a custom prompt"
+                    type="hidden"
+                    {...field}
+                  />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            );
+          }}
+        />
+        <FormField
+          control={form.control}
+          name="text"
+          render={({ field }) => {
+            return (
+              <FormItem className="flex-1">
+                <FormControl>
+                  <Input
+                    placeholder="Add a custom prompt"
+                    type="text"
+                    {...field}
+                  />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            );
+          }}
+        />
+
+        <FormField
+          control={form.control}
+          name="appliesTo"
+          render={({ field }) => {
+            return (
+              <FormItem className="flex-0">
+                <FormControl>
+                  <Select
+                    onValueChange={field.onChange}
+                    defaultValue={field.value}
+                  >
+                    <SelectTrigger>
+                      <SelectValue placeholder="Applies To" />
+                    </SelectTrigger>
+                    <SelectContent>
+                      <SelectGroup>
+                        <SelectItem value="all">All</SelectItem>
+                        <SelectItem value="text">Text</SelectItem>
+                        <SelectItem value="images">Images</SelectItem>
+                      </SelectGroup>
+                    </SelectContent>
+                  </Select>
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            );
+          }}
+        />
+        <ActionButton
+          loading={isUpdating}
+          variant="secondary"
+          type="submit"
+          className="items-center"
+        >
+          <Save className="mr-2 size-4" />
+          Save
+        </ActionButton>
+        <ActionButton
+          loading={isDeleting}
+          variant="destructive"
+          onClick={() => deletePrompt({ promptId: prompt.id })}
+          className="items-center"
+          type="button"
+        >
+          <Trash2 className="mr-2 size-4" />
+          Delete
+        </ActionButton>
+      </form>
+    </Form>
+  );
+}
+
+export function TaggingRules() {
+  const { data: prompts, isLoading } = api.prompts.list.useQuery();
+
+  return (
+    <div className="mt-2 flex flex-col gap-2">
+      <div className="w-full text-xl font-medium sm:w-1/3">Tagging Rules</div>
+      <p className="mb-1 text-xs italic text-muted-foreground">
+        Prompts that you add here will be included as rules to the model during
+        tag generation. You can view the final prompts in the prompt preview
+        section.
+      </p>
+      {isLoading && <FullPageSpinner />}
+      {prompts && prompts.length == 0 && (
+        <p className="rounded-md bg-muted p-2 text-sm text-muted-foreground">
+          You don&apos;t have any custom prompts yet.
+        </p>
+      )}
+      {prompts &&
+        prompts.map((prompt) => <PromptRow key={prompt.id} prompt={prompt} />)}
+      <PromptEditor />
+    </div>
+  );
+}
+
+export function PromptDemo() {
+  const { data: prompts } = api.prompts.list.useQuery();
+  const clientConfig = useClientConfig();
+  return (
+    <div className="flex flex-col gap-2">
+      <div className="mb-4 w-full text-xl font-medium sm:w-1/3">
+        Prompt Preview
+      </div>
+      <p>Text Prompt</p>
+      <code className="whitespace-pre-wrap rounded-md bg-muted p-3 text-sm text-muted-foreground">
+        {buildTextPrompt(
+          clientConfig.inference.inferredTagLang,
+          (prompts ?? [])
+            .filter((p) => p.appliesTo == "text" || p.appliesTo == "all")
+            .map((p) => p.text),
+          "\n<CONTENT_HERE>\n",
+        ).trim()}
+      </code>
+      <p>Image Prompt</p>
+      <code className="whitespace-pre-wrap rounded-md bg-muted p-3 text-sm text-muted-foreground">
+        {buildImagePrompt(
+          clientConfig.inference.inferredTagLang,
+          (prompts ?? [])
+            .filter((p) => p.appliesTo == "images" || p.appliesTo == "all")
+            .map((p) => p.text),
+        ).trim()}
+      </code>
+    </div>
+  );
+}
+
+export default function PromptsPage() {
+  return (
+    <>
+      <div className="rounded-md border bg-background p-4">
+        <div className="mb-2 flex flex-col gap-3">
+          <div className="w-full text-2xl font-medium sm:w-1/3">
+            AI Settings
+          </div>
+          <TaggingRules />
+        </div>
+      </div>
+      <div className="mt-4 rounded-md border bg-background p-4">
+        <PromptDemo />
+      </div>
+    </>
+  );
+}

apps/web/app/dashboard/settings/page.tsx

diff --git a/apps/web/app/dashboard/settings/page.tsx b/apps/web/app/dashboard/settings/page.tsx
index 3c02df2e..97048657 100644
--- a/apps/web/app/dashboard/settings/page.tsx
+++ b/apps/web/app/dashboard/settings/page.tsx
@@ -12,18 +12,18 @@ export default async function Settings() {
         <UserDetails />
         <ChangePassword />
       </div>
-      <div className="mt-4 rounded-md border bg-background p-4">
-        <ImportExport />
-      </div>
       <div className="mt-4 rounded-md border bg-background p-4">
         <Link
           className="flex items-center gap-2 text-lg font-medium"
-          href="/dashboard/settings/prompts"
+          href="/dashboard/settings/ai"
         >
-          Inference Settings
+          AI Settings
           <ExternalLink />
         </Link>
       </div>
+      <div className="mt-4 rounded-md border bg-background p-4">
+        <ImportExport />
+      </div>
       <div className="mt-4 rounded-md border bg-background p-4">
         <ApiKeySettings />
       </div>

apps/web/app/dashboard/settings/prompts/page.tsx

diff --git a/apps/web/app/dashboard/settings/prompts/page.tsx b/apps/web/app/dashboard/settings/prompts/page.tsx
deleted file mode 100644
index ba1c3f4f..00000000
--- a/apps/web/app/dashboard/settings/prompts/page.tsx
+++ /dev/null
@@ -1,325 +0,0 @@
-"use client";
-
-import { ActionButton } from "@/components/ui/action-button";
-import {
-  Form,
-  FormControl,
-  FormField,
-  FormItem,
-  FormMessage,
-} from "@/components/ui/form";
-import { FullPageSpinner } from "@/components/ui/full-page-spinner";
-import { Input } from "@/components/ui/input";
-import {
-  Select,
-  SelectContent,
-  SelectGroup,
-  SelectItem,
-  SelectTrigger,
-  SelectValue,
-} from "@/components/ui/select";
-import { toast } from "@/components/ui/use-toast";
-import { useClientConfig } from "@/lib/clientConfig";
-import { api } from "@/lib/trpc";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { Plus, Save, Trash2 } from "lucide-react";
-import { useForm } from "react-hook-form";
-import { z } from "zod";
-
-import { buildImagePrompt, buildTextPrompt } from "@hoarder/shared/prompts";
-import {
-  zNewPromptSchema,
-  ZPrompt,
-  zUpdatePromptSchema,
-} from "@hoarder/shared/types/prompts";
-
-export function PromptEditor() {
-  const apiUtils = api.useUtils();
-
-  const form = useForm<z.infer<typeof zNewPromptSchema>>({
-    resolver: zodResolver(zNewPromptSchema),
-    defaultValues: {
-      text: "",
-      appliesTo: "all",
-    },
-  });
-
-  const { mutateAsync: createPrompt, isPending: isCreating } =
-    api.prompts.create.useMutation({
-      onSuccess: () => {
-        toast({
-          description: "Prompt has been created!",
-        });
-        apiUtils.prompts.list.invalidate();
-      },
-    });
-
-  return (
-    <Form {...form}>
-      <form
-        className="flex gap-2"
-        onSubmit={form.handleSubmit(async (value) => {
-          await createPrompt(value);
-          form.resetField("text");
-        })}
-      >
-        <FormField
-          control={form.control}
-          name="text"
-          render={({ field }) => {
-            return (
-              <FormItem className="flex-1">
-                <FormControl>
-                  <Input
-                    placeholder="Add a custom prompt"
-                    type="text"
-                    {...field}
-                  />
-                </FormControl>
-                <FormMessage />
-              </FormItem>
-            );
-          }}
-        />
-
-        <FormField
-          control={form.control}
-          name="appliesTo"
-          render={({ field }) => {
-            return (
-              <FormItem className="flex-0">
-                <FormControl>
-                  <Select
-                    onValueChange={field.onChange}
-                    defaultValue={field.value}
-                  >
-                    <SelectTrigger>
-                      <SelectValue placeholder="Applies To" />
-                    </SelectTrigger>
-                    <SelectContent>
-                      <SelectGroup>
-                        <SelectItem value="all">All</SelectItem>
-                        <SelectItem value="text">Text</SelectItem>
-                        <SelectItem value="images">Images</SelectItem>
-                      </SelectGroup>
-                    </SelectContent>
-                  </Select>
-                </FormControl>
-                <FormMessage />
-              </FormItem>
-            );
-          }}
-        />
-        <ActionButton
-          type="submit"
-          loading={isCreating}
-          variant="default"
-          className="items-center"
-        >
-          <Plus className="mr-2 size-4" />
-          Add
-        </ActionButton>
-      </form>
-    </Form>
-  );
-}
-
-export function PromptRow({ prompt }: { prompt: ZPrompt }) {
-  const apiUtils = api.useUtils();
-  const { mutateAsync: updatePrompt, isPending: isUpdating } =
-    api.prompts.update.useMutation({
-      onSuccess: () => {
-        toast({
-          description: "Prompt has been updated!",
-        });
-        apiUtils.prompts.list.invalidate();
-      },
-    });
-  const { mutate: deletePrompt, isPending: isDeleting } =
-    api.prompts.delete.useMutation({
-      onSuccess: () => {
-        toast({
-          description: "Prompt has been deleted!",
-        });
-        apiUtils.prompts.list.invalidate();
-      },
-    });
-
-  const form = useForm<z.infer<typeof zUpdatePromptSchema>>({
-    resolver: zodResolver(zUpdatePromptSchema),
-    defaultValues: {
-      promptId: prompt.id,
-      text: prompt.text,
-      appliesTo: prompt.appliesTo,
-    },
-  });
-
-  return (
-    <Form {...form}>
-      <form
-        className="flex gap-2"
-        onSubmit={form.handleSubmit(async (value) => {
-          await updatePrompt(value);
-        })}
-      >
-        <FormField
-          control={form.control}
-          name="promptId"
-          render={({ field }) => {
-            return (
-              <FormItem className="hidden">
-                <FormControl>
-                  <Input
-                    placeholder="Add a custom prompt"
-                    type="hidden"
-                    {...field}
-                  />
-                </FormControl>
-                <FormMessage />
-              </FormItem>
-            );
-          }}
-        />
-        <FormField
-          control={form.control}
-          name="text"
-          render={({ field }) => {
-            return (
-              <FormItem className="flex-1">
-                <FormControl>
-                  <Input
-                    placeholder="Add a custom prompt"
-                    type="text"
-                    {...field}
-                  />
-                </FormControl>
-                <FormMessage />
-              </FormItem>
-            );
-          }}
-        />
-
-        <FormField
-          control={form.control}
-          name="appliesTo"
-          render={({ field }) => {
-            return (
-              <FormItem className="flex-0">
-                <FormControl>
-                  <Select
-                    onValueChange={field.onChange}
-                    defaultValue={field.value}
-                  >
-                    <SelectTrigger>
-                      <SelectValue placeholder="Applies To" />
-                    </SelectTrigger>
-                    <SelectContent>
-                      <SelectGroup>
-                        <SelectItem value="all">All</SelectItem>
-                        <SelectItem value="text">Text</SelectItem>
-                        <SelectItem value="images">Images</SelectItem>
-                      </SelectGroup>
-                    </SelectContent>
-                  </Select>
-                </FormControl>
-                <FormMessage />
-              </FormItem>
-            );
-          }}
-        />
-        <ActionButton
-          loading={isUpdating}
-          variant="secondary"
-          type="submit"
-          className="items-center"
-        >
-          <Save className="mr-2 size-4" />
-          Save
-        </ActionButton>
-        <ActionButton
-          loading={isDeleting}
-          variant="destructive"
-          onClick={() => deletePrompt({ promptId: prompt.id })}
-          className="items-center"
-          type="button"
-        >
-          <Trash2 className="mr-2 size-4" />
-          Delete
-        </ActionButton>
-      </form>
-    </Form>
-  );
-}
-
-export function CustomPrompts() {
-  const { data: prompts, isLoading } = api.prompts.list.useQuery();
-
-  return (
-    <div className="mt-2 flex flex-col gap-2">
-      <div className="w-full text-xl font-medium sm:w-1/3">Custom Prompts</div>
-      <p className="mb-1 text-xs italic text-muted-foreground">
-        Prompts that you add here will be included as rules to the model during
-        tag generation. You can view the final prompts in the prompt preview
-        section.
-      </p>
-      {isLoading && <FullPageSpinner />}
-      {prompts && prompts.length == 0 && (
-        <p className="rounded-md bg-muted p-2 text-sm text-muted-foreground">
-          You don&apos;t have any custom prompts yet.
-        </p>
-      )}
-      {prompts &&
-        prompts.map((prompt) => <PromptRow key={prompt.id} prompt={prompt} />)}
-      <PromptEditor />
-    </div>
-  );
-}
-
-export function PromptDemo() {
-  const { data: prompts } = api.prompts.list.useQuery();
-  const clientConfig = useClientConfig();
-  return (
-    <div className="flex flex-col gap-2">
-      <div className="mb-4 w-full text-xl font-medium sm:w-1/3">
-        Prompt Preview
-      </div>
-      <p>Text Prompt</p>
-      <code className="whitespace-pre-wrap rounded-md bg-muted p-3 text-sm text-muted-foreground">
-        {buildTextPrompt(
-          clientConfig.inference.inferredTagLang,
-          (prompts ?? [])
-            .filter((p) => p.appliesTo == "text" || p.appliesTo == "all")
-            .map((p) => p.text),
-          "\n<CONTENT_HERE>\n",
-        ).trim()}
-      </code>
-      <p>Image Prompt</p>
-      <code className="whitespace-pre-wrap rounded-md bg-muted p-3 text-sm text-muted-foreground">
-        {buildImagePrompt(
-          clientConfig.inference.inferredTagLang,
-          (prompts ?? [])
-            .filter((p) => p.appliesTo == "images" || p.appliesTo == "all")
-            .map((p) => p.text),
-        ).trim()}
-      </code>
-    </div>
-  );
-}
-
-export default function PromptsPage() {
-  return (
-    <>
-      <div className="rounded-md border bg-background p-4">
-        <div className="mb-2 flex flex-col gap-3">
-          <div className="w-full text-2xl font-medium sm:w-1/3">
-            Inference Settings
-          </div>
-          <CustomPrompts />
-        </div>
-      </div>
-      <div className="mt-4 rounded-md border bg-background p-4">
-        <PromptDemo />
-      </div>
-    </>
-  );
-}