Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
7f0ab64
feat(webapp): the dashboard agent's UI
kathiekiwi Aug 7, 2026
9e25c5c
style(webapp): use the agent's own logo in the Help & Feedback entry
kathiekiwi Aug 7, 2026
53ecf11
fix(webapp): drop Keep digging once the turn has already kept digging
kathiekiwi Aug 7, 2026
6203393
feat(webapp): give Cmd-I and the CLI's help link back to Ask AI
kathiekiwi Aug 8, 2026
9e8b7c1
test(webapp): give the untrustworthy report fixture the reason field …
kathiekiwi Aug 8, 2026
13312e4
fix(webapp): give each copyable task snippet its own SDK import
kathiekiwi Aug 8, 2026
f6d5b44
fix(webapp): drop a run diagnosis action that has nowhere to go
kathiekiwi Aug 8, 2026
eae15fa
fix(webapp): stop Cmd-J opening Chrome's downloads
kathiekiwi Aug 8, 2026
202bb94
fix(webapp): keep an empty chat open instead of showing a new one
kathiekiwi Aug 8, 2026
a6584c8
docs: say where the docs went now the header button is gone
kathiekiwi Aug 8, 2026
770ad36
perf(webapp): keep report blocks out of the investigation winner pass
kathiekiwi Aug 8, 2026
8f52bd1
fix(webapp): give icon-only tooltip buttons an accessible name
kathiekiwi Aug 8, 2026
765a1c6
fix(webapp): stop a zero-limit queue reading as at capacity
kathiekiwi Aug 8, 2026
69665f1
fix(webapp): stop the agent's retry duplicating the failed message
kathiekiwi Aug 8, 2026
d3d0bdd
docs(webapp): say who Cmd-I actually opens the agent for
kathiekiwi Aug 8, 2026
c03de0d
fix(webapp): time the report sparkline's bars from the report, not th…
kathiekiwi Aug 8, 2026
f778204
fix(webapp): key a repeated view block by its own position
kathiekiwi Aug 8, 2026
0bbe3fa
fix(webapp): send a deep-linked question once
kathiekiwi Aug 8, 2026
14a498b
fix(webapp): stop the URI resolver working after the panel closes
kathiekiwi Aug 8, 2026
b69da4f
fix(webapp): announce the composer's counter and name the history button
kathiekiwi Aug 8, 2026
0af5251
fix(webapp): keep the hero's heading reachable when it outgrows the p…
kathiekiwi Aug 8, 2026
16079fd
fix(webapp): pin the AI-help redirect to the dashboard's own origin
kathiekiwi Aug 8, 2026
921d582
fix(webapp): read the agent's page label from the left of the path
kathiekiwi Aug 8, 2026
141e989
fix(webapp): step the composer's live region instead of reading every…
kathiekiwi Aug 8, 2026
a13da30
fix(webapp): key an investigation's action rows by position, not by kind
kathiekiwi Aug 8, 2026
e42deea
fix(webapp): treat a transcript re-read that isn't a list as a failed…
kathiekiwi Aug 8, 2026
d4ce5eb
fix(webapp): stop the Ask AI host remounting the dashboard, and four …
kathiekiwi Aug 9, 2026
ff36c6b
feat(dashboard-agent): move the watch data layer in from the base PR
kathiekiwi Aug 8, 2026
6bb258e
feat(webapp): tell the user later — the dashboard agent's Watch
kathiekiwi Aug 6, 2026
5ad9198
fix(webapp): one watch button per answer, not two
kathiekiwi Aug 7, 2026
d030fb1
fix(webapp): stop saying error twice when naming a watched error
kathiekiwi Aug 7, 2026
5008c53
fix(webapp): name a watched error in full, not truncated
kathiekiwi Aug 7, 2026
2163f64
fix(webapp): give a watch email the same headline the chat shows
kathiekiwi Aug 7, 2026
44fde59
docs(dashboard-agent): say where each agent button appears, and on wh…
kathiekiwi Aug 7, 2026
b3c3487
fix(dashboard-agent): stop reporting a queue as missing when only its…
kathiekiwi Aug 7, 2026
b75091a
fix(webapp): no watch offer on a paused queue, in the button or the c…
kathiekiwi Aug 7, 2026
86d9166
fix(webapp): a paused queue offers no backlog chips either
kathiekiwi Aug 7, 2026
5342e62
fix(dashboard-agent): say a queue is paused before reciting its metrics
kathiekiwi Aug 7, 2026
85cc923
fix(dashboard-agent): name the state that explains the numbers, and s…
kathiekiwi Aug 7, 2026
0d68e63
fix(webapp): let the agent read a queue's own row, not only its metrics
kathiekiwi Aug 7, 2026
e7e8cfe
test(webapp): pin every tool's API against the scope its route author…
kathiekiwi Aug 7, 2026
7d97f73
fix(dashboard-agent): mint the environment JWT with the scopes the to…
kathiekiwi Aug 7, 2026
eee4a1f
feat(webapp): mark a chat unread when work finished behind a closed p…
kathiekiwi Aug 7, 2026
d419395
feat(webapp): poll for unread work while the panel is closed
kathiekiwi Aug 7, 2026
1cedc16
fix(webapp): keep the wake poll running for a turn started behind a c…
kathiekiwi Aug 7, 2026
75b4a0c
fix(dashboard-agent): tell get_queue who feeds a custom queue
kathiekiwi Aug 7, 2026
b2599cb
refactor(dashboard-agent): factor the repeated shapes out of render_v…
kathiekiwi Aug 7, 2026
2d0b9ac
test(webapp): regenerate the watch wording snapshot for the untruncat…
kathiekiwi Aug 7, 2026
a9ef3a8
test(webapp): give the watch container tests a timeout that fits boot…
kathiekiwi Aug 7, 2026
c15cb6f
fix(webapp): send a task queue's stored name to the watch it offers
kathiekiwi Aug 7, 2026
74e37e5
fix(webapp): claim the alert dispatch before the watch sweep fans out
kathiekiwi Aug 7, 2026
9c6ab02
fix(webapp): normalise an error watch's fingerprint before its identity
kathiekiwi Aug 7, 2026
fc6aad6
fix(webapp): let a wait-time watch end when its queue is gone or unre…
kathiekiwi Aug 7, 2026
48f6549
fix(dashboard-agent): fail a watch investigation whose card refuses t…
kathiekiwi Aug 7, 2026
f45c624
fix(webapp): count unread chats from the list instead of subtracting …
kathiekiwi Aug 8, 2026
b67422d
fix(webapp): drop a watch draft when the panel changes chat
kathiekiwi Aug 8, 2026
f6fb901
fix(webapp): keep the delete-chat confirmation alive outside the hist…
kathiekiwi Aug 8, 2026
091bd8f
fix(dashboard-agent): tie a watch's investigation to the watch, not t…
kathiekiwi Aug 8, 2026
4afb83a
feat(webapp): count wake-feed requests
kathiekiwi Aug 8, 2026
ef41e74
test(webapp): pin read:queues on both of the agent's scope allowlists
kathiekiwi Aug 8, 2026
d2fbe75
fix(webapp): cancel a dashboard agent turn when the user leaves the page
kathiekiwi Aug 8, 2026
69556e0
fix(webapp): scope a turn's navigation to the page it was asked on
kathiekiwi Aug 8, 2026
cc8f73d
fix(webapp): send every prompt the user clicks, never just fill the c…
kathiekiwi Aug 8, 2026
5343a17
fix(dashboard-agent): read the report's untrustworthy reason under it…
kathiekiwi Aug 8, 2026
7e80282
fix(dashboard-agent): report the line read_file actually reached when…
kathiekiwi Aug 8, 2026
ee063f6
fix(dashboard-agent): refuse a view carrying two investigations inste…
kathiekiwi Aug 8, 2026
7f422cc
fix(dashboard-agent-db): require a role on the messages the append-on…
kathiekiwi Aug 8, 2026
8a11842
test(dashboard-agent): assert the chart validation window outside the…
kathiekiwi Aug 8, 2026
7319d99
refactor(webapp): type the query scope ceiling by credential kind, no…
kathiekiwi Aug 8, 2026
1dbf8cb
test(webapp): close the watch suites' agent-db pool and give the slow…
kathiekiwi Aug 8, 2026
cd140a6
docs(dashboard-agent): say that the health verbs rewrite the environm…
kathiekiwi Aug 8, 2026
3cdb40d
fix(dashboard-agent): read the alerts API's real response shape
kathiekiwi Aug 8, 2026
6dc41f9
fix(dashboard-agent): stop a run of failed watch checks nesting forever
kathiekiwi Aug 8, 2026
a19613a
fix(webapp): decide the one-watch-button rule per turn, not per rende…
kathiekiwi Aug 8, 2026
228c221
fix(webapp): don't re-toast a watch wake another browser already read
kathiekiwi Aug 8, 2026
d9a8325
fix(webapp): put the watch chip's tooltip back in the tab order
kathiekiwi Aug 8, 2026
4da69c4
fix(webapp): guard the scenario kit's app origin like its other hosts
kathiekiwi Aug 8, 2026
6e80687
fix(dashboard-agent-db): backfill last_read_at so existing chats star…
kathiekiwi Aug 8, 2026
8c2a99a
test(webapp): let the chat-ordering test load where the route reaches…
kathiekiwi Aug 8, 2026
31e254c
test(webapp): close the watch tenancy suite's agent db pool
kathiekiwi Aug 8, 2026
8744985
fix(webapp): restore the soft-delete import a rebase dropped
kathiekiwi Aug 8, 2026
ecd7cfd
test(webapp): match the per-turn watch rule by shape, not by line breaks
kathiekiwi Aug 8, 2026
9451740
fix(webapp): treat a corrupt watch-activity key as nothing known
kathiekiwi Aug 8, 2026
c12e9b4
fix(webapp): keep the environments a watch-alert channel already covers
kathiekiwi Aug 8, 2026
090ff95
fix(dashboard-agent): tell a queue that can't be read apart from one …
kathiekiwi Aug 8, 2026
40bfbc1
fix(webapp): stop the launcher dot counting the chat the user is reading
kathiekiwi Aug 8, 2026
be5b2f3
fix(dashboard-agent): summarise what the transcript recorded about a …
kathiekiwi Aug 8, 2026
0bf4765
test(webapp): prove a dropped navigation stays dropped
kathiekiwi Aug 8, 2026
fbd295a
test(webapp): pin the query string where it can actually change
kathiekiwi Aug 8, 2026
9e964f5
test(webapp): default SESSION_SECRET in setup, not at module scope
kathiekiwi Aug 8, 2026
13478ba
fix(webapp): cap a public API key's query scope like a public access …
kathiekiwi Aug 8, 2026
0fbc4af
fix(webapp): strip every task/ prefix from a stored queue name
kathiekiwi Aug 8, 2026
eb3987d
docs(dashboard-agent): count the watch kinds the spec union has
kathiekiwi Aug 8, 2026
03d15a0
test(webapp): give the contract test the watch block's fixture
kathiekiwi Aug 8, 2026
1b5404f
chore(webapp): drop the watch scenario kit
kathiekiwi Aug 8, 2026
896ed48
docs(dashboard-agent): state what makes a watch fire, not how to repr…
kathiekiwi Aug 8, 2026
60a0c4c
fix(webapp,dashboard-agent-db): settle read state, the dot and unavai…
kathiekiwi Aug 9, 2026
59378cf
feat(webapp): let the agent's environment JWT read a queue's own row
kathiekiwi Aug 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .changeset/quiet-hounds-shave.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/core": patch
---

The current-worker API now reports each task's queue, so you can see which tasks write to a given queue.
6 changes: 5 additions & 1 deletion .server-changes/dashboard-agent.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,10 +7,14 @@ Meet the dashboard agent: a chat in every environment that answers questions abo

**Investigate** on a failed run, an error, a backed-up queue or a run that hasn't started gets you a worked-through answer — what happened, why, and how to fix it, with every claim linked to the runs, errors and deploys behind it.

**Watch…** on a run, queue, error or the health report tells you when things change: a run finishes, a queue clears or grows past a number you pick, an error comes back, an environment recovers. The answer arrives in the chat and, if you want, by email, Slack or webhook — and the agent can look into bad news on its own. A watch reaches you on any browser you sign in from, without opening the chat first.

The agent works on preview and dev branches, with that branch's own data.

The health report reads the same everywhere — dashboard, terminal, editor. A very long chat keeps working: the agent summarises the earlier part and carries on. The agent's replies no longer show images.

A sample of conversations is scored automatically so the agent keeps getting better. Only the score and a one-line summary are kept, never your messages, data or code, and we can switch it off for your organization on request.

Separately: a queue's wait times, peak depth, throughput and throttling can now be read from the API, and the Docs button has been removed from page headers.
The Docs button is gone from page headers — ask the agent instead, or open Documentation from Help & Feedback.

Separately: a queue's wait times, peak depth, throughput and throttling can now be read from the API.
78 changes: 31 additions & 47 deletions apps/webapp/app/components/AskAI.tsx
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
/**
* @deprecated Superseded by the dashboard agent (`components/dashboard-agent`). Nothing mounts
* this any more — every Ask AI entry point now opens Ask Trigger. Kept until the agent has
* shipped, then removed along with `@kapaai/react-sdk` and `KAPA_AI_WEBSITE_ID`.
* Mostly superseded by the dashboard agent (`components/dashboard-agent`), which owns every
* entry point except two: ⌘I and the CLI's `?aiHelp=` link still open Ask AI. `AskAIRoot` is
* mounted by the `_app` layout for those; the `AskAI` button below is mounted nowhere.
*/

import {
Expand All@@ -17,13 +17,17 @@ import { useSearchParams } from "@remix-run/react";
import DOMPurify from "dompurify";
import { motion } from "framer-motion";
import { marked } from "marked";
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
import { useTypedRouteLoaderData } from "remix-typedjson";
import { useCallback, useEffect, useRef, useState } from "react";
import { AISparkleIcon } from "~/assets/icons/AISparkleIcon";
import { SparkleListIcon } from "~/assets/icons/SparkleListIcon";
import { useFeatures } from "~/hooks/useFeatures";
import { useAskAiAvailability } from "~/hooks/useAskAiAvailability";
import { useShortcutKeys } from "~/hooks/useShortcutKeys";
import { type loader } from "~/root";
import {
ASK_AI_DEEP_LINK_PARAM,
ASK_AI_SHORTCUT,
askAiCanOpen,
} from "./dashboard-agent/ask-ai-channels";
import { useAskAiHost } from "./dashboard-agent/askAiOpenRequest";
import { Button } from "./primitives/Buttons";
import { Callout } from "./primitives/Callout";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "./primitives/Dialog";
Expand All@@ -40,11 +44,6 @@ import {
} from "./primitives/Tooltip";
import { ClientOnly } from "remix-utils/client-only";

function useKapaWebsiteId() {
const routeMatch = useTypedRouteLoaderData<typeof loader>("root");
return routeMatch?.kapa.websiteId;
}

/** Open/close state for the Ask AI dialog, including the `?aiHelp=` deep-link handling. */
function useAskAIState() {
const [isOpen, setIsOpen] = useState(false);
Expand All@@ -67,14 +66,14 @@ function useAskAIState() {

// Handle URL param functionality
useEffect(() => {
const aiHelp = searchParams.get("aiHelp");
const aiHelp = searchParams.get(ASK_AI_DEEP_LINK_PARAM);
if (aiHelp) {
// Delay to avoid hCaptcha bot detection
window.setTimeout(() => openAskAI(aiHelp), 1000);

// Clone instead of mutating in place
const next = new URLSearchParams(searchParams);
next.delete("aiHelp");
next.delete(ASK_AI_DEEP_LINK_PARAM);
setSearchParams(next);
}
}, [searchParams, openAskAI]);
Expand All@@ -83,45 +82,30 @@ function useAskAIState() {
}

/**
* Hosts Ask AI (Kapa provider, ⌘I shortcut, dialog) for a menu that renders its own trigger. Wrap
* it around the popover, not inside, so the dialog and shortcut survive the popover closing.
* `children` receives the open function, or undefined when Ask AI is unavailable (self-hosted, no
* Kapa website id, or SSR).
*
* @deprecated See the note at the top of this file.
* Hosts Ask AI (Kapa provider, ⌘I shortcut, dialog). It renders no page content and wraps
* nothing: entry points reach it through `requestAskAi`, so the Kapa provider mounting after
* hydration can never remount the app around it.
*/
export function AskAIRoot({
children,
}: {
children: (openAskAI: (() => void) | undefined) => ReactNode;
}) {
const { isManagedCloud } = useFeatures();
const websiteId = useKapaWebsiteId();
export function AskAIRoot() {
const availability = useAskAiAvailability();

if (!isManagedCloud || !websiteId) {
return <>{children(undefined)}</>;
if (!askAiCanOpen(availability)) {
return null;
}

return (
<ClientOnly fallback={<>{children(undefined)}</>}>
{() => <AskAIRootProvider websiteId={websiteId}>{children}</AskAIRootProvider>}
</ClientOnly>
);
const websiteId = availability.kapaWebsiteId!;

return <ClientOnly>{() => <AskAIRootProvider websiteId={websiteId} />}</ClientOnly>;
}

function AskAIRootProvider({
websiteId,
children,
}: {
websiteId: string;
children: (openAskAI: () => void) => ReactNode;
}) {
function AskAIRootProvider({ websiteId }: { websiteId: string }) {
const { isOpen, setIsOpen, initialQuery, openAskAI, closeAskAI } = useAskAIState();

useShortcutKeys({
shortcut: { modifiers: ["mod"], key: "i", enabledOnInputElements: true },
shortcut: ASK_AI_SHORTCUT,
action: () => openAskAI(),
});
useAskAiHost(openAskAI);

return (
<KapaProvider
Expand All@@ -134,7 +118,6 @@ function AskAIRootProvider({
}}
botProtectionMechanism="hcaptcha"
>
{children(() => openAskAI())}
<AskAIDialog
initialQuery={initialQuery}
isOpen={isOpen}
Expand All@@ -145,15 +128,16 @@ function AskAIRootProvider({
);
}

/** @deprecated See the note at the top of this file. */
/** @deprecated Mounted nowhere: the sidebar's AI entry point is the dashboard agent. */
export function AskAI({ isCollapsed = false }: { isCollapsed?: boolean }) {
const { isManagedCloud } = useFeatures();
const websiteId = useKapaWebsiteId();
const availability = useAskAiAvailability();

if (!isManagedCloud || !websiteId) {
if (!askAiCanOpen(availability)) {
return null;
}

const websiteId = availability.kapaWebsiteId!;

return (
<ClientOnly
fallback={
Expand Down
105 changes: 58 additions & 47 deletions apps/webapp/app/components/BlankStatePanels.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,7 +34,7 @@ import {
v3NewProjectAlertPath,
v3NewSchedulePath,
} from "~/utils/pathBuilder";
import { AskAI } from "./AskAI";
import { AskAgentButton } from "./dashboard-agent/AskAgentButton";
import { CodeBlock } from "./code/CodeBlock";
import { InlineCode } from "./code/InlineCode";
import { environmentFullTitle, EnvironmentIcon } from "./environments/EnvironmentLabel";
Expand DownExpand Up@@ -62,6 +62,55 @@ import {
import { StepContentContainer } from "./StepContentContainer";
import { V4Badge } from "./V4Badge";

/**
* What the agent is asked when it's opened from a deployment setup panel. The panel is the docs
* answer; the agent is for the part the docs can't answer — this project, this environment.
*/
const ASK_AGENT_DEPLOY_PROMPT =
"I'm trying to deploy my tasks to this environment. Walk me through it and tell me if anything about this project or environment is going to get in the way.";

/** The docs links the deployment panels offer to anyone without the agent. */
function DeployDocsLinks() {
return (
<>
<SimpleTooltip
asChild
tabbable
button={
// Span wrapper: LinkButton drops the pointer-event props Radix injects via asChild, so
// the tooltip trigger has to be a plain element (same pattern as FavoritePageButton).
<span className="flex">
<LinkButton
variant="small-menu-item"
LeadingIcon={BookOpenIcon}
leadingIconClassName="text-blue-500"
to={docsPath("deployment/overview")}
aria-label="Deploy docs"
/>
</span>
}
content="Deploy docs"
/>
<SimpleTooltip
asChild
tabbable
button={
<span className="flex">
<LinkButton
variant="small-menu-item"
LeadingIcon={QuestionMarkCircleIcon}
leadingIconClassName="text-blue-500"
to={docsPath("troubleshooting#deployment")}
aria-label="Troubleshooting docs"
/>
</span>
}
content="Troubleshooting docs"
/>
</>
);
}

export function HasNoTasksDev() {
return (
<PackageManagerProvider>
Expand DownExpand Up@@ -270,29 +319,10 @@ export function DeploymentsNoneDev() {
<Header1>Deploy your tasks</Header1>
</div>
<div className="flex items-center">
<SimpleTooltip
button={
<LinkButton
variant="small-menu-item"
LeadingIcon={BookOpenIcon}
leadingIconClassName="text-blue-500"
to={docsPath("deployment/overview")}
/>
}
content="Deploy docs"
/>
<SimpleTooltip
button={
<LinkButton
variant="small-menu-item"
LeadingIcon={QuestionMarkCircleIcon}
leadingIconClassName="text-blue-500"
to={docsPath("troubleshooting#deployment")}
/>
}
content="Troubleshooting docs"
/>
<AskAI />
{/* One entry point instead of two: the docs links were a guess at which page you
needed, and the agent can look at this project and answer for it. Someone with no
agent still gets the links. */}
<AskAgentButton prompt={ASK_AGENT_DEPLOY_PROMPT} fallback={<DeployDocsLinks />} />
</div>
</div>
<StepNumber stepNumber="→" title="Switch to a deployed environment" />
Expand DownExpand Up@@ -658,29 +688,10 @@ function DeploymentOnboardingSteps() {
</Header1>
</div>
<div className="flex items-center">
<SimpleTooltip
button={
<LinkButton
variant="small-menu-item"
LeadingIcon={BookOpenIcon}
leadingIconClassName="text-blue-500"
to={docsPath("deployment/overview")}
/>
}
content="Deploy docs"
/>
<SimpleTooltip
button={
<LinkButton
variant="small-menu-item"
LeadingIcon={QuestionMarkCircleIcon}
leadingIconClassName="text-blue-500"
to={docsPath("troubleshooting#deployment")}
/>
}
content="Troubleshooting docs"
/>
<AskAI />
{/* One entry point instead of two: the docs links were a guess at which page you
needed, and the agent can look at this project and answer for it. Someone with no
agent still gets the links. */}
<AskAgentButton prompt={ASK_AGENT_DEPLOY_PROMPT} fallback={<DeployDocsLinks />} />
</div>
</div>
<ClientTabs defaultValue="github">
Expand Down
54 changes: 50 additions & 4 deletions apps/webapp/app/components/Shortcuts.tsx
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
import { KeyboardIcon } from "~/assets/icons/KeyboardIcon";
import { useState } from "react";
import { ASK_AGENT_LABEL } from "~/components/dashboard-agent/agent-identity";
import { type AiShortcutRow, aiShortcutRows } from "~/components/dashboard-agent/ai-entry-points";
import { ASK_AI_SHORTCUT, askAiCanOpen } from "~/components/dashboard-agent/ask-ai-channels";
import { useDashboardAgentAvailable } from "~/components/dashboard-agent/dashboardAgentOpenRequest";
import { NEW_CHAT_SHORTCUT } from "~/components/dashboard-agent/DashboardAgentHeader";
import { TOGGLE_PANEL_SHORTCUT } from "~/components/dashboard-agent/dashboardAgentLauncher";
import { useAskAiAvailability } from "~/hooks/useAskAiAvailability";
import { useShortcutKeys } from "~/hooks/useShortcutKeys";
import { Header3 } from "./primitives/Headers";
import { SideMenuItemButton } from "./navigation/SideMenuItem";
Expand DownExpand Up@@ -41,6 +48,11 @@ export function ShortcutsAutoOpen() {
}

function ShortcutContent() {
const agent = useDashboardAgentAvailable();
const askAi = askAiCanOpen(useAskAiAvailability());
const rows = aiShortcutRows({ agent, askAi });
const shows = (row: AiShortcutRow) => rows.includes(row);

return (
<SheetContent>
<SheetHeader>
Expand All@@ -62,10 +74,27 @@ function ShortcutContent() {
<ShortcutKey shortcut={{ modifiers: ["mod"] }} variant="medium/bright" />
<ShortcutKey shortcut={{ key: "enter" }} variant="medium/bright" />
</Shortcut>
<Shortcut name="Ask AI">
<ShortcutKey shortcut={{ modifiers: ["mod"] }} variant="medium/bright" />
<ShortcutKey shortcut={{ key: "i" }} variant="medium/bright" />
</Shortcut>
{shows("agent-toggle") && (
<Shortcut name={ASK_AGENT_LABEL}>
<ShortcutKey
shortcut={{ modifiers: TOGGLE_PANEL_SHORTCUT.modifiers }}
variant="medium/bright"
/>
<ShortcutKey
shortcut={{ key: TOGGLE_PANEL_SHORTCUT.key }}
variant="medium/bright"
/>
</Shortcut>
)}
{shows("ask-ai") && (
<Shortcut name="Ask AI">
<ShortcutKey
shortcut={{ modifiers: ASK_AI_SHORTCUT.modifiers }}
variant="medium/bright"
/>
<ShortcutKey shortcut={{ key: ASK_AI_SHORTCUT.key }} variant="medium/bright" />
</Shortcut>
)}
<Shortcut name="Filter">
<ShortcutKey shortcut={{ key: "f" }} variant="medium/bright" />
</Shortcut>
Expand DownExpand Up@@ -94,6 +123,23 @@ function ShortcutContent() {
<ShortcutKey shortcut={{ key: "h" }} variant="medium/bright" />
</Shortcut>
</div>
{shows("agent-new-chat") && (
<div className="space-y-3">
<Header3>Chat</Header3>
<Shortcut name="New chat">
<ShortcutKey
shortcut={{ modifiers: NEW_CHAT_SHORTCUT.modifiers }}
variant="medium/bright"
/>
<ShortcutKey shortcut={{ key: NEW_CHAT_SHORTCUT.key }} variant="medium/bright" />
</Shortcut>
{shows("agent-close-chat") && (
<Shortcut name="Close chat">
<ShortcutKey shortcut={{ key: "esc" }} variant="medium/bright" />
</Shortcut>
)}
</div>
)}
<div className="space-y-3">
<Header3>Runs page</Header3>
<Shortcut name="Bulk action: Cancel runs">
Expand Down
Loading
Loading