dsh-site-bridge 0.5.3 → 0.6.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/extension/manifest.json +1 -1
- package/extension/popup.html +20 -9
- package/extension/popup.js +50 -34
- package/package.json +1 -1
- package/tools/preview-popup.mjs +11 -0
package/extension/manifest.json
CHANGED
package/extension/popup.html
CHANGED
|
@@ -94,6 +94,23 @@
|
|
|
94
94
|
button.link:hover:not(:disabled) { color: #7e98ff; background: none; }
|
|
95
95
|
|
|
96
96
|
.target-text { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
|
|
97
|
+
.tabs-list {
|
|
98
|
+
display: flex; flex-direction: column; gap: 6px; max-height: 150px; overflow-y: auto;
|
|
99
|
+
margin-top: 8px; scrollbar-width: thin; scrollbar-color: #3a4150 transparent;
|
|
100
|
+
}
|
|
101
|
+
.tabs-list::-webkit-scrollbar { width: 6px; }
|
|
102
|
+
.tabs-list::-webkit-scrollbar-thumb { background: #3a4150; border-radius: 3px; }
|
|
103
|
+
.tabs-list::-webkit-scrollbar-track { background: transparent; }
|
|
104
|
+
.tab-item {
|
|
105
|
+
display: flex; align-items: center; justify-content: space-between; gap: 8px;
|
|
106
|
+
padding: 6px 8px; background: #15181e; border: 1px solid var(--line);
|
|
107
|
+
border-radius: 7px; cursor: pointer; min-height: 44px;
|
|
108
|
+
}
|
|
109
|
+
.tab-item:hover { border-color: #3a4150; }
|
|
110
|
+
.tab-item.active { border-color: var(--accent); background: #1a1f2c; }
|
|
111
|
+
.tab-item-text { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
|
|
112
|
+
.tab-item-title { font-size: 12px; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
113
|
+
.tab-item-url { color: var(--muted); font-size: 10.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
97
114
|
.capture-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--line); }
|
|
98
115
|
.capture-row button { padding: 5px 9px; font-size: 11.5px; }
|
|
99
116
|
select {
|
|
@@ -148,12 +165,9 @@
|
|
|
148
165
|
|
|
149
166
|
<div class="target empty" id="target-block">
|
|
150
167
|
<div class="target-text">
|
|
151
|
-
<div class="target-title"
|
|
152
|
-
<div class="target-url"
|
|
168
|
+
<div class="target-title" data-i18n="targetNoneTitle">Вкладка не подключена</div>
|
|
169
|
+
<div class="target-url" data-i18n="targetNoneUrl">Откройте нужную страницу и подключите её</div>
|
|
153
170
|
</div>
|
|
154
|
-
<button class="icon" id="release" hidden data-i18n-title="disconnectTab" title="Отключить вкладку">
|
|
155
|
-
<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 2 L10 10 M10 2 L2 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
|
|
156
|
-
</button>
|
|
157
171
|
</div>
|
|
158
172
|
|
|
159
173
|
<button class="primary" id="claim" data-i18n="connectTab">Подключить эту вкладку</button>
|
|
@@ -161,10 +175,7 @@
|
|
|
161
175
|
|
|
162
176
|
<button class="primary" id="claim-more" hidden data-i18n="attachAnother">Подключить ещё вкладку</button>
|
|
163
177
|
|
|
164
|
-
<div class="
|
|
165
|
-
<span class="muted" data-i18n="tabsLabel">Вкладки</span>
|
|
166
|
-
<select id="tabs-select"></select>
|
|
167
|
-
</div>
|
|
178
|
+
<div class="tabs-list" id="tabs-list" hidden></div>
|
|
168
179
|
|
|
169
180
|
<div class="capture-row">
|
|
170
181
|
<span class="muted" id="captured">0</span>
|
package/extension/popup.js
CHANGED
|
@@ -59,7 +59,6 @@ function render(status) {
|
|
|
59
59
|
const dot = $('dot')
|
|
60
60
|
const text = $('connection-text')
|
|
61
61
|
const claim = $('claim')
|
|
62
|
-
const release = $('release')
|
|
63
62
|
const capture = $('capture')
|
|
64
63
|
const target = $('target-block')
|
|
65
64
|
|
|
@@ -93,35 +92,48 @@ function render(status) {
|
|
|
93
92
|
text.textContent = t('statusNotConfigured')
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
$('target-url').textContent = hasTarget ? String(status.target.url || '') : t('targetNoneUrl')
|
|
99
|
-
|
|
100
|
-
// Several attached tabs: a compact switcher, nothing else changes.
|
|
95
|
+
// One block per attached tab: title, address and its own detach cross. The
|
|
96
|
+
// list scrolls after three blocks so the popup keeps its height.
|
|
101
97
|
const targets = Array.isArray(status.targets) ? status.targets : []
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
98
|
+
const activeId = status.activeTabId ?? status.target?.id ?? null
|
|
99
|
+
target.classList.toggle('empty', targets.length === 0)
|
|
100
|
+
target.hidden = targets.length > 0
|
|
101
|
+
|
|
102
|
+
const list = $('tabs-list')
|
|
103
|
+
list.hidden = targets.length === 0
|
|
104
|
+
list.textContent = ''
|
|
105
|
+
for (const item of targets) {
|
|
106
|
+
const row = document.createElement('div')
|
|
107
|
+
row.className = item.id === activeId ? 'tab-item active' : 'tab-item'
|
|
108
|
+
row.dataset.id = String(item.id)
|
|
109
|
+
row.title = item.url || ''
|
|
110
|
+
|
|
111
|
+
const body = document.createElement('div')
|
|
112
|
+
body.className = 'tab-item-text'
|
|
113
|
+
const title = document.createElement('div')
|
|
114
|
+
title.className = 'tab-item-title'
|
|
115
|
+
title.textContent = item.title || item.url || `#${item.id}`
|
|
116
|
+
const address = document.createElement('div')
|
|
117
|
+
address.className = 'tab-item-url'
|
|
118
|
+
address.textContent = item.url || ''
|
|
119
|
+
body.append(title, address)
|
|
120
|
+
|
|
121
|
+
const remove = document.createElement('button')
|
|
122
|
+
remove.className = 'icon'
|
|
123
|
+
remove.dataset.remove = String(item.id)
|
|
124
|
+
remove.title = t('disconnectTab')
|
|
125
|
+
remove.innerHTML = '<svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M2 2 L10 10 M10 2 L2 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>'
|
|
126
|
+
|
|
127
|
+
row.append(body, remove)
|
|
128
|
+
list.append(row)
|
|
118
129
|
}
|
|
119
130
|
|
|
120
131
|
// Nothing to claim once a tab is bound: the action would be a no-op.
|
|
121
|
-
|
|
122
|
-
claim.
|
|
123
|
-
claim.
|
|
124
|
-
|
|
132
|
+
const selfAttached = selfTabId != null && targets.some((item) => item.id === selfTabId)
|
|
133
|
+
claim.hidden = targets.length > 0
|
|
134
|
+
claim.disabled = !connected || targets.length > 0
|
|
135
|
+
claim.textContent = t('connectTab')
|
|
136
|
+
$('claim-more').hidden = targets.length === 0 || selfAttached
|
|
125
137
|
|
|
126
138
|
const pick = $('pick')
|
|
127
139
|
pick.classList.toggle('active', status.picking === true)
|
|
@@ -273,8 +285,17 @@ $('capture').addEventListener('click', async () => {
|
|
|
273
285
|
await refresh()
|
|
274
286
|
})
|
|
275
287
|
|
|
276
|
-
$('
|
|
277
|
-
|
|
288
|
+
$('tabs-list').addEventListener('click', async (event) => {
|
|
289
|
+
const remove = event.target.closest('[data-remove]')
|
|
290
|
+
if (remove) {
|
|
291
|
+
event.stopPropagation()
|
|
292
|
+
await send({ type: 'release', tabId: Number(remove.dataset.remove) })
|
|
293
|
+
await refresh()
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
const row = event.target.closest('.tab-item')
|
|
297
|
+
if (!row || row.classList.contains('active')) return
|
|
298
|
+
await send({ type: 'use', tabId: Number(row.dataset.id) })
|
|
278
299
|
await refresh()
|
|
279
300
|
})
|
|
280
301
|
|
|
@@ -296,11 +317,6 @@ $('pick').addEventListener('click', async () => {
|
|
|
296
317
|
|
|
297
318
|
$('claim-more').addEventListener('click', attachCurrentTab)
|
|
298
319
|
|
|
299
|
-
|
|
300
|
-
const tabId = Number(event.target.value)
|
|
301
|
-
if (!Number.isFinite(tabId)) return
|
|
302
|
-
await send({ type: 'use', tabId })
|
|
303
|
-
await refresh()
|
|
304
|
-
})
|
|
320
|
+
|
|
305
321
|
|
|
306
322
|
void init()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-site-bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Chrome/Edge MV3 bridge for DeepSeek Harness: capture page markup (DOM, styles, selectors) and BFF traffic (xhr/fetch with headers and bodies) into a workspace snapshot bundle, drive the page with trusted CDP input, and verify injected scripts. Authorization never leaves the browser and is never written to disk.",
|
|
5
5
|
"icon": "./icon.svg",
|
|
6
6
|
"type": "module",
|
package/tools/preview-popup.mjs
CHANGED
|
@@ -21,6 +21,17 @@ const chromePath = process.env.CHROME_PATH ?? 'C:\\Program Files\\Google\\Chrome
|
|
|
21
21
|
const sleep = (ms) => new Promise((done) => setTimeout(done, ms))
|
|
22
22
|
|
|
23
23
|
const statuses = {
|
|
24
|
+
many: {
|
|
25
|
+
ok: true, configured: true, connected: true, port: 47321, recording: true, captured: 7, selfTabId: 9,
|
|
26
|
+
activeTabId: 1,
|
|
27
|
+
target: { id: 1, title: 'Example Store — Products', url: 'https://shop.example/products' },
|
|
28
|
+
targets: [
|
|
29
|
+
{ id: 1, title: 'Example Store — Products', url: 'https://shop.example/products', recording: true, captured: 7 },
|
|
30
|
+
{ id: 2, title: 'Example Store — Cart', url: 'https://shop.example/cart', recording: false, captured: 0 },
|
|
31
|
+
{ id: 3, title: 'Example Store — Checkout', url: 'https://shop.example/checkout', recording: false, captured: 0 },
|
|
32
|
+
{ id: 4, title: 'Example Store — Orders', url: 'https://shop.example/orders', recording: false, captured: 0 }
|
|
33
|
+
]
|
|
34
|
+
},
|
|
24
35
|
'connected-one': {
|
|
25
36
|
ok: true, configured: true, connected: true, port: 47321, recording: false, captured: 0, selfTabId: 1,
|
|
26
37
|
activeTabId: 1,
|