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 +90 -3
- package/lib/index.js +1 -1
- package/package.json +10 -1
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
}
|