@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 +6 -5
- package/README.zh.md +6 -5
- package/lib/client.js +68 -277
- package/package.json +2 -2
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,
|
|
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
|
-
|
|
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.
|
|
46
|
-
|
|
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.
|
|
46
|
-
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* against React re-renders
|
|
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 = {
|
|
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: '
|
|
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
|
-
|
|
35
|
+
opened: '已打开 DeepSeek 网页',
|
|
36
|
+
handed: '已交给系统默认浏览器打开',
|
|
49
37
|
failed: '打开失败',
|
|
50
38
|
}
|
|
51
39
|
: {
|
|
52
40
|
label: 'DeepSeek Web',
|
|
53
|
-
tooltip: 'Open chat.deepseek.com
|
|
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
|
-
|
|
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
|
-
/**
|
|
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 + '][
|
|
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
|
-
'
|
|
82
|
-
'
|
|
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
|
-
/**
|
|
110
|
-
function
|
|
111
|
-
var
|
|
112
|
-
|
|
113
|
-
var
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
168
|
+
function mountSidebarEntry() {
|
|
190
169
|
if (document.querySelector('[' + ENTRY_ATTR + ']') !== null) return function () {}
|
|
191
|
-
var entry = createEntry(
|
|
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
|
-
/**
|
|
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
|
|
422
|
-
var disposers = [mountSidebarEntry(controller), mountPanel(controller)]
|
|
215
|
+
var dispose = mountSidebarEntry()
|
|
423
216
|
ctx.effect(function () {
|
|
424
|
-
return function () {
|
|
425
|
-
|
|
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.
|
|
4
|
-
"description": "Opens the official DeepSeek web app (chat.deepseek.com) in a window from inside DeepSeek Harness: one sidebar entry
|
|
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",
|