Skip to content

Commit 8a2273e

Browse files
nszhslclaude
andauthored
feat(feishu): support optional header in streaming cards (#22826)
Add an optional `header` parameter to `FeishuStreamingSession.start()` so that streaming cards can display a colored title bar, matching the appearance of non-streaming interactive cards. The Card Kit API already supports `header` alongside `streaming_mode`, but the current implementation omits it, producing headerless cards. This change is fully backward-compatible: when `header` is not provided, behavior is identical to before. Closes #13267 (partial) Co-authored-by: Claude Opus 4.6 <[email protected]>
1 parent 0a23739 commit 8a2273e

File tree

1 file changed

+20
-2
lines changed

1 file changed

+20
-2
lines changed

extensions/feishu/src/streaming-card.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ import type { FeishuDomain } from "./types.js";
99
type Credentials = { appId: string; appSecret: string; domain?: FeishuDomain };
1010
type CardState = { cardId: string; messageId: string; sequence: number; currentText: string };
1111

12+
/** Optional header for streaming cards (title bar with color template) */
13+
export type StreamingCardHeader = {
14+
title: string;
15+
/** Color template: blue, green, red, orange, purple, indigo, wathet, turquoise, yellow, grey, carmine, violet, lime */
16+
template?: string;
17+
};
18+
1219
// Token cache (keyed by domain + appId)
1320
const tokenCache = new Map<string, { token: string; expiresAt: number }>();
1421

@@ -99,14 +106,19 @@ export class FeishuStreamingSession {
99106
async start(
100107
receiveId: string,
101108
receiveIdType: "open_id" | "user_id" | "union_id" | "email" | "chat_id" = "chat_id",
102-
options?: { replyToMessageId?: string; replyInThread?: boolean; rootId?: string },
109+
options?: {
110+
replyToMessageId?: string;
111+
replyInThread?: boolean;
112+
rootId?: string;
113+
header?: StreamingCardHeader;
114+
},
103115
): Promise<void> {
104116
if (this.state) {
105117
return;
106118
}
107119

108120
const apiBase = resolveApiBase(this.creds.domain);
109-
const cardJson = {
121+
const cardJson: Record<string, unknown> = {
110122
schema: "2.0",
111123
config: {
112124
streaming_mode: true,
@@ -117,6 +129,12 @@ export class FeishuStreamingSession {
117129
elements: [{ tag: "markdown", content: "⏳ Thinking...", element_id: "content" }],
118130
},
119131
};
132+
if (options?.header) {
133+
cardJson.header = {
134+
title: { tag: "plain_text", content: options.header.title },
135+
template: options.header.template ?? "blue",
136+
};
137+
}
120138

121139
// Create card entity
122140
const { response: createRes, release: releaseCreate } = await fetchWithSsrFGuard({

0 commit comments

Comments
 (0)