dsh-x-core 0.1.21 → 0.1.23
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/assets/dsh-preview.png +0 -0
- package/assets/dsh.ico +0 -0
- package/assets/icon.ico +0 -0
- package/assets/tray.ico +0 -0
- package/assets/tray.png +0 -0
- package/core-npm.js +201 -183
- package/desktop-plugins.js +86 -0
- package/desktop.js +200 -0
- package/filemanager-base.js +7 -8
- package/filemanager-linux.js +124 -0
- package/filemanager.js +4 -7
- package/native-notify.js +74 -0
- package/node/node +2 -1
- package/package.json +1 -1
- package/packs/dsh-x-workbench/README.md +61 -0
- package/packs/dsh-x-workbench/dspack.json +4 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/LICENSE-APACHE-2.0.txt +202 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/NOTICE.md +6 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/SKILL.md +697 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/authentication.md +313 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/commands.md +300 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/profiling.md +120 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/proxy-support.md +194 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/session-management.md +201 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/snapshot-refs.md +229 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/references/video-recording.md +173 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/templates/authenticated-session.sh +105 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/templates/capture-workflow.sh +69 -0
- package/packs/dsh-x-workbench/home/skills/agent-browser/templates/form-automation.sh +62 -0
- package/packs/dsh-x-workbench/home/skills/electron/LICENSE-APACHE-2.0.txt +202 -0
- package/packs/dsh-x-workbench/home/skills/electron/NOTICE.md +6 -0
- package/packs/dsh-x-workbench/home/skills/electron/SKILL.md +243 -0
- package/packs/dsh-x-workbench/manifest.json +35 -0
- package/perf/patch-hooks.mjs +14 -1
- package/perf/plugin-profile.mjs +204 -0
- package/perf/register.mjs +4 -0
- package/platform.js +2 -1
- package/public/booting.html +475 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/public/icon.png +0 -0
- package/public/index.html +9277 -8173
- package/public/internal-tabs.js +45 -41
- package/public/launcher.css +285 -98
- package/public/mascot.js +1 -1
- package/public/theme.css +33 -6
- package/public/water.js +1 -1
- package/registry.js +38 -3
- package/server.js +1060 -119
- package/settings.js +78 -1
package/public/internal-tabs.js
CHANGED
|
@@ -9,16 +9,10 @@
|
|
|
9
9
|
const send = (message) => window.ipc?.postMessage(typeof message === 'string' ? message : JSON.stringify(message))
|
|
10
10
|
const closeIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="m7 7 10 10M7 17 17 7"/></svg>'
|
|
11
11
|
const items = new Map()
|
|
12
|
-
const indicator = document.createElement('div')
|
|
13
|
-
indicator.className = 'internal-tab-indicator'
|
|
14
|
-
indicator.setAttribute('aria-hidden', 'true')
|
|
15
|
-
list.prepend(indicator)
|
|
16
12
|
let lastHeight = 0
|
|
17
13
|
let selected = ''
|
|
18
14
|
let presentation = 0
|
|
19
15
|
let submittedPresentation = 0
|
|
20
|
-
let indicatorFrame = 0
|
|
21
|
-
let instantFrame = 0
|
|
22
16
|
let nextRequest = Date.now()
|
|
23
17
|
let pendingSelection = null
|
|
24
18
|
let presentationFrame = 0
|
|
@@ -29,7 +23,7 @@
|
|
|
29
23
|
let suppressedClick = null
|
|
30
24
|
const moves = new Map()
|
|
31
25
|
const reducedMotion = () => root.classList.contains('reduce-motion') || matchMedia('(prefers-reduced-motion: reduce)').matches
|
|
32
|
-
const tabNodes = () => [...list.children]
|
|
26
|
+
const tabNodes = () => [...list.children]
|
|
33
27
|
const order = () => tabNodes().map((item) => item.dataset.id)
|
|
34
28
|
const glide = (item, from) => {
|
|
35
29
|
moves.get(item)?.cancel()
|
|
@@ -39,21 +33,28 @@
|
|
|
39
33
|
moves.set(item, animation)
|
|
40
34
|
animation.finished.catch(() => {}).then(() => { if (moves.get(item) === animation) moves.delete(item) })
|
|
41
35
|
}
|
|
42
|
-
|
|
36
|
+
/** 新标签淡入并轻微放大到原尺寸。用独立的 scale 属性,不和 glide 的 translateX 抢 transform。 */
|
|
37
|
+
const enter = (item) => {
|
|
38
|
+
if (reducedMotion()) return
|
|
39
|
+
item.animate([{ opacity: 0, scale: '0.94' }, { opacity: 1, scale: '1' }], { duration: 180, easing: 'cubic-bezier(.2, .8, .2, 1)' })
|
|
40
|
+
}
|
|
41
|
+
const arrange = (ids, animate = false, snapshot = null) => {
|
|
43
42
|
const current = order()
|
|
44
43
|
const desired = [...ids.filter((id) => items.has(id)), ...current.filter((id) => !ids.includes(id))]
|
|
45
44
|
// 原生回执和标题轮询经常不改变顺序,不能因此提前结束正在播放的让位动画。
|
|
46
|
-
if (desired.join('\0') === current.join('\0'))
|
|
45
|
+
if (desired.join('\0') === current.join('\0')) return
|
|
47
46
|
const focus = list.contains(document.activeElement) ? document.activeElement : null
|
|
48
|
-
|
|
47
|
+
// snapshot 是调用方在删掉节点之前量好的位置(关标签时用),拿不到才现量。
|
|
48
|
+
const before = snapshot || new Map(tabNodes().map((item) => [item, item.getBoundingClientRect().left]))
|
|
49
49
|
for (const animation of moves.values()) animation.cancel()
|
|
50
50
|
moves.clear()
|
|
51
|
-
let previous =
|
|
51
|
+
let previous = null
|
|
52
52
|
// 只移动节点,保留点击焦点;拖动期间新出现的标签继续排在末尾。
|
|
53
53
|
for (const id of desired) {
|
|
54
54
|
const item = items.get(id)
|
|
55
55
|
if (!item) continue
|
|
56
|
-
if (previous
|
|
56
|
+
if (!previous) list.append(item)
|
|
57
|
+
else if (previous.nextSibling !== item) previous.after(item)
|
|
57
58
|
previous = item
|
|
58
59
|
}
|
|
59
60
|
if (focus?.isConnected && document.activeElement !== focus) focus.focus({ preventScroll: true })
|
|
@@ -62,34 +63,13 @@
|
|
|
62
63
|
if (animate) for (const item of tabNodes()) {
|
|
63
64
|
if (item !== drag?.item && before.has(item)) glide(item, before.get(item) - item.getBoundingClientRect().left)
|
|
64
65
|
}
|
|
65
|
-
positionIndicator()
|
|
66
66
|
}
|
|
67
67
|
const commitOrder = () => {
|
|
68
68
|
pendingOrder = { ids: order(), request: ++nextRequest }
|
|
69
69
|
send({ action: 'reorder', ...pendingOrder })
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
const positionIndicator = ({ instant = false } = {}) => {
|
|
73
|
-
const item = items.get(selected)
|
|
74
|
-
if (!item || area.hidden) { indicator.classList.remove('visible'); return }
|
|
75
|
-
const first = instant || !indicator.classList.contains('visible')
|
|
76
|
-
cancelAnimationFrame(instantFrame)
|
|
77
|
-
if (first) indicator.classList.add('instant')
|
|
78
|
-
else indicator.classList.remove('instant')
|
|
79
|
-
indicator.style.width = `${item.offsetWidth}px`
|
|
80
|
-
indicator.style.transform = `translate3d(${item.offsetLeft + (drag?.item === item ? drag.offset || 0 : 0)}px, ${item.offsetTop}px, 0)`
|
|
81
|
-
indicator.classList.add('visible')
|
|
82
|
-
if (first) {
|
|
83
|
-
void indicator.offsetWidth
|
|
84
|
-
instantFrame = requestAnimationFrame(() => indicator.classList.remove('instant'))
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const scheduleIndicator = () => {
|
|
88
|
-
cancelAnimationFrame(indicatorFrame)
|
|
89
|
-
indicatorFrame = requestAnimationFrame(() => positionIndicator({ instant: true }))
|
|
90
|
-
}
|
|
91
72
|
const selectVisual = (id) => {
|
|
92
|
-
cancelAnimationFrame(indicatorFrame)
|
|
93
73
|
const changed = id !== selected
|
|
94
74
|
selected = id
|
|
95
75
|
for (const [key, item] of items) {
|
|
@@ -97,7 +77,6 @@
|
|
|
97
77
|
item.querySelector('.internal-tab-label').setAttribute('aria-selected', String(key === id))
|
|
98
78
|
}
|
|
99
79
|
if (changed) items.get(id)?.scrollIntoView({ block: 'nearest', inline: 'nearest' })
|
|
100
|
-
positionIndicator()
|
|
101
80
|
}
|
|
102
81
|
const selectTab = (id) => {
|
|
103
82
|
// 点击即开始滑动,不等待跨 WebView 的回传;请求编号防止快速连点被旧状态拉回。
|
|
@@ -129,7 +108,6 @@
|
|
|
129
108
|
if (ids.join('\0') !== order().join('\0')) arrange(ids, true)
|
|
130
109
|
drag.offset = left - item.offsetLeft
|
|
131
110
|
item.style.transform = `translateX(${drag.offset}px)`
|
|
132
|
-
positionIndicator({ instant: true })
|
|
133
111
|
}
|
|
134
112
|
const scrollDrag = () => {
|
|
135
113
|
if (!drag?.started) return
|
|
@@ -154,7 +132,6 @@
|
|
|
154
132
|
glide(ended.item, ended.offset)
|
|
155
133
|
if (order().join('\0') !== ended.original.join('\0')) commitOrder()
|
|
156
134
|
}
|
|
157
|
-
positionIndicator()
|
|
158
135
|
}
|
|
159
136
|
}
|
|
160
137
|
list.addEventListener('pointerdown', (event) => {
|
|
@@ -210,7 +187,15 @@
|
|
|
210
187
|
const active = String(state.active || '')
|
|
211
188
|
const en = state.lang === 'en'
|
|
212
189
|
root.classList.toggle('internal-tabs', enabled)
|
|
190
|
+
// 顶栏形态由「有没有标签」决定:一个标签都没有时还是原来那条高顶栏,浏览器式扁条只在真有标签时出现。
|
|
191
|
+
root.classList.toggle('has-tabs', enabled && (state.tabs || []).length > 0)
|
|
213
192
|
root.classList.toggle('internal-dsh-active', enabled && !!active)
|
|
193
|
+
// 选中标签的底色跟选中那块页面走(外壳采样来的):同色才看不出接缝,采样不到就退回主题色。
|
|
194
|
+
if (state.surface) root.style.setProperty('--tab-surface', state.surface)
|
|
195
|
+
else root.style.removeProperty('--tab-surface')
|
|
196
|
+
// 标签上的字跟着采到的底色走(亮底给深字):外壳算好放在 state.ink 里
|
|
197
|
+
if (state.ink) root.style.setProperty('--tab-ink', state.ink)
|
|
198
|
+
else root.style.removeProperty('--tab-ink')
|
|
214
199
|
area.hidden = !enabled
|
|
215
200
|
// 隐藏的管理内容不能被键盘 Tab 聚焦;顶部设置、Star 与窗口控制仍保留原来的交互。
|
|
216
201
|
const main = document.querySelector('main')
|
|
@@ -225,13 +210,18 @@
|
|
|
225
210
|
if (drag && (!enabled || !ids.has(drag.item.dataset.id))) finishDrag(true)
|
|
226
211
|
if (pendingOrder && state.orderRequest === pendingOrder.request) pendingOrder = null
|
|
227
212
|
if (pendingSelection && (state.request === pendingSelection.request || (pendingSelection.id && !ids.has(pendingSelection.id)))) pendingSelection = null
|
|
228
|
-
|
|
229
|
-
|
|
213
|
+
// 关标签:先量一遍现在的位置,删掉节点后让剩下的从老位置滑过去,别硬跳一格。
|
|
214
|
+
const closing = [...items].filter(([id]) => !ids.has(id))
|
|
215
|
+
const closingBefore = closing.length ? new Map(tabNodes().map((item) => [item, item.getBoundingClientRect().left])) : null
|
|
216
|
+
for (const [id, item] of closing) {
|
|
217
|
+
item.remove()
|
|
218
|
+
items.delete(id)
|
|
230
219
|
}
|
|
231
220
|
const localOrder = drag || pendingOrder ? order() : null
|
|
232
221
|
for (const tab of state.tabs || []) {
|
|
233
222
|
let item = items.get(tab.id)
|
|
234
|
-
|
|
223
|
+
const created = !item
|
|
224
|
+
if (created) {
|
|
235
225
|
item = document.createElement('div')
|
|
236
226
|
item.className = 'internal-tab'
|
|
237
227
|
item.dataset.id = tab.id
|
|
@@ -245,6 +235,8 @@
|
|
|
245
235
|
}
|
|
246
236
|
// 复用节点让选中态连续过渡,也保留键盘焦点;后台标题变化不能重建整排标签。
|
|
247
237
|
if (!item.parentNode) list.append(item)
|
|
238
|
+
// 入场动画要等节点进了文档再起,否则(未挂载的元素不动画)什么都看不到。
|
|
239
|
+
if (created) enter(item)
|
|
248
240
|
item.classList.toggle('stopped', !!tab.stopped)
|
|
249
241
|
const label = item.querySelector('.internal-tab-label')
|
|
250
242
|
label.title = [tab.title, tab.pageTitle].filter(Boolean).join(' — ')
|
|
@@ -254,7 +246,7 @@
|
|
|
254
246
|
close.title = en ? 'Close tab' : '关闭标签'
|
|
255
247
|
close.setAttribute('aria-label', close.title)
|
|
256
248
|
}
|
|
257
|
-
arrange(localOrder || (state.tabs || []).map((tab) => tab.id))
|
|
249
|
+
arrange(localOrder || (state.tabs || []).map((tab) => tab.id), !!closingBefore, closingBefore)
|
|
258
250
|
if (drag?.started) moveDrag()
|
|
259
251
|
selectVisual(pendingSelection?.id ?? active)
|
|
260
252
|
reportHeight()
|
|
@@ -285,7 +277,19 @@
|
|
|
285
277
|
document.getElementById('settingsEntry')?.addEventListener('click', () => {
|
|
286
278
|
if (root.classList.contains('internal-dsh-active')) selectTab('')
|
|
287
279
|
}, true)
|
|
288
|
-
new ResizeObserver(
|
|
280
|
+
new ResizeObserver(reportHeight).observe(header)
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* 标签中心的 x(给外壳采「这张标签正下方」的底色用):视图和管理页同宽同原点,
|
|
284
|
+
* 所以 DOM 坐标就是页面里的坐标。拿不到(刚建、已关)返回 0,外壳那边退回左上角。
|
|
285
|
+
*/
|
|
286
|
+
window.launcherTabCenterX = (id) => {
|
|
287
|
+
const item = items.get(id)
|
|
288
|
+
if (!item) return 0
|
|
289
|
+
const box = item.getBoundingClientRect()
|
|
290
|
+
return Math.round(box.left + box.width / 2)
|
|
291
|
+
}
|
|
292
|
+
|
|
289
293
|
// 原生标签页叠在管理页之上,管理页的弹窗(.ask)会被它盖住、遮罩却挡住了标签栏,弹窗就关不掉了。
|
|
290
294
|
// 有弹窗开着就让外壳先收起标签页,关完再放回;初值为 null,保证页面重载后先把真实状态同步一次。
|
|
291
295
|
let overlay = null
|