Refactor and optimize various components and files

- Removed deprecated `ccw-contentPattern-optimization-summary.md` and related files.
- Updated `A2UIPopupCard.tsx` to clarify comments on interaction handling.
- Enhanced `QueueListColumn.tsx` and `QueuePanel.tsx` to handle potential undefined values for `config`.
- Added `useEffect` in `QueuePanel.tsx` to load scheduler state on mount.
- Improved `SchedulerPanel.tsx` to handle potential undefined values for `sessionPool`.
- Introduced auto-initialization logic in `queueSchedulerStore.ts` to prevent multiple initialization calls.
- Updated `A2UIWebSocketHandler.ts` to refine selection handling logic.
- Enhanced `hooks-routes.ts` to support multi-question surfaces.
- Added submit and cancel buttons in `ask-question.ts` for better user interaction.
- Deleted `codex_prompt.md` and `contentPattern-library-options.md` as part of cleanup.
- Removed `status-reference.md` to streamline documentation.
This commit is contained in:
catlog22
2026-02-27 22:35:05 +08:00
parent 9be35ed5fb
commit be061dd2a2
34 changed files with 1543 additions and 8895 deletions

View File

@@ -329,11 +329,11 @@ function SinglePagePopup({ surface, onClose }: A2UIPopupCardProps) {
'data-[state=open]:duration-300 data-[state=closed]:duration-200'
)}
onInteractOutside={(e) => {
// Prevent closing when clicking outside
// Prevent closing when clicking outside - only cancel button can close
e.preventDefault();
}}
onEscapeKeyDown={(e) => {
// Prevent closing with ESC key
// Prevent closing with ESC key - only cancel button can close
e.preventDefault();
}}
>
@@ -574,8 +574,14 @@ function MultiPagePopup({ surface, onClose }: A2UIPopupCardProps) {
'data-[state=open]:zoom-in-95 data-[state=closed]:zoom-out-95',
'data-[state=open]:duration-300 data-[state=closed]:duration-200'
)}
onInteractOutside={(e) => e.preventDefault()}
onEscapeKeyDown={(e) => e.preventDefault()}
onInteractOutside={(e) => {
// Prevent closing when clicking outside - only cancel button can close
e.preventDefault();
}}
onEscapeKeyDown={(e) => {
// Prevent closing with ESC key - only cancel button can close
e.preventDefault();
}}
>
{/* Header with current page title */}
<DialogHeader className="space-y-2 pb-4">

View File

@@ -199,7 +199,7 @@ function SchedulerBar() {
{/* Progress + Concurrency */}
{isActive && (
<span className="text-[10px] text-muted-foreground">
{progress}% | {concurrency}/{config.maxConcurrentSessions}
{progress}% | {concurrency}/{config?.maxConcurrentSessions ?? 2}
</span>
)}
@@ -285,6 +285,9 @@ export function QueueListColumn() {
const associationChain = useIssueQueueIntegrationStore(selectAssociationChain);
const buildAssociationChain = useIssueQueueIntegrationStore((s) => s.buildAssociationChain);
// NOTE: loadInitialState is called from a parent component or app initialization
// to avoid infinite loop issues with Zustand selectors
const sortedItems = useMemo(
() => [...items].sort((a, b) => a.execution_order - b.execution_order),
[items]

View File

@@ -7,7 +7,7 @@
// Integrates with issueQueueIntegrationStore for association chain.
// Note: Scheduler controls are in the standalone SchedulerPanel.
import { useState, useMemo, useCallback, memo } from 'react';
import { useState, useMemo, useCallback, memo, useEffect } from 'react';
import { useIntl } from 'react-intl';
import {
ListChecks,
@@ -193,6 +193,12 @@ function QueueTabContent(_props: { embedded?: boolean }) {
// Scheduler store data
const schedulerItems = useQueueSchedulerStore(selectQueueItems);
const schedulerStatus = useQueueSchedulerStore(selectQueueSchedulerStatus);
const loadInitialState = useQueueSchedulerStore((s) => s.loadInitialState);
// Load scheduler state on mount
useEffect(() => {
loadInitialState();
}, [loadInitialState]);
// Legacy API data (fallback)
const queueQuery = useIssueQueue();

View File

@@ -82,7 +82,7 @@ export function SchedulerPanel() {
[updateConfig]
);
const sessionEntries = Object.entries(sessionPool);
const sessionEntries = Object.entries(sessionPool ?? {});
return (
<div className="flex flex-col h-full">
@@ -102,7 +102,7 @@ export function SchedulerPanel() {
})}
</span>
<span className="text-xs text-muted-foreground ml-auto tabular-nums">
{concurrency}/{config.maxConcurrentSessions}
{concurrency}/{config?.maxConcurrentSessions ?? 2}
</span>
</div>
@@ -188,7 +188,7 @@ export function SchedulerPanel() {
type="number"
min={1}
max={10}
value={config.maxConcurrentSessions}
value={config?.maxConcurrentSessions ?? 2}
onChange={handleConcurrencyChange}
className="w-14 h-6 text-xs text-center rounded border border-border bg-background px-1"
/>

View File

@@ -28,6 +28,7 @@ import { FileSidebarPanel } from '@/components/terminal-dashboard/FileSidebarPan
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
import { useAppStore, selectIsImmersiveMode } from '@/stores/appStore';
import { useConfigStore } from '@/stores/configStore';
import { useQueueSchedulerStore } from '@/stores/queueSchedulerStore';
// ========== Main Page Component ==========

View File

@@ -305,11 +305,11 @@ const EMPTY_ITEMS: QueueItem[] = [];
/** Select current scheduler status */
export const selectQueueSchedulerStatus = (state: QueueSchedulerStore): QueueSchedulerStatus =>
state.status;
state?.status ?? 'idle';
/** Select all queue items */
export const selectQueueItems = (state: QueueSchedulerStore): QueueItem[] =>
state.items;
state?.items ?? [];
/**
* Select items that are ready to execute (status 'queued' or 'pending').
@@ -346,9 +346,11 @@ export const selectExecutingItems = (state: QueueSchedulerStore): QueueItem[] =>
* Returns 0 when there are no items.
*/
export const selectSchedulerProgress = (state: QueueSchedulerStore): number => {
const total = state.items.length;
if (!state) return 0;
const items = state.items ?? [];
const total = items.length;
if (total === 0) return 0;
const terminal = state.items.filter(
const terminal = items.filter(
(item) => item.status === 'completed' || item.status === 'failed'
).length;
return Math.round((terminal / total) * 100);
@@ -369,3 +371,35 @@ export const selectCurrentConcurrency = (state: QueueSchedulerStore): number =>
/** Select scheduler error */
export const selectSchedulerError = (state: QueueSchedulerStore): string | null =>
state.error;
// ========== Auto-initialization ==========
/**
* Flag to prevent multiple initialization calls.
* This is set outside the store to avoid triggering re-renders.
*/
let schedulerInitialized = false;
/**
* Initialize the queue scheduler state once.
* Safe to call multiple times - will only initialize once.
*/
export function initializeScheduler(): void {
if (!schedulerInitialized) {
schedulerInitialized = true;
useQueueSchedulerStore.getState().loadInitialState().catch((error) => {
console.error('[QueueScheduler] Failed to initialize:', error);
// Reset flag on error to allow retry
schedulerInitialized = false;
});
}
}
// Auto-initialize when this module is imported (deferred to next tick)
if (typeof window !== 'undefined') {
// Defer initialization to avoid blocking initial render
// and to ensure all store subscriptions are set up first
setTimeout(() => {
initializeScheduler();
}, 100);
}