mirror of
https://github.com/esengine/DeepSeek-Reasonix.git
synced 2026-11-07 10:45:59 -10:00
Merge pull request #14335 from lancy69/fix/14324-remove-workbench-wide
fix(studio): remove redundant workbench widening mode
This commit is contained in:
17 files changed
+72
-160
No files matched your search
@@ -112,13 +112,8 @@ async function main() {
|
||||
|
||||
const js = (src) => win.webContents.executeJavaScript(src);
|
||||
const reserved = () => js(`(() => { const e = document.querySelector('.bview'); if (!e) return null; const b = e.getBoundingClientRect(); return { x: b.left, y: b.top, width: b.width, height: b.height }; })()`);
|
||||
// The browser tab beside the conversation is a switch for that view itself, so
|
||||
// the page is widened with the browser's own wide control, which is how the
|
||||
// person makes it the whole pane.
|
||||
await until("the wide control on the browser panel", () => js(`!!document.querySelector('[data-action="browser.wide"]')`));
|
||||
await js(`document.querySelector('[data-action="browser.wide"]').click()`);
|
||||
// A page that is put away is as wide as an agent's viewport, so wide means drawn
|
||||
// and wider than it was beside the conversation.
|
||||
await until("the full-view control on the browser panel", () => js(`!!document.querySelector('[data-action="browser.full"]')`));
|
||||
await js(`document.querySelector('[data-action="browser.full"]').click()`);
|
||||
await until("the page drawn full width", async () => (!beneath() && width() > aside.width ? guest.getBounds() : null));
|
||||
// The panel widens over a moment; the rectangle is read once it has stopped.
|
||||
const same = still;
|
||||
@@ -149,7 +144,7 @@ async function main() {
|
||||
process.stdout.write(` (no screenshot: ${err.message})\n`);
|
||||
}
|
||||
|
||||
await js(`document.querySelector('[data-action="browser.wide"]').click()`);
|
||||
await js(`document.querySelector('[data-action="browser.full"]').click()`);
|
||||
const back = await until("the page beside the conversation again", async () => (!beneath() && width() < placed.width ? guest.getBounds() : null));
|
||||
// The column settles a pixel or two after the first measurement, so beside means
|
||||
// the same place and size to within a few pixels.
|
||||
|
||||
@@ -36,33 +36,32 @@ async function main() {
|
||||
await client.request("POST", `${base}/submit`, { input: "open the page" });
|
||||
const guest = await until("a page view", async () => guests()[0], 60000);
|
||||
await until("the page beside the conversation", async () => (guest.getBounds().x >= 0 ? true : null), 30000);
|
||||
await until("the wide control", () => js(`!!document.querySelector('[data-action="browser.wide"]')`));
|
||||
await until("the full-view control", () => js(`!!document.querySelector('[data-action="browser.full"]')`));
|
||||
|
||||
const panelRect = () => js(`(() => { const e = document.querySelector('.sidepanel'); if (!e) return null; const b = e.getBoundingClientRect(); return { x: b.left, y: b.top, width: b.width, height: b.height }; })()`);
|
||||
const covers = (view, rect) => view.x < rect.x + rect.width && view.x + view.width > rect.x && view.y < rect.y + rect.height && view.y + view.height > rect.y;
|
||||
|
||||
for (const [width, height, wide] of [[1440, 900, false], [1440, 900, true], [1100, 760, false], [1100, 760, true], [900, 700, true], [700, 700, true], [1440, 900, "panel-first"], [1100, 760, "panel-first"]]) {
|
||||
for (const [width, height, full] of [[1440, 900, false], [1440, 900, true], [1100, 760, false], [1100, 760, true], [900, 700, true], [700, 700, true], [1440, 900, "panel-first"], [1100, 760, "panel-first"]]) {
|
||||
win.setContentSize(width, height);
|
||||
await wait(900);
|
||||
const label = `${width}px ${wide === "panel-first" ? "side question open, then the browser widened" : wide ? "widened" : "beside the conversation"}`;
|
||||
if (wide === true && (await js(`document.querySelector('.pane')?.getAttribute('data-browser-size')`)) !== "wide") {
|
||||
await js(`document.querySelector('[data-action="browser.wide"]')?.click()`);
|
||||
const label = `${width}px ${full === "panel-first" ? "side question open, then full view" : full ? "full view" : "beside the conversation"}`;
|
||||
if (full === true && (await js(`document.querySelector('.pane')?.getAttribute('data-browser-size')`)) !== "full") {
|
||||
await js(`document.querySelector('[data-action="browser.full"]')?.click()`);
|
||||
await wait(1200);
|
||||
}
|
||||
if (wide === false && (await js(`document.querySelector('.pane')?.getAttribute('data-browser-size')`)) === "wide") {
|
||||
await js(`document.querySelector('[data-action="browser.wide"]')?.click()`);
|
||||
if (full === false && (await js(`document.querySelector('.pane')?.getAttribute('data-browser-size')`)) === "full") {
|
||||
await js(`document.querySelector('[data-action="browser.full"]')?.click()`);
|
||||
await wait(1200);
|
||||
}
|
||||
if (wide === "panel-first") {
|
||||
// Back beside the conversation first, then the question, then the widening.
|
||||
if ((await js(`document.querySelector('.pane')?.getAttribute('data-browser-size')`)) === "wide") {
|
||||
await js(`document.querySelector('[data-action="browser.wide"]')?.click()`);
|
||||
if (full === "panel-first") {
|
||||
if ((await js(`document.querySelector('.pane')?.getAttribute('data-browser-size')`)) === "full") {
|
||||
await js(`document.querySelector('[data-action="browser.full"]')?.click()`);
|
||||
await wait(1000);
|
||||
}
|
||||
await js(`document.querySelector('[data-action="side.toggle"]').click()`);
|
||||
await until("the side panel", async () => (await panelRect()) || null, 10000);
|
||||
await wait(800);
|
||||
await js(`document.querySelector('[data-action="browser.wide"]')?.click()`);
|
||||
await js(`document.querySelector('[data-action="browser.full"]')?.click()`);
|
||||
await wait(1500);
|
||||
} else {
|
||||
await js(`document.querySelector('[data-action="side.toggle"]').click()`);
|
||||
|
||||
@@ -41,7 +41,6 @@ try {
|
||||
['.chrome [data-action="settings.section"][data-value="tools"]', "沙盒与运行边界"],
|
||||
['[data-action="pane.view"][data-value="analysis"]', "打开运行分析"],
|
||||
['[data-action="pane.view"][data-value="browser"]', "关闭工作台"],
|
||||
['[data-action="browser.wide"]', "加宽工作台"],
|
||||
['[data-action="browser.full"]', "全屏工作台"],
|
||||
['[data-action="workbench.files"]', "展开文件树"],
|
||||
['[data-action="workbench.new"]', "新建标签"],
|
||||
@@ -56,13 +55,13 @@ try {
|
||||
assert.equal(await button.getAttribute("title"), null);
|
||||
const box = await tip.boundingBox();
|
||||
assert.ok(box.x >= 0 && box.y >= 0 && box.x + box.width <= 1440 && box.y + box.height <= 960);
|
||||
if (selector.includes("browser.wide")) await page.screenshot({ path: "/tmp/reasonix-tooltip-workbench.png" });
|
||||
if (selector.includes("browser.full")) await page.screenshot({ path: "/tmp/reasonix-tooltip-workbench.png" });
|
||||
if (selector.includes("reply.quote")) await page.screenshot({ path: "/tmp/reasonix-tooltip-reply.png" });
|
||||
await page.mouse.move(1, 1);
|
||||
await tip.waitFor({ state: "detached" });
|
||||
}
|
||||
await page.locator('[data-action="browser.wide"]').focus();
|
||||
await page.getByRole("tooltip").filter({ hasText: "加宽工作台" }).waitFor();
|
||||
await page.locator('[data-action="browser.full"]').focus();
|
||||
await page.getByRole("tooltip").filter({ hasText: "全屏工作台" }).waitFor();
|
||||
await page.keyboard.press("Escape");
|
||||
assert.equal(await page.locator(".icon-tooltip").count(), 0);
|
||||
await page.getByRole("button", { name: "下一页", exact: true }).click();
|
||||
|
||||
@@ -128,7 +128,6 @@ export const ACTIONS: UIAction[] = [
|
||||
{ id: "session.batch-delete", kind: "destructive", target: "none", proof: "authority-effect" },
|
||||
{ id: "chrome.settings", kind: "navigation", target: "none", proof: "interaction" },
|
||||
{ id: "browser.open", kind: "view", target: "none", proof: "interaction" },
|
||||
{ id: "browser.wide", kind: "view", target: "none", proof: "interaction" },
|
||||
{ id: "browser.full", kind: "view", target: "none", proof: "interaction" },
|
||||
{ id: "browser.back", kind: "navigation", target: "none", proof: "interaction" },
|
||||
{ id: "browser.reload", kind: "repeatable", target: "none", proof: "interaction" },
|
||||
|
||||
@@ -949,7 +949,6 @@ export const EN: Record<string, string> = {
|
||||
"搜索会话": "Search conversations",
|
||||
"在提问之间移动": "Move between your questions",
|
||||
"工作台全屏": "Workbench full screen",
|
||||
"工作台加宽": "Widen the workbench",
|
||||
"缩小 / 放大 / 复原界面": "Make the interface smaller / larger / its default size",
|
||||
"发送(发送键设为修饰键 + Enter 时)": "Send when the send key is set to the modifier plus Enter",
|
||||
"发送,或用 Shift 换行": "Send, or a new line with Shift",
|
||||
|
||||
@@ -8,8 +8,6 @@ export const EN_DOCUMENT: Record<string, string> = {
|
||||
"已请求系统应用打开": "Requested opening in the system application",
|
||||
"已请求显示文件位置": "Requested showing the file location",
|
||||
"已复制路径": "Path copied",
|
||||
"加宽工作台": "Widen workbench",
|
||||
"恢复分栏": "Restore split view",
|
||||
"全屏工作台": "Expand workbench",
|
||||
"退出工作台全屏": "Exit expanded workbench",
|
||||
"展开文件树": "Show file tree",
|
||||
|
||||
@@ -292,18 +292,13 @@ and selected states, while long titles truncate within the remaining width.
|
||||
the global icon navigation is not visible. It follows the window's existing
|
||||
navigation visibility state, including collapsed-only mode and narrow-window
|
||||
folds, so both columns never offer the same footer actions together.
|
||||
- Browser wide mode changes the existing pane's grid tracks, keeping the
|
||||
workbench, pages, transcript and composer in place. The conversation takes
|
||||
34% of the pane up to 380px; the browser takes the remaining width.
|
||||
- The saved split width is untouched, and its divider is absent while wide so dragging
|
||||
cannot silently overwrite a width that is not being drawn.
|
||||
- The compact composer's metrics row wraps inside the composer. Absolute
|
||||
positioning below the box lets balance and task controls cross the browser seam.
|
||||
- Under the side fold, wide mode stacks the browser above the conversation and
|
||||
composer. Two columns at phone width cannot keep either surface usable.
|
||||
- The workbench toggle and Ctrl/Cmd+Shift+B share `browser.wide`. Escape returns
|
||||
to split before the window can close the browser or stop the run. Closing the
|
||||
dock or leaving the conversation resets wide mode; mounting never expands it.
|
||||
- The docked workbench uses the divider's remembered width. Pointer dragging and
|
||||
keyboard resizing share that width; there is no intermediate width preset.
|
||||
- Full-workbench view preserves the divider width and keeps the conversation and
|
||||
composer mounted. Its button and Ctrl/Cmd+Shift+F share `browser.full`.
|
||||
- Escape restores split view before the window can close the workbench or stop
|
||||
the run. Closing the dock resets full view; a pending decision returns to split
|
||||
so the person can answer.
|
||||
|
||||
- Column widths change on `.app`, so the tween belongs there and `.cols` only
|
||||
reads the result into a track. The tween must be cut during a drag: a .34s
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.pane[data-browser-size="full"]:has(> .pbody[data-dock]) > .findhost { grid-area: 1 / 1 / 2 / 3 }
|
||||
.pane[data-browser-size="full"]:has(> .pbody[data-dock]) > .scroll[data-pane="browser"] { grid-area: 2 / 1 / 4 / 3; border-left: 0 }
|
||||
.pane[data-browser-size="full"]:has(> .pbody[data-dock]) > :is(.pbody, .compose, .gutter-r) { display: none }
|
||||
.workbench-tabs > [data-action="browser.full"] { margin-left: auto; }
|
||||
@@ -1,27 +0,0 @@
|
||||
.pane[data-browser-size="wide"]:has(> .pbody[data-dock]) {
|
||||
grid-template-columns: minmax(0, min(34%, 380px)) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.pane[data-browser-size="wide"] .studio-meterrail {
|
||||
position: static;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 8px;
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
[data-fold~="side"] .app[data-browser="on"] .pane[data-browser-size="wide"]:has(> .pbody[data-dock]) {
|
||||
grid-template: "nav" auto "find" auto "side" minmax(0, 1.2fr) "body" minmax(0, 1fr) "compose" auto / minmax(0, 1fr);
|
||||
}
|
||||
|
||||
[data-fold~="side"] .app[data-browser="on"] .pane[data-browser-size="wide"]:has(> .pbody[data-dock]) > .pbody {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
[data-fold~="side"] .app[data-browser="on"] .pane[data-browser-size="wide"]:has(> .pbody[data-dock]) > .compose {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pane[data-browser-size="full"]:has(> .pbody[data-dock]) > .findhost { grid-area: 1 / 1 / 2 / 3 }
|
||||
.pane[data-browser-size="full"]:has(> .pbody[data-dock]) > .scroll[data-pane="browser"] { grid-area: 2 / 1 / 4 / 3; border-left: 0 }
|
||||
.pane[data-browser-size="full"]:has(> .pbody[data-dock]) > :is(.pbody, .compose, .gutter-r) { display: none }
|
||||
.workbench-tabs > [data-action="browser.full"] { margin-left: auto; }
|
||||
@@ -1,15 +0,0 @@
|
||||
import { t } from "../i18n";
|
||||
import { StudioIcon } from "./StudioIcon";
|
||||
import { ariaChord, chord } from "./keys";
|
||||
import { TooltipButton } from "./TooltipButton";
|
||||
|
||||
export function BrowserWideToggle({ wide, onToggle }: { wide: boolean; onToggle: () => void }) {
|
||||
const label = wide ? t("恢复分栏") : t("加宽工作台");
|
||||
return (
|
||||
<TooltipButton className="workbench-files" data-action="browser.wide" aria-pressed={wide}
|
||||
aria-label={label} aria-keyshortcuts={`${ariaChord("B")}+Shift`}
|
||||
title={label} shortcut={chord("Shift+B")} onClick={onToggle}>
|
||||
<StudioIcon name="panel" />
|
||||
</TooltipButton>
|
||||
);
|
||||
}
|
||||
@@ -12,7 +12,6 @@ const KEYS: Array<[string, string]> = [
|
||||
[`${chord("↑")} / ${chord("↓")}`, "在提问之间移动"],
|
||||
[chord("Shift+E"), "优化提示词"],
|
||||
[chord("Shift+F"), "工作台全屏"],
|
||||
[chord("Shift+B"), "工作台加宽"],
|
||||
[`${chord("-")} / ${chord("+")} / ${chord("0")}`, "缩小 / 放大 / 复原界面"],
|
||||
[chord("Enter"), "发送(发送键设为修饰键 + Enter 时)"],
|
||||
["Enter / Shift+Enter", "发送,或用 Shift 换行"],
|
||||
|
||||
@@ -55,7 +55,7 @@ import { RunAnalysis } from "./RunAnalysis";
|
||||
import { useBrowserTabs } from "./BrowserPanel";
|
||||
import { useRevealAgentPages, useRevealBrowserOpen } from "./browserreveal";
|
||||
import { WorkbenchPanel } from "./WorkbenchPanel";
|
||||
import { useBrowserWide } from "./browserwide";
|
||||
import { useBrowserFull } from "./browserfull";
|
||||
import { refreshTodos } from "../state/restore";
|
||||
import { speedOf } from "./speed";
|
||||
import { RuntimeBar } from "./RuntimeBar";
|
||||
@@ -459,7 +459,7 @@ function PaneView({ port, hub, rt, title, active, visible, sideHost, side, onFoc
|
||||
// Chinese literals that the reducer had written — a translation key deciding
|
||||
// whether the run reads as moving.
|
||||
const blocked = hasPendingDecision(status);
|
||||
const browserSize = useBrowserWide(docked, active && visible, blocked);
|
||||
const browserSize = useBrowserFull(docked, active && visible, blocked);
|
||||
const run = runState({ blocked, running, hasItems: s.items.length > 0, terminal: s.terminal });
|
||||
const cost = s.metrics.coverage === "none" ? NO_COST : s.metrics.cost;
|
||||
const contextPercent = ctx && ctx.window > 0 ? Math.min(100, Math.round((ctx.used / ctx.window) * 100)) : null;
|
||||
@@ -581,7 +581,6 @@ function PaneView({ port, hub, rt, title, active, visible, sideHost, side, onFoc
|
||||
fileRequest={conversationFiles.request}
|
||||
agentRequest={agentRuns.request}
|
||||
tabs={pages}
|
||||
wideMode={docked ? { wide: browserSize.size === "wide", onToggle: browserSize.toggle } : undefined}
|
||||
fullMode={docked ? { full: browserSize.size === "full", onToggle: browserSize.toggleFull } : undefined}
|
||||
manual={manualBrowser}
|
||||
shown={visible && workbench}
|
||||
|
||||
@@ -30,13 +30,13 @@ it("waits 400ms for hover and cancels both pending and visible hints on leave",
|
||||
it("supports keyboard focus, Escape, current-state labels and the existing button ref", () => {
|
||||
const ref = createRef<HTMLButtonElement>();
|
||||
const clicked = vi.fn();
|
||||
const view = render(<TooltipButton ref={ref} title="加宽工作台" shortcut="⌘Shift+B" onClick={clicked}>Wide</TooltipButton>);
|
||||
const view = render(<TooltipButton ref={ref} title="Expand" shortcut="Shortcut" onClick={clicked}>Toggle</TooltipButton>);
|
||||
const button = screen.getByRole("button");
|
||||
expect(ref.current).toBe(button);
|
||||
fireEvent.focus(button);
|
||||
expect(screen.getByRole("tooltip").textContent).toContain("⌘Shift+B");
|
||||
view.rerender(<TooltipButton ref={ref} title="恢复分栏" onClick={clicked}>Wide</TooltipButton>);
|
||||
expect(screen.getByRole("tooltip").textContent).toBe("恢复分栏");
|
||||
expect(screen.getByRole("tooltip").textContent).toContain("Shortcut");
|
||||
view.rerender(<TooltipButton ref={ref} title="Restore" onClick={clicked}>Toggle</TooltipButton>);
|
||||
expect(screen.getByRole("tooltip").textContent).toBe("Restore");
|
||||
fireEvent.keyDown(button, { key: "Escape" });
|
||||
expect(screen.queryByRole("tooltip")).toBeNull();
|
||||
fireEvent.blur(button);
|
||||
|
||||
@@ -35,7 +35,6 @@ import { OpenWithMenu } from "./OpenWithMenu";
|
||||
import type { HubPort } from "../port/hub";
|
||||
import { useHtmlPreview } from "./useHtmlPreview";
|
||||
import { setShowsHiddenFiles, showsHiddenFiles } from "../state/prefs";
|
||||
import { BrowserWideToggle } from "./BrowserWideToggle";
|
||||
import { BrowserFullToggle } from "./BrowserFullToggle";
|
||||
import { documentView, sourceView } from "./document/registry";
|
||||
import { TooltipButton } from "./TooltipButton";
|
||||
@@ -70,7 +69,6 @@ export function WorkbenchPanel({
|
||||
onExternal,
|
||||
onAddToChat,
|
||||
onQuoteSelection,
|
||||
wideMode,
|
||||
fullMode,
|
||||
fileRequest,
|
||||
agentRequest,
|
||||
@@ -104,7 +102,6 @@ export function WorkbenchPanel({
|
||||
onAddToChat?: (reference: string) => void;
|
||||
// Text selected in an open file, with the file it came from, on its way to the composer.
|
||||
onQuoteSelection?: (text: string, path: string) => void;
|
||||
wideMode?: { wide: boolean; onToggle: () => void };
|
||||
fullMode?: { full: boolean; onToggle: () => void };
|
||||
fileRequest?: { path: string; consumed?: () => void };
|
||||
agentRequest?: AgentRequest;
|
||||
@@ -450,7 +447,6 @@ export function WorkbenchPanel({
|
||||
if (launcher && e.key === "Escape") { e.stopPropagation(); toggleLauncher(); newTab.current?.focus(); }
|
||||
}}>
|
||||
<header className="workbench-tabs">
|
||||
{wideMode && <BrowserWideToggle {...wideMode} />}
|
||||
<div
|
||||
className="workbench-tablist"
|
||||
role="tablist"
|
||||
|
||||
+29
-55
@@ -35,54 +35,23 @@ function open(active = true, setup?: (port: MockPort) => void) {
|
||||
return { ...view, width, close };
|
||||
}
|
||||
|
||||
describe("browser wide mode", () => {
|
||||
it("keeps the page and conversation mounted and leaves the saved split width alone", async () => {
|
||||
describe("workbench sizing", () => {
|
||||
it("keeps divider resizing available when the retired widening shortcut is pressed", async () => {
|
||||
const { container, width } = open();
|
||||
const frame = await screen.findByLabelText("网页地址");
|
||||
const composer = container.querySelector(".compose");
|
||||
const button = screen.getByRole("button", { name: "加宽工作台" });
|
||||
expect(button.getAttribute("aria-pressed")).toBe("false");
|
||||
fireEvent.click(button);
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("wide");
|
||||
expect(screen.getByRole("button", { name: "恢复分栏" }).getAttribute("aria-pressed")).toBe("true");
|
||||
expect(screen.getByLabelText("网页地址")).toBe(frame);
|
||||
expect(container.querySelector(".compose")).toBe(composer);
|
||||
expect(composer?.hasAttribute("hidden")).toBe(false);
|
||||
expect(container.querySelector(".pbody")?.hasAttribute("data-full")).toBe(false);
|
||||
fireEvent.click(screen.getByRole("button", { name: "恢复分栏" }));
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
expect(screen.getByLabelText("网页地址")).toBe(frame);
|
||||
expect(width).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("puts the wide toggle at the left edge of the workbench bar and the full-size toggle at the right edge", async () => {
|
||||
const { container } = open();
|
||||
await screen.findByLabelText("网页地址");
|
||||
const bar = container.querySelector<HTMLElement>(".workbench-tabs")!;
|
||||
expect(bar.firstElementChild).toBe(screen.getByRole("button", { name: "加宽工作台" }));
|
||||
expect(bar.lastElementChild).toBe(screen.getByRole("button", { name: "全屏工作台" }));
|
||||
const event = new KeyboardEvent("keydown", { key: "B", ctrlKey: true, shiftKey: true, bubbles: true, cancelable: true });
|
||||
act(() => { document.body.dispatchEvent(event); });
|
||||
expect(event.defaultPrevented).toBe(false);
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
const divider = screen.getByRole("separator", { name: "调整浏览器宽度" });
|
||||
fireEvent.keyDown(divider, { key: "ArrowLeft", shiftKey: true });
|
||||
expect(width).toHaveBeenCalledWith(608);
|
||||
expect(screen.queryByRole("button", { name: "加宽工作台" })).toBeNull();
|
||||
});
|
||||
|
||||
it("toggles with Ctrl+Shift+B and spends Escape leaving wide mode before the browser closes", async () => {
|
||||
const { container, close } = open();
|
||||
await screen.findByLabelText("网页地址");
|
||||
fireEvent.keyDown(document.body, { key: "B", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("wide");
|
||||
fireEvent.keyDown(document.body, { key: "Escape" });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
expect(close).not.toHaveBeenCalled();
|
||||
fireEvent.keyDown(document.body, { key: "B", ctrlKey: true, shiftKey: true });
|
||||
fireEvent.keyDown(document.body, { key: "B", ctrlKey: true, shiftKey: true, repeat: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("wide");
|
||||
fireEvent.keyDown(document.body, { key: "B", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
fireEvent.keyDown(document.body, { key: "Escape" });
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("ignores shortcuts on an inactive pane and keys already handled by a field", async () => {
|
||||
it("ignores full-view shortcuts on an inactive pane and keys already handled by a field", async () => {
|
||||
const hidden = open(false);
|
||||
fireEvent.keyDown(document.body, { key: "B", ctrlKey: true, shiftKey: true });
|
||||
fireEvent.keyDown(document.body, { key: "F", ctrlKey: true, shiftKey: true });
|
||||
expect(hidden.container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
cleanup();
|
||||
const { container } = open();
|
||||
@@ -90,7 +59,7 @@ describe("browser wide mode", () => {
|
||||
const input = document.createElement("input");
|
||||
input.addEventListener("keydown", e => e.preventDefault());
|
||||
document.body.append(input);
|
||||
fireEvent.keyDown(input, { key: "B", ctrlKey: true, shiftKey: true });
|
||||
fireEvent.keyDown(input, { key: "F", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
input.remove();
|
||||
});
|
||||
@@ -98,38 +67,38 @@ describe("browser wide mode", () => {
|
||||
it("resets to split after closing the dock without replacing the page", async () => {
|
||||
open();
|
||||
const frame = await screen.findByLabelText("网页地址");
|
||||
fireEvent.click(screen.getByRole("button", { name: "加宽工作台" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "全屏工作台" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "工作台" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "工作台" }));
|
||||
expect(screen.getByRole("button", { name: "加宽工作台" }).getAttribute("aria-pressed")).toBe("false");
|
||||
expect(screen.getByRole("button", { name: "全屏工作台" }).getAttribute("aria-pressed")).toBe("false");
|
||||
expect(screen.getByLabelText("网页地址")).toBe(frame);
|
||||
});
|
||||
|
||||
it("gives a transient layer first use of Escape", async () => {
|
||||
const { container, close } = open();
|
||||
await screen.findByLabelText("网页地址");
|
||||
fireEvent.click(screen.getByRole("button", { name: "加宽工作台" }));
|
||||
fireEvent.click(screen.getByRole("button", { name: "全屏工作台" }));
|
||||
const dismiss = (e: KeyboardEvent) => e.stopPropagation();
|
||||
window.addEventListener("keydown", dismiss, true);
|
||||
try {
|
||||
fireEvent.keyDown(document.body, { key: "Escape" });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("wide");
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("full");
|
||||
expect(close).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
window.removeEventListener("keydown", dismiss, true);
|
||||
}
|
||||
});
|
||||
|
||||
it("does not treat IME composition or a plain Ctrl+B as the wide shortcut", async () => {
|
||||
it("does not treat IME composition or a plain Ctrl+F as the full-view shortcut", async () => {
|
||||
const { container } = open();
|
||||
await screen.findByLabelText("网页地址");
|
||||
act(() => document.body.dispatchEvent(new KeyboardEvent("keydown", { key: "B", ctrlKey: true, shiftKey: true, isComposing: true, bubbles: true })));
|
||||
fireEvent.keyDown(document.body, { key: "b", ctrlKey: true });
|
||||
act(() => document.body.dispatchEvent(new KeyboardEvent("keydown", { key: "F", ctrlKey: true, shiftKey: true, isComposing: true, bubbles: true })));
|
||||
fireEvent.keyDown(document.body, { key: "f", ctrlKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
});
|
||||
|
||||
it("shows the page alone in full mode, keeps the conversation mounted, and Escape returns to the split", async () => {
|
||||
const { container, close } = open();
|
||||
const { container, close, width } = open();
|
||||
const frame = await screen.findByLabelText("网页地址");
|
||||
const composer = container.querySelector(".compose");
|
||||
fireEvent.click(screen.getByRole("button", { name: "全屏工作台" }));
|
||||
@@ -140,16 +109,21 @@ describe("browser wide mode", () => {
|
||||
fireEvent.keyDown(document.body, { key: "Escape" });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
expect(close).not.toHaveBeenCalled();
|
||||
expect(width).not.toHaveBeenCalled();
|
||||
expect(screen.getByRole("separator", { name: "调整浏览器宽度" })).toBeTruthy();
|
||||
fireEvent.keyDown(document.body, { key: "Escape" });
|
||||
expect(close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it("toggles full mode with Ctrl+Shift+F and does not mix it up with wide mode", async () => {
|
||||
it("toggles full mode with Ctrl+Shift+F without reacting to the retired widening shortcut or repeats", async () => {
|
||||
const { container } = open();
|
||||
await screen.findByLabelText("网页地址");
|
||||
fireEvent.keyDown(document.body, { key: "F", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("full");
|
||||
fireEvent.keyDown(document.body, { key: "B", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("wide");
|
||||
fireEvent.keyDown(document.body, { key: "F", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("full");
|
||||
fireEvent.keyDown(document.body, { key: "F", ctrlKey: true, shiftKey: true, repeat: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("full");
|
||||
fireEvent.keyDown(document.body, { key: "F", ctrlKey: true, shiftKey: true });
|
||||
expect(container.querySelector(".pane")?.getAttribute("data-browser-size")).toBe("split");
|
||||
});
|
||||
+7
-11
@@ -1,13 +1,12 @@
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { pressedChord } from "./keys";
|
||||
import { listenAction } from "./listen";
|
||||
import "../styles/browserwide.css";
|
||||
import "../styles/browserfull.css";
|
||||
|
||||
export type BrowserSize = "split" | "wide" | "full";
|
||||
type BrowserSize = "split" | "full";
|
||||
|
||||
export function useBrowserWide(docked: boolean, active: boolean, blocked = false) {
|
||||
export function useBrowserFull(docked: boolean, active: boolean, blocked = false) {
|
||||
const [size, setSize] = useState<BrowserSize>("split");
|
||||
const toggle = useCallback(() => setSize((size) => size === "wide" ? "split" : "wide"), []);
|
||||
const toggleFull = useCallback(() => setSize((size) => size === "full" ? "split" : "full"), []);
|
||||
useEffect(() => {
|
||||
if (!docked) setSize("split");
|
||||
@@ -19,10 +18,7 @@ export function useBrowserWide(docked: boolean, active: boolean, blocked = false
|
||||
if (!docked || !active) return;
|
||||
const key = (e: KeyboardEvent) => {
|
||||
if (e.defaultPrevented || e.isComposing || e.repeat) return;
|
||||
if (pressedChord(e, "b", true)) {
|
||||
e.preventDefault();
|
||||
toggle();
|
||||
} else if (pressedChord(e, "f", true)) {
|
||||
if (pressedChord(e, "f", true)) {
|
||||
e.preventDefault();
|
||||
toggleFull();
|
||||
} else if (e.key === "Escape" && size !== "split") {
|
||||
@@ -33,7 +29,7 @@ export function useBrowserWide(docked: boolean, active: boolean, blocked = false
|
||||
};
|
||||
// Document bubbling follows controls and dismiss layers, but precedes the
|
||||
// window's Escape handler that closes the browser or stops the turn.
|
||||
return listenAction(document, "keydown", { action: "browser.wide", listener: key as EventListener });
|
||||
}, [docked, active, size, toggle, toggleFull]);
|
||||
return { size: docked ? size : "split", toggle, toggleFull };
|
||||
return listenAction(document, "keydown", { action: "browser.full", listener: key as EventListener });
|
||||
}, [docked, active, size, toggleFull]);
|
||||
return { size: docked ? size : "split", toggleFull };
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
- 调整:移除工作台的“加宽”按钮、独立加宽模式及 Ctrl/Cmd+Shift+B 快捷键。可拖动分隔条或聚焦后用方向键调宽;工作台全屏及退出后恢复原分栏宽度保持不变。关联 #14324。
|
||||
- Changed: removed Widen mode, its button, and Ctrl/Cmd+Shift+B. Use the divider to resize; full-workbench view still restores the saved split width. Refs #14324.
|
||||
Reference in new issue
Block a user