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 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.80.0'
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.80.0'
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(() => fetchFaviconDataUrl(target))
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.80.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",