Skip to content

Feat(webapp) AI screen UI improvements#3269

Merged
ericallam merged 35 commits intomainfrom
feat(webapp)-ai-screen-ui-improvements
Mar 25, 2026
Merged

Feat(webapp) AI screen UI improvements#3269
ericallam merged 35 commits intomainfrom
feat(webapp)-ai-screen-ui-improvements

Conversation

@samejr
Copy link
Copy Markdown
Member

@samejr samejr commented Mar 25, 2026

Lots of UI improvements to the Prompts pages:

New side menu icons

CleanShot 2026-03-25 at 14 26 33

Compact horizontal start finish times so scanning generations list is consistent

CleanShot 2026-03-25 at 14 24 03

Tidied up the metrics view

CleanShot 2026-03-25 at 14 23 52

Copiable metadata

CleanShot 2026-03-25 at 14 23 44

Cleaner versions list

CleanShot 2026-03-25 at 14 23 28

Overall consistency improvements, shortcut keys and UI behaviours improvements

CleanShot 2026-03-25 at 14 23 02

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 25, 2026

⚠️ No Changeset found

Latest commit: a08ffac

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 25, 2026

Walkthrough

Adds two new icon components (AIMetricsIcon, AIPromptsIcon) and two Tailwind color tokens (aiPrompts, aiMetrics). Introduces SpanHorizontalTimeline and extends several span/detail components (PromptSpanDetails, AISpanDetails, runs routing) to accept optional startTime and duration and conditionally render the timeline. Large prompts route refactor adds PromptCopyPopover, animated override banner, table-based generations view, and other UI/UX changes. Miscellaneous updates: CodeBlock and Resizable/ResizableHandle layout/styling changes, PageContainer accepts className, keyboard shortcut adjustments in multiple filters, TimeFilter shortcut integration with tooltip, and various icon replacements.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides comprehensive context with visual examples but lacks required checklist items and structured sections per the template (Testing, Changelog, Screenshots sections are missing substantive content). Complete the description template by filling in the Checklist boxes, providing explicit testing steps under Testing, a written summary under Changelog, and confirming all required sections are addressed.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main objective of the PR, describing UI improvements to the AI/Prompts screens with specific enhancements like new icons, compact timelines, and metadata features.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat(webapp)-ai-screen-ui-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 3 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx (1)

1290-1300: ⚠️ Potential issue | 🟠 Major

Keep the inspector selection in sync with the current generations list.

After refresh/polling updates the list, selectedSpan can point at a span that is no longer present in generations. In that state the table shows no selected row, but Lines 1562-1564 still render the old span details, so the inspector no longer matches the visible result set.

Possible fix
+  useEffect(() => {
+    if (generations.length === 0) {
+      onSelectSpan(null);
+      return;
+    }
+
+    if (!selectedSpan || !generations.some((g) => g.span_id === selectedSpan.spanId)) {
+      const first = generations[0];
+      onSelectSpan({ runId: first.run_id, spanId: first.span_id });
+    }
+  }, [generations, onSelectSpan, selectedSpan]);

Also applies to: 1307-1310, 1480-1564

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
around lines 1290 - 1300, When pollFetcher updates generations (see pollFetcher
handling that updates lastPollDataRef.current and setNewGenerationCount) ensure
the inspector selection stays in sync by validating selectedSpan against the
current generations array and clearing or updating it when the selected span is
no longer present; specifically, after computing the new generations list (and
where generations are replaced/merged in the poll/update paths) check if
selectedSpan is null or its span_id exists in generations, and if not call
setSelectedSpan(null) (or set to a sensible fallback like the first generation)
so the inspector rendering (the code that reads selectedSpan to show details)
always matches the visible table. Apply the same selection-validation logic to
the other poll/update blocks noted (around the blocks corresponding to lines
handling pollFetcher at 1307-1310 and the generation merge/render section around
1480-1564).
🧹 Nitpick comments (2)
apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx (1)

132-134: Consider simplifying the nested spans.

The current structure creates redundant nesting when defaultModel is null, resulting in <span><span>-</span></span>.

Suggested simplification
                    <TableCell to={path}>
-                      <span>{prompt.defaultModel ?? <span>-</span>}</span>
+                      {prompt.defaultModel ?? "-"}
                    </TableCell>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
around lines 132 - 134, The TableCell currently nests spans redundantly when
prompt.defaultModel is null; update the rendering inside the TableCell (where it
references prompt.defaultModel) to render either the model string or a single
'-' element without wrapping an extra <span>—e.g., use a single conditional that
returns prompt.defaultModel or '-' (or a single span around the fallback) so
TableCell contains only one span element instead of nested spans.
apps/webapp/app/components/code/CodeBlock.tsx (1)

494-498: Simplify the redundant conditional.

Both branches of the isWrapped conditional apply the same overflow-auto class, making the conditional unnecessary.

♻️ Proposed simplification
   const containerClasses = cn(
     "min-h-0 flex-1 px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600",
-    !isWrapped && "overflow-auto",
-    isWrapped && "overflow-auto",
+    "overflow-auto",
     className
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/components/code/CodeBlock.tsx` around lines 494 - 498, The
conditional in the containerClasses construction is redundant because both
branches add "overflow-auto"; update the cn call in the CodeBlock component to
remove the isWrapped checks and always include "overflow-auto" alongside the
existing classes (referencing containerClasses, isWrapped, cn, and className) so
the class list is simplified and the dead conditional is eliminated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/webapp/app/components/navigation/SideMenu.tsx`:
- Around line 468-481: The trailingIconClassName prop is being passed to
SideMenuItem here but SideMenuItem only applies that prop to trailingIcon (so
icon prop sizes are no-ops); update SideMenuItem (component SideMenuItem) to
accept a new iconClassName prop and apply it to the main icon element (where it
renders the icon prop), or alternatively change these callers in SideMenu.tsx to
pass the icon via the trailingIcon prop with trailingIconClassName — pick one
approach and implement it consistently so the AI icon sizes
(AIPromptsIcon/AIMetricsIcon) actually receive the intended class.

In `@apps/webapp/app/components/primitives/Resizable.tsx`:
- Around line 28-30: The onMouseDown handler in Resizable.tsx currently calls
e.preventDefault(), which prevents the separator from receiving focus; either
remove the e.preventDefault() call so clicks can focus the resizer, or if you
must keep preventing default behavior, immediately call (e.currentTarget as
HTMLElement).focus() after preventDefault to explicitly restore focus to the
separator (update the onMouseDown arrow function in the Resizable component
accordingly).

In `@apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx`:
- Around line 3-10: The formatSpanDuration function can produce rollovers like
"1m 60s" because seconds are computed after toFixed rounding; change it to round
the total milliseconds (or total seconds) once first, then compute minutes and
seconds from that integer value so seconds never reach 60; update the
implementation in SpanHorizontalTimeline.tsx (function formatSpanDuration) to:
compute roundedMs = Math.round(nanoseconds / 1_000_000) (or roundedSeconds =
Math.round(nanoseconds / 1_000_000 / 1000) for second-granularity), then derive
mins = Math.floor(roundedMs / 60000) and secs = Math.floor((roundedMs % 60000) /
1000) (or use roundedSeconds % 60) and format accordingly; also extract/export
this helper so other components (span route) reuse the corrected formatter.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx:
- Around line 1963-2008: The version row is a non-focusable div; replace it with
a semantic, keyboard-focusable control (e.g., change the outer <div key={v.id}
...> to a <button type="button"> or a role="radio" element) so keyboard users
can tab and activate with Enter/Space, and keep the existing onClick={() =>
onSelectVersion(v.version)} and className logic; ensure RadioButtonCircle
receives the selected state (isSelected) and add appropriate ARIA
(aria-checked/aria-pressed or role="radio"/aria-checked) and tabindex handling
for accessibility, and make sure you do not create nested interactive controls
(adjust Badge/DateTime if they are interactive).

---

Outside diff comments:
In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx:
- Around line 1290-1300: When pollFetcher updates generations (see pollFetcher
handling that updates lastPollDataRef.current and setNewGenerationCount) ensure
the inspector selection stays in sync by validating selectedSpan against the
current generations array and clearing or updating it when the selected span is
no longer present; specifically, after computing the new generations list (and
where generations are replaced/merged in the poll/update paths) check if
selectedSpan is null or its span_id exists in generations, and if not call
setSelectedSpan(null) (or set to a sensible fallback like the first generation)
so the inspector rendering (the code that reads selectedSpan to show details)
always matches the visible table. Apply the same selection-validation logic to
the other poll/update blocks noted (around the blocks corresponding to lines
handling pollFetcher at 1307-1310 and the generation merge/render section around
1480-1564).

---

Nitpick comments:
In `@apps/webapp/app/components/code/CodeBlock.tsx`:
- Around line 494-498: The conditional in the containerClasses construction is
redundant because both branches add "overflow-auto"; update the cn call in the
CodeBlock component to remove the isWrapped checks and always include
"overflow-auto" alongside the existing classes (referencing containerClasses,
isWrapped, cn, and className) so the class list is simplified and the dead
conditional is eliminated.

In
`@apps/webapp/app/routes/_app.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx:
- Around line 132-134: The TableCell currently nests spans redundantly when
prompt.defaultModel is null; update the rendering inside the TableCell (where it
references prompt.defaultModel) to render either the model string or a single
'-' element without wrapping an extra <span>—e.g., use a single conditional that
returns prompt.defaultModel or '-' (or a single span around the fallback) so
TableCell contains only one span element instead of nested spans.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b3d023a3-e014-4ac3-88db-33015ca92d89

📥 Commits

Reviewing files that changed from the base of the PR and between c00dae0 and 99d079a.

📒 Files selected for processing (19)
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/tailwind.config.js
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Bun Runtime
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: typecheck / typecheck
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: For apps and internal packages (apps/*, internal-packages/*), use pnpm run typecheck --filter <package> for verification, never use build as it proves almost nothing about correctness
Use testcontainers helpers (redisTest, postgresTest, containerTest from @internal/testcontainers) for integration tests with Redis and PostgreSQL instead of mocking
When writing Trigger.dev tasks, always import from @trigger.dev/sdk - never use @trigger.dev/sdk/v3 or deprecated client.defineJob

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

**/*.{ts,tsx,js,jsx}: Use pnpm for package management in this monorepo (version 10.23.0) with Turborepo for orchestration - run commands from root with pnpm run
Add crumbs as you write code for debug tracing using // @Crumbs comments or `// `#region` `@crumbs blocks - they stay on the branch throughout development and are stripped via agentcrumbs strip before merge

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/tailwind.config.js
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/tailwind.config.js
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/tailwind.config.js
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
apps/webapp/app/**/*.{ts,tsx,server.ts}

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Access environment variables via env export from app/env.server.ts. Never use process.env directly

Files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
🧠 Learnings (33)
📚 Learning: 2026-01-28T16:57:47.620Z
Learnt from: samejr
Repo: triggerdotdev/trigger.dev PR: 2964
File: apps/webapp/app/components/AskAI.tsx:121-141
Timestamp: 2026-01-28T16:57:47.620Z
Learning: In the trigger.dev webapp codebase, the Button component (apps/webapp/app/components/primitives/Buttons) does not spread unknown props to the underlying <button> element—it only passes specific props (type, disabled, onClick, name, value, ref, form, autoFocus). When using Radix UI's TooltipTrigger with asChild, a span wrapper around the Button is necessary to receive Radix props (aria-describedby, onPointerEnter, onPointerLeave, data-state) while the Button handles its own behavior. Directly making the Button the child of TooltipTrigger with asChild will break accessibility and tooltip functionality.

Applied to files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-03-22T19:24:14.403Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts:200-204
Timestamp: 2026-03-22T19:24:14.403Z
Learning: In the triggerdotdev/trigger.dev codebase, webhook URLs are not expected to contain embedded credentials/secrets (e.g., fields like `ProjectAlertWebhookProperties` should only hold credential-free webhook endpoints). During code review, if you see logging or inclusion of raw webhook URLs in error messages, do not automatically treat it as a credential-leak/secrets-in-logs issue by default—first verify the URL does not contain embedded credentials (for example, no username/password in the URL, no obvious secret/token query params or fragments). If the URL is credential-free per this project’s conventions, allow the logging.

Applied to files:

  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-03-22T13:32:47.004Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/ProvidersFilter.tsx:74-96
Timestamp: 2026-03-22T13:32:47.004Z
Learning: In the triggerdotdev/trigger.dev codebase, `FilterMenuProvider` (in `apps/webapp/app/components/runs/v3/SharedFilters.tsx`) wraps Ariakit's `ComboboxProvider` and exposes `(search, setSearch)` as render props. Filter components like `ProvidersFilter`, `OperationsFilter`, `PromptsFilter`, `ModelsFilter`, and `QueuesFilter` all use this pattern: `searchValue` (from the render prop) is passed into the dropdown child and used in a `useMemo` to filter options. Because Ariakit's ComboboxProvider re-renders the render prop on every keystroke, `searchValue` is reactive and the filtered list updates correctly. Do not flag these patterns as broken/unconnected search state — the wiring is intentional and correct.

Applied to files:

  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-03-22T13:32:40.255Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/PromptsFilter.tsx:74-98
Timestamp: 2026-03-22T13:32:40.255Z
Learning: In this repo’s filter components under apps/webapp/app/components/metrics/ (e.g., PromptsFilter, ModelsFilter, QueuesFilter), the search wiring intentionally uses a FilterMenuProvider render-prop pattern that wraps Ariakit’s ComboboxProvider. The render-prop `(search, setSearch)` is the live combobox search state, and the `searchValue` passed down to the inner `*Dropdown` component is designed to be updated on every keystroke. Do not raise review findings like “unconnected search state” or “broken filtering” for these components; the state propagation is correct by design and consistent with the other filter components in this directory.

Applied to files:

  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
📚 Learning: 2026-03-22T13:32:43.005Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/OperationsFilter.tsx:85-110
Timestamp: 2026-03-22T13:32:43.005Z
Learning: In triggerdotdev/trigger.dev, the shared `FilterMenuProvider` (from `~/components/runs/v3/SharedFilters`) manages Ariakit `ComboboxProvider` state internally and exposes the current `search` value and `setSearch` via render props `(search, setSearch) => …`. For filter components (e.g., `OperationsFilter`, `ModelsFilter`, `QueuesFilter`, `ProvidersFilter`), treat the render-prop tuple and their use of `searchValue={search}` and `clearSearchValue={() => setSearch("")}` as the correct wiring. Do not raise a review finding about missing/broken connection between the ComboBox input and the search state—if the component follows this provider/render-prop pattern and the `filtered` `useMemo` dependencies are reactive, the state synchronization is expected to be handled by Ariakit through the provider.

Applied to files:

  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `trigger.dev/sdk/v3` for all imports in Trigger.dev tasks

Applied to files:

  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger.config.ts : Configure OpenTelemetry instrumentations and exporters in trigger.config.ts for enhanced logging

Applied to files:

  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
📚 Learning: 2026-03-24T10:42:43.111Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3255
File: apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts:100-100
Timestamp: 2026-03-24T10:42:43.111Z
Learning: In `apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts` (and related span-handling code in trigger.dev), `span.entity` is a required (non-optional) field on the `SpanDetail` type and is always present. Do not flag `span.entity.type` as a potential null pointer / suggest optional chaining (`span.entity?.type`) in this context.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to apps/webapp/app/v3/**/*.{ts,tsx} : In the webapp v3 directory, only modify V2 code paths when encountering V1/V2 branching in services - all new work uses Run Engine 2.0 (`internal/run-engine`) and redis-worker, not legacy V1 engine code

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `tasks.trigger()` with type-only imports to trigger tasks from backend code without importing the task implementation

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use the `task()` function from `trigger.dev/sdk/v3` to define tasks with id and run properties

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2026-03-23T06:24:14.566Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-23T06:24:14.566Z
Learning: Applies to **/*.{ts,tsx} : When writing Trigger.dev tasks, always import from `trigger.dev/sdk` - never use `trigger.dev/sdk/v3` or deprecated `client.defineJob`

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use lifecycle functions (init, cleanup, onStart, onSuccess, onFailure, handleError) for task event handling

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use logger methods (debug, log, info, warn, error) from `trigger.dev/sdk/v3` for structured logging in tasks

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schedules.task()` for scheduled/cron tasks instead of regular `task()`

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `schemaTask()` from `trigger.dev/sdk/v3` with Zod schema for payload validation

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Use `yourTask.trigger()` to trigger a task from inside another task with specified payload

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Applies to **/trigger/**/*.{ts,tsx,js,jsx} : Export tasks with unique IDs within the project to enable proper task discovery and execution

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
📚 Learning: 2026-03-22T13:32:44.229Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/ProvidersFilter.tsx:74-96
Timestamp: 2026-03-22T13:32:44.229Z
Learning: When reviewing components under `apps/webapp/app/components/runs/v3/`, avoid flagging “broken/unconnected search state” in filters that use `FilterMenuProvider` wrapping Ariakit’s `ComboboxProvider` and expose `(search, setSearch)` (render props). In this intentional pattern, the `searchValue` render-prop value should be treated as reactive (it re-renders on every keystroke), passed into the dropdown child, and used in `useMemo` to filter options. Do not require additional wiring beyond this established render-prop/ComboboxProvider integration.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:26:58.661Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/webapp.mdc:0-0
Timestamp: 2025-11-27T16:26:58.661Z
Learning: Applies to apps/webapp/**/*.{ts,tsx} : Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-03-21T21:23:35.117Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts:149-150
Timestamp: 2026-03-21T21:23:35.117Z
Learning: In `apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts` (and related AI span extraction files in `apps/webapp/app/components/runs/v3/ai/`), manual JSON.parse with typeof guards and type assertions is intentional. These functions are on the hot path for span rendering, so Zod validation is deliberately avoided for performance reasons. Do not suggest replacing manual JSON parsing with Zod schemas in these files.

Applied to files:

  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Use `useRun`, `useRealtimeRun` and other SWR/realtime hooks from `trigger.dev/react-hooks` for data fetching

Applied to files:

  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
📚 Learning: 2026-02-03T18:27:49.039Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:49.039Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (like the Edit button with PencilSquareIcon) intentionally have no text labels - only icons are shown in the TableCellMenu. This is a deliberate UI design pattern for compact icon-only menu items.

Applied to files:

  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
📚 Learning: 2025-11-27T16:27:35.304Z
Learnt from: CR
Repo: triggerdotdev/trigger.dev PR: 0
File: .cursor/rules/writing-tasks.mdc:0-0
Timestamp: 2025-11-27T16:27:35.304Z
Learning: Run `npx trigger.devlatest init` to initialize a Trigger.dev project

Applied to files:

  • apps/webapp/app/components/BlankStatePanels.tsx
📚 Learning: 2026-03-22T13:45:36.346Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/navigation/SideMenu.tsx:460-489
Timestamp: 2026-03-22T13:45:36.346Z
Learning: In triggerdotdev/trigger.dev, sidebar navigation items (SideMenu.tsx) are intentionally NOT gated behind feature-flag or permission checks at the nav level. Authorization is enforced at the route/loader level instead. Hiding nav items based on access checks is considered confusing UX. This applies to items like "AI Metrics" (v3BuiltInDashboardPath) and other dashboard links — they are always rendered in the sidebar regardless of hasQueryAccess or similar flags.

Applied to files:

  • apps/webapp/app/components/navigation/SideMenu.tsx
📚 Learning: 2026-03-22T13:32:43.689Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/PromptsFilter.tsx:74-98
Timestamp: 2026-03-22T13:32:43.689Z
Learning: In triggerdotdev/trigger.dev, filter components (e.g. PromptsFilter, ModelsFilter, QueuesFilter in apps/webapp/app/components/metrics/) use a `FilterMenuProvider` render-prop pattern that wraps Ariakit's ComboboxProvider. The render props `(search, setSearch)` represent live combobox search state; `searchValue` passed to the inner `*Dropdown` component is always up-to-date on each keystroke. Do not flag these components for "unconnected search state" or "broken filtering" — the wiring is correct by design and consistent across all filter components.

Applied to files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
📚 Learning: 2026-01-28T14:15:09.778Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2953
File: apps/webapp/app/components/runs/v3/SharedFilters.tsx:441-452
Timestamp: 2026-01-28T14:15:09.778Z
Learning: In apps/webapp/app/components/runs/v3/SharedFilters.tsx, enforce the maxPeriodDays limit based only on the from date (fromValue) using dateRangeToDays(fromValue) to reflect data retention limits. The to date should be ignored for retention checks. Ensure any retention-window logic or validations rely solely on the fromValue, and update related comments/tests if needed.

Applied to files:

  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
📚 Learning: 2026-03-22T13:32:46.525Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/OperationsFilter.tsx:85-110
Timestamp: 2026-03-22T13:32:46.525Z
Learning: In triggerdotdev/trigger.dev, `FilterMenuProvider` (from `~/components/runs/v3/SharedFilters`) wraps Ariakit's `ComboboxProvider` and exposes the current search string and setter via render props `(search, setSearch) => …`. Components like `OperationsFilter`, `ModelsFilter`, `QueuesFilter`, and `ProvidersFilter` all use this pattern: they pass `searchValue={search}` and `clearSearchValue={() => setSearch("")}` into their inner dropdown component, and the `filtered` useMemo is correctly reactive. Do not flag this as a missing/broken wiring between the ComboBox input and the search state — the state is managed by Ariakit's provider and surfaced through the render-prop tuple.

Applied to files:

  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
🔇 Additional comments (20)
apps/webapp/app/components/primitives/TextArea.tsx (1)

11-11: Nice cleanup on the textarea class list.

Removing the duplicated focus utility keeps styles predictable without changing behavior.

apps/webapp/app/components/metrics/ProvidersFilter.tsx (1)

16-16: Shortcut remap looks good.

The Providers trigger shortcut update is clean and safely scoped to UI behavior.

apps/webapp/app/components/metrics/ModelsFilter.tsx (1)

19-19: Shortcut update is solid.

The key change is narrowly scoped and aligns with the UI shortcut cleanup in this PR.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx (4)

85-89: LGTM!

The documentation link follows the established pattern for adding page accessories with a docs button. Clean implementation.


118-131: LGTM!

Good simplification of the table cell markup. The description truncation with tooltip for long text is a nice UX touch.


136-147: Good use of semantic color tokens.

Switching from hardcoded Tailwind colors (bg-amber-400/bg-green-500) to semantic tokens (bg-warning/bg-success) improves maintainability and ensures consistency with the design system.


174-185: LGTM!

The sparkline component is clean and handles the empty/zero data case appropriately.

apps/webapp/app/components/primitives/Resizable.tsx (1)

7-16: LGTM on the slimmer PanelGroup wrapper.

Keeping snapshot inside ...props preserves the existing snapshot={...} callers while simplifying this wrapper.

apps/webapp/app/components/runs/v3/SpanTitle.tsx (2)

2-2: LGTM!

The type-only import for TaskEventStyle is correct since it's only used as a type annotation, improving tree-shaking.


22-22: LGTM!

Adding text-text-bright improves visual consistency for span messages.

apps/webapp/app/components/layout/AppLayout.tsx (1)

23-35: LGTM!

The PageContainer update follows the same pattern as other container components in this file (AppContainer, PageBody, MainCenteredContainer), maintaining API consistency.

apps/webapp/app/assets/icons/AIPromptsIcon.tsx (1)

1-10: LGTM!

The icon component follows established patterns with fill="currentColor" for Tailwind color utility support and an optional className prop for styling flexibility.

apps/webapp/tailwind.config.js (1)

178-179: LGTM!

The new aiPrompts and aiMetrics color tokens follow the established pattern for semantic icon colors in this config (e.g., tasks, runs, batches).

Also applies to: 263-264

apps/webapp/app/assets/icons/AIMetricsIcon.tsx (1)

1-16: LGTM!

The icon component is consistent with AIPromptsIcon and follows established patterns.

apps/webapp/app/components/runs/v3/SharedFilters.tsx (2)

379-388: LGTM!

The shortcut integration correctly prevents default behavior and stops propagation before programmatically clicking the trigger. Using a ref to trigger the click is a valid pattern for opening dropdowns via keyboard shortcuts.


403-433: LGTM!

The Ariakit tooltip integration is well-structured: the TooltipAnchor wraps the Select using the render prop pattern, and the Tooltip is conditionally rendered only when a shortcut is provided. The z-40 ensures proper stacking.

apps/webapp/app/components/code/CodeBlock.tsx (1)

268-271: LGTM!

Converting to flex column layout with overflow-hidden on the outer container provides better control over child overflow behavior, which is necessary for the flex-1 and min-h-0 approach used by inner elements.

apps/webapp/app/components/BlankStatePanels.tsx (2)

696-697: LGTM!

Switching to AIPromptsIcon with the new text-aiPrompts color token aligns with the PR's goal of consistent AI-related styling.


717-730: LGTM!

Replacing the hardcoded <pre> with the shared CodeBlock component improves consistency and leverages built-in features like copy functionality. The showLineNumbers={false} and showOpenInModal={false} options are appropriate for this inline example context.

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx (1)

488-492: Nice compact metadata copy UX.

Surfacing slug, friendly ID, and description copy actions from the title popover keeps the header clean while still making the common identifiers easy to grab.

Also applies to: 2017-2118

@ericallam ericallam force-pushed the feat(webapp)-ai-screen-ui-improvements branch from 99d079a to a08ffac Compare March 25, 2026 15:02
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 4 new potential issues.

View 6 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (1)

305-312: Consider extracting and sharing formatSpanDuration to avoid divergence.

This file has its own formatSpanDuration implementation that differs from the one in SpanHorizontalTimeline.tsx. The route version still uses ((ms % 60_000) / 1000).toFixed(0) which can produce "1m 60s" for edge cases like ms = 119600.

Since this function isn't used in the current render paths (the removed header row was the consumer), you could either:

  1. Remove this unused function, or
  2. Export the fixed version from SpanHorizontalTimeline.tsx and reuse it here if needed elsewhere.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
around lines 305 - 312, The local formatSpanDuration implementation can produce
edge-case outputs like "1m 60s" and diverges from the canonical version in
SpanHorizontalTimeline.tsx; either remove this unused formatSpanDuration from
route.tsx, or replace it by importing the fixed, exported formatSpanDuration
from SpanHorizontalTimeline.tsx so both locations share one implementation
(update exports in SpanHorizontalTimeline.tsx if needed and change the route to
import and use that exported function).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/webapp/app/routes/resources.orgs`.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx:
- Around line 305-312: The local formatSpanDuration implementation can produce
edge-case outputs like "1m 60s" and diverges from the canonical version in
SpanHorizontalTimeline.tsx; either remove this unused formatSpanDuration from
route.tsx, or replace it by importing the fixed, exported formatSpanDuration
from SpanHorizontalTimeline.tsx so both locations share one implementation
(update exports in SpanHorizontalTimeline.tsx if needed and change the route to
import and use that exported function).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5ae95c53-6711-4e76-9a2f-47b80e99a110

📥 Commits

Reviewing files that changed from the base of the PR and between 99d079a and a08ffac.

📒 Files selected for processing (19)
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
  • apps/webapp/tailwind.config.js
✅ Files skipped from review due to trivial changes (7)
  • apps/webapp/app/components/metrics/ProvidersFilter.tsx
  • apps/webapp/app/components/primitives/TextArea.tsx
  • apps/webapp/app/components/metrics/ModelsFilter.tsx
  • apps/webapp/app/assets/icons/AIMetricsIcon.tsx
  • apps/webapp/tailwind.config.js
  • apps/webapp/app/components/code/CodeBlock.tsx
  • apps/webapp/app/assets/icons/AIPromptsIcon.tsx
🚧 Files skipped from review as they are similar to previous changes (9)
  • apps/webapp/app/components/runs/v3/PromptSpanDetails.tsx
  • apps/webapp/app/components/runs/v3/SpanTitle.tsx
  • apps/webapp/app/components/runs/v3/ai/AISpanDetails.tsx
  • apps/webapp/app/components/layout/AppLayout.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts._index/route.tsx
  • apps/webapp/app/components/BlankStatePanels.tsx
  • apps/webapp/app/components/runs/v3/SharedFilters.tsx
  • apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.prompts.$promptSlug/route.tsx
  • apps/webapp/app/components/navigation/SideMenu.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (27)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (1, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (5, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (7, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (3, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (2, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (1, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (7, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (8, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (3, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (6, 8)
  • GitHub Check: units / internal / 🧪 Unit Tests: Internal (4, 8)
  • GitHub Check: units / webapp / 🧪 Unit Tests: Webapp (2, 8)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - pnpm)
  • GitHub Check: sdk-compat / Deno Runtime
  • GitHub Check: units / packages / 🧪 Unit Tests: Packages (1, 1)
  • GitHub Check: typecheck / typecheck
  • GitHub Check: sdk-compat / Node.js 22.12 (ubuntu-latest)
  • GitHub Check: sdk-compat / Node.js 20.20 (ubuntu-latest)
  • GitHub Check: sdk-compat / Cloudflare Workers
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - pnpm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (windows-latest - npm)
  • GitHub Check: e2e / 🧪 CLI v3 tests (ubuntu-latest - npm)
  • GitHub Check: sdk-compat / Bun Runtime
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{ts,tsx}: Use types over interfaces for TypeScript
Avoid using enums; prefer string unions or const objects instead

**/*.{ts,tsx}: For apps and internal packages (apps/*, internal-packages/*), use pnpm run typecheck --filter <package> for verification, never use build as it proves almost nothing about correctness
Use testcontainers helpers (redisTest, postgresTest, containerTest from @internal/testcontainers) for integration tests with Redis and PostgreSQL instead of mocking
When writing Trigger.dev tasks, always import from @trigger.dev/sdk - never use @trigger.dev/sdk/v3 or deprecated client.defineJob

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
{packages/core,apps/webapp}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use zod for validation in packages/core and apps/webapp

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use function declarations instead of default exports

**/*.{ts,tsx,js,jsx}: Use pnpm for package management in this monorepo (version 10.23.0) with Turborepo for orchestration - run commands from root with pnpm run
Add crumbs as you write code for debug tracing using // @Crumbs comments or `// `#region` `@crumbs blocks - they stay on the branch throughout development and are stripped via agentcrumbs strip before merge

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

Access all environment variables through the env export of env.server.ts instead of directly accessing process.env in the Trigger.dev webapp

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/webapp.mdc)

apps/webapp/**/*.{ts,tsx}: When importing from @trigger.dev/core in the webapp, use subpath exports from the package.json instead of importing from the root path
Follow the Remix 2.1.0 and Express server conventions when updating the main trigger.dev webapp

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
**/*.{js,ts,jsx,tsx,json,md,yaml,yml}

📄 CodeRabbit inference engine (AGENTS.md)

Format code using Prettier before committing

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

When modifying only server components (apps/webapp/, apps/supervisor/, etc.) with no package changes, add a .server-changes/ file instead of a changeset

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
apps/webapp/app/**/*.{ts,tsx,server.ts}

📄 CodeRabbit inference engine (apps/webapp/CLAUDE.md)

Access environment variables via env export from app/env.server.ts. Never use process.env directly

Files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
🧠 Learnings (13)
📚 Learning: 2026-03-24T10:42:43.111Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3255
File: apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts:100-100
Timestamp: 2026-03-24T10:42:43.111Z
Learning: In `apps/webapp/app/routes/api.v1.runs.$runId.spans.$spanId.ts` (and related span-handling code in trigger.dev), `span.entity` is a required (non-optional) field on the `SpanDetail` type and is always present. Do not flag `span.entity.type` as a potential null pointer / suggest optional chaining (`span.entity?.type`) in this context.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-01-28T14:15:15.011Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 2953
File: apps/webapp/app/components/runs/v3/SharedFilters.tsx:441-452
Timestamp: 2026-01-28T14:15:15.011Z
Learning: In apps/webapp/app/components/runs/v3/SharedFilters.tsx, the maxPeriodDays limit for date ranges should only check the from date (via dateRangeToDays(fromValue)) because it enforces data retention limits—how far back in history queries can reach. The to date is irrelevant for retention-based limits.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
📚 Learning: 2026-03-22T13:51:25.797Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/presenters/v3/PromptPresenter.server.ts:100-141
Timestamp: 2026-03-22T13:51:25.797Z
Learning: In the triggerdotdev/trigger.dev codebase, the ClickHouse server is configured with UTC as its timezone. Therefore, `toStartOfHour(start_time)` (without an explicit timezone argument) in ClickHouse queries correctly returns UTC-formatted strings that align with JavaScript `toISOString()`-derived UTC bucket keys (e.g., in `apps/webapp/app/presenters/v3/PromptPresenter.server.ts`). Do not flag this as a timezone mismatch bug.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
📚 Learning: 2026-03-22T13:51:25.797Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/presenters/v3/PromptPresenter.server.ts:100-141
Timestamp: 2026-03-22T13:51:25.797Z
Learning: In the triggerdotdev/trigger.dev codebase, the ClickHouse server is configured with UTC timezone. Therefore, `toStartOfHour(start_time)` (without an explicit timezone argument) in ClickHouse queries returns UTC-formatted strings, which correctly align with JavaScript `toISOString()`-derived UTC bucket keys. Do not flag this pattern as a timezone mismatch bug.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
📚 Learning: 2026-02-11T16:37:32.429Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/components/primitives/charts/Card.tsx:26-30
Timestamp: 2026-02-11T16:37:32.429Z
Learning: In projects using react-grid-layout, avoid relying on drag-handle class to imply draggability. Ensure drag-handle elements only affect dragging when the parent grid item is configured draggable in the layout; conditionally apply cursor styles based on the draggable prop. This improves correctness and accessibility.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-03-22T13:26:12.060Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/code/TextEditor.tsx:81-86
Timestamp: 2026-03-22T13:26:12.060Z
Learning: In the triggerdotdev/trigger.dev codebase, do not flag `navigator.clipboard.writeText(...)` calls for `missing-await`/`unhandled-promise` issues. These clipboard writes are intentionally invoked without `await` and without `catch` handlers across the project; keep that behavior consistent when reviewing TypeScript/TSX files (e.g., usages like in `apps/webapp/app/components/code/TextEditor.tsx`).

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-03-22T19:24:14.403Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3187
File: apps/webapp/app/v3/services/alerts/deliverErrorGroupAlert.server.ts:200-204
Timestamp: 2026-03-22T19:24:14.403Z
Learning: In the triggerdotdev/trigger.dev codebase, webhook URLs are not expected to contain embedded credentials/secrets (e.g., fields like `ProjectAlertWebhookProperties` should only hold credential-free webhook endpoints). During code review, if you see logging or inclusion of raw webhook URLs in error messages, do not automatically treat it as a credential-leak/secrets-in-logs issue by default—first verify the URL does not contain embedded credentials (for example, no username/password in the URL, no obvious secret/token query params or fragments). If the URL is credential-free per this project’s conventions, allow the logging.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
  • apps/webapp/app/components/primitives/Resizable.tsx
  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-03-22T13:32:44.229Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/metrics/ProvidersFilter.tsx:74-96
Timestamp: 2026-03-22T13:32:44.229Z
Learning: When reviewing components under `apps/webapp/app/components/runs/v3/`, avoid flagging “broken/unconnected search state” in filters that use `FilterMenuProvider` wrapping Ariakit’s `ComboboxProvider` and expose `(search, setSearch)` (render props). In this intentional pattern, the `searchValue` render-prop value should be treated as reactive (it re-renders on every keystroke), passed into the dropdown child, and used in `useMemo` to filter options. Do not require additional wiring beyond this established render-prop/ComboboxProvider integration.

Applied to files:

  • apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx
📚 Learning: 2026-02-11T16:50:14.167Z
Learnt from: matt-aitken
Repo: triggerdotdev/trigger.dev PR: 3019
File: apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx:126-131
Timestamp: 2026-02-11T16:50:14.167Z
Learning: In apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.dashboards.$dashboardId.widgets.tsx, MetricsDashboard entities are intentionally scoped to the organization level, not the project level. The dashboard lookup should filter by organizationId only (not projectId), allowing dashboards to be accessed across projects within the same organization. The optional projectId field on MetricsDashboard serves other purposes and should not be used as an authorization constraint.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2025-12-08T15:19:56.823Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2760
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx:278-281
Timestamp: 2025-12-08T15:19:56.823Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam/route.tsx, the tableState search parameter uses intentional double-encoding: the parameter value contains a URL-encoded URLSearchParams string, so decodeURIComponent(value("tableState") ?? "") is required to fully decode it before parsing with new URLSearchParams(). This pattern allows bundling multiple filter/pagination params as a single search parameter.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-03-21T21:23:35.117Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3244
File: apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts:149-150
Timestamp: 2026-03-21T21:23:35.117Z
Learning: In `apps/webapp/app/components/runs/v3/ai/extractAISummarySpanData.ts` (and related AI span extraction files in `apps/webapp/app/components/runs/v3/ai/`), manual JSON.parse with typeof guards and type assertions is intentional. These functions are on the hot path for span rendering, so Zod validation is deliberately avoided for performance reasons. Do not suggest replacing manual JSON parsing with Zod schemas in these files.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-03-13T13:43:06.471Z
Learnt from: ericallam
Repo: triggerdotdev/trigger.dev PR: 3213
File: apps/webapp/app/components/runs/v3/ai/extractAISpanData.ts:52-52
Timestamp: 2026-03-13T13:43:06.471Z
Learning: In the trigger.dev codebase (PR `#3213`), `extractAISpanData.ts` (`apps/webapp/app/components/runs/v3/ai/extractAISpanData.ts`) is a read-side UI helper that reads already-enriched `trigger.llm.*` span attributes for display. The actual LLM cost computation and gateway/OpenRouter cost fallback logic lives in `enrichCreatableEvents.server.ts` (`apps/webapp/app/v3/utils/enrichCreatableEvents.server.ts`) via `extractProviderCost()`. The `gatewayCost` parsed in `extractAISpanData` is for UI display purposes only, not for cost calculation.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
📚 Learning: 2026-02-03T18:27:40.429Z
Learnt from: 0ski
Repo: triggerdotdev/trigger.dev PR: 2994
File: apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx:553-555
Timestamp: 2026-02-03T18:27:40.429Z
Learning: In apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx, the menu buttons (e.g., Edit with PencilSquareIcon) in the TableCellMenu are intentionally icon-only with no text labels as a compact UI pattern. This is a deliberate design choice for this route; preserve the icon-only behavior for consistency in this file.

Applied to files:

  • apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx
🔇 Additional comments (6)
apps/webapp/app/components/primitives/Resizable.tsx (3)

28-30: Restore focus after preventing default to preserve keyboard accessibility.

The e.preventDefault() on mousedown prevents text selection during drag (good), but also prevents the handle from receiving focus on click. This breaks keyboard resizing until the user tabs back to the handle.

♿ Suggested fix
 const ResizableHandle = ({
   withHandle = true,
   className,
+  onMouseDown,
   ...props
 }: React.ComponentProps<typeof PanelResizer> & {
   withHandle?: boolean;
 }) => (
   <PanelResizer
-    onMouseDown={(e: React.MouseEvent) => {
+    onMouseDown={(e: React.MouseEvent<HTMLElement>) => {
+      onMouseDown?.(e);
+      if (e.defaultPrevented) return;
+
       e.preventDefault();
+      e.currentTarget.focus();
     }}

7-16: LGTM!

The refactored ResizablePanelGroup cleanly preserves functionality. The snapshot prop correctly flows through via the spread, and the autosaveStrategy computation based on autosaveId is appropriately placed before the spread to allow explicit overrides when needed.


51-57: LGTM!

The visual refinements are well-implemented: pointer-events-none on the overlay divs correctly prevents interaction interference, and the opacity transition approach provides smoother UX than the previous display toggling.

apps/webapp/app/components/runs/v3/SpanHorizontalTimeline.tsx (1)

1-54: LGTM!

The component is well-structured with clear separation between the duration formatting logic and the timeline rendering. The nanosecond-to-millisecond conversion is handled correctly, and the conditional rendering based on duration availability is clean.

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx (2)

1413-1452: Clean integration of timing props across AI span entity types.

The consistent pattern of passing startTime and duration to the detail components, combined with wrapping ai-tool-call and ai-embed types with SpanHorizontalTimeline, provides a unified timing display across all AI-related spans. The scrollable container wrapper is appropriate for these detail views.


1426-1443: No changes needed. The span.duration field is a required number property on the SpanDetail type, not optional. Passing it directly to SpanHorizontalTimeline (which accepts duration: number | null) is type-safe. The defensive duration ?? null pattern used in AISpanDetails and PromptSpanDetails is necessary in those components because they receive optional duration parameters from their callers, not because span.duration itself could be undefined.

			> Likely an incorrect or invalid review comment.

@ericallam ericallam merged commit 2037254 into main Mar 25, 2026
63 checks passed
@ericallam ericallam deleted the feat(webapp)-ai-screen-ui-improvements branch March 25, 2026 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants