diff --git a/src/Layout.tsx b/src/Layout.tsx
index ad24c11..053926e 100644
--- a/src/Layout.tsx
+++ b/src/Layout.tsx
@@ -19,7 +19,15 @@ export default function Layout() {
}, [pathname]);
return (
-
+
+ {/* Demo Badge */}
+
+
+ DEMO ONLY
+ TECHNICAL SHARING 2026
+
+
+
{/* 侧边栏导航 */}
+
+ {/* Sidebar Demo Badge */}
+
+
Tech Sharing
+
DEMO ONLY
+
{/* 主内容区 */}
diff --git a/src/pages/PlanningAgent.tsx b/src/pages/PlanningAgent.tsx
index ac0f8b1..9a9d7d1 100644
--- a/src/pages/PlanningAgent.tsx
+++ b/src/pages/PlanningAgent.tsx
@@ -14,10 +14,25 @@ type ChatMessage = {
};
type StreamEvent = {
- type: "thought" | "tool_call" | "tool_result" | "final" | "error";
+ type: "thought" | "tool_call" | "tool_result" | "final" | "error" | "workspace_start" | "workspace_delta" | "workspace_end";
content: string;
step?: number;
tool_name?: string;
+ workspace_title?: string;
+};
+
+type WorkspaceState = {
+ isOpen: boolean;
+ title: string;
+ content: string;
+ isGenerating: boolean;
+};
+
+const WORKSPACE_INIT: WorkspaceState = {
+ isOpen: false,
+ title: "",
+ content: "",
+ isGenerating: false,
};
type HistorySession = {
@@ -34,6 +49,7 @@ type State = {
reasoning: Record
; // Map message ID to its reasoning/traces
chatting: boolean;
historySessions: HistorySession[];
+ workspace: WorkspaceState;
error?: string;
};
@@ -45,7 +61,11 @@ type Action =
| { type: "set-error"; v?: string }
| { type: "set-history"; sessions: HistorySession[] }
| { type: "load-session"; sessionId: string; messages: ChatMessage[] }
- | { type: "reset"; sessionId: string; userId: string };
+ | { type: "reset"; sessionId: string; userId: string }
+ | { type: "ws-start"; title: string }
+ | { type: "ws-delta"; content: string }
+ | { type: "ws-end" }
+ | { type: "ws-toggle"; open: boolean };
/* ─── Helpers ─── */
function uid() {
@@ -129,9 +149,30 @@ function reducer(state: State, action: Action): State {
reasoning: {},
chatting: false,
historySessions: state.historySessions,
+ workspace: WORKSPACE_INIT,
error: undefined,
};
}
+ case "ws-start":
+ return {
+ ...state,
+ workspace: { isOpen: true, title: action.title || "New Artifact", content: "", isGenerating: true },
+ };
+ case "ws-delta":
+ return {
+ ...state,
+ workspace: { ...state.workspace, content: state.workspace.content + action.content },
+ };
+ case "ws-end":
+ return {
+ ...state,
+ workspace: { ...state.workspace, isGenerating: false },
+ };
+ case "ws-toggle":
+ return {
+ ...state,
+ workspace: { ...state.workspace, isOpen: action.open },
+ };
default:
return state;
}
@@ -212,6 +253,7 @@ export default function PlanningAgent() {
reasoning: {},
chatting: false,
historySessions: getStoredHistory(),
+ workspace: WORKSPACE_INIT,
});
const [input, setInput] = useState("");
@@ -285,7 +327,13 @@ export default function PlanningAgent() {
file_ids: files.map((f) => f.id),
},
(evt) => {
- if (evt.type === "final") {
+ if (evt.type === "workspace_start") {
+ dispatch({ type: "ws-start", title: evt.workspace_title || "New Artifact" });
+ } else if (evt.type === "workspace_delta") {
+ dispatch({ type: "ws-delta", content: evt.content });
+ } else if (evt.type === "workspace_end") {
+ dispatch({ type: "ws-end" });
+ } else if (evt.type === "final") {
finalText += evt.content;
dispatch({
type: "update-msg",
@@ -367,8 +415,10 @@ export default function PlanningAgent() {
setFiles([]);
};
+ const wsHasContent = !state.workspace.isOpen && state.workspace.content.length > 0;
+
return (
-
+
{/* ─── Sidebar Overlay ─── */}
{isHistoryOpen && (
{/* ─── Main Chat ─── */}
-
-
+
+
{/* History Toggle Button */}