fix: honor canvas agent view rollout on client
This commit is contained in:
parent
d434485987
commit
d548e31063
@ -204,7 +204,11 @@ import {
|
||||
getDefaultCanvasToolbarItem,
|
||||
normalizeCanvasToolbarConfig,
|
||||
} from "@/lib/canvas-toolbar-config";
|
||||
import type { CanvasAgentFeatureSettings } from "@/lib/canvas/agent-feature-flags";
|
||||
import {
|
||||
defaultCanvasAgentFeatureSettings,
|
||||
normalizeCanvasAgentFeatureSettings,
|
||||
type CanvasAgentFeatureSettings,
|
||||
} from "@/lib/canvas/agent-feature-flags";
|
||||
import { fetchUploadWithRetry } from "@/lib/client/upload-with-retry";
|
||||
import {
|
||||
AI_WASH_CONFIG_KEY,
|
||||
@ -1227,43 +1231,6 @@ type HomeAnimationConfig = {
|
||||
updatedAt: string | null;
|
||||
};
|
||||
|
||||
const defaultCanvasAgentFeatureSettings: CanvasAgentFeatureSettings = {
|
||||
operationProtocolEnabled: true,
|
||||
writebackMode: "operation_authoritative",
|
||||
slotEnabled: true,
|
||||
viewOperationEnabled: true,
|
||||
annotationEditEnabled: true,
|
||||
};
|
||||
|
||||
function normalizeCanvasAgentFeatureSettingsForClient(
|
||||
value: unknown
|
||||
): CanvasAgentFeatureSettings {
|
||||
const raw =
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Partial<CanvasAgentFeatureSettings>)
|
||||
: {};
|
||||
const writebackMode =
|
||||
raw.writebackMode === "legacy_canvas_media" ||
|
||||
raw.writebackMode === "operation_shadow" ||
|
||||
raw.writebackMode === "operation_authoritative"
|
||||
? raw.writebackMode
|
||||
: defaultCanvasAgentFeatureSettings.writebackMode;
|
||||
|
||||
return {
|
||||
operationProtocolEnabled:
|
||||
raw.operationProtocolEnabled ??
|
||||
defaultCanvasAgentFeatureSettings.operationProtocolEnabled,
|
||||
writebackMode,
|
||||
slotEnabled: raw.slotEnabled ?? defaultCanvasAgentFeatureSettings.slotEnabled,
|
||||
viewOperationEnabled:
|
||||
raw.viewOperationEnabled ??
|
||||
defaultCanvasAgentFeatureSettings.viewOperationEnabled,
|
||||
annotationEditEnabled:
|
||||
raw.annotationEditEnabled ??
|
||||
defaultCanvasAgentFeatureSettings.annotationEditEnabled,
|
||||
};
|
||||
}
|
||||
|
||||
const defaultLovartPromptTagVisibility: LovartPromptTagVisibility = {
|
||||
nanoBananaPro: true,
|
||||
design: true,
|
||||
@ -2263,7 +2230,7 @@ export function AdminSettingsClient() {
|
||||
agentEnabled: (siteSettingsData as any).agentEnabled ?? true,
|
||||
canvasAgentModeEnabled:
|
||||
(siteSettingsData as any).canvasAgentModeEnabled ?? true,
|
||||
canvasAgentFeatureSettings: normalizeCanvasAgentFeatureSettingsForClient(
|
||||
canvasAgentFeatureSettings: normalizeCanvasAgentFeatureSettings(
|
||||
(siteSettingsData as any).canvasAgentFeatureSettings
|
||||
),
|
||||
videoCanvasEnabled:
|
||||
|
||||
@ -886,6 +886,7 @@ async function getScopedPublicSiteSettings(
|
||||
LOVART_COMPOSER_PREVIEW_TEXTS_KEY,
|
||||
LOVART_COMPOSER_PREFIX_KEY,
|
||||
CANVAS_TOOLBAR_CONFIG_SETTING_KEY,
|
||||
...CANVAS_AGENT_FEATURE_SETTING_KEYS,
|
||||
OPENCLAW_CHAT_CONFIG_KEY,
|
||||
]);
|
||||
const typedSettings = settings as Record<string, unknown> | undefined;
|
||||
@ -903,6 +904,7 @@ async function getScopedPublicSiteSettings(
|
||||
valueByKey.get(LOVART_COMPOSER_PREVIEW_TEXTS_KEY)
|
||||
),
|
||||
canvasEnabled: typedSettings?.canvasEnabled ?? true,
|
||||
canvasAgentFeatureSettings: buildCanvasAgentFeatureSettings(valueByKey),
|
||||
aiDetailGenerationEnabled: Boolean(
|
||||
valueByKey.get(AI_DETAIL_GENERATOR_ENABLED_KEY)
|
||||
),
|
||||
|
||||
@ -172,6 +172,7 @@ import {
|
||||
type CanvasToolbarItemConfig,
|
||||
type CanvasToolbarItemKey,
|
||||
} from "@/lib/canvas-toolbar-config";
|
||||
import type { CanvasAgentFeatureSettings } from "@/lib/canvas/agent-feature-flags";
|
||||
import {
|
||||
buildAgentWorkflowComposerContext,
|
||||
} from "@/lib/agents/workflow-composer";
|
||||
@ -2060,6 +2061,7 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
siteLogoDark?: string | null;
|
||||
agentEnabled?: boolean;
|
||||
canvasAgentModeEnabled?: boolean;
|
||||
canvasAgentFeatureSettings?: CanvasAgentFeatureSettings;
|
||||
videoCanvasSidebarEnabled?: boolean;
|
||||
layerSplitEnabled?: boolean;
|
||||
angleEditEnabled?: boolean;
|
||||
@ -10454,6 +10456,9 @@ export default function CanvasEditorLegacyPage({ params }: PageProps) {
|
||||
<div className="flex-1 overflow-hidden" key={`chat-${id}`}>
|
||||
<CanvasChatPanel
|
||||
agentEnabled={isCanvasAgentModeEnabled}
|
||||
canvasAgentFeatureSettings={
|
||||
siteSettingsData?.canvasAgentFeatureSettings
|
||||
}
|
||||
canvasToolbarConfig={canvasToolbarConfig}
|
||||
chatId={`canvas-${id}`}
|
||||
chatScope="canvas"
|
||||
|
||||
@ -96,6 +96,11 @@ import {
|
||||
isCanvasImageReadyForAi,
|
||||
isCanvasImageUploadPending,
|
||||
} from "@/lib/canvas/local-upload-state";
|
||||
import {
|
||||
defaultCanvasAgentFeatureSettings,
|
||||
normalizeCanvasAgentFeatureSettings,
|
||||
type CanvasAgentFeatureSettings,
|
||||
} from "@/lib/canvas/agent-feature-flags";
|
||||
import {
|
||||
resolveCanvasViewOperation,
|
||||
} from "@/lib/canvas/agent-view-operations";
|
||||
@ -297,6 +302,7 @@ interface CanvasChatPanelProps {
|
||||
agentEnabled?: boolean;
|
||||
workspaceVariant?: "canvas" | "video";
|
||||
canvasToolbarConfig?: CanvasToolbarConfig;
|
||||
canvasAgentFeatureSettings?: CanvasAgentFeatureSettings;
|
||||
}
|
||||
|
||||
type CanvasComposerMode = "agent" | "image" | "video" | "detail" | "clawd";
|
||||
@ -1191,6 +1197,7 @@ export function CanvasChatPanel({
|
||||
agentEnabled = true,
|
||||
workspaceVariant = "canvas",
|
||||
canvasToolbarConfig: rawCanvasToolbarConfig,
|
||||
canvasAgentFeatureSettings: propCanvasAgentFeatureSettings,
|
||||
}: CanvasChatPanelProps) {
|
||||
const isWorkspacePresentation = presentation === "workspace";
|
||||
const isVideoWorkspace = workspaceVariant === "video";
|
||||
@ -1198,6 +1205,9 @@ export function CanvasChatPanel({
|
||||
() => normalizeCanvasToolbarConfig(rawCanvasToolbarConfig),
|
||||
[rawCanvasToolbarConfig]
|
||||
);
|
||||
const canvasAgentFeatureSettingsRef = useRef<CanvasAgentFeatureSettings>(
|
||||
normalizeCanvasAgentFeatureSettings(propCanvasAgentFeatureSettings)
|
||||
);
|
||||
const effectiveAgentEnabled = agentEnabled && !isVideoWorkspace;
|
||||
const { data: session } = useSession();
|
||||
const [input, setInput] = useState("");
|
||||
@ -1988,7 +1998,12 @@ export function CanvasChatPanel({
|
||||
|
||||
const applyCanvasViewOperation = useCallback(
|
||||
(payload: CanvasViewOperationStreamPayload) => {
|
||||
if (!CANVAS_AGENT_VIEW_OPERATION_CLIENT_ENABLED) return false;
|
||||
if (
|
||||
!CANVAS_AGENT_VIEW_OPERATION_CLIENT_ENABLED ||
|
||||
!canvasAgentFeatureSettingsRef.current.viewOperationEnabled
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (!payload || typeof payload !== "object") return false;
|
||||
const targetProjectId = String(payload.projectId || "").trim();
|
||||
if (targetProjectId && targetProjectId !== canvasId) return false;
|
||||
@ -2580,11 +2595,20 @@ export function CanvasChatPanel({
|
||||
canvasAutoModeDefaultModelId?: string | null;
|
||||
canvasDetailDefaultModelId?: string | null;
|
||||
canvasToolbarConfig?: CanvasToolbarConfig;
|
||||
canvasAgentFeatureSettings?: CanvasAgentFeatureSettings;
|
||||
openClawChatConfig?: {
|
||||
enabled?: boolean;
|
||||
baseUrl?: string;
|
||||
};
|
||||
}>("/api/site-settings?scope=canvas-chat", fetcher);
|
||||
useEffect(() => {
|
||||
canvasAgentFeatureSettingsRef.current =
|
||||
normalizeCanvasAgentFeatureSettings(
|
||||
propCanvasAgentFeatureSettings ||
|
||||
siteSettings?.canvasAgentFeatureSettings ||
|
||||
defaultCanvasAgentFeatureSettings
|
||||
);
|
||||
}, [propCanvasAgentFeatureSettings, siteSettings?.canvasAgentFeatureSettings]);
|
||||
const { data: canvasSkillTagsData } = useSWR<{
|
||||
enabled?: boolean;
|
||||
title?: string;
|
||||
|
||||
@ -13,6 +13,44 @@ export type CanvasAgentFeatureFlags = {
|
||||
|
||||
export type CanvasAgentFeatureSettings = CanvasAgentFeatureFlags;
|
||||
|
||||
export const defaultCanvasAgentFeatureSettings: CanvasAgentFeatureSettings = {
|
||||
operationProtocolEnabled: true,
|
||||
writebackMode: "operation_authoritative",
|
||||
slotEnabled: true,
|
||||
viewOperationEnabled: true,
|
||||
annotationEditEnabled: true,
|
||||
};
|
||||
|
||||
export function normalizeCanvasAgentFeatureSettings(
|
||||
value: unknown,
|
||||
fallback: CanvasAgentFeatureSettings = defaultCanvasAgentFeatureSettings
|
||||
): CanvasAgentFeatureSettings {
|
||||
const raw =
|
||||
value && typeof value === "object" && !Array.isArray(value)
|
||||
? (value as Partial<CanvasAgentFeatureSettings>)
|
||||
: {};
|
||||
|
||||
return {
|
||||
operationProtocolEnabled: normalizeCanvasAgentBoolean(
|
||||
raw.operationProtocolEnabled,
|
||||
fallback.operationProtocolEnabled
|
||||
),
|
||||
writebackMode: normalizeCanvasAgentWritebackMode(
|
||||
raw.writebackMode,
|
||||
fallback.writebackMode
|
||||
),
|
||||
slotEnabled: normalizeCanvasAgentBoolean(raw.slotEnabled, fallback.slotEnabled),
|
||||
viewOperationEnabled: normalizeCanvasAgentBoolean(
|
||||
raw.viewOperationEnabled,
|
||||
fallback.viewOperationEnabled
|
||||
),
|
||||
annotationEditEnabled: normalizeCanvasAgentBoolean(
|
||||
raw.annotationEditEnabled,
|
||||
fallback.annotationEditEnabled
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export const CANVAS_AGENT_OPERATION_PROTOCOL_ENABLED_KEY =
|
||||
"canvas_agent_operation_protocol_enabled";
|
||||
export const CANVAS_AGENT_WRITEBACK_MODE_KEY = "canvas_agent_writeback_mode";
|
||||
|
||||
@ -138,6 +138,7 @@ import {
|
||||
canvasAgentFeatureSettingsToSystemSettings,
|
||||
canApplyCanvasAgentMutations,
|
||||
getCanvasAgentFeatureFlags,
|
||||
normalizeCanvasAgentFeatureSettings,
|
||||
normalizeCanvasAgentBoolean,
|
||||
normalizeCanvasAgentWritebackMode,
|
||||
shouldStreamCanvasAgentShadowOperations,
|
||||
@ -1857,6 +1858,21 @@ test.describe("canvas chat pure helpers", () => {
|
||||
[CANVAS_AGENT_VIEW_OPERATION_ENABLED_KEY]: true,
|
||||
[CANVAS_AGENT_ANNOTATION_EDIT_ENABLED_KEY]: false,
|
||||
});
|
||||
expect(
|
||||
normalizeCanvasAgentFeatureSettings({
|
||||
operationProtocolEnabled: "off",
|
||||
writebackMode: "invalid-mode",
|
||||
slotEnabled: 0,
|
||||
viewOperationEnabled: "on",
|
||||
annotationEditEnabled: false,
|
||||
})
|
||||
).toEqual({
|
||||
operationProtocolEnabled: false,
|
||||
writebackMode: "operation_authoritative",
|
||||
slotEnabled: false,
|
||||
viewOperationEnabled: true,
|
||||
annotationEditEnabled: false,
|
||||
});
|
||||
|
||||
const invalidSettingsFallback = getCanvasAgentFeatureFlags(
|
||||
{ CANVAS_AGENT_WRITEBACK_MODE: "operation_shadow" },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user