Files
vibe-kanban/frontend/src/components/ui-new/actions/index.ts

766 lines
22 KiB
TypeScript
Raw Normal View History

2026-01-08 22:14:38 +00:00
import type { Icon } from '@phosphor-icons/react';
import type { NavigateFunction } from 'react-router-dom';
import type { QueryClient } from '@tanstack/react-query';
import type { EditorType, ExecutionProcess, Workspace } from 'shared/types';
2026-01-08 22:14:38 +00:00
import type { DiffViewMode } from '@/stores/useDiffViewStore';
import {
CopyIcon,
PushPinIcon,
ArchiveIcon,
TrashIcon,
PlusIcon,
GearIcon,
ColumnsIcon,
RowsIcon,
TextAlignLeftIcon,
EyeSlashIcon,
SidebarSimpleIcon,
ChatsTeardropIcon,
GitDiffIcon,
TerminalIcon,
SignOutIcon,
CaretDoubleUpIcon,
CaretDoubleDownIcon,
PlayIcon,
PauseIcon,
SpinnerIcon,
GitPullRequestIcon,
GitMergeIcon,
ArrowsClockwiseIcon,
CrosshairIcon,
DesktopIcon,
Add rename workspace functionality (Vibe Kanban) (#1868) * I've added the rename workspace functionality. Here's a summary of the changes: ## Summary The backend already had rename workspace support via the `PUT /api/task-attempts/{attemptId}` endpoint with the `name` field. I added the frontend UI to expose this functionality. ### Files Created 1. **`frontend/src/components/ui-new/dialogs/RenameWorkspaceDialog.tsx`** - New dialog component for renaming workspaces - Shows the current name (or branch name as fallback) - Allows user to input a new name - Handles mutation with proper error handling and cache invalidation ### Files Modified 1. **`frontend/src/components/ui-new/actions/index.ts`** - Added `PencilSimpleIcon` import - Added `RenameWorkspaceDialog` import - Added new `RenameWorkspace` action that shows the rename dialog 2. **`frontend/src/components/ui-new/actions/pages.ts`** - Added `Actions.RenameWorkspace` to the `workspaceActions` page (at the top of the list) 3. **`frontend/src/i18n/locales/en/common.json`** - Added translations for the rename workspace dialog under `workspaces.rename` ### How to Use Users can now rename a workspace by: 1. Clicking the three-dot menu (⋯) on any workspace in the sidebar 2. Selecting "Rename" from the command bar 3. Entering a new name in the dialog 4. Clicking "Rename" to save * Cleanup script changes for workspace 4eb64331-cc43-43af-849e-3731664e53b9 * Added the missing translations for the rename workspace dialog to all locale files: - **es** (Spanish) - **ja** (Japanese) - **ko** (Korean) - **zh-Hans** (Simplified Chinese) - **zh-Hant** (Traditional Chinese)
2026-01-09 10:06:29 +01:00
PencilSimpleIcon,
Add Push button to Git panel when PR is open with unpushed commits (Vibe Kanban) (#1951) * The implementation is complete. Here's a summary of what I changed: ## Summary Added a "Push" button to the workspaces UI Git panel. The changes allow users to push changes when a PR is already open. ### Files Modified: 1. **`frontend/src/components/ui-new/primitives/RepoCard.tsx`** (lines 27-42) - Added `'push'` to the `RepoAction` type union - Added a new option to `repoActionOptions` array with the Push button configuration using `ArrowUpIcon` 2. **`frontend/src/components/ui-new/actions/index.ts`** (lines 33, 691-707) - Imported `ArrowUpIcon` from phosphor-icons - Added `GitPush` action definition that: - Calls `attemptsApi.push()` with the workspace and repo IDs - Handles the `force_push_required` error case - Invalidates workspace queries on success to refresh the UI 3. **`frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`** (line 103) - Added mapping from `'push'` action to `Actions.GitPush` in the action handler The Push button now appears in the Git panel dropdown alongside "Open pull request" and "Merge" options. * The type check passes. Here's a summary of the updated changes: ## Summary The Push button now appears **next to the PR number** when: 1. A PR is already open (`prStatus === 'open'`) 2. There are new commits ahead (`commitsAhead > 0`) ### Changes Made: **`frontend/src/components/ui-new/primitives/RepoCard.tsx`**: - Removed `push` from the `repoActionOptions` dropdown (lines 34-41) - Added a dedicated Push button in the PR status row (lines 213-222) that: - Only appears when `prStatus === 'open'` AND `commitsAhead > 0` - Uses brand primary styling (`bg-brand-primary`) to make it prominent - Displays with an up arrow icon and "Push" label using existing translation - Calls `onActionsClick?.('push')` when clicked The `GitPush` action and handler mapping from the previous changes remain in place to handle the push operation. * Done. The Push button now uses the same styling as the "Open PR" button: `bg-panel text-normal hover:bg-tertiary`. * The type check passes. Here's a summary of the fix: ## Summary The Push button now correctly uses `remoteCommitsAhead` (unpushed commits to remote) instead of `commitsAhead` (total commits ahead of target branch) to determine when to show. ### Changes Made: 1. **`frontend/src/components/ui-new/views/GitPanel.tsx`**: - Added `remoteCommitsAhead: number` to the `RepoInfo` interface - Pass `remoteCommitsAhead` to `RepoCard` 2. **`frontend/src/components/ui-new/primitives/RepoCard.tsx`**: - Added `remoteCommitsAhead?: number` to `RepoCardProps` - Changed the push button condition from `commitsAhead > 0` to `remoteCommitsAhead > 0` 3. **`frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`**: - Added `remoteCommitsAhead: repoStatus?.remote_commits_ahead ?? 0` to the `repoInfos` mapping Now the Push button will: - Only appear when there's an open PR AND there are unpushed commits (`remote_commits_ahead > 0`) - Disappear after a successful push (when `remote_commits_ahead` becomes 0) - Reappear when new commits are made locally that haven't been pushed * The type check passes. Here's a summary of the changes: ## Summary The Push button now hides immediately when clicked and only reappears when new commits are available: ### Changes to `RepoCard.tsx`: - Added `useState` and `useRef` imports - Added `isPushHidden` state to track when the button should be hidden - Added `lastRemoteCommitsAhead` ref to track the previous value - Added `useEffect` that resets `isPushHidden` to `false` when `remoteCommitsAhead` increases (meaning new commits are available) - Added `handlePushClick` function that hides the button immediately and triggers the push action - Updated the button condition to include `!isPushHidden` The button will: 1. Hide immediately when clicked (no double-clicking possible) 2. Stay hidden until `remoteCommitsAhead` increases above the last known value 3. Reappear when new local commits are made that need to be pushed * Both the linter and type check pass. The linter errors are fixed. Here's a summary of the changes: ## Summary of Linter Fixes The linter was flagging that `RepoCard` (a presentational component) should not manage state or have side effects. I refactored the code to lift the state up to the container component: ### Changes: 1. **`RepoCard.tsx`** - Made purely presentational: - Removed `useState`, `useRef`, `useEffect` imports - Removed `remoteCommitsAhead` prop - Added `showPushButton` prop (boolean, controlled by parent) - Added `onPushClick` callback prop - The button now just renders based on `showPushButton` and calls `onPushClick` 2. **`GitPanel.tsx`** - Updated interface and props: - `RepoInfo` now has optional `remoteCommitsAhead` and `showPushButton` fields - Added `onPushClick` callback to `GitPanelProps` - Passes `showPushButton` and `onPushClick` to `RepoCard` 3. **`WorkspacesLayout.tsx`** - State management moved here: - Added `hiddenPushRepos` state (Set of repo IDs where push was clicked) - Added `lastRemoteCommitsAhead` ref to track previous values - Added `useEffect` to reset hidden state when `remoteCommitsAhead` increases - Added `repoInfosWithPushButton` memo that computes `showPushButton` for each repo - Added `handlePushClick` that hides button immediately and executes push action * prettier fix * Done. I've added `GitPush` to the Git actions list in the command bar. It now appears between "Merge" and "Rebase" in the Git Actions menu. * Both type check and lint pass. Done! The Push action in the command bar now has the same visibility conditions as in the Git panel - it only appears when: 1. There's a workspace 2. There are git repos 3. There's an open PR (`hasOpenPR`) 4. There are unpushed commits (`hasUnpushedCommits`) ### Summary of changes: 1. **`frontend/src/components/ui-new/actions/index.ts`**: - Added `hasOpenPR` and `hasUnpushedCommits` to `ActionVisibilityContext` - Updated `GitPush.isVisible` to check these new context properties 2. **`frontend/src/components/ui-new/actions/useActionVisibility.ts`**: - Added `useBranchStatus` hook to get branch status data - Computed `hasOpenPR` by checking if any repo has an open PR in merges - Computed `hasUnpushedCommits` by checking if any repo has `remote_commits_ahead > 0` - Added these values to the returned context * The refactoring is complete. Here's a summary of the changes: ## Summary of Changes The push button logic has been simplified by using React Query's mutation state instead of manually tracking hidden state with refs and effects. ### Key Changes: 1. **WorkspacesLayout.tsx (`GitPanelContainer`)**: - Removed the complex `useEffect` that tracked `lastRemoteCommitsAhead` and managed `hiddenPushRepos` state - Removed `hiddenPushRepos` state and `lastRemoteCommitsAhead` ref - Added `usePush` hook with callbacks that: - On success: awaits query invalidation before clearing the loading state - On error: clears the loading state - Added simple `pushingRepoId` state to track which repo is being pushed - The button now shows loading state during push and automatically disappears after refetch if `remoteCommitsAhead` becomes 0 2. **GitPanel.tsx**: - Added `isPushPending?: boolean` to `RepoInfo` interface - Passes `isPushPending` to `RepoCard` 3. **RepoCard.tsx**: - Added `isPushPending` prop - Button shows while either `showPushButton` or `isPushPending` is true - Button is disabled and shows spinner with "Pushing..." text when pending - Uses existing translation key `git.states.pushing` ### How it works now: 1. User clicks push button 2. Button immediately shows loading state (spinner + "Pushing...") 3. Button is disabled to prevent multiple clicks 4. Push mutation runs 5. On success, query invalidation completes before clearing loading state 6. Once `branchStatus` refetches, if `remoteCommitsAhead` is 0, `showPushButton` becomes false and button disappears 7. On error, loading state clears and button returns to normal This approach is simpler and more predictable than manually tracking previous values. * Done. The push button now shows success feedback like the old UI: ## Summary of Changes The push button now has three states: 1. **Normal**: Shows "Push" with an up arrow icon 2. **Pending**: Shows "Pushing..." with a spinning icon, button is disabled 3. **Success**: Shows "Pushed!" with a green checkmark icon and green background for 2 seconds The flow is: 1. User clicks "Push" 2. Button immediately shows "Pushing..." with spinner 3. Push completes, queries are invalidated and refetched 4. Button shows green "Pushed!" for 2 seconds 5. Button disappears (if no more commits to push) or returns to normal state * All code review issues have been addressed: ## Summary of Changes 1. **Unified push logic** - Now uses `executeAction(Actions.GitPush, ...)` instead of calling `usePush` hook directly. This ensures consistent error handling between the button and command bar. 2. **Proper setTimeout cleanup** - Added `successTimeoutRef` with a cleanup effect that clears the timeout when the component unmounts: ```typescript const successTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); useEffect(() => { return () => { if (successTimeoutRef.current) { clearTimeout(successTimeoutRef.current); } }; }, []); ``` 3. **Error feedback** - Added error state handling: - New `isPushError` prop flows through GitPanel to RepoCard - Button shows red "Failed" with warning icon when push fails - Error state clears after 3 seconds - Added `git.states.pushFailed` translation key 4. **Simplified state management** - Changed from separate `pushingRepoId` and `pushSuccessRepoId` to a single `pushStates` record with `PushState` type (`'idle' | 'pending' | 'success' | 'error'`) * All i18n checks pass now. * Both code review issues have been addressed: ## Summary of Changes ### 1. Fixed stale closure issue - Added a `pushStatesRef` that mirrors the `pushStates` state - Changed the check from `pushStates[repoId]` to `pushStatesRef.current[repoId]` - Removed `pushStates` from the dependency array ### 2. Added proper error logging and user-facing error message - Modified `ActionsContext.tsx` to re-throw errors after showing the dialog, so callers can handle them - Added `console.error('Push failed:', error)` for debugging purposes - The error dialog is already shown by `executeAction` via `ConfirmDialog` - The button now properly shows the error state when push fails * fmt * The fix is in place. The key change is: - When we're in a "push flow" (state is `pending`, `success`, or `error`), we set `showPushButton: false` but the button remains visible because of the `isPushPending || isPushSuccess || isPushError` condition in RepoCard - This prevents the race condition where the query refetch could hide the button before we set the success state * All checks pass. The changes now: 1. **Reverted ActionsContext** - Removed the re-throw to maintain backwards compatibility 2. **Use `usePush` hook directly** - For proper success/error detection instead of going through `executeAction` 3. **Show error dialog manually** - Using `ConfirmDialog.show()` when push fails 4. **Track current repo with ref** - `currentPushRepoRef` tracks which repo is being pushed so callbacks can update the correct state * All checks pass. Here's a summary of the force push handling: ## Force Push Functionality Yes, force push is fully supported. Here's how it works: **Old UI (`useGitOperations.ts`):** 1. When `usePush` fails with `errorData.type === 'force_push_required'` 2. It automatically shows `ForcePushDialog` 3. The dialog warns the user about the consequences and lets them confirm 4. If confirmed, it calls `useForcePush` to execute the force push **New UI (now updated):** 1. When the push button is clicked and fails with `force_push_required` 2. The push state is reset to `idle` (so the button returns to normal) 3. `ForcePushDialog` is shown with the attemptId and repoId 4. User can confirm or cancel the force push from the dialog The `ForcePushDialog` handles its own loading state, success/error feedback, and API call internally via `useForcePush` hook. * clear pushStates on workspace change Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-12 15:13:57 +01:00
ArrowUpIcon,
2026-01-08 22:14:38 +00:00
} from '@phosphor-icons/react';
import { useDiffViewStore } from '@/stores/useDiffViewStore';
import { useUiPreferencesStore } from '@/stores/useUiPreferencesStore';
import { useLayoutStore } from '@/stores/useLayoutStore';
import { attemptsApi, tasksApi, repoApi } from '@/lib/api';
import { attemptKeys } from '@/hooks/useAttempt';
import { taskKeys } from '@/hooks/useTask';
import { workspaceSummaryKeys } from '@/components/ui-new/hooks/useWorkspaces';
import { ConfirmDialog } from '@/components/ui-new/dialogs/ConfirmDialog';
import { ChangeTargetDialog } from '@/components/ui-new/dialogs/ChangeTargetDialog';
import { RebaseDialog } from '@/components/ui-new/dialogs/RebaseDialog';
Add rename workspace functionality (Vibe Kanban) (#1868) * I've added the rename workspace functionality. Here's a summary of the changes: ## Summary The backend already had rename workspace support via the `PUT /api/task-attempts/{attemptId}` endpoint with the `name` field. I added the frontend UI to expose this functionality. ### Files Created 1. **`frontend/src/components/ui-new/dialogs/RenameWorkspaceDialog.tsx`** - New dialog component for renaming workspaces - Shows the current name (or branch name as fallback) - Allows user to input a new name - Handles mutation with proper error handling and cache invalidation ### Files Modified 1. **`frontend/src/components/ui-new/actions/index.ts`** - Added `PencilSimpleIcon` import - Added `RenameWorkspaceDialog` import - Added new `RenameWorkspace` action that shows the rename dialog 2. **`frontend/src/components/ui-new/actions/pages.ts`** - Added `Actions.RenameWorkspace` to the `workspaceActions` page (at the top of the list) 3. **`frontend/src/i18n/locales/en/common.json`** - Added translations for the rename workspace dialog under `workspaces.rename` ### How to Use Users can now rename a workspace by: 1. Clicking the three-dot menu (⋯) on any workspace in the sidebar 2. Selecting "Rename" from the command bar 3. Entering a new name in the dialog 4. Clicking "Rename" to save * Cleanup script changes for workspace 4eb64331-cc43-43af-849e-3731664e53b9 * Added the missing translations for the rename workspace dialog to all locale files: - **es** (Spanish) - **ja** (Japanese) - **ko** (Korean) - **zh-Hans** (Simplified Chinese) - **zh-Hant** (Traditional Chinese)
2026-01-09 10:06:29 +01:00
import { RenameWorkspaceDialog } from '@/components/ui-new/dialogs/RenameWorkspaceDialog';
2026-01-08 22:14:38 +00:00
import { CreatePRDialog } from '@/components/dialogs/tasks/CreatePRDialog';
import { getIdeName } from '@/components/ide/IdeIcon';
import { EditorSelectionDialog } from '@/components/dialogs/tasks/EditorSelectionDialog';
// Special icon types for ContextBar
export type SpecialIconType = 'ide-icon' | 'copy-icon';
export type ActionIcon = Icon | SpecialIconType;
// Workspace type for sidebar (minimal subset needed for workspace selection)
interface SidebarWorkspace {
id: string;
}
// Dev server state type for visibility context
export type DevServerState = 'stopped' | 'starting' | 'running' | 'stopping';
// Context provided to action executors (from React hooks)
export interface ActionExecutorContext {
navigate: NavigateFunction;
queryClient: QueryClient;
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
selectWorkspace: (workspaceId: string) => void;
activeWorkspaces: SidebarWorkspace[];
currentWorkspaceId: string | null;
containerRef: string | null;
runningDevServers: ExecutionProcess[];
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
startDevServer: () => void;
stopDevServer: () => void;
2026-01-08 22:14:38 +00:00
}
// Context for evaluating action visibility and state conditions
export interface ActionVisibilityContext {
// Layout state
isChangesMode: boolean;
isLogsMode: boolean;
isPreviewMode: boolean;
isSidebarVisible: boolean;
isMainPanelVisible: boolean;
isGitPanelVisible: boolean;
isCreateMode: boolean;
// Workspace state
hasWorkspace: boolean;
workspaceArchived: boolean;
// Diff state
hasDiffs: boolean;
diffViewMode: DiffViewMode;
isAllDiffsExpanded: boolean;
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
// Dev server state
editorType: EditorType | null;
devServerState: DevServerState;
runningDevServers: ExecutionProcess[];
2026-01-08 22:14:38 +00:00
// Git panel state
hasGitRepos: boolean;
hasMultipleRepos: boolean;
Add Push button to Git panel when PR is open with unpushed commits (Vibe Kanban) (#1951) * The implementation is complete. Here's a summary of what I changed: ## Summary Added a "Push" button to the workspaces UI Git panel. The changes allow users to push changes when a PR is already open. ### Files Modified: 1. **`frontend/src/components/ui-new/primitives/RepoCard.tsx`** (lines 27-42) - Added `'push'` to the `RepoAction` type union - Added a new option to `repoActionOptions` array with the Push button configuration using `ArrowUpIcon` 2. **`frontend/src/components/ui-new/actions/index.ts`** (lines 33, 691-707) - Imported `ArrowUpIcon` from phosphor-icons - Added `GitPush` action definition that: - Calls `attemptsApi.push()` with the workspace and repo IDs - Handles the `force_push_required` error case - Invalidates workspace queries on success to refresh the UI 3. **`frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`** (line 103) - Added mapping from `'push'` action to `Actions.GitPush` in the action handler The Push button now appears in the Git panel dropdown alongside "Open pull request" and "Merge" options. * The type check passes. Here's a summary of the updated changes: ## Summary The Push button now appears **next to the PR number** when: 1. A PR is already open (`prStatus === 'open'`) 2. There are new commits ahead (`commitsAhead > 0`) ### Changes Made: **`frontend/src/components/ui-new/primitives/RepoCard.tsx`**: - Removed `push` from the `repoActionOptions` dropdown (lines 34-41) - Added a dedicated Push button in the PR status row (lines 213-222) that: - Only appears when `prStatus === 'open'` AND `commitsAhead > 0` - Uses brand primary styling (`bg-brand-primary`) to make it prominent - Displays with an up arrow icon and "Push" label using existing translation - Calls `onActionsClick?.('push')` when clicked The `GitPush` action and handler mapping from the previous changes remain in place to handle the push operation. * Done. The Push button now uses the same styling as the "Open PR" button: `bg-panel text-normal hover:bg-tertiary`. * The type check passes. Here's a summary of the fix: ## Summary The Push button now correctly uses `remoteCommitsAhead` (unpushed commits to remote) instead of `commitsAhead` (total commits ahead of target branch) to determine when to show. ### Changes Made: 1. **`frontend/src/components/ui-new/views/GitPanel.tsx`**: - Added `remoteCommitsAhead: number` to the `RepoInfo` interface - Pass `remoteCommitsAhead` to `RepoCard` 2. **`frontend/src/components/ui-new/primitives/RepoCard.tsx`**: - Added `remoteCommitsAhead?: number` to `RepoCardProps` - Changed the push button condition from `commitsAhead > 0` to `remoteCommitsAhead > 0` 3. **`frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`**: - Added `remoteCommitsAhead: repoStatus?.remote_commits_ahead ?? 0` to the `repoInfos` mapping Now the Push button will: - Only appear when there's an open PR AND there are unpushed commits (`remote_commits_ahead > 0`) - Disappear after a successful push (when `remote_commits_ahead` becomes 0) - Reappear when new commits are made locally that haven't been pushed * The type check passes. Here's a summary of the changes: ## Summary The Push button now hides immediately when clicked and only reappears when new commits are available: ### Changes to `RepoCard.tsx`: - Added `useState` and `useRef` imports - Added `isPushHidden` state to track when the button should be hidden - Added `lastRemoteCommitsAhead` ref to track the previous value - Added `useEffect` that resets `isPushHidden` to `false` when `remoteCommitsAhead` increases (meaning new commits are available) - Added `handlePushClick` function that hides the button immediately and triggers the push action - Updated the button condition to include `!isPushHidden` The button will: 1. Hide immediately when clicked (no double-clicking possible) 2. Stay hidden until `remoteCommitsAhead` increases above the last known value 3. Reappear when new local commits are made that need to be pushed * Both the linter and type check pass. The linter errors are fixed. Here's a summary of the changes: ## Summary of Linter Fixes The linter was flagging that `RepoCard` (a presentational component) should not manage state or have side effects. I refactored the code to lift the state up to the container component: ### Changes: 1. **`RepoCard.tsx`** - Made purely presentational: - Removed `useState`, `useRef`, `useEffect` imports - Removed `remoteCommitsAhead` prop - Added `showPushButton` prop (boolean, controlled by parent) - Added `onPushClick` callback prop - The button now just renders based on `showPushButton` and calls `onPushClick` 2. **`GitPanel.tsx`** - Updated interface and props: - `RepoInfo` now has optional `remoteCommitsAhead` and `showPushButton` fields - Added `onPushClick` callback to `GitPanelProps` - Passes `showPushButton` and `onPushClick` to `RepoCard` 3. **`WorkspacesLayout.tsx`** - State management moved here: - Added `hiddenPushRepos` state (Set of repo IDs where push was clicked) - Added `lastRemoteCommitsAhead` ref to track previous values - Added `useEffect` to reset hidden state when `remoteCommitsAhead` increases - Added `repoInfosWithPushButton` memo that computes `showPushButton` for each repo - Added `handlePushClick` that hides button immediately and executes push action * prettier fix * Done. I've added `GitPush` to the Git actions list in the command bar. It now appears between "Merge" and "Rebase" in the Git Actions menu. * Both type check and lint pass. Done! The Push action in the command bar now has the same visibility conditions as in the Git panel - it only appears when: 1. There's a workspace 2. There are git repos 3. There's an open PR (`hasOpenPR`) 4. There are unpushed commits (`hasUnpushedCommits`) ### Summary of changes: 1. **`frontend/src/components/ui-new/actions/index.ts`**: - Added `hasOpenPR` and `hasUnpushedCommits` to `ActionVisibilityContext` - Updated `GitPush.isVisible` to check these new context properties 2. **`frontend/src/components/ui-new/actions/useActionVisibility.ts`**: - Added `useBranchStatus` hook to get branch status data - Computed `hasOpenPR` by checking if any repo has an open PR in merges - Computed `hasUnpushedCommits` by checking if any repo has `remote_commits_ahead > 0` - Added these values to the returned context * The refactoring is complete. Here's a summary of the changes: ## Summary of Changes The push button logic has been simplified by using React Query's mutation state instead of manually tracking hidden state with refs and effects. ### Key Changes: 1. **WorkspacesLayout.tsx (`GitPanelContainer`)**: - Removed the complex `useEffect` that tracked `lastRemoteCommitsAhead` and managed `hiddenPushRepos` state - Removed `hiddenPushRepos` state and `lastRemoteCommitsAhead` ref - Added `usePush` hook with callbacks that: - On success: awaits query invalidation before clearing the loading state - On error: clears the loading state - Added simple `pushingRepoId` state to track which repo is being pushed - The button now shows loading state during push and automatically disappears after refetch if `remoteCommitsAhead` becomes 0 2. **GitPanel.tsx**: - Added `isPushPending?: boolean` to `RepoInfo` interface - Passes `isPushPending` to `RepoCard` 3. **RepoCard.tsx**: - Added `isPushPending` prop - Button shows while either `showPushButton` or `isPushPending` is true - Button is disabled and shows spinner with "Pushing..." text when pending - Uses existing translation key `git.states.pushing` ### How it works now: 1. User clicks push button 2. Button immediately shows loading state (spinner + "Pushing...") 3. Button is disabled to prevent multiple clicks 4. Push mutation runs 5. On success, query invalidation completes before clearing loading state 6. Once `branchStatus` refetches, if `remoteCommitsAhead` is 0, `showPushButton` becomes false and button disappears 7. On error, loading state clears and button returns to normal This approach is simpler and more predictable than manually tracking previous values. * Done. The push button now shows success feedback like the old UI: ## Summary of Changes The push button now has three states: 1. **Normal**: Shows "Push" with an up arrow icon 2. **Pending**: Shows "Pushing..." with a spinning icon, button is disabled 3. **Success**: Shows "Pushed!" with a green checkmark icon and green background for 2 seconds The flow is: 1. User clicks "Push" 2. Button immediately shows "Pushing..." with spinner 3. Push completes, queries are invalidated and refetched 4. Button shows green "Pushed!" for 2 seconds 5. Button disappears (if no more commits to push) or returns to normal state * All code review issues have been addressed: ## Summary of Changes 1. **Unified push logic** - Now uses `executeAction(Actions.GitPush, ...)` instead of calling `usePush` hook directly. This ensures consistent error handling between the button and command bar. 2. **Proper setTimeout cleanup** - Added `successTimeoutRef` with a cleanup effect that clears the timeout when the component unmounts: ```typescript const successTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); useEffect(() => { return () => { if (successTimeoutRef.current) { clearTimeout(successTimeoutRef.current); } }; }, []); ``` 3. **Error feedback** - Added error state handling: - New `isPushError` prop flows through GitPanel to RepoCard - Button shows red "Failed" with warning icon when push fails - Error state clears after 3 seconds - Added `git.states.pushFailed` translation key 4. **Simplified state management** - Changed from separate `pushingRepoId` and `pushSuccessRepoId` to a single `pushStates` record with `PushState` type (`'idle' | 'pending' | 'success' | 'error'`) * All i18n checks pass now. * Both code review issues have been addressed: ## Summary of Changes ### 1. Fixed stale closure issue - Added a `pushStatesRef` that mirrors the `pushStates` state - Changed the check from `pushStates[repoId]` to `pushStatesRef.current[repoId]` - Removed `pushStates` from the dependency array ### 2. Added proper error logging and user-facing error message - Modified `ActionsContext.tsx` to re-throw errors after showing the dialog, so callers can handle them - Added `console.error('Push failed:', error)` for debugging purposes - The error dialog is already shown by `executeAction` via `ConfirmDialog` - The button now properly shows the error state when push fails * fmt * The fix is in place. The key change is: - When we're in a "push flow" (state is `pending`, `success`, or `error`), we set `showPushButton: false` but the button remains visible because of the `isPushPending || isPushSuccess || isPushError` condition in RepoCard - This prevents the race condition where the query refetch could hide the button before we set the success state * All checks pass. The changes now: 1. **Reverted ActionsContext** - Removed the re-throw to maintain backwards compatibility 2. **Use `usePush` hook directly** - For proper success/error detection instead of going through `executeAction` 3. **Show error dialog manually** - Using `ConfirmDialog.show()` when push fails 4. **Track current repo with ref** - `currentPushRepoRef` tracks which repo is being pushed so callbacks can update the correct state * All checks pass. Here's a summary of the force push handling: ## Force Push Functionality Yes, force push is fully supported. Here's how it works: **Old UI (`useGitOperations.ts`):** 1. When `usePush` fails with `errorData.type === 'force_push_required'` 2. It automatically shows `ForcePushDialog` 3. The dialog warns the user about the consequences and lets them confirm 4. If confirmed, it calls `useForcePush` to execute the force push **New UI (now updated):** 1. When the push button is clicked and fails with `force_push_required` 2. The push state is reset to `idle` (so the button returns to normal) 3. `ForcePushDialog` is shown with the attemptId and repoId 4. User can confirm or cancel the force push from the dialog The `ForcePushDialog` handles its own loading state, success/error feedback, and API call internally via `useForcePush` hook. * clear pushStates on workspace change Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-12 15:13:57 +01:00
hasOpenPR: boolean;
hasUnpushedCommits: boolean;
2026-01-08 22:14:38 +00:00
}
// Base properties shared by all actions
interface ActionBase {
id: string;
label: string | ((workspace?: Workspace) => string);
icon: ActionIcon;
shortcut?: string;
variant?: 'default' | 'destructive';
// Optional visibility condition - if omitted, action is always visible
isVisible?: (ctx: ActionVisibilityContext) => boolean;
// Optional active state - if omitted, action is not active
isActive?: (ctx: ActionVisibilityContext) => boolean;
// Optional enabled state - if omitted, action is enabled
isEnabled?: (ctx: ActionVisibilityContext) => boolean;
// Optional dynamic icon - if omitted, uses static icon property
getIcon?: (ctx: ActionVisibilityContext) => ActionIcon;
// Optional dynamic tooltip - if omitted, uses label
getTooltip?: (ctx: ActionVisibilityContext) => string;
// Optional dynamic label - if omitted, uses static label property
getLabel?: (ctx: ActionVisibilityContext) => string;
}
// Global action (no target needed)
export interface GlobalActionDefinition extends ActionBase {
requiresTarget: false;
execute: (ctx: ActionExecutorContext) => Promise<void> | void;
}
// Workspace action (target required - validated by ActionsContext)
export interface WorkspaceActionDefinition extends ActionBase {
requiresTarget: true;
execute: (
ctx: ActionExecutorContext,
workspaceId: string
) => Promise<void> | void;
}
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
// Git action (requires workspace + repoId)
export interface GitActionDefinition extends ActionBase {
requiresTarget: 'git';
execute: (
ctx: ActionExecutorContext,
workspaceId: string,
repoId: string
) => Promise<void> | void;
}
2026-01-08 22:14:38 +00:00
// Discriminated union
export type ActionDefinition =
| GlobalActionDefinition
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
| WorkspaceActionDefinition
| GitActionDefinition;
2026-01-08 22:14:38 +00:00
// Helper to get workspace from query cache
function getWorkspaceFromCache(
queryClient: QueryClient,
workspaceId: string
): Workspace {
const workspace = queryClient.getQueryData<Workspace>(
attemptKeys.byId(workspaceId)
);
if (!workspace) {
throw new Error('Workspace not found');
}
return workspace;
}
// Helper to invalidate workspace-related queries
function invalidateWorkspaceQueries(
queryClient: QueryClient,
workspaceId: string
) {
queryClient.invalidateQueries({ queryKey: attemptKeys.byId(workspaceId) });
queryClient.invalidateQueries({ queryKey: workspaceSummaryKeys.all });
}
// All application actions
export const Actions = {
// === Workspace Actions ===
DuplicateWorkspace: {
id: 'duplicate-workspace',
label: 'Duplicate',
icon: CopyIcon,
requiresTarget: true,
execute: async (ctx, workspaceId) => {
try {
const firstMessage = await attemptsApi.getFirstUserMessage(workspaceId);
ctx.navigate('/workspaces/create', {
state: { duplicatePrompt: firstMessage },
});
} catch {
// Fallback to creating without the prompt
ctx.navigate('/workspaces/create');
}
},
},
Add rename workspace functionality (Vibe Kanban) (#1868) * I've added the rename workspace functionality. Here's a summary of the changes: ## Summary The backend already had rename workspace support via the `PUT /api/task-attempts/{attemptId}` endpoint with the `name` field. I added the frontend UI to expose this functionality. ### Files Created 1. **`frontend/src/components/ui-new/dialogs/RenameWorkspaceDialog.tsx`** - New dialog component for renaming workspaces - Shows the current name (or branch name as fallback) - Allows user to input a new name - Handles mutation with proper error handling and cache invalidation ### Files Modified 1. **`frontend/src/components/ui-new/actions/index.ts`** - Added `PencilSimpleIcon` import - Added `RenameWorkspaceDialog` import - Added new `RenameWorkspace` action that shows the rename dialog 2. **`frontend/src/components/ui-new/actions/pages.ts`** - Added `Actions.RenameWorkspace` to the `workspaceActions` page (at the top of the list) 3. **`frontend/src/i18n/locales/en/common.json`** - Added translations for the rename workspace dialog under `workspaces.rename` ### How to Use Users can now rename a workspace by: 1. Clicking the three-dot menu (⋯) on any workspace in the sidebar 2. Selecting "Rename" from the command bar 3. Entering a new name in the dialog 4. Clicking "Rename" to save * Cleanup script changes for workspace 4eb64331-cc43-43af-849e-3731664e53b9 * Added the missing translations for the rename workspace dialog to all locale files: - **es** (Spanish) - **ja** (Japanese) - **ko** (Korean) - **zh-Hans** (Simplified Chinese) - **zh-Hant** (Traditional Chinese)
2026-01-09 10:06:29 +01:00
RenameWorkspace: {
id: 'rename-workspace',
label: 'Rename',
icon: PencilSimpleIcon,
requiresTarget: true,
execute: async (ctx, workspaceId) => {
const workspace = getWorkspaceFromCache(ctx.queryClient, workspaceId);
await RenameWorkspaceDialog.show({
workspaceId,
currentName: workspace.name || workspace.branch,
});
},
},
2026-01-08 22:14:38 +00:00
PinWorkspace: {
id: 'pin-workspace',
label: (workspace?: Workspace) => (workspace?.pinned ? 'Unpin' : 'Pin'),
icon: PushPinIcon,
requiresTarget: true,
execute: async (ctx, workspaceId) => {
const workspace = getWorkspaceFromCache(ctx.queryClient, workspaceId);
await attemptsApi.update(workspaceId, {
pinned: !workspace.pinned,
});
invalidateWorkspaceQueries(ctx.queryClient, workspaceId);
},
},
ArchiveWorkspace: {
id: 'archive-workspace',
label: (workspace?: Workspace) =>
workspace?.archived ? 'Unarchive' : 'Archive',
icon: ArchiveIcon,
requiresTarget: true,
isVisible: (ctx) => ctx.hasWorkspace,
isActive: (ctx) => ctx.workspaceArchived,
execute: async (ctx, workspaceId) => {
const workspace = getWorkspaceFromCache(ctx.queryClient, workspaceId);
const wasArchived = workspace.archived;
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
// Calculate next workspace before archiving
2026-01-08 22:14:38 +00:00
let nextWorkspaceId: string | null = null;
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
if (!wasArchived) {
2026-01-08 22:14:38 +00:00
const currentIndex = ctx.activeWorkspaces.findIndex(
(ws) => ws.id === workspaceId
);
if (currentIndex >= 0 && ctx.activeWorkspaces.length > 1) {
const nextWorkspace =
ctx.activeWorkspaces[currentIndex + 1] ||
ctx.activeWorkspaces[currentIndex - 1];
nextWorkspaceId = nextWorkspace?.id ?? null;
}
}
// Perform the archive/unarchive
await attemptsApi.update(workspaceId, { archived: !wasArchived });
invalidateWorkspaceQueries(ctx.queryClient, workspaceId);
// Select next workspace after successful archive
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
if (!wasArchived && nextWorkspaceId) {
2026-01-08 22:14:38 +00:00
ctx.selectWorkspace(nextWorkspaceId);
}
},
},
DeleteWorkspace: {
id: 'delete-workspace',
label: 'Delete',
icon: TrashIcon,
variant: 'destructive',
requiresTarget: true,
execute: async (ctx, workspaceId) => {
const workspace = getWorkspaceFromCache(ctx.queryClient, workspaceId);
const result = await ConfirmDialog.show({
title: 'Delete Workspace',
message:
'Are you sure you want to delete this workspace? This action cannot be undone.',
confirmText: 'Delete',
cancelText: 'Cancel',
variant: 'destructive',
});
if (result === 'confirmed') {
await tasksApi.delete(workspace.task_id);
ctx.queryClient.invalidateQueries({ queryKey: taskKeys.all });
ctx.queryClient.invalidateQueries({
queryKey: workspaceSummaryKeys.all,
});
}
},
},
// === Global/Navigation Actions ===
NewWorkspace: {
id: 'new-workspace',
label: 'New Workspace',
icon: PlusIcon,
requiresTarget: false,
execute: (ctx) => {
ctx.navigate('/workspaces/create');
},
},
Settings: {
id: 'settings',
label: 'Settings',
icon: GearIcon,
requiresTarget: false,
execute: (ctx) => {
ctx.navigate('/settings');
},
},
// === Diff View Actions ===
ToggleDiffViewMode: {
id: 'toggle-diff-view-mode',
label: () =>
useDiffViewStore.getState().mode === 'unified'
? 'Switch to Side-by-Side View'
: 'Switch to Inline View',
icon: ColumnsIcon,
requiresTarget: false,
isVisible: (ctx) => ctx.isChangesMode,
isActive: (ctx) => ctx.diffViewMode === 'split',
getIcon: (ctx) => (ctx.diffViewMode === 'split' ? ColumnsIcon : RowsIcon),
getTooltip: (ctx) =>
ctx.diffViewMode === 'split' ? 'Inline view' : 'Side-by-side view',
execute: () => {
useDiffViewStore.getState().toggle();
},
},
ToggleIgnoreWhitespace: {
id: 'toggle-ignore-whitespace',
label: () =>
useDiffViewStore.getState().ignoreWhitespace
? 'Show Whitespace Changes'
: 'Ignore Whitespace Changes',
icon: EyeSlashIcon,
requiresTarget: false,
isVisible: (ctx) => ctx.isChangesMode,
execute: () => {
const store = useDiffViewStore.getState();
store.setIgnoreWhitespace(!store.ignoreWhitespace);
},
},
ToggleWrapLines: {
id: 'toggle-wrap-lines',
label: () =>
useDiffViewStore.getState().wrapText
? 'Disable Line Wrapping'
: 'Enable Line Wrapping',
icon: TextAlignLeftIcon,
requiresTarget: false,
isVisible: (ctx) => ctx.isChangesMode,
execute: () => {
const store = useDiffViewStore.getState();
store.setWrapText(!store.wrapText);
},
},
// === Layout Panel Actions ===
ToggleSidebar: {
id: 'toggle-sidebar',
label: () =>
useLayoutStore.getState().isSidebarVisible
? 'Hide Sidebar'
: 'Show Sidebar',
icon: SidebarSimpleIcon,
requiresTarget: false,
isActive: (ctx) => ctx.isSidebarVisible,
execute: () => {
useLayoutStore.getState().toggleSidebar();
},
},
ToggleMainPanel: {
id: 'toggle-main-panel',
label: () =>
useLayoutStore.getState().isMainPanelVisible
? 'Hide Chat Panel'
: 'Show Chat Panel',
icon: ChatsTeardropIcon,
requiresTarget: false,
isActive: (ctx) => ctx.isMainPanelVisible,
isEnabled: (ctx) => !(ctx.isMainPanelVisible && !ctx.isChangesMode),
execute: () => {
useLayoutStore.getState().toggleMainPanel();
},
},
ToggleGitPanel: {
id: 'toggle-git-panel',
label: () =>
useLayoutStore.getState().isGitPanelVisible
? 'Hide Git Panel'
: 'Show Git Panel',
icon: SidebarSimpleIcon,
requiresTarget: false,
isActive: (ctx) => ctx.isGitPanelVisible,
execute: () => {
useLayoutStore.getState().toggleGitPanel();
},
},
ToggleChangesMode: {
id: 'toggle-changes-mode',
label: () =>
useLayoutStore.getState().isChangesMode
? 'Hide Changes Panel'
: 'Show Changes Panel',
icon: GitDiffIcon,
requiresTarget: false,
isVisible: (ctx) => !ctx.isCreateMode,
isActive: (ctx) => ctx.isChangesMode,
isEnabled: (ctx) => !ctx.isCreateMode,
execute: () => {
useLayoutStore.getState().toggleChangesMode();
},
},
ToggleLogsMode: {
id: 'toggle-logs-mode',
label: () =>
useLayoutStore.getState().isLogsMode
? 'Hide Logs Panel'
: 'Show Logs Panel',
icon: TerminalIcon,
requiresTarget: false,
isVisible: (ctx) => !ctx.isCreateMode,
isActive: (ctx) => ctx.isLogsMode,
isEnabled: (ctx) => !ctx.isCreateMode,
execute: () => {
useLayoutStore.getState().toggleLogsMode();
},
},
TogglePreviewMode: {
id: 'toggle-preview-mode',
label: () =>
useLayoutStore.getState().isPreviewMode
? 'Hide Preview Panel'
: 'Show Preview Panel',
icon: DesktopIcon,
requiresTarget: false,
isVisible: (ctx) => !ctx.isCreateMode,
isActive: (ctx) => ctx.isPreviewMode,
isEnabled: (ctx) => !ctx.isCreateMode,
execute: () => {
useLayoutStore.getState().togglePreviewMode();
},
},
// === Navigation Actions ===
OpenInOldUI: {
id: 'open-in-old-ui',
label: 'Open in Old UI',
icon: SignOutIcon,
requiresTarget: false,
execute: async (ctx) => {
// If no workspace is selected, navigate to root
if (!ctx.currentWorkspaceId) {
ctx.navigate('/');
return;
}
const workspace = getWorkspaceFromCache(
ctx.queryClient,
ctx.currentWorkspaceId
);
if (!workspace?.task_id) {
ctx.navigate('/');
return;
}
// Fetch task lazily to get project_id
const task = await tasksApi.getById(workspace.task_id);
if (task?.project_id) {
ctx.navigate(
`/projects/${task.project_id}/tasks/${workspace.task_id}/attempts/${workspace.id}`
);
} else {
ctx.navigate('/');
}
},
},
// === Diff Actions for Navbar ===
ToggleAllDiffs: {
id: 'toggle-all-diffs',
label: () => {
const { diffPaths } = useDiffViewStore.getState();
const { expanded } = useUiPreferencesStore.getState();
const keys = diffPaths.map((p) => `diff:${p}`);
const isAllExpanded =
keys.length > 0 && keys.every((k) => expanded[k] !== false);
return isAllExpanded ? 'Collapse All Diffs' : 'Expand All Diffs';
},
icon: CaretDoubleUpIcon,
requiresTarget: false,
isVisible: (ctx) => ctx.isChangesMode,
getIcon: (ctx) =>
ctx.isAllDiffsExpanded ? CaretDoubleUpIcon : CaretDoubleDownIcon,
getTooltip: (ctx) =>
ctx.isAllDiffsExpanded ? 'Collapse all diffs' : 'Expand all diffs',
execute: () => {
const { diffPaths } = useDiffViewStore.getState();
const { expanded, setExpandedAll } = useUiPreferencesStore.getState();
const keys = diffPaths.map((p) => `diff:${p}`);
const isAllExpanded =
keys.length > 0 && keys.every((k) => expanded[k] !== false);
setExpandedAll(keys, !isAllExpanded);
},
},
// === ContextBar Actions ===
OpenInIDE: {
id: 'open-in-ide',
label: 'Open in IDE',
icon: 'ide-icon' as const,
requiresTarget: false,
isVisible: (ctx) => ctx.hasWorkspace,
getTooltip: (ctx) => `Open in ${getIdeName(ctx.editorType)}`,
execute: async (ctx) => {
if (!ctx.currentWorkspaceId) return;
try {
const response = await attemptsApi.openEditor(ctx.currentWorkspaceId, {
editor_type: null,
file_path: null,
});
if (response.url) {
window.open(response.url, '_blank');
}
} catch {
// Show editor selection dialog on failure
EditorSelectionDialog.show({
selectedAttemptId: ctx.currentWorkspaceId,
});
}
},
},
CopyPath: {
id: 'copy-path',
label: 'Copy path',
icon: 'copy-icon' as const,
requiresTarget: false,
isVisible: (ctx) => ctx.hasWorkspace,
execute: async (ctx) => {
if (!ctx.containerRef) return;
await navigator.clipboard.writeText(ctx.containerRef);
},
},
ToggleDevServer: {
id: 'toggle-dev-server',
label: 'Dev Server',
icon: PlayIcon,
requiresTarget: false,
isVisible: (ctx) => ctx.hasWorkspace,
isEnabled: (ctx) =>
ctx.devServerState !== 'starting' && ctx.devServerState !== 'stopping',
getIcon: (ctx) => {
if (
ctx.devServerState === 'starting' ||
ctx.devServerState === 'stopping'
) {
return SpinnerIcon;
}
if (ctx.devServerState === 'running') {
return PauseIcon;
}
return PlayIcon;
},
getTooltip: (ctx) => {
switch (ctx.devServerState) {
case 'starting':
return 'Starting dev server...';
case 'stopping':
return 'Stopping dev server...';
case 'running':
return 'Stop dev server';
default:
return 'Start dev server';
}
},
getLabel: (ctx) =>
ctx.devServerState === 'running' ? 'Stop Dev Server' : 'Start Dev Server',
execute: (ctx) => {
if (ctx.runningDevServers.length > 0) {
2026-01-08 22:14:38 +00:00
ctx.stopDevServer();
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
} else {
2026-01-08 22:14:38 +00:00
ctx.startDevServer();
// Auto-open preview mode when starting dev server
useLayoutStore.getState().setPreviewMode(true);
2026-01-08 22:14:38 +00:00
}
},
},
// === Git Actions ===
GitCreatePR: {
id: 'git-create-pr',
label: 'Create Pull Request',
icon: GitPullRequestIcon,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
requiresTarget: 'git',
2026-01-08 22:14:38 +00:00
isVisible: (ctx) => ctx.hasWorkspace && ctx.hasGitRepos,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
execute: async (ctx, workspaceId, repoId) => {
2026-01-08 22:14:38 +00:00
const workspace = getWorkspaceFromCache(ctx.queryClient, workspaceId);
const task = await tasksApi.getById(workspace.task_id);
const repos = await attemptsApi.getRepos(workspaceId);
const repo = repos.find((r) => r.id === repoId);
2026-01-08 22:14:38 +00:00
const result = await CreatePRDialog.show({
attempt: workspace,
task: {
...task,
has_in_progress_attempt: false,
last_attempt_failed: false,
executor: '',
},
repoId,
targetBranch: repo?.target_branch,
2026-01-08 22:14:38 +00:00
});
if (!result.success && result.error) {
throw new Error(result.error);
}
},
},
GitMerge: {
id: 'git-merge',
label: 'Merge',
icon: GitMergeIcon,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
requiresTarget: 'git',
2026-01-08 22:14:38 +00:00
isVisible: (ctx) => ctx.hasWorkspace && ctx.hasGitRepos,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
execute: async (ctx, workspaceId, repoId) => {
2026-01-08 22:14:38 +00:00
const confirmResult = await ConfirmDialog.show({
title: 'Merge Branch',
message:
'Are you sure you want to merge this branch into the target branch?',
confirmText: 'Merge',
cancelText: 'Cancel',
});
if (confirmResult === 'confirmed') {
await attemptsApi.merge(workspaceId, { repo_id: repoId });
invalidateWorkspaceQueries(ctx.queryClient, workspaceId);
}
},
},
GitRebase: {
id: 'git-rebase',
label: 'Rebase',
icon: ArrowsClockwiseIcon,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
requiresTarget: 'git',
2026-01-08 22:14:38 +00:00
isVisible: (ctx) => ctx.hasWorkspace && ctx.hasGitRepos,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
execute: async (_ctx, workspaceId, repoId) => {
2026-01-08 22:14:38 +00:00
const repos = await attemptsApi.getRepos(workspaceId);
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
const repo = repos.find((r) => r.id === repoId);
if (!repo) throw new Error('Repository not found');
2026-01-08 22:14:38 +00:00
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
const branches = await repoApi.getBranches(repoId);
2026-01-08 22:14:38 +00:00
await RebaseDialog.show({
attemptId: workspaceId,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
repoId,
2026-01-08 22:14:38 +00:00
branches,
initialTargetBranch: repo.target_branch,
});
},
},
GitChangeTarget: {
id: 'git-change-target',
label: 'Change Target Branch',
icon: CrosshairIcon,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
requiresTarget: 'git',
2026-01-08 22:14:38 +00:00
isVisible: (ctx) => ctx.hasWorkspace && ctx.hasGitRepos,
Fix CommandBar input and navigation issues with state machine refactor (Vibe Kanban) (#1885) * Done! I've removed all 7 `shortcut` properties from the action definitions in `index.ts`: - `NewWorkspace`: removed `shortcut: 'N'` - `Settings`: removed `shortcut: ','` - `ToggleSidebar`: removed `shortcut: '['` - `ToggleGitPanel`: removed `shortcut: ']'` - `ToggleChangesMode`: removed `shortcut: 'C'` - `ToggleLogsMode`: removed `shortcut: 'L'` - `TogglePreviewMode`: removed `shortcut: 'P'` The `ActionDefinition` type (and its base `ActionBase` interface) were left unchanged as requested. * fix tailwind nto auto creating styles * I've successfully completed all the changes. Here's a summary of what was done: ## Summary of Changes ### 1. `ActionExecutorContext` (index.ts) - Made all properties non-optional - Removed `gitRepoId` from context (now passed as action parameter) - Properties now use `| null` for values that may not exist ### 2. New `GitActionDefinition` Type (index.ts) - Added new action type with `requiresTarget: 'git'` - Git actions now explicitly require `repoId` as a parameter ### 3. Git Actions Updated (index.ts) - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` function takes `repoId` as third parameter - Simplified implementations (no more fallback repo fetching logic) ### 4. `ActionsContext` Provider (ActionsContext.tsx) - Now calls `useDevServer()` to get dev server state - Provides all required context properties - `executeAction` handles three action types: global, workspace, and git - Removed `contextOverride` pattern ### 5. `ContextBarContainer` (ContextBarContainer.tsx) - Simplified significantly - no longer builds extended context - Uses executor context directly from provider - Removed `containerPath` prop ### 6. `ActionVisibilityContext` (index.ts + useActionVisibility.ts) - Made `editorType`, `devServerState`, `runningDevServerId` required - Visibility hook now provides all values from hooks ### 7. Updated Call Sites - `WorkspacesLayout`: Git actions now pass `repoId` as third parameter - `WorkspacesMain`: Removed `containerPath` prop from ContextBarContainer * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * All changes are complete and type checks pass. Here's a summary of the changes made: ## Summary of Changes ### 1. `ActionExecutorContext` - Made all properties non-optional - Removed `?` from all properties - Removed `gitRepoId` (moved to per-action parameter) - Changed nullable properties to use `| null` type ### 2. New `GitActionDefinition` type - Added new action type with `requiresTarget: 'git'` - Takes `repoId` as explicit third parameter ### 3. Git actions updated - `GitCreatePR`, `GitMerge`, `GitRebase`, `GitChangeTarget` now use `requiresTarget: 'git'` - Each action's `execute` receives `repoId` as parameter ### 4. `ActionsContext` provider centralized - Now calls `useDevServer()` to provide dev server state - Provides all required context properties from hooks - `executeAction` handles three action types: global, workspace, git - Removed `contextOverride` pattern ### 5. `ActionVisibilityContext` - Made dev server properties required - `editorType`, `devServerState`, `runningDevServerId` now required - `useActionVisibilityContext` hook provides all values ### 6. `ContextBarContainer` simplified - Removed extended context creation - Uses executor context directly from provider ### 7. Provider order fix - Moved `ExecutionProcessesProvider` up to `NewDesignScope` - Required because `useDevServer` depends on `ExecutionProcessesContext` ### Files Modified: - `vibe-kanban/frontend/src/components/ui-new/actions/index.ts` - `vibe-kanban/frontend/src/components/ui-new/actions/useActionVisibility.ts` - `vibe-kanban/frontend/src/contexts/ActionsContext.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/ContextBarContainer.tsx` - `vibe-kanban/frontend/src/components/ui-new/containers/WorkspacesLayout.tsx` - `vibe-kanban/frontend/src/components/ui-new/views/WorkspacesMain.tsx` - `vibe-kanban/frontend/src/components/ui-new/scope/NewDesignScope.tsx` * The implementation is complete. Let me summarize what was changed: ## Summary of Changes ### `pages.ts` - Added `'selectRepo'` to `PageId` type for dynamic repo selection page - Created `StaticPageId` type (excludes `selectRepo`) for static page records - Added `RepoItem` interface for repo selection items - Extended `ResolvedGroupItem` union type to include `{ type: 'repo'; repo: RepoItem }` - Updated `Pages` record and `getPageActions` to use `StaticPageId` ### `CommandBarDialog.tsx` - Added `repos` from `useWorkspaceContext()` - Added `pendingGitAction` state to track git actions awaiting repo selection - Reset `pendingGitAction` when dialog opens/closes - Modified `getPageWithItems` to generate dynamic `selectRepo` page with repo list - Updated `handleSelect` to: - Execute git actions immediately with single repo - Navigate to `selectRepo` page when multiple repos exist - Execute pending git action when user selects a repo - Clear `pendingGitAction` when navigating back from `selectRepo` page ### `CommandBar.tsx` - Added `FolderIcon` import - Added rendering for `'repo'` item type with folder icon ## Behavior 1. **Single repo**: When user selects a git action (e.g., "Create Pull Request") and the workspace has only one git repo, the action executes immediately with that repo's ID. 2. **Multiple repos**: When workspace has multiple repos, selecting a git action navigates to a "Select Repository" page where the user can choose which repo to use, then the action executes with the selected repo. 3. **Back navigation**: Pressing back from the repo selection page clears the pending action and returns to the previous page. * Done! The `CommandBarDialog` has been refactored to use a state machine pattern with `useReducer`. Here's a summary of the changes: ## Summary of Changes **State Machine Types (lines 38-67):** - `CommandBarState`: Discriminated union with `browsing` and `selectingRepo` states - `CommandBarEvent`: `RESET`, `SEARCH_CHANGE`, `GO_BACK`, `SELECT_ITEM` - `CommandBarEffect`: Side effects returned from reducer (`none` or `execute`) **Reducer (lines 73-201):** - Pure function returning `[newState, effect]` tuple - Handles all state transitions based on current state and event - Git action flow: single repo → execute, multiple repos → transition to `selectingRepo` **Component Changes:** - Replaced 4 `useState` calls with single `useReducer` - `currentPage` is now derived from state (line 249-250) - `handleSelect` computes effect from reducer, dispatches event, then handles effect (lines 456-475) - `goBack` and `handleSearchChange` simply dispatch events (lines 446-453) **Benefits:** - All state transitions in one place (the reducer) - State transitions are pure and testable - Clear separation between state updates and side effects - Impossible states are unrepresentable (e.g., can't have `pendingAction` while `browsing`) * Done! Here's the final result: ## Summary **Before:** 520 lines in 1 file (after initial state machine refactor) **After:** 487 lines across 4 files | File | Lines | Purpose | |------|-------|---------| | `CommandBarDialog.tsx` | 101 | Thin orchestration (down from 520) | | `useCommandBarState.ts` | 177 | State machine hook | | `useResolvedPage.ts` | 150 | Page building logic | | `injectSearchMatches.ts` | 59 | DRY search helper | **Key wins:** - Main component: **520 → 101 lines** (80% reduction) - Search injection code: **100 → 59 lines** (4× duplication eliminated) - Each file has single responsibility - State machine is testable in isolation - Page building logic is reusable * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * Done! Here are the final results: ## Final Summary | File | Before | After | Reduction | |------|--------|-------|-----------| | `CommandBarDialog.tsx` | 101 | 106 | +5 (linter formatting) | | `useCommandBarState.ts` | 177 | 95 | **-82 (46%)** | | `useResolvedPage.ts` | 150 | 95 | **-55 (37%)** | | `injectSearchMatches.ts` | 59 | 42 | **-17 (29%)** | | **Total** | **487** | **338** | **-149 (31%)** | **Original single file:** 398 lines **Final 4 files:** 338 lines (15% less code overall) Key wins: - Main component is now just 106 lines of thin orchestration - State machine is flattened (if statements instead of nested switch/case) - Page resolution is simplified with reused types - Search injection uses reduce() instead of for-loop - All files pass type check * Cleanup script changes for workspace e961e311-6db4-41bf-83a1-ef8dd96eb497 * I've fixed both issues with the CommandBar. Here's a summary of what was wrong and how I fixed it: ## Root Causes ### Issue 1: Nested `<Command>` Components The `CommandDialog` component was wrapping children in a `<Command>` component, and `CommandBar` also rendered its own `<Command>`. This nested cmdk context caused conflicts with state management. **Fix:** Removed the `<Command>` wrapper from `CommandDialog` in `Command.tsx:26-44`, and moved the styling classes to `CommandBar`'s `<Command>` component. ### Issue 2: Unstable `dispatch` Function Causing Infinite RESET Loop In `useCommandBarState.ts`, the `dispatch` function had `[state, repoCount]` in its `useCallback` dependency array. This caused `dispatch` to be recreated on every state change. In `CommandBarDialog.tsx`, the `useEffect` that resets state when the modal opens had `dispatch` in its dependency array: ```tsx useEffect(() => { if (modal.visible) { dispatch({ type: 'RESET', page }); } }, [modal.visible, page, dispatch]); // dispatch changes → useEffect runs → RESET ``` This created an infinite loop: 1. User types → state updates → `dispatch` recreated 2. `dispatch` changed → `useEffect` runs → `RESET` dispatched 3. State resets to empty → repeat **Fix:** Made `dispatch` stable by using refs instead of state in dependencies: ```tsx const stateRef = useRef<CommandBarState>(browsing(initialPage)); const repoCountRef = useRef(repoCount); const dispatch = useCallback( (event: CommandBarEvent): CommandBarEffect => { const [, effect] = reducer(stateRef.current, event, repoCountRef.current); rawDispatch(event); return effect; }, [] // No dependencies - uses refs for current values ); ``` ## Files Changed 1. `vibe-kanban/frontend/src/components/ui-new/primitives/Command.tsx` - Removed nested `<Command>` wrapper from `CommandDialog` 2. `vibe-kanban/frontend/src/components/ui-new/primitives/CommandBar.tsx` - Added styling classes that were on the removed wrapper 3. `vibe-kanban/frontend/src/components/ui-new/dialogs/commandBar/useCommandBarState.ts` - Made `dispatch` stable using refs
2026-01-09 16:25:42 +00:00
execute: async (_ctx, workspaceId, repoId) => {
2026-01-08 22:14:38 +00:00
const branches = await repoApi.getBranches(repoId);
await ChangeTargetDialog.show({
attemptId: workspaceId,
repoId,
branches,
});
},
},
Add Push button to Git panel when PR is open with unpushed commits (Vibe Kanban) (#1951) * The implementation is complete. Here's a summary of what I changed: ## Summary Added a "Push" button to the workspaces UI Git panel. The changes allow users to push changes when a PR is already open. ### Files Modified: 1. **`frontend/src/components/ui-new/primitives/RepoCard.tsx`** (lines 27-42) - Added `'push'` to the `RepoAction` type union - Added a new option to `repoActionOptions` array with the Push button configuration using `ArrowUpIcon` 2. **`frontend/src/components/ui-new/actions/index.ts`** (lines 33, 691-707) - Imported `ArrowUpIcon` from phosphor-icons - Added `GitPush` action definition that: - Calls `attemptsApi.push()` with the workspace and repo IDs - Handles the `force_push_required` error case - Invalidates workspace queries on success to refresh the UI 3. **`frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`** (line 103) - Added mapping from `'push'` action to `Actions.GitPush` in the action handler The Push button now appears in the Git panel dropdown alongside "Open pull request" and "Merge" options. * The type check passes. Here's a summary of the updated changes: ## Summary The Push button now appears **next to the PR number** when: 1. A PR is already open (`prStatus === 'open'`) 2. There are new commits ahead (`commitsAhead > 0`) ### Changes Made: **`frontend/src/components/ui-new/primitives/RepoCard.tsx`**: - Removed `push` from the `repoActionOptions` dropdown (lines 34-41) - Added a dedicated Push button in the PR status row (lines 213-222) that: - Only appears when `prStatus === 'open'` AND `commitsAhead > 0` - Uses brand primary styling (`bg-brand-primary`) to make it prominent - Displays with an up arrow icon and "Push" label using existing translation - Calls `onActionsClick?.('push')` when clicked The `GitPush` action and handler mapping from the previous changes remain in place to handle the push operation. * Done. The Push button now uses the same styling as the "Open PR" button: `bg-panel text-normal hover:bg-tertiary`. * The type check passes. Here's a summary of the fix: ## Summary The Push button now correctly uses `remoteCommitsAhead` (unpushed commits to remote) instead of `commitsAhead` (total commits ahead of target branch) to determine when to show. ### Changes Made: 1. **`frontend/src/components/ui-new/views/GitPanel.tsx`**: - Added `remoteCommitsAhead: number` to the `RepoInfo` interface - Pass `remoteCommitsAhead` to `RepoCard` 2. **`frontend/src/components/ui-new/primitives/RepoCard.tsx`**: - Added `remoteCommitsAhead?: number` to `RepoCardProps` - Changed the push button condition from `commitsAhead > 0` to `remoteCommitsAhead > 0` 3. **`frontend/src/components/ui-new/containers/WorkspacesLayout.tsx`**: - Added `remoteCommitsAhead: repoStatus?.remote_commits_ahead ?? 0` to the `repoInfos` mapping Now the Push button will: - Only appear when there's an open PR AND there are unpushed commits (`remote_commits_ahead > 0`) - Disappear after a successful push (when `remote_commits_ahead` becomes 0) - Reappear when new commits are made locally that haven't been pushed * The type check passes. Here's a summary of the changes: ## Summary The Push button now hides immediately when clicked and only reappears when new commits are available: ### Changes to `RepoCard.tsx`: - Added `useState` and `useRef` imports - Added `isPushHidden` state to track when the button should be hidden - Added `lastRemoteCommitsAhead` ref to track the previous value - Added `useEffect` that resets `isPushHidden` to `false` when `remoteCommitsAhead` increases (meaning new commits are available) - Added `handlePushClick` function that hides the button immediately and triggers the push action - Updated the button condition to include `!isPushHidden` The button will: 1. Hide immediately when clicked (no double-clicking possible) 2. Stay hidden until `remoteCommitsAhead` increases above the last known value 3. Reappear when new local commits are made that need to be pushed * Both the linter and type check pass. The linter errors are fixed. Here's a summary of the changes: ## Summary of Linter Fixes The linter was flagging that `RepoCard` (a presentational component) should not manage state or have side effects. I refactored the code to lift the state up to the container component: ### Changes: 1. **`RepoCard.tsx`** - Made purely presentational: - Removed `useState`, `useRef`, `useEffect` imports - Removed `remoteCommitsAhead` prop - Added `showPushButton` prop (boolean, controlled by parent) - Added `onPushClick` callback prop - The button now just renders based on `showPushButton` and calls `onPushClick` 2. **`GitPanel.tsx`** - Updated interface and props: - `RepoInfo` now has optional `remoteCommitsAhead` and `showPushButton` fields - Added `onPushClick` callback to `GitPanelProps` - Passes `showPushButton` and `onPushClick` to `RepoCard` 3. **`WorkspacesLayout.tsx`** - State management moved here: - Added `hiddenPushRepos` state (Set of repo IDs where push was clicked) - Added `lastRemoteCommitsAhead` ref to track previous values - Added `useEffect` to reset hidden state when `remoteCommitsAhead` increases - Added `repoInfosWithPushButton` memo that computes `showPushButton` for each repo - Added `handlePushClick` that hides button immediately and executes push action * prettier fix * Done. I've added `GitPush` to the Git actions list in the command bar. It now appears between "Merge" and "Rebase" in the Git Actions menu. * Both type check and lint pass. Done! The Push action in the command bar now has the same visibility conditions as in the Git panel - it only appears when: 1. There's a workspace 2. There are git repos 3. There's an open PR (`hasOpenPR`) 4. There are unpushed commits (`hasUnpushedCommits`) ### Summary of changes: 1. **`frontend/src/components/ui-new/actions/index.ts`**: - Added `hasOpenPR` and `hasUnpushedCommits` to `ActionVisibilityContext` - Updated `GitPush.isVisible` to check these new context properties 2. **`frontend/src/components/ui-new/actions/useActionVisibility.ts`**: - Added `useBranchStatus` hook to get branch status data - Computed `hasOpenPR` by checking if any repo has an open PR in merges - Computed `hasUnpushedCommits` by checking if any repo has `remote_commits_ahead > 0` - Added these values to the returned context * The refactoring is complete. Here's a summary of the changes: ## Summary of Changes The push button logic has been simplified by using React Query's mutation state instead of manually tracking hidden state with refs and effects. ### Key Changes: 1. **WorkspacesLayout.tsx (`GitPanelContainer`)**: - Removed the complex `useEffect` that tracked `lastRemoteCommitsAhead` and managed `hiddenPushRepos` state - Removed `hiddenPushRepos` state and `lastRemoteCommitsAhead` ref - Added `usePush` hook with callbacks that: - On success: awaits query invalidation before clearing the loading state - On error: clears the loading state - Added simple `pushingRepoId` state to track which repo is being pushed - The button now shows loading state during push and automatically disappears after refetch if `remoteCommitsAhead` becomes 0 2. **GitPanel.tsx**: - Added `isPushPending?: boolean` to `RepoInfo` interface - Passes `isPushPending` to `RepoCard` 3. **RepoCard.tsx**: - Added `isPushPending` prop - Button shows while either `showPushButton` or `isPushPending` is true - Button is disabled and shows spinner with "Pushing..." text when pending - Uses existing translation key `git.states.pushing` ### How it works now: 1. User clicks push button 2. Button immediately shows loading state (spinner + "Pushing...") 3. Button is disabled to prevent multiple clicks 4. Push mutation runs 5. On success, query invalidation completes before clearing loading state 6. Once `branchStatus` refetches, if `remoteCommitsAhead` is 0, `showPushButton` becomes false and button disappears 7. On error, loading state clears and button returns to normal This approach is simpler and more predictable than manually tracking previous values. * Done. The push button now shows success feedback like the old UI: ## Summary of Changes The push button now has three states: 1. **Normal**: Shows "Push" with an up arrow icon 2. **Pending**: Shows "Pushing..." with a spinning icon, button is disabled 3. **Success**: Shows "Pushed!" with a green checkmark icon and green background for 2 seconds The flow is: 1. User clicks "Push" 2. Button immediately shows "Pushing..." with spinner 3. Push completes, queries are invalidated and refetched 4. Button shows green "Pushed!" for 2 seconds 5. Button disappears (if no more commits to push) or returns to normal state * All code review issues have been addressed: ## Summary of Changes 1. **Unified push logic** - Now uses `executeAction(Actions.GitPush, ...)` instead of calling `usePush` hook directly. This ensures consistent error handling between the button and command bar. 2. **Proper setTimeout cleanup** - Added `successTimeoutRef` with a cleanup effect that clears the timeout when the component unmounts: ```typescript const successTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); useEffect(() => { return () => { if (successTimeoutRef.current) { clearTimeout(successTimeoutRef.current); } }; }, []); ``` 3. **Error feedback** - Added error state handling: - New `isPushError` prop flows through GitPanel to RepoCard - Button shows red "Failed" with warning icon when push fails - Error state clears after 3 seconds - Added `git.states.pushFailed` translation key 4. **Simplified state management** - Changed from separate `pushingRepoId` and `pushSuccessRepoId` to a single `pushStates` record with `PushState` type (`'idle' | 'pending' | 'success' | 'error'`) * All i18n checks pass now. * Both code review issues have been addressed: ## Summary of Changes ### 1. Fixed stale closure issue - Added a `pushStatesRef` that mirrors the `pushStates` state - Changed the check from `pushStates[repoId]` to `pushStatesRef.current[repoId]` - Removed `pushStates` from the dependency array ### 2. Added proper error logging and user-facing error message - Modified `ActionsContext.tsx` to re-throw errors after showing the dialog, so callers can handle them - Added `console.error('Push failed:', error)` for debugging purposes - The error dialog is already shown by `executeAction` via `ConfirmDialog` - The button now properly shows the error state when push fails * fmt * The fix is in place. The key change is: - When we're in a "push flow" (state is `pending`, `success`, or `error`), we set `showPushButton: false` but the button remains visible because of the `isPushPending || isPushSuccess || isPushError` condition in RepoCard - This prevents the race condition where the query refetch could hide the button before we set the success state * All checks pass. The changes now: 1. **Reverted ActionsContext** - Removed the re-throw to maintain backwards compatibility 2. **Use `usePush` hook directly** - For proper success/error detection instead of going through `executeAction` 3. **Show error dialog manually** - Using `ConfirmDialog.show()` when push fails 4. **Track current repo with ref** - `currentPushRepoRef` tracks which repo is being pushed so callbacks can update the correct state * All checks pass. Here's a summary of the force push handling: ## Force Push Functionality Yes, force push is fully supported. Here's how it works: **Old UI (`useGitOperations.ts`):** 1. When `usePush` fails with `errorData.type === 'force_push_required'` 2. It automatically shows `ForcePushDialog` 3. The dialog warns the user about the consequences and lets them confirm 4. If confirmed, it calls `useForcePush` to execute the force push **New UI (now updated):** 1. When the push button is clicked and fails with `force_push_required` 2. The push state is reset to `idle` (so the button returns to normal) 3. `ForcePushDialog` is shown with the attemptId and repoId 4. User can confirm or cancel the force push from the dialog The `ForcePushDialog` handles its own loading state, success/error feedback, and API call internally via `useForcePush` hook. * clear pushStates on workspace change Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-12 15:13:57 +01:00
GitPush: {
id: 'git-push',
label: 'Push',
icon: ArrowUpIcon,
requiresTarget: 'git',
isVisible: (ctx) =>
ctx.hasWorkspace &&
ctx.hasGitRepos &&
ctx.hasOpenPR &&
ctx.hasUnpushedCommits,
execute: async (ctx, workspaceId, repoId) => {
const result = await attemptsApi.push(workspaceId, { repo_id: repoId });
if (!result.success) {
if (result.error?.type === 'force_push_required') {
throw new Error(
'Force push required. The remote branch has diverged.'
);
}
throw new Error('Failed to push changes');
}
invalidateWorkspaceQueries(ctx.queryClient, workspaceId);
},
},
2026-01-08 22:14:38 +00:00
} as const satisfies Record<string, ActionDefinition>;
// Helper to resolve dynamic label
export function resolveLabel(
action: ActionDefinition,
workspace?: Workspace
): string {
return typeof action.label === 'function'
? action.label(workspace)
: action.label;
}
// Divider marker for navbar action groups
export const NavbarDivider = { type: 'divider' } as const;
export type NavbarItem = ActionDefinition | typeof NavbarDivider;
// Navbar action groups define which actions appear in each section
export const NavbarActionGroups = {
left: [Actions.ArchiveWorkspace, Actions.OpenInOldUI] as ActionDefinition[],
right: [
Actions.ToggleDiffViewMode,
Actions.ToggleAllDiffs,
NavbarDivider,
Actions.ToggleSidebar,
Actions.ToggleMainPanel,
Actions.ToggleChangesMode,
Actions.ToggleLogsMode,
Actions.TogglePreviewMode,
Actions.ToggleGitPanel,
] as NavbarItem[],
};
// Divider marker for context bar action groups
export const ContextBarDivider = { type: 'divider' } as const;
export type ContextBarItem = ActionDefinition | typeof ContextBarDivider;
// ContextBar action groups define which actions appear in each section
export const ContextBarActionGroups = {
primary: [Actions.OpenInIDE, Actions.CopyPath] as ActionDefinition[],
secondary: [
Actions.ToggleDevServer,
Actions.TogglePreviewMode,
Actions.ToggleChangesMode,
] as ActionDefinition[],
};
// Helper to check if an icon is a special type
export function isSpecialIcon(icon: ActionIcon): icon is SpecialIconType {
return icon === 'ide-icon' || icon === 'copy-icon';
}