Skip to content

Commit f8f7985

Browse files
authored
Merge branch 'main' into docs/building-with-ai-improvements
2 parents 0b5c12c + 3bb9aac commit f8f7985

File tree

10 files changed

+208
-16
lines changed

10 files changed

+208
-16
lines changed

apps/webapp/app/components/AskAI.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,30 +118,31 @@ function AskAIProvider({ websiteId, isCollapsed = false }: AskAIProviderProps) {
118118
<motion.div layout="position" transition={{ duration: 0.2, ease: "easeInOut" }}>
119119
<TooltipProvider disableHoverableContent>
120120
<Tooltip>
121-
<div className={isCollapsed ? "w-full" : "inline-flex"}>
122-
<TooltipTrigger asChild>
121+
<TooltipTrigger asChild>
122+
<span className={cn("inline-flex h-8", isCollapsed && "w-full")}>
123123
<Button
124124
variant="small-menu-item"
125125
data-action="ask-ai"
126-
shortcut={{ modifiers: ["mod"], key: "/", enabledOnInputElements: true }}
126+
shortcut={{ modifiers: ["mod"], key: "i", enabledOnInputElements: true }}
127127
hideShortcutKey
128128
data-modal-override-open-class-ask-ai="true"
129129
onClick={() => openAskAI()}
130-
className={isCollapsed ? "w-full justify-center" : ""}
130+
fullWidth={isCollapsed}
131+
className={cn("h-full", isCollapsed && "justify-center")}
131132
>
132133
<AISparkleIcon className="size-5" />
133134
</Button>
134-
</TooltipTrigger>
135-
</div>
135+
</span>
136+
</TooltipTrigger>
136137
<TooltipContent
137-
side={isCollapsed ? "right" : "top"}
138-
sideOffset={isCollapsed ? 8 : 4}
139-
className="flex items-center gap-1 py-1.5 pl-2.5 pr-2 text-xs"
138+
side="right"
139+
sideOffset={8}
140+
className="flex items-center gap-2 text-xs"
140141
>
141142
Ask AI
142143
<span className="flex items-center">
143144
<ShortcutKey shortcut={{ modifiers: ["mod"] }} variant="medium/bright" />
144-
<ShortcutKey shortcut={{ key: "/" }} variant="medium/bright" />
145+
<ShortcutKey shortcut={{ key: "i" }} variant="medium/bright" />
145146
</span>
146147
</TooltipContent>
147148
</Tooltip>

apps/webapp/app/components/Shortcuts.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ function ShortcutContent() {
7676
<ShortcutKey shortcut={{ key: "enter" }} variant="medium/bright" />
7777
</Shortcut>
7878
<Shortcut name="Ask AI">
79-
<ShortcutKey shortcut={{ modifiers: ["mod"], key: "/" }} variant="medium/bright" />
79+
<ShortcutKey shortcut={{ modifiers: ["mod"] }} variant="medium/bright" />
80+
<ShortcutKey shortcut={{ key: "i" }} variant="medium/bright" />
8081
</Shortcut>
8182
<Shortcut name="Filter">
8283
<ShortcutKey shortcut={{ key: "f" }} variant="medium/bright" />

apps/webapp/app/components/navigation/SideMenu.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -993,7 +993,10 @@ function CollapseToggle({
993993
return (
994994
<div className="absolute -right-3 top-1/2 z-10 -translate-y-1/2">
995995
{/* Vertical line to mask the side menu border */}
996-
<div className="pointer-events-none absolute left-1/2 top-1/2 h-10 w-px -translate-y-1/2 bg-background-bright" />
996+
<div className={cn(
997+
"pointer-events-none absolute left-1/2 top-1/2 h-10 w-px -translate-y-1/2 transition-colors duration-200",
998+
isHovering ? "bg-charcoal-750" : "bg-background-bright"
999+
)} />
9971000
<TooltipProvider disableHoverableContent>
9981001
<Tooltip>
9991002
<TooltipTrigger asChild>

apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ export class DeploymentPresenter {
156156
},
157157
},
158158
buildServerMetadata: true,
159+
triggeredVia: true,
159160
},
160161
});
161162

@@ -225,6 +226,7 @@ export class DeploymentPresenter {
225226
isBuilt: !!deployment.builtAt,
226227
type: deployment.type,
227228
git: gitMetadata,
229+
triggeredVia: deployment.triggeredVia,
228230
},
229231
};
230232
}

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.deployments.$deploymentParam/route.tsx

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@ import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
33
import { typedjson, useTypedLoaderData } from "remix-typedjson";
44
import { useEffect, useState, useRef, useCallback } from "react";
55
import { S2, S2Error } from "@s2-dev/streamstore";
6-
import { Clipboard, ClipboardCheck, ChevronDown, ChevronUp } from "lucide-react";
6+
import {
7+
Clipboard,
8+
ClipboardCheck,
9+
ChevronDown,
10+
ChevronUp,
11+
TerminalSquareIcon,
12+
LayoutDashboardIcon,
13+
GitBranchIcon,
14+
ServerIcon,
15+
} from "lucide-react";
716
import { ExitIcon } from "~/assets/icons/ExitIcon";
817
import { GitMetadata } from "~/components/GitMetadata";
918
import { RuntimeIcon } from "~/components/RuntimeIcon";
@@ -73,6 +82,90 @@ type LogEntry = {
7382
level: "info" | "error" | "warn" | "debug";
7483
};
7584

85+
function getTriggeredViaDisplay(triggeredVia: string | null | undefined): {
86+
icon: React.ReactNode;
87+
label: string;
88+
} | null {
89+
if (!triggeredVia) return null;
90+
91+
const iconClass = "size-4 text-text-dimmed";
92+
93+
switch (triggeredVia) {
94+
case "cli:manual":
95+
return {
96+
icon: <TerminalSquareIcon className={iconClass} />,
97+
label: "CLI (Manual)",
98+
};
99+
case "cli:github_actions":
100+
return {
101+
icon: <GitBranchIcon className={iconClass} />,
102+
label: "CLI (GitHub Actions)",
103+
};
104+
case "cli:gitlab_ci":
105+
return {
106+
icon: <ServerIcon className={iconClass} />,
107+
label: "CLI (GitLab CI)",
108+
};
109+
case "cli:circleci":
110+
return {
111+
icon: <ServerIcon className={iconClass} />,
112+
label: "CLI (CircleCI)",
113+
};
114+
case "cli:jenkins":
115+
return {
116+
icon: <ServerIcon className={iconClass} />,
117+
label: "CLI (Jenkins)",
118+
};
119+
case "cli:azure_pipelines":
120+
return {
121+
icon: <ServerIcon className={iconClass} />,
122+
label: "CLI (Azure Pipelines)",
123+
};
124+
case "cli:bitbucket_pipelines":
125+
return {
126+
icon: <ServerIcon className={iconClass} />,
127+
label: "CLI (Bitbucket Pipelines)",
128+
};
129+
case "cli:travis_ci":
130+
return {
131+
icon: <ServerIcon className={iconClass} />,
132+
label: "CLI (Travis CI)",
133+
};
134+
case "cli:buildkite":
135+
return {
136+
icon: <ServerIcon className={iconClass} />,
137+
label: "CLI (Buildkite)",
138+
};
139+
case "cli:ci_other":
140+
return {
141+
icon: <ServerIcon className={iconClass} />,
142+
label: "CLI (CI)",
143+
};
144+
case "git_integration:github":
145+
return {
146+
icon: <GitBranchIcon className={iconClass} />,
147+
label: "GitHub Integration",
148+
};
149+
case "dashboard":
150+
return {
151+
icon: <LayoutDashboardIcon className={iconClass} />,
152+
label: "Dashboard",
153+
};
154+
default:
155+
// Handle any unknown values gracefully
156+
if (triggeredVia.startsWith("cli:")) {
157+
return {
158+
icon: <TerminalSquareIcon className={iconClass} />,
159+
label: `CLI (${triggeredVia.replace("cli:", "")})`,
160+
};
161+
}
162+
return {
163+
icon: <ServerIcon className={iconClass} />,
164+
label: triggeredVia,
165+
};
166+
}
167+
}
168+
76169
export default function Page() {
77170
const { deployment, eventStream } = useTypedLoaderData<typeof loader>();
78171
const organization = useOrganization();
@@ -408,6 +501,21 @@ export default function Page() {
408501
)}
409502
</Property.Value>
410503
</Property.Item>
504+
<Property.Item>
505+
<Property.Label>Triggered via</Property.Label>
506+
<Property.Value>
507+
{(() => {
508+
const display = getTriggeredViaDisplay(deployment.triggeredVia);
509+
if (!display) return "–";
510+
return (
511+
<span className="flex items-center gap-1.5">
512+
{display.icon}
513+
{display.label}
514+
</span>
515+
);
516+
})()}
517+
</Property.Value>
518+
</Property.Item>
411519
</Property.Table>
412520
</div>
413521

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.queues/route.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,14 +364,14 @@ export default function Page() {
364364
/>
365365
</div>
366366
}
367-
valueClassName={env.paused ? "text-warning" : undefined}
367+
valueClassName={cn(env.paused ? "text-warning" : undefined, "tabular-nums")}
368368
compactThreshold={1000000}
369369
/>
370370
<BigNumber
371371
title="Running"
372372
value={environment.running}
373373
animate
374-
valueClassName={limitClassName}
374+
valueClassName={cn(limitClassName, "tabular-nums")}
375375
suffix={
376376
limitStatus === "burst" ? (
377377
<span className={cn(limitClassName, "flex items-center gap-1")}>

docs/docs.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,8 @@
353353
"guides/frameworks/webhooks-guides-overview",
354354
"guides/frameworks/nextjs-webhooks",
355355
"guides/frameworks/remix-webhooks",
356-
"guides/examples/stripe-webhook"
356+
"guides/examples/stripe-webhook",
357+
"guides/examples/hookdeck-webhook"
357358
]
358359
}
359360
]
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: "Trigger tasks from Hookdeck webhooks"
3+
sidebarTitle: "Hookdeck webhooks"
4+
description: "This example demonstrates how to use Hookdeck to receive webhooks and trigger Trigger.dev tasks."
5+
---
6+
7+
## Overview
8+
9+
This example shows how to use [Hookdeck](https://hookdeck.com) as your webhook infrastructure to trigger Trigger.dev tasks. Hookdeck receives webhooks from external services, and forwards them directly to the Trigger.dev API. This gives you the best of both worlds: Hookdeck's webhook management, logging, and replay capabilities, combined with Trigger.dev's reliable task execution.
10+
11+
## Key features
12+
13+
- Use Hookdeck as your webhook endpoint for external services
14+
- Hookdeck forwards webhooks directly to Trigger.dev tasks via the API
15+
- All webhooks are logged and replayable in Hookdeck
16+
17+
## Setting up Hookdeck
18+
19+
You'll configure everything in the [Hookdeck dashboard](https://dashboard.hookdeck.com). No code changes needed in your app.
20+
21+
### 1. Create a destination
22+
23+
In Hookdeck, create a new [destination](https://hookdeck.com/docs/destinations) with the following settings:
24+
25+
- **URL**: `https://api.trigger.dev/api/v1/tasks/<task-id>/trigger` (replace `<task-id>` with your task ID)
26+
- **Method**: POST
27+
- **Authentication**: Bearer token (use your `TRIGGER_SECRET_KEY` from Trigger.dev)
28+
29+
### 2. Add a transformation
30+
31+
Create a [transformation](https://hookdeck.com/docs/transformations) to wrap the webhook body in the `payload` field that Trigger.dev expects:
32+
33+
```javascript
34+
addHandler("transform", (request, context) => {
35+
request.body = { payload: { ...request.body } };
36+
return request;
37+
});
38+
```
39+
40+
### 3. Create a connection
41+
42+
Create a [connection](https://hookdeck.com/docs/connections) that links your source (where webhooks come from) to the destination and transformation you created above.
43+
44+
## Task code
45+
46+
This task will be triggered when Hookdeck forwards a webhook to the Trigger.dev API.
47+
48+
```ts trigger/webhook-handler.ts
49+
import { task } from "@trigger.dev/sdk";
50+
51+
export const webhookHandler = task({
52+
id: "webhook-handler",
53+
run: async (payload: Record<string, unknown>) => {
54+
// The payload contains the original webhook data from the external service
55+
console.log("Received webhook:", payload);
56+
57+
// Add your custom logic here
58+
},
59+
});
60+
```
61+
62+
## Testing your setup
63+
64+
To test everything is working:
65+
66+
1. Set up your destination, transformation, and connection in [Hookdeck](https://dashboard.hookdeck.com)
67+
2. Send a test webhook to your Hookdeck source URL (use the Hookdeck Console or cURL)
68+
3. Check the Hookdeck dashboard to verify the webhook was received and forwarded
69+
4. Check the [Trigger.dev dashboard](https://cloud.trigger.dev) to see the successful run of your task
70+
71+
For more information on setting up Hookdeck, refer to the [Hookdeck Documentation](https://hookdeck.com/docs).

docs/guides/frameworks/webhooks-guides-overview.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ A webhook handler is code that executes in response to an event. They can be end
3131
How to create a Stripe webhook handler and trigger a task when a 'checkout session completed'
3232
event is received.
3333
</Card>
34+
<Card title="Hookdeck webhooks" icon="webhook" href="/guides/examples/hookdeck-webhook">
35+
Use Hookdeck to receive webhooks and forward them to Trigger.dev tasks with logging and replay
36+
capabilities.
37+
</Card>
3438
<Card
3539
title="Supabase database webhooks guide"
3640
icon="webhook"

docs/guides/introduction.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Get set up fast using our detailed walk-through guides.
3737
| [Python web crawler](/guides/python/python-crawl4ai) | Use Python, Crawl4AI and Playwright to create a headless web crawler |
3838
| [Sequin database triggers](/guides/frameworks/sequin) | Trigger tasks from database changes using Sequin |
3939
| [Stripe webhooks](/guides/examples/stripe-webhook) | Trigger tasks from incoming Stripe webhook events |
40+
| [Hookdeck webhooks](/guides/examples/hookdeck-webhook) | Use Hookdeck to receive webhooks and forward them to Trigger.dev tasks |
4041
| [Supabase database webhooks](/guides/frameworks/supabase-edge-functions-database-webhooks) | Trigger tasks using Supabase database webhooks |
4142
| [Supabase edge function hello world](/guides/frameworks/supabase-edge-functions-basic) | Trigger tasks from Supabase edge function |
4243
| [Using webhooks in Next.js](/guides/frameworks/nextjs-webhooks) | Trigger tasks from a webhook in Next.js |

0 commit comments

Comments
 (0)