Compare commits

..
Author SHA1 Message Date
jiang cdf97afbc4 fix(chat): preserve style defaults for omitted agent overrides
Generic Container CI/CD / test-build-publish (push) Successful in 1m6s
Frontend CI/CD v2 / build-test-publish-and-deploy (push) Successful in 1m6s
2026-09-25 16:02:10 +08:00
jiang ca10d8f384 fix(chat): clarify approval mode and verify permission prompts
Generic Container CI/CD / test-build-publish (push) Successful in 1m29s
Frontend CI/CD v2 / build-test-publish-and-deploy (push) Successful in 1m29s
2026-09-25 15:44:46 +08:00
4 changed files with 72 additions and 5 deletions
+1 -1
View File
@@ -79,7 +79,7 @@ const approvalModeOptions: ReadonlyArray<{
{
value: "always",
label: "始终允许",
description: "除明确禁止项外自动放行",
description: "常规权限自动放行,敏感路径仍需确认",
icon: WarningAmberRounded,
},
];
@@ -0,0 +1,47 @@
import React from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import { PermissionRequestGroup } from "./AgentPermissionRequests";
jest.mock("framer-motion", () => ({
AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
motion: {
div: ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div {...props}>{children}</div>
),
},
}));
describe("PermissionRequestGroup", () => {
it("shows an external path approval request and forwards the user's decision", () => {
const onReply = jest.fn();
render(
<ThemeProvider theme={createTheme()}>
<PermissionRequestGroup
permissions={[
{
requestId: "external-1",
sessionId: "session-1",
permission: "external_directory",
patterns: ["/app/private/*"],
target: "/app/private/report.json",
always: ["/app/private/*"],
createdAt: 1,
status: "pending",
},
]}
isRunning
onReply={onReply}
/>
</ThemeProvider>,
);
expect(screen.getByText("访问工作区外目录")).toBeInTheDocument();
expect(screen.getByText("/app/private/report.json")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "允许一次" }));
expect(onReply).toHaveBeenCalledWith("external-1", "once");
fireEvent.click(screen.getByRole("button", { name: "保存授权" }));
expect(onReply).toHaveBeenCalledWith("external-1", "always");
});
});
@@ -1,4 +1,6 @@
import { parseApplyLayerStylePayload } from "./toolCallStyleHelpers";
import { createDefaultLayerStyleState } from "@components/olmap/core/Controls/styleEditorPresets";
import { validateStyleConfig } from "@components/olmap/core/Controls/styleEditorUtils";
describe("parseApplyLayerStylePayload", () => {
it("accepts a valid snake_case interval contract", () => {
@@ -47,4 +49,17 @@ describe("parseApplyLayerStylePayload", () => {
}),
).toMatchObject({ layerId: "junctions", styleConfig: { opacity: 0.5 } });
});
it("preserves default palette indexes when only some style fields are provided", () => {
const payload = parseApplyLayerStylePayload({
layer_id: "pipes",
style_config: { property: "flow", color_type: "gradient" },
});
expect(payload).not.toBeNull();
const merged = {
...createDefaultLayerStyleState("pipes").styleConfig,
...payload?.styleConfig,
};
expect(validateStyleConfig(merged)).toEqual({ valid: true, errors: [] });
});
});
+9 -4
View File
@@ -198,10 +198,15 @@ export const parseApplyLayerStylePayload = (
) return null;
}
const definedStyleConfig = styleConfig
? (Object.fromEntries(
Object.entries(styleConfig).filter(([, value]) => value !== undefined),
) as Partial<StyleConfig>)
: undefined;
const hasStyleOverrides =
styleConfig &&
Object.values(styleConfig).some((value) =>
Array.isArray(value) ? value.length > 0 : value !== undefined,
definedStyleConfig &&
Object.values(definedStyleConfig).some((value) =>
Array.isArray(value) ? value.length > 0 : true,
);
if (!resetToDefault && !hasStyleOverrides) {
@@ -211,7 +216,7 @@ export const parseApplyLayerStylePayload = (
return {
layerId,
resetToDefault,
styleConfig: hasStyleOverrides ? styleConfig : undefined,
styleConfig: hasStyleOverrides ? definedStyleConfig : undefined,
};
};