dsh-image-gen 0.6.7 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +387 -375
- package/README.md +387 -375
- package/README.zh-CN.md +3 -3
- package/docs/assets/readme/chat-generate.webp +0 -0
- package/docs/assets/readme/other-features.webp +0 -0
- package/lib/client.js +146 -37
- package/lib/client.js.map +1 -1
- package/lib/index.js +200 -37
- package/lib/types/client/conversation-image-revisions.d.ts +2 -2
- package/lib/types/client/gallery-view.d.ts +4 -0
- package/lib/types/client/multi-model-compare.d.ts +3 -3
- package/lib/types/client/provider-pill.d.ts +1 -1
- package/lib/types/client/studio-chat-panel.d.ts +23 -0
- package/lib/types/client/studio-view.d.ts +4 -0
- package/lib/types/client/tl/diagram-landing.d.ts +75 -0
- package/lib/types/client/tl/diagram-layout.d.ts +76 -0
- package/lib/types/client/tl/diagram-measure.d.ts +40 -0
- package/lib/types/diagram.d.ts +42 -0
- package/lib/types/shared.d.ts +16 -4
- package/lib/types/studio-chat.d.ts +48 -0
- package/lib/types/studio.d.ts +3 -2
- package/lib/types/subscription/manager.d.ts +15 -0
- package/lib/types/subscription/vendors/antigravity.d.ts +10 -1
- package/lib/types/subscription/vendors/codex.d.ts +2 -0
- package/lib/types/subscription/vendors/grok.d.ts +2 -0
- package/lib/types/subscription.d.ts +7 -5
- package/package.json +10 -8
- package/skills/install-dsh-image-gen/SKILL.md +22 -22
- package/lib/types/client/inspiration-api.d.ts +0 -10
- package/lib/types/client/inspiration-detail.d.ts +0 -14
- package/lib/types/client/tldraw/generated/tldraw-assets.d.ts +0 -12
- package/lib/types/client/tldraw/generated/tldraw-css.d.ts +0 -1
- package/lib/types/client/tldraw/tldraw-style.d.ts +0 -1
- package/lib/types/client/tldraw/tldraw-workspace.d.ts +0 -1
- package/lib/types/inspiration/cache.d.ts +0 -20
- package/lib/types/inspiration/downloader.d.ts +0 -27
- package/lib/types/inspiration/routes.d.ts +0 -13
- package/lib/types/inspiration/sources.d.ts +0 -33
- package/lib/types/inspiration/types.d.ts +0 -65
- package/lib/types/inspiration/updater.d.ts +0 -7
package/lib/index.js
CHANGED
|
@@ -6006,6 +6006,15 @@ const CLOUD_IMAGE_PROVIDERS = [
|
|
|
6006
6006
|
"zhipu"
|
|
6007
6007
|
];
|
|
6008
6008
|
/**
|
|
6009
|
+
* Providers the browser workbench can drive: the BYOK cloud set plus the
|
|
6010
|
+
* logged-in subscription channels. ComfyUI stays out; it has its own workflow
|
|
6011
|
+
* pipeline and no shared request shape.
|
|
6012
|
+
*/
|
|
6013
|
+
const STUDIO_PROVIDERS = [...CLOUD_IMAGE_PROVIDERS, ...SUBSCRIPTION_PROVIDERS];
|
|
6014
|
+
/** Timeout for one subscription image call, shared by the tool path and the
|
|
6015
|
+
* studio route so neither drifts from the other. */
|
|
6016
|
+
const SUBSCRIPTION_TIMEOUT_MS = 3e5;
|
|
6017
|
+
/**
|
|
6009
6018
|
* Credential references resolved through the DSH Credentials service (BYOK).
|
|
6010
6019
|
* These are POSIX-style reference names, not environment variables: the host
|
|
6011
6020
|
* layers the process environment and its managed store behind them, so a name
|
|
@@ -7510,7 +7519,7 @@ async function editDashScopeImage(options) {
|
|
|
7510
7519
|
model: options.model,
|
|
7511
7520
|
input: { messages: [{
|
|
7512
7521
|
role: "user",
|
|
7513
|
-
content: [...options.sourceImages.map((sourceImage) => ({ image: toDataUrl$
|
|
7522
|
+
content: [...options.sourceImages.map((sourceImage) => ({ image: toDataUrl$3(sourceImage) })), { text: options.prompt }]
|
|
7514
7523
|
}] },
|
|
7515
7524
|
parameters: {
|
|
7516
7525
|
prompt_extend: true,
|
|
@@ -7527,7 +7536,7 @@ function formatSize(size) {
|
|
|
7527
7536
|
if (size === void 0 || size.length === 0) return void 0;
|
|
7528
7537
|
return size.replace("x", "*");
|
|
7529
7538
|
}
|
|
7530
|
-
function toDataUrl$
|
|
7539
|
+
function toDataUrl$3(image) {
|
|
7531
7540
|
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
7532
7541
|
}
|
|
7533
7542
|
async function requestQwenImage(options) {
|
|
@@ -8117,7 +8126,7 @@ async function editOpenAICompatibleImage(input) {
|
|
|
8117
8126
|
if (input.editFormat === "jsonImageUrlArray") {
|
|
8118
8127
|
const body = {
|
|
8119
8128
|
model: input.model,
|
|
8120
|
-
images: input.sourceImages.map((sourceImage) => ({ image_url: toDataUrl$
|
|
8129
|
+
images: input.sourceImages.map((sourceImage) => ({ image_url: toDataUrl$2(sourceImage) })),
|
|
8121
8130
|
prompt: input.prompt,
|
|
8122
8131
|
n: 1,
|
|
8123
8132
|
size: "auto",
|
|
@@ -8178,7 +8187,7 @@ function imageEndpoint$1(baseURL, operation) {
|
|
|
8178
8187
|
throw new Error("Image endpoint must be an absolute URL");
|
|
8179
8188
|
}
|
|
8180
8189
|
}
|
|
8181
|
-
function toDataUrl$
|
|
8190
|
+
function toDataUrl$2(image) {
|
|
8182
8191
|
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
8183
8192
|
}
|
|
8184
8193
|
function firstImage$1(value) {
|
|
@@ -8295,7 +8304,7 @@ async function editSeedreamImage(input) {
|
|
|
8295
8304
|
body: JSON.stringify({
|
|
8296
8305
|
model: input.model,
|
|
8297
8306
|
prompt: input.prompt,
|
|
8298
|
-
image: input.sourceImages.map(toDataUrl),
|
|
8307
|
+
image: input.sourceImages.map(toDataUrl$1),
|
|
8299
8308
|
...input.size === void 0 || input.size.length === 0 ? {} : { size: input.size },
|
|
8300
8309
|
response_format: "b64_json"
|
|
8301
8310
|
})
|
|
@@ -8323,7 +8332,7 @@ function imageEndpoint(baseURL) {
|
|
|
8323
8332
|
throw new Error("Seedream image endpoint must be an absolute URL");
|
|
8324
8333
|
}
|
|
8325
8334
|
}
|
|
8326
|
-
function toDataUrl(image) {
|
|
8335
|
+
function toDataUrl$1(image) {
|
|
8327
8336
|
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
8328
8337
|
}
|
|
8329
8338
|
function firstImage(value) {
|
|
@@ -8598,6 +8607,8 @@ const CODEX_TOKEN = "https://auth.openai.com/oauth/token";
|
|
|
8598
8607
|
const CODEX_SCOPE = "openid profile email offline_access api.connectors.read api.connectors.invoke";
|
|
8599
8608
|
/** Where the ChatGPT subscription image request goes. */
|
|
8600
8609
|
const CODEX_IMAGE_URL = "https://chatgpt.com/backend-api/codex/images/generations";
|
|
8610
|
+
/** Where the ChatGPT subscription image edit request goes. */
|
|
8611
|
+
const CODEX_IMAGE_EDIT_URL = "https://chatgpt.com/backend-api/codex/images/edits";
|
|
8601
8612
|
/** The model served by this endpoint (probed live: gpt-image-2.5-flare works
|
|
8602
8613
|
* on the ChatGPT internal generations route as of 2026-09-14). */
|
|
8603
8614
|
const CODEX_IMAGE_MODEL = "gpt-image-2.5-flare";
|
|
@@ -8695,6 +8706,8 @@ const GROK_TOKEN = "https://auth.x.ai/oauth2/token";
|
|
|
8695
8706
|
const GROK_SCOPE = "openid profile email offline_access grok-cli:access api:access conversations:read conversations:write";
|
|
8696
8707
|
/** Where the Grok subscription image request goes. */
|
|
8697
8708
|
const GROK_IMAGE_URL = "https://api.x.ai/v1/images/generations";
|
|
8709
|
+
/** Where the Grok subscription image edit request goes. */
|
|
8710
|
+
const GROK_IMAGE_EDIT_URL = "https://api.x.ai/v1/images/edits";
|
|
8698
8711
|
/** The model served by this endpoint. */
|
|
8699
8712
|
const GROK_IMAGE_MODEL = "grok-imagine-image-2.0";
|
|
8700
8713
|
/** Public client id of the grok-cli; vendor-fixed redirect on 127.0.0.1:56121. */
|
|
@@ -9039,15 +9052,22 @@ function platformOf() {
|
|
|
9039
9052
|
* Build the streamGenerateContent request body for one image generation.
|
|
9040
9053
|
* Ported from the reference buildImageRequest + the wrapping envelope: no
|
|
9041
9054
|
* tools, no systemInstruction, no thinkingConfig; responseModalities asks
|
|
9042
|
-
* for TEXT+IMAGE.
|
|
9055
|
+
* for TEXT+IMAGE. Reference images (edits) ride along as inlineData parts
|
|
9056
|
+
* before the text part, mirroring the reference implementation.
|
|
9043
9057
|
*/
|
|
9044
9058
|
function antigravityImageBody(options) {
|
|
9045
9059
|
const imageConfig = { aspectRatio: options.aspectRatio !== void 0 && ASPECT_RATIOS.includes(options.aspectRatio) ? options.aspectRatio : "1:1" };
|
|
9046
9060
|
if (options.hd === true) imageConfig.imageSize = "4K";
|
|
9061
|
+
const parts = [];
|
|
9062
|
+
for (const image of options.referenceImages ?? []) parts.push({ inlineData: {
|
|
9063
|
+
mimeType: image.mediaType,
|
|
9064
|
+
data: Buffer.from(image.data).toString("base64")
|
|
9065
|
+
} });
|
|
9066
|
+
parts.push({ text: options.prompt });
|
|
9047
9067
|
return {
|
|
9048
9068
|
contents: [{
|
|
9049
9069
|
role: "user",
|
|
9050
|
-
parts
|
|
9070
|
+
parts
|
|
9051
9071
|
}],
|
|
9052
9072
|
generationConfig: {
|
|
9053
9073
|
candidateCount: 1,
|
|
@@ -9081,7 +9101,8 @@ async function antigravityGenerateImage(options) {
|
|
|
9081
9101
|
const inner = antigravityImageBody({
|
|
9082
9102
|
prompt: options.prompt,
|
|
9083
9103
|
...options.aspectRatio !== void 0 ? { aspectRatio: options.aspectRatio } : {},
|
|
9084
|
-
...options.hd !== void 0 ? { hd: options.hd } : {}
|
|
9104
|
+
...options.hd !== void 0 ? { hd: options.hd } : {},
|
|
9105
|
+
...options.referenceImages !== void 0 ? { referenceImages: options.referenceImages } : {}
|
|
9085
9106
|
});
|
|
9086
9107
|
const body = antigravityEnvelope(options.projectId, inner);
|
|
9087
9108
|
let lastError = "no endpoint succeeded";
|
|
@@ -9296,6 +9317,8 @@ var SubscriptionManager = class {
|
|
|
9296
9317
|
const session = await this.ensureFresh(vendor);
|
|
9297
9318
|
const text = prompt.trim();
|
|
9298
9319
|
if (text.length === 0) throw new Error("prompt must be a non-empty string");
|
|
9320
|
+
const references = options.referenceImages ?? [];
|
|
9321
|
+
if (references.length > 5) throw new Error(`订阅生图最多支持 ${String(5)} 张参考图,当前 ${String(references.length)} 张`);
|
|
9299
9322
|
if (vendor === "antigravity") {
|
|
9300
9323
|
const projectId = await this.ensureAntigravityProject(session);
|
|
9301
9324
|
const aspectRatio = antigravityAspectRatioOf(options.size);
|
|
@@ -9305,6 +9328,7 @@ var SubscriptionManager = class {
|
|
|
9305
9328
|
prompt: text,
|
|
9306
9329
|
...aspectRatio !== void 0 ? { aspectRatio } : {},
|
|
9307
9330
|
hd: options.quality === "hd" || options.quality === "high",
|
|
9331
|
+
...references.length > 0 ? { referenceImages: references } : {},
|
|
9308
9332
|
...options.signal !== void 0 ? { signal: options.signal } : {}
|
|
9309
9333
|
})).b64 }];
|
|
9310
9334
|
}
|
|
@@ -9312,16 +9336,17 @@ var SubscriptionManager = class {
|
|
|
9312
9336
|
let headers;
|
|
9313
9337
|
let body;
|
|
9314
9338
|
if (vendor === "codex") {
|
|
9315
|
-
url = CODEX_IMAGE_URL;
|
|
9339
|
+
url = references.length > 0 ? CODEX_IMAGE_EDIT_URL : CODEX_IMAGE_URL;
|
|
9316
9340
|
headers = codexIdentityHeaders(session);
|
|
9317
9341
|
body = {
|
|
9318
9342
|
prompt: text,
|
|
9319
9343
|
model: CODEX_IMAGE_MODEL,
|
|
9344
|
+
...references.length > 0 ? { images: references.map((image) => ({ image_url: toDataUrl(image) })) } : {},
|
|
9320
9345
|
...options.size !== void 0 && options.size.length > 0 ? { size: options.size } : {},
|
|
9321
9346
|
...options.quality !== void 0 && options.quality.length > 0 ? { quality: options.quality } : {}
|
|
9322
9347
|
};
|
|
9323
9348
|
} else {
|
|
9324
|
-
url = GROK_IMAGE_URL;
|
|
9349
|
+
url = references.length > 0 ? GROK_IMAGE_EDIT_URL : GROK_IMAGE_URL;
|
|
9325
9350
|
headers = grokIdentityHeaders(session);
|
|
9326
9351
|
const aspect = {
|
|
9327
9352
|
"1024x1024": "1:1",
|
|
@@ -9334,6 +9359,10 @@ var SubscriptionManager = class {
|
|
|
9334
9359
|
prompt: text,
|
|
9335
9360
|
model: GROK_IMAGE_MODEL,
|
|
9336
9361
|
response_format: "b64_json",
|
|
9362
|
+
...references.length > 0 ? { images: references.map((image) => ({
|
|
9363
|
+
type: "image_url",
|
|
9364
|
+
image_url: toDataUrl(image)
|
|
9365
|
+
})) } : {},
|
|
9337
9366
|
...options.size !== void 0 && aspect[options.size] !== void 0 ? { aspect_ratio: aspect[options.size] } : {},
|
|
9338
9367
|
...level !== void 0 ? { quality: level } : {}
|
|
9339
9368
|
};
|
|
@@ -9380,6 +9409,10 @@ function antigravityAspectRatioOf(size) {
|
|
|
9380
9409
|
if (mapped !== void 0) return mapped;
|
|
9381
9410
|
return /^\d+:\d+$/.test(size) ? size : "1:1";
|
|
9382
9411
|
}
|
|
9412
|
+
/** Encode one reference image as the data URL the edit endpoints accept. */
|
|
9413
|
+
function toDataUrl(image) {
|
|
9414
|
+
return `data:${image.mediaType};base64,${Buffer.from(image.data).toString("base64")}`;
|
|
9415
|
+
}
|
|
9383
9416
|
/** Response parsing: both vendors reply in the same `{data:[{b64_json}]}` shape. */
|
|
9384
9417
|
function parseImages(payload) {
|
|
9385
9418
|
const body = typeof payload === "object" && payload !== null ? payload : {};
|
|
@@ -9524,20 +9557,21 @@ function jsonError$3(res, status, code) {
|
|
|
9524
9557
|
}
|
|
9525
9558
|
//#endregion
|
|
9526
9559
|
//#region lib/types/subscription.js
|
|
9527
|
-
/** Timeout for one subscription image call, mirroring the other adapters. */
|
|
9528
|
-
const SUBSCRIPTION_TIMEOUT_MS = 3e5;
|
|
9529
9560
|
/**
|
|
9530
9561
|
* Generate one image through a logged-in subscription account. Returns the
|
|
9531
9562
|
* same `{ data, mediaType }` contract as the API-key adapters so the caller
|
|
9532
|
-
* can feed it straight into `saveGenerated`.
|
|
9563
|
+
* can feed it straight into `saveGenerated`. With sourceImages the call is
|
|
9564
|
+
* an edit: the vendor layer picks the channel's edit endpoint.
|
|
9533
9565
|
*/
|
|
9534
9566
|
async function generateSubscriptionImage(options) {
|
|
9535
9567
|
const { manager, provider, prompt, maxBytes, signal } = options;
|
|
9536
9568
|
const size = options.size?.trim();
|
|
9569
|
+
const sourceImages = options.sourceImages ?? [];
|
|
9537
9570
|
const result = await withTimeout(manager.generate({
|
|
9538
9571
|
vendor: vendorOf(provider),
|
|
9539
9572
|
prompt,
|
|
9540
9573
|
...size !== void 0 && size.length > 0 ? { size } : {},
|
|
9574
|
+
...sourceImages.length > 0 ? { referenceImages: sourceImages } : {},
|
|
9541
9575
|
signal
|
|
9542
9576
|
}), signal);
|
|
9543
9577
|
const first = Array.isArray(result) ? result[0] : void 0;
|
|
@@ -9552,10 +9586,6 @@ async function generateSubscriptionImage(options) {
|
|
|
9552
9586
|
...typeof first.revisedPrompt === "string" && first.revisedPrompt.length > 0 ? { revisedPrompt: first.revisedPrompt } : {}
|
|
9553
9587
|
};
|
|
9554
9588
|
}
|
|
9555
|
-
/** Subscription channels are prompt-only today; edit_image is rejected outright. */
|
|
9556
|
-
function assertSubscriptionEditUnsupported(provider) {
|
|
9557
|
-
throw new Error(`edit_image with ${SUBSCRIPTION_PROVIDER_DISPLAY_NAMES[provider]} is not supported yet: the subscription channel only accepts text prompts. Retry with an API-key provider (for example google or openai) through the provider argument.`);
|
|
9558
|
-
}
|
|
9559
9589
|
/** Run one promise with the subscription timeout and the call's abort signal. */
|
|
9560
9590
|
async function withTimeout(promise, signal) {
|
|
9561
9591
|
let timer;
|
|
@@ -20467,24 +20497,63 @@ const RATIO_LABELS = {
|
|
|
20467
20497
|
"9:16": "9:16 竖屏"
|
|
20468
20498
|
};
|
|
20469
20499
|
/** Return only browser-safe capability data. */
|
|
20470
|
-
async function describeStudio(ctx, config) {
|
|
20500
|
+
async function describeStudio(ctx, config, subscriptions) {
|
|
20471
20501
|
const configuredEntries = await Promise.all(CLOUD_IMAGE_PROVIDERS.map(async (provider) => {
|
|
20472
20502
|
return [provider, await resolveApiKey(ctx, provider) !== void 0];
|
|
20473
20503
|
}));
|
|
20474
20504
|
const configured = Object.fromEntries(configuredEntries);
|
|
20475
|
-
const
|
|
20505
|
+
const cloudProfiles = CLOUD_IMAGE_PROVIDERS.map((provider) => studioProfile(config, provider, configured[provider]));
|
|
20506
|
+
const subStatuses = await Promise.all(SUBSCRIPTION_PROVIDERS.map(async (provider) => {
|
|
20507
|
+
return [provider, subscriptions !== void 0 && (await subscriptions.loginStatus(vendorOf(provider))).state === "logged-in"];
|
|
20508
|
+
}));
|
|
20509
|
+
const subProfiles = SUBSCRIPTION_PROVIDERS.map((provider) => {
|
|
20510
|
+
return subscriptionStudioProfile(provider, subStatuses.find((entry) => entry[0] === provider)?.[1] === true);
|
|
20511
|
+
});
|
|
20512
|
+
const profiles = [...cloudProfiles, ...subProfiles];
|
|
20476
20513
|
const preferred = config.provider;
|
|
20477
20514
|
return {
|
|
20478
20515
|
providers: profiles,
|
|
20479
|
-
activeProvider:
|
|
20516
|
+
activeProvider: isStudioPreferred(preferred) ? preferred : profiles.find((profile) => profile.configured)?.provider ?? "google"
|
|
20517
|
+
};
|
|
20518
|
+
}
|
|
20519
|
+
/** The persisted default provider is usable directly when the workbench can drive it. */
|
|
20520
|
+
function isStudioPreferred(preferred) {
|
|
20521
|
+
return preferred !== void 0 && CLOUD_IMAGE_PROVIDERS.includes(preferred) || preferred !== void 0 && SUBSCRIPTION_PROVIDERS.includes(preferred);
|
|
20522
|
+
}
|
|
20523
|
+
/** Fixed parameter shape for subscription channels: one "channel default" pair. */
|
|
20524
|
+
function subscriptionStudioProfile(provider, signedIn) {
|
|
20525
|
+
const channelDefault = {
|
|
20526
|
+
value: "auto",
|
|
20527
|
+
label: "通道默认"
|
|
20528
|
+
};
|
|
20529
|
+
return {
|
|
20530
|
+
provider,
|
|
20531
|
+
label: SUBSCRIPTION_DISPLAY_LABELS[provider],
|
|
20532
|
+
model: DEFAULT_SUBSCRIPTION_MODELS[provider],
|
|
20533
|
+
configured: signedIn,
|
|
20534
|
+
supportsEditing: true,
|
|
20535
|
+
ratioOptions: [channelDefault],
|
|
20536
|
+
qualityOptions: [channelDefault],
|
|
20537
|
+
defaultRatio: "auto",
|
|
20538
|
+
defaultQuality: "auto"
|
|
20480
20539
|
};
|
|
20481
20540
|
}
|
|
20541
|
+
/** Short display names for the workbench subscription rows. */
|
|
20542
|
+
const SUBSCRIPTION_DISPLAY_LABELS = {
|
|
20543
|
+
"chatgpt-sub": "ChatGPT 订阅",
|
|
20544
|
+
"grok-sub": "Grok 订阅",
|
|
20545
|
+
"google-sub": "Google 订阅"
|
|
20546
|
+
};
|
|
20482
20547
|
/** Execute one validated browser workbench request using the existing provider adapters. */
|
|
20483
|
-
async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceRoot) {
|
|
20548
|
+
async function generateFromStudio(ctx, config, input, signal, fallbackWorkspaceRoot, subscriptions) {
|
|
20549
|
+
if (isSubscriptionProvider(input.provider)) {
|
|
20550
|
+
if (subscriptions === void 0) throw new Error("订阅生图不可用:订阅管理器未初始化");
|
|
20551
|
+
assertAllowed(subscriptionStudioProfile(input.provider, true), input);
|
|
20552
|
+
return generateSubscriptionFromStudio(ctx, subscriptions, input, signal);
|
|
20553
|
+
}
|
|
20484
20554
|
assertAllowed(studioProfile(config, input.provider, true), input);
|
|
20485
20555
|
const active = resolveProvider(withProviderOverrides(config, input.provider, input.model));
|
|
20486
20556
|
if (active.provider === "comfyui") throw new Error("ComfyUI 暂未接入工作台");
|
|
20487
|
-
if (isSubscriptionProvider(active.provider)) throw new Error("订阅生图暂未接入工作台,请通过对话或设置切换默认 Provider 使用");
|
|
20488
20557
|
const wired = active;
|
|
20489
20558
|
const credential = await requireApiKey(ctx, input.provider);
|
|
20490
20559
|
const rawRefs = input.references ?? (input.reference ? [input.reference] : []);
|
|
@@ -20672,6 +20741,76 @@ async function runPool(tasks, concurrency = 2) {
|
|
|
20672
20741
|
await Promise.all(workers);
|
|
20673
20742
|
return results;
|
|
20674
20743
|
}
|
|
20744
|
+
/**
|
|
20745
|
+
* Subscription workbench generation: one prompt, channel-default parameters,
|
|
20746
|
+
* through the shared generateSubscriptionImage wrapper (timeout, b64 decode,
|
|
20747
|
+
* size check) so the downstream attachment flow is identical to API keys.
|
|
20748
|
+
*/
|
|
20749
|
+
async function generateSubscriptionFromStudio(ctx, subscriptions, input, signal) {
|
|
20750
|
+
const provider = input.provider;
|
|
20751
|
+
const rawRefs = input.references ?? (input.reference ? [input.reference] : []);
|
|
20752
|
+
if (input.mode === "edit" && rawRefs.length === 0) throw new Error("图生图需要至少一张参考图");
|
|
20753
|
+
const sourceImages = input.mode === "edit" ? await Promise.all(rawRefs.map((ref) => readStudioReference(ctx, ref, signal))) : [];
|
|
20754
|
+
const startedAt = Date.now();
|
|
20755
|
+
const count = input.count ?? 1;
|
|
20756
|
+
const generateSingle = async (index) => {
|
|
20757
|
+
const generated = await generateSubscriptionImage({
|
|
20758
|
+
manager: subscriptions,
|
|
20759
|
+
provider,
|
|
20760
|
+
prompt: input.prompt,
|
|
20761
|
+
...sourceImages.length > 0 ? { sourceImages } : {},
|
|
20762
|
+
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
20763
|
+
signal
|
|
20764
|
+
});
|
|
20765
|
+
if (!ctx.attachments.imageLimits.mediaTypes.includes(generated.mediaType)) throw new Error(`当前 DSH 不支持保存 ${generated.mediaType} 图片`);
|
|
20766
|
+
return {
|
|
20767
|
+
attachment: await ctx.attachments.saveImage({
|
|
20768
|
+
data: generated.data,
|
|
20769
|
+
mediaType: generated.mediaType,
|
|
20770
|
+
name: count > 1 ? `studio-image-${index + 1}` : "studio-image"
|
|
20771
|
+
}),
|
|
20772
|
+
output: "通道默认"
|
|
20773
|
+
};
|
|
20774
|
+
};
|
|
20775
|
+
if (count === 1) {
|
|
20776
|
+
const single = await generateSingle(0);
|
|
20777
|
+
return subscriptionResponse(input, startedAt, 1, 0, [single], single);
|
|
20778
|
+
}
|
|
20779
|
+
const poolResults = await runPool(Array.from({ length: count }, (_, i) => () => generateSingle(i)), 2);
|
|
20780
|
+
const successes = [];
|
|
20781
|
+
const errors = [];
|
|
20782
|
+
for (let i = 0; i < poolResults.length; i++) {
|
|
20783
|
+
const r = poolResults[i];
|
|
20784
|
+
if (r.status === "fulfilled") successes.push(r.value);
|
|
20785
|
+
else errors.push({
|
|
20786
|
+
index: i,
|
|
20787
|
+
message: r.reason instanceof Error ? r.reason.message : String(r.reason)
|
|
20788
|
+
});
|
|
20789
|
+
}
|
|
20790
|
+
if (successes.length === 0) {
|
|
20791
|
+
const firstReason = poolResults[0] && poolResults[0].status === "rejected" ? poolResults[0].reason : /* @__PURE__ */ new Error("订阅生图全部失败");
|
|
20792
|
+
throw firstReason instanceof Error ? firstReason : new Error(String(firstReason));
|
|
20793
|
+
}
|
|
20794
|
+
const first = successes[0];
|
|
20795
|
+
return subscriptionResponse(input, startedAt, count, errors.length, successes, first, errors);
|
|
20796
|
+
}
|
|
20797
|
+
/** Assemble the subscription workbench response with exactOptionalPropertyTypes-safe spreads. */
|
|
20798
|
+
function subscriptionResponse(input, startedAt, requestedCount, failedCount, items, primary, errors) {
|
|
20799
|
+
return {
|
|
20800
|
+
attachment: primary.attachment,
|
|
20801
|
+
output: primary.output,
|
|
20802
|
+
provider: input.provider,
|
|
20803
|
+
model: input.model,
|
|
20804
|
+
prompt: input.prompt,
|
|
20805
|
+
createdAt: Date.now(),
|
|
20806
|
+
elapsedMs: Date.now() - startedAt,
|
|
20807
|
+
requestedCount,
|
|
20808
|
+
failedCount,
|
|
20809
|
+
items,
|
|
20810
|
+
...primary.savedTo ? { savedTo: primary.savedTo } : {},
|
|
20811
|
+
...errors !== void 0 && errors.length > 0 ? { errors } : {}
|
|
20812
|
+
};
|
|
20813
|
+
}
|
|
20675
20814
|
function studioProfile(config, provider, configured) {
|
|
20676
20815
|
let active;
|
|
20677
20816
|
try {
|
|
@@ -20725,7 +20864,7 @@ function studioProfile(config, provider, configured) {
|
|
|
20725
20864
|
function profile(provider, model, configured, ratioOptions, qualityOptions, defaultRatio, defaultQuality) {
|
|
20726
20865
|
return {
|
|
20727
20866
|
provider,
|
|
20728
|
-
label:
|
|
20867
|
+
label: CLOUD_DISPLAY_LABELS[provider],
|
|
20729
20868
|
model,
|
|
20730
20869
|
configured,
|
|
20731
20870
|
supportsEditing: true,
|
|
@@ -20735,6 +20874,16 @@ function profile(provider, model, configured, ratioOptions, qualityOptions, defa
|
|
|
20735
20874
|
defaultQuality
|
|
20736
20875
|
};
|
|
20737
20876
|
}
|
|
20877
|
+
/** Display names for the BYOK cloud rows, mirroring the settings card. */
|
|
20878
|
+
const CLOUD_DISPLAY_LABELS = {
|
|
20879
|
+
google: "Google Gemini",
|
|
20880
|
+
openai: "OpenAI",
|
|
20881
|
+
"openai-compat": "OpenAI 兼容",
|
|
20882
|
+
seedream: "Seedream",
|
|
20883
|
+
dashscope: "DashScope",
|
|
20884
|
+
xai: "xAI Grok",
|
|
20885
|
+
zhipu: "智谱 GLM"
|
|
20886
|
+
};
|
|
20738
20887
|
function option(value) {
|
|
20739
20888
|
return {
|
|
20740
20889
|
value,
|
|
@@ -20788,9 +20937,6 @@ function dashScopeSize(ratio) {
|
|
|
20788
20937
|
"9:16": "928*1664"
|
|
20789
20938
|
}[ratio] ?? "1024*1024";
|
|
20790
20939
|
}
|
|
20791
|
-
function cloudProvider$1(value) {
|
|
20792
|
-
return CLOUD_IMAGE_PROVIDERS.includes(value);
|
|
20793
|
-
}
|
|
20794
20940
|
//#endregion
|
|
20795
20941
|
//#region lib/types/studio-route.js
|
|
20796
20942
|
/** Serve workbench capabilities and generation requests without exposing provider credentials. */
|
|
@@ -20804,8 +20950,10 @@ async function serveStudio(req, res, deps) {
|
|
|
20804
20950
|
if (req.method !== "POST") return jsonError$1(res, 405, "method-not-allowed");
|
|
20805
20951
|
if (!(req.headers["content-type"] ?? "").toLowerCase().startsWith("application/json")) return jsonError$1(res, 415, "json-required");
|
|
20806
20952
|
const controller = new AbortController();
|
|
20807
|
-
const
|
|
20808
|
-
const
|
|
20953
|
+
const watchdog = new AbortController();
|
|
20954
|
+
const CLOUD_WATCHDOG_MS = 15e4;
|
|
20955
|
+
let subscriptionChannel = false;
|
|
20956
|
+
const signal = AbortSignal.any([controller.signal, watchdog.signal]);
|
|
20809
20957
|
const onConnectionClose = () => {
|
|
20810
20958
|
if (!res.writableEnded) controller.abort(/* @__PURE__ */ new Error("The browser closed the image generation request."));
|
|
20811
20959
|
};
|
|
@@ -20821,11 +20969,15 @@ async function serveStudio(req, res, deps) {
|
|
|
20821
20969
|
} catch (error) {
|
|
20822
20970
|
return jsonError$1(res, 400, errorMessage(error, "invalid-request"));
|
|
20823
20971
|
}
|
|
20972
|
+
subscriptionChannel = isSubscriptionProvider(input.provider);
|
|
20973
|
+
setTimeout(() => {
|
|
20974
|
+
watchdog.abort();
|
|
20975
|
+
}, subscriptionChannel ? SUBSCRIPTION_TIMEOUT_MS : CLOUD_WATCHDOG_MS).unref?.();
|
|
20824
20976
|
const output = await deps.generate(input, signal);
|
|
20825
20977
|
if (!res.headersSent && !res.writableEnded && !res.destroyed) json$1(res, 200, output);
|
|
20826
20978
|
} catch (error) {
|
|
20827
20979
|
if (res.headersSent || res.writableEnded || res.destroyed) return;
|
|
20828
|
-
if (
|
|
20980
|
+
if (watchdog.signal.aborted) return jsonError$1(res, 504, subscriptionChannel ? "上游生成超时(订阅通道 300 秒未返回),请稍后重试" : "上游生成超时(150 秒未返回),请检查网络或代理后重试");
|
|
20829
20981
|
jsonError$1(res, controller.signal.aborted ? 499 : 502, errorMessage(error, "generation-failed"));
|
|
20830
20982
|
} finally {
|
|
20831
20983
|
res.off("close", onConnectionClose);
|
|
@@ -20837,7 +20989,7 @@ function parseStudioGenerateRequest(value) {
|
|
|
20837
20989
|
const input = record$1(value);
|
|
20838
20990
|
if (input === void 0) throw new Error("请求格式无效");
|
|
20839
20991
|
if (input.mode !== "generate" && input.mode !== "edit") throw new Error("请选择生成类型");
|
|
20840
|
-
if (!
|
|
20992
|
+
if (!studioProvider(input.provider)) throw new Error("不支持该图像 Provider");
|
|
20841
20993
|
const prompt = requiredText(input.prompt, "请输入提示词", 2e3);
|
|
20842
20994
|
const model = requiredText(input.model, "请选择模型", 200);
|
|
20843
20995
|
const ratio = requiredText(input.ratio, "请选择比例", 32);
|
|
@@ -20889,8 +21041,8 @@ function requiredText(value, message, maxLength) {
|
|
|
20889
21041
|
if (text.length > maxLength) throw new Error(`${message}(最多 ${String(maxLength)} 个字符)`);
|
|
20890
21042
|
return text;
|
|
20891
21043
|
}
|
|
20892
|
-
function
|
|
20893
|
-
return typeof value === "string" &&
|
|
21044
|
+
function studioProvider(value) {
|
|
21045
|
+
return typeof value === "string" && STUDIO_PROVIDERS.includes(value);
|
|
20894
21046
|
}
|
|
20895
21047
|
function imageMediaType(value) {
|
|
20896
21048
|
return value === "image/png" || value === "image/jpeg" || value === "image/webp" || value === "image/gif";
|
|
@@ -21386,7 +21538,7 @@ function apply(ctx, config = {}) {
|
|
|
21386
21538
|
path: STUDIO_ROUTE,
|
|
21387
21539
|
handler: (req, res) => serveStudio(req, res, {
|
|
21388
21540
|
describe: async () => {
|
|
21389
|
-
const base = await describeStudio(ctx, current());
|
|
21541
|
+
const base = await describeStudio(ctx, current(), subscriptionManager);
|
|
21390
21542
|
const workspaces = await getDshWorkspacesFull().catch(() => []);
|
|
21391
21543
|
const activeRoot = Array.from(knownWorkspaceRoots)[0] || workspaces[0]?.path || process.cwd();
|
|
21392
21544
|
return {
|
|
@@ -21397,7 +21549,7 @@ function apply(ctx, config = {}) {
|
|
|
21397
21549
|
},
|
|
21398
21550
|
generate: (input, signal) => {
|
|
21399
21551
|
const fallbackRoot = Array.from(knownWorkspaceRoots)[0] || process.cwd();
|
|
21400
|
-
return generateFromStudio(ctx, current(), input, signal, fallbackRoot);
|
|
21552
|
+
return generateFromStudio(ctx, current(), input, signal, fallbackRoot, subscriptionManager);
|
|
21401
21553
|
},
|
|
21402
21554
|
maxBodyBytes: Math.ceil(ctx.attachments.imageLimits.maxImageBytes * 1.4 * 5) + 262144
|
|
21403
21555
|
})
|
|
@@ -21560,7 +21712,7 @@ function apply(ctx, config = {}) {
|
|
|
21560
21712
|
"grok-sub",
|
|
21561
21713
|
"google-sub"
|
|
21562
21714
|
],
|
|
21563
|
-
description: "Optional provider for this call only (for example when the user asks to use a specific provider); omit to use the configured default. chatgpt-sub, grok-sub, and google-sub
|
|
21715
|
+
description: "Optional provider for this call only (for example when the user asks to use a specific provider); omit to use the configured default. chatgpt-sub, grok-sub, and google-sub edit images through the logged-in subscription account instead of an API key."
|
|
21564
21716
|
},
|
|
21565
21717
|
model: {
|
|
21566
21718
|
type: "string",
|
|
@@ -21623,7 +21775,6 @@ function apply(ctx, config = {}) {
|
|
|
21623
21775
|
output: imageOutput("Edited"),
|
|
21624
21776
|
async execute(args, exec) {
|
|
21625
21777
|
const active = resolveProvider(withProviderOverrides(current(), providerOverrideOf(args.provider), args.model));
|
|
21626
|
-
if (active.provider === "chatgpt-sub" || active.provider === "grok-sub" || active.provider === "google-sub") assertSubscriptionEditUnsupported(active.provider);
|
|
21627
21778
|
const canvasSelection = args.source === "canvas_selection";
|
|
21628
21779
|
if (canvasSelection && (args.source_attachment_id !== void 0 || Array.isArray(args.source_attachment_ids) || args.source_path !== void 0 || Array.isArray(args.source_paths))) throw new Error("edit_image source=canvas_selection cannot be combined with source_attachment_id, source_attachment_ids, source_path, or source_paths; provide exactly one selector");
|
|
21629
21780
|
const sourceImages = canvasSelection ? await resolveCanvasSelectionReferences({
|
|
@@ -21660,6 +21811,18 @@ function apply(ctx, config = {}) {
|
|
|
21660
21811
|
signal: exec.signal
|
|
21661
21812
|
}), active.provider, workflow.name, "API workflow", current(), exec, knownWorkspaceRoots);
|
|
21662
21813
|
}
|
|
21814
|
+
if (active.provider === "chatgpt-sub" || active.provider === "grok-sub" || active.provider === "google-sub") {
|
|
21815
|
+
if (sourceImages.length === 0) throw new Error("edit_image requires a reference image");
|
|
21816
|
+
return saveGenerated(ctx, await generateSubscriptionImage({
|
|
21817
|
+
manager: subscriptionManager,
|
|
21818
|
+
provider: active.provider,
|
|
21819
|
+
prompt: args.prompt,
|
|
21820
|
+
sourceImages,
|
|
21821
|
+
...args.size !== void 0 ? { size: args.size } : {},
|
|
21822
|
+
maxBytes: ctx.attachments.imageLimits.maxImageBytes,
|
|
21823
|
+
signal: exec.signal
|
|
21824
|
+
}), active.provider, active.model, "subscription edit", current(), exec, knownWorkspaceRoots);
|
|
21825
|
+
}
|
|
21663
21826
|
const credential = await requireApiKey(ctx, active.provider, "edit_image");
|
|
21664
21827
|
if (active.provider === "google") {
|
|
21665
21828
|
const aspectRatio = args.aspect_ratio ?? active.aspectRatio;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment';
|
|
2
|
-
import { type
|
|
2
|
+
import { type StudioProvider } from '../shared.js';
|
|
3
3
|
/** One regenerated version displayed in place of an original conversation image. */
|
|
4
4
|
export interface ConversationImageRevision {
|
|
5
5
|
attachment: ImageAttachmentRef;
|
|
6
6
|
prompt: string;
|
|
7
|
-
provider:
|
|
7
|
+
provider: StudioProvider;
|
|
8
8
|
model: string;
|
|
9
9
|
output: string;
|
|
10
10
|
createdAt: number;
|
|
@@ -192,6 +192,10 @@ export interface GalleryViewTabProps {
|
|
|
192
192
|
useSession?: (selector: (state: any) => any) => any;
|
|
193
193
|
useSessions?: (selector: (state: any) => any) => any;
|
|
194
194
|
useWorkspaces?: (selector: (state: any) => any) => any;
|
|
195
|
+
/** Host credential-change notifier; forwarded to the studio workbench. */
|
|
196
|
+
credentialEvents?: {
|
|
197
|
+
listen(callback: () => void): () => void;
|
|
198
|
+
} | undefined;
|
|
195
199
|
/**
|
|
196
200
|
* Right-sidebar variant (DSH official `sidebar.right.pane.tab` seat). The
|
|
197
201
|
* native conversation stays in the main area, so the composer must NOT be
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { StudioProvider, StudioProviderProfile } from '../shared.js';
|
|
2
2
|
export interface ComparisonTarget {
|
|
3
3
|
profile: StudioProviderProfile;
|
|
4
4
|
ratio: string;
|
|
@@ -6,6 +6,6 @@ export interface ComparisonTarget {
|
|
|
6
6
|
adjusted: boolean;
|
|
7
7
|
}
|
|
8
8
|
/** Map one shared output intent to settings accepted by every target model. */
|
|
9
|
-
export declare function buildComparisonTargets(profiles: readonly StudioProviderProfile[], selectedProviders: readonly
|
|
9
|
+
export declare function buildComparisonTargets(profiles: readonly StudioProviderProfile[], selectedProviders: readonly StudioProvider[], ratio: string, quality: string): ComparisonTarget[];
|
|
10
10
|
/** Start with two models, not every configured API, to avoid surprise spend. */
|
|
11
|
-
export declare function initialComparisonProviders(profiles: readonly StudioProviderProfile[], activeProvider:
|
|
11
|
+
export declare function initialComparisonProviders(profiles: readonly StudioProviderProfile[], activeProvider: StudioProvider): StudioProvider[];
|
|
@@ -45,5 +45,5 @@ export declare function pillModelOf(provider: ImageProvider, value: PillSettings
|
|
|
45
45
|
/** One fixed-position provider menu anchored to the pill button. */
|
|
46
46
|
export declare function ImageProviderPill(props: ProviderPillFace): import("react").JSX.Element | null;
|
|
47
47
|
/** Styles for the pill and its fixed-position menu; injected by the client entry. */
|
|
48
|
-
export declare const PROVIDER_PILL_STYLE = "\n.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:
|
|
48
|
+
export declare const PROVIDER_PILL_STYLE = "\n.dsh-ig-pill-root{position:relative;display:inline-flex;align-items:center;height:28px}\n.dsh-ig-pill-button{display:inline-flex;align-items:center;gap:5px;height:28px;padding:0 9px 0 8px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;background:var(--dsw-alias-bg-layer-3,#fff);color:var(--dsw-alias-label-secondary,#4b5563);font:inherit;font-size:13px;line-height:20px;cursor:pointer;transition:border-color .15s,background .15s,color .15s;-webkit-user-select:none;user-select:none}\n.dsh-ig-pill-button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed,#9ca3af);color:var(--dsw-alias-label-primary,#111827)}\n.dsh-ig-pill-button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:1px}\n.dsh-ig-pill-button:disabled{opacity:.5;cursor:not-allowed}\n.dsh-ig-pill-button-open{border-color:var(--dsw-alias-brand-primary,#4c78ff);color:var(--dsw-alias-label-primary,#111827)}\n.dsh-ig-pill-icon{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}\n.dsh-ig-pill-provider{max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-caret{display:inline-flex;flex:none;transition:transform .15s}\n.dsh-ig-pill-caret-open{transform:rotate(180deg)}\n@container (max-width:700px){\n .dsh-ig-pill-caret{display:none}\n .dsh-ig-pill-button{padding:0 7px 0 6px}\n}\n.dsh-ig-pill-menu{position:fixed;z-index:1000;min-width:264px;max-width:min(320px,calc(100vw - 24px));padding:6px;border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:12px;background:var(--dsw-alias-bg-layer-3,#fff);box-shadow:0 12px 32px rgba(0,0,0,.16),0 2px 8px rgba(0,0,0,.08);max-height:min(320px,calc(100vh - 48px));overflow-y:auto}\n.dsh-ig-pill-menu-title{padding:6px 8px 4px;font-size:11px;font-weight:600;color:var(--dsw-alias-label-tertiary,#6b7280)}\n.dsh-ig-pill-option{display:flex;width:100%;align-items:center;gap:9px;padding:7px 8px;border:0;border-radius:8px;background:none;font:inherit;color:inherit;text-align:left;cursor:pointer}\n.dsh-ig-pill-option:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2,#f3f4f6)}\n.dsh-ig-pill-option:disabled{opacity:.6;cursor:default}\n.dsh-ig-pill-option:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#4c78ff);outline-offset:-2px}\n.dsh-ig-pill-option-active{background:var(--dsw-alias-brand-primary-soft,rgba(76,120,255,.10))}\n.dsh-ig-pill-dot{flex:none;width:7px;height:7px;border-radius:50%}\n.dsh-ig-pill-dot-ok{background:#22c55e}\n.dsh-ig-pill-dot-missing{background:#9ca3af}\n.dsh-ig-pill-dot-neutral{background:#4c78ff}\n.dsh-ig-pill-option-text{flex:1;min-width:0;display:flex;flex-direction:column;gap:2px}\n.dsh-ig-pill-option-name{font-size:12.5px;font-weight:500;color:var(--dsw-alias-label-primary,#111827);line-height:1.2}\n.dsh-ig-pill-option-sub{font-size:11px;color:var(--dsw-alias-label-tertiary,#6b7280);line-height:1.2;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.dsh-ig-pill-check{flex:none;color:var(--dsw-alias-brand-primary,#4c78ff)}\n.dsh-ig-pill-error{margin-top:4px;padding:6px 8px;border-radius:8px;background:rgba(239,68,68,.08);color:#ef4444;font-size:11.5px;line-height:1.4;word-break:break-all}\n";
|
|
49
49
|
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workbench chat panel: the right-side DSH conversation surface.
|
|
3
|
+
*
|
|
4
|
+
* A dedicated archived agent session lives on the host side (studio-chat.ts);
|
|
5
|
+
* this panel pulls its projected event feed through CHAT_ROUTE with adaptive
|
|
6
|
+
* polling (?since=<seq> increments; faster while a turn is running, slower
|
|
7
|
+
* when idle, paused while the webview is hidden) and submits user turns with
|
|
8
|
+
* POST. Tool-generated images are mirrored onto the tldraw canvas through the
|
|
9
|
+
* landing queue and upserted into the gallery (the recent list refreshes via
|
|
10
|
+
* its subscription). The panel is intentionally unmount-friendly: re-pulls
|
|
11
|
+
* from seq 0 on remount, and both landing and gallery writes are deduped by
|
|
12
|
+
* attachmentId, so tab switches are safe.
|
|
13
|
+
*/
|
|
14
|
+
import { type FC } from 'react';
|
|
15
|
+
/** Workspace scoping mirrored onto gallery records; shape-compatible with StudioWorkspaceProps. */
|
|
16
|
+
export interface ChatWorkspaceScope {
|
|
17
|
+
workspaceId?: string | undefined;
|
|
18
|
+
path?: string | undefined;
|
|
19
|
+
}
|
|
20
|
+
export declare const StudioChatPanel: FC<{
|
|
21
|
+
lang: 'zh' | 'en';
|
|
22
|
+
workspace?: ChatWorkspaceScope | null | undefined;
|
|
23
|
+
}>;
|
|
@@ -15,6 +15,10 @@ export interface StudioWorkspaceProps {
|
|
|
15
15
|
}
|
|
16
16
|
export declare const StudioView: FC<{
|
|
17
17
|
locale?: LocaleService | undefined;
|
|
18
|
+
/** Host credential-change notifier; re-reads provider sign-in state silently. */
|
|
19
|
+
credentialEvents?: {
|
|
20
|
+
listen(callback: () => void): () => void;
|
|
21
|
+
} | undefined;
|
|
18
22
|
workspace?: StudioWorkspaceProps | null | undefined;
|
|
19
23
|
initialPrompt?: string | undefined;
|
|
20
24
|
/**
|