Commit b2800f26 authored by edy's avatar edy

refactor(ui): reduce app renderer prop assembly

parent 625ebaeb
...@@ -1355,110 +1355,120 @@ export default function App() { ...@@ -1355,110 +1355,120 @@ export default function App() {
const conversationWorkspaceProps = { const conversationWorkspaceProps = {
viewMode, viewMode,
workspaceRef: conversationWorkspaceRef, workspaceRef: conversationWorkspaceRef,
messageListRef, status: {
attachmentInputRef, panelActions,
skillMenuRef, showInlineStartupNotice,
panelActions, chatLaunchState,
selectedSkillBadge, startupCurtainStatus
selectedSkillIsDefault: selectedSkillId === DEFAULT_SKILL.id,
homeLeadIcon: <LobsterClawIcon />,
activeExpertName,
activeExpertKey,
activeExpertVisualKey,
activeExpertGuide,
expertWorkspaceLogo,
renderExpertIcon,
showInlineStartupNotice,
chatLaunchState,
startupCurtainStatus,
homeIntentSuggestion: pendingHomeIntentSuggestion?.suggestion ?? null,
homeIntentDecisionPending,
homeIntentLabels: {
title: ui.suggestionSwitchTitle,
prefix: ui.suggestionSwitchPrefix,
suffix: ui.suggestionSwitchSuffix,
continue: ui.suggestionContinue,
switchAction: ui.suggestionSwitchAction
}, },
renderIntentIcon: getIntentSuggestionIcon, emptyState: {
onContinueHomeIntent: continuePendingHomePromptInHome, selectedSkillBadge,
onSwitchHomeIntent: switchExpertAndContinuePendingHomePrompt, selectedSkillIsDefault: selectedSkillId === DEFAULT_SKILL.id,
showBindEntry, homeLeadIcon: <LobsterClawIcon />,
bindEntry: { activeExpertName,
lobsterKeyDraft, activeExpertKey,
workspaceApiKeyConfigured: Boolean(workspace?.apiKeyConfigured), activeExpertVisualKey,
saving, activeExpertGuide,
bindingLabel: ui.binding, expertWorkspaceLogo,
onLobsterKeyChange: setLobsterKeyDraft, homeIntentSuggestion: pendingHomeIntentSuggestion?.suggestion ?? null,
onSave: () => void saveLobsterKey() homeIntentDecisionPending,
homeIntentLabels: {
title: ui.suggestionSwitchTitle,
prefix: ui.suggestionSwitchPrefix,
suffix: ui.suggestionSwitchSuffix,
continue: ui.suggestionContinue,
switchAction: ui.suggestionSwitchAction
},
showBindEntry,
bindEntry: {
lobsterKeyDraft,
workspaceApiKeyConfigured: Boolean(workspace?.apiKeyConfigured),
saving,
bindingLabel: ui.binding,
onLobsterKeyChange: setLobsterKeyDraft,
onSave: () => void saveLobsterKey()
},
hasExpertProjects: Boolean(expertPageProjects.length),
noExpertsLabel: expertsPageCopy.noExperts,
starterQuestionsHint: ui.starterQuestionsHint,
homeEmptyTitle: homeChatCopy.emptyTitle,
homePrompts: homeChatCopy.prompts
}, },
hasExpertProjects: Boolean(expertPageProjects.length), messages: {
noExpertsLabel: expertsPageCopy.noExperts, messageListRef,
starterQuestionsHint: ui.starterQuestionsHint, messages,
homeEmptyTitle: homeChatCopy.emptyTitle, showEmptyState,
homePrompts: homeChatCopy.prompts, messageTraces,
onStarterPrompt: applyStarterPrompt, messageReactions,
messages, copiedToken,
showEmptyState, sending,
messageTraces, messageLabels: {
messageReactions, thinking: ui.thinking,
copiedToken, hideTrace: ui.hideTrace,
sending, traceCollapsed: ui.traceCollapsed
messageLabels: { },
thinking: ui.thinking, copyIcon: <CopyIcon />,
hideTrace: ui.hideTrace, copiedIcon: <CheckIcon />,
traceCollapsed: ui.traceCollapsed deleteIcon: <TrashIcon />,
regenerateIcon: <RefreshIcon />
}, },
copyIcon: <CopyIcon />, composer: {
copiedIcon: <CheckIcon />, attachmentInputRef,
deleteIcon: <TrashIcon />, skillMenuRef,
regenerateIcon: <RefreshIcon />, prompt,
renderThumbIcon: (direction) => <ThumbIcon direction={direction} />, isBound,
renderMarkdownContent, canSend,
buildDouyinVideoStatusCard, isComposerDragOver,
formatMessageTimestamp, isComposerResizeActive,
onMessageListScroll: handleMessageListScroll, composerShellStyle,
onCopyText: handleCopyText, attachmentAccept,
onDeleteMessage: deleteMessage, attachments: composerAttachments,
onTraceExpandedChange: setMessageTraceExpanded, composerPlaceholder,
onRegenerateAssistantMessage: regenerateAssistantMessage, sendButtonLabel,
onToggleMessageReaction: toggleMessageReaction, skillMenuTitle: ui.skillMenuTitle,
prompt, defaultChatLabel: ui.defaultChat,
isBound, defaultSkillId: DEFAULT_SKILL.id,
canSend, selectedSkillId,
isComposerDragOver, selectedSkillName: selectedSkill.name,
isComposerResizeActive, skills: effectiveSkills,
composerShellStyle, skillMenuOpen,
attachmentAccept, attachmentIcon: <AttachmentIcon />,
attachments: composerAttachments, submitIcon: sendPhase !== "idle" ? <StopIcon /> : <ArrowUpIcon />
composerPlaceholder, },
sendButtonLabel, actions: {
skillMenuTitle: ui.skillMenuTitle, renderExpertIcon,
defaultChatLabel: ui.defaultChat, renderIntentIcon: getIntentSuggestionIcon,
defaultSkillId: DEFAULT_SKILL.id, onContinueHomeIntent: continuePendingHomePromptInHome,
selectedSkillId, onSwitchHomeIntent: switchExpertAndContinuePendingHomePrompt,
selectedSkillName: selectedSkill.name, onStarterPrompt: applyStarterPrompt,
skills: effectiveSkills, renderThumbIcon: (direction) => <ThumbIcon direction={direction} />,
skillMenuOpen, renderMarkdownContent,
attachmentIcon: <AttachmentIcon />, buildDouyinVideoStatusCard,
submitIcon: sendPhase !== "idle" ? <StopIcon /> : <ArrowUpIcon />, formatMessageTimestamp,
onSubmit: sendPrompt, onMessageListScroll: handleMessageListScroll,
onCancel: cancelActiveStream, onCopyText: handleCopyText,
onPromptChange: setPrompt, onDeleteMessage: deleteMessage,
onTextareaKeyDown: handleComposerKeyDown, onTraceExpandedChange: setMessageTraceExpanded,
onAttachmentSelection: handleAttachmentSelection, onRegenerateAssistantMessage: regenerateAssistantMessage,
onOpenAttachmentPicker: openAttachmentPicker, onToggleMessageReaction: toggleMessageReaction,
onRemoveAttachment: removeComposerAttachment, onSubmit: sendPrompt,
onToggleSkillMenu: () => setSkillMenuOpen((current) => !current), onCancel: cancelActiveStream,
onClearSelectedSkill: clearSelectedSkill, onPromptChange: setPrompt,
onChooseSkill: chooseSkill, onTextareaKeyDown: handleComposerKeyDown,
onDragEnter: handleComposerDragEnter, onAttachmentSelection: handleAttachmentSelection,
onDragOver: handleComposerDragOver, onOpenAttachmentPicker: openAttachmentPicker,
onDragLeave: handleComposerDragLeave, onRemoveAttachment: removeComposerAttachment,
onDrop: handleComposerDrop, onToggleSkillMenu: () => setSkillMenuOpen((current) => !current),
onResizePointerDown: handleComposerResizePointerDown, onClearSelectedSkill: clearSelectedSkill,
onResizePointerMove: handleComposerResizePointerMove, onChooseSkill: chooseSkill,
onResizePointerEnd: handleComposerResizePointerEnd onDragEnter: handleComposerDragEnter,
onDragOver: handleComposerDragOver,
onDragLeave: handleComposerDragLeave,
onDrop: handleComposerDrop,
onResizePointerDown: handleComposerResizePointerDown,
onResizePointerMove: handleComposerResizePointerMove,
onResizePointerEnd: handleComposerResizePointerEnd
}
} satisfies ComponentProps<typeof ConversationWorkspaceView>; } satisfies ComponentProps<typeof ConversationWorkspaceView>;
const settingsStatusHint = showSettingsStatusHint const settingsStatusHint = showSettingsStatusHint
? <div className={"inline-hint settings-runtime-hint" + (chatLaunchState === "error" ? " error" : "")}>{startupMessage}</div> ? <div className={"inline-hint settings-runtime-hint" + (chatLaunchState === "error" ? " error" : "")}>{startupMessage}</div>
......
...@@ -258,7 +258,7 @@ ...@@ -258,7 +258,7 @@
- Modify: `apps/ui/src/features/settings/SettingsPanels.tsx` - Modify: `apps/ui/src/features/settings/SettingsPanels.tsx`
- Create or modify focused hooks only under existing `apps/ui/src/features/*` - Create or modify focused hooks only under existing `apps/ui/src/features/*`
- [ ] **Step 1: 拆出纯 props 组装** - [x] **Step 1: 拆出纯 props 组装**
Move only pure prop-building logic from `App.tsx` into focused helpers or hooks when all inputs are already available. Move only pure prop-building logic from `App.tsx` into focused helpers or hooks when all inputs are already available.
...@@ -274,7 +274,12 @@ ...@@ -274,7 +274,12 @@
- No IPC calls move across ownership boundaries in this step. - No IPC calls move across ownership boundaries in this step.
- `App.tsx` line count decreases, but line count is not the success metric; clearer ownership is. - `App.tsx` line count decreases, but line count is not the success metric; clearer ownership is.
- [ ] **Step 2: Keep high-risk flows in place** Progress note:
- Completed on 2026-05-25 by grouping `ConversationWorkspaceView` props into focused `status`, `emptyState`, `messages`, `composer`, and `actions` objects.
- IPC calls, stream lifecycle, composer submit/cancel, session actions, startup overlay actions, and smoke hooks remain in `App.tsx`.
- [x] **Step 2: Keep high-risk flows in place**
Do not move these during this phase: Do not move these during this phase:
...@@ -289,7 +294,11 @@ ...@@ -289,7 +294,11 @@
- Existing smoke tests do not need selector or action rewrites. - Existing smoke tests do not need selector or action rewrites.
- [ ] **Step 3: Reduce oversized component props** Progress note:
- Confirmed during the prop grouping pass; no smoke contract or high-risk flow was moved.
- [x] **Step 3: Reduce oversized component props**
For `ConversationWorkspaceView`, group related props into typed objects only when ownership is obvious: For `ConversationWorkspaceView`, group related props into typed objects only when ownership is obvious:
...@@ -304,7 +313,11 @@ ...@@ -304,7 +313,11 @@
- Component call site becomes easier to read. - Component call site becomes easier to read.
- Existing child component props stay unchanged unless required. - Existing child component props stay unchanged unless required.
- [ ] **Step 4: Validate App.tsx encoding and tests** Progress note:
- `ConversationWorkspaceView` now receives typed grouped props; child components still receive their existing prop shapes.
- [x] **Step 4: Validate App.tsx encoding and tests**
Run: Run:
...@@ -320,6 +333,12 @@ ...@@ -320,6 +333,12 @@
- Diff contains no mojibake and no unrelated Chinese copy changes. - Diff contains no mojibake and no unrelated Chinese copy changes.
- UI typecheck passes. - UI typecheck passes.
Progress note:
- Passed on 2026-05-25: BOM check printed `b'imp'`.
- Inspected `git diff -- apps/ui/src/App.tsx apps/ui/src/features/chat/ConversationWorkspaceView.tsx`; no mojibake or unrelated Chinese copy changes.
- Passed on 2026-05-25: `corepack pnpm --filter @qjclaw/ui typecheck`; `corepack pnpm build`.
## 6. Phase 4 - 修正核心工作流 UI/UX ## 6. Phase 4 - 修正核心工作流 UI/UX
**目标:** 优先改善用户每天会反复使用的界面:聊天、任务、自动化、设置。 **目标:** 优先改善用户每天会反复使用的界面:聊天、任务、自动化、设置。
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment