fix: UI glitch: config is not visible

Co-authored-by: sunlit-deng <253064511+sunlit-deng@users.noreply.github.com>
This commit is contained in:
clawsweeper
2026-06-23 15:56:19 +00:00
parent 9ff7abc898
commit a6ea91e6ed
2 changed files with 57 additions and 18 deletions

View File

@@ -473,6 +473,36 @@ describe("config view", () => {
expect(content.scrollLeft).toBe(0);
});
it("resets config content scroll when switching from form to raw mode", async () => {
const container = document.createElement("div");
document.body.append(container);
try {
const renderCase = (overrides: Partial<ConfigProps>) =>
render(renderConfig({ ...baseProps(), ...overrides }), container);
renderCase({ formMode: "form" });
const content = queryRequired(container, ".config-content", HTMLElement);
content.scrollTop = 320;
content.scrollLeft = 18;
content.scrollTo = vi.fn(({ top, left }: { top?: number; left?: number }) => {
content.scrollTop = top ?? content.scrollTop;
content.scrollLeft = left ?? content.scrollLeft;
}) as typeof content.scrollTo;
renderCase({ formMode: "raw" });
await Promise.resolve();
expect(content["scrollTo"]).toHaveBeenCalledOnce();
expect(content["scrollTo"]).toHaveBeenCalledWith({ top: 0, left: 0, behavior: "auto" });
expect(content.scrollTop).toBe(0);
expect(content.scrollLeft).toBe(0);
} finally {
container.remove();
}
});
it("can hide the root tab for scoped settings surfaces", () => {
const { container } = renderConfigView({
activeSection: "channels",

View File

@@ -1184,6 +1184,7 @@ function createConfigEphemeralState(): ConfigEphemeralState {
const cvs = createConfigEphemeralState();
let lastConfigContextKey: string | null = null;
let lastFormModeForScroll: ConfigProps["formMode"] | null = null;
function resetConfigEphemeralState() {
Object.assign(cvs, createConfigEphemeralState());
@@ -1222,6 +1223,7 @@ function toggleSensitivePathReveal(path: Array<string | number>) {
export function resetConfigViewStateForTests() {
resetConfigEphemeralState();
lastConfigContextKey = null;
lastFormModeForScroll = null;
}
export function renderConfig(props: ConfigProps) {
@@ -1237,6 +1239,31 @@ export function renderConfig(props: ConfigProps) {
const rawAvailable = props.rawAvailable ?? true;
const formMode = showModeToggle && rawAvailable ? props.formMode : "form";
const requestUpdate = props.onRequestUpdate ?? (() => {});
// Scroll helper: target-based (nav clicks) with global fallback (form/raw toggle)
const resetContentScroll = (target: EventTarget | null) => {
queueMicrotask(() => {
const origin = target instanceof Element ? target : null;
const content =
origin?.closest(".config-main")?.querySelector<HTMLElement>(".config-content") ??
globalThis.document?.querySelector<HTMLElement>(".config-content");
if (!content) {
return;
}
if (typeof content.scrollTo === "function") {
content.scrollTo({ top: 0, left: 0, behavior: "auto" });
return;
}
content.scrollTop = 0;
content.scrollLeft = 0;
});
};
// Reset scroll position when switching between form and raw mode
if (lastFormModeForScroll !== null && lastFormModeForScroll !== formMode) {
resetContentScroll(null);
}
lastFormModeForScroll = formMode;
const currentContextKey = configContextKey(props);
if (lastConfigContextKey !== currentContextKey) {
resetConfigEphemeralState();
@@ -1301,24 +1328,6 @@ export function renderConfig(props: ConfigProps) {
const settingsLayout = props.settingsLayout ?? "tabs";
const allCategories = [...visibleCategories, ...(otherCategory ? [otherCategory] : [])];
const resetContentScroll = (target: EventTarget | null) => {
queueMicrotask(() => {
const origin = target instanceof Element ? target : null;
const content = origin
?.closest(".config-main")
?.querySelector<HTMLElement>(".config-content");
if (!content) {
return;
}
if (typeof content.scrollTo === "function") {
content.scrollTo({ top: 0, left: 0, behavior: "auto" });
return;
}
content.scrollTop = 0;
content.scrollLeft = 0;
});
};
function renderAccordionNav() {
return html`
<div class="config-accordion-nav">