* integrated terminal
* ci
* persist terminal
* Done! I've moved the TerminalPanelContainer from the main panel area to the right sidebar. Here's a summary of the changes:
- Added `isTerminalVisible: boolean` to global state (defaults to `true`)
- Added `toggleTerminal()` and `setTerminalVisible(value)` actions
- Added `isTerminalVisible` to the persisted state and the `useWorkspacePanelState` hook
- Imported `TerminalPanelContainer` and `useUiPreferencesStore`
- Restructured the component to always include a terminal section at the bottom (when not in create mode and `isTerminalVisible` is true)
- Dynamic flex ratios:
- With upper content + terminal: flex-4 (content) / flex-2 (git) / flex-4 (terminal)
- Without upper content + terminal: flex-3 (git) / flex-7 (terminal)
- When terminal is hidden: original ratios preserved
- Removed the `TerminalPanelContainer` import
- Removed the TERMINAL case from the right main panel rendering
- Added `isTerminalVisible` to `ActionVisibilityContext` interface
- Updated `ToggleTerminalMode` action to use `toggleTerminal()` and check `isTerminalVisible`
- Added `isTerminalVisible` from `panelState` to the visibility context
The terminal now appears permanently at the bottom of the right sidebar (when visible), and the toggle button collapses/expands just the terminal section within the sidebar. The visibility state is persisted to localStorage.
* Frontend ESLint passes with no errors. The backend Rust error (`crates/utils/src/shell.rs:31`) is a pre-existing issue in the codebase, not related to my changes.
* `cargo check` passes now. The fix was adding `.to_path_buf()` to convert the `&Path` reference to an owned `PathBuf`.
* The code compiles. Done! I've added three environment variables in `crates/local-deployment/src/pty.rs:73-75`:
1. `ZDOTDIR=/nonexistent` - prevents zsh from loading `~/.zshrc`
2. `PS1="$ "` - sets the bash prompt
3. `PROMPT="$ "` - sets the zsh prompt
The terminal prompt will now show just `$ ` instead of `lkw@Louiss-MacBook-Pro vibe-kanban %`. Run `pnpm run dev:qa` and open the terminal panel to verify.
* Cleanup script changes for workspace 928e696e-5f40-4603-9227-798f0520e07f
* Done! The fix is simpler - using `cmd.arg("-f")` to tell zsh/bash to skip loading config files. This:
1. Removes the `ZDOTDIR=/nonexistent` that was causing errors
2. Adds `-f` flag which skips `.zshrc`/`.bashrc` loading
3. Keeps `PS1` and `PROMPT` env vars which will now be respected
Run `pnpm run dev:qa` and open the terminal to verify the prompt shows `$ `.
* Cleanup script changes for workspace 928e696e-5f40-4603-9227-798f0520e07f
* I've implemented the terminal theme integration. Here's a summary of the changes:
## Changes Made
**1. Created `frontend/src/utils/terminalTheme.ts`**
- `hslToHex()` function to convert HSL CSS variable format (`"210 40% 98%"`) to hex colors
- `getCssVariable()` helper to read CSS variables from the `.new-design` element
- `getTerminalTheme()` function that builds an xterm.js `ITheme` object from CSS variables:
- Uses `--console-background` and `--console-foreground` for main colors
- Uses `--console-success` and `--console-error` for green/red ANSI colors
- Provides appropriate ANSI color palettes for both light and dark modes
**2. Updated `frontend/src/components/ui-new/terminal/XTermInstance.tsx`**
- Imported `useTheme` hook and `getTerminalTheme` utility
- Replaced hardcoded Tokyo Night theme with dynamic `getTerminalTheme()` call
- Added effect to update terminal theme when app theme changes (`terminal.options.theme = getTerminalTheme()`)
The terminal will now:
- Initialize with colors matching the current theme
- Automatically update colors when switching between light and dark mode
- Use the console color variables defined in `index.css`
* Cleanup script changes for workspace 928e696e-5f40-4603-9227-798f0520e07f
* Now I understand the issue clearly. Here are the relevant variables:
| Variable | Light Mode | Dark Mode | Purpose |
|----------|------------|-----------|---------|
| `--_primary` | `0 0% 5%` (dark) | `240 4.8% 95.9%` (light) | Button background - **inverted** |
| `--_console-background` | `0 0% 100%` (white) | `0 0% 0%` (black) | Terminal background - **correct** |
| `--_console-foreground` | `0 0% 5%` (dark) | `210 40% 98%` (light) | Terminal text - **correct** |
| `--bg-primary` | `0 0% 100%` (white) | `0 0% 13%` (dark gray) | Page background |
The code was changed to use `--primary` but that's the wrong variable. For the terminal, you should use:
- `--console-background` / `--_console-background` for the background
- `--console-foreground` / `--_console-foreground` for the text
Or if you prefer to match other UI elements:
- `--bg-primary` for background
- `--text-high` for foreground text
Which approach would you prefer? The original `--console-*` variables were specifically designed for terminal styling, or we can use `--bg-primary`/`--text-high` to match the general UI background.
* Cleanup script changes for workspace 928e696e-5f40-4603-9227-798f0520e07f
* Done. The terminal will now use `--bg-primary` for the background and `--text-high` for the foreground text. This means:
- **Light mode**: White background (`0 0% 100%`) with dark text (`0 0% 5%`)
- **Dark mode**: Dark gray background (`0 0% 13%`) with light text (`0 0% 96%`)
* Done. The terminal will now use `--bg-secondary` for the background and `--text-high` for the foreground text. This means:
- **Light mode**: Light gray background (`0 0% 95%`) with dark text (`0 0% 5%`)
- **Dark mode**: Darker gray background (`0 0% 11%`) with light text (`0 0% 96%`)
* Let me know if you find any issues during testing or need adjustments!
* Cleanup script changes for workspace 928e696e-5f40-4603-9227-798f0520e07f
* terminal
* sidebar cleanup
* fix chat collapse
Amp-Thread-ID: https://ampcode.com/threads/T-019bc754-8db9-712a-a915-58d74d210cad
Co-authored-by: Amp <amp@ampcode.com>
* for the terminal on windows we need to default to powershell if avalibale for the pty (vibe-kanban 049dbf73)
only if powershell.exe cannot be resolved, we should use cmd.
* he colour theme used for the terminal ui isn't visible enough in light mode (vibe-kanban 5f50878a)
t I think we either don't override the ANSI colour mapping with our own, or it's not contrasted enough
* fmt
---------
Co-authored-by: Gabriel Gordon-Hall <ggordonhall@gmail.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: Solomon <abcpro11051@disroot.org>
308 lines
9.8 KiB
TypeScript
308 lines
9.8 KiB
TypeScript
import { useMemo, useEffect, useCallback } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { FileTreeContainer } from '@/components/ui-new/containers/FileTreeContainer';
|
|
import { ProcessListContainer } from '@/components/ui-new/containers/ProcessListContainer';
|
|
import { PreviewControlsContainer } from '@/components/ui-new/containers/PreviewControlsContainer';
|
|
import { GitPanelContainer } from '@/components/ui-new/containers/GitPanelContainer';
|
|
import { TerminalPanelContainer } from '@/components/ui-new/containers/TerminalPanelContainer';
|
|
import { ProjectSelectorContainer } from '@/components/ui-new/containers/ProjectSelectorContainer';
|
|
import { RecentReposListContainer } from '@/components/ui-new/containers/RecentReposListContainer';
|
|
import { BrowseRepoButtonContainer } from '@/components/ui-new/containers/BrowseRepoButtonContainer';
|
|
import { CreateRepoButtonContainer } from '@/components/ui-new/containers/CreateRepoButtonContainer';
|
|
import { useChangesView } from '@/contexts/ChangesViewContext';
|
|
import { useWorkspaceContext } from '@/contexts/WorkspaceContext';
|
|
import { useCreateMode } from '@/contexts/CreateModeContext';
|
|
import { useMultiRepoBranches } from '@/hooks/useRepoBranches';
|
|
import { useProjects } from '@/hooks/useProjects';
|
|
import { CreateProjectDialog } from '@/components/ui-new/dialogs/CreateProjectDialog';
|
|
import { SelectedReposList } from '@/components/ui-new/primitives/SelectedReposList';
|
|
import { WarningIcon } from '@phosphor-icons/react';
|
|
import type { Workspace, RepoWithTargetBranch } from 'shared/types';
|
|
import {
|
|
RIGHT_MAIN_PANEL_MODES,
|
|
PERSIST_KEYS,
|
|
type RightMainPanelMode,
|
|
useExpandedAll,
|
|
usePersistedExpanded,
|
|
useUiPreferencesStore,
|
|
PersistKey,
|
|
} from '@/stores/useUiPreferencesStore';
|
|
import { CollapsibleSectionHeader } from '../primitives/CollapsibleSectionHeader';
|
|
|
|
type SectionDef = {
|
|
title: string;
|
|
persistKey: PersistKey;
|
|
visible: boolean;
|
|
expanded: boolean;
|
|
content: React.ReactNode;
|
|
};
|
|
|
|
export interface RightSidebarProps {
|
|
isCreateMode: boolean;
|
|
rightMainPanelMode: RightMainPanelMode | null;
|
|
selectedWorkspace: Workspace | undefined;
|
|
repos: RepoWithTargetBranch[];
|
|
}
|
|
|
|
export function RightSidebar({
|
|
isCreateMode,
|
|
rightMainPanelMode,
|
|
selectedWorkspace,
|
|
repos,
|
|
}: RightSidebarProps) {
|
|
const { t } = useTranslation(['tasks', 'common']);
|
|
const { selectFile } = useChangesView();
|
|
const { diffs } = useWorkspaceContext();
|
|
const { setExpanded } = useExpandedAll();
|
|
const isTerminalVisible = useUiPreferencesStore((s) => s.isTerminalVisible);
|
|
|
|
const {
|
|
repos: createRepos,
|
|
addRepo,
|
|
removeRepo,
|
|
clearRepos,
|
|
targetBranches,
|
|
setTargetBranch,
|
|
selectedProjectId,
|
|
setSelectedProjectId,
|
|
} = useCreateMode();
|
|
const { projects } = useProjects();
|
|
|
|
const repoIds = useMemo(() => createRepos.map((r) => r.id), [createRepos]);
|
|
const { branchesByRepo } = useMultiRepoBranches(repoIds);
|
|
|
|
useEffect(() => {
|
|
if (!isCreateMode) return;
|
|
createRepos.forEach((repo) => {
|
|
const branches = branchesByRepo[repo.id];
|
|
if (branches && !targetBranches[repo.id]) {
|
|
const currentBranch = branches.find((b) => b.is_current);
|
|
if (currentBranch) {
|
|
setTargetBranch(repo.id, currentBranch.name);
|
|
}
|
|
}
|
|
});
|
|
}, [
|
|
isCreateMode,
|
|
createRepos,
|
|
branchesByRepo,
|
|
targetBranches,
|
|
setTargetBranch,
|
|
]);
|
|
|
|
const [changesExpanded] = usePersistedExpanded(
|
|
PERSIST_KEYS.changesSection,
|
|
true
|
|
);
|
|
const [processesExpanded] = usePersistedExpanded(
|
|
PERSIST_KEYS.processesSection,
|
|
true
|
|
);
|
|
const [devServerExpanded] = usePersistedExpanded(
|
|
PERSIST_KEYS.devServerSection,
|
|
true
|
|
);
|
|
const [gitExpanded] = usePersistedExpanded(
|
|
PERSIST_KEYS.gitPanelRepositories,
|
|
true
|
|
);
|
|
const [terminalExpanded] = usePersistedExpanded(
|
|
PERSIST_KEYS.terminalSection,
|
|
true
|
|
);
|
|
|
|
const selectedProject = projects.find((p) => p.id === selectedProjectId);
|
|
const registeredRepoPaths = useMemo(
|
|
() => createRepos.map((r) => r.path),
|
|
[createRepos]
|
|
);
|
|
|
|
const handleCreateProject = useCallback(async () => {
|
|
const result = await CreateProjectDialog.show({});
|
|
if (result.status === 'saved') {
|
|
setSelectedProjectId(result.project.id);
|
|
clearRepos();
|
|
}
|
|
}, [setSelectedProjectId, clearRepos]);
|
|
|
|
const hasNoRepos = createRepos.length === 0;
|
|
|
|
const hasUpperContent =
|
|
rightMainPanelMode === RIGHT_MAIN_PANEL_MODES.CHANGES ||
|
|
rightMainPanelMode === RIGHT_MAIN_PANEL_MODES.LOGS ||
|
|
rightMainPanelMode === RIGHT_MAIN_PANEL_MODES.PREVIEW;
|
|
|
|
const getUpperExpanded = () => {
|
|
if (rightMainPanelMode === RIGHT_MAIN_PANEL_MODES.CHANGES)
|
|
return changesExpanded;
|
|
if (rightMainPanelMode === RIGHT_MAIN_PANEL_MODES.LOGS)
|
|
return processesExpanded;
|
|
if (rightMainPanelMode === RIGHT_MAIN_PANEL_MODES.PREVIEW)
|
|
return devServerExpanded;
|
|
return false;
|
|
};
|
|
|
|
const upperExpanded = getUpperExpanded();
|
|
|
|
const sections: SectionDef[] = isCreateMode
|
|
? [
|
|
{
|
|
title: t('common:sections.project'),
|
|
persistKey: PERSIST_KEYS.gitPanelProject,
|
|
visible: true,
|
|
expanded: true,
|
|
content: (
|
|
<div className="p-base">
|
|
<ProjectSelectorContainer
|
|
projects={projects}
|
|
selectedProjectId={selectedProjectId}
|
|
selectedProjectName={selectedProject?.name}
|
|
onProjectSelect={(p) => setSelectedProjectId(p.id)}
|
|
onCreateProject={handleCreateProject}
|
|
/>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
title: t('common:sections.repositories'),
|
|
persistKey: PERSIST_KEYS.gitPanelRepositories,
|
|
visible: true,
|
|
expanded: true,
|
|
content: hasNoRepos ? (
|
|
<div className="p-base">
|
|
<div className="flex items-center gap-2 p-base rounded bg-warning/10 border border-warning/20">
|
|
<WarningIcon className="h-4 w-4 text-warning shrink-0" />
|
|
<p className="text-sm text-warning">
|
|
{t('gitPanel.create.warnings.noReposSelected')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<SelectedReposList
|
|
repos={createRepos}
|
|
onRemove={removeRepo}
|
|
branchesByRepo={branchesByRepo}
|
|
selectedBranches={targetBranches}
|
|
onBranchChange={setTargetBranch}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
title: t('common:sections.addRepositories'),
|
|
persistKey: PERSIST_KEYS.gitPanelAddRepositories,
|
|
visible: true,
|
|
expanded: true,
|
|
content: (
|
|
<div className="flex flex-col gap-base p-base">
|
|
<p className="text-xs text-low font-medium">
|
|
{t('common:sections.recent')}
|
|
</p>
|
|
<RecentReposListContainer
|
|
registeredRepoPaths={registeredRepoPaths}
|
|
onRepoRegistered={addRepo}
|
|
/>
|
|
<p className="text-xs text-low font-medium">
|
|
{t('common:sections.other')}
|
|
</p>
|
|
<BrowseRepoButtonContainer onRepoRegistered={addRepo} />
|
|
<CreateRepoButtonContainer onRepoCreated={addRepo} />
|
|
</div>
|
|
),
|
|
},
|
|
]
|
|
: buildWorkspaceSections();
|
|
|
|
function buildWorkspaceSections(): SectionDef[] {
|
|
const result: SectionDef[] = [
|
|
{
|
|
title: 'Git',
|
|
persistKey: PERSIST_KEYS.gitPanelRepositories,
|
|
visible: true,
|
|
expanded: gitExpanded,
|
|
content: (
|
|
<GitPanelContainer
|
|
selectedWorkspace={selectedWorkspace}
|
|
repos={repos}
|
|
diffs={diffs}
|
|
/>
|
|
),
|
|
},
|
|
{
|
|
title: 'Terminal',
|
|
persistKey: PERSIST_KEYS.terminalSection,
|
|
visible: isTerminalVisible,
|
|
expanded: terminalExpanded,
|
|
content: <TerminalPanelContainer />,
|
|
},
|
|
];
|
|
|
|
switch (rightMainPanelMode) {
|
|
case RIGHT_MAIN_PANEL_MODES.CHANGES:
|
|
result.unshift({
|
|
title: 'Changes',
|
|
persistKey: PERSIST_KEYS.changesSection,
|
|
visible: hasUpperContent,
|
|
expanded: upperExpanded,
|
|
content: (
|
|
<FileTreeContainer
|
|
key={selectedWorkspace?.id}
|
|
workspaceId={selectedWorkspace?.id}
|
|
diffs={diffs}
|
|
onSelectFile={(path) => {
|
|
selectFile(path);
|
|
setExpanded(`diff:${path}`, true);
|
|
}}
|
|
/>
|
|
),
|
|
});
|
|
break;
|
|
case RIGHT_MAIN_PANEL_MODES.LOGS:
|
|
result.unshift({
|
|
title: 'Logs',
|
|
persistKey: PERSIST_KEYS.rightPanelprocesses,
|
|
visible: hasUpperContent,
|
|
expanded: upperExpanded,
|
|
content: <ProcessListContainer />,
|
|
});
|
|
break;
|
|
case RIGHT_MAIN_PANEL_MODES.PREVIEW:
|
|
result.unshift({
|
|
title: 'Preview',
|
|
persistKey: PERSIST_KEYS.rightPanelPreview,
|
|
visible: hasUpperContent,
|
|
expanded: upperExpanded,
|
|
content: (
|
|
<PreviewControlsContainer attemptId={selectedWorkspace?.id} />
|
|
),
|
|
});
|
|
break;
|
|
case null:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return (
|
|
<div className="h-full border-l bg-secondary overflow-y-auto">
|
|
<div className="divide-y border-b">
|
|
{sections.map((section) => (
|
|
<div
|
|
key={section.persistKey}
|
|
className="max-h-[max(50vh,400px)] flex flex-col overflow-hidden"
|
|
>
|
|
<CollapsibleSectionHeader
|
|
title={section.title}
|
|
persistKey={section.persistKey}
|
|
>
|
|
<div className="flex flex-1 border-t min-h-[200px]">
|
|
{section.content}
|
|
</div>
|
|
</CollapsibleSectionHeader>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|