Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 56 additions & 18 deletions apps/website/app/(extract)/extract-nodes/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
import { useRef } from "react";
import { Button } from "@repo/ui/components/ui/button";
import { Checkbox } from "@repo/ui/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@repo/ui/components/ui/select";
import { Textarea } from "@repo/ui/components/ui/textarea";
import { Upload } from "lucide-react";
import { NODE_TYPE_DEFINITIONS } from "~/types/extraction";
import { MODEL_OPTIONS, NODE_TYPE_DEFINITIONS } from "~/types/extraction";

const SECTION_LABEL_CLASS =
"mb-3 block px-1 text-[18px] font-semibold tracking-[-0.016em] text-slate-800";
Expand All @@ -16,6 +23,8 @@ type SidebarProps = {
onResearchQuestionChange: (value: string) => void;
selectedTypes: Set<string>;
onToggleType: (candidateTag: string) => void;
model: string;
onModelChange: (model: string) => void;
onExtract: () => void;
canExtract: boolean;
isExtracting: boolean;
Expand All @@ -28,6 +37,8 @@ export const Sidebar = ({
onResearchQuestionChange,
selectedTypes,
onToggleType,
model,
onModelChange,
onExtract,
canExtract,
isExtracting,
Expand Down Expand Up @@ -98,6 +109,26 @@ export const Sidebar = ({
)}
</section>

<section className="mb-6">
<h3 className={SECTION_LABEL_CLASS}>Model</h3>
<Select value={model} onValueChange={onModelChange}>
<SelectTrigger className="h-12 rounded-xl text-base font-medium">
<SelectValue />
</SelectTrigger>
<SelectContent className="rounded-xl shadow-lg">
{MODEL_OPTIONS.map((option) => (
<SelectItem
key={option.id}
value={option.id}
className="text-base"
>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
</section>

<section className="mb-5">
<h3 className={SECTION_LABEL_CLASS}>Research Question</h3>
<Textarea
Expand All @@ -123,23 +154,30 @@ export const Sidebar = ({
</div>

<div className="space-y-1.5">
{NODE_TYPE_DEFINITIONS.map((type) => (
<label
key={type.candidateTag}
className="flex w-full cursor-pointer items-center gap-2.5 rounded-xl border border-slate-200 bg-white px-2.5 py-2.5 text-slate-800 shadow-sm"
>
<Checkbox
checked={selectedTypes.has(type.candidateTag)}
onCheckedChange={() => onToggleType(type.candidateTag)}
/>
<span className="min-w-0 flex-1 text-[16px] font-medium">
{type.label}
</span>
<span className="shrink-0 text-[11px] font-medium text-slate-400">
{type.candidateTag}
</span>
</label>
))}
{NODE_TYPE_DEFINITIONS.map((type) => {
const isChecked = selectedTypes.has(type.candidateTag);
return (
<label
key={type.candidateTag}
className="flex w-full cursor-pointer items-center gap-2.5 rounded-xl border border-slate-200 bg-white px-2.5 py-2.5 text-slate-800 shadow-sm"
>
<Checkbox
checked={isChecked}
onCheckedChange={() => onToggleType(type.candidateTag)}
style={{
borderColor: type.color,
backgroundColor: isChecked ? type.color : undefined,
}}
/>
<span className="min-w-0 flex-1 text-base font-medium">
{type.label}
</span>
<span className="shrink-0 text-[11px] font-medium text-slate-400">
{type.candidateTag}
</span>
</label>
);
})}
</div>
</section>
</div>
Expand Down
9 changes: 6 additions & 3 deletions apps/website/app/(extract)/extract-nodes/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useCallback, useState } from "react";
import { NODE_TYPE_DEFINITIONS } from "~/types/extraction";
import { DEFAULT_MODEL_ID, NODE_TYPE_DEFINITIONS } from "~/types/extraction";
import { buildSystemPrompt } from "~/prompts/extraction";
import { MainContent } from "./components/MainContent";
import { Sidebar } from "./components/Sidebar";
Expand All @@ -23,6 +23,7 @@ const ExtractNodesPage = (): React.ReactElement => {
const [selectedTypes, setSelectedTypes] = useState(
() => new Set(["#evd-candidate", "#clm-candidate"]),
);
const [model, setModel] = useState(DEFAULT_MODEL_ID);
const [isExtracting, setIsExtracting] = useState(false);

const toggleType = useCallback((candidateTag: string) => {
Expand Down Expand Up @@ -51,7 +52,7 @@ const ExtractNodesPage = (): React.ReactElement => {
const requestBody = {
pdfBase64,
provider: "anthropic",
model: "claude-sonnet-4-6",
model,
researchQuestion: researchQuestion || undefined,
systemPrompt,
};
Expand All @@ -70,7 +71,7 @@ const ExtractNodesPage = (): React.ReactElement => {
} finally {
setIsExtracting(false);
}
}, [pdfFile, researchQuestion, selectedTypes]);
}, [pdfFile, researchQuestion, selectedTypes, model]);

return (
<div className="flex h-full w-full flex-1 flex-col gap-4 p-4 lg:flex-row lg:gap-5 lg:p-5">
Expand All @@ -81,6 +82,8 @@ const ExtractNodesPage = (): React.ReactElement => {
onResearchQuestionChange={setResearchQuestion}
selectedTypes={selectedTypes}
onToggleType={toggleType}
model={model}
onModelChange={setModel}
onExtract={() => void handleExtract()}
canExtract={canExtract}
isExtracting={isExtracting}
Expand Down
13 changes: 13 additions & 0 deletions apps/website/app/types/extraction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ export type NodeTypeDefinition = {
color?: string;
};

type ModelOption = {
id: string;
label: string;
};

export const MODEL_OPTIONS: ModelOption[] = [
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5" },
];

export const DEFAULT_MODEL_ID = "claude-sonnet-4-6";

export const NODE_TYPE_DEFINITIONS: NodeTypeDefinition[] = [
{
label: "Evidence",
Expand Down
3 changes: 2 additions & 1 deletion apps/website/tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ const config: Pick<Config, "content" | "presets" | "plugins"> = {
content: [
"./components/**/*.{js,ts,jsx,tsx,mdx}",
"./app/**/*.{js,ts,jsx,tsx,mdx}",
"../../packages/ui/src/**/*.{js,ts,jsx,tsx,mdx}",
],
presets: [sharedConfig],
plugins: [require('@tailwindcss/typography')],
plugins: [require("@tailwindcss/typography")],
};

export default config;
1 change: 1 addition & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
Expand Down
78 changes: 78 additions & 0 deletions packages/ui/src/components/ui/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";

import * as React from "react";
import * as SelectPrimitive from "@radix-ui/react-select";
import { Check, ChevronDown } from "lucide-react";

import { cn } from "@repo/ui/lib/utils";

const Select = SelectPrimitive.Root;

const SelectValue = SelectPrimitive.Value;

const SelectTrigger = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Trigger
ref={ref}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-slate-200 bg-white px-3 py-2 text-sm ring-offset-white focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon asChild>
<ChevronDown className="h-4 w-4 shrink-0 opacity-50 data-[state=open]:rotate-180" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
));
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;

const SelectContent = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
>(({ className, children, position = "popper", ...props }, ref) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
ref={ref}
className={cn(
"relative z-50 max-h-96 overflow-hidden rounded-md border border-slate-200 bg-white text-slate-900 shadow-md",
position === "popper" && "w-[var(--radix-select-trigger-width)]",
className,
)}
position={position}
{...props}
>
<SelectPrimitive.Viewport className="max-h-96 overflow-y-auto p-1">
{children}
</SelectPrimitive.Viewport>
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
));
SelectContent.displayName = SelectPrimitive.Content.displayName;

const SelectItem = React.forwardRef<
React.ElementRef<typeof SelectPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
>(({ className, children, ...props }, ref) => (
<SelectPrimitive.Item
ref={ref}
className={cn(
"relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 pl-3 pr-8 text-sm outline-none focus:bg-slate-100 data-[state=checked]:font-semibold",
className,
)}
{...props}
>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
<span className="absolute right-2 flex h-4 w-4 items-center justify-center">
<SelectPrimitive.ItemIndicator>
<Check className="h-4 w-4" />
</SelectPrimitive.ItemIndicator>
</span>
</SelectPrimitive.Item>
));
SelectItem.displayName = SelectPrimitive.Item.displayName;

export { Select, SelectValue, SelectTrigger, SelectContent, SelectItem };
Loading
Loading