From 89a4e192e5fc3c04a9e4c82b6ed7b0128eae1d85 Mon Sep 17 00:00:00 2001
From: yu <2799341137@qq.com>
Date: Thu, 6 Aug 2026 15:25:34 +0800
Subject: [PATCH] feat(agent-ui): add inline skill and canvas references
---
.../agent/agent-canvas-reference-preview.tsx | 36 ++
.../components/agent/agent-chat-composer.tsx | 43 +-
.../agent/agent-chat-inline-tokens.ts | 48 ++
.../components/agent/agent-chat-message.tsx | 58 ++-
.../agent/agent-chat-prompt-input.tsx | 433 ++++++++++++++++++
web/src/components/agent/agent-chat.tsx | 4 +-
.../agent/agent-event-formatters.ts | 54 +--
.../components/agent/agent-skill-picker.tsx | 87 ----
.../components/agent/local-agent-panel.tsx | 124 ++---
web/src/i18n/locales/en-US.ts | 6 +-
web/src/i18n/locales/zh-CN.ts | 6 +-
.../lib/canvas/canvas-resource-references.ts | 33 +-
web/src/services/api/canvas-agent.ts | 8 +
web/src/stores/use-agent-skill-store.ts | 42 +-
web/src/stores/use-agent-store.ts | 8 +-
15 files changed, 758 insertions(+), 232 deletions(-)
create mode 100644 web/src/components/agent/agent-canvas-reference-preview.tsx
create mode 100644 web/src/components/agent/agent-chat-inline-tokens.ts
create mode 100644 web/src/components/agent/agent-chat-prompt-input.tsx
delete mode 100644 web/src/components/agent/agent-skill-picker.tsx
diff --git a/web/src/components/agent/agent-canvas-reference-preview.tsx b/web/src/components/agent/agent-canvas-reference-preview.tsx
new file mode 100644
index 0000000..ceebd80
--- /dev/null
+++ b/web/src/components/agent/agent-canvas-reference-preview.tsx
@@ -0,0 +1,36 @@
+import { FileText, Image as ImageIcon, Music2, Video } from "lucide-react";
+import { useTranslation } from "react-i18next";
+
+import i18n from "@/i18n";
+import { canvasThemes } from "@/lib/canvas-theme";
+import type { AgentCanvasReference } from "@/stores/use-agent-store";
+
+export function AgentCanvasReferencePreview({ reference, previewUrl, previewText, theme }: { reference: AgentCanvasReference; previewUrl?: string; previewText?: string; theme: (typeof canvasThemes)[keyof typeof canvasThemes] }) {
+ const { t } = useTranslation();
+ const Icon = canvasReferenceIcon(reference.kind);
+ return (
+
+
+
+ {reference.title}
+
+ {reference.kind === "image" && previewUrl ?

: null}
+ {reference.kind === "video" && previewUrl ?
: null}
+ {reference.kind === "audio" && previewUrl ?
: null}
+ {reference.kind === "text" && previewText ? (
+
+ {previewText}
+
+ ) : null}
+ {!previewUrl && !(reference.kind === "text" && previewText) ?
{t("agent.composer.mentions.previewUnavailable")}
: null}
+
+ );
+}
+
+export function canvasReferenceIcon(kind: AgentCanvasReference["kind"]) {
+ return kind === "audio" ? Music2 : kind === "video" ? Video : kind === "image" ? ImageIcon : FileText;
+}
+
+export function canvasReferenceKindLabel(kind: AgentCanvasReference["kind"]) {
+ return i18n.t(`agent.composer.mentions.kind.${kind}`);
+}
diff --git a/web/src/components/agent/agent-chat-composer.tsx b/web/src/components/agent/agent-chat-composer.tsx
index 73bd554..66f1037 100644
--- a/web/src/components/agent/agent-chat-composer.tsx
+++ b/web/src/components/agent/agent-chat-composer.tsx
@@ -5,11 +5,9 @@ import { useTranslation } from "react-i18next";
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";
import { canvasThemes } from "@/lib/canvas-theme";
-import { isPlainEnterKey } from "@/lib/keyboard-event";
-import { useAgentSkillStore } from "@/stores/use-agent-skill-store";
-import type { AgentModel, AgentPermissionMode, AgentReasoningEffort } from "@/stores/use-agent-store";
+import { useAgentStore, type AgentModel, type AgentPermissionMode, type AgentReasoningEffort } from "@/stores/use-agent-store";
import type { AgentChatAttachment } from "./agent-chat-message";
-import { AgentSkillPicker } from "./agent-skill-picker";
+import { AgentChatPromptInput } from "./agent-chat-prompt-input";
export function AgentChatComposer({
prompt,
@@ -58,9 +56,8 @@ export function AgentChatComposer({
}) {
const { t } = useTranslation();
const fileInputRef = useRef(null);
- const selectedSkill = useAgentSkillStore((state) => state.selectedSkill);
- const clearSkillSelection = useAgentSkillStore((state) => state.clearSelection);
- const canSubmit = !disabled && !sending && Boolean(prompt.trim() || attachments.length);
+ const canvasReferences = useAgentStore((state) => state.canvasReferences);
+ const canSubmit = !disabled && !sending && Boolean(prompt.trim() || attachments.length || canvasReferences.length);
return (
event.stopPropagation()}>
@@ -78,36 +75,7 @@ export function AgentChatComposer({
))}
) : null}
- {selectedSkill ? (
-
-
- {selectedSkill.interface?.displayName || selectedSkill.name}
-
-
-
- ) : null}
-