mirror of
https://github.com/binaricat/Netcatty.git
synced 2026-10-30 02:21:47 +00:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2af4fd0511 | ||
|
|
8b722a996e |
No files matched your search
@@ -602,6 +602,10 @@ export const enAiMessages: Messages = {
|
||||
'terminal.layer.movePanelLeft': 'Move panel to left',
|
||||
'terminal.layer.movePanelRight': 'Move panel to right',
|
||||
'terminal.layer.movePanelBottom': 'Move panel to bottom',
|
||||
'terminal.layer.choosePanelPosition': 'Change position',
|
||||
'terminal.layer.dockLeft': 'Left',
|
||||
'terminal.layer.dockRight': 'Right',
|
||||
'terminal.layer.dockBottom': 'Bottom',
|
||||
'terminal.layer.closePanel': 'Close panel',
|
||||
'terminal.layer.closePane': 'Close split',
|
||||
'terminal.layer.saveLayoutAsDefault': 'Save layout as default',
|
||||
|
||||
@@ -593,6 +593,10 @@ export const esAiMessages: Messages = {
|
||||
'terminal.layer.movePanelLeft': 'Mover panel a la izquierda',
|
||||
'terminal.layer.movePanelRight': 'Mover panel a la derecha',
|
||||
'terminal.layer.movePanelBottom': 'Mover panel a la parte inferior',
|
||||
'terminal.layer.choosePanelPosition': 'Cambiar posición',
|
||||
'terminal.layer.dockLeft': 'Izquierda',
|
||||
'terminal.layer.dockRight': 'Derecha',
|
||||
'terminal.layer.dockBottom': 'Abajo',
|
||||
'terminal.layer.closePanel': 'Cerrar panel',
|
||||
'terminal.layer.closePane': 'Cerrar división',
|
||||
'terminal.layer.resizeSplit': 'Redimensionar división',
|
||||
|
||||
@@ -561,6 +561,10 @@ export const ruAiMessages: Messages = {
|
||||
'terminal.layer.movePanelLeft': 'Переместить панель влево',
|
||||
'terminal.layer.movePanelRight': 'Переместить панель вправо',
|
||||
'terminal.layer.movePanelBottom': 'Переместить панель вниз',
|
||||
'terminal.layer.choosePanelPosition': 'Изменить положение',
|
||||
'terminal.layer.dockLeft': 'Слева',
|
||||
'terminal.layer.dockRight': 'Справа',
|
||||
'terminal.layer.dockBottom': 'Снизу',
|
||||
'terminal.layer.closePanel': 'Закрыть панель',
|
||||
'terminal.layer.closePane': 'Закрыть область',
|
||||
'terminal.layer.resizeSplit': 'Изменить размер области',
|
||||
|
||||
@@ -602,6 +602,10 @@ export const zhCNAiMessages: Messages = {
|
||||
'terminal.layer.movePanelLeft': '面板移至左侧',
|
||||
'terminal.layer.movePanelRight': '面板移至右侧',
|
||||
'terminal.layer.movePanelBottom': '面板移至底部',
|
||||
'terminal.layer.choosePanelPosition': '改变位置',
|
||||
'terminal.layer.dockLeft': '左侧',
|
||||
'terminal.layer.dockRight': '右侧',
|
||||
'terminal.layer.dockBottom': '底部',
|
||||
'terminal.layer.closePanel': '关闭面板',
|
||||
'terminal.layer.closePane': '关闭分屏',
|
||||
'terminal.layer.saveLayoutAsDefault': '将布局保存为默认',
|
||||
|
||||
@@ -592,6 +592,10 @@ export const zhTWAiMessages: Messages = {
|
||||
'terminal.layer.movePanelLeft': '面板移至左側',
|
||||
'terminal.layer.movePanelRight': '面板移至右側',
|
||||
'terminal.layer.movePanelBottom': '面板移至底部',
|
||||
'terminal.layer.choosePanelPosition': '變更位置',
|
||||
'terminal.layer.dockLeft': '左側',
|
||||
'terminal.layer.dockRight': '右側',
|
||||
'terminal.layer.dockBottom': '底部',
|
||||
'terminal.layer.closePanel': '關閉面板',
|
||||
'terminal.layer.closePane': '關閉分割',
|
||||
'terminal.layer.saveLayoutAsDefault': '將佈局儲存為預設',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { Activity, FolderTree, History, Maximize2, MessageSquare, Minimize2, NotebookText, Palette, PanelBottom, PanelLeft, PanelRight, Play, Save, SplitSquareHorizontal, SplitSquareVertical, X } from 'lucide-react';
|
||||
import { Activity, FolderTree, History, Maximize2, MessageSquare, Minimize2, NotebookText, Palette, PanelsTopLeft, Play, Save, SplitSquareHorizontal, SplitSquareVertical, X } from 'lucide-react';
|
||||
import {
|
||||
buildSidePanelChromeThemeFromTerminalTheme,
|
||||
buildTerminalSidePanelCssVars,
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { injectTerminalLayerChromeSurfaceVars } from '../../infrastructure/theme/terminalAppearanceVars';
|
||||
import React, { memo, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||
|
||||
import { useActiveTabId } from '../../application/state/activeTabStore';
|
||||
import {
|
||||
@@ -41,16 +42,17 @@ import {
|
||||
} from '../ui/toolbar-item-layout';
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
|
||||
import { Popover, PopoverClose, PopoverContent, PopoverTrigger } from '../ui/popover';
|
||||
import { Select, SelectContent, SelectItem } from '../ui/select';
|
||||
import type { SidePanelTab } from './TerminalLayerSupport';
|
||||
import {
|
||||
MAX_SIDE_PANEL_PANES,
|
||||
canSplitSidePanelPaneAtSize,
|
||||
collectSidePanelPanes,
|
||||
cycleSidePanelDockPosition,
|
||||
getFocusedSidePanelPane,
|
||||
getSidePanelNodeMinimumPixels,
|
||||
getSidePanelSplitResizeBounds,
|
||||
SIDE_PANEL_SPLIT_DIVIDER_PIXELS,
|
||||
SIDE_PANEL_DOCK_POSITIONS,
|
||||
type SidePanelDockPosition,
|
||||
type SidePanelLayout,
|
||||
type SidePanelLayoutNode,
|
||||
@@ -491,6 +493,46 @@ function SidePanelSplitMenu({
|
||||
);
|
||||
}
|
||||
|
||||
function SidePanelDockSelect({
|
||||
position,
|
||||
onSelect,
|
||||
t,
|
||||
buttonColor,
|
||||
}: {
|
||||
position: SidePanelDockPosition;
|
||||
onSelect: (position: SidePanelDockPosition) => void;
|
||||
t: (key: string) => string;
|
||||
buttonColor: string;
|
||||
}) {
|
||||
const labels = {
|
||||
left: 'terminal.layer.dockLeft',
|
||||
right: 'terminal.layer.dockRight',
|
||||
bottom: 'terminal.layer.dockBottom',
|
||||
};
|
||||
|
||||
return (
|
||||
<Select value={position} onValueChange={(value) => onSelect(value as SidePanelDockPosition)}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<SelectPrimitive.Trigger
|
||||
className="grid h-7 w-7 shrink-0 place-items-center rounded-md bg-transparent p-0 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
||||
style={{ color: buttonColor }}
|
||||
aria-label={t('terminal.layer.choosePanelPosition')}
|
||||
>
|
||||
<PanelsTopLeft size={15} />
|
||||
</SelectPrimitive.Trigger>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">{t('terminal.layer.choosePanelPosition')}</TooltipContent>
|
||||
</Tooltip>
|
||||
<SelectContent align="end" side="bottom" className="min-w-[7rem]" hideScrollButtons>
|
||||
{SIDE_PANEL_DOCK_POSITIONS.map((dock) => (
|
||||
<SelectItem key={dock} value={dock}>{t(labels[dock])}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
export function getTerminalSidePanelShellWidth({
|
||||
activeSidePanelTab,
|
||||
forceHideAiShell,
|
||||
@@ -1393,28 +1435,12 @@ function TerminalLayerSidePanelInner({ ctx }: { ctx: SidePanelContext }) {
|
||||
{t('terminal.layer.saveLayoutAsDefault')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Btn
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 rounded-md p-0 hover:bg-transparent"
|
||||
style={{ color: sidePanelTheme.mutedFg }}
|
||||
onClick={() => setSidePanelPosition((p: SidePanelDockPosition) => cycleSidePanelDockPosition(p))}
|
||||
>
|
||||
{sidePanelPosition === 'left' && <PanelRight size={15} />}
|
||||
{sidePanelPosition === 'right' && <PanelBottom size={15} />}
|
||||
{sidePanelPosition === 'bottom' && <PanelLeft size={15} />}
|
||||
</Btn>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{sidePanelPosition === 'left'
|
||||
? t('terminal.layer.movePanelRight')
|
||||
: sidePanelPosition === 'right'
|
||||
? t('terminal.layer.movePanelBottom')
|
||||
: t('terminal.layer.movePanelLeft')}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<SidePanelDockSelect
|
||||
position={sidePanelPosition}
|
||||
onSelect={setSidePanelPosition}
|
||||
t={t}
|
||||
buttonColor={sidePanelTheme.mutedFg}
|
||||
/>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Btn
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
closeSidePanelPane,
|
||||
collectSidePanelPanes,
|
||||
createSidePanelLayout,
|
||||
cycleSidePanelDockPosition,
|
||||
focusSidePanelPane,
|
||||
getSidePanelNodeMinimumPixels,
|
||||
getSidePanelSplitResizeBounds,
|
||||
@@ -169,12 +168,6 @@ test('the pane limit rejects additional splits without changing the layout', ()
|
||||
assert.equal(duplicateAttempt.focusedPaneId, 'pane-0');
|
||||
});
|
||||
|
||||
test('dock position cycles through left, right and bottom', () => {
|
||||
assert.equal(cycleSidePanelDockPosition('left'), 'right');
|
||||
assert.equal(cycleSidePanelDockPosition('right'), 'bottom');
|
||||
assert.equal(cycleSidePanelDockPosition('bottom'), 'left');
|
||||
});
|
||||
|
||||
test('dock position validator accepts only known positions', () => {
|
||||
assert.equal(isSidePanelDockPosition('bottom'), true);
|
||||
assert.equal(isSidePanelDockPosition('top'), false);
|
||||
|
||||
@@ -262,10 +262,3 @@ export function isSidePanelDockPosition(value: unknown): value is SidePanelDockP
|
||||
return typeof value === 'string'
|
||||
&& (SIDE_PANEL_DOCK_POSITIONS as readonly string[]).includes(value);
|
||||
}
|
||||
|
||||
export function cycleSidePanelDockPosition(
|
||||
position: SidePanelDockPosition,
|
||||
): SidePanelDockPosition {
|
||||
const index = SIDE_PANEL_DOCK_POSITIONS.indexOf(position);
|
||||
return SIDE_PANEL_DOCK_POSITIONS[(index + 1) % SIDE_PANEL_DOCK_POSITIONS.length];
|
||||
}
|
||||
Reference in new issue
Block a user