@dickpy/dsh-imagegen 1.3.0 → 1.4.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.
Files changed (45) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +203 -182
  3. package/cordis.patch.yml +8 -8
  4. package/docs/images/multi-model-comparison.png +0 -0
  5. package/lib/client.js +1103 -837
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +265 -135
  8. package/package.json +70 -68
  9. package/src/agent-image-tools.ts +418 -418
  10. package/src/client/ImageGenPanel.tsx +1699 -1508
  11. package/src/client/SettingsCard.tsx +936 -957
  12. package/src/client/TemplateLibrary.tsx +336 -336
  13. package/src/client/api.ts +193 -193
  14. package/src/client/channels-form.ts +263 -263
  15. package/src/client/controller.ts +46 -46
  16. package/src/client/conversation-sync.ts +14 -0
  17. package/src/client/css-modules.d.ts +5 -5
  18. package/src/client/helpers.ts +33 -33
  19. package/src/client/image-toolview.module.css +73 -73
  20. package/src/client/image-toolview.tsx +169 -158
  21. package/src/client/index.ts +32 -22
  22. package/src/client/locales.ts +610 -594
  23. package/src/client/mount.tsx +185 -96
  24. package/src/client/panel.module.css +1713 -1445
  25. package/src/client/settings-card.module.css +1023 -1023
  26. package/src/client/settings-form.ts +336 -336
  27. package/src/client/settings-scope.ts +298 -298
  28. package/src/client/sidebar-entry.ts +148 -102
  29. package/src/client/templates.module.css +453 -453
  30. package/src/engine.ts +520 -478
  31. package/src/gallery-store.ts +286 -286
  32. package/src/generation-runtime.ts +79 -75
  33. package/src/history-store.ts +250 -244
  34. package/src/image-format.ts +11 -11
  35. package/src/image-models.ts +19 -19
  36. package/src/index.ts +318 -318
  37. package/src/model-catalog.ts +115 -98
  38. package/src/presets.ts +71 -63
  39. package/src/prompt-enhancer.ts +137 -79
  40. package/src/protocol.ts +338 -326
  41. package/src/routes.ts +916 -906
  42. package/src/task-queue.ts +113 -103
  43. package/src/templates/cases.json +10196 -10196
  44. package/src/templates-store.ts +278 -278
  45. package/src/updater.ts +117 -117
package/src/task-queue.ts CHANGED
@@ -1,103 +1,113 @@
1
- /** In-memory, host-resident image generation queue. */
2
-
3
- import { randomUUID } from 'node:crypto'
4
- import type { GenerateRequest, GenerateResult, GenerationTask } from './protocol.ts'
5
-
6
- export type GenerationTaskListener = (task: GenerationTask) => void
7
-
8
- export class GenerationTaskQueue {
9
- private readonly tasks: GenerationTask[] = []
10
- private readonly controllers = new Map<string, AbortController>()
11
- private readonly listeners = new Set<GenerationTaskListener>()
12
- private running = false
13
-
14
- constructor(private readonly run: (request: GenerateRequest, signal: AbortSignal) => Promise<GenerateResult>) {}
15
-
16
- list(): GenerationTask[] {
17
- return this.tasks.map(task => this.snapshot(task))
18
- }
19
-
20
- /** Observe queue state changes. Listener failures never disrupt generation. */
21
- subscribe(listener: GenerationTaskListener): () => void {
22
- this.listeners.add(listener)
23
- return () => { this.listeners.delete(listener) }
24
- }
25
-
26
- submit(request: GenerateRequest): GenerationTask {
27
- const task: GenerationTask = { id: randomUUID(), request: { ...request }, status: 'queued', createdAt: Date.now() }
28
- this.tasks.unshift(task)
29
- this.publish(task)
30
- void this.drain()
31
- return this.snapshot(task)
32
- }
33
-
34
- cancel(id: string): GenerationTask | undefined {
35
- const task = this.tasks.find(item => item.id === id)
36
- if (task === undefined || task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') return task
37
- task.status = 'cancelled'
38
- task.finishedAt = Date.now()
39
- this.controllers.get(id)?.abort()
40
- this.publish(task)
41
- return this.snapshot(task)
42
- }
43
-
44
- retry(id: string): GenerationTask | undefined {
45
- const previous = this.tasks.find(item => item.id === id)
46
- return previous === undefined ? undefined : this.submit(previous.request)
47
- }
48
-
49
- private async drain(): Promise<void> {
50
- if (this.running) return
51
- this.running = true
52
- try {
53
- for (;;) {
54
- const task = this.tasks.find(item => item.status === 'queued')
55
- if (task === undefined) return
56
- task.status = 'running'
57
- task.startedAt = Date.now()
58
- this.publish(task)
59
- const controller = new AbortController()
60
- this.controllers.set(task.id, controller)
61
- try {
62
- const result = await this.run(task.request, controller.signal)
63
- if (this.tasks.find(item => item.id === task.id)?.status !== 'cancelled') {
64
- task.status = 'completed'
65
- task.result = result
66
- task.finishedAt = Date.now()
67
- this.publish(task)
68
- }
69
- } catch (error) {
70
- if (this.tasks.find(item => item.id === task.id)?.status !== 'cancelled') {
71
- task.status = 'failed'
72
- task.error = error instanceof Error ? error.message : String(error)
73
- task.finishedAt = Date.now()
74
- this.publish(task)
75
- }
76
- } finally {
77
- this.controllers.delete(task.id)
78
- }
79
- }
80
- } finally {
81
- this.running = false
82
- }
83
- }
84
-
85
- private publish(task: GenerationTask): void {
86
- const snapshot = this.snapshot(task)
87
- for (const listener of this.listeners) {
88
- try {
89
- listener(snapshot)
90
- } catch {
91
- // Observers must not be able to interrupt the queue pump.
92
- }
93
- }
94
- }
95
-
96
- private snapshot(task: GenerationTask): GenerationTask {
97
- return {
98
- ...task,
99
- request: { ...task.request },
100
- ...task.result === undefined ? {} : { result: task.result },
101
- }
102
- }
103
- }
1
+ /** In-memory, host-resident image generation queue. */
2
+
3
+ import { randomUUID } from 'node:crypto'
4
+ import type { GenerateRequest, GenerateResult, GenerationTask } from './protocol.ts'
5
+
6
+ export type GenerationTaskListener = (task: GenerationTask) => void
7
+
8
+ export class GenerationTaskQueue {
9
+ private readonly tasks: GenerationTask[] = []
10
+ private readonly controllers = new Map<string, AbortController>()
11
+ private readonly listeners = new Set<GenerationTaskListener>()
12
+ private running = 0
13
+ private serialRunning = false
14
+
15
+ constructor(
16
+ private readonly run: (request: GenerateRequest, signal: AbortSignal) => Promise<GenerateResult>,
17
+ private readonly concurrency = 1,
18
+ ) {}
19
+
20
+ list(): GenerationTask[] {
21
+ return this.tasks.map(task => this.snapshot(task))
22
+ }
23
+
24
+ /** Observe queue state changes. Listener failures never disrupt generation. */
25
+ subscribe(listener: GenerationTaskListener): () => void {
26
+ this.listeners.add(listener)
27
+ return () => { this.listeners.delete(listener) }
28
+ }
29
+
30
+ submit(request: GenerateRequest): GenerationTask {
31
+ const task: GenerationTask = { id: randomUUID(), request: { ...request }, status: 'queued', createdAt: Date.now() }
32
+ this.tasks.unshift(task)
33
+ this.publish(task)
34
+ this.drain()
35
+ return this.snapshot(task)
36
+ }
37
+
38
+ cancel(id: string): GenerationTask | undefined {
39
+ const task = this.tasks.find(item => item.id === id)
40
+ if (task === undefined || task.status === 'completed' || task.status === 'failed' || task.status === 'cancelled') return task
41
+ task.status = 'cancelled'
42
+ task.finishedAt = Date.now()
43
+ this.controllers.get(id)?.abort()
44
+ this.publish(task)
45
+ this.drain()
46
+ return this.snapshot(task)
47
+ }
48
+
49
+ retry(id: string): GenerationTask | undefined {
50
+ const previous = this.tasks.find(item => item.id === id)
51
+ return previous === undefined ? undefined : this.submit(previous.request)
52
+ }
53
+
54
+ private drain(): void {
55
+ while (this.running < Math.max(1, this.concurrency)) {
56
+ const task = this.tasks.find(item => item.status === 'queued'
57
+ && (this.running === 0 || (item.request.comparisonId !== undefined && !this.serialRunning)))
58
+ if (task === undefined) return
59
+ this.running += 1
60
+ if (task.request.comparisonId === undefined) this.serialRunning = true
61
+ void this.runTask(task).finally(() => {
62
+ this.running -= 1
63
+ if (task.request.comparisonId === undefined) this.serialRunning = false
64
+ this.drain()
65
+ })
66
+ }
67
+ }
68
+
69
+ private async runTask(task: GenerationTask): Promise<void> {
70
+ task.status = 'running'
71
+ task.startedAt = Date.now()
72
+ this.publish(task)
73
+ const controller = new AbortController()
74
+ this.controllers.set(task.id, controller)
75
+ try {
76
+ const result = await this.run(task.request, controller.signal)
77
+ if (this.tasks.find(item => item.id === task.id)?.status !== 'cancelled') {
78
+ task.status = 'completed'
79
+ task.result = result
80
+ task.finishedAt = Date.now()
81
+ this.publish(task)
82
+ }
83
+ } catch (error) {
84
+ if (this.tasks.find(item => item.id === task.id)?.status !== 'cancelled') {
85
+ task.status = 'failed'
86
+ task.error = error instanceof Error ? error.message : String(error)
87
+ task.finishedAt = Date.now()
88
+ this.publish(task)
89
+ }
90
+ } finally {
91
+ this.controllers.delete(task.id)
92
+ }
93
+ }
94
+
95
+ private publish(task: GenerationTask): void {
96
+ const snapshot = this.snapshot(task)
97
+ for (const listener of this.listeners) {
98
+ try {
99
+ listener(snapshot)
100
+ } catch {
101
+ // Observers must not be able to interrupt the queue pump.
102
+ }
103
+ }
104
+ }
105
+
106
+ private snapshot(task: GenerationTask): GenerationTask {
107
+ return {
108
+ ...task,
109
+ request: { ...task.request },
110
+ ...task.result === undefined ? {} : { result: task.result },
111
+ }
112
+ }
113
+ }