Release v0.89.1

This commit is contained in:
coso
2026-03-17 10:39:52 +08:00
parent 998fe555e5
commit 157e400c97
78 changed files with 774 additions and 447 deletions
+7
View File
@@ -33,6 +33,8 @@ docs/prd/
docs/roadmap/
docs/gongzonghao/
docs/bussniss/
docs/oem/
# Issues tracking (internal use only)
.issues/
@@ -63,3 +65,8 @@ openspec
# Build artifacts (alternate cargo target dirs)
src-tauri/target*/
logo-lime.png
lime.db
+4 -2
View File
@@ -1,8 +1,10 @@
## Lime v0.89.0
## Lime v0.89.1
### ✨ 主要更新
- **品牌正式更名为 Lime**:主程序名称、仓库链接、安装包命名、Tauri `productName` / `identifier`、深链 scheme、浏览器桥扩展目录与发布文案已统一从 `Proxycast` 切换到 `Lime`
- **品牌图标与启动形象刷新**:应用图标、安装包图标、通知图标与启动页专用 Logo 已统一替换为新的青柠小龙虾主视觉,启动页继续保留独立图像资源
- **ThemeWorkbench 侧边栏完成职责拆分**:上下文面板、工作流面板、执行日志与编排状态拆成独立构建模块,侧边栏主组件进一步收口
- **版本事实源继续收口**:统一 `package.json`、Cargo workspace / package、两份 Tauri 配置与 `RELEASE_NOTES.md` 的版本入口,发布流程继续只读取这一套版本源
- **运行时命名继续收口**:Tool Calling、Web Search、Durable Memory、workspace sandbox、真实测试与浏览器桥接等运行时环境变量统一以 `LIME_*` / `__LIME_*` 为现役事实源,旧 `PROXYCAST_*` 仅保留兼容读取
- **生态入口同步完成**:README、插件升级入口、About 页、扩展说明与下载地址等对外入口统一使用 `Lime`
@@ -28,4 +30,4 @@
---
**完整变更**: v0.88.0...v0.89.0
**完整变更**: v0.89.0...v0.89.1
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "lime",
"private": true,
"version": "0.89.0",
"version": "0.89.1",
"type": "module",
"engines": {
"node": ">=22.0.0"
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 881 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 339 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFBD4F"></stop><stop offset="100%" stop-color="#FF980E"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

+17 -17
View File
@@ -5071,7 +5071,7 @@ dependencies = [
[[package]]
name = "lime"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"anyhow",
"arboard",
@@ -5174,7 +5174,7 @@ dependencies = [
[[package]]
name = "lime-agent"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"aster-core",
"async-trait",
@@ -5199,7 +5199,7 @@ dependencies = [
[[package]]
name = "lime-browser-runtime"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"chrono",
"futures",
@@ -5216,7 +5216,7 @@ dependencies = [
[[package]]
name = "lime-config"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"async-trait",
"lime-core",
@@ -5232,7 +5232,7 @@ dependencies = [
[[package]]
name = "lime-core"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"aster-models",
"async-trait",
@@ -5272,7 +5272,7 @@ dependencies = [
[[package]]
name = "lime-credential"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"axum 0.7.9",
"base64 0.22.1",
@@ -5307,7 +5307,7 @@ dependencies = [
[[package]]
name = "lime-gateway"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"axum 0.7.9",
"chrono",
@@ -5328,7 +5328,7 @@ dependencies = [
[[package]]
name = "lime-infra"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"chrono",
"dashmap 5.5.3",
@@ -5348,7 +5348,7 @@ dependencies = [
[[package]]
name = "lime-mcp"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"async-trait",
"dirs 5.0.1",
@@ -5380,7 +5380,7 @@ dependencies = [
[[package]]
name = "lime-processor"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"async-trait",
"lime-core",
@@ -5399,7 +5399,7 @@ dependencies = [
[[package]]
name = "lime-providers"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"anyhow",
"async-stream",
@@ -5453,7 +5453,7 @@ dependencies = [
[[package]]
name = "lime-server"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"aster-core",
"async-stream",
@@ -5498,7 +5498,7 @@ dependencies = [
[[package]]
name = "lime-server-utils"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"axum 0.7.9",
"futures",
@@ -5513,7 +5513,7 @@ dependencies = [
[[package]]
name = "lime-services"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"anyhow",
"aster-core",
@@ -5555,7 +5555,7 @@ dependencies = [
[[package]]
name = "lime-skills"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"async-trait",
"dirs 5.0.1",
@@ -5573,7 +5573,7 @@ dependencies = [
[[package]]
name = "lime-terminal"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"async-trait",
"base64 0.22.1",
@@ -5600,7 +5600,7 @@ dependencies = [
[[package]]
name = "lime-websocket"
version = "0.89.0"
version = "0.89.1"
dependencies = [
"axum 0.7.9",
"chrono",
+2 -2
View File
@@ -3,7 +3,7 @@ members = ["crates/*"]
resolver = "2"
[workspace.package]
version = "0.89.0"
version = "0.89.1"
edition = "2021"
authors = ["coso"]
repository = "https://github.com/aiclientproxy/lime"
@@ -191,7 +191,7 @@ version = "2.4"
[package]
name = "lime"
version = "0.89.0"
version = "0.89.1"
description = "AI API Proxy Desktop App"
authors = ["you"]
edition = "2021"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 50 KiB

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#fff</color>
<color name="ic_launcher_background">#f7f9d7</color>
</resources>
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 KiB

After

Width:  |  Height:  |  Size: 986 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 41 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lime",
"version": "0.89.0",
"version": "0.89.1",
"identifier": "com.lime.app",
"build": {
"beforeDevCommand": "npm run dev:web-bridge",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Lime",
"version": "0.89.0",
"version": "0.89.1",
"identifier": "com.lime.app",
"build": {
"beforeDevCommand": "npm run dev",
@@ -1,186 +1,49 @@
import React, {
memo,
type ReactNode,
useMemo,
useState,
} from "react";
import type { StepStatus } from "@/components/content-creator/types";
import type { SkillDetailInfo } from "@/lib/api/skill-execution";
import type { TopicBranchItem, TopicBranchStatus } from "../hooks/useTopicBranchBoard";
import type { SidebarActivityLog } from "../hooks/useThemeContextWorkspace";
import type { Message } from "../types";
import type { AgentRun } from "@/lib/api/executionRun";
import React, { memo, useState } from "react";
import {
ThemeWorkbenchSidebarShell,
type ThemeWorkbenchSidebarTab,
} from "./ThemeWorkbenchSidebarShell";
import { ThemeWorkbenchSidebarPanels } from "./ThemeWorkbenchSidebarPanels";
import type {
ThemeWorkbenchContextBudget,
ThemeWorkbenchContextItem,
} from "./themeWorkbenchContextData";
import {
parseThemeWorkbenchRunMetadataSummary,
type ThemeWorkbenchCreationTaskEvent,
} from "./themeWorkbenchWorkflowData";
import { useThemeWorkbenchContextPanelState } from "./useThemeWorkbenchContextPanelState";
import { useThemeWorkbenchArtifactActions } from "./useThemeWorkbenchArtifactActions";
import { useThemeWorkbenchExecLogState } from "./useThemeWorkbenchExecLogState";
import { useThemeWorkbenchSidebarTelemetry } from "./useThemeWorkbenchSidebarTelemetry";
import { useThemeWorkbenchWorkflowPanelState } from "./useThemeWorkbenchWorkflowPanelState";
import {
formatThemeWorkbenchRunMetadata,
writeThemeWorkbenchClipboardText,
} from "./themeWorkbenchSidebarShared";
type BranchMode = "topic" | "version";
interface ThemeWorkbenchSidebarProps {
branchMode?: BranchMode;
onNewTopic: () => void;
onSwitchTopic: (topicId: string) => void;
onDeleteTopic: (topicId: string) => void;
branchItems: TopicBranchItem[];
onSetBranchStatus: (topicId: string, status: TopicBranchStatus) => void;
workflowSteps: Array<{ id: string; title: string; status: StepStatus }>;
contextSearchQuery: string;
onContextSearchQueryChange: (value: string) => void;
contextSearchMode: "web" | "social";
onContextSearchModeChange: (value: "web" | "social") => void;
contextSearchLoading: boolean;
contextSearchError?: string | null;
contextSearchBlockedReason?: string | null;
onSubmitContextSearch: () => Promise<void> | void;
onAddTextContext?: (payload: {
content: string;
name?: string;
}) => Promise<void> | void;
onAddLinkContext?: (payload: {
url: string;
name?: string;
}) => Promise<void> | void;
onAddFileContext?: (payload: {
path: string;
name?: string;
}) => Promise<void> | void;
onAddImage?: () => Promise<void> | void;
onImportDocument?: () => Promise<void> | void;
contextItems: ThemeWorkbenchContextItem[];
onToggleContextActive: (contextId: string) => void;
onViewContextDetail?: (contextId: string) => void;
contextBudget: ThemeWorkbenchContextBudget;
activityLogs: SidebarActivityLog[];
creationTaskEvents?: ThemeWorkbenchCreationTaskEvent[];
onViewRunDetail?: (runId: string) => void;
activeRunDetail?: AgentRun | null;
activeRunDetailLoading?: boolean;
onRequestCollapse?: () => void;
historyHasMore?: boolean;
historyLoading?: boolean;
onLoadMoreHistory?: () => void;
skillDetailMap?: Record<string, SkillDetailInfo | null>;
headerActionSlot?: ReactNode;
topSlot?: ReactNode;
/** 完整的对话消息列表,用于执行日志 tab */
messages?: Message[];
}
import { buildThemeWorkbenchSidebarOrchestrationSource } from "./buildThemeWorkbenchSidebarOrchestrationSource";
import { createThemeWorkbenchSidebarOrchestrationInput } from "./themeWorkbenchSidebarOrchestrationContract";
import { type ThemeWorkbenchSidebarProps } from "./themeWorkbenchSidebarContract";
import { areThemeWorkbenchSidebarPropsEqual } from "./themeWorkbenchSidebarComparator";
import { useThemeWorkbenchSidebarOrchestration } from "./useThemeWorkbenchSidebarOrchestration";
function ThemeWorkbenchSidebarComponent({
branchMode = "version",
onNewTopic,
onSwitchTopic,
onDeleteTopic,
branchItems,
onSetBranchStatus,
workflowSteps,
contextSearchQuery,
onContextSearchQueryChange,
contextSearchMode,
onContextSearchModeChange,
contextSearchLoading,
contextSearchError,
contextSearchBlockedReason,
onSubmitContextSearch,
onAddTextContext,
onAddLinkContext,
onAddFileContext,
onAddImage,
onImportDocument,
contextItems,
onToggleContextActive,
onViewContextDetail,
contextBudget,
activityLogs,
creationTaskEvents = [],
onViewRunDetail,
activeRunDetail,
activeRunDetailLoading = false,
onRequestCollapse,
historyHasMore = false,
historyLoading = false,
onLoadMoreHistory,
skillDetailMap = {},
headerActionSlot,
topSlot,
messages = [],
...props
}: ThemeWorkbenchSidebarProps) {
const [activeTab, setActiveTab] = useState<ThemeWorkbenchSidebarTab>("context");
const isVersionMode = branchMode === "version";
const runMetadataText = useMemo(
() => formatThemeWorkbenchRunMetadata(activeRunDetail?.metadata ?? null),
[activeRunDetail?.metadata],
const orchestrationInput = createThemeWorkbenchSidebarOrchestrationInput(
buildThemeWorkbenchSidebarOrchestrationSource({
isVersionMode,
props,
}),
);
const runMetadataSummary = useMemo(
() => parseThemeWorkbenchRunMetadataSummary(activeRunDetail?.metadata ?? null),
[activeRunDetail?.metadata],
);
const runDetailSessionId = activeRunDetail?.session_id?.trim() || null;
const { handleRevealArtifactInFinder, handleOpenArtifactWithDefaultApp } =
useThemeWorkbenchArtifactActions({
runDetailSessionId,
});
const workflowPanelState = useThemeWorkbenchWorkflowPanelState({
workflowSteps,
activityLogs,
creationTaskEvents,
runMetadataSummary,
});
const contextPanelState = useThemeWorkbenchContextPanelState({
contextItems,
contextSearchQuery,
contextSearchLoading,
contextSearchBlockedReason,
onAddTextContext,
onAddLinkContext,
onAddFileContext,
});
useThemeWorkbenchSidebarTelemetry({
const {
branchCount,
activeContextCount,
visibleExecLogCount,
contextPanelProps,
workflowPanelProps,
execLogProps,
} = useThemeWorkbenchSidebarOrchestration({
activeTab,
showActivityLogs: workflowPanelState.showActivityLogs,
contextSearchLoading,
branchItemsCount: branchItems.length,
workflowStepsCount: workflowSteps.length,
contextItemsCount: contextItems.length,
activeContextCount: contextPanelState.activeContextItems.length,
activityLogsCount: activityLogs.length,
creationTaskEventsCount: creationTaskEvents.length,
hasActiveRunDetail: Boolean(activeRunDetail),
});
const execLogState = useThemeWorkbenchExecLogState({
messages,
groupedActivityLogs: workflowPanelState.groupedActivityLogs,
groupedCreationTaskEvents: workflowPanelState.groupedCreationTaskEvents,
skillDetailMap,
input: orchestrationInput,
});
return (
<ThemeWorkbenchSidebarShell
activeTab={activeTab}
isVersionMode={isVersionMode}
activeContextCount={contextPanelState.activeContextItems.length}
branchCount={branchItems.length}
visibleExecLogCount={execLogState.visibleExecLogEntries.length}
activeContextCount={activeContextCount}
branchCount={branchCount}
visibleExecLogCount={visibleExecLogCount}
onTabChange={setActiveTab}
onRequestCollapse={onRequestCollapse}
headerActionSlot={headerActionSlot}
@@ -188,100 +51,14 @@ function ThemeWorkbenchSidebarComponent({
>
<ThemeWorkbenchSidebarPanels
activeTab={activeTab}
contextState={contextPanelState}
contextConfig={{
contextItems,
contextBudget,
contextSearchQuery,
contextSearchMode,
contextSearchLoading,
contextSearchError,
contextSearchBlockedReason,
onContextSearchQueryChange,
onContextSearchModeChange,
onSubmitContextSearch,
onToggleContextActive,
onViewContextDetail,
}}
workflowState={workflowPanelState}
workflowConfig={{
isVersionMode,
onNewTopic,
onSwitchTopic,
onDeleteTopic,
branchItems,
onSetBranchStatus,
workflowSteps,
onAddImage,
onImportDocument,
creationTaskEventsCount: creationTaskEvents.length,
onViewRunDetail,
activeRunDetail,
activeRunDetailLoading,
runMetadataText,
runMetadataSummary,
onCopyText: writeThemeWorkbenchClipboardText,
onRevealArtifactInFinder: handleRevealArtifactInFinder,
onOpenArtifactWithDefaultApp: handleOpenArtifactWithDefaultApp,
}}
execLogState={execLogState}
execLogConfig={{
onLoadMoreHistory,
historyHasMore,
historyLoading,
}}
contextPanelProps={contextPanelProps}
workflowPanelProps={workflowPanelProps}
execLogProps={execLogProps}
/>
</ThemeWorkbenchSidebarShell>
);
}
function areThemeWorkbenchSidebarPropsEqual(
previous: ThemeWorkbenchSidebarProps,
next: ThemeWorkbenchSidebarProps,
): boolean {
return (
previous.branchMode === next.branchMode &&
previous.onNewTopic === next.onNewTopic &&
previous.onSwitchTopic === next.onSwitchTopic &&
previous.onDeleteTopic === next.onDeleteTopic &&
previous.branchItems === next.branchItems &&
previous.onSetBranchStatus === next.onSetBranchStatus &&
previous.workflowSteps === next.workflowSteps &&
previous.contextSearchQuery === next.contextSearchQuery &&
previous.onContextSearchQueryChange === next.onContextSearchQueryChange &&
previous.contextSearchMode === next.contextSearchMode &&
previous.onContextSearchModeChange === next.onContextSearchModeChange &&
previous.contextSearchLoading === next.contextSearchLoading &&
previous.contextSearchError === next.contextSearchError &&
previous.contextSearchBlockedReason === next.contextSearchBlockedReason &&
previous.onSubmitContextSearch === next.onSubmitContextSearch &&
previous.onAddTextContext === next.onAddTextContext &&
previous.onAddLinkContext === next.onAddLinkContext &&
previous.onAddFileContext === next.onAddFileContext &&
previous.onAddImage === next.onAddImage &&
previous.onImportDocument === next.onImportDocument &&
previous.contextItems === next.contextItems &&
previous.onToggleContextActive === next.onToggleContextActive &&
previous.contextBudget.activeCount === next.contextBudget.activeCount &&
previous.contextBudget.activeCountLimit === next.contextBudget.activeCountLimit &&
previous.contextBudget.estimatedTokens === next.contextBudget.estimatedTokens &&
previous.contextBudget.tokenLimit === next.contextBudget.tokenLimit &&
previous.activityLogs === next.activityLogs &&
previous.creationTaskEvents === next.creationTaskEvents &&
previous.onViewRunDetail === next.onViewRunDetail &&
previous.activeRunDetail === next.activeRunDetail &&
previous.activeRunDetailLoading === next.activeRunDetailLoading &&
previous.onRequestCollapse === next.onRequestCollapse &&
previous.historyHasMore === next.historyHasMore &&
previous.historyLoading === next.historyLoading &&
previous.onLoadMoreHistory === next.onLoadMoreHistory &&
previous.skillDetailMap === next.skillDetailMap &&
previous.headerActionSlot === next.headerActionSlot &&
previous.topSlot === next.topSlot &&
previous.messages === next.messages
);
}
export const ThemeWorkbenchSidebar = memo(
ThemeWorkbenchSidebarComponent,
areThemeWorkbenchSidebarPropsEqual,
@@ -1,170 +1,28 @@
import type { ComponentProps } from "react";
import type { AgentRun } from "@/lib/api/executionRun";
import { ThemeWorkbenchContextPanel } from "./ThemeWorkbenchContextPanel";
import { ThemeWorkbenchExecLog } from "./ThemeWorkbenchExecLog";
import { ThemeWorkbenchWorkflowPanel } from "./ThemeWorkbenchWorkflowPanel";
import type { ThemeWorkbenchSidebarTab } from "./ThemeWorkbenchSidebarShell";
import type {
ThemeWorkbenchContextBudget,
ThemeWorkbenchContextItem,
} from "./themeWorkbenchContextData";
import type { ThemeWorkbenchRunMetadataSummary } from "./themeWorkbenchWorkflowData";
import type { ThemeWorkbenchContextPanelState } from "./useThemeWorkbenchContextPanelState";
import type { ThemeWorkbenchExecLogState } from "./useThemeWorkbenchExecLogState";
import type { ThemeWorkbenchWorkflowPanelState } from "./useThemeWorkbenchWorkflowPanelState";
import type { ThemeWorkbenchSidebarContentProps } from "./themeWorkbenchSidebarContentContract";
interface ThemeWorkbenchSidebarPanelsProps {
export interface ThemeWorkbenchSidebarPanelsProps
extends ThemeWorkbenchSidebarContentProps {
activeTab: ThemeWorkbenchSidebarTab;
contextState: ThemeWorkbenchContextPanelState;
contextConfig: {
contextItems: ThemeWorkbenchContextItem[];
contextBudget: ThemeWorkbenchContextBudget;
contextSearchQuery: string;
contextSearchMode: "web" | "social";
contextSearchLoading: boolean;
contextSearchError?: string | null;
contextSearchBlockedReason?: string | null;
onContextSearchQueryChange: (value: string) => void;
onContextSearchModeChange: (value: "web" | "social") => void;
onSubmitContextSearch: () => Promise<void> | void;
onToggleContextActive: (contextId: string) => void;
onViewContextDetail?: (contextId: string) => void;
};
workflowState: ThemeWorkbenchWorkflowPanelState;
workflowConfig: {
isVersionMode: boolean;
onNewTopic: () => void;
onSwitchTopic: (topicId: string) => void;
onDeleteTopic: (topicId: string) => void;
branchItems: ComponentProps<typeof ThemeWorkbenchWorkflowPanel>["branchItems"];
onSetBranchStatus: ComponentProps<
typeof ThemeWorkbenchWorkflowPanel
>["onSetBranchStatus"];
workflowSteps: ComponentProps<typeof ThemeWorkbenchWorkflowPanel>["workflowSteps"];
onAddImage?: () => Promise<void> | void;
onImportDocument?: () => Promise<void> | void;
creationTaskEventsCount: number;
onViewRunDetail?: (runId: string) => void;
activeRunDetail?: AgentRun | null;
activeRunDetailLoading?: boolean;
runMetadataText: string;
runMetadataSummary: ThemeWorkbenchRunMetadataSummary;
onCopyText: (text: string) => Promise<void> | void;
onRevealArtifactInFinder: (
artifactPath: string,
sessionId?: string | null,
) => Promise<void> | void;
onOpenArtifactWithDefaultApp: (
artifactPath: string,
sessionId?: string | null,
) => Promise<void> | void;
};
execLogState: ThemeWorkbenchExecLogState;
execLogConfig: {
onLoadMoreHistory?: () => void;
historyHasMore: boolean;
historyLoading: boolean;
};
}
export function ThemeWorkbenchSidebarPanels({
activeTab,
contextState,
contextConfig,
workflowState,
workflowConfig,
execLogState,
execLogConfig,
contextPanelProps,
workflowPanelProps,
execLogProps,
}: ThemeWorkbenchSidebarPanelsProps) {
if (activeTab === "context") {
return (
<ThemeWorkbenchContextPanel
contextItems={contextConfig.contextItems}
searchContextItems={contextState.searchContextItems}
orderedContextItems={contextState.orderedContextItems}
selectedSearchResult={contextState.selectedSearchResult}
latestSearchLabel={contextState.latestSearchLabel}
contextBudget={contextConfig.contextBudget}
contextSearchQuery={contextConfig.contextSearchQuery}
contextSearchMode={contextConfig.contextSearchMode}
contextSearchLoading={contextConfig.contextSearchLoading}
contextSearchError={contextConfig.contextSearchError}
contextSearchBlockedReason={contextConfig.contextSearchBlockedReason}
isSearchActionDisabled={contextState.isSearchActionDisabled}
searchInputRef={contextState.searchInputRef}
onContextSearchQueryChange={contextConfig.onContextSearchQueryChange}
onContextSearchModeChange={contextConfig.onContextSearchModeChange}
onSubmitContextSearch={contextConfig.onSubmitContextSearch}
onOpenAddContextDialog={contextState.openAddContextDialog}
onSelectSearchResult={contextState.handleSelectSearchResult}
onToggleContextActive={contextConfig.onToggleContextActive}
onViewContextDetail={contextConfig.onViewContextDetail}
addContextDialogOpen={contextState.addContextDialogOpen}
addTextDialogOpen={contextState.addTextDialogOpen}
addLinkDialogOpen={contextState.addLinkDialogOpen}
contextDraftText={contextState.contextDraftText}
contextDraftLink={contextState.contextDraftLink}
contextCreateLoading={contextState.contextCreateLoading}
contextCreateError={contextState.contextCreateError}
contextDropActive={contextState.contextDropActive}
onCloseAllContextDialogs={contextState.closeAllContextDialogs}
onChooseContextFile={contextState.handleChooseContextFile}
onDropContextFile={contextState.handleDropContextFile}
onOpenTextContextDialog={contextState.openTextContextDialog}
onOpenLinkContextDialog={contextState.openLinkContextDialog}
onContextDraftTextChange={contextState.handleContextDraftTextChange}
onContextDraftLinkChange={contextState.handleContextDraftLinkChange}
onContextDropActiveChange={contextState.handleContextDropActiveChange}
onSubmitTextContext={contextState.handleSubmitTextContext}
onSubmitLinkContext={contextState.handleSubmitLinkContext}
/>
);
return <ThemeWorkbenchContextPanel {...contextPanelProps} />;
}
if (activeTab === "workflow") {
return (
<ThemeWorkbenchWorkflowPanel
isVersionMode={workflowConfig.isVersionMode}
onNewTopic={workflowConfig.onNewTopic}
onSwitchTopic={workflowConfig.onSwitchTopic}
onDeleteTopic={workflowConfig.onDeleteTopic}
branchItems={workflowConfig.branchItems}
onSetBranchStatus={workflowConfig.onSetBranchStatus}
workflowSteps={workflowConfig.workflowSteps}
completedSteps={workflowState.completedSteps}
progressPercent={workflowState.progressPercent}
onAddImage={workflowConfig.onAddImage}
onImportDocument={workflowConfig.onImportDocument}
creationTaskEventsCount={workflowConfig.creationTaskEventsCount}
showCreationTasks={workflowState.showCreationTasks}
onToggleCreationTasks={workflowState.toggleCreationTasks}
groupedCreationTaskEvents={workflowState.groupedCreationTaskEvents}
showActivityLogs={workflowState.showActivityLogs}
onToggleActivityLogs={workflowState.toggleActivityLogs}
groupedActivityLogs={workflowState.groupedActivityLogs}
onViewRunDetail={workflowConfig.onViewRunDetail}
activeRunDetail={workflowConfig.activeRunDetail}
activeRunDetailLoading={workflowConfig.activeRunDetailLoading}
activeRunStagesLabel={workflowState.activeRunStagesLabel}
runMetadataText={workflowConfig.runMetadataText}
runMetadataSummary={workflowConfig.runMetadataSummary}
onCopyText={workflowConfig.onCopyText}
onRevealArtifactInFinder={workflowConfig.onRevealArtifactInFinder}
onOpenArtifactWithDefaultApp={workflowConfig.onOpenArtifactWithDefaultApp}
/>
);
return <ThemeWorkbenchWorkflowPanel {...workflowPanelProps} />;
}
if (activeTab === "log") {
return (
<ThemeWorkbenchExecLog
entries={execLogState.visibleExecLogEntries}
totalEntriesCount={execLogState.execLogEntries.length}
wasCleared={execLogState.wasExecLogCleared}
onClear={execLogState.clearExecLog}
onLoadMoreHistory={execLogConfig.onLoadMoreHistory}
historyHasMore={execLogConfig.historyHasMore}
historyLoading={execLogConfig.historyLoading}
/>
);
return <ThemeWorkbenchExecLog {...execLogProps} />;
}
return null;
}
@@ -0,0 +1,75 @@
import type { ThemeWorkbenchSidebarContextPanelProps } from "./themeWorkbenchSidebarContentContract";
import type { ThemeWorkbenchContextPanelState } from "./useThemeWorkbenchContextPanelState";
export interface BuildThemeWorkbenchContextPanelPropsParams {
contextPanelState: ThemeWorkbenchContextPanelState;
contextBudget: ThemeWorkbenchSidebarContextPanelProps["contextBudget"];
contextItems: ThemeWorkbenchSidebarContextPanelProps["contextItems"];
contextSearchBlockedReason?: ThemeWorkbenchSidebarContextPanelProps["contextSearchBlockedReason"];
contextSearchError?: ThemeWorkbenchSidebarContextPanelProps["contextSearchError"];
contextSearchLoading: ThemeWorkbenchSidebarContextPanelProps["contextSearchLoading"];
contextSearchMode: ThemeWorkbenchSidebarContextPanelProps["contextSearchMode"];
contextSearchQuery: ThemeWorkbenchSidebarContextPanelProps["contextSearchQuery"];
onContextSearchModeChange: ThemeWorkbenchSidebarContextPanelProps["onContextSearchModeChange"];
onContextSearchQueryChange: ThemeWorkbenchSidebarContextPanelProps["onContextSearchQueryChange"];
onSubmitContextSearch: ThemeWorkbenchSidebarContextPanelProps["onSubmitContextSearch"];
onToggleContextActive: ThemeWorkbenchSidebarContextPanelProps["onToggleContextActive"];
onViewContextDetail?: ThemeWorkbenchSidebarContextPanelProps["onViewContextDetail"];
}
export function buildThemeWorkbenchContextPanelProps({
contextBudget,
contextItems,
contextPanelState,
contextSearchBlockedReason,
contextSearchError,
contextSearchLoading,
contextSearchMode,
contextSearchQuery,
onContextSearchModeChange,
onContextSearchQueryChange,
onSubmitContextSearch,
onToggleContextActive,
onViewContextDetail,
}: BuildThemeWorkbenchContextPanelPropsParams): ThemeWorkbenchSidebarContextPanelProps {
return {
contextItems,
searchContextItems: contextPanelState.searchContextItems,
orderedContextItems: contextPanelState.orderedContextItems,
selectedSearchResult: contextPanelState.selectedSearchResult,
latestSearchLabel: contextPanelState.latestSearchLabel,
contextBudget,
contextSearchQuery,
contextSearchMode,
contextSearchLoading,
contextSearchError,
contextSearchBlockedReason,
isSearchActionDisabled: contextPanelState.isSearchActionDisabled,
searchInputRef: contextPanelState.searchInputRef,
onContextSearchQueryChange,
onContextSearchModeChange,
onSubmitContextSearch,
onOpenAddContextDialog: contextPanelState.openAddContextDialog,
onSelectSearchResult: contextPanelState.handleSelectSearchResult,
onToggleContextActive,
onViewContextDetail,
addContextDialogOpen: contextPanelState.addContextDialogOpen,
addTextDialogOpen: contextPanelState.addTextDialogOpen,
addLinkDialogOpen: contextPanelState.addLinkDialogOpen,
contextDraftText: contextPanelState.contextDraftText,
contextDraftLink: contextPanelState.contextDraftLink,
contextCreateLoading: contextPanelState.contextCreateLoading,
contextCreateError: contextPanelState.contextCreateError,
contextDropActive: contextPanelState.contextDropActive,
onCloseAllContextDialogs: contextPanelState.closeAllContextDialogs,
onChooseContextFile: contextPanelState.handleChooseContextFile,
onDropContextFile: contextPanelState.handleDropContextFile,
onOpenTextContextDialog: contextPanelState.openTextContextDialog,
onOpenLinkContextDialog: contextPanelState.openLinkContextDialog,
onContextDraftTextChange: contextPanelState.handleContextDraftTextChange,
onContextDraftLinkChange: contextPanelState.handleContextDraftLinkChange,
onContextDropActiveChange: contextPanelState.handleContextDropActiveChange,
onSubmitTextContext: contextPanelState.handleSubmitTextContext,
onSubmitLinkContext: contextPanelState.handleSubmitLinkContext,
};
}
@@ -0,0 +1,26 @@
import type { ThemeWorkbenchSidebarExecLogProps } from "./themeWorkbenchSidebarContentContract";
import type { ThemeWorkbenchExecLogState } from "./useThemeWorkbenchExecLogState";
export interface BuildThemeWorkbenchExecLogPropsParams {
execLogState: ThemeWorkbenchExecLogState;
historyHasMore?: ThemeWorkbenchSidebarExecLogProps["historyHasMore"];
historyLoading?: ThemeWorkbenchSidebarExecLogProps["historyLoading"];
onLoadMoreHistory?: ThemeWorkbenchSidebarExecLogProps["onLoadMoreHistory"];
}
export function buildThemeWorkbenchExecLogProps({
execLogState,
historyHasMore = false,
historyLoading = false,
onLoadMoreHistory,
}: BuildThemeWorkbenchExecLogPropsParams): ThemeWorkbenchSidebarExecLogProps {
return {
entries: execLogState.visibleExecLogEntries,
totalEntriesCount: execLogState.execLogEntries.length,
wasCleared: execLogState.wasExecLogCleared,
onClear: execLogState.clearExecLog,
onLoadMoreHistory,
historyHasMore,
historyLoading,
};
}
@@ -0,0 +1,64 @@
import type {
ThemeWorkbenchSidebarContextContract,
ThemeWorkbenchSidebarExecLogContract,
ThemeWorkbenchSidebarWorkflowContract,
} from "./themeWorkbenchSidebarContract";
import type { ThemeWorkbenchSidebarOrchestrationSource } from "./themeWorkbenchSidebarOrchestrationContract";
type ThemeWorkbenchSidebarOrchestrationSourceProps =
ThemeWorkbenchSidebarContextContract &
ThemeWorkbenchSidebarWorkflowContract &
ThemeWorkbenchSidebarExecLogContract;
interface BuildThemeWorkbenchSidebarOrchestrationSourceParams {
isVersionMode: boolean;
props: ThemeWorkbenchSidebarOrchestrationSourceProps;
}
export function buildThemeWorkbenchSidebarOrchestrationSource({
isVersionMode,
props,
}: BuildThemeWorkbenchSidebarOrchestrationSourceParams): ThemeWorkbenchSidebarOrchestrationSource {
return {
isVersionMode,
context: {
contextBudget: props.contextBudget,
contextItems: props.contextItems,
contextSearchBlockedReason: props.contextSearchBlockedReason,
contextSearchError: props.contextSearchError,
contextSearchLoading: props.contextSearchLoading,
contextSearchMode: props.contextSearchMode,
contextSearchQuery: props.contextSearchQuery,
onAddFileContext: props.onAddFileContext,
onAddLinkContext: props.onAddLinkContext,
onAddTextContext: props.onAddTextContext,
onContextSearchModeChange: props.onContextSearchModeChange,
onContextSearchQueryChange: props.onContextSearchQueryChange,
onSubmitContextSearch: props.onSubmitContextSearch,
onToggleContextActive: props.onToggleContextActive,
onViewContextDetail: props.onViewContextDetail,
},
workflow: {
activeRunDetail: props.activeRunDetail,
activeRunDetailLoading: props.activeRunDetailLoading,
activityLogs: props.activityLogs,
branchItems: props.branchItems,
creationTaskEvents: props.creationTaskEvents,
onAddImage: props.onAddImage,
onDeleteTopic: props.onDeleteTopic,
onImportDocument: props.onImportDocument,
onNewTopic: props.onNewTopic,
onSetBranchStatus: props.onSetBranchStatus,
onSwitchTopic: props.onSwitchTopic,
onViewRunDetail: props.onViewRunDetail,
workflowSteps: props.workflowSteps,
},
execLog: {
historyHasMore: props.historyHasMore,
historyLoading: props.historyLoading,
messages: props.messages,
onLoadMoreHistory: props.onLoadMoreHistory,
skillDetailMap: props.skillDetailMap,
},
};
}
@@ -0,0 +1,77 @@
import type { ThemeWorkbenchSidebarWorkflowPanelProps } from "./themeWorkbenchSidebarContentContract";
import { writeThemeWorkbenchClipboardText } from "./themeWorkbenchSidebarShared";
import type { ThemeWorkbenchWorkflowPanelState } from "./useThemeWorkbenchWorkflowPanelState";
export interface BuildThemeWorkbenchWorkflowPanelPropsParams {
isVersionMode: ThemeWorkbenchSidebarWorkflowPanelProps["isVersionMode"];
activeRunDetail: ThemeWorkbenchSidebarWorkflowPanelProps["activeRunDetail"];
activeRunDetailLoading: ThemeWorkbenchSidebarWorkflowPanelProps["activeRunDetailLoading"];
branchItems: ThemeWorkbenchSidebarWorkflowPanelProps["branchItems"];
creationTaskEventsCount: ThemeWorkbenchSidebarWorkflowPanelProps["creationTaskEventsCount"];
onAddImage?: ThemeWorkbenchSidebarWorkflowPanelProps["onAddImage"];
onDeleteTopic: ThemeWorkbenchSidebarWorkflowPanelProps["onDeleteTopic"];
onImportDocument?: ThemeWorkbenchSidebarWorkflowPanelProps["onImportDocument"];
onNewTopic: ThemeWorkbenchSidebarWorkflowPanelProps["onNewTopic"];
onOpenArtifactWithDefaultApp: (
artifactPath: string,
sessionId?: string | null,
) => Promise<void> | void;
onRevealArtifactInFinder: (
artifactPath: string,
sessionId?: string | null,
) => Promise<void> | void;
onSetBranchStatus: ThemeWorkbenchSidebarWorkflowPanelProps["onSetBranchStatus"];
onSwitchTopic: ThemeWorkbenchSidebarWorkflowPanelProps["onSwitchTopic"];
onViewRunDetail?: ThemeWorkbenchSidebarWorkflowPanelProps["onViewRunDetail"];
workflowPanelState: ThemeWorkbenchWorkflowPanelState;
workflowSteps: ThemeWorkbenchSidebarWorkflowPanelProps["workflowSteps"];
}
export function buildThemeWorkbenchWorkflowPanelProps({
isVersionMode,
branchItems,
creationTaskEventsCount,
onAddImage,
onDeleteTopic,
onImportDocument,
onNewTopic,
onOpenArtifactWithDefaultApp,
onRevealArtifactInFinder,
onSetBranchStatus,
onSwitchTopic,
onViewRunDetail,
workflowPanelState,
workflowSteps,
activeRunDetail,
activeRunDetailLoading,
}: BuildThemeWorkbenchWorkflowPanelPropsParams): ThemeWorkbenchSidebarWorkflowPanelProps {
return {
isVersionMode,
onNewTopic,
onSwitchTopic,
onDeleteTopic,
branchItems,
onSetBranchStatus,
workflowSteps,
completedSteps: workflowPanelState.completedSteps,
progressPercent: workflowPanelState.progressPercent,
onAddImage,
onImportDocument,
creationTaskEventsCount,
showCreationTasks: workflowPanelState.showCreationTasks,
onToggleCreationTasks: workflowPanelState.toggleCreationTasks,
groupedCreationTaskEvents: workflowPanelState.groupedCreationTaskEvents,
showActivityLogs: workflowPanelState.showActivityLogs,
onToggleActivityLogs: workflowPanelState.toggleActivityLogs,
groupedActivityLogs: workflowPanelState.groupedActivityLogs,
onViewRunDetail,
activeRunDetail,
activeRunDetailLoading,
activeRunStagesLabel: workflowPanelState.activeRunStagesLabel,
runMetadataText: workflowPanelState.runMetadataText,
runMetadataSummary: workflowPanelState.runMetadataSummary,
onCopyText: writeThemeWorkbenchClipboardText,
onRevealArtifactInFinder,
onOpenArtifactWithDefaultApp,
};
}
@@ -0,0 +1,103 @@
import type {
ThemeWorkbenchSidebarContextContract,
ThemeWorkbenchSidebarExecLogContract,
ThemeWorkbenchSidebarProps,
ThemeWorkbenchSidebarShellContract,
ThemeWorkbenchSidebarWorkflowContract,
} from "./themeWorkbenchSidebarContract";
function areThemeWorkbenchContextBudgetsEqual(
previous: ThemeWorkbenchSidebarProps["contextBudget"],
next: ThemeWorkbenchSidebarProps["contextBudget"],
): boolean {
return (
previous.activeCount === next.activeCount &&
previous.activeCountLimit === next.activeCountLimit &&
previous.estimatedTokens === next.estimatedTokens &&
previous.tokenLimit === next.tokenLimit
);
}
function areThemeWorkbenchSidebarShellPropsEqual(
previous: ThemeWorkbenchSidebarShellContract,
next: ThemeWorkbenchSidebarShellContract,
): boolean {
return (
previous.branchMode === next.branchMode &&
previous.onRequestCollapse === next.onRequestCollapse &&
previous.headerActionSlot === next.headerActionSlot &&
previous.topSlot === next.topSlot
);
}
function areThemeWorkbenchSidebarContextPropsEqual(
previous: ThemeWorkbenchSidebarContextContract,
next: ThemeWorkbenchSidebarContextContract,
): boolean {
return (
previous.contextSearchQuery === next.contextSearchQuery &&
previous.onContextSearchQueryChange === next.onContextSearchQueryChange &&
previous.contextSearchMode === next.contextSearchMode &&
previous.onContextSearchModeChange === next.onContextSearchModeChange &&
previous.contextSearchLoading === next.contextSearchLoading &&
previous.contextSearchError === next.contextSearchError &&
previous.contextSearchBlockedReason === next.contextSearchBlockedReason &&
previous.onSubmitContextSearch === next.onSubmitContextSearch &&
previous.onAddTextContext === next.onAddTextContext &&
previous.onAddLinkContext === next.onAddLinkContext &&
previous.onAddFileContext === next.onAddFileContext &&
previous.contextItems === next.contextItems &&
previous.onToggleContextActive === next.onToggleContextActive &&
previous.onViewContextDetail === next.onViewContextDetail &&
areThemeWorkbenchContextBudgetsEqual(
previous.contextBudget,
next.contextBudget,
)
);
}
function areThemeWorkbenchSidebarWorkflowPropsEqual(
previous: ThemeWorkbenchSidebarWorkflowContract,
next: ThemeWorkbenchSidebarWorkflowContract,
): boolean {
return (
previous.onNewTopic === next.onNewTopic &&
previous.onSwitchTopic === next.onSwitchTopic &&
previous.onDeleteTopic === next.onDeleteTopic &&
previous.branchItems === next.branchItems &&
previous.onSetBranchStatus === next.onSetBranchStatus &&
previous.workflowSteps === next.workflowSteps &&
previous.onAddImage === next.onAddImage &&
previous.onImportDocument === next.onImportDocument &&
previous.activityLogs === next.activityLogs &&
previous.creationTaskEvents === next.creationTaskEvents &&
previous.onViewRunDetail === next.onViewRunDetail &&
previous.activeRunDetail === next.activeRunDetail &&
previous.activeRunDetailLoading === next.activeRunDetailLoading
);
}
function areThemeWorkbenchSidebarExecLogPropsEqual(
previous: ThemeWorkbenchSidebarExecLogContract,
next: ThemeWorkbenchSidebarExecLogContract,
): boolean {
return (
previous.historyHasMore === next.historyHasMore &&
previous.historyLoading === next.historyLoading &&
previous.onLoadMoreHistory === next.onLoadMoreHistory &&
previous.skillDetailMap === next.skillDetailMap &&
previous.messages === next.messages
);
}
export function areThemeWorkbenchSidebarPropsEqual(
previous: ThemeWorkbenchSidebarProps,
next: ThemeWorkbenchSidebarProps,
): boolean {
return (
areThemeWorkbenchSidebarShellPropsEqual(previous, next) &&
areThemeWorkbenchSidebarContextPropsEqual(previous, next) &&
areThemeWorkbenchSidebarWorkflowPropsEqual(previous, next) &&
areThemeWorkbenchSidebarExecLogPropsEqual(previous, next)
);
}
@@ -0,0 +1,22 @@
import type { ComponentProps } from "react";
import { ThemeWorkbenchContextPanel } from "./ThemeWorkbenchContextPanel";
import { ThemeWorkbenchExecLog } from "./ThemeWorkbenchExecLog";
import { ThemeWorkbenchWorkflowPanel } from "./ThemeWorkbenchWorkflowPanel";
export type ThemeWorkbenchSidebarContextPanelProps = ComponentProps<
typeof ThemeWorkbenchContextPanel
>;
export type ThemeWorkbenchSidebarWorkflowPanelProps = ComponentProps<
typeof ThemeWorkbenchWorkflowPanel
>;
export type ThemeWorkbenchSidebarExecLogProps = ComponentProps<
typeof ThemeWorkbenchExecLog
>;
export interface ThemeWorkbenchSidebarContentProps {
contextPanelProps: ThemeWorkbenchSidebarContextPanelProps;
workflowPanelProps: ThemeWorkbenchSidebarWorkflowPanelProps;
execLogProps: ThemeWorkbenchSidebarExecLogProps;
}
@@ -0,0 +1,77 @@
import type { ReactNode } from "react";
import type { StepStatus } from "@/components/content-creator/types";
import type { SkillDetailInfo } from "@/lib/api/skill-execution";
import type { AgentRun } from "@/lib/api/executionRun";
import type {
TopicBranchItem,
TopicBranchStatus,
} from "../hooks/useTopicBranchBoard";
import type { SidebarActivityLog } from "../hooks/useThemeContextWorkspace";
import type { Message } from "../types";
import type {
ThemeWorkbenchContextBudget,
ThemeWorkbenchContextItem,
} from "./themeWorkbenchContextData";
import type { ThemeWorkbenchCreationTaskEvent } from "./themeWorkbenchWorkflowData";
import type {
ThemeWorkbenchAddFileContextAction,
ThemeWorkbenchAddLinkContextAction,
ThemeWorkbenchAddTextContextAction,
} from "./useThemeWorkbenchContextPanelState";
export type BranchMode = "topic" | "version";
export interface ThemeWorkbenchSidebarShellContract {
branchMode?: BranchMode;
onRequestCollapse?: () => void;
headerActionSlot?: ReactNode;
topSlot?: ReactNode;
}
export interface ThemeWorkbenchSidebarWorkflowContract {
onNewTopic: () => void;
onSwitchTopic: (topicId: string) => void;
onDeleteTopic: (topicId: string) => void;
branchItems: TopicBranchItem[];
onSetBranchStatus: (topicId: string, status: TopicBranchStatus) => void;
workflowSteps: Array<{ id: string; title: string; status: StepStatus }>;
onAddImage?: () => Promise<void> | void;
onImportDocument?: () => Promise<void> | void;
activityLogs: SidebarActivityLog[];
creationTaskEvents?: ThemeWorkbenchCreationTaskEvent[];
onViewRunDetail?: (runId: string) => void;
activeRunDetail?: AgentRun | null;
activeRunDetailLoading?: boolean;
}
export interface ThemeWorkbenchSidebarContextContract {
contextSearchQuery: string;
onContextSearchQueryChange: (value: string) => void;
contextSearchMode: "web" | "social";
onContextSearchModeChange: (value: "web" | "social") => void;
contextSearchLoading: boolean;
contextSearchError?: string | null;
contextSearchBlockedReason?: string | null;
onSubmitContextSearch: () => Promise<void> | void;
onAddTextContext?: ThemeWorkbenchAddTextContextAction;
onAddLinkContext?: ThemeWorkbenchAddLinkContextAction;
onAddFileContext?: ThemeWorkbenchAddFileContextAction;
contextItems: ThemeWorkbenchContextItem[];
onToggleContextActive: (contextId: string) => void;
onViewContextDetail?: (contextId: string) => void;
contextBudget: ThemeWorkbenchContextBudget;
}
export interface ThemeWorkbenchSidebarExecLogContract {
historyHasMore?: boolean;
historyLoading?: boolean;
onLoadMoreHistory?: () => void;
skillDetailMap?: Record<string, SkillDetailInfo | null>;
messages?: Message[];
}
export interface ThemeWorkbenchSidebarProps
extends ThemeWorkbenchSidebarShellContract,
ThemeWorkbenchSidebarWorkflowContract,
ThemeWorkbenchSidebarContextContract,
ThemeWorkbenchSidebarExecLogContract {}
@@ -0,0 +1,102 @@
import type { SkillDetailInfo } from "@/lib/api/skill-execution";
import type { SidebarActivityLog } from "../hooks/useThemeContextWorkspace";
import type { Message } from "../types";
import type { BuildThemeWorkbenchContextPanelPropsParams } from "./buildThemeWorkbenchContextPanelProps";
import type { BuildThemeWorkbenchExecLogPropsParams } from "./buildThemeWorkbenchExecLogProps";
import type { BuildThemeWorkbenchWorkflowPanelPropsParams } from "./buildThemeWorkbenchWorkflowPanelProps";
import type { ThemeWorkbenchCreationTaskEvent } from "./themeWorkbenchWorkflowData";
import type {
ThemeWorkbenchAddFileContextAction,
ThemeWorkbenchAddLinkContextAction,
ThemeWorkbenchAddTextContextAction,
} from "./useThemeWorkbenchContextPanelState";
export interface ThemeWorkbenchSidebarContextOrchestrationInput
extends Omit<BuildThemeWorkbenchContextPanelPropsParams, "contextPanelState"> {
onAddTextContext?: ThemeWorkbenchAddTextContextAction;
onAddLinkContext?: ThemeWorkbenchAddLinkContextAction;
onAddFileContext?: ThemeWorkbenchAddFileContextAction;
}
export interface ThemeWorkbenchSidebarWorkflowOrchestrationInput
extends Omit<
BuildThemeWorkbenchWorkflowPanelPropsParams,
| "creationTaskEventsCount"
| "isVersionMode"
| "onOpenArtifactWithDefaultApp"
| "onRevealArtifactInFinder"
| "workflowPanelState"
> {
activityLogs: SidebarActivityLog[];
creationTaskEvents: ThemeWorkbenchCreationTaskEvent[];
}
export interface ThemeWorkbenchSidebarExecLogOrchestrationInput
extends Omit<BuildThemeWorkbenchExecLogPropsParams, "execLogState"> {
messages: Message[];
skillDetailMap: Record<string, SkillDetailInfo | null>;
}
export interface ThemeWorkbenchSidebarOrchestrationInput {
isVersionMode: boolean;
context: ThemeWorkbenchSidebarContextOrchestrationInput;
workflow: ThemeWorkbenchSidebarWorkflowOrchestrationInput;
execLog: ThemeWorkbenchSidebarExecLogOrchestrationInput;
}
export type ThemeWorkbenchSidebarContextOrchestrationSource =
ThemeWorkbenchSidebarContextOrchestrationInput;
export interface ThemeWorkbenchSidebarWorkflowOrchestrationSource
extends Omit<
ThemeWorkbenchSidebarWorkflowOrchestrationInput,
"creationTaskEvents"
> {
creationTaskEvents?: ThemeWorkbenchCreationTaskEvent[];
}
export interface ThemeWorkbenchSidebarExecLogOrchestrationSource
extends Omit<
ThemeWorkbenchSidebarExecLogOrchestrationInput,
"messages" | "skillDetailMap"
> {
messages?: Message[];
skillDetailMap?: Record<string, SkillDetailInfo | null>;
}
export interface ThemeWorkbenchSidebarOrchestrationSource {
isVersionMode: boolean;
context: ThemeWorkbenchSidebarContextOrchestrationSource;
workflow: ThemeWorkbenchSidebarWorkflowOrchestrationSource;
execLog: ThemeWorkbenchSidebarExecLogOrchestrationSource;
}
export function createThemeWorkbenchSidebarOrchestrationInput(
source: ThemeWorkbenchSidebarOrchestrationSource,
): ThemeWorkbenchSidebarOrchestrationInput {
const { isVersionMode, context, workflow, execLog } = source;
const { creationTaskEvents = [], activeRunDetailLoading = false } = workflow;
const {
historyHasMore = false,
historyLoading = false,
messages = [],
skillDetailMap = {},
} = execLog;
return {
isVersionMode,
context,
workflow: {
...workflow,
activeRunDetailLoading,
creationTaskEvents,
},
execLog: {
...execLog,
historyHasMore,
historyLoading,
messages,
skillDetailMap,
},
};
}
@@ -1,15 +1,3 @@
export function formatThemeWorkbenchRunMetadata(raw: string | null): string {
if (!raw || !raw.trim()) {
return "-";
}
try {
const parsed = JSON.parse(raw);
return JSON.stringify(parsed, null, 2);
} catch {
return raw;
}
}
export function formatThemeWorkbenchActionErrorMessage(
prefix: string,
error: unknown,
@@ -40,6 +40,18 @@ export interface ThemeWorkbenchRunMetadataSummary {
artifactPaths: string[];
}
export function formatThemeWorkbenchRunMetadata(raw: string | null): string {
if (!raw || !raw.trim()) {
return "-";
}
try {
const parsed = JSON.parse(raw);
return JSON.stringify(parsed, null, 2);
} catch {
return raw;
}
}
function normalizeArtifactPaths(raw?: string[]): string[] {
if (!Array.isArray(raw)) {
return [];
@@ -21,17 +21,17 @@ import {
resolveThemeWorkbenchFileNameFromPath,
} from "./themeWorkbenchSidebarShared";
type AddTextContextHandler = (payload: {
export type ThemeWorkbenchAddTextContextAction = (payload: {
content: string;
name?: string;
}) => Promise<void> | void;
type AddLinkContextHandler = (payload: {
export type ThemeWorkbenchAddLinkContextAction = (payload: {
url: string;
name?: string;
}) => Promise<void> | void;
type AddFileContextHandler = (payload: {
export type ThemeWorkbenchAddFileContextAction = (payload: {
path: string;
name?: string;
}) => Promise<void> | void;
@@ -41,9 +41,9 @@ interface UseThemeWorkbenchContextPanelStateParams {
contextSearchQuery: string;
contextSearchLoading: boolean;
contextSearchBlockedReason?: string | null;
onAddTextContext?: AddTextContextHandler;
onAddLinkContext?: AddLinkContextHandler;
onAddFileContext?: AddFileContextHandler;
onAddTextContext?: ThemeWorkbenchAddTextContextAction;
onAddLinkContext?: ThemeWorkbenchAddLinkContextAction;
onAddFileContext?: ThemeWorkbenchAddFileContextAction;
}
export interface ThemeWorkbenchContextPanelState {
@@ -0,0 +1,122 @@
import type { ThemeWorkbenchSidebarTab } from "./ThemeWorkbenchSidebarShell";
import type { ThemeWorkbenchSidebarContentProps } from "./themeWorkbenchSidebarContentContract";
import type { ThemeWorkbenchSidebarOrchestrationInput } from "./themeWorkbenchSidebarOrchestrationContract";
import { buildThemeWorkbenchContextPanelProps } from "./buildThemeWorkbenchContextPanelProps";
import { buildThemeWorkbenchExecLogProps } from "./buildThemeWorkbenchExecLogProps";
import { buildThemeWorkbenchWorkflowPanelProps } from "./buildThemeWorkbenchWorkflowPanelProps";
import { useThemeWorkbenchArtifactActions } from "./useThemeWorkbenchArtifactActions";
import { useThemeWorkbenchContextPanelState } from "./useThemeWorkbenchContextPanelState";
import { useThemeWorkbenchExecLogState } from "./useThemeWorkbenchExecLogState";
import { useThemeWorkbenchSidebarTelemetry } from "./useThemeWorkbenchSidebarTelemetry";
import { useThemeWorkbenchWorkflowPanelState } from "./useThemeWorkbenchWorkflowPanelState";
interface UseThemeWorkbenchSidebarOrchestrationParams {
activeTab: ThemeWorkbenchSidebarTab;
input: ThemeWorkbenchSidebarOrchestrationInput;
}
export interface ThemeWorkbenchSidebarOrchestration {
branchCount: number;
isVersionMode: boolean;
activeContextCount: number;
visibleExecLogCount: number;
contextPanelProps: ThemeWorkbenchSidebarContentProps["contextPanelProps"];
workflowPanelProps: ThemeWorkbenchSidebarContentProps["workflowPanelProps"];
execLogProps: ThemeWorkbenchSidebarContentProps["execLogProps"];
}
export function useThemeWorkbenchSidebarOrchestration({
activeTab,
input,
}: UseThemeWorkbenchSidebarOrchestrationParams): ThemeWorkbenchSidebarOrchestration {
const { isVersionMode, context, workflow, execLog } = input;
const branchCount = workflow.branchItems.length;
const runDetailSessionId = workflow.activeRunDetail?.session_id?.trim() || null;
const { handleRevealArtifactInFinder, handleOpenArtifactWithDefaultApp } =
useThemeWorkbenchArtifactActions({
runDetailSessionId,
});
const workflowPanelState = useThemeWorkbenchWorkflowPanelState({
workflowSteps: workflow.workflowSteps,
activityLogs: workflow.activityLogs,
creationTaskEvents: workflow.creationTaskEvents,
activeRunMetadata: workflow.activeRunDetail?.metadata ?? null,
});
const contextPanelState = useThemeWorkbenchContextPanelState({
contextItems: context.contextItems,
contextSearchQuery: context.contextSearchQuery,
contextSearchLoading: context.contextSearchLoading,
contextSearchBlockedReason: context.contextSearchBlockedReason,
onAddTextContext: context.onAddTextContext,
onAddLinkContext: context.onAddLinkContext,
onAddFileContext: context.onAddFileContext,
});
useThemeWorkbenchSidebarTelemetry({
activeTab,
showActivityLogs: workflowPanelState.showActivityLogs,
contextSearchLoading: context.contextSearchLoading,
branchItemsCount: branchCount,
workflowStepsCount: workflow.workflowSteps.length,
contextItemsCount: context.contextItems.length,
activeContextCount: contextPanelState.activeContextItems.length,
activityLogsCount: workflow.activityLogs.length,
creationTaskEventsCount: workflow.creationTaskEvents.length,
hasActiveRunDetail: Boolean(workflow.activeRunDetail),
});
const execLogState = useThemeWorkbenchExecLogState({
messages: execLog.messages,
groupedActivityLogs: workflowPanelState.groupedActivityLogs,
groupedCreationTaskEvents: workflowPanelState.groupedCreationTaskEvents,
skillDetailMap: execLog.skillDetailMap,
});
return {
branchCount,
isVersionMode,
activeContextCount: contextPanelState.activeContextItems.length,
visibleExecLogCount: execLogState.visibleExecLogEntries.length,
contextPanelProps: buildThemeWorkbenchContextPanelProps({
contextBudget: context.contextBudget,
contextItems: context.contextItems,
contextPanelState,
contextSearchBlockedReason: context.contextSearchBlockedReason,
contextSearchError: context.contextSearchError,
contextSearchLoading: context.contextSearchLoading,
contextSearchMode: context.contextSearchMode,
contextSearchQuery: context.contextSearchQuery,
onContextSearchModeChange: context.onContextSearchModeChange,
onContextSearchQueryChange: context.onContextSearchQueryChange,
onSubmitContextSearch: context.onSubmitContextSearch,
onToggleContextActive: context.onToggleContextActive,
onViewContextDetail: context.onViewContextDetail,
}),
workflowPanelProps: buildThemeWorkbenchWorkflowPanelProps({
isVersionMode,
branchItems: workflow.branchItems,
creationTaskEventsCount: workflow.creationTaskEvents.length,
onAddImage: workflow.onAddImage,
onDeleteTopic: workflow.onDeleteTopic,
onImportDocument: workflow.onImportDocument,
onNewTopic: workflow.onNewTopic,
onOpenArtifactWithDefaultApp: handleOpenArtifactWithDefaultApp,
onRevealArtifactInFinder: handleRevealArtifactInFinder,
onSetBranchStatus: workflow.onSetBranchStatus,
onSwitchTopic: workflow.onSwitchTopic,
onViewRunDetail: workflow.onViewRunDetail,
workflowPanelState,
workflowSteps: workflow.workflowSteps,
activeRunDetail: workflow.activeRunDetail,
activeRunDetailLoading: workflow.activeRunDetailLoading,
}),
execLogProps: buildThemeWorkbenchExecLogProps({
execLogState,
historyHasMore: execLog.historyHasMore,
historyLoading: execLog.historyLoading,
onLoadMoreHistory: execLog.onLoadMoreHistory,
}),
};
}
@@ -4,7 +4,9 @@ import type { SidebarActivityLog } from "../hooks/useThemeContextWorkspace";
import {
buildThemeWorkbenchActivityLogGroups,
buildThemeWorkbenchCreationTaskGroups,
formatThemeWorkbenchRunMetadata,
formatThemeWorkbenchStagesLabel,
parseThemeWorkbenchRunMetadataSummary,
type ThemeWorkbenchCreationTaskEvent,
type ThemeWorkbenchRunMetadataSummary,
} from "./themeWorkbenchWorkflowData";
@@ -13,7 +15,7 @@ interface UseThemeWorkbenchWorkflowPanelStateParams {
workflowSteps: Array<{ id: string; title: string; status: StepStatus }>;
activityLogs: SidebarActivityLog[];
creationTaskEvents: ThemeWorkbenchCreationTaskEvent[];
runMetadataSummary: ThemeWorkbenchRunMetadataSummary;
activeRunMetadata: string | null;
}
export interface ThemeWorkbenchWorkflowPanelState {
@@ -24,6 +26,8 @@ export interface ThemeWorkbenchWorkflowPanelState {
typeof buildThemeWorkbenchCreationTaskGroups
>;
activeRunStagesLabel: string | null;
runMetadataText: string;
runMetadataSummary: ThemeWorkbenchRunMetadataSummary;
showActivityLogs: boolean;
showCreationTasks: boolean;
toggleActivityLogs: () => void;
@@ -34,7 +38,7 @@ export function useThemeWorkbenchWorkflowPanelState({
workflowSteps,
activityLogs,
creationTaskEvents,
runMetadataSummary,
activeRunMetadata,
}: UseThemeWorkbenchWorkflowPanelStateParams): ThemeWorkbenchWorkflowPanelState {
const [showActivityLogs, setShowActivityLogs] = useState(false);
const [showCreationTasks, setShowCreationTasks] = useState(true);
@@ -57,6 +61,16 @@ export function useThemeWorkbenchWorkflowPanelState({
[creationTaskEvents],
);
const runMetadataSummary = useMemo(
() => parseThemeWorkbenchRunMetadataSummary(activeRunMetadata),
[activeRunMetadata],
);
const runMetadataText = useMemo(
() => formatThemeWorkbenchRunMetadata(activeRunMetadata),
[activeRunMetadata],
);
const activeRunStagesLabel = useMemo(
() => formatThemeWorkbenchStagesLabel(runMetadataSummary.stages),
[runMetadataSummary.stages],
@@ -76,6 +90,8 @@ export function useThemeWorkbenchWorkflowPanelState({
groupedActivityLogs,
groupedCreationTaskEvents,
activeRunStagesLabel,
runMetadataText,
runMetadataSummary,
showActivityLogs,
showCreationTasks,
toggleActivityLogs,