dsh-links-panel 0.79.0 → 0.80.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/lib/client.js +85 -13
- package/lib/index.js +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3172,7 +3172,8 @@ window.__ModuleLoader__.load({
|
|
|
3172
3172
|
domain: rootDomain(reference.host),
|
|
3173
3173
|
}))
|
|
3174
3174
|
.find((item) => {
|
|
3175
|
-
|
|
3175
|
+
const cached = [faviconLibrary[item.host], faviconLibrary[item.domain]]
|
|
3176
|
+
if (cached.some((value) => typeof value === 'string' && value.indexOf('data:') === 0)) return false
|
|
3176
3177
|
if (faviconQueueRef.current.has(item.host)) return false
|
|
3177
3178
|
const failedAt = faviconFailedRef.current.get(item.host)
|
|
3178
3179
|
return failedAt === undefined || Date.now() - failedAt >= 10 * 60 * 1000
|
|
@@ -3321,7 +3322,8 @@ window.__ModuleLoader__.load({
|
|
|
3321
3322
|
(reference) => {
|
|
3322
3323
|
const host = reference.host.replace(/^www\./, '')
|
|
3323
3324
|
const domain = rootDomain(reference.host)
|
|
3324
|
-
|
|
3325
|
+
const cached = [faviconLibrary[host], faviconLibrary[domain]]
|
|
3326
|
+
if (cached.some((value) => typeof value === 'string' && value.indexOf('data:') === 0)) return
|
|
3325
3327
|
const requestFavicon = async (iconUrl) => {
|
|
3326
3328
|
try {
|
|
3327
3329
|
const url = new URL('/dsh-links/favicon', document.baseURI)
|
|
@@ -3365,17 +3367,27 @@ window.__ModuleLoader__.load({
|
|
|
3365
3367
|
const openReference = React.useCallback(
|
|
3366
3368
|
(reference, event) => {
|
|
3367
3369
|
const external = !!(event && (event.metaKey || event.ctrlKey || event.altKey))
|
|
3368
|
-
if (!external
|
|
3370
|
+
if (!external) {
|
|
3369
3371
|
try {
|
|
3370
3372
|
warmFavicon(reference)
|
|
3371
3373
|
} catch (error) {
|
|
3372
3374
|
/* caching is best-effort */
|
|
3373
3375
|
}
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3376
|
+
if (tab && tab.actions && typeof tab.actions.openTab === 'function') {
|
|
3377
|
+
try {
|
|
3378
|
+
tab.actions.openTab(BROWSER_KIND, { params: { url: reference.url } })
|
|
3379
|
+
return
|
|
3380
|
+
} catch (error) {
|
|
3381
|
+
/* fall through to the sidebar service */
|
|
3382
|
+
}
|
|
3383
|
+
}
|
|
3384
|
+
if (ctx && ctx.sidebarRight && typeof ctx.sidebarRight.openTab === 'function') {
|
|
3385
|
+
try {
|
|
3386
|
+
ctx.sidebarRight.openTab(BROWSER_KIND, { params: { url: reference.url } })
|
|
3387
|
+
return
|
|
3388
|
+
} catch (error) {
|
|
3389
|
+
/* fall through to the system browser */
|
|
3390
|
+
}
|
|
3379
3391
|
}
|
|
3380
3392
|
}
|
|
3381
3393
|
try {
|
|
@@ -3384,7 +3396,7 @@ window.__ModuleLoader__.load({
|
|
|
3384
3396
|
window.location.href = reference.url
|
|
3385
3397
|
}
|
|
3386
3398
|
},
|
|
3387
|
-
[tab, warmFavicon],
|
|
3399
|
+
[tab, warmFavicon, ctx],
|
|
3388
3400
|
)
|
|
3389
3401
|
|
|
3390
3402
|
const copyList = React.useCallback(() => {
|
|
@@ -3448,6 +3460,56 @@ window.__ModuleLoader__.load({
|
|
|
3448
3460
|
[],
|
|
3449
3461
|
)
|
|
3450
3462
|
|
|
3463
|
+
// Manual icon refresh: same fetch path the automatic queue uses, but with a
|
|
3464
|
+
// visible spinner state, so "is the chain working?" is answerable in one click.
|
|
3465
|
+
const refreshIcon = React.useCallback(
|
|
3466
|
+
async (reference) => {
|
|
3467
|
+
const host = reference.host.replace(/^www\./, '')
|
|
3468
|
+
const domain = rootDomain(reference.host)
|
|
3469
|
+
setRefreshingUrls((current) => new Set(current).add(reference.url))
|
|
3470
|
+
try {
|
|
3471
|
+
const requestIcon = async (iconUrl) => {
|
|
3472
|
+
try {
|
|
3473
|
+
const url = new URL('/dsh-links/favicon', document.baseURI)
|
|
3474
|
+
url.searchParams.set('url', iconUrl)
|
|
3475
|
+
const response = await fetch(url)
|
|
3476
|
+
if (!response || !response.ok) return ''
|
|
3477
|
+
const payload = await response.json()
|
|
3478
|
+
return payload && typeof payload.dataUrl === 'string' && payload.dataUrl.indexOf('data:') === 0 ? payload.dataUrl : ''
|
|
3479
|
+
} catch (error) {
|
|
3480
|
+
return ''
|
|
3481
|
+
}
|
|
3482
|
+
}
|
|
3483
|
+
const candidates = [
|
|
3484
|
+
`https://${host}/favicon.ico`,
|
|
3485
|
+
`https://${host}/apple-touch-icon.png`,
|
|
3486
|
+
]
|
|
3487
|
+
if (domain !== host) {
|
|
3488
|
+
candidates.push(`https://${domain}/favicon.ico`, `https://${domain}/apple-touch-icon.png`)
|
|
3489
|
+
}
|
|
3490
|
+
let dataUrl = ''
|
|
3491
|
+
for (const candidate of candidates) {
|
|
3492
|
+
dataUrl = await requestIcon(candidate)
|
|
3493
|
+
if (dataUrl !== '') break
|
|
3494
|
+
}
|
|
3495
|
+
if (dataUrl !== '') {
|
|
3496
|
+
setFaviconLibrary((current) => ({ ...current, [host]: dataUrl }))
|
|
3497
|
+
showNotice(tr('notice.iconUpdated'))
|
|
3498
|
+
} else {
|
|
3499
|
+
showNotice(tr('notice.iconMissing'))
|
|
3500
|
+
}
|
|
3501
|
+
} finally {
|
|
3502
|
+
setRefreshingUrls((current) => {
|
|
3503
|
+
if (!current.has(reference.url)) return current
|
|
3504
|
+
const next = new Set(current)
|
|
3505
|
+
next.delete(reference.url)
|
|
3506
|
+
return next
|
|
3507
|
+
})
|
|
3508
|
+
}
|
|
3509
|
+
},
|
|
3510
|
+
[showNotice],
|
|
3511
|
+
)
|
|
3512
|
+
|
|
3451
3513
|
const copyReference = React.useCallback((reference) => {
|
|
3452
3514
|
copyText(reference.url).then((ok) => {
|
|
3453
3515
|
showNotice(ok ? '已复制链接' : '复制失败:浏览器没有授予剪贴板权限')
|
|
@@ -3918,6 +3980,7 @@ window.__ModuleLoader__.load({
|
|
|
3918
3980
|
}),
|
|
3919
3981
|
menuSeparator('sep-1'),
|
|
3920
3982
|
h(MenuItem, { key: 'rename', label: tr('menu.rename'), onSelect: closeThen(openTitleEditor) }),
|
|
3983
|
+
h(MenuItem, { key: 'refresh-icon', label: tr('menu.refreshIcon'), onSelect: closeThen(refreshIcon) }),
|
|
3921
3984
|
menuSeparator('sep-2'),
|
|
3922
3985
|
h(MenuItem, { key: 'ignore-link', label: tr('menu.hideLink'), onSelect: closeThen(hideLinkOnly) }),
|
|
3923
3986
|
h(MenuItem, {
|
|
@@ -4138,10 +4201,10 @@ window.__ModuleLoader__.load({
|
|
|
4138
4201
|
// ------------------------------------------------------------------
|
|
4139
4202
|
|
|
4140
4203
|
/** Required browser services: the tab registry and the keyed seat. */
|
|
4141
|
-
const inject = ['slots', 'sidebarRightTabs', 'sessions', 'locale']
|
|
4204
|
+
const inject = ['slots', 'sidebarRightTabs', 'sidebarRight', 'sessions', 'locale']
|
|
4142
4205
|
|
|
4143
4206
|
/** Build stamp shown in the footer, so a stale bundle is visible at a glance. */
|
|
4144
|
-
const BUILD = 'v0.
|
|
4207
|
+
const BUILD = 'v0.80.0'
|
|
4145
4208
|
/**
|
|
4146
4209
|
* Translate one key and fill `{name}` placeholders.
|
|
4147
4210
|
* @param {string} key - dictionary key.
|
|
@@ -4208,6 +4271,9 @@ window.__ModuleLoader__.load({
|
|
|
4208
4271
|
'notice.unpinned': '已取消置顶',
|
|
4209
4272
|
'notice.loadOlderFailed': '加载更早的消息失败',
|
|
4210
4273
|
'notice.jumpMissing': '第 {turn} 轮还没加载出来,先向上滚动加载更早的历史',
|
|
4274
|
+
'notice.iconUpdated': '图标已更新',
|
|
4275
|
+
'notice.iconMissing': '没有取到这个网站的图标',
|
|
4276
|
+
'menu.refreshIcon': '刷新图标',
|
|
4211
4277
|
'button.close': '关闭',
|
|
4212
4278
|
'row.clickToView': '点击查看',
|
|
4213
4279
|
'row.turnLabel': '第 {turn} 轮对话',
|
|
@@ -4267,6 +4333,9 @@ window.__ModuleLoader__.load({
|
|
|
4267
4333
|
'notice.unpinned': 'Unpinned',
|
|
4268
4334
|
'notice.loadOlderFailed': 'Failed to load older messages',
|
|
4269
4335
|
'notice.jumpMissing': 'Turn {turn} is not loaded yet — scroll up to load older history',
|
|
4336
|
+
'notice.iconUpdated': 'Icon updated',
|
|
4337
|
+
'notice.iconMissing': 'No icon found for this site',
|
|
4338
|
+
'menu.refreshIcon': 'Refresh icon',
|
|
4270
4339
|
'button.close': 'Close',
|
|
4271
4340
|
'row.clickToView': 'Click to view',
|
|
4272
4341
|
'row.turnLabel': 'Turn {turn}',
|
|
@@ -4453,7 +4522,7 @@ window.__ModuleLoader__.load({
|
|
|
4453
4522
|
}
|
|
4454
4523
|
}
|
|
4455
4524
|
visit(snapshot, 0)
|
|
4456
|
-
return [...ids].slice(0,
|
|
4525
|
+
return [...ids].slice(0, 3)
|
|
4457
4526
|
}
|
|
4458
4527
|
|
|
4459
4528
|
const attach = (sessionId) => {
|
|
@@ -4493,7 +4562,10 @@ window.__ModuleLoader__.load({
|
|
|
4493
4562
|
}
|
|
4494
4563
|
const schedule = () => {
|
|
4495
4564
|
if (tracker.timer !== null) return
|
|
4496
|
-
tracker
|
|
4565
|
+
// Long debounce: the tracker only needs to be current when the panel
|
|
4566
|
+
// opens, not on every streamed token, and synchronous merges+storage
|
|
4567
|
+
// writes on the UI thread are what make streaming feel janky.
|
|
4568
|
+
tracker.timer = setTimeout(flush, 1500)
|
|
4497
4569
|
}
|
|
4498
4570
|
schedule()
|
|
4499
4571
|
tracker.dispose = source.subscribe(schedule)
|
package/lib/index.js
CHANGED
|
@@ -37,7 +37,7 @@ function withOutboundLock(task) {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** Host build stamp, echoed to the panel so a stale bundle is detectable. */
|
|
40
|
-
export const BUILD = 'v0.
|
|
40
|
+
export const BUILD = 'v0.80.0'
|
|
41
41
|
/**
|
|
42
42
|
* The desktop browser reaches the network through the system proxy; the Host's own
|
|
43
43
|
* Node requests bypass it. Detect that proxy so probes can use the same route.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-links-panel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.0",
|
|
4
4
|
"description": "Reference links panel for DeepSeek Harness: collects every URL of a conversation into the right sidebar with pins, turn counts, metadata and favicon caching. DSH 右栏「应用链接」面板。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|