@jaychang1989/dsh-webchat 0.4.0 → 0.4.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dsh-webchat
2
2
 
3
- Opens the official [chat.deepseek.com](https://chat.deepseek.com) web app from inside [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): one sidebar entry, one button, and the official page itself.
3
+ Opens the official [chat.deepseek.com](https://chat.deepseek.com) web app from inside [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness): one sidebar entry, and **clicking it opens the official page directly**.
4
4
 
5
5
  **It no longer reimplements the chat UI.** The official web app is the client — model picker, deep think, smart search, history and attachments all live there. This plugin opens it and nothing else.
6
6
 
@@ -15,7 +15,7 @@ A real window is therefore the only faithful way to show the official page.
15
15
 
16
16
  ## What opens, in order
17
17
 
18
- The button tries these in order and reports which one worked:
18
+ Clicking the entry tries these in order and reports which one worked in a short toast:
19
19
 
20
20
  1. **`app-window`** — a window created by the DSH desktop process itself (the desktop app is Electron). An already-open one is focused instead of duplicated.
21
21
  2. **`app-window-shell`** — a chromeless Edge/Chrome window (`--app=`) with its own `--user-data-dir` (`~/.dsh/dsh-webchat/app-window`), so its login stays separate from your everyday browser. Used when the host refuses (1).
@@ -41,9 +41,10 @@ dsh plugin --profile desktop add github:jaychang1989/dsh-webchat
41
41
 
42
42
  ## Use
43
43
 
44
- 1. Click the "DeepSeek 网页 / DeepSeek Web" entry in the sidebar.
45
- 2. Click "Open chat.deepseek.com" in the panel.
46
- 3. Sign in once in the window it opens; afterwards just use the official web app.
44
+ 1. Click the "DeepSeek 网页 / DeepSeek Web" entry in the sidebar — the page opens directly, there is no second click.
45
+ 2. Sign in once in the window it opens; afterwards just use the official web app.
46
+
47
+ A toast in the corner reports the outcome: `已打开 DeepSeek 网页` / `已交给系统默认浏览器打开` / a failure reason (an `HTTP 404` usually means the host half is still an older version — restart the desktop app).
47
48
 
48
49
  ## Difference from 0.3.x — this release deletes most of the plugin
49
50
 
package/README.zh.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # dsh-webchat
2
2
 
3
- 在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 里打开 [chat.deepseek.com](https://chat.deepseek.com) 官方网页版:侧边栏一个入口,面板一个按钮,点开就是官方页面。
3
+ 在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 里打开 [chat.deepseek.com](https://chat.deepseek.com) 官方网页版:侧边栏一个入口,**点一下就直接打开官方页面**。
4
4
 
5
5
  **不再重新实现聊天界面。** 官方网页版本身就是完整的客户端——模型选择、深度思考、智能搜索、历史记录、附件上传都在里面。本插件只负责把它打开,别的一概不管。
6
6
 
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## 打开顺序
17
17
 
18
- 按钮会按顺序尝试,第一个成功的生效,面板上会显示实际用了哪种:
18
+ 点击入口后会按顺序尝试,第一个成功的生效,结果用一条短提示告诉你实际用了哪种:
19
19
 
20
20
  1. **`app-window`** —— 由 DSH 桌面端进程直接创建的窗口(桌面端就是 Electron)。已经开着就聚焦,不会重复开第二个。
21
21
  2. **`app-window-shell`** —— 无边框的 Edge/Chrome 窗口(`--app=`),使用独立 `--user-data-dir`(`~/.dsh/dsh-webchat/app-window`),因此登录态与你的日常浏览器互不干扰。用于第 1 种被宿主拒绝的场景。
@@ -41,9 +41,10 @@ dsh plugin --profile desktop add github:jaychang1989/dsh-webchat
41
41
 
42
42
  ## 使用
43
43
 
44
- 1. 点侧边栏的「DeepSeek 网页」入口;
45
- 2. 点面板里的「打开 chat.deepseek.com」;
46
- 3. 在弹出的窗口里登录一次,之后正常使用官方网页版。
44
+ 1. 点侧边栏的「DeepSeek 网页」入口 —— 页面直接打开,不需要再点第二次;
45
+ 2. 在弹出的窗口里登录一次,之后正常使用官方网页版。
46
+
47
+ 点击后右下角会有一条短提示说明结果:`已打开 DeepSeek 网页` / `已交给系统默认浏览器打开` / 失败原因(例如 `HTTP 404`,通常意味着宿主半区还是旧版本,重启桌面端即可)。
47
48
 
48
49
  ## 与 0.3.x 的差异(这是一次大幅删减)
49
50
 
package/lib/client.js CHANGED
@@ -1,14 +1,15 @@
1
1
  /**
2
2
  * dsh-webchat — browser half.
3
3
  *
4
- * One sidebar entry and one panel holding one button. The panel does not chat:
5
- * chat.deepseek.com cannot be framed (`frame-ancestors 'none'`) and this build
6
- * has `webviewTag: false`, so the official page opens in a window instead (see
7
- * lib/index.js). Everything here is plain DOM — no React, no build step.
4
+ * One sidebar entry. Clicking it opens the official DeepSeek web app in a
5
+ * window (see lib/index.js) and reports the outcome in a short toast. There is
6
+ * no panel and no in-GUI chat: chat.deepseek.com cannot be framed
7
+ * (`frame-ancestors 'none'`) and this build has `webviewTag: false`, so the
8
+ * official page can only live in a window of its own.
8
9
  *
9
- * The shell exposes no slot an external plugin can register into, so the entry
10
- * row and the center-column view are injected at the DOM level and self-heal
11
- * against React re-renders, exactly as the previous release did.
10
+ * Everything here is plain DOM — no React, no build step. The shell exposes no
11
+ * slot an external plugin can register into, so the entry row is injected at
12
+ * the DOM level and self-heals against React re-renders.
12
13
  */
13
14
  window.__ModuleLoader__.load({
14
15
  id: "@jaychang1989/dsh-webchat",
@@ -17,16 +18,10 @@ window.__ModuleLoader__.load({
17
18
  var exports = module.exports
18
19
 
19
20
  /** Routes served by lib/index.js. */
20
- var API = { state: '/api/dsh-webchat/state', open: '/api/dsh-webchat/open' }
21
- /** The page the launcher opens (mirrors PAGE_URL on the host half). */
22
- var PAGE_URL = 'https://chat.deepseek.com/'
21
+ var API = { open: '/api/dsh-webchat/open' }
23
22
  var ENTRY_ATTR = 'data-dsh-webchat-entry'
24
- var VIEW_ATTR = 'data-dsh-webchat-view'
25
- var ACTIVE_ATTR = 'data-dsh-webchat-active'
26
- var OTHER_ACTIVE_ATTRS = ['data-dsh-taskboard-active', 'data-dsh-ssh-active']
27
- var ACTIVATE_EVENT = 'dsh-panel-activate'
28
- var PANEL_NAME = 'webchat'
29
23
  var STYLE_ID = 'dsh-webchat-style'
24
+ var TOAST_ATTR = 'data-dsh-webchat-toast'
30
25
 
31
26
  var ZH = typeof navigator === 'object' && typeof navigator.language === 'string'
32
27
  && navigator.language.toLowerCase().indexOf('zh') === 0
@@ -35,66 +30,32 @@ window.__ModuleLoader__.load({
35
30
  var T = ZH
36
31
  ? {
37
32
  label: 'DeepSeek 网页',
38
- tooltip: '在窗口里打开 chat.deepseek.com',
39
- title: 'DeepSeek 网页',
40
- lede: '官方网页版就是完整的客户端:模型选择、深度思考、智能搜索、历史记录、附件上传都在里面。这里只负责把它打开。',
41
- open: '打开 chat.deepseek.com',
42
- reopen: '回到已打开的窗口',
43
- hint: '首次使用请在打开的这个窗口里登录 DeepSeek,之后登录态会保留。',
44
- checking: '检查中…',
45
- closed: '窗口未打开',
46
- opened: '窗口已打开',
33
+ tooltip: '打开 chat.deepseek.com',
47
34
  busy: '正在打开…',
48
- done: '已打开',
35
+ opened: '已打开 DeepSeek 网页',
36
+ handed: '已交给系统默认浏览器打开',
49
37
  failed: '打开失败',
50
38
  }
51
39
  : {
52
40
  label: 'DeepSeek Web',
53
- tooltip: 'Open chat.deepseek.com in a window',
54
- title: 'DeepSeek Web',
55
- lede: 'The official web app is the whole client: model picker, deep think, smart search, history and attachments all live there. This plugin only opens it.',
56
- open: 'Open chat.deepseek.com',
57
- reopen: 'Focus the open window',
58
- hint: 'Sign in once inside the window it opens; the session is kept afterwards.',
59
- checking: 'Checking…',
60
- closed: 'Window is closed',
61
- opened: 'Window is open',
41
+ tooltip: 'Open chat.deepseek.com',
62
42
  busy: 'Opening…',
63
- done: 'Opened',
43
+ opened: 'DeepSeek Web is open',
44
+ handed: 'Handed to the system default browser',
64
45
  failed: 'Could not open',
65
46
  }
66
47
 
67
- /** Panel and entry styling, scoped to this plugin's own attributes. */
48
+ /** Entry and toast styling, scoped to this plugin's own attribute. */
68
49
  var CSS = [
69
- "[data-pane='conversation'],[class*='centerCol']{position:relative}",
70
- '[' + VIEW_ATTR + ']{position:absolute;inset:0;display:none;z-index:60;background:var(--dsw-alias-bg-base)}',
71
- 'html[' + ACTIVE_ATTR + ']:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [' + VIEW_ATTR + ']{display:block}',
72
- "html[" + ACTIVE_ATTR + "]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [data-pane='conversation'] > :not([" + VIEW_ATTR + ']),',
73
- 'html[' + ACTIVE_ATTR + "]:not([data-dsh-taskboard-active]):not([data-dsh-ssh-active]) [class*='centerCol'] > :not([" + VIEW_ATTR + ']){display:none !important}',
74
50
  '[' + ENTRY_ATTR + ']{display:flex;align-items:center;gap:8px;width:100%;height:32px;padding:0 12px;background:transparent;border:none;border-radius:8px;color:var(--dsw-alias-label-secondary);cursor:pointer;font-size:13px;white-space:nowrap}',
75
51
  '[' + ENTRY_ATTR + ']:hover{background:var(--dsw-specific-sidebar-nav-item-hover);color:var(--dsw-alias-label-primary)}',
76
- '[' + ENTRY_ATTR + '][data-active]{background:var(--dsw-specific-sidebar-nav-item-active);color:var(--dsw-alias-label-primary);font-weight:600}',
52
+ '[' + ENTRY_ATTR + '][disabled]{opacity:.55;cursor:progress}',
77
53
  '[' + ENTRY_ATTR + '] [data-part=icon]{display:inline-flex;align-items:center;justify-content:center;flex:none}',
78
54
  '[' + ENTRY_ATTR + '] [data-part=label]{overflow:hidden;text-overflow:ellipsis}',
79
55
  '[data-dsh-frame][data-sidebar-collapsed] [' + ENTRY_ATTR + ']{justify-content:center;padding:0}',
80
56
  '[data-dsh-frame][data-sidebar-collapsed] [' + ENTRY_ATTR + '] [data-part=label]{display:none}',
81
- '.dsh-wc-panel{box-sizing:border-box;display:flex;flex-direction:column;height:100%;min-height:0;padding:18px 20px;gap:14px;background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family);overflow:auto}',
82
- '.dsh-wc-head{display:flex;align-items:center;gap:10px;flex:none}',
83
- '.dsh-wc-title{margin:0;font-size:16px;font-weight:700;letter-spacing:-.01em}',
84
- '.dsh-wc-chip{margin-left:auto;display:inline-flex;align-items:center;gap:7px;padding:4px 10px;border:1px solid var(--dsw-alias-border-l1);border-radius:999px;font-size:11.5px;color:var(--dsw-alias-label-secondary)}',
85
- '.dsh-wc-dot{width:7px;height:7px;border-radius:50%;background:var(--dsw-alias-label-tertiary);flex:none}',
86
- ".dsh-wc-dot[data-state=ok]{background:var(--dsw-alias-state-success,#22c55e)}",
87
- ".dsh-wc-dot[data-state=bad]{background:var(--dsw-alias-state-danger,#ef4444)}",
88
- ".dsh-wc-dot[data-state=busy]{background:var(--dsw-alias-state-info,#3b82f6)}",
89
- '.dsh-wc-card{margin:auto;max-width:520px;display:flex;flex-direction:column;align-items:center;gap:14px;text-align:center;padding:26px 22px;border:1px solid var(--dsw-alias-border-l1);border-radius:14px;background:var(--dsw-alias-bg-layer1,var(--dsw-alias-bg-elevated))}',
90
- '.dsh-wc-lede{margin:0;font-size:13px;line-height:1.65;color:var(--dsw-alias-label-secondary)}',
91
- '.dsh-wc-url{font-family:var(--dsw-font-mono,ui-monospace,Consolas,monospace);font-size:12px;color:var(--dsw-alias-label-tertiary)}',
92
- '.dsh-wc-button{display:inline-flex;align-items:center;gap:6px;padding:9px 18px;font-size:13.5px;font-weight:600;border:1px solid transparent;border-radius:9px;background:var(--dsw-alias-state-business-primary,#4f6bfa);color:#fff;cursor:pointer}',
93
- '.dsh-wc-button:hover:not(:disabled){filter:brightness(1.08)}',
94
- '.dsh-wc-button:disabled{opacity:.5;cursor:not-allowed}',
95
- '.dsh-wc-hint{margin:0;font-size:12px;line-height:1.6;color:var(--dsw-alias-label-tertiary)}',
96
- '.dsh-wc-result{margin:0;min-height:18px;font-size:12px;color:var(--dsw-alias-label-secondary);overflow-wrap:anywhere}',
97
- ".dsh-wc-result[data-state=bad]{color:var(--dsw-alias-state-danger,#ef4444)}",
57
+ '[' + TOAST_ATTR + ']{position:fixed;right:20px;bottom:20px;z-index:200;max-width:420px;padding:10px 14px;border-radius:10px;background:var(--dsw-alias-bg-elevated);border:1px solid var(--dsw-alias-border-l2);box-shadow:0 8px 24px rgba(0,0,0,.18);font-size:13px;color:var(--dsw-alias-label-primary);overflow-wrap:anywhere}',
58
+ '[' + TOAST_ATTR + '][data-state=bad]{border-color:var(--dsw-alias-state-danger,#ef4444);color:var(--dsw-alias-state-danger,#ef4444)}',
98
59
  ].join('\n')
99
60
 
100
61
  /** Inject the stylesheet once. */
@@ -106,28 +67,27 @@ window.__ModuleLoader__.load({
106
67
  document.head.appendChild(tag)
107
68
  }
108
69
 
109
- /** Tiny observable for the panel's open state. */
110
- function createController() {
111
- var open = false
112
- var listeners = new Set()
113
- var notify = function () {
114
- listeners.forEach(function (listener) {
115
- try {
116
- listener()
117
- } catch (error) {
118
- console.warn('[dsh-webchat] listener failed:', error)
119
- }
120
- })
121
- }
122
- return {
123
- isOpen: function () { return open },
124
- subscribe: function (listener) {
125
- listeners.add(listener)
126
- return function () { listeners.delete(listener) }
127
- },
128
- toggle: function () { open = !open; notify() },
129
- close: function () { if (!open) return; open = false; notify() },
130
- }
70
+ /** Show one transient message next to the panel corner. */
71
+ function toast(text, state) {
72
+ var previous = document.querySelector('[' + TOAST_ATTR + ']')
73
+ if (previous !== null) previous.remove()
74
+ var box = document.createElement('div')
75
+ box.setAttribute(TOAST_ATTR, '')
76
+ if (state !== undefined) box.setAttribute('data-state', state)
77
+ box.textContent = text
78
+ document.body.appendChild(box)
79
+ setTimeout(function () {
80
+ if (box.isConnected) box.remove()
81
+ }, state === 'bad' ? 8000 : 3000)
82
+ }
83
+
84
+ /** Call the host route; a non-JSON or non-2xx answer becomes an error field. */
85
+ async function openPage() {
86
+ const response = await fetch(API.open, { method: 'POST', headers: { accept: 'application/json' } })
87
+ let payload = {}
88
+ try { payload = await response.json() } catch (error) { payload = {} }
89
+ if (!response.ok && payload.error === undefined) payload.error = 'HTTP ' + response.status
90
+ return payload
131
91
  }
132
92
 
133
93
  /** The sidebar shell root, or undefined while it is not mounted. */
@@ -150,7 +110,7 @@ window.__ModuleLoader__.load({
150
110
  }
151
111
 
152
112
  /** Build the entry row. */
153
- function createEntry(controller) {
113
+ function createEntry() {
154
114
  var entry = document.createElement('button')
155
115
  entry.type = 'button'
156
116
  entry.setAttribute(ENTRY_ATTR, '')
@@ -166,7 +126,26 @@ window.__ModuleLoader__.load({
166
126
  label.textContent = T.label
167
127
  entry.appendChild(icon)
168
128
  entry.appendChild(label)
169
- entry.addEventListener('click', function () { controller.toggle() })
129
+
130
+ entry.addEventListener('click', async function () {
131
+ if (entry.disabled) return
132
+ entry.disabled = true
133
+ var labelNode = entry.querySelector('[data-part=label]')
134
+ if (labelNode !== null) labelNode.textContent = T.busy
135
+ try {
136
+ var outcome = await openPage()
137
+ if (outcome.ok === true) {
138
+ toast(String(outcome.via) === 'system-browser' ? T.handed : T.opened)
139
+ } else {
140
+ toast(T.failed + ':' + String(outcome.error || outcome.via || ''), 'bad')
141
+ }
142
+ } catch (error) {
143
+ toast(T.failed + ':' + (error instanceof Error ? error.message : String(error)), 'bad')
144
+ }
145
+ if (labelNode !== null) labelNode.textContent = T.label
146
+ entry.disabled = false
147
+ })
148
+
170
149
  return entry
171
150
  }
172
151
 
@@ -186,9 +165,9 @@ window.__ModuleLoader__.load({
186
165
  }
187
166
 
188
167
  /** Mount the sidebar entry with its self-healing observers. */
189
- function mountSidebarEntry(controller) {
168
+ function mountSidebarEntry() {
190
169
  if (document.querySelector('[' + ENTRY_ATTR + ']') !== null) return function () {}
191
- var entry = createEntry(controller)
170
+ var entry = createEntry()
192
171
  var root
193
172
  var placed = false
194
173
 
@@ -221,210 +200,22 @@ window.__ModuleLoader__.load({
221
200
  if (!root.contains(entry)) placed = placeEntry(root, entry)
222
201
  })
223
202
 
224
- var syncActive = function () {
225
- if (controller.isOpen()) entry.dataset.active = 'true'
226
- else delete entry.dataset.active
227
- }
228
- var unsubscribe = controller.subscribe(syncActive)
229
- syncActive()
230
203
  tryPlace()
231
204
 
232
205
  return function () {
233
206
  waitObserver.disconnect()
234
207
  rootObserver.disconnect()
235
- unsubscribe()
236
208
  entry.remove()
237
209
  }
238
210
  }
239
211
 
240
- /** The center column that hosts takeover panels. */
241
- function conversationColumn() {
242
- return document.querySelector('[data-pane="conversation"], [class*="centerCol"]') || undefined
243
- }
244
-
245
- /** Call one host route. */
246
- async function call(path, method) {
247
- const response = await fetch(path, { method: method || 'GET', headers: { accept: 'application/json' } })
248
- let payload = {}
249
- try { payload = await response.json() } catch (error) { payload = {} }
250
- if (!response.ok && payload.error === undefined) payload.error = 'HTTP ' + response.status
251
- return payload
252
- }
253
-
254
- /** Build the panel body once; returns the parts the caller updates. */
255
- function buildPanel(container) {
256
- var panel = document.createElement('div')
257
- panel.className = 'dsh-wc-panel'
258
-
259
- var head = document.createElement('div')
260
- head.className = 'dsh-wc-head'
261
- var title = document.createElement('h2')
262
- title.className = 'dsh-wc-title'
263
- title.textContent = T.title
264
- var chip = document.createElement('span')
265
- chip.className = 'dsh-wc-chip'
266
- var dot = document.createElement('span')
267
- dot.className = 'dsh-wc-dot'
268
- var chipText = document.createElement('span')
269
- chipText.textContent = T.checking
270
- chip.appendChild(dot)
271
- chip.appendChild(chipText)
272
- head.appendChild(title)
273
- head.appendChild(chip)
274
-
275
- var card = document.createElement('div')
276
- card.className = 'dsh-wc-card'
277
- var lede = document.createElement('p')
278
- lede.className = 'dsh-wc-lede'
279
- lede.textContent = T.lede
280
- var url = document.createElement('p')
281
- url.className = 'dsh-wc-url'
282
- url.textContent = PAGE_URL
283
- var button = document.createElement('button')
284
- button.type = 'button'
285
- button.className = 'dsh-wc-button'
286
- button.textContent = T.open
287
- var hint = document.createElement('p')
288
- hint.className = 'dsh-wc-hint'
289
- hint.textContent = T.hint
290
- var result = document.createElement('p')
291
- result.className = 'dsh-wc-result'
292
- card.appendChild(lede)
293
- card.appendChild(url)
294
- card.appendChild(button)
295
- card.appendChild(hint)
296
- card.appendChild(result)
297
-
298
- panel.appendChild(head)
299
- panel.appendChild(card)
300
- container.appendChild(panel)
301
-
302
- var mark = function (state, text) {
303
- dot.setAttribute('data-state', state)
304
- chipText.textContent = text
305
- }
306
- var idle = function () {
307
- button.disabled = false
308
- button.textContent = T.open
309
- }
310
-
311
- var refresh = async function () {
312
- try {
313
- var state = await call(API.state)
314
- mark(state.appWindowOpen ? 'ok' : '', state.appWindowOpen ? T.opened : T.closed)
315
- button.textContent = state.appWindowOpen ? T.reopen : T.open
316
- } catch (error) {
317
- mark('bad', T.closed)
318
- }
319
- }
320
-
321
- button.addEventListener('click', async function () {
322
- button.disabled = true
323
- button.textContent = T.busy
324
- mark('busy', T.busy)
325
- result.removeAttribute('data-state')
326
- result.textContent = ''
327
- try {
328
- var outcome = await call(API.open, 'POST')
329
- if (outcome.ok) {
330
- var via = String(outcome.via || '')
331
- mark('ok', T.done)
332
- result.textContent = via === 'system-browser'
333
- ? (ZH ? '已交给系统默认浏览器打开。' : 'Handed to the system default browser.')
334
- : (ZH ? '已打开应用窗口:' + via : 'Opened an app window: ' + via)
335
- } else {
336
- mark('bad', T.failed)
337
- result.setAttribute('data-state', 'bad')
338
- result.textContent = String(outcome.error || outcome.via || T.failed)
339
- }
340
- } catch (error) {
341
- mark('bad', T.failed)
342
- result.setAttribute('data-state', 'bad')
343
- result.textContent = error instanceof Error ? error.message : String(error)
344
- }
345
- idle()
346
- await refresh()
347
- })
348
-
349
- return { refresh: refresh }
350
- }
351
-
352
- /** Mount the panel view into the center column and bind its visibility. */
353
- function mountPanel(controller) {
354
- var container
355
- var panel
356
-
357
- var ensure = function () {
358
- if (container !== undefined) {
359
- if (container.isConnected) return
360
- container.remove()
361
- container = undefined
362
- panel = undefined
363
- }
364
- var column = conversationColumn()
365
- if (column === undefined) return
366
- container = document.createElement('div')
367
- container.setAttribute(VIEW_ATTR, '')
368
- container.setAttribute('data-dsh-plugin', 'webchat')
369
- column.appendChild(container)
370
- panel = buildPanel(container)
371
- void panel.refresh()
372
- }
373
-
374
- var waitObserver = new MutationObserver(function () { ensure() })
375
-
376
- var applyActive = function () {
377
- if (controller.isOpen()) {
378
- OTHER_ACTIVE_ATTRS.forEach(function (attr) { document.documentElement.removeAttribute(attr) })
379
- document.documentElement.setAttribute(ACTIVE_ATTR, '')
380
- document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))
381
- ensure()
382
- if (panel !== undefined) void panel.refresh()
383
- } else {
384
- document.documentElement.removeAttribute(ACTIVE_ATTR)
385
- }
386
- }
387
- var onOtherActivate = function (event) {
388
- var detail = event.detail
389
- if ((detail === 'taskboard' || detail === 'ssh') && controller.isOpen()) controller.close()
390
- }
391
- var SIDEBAR_ROW_SELECTOR = '[class*="sessionRow"], [class*="projectRow"], [class*="searchResultRow"], [class*="searchResultWorkspace"], [class*="newSession"]'
392
- var onClickSidebarRow = function (event) {
393
- if (!controller.isOpen()) return
394
- var target = event.target
395
- if (target === null || target.closest === undefined) return
396
- if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) controller.close()
397
- }
398
-
399
- document.addEventListener('click', onClickSidebarRow, true)
400
- document.addEventListener(ACTIVATE_EVENT, onOtherActivate)
401
- var unsubscribe = controller.subscribe(applyActive)
402
- applyActive()
403
- ensure()
404
- waitObserver.observe(document.body, { childList: true, subtree: true })
405
-
406
- return function () {
407
- document.removeEventListener('click', onClickSidebarRow, true)
408
- document.removeEventListener(ACTIVATE_EVENT, onOtherActivate)
409
- waitObserver.disconnect()
410
- unsubscribe()
411
- document.documentElement.removeAttribute(ACTIVE_ATTR)
412
- if (container !== undefined) container.remove()
413
- container = undefined
414
- panel = undefined
415
- }
416
- }
417
-
418
- /** Mount the entry and the panel. */
212
+ /** Mount the entry. */
419
213
  function apply(ctx) {
420
214
  ensureStyle()
421
- var controller = createController()
422
- var disposers = [mountSidebarEntry(controller), mountPanel(controller)]
215
+ var dispose = mountSidebarEntry()
423
216
  ctx.effect(function () {
424
- return function () {
425
- disposers.splice(0).forEach(function (dispose) { dispose() })
426
- }
427
- }, 'dsh-webchat: ui mounts')
217
+ return function () { dispose() }
218
+ }, 'dsh-webchat: sidebar entry')
428
219
  }
429
220
 
430
221
  exports.apply = apply
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jaychang1989/dsh-webchat",
3
- "version": "0.4.0",
4
- "description": "Opens the official DeepSeek web app (chat.deepseek.com) in a window from inside DeepSeek Harness: one sidebar entry, one button, no reimplementation — the official page keeps its model picker, deep think, smart search, history and attachments. Fork of xmuwenxiang/dsh-web-chat, reduced to a launcher.",
3
+ "version": "0.4.1",
4
+ "description": "Opens the official DeepSeek web app (chat.deepseek.com) in a window from inside DeepSeek Harness: one sidebar entry that opens the page directly, no reimplementation — the official page keeps its model picker, deep think, smart search, history and attachments. Fork of xmuwenxiang/dsh-web-chat, reduced to a launcher.",
5
5
  "keywords": [
6
6
  "dsh",
7
7
  "deepseek-harness",