Polish tmux session modal (#1412)
Some checks failed
build-packages / ${{ needs.dedupe.outputs.skip_heavy_ci == 'true' && 'deduped build-linux-x64' || 'build-linux-x64' }} (push) Has been cancelled
build-packages / ${{ needs.dedupe.outputs.skip_heavy_ci == 'true' && 'deduped build-linux-arm64' || 'build-linux-arm64' }} (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / dedupe push run (push) Has been cancelled
build-packages / dedupe result (push) Has been cancelled
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled

This commit is contained in:
陈大猫
2026-06-11 18:07:11 +08:00
committed by GitHub
parent 3b525300e0
commit 1a3560a19f
3 changed files with 61 additions and 12 deletions

View File

@@ -19,14 +19,25 @@ const DEFAULT_HEIGHT = 120;
const MIN_HEIGHT = 80;
const MAX_HEIGHT = 520;
function clampHeight(height: number): number {
return Math.max(MIN_HEIGHT, Math.min(MAX_HEIGHT, height));
function clampHeight(height: number, minHeight = MIN_HEIGHT, maxHeight = MAX_HEIGHT): number {
return Math.max(minHeight, Math.min(maxHeight, height));
}
function readStoredHeight(): number {
function readStoredHeight({
defaultHeight,
minHeight,
maxHeight,
persistHeight,
}: {
defaultHeight: number;
minHeight: number;
maxHeight: number;
persistHeight: boolean;
}): number {
if (!persistHeight) return clampHeight(defaultHeight, minHeight, maxHeight);
const stored = localStorageAdapter.readNumber(STORAGE_KEY_SNIPPET_SCRIPT_EDITOR_HEIGHT);
if (stored === null) return DEFAULT_HEIGHT;
return clampHeight(stored);
if (stored === null) return clampHeight(defaultHeight, minHeight, maxHeight);
return clampHeight(stored, minHeight, maxHeight);
}
const editorFillClass =
@@ -39,6 +50,10 @@ export interface SnippetScriptEditorProps {
id?: string;
/** Shown on the same row as the expand button (e.g. "Script *"). */
label?: string;
defaultHeight?: number;
minHeight?: number;
maxHeight?: number;
persistHeight?: boolean;
}
export const SnippetScriptEditor: React.FC<SnippetScriptEditorProps> = ({
@@ -47,9 +62,18 @@ export const SnippetScriptEditor: React.FC<SnippetScriptEditorProps> = ({
placeholder,
id,
label,
defaultHeight = DEFAULT_HEIGHT,
minHeight = MIN_HEIGHT,
maxHeight = MAX_HEIGHT,
persistHeight = true,
}) => {
const { t } = useI18n();
const [height, setHeight] = useState(readStoredHeight);
const [height, setHeight] = useState(() => readStoredHeight({
defaultHeight,
minHeight,
maxHeight,
persistHeight,
}));
const [modalOpen, setModalOpen] = useState(false);
const dragRef = useRef<{ startY: number; startHeight: number } | null>(null);
const heightRef = useRef(height);
@@ -66,10 +90,10 @@ export const SnippetScriptEditor: React.FC<SnippetScriptEditorProps> = ({
const onMove = (e: MouseEvent) => {
if (!dragRef.current) return;
const delta = e.clientY - dragRef.current.startY;
setHeight(clampHeight(dragRef.current.startHeight + delta));
setHeight(clampHeight(dragRef.current.startHeight + delta, minHeight, maxHeight));
};
const onUp = () => {
if (dragRef.current) {
if (dragRef.current && persistHeight) {
localStorageAdapter.writeNumber(
STORAGE_KEY_SNIPPET_SCRIPT_EDITOR_HEIGHT,
heightRef.current,
@@ -87,7 +111,7 @@ export const SnippetScriptEditor: React.FC<SnippetScriptEditorProps> = ({
document.body.style.cursor = '';
document.body.style.userSelect = '';
};
}, []);
}, [maxHeight, minHeight, persistHeight]);
return (
<>

View File

@@ -0,0 +1,22 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
const modalSource = readFileSync(new URL("./TmuxNewSessionModal.tsx", import.meta.url), "utf8");
const editorSource = readFileSync(new URL("../snippets/SnippetScriptEditor.tsx", import.meta.url), "utf8");
test("tmux new session modal is only modestly wider than the default dialog", () => {
assert.match(modalSource, /w-\[min\(92vw,560px\)\]/);
assert.match(modalSource, /max-w-none/);
assert.doesNotMatch(modalSource, /bg-background\/95/);
assert.doesNotMatch(modalSource, /bg-muted\/10/);
assert.doesNotMatch(modalSource, /border-b border-border\/60/);
assert.doesNotMatch(modalSource, /border-t border-border\/60/);
});
test("tmux command editor does not inherit the global snippet editor height", () => {
assert.match(modalSource, /defaultHeight=\{150\}/);
assert.match(modalSource, /maxHeight=\{260\}/);
assert.match(modalSource, /persistHeight=\{false\}/);
assert.match(editorSource, /persistHeight\?: boolean/);
});

View File

@@ -100,10 +100,10 @@ export const TmuxNewSessionModal = memo(function TmuxNewSessionModal({
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
className="flex max-h-[min(90vh,720px)] max-w-md flex-col overflow-hidden"
className="flex max-h-[min(88vh,680px)] w-[min(92vw,560px)] max-w-none flex-col overflow-hidden"
onKeyDown={handleKeyDown}
>
<DialogHeader className="shrink-0">
<DialogHeader className="shrink-0 pr-8">
<DialogTitle>{t('systemManager.tmux.newSessionTitle')}</DialogTitle>
<DialogDescription>{t('systemManager.tmux.newSessionDesc')}</DialogDescription>
</DialogHeader>
@@ -146,6 +146,9 @@ export const TmuxNewSessionModal = memo(function TmuxNewSessionModal({
value={command}
onChange={handleCommandChange}
placeholder={t('systemManager.tmux.newSessionCommandPlaceholder')}
defaultHeight={150}
maxHeight={260}
persistHeight={false}
/>
<p className="text-[11px] text-muted-foreground">
{t('systemManager.tmux.newSessionCommandHint')}
@@ -158,7 +161,7 @@ export const TmuxNewSessionModal = memo(function TmuxNewSessionModal({
selectedId={selectedSnippetId}
onSelect={handleSnippetSelect}
showTitle={false}
className="min-h-[280px]"
className="h-[240px] min-h-[240px]"
/>
{selectedSnippet && (
<p className="text-[11px] text-muted-foreground">