@dickpy/dsh-imagegen 1.2.3 → 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.
- package/LICENSE +201 -201
- package/README.md +203 -181
- package/cordis.patch.yml +8 -8
- package/docs/images/multi-model-comparison.png +0 -0
- package/lib/client.js +2711 -1318
- package/lib/client.js.map +1 -1
- package/lib/index.js +830 -155
- package/package.json +70 -68
- package/src/agent-image-tools.ts +418 -316
- package/src/client/ImageGenPanel.tsx +1703 -1476
- package/src/client/SettingsCard.tsx +936 -648
- package/src/client/TemplateLibrary.tsx +336 -336
- package/src/client/api.ts +193 -193
- package/src/client/channels-form.ts +263 -0
- package/src/client/controller.ts +46 -46
- package/src/client/conversation-sync.ts +14 -0
- package/src/client/css-modules.d.ts +5 -5
- package/src/client/helpers.ts +33 -33
- package/src/client/image-toolview.module.css +73 -73
- package/src/client/image-toolview.tsx +170 -152
- package/src/client/index.ts +32 -22
- package/src/client/locales.ts +610 -484
- package/src/client/mount.tsx +185 -96
- package/src/client/panel.module.css +1713 -1445
- package/src/client/settings-card.module.css +1023 -536
- package/src/client/settings-form.ts +336 -336
- package/src/client/settings-scope.ts +298 -250
- package/src/client/sidebar-entry.ts +148 -102
- package/src/client/templates.module.css +453 -453
- package/src/engine.ts +520 -464
- package/src/gallery-store.ts +286 -280
- package/src/generation-runtime.ts +79 -48
- package/src/history-store.ts +250 -238
- package/src/image-format.ts +11 -0
- package/src/image-models.ts +19 -19
- package/src/index.ts +318 -212
- package/src/model-catalog.ts +115 -0
- package/src/presets.ts +71 -0
- package/src/prompt-enhancer.ts +137 -79
- package/src/protocol.ts +338 -253
- package/src/routes.ts +916 -738
- package/src/task-queue.ts +113 -103
- package/src/templates/cases.json +10196 -10196
- package/src/templates-store.ts +278 -278
- package/src/updater.ts +117 -117
|
@@ -1,31 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the
|
|
7
|
-
* sibling plugin family block). The injection self-heals: a MutationObserver
|
|
8
|
-
* watches the sidebar root and re-inserts the row whenever a React re-render
|
|
9
|
-
* displaces it (re-insertion happens in the same frame, before paint).
|
|
10
|
-
*
|
|
11
|
-
* The row is plain DOM (no React tree) so it can never disturb the shell's
|
|
12
|
-
* reconciliation; the panel view it toggles is a separate React root mounted
|
|
13
|
-
* in the center column (see mount.tsx).
|
|
2
|
+
* Replace the shell's standalone New Session affordance with a two-tab entry:
|
|
3
|
+
* New Session and Image Generation. When image generation is active, the
|
|
4
|
+
* plugin also uses the shell's region area as a dedicated history surface;
|
|
5
|
+
* the original workspace/session tree remains underneath and is restored when
|
|
6
|
+
* the panel closes.
|
|
14
7
|
*/
|
|
15
8
|
|
|
16
9
|
import type { ImageGenController } from './controller.ts'
|
|
17
10
|
import css from './panel.module.css'
|
|
18
11
|
|
|
19
|
-
/** Stable
|
|
20
|
-
export const ENTRY_SELECTOR = '[data-dsh-imagegen-
|
|
12
|
+
/** Stable selector for the injected two-tab host. */
|
|
13
|
+
export const ENTRY_SELECTOR = '[data-dsh-imagegen-session-tabs]'
|
|
14
|
+
/** Stable selector for the history surface in the shell region area. */
|
|
15
|
+
export const HISTORY_HOST_SELECTOR = '[data-dsh-imagegen-history-host]'
|
|
21
16
|
|
|
22
|
-
/** Inline
|
|
23
|
-
const
|
|
17
|
+
/** Inline picture glyph kept deliberately small for the sidebar rail. */
|
|
18
|
+
const IMAGE_ICON = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="2.5" width="12" height="11" rx="1.5"/><circle cx="5.6" cy="5.8" r="1"/><path d="M2.5 12.5l3.6-3.4 2.4 2.2 3-3 2 2.4"/></svg>'
|
|
24
19
|
|
|
25
|
-
/**
|
|
26
|
-
const
|
|
20
|
+
/** Inline plus glyph for the new-session tab. */
|
|
21
|
+
const NEW_SESSION_ICON = '<svg viewBox="0 0 16 16" width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M8 3v10M3 8h10"/></svg>'
|
|
27
22
|
|
|
28
|
-
/** Find the sidebar shell root
|
|
23
|
+
/** Find the sidebar shell root, or undefined while it is not mounted. */
|
|
29
24
|
function sidebarRoot(): HTMLElement | undefined {
|
|
30
25
|
const column = document.querySelector<HTMLElement>('[data-pane="sidebar"], [class*="sidebarCol"]')
|
|
31
26
|
if (column === null) return undefined
|
|
@@ -33,112 +28,163 @@ function sidebarRoot(): HTMLElement | undefined {
|
|
|
33
28
|
return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)
|
|
34
29
|
}
|
|
35
30
|
|
|
36
|
-
/** The New Session button
|
|
31
|
+
/** The shell-owned New Session button across current and legacy shells. */
|
|
37
32
|
function newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
return root.querySelector<HTMLButtonElement>(
|
|
34
|
+
'button[data-dsh-part="new-session"], button[class*="newSession"]',
|
|
35
|
+
) ?? Array.from(root.children).find(
|
|
36
|
+
(child): child is HTMLButtonElement => child instanceof HTMLElement && child.tagName === 'BUTTON',
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Locate the shell region that normally contains workspaces and sessions. */
|
|
41
|
+
function regionArea(root: HTMLElement): HTMLElement | undefined {
|
|
42
|
+
return root.querySelector<HTMLElement>('[class*="regionArea"]') ?? undefined
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function makeTab(
|
|
46
|
+
label: string,
|
|
47
|
+
tooltip: string,
|
|
48
|
+
icon: string,
|
|
49
|
+
onClick: () => void,
|
|
50
|
+
): HTMLButtonElement {
|
|
51
|
+
const tab = document.createElement('button')
|
|
52
|
+
tab.type = 'button'
|
|
53
|
+
tab.className = css.sessionTab
|
|
54
|
+
tab.setAttribute('aria-label', label)
|
|
55
|
+
tab.setAttribute('title', tooltip)
|
|
56
|
+
tab.innerHTML = `<span class="${css.sessionTabIcon}">${icon}</span><span class="${css.sessionTabLabel}">${label}</span>`
|
|
57
|
+
tab.addEventListener('click', onClick)
|
|
58
|
+
return tab
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function hideShellButton(button: HTMLButtonElement): void {
|
|
62
|
+
button.dataset.dshImagegenOriginal = ''
|
|
63
|
+
button.setAttribute('aria-hidden', 'true')
|
|
64
|
+
button.tabIndex = -1
|
|
65
|
+
button.style.display = 'none'
|
|
44
66
|
}
|
|
45
67
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
entry.className = css.entry
|
|
52
|
-
entry.setAttribute('aria-label', label)
|
|
53
|
-
entry.setAttribute('title', tooltip)
|
|
54
|
-
entry.innerHTML = '<span class="' + css.entryIcon + '">' + ICON + '</span><span class="' + css.entryLabel + '">' + label + '</span>'
|
|
55
|
-
entry.addEventListener('click', () => { controller.toggle() })
|
|
56
|
-
return entry
|
|
68
|
+
function restoreShellButton(button: HTMLButtonElement): void {
|
|
69
|
+
button.style.removeProperty('display')
|
|
70
|
+
button.removeAttribute('aria-hidden')
|
|
71
|
+
button.removeAttribute('tabindex')
|
|
72
|
+
delete button.dataset.dshImagegenOriginal
|
|
57
73
|
}
|
|
58
74
|
|
|
59
|
-
/**
|
|
60
|
-
function
|
|
75
|
+
/** Mount or repair the two-tab host at the shell's New Session position. */
|
|
76
|
+
function placeTabs(
|
|
77
|
+
root: HTMLElement,
|
|
78
|
+
controller: ImageGenController,
|
|
79
|
+
newSessionLabel: string,
|
|
80
|
+
newSessionTooltip: string,
|
|
81
|
+
imageLabel: string,
|
|
82
|
+
imageTooltip: string,
|
|
83
|
+
): HTMLDivElement | undefined {
|
|
61
84
|
const button = newSessionButton(root)
|
|
62
|
-
if (button === undefined) return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const base = (row !== null && row.parentElement === root) ? row : button
|
|
69
|
-
const family = Array.from(root.children).filter(
|
|
70
|
-
(el): el is HTMLElement => el instanceof HTMLElement && el.matches(FAMILY_ENTRY_SELECTOR),
|
|
71
|
-
)
|
|
72
|
-
const anchor = family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling
|
|
73
|
-
root.insertBefore(entry, anchor)
|
|
85
|
+
if (button === undefined) return undefined
|
|
86
|
+
|
|
87
|
+
const existing = root.querySelector<HTMLDivElement>(ENTRY_SELECTOR)
|
|
88
|
+
if (existing !== null && existing.parentElement === button.parentElement) {
|
|
89
|
+
hideShellButton(button)
|
|
90
|
+
return existing
|
|
74
91
|
}
|
|
75
|
-
|
|
92
|
+
|
|
93
|
+
existing?.remove()
|
|
94
|
+
const tabs = document.createElement('div')
|
|
95
|
+
tabs.dataset.dshImagegenSessionTabs = ''
|
|
96
|
+
tabs.className = css.sessionTabs
|
|
97
|
+
tabs.setAttribute('role', 'tablist')
|
|
98
|
+
tabs.setAttribute('aria-label', imageTooltip)
|
|
99
|
+
|
|
100
|
+
const newSessionTab = makeTab(newSessionLabel, newSessionTooltip, NEW_SESSION_ICON, () => {
|
|
101
|
+
controller.close()
|
|
102
|
+
button.click()
|
|
103
|
+
})
|
|
104
|
+
const imageTab = makeTab(imageLabel, imageTooltip, IMAGE_ICON, () => {
|
|
105
|
+
controller.open()
|
|
106
|
+
})
|
|
107
|
+
newSessionTab.dataset.dshImagegenTab = 'new-session'
|
|
108
|
+
imageTab.dataset.dshImagegenTab = 'image'
|
|
109
|
+
tabs.append(newSessionTab, imageTab)
|
|
110
|
+
|
|
111
|
+
button.parentElement?.insertBefore(tabs, button)
|
|
112
|
+
hideShellButton(button)
|
|
113
|
+
return tabs
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Mount an overlay host over the workspace/session tree for image history. */
|
|
117
|
+
function placeHistoryHost(root: HTMLElement): HTMLDivElement | undefined {
|
|
118
|
+
const region = regionArea(root)
|
|
119
|
+
if (region === undefined) return undefined
|
|
120
|
+
const existing = region.querySelector<HTMLDivElement>(HISTORY_HOST_SELECTOR)
|
|
121
|
+
if (existing !== null) return existing
|
|
122
|
+
const host = document.createElement('div')
|
|
123
|
+
host.dataset.dshImagegenHistoryHost = ''
|
|
124
|
+
host.className = css.sidebarHistoryHost
|
|
125
|
+
region.append(host)
|
|
126
|
+
return host
|
|
76
127
|
}
|
|
77
128
|
|
|
78
129
|
/**
|
|
79
|
-
* Mount the
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @param label - the entry label (localized).
|
|
83
|
-
* @param tooltip - the entry tooltip (localized).
|
|
84
|
-
* @returns disposer removing the entry and its observers.
|
|
130
|
+
* Mount the two tabs and self-heal after React rebuilds the sidebar. The
|
|
131
|
+
* shell-owned button is restored by the disposer so unloading the plugin
|
|
132
|
+
* leaves the host unchanged.
|
|
85
133
|
*/
|
|
86
|
-
export function mountSidebarEntry(
|
|
87
|
-
|
|
134
|
+
export function mountSidebarEntry(
|
|
135
|
+
controller: ImageGenController,
|
|
136
|
+
newSessionLabel: string,
|
|
137
|
+
newSessionTooltip: string,
|
|
138
|
+
imageLabel: string,
|
|
139
|
+
imageTooltip: string,
|
|
140
|
+
): () => void {
|
|
88
141
|
let root: HTMLElement | undefined
|
|
89
|
-
let
|
|
142
|
+
let tabs: HTMLDivElement | undefined
|
|
143
|
+
let historyHost: HTMLDivElement | undefined
|
|
144
|
+
let originalButton: HTMLButtonElement | undefined
|
|
90
145
|
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
146
|
+
const syncActive = (): void => {
|
|
147
|
+
if (tabs === undefined) return
|
|
148
|
+
const newTab = tabs.querySelector<HTMLElement>('[data-dsh-imagegen-tab="new-session"]')
|
|
149
|
+
const imageTab = tabs.querySelector<HTMLElement>('[data-dsh-imagegen-tab="image"]')
|
|
150
|
+
if (controller.getSnapshot().panelOpen) {
|
|
151
|
+
if (newTab !== null) delete newTab.dataset.active
|
|
152
|
+
if (imageTab !== null) imageTab.dataset.active = ''
|
|
153
|
+
} else {
|
|
154
|
+
if (newTab !== null) newTab.dataset.active = ''
|
|
155
|
+
if (imageTab !== null) delete imageTab.dataset.active
|
|
96
156
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const ensure = (): void => {
|
|
160
|
+
if (root !== undefined && !root.isConnected) {
|
|
100
161
|
root = undefined
|
|
101
|
-
|
|
162
|
+
tabs = undefined
|
|
163
|
+
historyHost = undefined
|
|
164
|
+
originalButton = undefined
|
|
102
165
|
}
|
|
103
166
|
root ??= sidebarRoot()
|
|
104
167
|
if (root === undefined) return
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
168
|
+
root.dataset.dshImagegenSidebarRoot = ''
|
|
169
|
+
const button = newSessionButton(root)
|
|
170
|
+
if (button === undefined) return
|
|
171
|
+
originalButton ??= button
|
|
172
|
+
tabs = placeTabs(root, controller, newSessionLabel, newSessionTooltip, imageLabel, imageTooltip)
|
|
173
|
+
historyHost = placeHistoryHost(root)
|
|
174
|
+
syncActive()
|
|
109
175
|
}
|
|
110
176
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
waitObserver.observe(document.body, { childList: true, subtree: true })
|
|
114
|
-
|
|
115
|
-
// Self-heal: if a React re-render displaces the row, re-insert it in the
|
|
116
|
-
// same frame (microtask before paint -> no visible flicker).
|
|
117
|
-
const rootObserver = new MutationObserver(() => {
|
|
118
|
-
if (root === undefined || !root.isConnected) {
|
|
119
|
-
placed = false
|
|
120
|
-
tryPlace()
|
|
121
|
-
return
|
|
122
|
-
}
|
|
123
|
-
if (!root.contains(entry)) {
|
|
124
|
-
placed = placeEntry(root, entry)
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
// Reflect the panel's open state on the row (active highlight).
|
|
129
|
-
const syncActive = () => {
|
|
130
|
-
if (controller.getSnapshot().panelOpen) entry.dataset.active = 'true'
|
|
131
|
-
else delete entry.dataset.active
|
|
132
|
-
}
|
|
177
|
+
const bodyObserver = new MutationObserver(ensure)
|
|
178
|
+
bodyObserver.observe(document.body, { childList: true, subtree: true })
|
|
133
179
|
const unsubscribe = controller.subscribe(syncActive)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
tryPlace()
|
|
180
|
+
ensure()
|
|
137
181
|
|
|
138
182
|
return () => {
|
|
139
|
-
|
|
140
|
-
rootObserver.disconnect()
|
|
183
|
+
bodyObserver.disconnect()
|
|
141
184
|
unsubscribe()
|
|
142
|
-
|
|
185
|
+
tabs?.remove()
|
|
186
|
+
historyHost?.remove()
|
|
187
|
+
if (originalButton !== undefined && originalButton.isConnected) restoreShellButton(originalButton)
|
|
188
|
+
if (root !== undefined) delete root.dataset.dshImagegenSidebarRoot
|
|
143
189
|
}
|
|
144
190
|
}
|