dsh-packycode-api 1.0.7 → 1.1.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/CHANGELOG.md +30 -0
- package/README.en.md +60 -9
- package/README.md +65 -10
- package/SECURITY.md +9 -0
- package/compatibility.json +1 -1
- package/lib/client.js +1161 -726
- package/lib/index.js +3544 -1213
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -29,87 +29,26 @@ window.__ModuleLoader__.load({
|
|
|
29
29
|
let react = require("react");
|
|
30
30
|
react = __toESM(react, 1);
|
|
31
31
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
32
|
-
//#region src/
|
|
33
|
-
/**
|
|
34
|
-
* Totals across the keys a balance read returned.
|
|
35
|
-
*
|
|
36
|
-
* The per-key quota (`/api/usage/token/`) is what a key can spend, so the
|
|
37
|
-
* combined figure is the sum of those rows. PackyCode also reports account
|
|
38
|
-
* level billing (`/v1/dashboard/billing/*`), which is a different scope — it
|
|
39
|
-
* counts every token on the account — and is deliberately not what this sum
|
|
40
|
-
* shows.
|
|
41
|
-
*
|
|
42
|
-
* @module dsh-packycode-api/totals
|
|
43
|
-
*/
|
|
44
|
-
const amount = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
45
|
-
/**
|
|
46
|
-
* Add up the balance rows the host returned.
|
|
47
|
-
* @param rows - the `keys` array of a balance projection.
|
|
48
|
-
* @returns summed amounts plus the conditions that make a sum incomplete:
|
|
49
|
-
* keys whose read failed, and keys with no quota limit at all.
|
|
50
|
-
*/
|
|
51
|
-
function sumBalances(rows) {
|
|
52
|
-
const total = {
|
|
53
|
-
keys: 0,
|
|
54
|
-
counted: 0,
|
|
55
|
-
unread: 0,
|
|
56
|
-
unlimited: 0,
|
|
57
|
-
remainingUsd: 0,
|
|
58
|
-
usedUsd: 0,
|
|
59
|
-
limitUsd: 0
|
|
60
|
-
};
|
|
61
|
-
for (const row of Array.isArray(rows) ? rows : []) {
|
|
62
|
-
total.keys += 1;
|
|
63
|
-
const usage = row?.usage;
|
|
64
|
-
if (usage === void 0) {
|
|
65
|
-
total.unread += 1;
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
total.counted += 1;
|
|
69
|
-
if (usage.unlimitedQuota === true) {
|
|
70
|
-
total.unlimited += 1;
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
const remaining = amount(usage.availableUsd);
|
|
74
|
-
const used = amount(usage.usedUsd);
|
|
75
|
-
const limit = amount(usage.limitUsd);
|
|
76
|
-
if (remaining !== void 0) total.remainingUsd += remaining;
|
|
77
|
-
if (used !== void 0) total.usedUsd += used;
|
|
78
|
-
if (limit !== void 0) total.limitUsd += limit;
|
|
79
|
-
}
|
|
80
|
-
return total;
|
|
81
|
-
}
|
|
82
|
-
//#endregion
|
|
83
|
-
//#region src/client.jsx
|
|
32
|
+
//#region src/client-copy.js
|
|
84
33
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* Every number here comes from the host over the plugin's own loopback RPC
|
|
88
|
-
* channel; no key material crosses it, and each read is a bounded projection
|
|
89
|
-
* the host built.
|
|
34
|
+
* The settings panel's copy, in both languages DSH ships.
|
|
90
35
|
*
|
|
91
|
-
* @module dsh-packycode-api/client
|
|
36
|
+
* @module dsh-packycode-api/client-copy
|
|
92
37
|
*/
|
|
93
|
-
const NS = "settings.packyCodeApi";
|
|
94
|
-
const PROVIDER = "packycode";
|
|
95
|
-
const CHANNEL = "/packycode-api";
|
|
96
|
-
const REFRESH_MS = 6e4;
|
|
97
38
|
const zh = {
|
|
98
39
|
nav: "PackyCode",
|
|
99
|
-
intro: "填入 PackyCode(PackyAPI)的 API Key
|
|
100
|
-
|
|
40
|
+
intro: "填入 PackyCode(PackyAPI)的 API Key 即可使用内置模型路由。多个 Key 按顺序使用:额度用尽或失效时自动切到下一个;某个 Key 的分组用不了所选模型时,自动改用分组合适的 Key。",
|
|
41
|
+
accountsTitle: "账户与 Key",
|
|
42
|
+
refresh: "刷新",
|
|
43
|
+
refreshing: "刷新中…",
|
|
101
44
|
totalRemaining: "合计剩余",
|
|
102
45
|
totalUsed: "合计已用",
|
|
103
46
|
totalLimit: "合计额度",
|
|
104
47
|
totalPartial: "有 {count} 个 Key 未读到余额,未计入合计。",
|
|
105
48
|
totalStale: "其中 {count} 个 Key 本次读取失败,显示的是上次读到的余额。",
|
|
106
49
|
staleNote: "上次读取于 {time},本次读取失败:{error}",
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
refresh: "刷新",
|
|
110
|
-
refreshing: "刷新中…",
|
|
111
|
-
keysTitle: "API Key",
|
|
112
|
-
keysHint: "按顺序使用:当前 Key 不可用时自动切到下一个可用 Key。上方的账户余额按备注名逐条列出,可用「重命名」修改备注。",
|
|
50
|
+
empty: "还没有 Key。添加一个即可在模型选择器里看到 PackyCode 的模型。",
|
|
51
|
+
keysHint: "按顺序使用:当前 Key 不可用时自动切到下一个;「切到此 Key」指定优先使用的 Key。",
|
|
113
52
|
keyActive: "使用中",
|
|
114
53
|
keyDisabled: "已停用",
|
|
115
54
|
keyUse: "切到此 Key",
|
|
@@ -123,28 +62,35 @@ window.__ModuleLoader__.load({
|
|
|
123
62
|
keyNoBalance: "余额未知",
|
|
124
63
|
keyRemaining: "剩余",
|
|
125
64
|
keyUsed: "已用",
|
|
65
|
+
accountLimit: "额度",
|
|
126
66
|
keyName: "账户名",
|
|
127
67
|
keyNameUnknown: "账户名待读取",
|
|
128
|
-
resetMonthly: "
|
|
129
|
-
resetWeekly: "
|
|
130
|
-
resetDaily: "
|
|
68
|
+
resetMonthly: "每月重置",
|
|
69
|
+
resetWeekly: "每周重置",
|
|
70
|
+
resetDaily: "每日重置",
|
|
71
|
+
loadFailed: "读取失败",
|
|
72
|
+
removeAll: "清空全部",
|
|
131
73
|
addTitle: "添加 Key",
|
|
132
74
|
addKeyPlaceholder: "粘贴 API Key",
|
|
133
75
|
addLabelPlaceholder: "备注(可选)",
|
|
134
76
|
add: "添加",
|
|
135
|
-
|
|
136
|
-
|
|
77
|
+
notWritable: "当前 profile 不允许写入设置,改动只在本次运行内生效。",
|
|
78
|
+
error: "读取 PackyCode 状态失败",
|
|
79
|
+
groupsTitle: "分组",
|
|
80
|
+
groupsChecking: "正在读取分组…",
|
|
81
|
+
groupsUnknown: "分组未读到",
|
|
82
|
+
groupsRejected: "Key 被网关拒绝",
|
|
83
|
+
groupsReach: "可用",
|
|
84
|
+
groupsMiss: "不可用",
|
|
85
|
+
groupsPartial: "部分",
|
|
86
|
+
groupsStale: "分组为上次读取的结果",
|
|
137
87
|
advanced: "高级:接口地址",
|
|
138
88
|
apiHost: "模型接口地址",
|
|
139
89
|
statusHost: "余额接口地址",
|
|
140
|
-
save: "保存",
|
|
141
|
-
saved: "已保存",
|
|
142
|
-
notWritable: "当前 profile 不允许写入设置,改动只在本次运行内生效。",
|
|
143
90
|
composerLabel: "PackyCode 剩余额度",
|
|
144
91
|
composerNoKey: "无可用 Key",
|
|
145
92
|
composerUnread: "余额读取失败",
|
|
146
|
-
|
|
147
|
-
loadFailed: "读取失败",
|
|
93
|
+
composerStale: "上次读取于 {time},本次读取失败",
|
|
148
94
|
cliTitle: "Claude Code CLI",
|
|
149
95
|
cliChecking: "正在检测本机的 Claude Code CLI…",
|
|
150
96
|
cliInstalled: "已安装",
|
|
@@ -174,8 +120,8 @@ window.__ModuleLoader__.load({
|
|
|
174
120
|
versionCheck: "检查更新",
|
|
175
121
|
versionChecking: "正在检查更新…",
|
|
176
122
|
versionFailed: "无法检查最新版本。",
|
|
177
|
-
versionUpToDate: "
|
|
178
|
-
versionAvailable: "发现新版本 v{version}
|
|
123
|
+
versionUpToDate: "已是最新版本",
|
|
124
|
+
versionAvailable: "发现新版本 v{version}",
|
|
179
125
|
versionLinked: "当前是本地开发安装,请在插件仓库拉取代码并重新构建。",
|
|
180
126
|
versionManual: "无法在这里更新。请在桌面应用的插件页重新安装 dsh-packycode-api。",
|
|
181
127
|
versionUpdate: "更新插件",
|
|
@@ -186,21 +132,48 @@ window.__ModuleLoader__.load({
|
|
|
186
132
|
versionRestart: "如何重启",
|
|
187
133
|
versionRestartHint: "插件无法安全地重启桌面应用。请完全退出 DSH,再重新打开。",
|
|
188
134
|
versionRefresh: "刷新界面",
|
|
189
|
-
versionLater: "稍后"
|
|
135
|
+
versionLater: "稍后",
|
|
136
|
+
searchTitle: "网页搜索",
|
|
137
|
+
searchScope: "DSH 全局设置",
|
|
138
|
+
searchIntro: "DSH 的 web_search 和 Claude CLI 的 MCP 搜索可使用这里选择的 DeepSeek/GPT 后端。每次搜索是独立的模型调用,用插件保存的 Key 计费。",
|
|
139
|
+
searchCliHint: "Claude CLI 始终通过 MCP 使用这里的搜索后端,原生 WebSearch 已禁用。下面的路由选项只控制 DSH 全局搜索,不影响 CLI。",
|
|
140
|
+
searchChecking: "正在读取搜索设置…",
|
|
141
|
+
searchLoadFailed: "搜索设置读取或保存失败",
|
|
142
|
+
searchModeTitle: "DSH 路由",
|
|
143
|
+
searchModeDefault: "不接管",
|
|
144
|
+
searchModeDefaultHint: "不改动 DSH 的搜索路由(默认)。切回此项会移除本插件写入 profile 的搜索补丁。",
|
|
145
|
+
searchModeAuto: "按模型",
|
|
146
|
+
searchModeAutoHint: "PackyCode 模型用 PackyCode 搜索;其他模型仍走原来的搜索(DSH 默认搜索,或其他插件的设置)。",
|
|
147
|
+
searchModePackycode: "始终 PackyCode",
|
|
148
|
+
searchModePackycodeHint: "原本由 DSH 默认搜索处理的请求都改走 PackyCode。Codex、Kimi 等订阅插件接管的模型仍按它们自己的设置。",
|
|
149
|
+
searchBackendTitle: "搜索后端",
|
|
150
|
+
searchBackendDeepseek: "DeepSeek",
|
|
151
|
+
searchBackendDeepseekHint: "{model}:每次搜索约几千 token;需要 Key 的分组包含 DeepSeek。",
|
|
152
|
+
searchBackendGpt: "GPT",
|
|
153
|
+
searchBackendGptHint: "{model}:结果通常更新、更准,但每次搜索约 2–3 万输入 token;需要 Key 的分组包含 Codex。",
|
|
154
|
+
searchEffective: "当前生效:基础搜索 {base} · 运行时 {runtime}",
|
|
155
|
+
searchPatchNote: "「按模型」和「始终」会在本 profile 的 cordis.patch.yml 里写入一段带标记的 web 配置,热加载生效,无需重启。卸载插件前请先切回「不接管」。",
|
|
156
|
+
searchApplyFailed: "写入搜索补丁失败:{error}",
|
|
157
|
+
searchForeign: "检测到 {names} 订阅插件:它们接管的模型仍按各自的搜索设置,其余请求才会到达这里的选择。",
|
|
158
|
+
searchOverridden: "基础搜索现在是 {base},不是 PackyCode:可能还在热加载,或被其他插件的补丁覆盖。",
|
|
159
|
+
searchUnavailable: "当前运行环境没有 DSH 网页搜索服务,全局路由暂不生效;Claude CLI 的 MCP 搜索不受影响。",
|
|
160
|
+
searchNotWritable: "当前 profile 不允许写入设置。",
|
|
161
|
+
searchUnknown: "未知"
|
|
190
162
|
};
|
|
191
163
|
const en = {
|
|
192
164
|
nav: "PackyCode",
|
|
193
|
-
intro: "Paste a PackyCode (PackyAPI) API key to use the built-in model route.
|
|
194
|
-
|
|
165
|
+
intro: "Paste a PackyCode (PackyAPI) API key to use the built-in model route. Keys are used in order: an exhausted or rejected key hands over to the next one, and a key whose groups cannot reach the selected model is passed over for one whose groups can.",
|
|
166
|
+
accountsTitle: "Accounts and keys",
|
|
167
|
+
refresh: "Refresh",
|
|
168
|
+
refreshing: "Refreshing…",
|
|
195
169
|
totalRemaining: "Total remaining",
|
|
196
170
|
totalUsed: "Total used",
|
|
197
171
|
totalLimit: "Total limit",
|
|
198
172
|
totalPartial: "{count} key(s) could not be read and are not counted here.",
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
keysHint: "Used in order: when the active key cannot serve a request, the next usable one takes over. The balances above are listed by these names; use Rename to change one.",
|
|
173
|
+
totalStale: "{count} of these keys failed this read and show their last read balance.",
|
|
174
|
+
staleNote: "Last read at {time}; this read failed: {error}",
|
|
175
|
+
empty: "No keys yet. Add one and the PackyCode models appear in the model picker.",
|
|
176
|
+
keysHint: "Used in order: when the active key cannot serve a request, the next one takes over. “Use this key” picks the preferred key.",
|
|
204
177
|
keyActive: "Active",
|
|
205
178
|
keyDisabled: "Disabled",
|
|
206
179
|
keyUse: "Use this key",
|
|
@@ -214,31 +187,35 @@ window.__ModuleLoader__.load({
|
|
|
214
187
|
keyNoBalance: "Balance unknown",
|
|
215
188
|
keyRemaining: "Remaining",
|
|
216
189
|
keyUsed: "Used",
|
|
217
|
-
|
|
190
|
+
accountLimit: "Limit",
|
|
191
|
+
keyName: "Account",
|
|
218
192
|
keyNameUnknown: "Account name not read yet",
|
|
219
|
-
resetMonthly: "monthly",
|
|
220
|
-
resetWeekly: "weekly",
|
|
221
|
-
resetDaily: "daily",
|
|
193
|
+
resetMonthly: "resets monthly",
|
|
194
|
+
resetWeekly: "resets weekly",
|
|
195
|
+
resetDaily: "resets daily",
|
|
196
|
+
loadFailed: "Read failed",
|
|
197
|
+
removeAll: "Remove all",
|
|
222
198
|
addTitle: "Add a key",
|
|
223
199
|
addKeyPlaceholder: "Paste API key",
|
|
224
200
|
addLabelPlaceholder: "Label (optional)",
|
|
225
201
|
add: "Add",
|
|
226
|
-
|
|
227
|
-
|
|
202
|
+
notWritable: "This profile does not allow settings writes; changes apply to this run only.",
|
|
203
|
+
error: "Could not read PackyCode status",
|
|
204
|
+
groupsTitle: "Groups",
|
|
205
|
+
groupsChecking: "Reading groups…",
|
|
206
|
+
groupsUnknown: "Groups not read",
|
|
207
|
+
groupsRejected: "Key rejected by the gateway",
|
|
208
|
+
groupsReach: "Reaches",
|
|
209
|
+
groupsMiss: "Not",
|
|
210
|
+
groupsPartial: "partly",
|
|
211
|
+
groupsStale: "groups from the last read",
|
|
228
212
|
advanced: "Advanced: endpoints",
|
|
229
213
|
apiHost: "Model endpoint",
|
|
230
214
|
statusHost: "Balance endpoint",
|
|
231
|
-
save: "Save",
|
|
232
|
-
saved: "Saved",
|
|
233
|
-
notWritable: "This profile does not allow settings writes; changes apply to this run only.",
|
|
234
215
|
composerLabel: "PackyCode remaining balance",
|
|
235
216
|
composerNoKey: "No usable key",
|
|
236
217
|
composerUnread: "Balance unavailable",
|
|
237
|
-
totalStale: "{count} of these keys failed this read and show their last read balance.",
|
|
238
|
-
staleNote: "Last read at {time}; this read failed: {error}",
|
|
239
218
|
composerStale: "last read at {time}; this read failed",
|
|
240
|
-
error: "Could not read PackyCode status",
|
|
241
|
-
loadFailed: "Read failed",
|
|
242
219
|
cliTitle: "Claude Code CLI",
|
|
243
220
|
cliChecking: "Checking this device for the Claude Code CLI…",
|
|
244
221
|
cliInstalled: "Installed",
|
|
@@ -268,8 +245,8 @@ window.__ModuleLoader__.load({
|
|
|
268
245
|
versionCheck: "Check for updates",
|
|
269
246
|
versionChecking: "Checking for updates…",
|
|
270
247
|
versionFailed: "Could not check the latest version.",
|
|
271
|
-
versionUpToDate: "
|
|
272
|
-
versionAvailable: "
|
|
248
|
+
versionUpToDate: "Up to date",
|
|
249
|
+
versionAvailable: "v{version} is available",
|
|
273
250
|
versionLinked: "Installed from a local checkout. Pull and rebuild the plugin repository instead.",
|
|
274
251
|
versionManual: "This install cannot be updated here. Reinstall dsh-packycode-api from the desktop Plugins page.",
|
|
275
252
|
versionUpdate: "Update plugin",
|
|
@@ -280,51 +257,242 @@ window.__ModuleLoader__.load({
|
|
|
280
257
|
versionRestart: "How to restart",
|
|
281
258
|
versionRestartHint: "The plugin cannot safely restart the desktop application. Quit DSH completely, then open it again.",
|
|
282
259
|
versionRefresh: "Refresh page",
|
|
283
|
-
versionLater: "Later"
|
|
260
|
+
versionLater: "Later",
|
|
261
|
+
searchTitle: "Web search",
|
|
262
|
+
searchScope: "DSH-wide setting",
|
|
263
|
+
searchIntro: "DSH’s web_search and Claude CLI’s MCP search can use the DeepSeek/GPT backend selected here. Each search is a separate model call billed to the plugin-stored keys.",
|
|
264
|
+
searchCliHint: "Claude CLI always uses this backend through MCP; native WebSearch is disabled. The routing options below only control DSH-wide search, not CLI search.",
|
|
265
|
+
searchChecking: "Reading the search preference…",
|
|
266
|
+
searchLoadFailed: "Could not read or save the search preference",
|
|
267
|
+
searchModeTitle: "DSH routing",
|
|
268
|
+
searchModeDefault: "Off",
|
|
269
|
+
searchModeDefaultHint: "Leave DSH search routing untouched (the default). Switching back here removes the search patch this plugin wrote into the profile.",
|
|
270
|
+
searchModeAuto: "By model",
|
|
271
|
+
searchModeAutoHint: "PackyCode models search through PackyCode; other models keep the search they had (DSH’s default, or another plugin’s choice).",
|
|
272
|
+
searchModePackycode: "Always PackyCode",
|
|
273
|
+
searchModePackycodeHint: "Every request DSH’s default search would have served goes through PackyCode. Models another subscription plugin (Codex, Kimi) routes keep that plugin’s choice.",
|
|
274
|
+
searchBackendTitle: "Search backend",
|
|
275
|
+
searchBackendDeepseek: "DeepSeek",
|
|
276
|
+
searchBackendDeepseekHint: "{model}: a few thousand tokens per search; needs a key whose group includes DeepSeek.",
|
|
277
|
+
searchBackendGpt: "GPT",
|
|
278
|
+
searchBackendGptHint: "{model}: usually fresher and more precise, but about 20–30K input tokens per search; needs a key whose group includes Codex.",
|
|
279
|
+
searchEffective: "In effect: base search {base} · runtime {runtime}",
|
|
280
|
+
searchPatchNote: "By model and Always write a marked web entry into this profile’s cordis.patch.yml; it hot-applies, no restart needed. Switch back to Off before uninstalling the plugin.",
|
|
281
|
+
searchApplyFailed: "Could not write the search patch: {error}",
|
|
282
|
+
searchForeign: "{names} subscription plugin detected: the models it routes keep its own search choice; only the other requests reach the choice made here.",
|
|
283
|
+
searchOverridden: "The base search is {base}, not PackyCode: the reload may still be running, or another plugin’s patch overrides it.",
|
|
284
|
+
searchUnavailable: "This runtime has no DSH web search service; global routing has no effect. Claude CLI’s MCP search is unaffected.",
|
|
285
|
+
searchNotWritable: "This profile does not allow settings writes.",
|
|
286
|
+
searchUnknown: "unknown"
|
|
284
287
|
};
|
|
288
|
+
//#endregion
|
|
289
|
+
//#region src/client-style.js
|
|
290
|
+
/**
|
|
291
|
+
* The settings panel's stylesheet, built only from DSH design tokens so it
|
|
292
|
+
* follows the host theme (light and dark) and matches the host's own settings
|
|
293
|
+
* pages: settings cards, hairline-divided rows, 0.5px tags, layer-1 inputs.
|
|
294
|
+
* Fallbacks cover hosts that predate the settings-card tokens.
|
|
295
|
+
*
|
|
296
|
+
* @module dsh-packycode-api/client-style
|
|
297
|
+
*/
|
|
285
298
|
const STYLE = `
|
|
286
|
-
.
|
|
287
|
-
.
|
|
288
|
-
.
|
|
289
|
-
.
|
|
290
|
-
.
|
|
291
|
-
.
|
|
292
|
-
.
|
|
293
|
-
.
|
|
294
|
-
.
|
|
295
|
-
.
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
.
|
|
299
|
-
.
|
|
300
|
-
.
|
|
301
|
-
.
|
|
302
|
-
.
|
|
303
|
-
.
|
|
304
|
-
.
|
|
305
|
-
.
|
|
306
|
-
.
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
.
|
|
310
|
-
.
|
|
311
|
-
.
|
|
312
|
-
.
|
|
313
|
-
.
|
|
314
|
-
|
|
315
|
-
.
|
|
316
|
-
.
|
|
317
|
-
.
|
|
318
|
-
.
|
|
319
|
-
.
|
|
320
|
-
.
|
|
321
|
-
.
|
|
322
|
-
|
|
299
|
+
.pkc{display:flex;flex-direction:column;gap:16px;color:var(--dsw-alias-label-primary);font-size:14px;line-height:22px}
|
|
300
|
+
.pkc-intro{margin:0;color:var(--dsw-alias-label-tertiary)}
|
|
301
|
+
.pkc-card{display:flex;flex-direction:column;gap:14px;padding:16px 18px;border:.5px solid var(--dsw-alias-settings-card-stroke,var(--dsw-alias-border-l4,rgba(127,127,127,.3)));border-radius:var(--dsw-radius-xl,20px);background:var(--dsw-alias-settings-card-fill,var(--dsw-alias-bg-layer-2))}
|
|
302
|
+
.pkc-head{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:28px}
|
|
303
|
+
.pkc-title{display:flex;align-items:center;gap:8px;margin:0;font-size:15px;font-weight:600;line-height:22px}
|
|
304
|
+
.pkc-note{margin:0;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
305
|
+
.pkc-warn{margin:0;color:var(--dsw-alias-state-warn-label,#dd8629);font-size:12px;line-height:18px}
|
|
306
|
+
.pkc-error{margin:0;color:var(--dsw-alias-state-error-primary,#ec1313);font-size:12px;line-height:18px}
|
|
307
|
+
.pkc-label{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}
|
|
308
|
+
.pkc-mono{font-family:var(--ds-font-family-code,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);font-size:12px;word-break:break-all}
|
|
309
|
+
.pkc-detail{margin:0;padding:8px 10px;border-radius:var(--dsw-radius-sm,8px);background:var(--dsw-alias-markdown-code-block);color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px;white-space:pre-wrap;word-break:break-all;max-height:160px;overflow:auto}
|
|
310
|
+
|
|
311
|
+
.pkc-btn{box-sizing:border-box;display:inline-flex;flex:none;align-items:center;justify-content:center;gap:4px;height:28px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-sm,8px);background:transparent;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;line-height:18px;white-space:nowrap;cursor:pointer}
|
|
312
|
+
.pkc-btn:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}
|
|
313
|
+
.pkc-btn:active:not(:disabled){background:var(--dsw-alias-interactive-bg-active)}
|
|
314
|
+
.pkc-btn:disabled{opacity:.4;cursor:not-allowed}
|
|
315
|
+
.pkc-btn[data-size=md]{height:32px;padding:0 14px;border-radius:var(--dsw-radius-md,12px);font-size:13px}
|
|
316
|
+
.pkc-btn[data-variant=primary]{border-color:transparent;background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground)}
|
|
317
|
+
.pkc-btn[data-variant=primary]:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}
|
|
318
|
+
.pkc-btn[data-variant=ghost]{border-color:transparent;color:var(--dsw-alias-label-secondary)}
|
|
319
|
+
.pkc-btn[data-variant=danger]{border-color:transparent;color:var(--dsw-alias-state-error-primary)}
|
|
320
|
+
.pkc-btn[data-variant=danger]:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-danger)}
|
|
321
|
+
|
|
322
|
+
.pkc-input{box-sizing:border-box;flex:1 1 200px;min-width:0;height:32px;padding:0 10px;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;font-size:13px;outline:none}
|
|
323
|
+
.pkc-input:focus{border-color:var(--dsw-alias-state-business-primary)}
|
|
324
|
+
.pkc-input::placeholder{color:var(--dsw-alias-label-caption)}
|
|
325
|
+
.pkc-input:disabled{opacity:.5}
|
|
326
|
+
.pkc-form{display:flex;flex-wrap:wrap;align-items:center;gap:8px}
|
|
327
|
+
|
|
328
|
+
.pkc-chip{flex:none;padding:0 8px;border-radius:999px;font-size:11px;font-weight:500;line-height:18px;white-space:nowrap}
|
|
329
|
+
.pkc-chip[data-tone=success]{background:var(--dsw-alias-state-success-tertiary);color:var(--dsw-alias-state-success-primary)}
|
|
330
|
+
.pkc-chip[data-tone=danger]{background:var(--dsw-alias-interactive-bg-hover-danger);color:var(--dsw-alias-state-error-primary)}
|
|
331
|
+
.pkc-chip[data-tone=info]{background:var(--dsw-alias-state-business-tertiary);color:var(--dsw-alias-state-business-primary)}
|
|
332
|
+
.pkc-chip[data-tone=neutral]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}
|
|
333
|
+
.pkc-tag{flex:none;padding:0 6px;border:.5px solid var(--dsw-alias-border-l3);border-radius:var(--dsw-radius-xs,4px);color:var(--dsw-alias-label-secondary);font-family:var(--ds-font-family-code,ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);font-size:11px;line-height:18px;cursor:default}
|
|
334
|
+
.pkc-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-idle-primary)}
|
|
335
|
+
.pkc-dot[data-tone=success]{background:var(--dsw-alias-state-success-primary)}
|
|
336
|
+
.pkc-dot[data-tone=warn]{background:var(--dsw-alias-state-warn-primary)}
|
|
337
|
+
.pkc-dot[data-tone=danger]{background:var(--dsw-alias-state-error-primary)}
|
|
338
|
+
|
|
339
|
+
.pkc-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}
|
|
340
|
+
.pkc-stat{display:flex;flex-direction:column;gap:2px;padding:10px 12px;border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-interactive-bg-hover)}
|
|
341
|
+
.pkc-stat-label{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
342
|
+
.pkc-stat-value{font-size:20px;font-weight:600;line-height:28px;font-variant-numeric:tabular-nums;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
343
|
+
|
|
344
|
+
.pkc-list{display:flex;flex-direction:column}
|
|
345
|
+
.pkc-row{display:flex;flex-direction:column;gap:8px;padding:12px 0;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
346
|
+
.pkc-row:first-child{border-top:0;padding-top:2px}
|
|
347
|
+
.pkc-row-top{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:8px 12px}
|
|
348
|
+
.pkc-row-name{display:flex;flex-wrap:wrap;align-items:center;gap:8px;min-width:0;font-weight:500}
|
|
349
|
+
.pkc-row-name>.pkc-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
350
|
+
.pkc-actions{display:flex;flex-wrap:wrap;gap:4px}
|
|
351
|
+
.pkc-meta{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px;font-variant-numeric:tabular-nums}
|
|
352
|
+
.pkc-figures{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 16px;color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}
|
|
353
|
+
.pkc-figures b{margin-left:4px;color:var(--dsw-alias-label-primary);font-size:13px;font-weight:600;font-variant-numeric:tabular-nums}
|
|
354
|
+
.pkc-bar{height:4px;overflow:hidden;border-radius:999px;background:var(--dsw-alias-interactive-bg-active)}
|
|
355
|
+
.pkc-bar>span{display:block;height:100%;border-radius:inherit;background:var(--dsw-alias-state-success-primary)}
|
|
356
|
+
.pkc-bar[data-level=warn]>span{background:var(--dsw-alias-state-warn-primary)}
|
|
357
|
+
.pkc-bar[data-level=low]>span{background:var(--dsw-alias-state-error-primary)}
|
|
358
|
+
.pkc-groups{display:flex;flex-wrap:wrap;align-items:center;gap:6px 6px}
|
|
359
|
+
.pkc-groups>.pkc-label{margin-right:2px}
|
|
360
|
+
.pkc-reach{display:flex;flex-wrap:wrap;align-items:center;gap:4px 12px;font-size:12px;line-height:18px}
|
|
361
|
+
.pkc-reach>span{display:inline-flex;align-items:center;gap:4px;cursor:default}
|
|
362
|
+
.pkc-reach>span[data-state=ok]{color:var(--dsw-alias-state-success-primary)}
|
|
363
|
+
.pkc-reach>span[data-state=partial]{color:var(--dsw-alias-state-warn-label,#dd8629)}
|
|
364
|
+
.pkc-reach>span[data-state=no]{color:var(--dsw-alias-label-caption);text-decoration:line-through}
|
|
365
|
+
.pkc-add{display:flex;flex-direction:column;gap:8px;padding-top:14px;border-top:.5px solid var(--dsw-alias-border-l2)}
|
|
366
|
+
|
|
367
|
+
.pkc-setting{display:grid;grid-template-columns:112px minmax(0,1fr);gap:6px 16px;align-items:start}
|
|
368
|
+
.pkc-setting>.pkc-label{padding-top:5px}
|
|
369
|
+
.pkc-setting>div{display:flex;flex-direction:column;gap:6px;min-width:0}
|
|
370
|
+
.pkc-seg{display:inline-flex;flex-wrap:wrap;align-self:flex-start;gap:2px;max-width:100%;padding:2px;border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-interactive-bg-hover)}
|
|
371
|
+
.pkc-seg>button{height:28px;padding:0 12px;border:0;border-radius:calc(var(--dsw-radius-md,12px) - 2px);background:transparent;color:var(--dsw-alias-label-secondary);font:inherit;font-size:12px;line-height:18px;cursor:pointer}
|
|
372
|
+
.pkc-seg>button:hover:not(:disabled):not([aria-checked=true]){color:var(--dsw-alias-label-primary)}
|
|
373
|
+
.pkc-seg>button[aria-checked=true]{background:var(--dsw-alias-button-elevated-fill);color:var(--dsw-alias-label-primary);font-weight:500;box-shadow:var(--dsw-shadow-lv1)}
|
|
374
|
+
.pkc-seg>button:disabled{cursor:not-allowed;opacity:.5}
|
|
375
|
+
.pkc-status{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;font-size:13px}
|
|
376
|
+
.pkc-status>b{font-weight:600;font-variant-numeric:tabular-nums}
|
|
377
|
+
.pkc-callout{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border-radius:var(--dsw-radius-md,12px);background:var(--dsw-alias-state-warn-tertiary)}
|
|
378
|
+
.pkc-callout[data-tone=info]{background:var(--dsw-alias-state-business-tertiary)}
|
|
379
|
+
.pkc-callout>.pkc-title{font-size:13px}
|
|
380
|
+
.pkc-split{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px}
|
|
381
|
+
|
|
382
|
+
.pkc-advanced>summary{width:fit-content;padding:2px 4px;margin-left:-4px;border-radius:var(--dsw-radius-sm,8px);color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px;cursor:pointer}
|
|
383
|
+
.pkc-advanced>summary:hover{background:var(--dsw-alias-interactive-bg-hover)}
|
|
384
|
+
.pkc-advanced[open]>summary{margin-bottom:10px}
|
|
385
|
+
.pkc-field{display:flex;flex-direction:column;gap:4px;margin-bottom:10px}
|
|
386
|
+
|
|
387
|
+
.pkc-composer{display:inline-flex;flex:0 0 auto;align-items:center;box-sizing:border-box;height:28px;color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:20px;font-variant-numeric:tabular-nums;white-space:nowrap;user-select:none}
|
|
388
|
+
|
|
389
|
+
@media (max-width:640px){
|
|
390
|
+
.pkc-stats{grid-template-columns:1fr}
|
|
391
|
+
.pkc-setting{grid-template-columns:1fr}
|
|
392
|
+
.pkc-setting>.pkc-label{padding-top:0}
|
|
393
|
+
}
|
|
323
394
|
`;
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
395
|
+
//#endregion
|
|
396
|
+
//#region src/totals.js
|
|
397
|
+
/**
|
|
398
|
+
* Totals across the keys a balance read returned.
|
|
399
|
+
*
|
|
400
|
+
* The per-key quota (`/api/usage/token/`) is what a key can spend, so the
|
|
401
|
+
* combined figure is the sum of those rows. PackyCode also reports account
|
|
402
|
+
* level billing (`/v1/dashboard/billing/*`), which is a different scope — it
|
|
403
|
+
* counts every token on the account — and is deliberately not what this sum
|
|
404
|
+
* shows.
|
|
405
|
+
*
|
|
406
|
+
* @module dsh-packycode-api/totals
|
|
407
|
+
*/
|
|
408
|
+
const amount = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
409
|
+
/**
|
|
410
|
+
* Add up the balance rows the host returned.
|
|
411
|
+
* @param rows - the `keys` array of a balance projection.
|
|
412
|
+
* @returns summed amounts plus the conditions that make a sum incomplete:
|
|
413
|
+
* keys whose read failed, and keys with no quota limit at all.
|
|
414
|
+
*/
|
|
415
|
+
function sumBalances(rows) {
|
|
416
|
+
const total = {
|
|
417
|
+
keys: 0,
|
|
418
|
+
counted: 0,
|
|
419
|
+
unread: 0,
|
|
420
|
+
unlimited: 0,
|
|
421
|
+
remainingUsd: 0,
|
|
422
|
+
usedUsd: 0,
|
|
423
|
+
limitUsd: 0
|
|
424
|
+
};
|
|
425
|
+
for (const row of Array.isArray(rows) ? rows : []) {
|
|
426
|
+
total.keys += 1;
|
|
427
|
+
const usage = row?.usage;
|
|
428
|
+
if (usage === void 0) {
|
|
429
|
+
total.unread += 1;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
total.counted += 1;
|
|
433
|
+
if (usage.unlimitedQuota === true) {
|
|
434
|
+
total.unlimited += 1;
|
|
435
|
+
continue;
|
|
436
|
+
}
|
|
437
|
+
const remaining = amount(usage.availableUsd);
|
|
438
|
+
const used = amount(usage.usedUsd);
|
|
439
|
+
const limit = amount(usage.limitUsd);
|
|
440
|
+
if (remaining !== void 0) total.remainingUsd += remaining;
|
|
441
|
+
if (used !== void 0) total.usedUsd += used;
|
|
442
|
+
if (limit !== void 0) total.limitUsd += limit;
|
|
443
|
+
}
|
|
444
|
+
return total;
|
|
445
|
+
}
|
|
446
|
+
//#endregion
|
|
447
|
+
//#region src/client.jsx
|
|
448
|
+
/**
|
|
449
|
+
* Browser half: the PackyCode settings page and the composer balance chip.
|
|
450
|
+
*
|
|
451
|
+
* Every number here comes from the host over the plugin's own loopback RPC
|
|
452
|
+
* channel; no key material crosses it, and each read is a bounded projection
|
|
453
|
+
* the host built.
|
|
454
|
+
*
|
|
455
|
+
* @module dsh-packycode-api/client
|
|
456
|
+
*/
|
|
457
|
+
const NS = "settings.packyCodeApi";
|
|
458
|
+
const PROVIDER = "packycode";
|
|
459
|
+
const CHANNEL = "/packycode-api";
|
|
460
|
+
const REFRESH_MS = 6e4;
|
|
461
|
+
const INSTALL_POLL_MS = 2e3;
|
|
462
|
+
/** How long after a search change the panel re-reads what DSH applied (the patch hot-reloads). */
|
|
463
|
+
const SEARCH_SETTLE_MS = 1500;
|
|
464
|
+
const OWN_SEARCH_IDS = {
|
|
465
|
+
auto: "packycode-api-auto",
|
|
466
|
+
packycode: "packycode-api"
|
|
327
467
|
};
|
|
468
|
+
const FOREIGN_NAMES = {
|
|
469
|
+
codex: "Codex",
|
|
470
|
+
kimi: "Kimi"
|
|
471
|
+
};
|
|
472
|
+
/** The catalog's models by family, for the "reaches" line under each key. */
|
|
473
|
+
const MODEL_FAMILIES = [
|
|
474
|
+
["DeepSeek", ["deepseek-flash"]],
|
|
475
|
+
["GPT", ["gpt-6.1-sol", "gpt-6-astra"]],
|
|
476
|
+
["Claude", [
|
|
477
|
+
"claude-opus-5-5",
|
|
478
|
+
"claude-sonnet-5-5",
|
|
479
|
+
"claude-haiku-5-5"
|
|
480
|
+
]]
|
|
481
|
+
];
|
|
482
|
+
/**
|
|
483
|
+
* The one way this bundle talks to the host. The transport drops an
|
|
484
|
+
* `undefined` payload and the channel's envelope requires the field, so every
|
|
485
|
+
* call carries an object.
|
|
486
|
+
*/
|
|
487
|
+
function channelCaller(rpc) {
|
|
488
|
+
return (endpoint, payload = {}) => rpc.call(CHANNEL, endpoint, payload).then((response) => {
|
|
489
|
+
if (!response?.ok) throw new Error(response?.error?.message ?? "PackyCode RPC failed");
|
|
490
|
+
return response.value;
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
const useCaller = (rpc) => react.useMemo(() => channelCaller(rpc), [rpc]);
|
|
494
|
+
/** Substitute `{name}` placeholders in a copy string. */
|
|
495
|
+
const fill = (text, values) => text.replace(/\{(\w+)\}/gu, (match, name) => values[name] === void 0 ? match : String(values[name]));
|
|
328
496
|
function formatUsd(value) {
|
|
329
497
|
if (typeof value !== "number" || !Number.isFinite(value)) return void 0;
|
|
330
498
|
try {
|
|
@@ -336,19 +504,6 @@ window.__ModuleLoader__.load({
|
|
|
336
504
|
return `$${value.toFixed(2)}`;
|
|
337
505
|
}
|
|
338
506
|
}
|
|
339
|
-
function resetLabel(period, t) {
|
|
340
|
-
switch (period) {
|
|
341
|
-
case "monthly": return t("resetMonthly");
|
|
342
|
-
case "weekly": return t("resetWeekly");
|
|
343
|
-
case "daily": return t("resetDaily");
|
|
344
|
-
default: return;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
/** Active key's usage row, preferring the key the ring currently points at. */
|
|
348
|
-
function activeUsage(balance) {
|
|
349
|
-
if (balance === void 0) return void 0;
|
|
350
|
-
return balance.keys?.find((entry) => entry.id === balance.activeId) ?? balance.keys?.[0];
|
|
351
|
-
}
|
|
352
507
|
/** The clock time of an epoch-millisecond reading, for a "last read at" note. */
|
|
353
508
|
function readTime(ms) {
|
|
354
509
|
if (typeof ms !== "number" || !Number.isFinite(ms)) return "—";
|
|
@@ -365,148 +520,804 @@ window.__ModuleLoader__.load({
|
|
|
365
520
|
const text = typeof error?.message === "string" && error.message.length > 0 ? error.message : String(error);
|
|
366
521
|
return text.length > 200 ? text.slice(0, 200) : text;
|
|
367
522
|
}
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
default: return;
|
|
379
|
-
}
|
|
523
|
+
const RESET_KEYS = {
|
|
524
|
+
monthly: "resetMonthly",
|
|
525
|
+
weekly: "resetWeekly",
|
|
526
|
+
daily: "resetDaily"
|
|
527
|
+
};
|
|
528
|
+
const resetLabel = (period, t) => RESET_KEYS[period] === void 0 ? void 0 : t(RESET_KEYS[period]);
|
|
529
|
+
/** Active key's usage row, preferring the key the ring currently points at. */
|
|
530
|
+
function activeUsage(balance) {
|
|
531
|
+
if (balance === void 0) return void 0;
|
|
532
|
+
return balance.keys?.find((entry) => entry.id === balance.activeId) ?? balance.keys?.[0];
|
|
380
533
|
}
|
|
381
|
-
/**
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
call("claude-cli/status").then((value) => {
|
|
403
|
-
if (live) setCli(value);
|
|
404
|
-
}).catch(() => void 0);
|
|
405
|
-
}, INSTALL_POLL_MS);
|
|
406
|
-
return () => {
|
|
407
|
-
live = false;
|
|
408
|
-
window.clearInterval(timer);
|
|
409
|
-
};
|
|
410
|
-
}, [call, running]);
|
|
411
|
-
const install = () => {
|
|
412
|
-
setError(void 0);
|
|
413
|
-
call("claude-cli/install").then(setCli).catch((failure) => setError(clearMessage(failure)));
|
|
414
|
-
};
|
|
415
|
-
const usable = cli?.installed === true && cli.version !== void 0;
|
|
416
|
-
const failed = cli?.install?.state === "failed";
|
|
534
|
+
/** How much of a key's limit is left, for its bar; undefined without a limit. */
|
|
535
|
+
function remainingShare(usage) {
|
|
536
|
+
if (usage?.unlimitedQuota === true || typeof usage?.limitUsd !== "number" || usage.limitUsd <= 0) return void 0;
|
|
537
|
+
if (typeof usage.availableUsd !== "number") return void 0;
|
|
538
|
+
return Math.max(0, Math.min(1, usage.availableUsd / usage.limitUsd));
|
|
539
|
+
}
|
|
540
|
+
/** Which catalog families a key reaches, from its per-model coverage. */
|
|
541
|
+
function reachOf(coverage) {
|
|
542
|
+
return MODEL_FAMILIES.flatMap(([name, models]) => {
|
|
543
|
+
const known = models.filter((id) => typeof coverage?.[id] === "boolean");
|
|
544
|
+
if (known.length === 0) return [];
|
|
545
|
+
const reached = known.filter((id) => coverage[id] === true);
|
|
546
|
+
return [{
|
|
547
|
+
name,
|
|
548
|
+
state: reached.length === 0 ? "no" : reached.length === known.length ? "ok" : "partial",
|
|
549
|
+
reached,
|
|
550
|
+
missing: known.filter((id) => coverage[id] === false)
|
|
551
|
+
}];
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
function Card({ title, aside, children }) {
|
|
417
555
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
418
|
-
className: "
|
|
556
|
+
className: "pkc-card",
|
|
557
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
558
|
+
className: "pkc-head",
|
|
559
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
560
|
+
className: "pkc-title",
|
|
561
|
+
children: title
|
|
562
|
+
}), aside]
|
|
563
|
+
}), children]
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
function Button({ variant, size, children, ...rest }) {
|
|
567
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
568
|
+
type: "button",
|
|
569
|
+
className: "pkc-btn",
|
|
570
|
+
"data-variant": variant,
|
|
571
|
+
"data-size": size,
|
|
572
|
+
...rest,
|
|
573
|
+
children
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function Segmented({ label, options, value, disabled, onChange }) {
|
|
577
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
578
|
+
className: "pkc-seg",
|
|
579
|
+
role: "radiogroup",
|
|
580
|
+
"aria-label": label,
|
|
581
|
+
children: options.map((option) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
582
|
+
type: "button",
|
|
583
|
+
role: "radio",
|
|
584
|
+
"aria-checked": value === option.value,
|
|
585
|
+
disabled,
|
|
586
|
+
onClick: () => value !== option.value && onChange(option.value),
|
|
587
|
+
children: option.label
|
|
588
|
+
}, option.value))
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
/** A key's groups and what they reach. */
|
|
592
|
+
function KeyGroups({ group, descriptions, loading, t }) {
|
|
593
|
+
if (group === void 0) return loading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
594
|
+
className: "pkc-meta",
|
|
595
|
+
children: t("groupsChecking")
|
|
596
|
+
}) : null;
|
|
597
|
+
if (group.rejected === true) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
598
|
+
className: "pkc-error",
|
|
599
|
+
children: t("groupsRejected")
|
|
600
|
+
});
|
|
601
|
+
const reach = reachOf(group.coverage);
|
|
602
|
+
const names = Array.isArray(group.groups) ? group.groups : [];
|
|
603
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
604
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
605
|
+
className: "pkc-groups",
|
|
606
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
607
|
+
className: "pkc-label",
|
|
608
|
+
children: t("groupsTitle")
|
|
609
|
+
}), names.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
610
|
+
className: "pkc-meta",
|
|
611
|
+
children: group.error === void 0 ? "—" : `${t("groupsUnknown")}: ${group.error}`
|
|
612
|
+
}) : names.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
613
|
+
className: "pkc-tag",
|
|
614
|
+
title: descriptions?.[name] ?? name,
|
|
615
|
+
children: name
|
|
616
|
+
}, name))]
|
|
617
|
+
}),
|
|
618
|
+
reach.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
619
|
+
className: "pkc-reach",
|
|
620
|
+
children: reach.map((family) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
621
|
+
"data-state": family.state,
|
|
622
|
+
title: [...family.reached.map((id) => `✓ ${id}`), ...family.missing.map((id) => `✗ ${id}`)].join("\n"),
|
|
623
|
+
children: [
|
|
624
|
+
family.state === "ok" ? "✓" : family.state === "partial" ? "◐" : "✕",
|
|
625
|
+
" ",
|
|
626
|
+
family.name,
|
|
627
|
+
family.state === "partial" && ` (${t("groupsPartial")})`
|
|
628
|
+
]
|
|
629
|
+
}, family.name))
|
|
630
|
+
}),
|
|
631
|
+
group.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
632
|
+
className: "pkc-meta",
|
|
633
|
+
children: t("groupsStale")
|
|
634
|
+
})
|
|
635
|
+
] });
|
|
636
|
+
}
|
|
637
|
+
function KeyRow({ entry, row, group, descriptions, groupsLoading, busy, renaming, onRename, onAction, t }) {
|
|
638
|
+
const usage = row?.usage;
|
|
639
|
+
const share = remainingShare(usage);
|
|
640
|
+
const figures = [
|
|
641
|
+
usage?.unlimitedQuota === true ? [t("keyRemaining"), t("keyUnlimited")] : usage?.availableUsd === void 0 ? void 0 : [t("keyRemaining"), formatUsd(usage.availableUsd)],
|
|
642
|
+
usage?.usedUsd === void 0 ? void 0 : [t("keyUsed"), formatUsd(usage.usedUsd)],
|
|
643
|
+
usage?.limitUsd === void 0 ? void 0 : [t("accountLimit"), formatUsd(usage.limitUsd)]
|
|
644
|
+
].filter(Boolean);
|
|
645
|
+
const meta = [usage?.name === void 0 ? t("keyNameUnknown") : `${t("keyName")} ${usage.name}`, resetLabel(usage?.resetPeriod, t)].filter(Boolean).join(" · ");
|
|
646
|
+
const editing = renaming?.id === entry.id;
|
|
647
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
648
|
+
className: "pkc-row",
|
|
419
649
|
children: [
|
|
420
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
421
|
-
className: "
|
|
422
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
423
|
-
className: "
|
|
424
|
-
children:
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
650
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
651
|
+
className: "pkc-row-top",
|
|
652
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
653
|
+
className: "pkc-row-name",
|
|
654
|
+
children: [
|
|
655
|
+
editing ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
656
|
+
className: "pkc-form",
|
|
657
|
+
onSubmit: (event) => {
|
|
658
|
+
event.preventDefault();
|
|
659
|
+
onRename.submit();
|
|
660
|
+
},
|
|
661
|
+
children: [
|
|
662
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
663
|
+
className: "pkc-input",
|
|
664
|
+
type: "text",
|
|
665
|
+
maxLength: 60,
|
|
666
|
+
autoFocus: true,
|
|
667
|
+
placeholder: t("keyRenamePlaceholder"),
|
|
668
|
+
value: renaming.label,
|
|
669
|
+
onChange: (event) => onRename.change(event.target.value),
|
|
670
|
+
onKeyDown: (event) => event.key === "Escape" && onRename.cancel()
|
|
671
|
+
}),
|
|
672
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
673
|
+
type: "submit",
|
|
674
|
+
variant: "primary",
|
|
675
|
+
disabled: busy || renaming.label.trim() === "",
|
|
676
|
+
children: t("keyRenameSave")
|
|
677
|
+
}),
|
|
678
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
679
|
+
variant: "ghost",
|
|
680
|
+
onClick: onRename.cancel,
|
|
681
|
+
children: t("keyRenameCancel")
|
|
682
|
+
})
|
|
683
|
+
]
|
|
684
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
685
|
+
className: "pkc-name",
|
|
686
|
+
children: entry.label
|
|
687
|
+
}),
|
|
688
|
+
entry.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
689
|
+
className: "pkc-chip",
|
|
690
|
+
"data-tone": "success",
|
|
691
|
+
children: t("keyActive")
|
|
692
|
+
}),
|
|
693
|
+
entry.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
694
|
+
className: "pkc-chip",
|
|
695
|
+
"data-tone": "danger",
|
|
696
|
+
children: entry.disabledReason ? `${t("keyDisabled")} · ${entry.disabledReason}` : t("keyDisabled")
|
|
697
|
+
})
|
|
698
|
+
]
|
|
699
|
+
}), !editing && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
700
|
+
className: "pkc-actions",
|
|
701
|
+
children: [
|
|
702
|
+
!entry.active && !entry.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
703
|
+
disabled: busy,
|
|
704
|
+
onClick: () => onAction("keys/select", { id: entry.id }),
|
|
705
|
+
children: t("keyUse")
|
|
706
|
+
}),
|
|
707
|
+
entry.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
708
|
+
disabled: busy,
|
|
709
|
+
onClick: () => onAction("keys/enable", { id: entry.id }),
|
|
710
|
+
children: t("keyEnable")
|
|
711
|
+
}),
|
|
712
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
713
|
+
variant: "ghost",
|
|
714
|
+
disabled: busy,
|
|
715
|
+
onClick: () => onRename.start(entry),
|
|
716
|
+
children: t("keyRename")
|
|
717
|
+
}),
|
|
718
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
719
|
+
variant: "danger",
|
|
720
|
+
disabled: busy,
|
|
721
|
+
onClick: () => onAction("keys/remove", { id: entry.id }),
|
|
722
|
+
children: t("keyRemove")
|
|
723
|
+
})
|
|
724
|
+
]
|
|
431
725
|
})]
|
|
432
726
|
}),
|
|
433
|
-
|
|
434
|
-
className: "
|
|
435
|
-
children:
|
|
727
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
728
|
+
className: "pkc-meta",
|
|
729
|
+
children: meta
|
|
436
730
|
}),
|
|
437
|
-
|
|
731
|
+
figures.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
732
|
+
className: "pkc-figures",
|
|
733
|
+
children: figures.map(([label, value]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [label, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: value })] }, label))
|
|
734
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
735
|
+
className: "pkc-meta",
|
|
736
|
+
children: row?.error === void 0 ? t("keyNoBalance") : `${t("loadFailed")}: ${row.error}`
|
|
737
|
+
}),
|
|
738
|
+
share !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
739
|
+
className: "pkc-bar",
|
|
740
|
+
"data-level": share < .1 ? "low" : share < .3 ? "warn" : "ok",
|
|
741
|
+
role: "presentation",
|
|
742
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { width: `${Math.round(share * 100)}%` } })
|
|
743
|
+
}),
|
|
744
|
+
row?.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
745
|
+
className: "pkc-warn",
|
|
746
|
+
children: fill(t("staleNote"), {
|
|
747
|
+
time: readTime(row.stale.fetchedAt),
|
|
748
|
+
error: row.stale.error ?? ""
|
|
749
|
+
})
|
|
750
|
+
}),
|
|
751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyGroups, {
|
|
752
|
+
group,
|
|
753
|
+
descriptions,
|
|
754
|
+
loading: groupsLoading,
|
|
755
|
+
t
|
|
756
|
+
})
|
|
757
|
+
]
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
function AccountsCard({ call, t }) {
|
|
761
|
+
const [status, setStatus] = react.useState();
|
|
762
|
+
const [balance, setBalance] = react.useState();
|
|
763
|
+
const [groups, setGroups] = react.useState();
|
|
764
|
+
const [groupsLoading, setGroupsLoading] = react.useState(true);
|
|
765
|
+
const [busy, setBusy] = react.useState(false);
|
|
766
|
+
const [error, setError] = react.useState();
|
|
767
|
+
const [draft, setDraft] = react.useState({
|
|
768
|
+
key: "",
|
|
769
|
+
label: ""
|
|
770
|
+
});
|
|
771
|
+
const [renaming, setRenaming] = react.useState();
|
|
772
|
+
const generation = react.useRef(0);
|
|
773
|
+
const readGroups = react.useCallback((force = false) => {
|
|
774
|
+
setGroupsLoading(true);
|
|
775
|
+
return call("groups", { force }).then(setGroups).catch(() => void 0).finally(() => setGroupsLoading(false));
|
|
776
|
+
}, [call]);
|
|
777
|
+
const load = react.useCallback((force = false) => {
|
|
778
|
+
const current = ++generation.current;
|
|
779
|
+
setBusy(true);
|
|
780
|
+
setError(void 0);
|
|
781
|
+
readGroups(force);
|
|
782
|
+
return Promise.all([call("status"), call("balance", { force })]).then(([nextStatus, nextBalance]) => {
|
|
783
|
+
if (generation.current !== current) return;
|
|
784
|
+
setStatus(nextStatus);
|
|
785
|
+
setBalance(nextBalance);
|
|
786
|
+
}).catch((failure) => {
|
|
787
|
+
if (generation.current === current) setError(clearMessage(failure));
|
|
788
|
+
}).finally(() => {
|
|
789
|
+
if (generation.current === current) setBusy(false);
|
|
790
|
+
});
|
|
791
|
+
}, [call, readGroups]);
|
|
792
|
+
react.useEffect(() => {
|
|
793
|
+
load(false);
|
|
794
|
+
return () => {
|
|
795
|
+
generation.current += 1;
|
|
796
|
+
};
|
|
797
|
+
}, [load]);
|
|
798
|
+
const mutate = (endpoint, payload) => {
|
|
799
|
+
setBusy(true);
|
|
800
|
+
setError(void 0);
|
|
801
|
+
return call(endpoint, payload).then((nextStatus) => {
|
|
802
|
+
setStatus(nextStatus);
|
|
803
|
+
readGroups(false);
|
|
804
|
+
return call("balance", { force: true });
|
|
805
|
+
}).then(setBalance).catch((failure) => setError(clearMessage(failure))).finally(() => setBusy(false));
|
|
806
|
+
};
|
|
807
|
+
const addKey = (event) => {
|
|
808
|
+
event.preventDefault();
|
|
809
|
+
if (draft.key.trim() === "") return;
|
|
810
|
+
mutate("keys/add", {
|
|
811
|
+
key: draft.key,
|
|
812
|
+
label: draft.label
|
|
813
|
+
}).then(() => setDraft({
|
|
814
|
+
key: "",
|
|
815
|
+
label: ""
|
|
816
|
+
}));
|
|
817
|
+
};
|
|
818
|
+
const rename = {
|
|
819
|
+
start: (entry) => setRenaming({
|
|
820
|
+
id: entry.id,
|
|
821
|
+
label: entry.label
|
|
822
|
+
}),
|
|
823
|
+
change: (label) => setRenaming((current) => ({
|
|
824
|
+
...current,
|
|
825
|
+
label
|
|
826
|
+
})),
|
|
827
|
+
cancel: () => setRenaming(void 0),
|
|
828
|
+
submit: () => {
|
|
829
|
+
if (renaming === void 0 || renaming.label.trim() === "") return;
|
|
830
|
+
mutate("keys/rename", {
|
|
831
|
+
id: renaming.id,
|
|
832
|
+
label: renaming.label
|
|
833
|
+
}).then(() => setRenaming(void 0));
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
const keys = status?.keys ?? [];
|
|
837
|
+
const rows = balance?.keys ?? [];
|
|
838
|
+
const totals = sumBalances(rows);
|
|
839
|
+
const staleCount = rows.filter((row) => row.stale !== void 0).length;
|
|
840
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
|
|
841
|
+
title: t("accountsTitle"),
|
|
842
|
+
aside: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
843
|
+
className: "pkc-actions",
|
|
844
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
845
|
+
disabled: busy,
|
|
846
|
+
onClick: () => load(true),
|
|
847
|
+
children: busy ? t("refreshing") : t("refresh")
|
|
848
|
+
}), keys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
849
|
+
variant: "danger",
|
|
850
|
+
disabled: busy,
|
|
851
|
+
onClick: () => mutate("keys/remove-all", {}),
|
|
852
|
+
children: t("removeAll")
|
|
853
|
+
})]
|
|
854
|
+
}),
|
|
855
|
+
children: [
|
|
856
|
+
keys.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
857
|
+
className: "pkc-note",
|
|
858
|
+
children: t("empty")
|
|
859
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
860
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
861
|
+
className: "pkc-stats",
|
|
862
|
+
children: [
|
|
863
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
864
|
+
className: "pkc-stat",
|
|
865
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
866
|
+
className: "pkc-stat-label",
|
|
867
|
+
children: t("totalRemaining")
|
|
868
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
869
|
+
className: "pkc-stat-value",
|
|
870
|
+
children: [totals.counted === 0 ? "—" : formatUsd(totals.remainingUsd) ?? "—", totals.unlimited > 0 && ` + ${t("keyUnlimited")}`]
|
|
871
|
+
})]
|
|
872
|
+
}),
|
|
873
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
874
|
+
className: "pkc-stat",
|
|
875
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
876
|
+
className: "pkc-stat-label",
|
|
877
|
+
children: t("totalUsed")
|
|
878
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
879
|
+
className: "pkc-stat-value",
|
|
880
|
+
children: totals.counted === 0 ? "—" : formatUsd(totals.usedUsd) ?? "—"
|
|
881
|
+
})]
|
|
882
|
+
}),
|
|
883
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
884
|
+
className: "pkc-stat",
|
|
885
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
886
|
+
className: "pkc-stat-label",
|
|
887
|
+
children: t("totalLimit")
|
|
888
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
889
|
+
className: "pkc-stat-value",
|
|
890
|
+
children: totals.counted === 0 ? "—" : totals.unlimited > 0 ? t("keyUnlimited") : formatUsd(totals.limitUsd) ?? "—"
|
|
891
|
+
})]
|
|
892
|
+
})
|
|
893
|
+
]
|
|
894
|
+
}),
|
|
895
|
+
(totals.unread > 0 || staleCount > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
896
|
+
className: "pkc-note",
|
|
897
|
+
children: [totals.unread > 0 ? fill(t("totalPartial"), { count: totals.unread }) : void 0, staleCount > 0 ? fill(t("totalStale"), { count: staleCount }) : void 0].filter(Boolean).join(" ")
|
|
898
|
+
}),
|
|
438
899
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
439
|
-
className: "
|
|
440
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
441
|
-
|
|
900
|
+
className: "pkc-list",
|
|
901
|
+
children: keys.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyRow, {
|
|
902
|
+
entry,
|
|
903
|
+
row: rows.find((row) => row.id === entry.id),
|
|
904
|
+
group: groups?.keys?.find((group) => group.id === entry.id),
|
|
905
|
+
descriptions: groups?.descriptions,
|
|
906
|
+
groupsLoading,
|
|
907
|
+
busy,
|
|
908
|
+
renaming,
|
|
909
|
+
onRename: rename,
|
|
910
|
+
onAction: mutate,
|
|
911
|
+
t
|
|
912
|
+
}, entry.id))
|
|
913
|
+
}),
|
|
914
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
915
|
+
className: "pkc-note",
|
|
916
|
+
children: t("keysHint")
|
|
917
|
+
})
|
|
918
|
+
] }),
|
|
919
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
920
|
+
className: "pkc-add",
|
|
921
|
+
onSubmit: addKey,
|
|
922
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
923
|
+
className: "pkc-label",
|
|
924
|
+
children: t("addTitle")
|
|
925
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
926
|
+
className: "pkc-form",
|
|
927
|
+
children: [
|
|
928
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
929
|
+
className: "pkc-input",
|
|
930
|
+
type: "password",
|
|
931
|
+
autoComplete: "off",
|
|
932
|
+
placeholder: t("addKeyPlaceholder"),
|
|
933
|
+
value: draft.key,
|
|
934
|
+
onChange: (event) => setDraft((current) => ({
|
|
935
|
+
...current,
|
|
936
|
+
key: event.target.value
|
|
937
|
+
}))
|
|
938
|
+
}),
|
|
939
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
940
|
+
className: "pkc-input",
|
|
941
|
+
type: "text",
|
|
942
|
+
placeholder: t("addLabelPlaceholder"),
|
|
943
|
+
value: draft.label,
|
|
944
|
+
onChange: (event) => setDraft((current) => ({
|
|
945
|
+
...current,
|
|
946
|
+
label: event.target.value
|
|
947
|
+
}))
|
|
948
|
+
}),
|
|
949
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
950
|
+
type: "submit",
|
|
951
|
+
variant: "primary",
|
|
952
|
+
size: "md",
|
|
953
|
+
disabled: busy || draft.key.trim() === "",
|
|
954
|
+
children: t("add")
|
|
955
|
+
})
|
|
956
|
+
]
|
|
957
|
+
})]
|
|
958
|
+
}),
|
|
959
|
+
status !== void 0 && status.writable !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
960
|
+
className: "pkc-note",
|
|
961
|
+
children: t("notWritable")
|
|
962
|
+
}),
|
|
963
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
964
|
+
className: "pkc-error",
|
|
965
|
+
role: "alert",
|
|
966
|
+
children: `${t("error")}: ${error}`
|
|
967
|
+
}),
|
|
968
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
969
|
+
className: "pkc-advanced",
|
|
970
|
+
children: [
|
|
971
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advanced") }),
|
|
972
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
973
|
+
className: "pkc-field",
|
|
442
974
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
443
|
-
className: "
|
|
444
|
-
children: t("
|
|
445
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
446
|
-
className: "
|
|
447
|
-
|
|
975
|
+
className: "pkc-label",
|
|
976
|
+
children: t("apiHost")
|
|
977
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
978
|
+
className: "pkc-input",
|
|
979
|
+
type: "text",
|
|
980
|
+
disabled: status?.writable !== true,
|
|
981
|
+
defaultValue: status?.hosts?.apiHost ?? "",
|
|
982
|
+
onBlur: (event) => mutate("hosts/update", {
|
|
983
|
+
apiHost: event.target.value,
|
|
984
|
+
statusHost: status?.hosts?.statusHost
|
|
985
|
+
})
|
|
986
|
+
})]
|
|
987
|
+
}),
|
|
988
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
989
|
+
className: "pkc-field",
|
|
990
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
991
|
+
className: "pkc-label",
|
|
992
|
+
children: t("statusHost")
|
|
993
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
994
|
+
className: "pkc-input",
|
|
995
|
+
type: "text",
|
|
996
|
+
disabled: status?.writable !== true,
|
|
997
|
+
defaultValue: status?.hosts?.statusHost ?? "",
|
|
998
|
+
onBlur: (event) => mutate("hosts/update", {
|
|
999
|
+
statusHost: event.target.value,
|
|
1000
|
+
apiHost: status?.hosts?.apiHost
|
|
1001
|
+
})
|
|
448
1002
|
})]
|
|
449
1003
|
})
|
|
1004
|
+
]
|
|
1005
|
+
})
|
|
1006
|
+
]
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
/** Web search routing and backend, written through the host. */
|
|
1010
|
+
function SearchCard({ call, t }) {
|
|
1011
|
+
const [state, setState] = react.useState();
|
|
1012
|
+
const [busy, setBusy] = react.useState(false);
|
|
1013
|
+
const [error, setError] = react.useState();
|
|
1014
|
+
const generation = react.useRef(0);
|
|
1015
|
+
const read = react.useCallback(() => {
|
|
1016
|
+
const current = ++generation.current;
|
|
1017
|
+
return call("search/status").then((value) => {
|
|
1018
|
+
if (generation.current === current) setState(value);
|
|
1019
|
+
}).catch((failure) => {
|
|
1020
|
+
if (generation.current === current) setError(clearMessage(failure));
|
|
1021
|
+
});
|
|
1022
|
+
}, [call]);
|
|
1023
|
+
react.useEffect(() => {
|
|
1024
|
+
read();
|
|
1025
|
+
return () => {
|
|
1026
|
+
generation.current += 1;
|
|
1027
|
+
};
|
|
1028
|
+
}, [read]);
|
|
1029
|
+
const update = (patch) => {
|
|
1030
|
+
if (busy || state?.writable !== true) return;
|
|
1031
|
+
setBusy(true);
|
|
1032
|
+
setError(void 0);
|
|
1033
|
+
call("search/update", patch).then((value) => {
|
|
1034
|
+
setState(value);
|
|
1035
|
+
window.setTimeout(() => {
|
|
1036
|
+
read();
|
|
1037
|
+
}, SEARCH_SETTLE_MS);
|
|
1038
|
+
}).catch((failure) => setError(clearMessage(failure))).finally(() => setBusy(false));
|
|
1039
|
+
};
|
|
1040
|
+
const mode = state?.searchProvider;
|
|
1041
|
+
const backend = state?.searchBackend;
|
|
1042
|
+
const models = state?.models ?? {};
|
|
1043
|
+
const base = state?.effective?.base;
|
|
1044
|
+
const runtime = state?.effective?.runtime;
|
|
1045
|
+
const foreign = Object.entries(state?.foreign ?? {}).filter(([, present]) => present).map(([name]) => FOREIGN_NAMES[name] ?? name);
|
|
1046
|
+
const expected = OWN_SEARCH_IDS[mode];
|
|
1047
|
+
const overridden = expected !== void 0 && state?.applied?.state === "applied" && base !== void 0 && base !== expected;
|
|
1048
|
+
const disabled = busy || state?.writable !== true;
|
|
1049
|
+
const modeHint = {
|
|
1050
|
+
default: "searchModeDefaultHint",
|
|
1051
|
+
auto: "searchModeAutoHint",
|
|
1052
|
+
packycode: "searchModePackycodeHint"
|
|
1053
|
+
}[mode];
|
|
1054
|
+
const backendHint = backend === "gpt" ? fill(t("searchBackendGptHint"), { model: models.gpt ?? "gpt-6.1-sol" }) : fill(t("searchBackendDeepseekHint"), { model: models.deepseek ?? "deepseek-flash" });
|
|
1055
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
|
|
1056
|
+
title: t("searchTitle"),
|
|
1057
|
+
aside: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1058
|
+
className: "pkc-chip",
|
|
1059
|
+
"data-tone": "neutral",
|
|
1060
|
+
children: t("searchScope")
|
|
1061
|
+
}),
|
|
1062
|
+
children: [
|
|
1063
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1064
|
+
className: "pkc-note",
|
|
1065
|
+
children: t("searchIntro")
|
|
1066
|
+
}),
|
|
1067
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1068
|
+
className: "pkc-note",
|
|
1069
|
+
children: t("searchCliHint")
|
|
1070
|
+
}),
|
|
1071
|
+
state === void 0 && error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1072
|
+
className: "pkc-note",
|
|
1073
|
+
role: "status",
|
|
1074
|
+
children: t("searchChecking")
|
|
1075
|
+
}),
|
|
1076
|
+
state !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1077
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1078
|
+
className: "pkc-setting",
|
|
1079
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1080
|
+
className: "pkc-label",
|
|
1081
|
+
children: t("searchModeTitle")
|
|
1082
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segmented, {
|
|
1083
|
+
label: t("searchModeTitle"),
|
|
1084
|
+
value: mode,
|
|
1085
|
+
disabled,
|
|
1086
|
+
onChange: (value) => update({ searchProvider: value }),
|
|
1087
|
+
options: [
|
|
1088
|
+
{
|
|
1089
|
+
value: "default",
|
|
1090
|
+
label: t("searchModeDefault")
|
|
1091
|
+
},
|
|
1092
|
+
{
|
|
1093
|
+
value: "auto",
|
|
1094
|
+
label: t("searchModeAuto")
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
value: "packycode",
|
|
1098
|
+
label: t("searchModePackycode")
|
|
1099
|
+
}
|
|
1100
|
+
]
|
|
1101
|
+
}), modeHint !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1102
|
+
className: "pkc-note",
|
|
1103
|
+
children: t(modeHint)
|
|
1104
|
+
})] })]
|
|
450
1105
|
}),
|
|
451
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
452
|
-
className: "
|
|
453
|
-
children: [
|
|
454
|
-
className: "
|
|
455
|
-
children:
|
|
1106
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1107
|
+
className: "pkc-setting",
|
|
1108
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1109
|
+
className: "pkc-label",
|
|
1110
|
+
children: t("searchBackendTitle")
|
|
1111
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Segmented, {
|
|
1112
|
+
label: t("searchBackendTitle"),
|
|
1113
|
+
value: backend,
|
|
1114
|
+
disabled,
|
|
1115
|
+
onChange: (value) => update({ searchBackend: value }),
|
|
1116
|
+
options: [{
|
|
1117
|
+
value: "deepseek",
|
|
1118
|
+
label: t("searchBackendDeepseek")
|
|
1119
|
+
}, {
|
|
1120
|
+
value: "gpt",
|
|
1121
|
+
label: t("searchBackendGpt")
|
|
1122
|
+
}]
|
|
1123
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1124
|
+
className: "pkc-note",
|
|
1125
|
+
children: backendHint
|
|
1126
|
+
})] })]
|
|
1127
|
+
}),
|
|
1128
|
+
state.available === true && (base !== void 0 || runtime !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1129
|
+
className: "pkc-status",
|
|
1130
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1131
|
+
className: "pkc-dot",
|
|
1132
|
+
"data-tone": overridden ? "warn" : expected !== void 0 && base === expected ? "success" : void 0
|
|
1133
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1134
|
+
className: "pkc-meta",
|
|
1135
|
+
children: fill(t("searchEffective"), {
|
|
1136
|
+
base: base ?? t("searchUnknown"),
|
|
1137
|
+
runtime: runtime ?? base ?? t("searchUnknown")
|
|
1138
|
+
})
|
|
456
1139
|
})]
|
|
457
1140
|
}),
|
|
458
|
-
|
|
459
|
-
className: "
|
|
460
|
-
children: t("
|
|
461
|
-
})
|
|
462
|
-
] }),
|
|
463
|
-
cli !== void 0 && !usable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
464
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
465
|
-
className: "packyCodeWarning",
|
|
466
|
-
children: cli.installed ? t("cliBroken") : t("cliMissing")
|
|
1141
|
+
overridden && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1142
|
+
className: "pkc-warn",
|
|
1143
|
+
children: fill(t("searchOverridden"), { base })
|
|
467
1144
|
}),
|
|
468
|
-
|
|
469
|
-
className: "
|
|
470
|
-
children:
|
|
471
|
-
className: "packyCodeMono",
|
|
472
|
-
children: cli.path
|
|
473
|
-
})]
|
|
474
|
-
}), cli.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
475
|
-
className: "packyCodeDetail",
|
|
476
|
-
children: cli.error
|
|
477
|
-
})] }),
|
|
478
|
-
cli.configuredPath !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
479
|
-
className: "packyCodeNote",
|
|
480
|
-
children: t("cliConfiguredMissing").replace("{path}", cli.configuredPath)
|
|
1145
|
+
foreign.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1146
|
+
className: "pkc-note",
|
|
1147
|
+
children: fill(t("searchForeign"), { names: foreign.join(" / ") })
|
|
481
1148
|
}),
|
|
482
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
483
|
-
className: "
|
|
484
|
-
|
|
1149
|
+
state.applied?.state === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1150
|
+
className: "pkc-error",
|
|
1151
|
+
role: "alert",
|
|
1152
|
+
children: fill(t("searchApplyFailed"), { error: state.applied.error ?? "" })
|
|
485
1153
|
}),
|
|
486
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
487
|
-
className: "
|
|
488
|
-
children:
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
1154
|
+
state.available !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1155
|
+
className: "pkc-note",
|
|
1156
|
+
children: t("searchUnavailable")
|
|
1157
|
+
}),
|
|
1158
|
+
state.writable !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1159
|
+
className: "pkc-note",
|
|
1160
|
+
children: t("searchNotWritable")
|
|
1161
|
+
}),
|
|
1162
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1163
|
+
className: "pkc-note",
|
|
1164
|
+
children: t("searchPatchNote")
|
|
1165
|
+
})
|
|
1166
|
+
] }),
|
|
1167
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1168
|
+
className: "pkc-error",
|
|
1169
|
+
role: "alert",
|
|
1170
|
+
children: `${t("searchLoadFailed")}: ${error}`
|
|
1171
|
+
})
|
|
1172
|
+
]
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
const INSTALL_FAILURES = {
|
|
1176
|
+
"npm-missing": () => "cliNpmMissing",
|
|
1177
|
+
permission: (cli) => cli.platform === "win32" ? "cliPermissionWindows" : "cliPermission",
|
|
1178
|
+
network: () => "cliNetwork",
|
|
1179
|
+
timeout: () => "cliTimeout",
|
|
1180
|
+
"not-found-after-install": () => "cliNotFoundAfterInstall"
|
|
1181
|
+
};
|
|
1182
|
+
/** The sentence explaining a failed install, by the code the host classified. */
|
|
1183
|
+
function installFailure(cli, t) {
|
|
1184
|
+
const key = INSTALL_FAILURES[cli.install?.code]?.(cli);
|
|
1185
|
+
return key === void 0 ? void 0 : fill(t(key), { command: cli.command ?? "" });
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Whether this device has the Claude Code CLI, which version, and — when it
|
|
1189
|
+
* has none — a button that installs it with npm.
|
|
1190
|
+
*/
|
|
1191
|
+
function ClaudeCliCard({ call, t }) {
|
|
1192
|
+
const [cli, setCli] = react.useState();
|
|
1193
|
+
const [checking, setChecking] = react.useState(false);
|
|
1194
|
+
const [error, setError] = react.useState();
|
|
1195
|
+
const check = react.useCallback((force = false) => {
|
|
1196
|
+
setChecking(true);
|
|
1197
|
+
setError(void 0);
|
|
1198
|
+
return call("claude-cli/status", { force }).then(setCli).catch((failure) => setError(clearMessage(failure))).finally(() => setChecking(false));
|
|
1199
|
+
}, [call]);
|
|
1200
|
+
react.useEffect(() => {
|
|
1201
|
+
check(false);
|
|
1202
|
+
}, [check]);
|
|
1203
|
+
const running = cli?.install?.state === "running";
|
|
1204
|
+
react.useEffect(() => {
|
|
1205
|
+
if (!running) return void 0;
|
|
1206
|
+
let live = true;
|
|
1207
|
+
const timer = window.setInterval(() => {
|
|
1208
|
+
call("claude-cli/status").then((value) => {
|
|
1209
|
+
if (live) setCli(value);
|
|
1210
|
+
}).catch(() => void 0);
|
|
1211
|
+
}, INSTALL_POLL_MS);
|
|
1212
|
+
return () => {
|
|
1213
|
+
live = false;
|
|
1214
|
+
window.clearInterval(timer);
|
|
1215
|
+
};
|
|
1216
|
+
}, [call, running]);
|
|
1217
|
+
const install = () => {
|
|
1218
|
+
setError(void 0);
|
|
1219
|
+
call("claude-cli/install").then(setCli).catch((failure) => setError(clearMessage(failure)));
|
|
1220
|
+
};
|
|
1221
|
+
const usable = cli?.installed === true && cli.version !== void 0;
|
|
1222
|
+
const failed = cli?.install?.state === "failed";
|
|
1223
|
+
const failure = cli === void 0 ? void 0 : installFailure(cli, t);
|
|
1224
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
|
|
1225
|
+
title: t("cliTitle"),
|
|
1226
|
+
aside: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1227
|
+
disabled: checking || running,
|
|
1228
|
+
onClick: () => check(true),
|
|
1229
|
+
children: checking ? t("refreshing") : t("cliRecheck")
|
|
1230
|
+
}),
|
|
1231
|
+
children: [
|
|
1232
|
+
cli === void 0 && error === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1233
|
+
className: "pkc-note",
|
|
1234
|
+
children: t("cliChecking")
|
|
1235
|
+
}),
|
|
1236
|
+
usable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1237
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1238
|
+
className: "pkc-status",
|
|
1239
|
+
children: [
|
|
1240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1241
|
+
className: "pkc-dot",
|
|
1242
|
+
"data-tone": "success"
|
|
1243
|
+
}),
|
|
1244
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("cliInstalled") }),
|
|
1245
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `${t("cliVersion")} ${cli.version}` })
|
|
1246
|
+
]
|
|
495
1247
|
}),
|
|
496
1248
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
497
|
-
className: "
|
|
498
|
-
children: [
|
|
1249
|
+
className: "pkc-meta",
|
|
1250
|
+
children: [
|
|
1251
|
+
t("cliPath"),
|
|
1252
|
+
" ",
|
|
1253
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1254
|
+
className: "pkc-mono",
|
|
1255
|
+
children: cli.path
|
|
1256
|
+
})
|
|
1257
|
+
]
|
|
1258
|
+
}),
|
|
1259
|
+
cli.install?.state === "succeeded" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1260
|
+
className: "pkc-note",
|
|
1261
|
+
children: t("cliInstallSucceeded")
|
|
499
1262
|
})
|
|
500
1263
|
] }),
|
|
501
|
-
|
|
502
|
-
className: "
|
|
503
|
-
children:
|
|
1264
|
+
cli !== void 0 && !usable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1265
|
+
className: "pkc-callout",
|
|
1266
|
+
children: [
|
|
1267
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1268
|
+
className: "pkc-status",
|
|
1269
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1270
|
+
className: "pkc-dot",
|
|
1271
|
+
"data-tone": cli.installed ? "danger" : "warn"
|
|
1272
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: cli.installed ? t("cliBroken") : t("cliMissing") })]
|
|
1273
|
+
}),
|
|
1274
|
+
cli.installed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1275
|
+
className: "pkc-meta",
|
|
1276
|
+
children: [
|
|
1277
|
+
t("cliPath"),
|
|
1278
|
+
" ",
|
|
1279
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1280
|
+
className: "pkc-mono",
|
|
1281
|
+
children: cli.path
|
|
1282
|
+
})
|
|
1283
|
+
]
|
|
1284
|
+
}), cli.error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
1285
|
+
className: "pkc-detail",
|
|
1286
|
+
children: cli.error
|
|
1287
|
+
})] }),
|
|
1288
|
+
cli.configuredPath !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1289
|
+
className: "pkc-note",
|
|
1290
|
+
children: fill(t("cliConfiguredMissing"), { path: cli.configuredPath })
|
|
1291
|
+
}),
|
|
1292
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1293
|
+
className: "pkc-note",
|
|
1294
|
+
children: t("cliMissingHint")
|
|
1295
|
+
}),
|
|
1296
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1297
|
+
className: "pkc-split",
|
|
1298
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1299
|
+
variant: "primary",
|
|
1300
|
+
size: "md",
|
|
1301
|
+
disabled: running || checking,
|
|
1302
|
+
onClick: install,
|
|
1303
|
+
children: running ? t("cliInstalling") : cli.installed ? t("cliReinstall") : t("cliInstall")
|
|
1304
|
+
})
|
|
1305
|
+
}),
|
|
1306
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1307
|
+
className: "pkc-note",
|
|
1308
|
+
children: [fill(t("cliInstallCommand"), { command: cli.command ?? "" }), cli.platform === "win32" && ` ${t("cliWindowsNote")}`]
|
|
1309
|
+
})
|
|
1310
|
+
]
|
|
1311
|
+
}),
|
|
1312
|
+
failed && !usable && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1313
|
+
className: "pkc-error",
|
|
1314
|
+
children: failure === void 0 ? t("cliInstallFailed") : `${t("cliInstallFailed")} · ${failure}`
|
|
504
1315
|
}), cli.install.detail !== void 0 && cli.install.detail !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("pre", {
|
|
505
|
-
className: "
|
|
1316
|
+
className: "pkc-detail",
|
|
506
1317
|
children: cli.install.detail
|
|
507
1318
|
})] }),
|
|
508
|
-
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
509
|
-
className: "
|
|
1319
|
+
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1320
|
+
className: "pkc-error",
|
|
510
1321
|
children: `${t("error")}: ${error}`
|
|
511
1322
|
})
|
|
512
1323
|
]
|
|
@@ -549,109 +1360,92 @@ window.__ModuleLoader__.load({
|
|
|
549
1360
|
};
|
|
550
1361
|
const installKind = info?.install?.kind;
|
|
551
1362
|
const showStatus = info !== void 0 && updated === void 0;
|
|
552
|
-
|
|
553
|
-
|
|
1363
|
+
const canUpdate = showStatus && info.updateAvailable && installKind === "npm";
|
|
1364
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(Card, {
|
|
1365
|
+
title: t("versionTitle"),
|
|
1366
|
+
aside: updated === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1367
|
+
className: "pkc-actions",
|
|
1368
|
+
children: [canUpdate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1369
|
+
variant: "primary",
|
|
1370
|
+
disabled: updating,
|
|
1371
|
+
onClick: update,
|
|
1372
|
+
children: updating ? t("versionUpdating") : t("versionUpdate")
|
|
1373
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1374
|
+
disabled: loading || updating,
|
|
1375
|
+
onClick: () => load(true),
|
|
1376
|
+
children: loading ? t("versionChecking") : t("versionCheck")
|
|
1377
|
+
})]
|
|
1378
|
+
}),
|
|
554
1379
|
children: [
|
|
555
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
556
|
-
className: "
|
|
557
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
558
|
-
className: "packyCodeCardTitle",
|
|
559
|
-
children: t("versionTitle")
|
|
560
|
-
}), updated === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
561
|
-
type: "button",
|
|
562
|
-
className: "packyCodeButton",
|
|
563
|
-
disabled: loading || updating,
|
|
564
|
-
onClick: () => load(true),
|
|
565
|
-
children: loading ? t("versionChecking") : t("versionCheck")
|
|
566
|
-
})]
|
|
567
|
-
}),
|
|
568
|
-
loading && info === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
569
|
-
className: "packyCodeNote",
|
|
1380
|
+
loading && info === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1381
|
+
className: "pkc-note",
|
|
570
1382
|
role: "status",
|
|
571
1383
|
children: t("versionChecking")
|
|
572
1384
|
}),
|
|
573
|
-
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
574
|
-
className: "
|
|
1385
|
+
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1386
|
+
className: "pkc-error",
|
|
575
1387
|
role: "alert",
|
|
576
1388
|
children: t("versionFailed")
|
|
577
1389
|
}),
|
|
578
|
-
|
|
579
|
-
className: "
|
|
580
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
581
|
-
className: "packyCodeVersionRow",
|
|
582
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("versionCurrent") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
583
|
-
className: "packyCodeVersionValue",
|
|
584
|
-
children: ["v", info.current]
|
|
585
|
-
})]
|
|
586
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
587
|
-
className: "packyCodeVersionRow",
|
|
588
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("versionLatest") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
589
|
-
className: "packyCodeVersionValue",
|
|
590
|
-
children: ["v", info.latest]
|
|
591
|
-
})]
|
|
592
|
-
})]
|
|
593
|
-
}),
|
|
594
|
-
showStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
595
|
-
className: "packyCodeNote",
|
|
1390
|
+
showStatus && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1391
|
+
className: "pkc-status",
|
|
596
1392
|
role: "status",
|
|
597
|
-
children:
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
1393
|
+
children: [
|
|
1394
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1395
|
+
className: "pkc-dot",
|
|
1396
|
+
"data-tone": info.updateAvailable ? "warn" : "success"
|
|
1397
|
+
}),
|
|
1398
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", { children: `v${info.current}` }),
|
|
1399
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1400
|
+
className: "pkc-meta",
|
|
1401
|
+
children: [info.updateAvailable ? fill(t("versionAvailable"), { version: info.latest }) : t("versionUpToDate"), info.updateAvailable && ` · ${t("versionLatest")} v${info.latest}`]
|
|
1402
|
+
})
|
|
1403
|
+
]
|
|
608
1404
|
}),
|
|
609
|
-
showStatus && installKind === "link" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
610
|
-
className: "
|
|
1405
|
+
showStatus && installKind === "link" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1406
|
+
className: "pkc-note",
|
|
611
1407
|
children: t("versionLinked")
|
|
612
1408
|
}),
|
|
613
|
-
showStatus && info.updateAvailable && installKind === "unknown" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
614
|
-
className: "
|
|
1409
|
+
showStatus && info.updateAvailable && installKind === "unknown" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1410
|
+
className: "pkc-note",
|
|
615
1411
|
children: t("versionManual")
|
|
616
1412
|
}),
|
|
617
|
-
updateFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
618
|
-
className: "
|
|
1413
|
+
updateFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1414
|
+
className: "pkc-error",
|
|
619
1415
|
role: "alert",
|
|
620
1416
|
children: t("versionUpdateFailed")
|
|
621
1417
|
}),
|
|
622
1418
|
updated !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1419
|
+
className: "pkc-callout",
|
|
1420
|
+
"data-tone": "info",
|
|
623
1421
|
role: "status",
|
|
624
1422
|
children: [
|
|
625
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
626
|
-
className: "
|
|
627
|
-
children: t("versionUpdated")
|
|
1423
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("b", {
|
|
1424
|
+
className: "pkc-title",
|
|
1425
|
+
children: fill(t("versionUpdated"), { version: updated.version })
|
|
628
1426
|
}),
|
|
629
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
630
|
-
className: "
|
|
1427
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1428
|
+
className: "pkc-note",
|
|
631
1429
|
children: t("versionUpdatedHint")
|
|
632
1430
|
}),
|
|
633
|
-
restartHint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
634
|
-
className: "
|
|
1431
|
+
restartHint && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1432
|
+
className: "pkc-note",
|
|
635
1433
|
children: t("versionRestartHint")
|
|
636
1434
|
}),
|
|
637
1435
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
638
|
-
className: "
|
|
1436
|
+
className: "pkc-actions",
|
|
639
1437
|
children: [
|
|
640
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
641
|
-
|
|
642
|
-
className: "packyCodeButton",
|
|
643
|
-
onClick: () => setRestartHint(true),
|
|
644
|
-
children: t("versionRestart")
|
|
645
|
-
}),
|
|
646
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
647
|
-
type: "button",
|
|
648
|
-
className: "packyCodeButton packyCodeButtonPrimary",
|
|
1438
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1439
|
+
variant: "primary",
|
|
649
1440
|
onClick: () => window.location.reload(),
|
|
650
1441
|
children: t("versionRefresh")
|
|
651
1442
|
}),
|
|
652
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
653
|
-
|
|
654
|
-
|
|
1443
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1444
|
+
onClick: () => setRestartHint(true),
|
|
1445
|
+
children: t("versionRestart")
|
|
1446
|
+
}),
|
|
1447
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Button, {
|
|
1448
|
+
variant: "ghost",
|
|
655
1449
|
onClick: () => setUpdated(void 0),
|
|
656
1450
|
children: t("versionLater")
|
|
657
1451
|
})
|
|
@@ -663,393 +1457,35 @@ window.__ModuleLoader__.load({
|
|
|
663
1457
|
});
|
|
664
1458
|
}
|
|
665
1459
|
function PackyCodeSection({ rpc, t }) {
|
|
666
|
-
const call =
|
|
667
|
-
const [status, setStatus] = react.useState();
|
|
668
|
-
const [balance, setBalance] = react.useState();
|
|
669
|
-
const [busy, setBusy] = react.useState(false);
|
|
670
|
-
const [error, setError] = react.useState();
|
|
671
|
-
const [draft, setDraft] = react.useState({
|
|
672
|
-
key: "",
|
|
673
|
-
label: ""
|
|
674
|
-
});
|
|
675
|
-
const [renaming, setRenaming] = react.useState();
|
|
676
|
-
const generation = react.useRef(0);
|
|
677
|
-
const load = react.useCallback((force = false) => {
|
|
678
|
-
const current = ++generation.current;
|
|
679
|
-
setBusy(true);
|
|
680
|
-
setError(void 0);
|
|
681
|
-
return Promise.all([call("status"), call("balance", { force })]).then(([nextStatus, nextBalance]) => {
|
|
682
|
-
if (generation.current !== current) return;
|
|
683
|
-
setStatus(nextStatus);
|
|
684
|
-
setBalance(nextBalance);
|
|
685
|
-
}).catch((failure) => {
|
|
686
|
-
if (generation.current === current) setError(clearMessage(failure));
|
|
687
|
-
}).finally(() => {
|
|
688
|
-
if (generation.current === current) setBusy(false);
|
|
689
|
-
});
|
|
690
|
-
}, [call]);
|
|
691
|
-
react.useEffect(() => {
|
|
692
|
-
load(false);
|
|
693
|
-
return () => {
|
|
694
|
-
generation.current += 1;
|
|
695
|
-
};
|
|
696
|
-
}, [load]);
|
|
697
|
-
const mutate = (endpoint) => (payload) => {
|
|
698
|
-
setBusy(true);
|
|
699
|
-
setError(void 0);
|
|
700
|
-
return call(endpoint, payload).then((nextStatus) => {
|
|
701
|
-
setStatus(nextStatus);
|
|
702
|
-
return call("balance", { force: true });
|
|
703
|
-
}).then((nextBalance) => setBalance(nextBalance)).catch((failure) => setError(clearMessage(failure))).finally(() => setBusy(false));
|
|
704
|
-
};
|
|
705
|
-
const addKey = (event) => {
|
|
706
|
-
event.preventDefault();
|
|
707
|
-
if (draft.key.trim() === "") return;
|
|
708
|
-
mutate("keys/add")({
|
|
709
|
-
key: draft.key,
|
|
710
|
-
label: draft.label
|
|
711
|
-
}).then(() => setDraft({
|
|
712
|
-
key: "",
|
|
713
|
-
label: ""
|
|
714
|
-
}));
|
|
715
|
-
};
|
|
716
|
-
const submitRename = (event) => {
|
|
717
|
-
event.preventDefault();
|
|
718
|
-
if (renaming === void 0 || renaming.label.trim() === "") return;
|
|
719
|
-
mutate("keys/rename")({
|
|
720
|
-
id: renaming.id,
|
|
721
|
-
label: renaming.label
|
|
722
|
-
}).then(() => setRenaming(void 0));
|
|
723
|
-
};
|
|
724
|
-
const rows = balance?.keys ?? [];
|
|
725
|
-
const usageOf = (id) => rows.find((row) => row.id === id);
|
|
726
|
-
const keys = status?.keys ?? [];
|
|
727
|
-
const totals = sumBalances(rows);
|
|
728
|
-
/** One account row: the PackyCode token name carries the match to a key. */
|
|
729
|
-
const accountRow = (row) => {
|
|
730
|
-
const usage = row.usage;
|
|
731
|
-
const figures = [
|
|
732
|
-
usage?.unlimitedQuota === true ? t("keyUnlimited") : usage?.availableUsd === void 0 ? void 0 : `${t("keyRemaining")} ${formatUsd(usage.availableUsd)}`,
|
|
733
|
-
usage?.usedUsd === void 0 ? void 0 : `${t("keyUsed")} ${formatUsd(usage.usedUsd)}`,
|
|
734
|
-
usage?.limitUsd === void 0 ? void 0 : `${t("accountLimit")} ${formatUsd(usage.limitUsd)}`,
|
|
735
|
-
resetLabel(usage?.resetPeriod, t) === void 0 ? void 0 : resetLabel(usage.resetPeriod, t)
|
|
736
|
-
].filter(Boolean);
|
|
737
|
-
const tokenName = usage?.name !== void 0 && usage.name !== row.label ? `${t("keyName")} ${usage.name}` : void 0;
|
|
738
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
739
|
-
className: "packyCodeRow",
|
|
740
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
741
|
-
className: "packyCodeRowMain",
|
|
742
|
-
children: [
|
|
743
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
744
|
-
className: "packyCodeRowName",
|
|
745
|
-
children: [
|
|
746
|
-
row.label,
|
|
747
|
-
row.id === status?.activeId && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
748
|
-
className: "packyCodeChip packyCodeChipActive",
|
|
749
|
-
children: t("keyActive")
|
|
750
|
-
}),
|
|
751
|
-
row.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
752
|
-
className: "packyCodeChip",
|
|
753
|
-
children: t("keyDisabled")
|
|
754
|
-
})
|
|
755
|
-
]
|
|
756
|
-
}),
|
|
757
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
758
|
-
className: "packyCodeRowMeta",
|
|
759
|
-
children: figures.length > 0 ? [tokenName, ...figures].filter(Boolean).join(" · ") : row.error === void 0 ? t("keyNoBalance") : `${t("loadFailed")}: ${row.error}`
|
|
760
|
-
}),
|
|
761
|
-
row.stale !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
762
|
-
className: "packyCodeRowMeta",
|
|
763
|
-
children: t("staleNote").replace("{time}", readTime(row.stale.fetchedAt)).replace("{error}", row.stale.error ?? "")
|
|
764
|
-
})
|
|
765
|
-
]
|
|
766
|
-
})
|
|
767
|
-
}, row.id);
|
|
768
|
-
};
|
|
1460
|
+
const call = useCaller(rpc);
|
|
769
1461
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
770
|
-
className: "
|
|
1462
|
+
className: "pkc",
|
|
771
1463
|
children: [
|
|
772
1464
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
773
|
-
className: "
|
|
1465
|
+
className: "pkc-intro",
|
|
774
1466
|
children: t("intro")
|
|
775
1467
|
}),
|
|
776
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1468
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AccountsCard, {
|
|
777
1469
|
call,
|
|
778
1470
|
t
|
|
779
1471
|
}),
|
|
780
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1472
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SearchCard, {
|
|
781
1473
|
call,
|
|
782
1474
|
t
|
|
783
1475
|
}),
|
|
784
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
className: "packyCodeCardHeader",
|
|
788
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
789
|
-
className: "packyCodeCardTitle",
|
|
790
|
-
children: t("accountTitle")
|
|
791
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
792
|
-
type: "button",
|
|
793
|
-
className: "packyCodeButton",
|
|
794
|
-
disabled: busy,
|
|
795
|
-
onClick: () => load(true),
|
|
796
|
-
children: busy ? t("refreshing") : t("refresh")
|
|
797
|
-
})]
|
|
798
|
-
}), rows.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
799
|
-
className: "packyCodeNote",
|
|
800
|
-
children: t("empty")
|
|
801
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
802
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
803
|
-
className: "packyCodeRows",
|
|
804
|
-
children: rows.map(accountRow)
|
|
805
|
-
}),
|
|
806
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
807
|
-
className: "packyCodeFigures",
|
|
808
|
-
children: [
|
|
809
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
810
|
-
className: "packyCodeFigure",
|
|
811
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
812
|
-
className: "packyCodeFigureLabel",
|
|
813
|
-
children: t("totalRemaining")
|
|
814
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
815
|
-
className: "packyCodeFigureValue",
|
|
816
|
-
children: [totals.counted === 0 ? "—" : formatUsd(totals.remainingUsd) ?? "—", totals.unlimited > 0 && ` + ${t("keyUnlimited")}`]
|
|
817
|
-
})]
|
|
818
|
-
}),
|
|
819
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
820
|
-
className: "packyCodeFigure",
|
|
821
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
822
|
-
className: "packyCodeFigureLabel",
|
|
823
|
-
children: t("totalUsed")
|
|
824
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
825
|
-
className: "packyCodeFigureValue",
|
|
826
|
-
children: totals.counted === 0 ? "—" : formatUsd(totals.usedUsd) ?? "—"
|
|
827
|
-
})]
|
|
828
|
-
}),
|
|
829
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
830
|
-
className: "packyCodeFigure",
|
|
831
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
832
|
-
className: "packyCodeFigureLabel",
|
|
833
|
-
children: t("totalLimit")
|
|
834
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
835
|
-
className: "packyCodeFigureValue",
|
|
836
|
-
children: totals.counted === 0 ? "—" : totals.unlimited > 0 ? t("keyUnlimited") : formatUsd(totals.limitUsd) ?? "—"
|
|
837
|
-
})]
|
|
838
|
-
})
|
|
839
|
-
]
|
|
840
|
-
}),
|
|
841
|
-
totals.unread > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
842
|
-
className: "packyCodeNote",
|
|
843
|
-
children: t("totalPartial").replace("{count}", String(totals.unread))
|
|
844
|
-
}),
|
|
845
|
-
rows.some((row) => row.stale !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
846
|
-
className: "packyCodeNote",
|
|
847
|
-
children: t("totalStale").replace("{count}", String(rows.filter((row) => row.stale !== void 0).length))
|
|
848
|
-
})
|
|
849
|
-
] })]
|
|
850
|
-
}),
|
|
851
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
852
|
-
className: "packyCodeCard",
|
|
853
|
-
children: [
|
|
854
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
855
|
-
className: "packyCodeCardHeader",
|
|
856
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
857
|
-
className: "packyCodeCardTitle",
|
|
858
|
-
children: t("keysTitle")
|
|
859
|
-
}), keys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
860
|
-
type: "button",
|
|
861
|
-
className: "packyCodeButton",
|
|
862
|
-
disabled: busy,
|
|
863
|
-
onClick: () => mutate("keys/remove-all")({}),
|
|
864
|
-
children: t("removeAll")
|
|
865
|
-
})]
|
|
866
|
-
}),
|
|
867
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
868
|
-
className: "packyCodeNote",
|
|
869
|
-
children: t("keysHint")
|
|
870
|
-
}),
|
|
871
|
-
keys.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
872
|
-
className: "packyCodeNote",
|
|
873
|
-
children: t("empty")
|
|
874
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
875
|
-
className: "packyCodeRows",
|
|
876
|
-
children: keys.map((key) => {
|
|
877
|
-
const row = usageOf(key.id);
|
|
878
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
879
|
-
className: "packyCodeRow",
|
|
880
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
881
|
-
className: "packyCodeRowMain",
|
|
882
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
883
|
-
className: "packyCodeRowName",
|
|
884
|
-
children: [
|
|
885
|
-
renaming?.id === key.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
886
|
-
className: "packyCodeForm",
|
|
887
|
-
onSubmit: submitRename,
|
|
888
|
-
children: [
|
|
889
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
890
|
-
className: "packyCodeInput",
|
|
891
|
-
type: "text",
|
|
892
|
-
maxLength: 60,
|
|
893
|
-
autoFocus: true,
|
|
894
|
-
placeholder: t("keyRenamePlaceholder"),
|
|
895
|
-
value: renaming.label,
|
|
896
|
-
onChange: (event) => setRenaming({
|
|
897
|
-
id: key.id,
|
|
898
|
-
label: event.target.value
|
|
899
|
-
}),
|
|
900
|
-
onKeyDown: (event) => event.key === "Escape" && setRenaming(void 0)
|
|
901
|
-
}),
|
|
902
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
903
|
-
type: "submit",
|
|
904
|
-
className: "packyCodeButton",
|
|
905
|
-
disabled: busy || renaming.label.trim() === "",
|
|
906
|
-
children: t("keyRenameSave")
|
|
907
|
-
}),
|
|
908
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
909
|
-
type: "button",
|
|
910
|
-
className: "packyCodeButton",
|
|
911
|
-
onClick: () => setRenaming(void 0),
|
|
912
|
-
children: t("keyRenameCancel")
|
|
913
|
-
})
|
|
914
|
-
]
|
|
915
|
-
}) : key.label,
|
|
916
|
-
key.active && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
917
|
-
className: "packyCodeChip packyCodeChipActive",
|
|
918
|
-
children: t("keyActive")
|
|
919
|
-
}),
|
|
920
|
-
key.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
921
|
-
className: "packyCodeChip",
|
|
922
|
-
children: `${t("keyDisabled")}: ${key.disabledReason ?? ""}`.trim()
|
|
923
|
-
})
|
|
924
|
-
]
|
|
925
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
926
|
-
className: "packyCodeRowMeta",
|
|
927
|
-
children: row?.usage?.name === void 0 ? t("keyNameUnknown") : `${t("keyName")} ${row.usage.name}`
|
|
928
|
-
})]
|
|
929
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
930
|
-
className: "packyCodeActions",
|
|
931
|
-
children: [
|
|
932
|
-
!key.active && !key.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
933
|
-
type: "button",
|
|
934
|
-
className: "packyCodeButton",
|
|
935
|
-
disabled: busy,
|
|
936
|
-
onClick: () => mutate("keys/select")({ id: key.id }),
|
|
937
|
-
children: t("keyUse")
|
|
938
|
-
}),
|
|
939
|
-
key.disabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
940
|
-
type: "button",
|
|
941
|
-
className: "packyCodeButton",
|
|
942
|
-
disabled: busy,
|
|
943
|
-
onClick: () => mutate("keys/enable")({ id: key.id }),
|
|
944
|
-
children: t("keyEnable")
|
|
945
|
-
}),
|
|
946
|
-
renaming?.id !== key.id && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
947
|
-
type: "button",
|
|
948
|
-
className: "packyCodeButton",
|
|
949
|
-
disabled: busy,
|
|
950
|
-
onClick: () => setRenaming({
|
|
951
|
-
id: key.id,
|
|
952
|
-
label: key.label
|
|
953
|
-
}),
|
|
954
|
-
children: t("keyRename")
|
|
955
|
-
}),
|
|
956
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
957
|
-
type: "button",
|
|
958
|
-
className: "packyCodeButton",
|
|
959
|
-
disabled: busy,
|
|
960
|
-
onClick: () => mutate("keys/remove")({ id: key.id }),
|
|
961
|
-
children: t("keyRemove")
|
|
962
|
-
})
|
|
963
|
-
]
|
|
964
|
-
})]
|
|
965
|
-
}, key.id);
|
|
966
|
-
})
|
|
967
|
-
}),
|
|
968
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
969
|
-
className: "packyCodeForm",
|
|
970
|
-
onSubmit: addKey,
|
|
971
|
-
children: [
|
|
972
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
973
|
-
className: "packyCodeInput",
|
|
974
|
-
type: "password",
|
|
975
|
-
autoComplete: "off",
|
|
976
|
-
placeholder: t("addKeyPlaceholder"),
|
|
977
|
-
value: draft.key,
|
|
978
|
-
onChange: (event) => setDraft((current) => ({
|
|
979
|
-
...current,
|
|
980
|
-
key: event.target.value
|
|
981
|
-
}))
|
|
982
|
-
}),
|
|
983
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
984
|
-
className: "packyCodeInput",
|
|
985
|
-
type: "text",
|
|
986
|
-
placeholder: t("addLabelPlaceholder"),
|
|
987
|
-
value: draft.label,
|
|
988
|
-
onChange: (event) => setDraft((current) => ({
|
|
989
|
-
...current,
|
|
990
|
-
label: event.target.value
|
|
991
|
-
}))
|
|
992
|
-
}),
|
|
993
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
994
|
-
type: "submit",
|
|
995
|
-
className: "packyCodeButton packyCodeButtonPrimary",
|
|
996
|
-
disabled: busy || draft.key.trim() === "",
|
|
997
|
-
children: t("add")
|
|
998
|
-
})
|
|
999
|
-
]
|
|
1000
|
-
}),
|
|
1001
|
-
status?.writable !== true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1002
|
-
className: "packyCodeNote",
|
|
1003
|
-
children: t("notWritable")
|
|
1004
|
-
}),
|
|
1005
|
-
error !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1006
|
-
className: "packyCodeError",
|
|
1007
|
-
children: `${t("error")}: ${error}`
|
|
1008
|
-
})
|
|
1009
|
-
]
|
|
1476
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ClaudeCliCard, {
|
|
1477
|
+
call,
|
|
1478
|
+
t
|
|
1010
1479
|
}),
|
|
1011
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
className: "packyCodeAdvancedBody",
|
|
1015
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1016
|
-
className: "packyCodeField",
|
|
1017
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1018
|
-
className: "packyCodeFigureLabel",
|
|
1019
|
-
children: t("apiHost")
|
|
1020
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1021
|
-
className: "packyCodeInput",
|
|
1022
|
-
type: "text",
|
|
1023
|
-
disabled: status?.writable !== true,
|
|
1024
|
-
defaultValue: status?.hosts?.apiHost ?? "",
|
|
1025
|
-
onBlur: (event) => mutate("hosts/update")({
|
|
1026
|
-
apiHost: event.target.value,
|
|
1027
|
-
statusHost: status?.hosts?.statusHost
|
|
1028
|
-
})
|
|
1029
|
-
})]
|
|
1030
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1031
|
-
className: "packyCodeField",
|
|
1032
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1033
|
-
className: "packyCodeFigureLabel",
|
|
1034
|
-
children: t("statusHost")
|
|
1035
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1036
|
-
className: "packyCodeInput",
|
|
1037
|
-
type: "text",
|
|
1038
|
-
disabled: status?.writable !== true,
|
|
1039
|
-
defaultValue: status?.hosts?.statusHost ?? "",
|
|
1040
|
-
onBlur: (event) => mutate("hosts/update")({
|
|
1041
|
-
statusHost: event.target.value,
|
|
1042
|
-
apiHost: status?.hosts?.apiHost
|
|
1043
|
-
})
|
|
1044
|
-
})]
|
|
1045
|
-
})]
|
|
1046
|
-
})]
|
|
1480
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(VersionCard, {
|
|
1481
|
+
call,
|
|
1482
|
+
t
|
|
1047
1483
|
})
|
|
1048
1484
|
]
|
|
1049
1485
|
});
|
|
1050
1486
|
}
|
|
1051
1487
|
function PackyCodeComposerQuota({ rpc, t, directory }) {
|
|
1052
|
-
const call =
|
|
1488
|
+
const call = useCaller(rpc);
|
|
1053
1489
|
const enabled = react.useSyncExternalStore((listener) => directory.subscribe(listener), () => directory.getSnapshot())?.current?.provider === PROVIDER;
|
|
1054
1490
|
const [balance, setBalance] = react.useState();
|
|
1055
1491
|
react.useEffect(() => {
|
|
@@ -1077,14 +1513,13 @@ window.__ModuleLoader__.load({
|
|
|
1077
1513
|
const usage = row?.usage;
|
|
1078
1514
|
const display = usage?.unlimitedQuota === true ? t("keyUnlimited") : formatUsd(usage?.availableUsd) ?? (row?.error === void 0 ? t("composerNoKey") : t("composerUnread"));
|
|
1079
1515
|
const stale = row?.stale !== void 0 && usage !== void 0;
|
|
1080
|
-
const
|
|
1081
|
-
const label = stale ? `${t("composerLabel")}: ${display}(${t("composerStale").replace("{time}", readTime(row.stale.fetchedAt))})` : `${t("composerLabel")}: ${display}`;
|
|
1516
|
+
const label = stale ? `${t("composerLabel")}: ${display}(${fill(t("composerStale"), { time: readTime(row.stale.fetchedAt) })})` : `${t("composerLabel")}: ${display}`;
|
|
1082
1517
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1083
|
-
className: "
|
|
1518
|
+
className: "pkc-composer",
|
|
1084
1519
|
role: "status",
|
|
1085
1520
|
"aria-label": label,
|
|
1086
1521
|
title: label,
|
|
1087
|
-
children:
|
|
1522
|
+
children: stale ? `≈${display}` : display
|
|
1088
1523
|
});
|
|
1089
1524
|
}
|
|
1090
1525
|
const inject = [
|