@goodandready/dsh-image-gen 0.11.43 → 0.12.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/lib/client.js +25 -0
- package/lib/design-export.js +205 -0
- package/lib/index.js +4 -0
- package/lib/register-tools.js +4 -0
- package/lib/tools/design-export.js +93 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -2908,6 +2908,9 @@ window.__ModuleLoader__.load({
|
|
|
2908
2908
|
'vault.search': 'Search prompt or tags...',
|
|
2909
2909
|
'vault.seed': 'Seed',
|
|
2910
2910
|
'vault.title': 'Asset Browser & Vault',
|
|
2911
|
+
'vault.exportDesign': '🎨 OpenDesign',
|
|
2912
|
+
'vault.exportDesignCopied': 'Figma/Penpot snippet & Data-URL copied to clipboard!',
|
|
2913
|
+
'vault.exportDesignFailed': 'Failed to export design snippet',
|
|
2911
2914
|
'f.comfyTemplate': 'ComfyUI Workflow Template',
|
|
2912
2915
|
'f.comfyTemplateHint': 'Pre-vetted workflow template (e.g. SDXL txt2img, img2img, InstantID face transfer, RealESRGAN upscaler).',
|
|
2913
2916
|
'f.activeProjectProfile': 'Active Project Profile',
|
|
@@ -3219,6 +3222,9 @@ window.__ModuleLoader__.load({
|
|
|
3219
3222
|
'vault.search': '搜索提示词或标签...',
|
|
3220
3223
|
'vault.seed': '种子',
|
|
3221
3224
|
'vault.title': '图像素材库与浏览器',
|
|
3225
|
+
'vault.exportDesign': '🎨 OpenDesign',
|
|
3226
|
+
'vault.exportDesignCopied': 'Figma/Penpot 片段与 Data-URL 已复制到剪贴板!',
|
|
3227
|
+
'vault.exportDesignFailed': '导出设计片段失败',
|
|
3222
3228
|
'f.comfyTemplate': 'ComfyUI 工作流模板',
|
|
3223
3229
|
'f.comfyTemplateHint': '经过验证的预设工作流模板(例如 SDXL 文生图、图生图、InstantID 人脸迁移、RealESRGAN 放大器)。',
|
|
3224
3230
|
'f.activeProjectProfile': '活动项目视觉配置',
|
|
@@ -3681,6 +3687,24 @@ window.__ModuleLoader__.load({
|
|
|
3681
3687
|
}
|
|
3682
3688
|
}
|
|
3683
3689
|
|
|
3690
|
+
const handleExportDesign = (item) => {
|
|
3691
|
+
const assetId = item.id || item.attachmentId
|
|
3692
|
+
fetchJson('/dsh-image-gen/design/export?image=' + encodeURIComponent(assetId) + '&name=' + encodeURIComponent(item.prompt || 'asset'))
|
|
3693
|
+
.then((r) => r.json())
|
|
3694
|
+
.then((data) => {
|
|
3695
|
+
if (data && data.ok && data.export) {
|
|
3696
|
+
const snippet = data.export.figmaHtml || data.export.dataUrl
|
|
3697
|
+
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
3698
|
+
navigator.clipboard.writeText(snippet)
|
|
3699
|
+
showFeedback(t('vault.exportDesignCopied') || 'Figma/Penpot snippet & Data-URL copied to clipboard!')
|
|
3700
|
+
}
|
|
3701
|
+
} else {
|
|
3702
|
+
showFeedback(t('vault.exportDesignFailed') || 'Failed to export design snippet')
|
|
3703
|
+
}
|
|
3704
|
+
})
|
|
3705
|
+
.catch(() => showFeedback(t('vault.exportDesignFailed') || 'Failed to export design snippet'))
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3684
3708
|
return react.createElement(
|
|
3685
3709
|
'div',
|
|
3686
3710
|
{ className: 'ig-vault-container', style: { display: 'flex', flexDirection: 'column', gap: '14px', width: '100%' } },
|
|
@@ -3824,6 +3848,7 @@ window.__ModuleLoader__.load({
|
|
|
3824
3848
|
{ style: { display: 'flex', gap: '8px', justifyContent: 'flex-end', marginTop: '6px' } },
|
|
3825
3849
|
react.createElement('button', { type: 'button', className: 'ig-tab-btn', onClick: () => handleReroll(selected) }, '🎲 ' + (t('vault.reroll') || 'Re-roll')),
|
|
3826
3850
|
react.createElement('button', { type: 'button', className: 'ig-tab-btn', onClick: () => handleInsert(selected) }, '💬 ' + (t('vault.insert') || 'Insert in Chat')),
|
|
3851
|
+
react.createElement('button', { type: 'button', className: 'ig-tab-btn', onClick: () => handleExportDesign(selected), title: 'Export for Figma / Penpot / OpenDesign' }, t('vault.exportDesign') || '🎨 OpenDesign'),
|
|
3827
3852
|
react.createElement(
|
|
3828
3853
|
'a',
|
|
3829
3854
|
{ href: selected.url || selected.thumbnailUrl, download: 'vault-asset-' + (selected.id || 'image') + '.png', className: 'ig-tab-btn', style: { textDecoration: 'none', display: 'inline-flex', alignItems: 'center' } },
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import fs from 'node:fs/promises'
|
|
4
|
+
import { isTrustedLocalRequest, resolveInside } from './security.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format image buffer or SVG string into targeted design clipboard payloads.
|
|
8
|
+
*
|
|
9
|
+
* @param {object} params
|
|
10
|
+
* @param {Buffer} params.buffer Image bytes
|
|
11
|
+
* @param {string} [params.mimeType] MIME type (image/png, image/jpeg, image/svg+xml, etc.)
|
|
12
|
+
* @param {string} [params.name] Asset name or filename
|
|
13
|
+
* @param {number} [params.width] Width in px
|
|
14
|
+
* @param {number} [params.height] Height in px
|
|
15
|
+
* @param {string} [params.target] 'all' | 'opendesign' | 'clipboard_svg' | 'clipboard_dataurl' | 'penpot_snippet' | 'figma_html'
|
|
16
|
+
* @returns {object} Formatted design payloads
|
|
17
|
+
*/
|
|
18
|
+
export function formatDesignExportPayload({
|
|
19
|
+
buffer,
|
|
20
|
+
mimeType = 'image/png',
|
|
21
|
+
name = 'design-asset',
|
|
22
|
+
width = 1024,
|
|
23
|
+
height = 1024,
|
|
24
|
+
target = 'all',
|
|
25
|
+
}) {
|
|
26
|
+
const isSvg = mimeType.includes('svg') || (buffer && buffer.subarray(0, 100).toString('utf8').includes('<svg'))
|
|
27
|
+
const base64Data = buffer ? buffer.toString('base64') : ''
|
|
28
|
+
const effectiveMime = isSvg ? 'image/svg+xml' : (mimeType || 'image/png')
|
|
29
|
+
const dataUrl = `data:${effectiveMime};base64,${base64Data}`
|
|
30
|
+
const svgMarkup = isSvg && buffer ? buffer.toString('utf8') : ''
|
|
31
|
+
|
|
32
|
+
const safeName = String(name || 'asset').replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
33
|
+
|
|
34
|
+
// HTML clipboard snippet compatible with Figma / Penpot / browser paste
|
|
35
|
+
const figmaHtml = isSvg && svgMarkup
|
|
36
|
+
? svgMarkup
|
|
37
|
+
: `<img src="${dataUrl}" alt="${safeName}" width="${width}" height="${height}" style="max-width:100%;height:auto;" />`
|
|
38
|
+
|
|
39
|
+
// Penpot-friendly snippet (SVG wrapper or embedded image object)
|
|
40
|
+
const penpotSnippet = isSvg && svgMarkup
|
|
41
|
+
? svgMarkup
|
|
42
|
+
: `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}"><image href="${dataUrl}" width="${width}" height="${height}"/></svg>`
|
|
43
|
+
|
|
44
|
+
// CSS snippet
|
|
45
|
+
const cssBackground = `background-image: url("${dataUrl}");\nbackground-size: contain;\nbackground-repeat: no-repeat;`
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
name: safeName,
|
|
49
|
+
mimeType: effectiveMime,
|
|
50
|
+
isSvg,
|
|
51
|
+
width,
|
|
52
|
+
height,
|
|
53
|
+
dataUrl,
|
|
54
|
+
svgMarkup: svgMarkup || null,
|
|
55
|
+
figmaHtml,
|
|
56
|
+
penpotSnippet,
|
|
57
|
+
cssBackground,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Push an asset directly into an OpenDesign project folder if the daemon / project directory exists.
|
|
63
|
+
*
|
|
64
|
+
* @param {object} params
|
|
65
|
+
* @param {Buffer} params.buffer Asset bytes
|
|
66
|
+
* @param {string} params.filename Target filename (e.g. hero-illustration.png)
|
|
67
|
+
* @param {string} params.projectId OpenDesign project id or name
|
|
68
|
+
* @param {string} [params.openDesignRoot] Path to .od/projects directory
|
|
69
|
+
* @returns {Promise<{ ok: boolean, savedPath?: string, error?: string }>}
|
|
70
|
+
*/
|
|
71
|
+
export async function pushAssetToOpenDesignProject({
|
|
72
|
+
buffer,
|
|
73
|
+
filename,
|
|
74
|
+
projectId,
|
|
75
|
+
openDesignRoot = path.join(process.env.HOME || '/home/vadim', 'open-design', '.od', 'projects'),
|
|
76
|
+
}) {
|
|
77
|
+
if (!buffer || !buffer.length) {
|
|
78
|
+
return { ok: false, error: 'Empty buffer provided for design asset export' }
|
|
79
|
+
}
|
|
80
|
+
if (!projectId) {
|
|
81
|
+
return { ok: false, error: 'Missing projectId for OpenDesign export' }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const safeProjectId = path.basename(projectId)
|
|
85
|
+
const safeFilename = path.basename(filename || `asset-${Date.now()}.png`)
|
|
86
|
+
const projectDir = path.join(openDesignRoot, safeProjectId)
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
const stats = await fs.stat(projectDir)
|
|
90
|
+
if (!stats.isDirectory()) {
|
|
91
|
+
return { ok: false, error: `OpenDesign project path is not a directory: ${projectDir}` }
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
return { ok: false, error: `OpenDesign project not found on disk: ${safeProjectId} (${err?.message || err})` }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const targetPath = resolveInside(projectDir, safeFilename)
|
|
98
|
+
try {
|
|
99
|
+
await fs.writeFile(targetPath, buffer)
|
|
100
|
+
return {
|
|
101
|
+
ok: true,
|
|
102
|
+
savedPath: targetPath,
|
|
103
|
+
filename: safeFilename,
|
|
104
|
+
bytes: buffer.length,
|
|
105
|
+
projectId: safeProjectId,
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
return { ok: false, error: `Failed to write file to OpenDesign project: ${err?.message || err}` }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Register design export routes on DSH web server.
|
|
114
|
+
*
|
|
115
|
+
* @param {object} ctx Cordis context
|
|
116
|
+
* @param {object} deps Dependencies ({ resolveSource })
|
|
117
|
+
*/
|
|
118
|
+
export function registerDesignExportRoutes(ctx, deps) {
|
|
119
|
+
const { resolveSource } = deps
|
|
120
|
+
|
|
121
|
+
ctx.effect(() => {
|
|
122
|
+
ctx.webServer.register({
|
|
123
|
+
kind: 'exact',
|
|
124
|
+
path: '/dsh-image-gen/design/export',
|
|
125
|
+
handler: async (req, res) => {
|
|
126
|
+
if (!isTrustedLocalRequest(req)) {
|
|
127
|
+
res.writeHead(403, { 'Content-Type': 'application/json' })
|
|
128
|
+
res.end(JSON.stringify({ ok: false, error: 'forbidden: local trusted requests only' }))
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (req.method === 'GET' || req.method === 'POST') {
|
|
133
|
+
try {
|
|
134
|
+
const url = new URL(req.url, 'http://localhost')
|
|
135
|
+
let image = url.searchParams.get('image') || url.searchParams.get('id')
|
|
136
|
+
let target = url.searchParams.get('target') || 'all'
|
|
137
|
+
let projectId = url.searchParams.get('projectId') || url.searchParams.get('project') || ''
|
|
138
|
+
let name = url.searchParams.get('name') || 'exported-asset'
|
|
139
|
+
|
|
140
|
+
if (req.method === 'POST') {
|
|
141
|
+
let bodyStr = ''
|
|
142
|
+
for await (const chunk of req) bodyStr += chunk
|
|
143
|
+
if (bodyStr) {
|
|
144
|
+
try {
|
|
145
|
+
const parsed = JSON.parse(bodyStr)
|
|
146
|
+
if (parsed.image) image = parsed.image
|
|
147
|
+
if (parsed.target) target = parsed.target
|
|
148
|
+
if (parsed.projectId) projectId = parsed.projectId
|
|
149
|
+
if (parsed.name) name = parsed.name
|
|
150
|
+
} catch (_) { /* ignore JSON parse error */ }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!image) {
|
|
155
|
+
res.writeHead(400, { 'Content-Type': 'application/json' })
|
|
156
|
+
res.end(JSON.stringify({ ok: false, error: 'Missing image or id parameter' }))
|
|
157
|
+
return
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const source = await resolveSource(ctx, null, image)
|
|
161
|
+
if (!source || !source.bytes) {
|
|
162
|
+
res.writeHead(404, { 'Content-Type': 'application/json' })
|
|
163
|
+
res.end(JSON.stringify({ ok: false, error: `Image source not found: ${image}` }))
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const formatted = formatDesignExportPayload({
|
|
168
|
+
buffer: source.bytes,
|
|
169
|
+
mimeType: source.mediaType || 'image/png',
|
|
170
|
+
name,
|
|
171
|
+
width: source.width || 1024,
|
|
172
|
+
height: source.height || 1024,
|
|
173
|
+
target,
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
let openDesignResult = null
|
|
177
|
+
if (projectId) {
|
|
178
|
+
const ext = formatted.isSvg ? '.svg' : '.png'
|
|
179
|
+
const fn = name.endsWith(ext) ? name : `${name}${ext}`
|
|
180
|
+
openDesignResult = await pushAssetToOpenDesignProject({
|
|
181
|
+
buffer: source.bytes,
|
|
182
|
+
filename: fn,
|
|
183
|
+
projectId,
|
|
184
|
+
})
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
res.writeHead(200, { 'Content-Type': 'application/json' })
|
|
188
|
+
res.end(JSON.stringify({
|
|
189
|
+
ok: true,
|
|
190
|
+
export: formatted,
|
|
191
|
+
openDesign: openDesignResult,
|
|
192
|
+
}))
|
|
193
|
+
} catch (err) {
|
|
194
|
+
res.writeHead(500, { 'Content-Type': 'application/json' })
|
|
195
|
+
res.end(JSON.stringify({ ok: false, error: err?.message || String(err) }))
|
|
196
|
+
}
|
|
197
|
+
return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
res.writeHead(405, { 'Content-Type': 'application/json' })
|
|
201
|
+
res.end(JSON.stringify({ ok: false, error: 'Method Not Allowed' }))
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
}, 'dsh-image-gen: route /dsh-image-gen/design/export')
|
|
205
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -56,6 +56,7 @@ import { getStagingSession, reviewStagedEdit, createStagingSession } from './inp
|
|
|
56
56
|
import { enqueueBatch, getBatchQueue, getBatchProgress, cancelBatch, retryFailedBatchItems, reconcileQueueOnStartup } from './batch-queue.js'
|
|
57
57
|
import { createVariantSession, selectVariantWinner, getVariantSession } from './variant-selection.js'
|
|
58
58
|
import { getImageLineage, getReplayPayload } from './lineage.js'
|
|
59
|
+
import { registerDesignExportRoutes } from './design-export.js'
|
|
59
60
|
import { resetLoopGuard, getLoopGuardState } from './loop-guard.js'
|
|
60
61
|
import { Config, plainConfig, volatileConfig, publicConfig, ensureConfig } from './config-schema.js'
|
|
61
62
|
import { resolveSource } from './resolve-source.js'
|
|
@@ -650,6 +651,9 @@ export function apply(ctx, rawConfig) {
|
|
|
650
651
|
},
|
|
651
652
|
}), 'dsh-image-gen: lineage route')
|
|
652
653
|
|
|
654
|
+
// Design export route (#446)
|
|
655
|
+
registerDesignExportRoutes(ctx, { resolveSource })
|
|
656
|
+
|
|
653
657
|
registerLiveProgressRoutes(ctx)
|
|
654
658
|
|
|
655
659
|
// Reset loop guard when user sends a new message (#370)
|
package/lib/register-tools.js
CHANGED
|
@@ -26,6 +26,7 @@ import { registerInpaintStagingTools } from './tools/inpaint-staging.js'
|
|
|
26
26
|
import { registerBatchQueueTools } from './tools/batch-queue.js'
|
|
27
27
|
import { registerVariantTools } from './tools/variant-selection.js'
|
|
28
28
|
import { registerLineageTools } from './tools/lineage.js'
|
|
29
|
+
import { registerDesignExportTools } from './tools/design-export.js'
|
|
29
30
|
import {
|
|
30
31
|
createLiveProgressSession,
|
|
31
32
|
publishLiveProgress,
|
|
@@ -173,6 +174,9 @@ export function registerAllTools(ctx, deps) {
|
|
|
173
174
|
if (Boolean(sets.lineage)) {
|
|
174
175
|
registerLineageTools(targetCtx, deps)
|
|
175
176
|
}
|
|
177
|
+
if (Boolean(sets.designExport || sets.design_export)) {
|
|
178
|
+
registerDesignExportTools(targetCtx, deps)
|
|
179
|
+
}
|
|
176
180
|
}
|
|
177
181
|
|
|
178
182
|
function dispose() {
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { renderToolOutput } from "../attachment-helper.js"
|
|
2
|
+
// design-export tool (#446) — export_design_asset for Figma / Penpot / OpenDesign
|
|
3
|
+
|
|
4
|
+
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
5
|
+
import { toLosslessJson } from '../providers.js'
|
|
6
|
+
import { formatDesignExportPayload, pushAssetToOpenDesignProject } from '../design-export.js'
|
|
7
|
+
|
|
8
|
+
export function registerDesignExportTools(ctx, deps) {
|
|
9
|
+
const { resolveSource } = deps
|
|
10
|
+
|
|
11
|
+
ctx.effect(() => {
|
|
12
|
+
ctx.tools.register(
|
|
13
|
+
defineTool({
|
|
14
|
+
name: 'export_design_asset',
|
|
15
|
+
description:
|
|
16
|
+
'Export a generated image, SVG stencil, or asset into design-friendly clipboard snippets '
|
|
17
|
+
+ '(Figma HTML img/SVG markup, Penpot SVG snippet, CSS background Data-URL) '
|
|
18
|
+
+ 'or push it directly into a local OpenDesign project folder (#446).',
|
|
19
|
+
parameters: {
|
|
20
|
+
image: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
required: true,
|
|
23
|
+
description: 'Path or attachment ID of the image to export.',
|
|
24
|
+
},
|
|
25
|
+
target: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
enum: ['all', 'opendesign', 'clipboard_svg', 'clipboard_dataurl', 'penpot_snippet', 'figma_html'],
|
|
28
|
+
description: 'Target export format or destination (default: all).',
|
|
29
|
+
},
|
|
30
|
+
project_id: {
|
|
31
|
+
type: 'string',
|
|
32
|
+
description: 'Optional OpenDesign project ID to directly save the file into the project artifacts directory.',
|
|
33
|
+
},
|
|
34
|
+
name: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Optional stem or filename for the exported design asset.',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
output: {
|
|
40
|
+
render: (_args, value) => renderToolOutput(value),
|
|
41
|
+
schema: {
|
|
42
|
+
type: 'object',
|
|
43
|
+
additionalProperties: true,
|
|
44
|
+
properties: {
|
|
45
|
+
name: { type: 'string' },
|
|
46
|
+
mimeType: { type: 'string' },
|
|
47
|
+
isSvg: { type: 'boolean' },
|
|
48
|
+
width: { type: 'number' },
|
|
49
|
+
height: { type: 'number' },
|
|
50
|
+
dataUrl: { type: 'string' },
|
|
51
|
+
svgMarkup: { type: 'string' },
|
|
52
|
+
figmaHtml: { type: 'string' },
|
|
53
|
+
penpotSnippet: { type: 'string' },
|
|
54
|
+
cssBackground: { type: 'string' },
|
|
55
|
+
openDesign: { type: 'object', additionalProperties: true },
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
async execute(args, exec) {
|
|
60
|
+
const source = await resolveSource(ctx, exec, args.image)
|
|
61
|
+
if (!source || !source.bytes) {
|
|
62
|
+
throw new Error(`Image not found or unreadable: ${args.image}`)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const payload = formatDesignExportPayload({
|
|
66
|
+
buffer: source.bytes,
|
|
67
|
+
mimeType: source.mediaType || 'image/png',
|
|
68
|
+
name: args.name || 'exported-asset',
|
|
69
|
+
width: source.width || 1024,
|
|
70
|
+
height: source.height || 1024,
|
|
71
|
+
target: args.target || 'all',
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
let openDesign = null
|
|
75
|
+
if (args.project_id) {
|
|
76
|
+
const ext = payload.isSvg ? '.svg' : '.png'
|
|
77
|
+
const filename = (args.name || 'asset').endsWith(ext) ? args.name : `${args.name || 'asset'}${ext}`
|
|
78
|
+
openDesign = await pushAssetToOpenDesignProject({
|
|
79
|
+
buffer: source.bytes,
|
|
80
|
+
filename,
|
|
81
|
+
projectId: args.project_id,
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return toLosslessJson({
|
|
86
|
+
...payload,
|
|
87
|
+
...(openDesign ? { openDesign } : {}),
|
|
88
|
+
})
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
92
|
+
})
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-image-gen",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Image generation for DeepSeek Harness: a generate_image tool with pluggable providers — the FAL queue, any OpenAI-compatible images API, or a ChatGPT/Grok subscription with no API key at all. The picture is shown inline in the conversation; the model receives either a link (works with any chat model) or the image itself (needs dsh-vision-bridge or a vision-capable model).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|