From 6a23f974954b73b693cb6b35d8e3c120848c6902 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 23 Mar 2026 17:05:34 +0000 Subject: [PATCH 01/34] New side menu icons --- .../webapp/app/assets/icons/AIMetricsIcon.tsx | 16 ++++++++++ .../webapp/app/assets/icons/AIPromptsIcon.tsx | 10 ++++++ .../app/components/navigation/SideMenu.tsx | 32 +++++++++---------- apps/webapp/tailwind.config.js | 4 +++ 4 files changed, 45 insertions(+), 17 deletions(-) create mode 100644 apps/webapp/app/assets/icons/AIMetricsIcon.tsx create mode 100644 apps/webapp/app/assets/icons/AIPromptsIcon.tsx diff --git a/apps/webapp/app/assets/icons/AIMetricsIcon.tsx b/apps/webapp/app/assets/icons/AIMetricsIcon.tsx new file mode 100644 index 00000000000..038eea70b49 --- /dev/null +++ b/apps/webapp/app/assets/icons/AIMetricsIcon.tsx @@ -0,0 +1,16 @@ +export function AIMetricsIcon({ className }: { className?: string }) { + return ( + + + + + ); +} diff --git a/apps/webapp/app/assets/icons/AIPromptsIcon.tsx b/apps/webapp/app/assets/icons/AIPromptsIcon.tsx new file mode 100644 index 00000000000..dd434df9931 --- /dev/null +++ b/apps/webapp/app/assets/icons/AIPromptsIcon.tsx @@ -0,0 +1,10 @@ +export function AIPromptsIcon({ className }: { className?: string }) { + return ( + + + + ); +} diff --git a/apps/webapp/app/components/navigation/SideMenu.tsx b/apps/webapp/app/components/navigation/SideMenu.tsx index 1a1521bc68b..18a4387c996 100644 --- a/apps/webapp/app/components/navigation/SideMenu.tsx +++ b/apps/webapp/app/components/navigation/SideMenu.tsx @@ -11,7 +11,6 @@ import { Cog8ToothIcon, CogIcon, ExclamationTriangleIcon, - PuzzlePieceIcon, FolderIcon, FolderOpenIcon, GlobeAmericasIcon, @@ -19,19 +18,20 @@ import { KeyIcon, PencilSquareIcon, PlusIcon, + PuzzlePieceIcon, RectangleStackIcon, - DocumentTextIcon, ServerStackIcon, - SparklesIcon, Squares2X2Icon, TableCellsIcon, UsersIcon, - BugAntIcon, } from "@heroicons/react/20/solid"; import { Link, useFetcher, useNavigation } from "@remix-run/react"; +import { IconBugFilled } from "@tabler/icons-react"; import { LayoutGroup, motion } from "framer-motion"; import { type ReactNode, useCallback, useEffect, useRef, useState } from "react"; import simplur from "simplur"; +import { AIMetricsIcon } from "~/assets/icons/AIMetricsIcon"; +import { AIPromptsIcon } from "~/assets/icons/AIPromptsIcon"; import { ConcurrencyIcon } from "~/assets/icons/ConcurrencyIcon"; import { DropdownIcon } from "~/assets/icons/DropdownIcon"; import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons"; @@ -75,13 +75,13 @@ import { v3DeploymentsPath, v3EnvironmentPath, v3EnvironmentVariablesPath, - v3LogsPath, v3ErrorsPath, - v3PromptsPath, + v3LogsPath, v3ProjectAlertsPath, v3ProjectPath, v3ProjectSettingsGeneralPath, v3ProjectSettingsIntegrationsPath, + v3PromptsPath, v3QueuesPath, v3RunsPath, v3SchedulesPath, @@ -117,7 +117,6 @@ import { SideMenuHeader } from "./SideMenuHeader"; import { SideMenuItem } from "./SideMenuItem"; import { SideMenuSection } from "./SideMenuSection"; import { type SideMenuSectionId } from "./sideMenuTypes"; -import { IconBugFilled } from "@tabler/icons-react"; /** Get the collapsed state for a specific side menu section from user preferences */ function getSectionCollapsed( @@ -461,26 +460,25 @@ export function SideMenu({ title="AI" isSideMenuCollapsed={isCollapsed} itemSpacingClassName="space-y-0" - initialCollapsed={getSectionCollapsed( - user.dashboardPreferences.sideMenu, - "ai" - )} + initialCollapsed={getSectionCollapsed(user.dashboardPreferences.sideMenu, "ai")} onCollapseToggle={handleSectionToggle("ai")} > Date: Mon, 23 Mar 2026 17:21:47 +0000 Subject: [PATCH 02/34] Adds Prompt docs link to top bar --- .../route.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx index b8b9dde474d..5f248704f8f 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx @@ -6,7 +6,7 @@ import { PromptsNone } from "~/components/BlankStatePanels"; import { MainCenteredContainer, PageBody, PageContainer } from "~/components/layout/AppLayout"; import { TruncatedCopyableValue } from "~/components/primitives/TruncatedCopyableValue"; import { DateTime } from "~/components/primitives/DateTime"; -import { NavBar, PageTitle } from "~/components/primitives/PageHeader"; +import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; import { Table, TableBlankRow, @@ -24,7 +24,9 @@ import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; import { PromptPresenter } from "~/presenters/v3/PromptPresenter.server"; import { clickhouseClient } from "~/services/clickhouseInstance.server"; import { requireUserId } from "~/services/session.server"; -import { EnvironmentParamSchema, v3PromptsPath } from "~/utils/pathBuilder"; +import { docsPath, EnvironmentParamSchema, v3PromptsPath } from "~/utils/pathBuilder"; +import { LinkButton } from "~/components/primitives/Buttons"; +import { BookOpenIcon } from "@heroicons/react/24/solid"; export const meta: MetaFunction = () => { return [{ title: "Prompts | Trigger.dev" }]; @@ -80,6 +82,11 @@ export default function PromptsPage() { + + + Prompts docs + + From 48ffef90a9957690224dfb20f02bea2bae177963 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 23 Mar 2026 17:22:22 +0000 Subject: [PATCH 03/34] Table text color update --- .../route.tsx | 31 +++++++------------ 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx index 5f248704f8f..4d875a18935 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx @@ -115,39 +115,35 @@ export default function PromptsPage() { - - {prompt.slug} - + {prompt.slug} {prompt.description ? ( - 80 ? prompt.description : undefined}> + 80 ? prompt.description : undefined} + > {prompt.description.length > 80 - ? prompt.description.slice(0, 80) + "..." + ? prompt.description.slice(0, 80) + "…" : prompt.description} ) : ( - - + - )} - - {prompt.defaultModel ?? -} - + {prompt.defaultModel ?? -} {activeVersion ? (
- - v{activeVersion.version} - + v{activeVersion.version}
) : ( - - + - )} @@ -180,12 +176,7 @@ function UsageSparkline({ data }: { data?: number[] }) {
- +
From 01d6c075b58142a7933a2cf686a6a1f69920d970 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 23 Mar 2026 17:35:20 +0000 Subject: [PATCH 04/34] Use the CodeBlock component instead --- .../app/components/BlankStatePanels.tsx | 79 +++++++++---------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/apps/webapp/app/components/BlankStatePanels.tsx b/apps/webapp/app/components/BlankStatePanels.tsx index 111e4efebb8..fe39f6785c5 100644 --- a/apps/webapp/app/components/BlankStatePanels.tsx +++ b/apps/webapp/app/components/BlankStatePanels.tsx @@ -4,16 +4,14 @@ import { BookOpenIcon, ChatBubbleLeftRightIcon, ClockIcon, - DocumentTextIcon, PlusIcon, QuestionMarkCircleIcon, RectangleGroupIcon, RectangleStackIcon, - ServerStackIcon, - SparklesIcon, Squares2X2Icon, } from "@heroicons/react/20/solid"; import { useLocation } from "react-use"; +import { AIPromptsIcon } from "~/assets/icons/AIPromptsIcon"; import { BranchEnvironmentIconSmall } from "~/assets/icons/EnvironmentIcons"; import { WaitpointTokenIcon } from "~/assets/icons/WaitpointTokenIcon"; import openBulkActionsPanel from "~/assets/images/open-bulk-actions-panel.png"; @@ -25,21 +23,28 @@ import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; import { type MinimumEnvironment } from "~/presenters/SelectBestEnvironmentPresenter.server"; import { NewBranchPanel } from "~/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.branches/route"; +import { GitHubSettingsPanel } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github"; import { docsPath, v3BillingPath, v3CreateBulkActionPath, v3EnvironmentPath, - v3EnvironmentVariablesPath, v3NewProjectAlertPath, v3NewSchedulePath, } from "~/utils/pathBuilder"; import { AskAI } from "./AskAI"; +import { CodeBlock } from "./code/CodeBlock"; import { InlineCode } from "./code/InlineCode"; import { environmentFullTitle, EnvironmentIcon } from "./environments/EnvironmentLabel"; import { Feedback } from "./Feedback"; import { EnvironmentSelector } from "./navigation/EnvironmentSelector"; import { Button, LinkButton } from "./primitives/Buttons"; +import { + ClientTabs, + ClientTabsContent, + ClientTabsList, + ClientTabsTrigger, +} from "./primitives/ClientTabs"; import { Header1 } from "./primitives/Headers"; import { InfoPanel } from "./primitives/InfoPanel"; import { Paragraph } from "./primitives/Paragraph"; @@ -54,13 +59,6 @@ import { } from "./SetupCommands"; import { StepContentContainer } from "./StepContentContainer"; import { V4Badge } from "./V4Badge"; -import { - ClientTabs, - ClientTabsContent, - ClientTabsList, - ClientTabsTrigger, -} from "./primitives/ClientTabs"; -import { GitHubSettingsPanel } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github"; export function HasNoTasksDev() { return ( @@ -603,7 +601,9 @@ function DeploymentOnboardingSteps() {
- Deploy your tasks to {environmentFullTitle(environment)} + + Deploy your tasks to {environmentFullTitle(environment)} +
@@ -693,12 +693,16 @@ export function PromptsNone() { return ( - Prompt docs + + Prompts docs } > @@ -707,32 +711,23 @@ export function PromptsNone() { version them from the dashboard without redeploying. - Add a prompt to your project using prompts.define(): + Add a prompt to your project using prompts.define() + : -
-
-          import
-          {" { prompts } "}
-          from
-          {' "@trigger.dev/sdk";\n'}
-          import
-          {" { z } "}
-          from
-          {' "zod";\n\n'}
-          export const
-          {" myPrompt = "}
-          prompts.define
-          {"({\n"}
-          {"  id: "}
-          "my-prompt"
-          {",\n"}
-          {"  variables: z.object({\n"}
-          {"    name: z.string(),\n"}
-          {"  }),\n"}
-          {"  content: "}
-          {"`Hello {{name}}!`"}
-          {",\n"});
-
+ Deploy your project and your prompts will appear here with version history and a live editor. From 3a0be5e893858d60fe4e088d378d7ad7c0f3d5eb Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 23 Mar 2026 18:20:41 +0000 Subject: [PATCH 05/34] Fixes z-index issues with resizable panels handles --- apps/webapp/app/components/primitives/Resizable.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/webapp/app/components/primitives/Resizable.tsx b/apps/webapp/app/components/primitives/Resizable.tsx index 556658080bf..a83c008e518 100644 --- a/apps/webapp/app/components/primitives/Resizable.tsx +++ b/apps/webapp/app/components/primitives/Resizable.tsx @@ -30,7 +30,7 @@ const ResizableHandle = ({ {/* Horizontal orientation line indicator */} -
+
{/* Vertical orientation line indicator */} -
+
{withHandle && ( <> {/* Horizontal orientation dots (vertical arrangement) */} -
+
{Array.from({ length: 3 }).map((_, index) => (
))}
{/* Vertical orientation dots (horizontal arrangement) */} -
+
{Array.from({ length: 3 }).map((_, index) => (
))} From 96868f87f06efa6099f1b226c274118f83c910bb Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Mon, 23 Mar 2026 18:21:09 +0000 Subject: [PATCH 06/34] Icon update --- .../route.tsx | 119 ++++++++++-------- 1 file changed, 70 insertions(+), 49 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx index dc84de22bae..66d59e77a61 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx @@ -1,5 +1,6 @@ import * as Ariakit from "@ariakit/react"; -import { ArrowPathIcon, SparklesIcon } from "@heroicons/react/20/solid"; +import { ArrowPathIcon } from "@heroicons/react/20/solid"; +import { DialogClose } from "@radix-ui/react-dialog"; import { type MetaFunction, useFetcher } from "@remix-run/react"; import { type ActionFunctionArgs, @@ -7,25 +8,29 @@ import { type LoaderFunctionArgs, redirect, } from "@remix-run/server-runtime"; -import { DialogClose } from "@radix-ui/react-dialog"; import { useCallback, useEffect, useRef, useState } from "react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { CodeBlock } from "~/components/code/CodeBlock"; import { TextEditor } from "~/components/code/TextEditor"; import { PageBody, PageContainer } from "~/components/layout/AppLayout"; +import { ModelsFilter } from "~/components/metrics/ModelsFilter"; +import { OperationsFilter } from "~/components/metrics/OperationsFilter"; +import { ProvidersFilter } from "~/components/metrics/ProvidersFilter"; +import { AppliedFilter } from "~/components/primitives/AppliedFilter"; import { Badge } from "~/components/primitives/Badge"; -import { DateTime } from "~/components/primitives/DateTime"; import { Button } from "~/components/primitives/Buttons"; -import { Header2, Header3 } from "~/components/primitives/Headers"; +import { CopyButton } from "~/components/primitives/CopyButton"; +import { CopyableText } from "~/components/primitives/CopyableText"; +import { DateTime } from "~/components/primitives/DateTime"; import { Dialog, DialogContent, DialogHeader } from "~/components/primitives/Dialog"; +import { Header3 } from "~/components/primitives/Headers"; import { Hint } from "~/components/primitives/Hint"; import { Input } from "~/components/primitives/Input"; import { InputGroup } from "~/components/primitives/InputGroup"; import { Label } from "~/components/primitives/Label"; import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader"; import { Paragraph } from "~/components/primitives/Paragraph"; -import { Spinner } from "~/components/primitives/Spinner"; import * as Property from "~/components/primitives/PropertyTable"; import { ResizableHandle, @@ -33,9 +38,6 @@ import { ResizablePanelGroup, type ResizableSnapshot, } from "~/components/primitives/Resizable"; -import { TabButton, TabContainer } from "~/components/primitives/Tabs"; -import { CopyButton } from "~/components/primitives/CopyButton"; -import { CopyableText } from "~/components/primitives/CopyableText"; import { SelectItem, SelectList, @@ -43,35 +45,34 @@ import { SelectProvider, SelectTrigger, } from "~/components/primitives/Select"; +import { Spinner } from "~/components/primitives/Spinner"; +import { TabButton, TabContainer } from "~/components/primitives/Tabs"; import { TextArea } from "~/components/primitives/TextArea"; -import { ModelsFilter } from "~/components/metrics/ModelsFilter"; -import { OperationsFilter } from "~/components/metrics/OperationsFilter"; -import { ProvidersFilter } from "~/components/metrics/ProvidersFilter"; -import { AppliedFilter } from "~/components/primitives/AppliedFilter"; import tablerSpritePath from "~/components/primitives/tabler-sprite.svg"; import { TimeFilter } from "~/components/runs/v3/SharedFilters"; -import { SpanView } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route"; +import { prisma } from "~/db.server"; import { useEnvironment } from "~/hooks/useEnvironment"; +import { useInterval } from "~/hooks/useInterval"; import { useOrganization } from "~/hooks/useOrganizations"; import { useProject } from "~/hooks/useProject"; -import { prisma } from "~/db.server"; -import { clickhouseClient } from "~/services/clickhouseInstance.server"; -import { useInterval } from "~/hooks/useInterval"; import { useSearchParams } from "~/hooks/useSearchParam"; import { findProjectBySlug } from "~/models/project.server"; import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server"; -import { PromptPresenter, type GenerationRow } from "~/presenters/v3/PromptPresenter.server"; +import { type GenerationRow, PromptPresenter } from "~/presenters/v3/PromptPresenter.server"; +import { SpanView } from "~/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route"; +import { clickhouseClient } from "~/services/clickhouseInstance.server"; import { getResizableSnapshot } from "~/services/resizablePanel.server"; import { requireUserId } from "~/services/session.server"; import { PromptService } from "~/v3/services/promptService.server"; -import { MetricWidget } from "~/routes/resources.metric"; -import { InfoPanel } from "~/components/primitives/InfoPanel"; +import { z } from "zod"; +import { AIPromptsIcon } from "~/assets/icons/AIPromptsIcon"; import { InlineCode } from "~/components/code/InlineCode"; +import { InfoPanel } from "~/components/primitives/InfoPanel"; import { TextLink } from "~/components/primitives/TextLink"; +import { MetricWidget } from "~/routes/resources.metric"; import { EnvironmentParamSchema, v3PromptsPath, v3RunSpanPath } from "~/utils/pathBuilder"; import { parsePeriodToMs } from "~/utils/periods"; -import { z } from "zod"; const ParamSchema = EnvironmentParamSchema.extend({ promptSlug: z.string(), @@ -233,7 +234,11 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { let generations: Awaited>["generations"] = []; let generationsPagination: { next?: string } = {}; try { - const urlVersions = url.searchParams.getAll("versions").filter(Boolean).map(Number).filter((n) => !isNaN(n)); + const urlVersions = url.searchParams + .getAll("versions") + .filter(Boolean) + .map(Number) + .filter((n) => !isNaN(n)); const urlModels = url.searchParams.getAll("models").filter(Boolean); const urlOperations = url.searchParams.getAll("operations").filter(Boolean); const urlProviders = url.searchParams.getAll("providers").filter(Boolean); @@ -284,7 +289,11 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { const possibleProviders = provsErr ? [] : provsRows.map((r) => r.val); return typedjson({ - resizable: { outer: resizableOuter, vertical: resizableVertical, generations: resizableGenerations }, + resizable: { + outer: resizableOuter, + vertical: resizableVertical, + generations: resizableGenerations, + }, prompt: { id: prompt.id, friendlyId: prompt.friendlyId, @@ -520,20 +529,16 @@ export default function PromptDetailPage() { {overrideVersion && (
- Override v{overrideVersion.version} is active. API calls resolve this version instead of the deployed prompt. + Override v{overrideVersion.version} is active. API calls resolve this version instead of + the deployed prompt.
- )} {!overrideVersion && ( - )} @@ -1950,3 +1953,108 @@ function VersionsTab({
); } + +const MAX_DESCRIPTION_PREVIEW = 80; + +function PromptCopyPopover({ + slug, + friendlyId, + description, +}: { + slug: string; + friendlyId: string; + description: string | null; +}) { + const [open, setOpen] = useState(false); + + return ( + + + {slug} + + + { + e.preventDefault(); + const el = e.currentTarget as HTMLElement; + el.style.pointerEvents = "none"; + requestAnimationFrame(() => { + el.style.pointerEvents = ""; + }); + }} + > + setOpen(false)} /> + setOpen(false)} + /> + {description && ( + MAX_DESCRIPTION_PREVIEW + ? description.slice(0, MAX_DESCRIPTION_PREVIEW) + "…" + : description + } + onCopied={() => setOpen(false)} + /> + )} + + + ); +} + +function CopyPopoverItem({ + label, + value, + preview, + onCopied, +}: { + label: string; + value: string; + preview?: string; + onCopied?: () => void; +}) { + const [copied, setCopied] = useState(false); + + const handleCopy = () => { + navigator.clipboard.writeText(value); + setCopied(true); + setTimeout(() => { + setCopied(false); + onCopied?.(); + }, 600); + }; + + return ( + + {copied ? ( + + ) : ( + + )} + {label} + + } + content={{preview ?? value}} + side="right" + disableHoverableContent + asChild + /> + ); +} From 839323c9e2822519696787f14fdfe07dd1a5463e Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 15:11:06 +0000 Subject: [PATCH 09/34] Fixes shortcuts conflict --- apps/webapp/app/components/metrics/ProvidersFilter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/metrics/ProvidersFilter.tsx b/apps/webapp/app/components/metrics/ProvidersFilter.tsx index f73a6f00da3..fe018eefb98 100644 --- a/apps/webapp/app/components/metrics/ProvidersFilter.tsx +++ b/apps/webapp/app/components/metrics/ProvidersFilter.tsx @@ -13,7 +13,7 @@ import { import { useSearchParams } from "~/hooks/useSearchParam"; import { appliedSummary, FilterMenuProvider } from "~/components/runs/v3/SharedFilters"; -const shortcut = { key: "v" }; +const shortcut = { key: "r" }; interface ProvidersFilterProps { possibleProviders: string[]; From 97844adeb0dab62c3455d9f38509128d31ea4ccc Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 15:11:42 +0000 Subject: [PATCH 10/34] Date picker supports shortcut key --- .../app/components/runs/v3/SharedFilters.tsx | 59 +++++++++++++++---- 1 file changed, 47 insertions(+), 12 deletions(-) diff --git a/apps/webapp/app/components/runs/v3/SharedFilters.tsx b/apps/webapp/app/components/runs/v3/SharedFilters.tsx index 5538c628178..02a6d90ba3d 100644 --- a/apps/webapp/app/components/runs/v3/SharedFilters.tsx +++ b/apps/webapp/app/components/runs/v3/SharedFilters.tsx @@ -11,7 +11,7 @@ import { subWeeks, } from "date-fns"; import parse from "parse-duration"; -import { startTransition, useCallback, useEffect, useState, type ReactNode } from "react"; +import { startTransition, useCallback, useEffect, useRef, useState, type ReactNode } from "react"; import simplur from "simplur"; import { AppliedFilter } from "~/components/primitives/AppliedFilter"; import { Callout } from "~/components/primitives/Callout"; @@ -23,7 +23,8 @@ import { RadioButtonCircle } from "~/components/primitives/RadioButton"; import { ComboboxProvider, SelectPopover, SelectProvider } from "~/components/primitives/Select"; import { useOptionalOrganization } from "~/hooks/useOrganizations"; import { useSearchParams } from "~/hooks/useSearchParam"; -import { type ShortcutDefinition } from "~/hooks/useShortcutKeys"; +import { type ShortcutDefinition, useShortcutKeys } from "~/hooks/useShortcutKeys"; +import { ShortcutKey } from "~/components/primitives/ShortcutKey"; import { cn } from "~/utils/cn"; import { organizationBillingPath } from "~/utils/pathBuilder"; import { Button, LinkButton } from "../../primitives/Buttons"; @@ -347,6 +348,8 @@ export interface TimeFilterProps { /** Label name used in the filter display, defaults to "Created" */ labelName?: string; hideLabel?: boolean; + /** Keyboard shortcut to open the dropdown */ + shortcut?: ShortcutDefinition; applyShortcut?: ShortcutDefinition | undefined; /** Callback when the user applies a time filter selection, receives the applied values */ onValueChange?: (values: TimeFilterApplyValues) => void; @@ -363,6 +366,7 @@ export function TimeFilter({ to, labelName = "Created", hideLabel = false, + shortcut, applyShortcut, onValueChange, maxPeriodDays, @@ -372,6 +376,16 @@ export function TimeFilter({ const periodValue = period ?? value("period"); const fromValue = from ?? value("from"); const toValue = to ?? value("to"); + const triggerRef = useRef(null); + + useShortcutKeys({ + shortcut, + action: (e) => { + e.preventDefault(); + e.stopPropagation(); + triggerRef.current?.click(); + }, + }); const constrained = timeFilters({ period: periodValue, @@ -386,16 +400,37 @@ export function TimeFilter({ {() => ( }> - - + + } + /> + } + > + + + {shortcut && ( + +
+ Filter by time period + +
+
+ )} +
} period={constrained.period} from={constrained.from} From 1d2534d60533d080e72b59e562652165b263bdbf Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 15:11:56 +0000 Subject: [PATCH 11/34] Adds shortcut keys to missing filters --- .../route.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx index d88b7a7e5e6..fcab148187c 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx @@ -648,6 +648,7 @@ export default function PromptDetailPage() { labelName="Period" hideLabel valueClassName="text-text-bright" + shortcut={{ key: "t" }} />
@@ -1819,6 +1820,7 @@ function PromptVersionsFilter({ versions }: { versions: VersionData[] }) { } variant="secondary/small" tooltipTitle="Filter by version" + shortcut={{ key: "e" }} > Versions From 24b5e51f17109fdbc1f56c029281666d06c63df1 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 15:42:22 +0000 Subject: [PATCH 12/34] Simplified templates viewer --- .../route.tsx | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx index fcab148187c..cd36113cb65 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx @@ -565,34 +565,24 @@ export default function PromptDetailPage() { > {/* Template panel */} -
- {/* Sticky header */} -
-
- Template - {content && } -
+ {content ? ( + + ) : ( +
+ + No content +
- {/* Scrollable content */} - {content ? ( -
- -
- ) : ( -
- - No content - -
- )} -
+ )} From 934307c025f44f776e4a1ec3801b66d5c8d57229 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 15:54:44 +0000 Subject: [PATCH 13/34] Details panel imrprovements --- .../route.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx index cd36113cb65..3a7d8b3f2cf 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx @@ -958,7 +958,7 @@ function DetailsTab({ Slug - {prompt.slug} + {prompt.slug} {prompt.description && ( @@ -975,7 +975,7 @@ function DetailsTab({ )} {prompt.defaultConfig && ( - Config + Config Source - + {prompt.filePath} {prompt.exportName ? ` (${prompt.exportName})` : ""} From 872a4e5ab1175d2bebe7f9dc22d58927f836e924 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 17:05:46 +0000 Subject: [PATCH 14/34] Adds focus-custom class --- apps/webapp/app/components/primitives/TextArea.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/components/primitives/TextArea.tsx b/apps/webapp/app/components/primitives/TextArea.tsx index f5350a510bc..06dc84622da 100644 --- a/apps/webapp/app/components/primitives/TextArea.tsx +++ b/apps/webapp/app/components/primitives/TextArea.tsx @@ -8,7 +8,7 @@ export function TextArea({ className, rows, ...props }: TextAreaProps) { {...props} rows={rows ?? 6} className={cn( - "placeholder:text-muted-foreground w-full rounded border border-charcoal-800 bg-charcoal-750 px-3 text-sm text-text-bright transition focus-custom focus-custom file:border-0 file:bg-transparent file:text-base file:font-medium hover:border-charcoal-600 hover:bg-charcoal-650 disabled:cursor-not-allowed disabled:opacity-50", + "placeholder:text-muted-foreground w-full rounded border border-charcoal-800 bg-charcoal-750 px-3 text-sm text-text-bright transition focus-custom file:border-0 file:bg-transparent file:text-base file:font-medium hover:border-charcoal-600 hover:bg-charcoal-650 disabled:cursor-not-allowed disabled:opacity-50", className )} /> From 1b2a44795cc631e1486e9700dbfabd3e924b2175 Mon Sep 17 00:00:00 2001 From: James Ritchie Date: Tue, 24 Mar 2026 17:05:53 +0000 Subject: [PATCH 15/34] Improvements to the Preview tab --- .../route.tsx | 53 +++++++++---------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx index 3a7d8b3f2cf..a8dbeeb0c84 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx @@ -1037,7 +1037,6 @@ function PreviewTab({ content: string; }) { const variableFields = prompt.variableSchema ? extractVariableFields(prompt.variableSchema) : []; - const [testVariables, setTestVariables] = useState>(() => Object.fromEntries(variableFields.map((f) => [f.name, ""])) ); @@ -1049,14 +1048,20 @@ function PreviewTab({ {variableFields.length > 0 ? ( <>
- Variables - {variableFields.map((field) => ( - +
+ Variables + + Fill in values to preview your resolved prompt template. + +
+ {variableFields.map((field, index) => ( + {field.enumValues ? ( ) : field.isLongText ? (