@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.
- package/LICENSE +201 -201
- package/README.md +270 -124
- package/cordis.patch.yml +8 -8
- package/docs/images/ecommerce-mode.png +0 -0
- package/docs/images/image-generation-studio-three-column.png +0 -0
- package/docs/images/imagegen-overview.png +0 -0
- package/docs/videos/agent-chat-edit.gif +0 -0
- package/docs/videos/agent-chat-edit.mp4 +0 -0
- package/lib/client.js +1873 -431
- package/lib/client.js.map +1 -1
- package/lib/index.js +355 -116
- package/package.json +77 -70
- package/src/agent-image-tools.ts +447 -418
- package/src/client/ImageGenPanel.tsx +1243 -348
- package/src/client/SettingsCard.tsx +936 -936
- package/src/client/TemplateLibrary.tsx +336 -336
- package/src/client/api.ts +203 -193
- package/src/client/channels-form.ts +263 -263
- package/src/client/controller.ts +46 -46
- package/src/client/conversation-sync.ts +14 -14
- 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 +34 -28
- package/src/client/index.ts +25 -24
- package/src/client/locales.ts +156 -28
- package/src/client/mount.tsx +117 -117
- package/src/client/panel.module.css +1243 -455
- package/src/client/settings-card.module.css +1023 -1023
- package/src/client/settings-form.ts +337 -336
- package/src/client/settings-scope.ts +302 -298
- package/src/client/sidebar-entry.ts +190 -190
- package/src/client/templates.module.css +453 -453
- package/src/edit-image-command.ts +110 -0
- package/src/engine.ts +520 -520
- package/src/gallery-store.ts +306 -286
- package/src/generation-runtime.ts +84 -79
- package/src/history-store.ts +270 -250
- package/src/image-format.ts +11 -11
- package/src/image-models.ts +19 -19
- package/src/index.ts +337 -318
- package/src/model-catalog.ts +115 -115
- package/src/presets.ts +71 -71
- package/src/prompt-enhancer.ts +137 -137
- package/src/protocol.ts +380 -338
- package/src/routes.ts +966 -916
- package/src/settings-compat.ts +60 -0
- package/src/task-queue.ts +113 -113
- package/src/templates/cases.json +10196 -10196
- package/src/templates-store.ts +278 -278
- package/src/updater.ts +117 -117
- package/docs/images/agent-chat-edit.png +0 -0
- package/docs/images/agent-chat-generate.png +0 -0
- package/docs/images/agent-chat-poster-workflow.png +0 -0
|
@@ -1,190 +1,190 @@
|
|
|
1
|
-
/**
|
|
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.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { ImageGenController } from './controller.ts'
|
|
10
|
-
import css from './panel.module.css'
|
|
11
|
-
|
|
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]'
|
|
16
|
-
|
|
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>'
|
|
19
|
-
|
|
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>'
|
|
22
|
-
|
|
23
|
-
/** Find the sidebar shell root, or undefined while it is not mounted. */
|
|
24
|
-
function sidebarRoot(): HTMLElement | undefined {
|
|
25
|
-
const column = document.querySelector<HTMLElement>('[data-pane="sidebar"], [class*="sidebarCol"]')
|
|
26
|
-
if (column === null) return undefined
|
|
27
|
-
const logoOwner = column.querySelector<HTMLElement>('[class*="logoRow"]')?.parentElement
|
|
28
|
-
return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** The shell-owned New Session button across current and legacy shells. */
|
|
32
|
-
function newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {
|
|
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'
|
|
66
|
-
}
|
|
67
|
-
|
|
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
|
|
73
|
-
}
|
|
74
|
-
|
|
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 {
|
|
84
|
-
const button = newSessionButton(root)
|
|
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
|
|
91
|
-
}
|
|
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
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
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.
|
|
133
|
-
*/
|
|
134
|
-
export function mountSidebarEntry(
|
|
135
|
-
controller: ImageGenController,
|
|
136
|
-
newSessionLabel: string,
|
|
137
|
-
newSessionTooltip: string,
|
|
138
|
-
imageLabel: string,
|
|
139
|
-
imageTooltip: string,
|
|
140
|
-
): () => void {
|
|
141
|
-
let root: HTMLElement | undefined
|
|
142
|
-
let tabs: HTMLDivElement | undefined
|
|
143
|
-
let historyHost: HTMLDivElement | undefined
|
|
144
|
-
let originalButton: HTMLButtonElement | undefined
|
|
145
|
-
|
|
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
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const ensure = (): void => {
|
|
160
|
-
if (root !== undefined && !root.isConnected) {
|
|
161
|
-
root = undefined
|
|
162
|
-
tabs = undefined
|
|
163
|
-
historyHost = undefined
|
|
164
|
-
originalButton = undefined
|
|
165
|
-
}
|
|
166
|
-
root ??= sidebarRoot()
|
|
167
|
-
if (root === undefined) return
|
|
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()
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const bodyObserver = new MutationObserver(ensure)
|
|
178
|
-
bodyObserver.observe(document.body, { childList: true, subtree: true })
|
|
179
|
-
const unsubscribe = controller.subscribe(syncActive)
|
|
180
|
-
ensure()
|
|
181
|
-
|
|
182
|
-
return () => {
|
|
183
|
-
bodyObserver.disconnect()
|
|
184
|
-
unsubscribe()
|
|
185
|
-
tabs?.remove()
|
|
186
|
-
historyHost?.remove()
|
|
187
|
-
if (originalButton !== undefined && originalButton.isConnected) restoreShellButton(originalButton)
|
|
188
|
-
if (root !== undefined) delete root.dataset.dshImagegenSidebarRoot
|
|
189
|
-
}
|
|
190
|
-
}
|
|
1
|
+
/**
|
|
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.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ImageGenController } from './controller.ts'
|
|
10
|
+
import css from './panel.module.css'
|
|
11
|
+
|
|
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]'
|
|
16
|
+
|
|
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>'
|
|
19
|
+
|
|
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>'
|
|
22
|
+
|
|
23
|
+
/** Find the sidebar shell root, or undefined while it is not mounted. */
|
|
24
|
+
function sidebarRoot(): HTMLElement | undefined {
|
|
25
|
+
const column = document.querySelector<HTMLElement>('[data-pane="sidebar"], [class*="sidebarCol"]')
|
|
26
|
+
if (column === null) return undefined
|
|
27
|
+
const logoOwner = column.querySelector<HTMLElement>('[class*="logoRow"]')?.parentElement
|
|
28
|
+
return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The shell-owned New Session button across current and legacy shells. */
|
|
32
|
+
function newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {
|
|
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'
|
|
66
|
+
}
|
|
67
|
+
|
|
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
|
|
73
|
+
}
|
|
74
|
+
|
|
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 {
|
|
84
|
+
const button = newSessionButton(root)
|
|
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
|
|
91
|
+
}
|
|
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
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
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.
|
|
133
|
+
*/
|
|
134
|
+
export function mountSidebarEntry(
|
|
135
|
+
controller: ImageGenController,
|
|
136
|
+
newSessionLabel: string,
|
|
137
|
+
newSessionTooltip: string,
|
|
138
|
+
imageLabel: string,
|
|
139
|
+
imageTooltip: string,
|
|
140
|
+
): () => void {
|
|
141
|
+
let root: HTMLElement | undefined
|
|
142
|
+
let tabs: HTMLDivElement | undefined
|
|
143
|
+
let historyHost: HTMLDivElement | undefined
|
|
144
|
+
let originalButton: HTMLButtonElement | undefined
|
|
145
|
+
|
|
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
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const ensure = (): void => {
|
|
160
|
+
if (root !== undefined && !root.isConnected) {
|
|
161
|
+
root = undefined
|
|
162
|
+
tabs = undefined
|
|
163
|
+
historyHost = undefined
|
|
164
|
+
originalButton = undefined
|
|
165
|
+
}
|
|
166
|
+
root ??= sidebarRoot()
|
|
167
|
+
if (root === undefined) return
|
|
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()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const bodyObserver = new MutationObserver(ensure)
|
|
178
|
+
bodyObserver.observe(document.body, { childList: true, subtree: true })
|
|
179
|
+
const unsubscribe = controller.subscribe(syncActive)
|
|
180
|
+
ensure()
|
|
181
|
+
|
|
182
|
+
return () => {
|
|
183
|
+
bodyObserver.disconnect()
|
|
184
|
+
unsubscribe()
|
|
185
|
+
tabs?.remove()
|
|
186
|
+
historyHost?.remove()
|
|
187
|
+
if (originalButton !== undefined && originalButton.isConnected) restoreShellButton(originalButton)
|
|
188
|
+
if (root !== undefined) delete root.dataset.dshImagegenSidebarRoot
|
|
189
|
+
}
|
|
190
|
+
}
|