@chentyit/dsh-chatbot-panel 0.1.0 → 0.2.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/README.md +43 -19
- package/client.js +78 -30
- package/cordis.patch.yml +1 -0
- package/index.js +454 -387
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +6 -3
- package/pure.js +466 -0
package/README.md
CHANGED
|
@@ -13,16 +13,22 @@ tool catalog entirely.
|
|
|
13
13
|
- **Per-session capability switch.** Click the chip to open/close the panel. The chip itself never
|
|
14
14
|
toggles a capability; enabling anything in the panel marks it active (tinted bot icon).
|
|
15
15
|
- **Web search** — exposes the built-in `web_search` / `web_fetch` tools only while enabled.
|
|
16
|
+
- **Image search** — adds `chatbot_image_search` while web search is on. When the user's message
|
|
17
|
+
carries an image, the tool reads that image from the current turn and asks DeepSeek's native
|
|
18
|
+
`deepseek_web_search` server tool to search by it, returning an identification, source pages and
|
|
19
|
+
the matched `image_url`s. An explicit workspace path, `http(s)` URL or base64 data URI also works.
|
|
16
20
|
- **Deep thinking search** — adds `chatbot_deep_search`, which runs DeepSeek's server-side
|
|
17
21
|
`deepseek_web_search` tool with thinking enabled: the search model plans several queries,
|
|
18
|
-
searches, opens and reads relevant pages over multiple steps, then answers with sources.
|
|
22
|
+
searches, opens and reads relevant pages over multiple steps, then answers with sources. Pass
|
|
23
|
+
`image: "auto"` to combine it with the current message's image.
|
|
19
24
|
- **Default off.** A fresh session starts with no web tools declared; turning a capability off
|
|
20
25
|
restores that state.
|
|
21
26
|
- **Durable state.** The switch is stored in the session log through the `/chatbot` command
|
|
22
27
|
lifecycle and read back from a session projection, so it survives page reloads, resume and forks.
|
|
23
|
-
- **Usage and estimated cost.** The panel footer shows
|
|
24
|
-
|
|
25
|
-
the estimated total, with a link to the DeepSeek
|
|
28
|
+
- **Usage and estimated cost.** The panel footer shows billed searches / sources, fetch calls,
|
|
29
|
+
image searches / images sent / billed requests, deep-search calls and tool calls, deep-search
|
|
30
|
+
tokens and failures, per-category fees and the estimated total, with a link to the DeepSeek
|
|
31
|
+
platform usage page. Image and deep fees are split into tool and auxiliary-token fees.
|
|
26
32
|
- **Theme-native, borderless.** The chip uses the host theme tokens and matches the permission
|
|
27
33
|
chip's 28 px text-chip look; no border, no fill, hover tints the whole chip.
|
|
28
34
|
|
|
@@ -32,8 +38,8 @@ Requires DeepSeek Harness `0.2.2-rc.3` or another compatible `0.2.x` runtime (`e
|
|
|
32
38
|
|
|
33
39
|
After the package is published to a registry:
|
|
34
40
|
|
|
35
|
-
1. **Settings → Plugins → install** and enter `@chentyit/dsh-chatbot-panel@0.
|
|
36
|
-
2. use the plugin manager tool: `install_bundle` with target `@chentyit/dsh-chatbot-panel@0.
|
|
41
|
+
1. **Settings → Plugins → install** and enter `@chentyit/dsh-chatbot-panel@0.2.0`, or
|
|
42
|
+
2. use the plugin manager tool: `install_bundle` with target `@chentyit/dsh-chatbot-panel@0.2.0`.
|
|
37
43
|
|
|
38
44
|
From a local checkout (development):
|
|
39
45
|
|
|
@@ -50,8 +56,11 @@ restart the app. Uninstall from Settings → Plugins (or remove the bundle).
|
|
|
50
56
|
1. Open a session. The **ChatBot** chip sits right of the permission chip.
|
|
51
57
|
2. Click it; the panel opens upward. Tick **Web search** (and optionally **Deep thinking search**).
|
|
52
58
|
3. Ask the model a question that needs current information — it now has the matching tools.
|
|
53
|
-
4.
|
|
54
|
-
|
|
59
|
+
4. Attach an image and ask what it is, where it comes from, or to find similar images. With web
|
|
60
|
+
search on, the model calls `chatbot_image_search`; with deep search on it can also add
|
|
61
|
+
`image: "auto"` to `chatbot_deep_search` for a slower, multi-step image search.
|
|
62
|
+
5. Untick everything, and the tools disappear from the model's catalog again.
|
|
63
|
+
6. The panel footer reports this session's usage and estimated cost.
|
|
55
64
|
|
|
56
65
|
You can also drive the same state with the slash command:
|
|
57
66
|
|
|
@@ -63,7 +72,7 @@ You can also drive the same state with the slash command:
|
|
|
63
72
|
|
|
64
73
|
## Usage and billing
|
|
65
74
|
|
|
66
|
-
The official price list bills web search and page fetch separately from model tokens:
|
|
75
|
+
The official price list bills web search, image search and page fetch separately from model tokens:
|
|
67
76
|
**CNY 5 per 1,000 successful requests** (≈ ¥0.005 per call; failed or empty results are not
|
|
68
77
|
billed).
|
|
69
78
|
|
|
@@ -71,6 +80,7 @@ billed).
|
|
|
71
80
|
|---|---|
|
|
72
81
|
| Search | one successful `web_search` tool call fans out one request per query, so a successful call counts `queries.length` billed searches |
|
|
73
82
|
| Fetch | successful `web_fetch` calls; anonymous HTTP fetch is free, so no fee is added unless the fetch provider is the DeepSeek Fetch API (`countFetchCost: true`) |
|
|
83
|
+
| Image | `chatbot_image_search` calls, images sent, and the billed request count (`tool_search` + `tool_fetch` + `tool_image_search`); the fee is split into tool and auxiliary-token fees |
|
|
74
84
|
| Deep | deep-search tool calls plus the billed `tool_search` / `tool_fetch` / `tool_image_search` counters from the provider and the auxiliary model's input/output tokens |
|
|
75
85
|
| Failed | calls that ended in an error; a failed multi-query search may already have billed some requests |
|
|
76
86
|
|
|
@@ -95,18 +105,26 @@ Override the row's `config` in the profile patch (`~/.dsh/profiles/<profile>/cor
|
|
|
95
105
|
model: deepseek-flash
|
|
96
106
|
maxTokens: 4096
|
|
97
107
|
timeoutMs: 180000
|
|
98
|
-
tokenPrice: # auxiliary
|
|
108
|
+
tokenPrice: # auxiliary search turn, deepseek-flash idle prices
|
|
99
109
|
inputPerMillion: 1
|
|
100
110
|
outputPerMillion: 4
|
|
101
111
|
```
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
(
|
|
113
|
+
Image search uses the same `deepSearch` endpoint, model and credential as deep search. It
|
|
114
|
+
authenticates with the signed-in DeepSeek account when the session runs on the account route
|
|
115
|
+
(`x-dsh-auth-token`), otherwise with a stored API key or the key named by
|
|
116
|
+
`deepSearch.apiKeyEnv` (`$DSH_HOME/.env`, the credential service, or the process environment).
|
|
117
|
+
Without either it fails with a clear message.
|
|
106
118
|
|
|
107
119
|
## Limitations
|
|
108
120
|
|
|
109
|
-
- Deep search
|
|
121
|
+
- Deep search and image search need a DeepSeek account sign-in or an API key.
|
|
122
|
+
- Image search reads the images attached to the **current turn's** user message. In a later turn,
|
|
123
|
+
attach the image again or pass an explicit workspace path, `http(s)` URL or data URI.
|
|
124
|
+
- One image search accepts up to four images; each must be PNG, JPEG, WebP or GIF, up to 8 MB
|
|
125
|
+
(attached images are resized toward the DeepSeek vision budget first).
|
|
126
|
+
- Searching by image uploads that image to DeepSeek's Messages API. The result's `image_url` is for
|
|
127
|
+
display only and is not guaranteed to be reachable.
|
|
110
128
|
- Toggling a capability changes the request's tool catalog, so that session's DeepSeek prompt
|
|
111
129
|
cache is invalidated once.
|
|
112
130
|
- With plan mode active, the plan chip sits between the permission chip and this chip (official
|
|
@@ -121,11 +139,15 @@ account route (`x-dsh-auth-token`), otherwise with the API key named by `deepSea
|
|
|
121
139
|
The package is plain JavaScript — no build step, no runtime dependencies:
|
|
122
140
|
|
|
123
141
|
```bash
|
|
124
|
-
npm run check # syntax + metadata validation
|
|
142
|
+
npm run check # syntax + metadata validation + node:test suites
|
|
125
143
|
npm pack --dry-run # inspect the published file list
|
|
126
144
|
npm publish --access public
|
|
127
145
|
```
|
|
128
146
|
|
|
147
|
+
The Host half is `index.js`; `pure.js` holds the testable capability, usage and response helpers;
|
|
148
|
+
`client.js` is the browser half. `test/` covers the pure helpers and the two auxiliary tools with a
|
|
149
|
+
mocked Cordis context and Messages response.
|
|
150
|
+
|
|
129
151
|
## License
|
|
130
152
|
|
|
131
153
|
MIT. Copyright (c) 2026 chentyit.
|
|
@@ -140,12 +162,14 @@ MIT. Copyright (c) 2026 chentyit.
|
|
|
140
162
|
点击按钮展开向上弹出的能力面板;在面板里勾选能力,才对本会话生效。**关闭时不是"拒绝调用",而是把对应工具从模型的工具目录里彻底移除。**
|
|
141
163
|
|
|
142
164
|
- **联网搜索**:仅在开启时向模型暴露内置 `web_search` / `web_fetch`。
|
|
143
|
-
-
|
|
165
|
+
- **以图搜图**:联网搜索开启时额外提供 `chatbot_image_search`。用户消息带图时,工具读取本轮的图片,交给 DeepSeek 服务端 `deepseek_web_search` 按图搜索,返回识别结论、来源页面和匹配到的 `image_url`;也支持工作区路径、`http(s)` URL 或 base64 data URI。
|
|
166
|
+
- **深度思考搜索**:额外提供 `chatbot_deep_search`,走 DeepSeek 服务端 `deepseek_web_search`(thinking 开启)多步搜索、打开网页后带来源作答;传 `image: "auto"` 可结合本轮图片做深度以图搜图。
|
|
144
167
|
- **默认关闭**;开关按会话写入会话日志,刷新、恢复、分叉后仍有效。
|
|
145
|
-
-
|
|
168
|
+
- **用量与费用**:面板底部按官方口径记录搜索、抓取、搜图、深搜的次数与估算费用,并链接开放平台用量页。
|
|
169
|
+
- **图片限制**:只读取本条消息里的图片;一次最多 4 张,支持 PNG/JPEG/WebP/GIF,单张 ≤ 8MB;搜索时图片会上传给 DeepSeek,搜图与深搜需 DeepSeek 账号或 API key。
|
|
146
170
|
|
|
147
|
-
安装:`Settings → Plugins` 输入 `@chentyit/dsh-chatbot-panel@0.
|
|
171
|
+
安装:`Settings → Plugins` 输入 `@chentyit/dsh-chatbot-panel@0.2.0`,或用 `plugin_manager` 的 `install_bundle`。
|
|
148
172
|
|
|
149
|
-
|
|
173
|
+
计费口径:联网搜索/抓取/图片搜索官方价 **5 元 / 1000 次成功请求**(失败或零结果不计费);一次 `web_search` 按查询词数拆成多次请求;图片搜索每张图片计 1 次,模型框选图片中的区域时每个区域另计 1 次。面板金额是**估算**(API 只返回次数),以平台账单为准。
|
|
150
174
|
|
|
151
175
|
配置项与限制见上方英文段落(`manageBuiltinWebTools` / `pricePerThousand` / `countFetchCost` / `deepSearch` / `tokenPrice`)。
|
package/client.js
CHANGED
|
@@ -28,18 +28,21 @@ window.__ModuleLoader__.load({
|
|
|
28
28
|
scope: '本会话',
|
|
29
29
|
groupWeb: '联网工具',
|
|
30
30
|
web: '联网搜索',
|
|
31
|
-
webHint: '
|
|
31
|
+
webHint: '¥5 / 千次 · 附图提问即搜图',
|
|
32
32
|
deep: '深度思考搜索',
|
|
33
|
-
deepHint: '
|
|
33
|
+
deepHint: '多步推理 · 支持以图搜图 · 更耗 token',
|
|
34
34
|
deepNeedsWeb: '先开启联网搜索',
|
|
35
|
+
searchNote: '以图搜图只读本轮图片;搜图与深搜需 DeepSeek 账号或 API key。',
|
|
35
36
|
pending: '应用中…',
|
|
36
37
|
usageTitle: '本会话用量',
|
|
37
38
|
search: '搜索',
|
|
38
39
|
fetch: '抓取',
|
|
40
|
+
imageUsage: '搜图',
|
|
41
|
+
billed: '次计费请求',
|
|
42
|
+
imagesCount: '张图',
|
|
39
43
|
deepUsage: '深搜',
|
|
40
44
|
failed: '失败',
|
|
41
45
|
times: '次',
|
|
42
|
-
queries: '条查询',
|
|
43
46
|
sources: '个来源',
|
|
44
47
|
toolCalls: '次工具调用',
|
|
45
48
|
input: '输入',
|
|
@@ -47,12 +50,16 @@ window.__ModuleLoader__.load({
|
|
|
47
50
|
feeTitle: '费用估算',
|
|
48
51
|
searchFee: '搜索费',
|
|
49
52
|
fetchFee: '抓取费',
|
|
53
|
+
imageToolFee: '搜图工具费',
|
|
54
|
+
imageTokenFee: '搜图 token 费',
|
|
50
55
|
deepToolFee: '深搜工具费',
|
|
51
56
|
deepTokenFee: '深搜 token 费',
|
|
52
57
|
totalFee: '合计',
|
|
53
58
|
fetchFree: '匿名抓取不计费',
|
|
54
59
|
feeNote: '按官方价估算,实际以平台账单为准',
|
|
55
60
|
usageLink: '查看官方用量 ↗',
|
|
61
|
+
active: '已开启',
|
|
62
|
+
inactive: '未开启',
|
|
56
63
|
errorUnknown: '命令未生效',
|
|
57
64
|
errorPrefix: '操作失败:',
|
|
58
65
|
}
|
|
@@ -64,18 +71,21 @@ window.__ModuleLoader__.load({
|
|
|
64
71
|
scope: 'this session',
|
|
65
72
|
groupWeb: 'Web tools',
|
|
66
73
|
web: 'Web search',
|
|
67
|
-
webHint: '
|
|
74
|
+
webHint: '¥5 / 1,000 · attach an image and ask',
|
|
68
75
|
deep: 'Deep thinking search',
|
|
69
|
-
deepHint: '
|
|
76
|
+
deepHint: 'Multi-step · image search · token-heavy',
|
|
70
77
|
deepNeedsWeb: 'Turn on web search first',
|
|
78
|
+
searchNote: 'Image search reads only this turn\'s images; image and deep search need a DeepSeek sign-in or API key.',
|
|
71
79
|
pending: 'Applying…',
|
|
72
80
|
usageTitle: 'Session usage',
|
|
73
81
|
search: 'Search',
|
|
74
82
|
fetch: 'Fetch',
|
|
83
|
+
imageUsage: 'Image search',
|
|
84
|
+
billed: 'billed requests',
|
|
85
|
+
imagesCount: 'images',
|
|
75
86
|
deepUsage: 'Deep',
|
|
76
87
|
failed: 'Failed',
|
|
77
88
|
times: '',
|
|
78
|
-
queries: 'queries',
|
|
79
89
|
sources: 'sources',
|
|
80
90
|
toolCalls: 'tool calls',
|
|
81
91
|
input: 'in',
|
|
@@ -83,12 +93,16 @@ window.__ModuleLoader__.load({
|
|
|
83
93
|
feeTitle: 'Estimated cost',
|
|
84
94
|
searchFee: 'Search',
|
|
85
95
|
fetchFee: 'Fetch',
|
|
96
|
+
imageToolFee: 'Image tools',
|
|
97
|
+
imageTokenFee: 'Image tokens',
|
|
86
98
|
deepToolFee: 'Deep tools',
|
|
87
99
|
deepTokenFee: 'Deep tokens',
|
|
88
100
|
totalFee: 'Total',
|
|
89
101
|
fetchFree: 'Anonymous fetch is free',
|
|
90
102
|
feeNote: 'Estimated from official prices; the platform bill is authoritative',
|
|
91
103
|
usageLink: 'Open platform usage ↗',
|
|
104
|
+
active: 'on',
|
|
105
|
+
inactive: 'off',
|
|
92
106
|
errorUnknown: 'The command did not apply',
|
|
93
107
|
errorPrefix: 'Failed: ',
|
|
94
108
|
}
|
|
@@ -113,15 +127,16 @@ window.__ModuleLoader__.load({
|
|
|
113
127
|
'.cbp-arrow{display:inline-flex;flex:0 0 auto;margin-left:1px;color:var(--dsw-alias-label-caption)}',
|
|
114
128
|
'.cbp-arrow svg{display:block;width:12px;height:12px;transition:transform 120ms ease}',
|
|
115
129
|
'.cbp-main[data-open="true"] .cbp-arrow svg{transform:rotate(180deg)}',
|
|
116
|
-
'.cbp-panel{position:fixed;z-index:1200;box-sizing:border-box;display:flex;flex-direction:column;padding:6px;border-radius:var(--dsw-radius-lg);background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-1,#fff));backdrop-filter:var(--dsw-menu-backdrop-filter,none);box-shadow:var(--dsw-elevation-prominent,0 12px 32px rgba(0,0,0,.18));color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family,inherit)}',
|
|
130
|
+
'.cbp-panel{position:fixed;z-index:1200;box-sizing:border-box;display:flex;flex-direction:column;padding:6px;border-radius:var(--dsw-radius-lg);background:var(--dsw-specific-menu,var(--dsw-alias-bg-layer-1,#fff));backdrop-filter:var(--dsw-menu-backdrop-filter,none);box-shadow:var(--dsw-elevation-prominent,0 12px 32px rgba(0,0,0,.18));color:var(--dsw-alias-label-primary);font-family:var(--dsw-font-family,inherit);overflow-y:auto;overscroll-behavior:contain}',
|
|
117
131
|
'.cbp-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;padding:6px 8px 2px}',
|
|
118
132
|
'.cbp-head-title{font-size:12px;font-weight:600;color:var(--dsw-alias-label-primary)}',
|
|
119
133
|
'.cbp-head-scope{font-size:11px;color:var(--dsw-alias-label-caption)}',
|
|
120
134
|
'.cbp-group{padding:8px 8px 2px;font-size:11px;font-weight:600;letter-spacing:.03em;color:var(--dsw-alias-label-tertiary)}',
|
|
135
|
+
'.cbp-group-note{padding:4px 8px 2px}',
|
|
121
136
|
'.cbp-row{display:flex;align-items:center;gap:10px;width:100%;box-sizing:border-box;padding:7px 8px;border:0;border-radius:var(--dsw-radius-md,8px);background:transparent;color:var(--dsw-alias-label-primary);font-family:inherit;text-align:left;cursor:pointer}',
|
|
122
|
-
'.cbp-row:hover:not(
|
|
137
|
+
'.cbp-row:hover:not([aria-disabled="true"]){background:var(--dsw-alias-interactive-bg-hover)}',
|
|
123
138
|
'.cbp-row:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:-1px}',
|
|
124
|
-
'.cbp-row
|
|
139
|
+
'.cbp-row[aria-disabled="true"]{cursor:default;opacity:.55}',
|
|
125
140
|
'.cbp-row-sub{padding-left:22px}',
|
|
126
141
|
'.cbp-row-text{display:flex;flex-direction:column;gap:1px;min-width:0;flex:1}',
|
|
127
142
|
'.cbp-row-label{font-size:13px;line-height:18px}',
|
|
@@ -139,7 +154,7 @@ window.__ModuleLoader__.load({
|
|
|
139
154
|
'.cbp-note{font-size:10px;line-height:15px;color:var(--dsw-alias-label-caption)}',
|
|
140
155
|
'.cbp-link{font-size:11px;line-height:17px;color:var(--dsw-alias-link);text-decoration:none}',
|
|
141
156
|
'.cbp-link:hover{text-decoration:underline}',
|
|
142
|
-
'.cbp-error{margin:4px 6px 2px;padding:5px 8px;border-radius:var(--dsw-radius-sm,6px);background:var(--dsw-alias-state-error-
|
|
157
|
+
'.cbp-error{margin:4px 6px 2px;padding:5px 8px;border-radius:var(--dsw-radius-sm,6px);background:color-mix(in srgb,var(--dsw-alias-state-error-primary) 12%,transparent);color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:15px}',
|
|
143
158
|
].join('')
|
|
144
159
|
|
|
145
160
|
/** The capsule owns its stylesheet, removed with the plugin's activation. */
|
|
@@ -164,6 +179,12 @@ window.__ModuleLoader__.load({
|
|
|
164
179
|
return amount >= 1000 ? `${(amount / 1000).toFixed(1)}k` : String(amount)
|
|
165
180
|
}
|
|
166
181
|
|
|
182
|
+
/** Join a count and its unit without a trailing space when the unit is empty. */
|
|
183
|
+
function count(value, unit) {
|
|
184
|
+
const amount = typeof value === 'number' && Number.isFinite(value) ? value : 0
|
|
185
|
+
return typeof unit === 'string' && unit.length > 0 ? `${amount} ${unit}` : String(amount)
|
|
186
|
+
}
|
|
187
|
+
|
|
167
188
|
/** A small check mark drawn in the theme's inverted label color. */
|
|
168
189
|
function CheckGlyph() {
|
|
169
190
|
return h('svg', { viewBox: '0 0 12 12', 'aria-hidden': true },
|
|
@@ -232,11 +253,11 @@ window.__ModuleLoader__.load({
|
|
|
232
253
|
const { t, checked, label, hint, sub, disabled, onToggle } = props
|
|
233
254
|
return h('button', {
|
|
234
255
|
type: 'button',
|
|
235
|
-
role: '
|
|
256
|
+
role: 'checkbox',
|
|
236
257
|
'aria-checked': checked ? 'true' : 'false',
|
|
237
|
-
disabled,
|
|
258
|
+
'aria-disabled': disabled ? 'true' : undefined,
|
|
238
259
|
className: sub === true ? 'cbp-row cbp-row-sub' : 'cbp-row',
|
|
239
|
-
onClick: onToggle,
|
|
260
|
+
onClick: disabled ? undefined : onToggle,
|
|
240
261
|
},
|
|
241
262
|
h('span', { className: 'cbp-row-text' },
|
|
242
263
|
h('span', { className: 'cbp-row-label' }, label),
|
|
@@ -251,40 +272,56 @@ window.__ModuleLoader__.load({
|
|
|
251
272
|
if (usage === undefined || usage === null) {
|
|
252
273
|
return h('div', { className: 'cbp-usage' },
|
|
253
274
|
h('div', { className: 'cbp-usage-title' }, t('usageTitle')),
|
|
254
|
-
h('div', { className: 'cbp-usage-line' }, h('span', null, t('search')), h('b', null,
|
|
275
|
+
h('div', { className: 'cbp-usage-line' }, h('span', null, t('search')), h('b', null, count(0, t('times')))))
|
|
255
276
|
}
|
|
256
277
|
const search = usage.search || { calls: 0, queries: 0, sources: 0, failed: 0, failedQueries: 0 }
|
|
257
278
|
const fetch = usage.fetch || { calls: 0, failed: 0 }
|
|
279
|
+
const image = usage.image || { calls: 0, failed: 0, images: 0, results: 0, imageResults: 0, toolSearch: 0, toolFetch: 0, imageSearch: 0, inputTokens: 0, outputTokens: 0 }
|
|
258
280
|
const deep = usage.deep || { calls: 0, failed: 0, toolSearch: 0, toolFetch: 0, imageSearch: 0, inputTokens: 0, outputTokens: 0, sources: 0 }
|
|
259
|
-
const fees = usage.fees || { search: 0, fetch: 0, deepTools: 0, deepTokens: 0, total: 0 }
|
|
281
|
+
const fees = usage.fees || { search: 0, fetch: 0, image: 0, imageTools: 0, imageTokens: 0, deepTools: 0, deepTokens: 0, total: 0 }
|
|
260
282
|
const deepTools = (deep.toolSearch || 0) + (deep.toolFetch || 0) + (deep.imageSearch || 0)
|
|
261
|
-
const
|
|
283
|
+
const imageBilled = (image.toolSearch || 0) + (image.toolFetch || 0) + (image.imageSearch || 0)
|
|
284
|
+
const showImage = image.calls > 0 || image.failed > 0 || imageBilled > 0
|
|
285
|
+
const failures = (search.failed || 0) + (fetch.failed || 0) + (image.failed || 0) + (deep.failed || 0)
|
|
262
286
|
const lines = []
|
|
263
287
|
lines.push(h('div', { key: 'title', className: 'cbp-usage-title' }, t('usageTitle')))
|
|
264
288
|
lines.push(h('div', { key: 'search', className: 'cbp-usage-line' },
|
|
265
289
|
h('span', null, t('search')),
|
|
266
290
|
h('span', null,
|
|
267
|
-
h('b', null,
|
|
268
|
-
` · ${
|
|
291
|
+
h('b', null, count(search.calls, t('billed'))),
|
|
292
|
+
` · ${count(search.sources, t('sources'))}`)))
|
|
293
|
+
if (showImage) {
|
|
294
|
+
lines.push(h('div', { key: 'image', className: 'cbp-usage-line' },
|
|
295
|
+
h('span', null, t('imageUsage')),
|
|
296
|
+
h('span', null,
|
|
297
|
+
h('b', null, count(image.calls, t('times'))),
|
|
298
|
+
` · ${count(image.images, t('imagesCount'))} · ${count(imageBilled, t('billed'))}`)))
|
|
299
|
+
}
|
|
269
300
|
lines.push(h('div', { key: 'fetch', className: 'cbp-usage-line' },
|
|
270
301
|
h('span', null, t('fetch')),
|
|
271
302
|
h('span', null,
|
|
272
|
-
h('b', null,
|
|
303
|
+
h('b', null, count(fetch.calls, t('times'))),
|
|
273
304
|
usage.countFetchCost === true ? '' : ` · ${t('fetchFree')}`)))
|
|
274
305
|
lines.push(h('div', { key: 'deep', className: 'cbp-usage-line' },
|
|
275
306
|
h('span', null, t('deepUsage')),
|
|
276
307
|
h('span', null,
|
|
277
|
-
h('b', null,
|
|
278
|
-
` · ${deepTools
|
|
308
|
+
h('b', null, count(deep.calls, t('times'))),
|
|
309
|
+
` · ${count(deepTools, t('toolCalls'))} · ${tokens(deep.inputTokens)}/${tokens(deep.outputTokens)} tok`)))
|
|
279
310
|
if (failures > 0) {
|
|
280
311
|
lines.push(h('div', { key: 'failed', className: 'cbp-usage-line' },
|
|
281
312
|
h('span', null, t('failed')),
|
|
282
|
-
h('span', null,
|
|
313
|
+
h('span', null, count(failures, t('times')))))
|
|
283
314
|
}
|
|
284
315
|
lines.push(h('div', { key: 'sep', className: 'cbp-sep' }))
|
|
285
316
|
lines.push(h('div', { key: 'fee-title', className: 'cbp-usage-title' }, t('feeTitle')))
|
|
286
317
|
lines.push(h('div', { key: 'fee-search', className: 'cbp-usage-line' },
|
|
287
318
|
h('span', null, t('searchFee')), h('span', null, yuan(fees.search))))
|
|
319
|
+
if (showImage) {
|
|
320
|
+
lines.push(h('div', { key: 'fee-image-tools', className: 'cbp-usage-line' },
|
|
321
|
+
h('span', null, t('imageToolFee')), h('span', null, yuan(fees.imageTools))))
|
|
322
|
+
lines.push(h('div', { key: 'fee-image-tokens', className: 'cbp-usage-line' },
|
|
323
|
+
h('span', null, t('imageTokenFee')), h('span', null, yuan(fees.imageTokens))))
|
|
324
|
+
}
|
|
288
325
|
lines.push(h('div', { key: 'fee-fetch', className: 'cbp-usage-line' },
|
|
289
326
|
h('span', null, t('fetchFee')), h('span', null, yuan(fees.fetch))))
|
|
290
327
|
lines.push(h('div', { key: 'fee-deep-tools', className: 'cbp-usage-line' },
|
|
@@ -324,7 +361,8 @@ window.__ModuleLoader__.load({
|
|
|
324
361
|
const run = React.useCallback((line) => {
|
|
325
362
|
setBusy(true)
|
|
326
363
|
setError(null)
|
|
327
|
-
Promise.resolve(
|
|
364
|
+
Promise.resolve()
|
|
365
|
+
.then(() => submit(sessionId, line))
|
|
328
366
|
.then((result) => {
|
|
329
367
|
if (result === undefined || result === null) { setError(t('errorUnknown')); return }
|
|
330
368
|
if (result.ok !== true) {
|
|
@@ -346,9 +384,10 @@ window.__ModuleLoader__.load({
|
|
|
346
384
|
const element = wrapRef.current
|
|
347
385
|
if (element === null || typeof window === 'undefined') return
|
|
348
386
|
const rect = element.getBoundingClientRect()
|
|
349
|
-
const
|
|
387
|
+
const width = Math.min(PANEL_WIDTH, Math.max(160, window.innerWidth - 16))
|
|
388
|
+
const left = Math.min(Math.max(8, rect.left), Math.max(8, window.innerWidth - width - 8))
|
|
350
389
|
const bottom = Math.max(8, window.innerHeight - rect.top + 8)
|
|
351
|
-
setPosition({ left, bottom })
|
|
390
|
+
setPosition({ left, bottom, width, maxHeight: Math.max(120, window.innerHeight - bottom - 8) })
|
|
352
391
|
}, [])
|
|
353
392
|
|
|
354
393
|
React.useLayoutEffect(() => {
|
|
@@ -359,6 +398,13 @@ window.__ModuleLoader__.load({
|
|
|
359
398
|
return () => { window.removeEventListener('resize', onResize) }
|
|
360
399
|
}, [open, place])
|
|
361
400
|
|
|
401
|
+
React.useEffect(() => {
|
|
402
|
+
if (!open || panelRef.current === null) return undefined
|
|
403
|
+
const first = panelRef.current.querySelector('button[role="checkbox"]:not([aria-disabled="true"])')
|
|
404
|
+
if (first !== null && typeof first.focus === 'function') first.focus({ preventScroll: true })
|
|
405
|
+
return undefined
|
|
406
|
+
}, [open])
|
|
407
|
+
|
|
362
408
|
React.useEffect(() => {
|
|
363
409
|
if (!open) return undefined
|
|
364
410
|
const onPointerDown = (event) => {
|
|
@@ -393,9 +439,9 @@ window.__ModuleLoader__.load({
|
|
|
393
439
|
type: 'button',
|
|
394
440
|
className: 'cbp-main',
|
|
395
441
|
'data-open': open ? 'true' : 'false',
|
|
396
|
-
'aria-haspopup': '
|
|
442
|
+
'aria-haspopup': 'dialog',
|
|
397
443
|
'aria-expanded': open ? 'true' : 'false',
|
|
398
|
-
'aria-label': t('openPanel')
|
|
444
|
+
'aria-label': `${t('openPanel')} · ${active ? t('active') : t('inactive')}`,
|
|
399
445
|
title: t('openPanel'),
|
|
400
446
|
onClick: () => { setOpen(value => !value) },
|
|
401
447
|
},
|
|
@@ -408,12 +454,13 @@ window.__ModuleLoader__.load({
|
|
|
408
454
|
panel = createPortal(h('div', {
|
|
409
455
|
ref: panelRef,
|
|
410
456
|
className: 'cbp-panel',
|
|
411
|
-
role: '
|
|
457
|
+
role: 'dialog',
|
|
412
458
|
'aria-label': t('panelTitle'),
|
|
413
459
|
style: {
|
|
414
460
|
left: position === null ? 0 : `${position.left}px`,
|
|
415
461
|
bottom: position === null ? 12 : `${position.bottom}px`,
|
|
416
|
-
width: `${
|
|
462
|
+
width: position === null ? 'min(328px, calc(100vw - 16px))' : `${position.width}px`,
|
|
463
|
+
maxHeight: position === null ? 'calc(100vh - 24px)' : `${position.maxHeight}px`,
|
|
417
464
|
},
|
|
418
465
|
},
|
|
419
466
|
h('div', { className: 'cbp-head' },
|
|
@@ -437,9 +484,10 @@ window.__ModuleLoader__.load({
|
|
|
437
484
|
disabled: disabled || !web,
|
|
438
485
|
onToggle: () => { run(deep ? 'deep off' : 'deep on') },
|
|
439
486
|
}),
|
|
487
|
+
h('div', { className: 'cbp-note cbp-group-note' }, t('searchNote')),
|
|
440
488
|
h('div', { className: 'cbp-sep' }),
|
|
441
489
|
h(UsageBlock, { t, usage }),
|
|
442
|
-
pending ? h('div', { className: 'cbp-note' }, t('pending')) : null,
|
|
490
|
+
pending || busy ? h('div', { className: 'cbp-note' }, t('pending')) : null,
|
|
443
491
|
error === null ? null : h('div', { className: 'cbp-error', role: 'alert' }, error)),
|
|
444
492
|
document.body)
|
|
445
493
|
}
|
package/cordis.patch.yml
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
# Anonymous HTTP fetch is free; enable only when the fetch provider is
|
|
12
12
|
# the DeepSeek Fetch API.
|
|
13
13
|
countFetchCost: false
|
|
14
|
+
# Deep and image search share this endpoint, model and credential.
|
|
14
15
|
deepSearch:
|
|
15
16
|
baseURL: https://api.deepseek.com/anthropic/v1
|
|
16
17
|
apiKeyEnv: DEEPSEEK_API_KEY
|