Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qjclaw-dmg
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
AI-甘富林
qjclaw-dmg
Commits
0a63c6b5
Commit
0a63c6b5
authored
May 13, 2026
by
edy
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(ui): add task panel view
parent
613f5921
Pipeline
#18458
failed
Changes
26
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
499 additions
and
65 deletions
+499
-65
ipc.ts
apps/desktop/src/main/ipc.ts
+4
-0
index.ts
apps/desktop/src/preload/index.ts
+3
-0
App.tsx
apps/ui/src/App.tsx
+5
-1
AppIcons.tsx
apps/ui/src/components/icons/AppIcons.tsx
+9
-1
useAppBootstrap.ts
apps/ui/src/features/app/useAppBootstrap.ts
+1
-1
ChatComposer.tsx
apps/ui/src/features/chat/ChatComposer.tsx
+1
-1
ConversationWorkspaceView.tsx
apps/ui/src/features/chat/ConversationWorkspaceView.tsx
+1
-1
MessageList.tsx
apps/ui/src/features/chat/MessageList.tsx
+1
-1
useChatSessionsController.ts
apps/ui/src/features/chat/useChatSessionsController.ts
+1
-1
useChatStreamingController.ts
apps/ui/src/features/chat/useChatStreamingController.ts
+1
-1
useHomeIntentSuggestion.ts
apps/ui/src/features/chat/useHomeIntentSuggestion.ts
+1
-1
usePromptSubmission.ts
apps/ui/src/features/chat/usePromptSubmission.ts
+1
-1
AppSidebar.tsx
apps/ui/src/features/shell/AppSidebar.tsx
+2
-1
ExpertTree.tsx
apps/ui/src/features/shell/ExpertTree.tsx
+1
-1
useHomeNavigation.ts
apps/ui/src/features/shell/useHomeNavigation.ts
+1
-1
useSidebarModel.ts
apps/ui/src/features/shell/useSidebarModel.ts
+1
-1
useSmokeActionHandlers.ts
apps/ui/src/features/smoke/useSmokeActionHandlers.ts
+1
-1
useSmokeActions.ts
apps/ui/src/features/smoke/useSmokeActions.ts
+1
-1
useSmokeSnapshot.ts
apps/ui/src/features/smoke/useSmokeSnapshot.ts
+1
-1
TaskPanelView.tsx
apps/ui/src/features/tasks/TaskPanelView.tsx
+136
-0
taskPanelData.ts
apps/ui/src/features/tasks/taskPanelData.ts
+66
-0
mock-desktop-api.ts
apps/ui/src/lib/mock-desktop-api.ts
+1
-0
styles.css
apps/ui/src/styles.css
+1
-0
shell.css
apps/ui/src/styles/shell.css
+25
-47
tasks.css
apps/ui/src/styles/tasks.css
+210
-0
index.ts
packages/shared-types/src/index.ts
+23
-1
No files found.
apps/desktop/src/main/ipc.ts
View file @
0a63c6b5
...
@@ -2544,6 +2544,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
...
@@ -2544,6 +2544,7 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
ipcMain.handle(IPC_CHANNELS.expertsList, async () => expertCatalogService.list());
ipcMain.handle(IPC_CHANNELS.expertsList, async () => expertCatalogService.list());
ipcMain.handle(IPC_CHANNELS.modelConfigGetSummary, async () => modelConfigClient.getSummary());
ipcMain.handle(IPC_CHANNELS.modelConfigGetSummary, async () => modelConfigClient.getSummary());
ipcMain.handle(IPC_CHANNELS.systemGetSummary, async () => systemSummary);
ipcMain.handle(IPC_CHANNELS.systemGetSummary, async () => systemSummary);
ipcMain.handle(IPC_CHANNELS.tasksListByDate, async () => []);
ipcMain.handle(IPC_CHANNELS.skillCatalogList, async () => skillCatalogService.listForActiveProject());
ipcMain.handle(IPC_CHANNELS.skillCatalogList, async () => skillCatalogService.listForActiveProject());
ipcMain.handle(IPC_CHANNELS.projectsList, async () => projectStore.listProjects());
ipcMain.handle(IPC_CHANNELS.projectsList, async () => projectStore.listProjects());
...
@@ -2673,6 +2674,9 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
...
@@ -2673,6 +2674,9 @@ export function registerDesktopIpc(services: MainServices): RegisteredDesktopIpc
system: {
system: {
getSummary: () => Promise.resolve(systemSummary)
getSummary: () => Promise.resolve(systemSummary)
},
},
tasks: {
listByDate: async () => []
},
chat: {
chat: {
listSessions: async () => {
listSessions: async () => {
const sessions = await listSessionsForActiveProject(projectStore);
const sessions = await listSessionsForActiveProject(projectStore);
...
...
apps/desktop/src/preload/index.ts
View file @
0a63c6b5
...
@@ -78,6 +78,9 @@ const desktopApi: DesktopApi = {
...
@@ -78,6 +78,9 @@ const desktopApi: DesktopApi = {
system
:
{
system
:
{
getSummary
:
()
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
systemGetSummary
)
getSummary
:
()
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
systemGetSummary
)
},
},
tasks
:
{
listByDate
:
(
date
:
string
)
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
tasksListByDate
,
date
)
},
chat
:
{
chat
:
{
listSessions
:
()
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
chatListSessions
),
listSessions
:
()
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
chatListSessions
),
listSessionsByProject
:
(
projectId
:
string
)
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
chatListSessionsByProject
,
projectId
),
listSessionsByProject
:
(
projectId
:
string
)
=>
ipcRenderer
.
invoke
(
IPC_CHANNELS
.
chatListSessionsByProject
,
projectId
),
...
...
apps/ui/src/App.tsx
View file @
0a63c6b5
...
@@ -46,6 +46,7 @@ import { useMessageTraces } from "./features/chat/useMessageTraces";
...
@@ -46,6 +46,7 @@ import { useMessageTraces } from "./features/chat/useMessageTraces";
import
{
usePromptSubmission
}
from
"./features/chat/usePromptSubmission"
;
import
{
usePromptSubmission
}
from
"./features/chat/usePromptSubmission"
;
import
{
useSessionMessageStore
}
from
"./features/chat/useSessionMessageStore"
;
import
{
useSessionMessageStore
}
from
"./features/chat/useSessionMessageStore"
;
import
{
KnowledgeView
}
from
"./features/knowledge/KnowledgeView"
;
import
{
KnowledgeView
}
from
"./features/knowledge/KnowledgeView"
;
import
{
TaskPanelView
}
from
"./features/tasks/TaskPanelView"
;
import
{
getPluginCopy
,
getPluginStatusLabel
,
getPluginTone
,
groupPluginsByStatus
}
from
"./features/plugins/pluginDisplay"
;
import
{
getPluginCopy
,
getPluginStatusLabel
,
getPluginTone
,
groupPluginsByStatus
}
from
"./features/plugins/pluginDisplay"
;
import
{
PluginsView
}
from
"./features/plugins/PluginsView"
;
import
{
PluginsView
}
from
"./features/plugins/PluginsView"
;
import
{
AppSidebar
}
from
"./features/shell/AppSidebar"
;
import
{
AppSidebar
}
from
"./features/shell/AppSidebar"
;
...
@@ -97,7 +98,7 @@ import {
...
@@ -97,7 +98,7 @@ import {
}
from
"./lib/constants"
;
}
from
"./lib/constants"
;
import
{
desktopApi
,
isMockDesktopApi
,
smokeEnabled
}
from
"./lib/desktop-api"
;
import
{
desktopApi
,
isMockDesktopApi
,
smokeEnabled
}
from
"./lib/desktop-api"
;
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
;
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
;
type
SendPhase
=
"idle"
|
"preparing"
|
"streaming"
|
"finalizing"
;
type
SendPhase
=
"idle"
|
"preparing"
|
"streaming"
|
"finalizing"
;
type
MessageReaction
=
"up"
|
"down"
;
type
MessageReaction
=
"up"
|
"down"
;
...
@@ -1380,6 +1381,9 @@ export default function App() {
...
@@ -1380,6 +1381,9 @@ export default function App() {
{
viewMode
===
"knowledge"
?
(
{
viewMode
===
"knowledge"
?
(
<
KnowledgeView
/>
<
KnowledgeView
/>
)
:
null
}
)
:
null
}
{
viewMode
===
"tasks"
?
(
<
TaskPanelView
/>
)
:
null
}
{
viewMode
===
"settings"
?
(
{
viewMode
===
"settings"
?
(
<
SettingsView
statusHint=
{
settingsStatusHint
}
>
<
SettingsView
statusHint=
{
settingsStatusHint
}
>
<
SettingsPanels
{
...
settingsPanelsProps
}
/>
<
SettingsPanels
{
...
settingsPanelsProps
}
/>
...
...
apps/ui/src/components/icons/AppIcons.tsx
View file @
0a63c6b5
...
@@ -234,7 +234,7 @@ export function getIntentSuggestionIcon(platform?: string): ReactNode {
...
@@ -234,7 +234,7 @@ export function getIntentSuggestionIcon(platform?: string): ReactNode {
return
<
BrowserExpertIcon
/>;
return
<
BrowserExpertIcon
/>;
}
}
export
function
NavIcon
({
kind
}:
{
kind
:
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
})
{
export
function
NavIcon
({
kind
}:
{
kind
:
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
})
{
switch
(
kind
)
{
switch
(
kind
)
{
case
"chat"
:
case
"chat"
:
return
(
return
(
...
@@ -250,6 +250,14 @@ export function NavIcon({ kind }: { kind: "chat" | "experts" | "plugins" | "sett
...
@@ -250,6 +250,14 @@ export function NavIcon({ kind }: { kind: "chat" | "experts" | "plugins" | "sett
<
path
d=
"m12 7.1.78 1.58 1.75.25-1.27 1.24.3 1.74L12 11.09l-1.56.82.3-1.74-1.27-1.24 1.75-.25L12 7.1Z"
fill=
"#F59E0B"
/>
<
path
d=
"m12 7.1.78 1.58 1.75.25-1.27 1.24.3 1.74L12 11.09l-1.56.82.3-1.74-1.27-1.24 1.75-.25L12 7.1Z"
fill=
"#F59E0B"
/>
</
svg
>
</
svg
>
);
);
case
"tasks"
:
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
path
d=
"M6.2 4.25h11.6A2.45 2.45 0 0 1 20.25 6.7v10.6a2.45 2.45 0 0 1-2.45 2.45H6.2a2.45 2.45 0 0 1-2.45-2.45V6.7A2.45 2.45 0 0 1 6.2 4.25Z"
fill=
"#ECFDF5"
stroke=
"#059669"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.45"
/>
<
path
d=
"M7.4 8.05h1.55l.72 1.12 1.6-2.18M13.05 8.25h3.8M7.4 13h1.55l.72 1.12 1.6-2.18M13.05 13.2h3.8"
fill=
"none"
stroke=
"#047857"
strokeLinecap=
"round"
strokeLinejoin=
"round"
strokeWidth=
"1.35"
/>
<
path
d=
"M13.05 16.95h2.8"
fill=
"none"
stroke=
"#2563EB"
strokeLinecap=
"round"
strokeWidth=
"1.35"
/>
</
svg
>
);
case
"plugins"
:
case
"plugins"
:
return
(
return
(
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
<
svg
viewBox=
"0 0 24 24"
aria
-
hidden=
"true"
focusable=
"false"
>
...
...
apps/ui/src/features/app/useAppBootstrap.ts
View file @
0a63c6b5
...
@@ -13,7 +13,7 @@ import type {
...
@@ -13,7 +13,7 @@ import type {
WorkspaceSummary
WorkspaceSummary
}
from
"@qjclaw/shared-types"
}
from
"@qjclaw/shared-types"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
interface
BootstrapSkill
{
interface
BootstrapSkill
{
id
:
string
id
:
string
...
...
apps/ui/src/features/chat/ChatComposer.tsx
View file @
0a63c6b5
...
@@ -22,7 +22,7 @@ interface ChatComposerProps {
...
@@ -22,7 +22,7 @@ interface ChatComposerProps {
canSend
:
boolean
canSend
:
boolean
isDragOver
:
boolean
isDragOver
:
boolean
isResizeActive
:
boolean
isResizeActive
:
boolean
viewMode
:
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
viewMode
:
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
shellStyle
:
CSSProperties
shellStyle
:
CSSProperties
attachmentInputRef
:
RefObject
<
HTMLInputElement
|
null
>
attachmentInputRef
:
RefObject
<
HTMLInputElement
|
null
>
skillMenuRef
:
RefObject
<
HTMLDivElement
|
null
>
skillMenuRef
:
RefObject
<
HTMLDivElement
|
null
>
...
...
apps/ui/src/features/chat/ConversationWorkspaceView.tsx
View file @
0a63c6b5
...
@@ -19,7 +19,7 @@ import { ConversationStatusNotice, HomeIntentSuggestionNotice } from "./Conversa
...
@@ -19,7 +19,7 @@ import { ConversationStatusNotice, HomeIntentSuggestionNotice } from "./Conversa
import
{
MessageList
,
type
ExpertKey
,
type
MessageListMessage
}
from
"./MessageList"
import
{
MessageList
,
type
ExpertKey
,
type
MessageListMessage
}
from
"./MessageList"
import
type
{
MessageTraceState
}
from
"./useMessageTraces"
import
type
{
MessageTraceState
}
from
"./useMessageTraces"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
type
MessageReaction
=
"up"
|
"down"
type
MessageReaction
=
"up"
|
"down"
interface
HomeStarterPrompt
{
interface
HomeStarterPrompt
{
...
...
apps/ui/src/features/chat/MessageList.tsx
View file @
0a63c6b5
...
@@ -4,7 +4,7 @@ import { desktopApi } from "../../lib/desktop-api"
...
@@ -4,7 +4,7 @@ import { desktopApi } from "../../lib/desktop-api"
import
{
getTraceDisplayLines
,
getTraceLineClassName
,
getTraceStripTitle
}
from
"./messageTraceDisplay"
import
{
getTraceDisplayLines
,
getTraceLineClassName
,
getTraceStripTitle
}
from
"./messageTraceDisplay"
import
type
{
MessageTraceState
}
from
"./useMessageTraces"
import
type
{
MessageTraceState
}
from
"./useMessageTraces"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
type
MessageReaction
=
"up"
|
"down"
type
MessageReaction
=
"up"
|
"down"
export
type
ExpertKey
=
"xiaohongshu"
|
"douyin"
|
"browser"
|
"general"
export
type
ExpertKey
=
"xiaohongshu"
|
"douyin"
|
"browser"
|
"general"
...
...
apps/ui/src/features/chat/useChatSessionsController.ts
View file @
0a63c6b5
...
@@ -3,7 +3,7 @@ import type { DesktopApi, WorkspaceSummary } from "@qjclaw/shared-types"
...
@@ -3,7 +3,7 @@ import type { DesktopApi, WorkspaceSummary } from "@qjclaw/shared-types"
import
{
EMPTY_SESSION_ID
,
HOME_CHAT_PROJECT_ID
}
from
"../../lib/constants"
import
{
EMPTY_SESSION_ID
,
HOME_CHAT_PROJECT_ID
}
from
"../../lib/constants"
import
{
resolvePreferredSessionId
}
from
"../../lib/chat-utils"
import
{
resolvePreferredSessionId
}
from
"../../lib/chat-utils"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
interface
UseChatSessionsControllerDeps
{
interface
UseChatSessionsControllerDeps
{
desktopApi
:
DesktopApi
desktopApi
:
DesktopApi
...
...
apps/ui/src/features/chat/useChatStreamingController.ts
View file @
0a63c6b5
...
@@ -6,7 +6,7 @@ import { TYPEWRITER_CHARS_PER_FRAME } from "../../lib/constants"
...
@@ -6,7 +6,7 @@ import { TYPEWRITER_CHARS_PER_FRAME } from "../../lib/constants"
import
{
canExchangeMessages
}
from
"../../lib/workspace-state"
import
{
canExchangeMessages
}
from
"../../lib/workspace-state"
import
type
{
SmokeStreamSnapshot
}
from
"../smoke/types"
import
type
{
SmokeStreamSnapshot
}
from
"../smoke/types"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
type
SendPhase
=
"idle"
|
"preparing"
|
"streaming"
|
"finalizing"
type
SendPhase
=
"idle"
|
"preparing"
|
"streaming"
|
"finalizing"
interface
ActiveStreamState
{
interface
ActiveStreamState
{
requestId
:
string
requestId
:
string
...
...
apps/ui/src/features/chat/useHomeIntentSuggestion.ts
View file @
0a63c6b5
...
@@ -4,7 +4,7 @@ import type { SubmitPromptOptions } from "./useChatStreamingController"
...
@@ -4,7 +4,7 @@ import type { SubmitPromptOptions } from "./useChatStreamingController"
import
{
resolvePreferredSessionId
}
from
"../../lib/chat-utils"
import
{
resolvePreferredSessionId
}
from
"../../lib/chat-utils"
import
{
HOME_CHAT_PROJECT_ID
}
from
"../../lib/constants"
import
{
HOME_CHAT_PROJECT_ID
}
from
"../../lib/constants"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
export
interface
PendingHomeIntentSuggestion
{
export
interface
PendingHomeIntentSuggestion
{
suggestion
:
ProjectIntentSuggestion
suggestion
:
ProjectIntentSuggestion
...
...
apps/ui/src/features/chat/usePromptSubmission.ts
View file @
0a63c6b5
...
@@ -4,7 +4,7 @@ import type { SubmitPromptOptions } from "./useChatStreamingController"
...
@@ -4,7 +4,7 @@ import type { SubmitPromptOptions } from "./useChatStreamingController"
import
{
HOME_CHAT_PROJECT_ID
,
HOME_EXPERT_SUGGESTION_PROJECT_IDS
}
from
"../../lib/constants"
import
{
HOME_CHAT_PROJECT_ID
,
HOME_EXPERT_SUGGESTION_PROJECT_IDS
}
from
"../../lib/constants"
import
{
shouldOfferHomeExpertSwitch
}
from
"../../lib/chat-utils"
import
{
shouldOfferHomeExpertSwitch
}
from
"../../lib/chat-utils"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
interface
ResumePromptOptions
{
interface
ResumePromptOptions
{
skipHomeIntentSuggestion
?:
boolean
skipHomeIntentSuggestion
?:
boolean
...
...
apps/ui/src/features/shell/AppSidebar.tsx
View file @
0a63c6b5
...
@@ -4,7 +4,7 @@ import { Sidebar } from "./Sidebar"
...
@@ -4,7 +4,7 @@ import { Sidebar } from "./Sidebar"
import
{
ExpertTree
,
type
ExpertCategoryId
,
type
ExpertVisualKey
,
type
SidebarExpertEntry
}
from
"./ExpertTree"
import
{
ExpertTree
,
type
ExpertCategoryId
,
type
ExpertVisualKey
,
type
SidebarExpertEntry
}
from
"./ExpertTree"
import
{
SessionList
}
from
"./SessionList"
import
{
SessionList
}
from
"./SessionList"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
type
SendPhase
=
"idle"
|
"preparing"
|
"streaming"
|
"finalizing"
type
SendPhase
=
"idle"
|
"preparing"
|
"streaming"
|
"finalizing"
interface
AppSidebarProps
{
interface
AppSidebarProps
{
...
@@ -70,6 +70,7 @@ export function AppSidebar({
...
@@ -70,6 +70,7 @@ export function AppSidebar({
<
nav
className=
"nav-list"
aria
-
label=
"主导航"
>
<
nav
className=
"nav-list"
aria
-
label=
"主导航"
>
{
[
{
[
{
id
:
"chat"
as
const
,
label
:
"对话"
},
{
id
:
"chat"
as
const
,
label
:
"对话"
},
{
id
:
"tasks"
as
const
,
label
:
"任务面板"
},
{
id
:
"knowledge"
as
const
,
label
:
ui
.
knowledge
},
{
id
:
"knowledge"
as
const
,
label
:
ui
.
knowledge
},
{
id
:
"plugins"
as
const
,
label
:
ui
.
plugins
},
{
id
:
"plugins"
as
const
,
label
:
ui
.
plugins
},
{
id
:
"settings"
as
const
,
label
:
ui
.
settings
}
{
id
:
"settings"
as
const
,
label
:
ui
.
settings
}
...
...
apps/ui/src/features/shell/ExpertTree.tsx
View file @
0a63c6b5
...
@@ -104,7 +104,7 @@ function expertMatchesCategory(entry: SidebarExpertEntry, categoryId: ExpertCate
...
@@ -104,7 +104,7 @@ function expertMatchesCategory(entry: SidebarExpertEntry, categoryId: ExpertCate
interface
ExpertTreeProps
{
interface
ExpertTreeProps
{
entries
:
SidebarExpertEntry
[]
entries
:
SidebarExpertEntry
[]
expandedCategories
:
Record
<
string
,
boolean
>
expandedCategories
:
Record
<
string
,
boolean
>
viewMode
:
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
viewMode
:
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
prompt
:
string
prompt
:
string
activeProjectId
?:
string
activeProjectId
?:
string
onToggleCategory
(
categoryId
:
string
):
void
onToggleCategory
(
categoryId
:
string
):
void
...
...
apps/ui/src/features/shell/useHomeNavigation.ts
View file @
0a63c6b5
...
@@ -3,7 +3,7 @@ import type { DesktopApi, ExpertDefinition, WorkspaceSummary } from "@qjclaw/sha
...
@@ -3,7 +3,7 @@ import type { DesktopApi, ExpertDefinition, WorkspaceSummary } from "@qjclaw/sha
import
{
EMPTY_SESSION_ID
,
HOME_CHAT_PROJECT_ID
}
from
"../../lib/constants"
import
{
EMPTY_SESSION_ID
,
HOME_CHAT_PROJECT_ID
}
from
"../../lib/constants"
import
type
{
SidebarExpertEntry
}
from
"./ExpertTree"
import
type
{
SidebarExpertEntry
}
from
"./ExpertTree"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
interface
UseHomeNavigationDeps
{
interface
UseHomeNavigationDeps
{
desktopApi
:
DesktopApi
desktopApi
:
DesktopApi
...
...
apps/ui/src/features/shell/useSidebarModel.ts
View file @
0a63c6b5
...
@@ -14,7 +14,7 @@ import {
...
@@ -14,7 +14,7 @@ import {
buildStandaloneExpertEntries
buildStandaloneExpertEntries
}
from
"./expertEntries"
}
from
"./expertEntries"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
interface
UseSidebarModelOptions
{
interface
UseSidebarModelOptions
{
workspace
:
WorkspaceSummary
|
null
workspace
:
WorkspaceSummary
|
null
...
...
apps/ui/src/features/smoke/useSmokeActionHandlers.ts
View file @
0a63c6b5
...
@@ -4,7 +4,7 @@ import { HOME_CHAT_PROJECT_ID } from "../../lib/constants"
...
@@ -4,7 +4,7 @@ import { HOME_CHAT_PROJECT_ID } from "../../lib/constants"
import
type
{
SubmitPromptOptions
}
from
"../chat/useChatStreamingController"
import
type
{
SubmitPromptOptions
}
from
"../chat/useChatStreamingController"
import
{
waitForSmokeUiReady
}
from
"./useSmokeActions"
import
{
waitForSmokeUiReady
}
from
"./useSmokeActions"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
type
ExpertProject
=
WorkspaceSummary
[
"projects"
][
number
]
type
ExpertProject
=
WorkspaceSummary
[
"projects"
][
number
]
type
SidebarExpertEntry
=
{
type
SidebarExpertEntry
=
{
definition
:
ExpertDefinition
definition
:
ExpertDefinition
...
...
apps/ui/src/features/smoke/useSmokeActions.ts
View file @
0a63c6b5
import
{
useEffect
}
from
"react"
import
{
useEffect
}
from
"react"
import
{
smokeEnabled
}
from
"../../lib/desktop-api"
import
{
smokeEnabled
}
from
"../../lib/desktop-api"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
export
async
function
waitForSmokeUiReady
(
targetView
:
ViewMode
,
timeoutMs
=
10000
)
{
export
async
function
waitForSmokeUiReady
(
targetView
:
ViewMode
,
timeoutMs
=
10000
)
{
const
started
=
Date
.
now
()
const
started
=
Date
.
now
()
...
...
apps/ui/src/features/smoke/useSmokeSnapshot.ts
View file @
0a63c6b5
...
@@ -15,7 +15,7 @@ import type {
...
@@ -15,7 +15,7 @@ import type {
import
{
isMockDesktopApi
,
smokeEnabled
}
from
"../../lib/desktop-api"
import
{
isMockDesktopApi
,
smokeEnabled
}
from
"../../lib/desktop-api"
import
type
{
SmokeStreamSnapshot
}
from
"./types"
import
type
{
SmokeStreamSnapshot
}
from
"./types"
type
ViewMode
=
"chat"
|
"experts"
|
"plugins"
|
"settings"
|
"knowledge"
type
ViewMode
=
"chat"
|
"experts"
|
"
tasks"
|
"
plugins"
|
"settings"
|
"knowledge"
export
interface
SmokeUiSnapshot
{
export
interface
SmokeUiSnapshot
{
shellReady
:
boolean
shellReady
:
boolean
...
...
apps/ui/src/features/tasks/TaskPanelView.tsx
0 → 100644
View file @
0a63c6b5
import
{
useEffect
,
useMemo
,
useState
}
from
"react"
import
type
{
TaskPanelItem
,
TaskPanelStatus
}
from
"@qjclaw/shared-types"
import
{
Panel
}
from
"../../components/ui/Panel"
import
{
ScrollArea
}
from
"../../components/ui/ScrollArea"
import
{
StatusChip
,
type
StatusChipTone
}
from
"../../components/ui/StatusChip"
import
{
getDefaultTaskPanelDate
,
loadTaskPanelItems
}
from
"./taskPanelData"
const
statusLabels
:
Record
<
TaskPanelStatus
,
string
>
=
{
pending
:
"待处理"
,
running
:
"执行中"
,
completed
:
"已完成"
,
failed
:
"失败"
}
const
statusTones
:
Record
<
TaskPanelStatus
,
StatusChipTone
>
=
{
pending
:
"warning"
,
running
:
"info"
,
completed
:
"positive"
,
failed
:
"warning"
}
function
TaskStatus
({
item
}:
{
item
:
TaskPanelItem
})
{
return
(
<
div
className=
"task-panel-status"
>
<
StatusChip
tone=
{
statusTones
[
item
.
status
]
}
>
<
span
className=
{
"task-panel-status-dot task-panel-status-dot-"
+
item
.
status
}
aria
-
hidden=
"true"
/>
{
statusLabels
[
item
.
status
]
}
</
StatusChip
>
<
span
>
{
item
.
statusDetail
}
</
span
>
</
div
>
)
}
function
TaskArtifacts
({
item
}:
{
item
:
TaskPanelItem
})
{
if
(
!
item
.
artifacts
.
length
)
{
return
<
span
className=
"task-panel-muted"
>
暂无产物
</
span
>
}
return
(
<
ul
className=
"task-panel-artifact-list"
aria
-
label=
{
item
.
taskTitle
+
"产物清单"
}
>
{
item
.
artifacts
.
map
((
artifact
)
=>
(
<
li
key=
{
artifact
.
id
}
>
<
span
className=
"task-panel-artifact-name"
>
{
artifact
.
name
}
</
span
>
{
artifact
.
kind
?
<
span
className=
"task-panel-artifact-kind"
>
{
artifact
.
kind
}
</
span
>
:
null
}
</
li
>
))
}
</
ul
>
)
}
export
function
TaskPanelView
()
{
const
[
selectedDate
,
setSelectedDate
]
=
useState
(
getDefaultTaskPanelDate
)
const
[
items
,
setItems
]
=
useState
<
TaskPanelItem
[]
>
([])
const
[
loading
,
setLoading
]
=
useState
(
true
)
const
[
errorText
,
setErrorText
]
=
useState
(
""
)
useEffect
(()
=>
{
let
active
=
true
setLoading
(
true
)
setErrorText
(
""
)
void
loadTaskPanelItems
(
selectedDate
)
.
then
((
nextItems
)
=>
{
if
(
active
)
{
setItems
(
nextItems
)
}
})
.
catch
((
error
:
unknown
)
=>
{
if
(
active
)
{
setItems
([])
setErrorText
(
error
instanceof
Error
?
error
.
message
:
"任务列表加载失败"
)
}
})
.
finally
(()
=>
{
if
(
active
)
{
setLoading
(
false
)
}
})
return
()
=>
{
active
=
false
}
},
[
selectedDate
])
const
completedCount
=
useMemo
(()
=>
items
.
filter
((
item
)
=>
item
.
status
===
"completed"
).
length
,
[
items
])
return
(
<
div
className=
"page-stack task-panel-page-stack"
>
<
Panel
className=
"task-panel-page"
bodyClassName=
"task-panel-body"
>
<
div
className=
"task-panel-header"
>
<
div
className=
"task-panel-title-group"
>
<
h1
>
任务面板
</
h1
>
<
p
>
{
items
.
length
?
`${items.length} 个任务,${completedCount} 个已完成`
:
"按日期查看专家任务与产物"
}
</
p
>
</
div
>
<
label
className=
"task-panel-date-field"
>
<
span
>
日期
</
span
>
<
input
type=
"date"
value=
{
selectedDate
}
onChange=
{
(
event
)
=>
setSelectedDate
(
event
.
currentTarget
.
value
)
}
/>
</
label
>
</
div
>
<
ScrollArea
className=
"scroll-panel task-panel-scroll"
aria
-
busy=
{
loading
}
>
{
loading
?
<
div
className=
"empty-state task-panel-state"
>
任务列表加载中...
</
div
>
:
null
}
{
!
loading
&&
errorText
?
<
div
className=
"notice error task-panel-state"
role=
"alert"
>
{
errorText
}
</
div
>
:
null
}
{
!
loading
&&
!
errorText
&&
!
items
.
length
?
<
div
className=
"empty-state task-panel-state"
>
当天暂无任务
</
div
>
:
null
}
{
!
loading
&&
!
errorText
&&
items
.
length
?
(
<
div
className=
"task-panel-table"
role=
"table"
aria
-
label=
"任务面板"
>
<
div
className=
"task-panel-row task-panel-row-head"
role=
"row"
>
<
div
role=
"columnheader"
>
专家
</
div
>
<
div
role=
"columnheader"
>
执行状态
</
div
>
<
div
role=
"columnheader"
>
产物清单
</
div
>
</
div
>
{
items
.
map
((
item
)
=>
(
<
article
key=
{
item
.
id
}
className=
"task-panel-row"
role=
"row"
>
<
div
className=
"task-panel-expert-cell"
role=
"cell"
>
<
strong
>
{
item
.
expertName
}
</
strong
>
<
span
>
{
item
.
taskTitle
}
</
span
>
</
div
>
<
div
role=
"cell"
>
<
TaskStatus
item=
{
item
}
/>
</
div
>
<
div
role=
"cell"
>
<
TaskArtifacts
item=
{
item
}
/>
</
div
>
</
article
>
))
}
</
div
>
)
:
null
}
</
ScrollArea
>
</
Panel
>
</
div
>
)
}
apps/ui/src/features/tasks/taskPanelData.ts
0 → 100644
View file @
0a63c6b5
import
type
{
TaskPanelItem
}
from
"@qjclaw/shared-types"
function
toDateInputValue
(
date
:
Date
)
{
const
year
=
date
.
getFullYear
()
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
)
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
"0"
)
return
`
${
year
}
-
${
month
}
-
${
day
}
`
}
function
addDays
(
date
:
Date
,
days
:
number
)
{
const
next
=
new
Date
(
date
)
next
.
setDate
(
next
.
getDate
()
+
days
)
return
next
}
export
function
getDefaultTaskPanelDate
()
{
return
toDateInputValue
(
new
Date
())
}
export
const
mockTaskPanelItems
:
TaskPanelItem
[]
=
[
{
id
:
"mock-task-content-plan"
,
date
:
getDefaultTaskPanelDate
(),
expertName
:
"内容账号规划专家"
,
taskTitle
:
"整理本周选题方向与发布节奏"
,
status
:
"running"
,
statusDetail
:
"正在汇总账号定位、目标人群和栏目节奏"
,
artifacts
:
[
{
id
:
"artifact-content-outline"
,
name
:
"选题规划草稿.md"
,
kind
:
"文档"
},
{
id
:
"artifact-content-calendar"
,
name
:
"发布日历.xlsx"
,
kind
:
"表格"
}
]
},
{
id
:
"mock-task-zhihu"
,
date
:
getDefaultTaskPanelDate
(),
expertName
:
"知乎专家"
,
taskTitle
:
"生成知乎回答结构"
,
status
:
"completed"
,
statusDetail
:
"已完成回答大纲与首版正文"
,
artifacts
:
[
{
id
:
"artifact-zhihu-answer"
,
name
:
"知乎回答初稿.md"
,
kind
:
"文档"
}
]
},
{
id
:
"mock-task-leads"
,
date
:
getDefaultTaskPanelDate
(),
expertName
:
"平台精准线索专家"
,
taskTitle
:
"筛选高意向线索名单"
,
status
:
"pending"
,
statusDetail
:
"等待线索表上传后开始处理"
,
artifacts
:
[]
},
{
id
:
"mock-task-poster"
,
date
:
toDateInputValue
(
addDays
(
new
Date
(),
-
1
)),
expertName
:
"海报专家"
,
taskTitle
:
"生成活动海报文案"
,
status
:
"failed"
,
statusDetail
:
"素材包缺少主视觉图片"
,
artifacts
:
[]
}
]
export
async
function
loadTaskPanelItems
(
date
:
string
):
Promise
<
TaskPanelItem
[]
>
{
return
mockTaskPanelItems
.
filter
((
item
)
=>
item
.
date
===
date
)
}
apps/ui/src/lib/mock-desktop-api.ts
View file @
0a63c6b5
...
@@ -376,6 +376,7 @@ export const mockDesktopApi = {
...
@@ -376,6 +376,7 @@ export const mockDesktopApi = {
},
},
modelConfig
:
{
getSummary
:
async
()
=>
({
source
:
"cloud"
,
updatedAt
:
new
Date
().
toISOString
(),
fetchedAt
:
new
Date
().
toISOString
(),
routingMode
:
"platform-managed"
,
fallbackMode
:
"cloud-required"
,
defaultChatModelId
:
"gpt-5.4-mini"
,
defaultChatModelLabel
:
"GPT-5.4 Mini"
,
items
:
[],
skillBindings
:
[],
message
:
"mock"
})
},
modelConfig
:
{
getSummary
:
async
()
=>
({
source
:
"cloud"
,
updatedAt
:
new
Date
().
toISOString
(),
fetchedAt
:
new
Date
().
toISOString
(),
routingMode
:
"platform-managed"
,
fallbackMode
:
"cloud-required"
,
defaultChatModelId
:
"gpt-5.4-mini"
,
defaultChatModelLabel
:
"GPT-5.4 Mini"
,
items
:
[],
skillBindings
:
[],
message
:
"mock"
})
},
system
:
{
getSummary
:
async
()
=>
({
appName
:
"千匠问天"
,
appVersion
:
"0.1.0"
,
isPackaged
:
false
,
platform
:
"win32"
,
arch
:
"x64"
,
appPath
:
"D:/qjclaw/apps/desktop"
,
resourcesPath
:
"D:/qjclaw/apps/desktop/dist"
,
userDataPath
:
"D:/qjclaw/.tmp/user-data"
,
logsPath
:
"D:/qjclaw/.tmp/logs"
})
},
system
:
{
getSummary
:
async
()
=>
({
appName
:
"千匠问天"
,
appVersion
:
"0.1.0"
,
isPackaged
:
false
,
platform
:
"win32"
,
arch
:
"x64"
,
appPath
:
"D:/qjclaw/apps/desktop"
,
resourcesPath
:
"D:/qjclaw/apps/desktop/dist"
,
userDataPath
:
"D:/qjclaw/.tmp/user-data"
,
logsPath
:
"D:/qjclaw/.tmp/logs"
})
},
tasks
:
{
listByDate
:
async
()
=>
[]
},
chat
:
{
chat
:
{
listSessions
:
async
()
=>
getMockSessions
(),
listSessions
:
async
()
=>
getMockSessions
(),
listSessionsByProject
:
async
(
projectId
:
string
)
=>
getMockSessions
(
projectId
),
listSessionsByProject
:
async
(
projectId
:
string
)
=>
getMockSessions
(
projectId
),
...
...
apps/ui/src/styles.css
View file @
0a63c6b5
...
@@ -5,4 +5,5 @@
...
@@ -5,4 +5,5 @@
@import
"./styles/settings.css"
;
@import
"./styles/settings.css"
;
@import
"./styles/plugins.css"
;
@import
"./styles/plugins.css"
;
@import
"./styles/knowledge.css"
;
@import
"./styles/knowledge.css"
;
@import
"./styles/tasks.css"
;
@import
"./styles/theme-openclaw.css"
;
@import
"./styles/theme-openclaw.css"
;
apps/ui/src/styles/shell.css
View file @
0a63c6b5
...
@@ -399,15 +399,19 @@
...
@@ -399,15 +399,19 @@
padding
:
28px
;
padding
:
28px
;
overflow
:
hidden
;
overflow
:
hidden
;
background
:
background
:
linear-gradient
(
135deg
,
rgba
(
219
,
234
,
254
,
0.94
)
0%
,
rgba
(
245
,
243
,
255
,
0.9
)
48%
,
rgba
(
255
,
255
,
255
,
0.98
)
100%
),
radial-gradient
(
circle
at
16%
18%
,
rgba
(
90
,
176
,
255
,
0.34
),
transparent
28%
),
linear-gradient
(
180deg
,
#eff6ff
0%
,
#ffffff
100%
);
radial-gradient
(
circle
at
84%
16%
,
rgba
(
255
,
255
,
255
,
0.92
),
transparent
34%
),
radial-gradient
(
circle
at
50%
78%
,
rgba
(
190
,
228
,
255
,
0.36
),
transparent
38%
),
linear-gradient
(
145deg
,
#dff1ff
0%
,
#edf7ff
42%
,
#ffffff
100%
);
}
}
.startup-overlay
::before
,
.startup-overlay
::before
,
.startup-overlay
::after
{
.startup-overlay
::after
{
content
:
""
;
content
:
""
;
position
:
absolute
;
position
:
absolute
;
opacity
:
0
;
border-radius
:
999px
;
filter
:
blur
(
18px
);
opacity
:
0.55
;
pointer-events
:
none
;
pointer-events
:
none
;
}
}
...
@@ -430,20 +434,14 @@
...
@@ -430,20 +434,14 @@
.startup-overlay-panel
{
.startup-overlay-panel
{
position
:
relative
;
position
:
relative
;
z-index
:
1
;
z-index
:
1
;
width
:
min
(
68
0px
,
100%
);
width
:
min
(
76
0px
,
100%
);
min-height
:
auto
;
min-height
:
auto
;
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
justify-content
:
center
;
gap
:
0
;
gap
:
0
;
padding
:
36px
;
padding
:
0
;
border
:
1px
solid
rgba
(
147
,
197
,
253
,
0.38
);
border-radius
:
30px
;
background
:
rgba
(
255
,
255
,
255
,
0.72
);
box-shadow
:
0
30px
70px
rgba
(
37
,
99
,
235
,
0.14
);
backdrop-filter
:
blur
(
18px
);
-webkit-backdrop-filter
:
blur
(
18px
);
text-align
:
center
;
text-align
:
center
;
animation
:
startup-overlay-enter
420ms
ease-out
both
;
animation
:
startup-overlay-enter
420ms
ease-out
both
;
}
}
...
@@ -463,11 +461,11 @@
...
@@ -463,11 +461,11 @@
.startup-overlay-copy
h1
{
.startup-overlay-copy
h1
{
margin
:
0
;
margin
:
0
;
font-size
:
clamp
(
48px
,
5vw
,
72
px
);
font-size
:
clamp
(
56px
,
6vw
,
84
px
);
line-height
:
1.02
;
line-height
:
1.02
;
letter-spacing
:
0
;
letter-spacing
:
-0.06em
;
font-weight
:
800
;
font-weight
:
800
;
background-image
:
linear-gradient
(
135deg
,
#
1d4ed8
0%
,
#2563eb
46%
,
#7c3aed
100%
);
background-image
:
linear-gradient
(
135deg
,
#
9fd4ff
0%
,
#bdd7ff
45%
,
#d8c5ff
100%
);
-webkit-background-clip
:
text
;
-webkit-background-clip
:
text
;
background-clip
:
text
;
background-clip
:
text
;
color
:
transparent
;
color
:
transparent
;
...
@@ -492,16 +490,15 @@
...
@@ -492,16 +490,15 @@
}
}
.startup-overlay-mark-shell
{
.startup-overlay-mark-shell
{
width
:
6
4
px
;
width
:
6
0
px
;
height
:
6
4
px
;
height
:
6
0
px
;
flex
:
0
0
auto
;
flex
:
0
0
auto
;
display
:
inline-flex
;
display
:
inline-flex
;
align-items
:
center
;
align-items
:
center
;
justify-content
:
center
;
justify-content
:
center
;
border-radius
:
18px
;
border-radius
:
0
;
border
:
1px
solid
rgba
(
147
,
197
,
253
,
0.42
);
background
:
transparent
;
background
:
rgba
(
255
,
255
,
255
,
0.78
);
box-shadow
:
none
;
box-shadow
:
0
16px
32px
rgba
(
37
,
99
,
235
,
0.14
);
}
}
.startup-overlay-logo
{
.startup-overlay-logo
{
...
@@ -515,9 +512,9 @@
...
@@ -515,9 +512,9 @@
margin
:
0
;
margin
:
0
;
font-size
:
15px
;
font-size
:
15px
;
font-weight
:
600
;
font-weight
:
600
;
letter-spacing
:
0.
18
em
;
letter-spacing
:
0.
32
em
;
text-transform
:
uppercase
;
text-transform
:
uppercase
;
color
:
#
5c78a0
;
color
:
#
7ea2d9
;
}
}
.startup-overlay-body
{
.startup-overlay-body
{
...
@@ -526,64 +523,45 @@
...
@@ -526,64 +523,45 @@
justify-items
:
center
;
justify-items
:
center
;
gap
:
16px
;
gap
:
16px
;
margin-top
:
34px
;
margin-top
:
34px
;
padding
:
18px
;
border-radius
:
22px
;
border
:
1px
solid
rgba
(
147
,
197
,
253
,
0.3
);
background
:
rgba
(
255
,
255
,
255
,
0.74
);
box-shadow
:
inset
0
1px
0
rgba
(
255
,
255
,
255
,
0.84
);
}
}
.startup-overlay-progress
{
.startup-overlay-progress
{
width
:
100%
;
width
:
100%
;
height
:
8
px
;
height
:
7
px
;
overflow
:
hidden
;
overflow
:
hidden
;
border-radius
:
999px
;
border-radius
:
999px
;
background
:
rgba
(
1
48
,
163
,
184
,
0.18
);
background
:
rgba
(
1
28
,
174
,
223
,
0.18
);
}
}
.startup-overlay-progress
span
{
.startup-overlay-progress
span
{
display
:
block
;
display
:
block
;
height
:
100%
;
height
:
100%
;
border-radius
:
inherit
;
border-radius
:
inherit
;
background
:
linear-gradient
(
90deg
,
#
2563eb
0%
,
#7c3aed
100%
);
background
:
linear-gradient
(
90deg
,
#
69bcff
0%
,
#3f8cff
100%
);
box-shadow
:
0
0
18px
rgba
(
37
,
99
,
235
,
0.24
);
box-shadow
:
0
0
18px
rgba
(
63
,
140
,
255
,
0.28
);
transition
:
width
240ms
ease
;
transition
:
width
240ms
ease
;
}
}
.startup-overlay-status
{
.startup-overlay-status
{
width
:
100%
;
display
:
grid
;
display
:
grid
;
gap
:
8px
;
gap
:
8px
;
justify-items
:
center
;
justify-items
:
center
;
}
}
.startup-overlay-status
strong
{
.startup-overlay-status
strong
{
color
:
#
0f2f59
;
color
:
#
1f426d
;
font-size
:
16px
;
font-size
:
16px
;
line-height
:
1.5
;
line-height
:
1.5
;
}
}
.startup-overlay-status
span
{
.startup-overlay-status
span
{
color
:
#
5f7188
;
color
:
#
67819f
;
font-size
:
13px
;
font-size
:
13px
;
line-height
:
1.7
;
line-height
:
1.7
;
}
}
.startup-overlay-detail
{
width
:
100%
;
margin
:
4px
0
0
;
padding
:
10px
12px
;
border-radius
:
14px
;
border
:
1px
solid
rgba
(
239
,
68
,
68
,
0.18
);
background
:
rgba
(
254
,
242
,
242
,
0.84
);
color
:
#9f2f2f
;
font-size
:
13px
;
line-height
:
1.6
;
}
.startup-overlay-actions
{
.startup-overlay-actions
{
justify-content
:
center
;
justify-content
:
center
;
flex-wrap
:
wrap
;
}
}
@keyframes
startup-overlay-enter
{
@keyframes
startup-overlay-enter
{
...
...
apps/ui/src/styles/tasks.css
0 → 100644
View file @
0a63c6b5
.task-panel-page-stack
{
height
:
100%
;
display
:
flex
;
flex-direction
:
column
;
}
.task-panel-page
{
flex
:
1
;
min-height
:
0
;
padding
:
22px
;
overflow
:
hidden
;
border-radius
:
28px
;
border
:
1px
solid
var
(
--ui-color-border
);
background
:
linear-gradient
(
180deg
,
rgba
(
255
,
255
,
255
,
0.98
),
rgba
(
248
,
252
,
251
,
0.96
));
box-shadow
:
var
(
--ui-shadow-panel
);
}
.task-panel-body
{
display
:
grid
;
grid-template-rows
:
auto
minmax
(
0
,
1
fr
);
gap
:
16px
;
min-height
:
0
;
height
:
100%
;
padding
:
0
;
}
.task-panel-header
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
16px
;
min-width
:
0
;
}
.task-panel-title-group
{
display
:
grid
;
gap
:
6px
;
min-width
:
0
;
}
.task-panel-title-group
h1
{
margin
:
0
;
color
:
#1d344f
;
font-size
:
28px
;
line-height
:
1.2
;
}
.task-panel-title-group
p
{
margin
:
0
;
color
:
#61758f
;
font-size
:
14px
;
line-height
:
1.6
;
}
.task-panel-date-field
{
display
:
grid
;
gap
:
6px
;
flex
:
0
0
auto
;
color
:
#53637f
;
font-size
:
12px
;
font-weight
:
700
;
}
.task-panel-date-field
input
{
min-height
:
38px
;
padding
:
0
12px
;
border-radius
:
12px
;
border
:
1px
solid
#d8e1ef
;
background
:
#ffffff
;
color
:
#1f2f49
;
font
:
inherit
;
font-size
:
13px
;
}
.task-panel-scroll
{
min-height
:
0
;
padding-right
:
4px
;
}
.task-panel-state
{
padding
:
20px
;
}
.task-panel-table
{
display
:
grid
;
gap
:
10px
;
}
.task-panel-row
{
display
:
grid
;
grid-template-columns
:
minmax
(
190px
,
1
fr
)
minmax
(
190px
,
1
fr
)
minmax
(
220px
,
1.15
fr
);
gap
:
16px
;
align-items
:
start
;
padding
:
16px
;
border-radius
:
18px
;
border
:
1px
solid
rgba
(
215
,
216
,
229
,
0.96
);
background
:
rgba
(
255
,
255
,
255
,
0.92
);
box-shadow
:
0
12px
28px
rgba
(
17
,
24
,
39
,
0.04
);
}
.task-panel-row-head
{
min-height
:
44px
;
align-items
:
center
;
padding
:
10px
16px
;
border-radius
:
14px
;
background
:
rgba
(
239
,
246
,
255
,
0.78
);
color
:
#53637f
;
font-size
:
12px
;
font-weight
:
800
;
}
.task-panel-expert-cell
,
.task-panel-status
{
display
:
grid
;
gap
:
7px
;
min-width
:
0
;
}
.task-panel-expert-cell
strong
{
color
:
#1c324d
;
font-size
:
15px
;
line-height
:
1.4
;
}
.task-panel-expert-cell
span
,
.task-panel-status
>
span
:not
(
.status-chip
),
.task-panel-muted
{
color
:
#61758f
;
font-size
:
13px
;
line-height
:
1.6
;
}
.task-panel-status
.status-chip
{
width
:
fit-content
;
gap
:
6px
;
}
.task-panel-status-dot
{
width
:
7px
;
height
:
7px
;
border-radius
:
999px
;
background
:
currentColor
;
}
.task-panel-status-dot-running
{
animation
:
task-status-pulse
1.2s
ease-in-out
infinite
;
}
.task-panel-status-dot-failed
{
color
:
#b42318
;
}
.task-panel-artifact-list
{
display
:
grid
;
gap
:
8px
;
margin
:
0
;
padding
:
0
;
list-style
:
none
;
}
.task-panel-artifact-list
li
{
display
:
flex
;
align-items
:
center
;
justify-content
:
space-between
;
gap
:
10px
;
min-width
:
0
;
padding
:
8px
10px
;
border-radius
:
12px
;
background
:
#f8fbff
;
border
:
1px
solid
#e3ebf5
;
}
.task-panel-artifact-name
{
min-width
:
0
;
color
:
#1f2f49
;
font-size
:
13px
;
line-height
:
1.5
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
white-space
:
nowrap
;
}
.task-panel-artifact-kind
{
flex
:
0
0
auto
;
color
:
#60728c
;
font-size
:
12px
;
font-weight
:
700
;
}
@keyframes
task-status-pulse
{
0
%,
100
%
{
opacity
:
0.42
;
}
50
%
{
opacity
:
1
;
}
}
@media
(
max-width
:
980px
)
{
.task-panel-row
{
grid-template-columns
:
1
fr
;
}
}
@media
(
max-width
:
720px
)
{
.task-panel-header
{
align-items
:
stretch
;
flex-direction
:
column
;
}
.task-panel-date-field
{
width
:
100%
;
}
}
packages/shared-types/src/index.ts
View file @
0a63c6b5
...
@@ -48,7 +48,8 @@
...
@@ -48,7 +48,8 @@
skillsList
:
"skills:list"
,
skillsList
:
"skills:list"
,
expertsList
:
"experts:list"
,
expertsList
:
"experts:list"
,
modelConfigGetSummary
:
"model-config:get-summary"
,
modelConfigGetSummary
:
"model-config:get-summary"
,
systemGetSummary
:
"system:get-summary"
systemGetSummary
:
"system:get-summary"
,
tasksListByDate
:
"tasks:list-by-date"
}
as
const
;
}
as
const
;
export
type
GatewayState
=
"unknown"
|
"connecting"
|
"connected"
|
"disconnected"
|
"error"
;
export
type
GatewayState
=
"unknown"
|
"connecting"
|
"connected"
|
"disconnected"
|
"error"
;
...
@@ -77,6 +78,7 @@ export type WorkspaceStartupPhase = "idle" | "syncing-config" | "syncing-project
...
@@ -77,6 +78,7 @@ export type WorkspaceStartupPhase = "idle" | "syncing-config" | "syncing-project
export
type
SkillDownloadState
=
"pending"
|
"downloading"
|
"ready"
|
"failed"
|
"removed"
;
export
type
SkillDownloadState
=
"pending"
|
"downloading"
|
"ready"
|
"failed"
|
"removed"
;
export
type
ExpertEntryMode
=
"standalone"
|
"home-chat-shortcut"
;
export
type
ExpertEntryMode
=
"standalone"
|
"home-chat-shortcut"
;
export
type
DailyReportDeliveryState
=
"draft"
|
"sent"
|
"failed"
;
export
type
DailyReportDeliveryState
=
"draft"
|
"sent"
|
"failed"
;
export
type
TaskPanelStatus
=
"pending"
|
"running"
|
"completed"
|
"failed"
;
export
interface
WorkspaceWarmupResult
{
export
interface
WorkspaceWarmupResult
{
accepted
:
boolean
;
accepted
:
boolean
;
...
@@ -838,6 +840,23 @@ export interface SystemSummary {
...
@@ -838,6 +840,23 @@ export interface SystemSummary {
logsPath
:
string
;
logsPath
:
string
;
}
}
export
interface
TaskPanelArtifact
{
id
:
string
;
name
:
string
;
kind
?:
string
;
url
?:
string
;
}
export
interface
TaskPanelItem
{
id
:
string
;
date
:
string
;
expertName
:
string
;
taskTitle
:
string
;
status
:
TaskPanelStatus
;
statusDetail
:
string
;
artifacts
:
TaskPanelArtifact
[];
}
export
interface
DesktopApi
{
export
interface
DesktopApi
{
workspace
:
{
workspace
:
{
getSummary
():
Promise
<
WorkspaceSummary
>
;
getSummary
():
Promise
<
WorkspaceSummary
>
;
...
@@ -907,6 +926,9 @@ export interface DesktopApi {
...
@@ -907,6 +926,9 @@ export interface DesktopApi {
system
:
{
system
:
{
getSummary
():
Promise
<
SystemSummary
>
;
getSummary
():
Promise
<
SystemSummary
>
;
};
};
tasks
:
{
listByDate
(
date
:
string
):
Promise
<
TaskPanelItem
[]
>
;
};
chat
:
{
chat
:
{
listSessions
():
Promise
<
ProjectSessionSummary
[]
>
;
listSessions
():
Promise
<
ProjectSessionSummary
[]
>
;
listSessionsByProject
(
projectId
:
string
):
Promise
<
ProjectSessionSummary
[]
>
;
listSessionsByProject
(
projectId
:
string
):
Promise
<
ProjectSessionSummary
[]
>
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment