Skip to content
Merged
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
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ hadrian.toml
/coverage
.kreuzberg
todo/
AGENTS.md
CLAUDE.md

# Model catalog (fetched via scripts/fetch-model-catalog.sh)
data/models-dev-catalog.json
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
681 changes: 681 additions & 0 deletions CLAUDE.md

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,19 @@
return (
<div className="mx-auto max-w-screen-2xl px-4">
<div
className="mx-auto mb-6 flex max-w-6xl flex-wrap justify-center gap-2"
className="scrollbar-none mx-auto mb-6 flex max-w-6xl gap-2 overflow-x-auto px-4 pb-2 sm:flex-wrap sm:justify-center sm:overflow-visible sm:px-0 sm:pb-0"
role="tablist"
aria-label="Demo gallery"
tabIndex={0}
>
{demos.map((demo) => (
<button
key={demo.id}
role="tab"
aria-selected={active === demo.id}
aria-controls={`demo-panel-${demo.id}`}
onMouseEnter={() => setActive(demo.id)}
onClick={() => setActive(demo.id)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed hover preview functionality - users could previously preview demos by hovering over tabs without clicking

Suggested change
onClick={() => setActive(demo.id)}
onMouseEnter={() => setActive(demo.id)}
onClick={() => setActive(demo.id)}
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/app/(home)/page.tsx
Line: 88

Comment:
removed hover preview functionality - users could previously preview demos by hovering over tabs without clicking

```suggestion
            onMouseEnter={() => setActive(demo.id)}
            onClick={() => setActive(demo.id)}
```

How can I resolve this? If you propose a fix, please make it concise.

className={`shrink-0 rounded-lg border px-4 py-3 text-left transition-colors ${
className={`shrink-0 cursor-pointer rounded-lg border px-4 py-3 text-left transition-colors ${
active === demo.id
? "border-fd-primary bg-fd-primary/10 text-fd-foreground"
: "border-fd-border bg-fd-card text-fd-muted-foreground hover:border-fd-primary/50 hover:text-fd-foreground"
Expand All @@ -97,16 +97,16 @@
</button>
))}
</div>
<div className="relative overflow-hidden rounded-xl border border-fd-border shadow-lg">
<div className="relative h-[500px] overflow-hidden rounded-xl border border-fd-border shadow-lg sm:h-[700px] lg:h-[950px]">
{demos.map((demo) => (
<div
key={demo.id}
id={`demo-panel-${demo.id}`}
role="tabpanel"
aria-label={demo.title}
className={active === demo.id ? "" : "invisible absolute inset-0"}
className={active === demo.id ? "h-full" : "invisible absolute inset-0"}
>
<StoryEmbed storyId={demo.storyId} height={950} />
<StoryEmbed storyId={demo.storyId} height="100%" />
</div>
))}
</div>
Expand Down Expand Up @@ -225,7 +225,7 @@
<section className="relative overflow-hidden border-b bg-gradient-to-b from-fd-background to-fd-muted/30 py-16 md:py-24">
<div className="mx-auto max-w-6xl px-4">
<div className="text-center">
<img

Check warning on line 228 in docs/app/(home)/page.tsx

View workflow job for this annotation

GitHub Actions / Documentation

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
src={`${process.env.DOCS_BASE_PATH || ""}/icon.svg`}
alt=""
width={96}
Expand Down
113 changes: 60 additions & 53 deletions docs/components/quick-start-selector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Check, Copy, Download, X } from "lucide-react";

type Method = "binary" | "docker" | "cargo";
type OS = "linux-x86_64" | "linux-arm64" | "macos-arm64" | "windows";
type Profile = "full" | "standard" | "minimal" | "tiny";
type Profile = "full" | "headless" | "standard" | "minimal" | "tiny";
type Libc = "gnu" | "musl";

const osLabels: Record<OS, string> = {
Expand All @@ -20,6 +20,14 @@ const libcLabels: Record<Libc, string> = {
musl: "musl",
};

const profileLabels: Record<Profile, string> = {
full: "Full",
headless: "Headless",
standard: "Standard",
minimal: "Minimal",
tiny: "Tiny",
};

function getTarget(os: OS, libc: Libc): string {
switch (os) {
case "linux-x86_64":
Expand All @@ -35,38 +43,42 @@ function getTarget(os: OS, libc: Libc): string {

const profileSummaries: Record<Profile, string> = {
full: "Everything",
headless: "Full features, no embedded assets (serve frontend separately)",
standard: "Production deployment",
minimal: "Development and embedded use",
tiny: "Stateless proxy",
};

const allProfiles: Profile[] = ["full", "headless", "standard", "minimal", "tiny"];
const embeddedAssetProfiles: Profile[] = ["minimal", "standard", "full"];

const featureMatrix: { name: string; profiles: Profile[] }[] = [
{ name: "OpenAI", profiles: ["tiny", "minimal", "standard", "full"] },
{ name: "Anthropic", profiles: ["minimal", "standard", "full"] },
{ name: "AWS Bedrock", profiles: ["minimal", "standard", "full"] },
{ name: "Google Vertex AI", profiles: ["minimal", "standard", "full"] },
{ name: "Azure OpenAI", profiles: ["minimal", "standard", "full"] },
{ name: "SQLite", profiles: ["minimal", "standard", "full"] },
{ name: "Embedded UI", profiles: ["minimal", "standard", "full"] },
{ name: "Model catalog", profiles: ["minimal", "standard", "full"] },
{ name: "Setup wizard", profiles: ["minimal", "standard", "full"] },
{ name: "PostgreSQL", profiles: ["standard", "full"] },
{ name: "Redis caching", profiles: ["standard", "full"] },
{ name: "SSO (OIDC / OAuth)", profiles: ["standard", "full"] },
{ name: "CEL RBAC", profiles: ["standard", "full"] },
{ name: "S3 storage", profiles: ["standard", "full"] },
{ name: "Secrets managers", profiles: ["standard", "full"] },
{ name: "OTLP & Prometheus", profiles: ["standard", "full"] },
{ name: "OpenAPI docs", profiles: ["standard", "full"] },
{ name: "Embedded docs", profiles: ["standard", "full"] },
{ name: "Doc extraction", profiles: ["standard", "full"] },
{ name: "Cost forecasting", profiles: ["standard", "full"] },
{ name: "CSV export", profiles: ["standard", "full"] },
{ name: "Response validation", profiles: ["standard", "full"] },
{ name: "JSON schema", profiles: ["standard", "full"] },
{ name: "SAML SSO", profiles: ["full"] },
{ name: "Kreuzberg OCR", profiles: ["full"] },
{ name: "ClamAV scanning", profiles: ["full"] },
{ name: "OpenAI", profiles: allProfiles },
{ name: "Anthropic", profiles: ["minimal", "standard", "headless", "full"] },
{ name: "AWS Bedrock", profiles: ["minimal", "standard", "headless", "full"] },
{ name: "Google Vertex AI", profiles: ["minimal", "standard", "headless", "full"] },
{ name: "Azure OpenAI", profiles: ["minimal", "standard", "headless", "full"] },
{ name: "SQLite", profiles: ["minimal", "standard", "headless", "full"] },
{ name: "Embedded UI", profiles: embeddedAssetProfiles },
{ name: "Model catalog", profiles: embeddedAssetProfiles },
{ name: "Setup wizard", profiles: embeddedAssetProfiles },
{ name: "PostgreSQL", profiles: ["standard", "headless", "full"] },
{ name: "Redis caching", profiles: ["standard", "headless", "full"] },
{ name: "SSO (OIDC / OAuth)", profiles: ["standard", "headless", "full"] },
{ name: "CEL RBAC", profiles: ["standard", "headless", "full"] },
{ name: "S3 storage", profiles: ["standard", "headless", "full"] },
{ name: "Secrets managers", profiles: ["standard", "headless", "full"] },
{ name: "OTLP & Prometheus", profiles: ["standard", "headless", "full"] },
{ name: "OpenAPI docs", profiles: ["standard", "headless", "full"] },
{ name: "Embedded docs", profiles: embeddedAssetProfiles },
{ name: "Doc extraction", profiles: ["standard", "headless", "full"] },
{ name: "Cost forecasting", profiles: ["standard", "headless", "full"] },
{ name: "CSV export", profiles: ["standard", "headless", "full"] },
{ name: "Response validation", profiles: ["standard", "headless", "full"] },
{ name: "JSON schema", profiles: ["standard", "headless", "full"] },
{ name: "SAML SSO", profiles: ["headless", "full"] },
{ name: "Kreuzberg OCR", profiles: ["headless", "full"] },
{ name: "ClamAV scanning", profiles: ["headless", "full"] },
];

function getInstallCommand(method: Method, os: OS, profile: Profile, libc: Libc): string {
Expand Down Expand Up @@ -135,7 +147,7 @@ function ToggleGroup<T extends string>({
key={opt}
onClick={() => onChange(opt)}
disabled={isDisabled}
className={`rounded-md px-3 py-1.5 text-sm font-medium transition-colors ${
className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors sm:px-3 sm:py-1.5 sm:text-sm ${
isDisabled
? "cursor-not-allowed bg-fd-muted text-fd-muted-foreground/40"
: value === opt
Expand All @@ -152,9 +164,10 @@ function ToggleGroup<T extends string>({
}

function getDisabledProfiles(os: OS, libc: Libc): Set<Profile> | undefined {
if (os === "windows") return new Set(["full", "standard"]);
if (os === "linux-arm64") return new Set(["full"]);
if (os.startsWith("linux-") && libc === "musl") return new Set(["full"]);
// headless and full only built for linux-x86_64-gnu and macos-arm64
if (os === "windows") return new Set(["full", "headless"]);
if (os === "linux-arm64") return new Set(["full", "headless"]);
if (os.startsWith("linux-") && libc === "musl") return new Set(["full", "headless"]);
return undefined;
}

Expand Down Expand Up @@ -184,7 +197,7 @@ export function QuickStartSelector() {

const handleLibcChange = (newLibc: Libc) => {
setLibc(newLibc);
if (newLibc === "musl" && profile === "full") {
if (newLibc === "musl" && (profile === "full" || profile === "headless")) {
setProfile("standard");
}
};
Expand All @@ -193,27 +206,18 @@ export function QuickStartSelector() {
const downloadUrl = method === "binary" ? getDownloadUrl(os, profile, libc) : null;

const handleCopy = async () => {
if (navigator.clipboard) {
await navigator.clipboard.writeText(command);
} else {
const textarea = document.createElement("textarea");
textarea.value = command;
textarea.style.position = "fixed";
textarea.style.opacity = "0";
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
}
await navigator.clipboard.writeText(command);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};

return (
<div className="not-prose overflow-hidden rounded-lg border border-fd-border bg-fd-card">
<div className="space-y-3 border-b border-fd-border bg-fd-muted/50 p-4">
<div className="flex flex-wrap items-center gap-3">
<span className="w-16 shrink-0 text-sm font-medium text-fd-muted-foreground">Method</span>
<div className="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:gap-3">
<span className="text-sm font-medium text-fd-muted-foreground sm:w-16 sm:shrink-0">
Method
</span>
<ToggleGroup
options={["binary", "docker", "cargo"] as Method[]}
value={method}
Expand All @@ -223,8 +227,10 @@ export function QuickStartSelector() {
</div>
{method === "binary" && (
<>
<div className="flex flex-wrap items-center gap-3">
<span className="w-16 shrink-0 text-sm font-medium text-fd-muted-foreground">OS</span>
<div className="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:gap-3">
<span className="text-sm font-medium text-fd-muted-foreground sm:w-16 sm:shrink-0">
OS
</span>
<ToggleGroup
options={["linux-x86_64", "linux-arm64", "macos-arm64", "windows"] as OS[]}
value={os}
Expand All @@ -233,8 +239,8 @@ export function QuickStartSelector() {
/>
</div>
{isLinux && (
<div className="flex flex-wrap items-center gap-3">
<span className="w-16 shrink-0 text-sm font-medium text-fd-muted-foreground">
<div className="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:gap-3">
<span className="text-sm font-medium text-fd-muted-foreground sm:w-16 sm:shrink-0">
Libc
</span>
<ToggleGroup
Expand All @@ -246,14 +252,15 @@ export function QuickStartSelector() {
/>
</div>
)}
<div className="flex flex-wrap items-center gap-3">
<span className="w-16 shrink-0 text-sm font-medium text-fd-muted-foreground">
<div className="flex flex-col gap-1.5 sm:flex-row sm:items-center sm:gap-3">
<span className="text-sm font-medium text-fd-muted-foreground sm:w-16 sm:shrink-0">
Features
</span>
<ToggleGroup
options={["full", "standard", "minimal", "tiny"] as Profile[]}
options={allProfiles}
value={profile}
onChange={setProfile}
labels={profileLabels}
disabled={disabledProfiles}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/Markdown/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function Markdown({ content, className }: MarkdownProps) {
ref={containerRef}
className={cn(
"markdown-content prose prose-sm dark:prose-invert",
"max-w-[calc(100vw-8rem)] sm:max-w-[500px] md:max-w-[600px] lg:max-w-[700px]",
"max-w-none",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing max-width constraints could make markdown content too wide on large screens, reducing readability - consider keeping a reasonable max-width (e.g., max-w-prose or max-w-4xl) for better typography

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/components/Markdown/Markdown.tsx
Line: 48

Comment:
removing max-width constraints could make markdown content too wide on large screens, reducing readability - consider keeping a reasonable max-width (e.g., `max-w-prose` or `max-w-4xl`) for better typography

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

"[&_pre]:overflow-x-auto",
className
)}
Expand Down
11 changes: 9 additions & 2 deletions ui/src/components/ModelSelector/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -309,8 +309,15 @@ export function ModelSelector({

return (
<div className="flex items-center gap-2 min-w-0">
{/* Horizontally scrollable chip container */}
<div className="flex flex-wrap items-center gap-2 min-w-0">
{/* Model count badge - mobile only, shown when multiple models selected */}
{selectedInstances.length > 1 && (
<span className="sm:hidden shrink-0 rounded-full bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground tabular-nums">
{selectedInstances.length}
</span>
)}

{/* Chip container - horizontal scroll on mobile, wraps on desktop */}
<div className="flex items-center gap-2 min-w-0 overflow-x-auto sm:overflow-x-visible sm:flex-wrap scrollbar-none">
<TooltipProvider>
<DndContext
sensors={sensors}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,15 +288,15 @@ export const ViewModeToggle: Story = {
// Should have 2 toggle buttons
await expect(toggleButtons.length).toBe(2);

// In grid mode, cards should have min-w-[500px] class (horizontal layout)
let gridCards = canvasElement.querySelectorAll('[class*="min-w-[500px]"]');
// In grid mode, cards should have basis-[min(500px,85vw)] class (horizontal layout)
let gridCards = canvasElement.querySelectorAll('[class*="basis-"]');
await expect(gridCards.length).toBe(2);

// Click the stacked button (second toggle button)
await userEvent.click(toggleButtons[1]);

// After clicking stacked, cards should NOT have min-w-[500px] (vertical layout)
gridCards = canvasElement.querySelectorAll('[class*="min-w-[500px]"]');
// After clicking stacked, cards should NOT have basis-[min(500px,85vw)] (vertical layout)
gridCards = canvasElement.querySelectorAll('[class*="basis-"]');
await expect(gridCards.length).toBe(0);

// Cards should now be full width (w-full)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ const ModelResponseCard = memo(function ModelResponseCard({
"hover:shadow-md",
"animate-slide-up-bounce",
isSelectedBest && "ring-2 ring-success ring-offset-2 ring-offset-background",
useHorizontalLayout ? "min-w-[500px] w-[500px] shrink-0" : "w-full"
useHorizontalLayout ? "grow shrink-0 basis-[min(500px,85vw)]" : "w-full"
)}
style={{ animationDelay: `${index * 100}ms` }}
>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/StreamingMarkdown/StreamingMarkdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ function StreamingMarkdownComponent({ content, isStreaming, className }: Streami
<div
className={cn(
"markdown-content prose prose-sm dark:prose-invert",
"max-w-[calc(100vw-8rem)] sm:max-w-[500px] md:max-w-[600px] lg:max-w-[700px]",
"max-w-none",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing max-width constraints could make markdown content too wide on large screens, reducing readability - consider keeping a reasonable max-width (e.g., max-w-prose or max-w-4xl) for better typography

Prompt To Fix With AI
This is a comment left during a code review.
Path: ui/src/components/StreamingMarkdown/StreamingMarkdown.tsx
Line: 78

Comment:
removing max-width constraints could make markdown content too wide on large screens, reducing readability - consider keeping a reasonable max-width (e.g., `max-w-prose` or `max-w-4xl`) for better typography

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

"[&_pre]:overflow-x-auto",
className
)}
Expand Down
Loading