dsh-image-gen 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dsh-image-gen contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # dsh-image-gen
2
+
3
+ English | [简体中文](README.zh-CN.md)
4
+
5
+ `dsh-image-gen` is an installable DeepSeek Harness (DSH) Bundle that contributes a native `generate_image` tool and renders durable image result cards directly in Web conversations.
6
+
7
+ ![Chat Preview](docs/assets/chat-preview.png)
8
+
9
+ ---
10
+
11
+ ## ✨ Features
12
+
13
+ - **Multi-Provider Support**: Seamless integration with **Google Gemini**, **OpenAI / OpenAI-compatible relays**, and **ByteDance Seedream (Volcengine Ark)**.
14
+ - **Secure Credential Management**: Write-only API Key storage powered by DSH `credentials` service. Keys are never logged or exposed in plaintext.
15
+ - **Web Settings Integration**: Built-in settings card in DSH Settings with auto-filled defaults and one-click endpoint reset.
16
+ - **Durable Media Persistence**: Generated images are automatically saved to DSH conversation attachment storage and rendered in rich UI cards.
17
+
18
+ ![Settings Preview](docs/assets/settings-preview.png)
19
+
20
+ ---
21
+
22
+ ## 📦 Supported Providers
23
+
24
+ | Provider | Credential Key | Default Model | Default Endpoint / Base URL | Note |
25
+ | :--- | :--- | :--- | :--- | :--- |
26
+ | **Google Gemini** | `GEMINI_API_KEY` | `gemini-3.1-flash-image` | `https://generativelanguage.googleapis.com/v1beta/interactions` | Native Gemini Interactions API |
27
+ | **OpenAI / Relay** | `OPENAI_API_KEY` | `gpt-image-2` | `https://api.openai.com/v1` | Standard OpenAI / OneAPI / NewAPI |
28
+ | **ByteDance Seedream** | `ARK_API_KEY` | `doubao-seedream-5-0-260128` | `https://ark.cn-beijing.volces.com/api/v3` | Volcengine Ark Doubao T2I |
29
+
30
+ ---
31
+
32
+ ## 🚀 Installation & Usage
33
+
34
+ ### 1. Install Plugin
35
+
36
+ In your DeepSeek Harness workspace, add the plugin to your profile (default `web`):
37
+
38
+ ```bash
39
+ # Install via npm (when published)
40
+ dsh plugin --profile web add dsh-image-gen
41
+
42
+ # Or install from local path (development)
43
+ dsh plugin --profile web add /path/to/dsh-image-gen
44
+ ```
45
+
46
+ ### 2. Configure in Web UI
47
+
48
+ 1. Open DSH Web UI (e.g. `http://localhost:3080`).
49
+ 2. Navigate to **Settings → Plugins → Image generation (图像生成)**.
50
+ 3. Select your provider, enter the API Key, verify or customize endpoint / model, and click **Save**.
51
+
52
+ ### 3. Generate Images
53
+
54
+ Start a conversation and ask the agent naturally:
55
+ > *"Generate a realistic picture of a Holstein cow in a green meadow with wildflowers under golden sunlight."*
56
+
57
+ The model will automatically invoke `generate_image`, fetch the image, and render it inline.
58
+
59
+ ---
60
+
61
+ ## 🛠️ Development
62
+
63
+ ```bash
64
+ # Install dependencies
65
+ pnpm install
66
+
67
+ # Typecheck
68
+ pnpm run typecheck
69
+
70
+ # Run unit tests
71
+ pnpm run test
72
+
73
+ # Build host and client bundles
74
+ pnpm run build
75
+
76
+ # Verify packaging
77
+ pnpm run pack:check
78
+ ```
79
+
80
+ ---
81
+
82
+ ## 📄 License
83
+
84
+ [MIT](LICENSE)
85
+
@@ -0,0 +1,85 @@
1
+ # dsh-image-gen
2
+
3
+ [English](README.md) | 简体中文
4
+
5
+ `dsh-image-gen` 是适用于 DeepSeek Harness (DSH) 的多厂商生图 Bundle 插件。它向智能体注册 `generate_image` 工具,并在 Web 对话流中直接持久化渲染高清图片卡片。
6
+
7
+ ![对话效果预览](docs/assets/chat-preview.png)
8
+
9
+ ---
10
+
11
+ ## ✨ 核心特性
12
+
13
+ - **多厂商生态支持**:原生支持 **Google Gemini**、**OpenAI / 兼容中转站 (OneAPI/NewAPI)** 以及 **字节火山方舟 Seedream (豆包绘图)**。
14
+ - **安全凭证隔离**:借助 DSH `credentials` 服务实现 API Key 仅写入保护,前端防读取,后端内存透传,杜绝密钥泄露风险。
15
+ - **开箱即用设置面板**:无缝集成至 DSH 设置中心,自动带出最新推荐模型与默认地址,支持自定义反代及一键还原官方地址。
16
+ - **附件持久化存储**:生成的图片自动入库 DSH 附件体系(Attachment),会话历史完整可溯。
17
+
18
+ ![设置面板预览](docs/assets/settings-preview.png)
19
+
20
+ ---
21
+
22
+ ## 📦 支持厂商与默认规范
23
+
24
+ | 厂商 (Provider) | 凭据环境变量 | 默认模型 | 默认接口地址 (Endpoint / Base URL) | 协议说明 |
25
+ | :------------------ | :--------------- | :--------------------------- | :-------------------------------------------------------------- | :----------------------------------- |
26
+ | **Google Gemini** | `GEMINI_API_KEY` | `gemini-3.1-flash-image` | `https://generativelanguage.googleapis.com/v1beta/interactions` | Google 原生 Interactions 协议 |
27
+ | **OpenAI / 中转站** | `OPENAI_API_KEY` | `gpt-image-2` | `https://api.openai.com/v1` | 标准 OpenAI `/v1/images/generations` |
28
+ | **字节 Seedream** | `ARK_API_KEY` | `doubao-seedream-5-0-260128` | `https://ark.cn-beijing.volces.com/api/v3` | 火山方舟豆包文生图标准网关 |
29
+
30
+ ---
31
+
32
+ ## 🚀 安装与使用
33
+
34
+ ### 1. 安装插件
35
+
36
+ 在你的 DeepSeek Harness 工作区中安装本插件(以默认 `web` profile 为例):
37
+
38
+ ```bash
39
+ # 方式 A:npm 安装
40
+ dsh plugin --profile web add dsh-image-gen
41
+
42
+ # 方式 B:本地开发引入
43
+ dsh plugin --profile web add /path/to/dsh-image-gen
44
+ ```
45
+
46
+ ### 2. 在 Web 界面配置
47
+
48
+ 1. 打开 DSH Web 页面(默认 `http://localhost:3080`)。
49
+ 2. 进入 **设置 (Settings) → 插件 (Plugins) → 图像生成 (Image generation)**。
50
+ 3. 选择厂商,填入对应的 API Key,确认或自定义接口地址与模型,点击 **保存**。
51
+
52
+ ### 3. 在对话中触发生图
53
+
54
+ 新建对话,直接向大模型输入绘图提示词即可:
55
+
56
+ > _"生成一只牛来的图片"_ 或 _"帮我画一张在雨夜霓虹街头的赛博朋克猫咪"_
57
+
58
+ 模型将自动调用 `generate_image` 工具生成图片,并在对话中渲染出精美卡片。
59
+
60
+ ---
61
+
62
+ ## 🛠️ 本地开发与构建
63
+
64
+ ```bash
65
+ # 安装依赖
66
+ pnpm install
67
+
68
+ # 类型检查
69
+ pnpm run typecheck
70
+
71
+ # 运行单元测试
72
+ pnpm run test
73
+
74
+ # 编译 Node 端与浏览器端产物
75
+ pnpm run build
76
+
77
+ # 预打包检查
78
+ pnpm run pack:check
79
+ ```
80
+
81
+ ---
82
+
83
+ ## 📄 开源协议
84
+
85
+ [MIT](LICENSE)
@@ -0,0 +1,5 @@
1
+ - insert:
2
+ - id: image-gen
3
+ name: dsh-image-gen
4
+ config:
5
+ provider: google
package/lib/client.js ADDED
@@ -0,0 +1,367 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-image-gen",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ //#region lib/types/shared.js
10
+ /** Values shared by the Host and browser Bundle faces. */
11
+ /** Browser route used by the generated-image card. */
12
+ const IMAGE_ROUTE = "/plugins/dsh-image-gen/image";
13
+ /** Namespace persisted through DSH Settings. */
14
+ const IMAGE_GENERATION_NAMESPACE = "image-generation";
15
+ /** Default endpoints and base URLs. */
16
+ const DEFAULT_GOOGLE_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/interactions";
17
+ const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
18
+ const DEFAULT_SEEDREAM_BASE_URL = "https://ark.cn-beijing.volces.com/api/v3";
19
+ const DEFAULT_MODELS = {
20
+ google: "gemini-3.1-flash-image",
21
+ openai: "gpt-image-2",
22
+ seedream: "doubao-seedream-5-0-260128"
23
+ };
24
+ const DEFAULT_BASE_URLS = {
25
+ google: DEFAULT_GOOGLE_ENDPOINT,
26
+ openai: DEFAULT_OPENAI_BASE_URL,
27
+ seedream: DEFAULT_SEEDREAM_BASE_URL
28
+ };
29
+ //#endregion
30
+ //#region lib/types/client/index.js
31
+ /** Web settings and generated-image cards contributed by the Bundle. */
32
+ const KEY_REF = {
33
+ google: "GEMINI_API_KEY",
34
+ openai: "OPENAI_API_KEY",
35
+ seedream: "ARK_API_KEY"
36
+ };
37
+ const PROVIDER_LABEL = {
38
+ google: "Google Gemini",
39
+ openai: "OpenAI / 中转站",
40
+ seedream: "字节 Seedream"
41
+ };
42
+ const STYLE = `
43
+ .dsh-ig-card{list-style:none;border:1px solid var(--border-color,#e5e7eb);border-radius:14px;background:var(--surface-color,#fff);overflow:hidden}.dsh-ig-head{width:100%;padding:18px 20px;border:0;background:transparent;text-align:left;cursor:pointer;display:flex;justify-content:space-between;gap:16px;color:inherit}.dsh-ig-title{display:block;font-size:16px;font-weight:650}.dsh-ig-desc{display:block;margin-top:5px;color:#7b818b;font-size:14px}.dsh-ig-body{padding:0 20px 20px;border-top:1px solid var(--border-color,#eee)}.dsh-ig-field{display:grid;gap:7px;margin-top:18px}.dsh-ig-label{font-size:14px;font-weight:600}.dsh-ig-input{box-sizing:border-box;width:100%;padding:10px 12px;border:1px solid #d7dbe0;border-radius:9px;background:transparent;color:inherit}.dsh-ig-input-group{display:flex;gap:8px;align-items:center}.dsh-ig-btn-reset{padding:9px 14px;border:1px solid #d7dbe0;border-radius:9px;background:var(--surface-color,#f9fafb);color:inherit;font-size:13px;cursor:pointer;white-space:nowrap;transition:background .15s}.dsh-ig-btn-reset:hover{background:#edf0f3}.dsh-ig-hint,.dsh-ig-status{margin:0;color:#7b818b;font-size:12px}.dsh-ig-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:16px}.dsh-ig-save{border:0;border-radius:9px;padding:9px 16px;background:#4c78ff;color:#fff;cursor:pointer}.dsh-ig-save:disabled{opacity:.55;cursor:default}.dsh-ig-result{display:grid;gap:10px;max-width:520px}.dsh-ig-result-title{font-size:14px;font-weight:600}.dsh-ig-image{display:block;max-width:100%;max-height:520px;border-radius:14px;background:#f2f3f5}.dsh-ig-error{color:#d33;font-size:13px}.dsh-ig-loading{color:#7b818b;font-size:13px}`;
44
+ /** Required browser services. */
45
+ const inject = [
46
+ "slots",
47
+ "connection",
48
+ "remote",
49
+ "settingsScope"
50
+ ];
51
+ /** Mount the settings card and generated-image card. */
52
+ function apply(ctx) {
53
+ const { api } = ctx.get("connection");
54
+ const scope = ctx.settingsScope.bind({ namespace: IMAGE_GENERATION_NAMESPACE });
55
+ ctx.effect(() => {
56
+ const style = document.createElement("style");
57
+ style.dataset.plugin = "dsh-image-gen";
58
+ style.textContent = STYLE;
59
+ document.head.appendChild(style);
60
+ return () => {
61
+ style.remove();
62
+ };
63
+ }, "dsh-image-gen: styles");
64
+ ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
65
+ name: "settings.plugin.item",
66
+ id: "dsh-image-gen",
67
+ order: 30,
68
+ inject: () => ({
69
+ scope,
70
+ credentials: api.credentials
71
+ })
72
+ }, ImageGenerationSettingsCard));
73
+ ctx.slots.inject("tool.call.toolview", () => ctx.slots.register({
74
+ name: "tool.call.toolview",
75
+ key: "generate_image"
76
+ }, GeneratedImageCard));
77
+ }
78
+ /** Edit provider settings and its write-only API credential. */
79
+ function ImageGenerationSettingsCard(props) {
80
+ const [open, setOpen] = (0, react.useState)(false);
81
+ const [snapshot, setSnapshot] = (0, react.useState)(() => props.scope.getSnapshot());
82
+ const [provider, setProvider] = (0, react.useState)("google");
83
+ const [model, setModel] = (0, react.useState)("");
84
+ const [baseURL, setBaseURL] = (0, react.useState)("");
85
+ const [key, setKey] = (0, react.useState)("");
86
+ const [configured, setConfigured] = (0, react.useState)();
87
+ const [saving, setSaving] = (0, react.useState)(false);
88
+ const [message, setMessage] = (0, react.useState)("");
89
+ (0, react.useEffect)(() => props.scope.subscribe(() => {
90
+ setSnapshot(props.scope.getSnapshot());
91
+ }), [props.scope]);
92
+ (0, react.useEffect)(() => {
93
+ const value = snapshot.value;
94
+ const next = value?.provider ?? "google";
95
+ setProvider(next);
96
+ setModel(modelOf(next, value));
97
+ setBaseURL(baseURLOf(next, value));
98
+ }, [snapshot]);
99
+ (0, react.useEffect)(() => {
100
+ let active = true;
101
+ props.credentials.describe({ refs: [KEY_REF[provider]] }).then((response) => {
102
+ if (active) setConfigured(response.result.ok ? response.result.value.credentials[KEY_REF[provider]]?.configured ?? false : void 0);
103
+ }).catch(() => {
104
+ if (active) setConfigured(void 0);
105
+ });
106
+ return () => {
107
+ active = false;
108
+ };
109
+ }, [props.credentials, provider]);
110
+ const save = async (event) => {
111
+ event.preventDefault();
112
+ setSaving(true);
113
+ setMessage("");
114
+ try {
115
+ await props.scope.set("provider", provider);
116
+ await props.scope.set(provider === "google" ? "googleModel" : provider === "openai" ? "openaiModel" : "seedreamModel", model);
117
+ await props.scope.set(provider === "google" ? "googleEndpoint" : provider === "openai" ? "openaiBaseURL" : "seedreamBaseURL", baseURL);
118
+ if (key.trim().length > 0) {
119
+ const response = await props.credentials.set({
120
+ ref: KEY_REF[provider],
121
+ value: key.trim()
122
+ });
123
+ if (!response.result.ok) throw new Error(response.result.error.message);
124
+ setKey("");
125
+ setConfigured(true);
126
+ }
127
+ setMessage("已保存");
128
+ } catch (cause) {
129
+ setMessage(cause instanceof Error ? cause.message : String(cause));
130
+ } finally {
131
+ setSaving(false);
132
+ }
133
+ };
134
+ const keyStatus = configured === void 0 ? "正在检查 API Key…" : configured ? "已配置 API Key" : "尚未配置 API Key";
135
+ return (0, react_jsx_runtime.jsxs)("li", {
136
+ className: "dsh-ig-card",
137
+ children: [(0, react_jsx_runtime.jsxs)("button", {
138
+ type: "button",
139
+ className: "dsh-ig-head",
140
+ "aria-expanded": open,
141
+ onClick: () => {
142
+ setOpen((value) => !value);
143
+ },
144
+ children: [(0, react_jsx_runtime.jsxs)("span", { children: [(0, react_jsx_runtime.jsx)("span", {
145
+ className: "dsh-ig-title",
146
+ children: "图像生成"
147
+ }), (0, react_jsx_runtime.jsx)("span", {
148
+ className: "dsh-ig-desc",
149
+ children: "选择厂商并配置生图模型。"
150
+ })] }), (0, react_jsx_runtime.jsx)("span", {
151
+ "aria-hidden": "true",
152
+ children: open ? "⌃" : "⌄"
153
+ })]
154
+ }), open ? (0, react_jsx_runtime.jsxs)("form", {
155
+ className: "dsh-ig-body",
156
+ onSubmit: (event) => {
157
+ save(event);
158
+ },
159
+ children: [
160
+ (0, react_jsx_runtime.jsxs)("label", {
161
+ className: "dsh-ig-field",
162
+ children: [
163
+ (0, react_jsx_runtime.jsx)("span", {
164
+ className: "dsh-ig-label",
165
+ children: "Provider"
166
+ }),
167
+ (0, react_jsx_runtime.jsxs)("select", {
168
+ className: "dsh-ig-input",
169
+ value: provider,
170
+ onChange: (event) => {
171
+ const next = event.target.value;
172
+ setProvider(next);
173
+ setModel(modelOf(next, snapshot.value));
174
+ setBaseURL(baseURLOf(next, snapshot.value));
175
+ setKey("");
176
+ },
177
+ children: [
178
+ (0, react_jsx_runtime.jsx)("option", {
179
+ value: "google",
180
+ children: "Google Gemini"
181
+ }),
182
+ (0, react_jsx_runtime.jsx)("option", {
183
+ value: "openai",
184
+ children: "OpenAI / 中转站"
185
+ }),
186
+ (0, react_jsx_runtime.jsx)("option", {
187
+ value: "seedream",
188
+ children: "字节 Seedream"
189
+ })
190
+ ]
191
+ }),
192
+ (0, react_jsx_runtime.jsx)("span", {
193
+ className: "dsh-ig-hint",
194
+ children: PROVIDER_LABEL[provider]
195
+ })
196
+ ]
197
+ }),
198
+ (0, react_jsx_runtime.jsxs)("label", {
199
+ className: "dsh-ig-field",
200
+ children: [
201
+ (0, react_jsx_runtime.jsxs)("span", {
202
+ className: "dsh-ig-label",
203
+ children: [PROVIDER_LABEL[provider], " API Key"]
204
+ }),
205
+ (0, react_jsx_runtime.jsx)("input", {
206
+ className: "dsh-ig-input",
207
+ type: "password",
208
+ autoComplete: "off",
209
+ value: key,
210
+ onChange: (event) => {
211
+ setKey(event.target.value);
212
+ },
213
+ placeholder: configured ? "留空即可保留已配置的 Key" : ""
214
+ }),
215
+ (0, react_jsx_runtime.jsxs)("span", {
216
+ className: "dsh-ig-hint",
217
+ children: [
218
+ "安全保存为 ",
219
+ KEY_REF[provider],
220
+ ";页面不会读回明文。"
221
+ ]
222
+ })
223
+ ]
224
+ }),
225
+ (0, react_jsx_runtime.jsxs)("label", {
226
+ className: "dsh-ig-field",
227
+ children: [
228
+ (0, react_jsx_runtime.jsx)("span", {
229
+ className: "dsh-ig-label",
230
+ children: "接口地址"
231
+ }),
232
+ (0, react_jsx_runtime.jsxs)("div", {
233
+ className: "dsh-ig-input-group",
234
+ children: [(0, react_jsx_runtime.jsx)("input", {
235
+ className: "dsh-ig-input",
236
+ type: "url",
237
+ value: baseURL,
238
+ onChange: (event) => {
239
+ setBaseURL(event.target.value);
240
+ },
241
+ required: true
242
+ }), (0, react_jsx_runtime.jsx)("button", {
243
+ type: "button",
244
+ className: "dsh-ig-btn-reset",
245
+ title: "重置为默认官方地址",
246
+ onClick: () => {
247
+ setBaseURL(DEFAULT_BASE_URLS[provider]);
248
+ },
249
+ children: "重置"
250
+ })]
251
+ }),
252
+ (0, react_jsx_runtime.jsx)("span", {
253
+ className: "dsh-ig-hint",
254
+ children: provider === "google" ? "Google 官方地址或反代端点(全路径)。" : provider === "openai" ? "中转站请填其 OpenAI 兼容的 /v1 地址。" : "火山方舟兼容的 /api/v3 地址。"
255
+ })
256
+ ]
257
+ }),
258
+ (0, react_jsx_runtime.jsxs)("label", {
259
+ className: "dsh-ig-field",
260
+ children: [(0, react_jsx_runtime.jsx)("span", {
261
+ className: "dsh-ig-label",
262
+ children: "模型"
263
+ }), (0, react_jsx_runtime.jsx)("input", {
264
+ className: "dsh-ig-input",
265
+ value: model,
266
+ onChange: (event) => {
267
+ setModel(event.target.value);
268
+ },
269
+ required: true
270
+ })]
271
+ }),
272
+ (0, react_jsx_runtime.jsxs)("div", {
273
+ className: "dsh-ig-actions",
274
+ children: [(0, react_jsx_runtime.jsx)("p", {
275
+ className: "dsh-ig-status",
276
+ role: "status",
277
+ children: message || keyStatus
278
+ }), (0, react_jsx_runtime.jsx)("button", {
279
+ className: "dsh-ig-save",
280
+ type: "submit",
281
+ disabled: saving || !snapshot.writable,
282
+ children: saving ? "保存中…" : "保存"
283
+ })]
284
+ })
285
+ ]
286
+ }) : null]
287
+ });
288
+ }
289
+ /** Render the durable attachment referenced by a completed image tool call. */
290
+ function GeneratedImageCard(props) {
291
+ const attachment = imageRef(props.block);
292
+ const [url, setUrl] = (0, react.useState)();
293
+ const [error, setError] = (0, react.useState)();
294
+ (0, react.useEffect)(() => {
295
+ if (attachment === void 0) return;
296
+ const controller = new AbortController();
297
+ let objectUrl;
298
+ fetch(IMAGE_ROUTE, {
299
+ method: "POST",
300
+ signal: controller.signal,
301
+ headers: { "content-type": "application/json" },
302
+ body: JSON.stringify({ attachment })
303
+ }).then(async (response) => {
304
+ if (!response.ok) throw new Error(`图片读取失败 (${String(response.status)})`);
305
+ const blob = await response.blob();
306
+ if (controller.signal.aborted) return;
307
+ objectUrl = URL.createObjectURL(blob);
308
+ setUrl(objectUrl);
309
+ }).catch((cause) => {
310
+ if (!controller.signal.aborted) setError(cause instanceof Error ? cause.message : String(cause));
311
+ });
312
+ return () => {
313
+ controller.abort();
314
+ if (objectUrl !== void 0) URL.revokeObjectURL(objectUrl);
315
+ };
316
+ }, [attachment?.attachmentId]);
317
+ if (attachment === void 0) return (0, react_jsx_runtime.jsx)("div", {
318
+ className: "dsh-ig-loading",
319
+ children: "正在生成图片…"
320
+ });
321
+ return (0, react_jsx_runtime.jsxs)("section", {
322
+ className: "dsh-ig-result",
323
+ "aria-label": "Generated image",
324
+ children: [
325
+ (0, react_jsx_runtime.jsx)("div", {
326
+ className: "dsh-ig-result-title",
327
+ children: "Generated image"
328
+ }),
329
+ error !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
330
+ className: "dsh-ig-error",
331
+ children: error
332
+ }) : null,
333
+ url === void 0 && error === void 0 ? (0, react_jsx_runtime.jsx)("div", {
334
+ className: "dsh-ig-loading",
335
+ children: "正在加载图片…"
336
+ }) : null,
337
+ url !== void 0 ? (0, react_jsx_runtime.jsx)("img", {
338
+ className: "dsh-ig-image",
339
+ src: url,
340
+ alt: attachment.name ?? "Generated image"
341
+ }) : null
342
+ ]
343
+ });
344
+ }
345
+ function modelOf(provider, value) {
346
+ const stored = provider === "google" ? value?.googleModel : provider === "openai" ? value?.openaiModel : value?.seedreamModel;
347
+ return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_MODELS[provider];
348
+ }
349
+ function baseURLOf(provider, value) {
350
+ const stored = provider === "google" ? value?.googleEndpoint : provider === "openai" ? value?.openaiBaseURL : value?.seedreamBaseURL;
351
+ return typeof stored === "string" && stored.length > 0 ? stored : DEFAULT_BASE_URLS[provider];
352
+ }
353
+ function imageRef(block) {
354
+ if (!("kind" in block) || block.resultView?.card !== "generic") return void 0;
355
+ const image = block.resultView.content?.find((item) => item.type === "image");
356
+ return image?.type === "image" ? image.attachment : void 0;
357
+ }
358
+ //#endregion
359
+ exports.GeneratedImageCard = GeneratedImageCard;
360
+ exports.ImageGenerationSettingsCard = ImageGenerationSettingsCard;
361
+ exports.apply = apply;
362
+ exports.inject = inject;
363
+ return module.exports;
364
+ }
365
+ });
366
+
367
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","names":["useState","_jsxs","_jsx"],"sources":["types/shared.js","types/client/index.js"],"sourcesContent":["/** Values shared by the Host and browser Bundle faces. */\n/** Browser route used by the generated-image card. */\nexport const IMAGE_ROUTE = '/plugins/dsh-image-gen/image';\n/** Namespace persisted through DSH Settings. */\nexport const IMAGE_GENERATION_NAMESPACE = 'image-generation';\n/** Supported providers. */\nexport const IMAGE_PROVIDERS = ['google', 'openai', 'seedream'];\n/** Default endpoints and base URLs. */\nexport const DEFAULT_GOOGLE_ENDPOINT = 'https://generativelanguage.googleapis.com/v1beta/interactions';\nexport const DEFAULT_OPENAI_BASE_URL = 'https://api.openai.com/v1';\nexport const DEFAULT_SEEDREAM_BASE_URL = 'https://ark.cn-beijing.volces.com/api/v3';\n/** Default model names. */\nexport const DEFAULT_GOOGLE_MODEL = 'gemini-3.1-flash-image';\nexport const DEFAULT_OPENAI_MODEL = 'gpt-image-2';\nexport const DEFAULT_SEEDREAM_MODEL = 'doubao-seedream-5-0-260128';\nexport const DEFAULT_MODELS = {\n google: DEFAULT_GOOGLE_MODEL,\n openai: DEFAULT_OPENAI_MODEL,\n seedream: DEFAULT_SEEDREAM_MODEL,\n};\nexport const DEFAULT_BASE_URLS = {\n google: DEFAULT_GOOGLE_ENDPOINT,\n openai: DEFAULT_OPENAI_BASE_URL,\n seedream: DEFAULT_SEEDREAM_BASE_URL,\n};\n//# sourceMappingURL=shared.js.map","import { jsx as _jsx, jsxs as _jsxs } from \"react/jsx-runtime\";\n/** Web settings and generated-image cards contributed by the Bundle. */\nimport { useEffect, useState } from 'react';\nimport { DEFAULT_BASE_URLS, DEFAULT_MODELS, IMAGE_GENERATION_NAMESPACE, IMAGE_ROUTE, } from '../shared.js';\nconst KEY_REF = { google: 'GEMINI_API_KEY', openai: 'OPENAI_API_KEY', seedream: 'ARK_API_KEY' };\nconst PROVIDER_LABEL = { google: 'Google Gemini', openai: 'OpenAI / 中转站', seedream: '字节 Seedream' };\nconst STYLE = `\n.dsh-ig-card{list-style:none;border:1px solid var(--border-color,#e5e7eb);border-radius:14px;background:var(--surface-color,#fff);overflow:hidden}.dsh-ig-head{width:100%;padding:18px 20px;border:0;background:transparent;text-align:left;cursor:pointer;display:flex;justify-content:space-between;gap:16px;color:inherit}.dsh-ig-title{display:block;font-size:16px;font-weight:650}.dsh-ig-desc{display:block;margin-top:5px;color:#7b818b;font-size:14px}.dsh-ig-body{padding:0 20px 20px;border-top:1px solid var(--border-color,#eee)}.dsh-ig-field{display:grid;gap:7px;margin-top:18px}.dsh-ig-label{font-size:14px;font-weight:600}.dsh-ig-input{box-sizing:border-box;width:100%;padding:10px 12px;border:1px solid #d7dbe0;border-radius:9px;background:transparent;color:inherit}.dsh-ig-input-group{display:flex;gap:8px;align-items:center}.dsh-ig-btn-reset{padding:9px 14px;border:1px solid #d7dbe0;border-radius:9px;background:var(--surface-color,#f9fafb);color:inherit;font-size:13px;cursor:pointer;white-space:nowrap;transition:background .15s}.dsh-ig-btn-reset:hover{background:#edf0f3}.dsh-ig-hint,.dsh-ig-status{margin:0;color:#7b818b;font-size:12px}.dsh-ig-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:16px}.dsh-ig-save{border:0;border-radius:9px;padding:9px 16px;background:#4c78ff;color:#fff;cursor:pointer}.dsh-ig-save:disabled{opacity:.55;cursor:default}.dsh-ig-result{display:grid;gap:10px;max-width:520px}.dsh-ig-result-title{font-size:14px;font-weight:600}.dsh-ig-image{display:block;max-width:100%;max-height:520px;border-radius:14px;background:#f2f3f5}.dsh-ig-error{color:#d33;font-size:13px}.dsh-ig-loading{color:#7b818b;font-size:13px}`;\n/** Required browser services. */\nexport const inject = ['slots', 'connection', 'remote', 'settingsScope'];\n/** Mount the settings card and generated-image card. */\nexport function apply(ctx) {\n const { api } = ctx.get('connection');\n const scope = ctx.settingsScope.bind({ namespace: IMAGE_GENERATION_NAMESPACE });\n ctx.effect(() => {\n const style = document.createElement('style');\n style.dataset.plugin = 'dsh-image-gen';\n style.textContent = STYLE;\n document.head.appendChild(style);\n return () => { style.remove(); };\n }, 'dsh-image-gen: styles');\n ctx.slots.inject('settings.plugin.item', () => ctx.slots.register({ name: 'settings.plugin.item', id: 'dsh-image-gen', order: 30, inject: () => ({ scope, credentials: api.credentials }) }, ImageGenerationSettingsCard));\n ctx.slots.inject('tool.call.toolview', () => ctx.slots.register({ name: 'tool.call.toolview', key: 'generate_image' }, GeneratedImageCard));\n}\n/** Edit provider settings and its write-only API credential. */\nexport function ImageGenerationSettingsCard(props) {\n const [open, setOpen] = useState(false);\n const [snapshot, setSnapshot] = useState(() => props.scope.getSnapshot());\n const [provider, setProvider] = useState('google');\n const [model, setModel] = useState('');\n const [baseURL, setBaseURL] = useState('');\n const [key, setKey] = useState('');\n const [configured, setConfigured] = useState();\n const [saving, setSaving] = useState(false);\n const [message, setMessage] = useState('');\n useEffect(() => props.scope.subscribe(() => { setSnapshot(props.scope.getSnapshot()); }), [props.scope]);\n useEffect(() => {\n const value = snapshot.value;\n const next = value?.provider ?? 'google';\n setProvider(next);\n setModel(modelOf(next, value));\n setBaseURL(baseURLOf(next, value));\n }, [snapshot]);\n useEffect(() => {\n let active = true;\n void props.credentials.describe({ refs: [KEY_REF[provider]] }).then(response => {\n if (active)\n setConfigured(response.result.ok ? response.result.value.credentials[KEY_REF[provider]]?.configured ?? false : undefined);\n }).catch(() => { if (active)\n setConfigured(undefined); });\n return () => { active = false; };\n }, [props.credentials, provider]);\n const save = async (event) => {\n event.preventDefault();\n setSaving(true);\n setMessage('');\n try {\n await props.scope.set('provider', provider);\n await props.scope.set(provider === 'google' ? 'googleModel' : provider === 'openai' ? 'openaiModel' : 'seedreamModel', model);\n await props.scope.set(provider === 'google' ? 'googleEndpoint' : provider === 'openai' ? 'openaiBaseURL' : 'seedreamBaseURL', baseURL);\n if (key.trim().length > 0) {\n const response = await props.credentials.set({ ref: KEY_REF[provider], value: key.trim() });\n if (!response.result.ok)\n throw new Error(response.result.error.message);\n setKey('');\n setConfigured(true);\n }\n setMessage('已保存');\n }\n catch (cause) {\n setMessage(cause instanceof Error ? cause.message : String(cause));\n }\n finally {\n setSaving(false);\n }\n };\n const keyStatus = configured === undefined ? '正在检查 API Key…' : configured ? '已配置 API Key' : '尚未配置 API Key';\n return _jsxs(\"li\", { className: \"dsh-ig-card\", children: [_jsxs(\"button\", { type: \"button\", className: \"dsh-ig-head\", \"aria-expanded\": open, onClick: () => { setOpen(value => !value); }, children: [_jsxs(\"span\", { children: [_jsx(\"span\", { className: \"dsh-ig-title\", children: \"\\u56FE\\u50CF\\u751F\\u6210\" }), _jsx(\"span\", { className: \"dsh-ig-desc\", children: \"\\u9009\\u62E9\\u5382\\u5546\\u5E76\\u914D\\u7F6E\\u751F\\u56FE\\u6A21\\u578B\\u3002\" })] }), _jsx(\"span\", { \"aria-hidden\": \"true\", children: open ? '⌃' : '⌄' })] }), open ? _jsxs(\"form\", { className: \"dsh-ig-body\", onSubmit: (event) => { void save(event); }, children: [_jsxs(\"label\", { className: \"dsh-ig-field\", children: [_jsx(\"span\", { className: \"dsh-ig-label\", children: \"Provider\" }), _jsxs(\"select\", { className: \"dsh-ig-input\", value: provider, onChange: event => { const next = event.target.value; setProvider(next); setModel(modelOf(next, snapshot.value)); setBaseURL(baseURLOf(next, snapshot.value)); setKey(''); }, children: [_jsx(\"option\", { value: \"google\", children: \"Google Gemini\" }), _jsx(\"option\", { value: \"openai\", children: \"OpenAI / \\u4E2D\\u8F6C\\u7AD9\" }), _jsx(\"option\", { value: \"seedream\", children: \"\\u5B57\\u8282 Seedream\" })] }), _jsx(\"span\", { className: \"dsh-ig-hint\", children: PROVIDER_LABEL[provider] })] }), _jsxs(\"label\", { className: \"dsh-ig-field\", children: [_jsxs(\"span\", { className: \"dsh-ig-label\", children: [PROVIDER_LABEL[provider], \" API Key\"] }), _jsx(\"input\", { className: \"dsh-ig-input\", type: \"password\", autoComplete: \"off\", value: key, onChange: event => { setKey(event.target.value); }, placeholder: configured ? '留空即可保留已配置的 Key' : '' }), _jsxs(\"span\", { className: \"dsh-ig-hint\", children: [\"\\u5B89\\u5168\\u4FDD\\u5B58\\u4E3A \", KEY_REF[provider], \"\\uFF1B\\u9875\\u9762\\u4E0D\\u4F1A\\u8BFB\\u56DE\\u660E\\u6587\\u3002\"] })] }), _jsxs(\"label\", { className: \"dsh-ig-field\", children: [_jsx(\"span\", { className: \"dsh-ig-label\", children: \"\\u63A5\\u53E3\\u5730\\u5740\" }), _jsxs(\"div\", { className: \"dsh-ig-input-group\", children: [_jsx(\"input\", { className: \"dsh-ig-input\", type: \"url\", value: baseURL, onChange: event => { setBaseURL(event.target.value); }, required: true }), _jsx(\"button\", { type: \"button\", className: \"dsh-ig-btn-reset\", title: \"\\u91CD\\u7F6E\\u4E3A\\u9ED8\\u8BA4\\u5B98\\u65B9\\u5730\\u5740\", onClick: () => { setBaseURL(DEFAULT_BASE_URLS[provider]); }, children: \"\\u91CD\\u7F6E\" })] }), _jsx(\"span\", { className: \"dsh-ig-hint\", children: provider === 'google' ? 'Google 官方地址或反代端点(全路径)。' : provider === 'openai' ? '中转站请填其 OpenAI 兼容的 /v1 地址。' : '火山方舟兼容的 /api/v3 地址。' })] }), _jsxs(\"label\", { className: \"dsh-ig-field\", children: [_jsx(\"span\", { className: \"dsh-ig-label\", children: \"\\u6A21\\u578B\" }), _jsx(\"input\", { className: \"dsh-ig-input\", value: model, onChange: event => { setModel(event.target.value); }, required: true })] }), _jsxs(\"div\", { className: \"dsh-ig-actions\", children: [_jsx(\"p\", { className: \"dsh-ig-status\", role: \"status\", children: message || keyStatus }), _jsx(\"button\", { className: \"dsh-ig-save\", type: \"submit\", disabled: saving || !snapshot.writable, children: saving ? '保存中…' : '保存' })] })] }) : null] });\n}\n/** Render the durable attachment referenced by a completed image tool call. */\nexport function GeneratedImageCard(props) {\n const attachment = imageRef(props.block);\n const [url, setUrl] = useState();\n const [error, setError] = useState();\n useEffect(() => {\n if (attachment === undefined)\n return;\n const controller = new AbortController();\n let objectUrl;\n void fetch(IMAGE_ROUTE, { method: 'POST', signal: controller.signal, headers: { 'content-type': 'application/json' }, body: JSON.stringify({ attachment }) }).then(async (response) => { if (!response.ok)\n throw new Error(`图片读取失败 (${String(response.status)})`); const blob = await response.blob(); if (controller.signal.aborted)\n return; objectUrl = URL.createObjectURL(blob); setUrl(objectUrl); }).catch(cause => { if (!controller.signal.aborted)\n setError(cause instanceof Error ? cause.message : String(cause)); });\n return () => { controller.abort(); if (objectUrl !== undefined)\n URL.revokeObjectURL(objectUrl); };\n }, [attachment?.attachmentId]);\n if (attachment === undefined)\n return _jsx(\"div\", { className: \"dsh-ig-loading\", children: \"\\u6B63\\u5728\\u751F\\u6210\\u56FE\\u7247\\u2026\" });\n return _jsxs(\"section\", { className: \"dsh-ig-result\", \"aria-label\": \"Generated image\", children: [_jsx(\"div\", { className: \"dsh-ig-result-title\", children: \"Generated image\" }), error !== undefined ? _jsx(\"div\", { className: \"dsh-ig-error\", children: error }) : null, url === undefined && error === undefined ? _jsx(\"div\", { className: \"dsh-ig-loading\", children: \"\\u6B63\\u5728\\u52A0\\u8F7D\\u56FE\\u7247\\u2026\" }) : null, url !== undefined ? _jsx(\"img\", { className: \"dsh-ig-image\", src: url, alt: attachment.name ?? 'Generated image' }) : null] });\n}\nfunction modelOf(provider, value) {\n const stored = provider === 'google' ? value?.googleModel : provider === 'openai' ? value?.openaiModel : value?.seedreamModel;\n return typeof stored === 'string' && stored.length > 0 ? stored : DEFAULT_MODELS[provider];\n}\nfunction baseURLOf(provider, value) {\n const stored = provider === 'google' ? value?.googleEndpoint : provider === 'openai' ? value?.openaiBaseURL : value?.seedreamBaseURL;\n return typeof stored === 'string' && stored.length > 0 ? stored : DEFAULT_BASE_URLS[provider];\n}\nfunction imageRef(block) { if (!('kind' in block) || block.resultView?.card !== 'generic')\n return undefined; const image = block.resultView.content?.find(item => item.type === 'image'); return image?.type === 'image' ? image.attachment : undefined; }\n//# sourceMappingURL=index.js.map"],"mappings":"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"}