dsh-connect-workbuddy 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js ADDED
@@ -0,0 +1,872 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-connect-workbuddy",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
9
+ let react_jsx_runtime = require("react/jsx-runtime");
10
+ //#region src/status-paths.ts
11
+ /**
12
+ * Node-free constants and types shared by the Host and browser halves.
13
+ *
14
+ * 参考:dingminhua/dsh-connect-trae(MIT,Copyright (c) 2026 LaoDing)
15
+ * — 「3 条同源只读路由(usage / models:refresh / accounts:refresh)+ 一份
16
+ * 与浏览器共享的 node-free 类型定义」的 host↔client 桥梁形态来自该项目
17
+ * (其 `status-paths.ts` 亦如此,并注明沿用
18
+ * corrinehu/dsh-workbuddy-connect 的 status-route 模式)。
19
+ * 改动:路由路径改用本插件 id;类型字段按 WorkBuddy 上游实际给出的能力
20
+ * (积分倍率、多模态、推理档位)调整,不保留 trae 的 1M 变体字段。
21
+ *
22
+ * @module dsh-connect-workbuddy/status-paths
23
+ */
24
+ /** Plugin-owned usage endpoint consumed by its browser half. */
25
+ const WORKBUDDY_USAGE_PATH = "/plugins/dsh-connect-workbuddy/usage";
26
+ /** Plugin-owned live model refresh endpoint. */
27
+ const WORKBUDDY_MODELS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/models/refresh";
28
+ /** Plugin-owned local account rescan endpoint. */
29
+ const WORKBUDDY_ACCOUNTS_REFRESH_PATH = "/plugins/dsh-connect-workbuddy/accounts/refresh";
30
+ /** Plugin-owned daily check-in action endpoint. */
31
+ const WORKBUDDY_CHECKIN_PATH = "/plugins/dsh-connect-workbuddy/checkin";
32
+ //#endregion
33
+ //#region src/client/icon.ts
34
+ /**
35
+ * Plugin icon (LD brand logo, 64px) shared across the LaoDing plugin family.
36
+ *
37
+ * 参考:dingminhua/dsh-connect-trae(MIT,Copyright (c) 2026 LaoDing)
38
+ * — 同一 data URI,其注释说明来自
39
+ * dingminhua/dsh-subagent-default-model(MIT),用于让插件家族的
40
+ * 外观保持一致。本项目沿用同一图标以达到同样目的。
41
+ * 改动:无。
42
+ *
43
+ * @module dsh-connect-workbuddy/client/icon
44
+ */
45
+ const WORKBUDDY_PLUGIN_ICON = "data:image/png;base64,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";
46
+ //#endregion
47
+ //#region src/client/styles.ts
48
+ /**
49
+ * Client styles for the WorkBuddy plugin card.
50
+ *
51
+ * 参考:dingminhua/dsh-connect-trae(MIT,Copyright (c) 2026 LaoDing)
52
+ * — 整套 `dsm-*` 卡片样式系统(卡片外壳、按钮原语、`--dsw-alias-*`
53
+ * 主题变量与十六进制回退值)逐字沿用自该项目,其又复制自
54
+ * dingminhua/dsh-subagent-default-model(MIT)的 SETTINGS_CSS。
55
+ * 沿用目的是让两个插件共享同一套外部表现语言。
56
+ * 改动:类名由 `dsm-trae-*` 改为 `dsm-workbuddy-*`;
57
+ * 移除 trae 特有的 1M 变体样式,新增按套餐聚合的积分行样式。
58
+ *
59
+ * @module dsh-connect-workbuddy/client/styles
60
+ */
61
+ const WORKBUDDY_CARD_CSS = `
62
+ .dsm-plugin-card{border:1px solid var(--dsw-alias-border-l2,#36373b);background:var(--dsw-alias-bg-layer-3,#202126);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}
63
+ .dsm-plugin-card:hover{border-color:var(--dsw-alias-label-dimmed,#777)}
64
+ .dsm-plugin-card-open{background:var(--dsw-alias-bg-layer-2,#25262b);border-color:var(--dsw-alias-label-dimmed,#777)}
65
+ .dsm-plugin-card-header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:transparent;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}
66
+ .dsm-plugin-card-header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:-2px}
67
+ .dsm-plugin-card-head{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}
68
+ .dsm-plugin-card-title{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-weight:600;line-height:1.4}
69
+ .dsm-plugin-card-description{color:var(--dsw-alias-label-tertiary,#999);font-size:13px;line-height:1.5}
70
+ .dsm-plugin-card-chevron{color:var(--dsw-alias-label-tertiary,#999);flex:none;display:inline-flex;transition:transform .16s}
71
+ .dsm-plugin-card-chevron-open{transform:rotate(180deg)}
72
+ .dsm-plugin-card-body{border-top:1px solid var(--dsw-alias-border-l2,#36373b);margin:0 16px;padding:0 0 8px}
73
+ .dsm-plugin-card-icon{width:32px;height:32px;flex:none;border-radius:7px}
74
+ .dsm-btn{appearance:none;font:inherit;cursor:pointer;border:1px solid transparent;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}
75
+ .dsm-btn:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:1px}
76
+ .dsm-btn:disabled{opacity:.4;cursor:default}
77
+ .dsm-btn-outline{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:transparent;font-weight:500}
78
+ .dsm-btn-outline:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed);background:rgba(255,255,255,.04)}
79
+ .dsm-btn-primary{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}
80
+ .dsm-btn-primary:hover:not(:disabled){opacity:.9}
81
+ .dsm-workbuddy-usage{display:flex;flex-direction:column;gap:16px;margin:0;padding:16px 0 4px}
82
+ .dsm-workbuddy-usage-account{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:14px;background:var(--dsw-alias-bg-layer-2,#24262c)}
83
+ .dsm-workbuddy-usage-account-copy{display:flex;flex-direction:column;gap:3px;min-width:0}
84
+ .dsm-workbuddy-usage-expiry{padding-left:19px;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
85
+ .dsm-workbuddy-account-picker{display:block}
86
+ .dsm-workbuddy-usage-select-wrap{position:relative}
87
+ .dsm-workbuddy-usage-select{appearance:none;width:100%;font:inherit;padding:10px 34px 10px 12px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:10px;color:var(--dsw-alias-label-primary,#e6e6e6);background:var(--dsw-alias-bg-layer-3,#2a2c33);cursor:pointer;transition:border-color .15s,box-shadow .15s}
88
+ .dsm-workbuddy-usage-select:focus-visible{outline:none;border-color:var(--dsw-alias-brand-primary,#5686fe);box-shadow:0 0 0 3px rgba(86,134,254,.22)}
89
+ .dsm-workbuddy-usage-select:disabled{opacity:.6;cursor:default}
90
+ .dsm-workbuddy-usage-select-wrap::after{content:"";position:absolute;top:50%;right:12px;width:7px;height:7px;transform:translateY(-65%) rotate(45deg);border-right:1.6px solid var(--dsw-alias-label-secondary,#c6c9d0);border-bottom:1.6px solid var(--dsw-alias-label-secondary,#c6c9d0);pointer-events:none}
91
+ .dsm-workbuddy-usage-text{margin:0;font-size:14px;line-height:22px;color:var(--dsw-alias-label-secondary,#b8b8b8)}
92
+ .dsm-workbuddy-usage-error{margin:0;font-size:14px;line-height:22px;color:var(--dsw-alias-state-error-primary,#ef4444)}
93
+ .dsm-workbuddy-usage-dot{width:9px;height:9px;border-radius:50%;flex:0 0 auto}
94
+ .dsm-workbuddy-usage-status{display:flex;align-items:center;gap:10px;font-size:15px;font-weight:500;color:var(--dsw-alias-label-primary,#e6e6e6)}
95
+ .dsm-workbuddy-credits-panels{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(150px,.8fr);gap:10px}
96
+ .dsm-workbuddy-credit-panel{display:flex;flex-direction:column;min-width:0;gap:7px;padding:14px;border:1px solid var(--dsw-alias-border-l2,#3a3d45);border-radius:12px;background:var(--dsw-alias-bg-layer-2,#24262c)}
97
+ .dsm-workbuddy-credit-panel-title{color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
98
+ .dsm-workbuddy-credit-panel-value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;line-height:21px}
99
+ .dsm-workbuddy-credit-panel-meta,.dsm-workbuddy-credit-panel-empty{color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
100
+ .dsm-workbuddy-credit-monthly-row{position:relative;display:flex;flex-direction:column;gap:3px;margin:-14px -14px 0;padding:11px 14px 10px;border-bottom:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:14px 14px 0 0;background:var(--dsw-alias-bg-layer-2,#24262c);overflow:hidden}
101
+ .dsm-workbuddy-credit-monthly-row::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;opacity:.9;background:#9ca2aa}
102
+ .dsm-workbuddy-credit-monthly-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
103
+ .dsm-workbuddy-credit-monthly-meta{color:var(--dsw-alias-label-tertiary,#999);font-size:11.5px;line-height:17px;font-variant-numeric:tabular-nums}
104
+ .dsm-workbuddy-credit-packages{display:flex;flex-direction:column;gap:5px;margin:0;padding:0;list-style:none}
105
+ .dsm-workbuddy-credit-packages li{display:flex;align-items:baseline;justify-content:space-between;gap:10px;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
106
+ .dsm-workbuddy-credit-packages li span:first-child{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
107
+ .dsm-workbuddy-credit-packages li span:last-child{flex:none;color:var(--dsw-alias-label-tertiary,#999);font-size:11px}
108
+ .dsm-workbuddy-credit-soon{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-top:9px;padding-top:9px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:12px;line-height:18px}
109
+ .dsm-workbuddy-credit-soon strong{color:var(--dsw-alias-label-primary,#e6e6e6);font-size:15px;font-variant-numeric:tabular-nums}
110
+ .dsm-workbuddy-credit-panel-total{position:relative;align-items:center;text-align:center;overflow:hidden}
111
+ .dsm-workbuddy-credit-panel-total::before{content:"";position:absolute;top:0;left:0;right:0;height:3px;opacity:.9;background:#4d9b6d}
112
+ .dsm-workbuddy-credit-total-body{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:7px;width:100%}
113
+ .dsm-workbuddy-credit-total-value{color:#3f8d60;font-size:32px;line-height:36px;font-weight:700;letter-spacing:-.5px;white-space:nowrap;font-variant-numeric:tabular-nums}
114
+ .dsm-workbuddy-checkin{display:flex;flex-direction:column;align-items:center;width:100%;padding-top:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b)}
115
+ .dsm-workbuddy-checkin-button{width:100%;padding:4px 10px;font-size:12px}
116
+ .dsm-workbuddy-checkin-error{color:var(--dsw-alias-state-error-primary,#ef4444);font-size:11px;line-height:16px;text-align:center}
117
+ @media (max-width:760px){.dsm-workbuddy-credits-panels{grid-template-columns:1fr}.dsm-workbuddy-credit-panel-total{align-items:flex-start;text-align:left}.dsm-workbuddy-credit-total-body{align-items:flex-start}}
118
+ .dsm-workbuddy-models{display:flex;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:14px}
119
+ .dsm-workbuddy-models-head{display:flex;align-items:center;justify-content:space-between;gap:12px}
120
+ .dsm-workbuddy-models-title{margin:0;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:14px;font-weight:600;line-height:20px}
121
+ .dsm-workbuddy-models-summary{margin:2px 0 0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
122
+ .dsm-workbuddy-model-list{display:flex;flex-direction:column;border:1px solid var(--dsw-alias-border-l2,#36373b);border-radius:10px;overflow:hidden}
123
+ .dsm-workbuddy-model{display:grid;grid-template-columns:minmax(0,1fr);gap:7px;padding:10px 12px;background:var(--dsw-alias-bg-layer-2,#232529);transition:opacity .16s}
124
+ .dsm-workbuddy-model-disabled{opacity:.55}
125
+ .dsm-workbuddy-model+.dsm-workbuddy-model{border-top:1px solid var(--dsw-alias-border-l2,#36373b)}
126
+ .dsm-workbuddy-model-head{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}
127
+ .dsm-workbuddy-model-enabled{display:flex;align-items:center;gap:8px;min-width:0;cursor:pointer;flex:1}
128
+ .dsm-workbuddy-model-enabled input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe);flex:none}
129
+ .dsm-workbuddy-model-image{display:inline-flex;align-items:center;gap:5px;flex:none;cursor:pointer;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px}
130
+ .dsm-workbuddy-model-image input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
131
+ .dsm-workbuddy-model-copy{display:flex;align-items:baseline;gap:8px;min-width:0}
132
+ .dsm-workbuddy-model-name{display:inline-flex;align-items:baseline;gap:7px;color:var(--dsw-alias-label-primary,#e6e6e6);font-size:13px;font-weight:500;line-height:19px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
133
+ .dsm-workbuddy-model-name-rate{color:var(--dsw-alias-label-tertiary,#999);font-size:11px;font-weight:400;line-height:16px;flex:none}
134
+ .dsm-workbuddy-model-id{color:var(--dsw-alias-label-tertiary,#999);font-size:11px;line-height:16px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
135
+ .dsm-workbuddy-model-desc{margin:0;color:var(--dsw-alias-label-tertiary,#9aa0a8);font-size:12px;line-height:18px}
136
+ .dsm-workbuddy-model-details{display:flex;align-items:center;justify-content:space-between;gap:12px;min-width:0}
137
+ .dsm-workbuddy-model-meta{display:flex;align-items:center;gap:7px 12px;flex-wrap:wrap;color:var(--dsw-alias-label-tertiary,#999);font-size:11px;line-height:16px}
138
+ .dsm-workbuddy-model-meta-tag{padding:1px 7px;border-radius:999px;font-size:11px;line-height:15px;background:rgba(174,179,187,.11);color:var(--dsw-alias-label-secondary,#c6c9d0)}
139
+ .dsm-workbuddy-context-budget{display:flex;align-items:center;justify-content:flex-end;gap:12px;flex:none;margin:0;padding:0;border:0;color:var(--dsw-alias-label-secondary,#c6c9d0);font-size:11px;line-height:16px}
140
+ .dsm-workbuddy-context-budget label{display:inline-flex;align-items:center;gap:4px;cursor:pointer}
141
+ .dsm-workbuddy-context-budget input{margin:0;accent-color:var(--dsw-alias-brand-primary,#5686fe)}
142
+ .dsm-workbuddy-model-capability-note{margin:0;color:var(--dsw-alias-label-tertiary,#999);font-size:12px;line-height:18px}
143
+ .dsm-workbuddy-model-actions{display:flex;align-items:center;justify-content:space-between;gap:12px;border-top:1px solid var(--dsw-alias-border-l2,#36373b);padding-top:12px}
144
+ .dsm-workbuddy-model-actions-buttons{display:flex;align-items:center;justify-content:flex-end;gap:8px}
145
+ .dsm-workbuddy-usage-cheer{display:inline-flex;align-items:center;gap:4px;flex:none;text-decoration:underline;text-underline-offset:2px;color:var(--dsw-alias-label-tertiary,#999);font-size:13px;line-height:1.5;transition:color .16s}
146
+ .dsm-workbuddy-usage-cheer-star{font-size:12px;line-height:1;display:inline-flex}
147
+ .dsm-workbuddy-usage-cheer:hover{color:var(--dsw-alias-label-primary,#e6e6e6)}
148
+ .dsm-workbuddy-usage-cheer:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#5686fe);outline-offset:2px}
149
+ `;
150
+ //#endregion
151
+ //#region src/client/WorkBuddyCard.tsx
152
+ /**
153
+ * WorkBuddy credits & models card contributed to Harness Plugin configuration.
154
+ *
155
+ * 参考:dingminhua/dsh-connect-trae(MIT,Copyright (c) 2026 LaoDing)
156
+ * — 卡片的整体结构(折叠外壳 / 账号状态行 / 账号下拉 / 积分区 / 模型表 /
157
+ * 操作按钮行)、模块加载时注入一次 `<style>` 的写法、
158
+ * 草稿态(draftModels/draftEnabledIds)与 dirty 标记的保存流程、
159
+ * 60 秒轮询与 AbortController 清理、以及
160
+ * `IconChevronDownOutline14` 的使用,均来自该项目的 TraeUsageCard。
161
+ * 折叠卡片外壳与 `settings.plugin.item` 槽位形态来自
162
+ * dingminhua/dsh-subagent-default-model(MIT)。
163
+ * 改动:
164
+ * 1. 积分区改为「合计 + 按套餐名聚合的进度条」,因为实测单个账号下
165
+ * 同名套餐可达 19 个,逐条渲染会淹没卡片(原项目按上游条目直出);
166
+ * 2. 模型行补上 WorkBuddy 上游给出的积分倍率、多模态与推理档位;
167
+ * 3. 移除与 WorkBuddy 上游无关的 1M 变体勾选。
168
+ *
169
+ * @module dsh-connect-workbuddy/client/WorkBuddyCard
170
+ */
171
+ const POLL_INTERVAL_MS = 6e4;
172
+ const WORKBUDDY_GITHUB_URL = "https://github.com/dingminhua/dsh-connect-workbuddy";
173
+ /** Inject or refresh the shared card CSS for the current client bundle. */
174
+ if (typeof document !== "undefined") {
175
+ const cssId = "dsh-connect-workbuddy/client.css";
176
+ const existing = document.querySelector(`style[data-plugin-css="${cssId}"]`);
177
+ if (existing !== null) existing.textContent = WORKBUDDY_CARD_CSS;
178
+ else {
179
+ const styleTag = document.createElement("style");
180
+ styleTag.dataset.plugin = "dsh-connect-workbuddy";
181
+ styleTag.dataset.pluginCss = cssId;
182
+ styleTag.textContent = WORKBUDDY_CARD_CSS;
183
+ document.head.appendChild(styleTag);
184
+ }
185
+ }
186
+ function formatNumber(value) {
187
+ return new Intl.NumberFormat(void 0, {
188
+ minimumFractionDigits: 0,
189
+ maximumFractionDigits: 2
190
+ }).format(value);
191
+ }
192
+ function formatDateTime(value) {
193
+ return new Intl.DateTimeFormat(void 0, {
194
+ year: "numeric",
195
+ month: "2-digit",
196
+ day: "2-digit",
197
+ hour: "2-digit",
198
+ minute: "2-digit"
199
+ }).format(new Date(value));
200
+ }
201
+ /** Compact package-date rendering with time, e.g. 08/25 14:44. */
202
+ function formatDate(value) {
203
+ return new Intl.DateTimeFormat(void 0, {
204
+ month: "2-digit",
205
+ day: "2-digit",
206
+ hour: "2-digit",
207
+ minute: "2-digit"
208
+ }).format(new Date(value));
209
+ }
210
+ function formatCapacity(value, unknown) {
211
+ if (value === void 0) return unknown;
212
+ if (value >= 1e6 && value % 1e6 === 0) return `${value / 1e6}M`;
213
+ if (value >= 1e3 && value % 1e3 === 0) return `${value / 1e3}K`;
214
+ return formatNumber(value);
215
+ }
216
+ function dotStyle(status) {
217
+ return { background: status === "signed-in" ? "var(--dsw-alias-state-success-primary, #22a06b)" : status === "error" ? "var(--dsw-alias-state-error-primary, #d92d20)" : "var(--dsw-alias-label-dimmed, #9aa0a6)" };
218
+ }
219
+ /** Render WorkBuddy sign-in state, credits, and model selection as one card. */
220
+ function WorkBuddyCard({ t, settingsScope }) {
221
+ if (t === void 0) throw new Error("WorkBuddy plugin card requires its translation function");
222
+ const [open, setOpen] = (0, react.useState)(false);
223
+ const [status, setStatus] = (0, react.useState)({
224
+ status: "signed-out",
225
+ accounts: []
226
+ });
227
+ const [busy, setBusy] = (0, react.useState)(false);
228
+ const [settingsRevision, setSettingsRevision] = (0, react.useState)(0);
229
+ const [draftModels, setDraftModels] = (0, react.useState)(void 0);
230
+ const [draftEnabledIds, setDraftEnabledIds] = (0, react.useState)(void 0);
231
+ const [draftImageIds, setDraftImageIds] = (0, react.useState)(void 0);
232
+ const [draftContextBudgets, setDraftContextBudgets] = (0, react.useState)(void 0);
233
+ const [saving, setSaving] = (0, react.useState)(false);
234
+ const [switchingAccount, setSwitchingAccount] = (0, react.useState)(false);
235
+ const [checkingIn, setCheckingIn] = (0, react.useState)(false);
236
+ const [checkinActionError, setCheckinActionError] = (0, react.useState)(void 0);
237
+ const mounted = (0, react.useRef)(true);
238
+ (0, react.useEffect)(() => {
239
+ mounted.current = true;
240
+ return () => {
241
+ mounted.current = false;
242
+ };
243
+ }, []);
244
+ (0, react.useEffect)(() => settingsScope?.subscribe(() => {
245
+ setSettingsRevision((value) => value + 1);
246
+ }), [settingsScope]);
247
+ const refreshUsage = (0, react.useCallback)(async (signal) => {
248
+ try {
249
+ const response = await fetch(WORKBUDDY_USAGE_PATH, {
250
+ headers: { accept: "application/json" },
251
+ credentials: "same-origin",
252
+ ...signal === void 0 ? {} : { signal }
253
+ });
254
+ const value = await response.json().catch(() => void 0);
255
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
256
+ if (mounted.current && signal?.aborted !== true) setStatus(value);
257
+ return value;
258
+ } catch (error) {
259
+ if (mounted.current && signal?.aborted !== true) setStatus({
260
+ status: "error",
261
+ message: error instanceof Error ? error.message : t("row.requestFailed")
262
+ });
263
+ return;
264
+ }
265
+ }, [t]);
266
+ (0, react.useEffect)(() => {
267
+ if (!open) return;
268
+ const controller = new AbortController();
269
+ refreshUsage(controller.signal);
270
+ return () => {
271
+ controller.abort();
272
+ };
273
+ }, [open, refreshUsage]);
274
+ (0, react.useEffect)(() => {
275
+ if (!open || status.status !== "signed-in") return;
276
+ const controller = new AbortController();
277
+ const timer = window.setInterval(() => {
278
+ refreshUsage(controller.signal);
279
+ }, POLL_INTERVAL_MS);
280
+ return () => {
281
+ window.clearInterval(timer);
282
+ controller.abort();
283
+ };
284
+ }, [
285
+ open,
286
+ refreshUsage,
287
+ status.status
288
+ ]);
289
+ const rescanAccounts = async () => {
290
+ setBusy(true);
291
+ try {
292
+ const response = await fetch(WORKBUDDY_ACCOUNTS_REFRESH_PATH, {
293
+ method: "POST",
294
+ headers: { accept: "application/json" },
295
+ credentials: "same-origin"
296
+ });
297
+ const body = await response.json();
298
+ if (!response.ok || !Array.isArray(body.accounts)) throw new Error(`HTTP ${response.status}`);
299
+ const selected = body.accounts.find((account) => account.selected)?.id;
300
+ const configured = settingsScope?.getSnapshot().value;
301
+ const configuredId = typeof configured === "object" && configured !== null && typeof configured.accountId === "string" ? configured.accountId : void 0;
302
+ if (selected !== void 0 && selected !== configuredId && settingsScope?.getSnapshot().writable === true) await settingsScope.set("accountId", selected);
303
+ await refreshUsage();
304
+ } finally {
305
+ if (mounted.current) setBusy(false);
306
+ }
307
+ };
308
+ const switchAccount = async (accountId) => {
309
+ if (settingsScope === void 0) return;
310
+ setSwitchingAccount(true);
311
+ try {
312
+ await settingsScope.set("accountId", accountId);
313
+ await refreshUsage();
314
+ } finally {
315
+ if (mounted.current) setSwitchingAccount(false);
316
+ }
317
+ };
318
+ const claimDailyCheckin = async () => {
319
+ setCheckingIn(true);
320
+ setCheckinActionError(void 0);
321
+ try {
322
+ const response = await fetch(WORKBUDDY_CHECKIN_PATH, {
323
+ method: "POST",
324
+ headers: { accept: "application/json" },
325
+ credentials: "same-origin"
326
+ });
327
+ const body = await response.json().catch(() => void 0);
328
+ if (!response.ok) throw new Error(body?.error ?? `HTTP ${response.status}`);
329
+ await refreshUsage();
330
+ } catch (error) {
331
+ if (mounted.current) setCheckinActionError(error instanceof Error ? error.message : t("row.requestFailed"));
332
+ } finally {
333
+ if (mounted.current) setCheckingIn(false);
334
+ }
335
+ };
336
+ const refreshModels = async () => {
337
+ setBusy(true);
338
+ try {
339
+ const response = await fetch(WORKBUDDY_MODELS_REFRESH_PATH, {
340
+ method: "POST",
341
+ headers: { accept: "application/json" },
342
+ credentials: "same-origin"
343
+ });
344
+ const body = await response.json();
345
+ if (!response.ok || !Array.isArray(body.models)) throw new Error(`HTTP ${response.status}`);
346
+ const fresh = body.models;
347
+ const freshIds = new Set(fresh.map((model) => model.id));
348
+ const stillEnabled = [...activeEnabledIds].filter((id) => freshIds.has(id));
349
+ const stillImages = [...activeImageIds].filter((id) => freshIds.has(id));
350
+ const stillBudgets = {};
351
+ for (const id of freshIds) {
352
+ const budget = activeContextBudgets[id];
353
+ if (typeof budget === "number") stillBudgets[id] = budget;
354
+ }
355
+ setDraftModels(fresh);
356
+ setDraftEnabledIds(new Set(stillEnabled));
357
+ setDraftImageIds(new Set(stillImages));
358
+ setDraftContextBudgets(stillBudgets);
359
+ } catch (error) {
360
+ if (mounted.current) setStatus({
361
+ status: "error",
362
+ message: error instanceof Error ? error.message : t("row.requestFailed")
363
+ });
364
+ } finally {
365
+ if (mounted.current) setBusy(false);
366
+ }
367
+ };
368
+ const visibleModels = draftModels ?? (status.status === "signed-in" ? status.models : []);
369
+ const savedEnabledIds = status.status === "signed-in" ? new Set(status.enabledModelIds) : /* @__PURE__ */ new Set();
370
+ const activeEnabledIds = draftEnabledIds ?? savedEnabledIds;
371
+ const savedImageIds = status.status === "signed-in" ? new Set(status.imageModelIds) : /* @__PURE__ */ new Set();
372
+ const activeImageIds = draftImageIds ?? savedImageIds;
373
+ const configured = settingsScope?.getSnapshot().value;
374
+ const savedContextBudgets = typeof configured === "object" && configured !== null && typeof configured.contextBudgets === "object" ? configured.contextBudgets : {};
375
+ const activeContextBudgets = draftContextBudgets ?? savedContextBudgets;
376
+ const dirty = draftModels !== void 0 || draftEnabledIds !== void 0 || draftImageIds !== void 0 || draftContextBudgets !== void 0;
377
+ const toggleModel = (modelId) => {
378
+ const next = new Set(activeEnabledIds);
379
+ if (!next.delete(modelId)) next.add(modelId);
380
+ setDraftEnabledIds(next);
381
+ setDraftModels([...visibleModels]);
382
+ };
383
+ const toggleImage = (modelId) => {
384
+ const next = new Set(activeImageIds);
385
+ if (!next.delete(modelId)) next.add(modelId);
386
+ setDraftImageIds(next);
387
+ setDraftModels([...visibleModels]);
388
+ };
389
+ const setContextBudget = (modelId, budget) => {
390
+ setDraftContextBudgets({
391
+ ...activeContextBudgets,
392
+ [modelId]: budget
393
+ });
394
+ setDraftModels([...visibleModels]);
395
+ };
396
+ const discardModels = () => {
397
+ setDraftModels(void 0);
398
+ setDraftEnabledIds(void 0);
399
+ setDraftImageIds(void 0);
400
+ setDraftContextBudgets(void 0);
401
+ };
402
+ const saveModels = async () => {
403
+ if (settingsScope === void 0) return;
404
+ setSaving(true);
405
+ try {
406
+ await settingsScope.set("lastCatalog", visibleModels.map((model) => ({
407
+ ...model,
408
+ contextWindow: model.nativeContextWindow,
409
+ nativeContextWindow: void 0,
410
+ multimodal: void 0
411
+ })));
412
+ await settingsScope.set("enabledModelIds", [...activeEnabledIds]);
413
+ await settingsScope.set("imageModelIds", [...activeImageIds]);
414
+ await settingsScope.set("contextBudgets", activeContextBudgets);
415
+ discardModels();
416
+ await refreshUsage();
417
+ } finally {
418
+ if (mounted.current) setSaving(false);
419
+ }
420
+ };
421
+ const title = t("row.title");
422
+ const label = status.status === "signed-in" ? t("row.signedIn", { accountName: status.accountName }) : status.status === "error" ? t("row.requestFailed") : t("row.signedOut");
423
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
424
+ className: `dsm-plugin-card${open ? " dsm-plugin-card-open" : ""}`,
425
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
426
+ type: "button",
427
+ className: "dsm-plugin-card-header",
428
+ "aria-expanded": open,
429
+ "aria-label": `${t(open ? "row.collapse" : "row.expand")}: ${title}`,
430
+ onClick: () => {
431
+ setOpen(!open);
432
+ },
433
+ children: [
434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
435
+ className: "dsm-plugin-card-icon",
436
+ src: WORKBUDDY_PLUGIN_ICON,
437
+ alt: ""
438
+ }),
439
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
440
+ className: "dsm-plugin-card-head",
441
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
442
+ className: "dsm-plugin-card-title",
443
+ children: title
444
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
445
+ className: "dsm-plugin-card-description",
446
+ children: t("row.desc")
447
+ })]
448
+ }),
449
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
450
+ "aria-hidden": "true",
451
+ className: `dsm-plugin-card-chevron${open ? " dsm-plugin-card-chevron-open" : ""}`,
452
+ children: (0, react.createElement)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { size: 14 })
453
+ })
454
+ ]
455
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
456
+ className: "dsm-plugin-card-body",
457
+ hidden: !open,
458
+ children: open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
459
+ className: "dsm-workbuddy-usage",
460
+ children: [
461
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
462
+ className: "dsm-workbuddy-usage-account",
463
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
464
+ className: "dsm-workbuddy-usage-account-copy",
465
+ role: "status",
466
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
467
+ className: "dsm-workbuddy-usage-status",
468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
469
+ "aria-hidden": "true",
470
+ className: "dsm-workbuddy-usage-dot",
471
+ style: dotStyle(status.status)
472
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label })]
473
+ }), status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
474
+ className: "dsm-workbuddy-usage-expiry",
475
+ children: t("row.tokenExpiry", { expiresAt: formatDateTime(status.tokenExpiresAtMs) })
476
+ }) : null]
477
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
478
+ type: "button",
479
+ className: "dsm-btn dsm-btn-outline",
480
+ disabled: busy,
481
+ onClick: () => {
482
+ rescanAccounts();
483
+ },
484
+ children: busy ? t("row.accountsScanning") : t("row.accountsRescan")
485
+ })]
486
+ }),
487
+ status.status !== "error" && status.accounts.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
488
+ className: "dsm-workbuddy-account-picker",
489
+ "aria-label": t("row.accountsTitle"),
490
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
491
+ className: "dsm-workbuddy-usage-select-wrap",
492
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
493
+ className: "dsm-workbuddy-usage-select",
494
+ value: status.status === "signed-in" ? status.accountId : "",
495
+ disabled: switchingAccount || settingsScope?.getSnapshot().writable !== true,
496
+ onChange: (event) => {
497
+ switchAccount(event.currentTarget.value);
498
+ },
499
+ children: status.accounts.map((account) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
500
+ value: account.id,
501
+ children: [account.accountName, account.domain === "" ? "" : ` · ${account.domain}`]
502
+ }, account.id))
503
+ })
504
+ })
505
+ }) : null,
506
+ status.status === "signed-in" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
507
+ status.credits === void 0 ? null : (() => {
508
+ const monthly = [...status.credits.packages].filter((pack) => pack.monthly).sort((left, right) => right.remain - left.remain);
509
+ const SOON_MS = 2592e5;
510
+ const now = Date.now();
511
+ const expiring = [...status.credits.packages].filter((pack) => !pack.monthly && pack.remain > 0 && (pack.expiresAtMs ?? Number.MAX_SAFE_INTEGER) - now <= SOON_MS).sort((left, right) => (left.expiresAtMs ?? Number.MAX_SAFE_INTEGER) - (right.expiresAtMs ?? Number.MAX_SAFE_INTEGER));
512
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
513
+ className: "dsm-workbuddy-credits-panels",
514
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
515
+ className: "dsm-workbuddy-credit-panel dsm-workbuddy-credit-panel-activities",
516
+ children: [
517
+ monthly.map((pack, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
518
+ className: "dsm-workbuddy-credit-monthly-row",
519
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
520
+ className: "dsm-workbuddy-credit-monthly-name",
521
+ children: pack.packageName
522
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
523
+ className: "dsm-workbuddy-credit-monthly-meta",
524
+ children: t("row.creditsMonthlyRemain", {
525
+ remain: formatNumber(pack.remain),
526
+ size: formatNumber(pack.size),
527
+ at: pack.cycleRefreshMs === void 0 ? "" : formatDate(pack.cycleRefreshMs)
528
+ })
529
+ })]
530
+ }, `monthly-${pack.packageName}-${String(index)}`)),
531
+ expiring.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
532
+ className: "dsm-workbuddy-credit-panel-empty",
533
+ children: t("row.creditsNoSoon")
534
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
535
+ className: "dsm-workbuddy-credit-packages",
536
+ children: expiring.map((pack, index) => {
537
+ const at = pack.expiresAtMs;
538
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: pack.packageName }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [formatNumber(pack.remain), at === void 0 ? "" : ` · ${formatDate(at)}`] })] }, `${pack.packageName}-${String(index)}`);
539
+ })
540
+ }),
541
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
542
+ className: "dsm-workbuddy-credit-soon",
543
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.creditsExpiringSoon") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatNumber(status.credits.expiringSoon) })]
544
+ })
545
+ ]
546
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
547
+ className: "dsm-workbuddy-credit-panel dsm-workbuddy-credit-panel-total",
548
+ children: [
549
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
550
+ className: "dsm-workbuddy-credit-total-body",
551
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
552
+ className: "dsm-workbuddy-credit-panel-title",
553
+ children: t("row.creditsTotalLabel")
554
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
555
+ className: "dsm-workbuddy-credit-total-value",
556
+ children: formatNumber(status.credits.total)
557
+ })]
558
+ }),
559
+ status.checkin === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
560
+ className: "dsm-workbuddy-checkin",
561
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
562
+ type: "button",
563
+ className: "dsm-btn dsm-btn-primary dsm-workbuddy-checkin-button",
564
+ disabled: !status.checkin.active || status.checkin.todayCheckedIn || checkingIn,
565
+ onClick: () => {
566
+ claimDailyCheckin();
567
+ },
568
+ children: checkingIn ? t("row.checkinClaiming") : status.checkin.todayCheckedIn ? t("row.checkinClaimed") : status.checkin.claimButtonText ?? t("row.checkinClaim")
569
+ })
570
+ }),
571
+ status.checkinError === void 0 && checkinActionError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
572
+ className: "dsm-workbuddy-checkin-error",
573
+ children: t("row.checkinError", { message: checkinActionError ?? status.checkinError ?? "" })
574
+ })
575
+ ]
576
+ })]
577
+ });
578
+ })(),
579
+ status.creditsError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
580
+ className: "dsm-workbuddy-usage-error",
581
+ children: t("row.creditsError", { message: status.creditsError })
582
+ }),
583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
584
+ className: "dsm-workbuddy-models",
585
+ "aria-label": t("row.modelsTitle"),
586
+ children: [
587
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
588
+ className: "dsm-workbuddy-models-head",
589
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
590
+ className: "dsm-workbuddy-models-title",
591
+ children: t("row.modelsTitle")
592
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
593
+ className: "dsm-workbuddy-models-summary",
594
+ children: t("row.modelsSummary", { count: activeEnabledIds.size })
595
+ })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
596
+ type: "button",
597
+ className: "dsm-btn dsm-btn-outline",
598
+ disabled: busy,
599
+ onClick: () => {
600
+ refreshModels();
601
+ },
602
+ children: busy ? t("row.modelsRefreshing") : t("row.modelsRefresh")
603
+ })]
604
+ }),
605
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
606
+ className: "dsm-workbuddy-model-list",
607
+ children: visibleModels.map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
608
+ className: `dsm-workbuddy-model${activeEnabledIds.has(model.id) ? "" : " dsm-workbuddy-model-disabled"}`,
609
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
610
+ className: "dsm-workbuddy-model-head",
611
+ children: [
612
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
613
+ className: "dsm-workbuddy-model-enabled",
614
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
615
+ type: "checkbox",
616
+ checked: activeEnabledIds.has(model.id),
617
+ disabled: settingsScope?.getSnapshot().writable !== true || saving,
618
+ onChange: () => {
619
+ toggleModel(model.id);
620
+ }
621
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
622
+ className: "dsm-workbuddy-model-copy",
623
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
624
+ className: "dsm-workbuddy-model-name",
625
+ children: [model.name, model.creditMultiplier === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
626
+ className: "dsm-workbuddy-model-name-rate",
627
+ children: [
628
+ "(",
629
+ model.creditMultiplier.toFixed(2),
630
+ "x)"
631
+ ]
632
+ })]
633
+ })
634
+ })]
635
+ }),
636
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
637
+ className: "dsm-workbuddy-model-image",
638
+ title: t("row.modelImage"),
639
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
640
+ type: "checkbox",
641
+ checked: activeImageIds.has(model.id),
642
+ disabled: settingsScope?.getSnapshot().writable !== true || saving,
643
+ onChange: () => {
644
+ toggleImage(model.id);
645
+ }
646
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelImage") })]
647
+ }),
648
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
649
+ className: "dsm-workbuddy-context-budget",
650
+ "aria-label": t("row.contextBudget"),
651
+ children: [model.nativeContextWindow > 2e5 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
652
+ type: "radio",
653
+ name: `context-${model.id}`,
654
+ checked: (activeContextBudgets[model.id] ?? 2e5) === 2e5,
655
+ disabled: settingsScope?.getSnapshot().writable !== true || saving,
656
+ onChange: () => {
657
+ setContextBudget(model.id, 2e5);
658
+ }
659
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "200K" })] }) : null, /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
660
+ type: "radio",
661
+ name: `context-${model.id}`,
662
+ checked: model.nativeContextWindow <= 2e5 || activeContextBudgets[model.id] === model.nativeContextWindow,
663
+ disabled: model.nativeContextWindow <= 2e5 || settingsScope?.getSnapshot().writable !== true || saving,
664
+ onChange: () => {
665
+ setContextBudget(model.id, model.nativeContextWindow);
666
+ }
667
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) })] })]
668
+ })
669
+ ]
670
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
671
+ className: "dsm-workbuddy-model-details",
672
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
673
+ className: "dsm-workbuddy-model-meta",
674
+ children: [
675
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelContext", { context: formatCapacity(model.nativeContextWindow, t("row.modelUnknown")) }) }),
676
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelOutput", { output: formatCapacity(model.maxTokens, t("row.modelUnknown")) }) }),
677
+ model.reasoning === void 0 || model.reasoning.supportedEfforts === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("row.modelReasoning", { efforts: model.reasoning.supportedEfforts.join(" / ") }) })
678
+ ]
679
+ })
680
+ })]
681
+ }, model.id))
682
+ }),
683
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
684
+ className: "dsm-workbuddy-model-capability-note",
685
+ children: t("row.modelCapabilityPending")
686
+ }),
687
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
688
+ className: "dsm-workbuddy-model-actions",
689
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("a", {
690
+ className: "dsm-workbuddy-usage-cheer",
691
+ href: WORKBUDDY_GITHUB_URL,
692
+ target: "_blank",
693
+ rel: "noopener noreferrer",
694
+ children: [t("row.cheer"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
695
+ className: "dsm-workbuddy-usage-cheer-star",
696
+ "aria-hidden": "true",
697
+ children: "★"
698
+ })]
699
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
700
+ className: "dsm-workbuddy-model-actions-buttons",
701
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
702
+ type: "button",
703
+ className: "dsm-btn dsm-btn-outline",
704
+ disabled: !dirty || saving,
705
+ onClick: discardModels,
706
+ children: t("row.discard")
707
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
708
+ type: "button",
709
+ className: "dsm-btn dsm-btn-primary",
710
+ disabled: !dirty || saving || activeEnabledIds.size === 0,
711
+ onClick: () => {
712
+ saveModels();
713
+ },
714
+ children: saving ? t("row.saving") : t("row.save")
715
+ })]
716
+ })]
717
+ })
718
+ ]
719
+ })
720
+ ] }) : null,
721
+ status.status === "signed-out" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
722
+ className: "dsm-workbuddy-usage-text",
723
+ children: status.message ?? t("row.signedOutHint")
724
+ }) : null,
725
+ status.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
726
+ className: "dsm-workbuddy-usage-error",
727
+ children: status.message
728
+ }) : null
729
+ ]
730
+ }) : null
731
+ })]
732
+ });
733
+ }
734
+ //#endregion
735
+ //#region src/client/locales.ts
736
+ /**
737
+ * Plugin-card copy registered under the settings.workbuddy locale namespace.
738
+ *
739
+ * 参考:dingminhua/dsh-connect-trae(MIT,Copyright (c) 2026 LaoDing)
740
+ * — `row.*` 的文案键约定与中英 1:1 键对齐(`zh: Record<Key, string>`
741
+ * 强制双语同步)来自该项目,其又继承自
742
+ * dingminhua/dsh-subagent-default-model(MIT)。
743
+ * 改动:文案按 WorkBuddy 的实际情况改写(积分套餐、模型倍率、多账号)。
744
+ *
745
+ * @module dsh-connect-workbuddy/client/locales
746
+ */
747
+ const en = {
748
+ "row.title": "WorkBuddy credits & models (dsh-connect-workbuddy)",
749
+ "row.desc": "Use the models in the WorkBuddy desktop app directly in DSH, and see your remaining credits at a glance.",
750
+ "row.expand": "Expand",
751
+ "row.collapse": "Collapse",
752
+ "row.signedOut": "Not signed in",
753
+ "row.signedOutHint": "Sign in once in the WorkBuddy desktop app; this plugin follows that sign-in automatically.",
754
+ "row.signedIn": "Signed in: {accountName}",
755
+ "row.tokenExpiry": "Access token expires {expiresAt} (auto-renewal)",
756
+ "row.requestFailed": "Request failed",
757
+ "row.creditsTotalLabel": "Total remaining credits",
758
+ "row.creditsMonthlyRemain": "Remaining {remain} / {size} · Refreshes {at}",
759
+ "row.checkinClaim": "Check in",
760
+ "row.checkinClaiming": "Checking in…",
761
+ "row.checkinClaimed": "Checked in",
762
+ "row.checkinError": "Check-in unavailable: {message}",
763
+ "row.creditsError": "Credit query unavailable: {message}",
764
+ "row.creditsNoSoon": "No packages expire within 3 days.",
765
+ "row.creditsExpiringSoon": "Expiring in 3 days",
766
+ "row.refresh": "Refresh",
767
+ "row.refreshing": "Refreshing…",
768
+ "row.accountsTitle": "WorkBuddy account",
769
+ "row.accountsHint": "Choose from locally detected sign-ins. Tokens are never shown or saved in DSH settings.",
770
+ "row.accountsRescan": "Detect accounts again",
771
+ "row.accountsScanning": "Detecting…",
772
+ "row.modelsTitle": "Models",
773
+ "row.modelsSummary": "{count} enabled",
774
+ "row.modelsRefresh": "Refresh from WorkBuddy",
775
+ "row.modelsRefreshing": "Refreshing models…",
776
+ "row.discard": "Discard changes",
777
+ "row.save": "Save",
778
+ "row.saving": "Saving…",
779
+ "row.modelContext": "Maximum context {context}",
780
+ "row.contextBudget": "DSH context budget",
781
+ "row.modelOutput": "Output {output}",
782
+ "row.modelRate": "{rate}x credits",
783
+ "row.modelMultimodal": "Multimodal",
784
+ "row.modelImage": "Image",
785
+ "row.modelReasoning": "Reasoning: {efforts}",
786
+ "row.modelUnknown": "Unknown",
787
+ "row.modelCapabilityPending": "Only capabilities advertised by WorkBuddy are shown.",
788
+ "row.cheer": "Star on GitHub"
789
+ };
790
+ const zh = {
791
+ "row.title": "接入使用 WorkBuddy 积分与模型(dsh-connect-workbuddy)",
792
+ "row.desc": "在 DSH 中直接使用 WorkBuddy 桌面 App 包含的模型,并随时查看剩余积分。",
793
+ "row.expand": "展开",
794
+ "row.collapse": "收起",
795
+ "row.signedOut": "未登录",
796
+ "row.signedOutHint": "在 WorkBuddy 桌面 App 里登录一次即可,插件会自动跟随当前登录的账号。",
797
+ "row.signedIn": "已登录:{accountName}",
798
+ "row.tokenExpiry": "访问令牌 {expiresAt} 过期(自动续期)",
799
+ "row.requestFailed": "请求失败",
800
+ "row.creditsTotalLabel": "总剩余积分",
801
+ "row.creditsMonthlyRemain": "剩余 {remain} / {size} · {at} 刷新",
802
+ "row.checkinClaim": "立即签到",
803
+ "row.checkinClaiming": "签到中…",
804
+ "row.checkinClaimed": "今日已签到",
805
+ "row.checkinError": "签到状态获取失败:{message}",
806
+ "row.creditsError": "积分查询失败:{message}",
807
+ "row.creditsNoSoon": "3 天内没有到期礼包。",
808
+ "row.creditsExpiringSoon": "最近 3 天到期",
809
+ "row.refresh": "刷新",
810
+ "row.refreshing": "正在刷新…",
811
+ "row.accountsTitle": "WorkBuddy 账号",
812
+ "row.accountsHint": "选择本机检测到的登录账号;Token 不会显示,也不会保存到 DSH 设置。",
813
+ "row.accountsRescan": "重新检测账号",
814
+ "row.accountsScanning": "正在检测…",
815
+ "row.modelsTitle": "模型",
816
+ "row.modelsSummary": "已启用 {count} 个",
817
+ "row.modelsRefresh": "从 WorkBuddy 刷新",
818
+ "row.modelsRefreshing": "正在刷新模型…",
819
+ "row.discard": "放弃修改",
820
+ "row.save": "保存",
821
+ "row.saving": "保存中…",
822
+ "row.modelContext": "最大上下文 {context}",
823
+ "row.contextBudget": "DSH 上下文预算",
824
+ "row.modelOutput": "最大输出 {output}",
825
+ "row.modelRate": "积分 {rate}x",
826
+ "row.modelMultimodal": "多模态",
827
+ "row.modelImage": "图片",
828
+ "row.modelReasoning": "推理强度:{efforts}",
829
+ "row.modelUnknown": "未知",
830
+ "row.modelCapabilityPending": "仅展示 WorkBuddy 接口明确公布的模型能力。",
831
+ "row.cheer": "鼓励一下"
832
+ };
833
+ //#endregion
834
+ //#region src/client/index.tsx
835
+ /** Stable browser-plugin name. */
836
+ const name = "dsh-connect-workbuddy-client";
837
+ /** Client services required by the Plugin configuration contribution. */
838
+ const inject = [
839
+ "slots",
840
+ "locale",
841
+ "settingsScope"
842
+ ];
843
+ /** Register card copy and the WorkBuddy card under Plugin configuration. */
844
+ function apply(ctx) {
845
+ try {
846
+ const namespace = "settings.workbuddy";
847
+ ctx.effect(() => ctx.locale.register(namespace, {
848
+ zh,
849
+ en
850
+ }), "dsh-connect-workbuddy: settings copy");
851
+ const t = ctx.locale.bind(namespace);
852
+ const settingsScope = ctx.settingsScope.bind({ namespace: "workbuddy" });
853
+ ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
854
+ name: "settings.plugin.item",
855
+ key: "workbuddy",
856
+ priority: 30,
857
+ inject: () => ({
858
+ t,
859
+ settingsScope
860
+ })
861
+ }, WorkBuddyCard));
862
+ } catch (error) {
863
+ console.error("[dsh-connect-workbuddy] client card failed to load (host provider unaffected):", error);
864
+ }
865
+ }
866
+ //#endregion
867
+ exports.apply = apply;
868
+ exports.inject = inject;
869
+ exports.name = name;
870
+ return module.exports;
871
+ }
872
+ });