Files
James Ritchie af526dea18 feat(webapp): chat AI UI improvements, new task landing pages and side menu (#3941)
Major dashboard restructure plus the new task landing pages and
self-serve schedules add-on integration.

## Side menu

- Full restructure: standalone Tasks / Runs / Sessions block at the top;
new collapsible sections for AI, Observability, Deployments, Manage
- Persisted collapse state per section in `dashboardPreferences`
- New / updated icons across the menu
- Dashboards section: built-in Run metrics + AI metrics + custom
dashboards, with drag-to-reorder via ReactGridLayout
(`DashboardList.tsx`)
- DevPresence connection indicator in the env selector (DEV + V2)

## Tasks (`_index` — unified Tasks page)

- Replaces the separated Agents / Standard / Schedules listing pages
with one table
- New `UnifiedTaskListPresenter` composes `TaskListPresenter` +
`AgentListPresenter` (shared `currentWorker` lookup)
- Columns: Type (with kind badge), ID, File, Running (numeric for tasks;
running + suspended pills for agents), Activity (24h stacked-by-status),
sticky menu
- Search + "Task type" multi-select filter (URL-synced)
- Client-side pagination at 25/page
- Right-hand "useful links" panel (cookie-persisted state)
- Live-reload SSE: page revalidates on `WORKER_CREATED` so onboarding
`trigger dev` flips the blank state automatically

## Agent landing page (`/agents/$agentParam`)

- New per-agent detail page
- Top tabs (Sessions / Runs) toggle both the chart panel and the table
- Three dashboard-style chart cards: Sessions/Runs activity, LLM spend,
Tokens
- `AgentDetailPresenter` queries ClickHouse for run activity, session
activity (with FINAL on `sessions_v1`), and LLM cost/token activity from
`llm_metrics_v1`
- TimeFilter at the top drives all three charts
- Sticky table header, resizable horizontal handle, sidebar with Test
agent button + properties
- Docs link → `ai-chat/overview`

## Standard Task landing page (`/tasks/standard/$taskParam`)

- New per-task detail page mirroring the Agent layout
- `TaskDetailPresenter` for activity + properties
- Chart panel wrapped in a Card with "Runs by status" header
- Top bar with title, TimeFilter, pagination
- Right sidebar: Test task + identifier, queue, machine, retry, TTL,
payload schema, etc.

## Scheduled Task landing page (`/tasks/scheduled/$taskParam`)

- New per-task detail page mirroring the Agent / Standard layout
- Top-bar actions (right → left): pagination, Bulk replay…, View all
runs, TimeFilter, Create schedule
- Connected schedules mini-table in the sidebar
- **Self-serve schedules add-on integration** (reincarnated from the
now-removed `/schedules` listing page during the `origin/main` merge):
- Bottom usage bar pinned via `grid-rows-[auto_1fr_auto]` — progress
ring + "X/Y of your schedules" + Purchase / Upgrade / Request CTA
  - At-limit "Create schedule" intercept dialog
- `PurchaseSchedulesModal` extracted as a shared component
(`apps/webapp/app/components/schedules/PurchaseSchedulesModal.tsx`)
handling increase / decrease / above-quota / need-to-delete states
- New resource action route at
`/resources/orgs/$organizationSlug/schedules-addon`

## Sessions

- Index page: list, filters, blank state, help tooltip rework
- Detail page: combined input/output chronological view (replaces split
tabs)
- Improved raw-message view layout (full-height)
- AI payload UI: `data-*` parts grouped under "AI SDK data parts:" label
- `toSafeUrl` helper guards rendered URLs from streamed content
- Fix: duplicate assistant content on inspector tab switch

## Playground (Test agent)

- Restructured top menu; back button + agent-selector popover
- Improved blank state
- Recent agent chat history moved into the tabbed menu
- Better message-scroll container (full height)

## Dashboards

- New Dashboards landing page (`/dashboards`) — Run metrics, AI metrics,
Create your own CTAs
- `BuiltInDashboards` updated; new `TasksDashboardPresenter` for the
tasks overview
- Custom dashboards section gains drag-to-reorder; cosmetic fix for
active-row drag-handle blending

## PageHeader / shared primitives

- `PageTitle` gains an `accessory` prop supporting string (auto-wrapped
in tooltip) and ReactNode
- Help tooltips on Tasks, Runs, Sessions PageTitles explaining the
concept and sub-categories
- `Card` primitive used for dashboard-style chart panels throughout

## Code review fixes (last batch on this branch)

- ClickHouse activity queries hardened: `FINAL` + `_is_deleted = 0` on
`task_runs_v2` (ReplacingMergeTree); `organization_id` + `project_id`
filters for sort-key prefix; `inserted_at` partition filter on
`llm_metrics_v1`
- `UnifiedTaskListPresenter`: shared `currentWorker` lookup;
slug-collision guard in `mergeRunningStates`; off-by-one fixed in 24h
bucket alignment
- `ScheduleListPresenter`: halved platform RPCs by deriving limit from
`currentPlan` instead of calling `getLimit`
- Sessions detail: stopped IntersectionObserver / scroll listener
re-attach on every chunk; `requestAnimationFrame` deferral on
auto-scroll to avoid virtualizer race
- URL hardening: `?types=` validated against known kinds; new
`parseFiniteInt` helper applied to `from`/`to`/`page` params
- AgentView: HITL resolution buffer now cleared once parts reach a
terminal state (was an unbounded Map on long sessions); subscription
effect deps documented with eslint suppression
- `PurchaseSchedulesModal`: bundle state resets on each open instead of
persisting stale drafts

## Manual testing

Manual smoke-test plan is tracked under
[TRI-10883](https://linear.app/triggerdotdev/issue/TRI-10883), broken
into 20 sub-issues covering onboarding, self-serve schedules, side menu,
the four landing pages, sessions, runs, dashboards, regressions and
performance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-15 12:10:50 +01:00

257 lines
6.3 KiB
CSS

@import url("non.geist");
@import url("non.geist/mono");
@import "react-grid-layout/css/styles.css";
@import "react-resizable/css/styles.css";
/* Override react-grid-layout placeholder color (default is red) */
.react-grid-item.react-grid-placeholder {
background: rgb(99 102 241) !important; /* indigo-500 */
border-radius: 0.375rem !important; /* rounded-md */
}
/* Sidebar reorder grid: subtle placeholder */
.sidebar-reorder-grid .react-grid-item.react-grid-placeholder {
background: rgb(39 42 46) !important; /* charcoal-700 */
border-radius: 0.25rem;
opacity: 1 !important;
}
/* Sidebar reorder grid: only animate transform (vertical position), not width/height */
.sidebar-reorder-grid .react-grid-item {
transition: transform 200ms ease !important;
}
/* Override resize handle icon to white */
.react-resizable-handle {
background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9zdmc+') !important;
}
.react-resizable-handle::after {
border-bottom-color: rgba(255, 255, 255, 0.4) !important;
border-right-color: rgba(255, 255, 255, 0.4) !important;
border-top-color: rgba(255, 255, 255, 0.4) !important;
border-left-color: rgba(255, 255, 255, 0.4) !important;
}
@tailwind base;
@tailwind components;
@tailwind utilities;
@property --gradient-angle {
syntax: "<angle>";
initial-value: 0deg;
inherits: false;
}
@supports (-webkit-hyphens: none) {
.safari-only {
@apply border-b border-grid-dimmed;
}
}
@layer base {
* {
@apply border-grid-bright;
}
body {
@apply bg-background-dimmed text-text-dimmed;
font-feature-settings: "rlig" 1, "calt" 1;
}
/* Text selection styles */
::selection {
@apply bg-text-bright/30 text-text-bright;
}
::-moz-selection {
@apply bg-text-bright/30 text-text-bright;
}
/* shadcn charts: https://ui.shadcn.com/docs/components/chart#add-a-grid */
:root {
--chart-1: 12 76% 61%;
--chart-2: 173 58% 39%;
--chart-3: 197 37% 24%;
--chart-4: 43 74% 66%;
--chart-5: 27 87% 67%;
}
.dark {
--chart-1: 220 70% 50%;
--chart-2: 160 60% 45%;
--chart-3: 30 80% 55%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
}
@layer utilities {
.scrollbar-gutter-stable {
scrollbar-gutter: stable;
}
.animated-gradient-glow {
position: relative;
overflow: visible;
}
.animated-gradient-glow::before {
content: "";
position: absolute;
inset: -8px;
z-index: -1;
background: conic-gradient(
from var(--gradient-angle),
rgb(99 102 241),
rgb(245 158 11),
rgb(236 72 153),
rgb(245 158 11),
rgb(99 102 241)
);
border-radius: inherit;
animation: gradient-rotation 3s linear infinite;
pointer-events: none;
filter: blur(0.5rem);
opacity: 0.1;
}
.animated-gradient-glow-small {
position: relative;
overflow: visible;
}
.animated-gradient-glow-small::before {
content: "";
position: absolute;
inset: -1px;
z-index: -1;
background: conic-gradient(
from var(--gradient-angle),
rgb(99 102 241),
rgb(245 158 11),
rgb(236 72 153),
rgb(245 158 11),
rgb(99 102 241)
);
border-radius: inherit;
animation: gradient-rotation 3s linear infinite;
pointer-events: none;
filter: blur(0.2rem);
opacity: 0.3;
}
}
@keyframes gradient-rotation {
0% {
--gradient-angle: 0deg;
}
100% {
--gradient-angle: 360deg;
}
}
/* Streamdown markdown styling */
.streamdown-container {
/* Streamdown uses shadcn/ui CSS variables - define them for our theme.
These map Tailwind utility classes like bg-background, bg-primary, etc.
that streamdown uses internally for its link safety modal, code blocks,
and other interactive elements. */
--background: 230 16% 9%; /* charcoal-900 #121317 */
--foreground: 215 19% 87%; /* charcoal-200 #D7D9DD */
--muted: 220 8% 17%; /* charcoal-775 #1C1E21 */
--muted-foreground: 220 8% 57%; /* charcoal-400 #878C99 */
--border: 216 7% 27%; /* charcoal-650 #2C3034 */
--primary: 95 100% 66%; /* apple-500 #A8FF53 */
--primary-foreground: 230 16% 9%; /* charcoal-900 */
--sidebar: 228 10% 11%; /* charcoal-850 #15171A */;
/* Code block styling */
& [data-code-block-container] {
@apply rounded border-charcoal-650 my-0;
}
& [data-code-block] {
border-top: none;
}
& p {
@apply my-1;
}
& h1, & h2, & h3, & h4, & h5, & h6 {
@apply font-semibold text-text-bright mt-2 mb-1;
}
& h1 {
@apply text-base;
}
& h2 {
@apply text-sm;
}
& h3, & h4, & h5, & h6 {
@apply text-xs;
}
& ul, & ol {
@apply ml-4 my-1;
}
& ul {
@apply list-disc;
}
& ol {
@apply list-decimal;
}
& li {
@apply my-0.5;
}
/* Inline code (not in pre blocks) */
& code:not(pre code) {
@apply bg-charcoal-700 px-1 py-0.5 rounded text-text-bright font-mono;
}
& blockquote {
@apply border-l-2 border-charcoal-600 pl-3 my-2 italic;
}
& a {
@apply text-text-link hover:underline;
}
& strong {
@apply font-semibold text-text-bright;
}
& em {
@apply italic;
}
& hr {
@apply my-2 border-charcoal-600;
}
& table {
@apply w-full my-2 border-collapse;
}
& th, & td {
@apply border border-charcoal-600 px-2 py-1 text-left;
}
& th {
@apply bg-charcoal-700 font-semibold;
}
/* Streamdown code block container */
& [data-code-block-container] {
@apply my-2 border-charcoal-700;
}
& [data-code-block-header] {
@apply bg-charcoal-800 text-text-dimmed border-b border-charcoal-700;
}
/* Hide light mode code block, show dark mode */
& [data-code-block].dark\:hidden {
display: none !important;
}
& [data-code-block].hidden.dark\:block {
display: block !important;
}
/* Override the bg-muted/40 class to let inline styles work */
& [data-code-block] pre {
background-color: inherit !important;
@apply scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600;
}
& [data-code-block] pre code {
@apply bg-transparent;
}
& [data-code-block] .line {
@apply leading-relaxed;
}
}