@dickpy/dsh-imagegen 1.4.0 → 1.5.1

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.
Files changed (54) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +270 -124
  3. package/cordis.patch.yml +8 -8
  4. package/docs/images/ecommerce-mode.png +0 -0
  5. package/docs/images/image-generation-studio-three-column.png +0 -0
  6. package/docs/images/imagegen-overview.png +0 -0
  7. package/docs/videos/agent-chat-edit.gif +0 -0
  8. package/docs/videos/agent-chat-edit.mp4 +0 -0
  9. package/lib/client.js +1873 -431
  10. package/lib/client.js.map +1 -1
  11. package/lib/index.js +355 -116
  12. package/package.json +77 -70
  13. package/src/agent-image-tools.ts +447 -418
  14. package/src/client/ImageGenPanel.tsx +1243 -348
  15. package/src/client/SettingsCard.tsx +936 -936
  16. package/src/client/TemplateLibrary.tsx +336 -336
  17. package/src/client/api.ts +203 -193
  18. package/src/client/channels-form.ts +263 -263
  19. package/src/client/controller.ts +46 -46
  20. package/src/client/conversation-sync.ts +14 -14
  21. package/src/client/css-modules.d.ts +5 -5
  22. package/src/client/helpers.ts +33 -33
  23. package/src/client/image-toolview.module.css +73 -73
  24. package/src/client/image-toolview.tsx +34 -28
  25. package/src/client/index.ts +25 -24
  26. package/src/client/locales.ts +156 -28
  27. package/src/client/mount.tsx +117 -117
  28. package/src/client/panel.module.css +1243 -455
  29. package/src/client/settings-card.module.css +1023 -1023
  30. package/src/client/settings-form.ts +337 -336
  31. package/src/client/settings-scope.ts +302 -298
  32. package/src/client/sidebar-entry.ts +190 -190
  33. package/src/client/templates.module.css +453 -453
  34. package/src/edit-image-command.ts +110 -0
  35. package/src/engine.ts +520 -520
  36. package/src/gallery-store.ts +306 -286
  37. package/src/generation-runtime.ts +84 -79
  38. package/src/history-store.ts +270 -250
  39. package/src/image-format.ts +11 -11
  40. package/src/image-models.ts +19 -19
  41. package/src/index.ts +337 -318
  42. package/src/model-catalog.ts +115 -115
  43. package/src/presets.ts +71 -71
  44. package/src/prompt-enhancer.ts +137 -137
  45. package/src/protocol.ts +380 -338
  46. package/src/routes.ts +966 -916
  47. package/src/settings-compat.ts +60 -0
  48. package/src/task-queue.ts +113 -113
  49. package/src/templates/cases.json +10196 -10196
  50. package/src/templates-store.ts +278 -278
  51. package/src/updater.ts +117 -117
  52. package/docs/images/agent-chat-edit.png +0 -0
  53. package/docs/images/agent-chat-generate.png +0 -0
  54. package/docs/images/agent-chat-poster-workflow.png +0 -0
package/src/client/api.ts CHANGED
@@ -1,193 +1,203 @@
1
- /**
2
- * Browser-side API client for the /api/dsh-imagegen route family. The only
3
- * data access path the panel uses — plain fetch, same origin.
4
- */
5
-
6
- import { GALLERY_API, GENERATE_API, HISTORY_API, PROMPT_ENHANCE_API, TASK_API, TEMPLATES_API, UPDATE_API, type GenerateRequest, type GenerateResult, type GenerationTask, type HistoryEntry, type HistoryEntryInput, type TemplateListResult, type TemplateRefreshResult, type UpdateInfo } from '../protocol.ts'
7
-
8
- /** Error carrying the route's JSON error message. */
9
- export class ImageGenApiError extends Error {
10
- /** Stable wire code from the host. */
11
- readonly code: string
12
-
13
- constructor(message: string, code = 'generate-failed') {
14
- super(message)
15
- this.name = 'ImageGenApiError'
16
- this.code = code
17
- }
18
- }
19
-
20
- /** Parse the { ok, ... } envelope or throw an ImageGenApiError. */
21
- async function readEnvelope<T>(response: Response): Promise<T> {
22
- let body: unknown
23
- try {
24
- body = await response.json()
25
- } catch {
26
- throw new ImageGenApiError(`HTTP ${response.status}: invalid JSON response`)
27
- }
28
- if (body === null || typeof body !== 'object') {
29
- throw new ImageGenApiError(`HTTP ${response.status}: malformed response`)
30
- }
31
- const record = body as { ok?: unknown; message?: unknown; code?: unknown }
32
- if (record.ok !== true) {
33
- throw new ImageGenApiError(
34
- typeof record.message === 'string' ? record.message : `HTTP ${response.status}`,
35
- typeof record.code === 'string' ? record.code : 'generate-failed',
36
- )
37
- }
38
- return body as T
39
- }
40
-
41
- /** The browser half's data entry point. */
42
- export class ImageGenApi {
43
- /** Ask the host to check the latest stable GitHub Release. */
44
- async updateCheck(): Promise<UpdateInfo> {
45
- const response = await fetch(UPDATE_API.check, { method: 'POST' })
46
- const body = await readEnvelope<{ ok: true; update: UpdateInfo }>(response)
47
- return body.update
48
- }
49
-
50
- /** Ask the host to install a previously discovered Release. */
51
- async updateApply(version: string): Promise<{ updatedVersion: string; restartRequired: boolean }> {
52
- const response = await fetch(UPDATE_API.apply, {
53
- method: 'POST',
54
- headers: { 'content-type': 'application/json' },
55
- body: JSON.stringify({ version }),
56
- })
57
- const body = await readEnvelope<{ ok: true; updatedVersion: string; restartRequired: boolean }>(response)
58
- return { updatedVersion: body.updatedVersion, restartRequired: body.restartRequired }
59
- }
60
-
61
- /** Forward one generate request to the host proxy. */
62
- async generate(request: GenerateRequest): Promise<GenerateResult> {
63
- const response = await fetch(GENERATE_API, {
64
- method: 'POST',
65
- headers: { 'content-type': 'application/json' },
66
- body: JSON.stringify(request),
67
- })
68
- const body = await readEnvelope<{ ok: true; images: GenerateResult['images']; history?: HistoryEntry[]; historyError?: string }>(response)
69
- return {
70
- images: body.images,
71
- ...body.history === undefined ? {} : { history: body.history },
72
- ...body.historyError === undefined ? {} : { historyError: body.historyError },
73
- }
74
- }
75
-
76
- /** Ask the configured chat model to expand a concise image prompt. */
77
- async enhancePrompt(prompt: string): Promise<string> {
78
- const response = await fetch(PROMPT_ENHANCE_API.enhance, {
79
- method: 'POST',
80
- headers: { 'content-type': 'application/json' },
81
- body: JSON.stringify({ prompt }),
82
- })
83
- const body = await readEnvelope<{ ok: true; prompt: string }>(response)
84
- return body.prompt
85
- }
86
-
87
- async taskSubmit(request: GenerateRequest): Promise<GenerationTask> {
88
- const response = await fetch(TASK_API.submit, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(request) })
89
- return (await readEnvelope<{ ok: true; task: GenerationTask }>(response)).task
90
- }
91
-
92
- async taskList(): Promise<GenerationTask[]> {
93
- const response = await fetch(TASK_API.list, { method: 'POST' })
94
- return (await readEnvelope<{ ok: true; tasks: GenerationTask[] }>(response)).tasks
95
- }
96
-
97
- async taskCancel(id: string): Promise<GenerationTask> {
98
- const response = await fetch(TASK_API.cancel, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) })
99
- return (await readEnvelope<{ ok: true; task: GenerationTask }>(response)).task
100
- }
101
-
102
- async taskRetry(id: string): Promise<GenerationTask> {
103
- const response = await fetch(TASK_API.retry, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) })
104
- return (await readEnvelope<{ ok: true; task: GenerationTask }>(response)).task
105
- }
106
-
107
- /** List the host-persisted history (newest first). */
108
- async historyList(): Promise<HistoryEntry[]> {
109
- const response = await fetch(HISTORY_API.list, { method: 'POST' })
110
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
111
- return body.entries
112
- }
113
-
114
- /** Remove one history entry by id. */
115
- async historyRemove(id: string): Promise<HistoryEntry[]> {
116
- const response = await fetch(HISTORY_API.remove, {
117
- method: 'POST',
118
- headers: { 'content-type': 'application/json' },
119
- body: JSON.stringify({ id }),
120
- })
121
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
122
- return body.entries
123
- }
124
-
125
- /** Clear the entire history. */
126
- async historyClear(): Promise<HistoryEntry[]> {
127
- const response = await fetch(HISTORY_API.clear, { method: 'POST' })
128
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
129
- return body.entries
130
- }
131
-
132
- /** List the host-persisted gallery (newest first). */
133
- async galleryList(): Promise<HistoryEntry[]> {
134
- const response = await fetch(GALLERY_API.list, { method: 'POST' })
135
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
136
- return body.entries
137
- }
138
-
139
- /** Append one image to the gallery. The host assigns the id and skips the
140
- * append when a content-identical image is already in the gallery. */
141
- async galleryAppend(entry: HistoryEntryInput): Promise<{ entries: HistoryEntry[]; added: boolean }> {
142
- const response = await fetch(GALLERY_API.append, {
143
- method: 'POST',
144
- headers: { 'content-type': 'application/json' },
145
- body: JSON.stringify({ entry }),
146
- })
147
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[]; added: boolean }>(response)
148
- return { entries: body.entries, added: body.added }
149
- }
150
-
151
- /** Remove one gallery entry by id. */
152
- async galleryRemove(id: string): Promise<HistoryEntry[]> {
153
- const response = await fetch(GALLERY_API.remove, {
154
- method: 'POST',
155
- headers: { 'content-type': 'application/json' },
156
- body: JSON.stringify({ id }),
157
- })
158
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
159
- return body.entries
160
- }
161
-
162
- /** Clear the entire gallery. */
163
- async galleryClear(): Promise<HistoryEntry[]> {
164
- const response = await fetch(GALLERY_API.clear, { method: 'POST' })
165
- const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
166
- return body.entries
167
- }
168
-
169
- async gallerySetTags(id: string, tags: string[]): Promise<HistoryEntry[]> {
170
- const response = await fetch(GALLERY_API.tags, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, tags }) })
171
- return (await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)).entries
172
- }
173
-
174
- /** Fetch the prompt-template library (bundled snapshot or refreshed copy). */
175
- async templatesList(): Promise<TemplateListResult> {
176
- const response = await fetch(TEMPLATES_API.list, { method: 'POST' })
177
- const body = await readEnvelope<TemplateListResult & { ok: true }>(response)
178
- return {
179
- cases: body.cases,
180
- total: body.total,
181
- origin: body.origin,
182
- repository: body.repository,
183
- fetchedAt: body.fetchedAt,
184
- }
185
- }
186
-
187
- /** Re-download the template library from the upstream mirror (host-side). */
188
- async templatesRefresh(): Promise<TemplateRefreshResult> {
189
- const response = await fetch(TEMPLATES_API.refresh, { method: 'POST' })
190
- const body = await readEnvelope<TemplateRefreshResult & { ok: true }>(response)
191
- return { total: body.total, fetchedAt: body.fetchedAt }
192
- }
193
- }
1
+ /**
2
+ * Browser-side API client for the /api/dsh-imagegen route family. The only
3
+ * data access path the panel uses — plain fetch, same origin.
4
+ */
5
+
6
+ import { CONVERSATION_IMAGE_API, GALLERY_API, GENERATE_API, HISTORY_API, PROMPT_ENHANCE_API, TASK_API, TEMPLATES_API, UPDATE_API, type GenerateRequest, type GenerateResult, type GenerationTask, type HistoryEntry, type HistoryEntryInput, type TemplateListResult, type TemplateRefreshResult, type UpdateInfo } from '../protocol.ts'
7
+
8
+ /** Error carrying the route's JSON error message. */
9
+ export class ImageGenApiError extends Error {
10
+ /** Stable wire code from the host. */
11
+ readonly code: string
12
+
13
+ constructor(message: string, code = 'generate-failed') {
14
+ super(message)
15
+ this.name = 'ImageGenApiError'
16
+ this.code = code
17
+ }
18
+ }
19
+
20
+ /** Parse the { ok, ... } envelope or throw an ImageGenApiError. */
21
+ async function readEnvelope<T>(response: Response): Promise<T> {
22
+ let body: unknown
23
+ try {
24
+ body = await response.json()
25
+ } catch {
26
+ throw new ImageGenApiError(`HTTP ${response.status}: invalid JSON response`)
27
+ }
28
+ if (body === null || typeof body !== 'object') {
29
+ throw new ImageGenApiError(`HTTP ${response.status}: malformed response`)
30
+ }
31
+ const record = body as { ok?: unknown; message?: unknown; code?: unknown }
32
+ if (record.ok !== true) {
33
+ throw new ImageGenApiError(
34
+ typeof record.message === 'string' ? record.message : `HTTP ${response.status}`,
35
+ typeof record.code === 'string' ? record.code : 'generate-failed',
36
+ )
37
+ }
38
+ return body as T
39
+ }
40
+
41
+ /** The browser half's data entry point. */
42
+ export class ImageGenApi {
43
+ /** Ask the host to check the latest stable GitHub Release. */
44
+ async updateCheck(): Promise<UpdateInfo> {
45
+ const response = await fetch(UPDATE_API.check, { method: 'POST' })
46
+ const body = await readEnvelope<{ ok: true; update: UpdateInfo }>(response)
47
+ return body.update
48
+ }
49
+
50
+ /** Ask the host to install a previously discovered Release. */
51
+ async updateApply(version: string): Promise<{ updatedVersion: string; restartRequired: boolean }> {
52
+ const response = await fetch(UPDATE_API.apply, {
53
+ method: 'POST',
54
+ headers: { 'content-type': 'application/json' },
55
+ body: JSON.stringify({ version }),
56
+ })
57
+ const body = await readEnvelope<{ ok: true; updatedVersion: string; restartRequired: boolean }>(response)
58
+ return { updatedVersion: body.updatedVersion, restartRequired: body.restartRequired }
59
+ }
60
+
61
+ /** Forward one generate request to the host proxy. */
62
+ async generate(request: GenerateRequest): Promise<GenerateResult> {
63
+ const response = await fetch(GENERATE_API, {
64
+ method: 'POST',
65
+ headers: { 'content-type': 'application/json' },
66
+ body: JSON.stringify(request),
67
+ })
68
+ const body = await readEnvelope<{ ok: true; images: GenerateResult['images']; history?: HistoryEntry[]; historyError?: string }>(response)
69
+ return {
70
+ images: body.images,
71
+ ...body.history === undefined ? {} : { history: body.history },
72
+ ...body.historyError === undefined ? {} : { historyError: body.historyError },
73
+ }
74
+ }
75
+
76
+ /** Ask the configured chat model to expand a concise image prompt. */
77
+ async enhancePrompt(prompt: string): Promise<string> {
78
+ const response = await fetch(PROMPT_ENHANCE_API.enhance, {
79
+ method: 'POST',
80
+ headers: { 'content-type': 'application/json' },
81
+ body: JSON.stringify({ prompt }),
82
+ })
83
+ const body = await readEnvelope<{ ok: true; prompt: string }>(response)
84
+ return body.prompt
85
+ }
86
+
87
+ /** Stage a generated image as a durable reference for `/edit_image`. */
88
+ async attachConversationImage(sessionId: string, dataUrl: string, name: string): Promise<void> {
89
+ const response = await fetch(CONVERSATION_IMAGE_API, {
90
+ method: 'POST',
91
+ headers: { 'content-type': 'application/json' },
92
+ body: JSON.stringify({ sessionId, dataUrl, name }),
93
+ })
94
+ await readEnvelope<{ ok: true }>(response)
95
+ }
96
+
97
+ async taskSubmit(request: GenerateRequest): Promise<GenerationTask> {
98
+ const response = await fetch(TASK_API.submit, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(request) })
99
+ return (await readEnvelope<{ ok: true; task: GenerationTask }>(response)).task
100
+ }
101
+
102
+ async taskList(): Promise<GenerationTask[]> {
103
+ const response = await fetch(TASK_API.list, { method: 'POST' })
104
+ return (await readEnvelope<{ ok: true; tasks: GenerationTask[] }>(response)).tasks
105
+ }
106
+
107
+ async taskCancel(id: string): Promise<GenerationTask> {
108
+ const response = await fetch(TASK_API.cancel, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) })
109
+ return (await readEnvelope<{ ok: true; task: GenerationTask }>(response)).task
110
+ }
111
+
112
+ async taskRetry(id: string): Promise<GenerationTask> {
113
+ const response = await fetch(TASK_API.retry, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id }) })
114
+ return (await readEnvelope<{ ok: true; task: GenerationTask }>(response)).task
115
+ }
116
+
117
+ /** List the host-persisted history (newest first). */
118
+ async historyList(): Promise<HistoryEntry[]> {
119
+ const response = await fetch(HISTORY_API.list, { method: 'POST' })
120
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
121
+ return body.entries
122
+ }
123
+
124
+ /** Remove one history entry by id. */
125
+ async historyRemove(id: string): Promise<HistoryEntry[]> {
126
+ const response = await fetch(HISTORY_API.remove, {
127
+ method: 'POST',
128
+ headers: { 'content-type': 'application/json' },
129
+ body: JSON.stringify({ id }),
130
+ })
131
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
132
+ return body.entries
133
+ }
134
+
135
+ /** Clear the entire history. */
136
+ async historyClear(): Promise<HistoryEntry[]> {
137
+ const response = await fetch(HISTORY_API.clear, { method: 'POST' })
138
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
139
+ return body.entries
140
+ }
141
+
142
+ /** List the host-persisted gallery (newest first). */
143
+ async galleryList(): Promise<HistoryEntry[]> {
144
+ const response = await fetch(GALLERY_API.list, { method: 'POST' })
145
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
146
+ return body.entries
147
+ }
148
+
149
+ /** Append one image to the gallery. The host assigns the id and skips the
150
+ * append when a content-identical image is already in the gallery. */
151
+ async galleryAppend(entry: HistoryEntryInput): Promise<{ entries: HistoryEntry[]; added: boolean }> {
152
+ const response = await fetch(GALLERY_API.append, {
153
+ method: 'POST',
154
+ headers: { 'content-type': 'application/json' },
155
+ body: JSON.stringify({ entry }),
156
+ })
157
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[]; added: boolean }>(response)
158
+ return { entries: body.entries, added: body.added }
159
+ }
160
+
161
+ /** Remove one gallery entry by id. */
162
+ async galleryRemove(id: string): Promise<HistoryEntry[]> {
163
+ const response = await fetch(GALLERY_API.remove, {
164
+ method: 'POST',
165
+ headers: { 'content-type': 'application/json' },
166
+ body: JSON.stringify({ id }),
167
+ })
168
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
169
+ return body.entries
170
+ }
171
+
172
+ /** Clear the entire gallery. */
173
+ async galleryClear(): Promise<HistoryEntry[]> {
174
+ const response = await fetch(GALLERY_API.clear, { method: 'POST' })
175
+ const body = await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)
176
+ return body.entries
177
+ }
178
+
179
+ async gallerySetTags(id: string, tags: string[]): Promise<HistoryEntry[]> {
180
+ const response = await fetch(GALLERY_API.tags, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ id, tags }) })
181
+ return (await readEnvelope<{ ok: true; entries: HistoryEntry[] }>(response)).entries
182
+ }
183
+
184
+ /** Fetch the prompt-template library (bundled snapshot or refreshed copy). */
185
+ async templatesList(): Promise<TemplateListResult> {
186
+ const response = await fetch(TEMPLATES_API.list, { method: 'POST' })
187
+ const body = await readEnvelope<TemplateListResult & { ok: true }>(response)
188
+ return {
189
+ cases: body.cases,
190
+ total: body.total,
191
+ origin: body.origin,
192
+ repository: body.repository,
193
+ fetchedAt: body.fetchedAt,
194
+ }
195
+ }
196
+
197
+ /** Re-download the template library from the upstream mirror (host-side). */
198
+ async templatesRefresh(): Promise<TemplateRefreshResult> {
199
+ const response = await fetch(TEMPLATES_API.refresh, { method: 'POST' })
200
+ const body = await readEnvelope<TemplateRefreshResult & { ok: true }>(response)
201
+ return { total: body.total, fetchedAt: body.fetchedAt }
202
+ }
203
+ }