dsh-image-gen 0.1.5 → 0.1.7
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 +2 -1
- package/README.md +2 -1
- package/README.zh-CN.md +2 -1
- package/lib/client.js +221 -3
- package/lib/client.js.map +1 -1
- package/lib/index.js +171 -17
- package/lib/types/client/gallery-store.d.ts +4 -2
- package/lib/types/client/gallery-view.d.ts +8 -0
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/config.d.ts +6 -0
- package/lib/types/workspace-save.d.ts +52 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -122,11 +122,12 @@ Click the **`[Gallery]`** Tab in the top navigation bar to browse and search all
|
|
|
122
122
|
## ✨ Key Features
|
|
123
123
|
|
|
124
124
|
- 💬 **In-Chat Image Generation**: Tell your Agent what you want to draw without switching tabs or tools.
|
|
125
|
-
- 🖼️ **Native Image Gallery**: Dedicated "Gallery" tab automatically collecting all generated images with keyword search, provider filtering, and quick copy/download.
|
|
125
|
+
- 🖼️ **Native Image Gallery**: Dedicated "Gallery" tab automatically collecting all generated images with keyword search, provider filtering, single-item deletion (with confirmation and persistent tombstones), and quick copy/download.
|
|
126
126
|
- 🔍 **Interactive Image Tools**: High-res fullscreen preview, one-click copy to clipboard, local download, and open in new tab.
|
|
127
127
|
- 🎨 **Multi-Provider Support**: Supports Google Gemini, OpenAI Images, OpenAI Compatible API, ByteDance Seedream / Volcengine Ark, and Aliyun DashScope (Wanx / Qwen-Image).
|
|
128
128
|
- 🔑 **BYOK (Bring Your Own Key)**: Uses your own API keys managed securely by DSH credentials service with write-only protection.
|
|
129
129
|
- 🖼️ **Durable Session Persistence**: Images integrate with DSH Attachment and conversation lifecycle, preserved across reloads.
|
|
130
|
+
- 💾 **Workspace File Output**: By default each generated image is also written as a file into the current session workspace (`dsh-image-gen/` subfolder); the tool result carries the absolute file path. Can be disabled or re-pointed in settings.
|
|
130
131
|
- ⚙️ **Native Web Settings**: Configure providers, keys, models, and endpoints directly in DSH settings.
|
|
131
132
|
|
|
132
133
|
---
|
package/README.md
CHANGED
|
@@ -122,11 +122,12 @@ pnpm dsh plugin --profile web add ./dsh-image-gen
|
|
|
122
122
|
## ✨ 主要能力
|
|
123
123
|
|
|
124
124
|
- 💬 **对话中直接生图**:不需要切换到其他网站,也不需要手动复制 Prompt,直接告诉 Agent 你想画什么即可。
|
|
125
|
-
- 🖼️ **历史生图画廊**:顶栏自带「画廊」Tab
|
|
125
|
+
- 🖼️ **历史生图画廊**:顶栏自带「画廊」Tab,自动汇总所有历史生成的图片,支持关键词搜索、厂商筛选、单张删除(带防误触确认与持久化墓碑)与一键复制/下载。
|
|
126
126
|
- 🔍 **交互式图片工具**:支持点击全屏大图预览、一键复制图片到剪贴板、本地下载与新标签页打开。
|
|
127
127
|
- 🎨 **多 Provider 支持**:支持 Google Gemini、OpenAI Images、OpenAI Compatible API、ByteDance Seedream / 火山方舟以及阿里云 DashScope(通义万相 / Qwen-Image)。Provider、模型和 Endpoint 均可在设置中自由定制。
|
|
128
128
|
- 🔑 **BYOK (自带 Key)**:插件使用你自己的 API Key。API Key 通过 DeepSeek Harness 的 `credentials` 服务管理,采用写保护隔离,不需要写进项目源码或配置文件,前端不存明文。
|
|
129
129
|
- 🖼️ **图片跟随会话保存**:生成结果会接入 DeepSeek Harness 的 Attachment / Conversation 体系,重新打开历史会话后,仍然可以看到之前生成的图片。
|
|
130
|
+
- 💾 **生成图片自动落盘到工作区**:默认在每次生成后把图片文件保存到当前会话工作区(`dsh-image-gen/` 子目录),工具结果会返回文件的绝对路径;可在设置中关闭或修改保存目录。
|
|
130
131
|
- ⚙️ **原生设置界面**:Provider、API Key、模型和 Endpoint 都可以直接在 DSH Web 设置中修改,不需要手动编辑配置文件。
|
|
131
132
|
|
|
132
133
|
---
|
package/README.zh-CN.md
CHANGED
|
@@ -122,11 +122,12 @@ pnpm dsh plugin --profile web add ./dsh-image-gen
|
|
|
122
122
|
## ✨ 主要能力
|
|
123
123
|
|
|
124
124
|
- 💬 **对话中直接生图**:不需要切换到其他网站,也不需要手动复制 Prompt,直接告诉 Agent 你想画什么即可。
|
|
125
|
-
- 🖼️ **历史生图画廊**:顶栏自带「画廊」Tab
|
|
125
|
+
- 🖼️ **历史生图画廊**:顶栏自带「画廊」Tab,自动汇总所有历史生成的图片,支持关键词搜索、厂商筛选、单张删除(带防误触确认与持久化墓碑)与一键复制/下载。
|
|
126
126
|
- 🔍 **交互式图片工具**:支持点击全屏大图预览、一键复制图片到剪贴板、本地下载与新标签页打开。
|
|
127
127
|
- 🎨 **多 Provider 支持**:支持 Google Gemini、OpenAI Images、OpenAI Compatible API、ByteDance Seedream / 火山方舟以及阿里云 DashScope(通义万相 / Qwen-Image)。Provider、模型和 Endpoint 均可在设置中自由定制。
|
|
128
128
|
- 🔑 **BYOK (自带 Key)**:插件使用你自己的 API Key。API Key 通过 DeepSeek Harness 的 `credentials` 服务管理,采用写保护隔离,不需要写进项目源码或配置文件,前端不存明文。
|
|
129
129
|
- 🖼️ **图片跟随会话保存**:生成结果会接入 DeepSeek Harness 的 Attachment / Conversation 体系,重新打开历史会话后,仍然可以看到之前生成的图片。
|
|
130
|
+
- 💾 **生成图片自动落盘到工作区**:默认在每次生成后把图片文件保存到当前会话工作区(`dsh-image-gen/` 子目录),工具结果会返回文件的绝对路径;可在设置中关闭或修改保存目录。
|
|
130
131
|
- ⚙️ **原生设置界面**:Provider、API Key、模型和 Endpoint 都可以直接在 DSH Web 设置中修改,不需要手动编辑配置文件。
|
|
131
132
|
|
|
132
133
|
---
|
package/lib/client.js
CHANGED
|
@@ -32,9 +32,11 @@ window.__ModuleLoader__.load({
|
|
|
32
32
|
//#endregion
|
|
33
33
|
//#region lib/types/client/gallery-store.js
|
|
34
34
|
const DB_NAME = "dsh_image_gen_db";
|
|
35
|
-
const DB_VERSION =
|
|
35
|
+
const DB_VERSION = 2;
|
|
36
36
|
const STORE_NAME = "gallery_history";
|
|
37
|
+
const TOMBSTONE_STORE = "gallery_tombstones";
|
|
37
38
|
let dbPromise = null;
|
|
39
|
+
let tombstonesCache = null;
|
|
38
40
|
function getDB() {
|
|
39
41
|
if (dbPromise) return dbPromise;
|
|
40
42
|
dbPromise = new Promise((resolve, reject) => {
|
|
@@ -46,6 +48,7 @@ window.__ModuleLoader__.load({
|
|
|
46
48
|
request.onupgradeneeded = (event) => {
|
|
47
49
|
const db = event.target.result;
|
|
48
50
|
if (!db.objectStoreNames.contains(STORE_NAME)) db.createObjectStore(STORE_NAME, { keyPath: "id" }).createIndex("createdAt", "createdAt", { unique: false });
|
|
51
|
+
if (!db.objectStoreNames.contains(TOMBSTONE_STORE)) db.createObjectStore(TOMBSTONE_STORE, { keyPath: "id" });
|
|
49
52
|
};
|
|
50
53
|
request.onsuccess = () => {
|
|
51
54
|
resolve(request.result);
|
|
@@ -56,6 +59,30 @@ window.__ModuleLoader__.load({
|
|
|
56
59
|
});
|
|
57
60
|
return dbPromise;
|
|
58
61
|
}
|
|
62
|
+
async function loadTombstones(db) {
|
|
63
|
+
if (tombstonesCache) return tombstonesCache;
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
if (!db.objectStoreNames.contains(TOMBSTONE_STORE)) {
|
|
66
|
+
tombstonesCache = /* @__PURE__ */ new Set();
|
|
67
|
+
resolve(tombstonesCache);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
const req = db.transaction(TOMBSTONE_STORE, "readonly").objectStore(TOMBSTONE_STORE).getAllKeys();
|
|
72
|
+
req.onsuccess = () => {
|
|
73
|
+
tombstonesCache = new Set(req.result.map(String));
|
|
74
|
+
resolve(tombstonesCache);
|
|
75
|
+
};
|
|
76
|
+
req.onerror = () => {
|
|
77
|
+
tombstonesCache = /* @__PURE__ */ new Set();
|
|
78
|
+
resolve(tombstonesCache);
|
|
79
|
+
};
|
|
80
|
+
} catch {
|
|
81
|
+
tombstonesCache = /* @__PURE__ */ new Set();
|
|
82
|
+
resolve(tombstonesCache);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
59
86
|
const listeners = /* @__PURE__ */ new Set();
|
|
60
87
|
function notifyListeners() {
|
|
61
88
|
for (const listener of listeners) try {
|
|
@@ -75,10 +102,12 @@ window.__ModuleLoader__.load({
|
|
|
75
102
|
}
|
|
76
103
|
/**
|
|
77
104
|
* Save or update a gallery record by attachmentId.
|
|
105
|
+
* Skipped if the item was previously deleted (tombstoned).
|
|
78
106
|
*/
|
|
79
107
|
async function saveGalleryItem(item) {
|
|
80
108
|
try {
|
|
81
109
|
const db = await getDB();
|
|
110
|
+
if ((await loadTombstones(db)).has(item.id)) return;
|
|
82
111
|
const record = {
|
|
83
112
|
...item,
|
|
84
113
|
createdAt: item.createdAt ?? Date.now()
|
|
@@ -116,6 +145,30 @@ window.__ModuleLoader__.load({
|
|
|
116
145
|
return [];
|
|
117
146
|
}
|
|
118
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* Delete a single gallery record by ID and record a tombstone.
|
|
150
|
+
*/
|
|
151
|
+
async function deleteGalleryItem(id) {
|
|
152
|
+
try {
|
|
153
|
+
const db = await getDB();
|
|
154
|
+
(await loadTombstones(db)).add(id);
|
|
155
|
+
await new Promise((resolve, reject) => {
|
|
156
|
+
const tx = db.transaction([STORE_NAME, TOMBSTONE_STORE], "readwrite");
|
|
157
|
+
const store = tx.objectStore(STORE_NAME);
|
|
158
|
+
const tombstoneStore = tx.objectStore(TOMBSTONE_STORE);
|
|
159
|
+
store.delete(id);
|
|
160
|
+
tombstoneStore.put({
|
|
161
|
+
id,
|
|
162
|
+
deletedAt: Date.now()
|
|
163
|
+
});
|
|
164
|
+
tx.oncomplete = () => resolve();
|
|
165
|
+
tx.onerror = () => reject(tx.error);
|
|
166
|
+
});
|
|
167
|
+
notifyListeners();
|
|
168
|
+
} catch (err) {
|
|
169
|
+
console.warn("[dsh-image-gen] Failed to delete gallery item from IndexedDB:", err);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
119
172
|
//#endregion
|
|
120
173
|
//#region lib/types/client/gallery-view.js
|
|
121
174
|
/**
|
|
@@ -130,6 +183,7 @@ window.__ModuleLoader__.load({
|
|
|
130
183
|
filterGoogle: "Google Gemini",
|
|
131
184
|
filterOpenAI: "OpenAI / 中转站",
|
|
132
185
|
filterSeedream: "字节 Seedream",
|
|
186
|
+
filterDashScope: "阿里 DashScope",
|
|
133
187
|
emptyTitle: "暂无生图记录",
|
|
134
188
|
emptyDesc: "在对话中让 Agent 生图后,生成的图片会自动收录到这里。",
|
|
135
189
|
noMatchTitle: "未找到匹配结果",
|
|
@@ -141,6 +195,9 @@ window.__ModuleLoader__.load({
|
|
|
141
195
|
download: "下载图片",
|
|
142
196
|
copyImg: "复制图片",
|
|
143
197
|
copyPpt: "复制 Prompt",
|
|
198
|
+
delete: "从画廊删除",
|
|
199
|
+
confirmDelete: "确定要从画廊中删除这张图片吗?(不会影响原聊天记录)",
|
|
200
|
+
deleted: "已从画廊删除",
|
|
144
201
|
model: "模型",
|
|
145
202
|
prompt: "Prompt",
|
|
146
203
|
close: "关闭 (Esc)"
|
|
@@ -152,6 +209,7 @@ window.__ModuleLoader__.load({
|
|
|
152
209
|
filterGoogle: "Google Gemini",
|
|
153
210
|
filterOpenAI: "OpenAI / Relay",
|
|
154
211
|
filterSeedream: "ByteDance Seedream",
|
|
212
|
+
filterDashScope: "Aliyun DashScope",
|
|
155
213
|
emptyTitle: "No images generated yet",
|
|
156
214
|
emptyDesc: "Images generated during conversations will automatically appear here.",
|
|
157
215
|
noMatchTitle: "No matching images",
|
|
@@ -163,6 +221,9 @@ window.__ModuleLoader__.load({
|
|
|
163
221
|
download: "Download",
|
|
164
222
|
copyImg: "Copy Image",
|
|
165
223
|
copyPpt: "Copy Prompt",
|
|
224
|
+
delete: "Delete from gallery",
|
|
225
|
+
confirmDelete: "Are you sure you want to remove this image from the gallery? (Chat history will not be affected)",
|
|
226
|
+
deleted: "Deleted from gallery",
|
|
166
227
|
model: "Model",
|
|
167
228
|
prompt: "Prompt",
|
|
168
229
|
close: "Close (Esc)"
|
|
@@ -312,6 +373,10 @@ window.__ModuleLoader__.load({
|
|
|
312
373
|
(0, react_jsx_runtime.jsx)("option", {
|
|
313
374
|
value: "seedream",
|
|
314
375
|
children: t("filterSeedream")
|
|
376
|
+
}),
|
|
377
|
+
(0, react_jsx_runtime.jsx)("option", {
|
|
378
|
+
value: "dashscope",
|
|
379
|
+
children: t("filterDashScope")
|
|
315
380
|
})
|
|
316
381
|
]
|
|
317
382
|
})]
|
|
@@ -527,6 +592,45 @@ window.__ModuleLoader__.load({
|
|
|
527
592
|
})
|
|
528
593
|
]
|
|
529
594
|
}), (0, react_jsx_runtime.jsx)("span", { children: t("download") })]
|
|
595
|
+
}),
|
|
596
|
+
(0, react_jsx_runtime.jsxs)("button", {
|
|
597
|
+
type: "button",
|
|
598
|
+
className: "dsh-ig-lightbox-btn dsh-ig-lightbox-btn-danger",
|
|
599
|
+
title: t("delete"),
|
|
600
|
+
onClick: async () => {
|
|
601
|
+
if (!window.confirm(t("confirmDelete"))) return;
|
|
602
|
+
await deleteGalleryItem(previewItem.id);
|
|
603
|
+
setPreviewItem(null);
|
|
604
|
+
setPreviewUrl(null);
|
|
605
|
+
setPreviewBlob(null);
|
|
606
|
+
showToast(t("deleted"));
|
|
607
|
+
},
|
|
608
|
+
children: [(0, react_jsx_runtime.jsxs)("svg", {
|
|
609
|
+
width: "14",
|
|
610
|
+
height: "14",
|
|
611
|
+
viewBox: "0 0 24 24",
|
|
612
|
+
fill: "none",
|
|
613
|
+
stroke: "currentColor",
|
|
614
|
+
strokeWidth: "2",
|
|
615
|
+
strokeLinecap: "round",
|
|
616
|
+
strokeLinejoin: "round",
|
|
617
|
+
children: [
|
|
618
|
+
(0, react_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
619
|
+
(0, react_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
|
|
620
|
+
(0, react_jsx_runtime.jsx)("line", {
|
|
621
|
+
x1: "10",
|
|
622
|
+
y1: "11",
|
|
623
|
+
x2: "10",
|
|
624
|
+
y2: "17"
|
|
625
|
+
}),
|
|
626
|
+
(0, react_jsx_runtime.jsx)("line", {
|
|
627
|
+
x1: "14",
|
|
628
|
+
y1: "11",
|
|
629
|
+
x2: "14",
|
|
630
|
+
y2: "17"
|
|
631
|
+
})
|
|
632
|
+
]
|
|
633
|
+
}), (0, react_jsx_runtime.jsx)("span", { children: t("delete") })]
|
|
530
634
|
})
|
|
531
635
|
]
|
|
532
636
|
})]
|
|
@@ -694,6 +798,43 @@ window.__ModuleLoader__.load({
|
|
|
694
798
|
ry: "1"
|
|
695
799
|
})]
|
|
696
800
|
})
|
|
801
|
+
}),
|
|
802
|
+
(0, react_jsx_runtime.jsx)("button", {
|
|
803
|
+
type: "button",
|
|
804
|
+
className: "dsh-ig-tool-btn dsh-ig-tool-btn-danger",
|
|
805
|
+
title: t("delete"),
|
|
806
|
+
onClick: async (e) => {
|
|
807
|
+
e.stopPropagation();
|
|
808
|
+
if (!window.confirm(t("confirmDelete"))) return;
|
|
809
|
+
await deleteGalleryItem(item.id);
|
|
810
|
+
onToast(t("deleted"));
|
|
811
|
+
},
|
|
812
|
+
children: (0, react_jsx_runtime.jsxs)("svg", {
|
|
813
|
+
width: "14",
|
|
814
|
+
height: "14",
|
|
815
|
+
viewBox: "0 0 24 24",
|
|
816
|
+
fill: "none",
|
|
817
|
+
stroke: "currentColor",
|
|
818
|
+
strokeWidth: "2",
|
|
819
|
+
strokeLinecap: "round",
|
|
820
|
+
strokeLinejoin: "round",
|
|
821
|
+
children: [
|
|
822
|
+
(0, react_jsx_runtime.jsx)("polyline", { points: "3 6 5 6 21 6" }),
|
|
823
|
+
(0, react_jsx_runtime.jsx)("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }),
|
|
824
|
+
(0, react_jsx_runtime.jsx)("line", {
|
|
825
|
+
x1: "10",
|
|
826
|
+
y1: "11",
|
|
827
|
+
x2: "10",
|
|
828
|
+
y2: "17"
|
|
829
|
+
}),
|
|
830
|
+
(0, react_jsx_runtime.jsx)("line", {
|
|
831
|
+
x1: "14",
|
|
832
|
+
y1: "11",
|
|
833
|
+
x2: "14",
|
|
834
|
+
y2: "17"
|
|
835
|
+
})
|
|
836
|
+
]
|
|
837
|
+
})
|
|
697
838
|
})
|
|
698
839
|
]
|
|
699
840
|
})
|
|
@@ -776,9 +917,14 @@ window.__ModuleLoader__.load({
|
|
|
776
917
|
endpointHintSeedream: "火山方舟兼容的 /api/v3 地址。",
|
|
777
918
|
endpointHintDashScope: "阿里云百炼 DashScope 官方接口地址。",
|
|
778
919
|
model: "模型",
|
|
920
|
+
saveToWorkspace: "保存到工作区",
|
|
921
|
+
saveToWorkspaceHint: "每次生成后,把图片文件保存到当前会话工作区。",
|
|
922
|
+
folder: "工作区文件夹",
|
|
923
|
+
folderHint: "相对当前会话工作区的子目录;留空表示工作区根目录。",
|
|
779
924
|
saving: "保存中…",
|
|
780
925
|
save: "保存",
|
|
781
926
|
saved: "已保存",
|
|
927
|
+
savedToPath: "已保存到",
|
|
782
928
|
checkingKey: "正在检查 API Key…",
|
|
783
929
|
keyConfigured: "已配置 API Key",
|
|
784
930
|
keyNotConfigured: "尚未配置 API Key",
|
|
@@ -811,9 +957,14 @@ window.__ModuleLoader__.load({
|
|
|
811
957
|
endpointHintSeedream: "Volcengine Ark compatible /api/v3 base URL.",
|
|
812
958
|
endpointHintDashScope: "Official Aliyun DashScope endpoint.",
|
|
813
959
|
model: "Model",
|
|
960
|
+
saveToWorkspace: "Save to workspace",
|
|
961
|
+
saveToWorkspaceHint: "Write each generated image as a file into the session workspace.",
|
|
962
|
+
folder: "Workspace folder",
|
|
963
|
+
folderHint: "Subdirectory of the session workspace; empty means the workspace root.",
|
|
814
964
|
saving: "Saving…",
|
|
815
965
|
save: "Save",
|
|
816
966
|
saved: "Saved",
|
|
967
|
+
savedToPath: "Saved to",
|
|
817
968
|
checkingKey: "Checking API Key…",
|
|
818
969
|
keyConfigured: "API Key configured",
|
|
819
970
|
keyNotConfigured: "API Key not configured",
|
|
@@ -849,6 +1000,9 @@ window.__ModuleLoader__.load({
|
|
|
849
1000
|
.dsh-ig-btn-reset:hover{background:var(--dsw-alias-bg-layer-2,#edf0f3);border-color:var(--dsw-alias-label-dimmed,#9ca3af)}
|
|
850
1001
|
.dsh-ig-hint,.dsh-ig-status{margin:0;color:var(--dsw-alias-label-tertiary,#7b818b);font-size:12px;line-height:1.4}
|
|
851
1002
|
.dsh-ig-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:16px;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#eee)}
|
|
1003
|
+
.dsh-ig-check-row{display:flex;align-items:center;gap:8px;cursor:pointer}
|
|
1004
|
+
.dsh-ig-check-row input[type=checkbox]{width:15px;height:15px;accent-color:var(--dsw-alias-brand-primary,#4c78ff);margin:0}
|
|
1005
|
+
.dsh-ig-savedto{font-size:12px;line-height:1.5;color:var(--dsw-alias-label-tertiary,#7b818b);word-break:break-all}
|
|
852
1006
|
.dsh-ig-save{appearance:none;border:0;border-radius:8px;padding:6px 16px;background:var(--dsw-alias-label-primary,#111827);color:var(--dsw-alias-bg-layer-3,#fff);font:inherit;font-size:13px;font-weight:500;cursor:pointer;transition:opacity .15s}
|
|
853
1007
|
.dsh-ig-save:disabled{opacity:.4;cursor:default}
|
|
854
1008
|
|
|
@@ -857,8 +1011,9 @@ window.__ModuleLoader__.load({
|
|
|
857
1011
|
.dsh-ig-container{position:relative;display:inline-block;width:fit-content;max-width:100%;justify-self:start;border-radius:12px;overflow:hidden;line-height:0}
|
|
858
1012
|
.dsh-ig-container:hover .dsh-ig-toolbar{opacity:1;pointer-events:auto}
|
|
859
1013
|
.dsh-ig-toolbar{position:absolute;top:8px;left:8px;display:flex;align-items:center;gap:5px;padding:3px 5px;border-radius:8px;background:rgba(0,0,0,0.65);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);opacity:0;pointer-events:none;transition:opacity .18s ease;z-index:10;line-height:1}
|
|
860
|
-
.dsh-ig-tool-btn{appearance:none;border:0;background:transparent;color:#fff;padding:5px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .15s}
|
|
1014
|
+
.dsh-ig-tool-btn{appearance:none;border:0;background:transparent;color:#fff;padding:5px;border-radius:6px;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;transition:background .15s,color .15s}
|
|
861
1015
|
.dsh-ig-tool-btn:hover{background:rgba(255,255,255,0.25)}
|
|
1016
|
+
.dsh-ig-tool-btn-danger:hover{background:rgba(239,68,68,0.75)!important;color:#fff!important}
|
|
862
1017
|
.dsh-ig-toast{position:absolute;top:100%;left:0;margin-top:5px;padding:3px 8px;border-radius:6px;background:rgba(0,0,0,0.85);color:#fff;font-size:11px;white-space:nowrap;pointer-events:none;z-index:20}
|
|
863
1018
|
.dsh-ig-image{display:block;max-width:100%;max-height:520px;border-radius:12px;background:#f2f3f5;cursor:pointer}
|
|
864
1019
|
@keyframes dsh-ig-fade{from{opacity:0}to{opacity:1}}
|
|
@@ -911,8 +1066,10 @@ window.__ModuleLoader__.load({
|
|
|
911
1066
|
.dsh-ig-lightbox-bottombar{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);max-width:min(90vw,640px);background:rgba(20,22,26,0.85);backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.15);border-radius:14px;padding:10px 16px;display:flex;flex-direction:column;gap:8px;color:#fff;box-shadow:0 16px 40px rgba(0,0,0,0.5);cursor:default}
|
|
912
1067
|
.dsh-ig-lightbox-prompt-text{font-size:13px;line-height:1.4;color:rgba(255,255,255,0.92);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;word-break:break-word}
|
|
913
1068
|
.dsh-ig-lightbox-actions{display:flex;align-items:center;gap:8px;justify-content:flex-end;border-top:1px solid rgba(255,255,255,0.1);padding-top:8px}
|
|
914
|
-
.dsh-ig-lightbox-btn{appearance:none;border:1px solid rgba(255,255,255,0.18);background:rgba(255,255,255,0.08);color:#fff;border-radius:6px;padding:5px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:background .15s}
|
|
1069
|
+
.dsh-ig-lightbox-btn{appearance:none;border:1px solid rgba(255,255,255,0.18);background:rgba(255,255,255,0.08);color:#fff;border-radius:6px;padding:5px 10px;font-size:12px;cursor:pointer;display:inline-flex;align-items:center;gap:5px;transition:background .15s,border-color .15s,color .15s}
|
|
915
1070
|
.dsh-ig-lightbox-btn:hover{background:rgba(255,255,255,0.22)}
|
|
1071
|
+
.dsh-ig-lightbox-btn-danger{border-color:rgba(239,68,68,0.4);color:#fca5a5}
|
|
1072
|
+
.dsh-ig-lightbox-btn-danger:hover{background:rgba(239,68,68,0.35)!important;color:#fff!important;border-color:rgba(239,68,68,0.7)!important}
|
|
916
1073
|
.dsh-ig-gallery-page-toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.85);color:#fff;padding:6px 14px;border-radius:8px;font-size:13px;z-index:99999;animation:dsh-ig-fade .15s}
|
|
917
1074
|
|
|
918
1075
|
/* Hide floating chat composer when gallery page is active */
|
|
@@ -973,6 +1130,8 @@ window.__ModuleLoader__.load({
|
|
|
973
1130
|
const [provider, setProvider] = (0, react.useState)("google");
|
|
974
1131
|
const [model, setModel] = (0, react.useState)("");
|
|
975
1132
|
const [baseURL, setBaseURL] = (0, react.useState)("");
|
|
1133
|
+
const [saveToWorkspace, setSaveToWorkspace] = (0, react.useState)(true);
|
|
1134
|
+
const [workspaceFolder, setWorkspaceFolder] = (0, react.useState)("dsh-image-gen");
|
|
976
1135
|
const [key, setKey] = (0, react.useState)("");
|
|
977
1136
|
const [configured, setConfigured] = (0, react.useState)();
|
|
978
1137
|
const [saving, setSaving] = (0, react.useState)(false);
|
|
@@ -1002,6 +1161,8 @@ window.__ModuleLoader__.load({
|
|
|
1002
1161
|
setProvider(next);
|
|
1003
1162
|
setModel(modelOf(next, value));
|
|
1004
1163
|
setBaseURL(baseURLOf(next, value));
|
|
1164
|
+
setSaveToWorkspace(value?.saveToWorkspace ?? true);
|
|
1165
|
+
setWorkspaceFolder(value?.workspaceFolder ?? "dsh-image-gen");
|
|
1005
1166
|
}, [snapshot]);
|
|
1006
1167
|
(0, react.useEffect)(() => {
|
|
1007
1168
|
let active = true;
|
|
@@ -1022,6 +1183,8 @@ window.__ModuleLoader__.load({
|
|
|
1022
1183
|
await props.scope.set("provider", provider);
|
|
1023
1184
|
await props.scope.set(provider === "google" ? "googleModel" : provider === "openai" ? "openaiModel" : provider === "seedream" ? "seedreamModel" : "dashscopeModel", model);
|
|
1024
1185
|
await props.scope.set(provider === "google" ? "googleEndpoint" : provider === "openai" ? "openaiBaseURL" : provider === "seedream" ? "seedreamBaseURL" : "dashscopeEndpoint", baseURL);
|
|
1186
|
+
await props.scope.set("saveToWorkspace", saveToWorkspace);
|
|
1187
|
+
await props.scope.set("workspaceFolder", workspaceFolder.trim());
|
|
1025
1188
|
if (key.trim().length > 0) {
|
|
1026
1189
|
const response = await props.credentials.set({
|
|
1027
1190
|
ref: KEY_REF[provider],
|
|
@@ -1190,6 +1353,46 @@ window.__ModuleLoader__.load({
|
|
|
1190
1353
|
required: true
|
|
1191
1354
|
})]
|
|
1192
1355
|
}),
|
|
1356
|
+
(0, react_jsx_runtime.jsxs)("div", {
|
|
1357
|
+
className: "dsh-ig-field",
|
|
1358
|
+
children: [(0, react_jsx_runtime.jsxs)("label", {
|
|
1359
|
+
className: "dsh-ig-check-row",
|
|
1360
|
+
children: [(0, react_jsx_runtime.jsx)("input", {
|
|
1361
|
+
type: "checkbox",
|
|
1362
|
+
checked: saveToWorkspace,
|
|
1363
|
+
onChange: (event) => {
|
|
1364
|
+
setSaveToWorkspace(event.target.checked);
|
|
1365
|
+
}
|
|
1366
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1367
|
+
className: "dsh-ig-label",
|
|
1368
|
+
children: t("saveToWorkspace")
|
|
1369
|
+
})]
|
|
1370
|
+
}), (0, react_jsx_runtime.jsx)("span", {
|
|
1371
|
+
className: "dsh-ig-hint",
|
|
1372
|
+
children: t("saveToWorkspaceHint")
|
|
1373
|
+
})]
|
|
1374
|
+
}),
|
|
1375
|
+
saveToWorkspace ? (0, react_jsx_runtime.jsxs)("label", {
|
|
1376
|
+
className: "dsh-ig-field",
|
|
1377
|
+
children: [
|
|
1378
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1379
|
+
className: "dsh-ig-label",
|
|
1380
|
+
children: t("folder")
|
|
1381
|
+
}),
|
|
1382
|
+
(0, react_jsx_runtime.jsx)("input", {
|
|
1383
|
+
className: "dsh-ig-input",
|
|
1384
|
+
value: workspaceFolder,
|
|
1385
|
+
onChange: (event) => {
|
|
1386
|
+
setWorkspaceFolder(event.target.value);
|
|
1387
|
+
},
|
|
1388
|
+
placeholder: "dsh-image-gen"
|
|
1389
|
+
}),
|
|
1390
|
+
(0, react_jsx_runtime.jsx)("span", {
|
|
1391
|
+
className: "dsh-ig-hint",
|
|
1392
|
+
children: t("folderHint")
|
|
1393
|
+
})
|
|
1394
|
+
]
|
|
1395
|
+
}) : null,
|
|
1193
1396
|
(0, react_jsx_runtime.jsxs)("div", {
|
|
1194
1397
|
className: "dsh-ig-actions",
|
|
1195
1398
|
children: [(0, react_jsx_runtime.jsx)("p", {
|
|
@@ -1210,6 +1413,7 @@ window.__ModuleLoader__.load({
|
|
|
1210
1413
|
/** Render the durable attachment referenced by a completed image tool call. */
|
|
1211
1414
|
function GeneratedImageCard(props) {
|
|
1212
1415
|
const attachment = imageRef(props.block);
|
|
1416
|
+
const savedTo = imageSavedTo(props.block);
|
|
1213
1417
|
const [url, setUrl] = (0, react.useState)();
|
|
1214
1418
|
const [blob, setBlob] = (0, react.useState)();
|
|
1215
1419
|
const [error, setError] = (0, react.useState)();
|
|
@@ -1313,6 +1517,14 @@ window.__ModuleLoader__.load({
|
|
|
1313
1517
|
className: "dsh-ig-result-title",
|
|
1314
1518
|
children: t("generatedTitle")
|
|
1315
1519
|
}),
|
|
1520
|
+
savedTo !== void 0 ? (0, react_jsx_runtime.jsxs)("div", {
|
|
1521
|
+
className: "dsh-ig-savedto",
|
|
1522
|
+
children: [
|
|
1523
|
+
t("savedToPath"),
|
|
1524
|
+
": ",
|
|
1525
|
+
savedTo
|
|
1526
|
+
]
|
|
1527
|
+
}) : null,
|
|
1316
1528
|
error !== void 0 ? (0, react_jsx_runtime.jsx)("div", {
|
|
1317
1529
|
className: "dsh-ig-error",
|
|
1318
1530
|
children: error
|
|
@@ -1451,6 +1663,12 @@ window.__ModuleLoader__.load({
|
|
|
1451
1663
|
const image = block.resultView.content?.find((item) => item.type === "image");
|
|
1452
1664
|
return image?.type === "image" ? image.attachment : void 0;
|
|
1453
1665
|
}
|
|
1666
|
+
/** The workspace file path a completed image call saved, when the result meta carries one. */
|
|
1667
|
+
function imageSavedTo(block) {
|
|
1668
|
+
if (!("kind" in block)) return void 0;
|
|
1669
|
+
const meta = block.meta ?? block.resultView?.meta;
|
|
1670
|
+
return typeof meta?.savedTo === "string" ? meta.savedTo : void 0;
|
|
1671
|
+
}
|
|
1454
1672
|
//#endregion
|
|
1455
1673
|
exports.GeneratedImageCard = GeneratedImageCard;
|
|
1456
1674
|
exports.ImageGenerationSettingsCard = ImageGenerationSettingsCard;
|