mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
Unify thread rename editing behavior
This commit is contained in:
parent
375ab9e2b3
commit
b3cb68ffbc
7 changed files with 42 additions and 72 deletions
|
|
@ -53,6 +53,10 @@ export class ThreadRenameController {
|
|||
};
|
||||
}
|
||||
|
||||
isEditing(): boolean {
|
||||
return this.renameThreadId !== null;
|
||||
}
|
||||
|
||||
start(threadId: string): void {
|
||||
const thread = this.thread(threadId);
|
||||
if (!thread) return;
|
||||
|
|
|
|||
|
|
@ -391,6 +391,9 @@ function renderThreadRenameRow(parent: HTMLElement, thread: ToolbarThreadRow, ac
|
|||
actions.cancelRenameThread(thread.threadId);
|
||||
}
|
||||
};
|
||||
input.onblur = () => {
|
||||
if (!(thread.rename?.generating ?? false)) actions.saveRenameThread(thread.threadId, input.value);
|
||||
};
|
||||
},
|
||||
});
|
||||
input.win.setTimeout(() => {
|
||||
|
|
@ -400,24 +403,13 @@ function renderThreadRenameRow(parent: HTMLElement, thread: ToolbarThreadRow, ac
|
|||
}
|
||||
}, 0);
|
||||
|
||||
const save = createToolbarButton(parent, "check", "Save thread name");
|
||||
save.addClass("codex-panel__thread-action");
|
||||
save.disabled = thread.rename?.generating ?? false;
|
||||
save.onclick = (event) => {
|
||||
event.stopPropagation();
|
||||
actions.saveRenameThread(thread.threadId, input.value);
|
||||
};
|
||||
|
||||
const cancel = createToolbarButton(parent, "x", "Cancel rename");
|
||||
cancel.addClass("codex-panel__thread-action");
|
||||
cancel.onclick = (event) => {
|
||||
event.stopPropagation();
|
||||
actions.cancelRenameThread(thread.threadId);
|
||||
};
|
||||
|
||||
const autoName = createToolbarButton(parent, thread.rename?.generating ? "loader" : "sparkles", "Auto-name thread");
|
||||
autoName.addClass("codex-panel__thread-action");
|
||||
autoName.disabled = thread.rename?.generating ?? false;
|
||||
autoName.onpointerdown = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
autoName.onclick = (event) => {
|
||||
event.stopPropagation();
|
||||
actions.autoNameThread(thread.threadId);
|
||||
|
|
|
|||
|
|
@ -1390,6 +1390,8 @@ export class CodexChatView extends ItemView {
|
|||
if (insideToolbarPanel && this.containerEl.contains(insideToolbarPanel)) return;
|
||||
}
|
||||
|
||||
if (this.threadRename.isEditing()) return;
|
||||
|
||||
this.closeToolbarPanel();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -112,18 +112,12 @@ function renderRenameRow(parent: HTMLElement, row: ThreadsRowModel, actions: Thr
|
|||
actions.cancelRename(row.thread.id);
|
||||
}
|
||||
};
|
||||
input.onblur = () => {
|
||||
if (!row.rename.generating) actions.saveRename(row.thread.id, input.value);
|
||||
};
|
||||
|
||||
const actionsGroup = parent.createDiv({ cls: "codex-panel-threads__actions codex-panel-threads__rename-actions" });
|
||||
|
||||
const save = iconButton(actionsGroup, "check", "Save thread name", "codex-panel-threads__row-button");
|
||||
save.disabled = row.rename.generating;
|
||||
save.onclick = () => {
|
||||
actions.saveRename(row.thread.id, input.value);
|
||||
};
|
||||
const cancel = iconButton(actionsGroup, "x", "Cancel rename", "codex-panel-threads__row-button");
|
||||
cancel.onclick = () => {
|
||||
actions.cancelRename(row.thread.id);
|
||||
};
|
||||
const autoName = iconButton(
|
||||
actionsGroup,
|
||||
row.rename.generating ? "loader" : "sparkles",
|
||||
|
|
@ -131,7 +125,12 @@ function renderRenameRow(parent: HTMLElement, row: ThreadsRowModel, actions: Thr
|
|||
"codex-panel-threads__row-button",
|
||||
);
|
||||
autoName.disabled = row.rename.generating;
|
||||
autoName.onclick = () => {
|
||||
autoName.onpointerdown = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
};
|
||||
autoName.onclick = (event) => {
|
||||
event.stopPropagation();
|
||||
actions.autoNameThread(row.thread.id);
|
||||
};
|
||||
input.win.setTimeout(() => {
|
||||
|
|
|
|||
44
styles.css
44
styles.css
|
|
@ -274,38 +274,22 @@
|
|||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
height: calc(var(--codex-panel-size-icon-s) + var(--codex-panel-panel-gap) * 2);
|
||||
padding-right: calc(
|
||||
var(--codex-panel-size-icon-s) * 3 + var(--codex-panel-panel-gap) * 6 + var(--codex-panel-toolbar-button-gap) * 2 +
|
||||
var(--codex-panel-item-gap)
|
||||
);
|
||||
min-height: calc(var(--codex-panel-size-icon-s) + var(--codex-panel-panel-gap) * 2);
|
||||
padding-right: calc(var(--codex-panel-size-icon-s) + var(--codex-panel-panel-gap) * 2 + var(--codex-panel-item-gap));
|
||||
}
|
||||
|
||||
.codex-panel-threads__rename-field {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
min-height: calc(var(--codex-panel-size-icon-s) + var(--codex-panel-panel-gap) * 2);
|
||||
height: calc(var(--codex-panel-size-icon-s) + var(--codex-panel-panel-gap) * 2);
|
||||
margin-left: calc(var(--codex-panel-item-gap) * -1);
|
||||
margin-right: var(--codex-panel-item-gap);
|
||||
padding: 0 var(--codex-panel-item-gap);
|
||||
overflow: visible;
|
||||
font-size: var(--font-ui-small);
|
||||
font-weight: var(--nav-item-weight, var(--font-normal));
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
.codex-panel-threads__rename-field::before {
|
||||
position: absolute;
|
||||
inset: calc(var(--codex-panel-control-gap) * -1) 0;
|
||||
border-radius: var(--nav-item-radius, var(--codex-panel-card-radius));
|
||||
background: var(--background-modifier-hover);
|
||||
content: "";
|
||||
}
|
||||
|
||||
.codex-panel-threads__rename-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
@ -334,9 +318,12 @@
|
|||
}
|
||||
|
||||
.codex-panel-threads__rename-input.codex-panel-threads__rename-input:focus,
|
||||
.codex-panel-threads__rename-input.codex-panel-threads__rename-input:focus-visible {
|
||||
.codex-panel-threads__rename-input.codex-panel-threads__rename-input:focus-visible,
|
||||
.codex-panel-threads__rename-input.codex-panel-threads__rename-input:hover,
|
||||
.codex-panel-threads__rename-input.codex-panel-threads__rename-input:active {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
|
|
@ -1162,7 +1149,7 @@
|
|||
}
|
||||
|
||||
.codex-panel__thread-row--renaming {
|
||||
grid-template-columns: minmax(0, 1fr) auto auto auto;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.codex-panel__thread:hover:not(:where(:disabled, .is-disabled)),
|
||||
|
|
@ -1176,26 +1163,12 @@
|
|||
}
|
||||
|
||||
.codex-panel__thread-rename {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
min-height: var(--codex-panel-size-nav-item);
|
||||
padding-block: var(--size-4-1);
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.codex-panel__thread-rename::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: calc(var(--codex-panel-size-icon-s) + var(--size-4-2));
|
||||
border-radius: var(--radius-s);
|
||||
background: var(--background-modifier-hover);
|
||||
content: "";
|
||||
}
|
||||
|
||||
.codex-panel__thread-rename-field {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
align-self: stretch;
|
||||
|
|
@ -1203,9 +1176,6 @@
|
|||
min-width: 0;
|
||||
min-height: 0;
|
||||
height: auto;
|
||||
margin-left: calc(var(--codex-panel-item-gap) * -1);
|
||||
margin-right: 0;
|
||||
padding: 0 var(--codex-panel-item-gap);
|
||||
overflow: visible;
|
||||
font-size: var(--codex-panel-toolbar-text-size);
|
||||
line-height: var(--codex-panel-toolbar-line-height);
|
||||
|
|
|
|||
|
|
@ -1871,10 +1871,12 @@ describe("toolbar renderer decisions", () => {
|
|||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
expect(updateRenameDraft).toHaveBeenCalledWith("editing", "New title");
|
||||
|
||||
parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.click();
|
||||
input.dispatchEvent(new FocusEvent("blur"));
|
||||
expect(saveRenameThread).toHaveBeenCalledWith("editing", "New title");
|
||||
parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')?.click();
|
||||
input.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }));
|
||||
expect(cancelRenameThread).toHaveBeenCalledWith("editing");
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')).toBeNull();
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')).toBeNull();
|
||||
parent.querySelector<HTMLButtonElement>('[aria-label="Auto-name thread"]')?.click();
|
||||
expect(autoNameThread).toHaveBeenCalledWith("editing");
|
||||
});
|
||||
|
|
@ -1902,9 +1904,9 @@ describe("toolbar renderer decisions", () => {
|
|||
);
|
||||
|
||||
expect(parent.querySelector<HTMLInputElement>(".codex-panel__thread-rename-input")?.disabled).toBe(false);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.disabled).toBe(true);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')).toBeNull();
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Auto-name thread"]')?.disabled).toBe(true);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')?.disabled).toBe(false);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1978,7 +1980,7 @@ describe("threads view renderer decisions", () => {
|
|||
const input = expectPresent(parent.querySelector<HTMLInputElement>(".codex-panel-threads__rename-input"));
|
||||
input.value = "New name";
|
||||
input.dispatchEvent(new Event("input"));
|
||||
parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.click();
|
||||
input.dispatchEvent(new FocusEvent("blur"));
|
||||
|
||||
expect(actions.updateRename).toHaveBeenCalledWith("thread", "New name");
|
||||
expect(actions.saveRename).toHaveBeenCalledWith("thread", "New name");
|
||||
|
|
@ -1998,8 +2000,9 @@ describe("threads view renderer decisions", () => {
|
|||
|
||||
expect(parent.querySelector<HTMLElement>(".codex-panel-threads__rename-form")).toBeTruthy();
|
||||
const actionsGroup = expectPresent(parent.querySelector<HTMLElement>(".codex-panel-threads__rename-actions"));
|
||||
expect(actionsGroup.querySelectorAll(".codex-panel-threads__row-button")).toHaveLength(3);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.disabled).toBe(false);
|
||||
expect(actionsGroup.querySelectorAll(".codex-panel-threads__row-button")).toHaveLength(1);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')).toBeNull();
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')).toBeNull();
|
||||
parent.querySelector<HTMLButtonElement>('[aria-label="Auto-name thread"]')?.click();
|
||||
|
||||
expect(actions.autoNameThread).toHaveBeenCalledWith("thread");
|
||||
|
|
@ -2017,9 +2020,9 @@ describe("threads view renderer decisions", () => {
|
|||
renderThreadsView(parent, { status: "1 thread", loading: false, rows: [row] }, threadsViewActions());
|
||||
|
||||
expect(parent.querySelector<HTMLInputElement>(".codex-panel-threads__rename-input")?.disabled).toBe(false);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.disabled).toBe(true);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')).toBeNull();
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Auto-name thread"]')?.disabled).toBe(true);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')?.disabled).toBe(false);
|
||||
expect(parent.querySelector<HTMLButtonElement>('[aria-label="Cancel rename"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ describe("CodexThreadsView", () => {
|
|||
expect(input).not.toBeNull();
|
||||
if (!input) return;
|
||||
input.value = "Renamed thread";
|
||||
view.containerEl.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.click();
|
||||
input.dispatchEvent(new FocusEvent("blur"));
|
||||
|
||||
await vi.waitFor(() => {
|
||||
expect(setThreadName).toHaveBeenCalledWith("thread", "Renamed thread");
|
||||
|
|
|
|||
Loading…
Reference in a new issue