Refine rewrite selection popover styling and shortcuts

This commit is contained in:
murashit 2026-05-18 14:27:29 +09:00
parent a1f550a5d7
commit 2496099850
2 changed files with 35 additions and 69 deletions

View file

@ -1,7 +1,7 @@
import { Notice, type Editor } from "obsidian";
import { syncComposerHeight } from "../ui/composer";
import { createIconButton } from "../ui/components";
import { syncTextareaHeight } from "../ui/textarea-autogrow";
import { diffLineClass, displayDiffLineText, displayDiffLines } from "../ui/turn-diff";
import { buildSelectionUnifiedDiff } from "./diff";
import { isRewriteActionKey, isRewriteGenerateKey } from "./keys";
@ -115,6 +115,7 @@ export class RewriteSelectionPopover {
} finally {
if (this.abortController === abortController) this.abortController = null;
this.syncControls();
if (this.options.session.status === "preview") this.focusApplyButton();
this.position();
}
}
@ -140,10 +141,9 @@ export class RewriteSelectionPopover {
const root = activeDocument.body.createDiv({ cls: "codex-panel-rewrite-popover" });
root.setAttr("role", "dialog");
root.setAttr("aria-label", "Rewrite selection");
root.onkeydown = (event) => this.handlePopoverKeydown(event);
const instruction = root.createEl("textarea", {
cls: "codex-panel-rewrite-popover__instruction",
cls: "codex-panel__input codex-panel-rewrite-popover__instruction",
attr: { placeholder: "How should Codex rewrite the selected text?" },
});
instruction.value = this.options.session.instruction;
@ -162,18 +162,38 @@ export class RewriteSelectionPopover {
const promptRow = root.createDiv({ cls: "codex-panel-rewrite-popover__prompt-row" });
promptRow.append(instruction);
const controls = promptRow.createDiv({ cls: "codex-panel-rewrite-popover__controls" });
const generateButton = createIconButton(controls, "sparkles", "Generate rewrite", "codex-panel-rewrite-popover__icon-button");
const controls = promptRow.createDiv({ cls: "codex-panel__composer-actions codex-panel-rewrite-popover__controls" });
const generateButton = createIconButton(
controls,
"sparkles",
"Generate rewrite",
"codex-panel__composer-action codex-panel-rewrite-popover__icon-button",
);
generateButton.onclick = () => void this.generate();
const cancelButton = createIconButton(controls, "x", "Cancel rewrite", "codex-panel-rewrite-popover__icon-button");
const cancelButton = createIconButton(
controls,
"x",
"Cancel rewrite",
"codex-panel__composer-action codex-panel-rewrite-popover__icon-button",
);
cancelButton.onclick = () => this.cancel();
const status = root.createDiv({ cls: "codex-panel-rewrite-popover__status" });
const streamPreview = root.createEl("pre", { cls: "codex-panel-rewrite-popover__stream-preview is-hidden" });
const resultRow = root.createDiv({ cls: "codex-panel-rewrite-popover__result-row" });
const diff = resultRow.createDiv({ cls: "codex-panel-rewrite-popover__diff" });
const applyButton = createIconButton(resultRow, "check", "Apply rewrite", "codex-panel-rewrite-popover__icon-button mod-cta");
const applyButton = createIconButton(
resultRow,
"check",
"Apply rewrite",
"codex-panel__composer-action codex-panel-rewrite-popover__icon-button mod-cta",
);
applyButton.onclick = () => this.apply();
applyButton.onkeydown = (event) => {
if (!isRewriteActionKey(event)) return;
event.preventDefault();
this.apply();
};
return { root, instruction, generateButton, applyButton, resultRow, status, streamPreview, diff, debug: null };
}
@ -254,13 +274,9 @@ export class RewriteSelectionPopover {
this.close();
}
private handlePopoverKeydown(event: KeyboardEvent): void {
if (!this.elements || event.target === this.elements.instruction) return;
if (isInteractiveEventTarget(event.target)) return;
if (this.options.session.replacementText === null || this.options.session.status === "generating") return;
if (!isRewriteActionKey(event)) return;
event.preventDefault();
this.apply();
private focusApplyButton(): void {
if (!this.elements || this.elements.applyButton.disabled) return;
this.elements.applyButton.focus({ preventScroll: true });
}
private setStatus(text: string, options: { active?: boolean } = {}): void {
@ -291,10 +307,7 @@ export class RewriteSelectionPopover {
}
private syncInstructionHeight(): void {
syncTextareaHeight(this.elements?.instruction ?? null, {
minHeightFallback: 0,
maxHeightFallback: Math.min(168, activeWindow.innerHeight * 0.28),
});
syncComposerHeight(this.elements?.instruction ?? null);
}
private position(): void {
@ -336,8 +349,3 @@ function renderRewriteDiff(parent: HTMLElement, diff: string): void {
});
}
}
function isInteractiveEventTarget(target: EventTarget | null): boolean {
if (!(target instanceof HTMLElement)) return false;
return Boolean(target.closest("button, input, textarea, select, a, [role='button']"));
}

View file

@ -1,5 +1,6 @@
.codex-panel,
.codex-panel-settings {
.codex-panel-settings,
.codex-panel-rewrite-popover {
--codex-panel-top-icon-size: var(--icon-s, 16px);
--codex-panel-icon-button-inline-size: calc(var(--codex-panel-top-icon-size) + var(--size-2-3, 6px) * 2);
--codex-panel-icon-button-block-size: calc(var(--codex-panel-top-icon-size) + var(--size-2-2, 4px) * 2);
@ -1397,8 +1398,6 @@
}
.codex-panel-rewrite-popover {
--rewrite-icon-button-size: calc(var(--icon-s, 16px) + var(--size-2-3, 6px) * 2);
--rewrite-control-stack-size: calc(var(--rewrite-icon-button-size) * 2 + var(--size-2-1, 2px));
position: fixed;
z-index: var(--layer-popover, 30);
box-sizing: border-box;
@ -1416,38 +1415,17 @@
color: var(--text-normal);
}
.codex-panel-rewrite-popover__instruction {
box-sizing: border-box;
display: block;
width: 100%;
height: var(--rewrite-control-stack-size);
min-height: var(--rewrite-control-stack-size);
max-height: min(168px, 28vh);
padding: var(--size-2-3, 6px) var(--size-4-2, 8px);
line-height: var(--line-height-normal);
resize: none;
overflow-y: hidden;
}
.codex-panel-rewrite-popover__prompt-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--size-2-2, 4px);
gap: var(--codex-panel-section-gap);
align-items: stretch;
}
.codex-panel-rewrite-popover__controls {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: flex-start;
gap: var(--size-2-1, 2px);
}
.codex-panel-rewrite-popover__result-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--size-2-2, 4px);
gap: var(--codex-panel-control-gap);
align-items: start;
}
@ -1456,30 +1434,10 @@
display: none;
}
.codex-panel-rewrite-popover__icon-button {
--icon-size: var(--icon-s, 16px);
--icon-stroke: var(--icon-s-stroke-width, 2px);
box-sizing: border-box;
width: var(--rewrite-icon-button-size);
height: var(--rewrite-icon-button-size);
min-width: var(--rewrite-icon-button-size);
min-height: var(--rewrite-icon-button-size);
padding: var(--size-2-3, 6px);
border: 0;
border-radius: var(--clickable-icon-radius, var(--radius-s));
line-height: 1;
}
.codex-panel-rewrite-popover__icon-button.is-hidden {
display: none;
}
.codex-panel-rewrite-popover__icon-button svg {
width: var(--icon-size);
height: var(--icon-size);
stroke-width: var(--icon-stroke);
}
.codex-panel-rewrite-popover__status {
color: var(--text-muted);
font-size: var(--font-ui-small);