@goodandready/dsh-image-gen 0.10.33 โ†’ 0.10.34

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.
@@ -80,3 +80,12 @@ export async function saveAndAttachResult(ctx, exec, cfg, {
80
80
  },
81
81
  })
82
82
  }
83
+
84
+ export function renderToolOutput(value) {
85
+ const text = (value && value.summary) || (typeof value === "string" ? value : JSON.stringify(value || ""))
86
+ const blocks = [{ type: "text", text }]
87
+ if (value && value.attachment) {
88
+ blocks.push({ type: "image", attachment: value.attachment })
89
+ }
90
+ return blocks
91
+ }
package/lib/client.js CHANGED
@@ -100,6 +100,7 @@ window.__ModuleLoader__.load({
100
100
  .ig-btn:hover:not(:disabled){background:var(--dsw-alias-bg-layer-4, var(--dsw-alias-bg-layer-2));border-color:var(--dsw-alias-label-dimmed, var(--dsw-alias-border-l2))}
101
101
  .ig-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3);border-color:transparent}
102
102
  .ig-btn-primary:hover:not(:disabled){opacity:0.9}
103
+ .ig-btn-active{background:var(--dsw-alias-brand-primary, #3b82f6);color:#ffffff;border-color:transparent}
103
104
  .ig-btn-danger{color:var(--dsw-alias-state-error-primary);border-color:var(--dsw-alias-state-error-primary)}
104
105
  .ig-btn-danger:hover:not(:disabled){background:var(--dsw-alias-state-error-bg, color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent));border-color:var(--dsw-alias-state-error-primary)}
105
106
  .ig-btn-disabled{opacity:0.5;cursor:not-allowed}
@@ -1603,6 +1604,7 @@ window.__ModuleLoader__.load({
1603
1604
  const canvasRef = react.useRef(null)
1604
1605
  const isDrawingRef = react.useRef(false)
1605
1606
  const [brushSize, setBrushSize] = react.useState(24)
1607
+ const [toolMode, setToolMode] = react.useState('brush')
1606
1608
  const [inpaintPrompt, setInpaintPrompt] = react.useState('')
1607
1609
  const [copied, setCopied] = react.useState(false)
1608
1610
  const [hasStrokes, setHasStrokes] = react.useState(false)
@@ -1652,8 +1654,9 @@ window.__ModuleLoader__.load({
1652
1654
  ctx.lineWidth = brushSize
1653
1655
  ctx.lineCap = 'round'
1654
1656
  ctx.lineJoin = 'round'
1655
- ctx.strokeStyle = '#ffffff'
1656
- ctx.fillStyle = '#ffffff'
1657
+ const color = toolMode === 'eraser' ? '#000000' : '#ffffff'
1658
+ ctx.strokeStyle = color
1659
+ ctx.fillStyle = color
1657
1660
 
1658
1661
  ctx.beginPath()
1659
1662
  ctx.arc(pos.x, pos.y, brushSize / 2, 0, Math.PI * 2)
@@ -1675,7 +1678,8 @@ window.__ModuleLoader__.load({
1675
1678
  ctx.lineWidth = brushSize
1676
1679
  ctx.lineCap = 'round'
1677
1680
  ctx.lineJoin = 'round'
1678
- ctx.strokeStyle = '#ffffff'
1681
+ const color = toolMode === 'eraser' ? '#000000' : '#ffffff'
1682
+ ctx.strokeStyle = color
1679
1683
  ctx.lineTo(pos.x, pos.y)
1680
1684
  ctx.stroke()
1681
1685
  setHasStrokes(true)
@@ -1711,6 +1715,27 @@ window.__ModuleLoader__.load({
1711
1715
  }
1712
1716
  }
1713
1717
 
1718
+ function handleInvert() {
1719
+ const canvas = canvasRef.current
1720
+ if (!canvas) return
1721
+ const ctx = canvas.getContext('2d')
1722
+ if (!ctx) return
1723
+ const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height)
1724
+ const d = imgData.data
1725
+ for (let i = 0; i < d.length; i += 4) {
1726
+ d[i] = 255 - d[i]
1727
+ d[i + 1] = 255 - d[i + 1]
1728
+ d[i + 2] = 255 - d[i + 2]
1729
+ d[i + 3] = 255
1730
+ }
1731
+ ctx.putImageData(imgData, 0, 0)
1732
+ if (undoStackRef.current.length >= 15) {
1733
+ undoStackRef.current.shift()
1734
+ }
1735
+ undoStackRef.current.push(ctx.getImageData(0, 0, canvas.width, canvas.height))
1736
+ setHasStrokes(true)
1737
+ }
1738
+
1714
1739
  function handleClear() {
1715
1740
  initCanvas()
1716
1741
  setHasStrokes(false)
@@ -1762,8 +1787,38 @@ window.__ModuleLoader__.load({
1762
1787
  max: '64',
1763
1788
  value: brushSize,
1764
1789
  onChange: (e) => setBrushSize(parseInt(e.target.value, 10)),
1765
- style: { width: '120px', cursor: 'pointer' },
1790
+ style: { width: '100px', cursor: 'pointer' },
1766
1791
  }),
1792
+ react.createElement(
1793
+ 'button',
1794
+ {
1795
+ type: 'button',
1796
+ className: 'ig-btn' + (toolMode === 'brush' ? ' ig-btn-active' : ''),
1797
+ style: { padding: '3px 8px', fontSize: '11px' },
1798
+ onClick: () => setToolMode('brush'),
1799
+ },
1800
+ '๐Ÿ–Œ๏ธ ' + (t('inpaint.brush') || 'Brush')
1801
+ ),
1802
+ react.createElement(
1803
+ 'button',
1804
+ {
1805
+ type: 'button',
1806
+ className: 'ig-btn' + (toolMode === 'eraser' ? ' ig-btn-active' : ''),
1807
+ style: { padding: '3px 8px', fontSize: '11px' },
1808
+ onClick: () => setToolMode('eraser'),
1809
+ },
1810
+ '๐Ÿงน ' + (t('inpaint.eraser') || 'Eraser')
1811
+ ),
1812
+ react.createElement(
1813
+ 'button',
1814
+ {
1815
+ type: 'button',
1816
+ className: 'ig-btn',
1817
+ style: { padding: '3px 8px', fontSize: '11px' },
1818
+ onClick: handleInvert,
1819
+ },
1820
+ '๐Ÿ”„ ' + (t('inpaint.invert') || 'Invert')
1821
+ ),
1767
1822
  react.createElement(
1768
1823
  'button',
1769
1824
  {
@@ -2015,7 +2070,10 @@ window.__ModuleLoader__.load({
2015
2070
  'card.actionRemoveBg': 'Remove background',
2016
2071
  'card.inpaint': 'Inpaint',
2017
2072
  'inpaint.title': 'Inpainting Canvas (Paint white mask over region to edit)',
2018
- 'inpaint.brush_size': 'Brush',
2073
+ 'inpaint.brush_size': 'Size',
2074
+ 'inpaint.brush': 'Brush',
2075
+ 'inpaint.eraser': 'Eraser',
2076
+ 'inpaint.invert': 'Invert Mask',
2019
2077
  'inpaint.clear': 'Clear Mask',
2020
2078
  'inpaint.undo': 'Undo',
2021
2079
  'inpaint.apply': 'Apply Inpaint',
@@ -2236,7 +2294,10 @@ window.__ModuleLoader__.load({
2236
2294
  'card.actionRemoveBg': '็งป้™ค่ƒŒๆ™ฏ',
2237
2295
  'card.inpaint': 'ๅฑ€้ƒจ้‡็ป˜',
2238
2296
  'inpaint.title': '็”ปๅธƒๅฑ€้ƒจ้‡็ป˜๏ผˆๆถ‚ๆŠน็™ฝ่‰ฒ่’™็‰ˆๆ ‡่ฎฐๅŒบๅŸŸ๏ผ‰',
2239
- 'inpaint.brush_size': '็”ป็ฌ”ๅคงๅฐ',
2297
+ 'inpaint.brush_size': 'ๅคงๅฐ',
2298
+ 'inpaint.brush': '็”ป็ฌ”',
2299
+ 'inpaint.eraser': 'ๆฉก็šฎๆ“ฆ',
2300
+ 'inpaint.invert': 'ๅ่ฝฌ่’™็‰ˆ',
2240
2301
  'inpaint.clear': 'ๆธ…็ฉบ่’™็‰ˆ',
2241
2302
  'inpaint.undo': 'ๆ’ค้”€',
2242
2303
  'inpaint.apply': '็”Ÿๆˆๅฑ€้ƒจ้‡็ป˜',
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // editing โ€” image-gen tools (edit_image, vary_image). Extracted from apply() (#216).
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -98,7 +99,8 @@ export function registerEditingTools(ctx, deps) {
98
99
  },
99
100
  },
100
101
  output: {
101
- schema: {
102
+ render: (_args, value) => renderToolOutput(value),
103
+ schema: {
102
104
  type: 'object',
103
105
  additionalProperties: true,
104
106
  properties: {
@@ -231,7 +233,8 @@ export function registerEditingTools(ctx, deps) {
231
233
  },
232
234
  },
233
235
  output: {
234
- schema: {
236
+ render: (_args, value) => renderToolOutput(value),
237
+ schema: {
235
238
  type: 'object',
236
239
  additionalProperties: true,
237
240
  properties: {
@@ -370,7 +373,8 @@ export function registerEditingTools(ctx, deps) {
370
373
  },
371
374
  },
372
375
  output: {
373
- schema: {
376
+ render: (_args, value) => renderToolOutput(value),
377
+ schema: {
374
378
  type: 'object',
375
379
  additionalProperties: true,
376
380
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // frontend โ€” image-gen tools (extract_design_tokens, image_to_css_gradient, check_image_contrast, optimize_vector_svg, generate_pwa_icon_suite). Extracted from apply() (#216).
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -57,7 +58,8 @@ export function registerFrontendTools(ctx, deps) {
57
58
  prefix: { type: 'string', description: 'Token naming prefix (default: color).' },
58
59
  },
59
60
  output: {
60
- schema: {
61
+ render: (_args, value) => renderToolOutput(value),
62
+ schema: {
61
63
  type: 'object',
62
64
  additionalProperties: true,
63
65
  properties: {
@@ -95,7 +97,8 @@ export function registerFrontendTools(ctx, deps) {
95
97
  type: { type: 'string', enum: ['mesh', 'linear', 'radial'], description: 'Gradient type: mesh, linear, or radial (default: mesh).' },
96
98
  },
97
99
  output: {
98
- schema: {
100
+ render: (_args, value) => renderToolOutput(value),
101
+ schema: {
99
102
  type: 'object',
100
103
  additionalProperties: true,
101
104
  properties: {
@@ -134,7 +137,8 @@ export function registerFrontendTools(ctx, deps) {
134
137
  text_color: { type: 'string', description: 'Text hex color to test against (default: #ffffff).' },
135
138
  },
136
139
  output: {
137
- schema: {
140
+ render: (_args, value) => renderToolOutput(value),
141
+ schema: {
138
142
  type: 'object',
139
143
  additionalProperties: true,
140
144
  properties: {
@@ -176,7 +180,8 @@ export function registerFrontendTools(ctx, deps) {
176
180
  component_name: { type: 'string', description: 'React component name for TSX export (default: VectorIcon).' },
177
181
  },
178
182
  output: {
179
- schema: {
183
+ render: (_args, value) => renderToolOutput(value),
184
+ schema: {
180
185
  type: 'object',
181
186
  additionalProperties: true,
182
187
  properties: {
@@ -220,7 +225,8 @@ export function registerFrontendTools(ctx, deps) {
220
225
  icons_dir: { type: 'string', description: 'Directory for icons relative to webroot (default: icons).' },
221
226
  },
222
227
  output: {
223
- schema: {
228
+ render: (_args, value) => renderToolOutput(value),
229
+ schema: {
224
230
  type: 'object',
225
231
  additionalProperties: true,
226
232
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  import { defineTool } from '@deepseek-ai/dsh-tools'
2
3
  import { toLosslessJson } from '../providers.js'
3
4
  import { slugify } from '../index.js'
@@ -16,7 +17,8 @@ export function registerGenerationPackTool(ctx, deps) {
16
17
  output_name: { type: 'string', description: 'Base file name for the pack.' },
17
18
  },
18
19
  output: {
19
- schema: {
20
+ render: (_args, value) => renderToolOutput(value),
21
+ schema: {
20
22
  type: 'object',
21
23
  additionalProperties: true,
22
24
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // inspect โ€” image-gen tools (compare_images, inspect_image_quality). Extracted from apply() (#216).
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -57,7 +58,8 @@ export function registerInspectTools(ctx, deps) {
57
58
  image_b: { type: 'string', required: true, description: 'Path or attachment id of the second image.' },
58
59
  },
59
60
  output: {
60
- schema: {
61
+ render: (_args, value) => renderToolOutput(value),
62
+ schema: {
61
63
  type: 'object',
62
64
  additionalProperties: false,
63
65
  properties: {
@@ -84,7 +86,8 @@ export function registerInspectTools(ctx, deps) {
84
86
  expected_elements: { type: 'string', description: 'What elements should be present and verified.' },
85
87
  },
86
88
  output: {
87
- schema: {
89
+ render: (_args, value) => renderToolOutput(value),
90
+ schema: {
88
91
  type: 'object',
89
92
  additionalProperties: false,
90
93
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // pattern โ€” generate_seamless_pattern tool (#178)
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -46,7 +47,8 @@ export function registerPatternTools(ctx, deps) {
46
47
  },
47
48
  },
48
49
  output: {
49
- schema: {
50
+ render: (_args, value) => renderToolOutput(value),
51
+ schema: {
50
52
  type: 'object',
51
53
  additionalProperties: true,
52
54
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // processing-basic โ€” core image-gen tools (remove_background, upscale_image, vectorize_image, blend_images). Split from processing.js (#239).
2
3
 
3
4
  // processing โ€” image-gen tools (remove_background, upscale_image, vectorize_image, blend_images). Extracted from apply() (#216).
@@ -80,7 +81,8 @@ export function registerProcessingAdvancedTools(ctx, deps) {
80
81
  },
81
82
  },
82
83
  output: {
83
- schema: {
84
+ render: (_args, value) => renderToolOutput(value),
85
+ schema: {
84
86
  type: 'object',
85
87
  additionalProperties: true,
86
88
  properties: {
@@ -254,7 +256,8 @@ export function registerProcessingAdvancedTools(ctx, deps) {
254
256
  },
255
257
  },
256
258
  output: {
257
- schema: {
259
+ render: (_args, value) => renderToolOutput(value),
260
+ schema: {
258
261
  type: 'object',
259
262
  additionalProperties: true,
260
263
  properties: {
@@ -420,7 +423,8 @@ export function registerProcessingAdvancedTools(ctx, deps) {
420
423
  },
421
424
  },
422
425
  output: {
423
- schema: {
426
+ render: (_args, value) => renderToolOutput(value),
427
+ schema: {
424
428
  type: 'object',
425
429
  additionalProperties: true,
426
430
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // processing-basic โ€” core image-gen tools (remove_background, upscale_image, vectorize_image, blend_images). Split from processing.js (#239).
2
3
 
3
4
  // processing โ€” image-gen tools (remove_background, upscale_image, vectorize_image, blend_images). Extracted from apply() (#216).
@@ -59,7 +60,8 @@ export function registerProcessingTools(ctx, deps) {
59
60
  output_name: { type: 'string', description: 'Custom output file name without extension.' },
60
61
  },
61
62
  output: {
62
- schema: {
63
+ render: (_args, value) => renderToolOutput(value),
64
+ schema: {
63
65
  type: 'object',
64
66
  additionalProperties: true,
65
67
  properties: {
@@ -127,7 +129,8 @@ export function registerProcessingTools(ctx, deps) {
127
129
  output_name: { type: 'string', description: 'Custom output file name.' },
128
130
  },
129
131
  output: {
130
- schema: {
132
+ render: (_args, value) => renderToolOutput(value),
133
+ schema: {
131
134
  type: 'object',
132
135
  additionalProperties: true,
133
136
  properties: {
@@ -193,7 +196,8 @@ export function registerProcessingTools(ctx, deps) {
193
196
  output_name: { type: 'string', description: 'Custom output file name.' },
194
197
  },
195
198
  output: {
196
- schema: {
199
+ render: (_args, value) => renderToolOutput(value),
200
+ schema: {
197
201
  type: 'object',
198
202
  additionalProperties: true,
199
203
  properties: {
@@ -256,7 +260,8 @@ export function registerProcessingTools(ctx, deps) {
256
260
  output_name: { type: 'string', description: 'Custom output file name.' },
257
261
  },
258
262
  output: {
259
- schema: {
263
+ render: (_args, value) => renderToolOutput(value),
264
+ schema: {
260
265
  type: 'object',
261
266
  additionalProperties: true,
262
267
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // responsive โ€” generate_responsive_mockups tool (#173)
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -82,7 +83,8 @@ export function registerResponsiveTools(ctx, deps) {
82
83
  },
83
84
  },
84
85
  output: {
85
- schema: {
86
+ render: (_args, value) => renderToolOutput(value),
87
+ schema: {
86
88
  type: 'object',
87
89
  additionalProperties: true,
88
90
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // sketch โ€” sketch_to_image tool (#146)
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -63,7 +64,8 @@ export function registerSketchTools(ctx, deps) {
63
64
  },
64
65
  },
65
66
  output: {
66
- schema: {
67
+ render: (_args, value) => renderToolOutput(value),
68
+ schema: {
67
69
  type: 'object',
68
70
  additionalProperties: true,
69
71
  properties: {
@@ -1,3 +1,4 @@
1
+ import { renderToolOutput } from "../attachment-helper.js"
1
2
  // spritesheet โ€” generate_spritesheet tool (#177)
2
3
 
3
4
  import { defineTool } from '@deepseek-ai/dsh-tools'
@@ -53,7 +54,8 @@ export function registerSpritesheetTools(ctx, deps) {
53
54
  },
54
55
  },
55
56
  output: {
56
- schema: {
57
+ render: (_args, value) => renderToolOutput(value),
58
+ schema: {
57
59
  type: 'object',
58
60
  additionalProperties: true,
59
61
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-image-gen",
3
- "version": "0.10.33",
3
+ "version": "0.10.34",
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",