feat(canvas): cut over design agent to living form v2
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { createWriteStream } from 'node:fs';
|
||||
import { rename, rm } from 'node:fs/promises';
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { basename, dirname, extname, join } from 'node:path';
|
||||
import { Readable } from 'node:stream';
|
||||
import { pipeline } from 'node:stream/promises';
|
||||
@@ -11,17 +11,14 @@ import {
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_CODE,
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE,
|
||||
type DesignAssetUploadInput,
|
||||
type DesignCreateConversationInput,
|
||||
type DesignConfirmGenerationInput,
|
||||
type DesignCommandInput,
|
||||
type DesignCreateWorkspaceInput,
|
||||
type DesignGenerationParameters,
|
||||
type DesignRenameWorkspaceInput,
|
||||
type DesignSubmitMessageInput,
|
||||
} from '../../../shared/image-workspace';
|
||||
import {
|
||||
DesignWorkspaceModuleError,
|
||||
type DesignUserFieldOperation,
|
||||
type DesignUserInput,
|
||||
type DesignWorkspaceEventSubscription,
|
||||
} from '../../image-workspace/module';
|
||||
} from '../../../shared/image-workspace';
|
||||
import { DesignWorkspaceModuleError } from '../../image-workspace/module';
|
||||
import type { HostApiContext } from '../context';
|
||||
import {
|
||||
flushStreamingHeaders,
|
||||
@@ -40,44 +37,124 @@ function decodedSegments(pathname: string): string[] | null {
|
||||
}
|
||||
}
|
||||
|
||||
function asString(value: unknown): string {
|
||||
return typeof value === 'string' ? value : '';
|
||||
}
|
||||
|
||||
function asInteger(value: unknown): number {
|
||||
return typeof value === 'number' && Number.isInteger(value) ? value : -1;
|
||||
}
|
||||
|
||||
function asStringArray(value: unknown): string[] {
|
||||
return Array.isArray(value) && value.every((item) => typeof item === 'string')
|
||||
? value
|
||||
: [];
|
||||
}
|
||||
|
||||
function asRecord(value: unknown): Record<string, unknown> {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? value as Record<string, unknown>
|
||||
: {};
|
||||
}
|
||||
|
||||
function generationParametersFromBody(body: Record<string, unknown>): DesignGenerationParameters {
|
||||
return {
|
||||
model: asString(body.model),
|
||||
resolution: asString(body.resolution),
|
||||
aspectRatio: asString(body.aspectRatio),
|
||||
durationSeconds: body.durationSeconds === null
|
||||
? null
|
||||
: typeof body.durationSeconds === 'number' && Number.isFinite(body.durationSeconds)
|
||||
? body.durationSeconds
|
||||
: null,
|
||||
};
|
||||
function asString(value: unknown): string {
|
||||
return typeof value === 'string' ? value : '';
|
||||
}
|
||||
|
||||
const DESIGN_IMAGE_UPLOAD_MIME_TYPES = new Set([
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/webp',
|
||||
]);
|
||||
function invalidCommand(message = 'AI 设计命令无效'): never {
|
||||
throw new DesignWorkspaceModuleError(400, 'IMAGE_WORKSPACE_INVALID_COMMAND', message);
|
||||
}
|
||||
|
||||
function requiredString(value: unknown, field: string): string {
|
||||
const result = asString(value).trim();
|
||||
if (!result) invalidCommand(`${field} 不能为空`);
|
||||
return result;
|
||||
}
|
||||
|
||||
function nonNegativeInteger(value: unknown, field: string): number {
|
||||
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
||||
invalidCommand(`${field} 必须是非负整数`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function decodeFieldOperation(value: unknown): DesignUserFieldOperation {
|
||||
const operation = asRecord(value);
|
||||
const kind = operation.kind;
|
||||
const path = requiredString(operation.path, '字段路径');
|
||||
if (kind === 'set') {
|
||||
if (!Object.hasOwn(operation, 'value')) invalidCommand('字段写入缺少值');
|
||||
return { kind, path, value: operation.value };
|
||||
}
|
||||
if (kind === 'clear') {
|
||||
const resolution = operation.resolution;
|
||||
if (resolution !== undefined && resolution !== 'open' && resolution !== 'not_applicable') {
|
||||
invalidCommand('字段清空状态无效');
|
||||
}
|
||||
return { kind, path, ...(resolution === undefined ? {} : { resolution }) };
|
||||
}
|
||||
if (kind === 'set_lock') {
|
||||
if (typeof operation.locked !== 'boolean') invalidCommand('字段锁定状态无效');
|
||||
return { kind, path, locked: operation.locked };
|
||||
}
|
||||
return invalidCommand('不支持的字段操作');
|
||||
}
|
||||
|
||||
function decodeUserInput(value: unknown): DesignUserInput {
|
||||
const input = asRecord(value);
|
||||
if (input.kind === 'direct_edit') {
|
||||
if (!Array.isArray(input.operations) || input.operations.length === 0) {
|
||||
invalidCommand('直接编辑至少需要一个字段操作');
|
||||
}
|
||||
return { kind: 'direct_edit', operations: input.operations.map(decodeFieldOperation) };
|
||||
}
|
||||
if (input.kind === 'chat') {
|
||||
return { kind: 'chat', message: requiredString(input.message, '消息') };
|
||||
}
|
||||
if (input.kind === 'prompt_resolution') {
|
||||
const action = input.action;
|
||||
if (action !== 'select' && action !== 'reject') invalidCommand('决策动作无效');
|
||||
const optionId = asString(input.optionId).trim();
|
||||
if (action === 'select' && !optionId) invalidCommand('选择决策时必须提供选项');
|
||||
return {
|
||||
kind: 'prompt_resolution',
|
||||
promptId: requiredString(input.promptId, '决策问题'),
|
||||
action,
|
||||
...(optionId ? { optionId } : {}),
|
||||
};
|
||||
}
|
||||
if (input.kind === 'restore') {
|
||||
return {
|
||||
kind: 'restore',
|
||||
specificationRevisionId: requiredString(
|
||||
input.specificationRevisionId,
|
||||
'设计版本',
|
||||
),
|
||||
};
|
||||
}
|
||||
return invalidCommand('不支持的设计输入');
|
||||
}
|
||||
|
||||
function decodeCommand(workspaceId: string, body: Record<string, unknown>): DesignCommandInput {
|
||||
const common = {
|
||||
workspaceId,
|
||||
sessionId: requiredString(body.sessionId, '会话'),
|
||||
expectedDirectionRevision: nonNegativeInteger(
|
||||
body.expectedDirectionRevision,
|
||||
'设计方向版本',
|
||||
),
|
||||
clientOperationId: requiredString(body.clientOperationId, '操作标识'),
|
||||
};
|
||||
if (body.kind === 'apply_input') {
|
||||
return { kind: 'apply_input', ...common, input: decodeUserInput(body.input) };
|
||||
}
|
||||
if (body.kind === 'request_quote') {
|
||||
return {
|
||||
kind: 'request_quote',
|
||||
...common,
|
||||
specificationRevision: nonNegativeInteger(
|
||||
body.specificationRevision,
|
||||
'设计规格版本',
|
||||
),
|
||||
};
|
||||
}
|
||||
if (body.kind === 'confirm_generation') {
|
||||
return {
|
||||
kind: 'confirm_generation',
|
||||
...common,
|
||||
quoteId: requiredString(body.quoteId, '报价'),
|
||||
};
|
||||
}
|
||||
return invalidCommand('不支持的设计命令');
|
||||
}
|
||||
|
||||
const DESIGN_IMAGE_UPLOAD_MIME_TYPES = new Set(['image/jpeg', 'image/png', 'image/webp']);
|
||||
const MAX_DESIGN_IMAGE_UPLOAD_BYTES = 10 * 1024 * 1024;
|
||||
const MAX_DESIGN_IMAGE_UPLOAD_BODY_BYTES = (
|
||||
Math.ceil(MAX_DESIGN_IMAGE_UPLOAD_BYTES / 3) * 4 + 16 * 1024
|
||||
@@ -164,15 +241,7 @@ function sendData(res: ServerResponse, data: unknown): void {
|
||||
}
|
||||
|
||||
const ASSET_FILE_EXTENSIONS = new Set([
|
||||
'.gif',
|
||||
'.jpeg',
|
||||
'.jpg',
|
||||
'.mov',
|
||||
'.mp4',
|
||||
'.png',
|
||||
'.svg',
|
||||
'.webm',
|
||||
'.webp',
|
||||
'.gif', '.jpeg', '.jpg', '.mov', '.mp4', '.png', '.svg', '.webm', '.webp',
|
||||
]);
|
||||
|
||||
function safeAssetFileName(value: string, assetId: string): string {
|
||||
@@ -320,17 +389,11 @@ async function relayWorkspaceEvents(
|
||||
res: ServerResponse,
|
||||
ctx: HostApiContext,
|
||||
workspaceId: string,
|
||||
conversationId: string,
|
||||
sessionId: string,
|
||||
resumedAfterEventId?: string,
|
||||
): Promise<void> {
|
||||
if (!ctx.imageWorkspace?.openWorkspaceEvents) {
|
||||
throw new DesignWorkspaceModuleError(
|
||||
501,
|
||||
'DESIGN_EVENT_STREAM_UNAVAILABLE',
|
||||
'AI 设计任务实时状态暂时不可用',
|
||||
);
|
||||
}
|
||||
const header = req.headers['last-event-id'];
|
||||
const afterEventId = Array.isArray(header) ? header[0] : header;
|
||||
const afterEventId = (Array.isArray(header) ? header[0] : header) ?? resumedAfterEventId;
|
||||
let subscription: DesignWorkspaceEventSubscription | null = null;
|
||||
let closed = false;
|
||||
const close = () => {
|
||||
@@ -340,9 +403,9 @@ async function relayWorkspaceEvents(
|
||||
};
|
||||
res.once('close', close);
|
||||
try {
|
||||
const opened = await ctx.imageWorkspace.openWorkspaceEvents({
|
||||
const opened = await ctx.imageWorkspace!.openWorkspaceEvents({
|
||||
workspaceId,
|
||||
conversationId,
|
||||
sessionId,
|
||||
...(afterEventId ? { afterEventId } : {}),
|
||||
});
|
||||
if (closed) {
|
||||
@@ -361,9 +424,7 @@ async function relayWorkspaceEvents(
|
||||
if (!await writeStreamingChunk(
|
||||
res,
|
||||
`id: ${event.id}\nevent: ${event.type}\ndata: ${JSON.stringify(event)}\n\n`,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
)) return;
|
||||
}
|
||||
if (!res.writableEnded) res.end();
|
||||
} finally {
|
||||
@@ -408,25 +469,13 @@ export async function handleImageWorkspaceRoutes(
|
||||
if (segments.length === 1 && segments[0] === 'workspaces' && req.method === 'POST') {
|
||||
const body = await parseJsonBody<Record<string, unknown>>(req);
|
||||
const input: DesignCreateWorkspaceInput = {
|
||||
clientWorkspaceId: asString(body.clientWorkspaceId),
|
||||
title: asString(body.title),
|
||||
clientWorkspaceId: requiredString(body.clientWorkspaceId, '客户端项目标识'),
|
||||
title: requiredString(body.title, '项目名称'),
|
||||
};
|
||||
sendData(res, await ctx.imageWorkspace.createWorkspace(input));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 1 && segments[0] === 'local-data' && req.method === 'DELETE') {
|
||||
if (!ctx.imageWorkspace.reset) {
|
||||
throw new DesignWorkspaceModuleError(
|
||||
405,
|
||||
'IMAGE_WORKSPACE_RESET_NOT_ALLOWED',
|
||||
'云端 AI 设计不支持清空本地数据',
|
||||
);
|
||||
}
|
||||
sendData(res, await ctx.imageWorkspace.reset());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 2 && segments[0] === 'workspaces' && req.method === 'GET') {
|
||||
sendData(res, await ctx.imageWorkspace.getWorkspace(segments[1]));
|
||||
return true;
|
||||
@@ -441,7 +490,7 @@ export async function handleImageWorkspaceRoutes(
|
||||
const body = await parseJsonBody<Record<string, unknown>>(req);
|
||||
const input: DesignRenameWorkspaceInput = {
|
||||
workspaceId: segments[1],
|
||||
title: asString(body.title),
|
||||
title: requiredString(body.title, '项目名称'),
|
||||
};
|
||||
sendData(res, await ctx.imageWorkspace.renameWorkspace(input));
|
||||
return true;
|
||||
@@ -449,69 +498,25 @@ export async function handleImageWorkspaceRoutes(
|
||||
|
||||
if (segments.length === 3
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'conversations'
|
||||
&& segments[2] === 'commands'
|
||||
&& req.method === 'POST') {
|
||||
const body = await parseJsonBody<Record<string, unknown>>(req);
|
||||
const input: DesignCreateConversationInput = {
|
||||
workspaceId: segments[1],
|
||||
clientConversationId: asString(body.clientConversationId),
|
||||
title: asString(body.title),
|
||||
};
|
||||
sendData(res, await ctx.imageWorkspace.createConversation(input));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 4
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'conversations'
|
||||
&& req.method === 'GET') {
|
||||
const before = url.searchParams.get('before');
|
||||
sendData(
|
||||
res,
|
||||
before === null
|
||||
? await ctx.imageWorkspace.getConversation(segments[1], segments[3])
|
||||
: await ctx.imageWorkspace.getConversation(segments[1], segments[3], before),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 5
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'conversations'
|
||||
&& segments[4] === 'messages'
|
||||
&& req.method === 'POST') {
|
||||
const body = await parseJsonBody<Record<string, unknown>>(req);
|
||||
const input: DesignSubmitMessageInput = {
|
||||
workspaceId: segments[1],
|
||||
conversationId: segments[3],
|
||||
clientTurnId: asString(body.clientTurnId),
|
||||
expectedTurnRevision: asInteger(body.expectedTurnRevision),
|
||||
message: asString(body.message),
|
||||
attachmentAssetIds: asStringArray(body.attachmentAssetIds),
|
||||
};
|
||||
sendData(res, await ctx.imageWorkspace.submitMessage(input));
|
||||
sendData(res, await ctx.imageWorkspace.submitCommand(decodeCommand(segments[1], body)));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 3
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'tasks'
|
||||
&& segments[2] === 'events'
|
||||
&& req.method === 'GET') {
|
||||
sendData(res, await ctx.imageWorkspace.listTasks(segments[1]));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 4
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'generation-quotes'
|
||||
&& req.method === 'PATCH') {
|
||||
const body = await parseJsonBody<Record<string, unknown>>(req);
|
||||
sendData(res, await ctx.imageWorkspace.updateGenerationQuote({
|
||||
workspaceId: segments[1],
|
||||
quoteId: segments[3],
|
||||
finalPrompt: typeof body.finalPrompt === 'string' ? body.finalPrompt : '',
|
||||
generationParameters: generationParametersFromBody(body),
|
||||
}));
|
||||
await relayWorkspaceEvents(
|
||||
req,
|
||||
res,
|
||||
ctx,
|
||||
segments[1],
|
||||
requiredString(url.searchParams.get('sessionId'), '会话'),
|
||||
asString(url.searchParams.get('afterEventId')).trim() || undefined,
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -519,47 +524,8 @@ export async function handleImageWorkspaceRoutes(
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'assets'
|
||||
&& req.method === 'POST') {
|
||||
if (!ctx.imageWorkspace.uploadAsset) {
|
||||
throw new DesignWorkspaceModuleError(
|
||||
501,
|
||||
IMAGE_WORKSPACE_UNAVAILABLE_CODE,
|
||||
'当前环境暂不支持上传设计图片',
|
||||
);
|
||||
}
|
||||
const body = await parseAssetUploadBody(req);
|
||||
sendData(
|
||||
res,
|
||||
await ctx.imageWorkspace.uploadAsset(decodeAssetUpload(segments[1], body)),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 5
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'conversations'
|
||||
&& segments[4] === 'events'
|
||||
&& req.method === 'GET') {
|
||||
await relayWorkspaceEvents(req, res, ctx, segments[1], segments[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (segments.length === 7
|
||||
&& segments[0] === 'workspaces'
|
||||
&& segments[2] === 'conversations'
|
||||
&& segments[4] === 'quotes'
|
||||
&& segments[6] === 'confirm'
|
||||
&& req.method === 'POST') {
|
||||
const body = await parseJsonBody<Record<string, unknown>>(req);
|
||||
const input: DesignConfirmGenerationInput = {
|
||||
workspaceId: segments[1],
|
||||
conversationId: segments[3],
|
||||
quoteId: segments[5],
|
||||
clientTurnId: asString(body.clientTurnId),
|
||||
expectedTurnRevision: asInteger(body.expectedTurnRevision),
|
||||
finalPrompt: typeof body.finalPrompt === 'string' ? body.finalPrompt : '',
|
||||
generationParameters: generationParametersFromBody(asRecord(body.generationParameters)),
|
||||
};
|
||||
sendData(res, await ctx.imageWorkspace.confirmGeneration(input));
|
||||
sendData(res, await ctx.imageWorkspace.uploadAsset(decodeAssetUpload(segments[1], body)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,17 @@
|
||||
import type {
|
||||
DesignAsset,
|
||||
DesignAssetUploadInput,
|
||||
DesignCapabilities,
|
||||
DesignConversation,
|
||||
DesignCreateConversationInput,
|
||||
DesignConfirmGenerationInput,
|
||||
DesignCommandInput,
|
||||
DesignCommandResult,
|
||||
DesignCreateWorkspaceInput,
|
||||
DesignDeleteWorkspaceResult,
|
||||
DesignGenerationQuote,
|
||||
DesignGenerationQuoteUpdateInput,
|
||||
DesignGenerationTask,
|
||||
DesignRenameWorkspaceInput,
|
||||
DesignSubmitMessageInput,
|
||||
DesignWorkspace,
|
||||
DesignWorkspaceBootstrap,
|
||||
DesignWorkspaceEvent,
|
||||
DesignWorkspaceEventSubscription,
|
||||
DesignWorkspaceEventSubscriptionInput,
|
||||
} from '../../shared/image-workspace';
|
||||
|
||||
export type DesignWorkspaceEventSubscription = {
|
||||
events: AsyncIterable<DesignWorkspaceEvent>;
|
||||
close(): void;
|
||||
};
|
||||
|
||||
export type DesignWorkspaceEventSubscriptionInput = {
|
||||
workspaceId: string;
|
||||
conversationId: string;
|
||||
afterEventId?: string;
|
||||
};
|
||||
|
||||
export type CloseEventSessionsOptions = {
|
||||
accessToken?: string;
|
||||
tolerateRemoteFailure?: boolean;
|
||||
@@ -45,28 +29,18 @@ export class DesignWorkspaceModuleError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
/** Main-owned deep boundary for one canonical V2 Living Form workspace. */
|
||||
export interface DesignWorkspaceModule {
|
||||
bootstrap(): Promise<DesignWorkspaceBootstrap>;
|
||||
getCapabilities(): Promise<DesignCapabilities>;
|
||||
createWorkspace(input: DesignCreateWorkspaceInput): Promise<DesignWorkspace>;
|
||||
deleteWorkspace(workspaceId: string): Promise<DesignDeleteWorkspaceResult>;
|
||||
renameWorkspace(input: DesignRenameWorkspaceInput): Promise<DesignWorkspace>;
|
||||
getWorkspace(workspaceId: string): Promise<DesignWorkspace>;
|
||||
createConversation(input: DesignCreateConversationInput): Promise<DesignConversation>;
|
||||
getConversation(
|
||||
workspaceId: string,
|
||||
conversationId: string,
|
||||
before?: string,
|
||||
): Promise<DesignConversation>;
|
||||
submitMessage(input: DesignSubmitMessageInput): Promise<DesignConversation>;
|
||||
updateGenerationQuote(input: DesignGenerationQuoteUpdateInput): Promise<DesignGenerationQuote>;
|
||||
confirmGeneration(input: DesignConfirmGenerationInput): Promise<DesignConversation>;
|
||||
listTasks(workspaceId: string): Promise<DesignGenerationTask[]>;
|
||||
uploadAsset?(input: DesignAssetUploadInput): Promise<DesignAsset>;
|
||||
openWorkspaceEvents?(
|
||||
submitCommand(input: DesignCommandInput): Promise<DesignCommandResult>;
|
||||
uploadAsset(input: DesignAssetUploadInput): Promise<DesignAsset>;
|
||||
openWorkspaceEvents(
|
||||
input: DesignWorkspaceEventSubscriptionInput,
|
||||
): Promise<DesignWorkspaceEventSubscription>;
|
||||
closeEventSessions?(options?: CloseEventSessionsOptions): Promise<void>;
|
||||
closeEventSessions(options?: CloseEventSessionsOptions): Promise<void>;
|
||||
openAssetContent(workspaceId: string, assetId: string, range?: string): Promise<Response>;
|
||||
reset?(): Promise<DesignWorkspaceBootstrap>;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -71,10 +71,6 @@ import { shouldUseSecureWorksSquareSessionPersistence } from '../services/works-
|
||||
import { initializeRememberedPassword } from '../services/remembered-password';
|
||||
import { clearManagedWorksSquareRuntimeBestEffort } from '../services/works-square-runtime';
|
||||
import { initializeMeowaGameAssetsCredential } from '../api/routes/meowa-game-assets';
|
||||
import {
|
||||
isLocalImageWorkspaceDevelopmentEnabled,
|
||||
LocalImageWorkspace,
|
||||
} from '../image-workspace/local-workspace';
|
||||
import { WorksSquareDesignWorkspace } from '../image-workspace/works-square-workspace';
|
||||
import type { DesignWorkspaceModule } from '../image-workspace/module';
|
||||
import {
|
||||
@@ -500,20 +496,9 @@ async function initialize(): Promise<void> {
|
||||
codingProjectStore = createCodingProjectStore(codingProjectStorage);
|
||||
worksSubmissionBinding = createWorksSubmissionBindingStore(codingProjectStore);
|
||||
|
||||
const localImageWorkspaceEnabled = isLocalImageWorkspaceDevelopmentEnabled({
|
||||
isPackaged: app.isPackaged,
|
||||
configuredMode: process.env.NIANCODE_IMAGE_WORKSPACE_MODE,
|
||||
isDevelopmentServer: Boolean(process.env.VITE_DEV_SERVER_URL),
|
||||
});
|
||||
const imageWorkspace = localImageWorkspaceEnabled
|
||||
? new LocalImageWorkspace({ userDataDir: app.getPath('userData') })
|
||||
: new WorksSquareDesignWorkspace();
|
||||
const imageWorkspace = new WorksSquareDesignWorkspace();
|
||||
imageWorkspaceModule = imageWorkspace;
|
||||
if (localImageWorkspaceEnabled) {
|
||||
logger.info('AI painting workspace is using local development storage');
|
||||
} else {
|
||||
logger.info('AI painting workspace is using the Works Square cloud contract');
|
||||
}
|
||||
logger.info('AI painting workspace is using the Works Square V2 cloud contract');
|
||||
|
||||
// Set application menu
|
||||
createMenu();
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV === 'development' || Boolean(process.env.VITE_DEV_SERVER_URL);
|
||||
const imageWorkspaceMode = process.env.NIANCODE_IMAGE_WORKSPACE_MODE?.trim().toLowerCase();
|
||||
const imageWorkspaceLocalDevelopment = Boolean(process.env.VITE_DEV_SERVER_URL)
|
||||
&& (imageWorkspaceMode === 'local' || !imageWorkspaceMode);
|
||||
|
||||
const validInvokeChannels = [
|
||||
'hostapi:fetch',
|
||||
@@ -162,10 +159,6 @@ const electronAPI = {
|
||||
*/
|
||||
isDev: isDevelopment,
|
||||
|
||||
/**
|
||||
* Narrow anonymous-route exception for the explicit unpackaged image workspace adapter.
|
||||
*/
|
||||
imageWorkspaceLocalDevelopment,
|
||||
};
|
||||
|
||||
// Expose the API to the renderer process
|
||||
|
||||
Reference in New Issue
Block a user