dsh-links-panel 0.80.0 → 0.81.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 +1 -58
- package/lib/index.js +24 -2
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3460,56 +3460,6 @@ window.__ModuleLoader__.load({
|
|
|
3460
3460
|
[],
|
|
3461
3461
|
)
|
|
3462
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
|
-
|
|
3513
3463
|
const copyReference = React.useCallback((reference) => {
|
|
3514
3464
|
copyText(reference.url).then((ok) => {
|
|
3515
3465
|
showNotice(ok ? '已复制链接' : '复制失败:浏览器没有授予剪贴板权限')
|
|
@@ -3980,7 +3930,6 @@ window.__ModuleLoader__.load({
|
|
|
3980
3930
|
}),
|
|
3981
3931
|
menuSeparator('sep-1'),
|
|
3982
3932
|
h(MenuItem, { key: 'rename', label: tr('menu.rename'), onSelect: closeThen(openTitleEditor) }),
|
|
3983
|
-
h(MenuItem, { key: 'refresh-icon', label: tr('menu.refreshIcon'), onSelect: closeThen(refreshIcon) }),
|
|
3984
3933
|
menuSeparator('sep-2'),
|
|
3985
3934
|
h(MenuItem, { key: 'ignore-link', label: tr('menu.hideLink'), onSelect: closeThen(hideLinkOnly) }),
|
|
3986
3935
|
h(MenuItem, {
|
|
@@ -4204,7 +4153,7 @@ window.__ModuleLoader__.load({
|
|
|
4204
4153
|
const inject = ['slots', 'sidebarRightTabs', 'sidebarRight', 'sessions', 'locale']
|
|
4205
4154
|
|
|
4206
4155
|
/** Build stamp shown in the footer, so a stale bundle is visible at a glance. */
|
|
4207
|
-
const BUILD = 'v0.
|
|
4156
|
+
const BUILD = 'v0.81.0'
|
|
4208
4157
|
/**
|
|
4209
4158
|
* Translate one key and fill `{name}` placeholders.
|
|
4210
4159
|
* @param {string} key - dictionary key.
|
|
@@ -4271,9 +4220,6 @@ window.__ModuleLoader__.load({
|
|
|
4271
4220
|
'notice.unpinned': '已取消置顶',
|
|
4272
4221
|
'notice.loadOlderFailed': '加载更早的消息失败',
|
|
4273
4222
|
'notice.jumpMissing': '第 {turn} 轮还没加载出来,先向上滚动加载更早的历史',
|
|
4274
|
-
'notice.iconUpdated': '图标已更新',
|
|
4275
|
-
'notice.iconMissing': '没有取到这个网站的图标',
|
|
4276
|
-
'menu.refreshIcon': '刷新图标',
|
|
4277
4223
|
'button.close': '关闭',
|
|
4278
4224
|
'row.clickToView': '点击查看',
|
|
4279
4225
|
'row.turnLabel': '第 {turn} 轮对话',
|
|
@@ -4333,9 +4279,6 @@ window.__ModuleLoader__.load({
|
|
|
4333
4279
|
'notice.unpinned': 'Unpinned',
|
|
4334
4280
|
'notice.loadOlderFailed': 'Failed to load older messages',
|
|
4335
4281
|
'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',
|
|
4339
4282
|
'button.close': 'Close',
|
|
4340
4283
|
'row.clickToView': 'Click to view',
|
|
4341
4284
|
'row.turnLabel': 'Turn {turn}',
|
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.81.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.
|
|
@@ -246,6 +246,24 @@ async function fetchFaviconDataUrl(url) {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
/**
|
|
250
|
+
* Some sites answer 403/404 to non-browser clients (npmjs.com behind Cloudflare
|
|
251
|
+
* answers 403 for every icon path). Fall back to a public favicon service keyed by
|
|
252
|
+
* hostname, which keeps the built-in browser and the panel visually consistent.
|
|
253
|
+
* @param {string} target - the icon URL that failed.
|
|
254
|
+
* @returns {Promise<{dataUrl: string} | null>} data URL or null.
|
|
255
|
+
*/
|
|
256
|
+
async function fetchServiceFavicon(target) {
|
|
257
|
+
let host = ''
|
|
258
|
+
try {
|
|
259
|
+
host = new URL(target).hostname.toLowerCase().replace(/^www\./, '')
|
|
260
|
+
} catch (error) {
|
|
261
|
+
return null
|
|
262
|
+
}
|
|
263
|
+
if (host === '' || host === 'localhost' || /^\d+\.\d+\.\d+\.\d+$/.test(host)) return null
|
|
264
|
+
return fetchFaviconDataUrl(`https://icons.duckduckgo.com/ip3/${host}.ico`)
|
|
265
|
+
}
|
|
266
|
+
|
|
249
267
|
/**
|
|
250
268
|
* Answer `GET /dsh-links/favicon?url=…` with a data URL.
|
|
251
269
|
* @param {object} req - request.
|
|
@@ -266,7 +284,11 @@ async function handleFaviconRequest(req, res) {
|
|
|
266
284
|
res.end()
|
|
267
285
|
return
|
|
268
286
|
}
|
|
269
|
-
const icon = await withOutboundLock(() =>
|
|
287
|
+
const icon = await withOutboundLock(async () => {
|
|
288
|
+
const direct = await fetchFaviconDataUrl(target)
|
|
289
|
+
if (direct !== null) return direct
|
|
290
|
+
return fetchServiceFavicon(target)
|
|
291
|
+
})
|
|
270
292
|
if (!icon) {
|
|
271
293
|
res.writeHead(404)
|
|
272
294
|
res.end()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-links-panel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.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",
|