dsh-session-title-stamp 0.4.1 → 0.4.3
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/README.md +6 -6
- package/README.zh.md +4 -4
- package/client.js +33 -30
- package/lib/index.js +10 -2
- package/market/index.json +5 -5
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ title. Beyond automatic naming:
|
|
|
11
11
|
|
|
12
12
|
- **Auto refresh** — after a completed turn, with a 2-minute cooldown and a
|
|
13
13
|
100-message freeze; forks and user-pinned titles are skipped.
|
|
14
|
-
- **
|
|
15
|
-
**
|
|
14
|
+
- **Generate title** — the conversation-header ↻ button, the session row's
|
|
15
|
+
**Generate title** menu item, or `/retitle`.
|
|
16
16
|
- **Hover stats** — elapsed time, estimated CNY cost, and token count on the
|
|
17
17
|
session hover card.
|
|
18
18
|
- **Category language** — `categoryPreset: zh | en`, or your own `categories`.
|
|
@@ -59,7 +59,7 @@ and mirrors conventions used by other coding agents.
|
|
|
59
59
|
Add this market index source in **Plugins → Discover → Index sources**:
|
|
60
60
|
|
|
61
61
|
```text
|
|
62
|
-
https://cdn.jsdelivr.net/gh/xuedipiaofei/dsh-session-title-stamp@v0.4.
|
|
62
|
+
https://cdn.jsdelivr.net/gh/xuedipiaofei/dsh-session-title-stamp@v0.4.3/market/index.json
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
Then install **Session title stamp** from the list. The plugin installs as a
|
|
@@ -127,15 +127,15 @@ Forks and subagent sessions are skipped, as are titles pinned by a manual
|
|
|
127
127
|
rename. Setting `refreshOnTurnEnd: false` restores the legacy message-count
|
|
128
128
|
mode using `retitleEvery`.
|
|
129
129
|
|
|
130
|
-
### Header
|
|
130
|
+
### Header button and hover stats
|
|
131
131
|
|
|
132
|
-
0.4.
|
|
132
|
+
0.4.3 adds a small browser half:
|
|
133
133
|
|
|
134
134
|
- a **refresh icon** in the conversation header; it runs the same `/retitle`
|
|
135
135
|
command as typing it, and is disabled while the session is running.
|
|
136
136
|
- a **hover-card line** under the native relative time, for example
|
|
137
137
|
`Duration 1h 51m · est. ≈¥4.11 · 131.7M tokens`;
|
|
138
|
-
- a **
|
|
138
|
+
- a **Generate title** item at the top of the session row's `···` menu. It
|
|
139
139
|
runs the same `/retitle` command and adds no dialog.
|
|
140
140
|
|
|
141
141
|
The hover line is derived from the `sessionStats` and `tokenUsage` projections,
|
package/README.zh.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
给 DSH 会话一个稳定的「日期 | 分类 | 名称」标题。除了自动命名,还包括:
|
|
6
6
|
|
|
7
7
|
- **自动重算**:回答完成后更新,2 分钟冷却,超过 100 条消息后冻结;fork、子代理和手动固定的标题跳过。
|
|
8
|
-
-
|
|
8
|
+
- **生成标题**:会话头 ↻ 按钮、会话行 `···` 菜单「自动生成标题」、或 `/retitle`。
|
|
9
9
|
- **悬停统计**:会话行悬停显示耗时、估算 CNY 成本、token 数。
|
|
10
10
|
- **分类语言**:`categoryPreset: zh | en`,也可以用 `categories` 自定义。
|
|
11
11
|
- **失败保护**:重算失败保留当前标题,不被兜底标题覆盖。
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
在 **插件 → 发现(Discover)→ 索引源** 添加:
|
|
44
44
|
|
|
45
45
|
```text
|
|
46
|
-
https://cdn.jsdelivr.net/gh/xuedipiaofei/dsh-session-title-stamp@v0.4.
|
|
46
|
+
https://cdn.jsdelivr.net/gh/xuedipiaofei/dsh-session-title-stamp@v0.4.3/market/index.json
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
然后在列表里安装 **Session title stamp / 会话标题印章**。插件以 profile bundle 的形式安装,之后可在插件页启停。
|
|
@@ -93,12 +93,12 @@ fork 子会话、子代理会话,以及用户手动改名固定的标题都会
|
|
|
93
93
|
|
|
94
94
|
### 会话头按钮与悬停统计
|
|
95
95
|
|
|
96
|
-
0.4.
|
|
96
|
+
0.4.3 增加了一个很小的浏览器半边:
|
|
97
97
|
|
|
98
98
|
- 会话头有一个**刷新图标**,效果等同输入 `/retitle`,会话运行中禁用;
|
|
99
99
|
- 鼠标悬停会话行时,在原生相对时间下面多一行:
|
|
100
100
|
`耗时 1h 51m · 估算 ≈¥4.11 · 131.7M tokens`;
|
|
101
|
-
- 会话行 `···`
|
|
101
|
+
- 会话行 `···` 菜单最上面多一项「自动生成标题」;同样调用 `/retitle`,
|
|
102
102
|
不新开弹窗。
|
|
103
103
|
|
|
104
104
|
数字来自 `sessionStats` / `tokenUsage` 投影,按 DeepSeek 当前 CNY 价格估算,并标注「估算」。只在悬停时向宿主请求,不会为了显示数字去恢复未加载的会话。DSH 原生已经有运行中/待交互/完成未读的状态点与 loader,所以本插件**不再加一层状态颜色**。
|
package/client.js
CHANGED
|
@@ -17,26 +17,25 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
const { useEffect, useState } = React
|
|
18
18
|
const h = React.createElement
|
|
19
19
|
|
|
20
|
+
const NS = 'dsh-session-title-stamp'
|
|
20
21
|
const STATS_PATH = '/api/session-title-stamp/stats'
|
|
21
|
-
const inject = ['slots', 'remote', 'remote.commands']
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
estimate: 'est.',
|
|
39
|
-
}
|
|
22
|
+
const inject = ['slots', 'remote', 'remote.commands', 'locale']
|
|
23
|
+
const DICT = {
|
|
24
|
+
zh: {
|
|
25
|
+
menu: '自动生成标题',
|
|
26
|
+
tip: '自动识别分类,生成「日期 | 分类 | 名称」标题',
|
|
27
|
+
running: '会话运行中,稍后再试',
|
|
28
|
+
duration: '耗时',
|
|
29
|
+
estimate: '估算',
|
|
30
|
+
},
|
|
31
|
+
en: {
|
|
32
|
+
menu: 'Generate title',
|
|
33
|
+
tip: 'Detect the category and generate a DATE | CATEGORY | NAME title',
|
|
34
|
+
running: 'Session is running; try again later',
|
|
35
|
+
duration: 'Duration',
|
|
36
|
+
estimate: 'est.',
|
|
37
|
+
},
|
|
38
|
+
}
|
|
40
39
|
|
|
41
40
|
function formatDuration(ms) {
|
|
42
41
|
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) return undefined
|
|
@@ -81,7 +80,7 @@ window.__ModuleLoader__.load({
|
|
|
81
80
|
)
|
|
82
81
|
}
|
|
83
82
|
|
|
84
|
-
function RefreshAction({ sessionId, useSession, run }) {
|
|
83
|
+
function RefreshAction({ sessionId, useSession, run, t }) {
|
|
85
84
|
const [busy, setBusy] = useState(false)
|
|
86
85
|
const [failed, setFailed] = useState(false)
|
|
87
86
|
const running =
|
|
@@ -109,8 +108,8 @@ window.__ModuleLoader__.load({
|
|
|
109
108
|
type: 'button',
|
|
110
109
|
className: 'dsh-title-refresh',
|
|
111
110
|
disabled,
|
|
112
|
-
title: running ?
|
|
113
|
-
'aria-label':
|
|
111
|
+
title: running ? t('running') : t('tip'),
|
|
112
|
+
'aria-label': t('menu'),
|
|
114
113
|
onClick: click,
|
|
115
114
|
style: {
|
|
116
115
|
display: 'inline-flex',
|
|
@@ -133,10 +132,11 @@ window.__ModuleLoader__.load({
|
|
|
133
132
|
)
|
|
134
133
|
}
|
|
135
134
|
|
|
136
|
-
function HoverStats({ sessionId }) {
|
|
135
|
+
function HoverStats({ sessionId, t }) {
|
|
137
136
|
const [data, setData] = useState(null)
|
|
138
137
|
useEffect(() => {
|
|
139
138
|
let cancelled = false
|
|
139
|
+
setData(null)
|
|
140
140
|
if (typeof sessionId !== 'string' || sessionId === '') return undefined
|
|
141
141
|
const url = `${STATS_PATH}?sessionId=${encodeURIComponent(sessionId)}`
|
|
142
142
|
fetch(url, { headers: { accept: 'application/json' } })
|
|
@@ -155,8 +155,8 @@ window.__ModuleLoader__.load({
|
|
|
155
155
|
const cost = formatCost(data.costCny)
|
|
156
156
|
const tokens = formatTokens(data.tokens)
|
|
157
157
|
const parts = [
|
|
158
|
-
duration === undefined ? undefined : `${
|
|
159
|
-
cost === undefined ? undefined : `${
|
|
158
|
+
duration === undefined ? undefined : `${t('duration')} ${duration}`,
|
|
159
|
+
cost === undefined ? undefined : `${t('estimate')} ${cost}`,
|
|
160
160
|
tokens === undefined ? undefined : `${tokens} tokens`,
|
|
161
161
|
].filter(Boolean)
|
|
162
162
|
if (parts.length === 0) return null
|
|
@@ -175,7 +175,7 @@ window.__ModuleLoader__.load({
|
|
|
175
175
|
)
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
function RegenerateMenuItem({ sessionId, useMenuOpenState, run }) {
|
|
178
|
+
function RegenerateMenuItem({ sessionId, useMenuOpenState, run, t }) {
|
|
179
179
|
const menuState =
|
|
180
180
|
typeof useMenuOpenState === 'function' ? useMenuOpenState() : null
|
|
181
181
|
return h(
|
|
@@ -184,6 +184,7 @@ window.__ModuleLoader__.load({
|
|
|
184
184
|
type: 'button',
|
|
185
185
|
role: 'menuitem',
|
|
186
186
|
className: 'dsh-title-menu-item',
|
|
187
|
+
title: t('tip'),
|
|
187
188
|
onClick: async () => {
|
|
188
189
|
if (Array.isArray(menuState) && typeof menuState[1] === 'function') {
|
|
189
190
|
menuState[1](false)
|
|
@@ -200,7 +201,7 @@ window.__ModuleLoader__.load({
|
|
|
200
201
|
{ className: 'dsh-title-menu-icon', 'aria-hidden': 'true' },
|
|
201
202
|
h(RefreshIcon),
|
|
202
203
|
),
|
|
203
|
-
h('span', null,
|
|
204
|
+
h('span', null, t('menu')),
|
|
204
205
|
)
|
|
205
206
|
}
|
|
206
207
|
|
|
@@ -222,6 +223,8 @@ window.__ModuleLoader__.load({
|
|
|
222
223
|
|
|
223
224
|
function apply(ctx) {
|
|
224
225
|
installStyle(ctx)
|
|
226
|
+
ctx.effect?.(() => ctx.locale.register(NS, DICT))
|
|
227
|
+
const t = ctx.locale.bind(NS)
|
|
225
228
|
const run = async (sessionId) => {
|
|
226
229
|
const response = await ctx.remote.commands.execute(sessionId, '/retitle', [])
|
|
227
230
|
if (!response?.ok) {
|
|
@@ -240,7 +243,7 @@ window.__ModuleLoader__.load({
|
|
|
240
243
|
name: 'conversation.session.header.actions',
|
|
241
244
|
id: 'session-title-stamp-refresh',
|
|
242
245
|
order: 40,
|
|
243
|
-
inject: () => ({ run }),
|
|
246
|
+
inject: () => ({ run, t }),
|
|
244
247
|
},
|
|
245
248
|
RefreshAction,
|
|
246
249
|
),
|
|
@@ -252,7 +255,7 @@ window.__ModuleLoader__.load({
|
|
|
252
255
|
name: 'sidebar.workspaces.session.menu.item',
|
|
253
256
|
id: 'session-title-stamp-retitle',
|
|
254
257
|
order: 0,
|
|
255
|
-
inject: () => ({ run }),
|
|
258
|
+
inject: () => ({ run, t }),
|
|
256
259
|
},
|
|
257
260
|
RegenerateMenuItem,
|
|
258
261
|
),
|
|
@@ -264,7 +267,7 @@ window.__ModuleLoader__.load({
|
|
|
264
267
|
name: 'sidebar.session.row.hover',
|
|
265
268
|
id: 'session-title-stamp-stats',
|
|
266
269
|
order: 0,
|
|
267
|
-
inject: () => ({}),
|
|
270
|
+
inject: () => ({ t }),
|
|
268
271
|
},
|
|
269
272
|
HoverStats,
|
|
270
273
|
),
|
package/lib/index.js
CHANGED
|
@@ -212,7 +212,14 @@ async function readCachedProjection(sessionId) {
|
|
|
212
212
|
}
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
function normalizeSessionId(sessionId) {
|
|
216
|
+
return typeof sessionId === 'string' && sessionId.startsWith('session-')
|
|
217
|
+
? sessionId.slice('session-'.length)
|
|
218
|
+
: sessionId
|
|
219
|
+
}
|
|
220
|
+
|
|
215
221
|
async function statsFor(scope, sessionId) {
|
|
222
|
+
sessionId = normalizeSessionId(sessionId)
|
|
216
223
|
let tokenUsage
|
|
217
224
|
let sessionStats
|
|
218
225
|
let modelSelection
|
|
@@ -255,10 +262,11 @@ function registerStatsRoute(ctx, logger) {
|
|
|
255
262
|
requestBody: 'buffered',
|
|
256
263
|
fetch: async (request) => {
|
|
257
264
|
const url = new URL(request.url)
|
|
258
|
-
const
|
|
259
|
-
if (!/^[A-Za-z0-9._-]{1,128}$/u.test(
|
|
265
|
+
const rawSessionId = url.searchParams.get('sessionId') ?? ''
|
|
266
|
+
if (!/^[A-Za-z0-9._-]{1,128}$/u.test(rawSessionId)) {
|
|
260
267
|
return jsonResponse({ ok: false, error: 'invalid sessionId' }, 400)
|
|
261
268
|
}
|
|
269
|
+
const sessionId = normalizeSessionId(rawSessionId)
|
|
262
270
|
const now = Date.now()
|
|
263
271
|
const hit = cache.get(sessionId)
|
|
264
272
|
if (hit !== undefined && now - hit.at < STATS_TTL_MS) return jsonResponse(hit.data)
|
package/market/index.json
CHANGED
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"zh": "会话标题印章"
|
|
35
35
|
},
|
|
36
36
|
"description": {
|
|
37
|
-
"en": "Keeps DSH session titles in DATE | CATEGORY | NAME with turn-aware auto-retitle,
|
|
38
|
-
"zh": "把 DSH 会话标题固定为「日期 | 分类 |
|
|
37
|
+
"en": "Keeps DSH session titles in DATE | CATEGORY | NAME with turn-aware auto-retitle, a Generate title action, and hover cost/duration.",
|
|
38
|
+
"zh": "把 DSH 会话标题固定为「日期 | 分类 | 名称」,支持回答后自动重算、自动生成标题和悬停成本/耗时。"
|
|
39
39
|
},
|
|
40
40
|
"author": "xuedipiaofei",
|
|
41
41
|
"icon": "../icon.svg",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
},
|
|
58
58
|
"versions": [
|
|
59
59
|
{
|
|
60
|
-
"version": "0.4.
|
|
61
|
-
"publishTimestamp":
|
|
60
|
+
"version": "0.4.3",
|
|
61
|
+
"publishTimestamp": 1793416672,
|
|
62
62
|
"source": {
|
|
63
63
|
"type": "git",
|
|
64
64
|
"url": "https://github.com/xuedipiaofei/dsh-session-title-stamp",
|
|
65
|
-
"commit": "
|
|
65
|
+
"commit": "86cbe7eb5532b241313cd462236d19abdbd7cec1"
|
|
66
66
|
},
|
|
67
67
|
"engines": {
|
|
68
68
|
"dsh": ">=0.2.1-rc.3 <0.3.0"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-session-title-stamp",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"description": "DSH session titles in a DATE | CATEGORY | NAME template, with turn-aware
|
|
3
|
+
"version": "0.4.3",
|
|
4
|
+
"description": "DSH session titles in a DATE | CATEGORY | NAME template, with turn-aware auto-generation, a Generate title button, and hover cost/duration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
7
7
|
"dsh-plugin",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"inject": [
|
|
53
53
|
"@deepseek-ai/dsh-client-ui-conversation",
|
|
54
54
|
"@deepseek-ai/dsh-client-ui-workspace",
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-renderer"
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-renderer",
|
|
56
|
+
"@deepseek-ai/dsh-client-locale"
|
|
56
57
|
]
|
|
57
58
|
}
|
|
58
59
|
},
|