dsh-links-panel 0.77.0 → 0.78.1

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
@@ -1032,6 +1032,18 @@ window.__ModuleLoader__.load({
1032
1032
  }
1033
1033
  }
1034
1034
 
1035
+ /**
1036
+ * Some search result titles carry a trailing status glyph (`npm-trusted-publisher - ⚠️`);
1037
+ * it is not part of the page name, so drop that decoration from displayed titles.
1038
+ * @param {string} text - title text.
1039
+ * @returns {string} title without a trailing status glyph.
1040
+ */
1041
+ function stripStatusGlyph(text) {
1042
+ return String(text)
1043
+ .replace(/\s*[-–—::·|]?\s*[\u26A0\u2757\u2755\u26D4\u274C\u274E\u2049\u203C\u{1F6A8}\u{1F6AB}\u{1F534}\u{1F7E0}]\uFE0F?$/gu, '')
1044
+ .trim()
1045
+ }
1046
+
1035
1047
  function titleOf(reference, probes) {
1036
1048
  // Strip bidi/zero-width marks before judging: some pages and labels contain only
1037
1049
  // invisible characters, which rendered as a blank row.
@@ -1039,7 +1051,8 @@ window.__ModuleLoader__.load({
1039
1051
  const usable = (value) => typeof value === 'string' && visible(value) !== ''
1040
1052
  // The conversation's own title (Markdown link text / tool result titles) wins.
1041
1053
  if (reference && usable(reference.title) && reference.titleFromPath === false && isUsableTitle(reference.title)) {
1042
- return visible(reference.title)
1054
+ const cleaned = stripStatusGlyph(visible(reference.title))
1055
+ return cleaned !== '' ? cleaned : reference.url
1043
1056
  }
1044
1057
  // Never render an empty row: fall back to the URL.
1045
1058
  return reference.url
@@ -3254,10 +3267,84 @@ window.__ModuleLoader__.load({
3254
3267
  * whether to bypass the Host cache.
3255
3268
  * @returns {Promise<void>} completion of this pass.
3256
3269
  */
3270
+ // Opening a link in the built-in browser is the moment we know the user cares
3271
+ // about that site: fetch its declared icon once, cache it, and update the row.
3272
+ // Requests are chained so they never run in parallel.
3273
+ const warmChainRef = React.useRef(Promise.resolve())
3274
+ const warmFavicon = React.useCallback(
3275
+ (reference) => {
3276
+ const host = reference.host.replace(/^www\./, '')
3277
+ const domain = rootDomain(reference.host)
3278
+ if (faviconLibrary[host] !== undefined || faviconLibrary[domain] !== undefined) return
3279
+ const requestFavicon = async (iconUrl) => {
3280
+ try {
3281
+ const url = new URL('/dsh-links/favicon', document.baseURI)
3282
+ url.searchParams.set('url', iconUrl)
3283
+ const response = await fetch(url)
3284
+ if (!response || !response.ok) return ''
3285
+ const payload = await response.json()
3286
+ return payload && typeof payload.dataUrl === 'string' && payload.dataUrl.indexOf('data:') === 0 ? payload.dataUrl : ''
3287
+ } catch (error) {
3288
+ return ''
3289
+ }
3290
+ }
3291
+ const run = async () => {
3292
+ try {
3293
+ const base = new URL('/dsh-links/meta', document.baseURI)
3294
+ base.searchParams.set('urls', JSON.stringify([reference.url]))
3295
+ const response = await fetch(base)
3296
+ let iconUrl = ''
3297
+ if (response && response.ok) {
3298
+ const payload = await response.json()
3299
+ const page = payload && payload.pages && payload.pages[reference.url]
3300
+ iconUrl = page && typeof page.icon === 'string' ? page.icon : ''
3301
+ }
3302
+ if (iconUrl !== '') {
3303
+ const dataUrl = await requestFavicon(iconUrl)
3304
+ if (dataUrl !== '') {
3305
+ setFaviconLibrary((current) => (current[host] ? current : { ...current, [host]: dataUrl }))
3306
+ return
3307
+ }
3308
+ }
3309
+ const candidates = [
3310
+ `https://${host}/favicon.ico`,
3311
+ `https://${host}/apple-touch-icon.png`,
3312
+ `https://${host}/favicon.png`,
3313
+ `https://${host}/favicon.svg`,
3314
+ ]
3315
+ if (domain !== host) {
3316
+ candidates.push(
3317
+ `https://${domain}/favicon.ico`,
3318
+ `https://${domain}/apple-touch-icon.png`,
3319
+ `https://${domain}/favicon.png`,
3320
+ `https://${domain}/favicon.svg`,
3321
+ )
3322
+ }
3323
+ for (const candidate of candidates) {
3324
+ const dataUrl = await requestFavicon(candidate)
3325
+ if (dataUrl !== '') {
3326
+ setFaviconLibrary((current) => (current[host] ? current : { ...current, [host]: dataUrl }))
3327
+ return
3328
+ }
3329
+ }
3330
+ } catch (error) {
3331
+ /* the background queue will retry later */
3332
+ }
3333
+ }
3334
+ warmChainRef.current = warmChainRef.current.then(run, run)
3335
+ },
3336
+ [faviconLibrary],
3337
+ )
3338
+
3257
3339
  const openReference = React.useCallback(
3258
3340
  (reference, event) => {
3259
3341
  const external = !!(event && (event.metaKey || event.ctrlKey || event.altKey))
3260
3342
  if (!external && tab && tab.actions && typeof tab.actions.openTab === 'function') {
3343
+ try {
3344
+ warmFavicon(reference)
3345
+ } catch (error) {
3346
+ /* caching is best-effort */
3347
+ }
3261
3348
  try {
3262
3349
  tab.actions.openTab(BROWSER_KIND, { params: { url: reference.url } })
3263
3350
  return
@@ -3271,7 +3358,7 @@ window.__ModuleLoader__.load({
3271
3358
  window.location.href = reference.url
3272
3359
  }
3273
3360
  },
3274
- [tab],
3361
+ [tab, warmFavicon],
3275
3362
  )
3276
3363
 
3277
3364
  const copyList = React.useCallback(() => {
@@ -4028,7 +4115,7 @@ window.__ModuleLoader__.load({
4028
4115
  const inject = ['slots', 'sidebarRightTabs', 'sessions', 'locale']
4029
4116
 
4030
4117
  /** Build stamp shown in the footer, so a stale bundle is visible at a glance. */
4031
- const BUILD = 'v0.77.0'
4118
+ const BUILD = 'v0.78.1'
4032
4119
  /**
4033
4120
  * Translate one key and fill `{name}` placeholders.
4034
4121
  * @param {string} key - dictionary key.
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.77.0'
40
+ export const BUILD = 'v0.78.1'
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.77.0",
3
+ "version": "0.78.1",
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",
@@ -47,5 +47,14 @@
47
47
  ],
48
48
  "publishConfig": {
49
49
  "access": "public"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/brickhu/dsh-plugins.git",
54
+ "directory": "references"
55
+ },
56
+ "homepage": "https://github.com/brickhu/dsh-plugins/tree/main/references#readme",
57
+ "bugs": {
58
+ "url": "https://github.com/brickhu/dsh-plugins/issues"
50
59
  }
51
60
  }