···11import type { ActionType } from "../../../lib/actions/registry.js";
22-import { webhookUiDefinition, type WebhookDraft } from "./webhook.tsx";
22+import { webhookUiDefinition, WebhookPublicDisplayBlock, type WebhookDraft } from "./webhook.tsx";
33+44+export { WebhookPublicDisplayBlock };
35import { recordUiDefinition, type RecordDraft } from "./record.tsx";
46import { bskyPostUiDefinition, type BskyPostDraft } from "./bsky-post.tsx";
57import { patchRecordUiDefinition, type PatchRecordDraft } from "./patch-record.tsx";
···7777 * fields (forEach, comment) are added by the form. */
7878 toInput: (draft: TDraft) => Omit<ActionInput, "forEach" | "comment">;
7979 EditorBlock: FC<EditorBlockProps<TDraft>>;
8080+ /** Read-only display rendered as <dt>/<dd> pairs inside a <DescriptionList>
8181+ * on the dashboard's automation-detail page. Used for the OWNER view —
8282+ * the public profile route uses sanitized data, so webhook ships a
8383+ * separate public block (see `webhookPublicDisplayBlock`); for every
8484+ * other action the public view reuses this same component. */
8585+ DisplayBlock: FC<{ action: TAction }>;
8686+ /** Domain whose favicon represents this action in `LexiconFlow`. Some
8787+ * action types know their target statically (e.g. `bsky-post` → bsky.app);
8888+ * others derive it from the action data (e.g. `record.targetCollection`
8989+ * via `nsidToDomain`, or follow's per-target FOLLOW_TARGETS lookup).
9090+ * Returning null hides the favicon (webhook is record-producing? no, but
9191+ * the caller already gates on that). */
9292+ getFaviconDomain?: (action: TAction) => string | null;
8093};
+32
app/islands/action-editors/webhook.tsx
···11import type { WebhookAction } from "../../../lib/db/schema.js";
22import { Webhook } from "../../icons.ts";
33+import { InlineCode } from "../../components/CodeBlock/index.tsx";
34import * as s from "../AutomationForm.css.ts";
45import type { ActionUIDefinition, ForEachDraft, HeaderDraft } from "./types.ts";
56···99100 );
100101}
101102103103+function WebhookDisplayBlock({ action }: { action: WebhookAction }) {
104104+ return (
105105+ <>
106106+ <dt>Callback URL</dt>
107107+ <dd>
108108+ <InlineCode>{action.callbackUrl}</InlineCode>
109109+ </dd>
110110+ <dt>HMAC Secret</dt>
111111+ <dd>
112112+ <InlineCode>{action.secret}</InlineCode>
113113+ </dd>
114114+ </>
115115+ );
116116+}
117117+118118+/** Public-view counterpart to {@link WebhookDisplayBlock}. The webhook record
119119+ * is the only action whose public projection has a different shape
120120+ * (`PublicWebhookAction` from `lib/automations/sanitize.ts`): the secret and
121121+ * full callback URL are stripped, and only the host domain is shown. */
122122+export function WebhookPublicDisplayBlock({ action }: { action: { callbackDomain: string } }) {
123123+ return (
124124+ <>
125125+ <dt>Destination</dt>
126126+ <dd>
127127+ <InlineCode>{action.callbackDomain}</InlineCode>
128128+ </dd>
129129+ </>
130130+ );
131131+}
132132+102133export const webhookUiDefinition: ActionUIDefinition<WebhookDraft, WebhookAction> = {
103134 type: "webhook",
104135 recordProducing: false,
···129160 };
130161 },
131162 EditorBlock: WebhookActionEditor,
163163+ DisplayBlock: WebhookDisplayBlock,
132164};