hiwork-core 0.0.30 → 0.0.37
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/cordis.patch.yml +15 -1
- package/lib/ai-router-client.js +394 -2
- package/lib/browser-login.js +42 -16
- package/lib/client-policy.js +2 -2
- package/lib/client.js +2496 -500
- package/lib/image-rpc.js +306 -0
- package/lib/imagegen.js +424 -0
- package/lib/index.js +18639 -7060
- package/lib/knowledge-config.js +91 -1
- package/lib/provision-session.js +10 -2
- package/lib/tool-names.js +10 -0
- package/lib/tools.js +205 -0
- package/lib/types/ai-router-client.d.ts +255 -2
- package/lib/types/browser-login.d.ts +10 -1
- package/lib/types/client/about-section.d.ts +27 -7
- package/lib/types/client/controller.d.ts +17 -9
- package/lib/types/client/daily-data.d.ts +31 -3
- package/lib/types/client/feature-center.d.ts +46 -1
- package/lib/types/client/feature-registry.d.ts +36 -3
- package/lib/types/client/image-cards.d.ts +218 -0
- package/lib/types/client/image-styles.d.ts +9 -0
- package/lib/types/client/images-data.d.ts +127 -0
- package/lib/types/client/images-page.d.ts +97 -0
- package/lib/types/client/index.d.ts +0 -1
- package/lib/types/client/login-gate.d.ts +2 -2
- package/lib/types/client/logo.d.ts +7 -2
- package/lib/types/client/menu-preferences.d.ts +163 -0
- package/lib/types/client/menu-section.d.ts +5 -0
- package/lib/types/client/menu-store.d.ts +101 -0
- package/lib/types/client/styles.d.ts +16 -0
- package/lib/types/client/types.d.ts +9 -0
- package/lib/types/image-rpc.d.ts +142 -0
- package/lib/types/imagegen.d.ts +241 -0
- package/lib/types/index.d.ts +89 -8
- package/lib/types/knowledge-config.d.ts +50 -2
- package/lib/types/tool-names.d.ts +11 -0
- package/lib/types/tools.d.ts +80 -0
- package/lib/types/user-agent.d.ts +31 -0
- package/lib/user-agent.js +54 -0
- package/lib/web-search.js +2 -2
- package/package.json +34 -24
package/lib/client.js
CHANGED
|
@@ -65,6 +65,10 @@ var secondaryButton = {
|
|
|
65
65
|
color: textSecondary,
|
|
66
66
|
fontSize: 14
|
|
67
67
|
};
|
|
68
|
+
var disabledButton = {
|
|
69
|
+
opacity: 0.45,
|
|
70
|
+
cursor: "not-allowed"
|
|
71
|
+
};
|
|
68
72
|
var textInput = {
|
|
69
73
|
padding: "9px 12px",
|
|
70
74
|
borderRadius: 10,
|
|
@@ -110,7 +114,338 @@ var interactionCss = [
|
|
|
110
114
|
// 账号行图标钮(用户名/设置/退出账号)hover:底变浅、图标提亮,同侧边栏 iconButton。
|
|
111
115
|
`[data-hiwork-core-icon]:hover { background: ${interactiveHover} !important; color: ${textPrimary} !important; }`,
|
|
112
116
|
// 品牌蓝按钮(登录门禁)hover:提亮而非换底色,保留渐变。
|
|
113
|
-
`[data-hiwork-core-brand]:hover:not(:disabled) { filter: brightness(1.08) !important; }
|
|
117
|
+
`[data-hiwork-core-brand]:hover:not(:disabled) { filter: brightness(1.08) !important; }`,
|
|
118
|
+
// 行内文字链接(设置「关于」的版本/官网/手册):hover 补下划线,走颜色之外的
|
|
119
|
+
// 唯一提示维度,不抢布局。
|
|
120
|
+
`[data-hiwork-core-linktext]:hover { text-decoration: underline; }`
|
|
121
|
+
].join("\n");
|
|
122
|
+
var imageCardsCss = [
|
|
123
|
+
/* ---- 会话内卡片 ---- */
|
|
124
|
+
`.hiwork-image-card {`,
|
|
125
|
+
` display: flex;`,
|
|
126
|
+
` flex-direction: column;`,
|
|
127
|
+
` gap: 8px;`,
|
|
128
|
+
` margin: 6px 0;`,
|
|
129
|
+
` padding: 10px 12px;`,
|
|
130
|
+
` border: 1px solid ${borderL2};`,
|
|
131
|
+
` border-radius: 10px;`,
|
|
132
|
+
` background: ${bgLayer2};`,
|
|
133
|
+
`}`,
|
|
134
|
+
`.hiwork-image-card-head {`,
|
|
135
|
+
` display: flex;`,
|
|
136
|
+
` align-items: baseline;`,
|
|
137
|
+
` gap: 8px;`,
|
|
138
|
+
`}`,
|
|
139
|
+
`.hiwork-image-card-title {`,
|
|
140
|
+
` font-size: 13px;`,
|
|
141
|
+
` font-weight: 600;`,
|
|
142
|
+
` color: ${textPrimary};`,
|
|
143
|
+
`}`,
|
|
144
|
+
`.hiwork-image-card-summary {`,
|
|
145
|
+
` margin-left: auto;`,
|
|
146
|
+
` font-size: 12px;`,
|
|
147
|
+
` color: ${textTertiary};`,
|
|
148
|
+
` font-variant-numeric: tabular-nums;`,
|
|
149
|
+
`}`,
|
|
150
|
+
`.hiwork-image-card-prompt {`,
|
|
151
|
+
` margin: 0;`,
|
|
152
|
+
` font-size: 12px;`,
|
|
153
|
+
` line-height: 18px;`,
|
|
154
|
+
` color: ${textSecondary};`,
|
|
155
|
+
` white-space: pre-wrap;`,
|
|
156
|
+
` word-break: break-word;`,
|
|
157
|
+
`}`,
|
|
158
|
+
`.hiwork-image-card-figure {`,
|
|
159
|
+
` display: flex;`,
|
|
160
|
+
` flex-direction: column;`,
|
|
161
|
+
` gap: 6px;`,
|
|
162
|
+
` margin: 0;`,
|
|
163
|
+
`}`,
|
|
164
|
+
`.hiwork-image-card-img {`,
|
|
165
|
+
` display: block;`,
|
|
166
|
+
` max-width: 100%;`,
|
|
167
|
+
` max-height: 320px;`,
|
|
168
|
+
` border: 1px solid ${borderL2};`,
|
|
169
|
+
` border-radius: 8px;`,
|
|
170
|
+
` background: ${bgLayer1};`,
|
|
171
|
+
` cursor: zoom-in;`,
|
|
172
|
+
`}`,
|
|
173
|
+
`.hiwork-image-card-path {`,
|
|
174
|
+
` font-size: 11px;`,
|
|
175
|
+
` line-height: 16px;`,
|
|
176
|
+
` color: ${textCaption};`,
|
|
177
|
+
` word-break: break-all;`,
|
|
178
|
+
`}`,
|
|
179
|
+
`.hiwork-image-card-meta {`,
|
|
180
|
+
` font-size: 11px;`,
|
|
181
|
+
` color: ${textTertiary};`,
|
|
182
|
+
` font-variant-numeric: tabular-nums;`,
|
|
183
|
+
`}`,
|
|
184
|
+
`.hiwork-image-card-actions {`,
|
|
185
|
+
` display: flex;`,
|
|
186
|
+
` align-items: center;`,
|
|
187
|
+
` gap: 8px;`,
|
|
188
|
+
` flex-wrap: wrap;`,
|
|
189
|
+
`}`,
|
|
190
|
+
`.hiwork-image-card-action {`,
|
|
191
|
+
` padding: 4px 10px;`,
|
|
192
|
+
` border: 1px solid ${borderL2};`,
|
|
193
|
+
` border-radius: 8px;`,
|
|
194
|
+
` background: transparent;`,
|
|
195
|
+
` color: ${textSecondary};`,
|
|
196
|
+
` font: inherit;`,
|
|
197
|
+
` font-size: 11px;`,
|
|
198
|
+
` line-height: 16px;`,
|
|
199
|
+
` cursor: pointer;`,
|
|
200
|
+
`}`,
|
|
201
|
+
`.hiwork-image-card-action:hover {`,
|
|
202
|
+
` background: ${interactiveHover};`,
|
|
203
|
+
` color: ${textPrimary};`,
|
|
204
|
+
`}`,
|
|
205
|
+
`.hiwork-image-card-copy-state {`,
|
|
206
|
+
` font-size: 11px;`,
|
|
207
|
+
` color: ${textTertiary};`,
|
|
208
|
+
`}`,
|
|
209
|
+
`.hiwork-image-card-error {`,
|
|
210
|
+
` margin: 0;`,
|
|
211
|
+
` font-size: 12px;`,
|
|
212
|
+
` line-height: 18px;`,
|
|
213
|
+
` color: ${errorText};`,
|
|
214
|
+
`}`,
|
|
215
|
+
`.hiwork-image-card-retry-hint {`,
|
|
216
|
+
` margin-left: 6px;`,
|
|
217
|
+
` font-size: 11px;`,
|
|
218
|
+
` color: ${warnText};`,
|
|
219
|
+
`}`,
|
|
220
|
+
/* ---- 放大浮层(半透明黑是浮层自带的,不引用宿主内部变量)---- */
|
|
221
|
+
`.hiwork-image-zoom {`,
|
|
222
|
+
` position: fixed;`,
|
|
223
|
+
` inset: 0;`,
|
|
224
|
+
` z-index: 2147483000;`,
|
|
225
|
+
` display: flex;`,
|
|
226
|
+
` align-items: center;`,
|
|
227
|
+
` justify-content: center;`,
|
|
228
|
+
` padding: 32px;`,
|
|
229
|
+
` background: rgba(0, 0, 0, 0.72);`,
|
|
230
|
+
` cursor: zoom-out;`,
|
|
231
|
+
`}`,
|
|
232
|
+
`.hiwork-image-zoom img {`,
|
|
233
|
+
` max-width: 100%;`,
|
|
234
|
+
` max-height: 100%;`,
|
|
235
|
+
` border-radius: 8px;`,
|
|
236
|
+
` background: ${bgLayer1};`,
|
|
237
|
+
`}`,
|
|
238
|
+
`.hiwork-image-zoom-close {`,
|
|
239
|
+
` position: absolute;`,
|
|
240
|
+
` top: 16px;`,
|
|
241
|
+
` right: 20px;`,
|
|
242
|
+
` padding: 4px 12px;`,
|
|
243
|
+
` border: 1px solid rgba(255, 255, 255, 0.4);`,
|
|
244
|
+
` border-radius: 8px;`,
|
|
245
|
+
` background: transparent;`,
|
|
246
|
+
` color: #ffffff;`,
|
|
247
|
+
` font: inherit;`,
|
|
248
|
+
` font-size: 12px;`,
|
|
249
|
+
` cursor: pointer;`,
|
|
250
|
+
`}`
|
|
251
|
+
].join("\n");
|
|
252
|
+
var imagesPageCss = [
|
|
253
|
+
`.hiwork-images {`,
|
|
254
|
+
` display: flex;`,
|
|
255
|
+
` flex-direction: column;`,
|
|
256
|
+
` gap: 14px;`,
|
|
257
|
+
` box-sizing: border-box;`,
|
|
258
|
+
` height: 100%;`,
|
|
259
|
+
` padding: 20px 24px;`,
|
|
260
|
+
` color: ${textPrimary};`,
|
|
261
|
+
` overflow: hidden;`,
|
|
262
|
+
`}`,
|
|
263
|
+
`.hiwork-images-head {`,
|
|
264
|
+
` display: flex;`,
|
|
265
|
+
` align-items: baseline;`,
|
|
266
|
+
` gap: 10px;`,
|
|
267
|
+
` flex-wrap: wrap;`,
|
|
268
|
+
`}`,
|
|
269
|
+
`.hiwork-images-title {`,
|
|
270
|
+
` margin: 0;`,
|
|
271
|
+
` font-size: 18px;`,
|
|
272
|
+
` font-weight: 600;`,
|
|
273
|
+
` color: ${textPrimary};`,
|
|
274
|
+
`}`,
|
|
275
|
+
`.hiwork-images-count {`,
|
|
276
|
+
` font-size: 12px;`,
|
|
277
|
+
` color: ${textTertiary};`,
|
|
278
|
+
` font-variant-numeric: tabular-nums;`,
|
|
279
|
+
`}`,
|
|
280
|
+
`.hiwork-images-body {`,
|
|
281
|
+
` display: grid;`,
|
|
282
|
+
` grid-template-columns: minmax(240px, 320px) minmax(0, 1fr);`,
|
|
283
|
+
` gap: 16px;`,
|
|
284
|
+
` flex: 1;`,
|
|
285
|
+
` min-height: 0;`,
|
|
286
|
+
`}`,
|
|
287
|
+
`.hiwork-images-list {`,
|
|
288
|
+
` display: flex;`,
|
|
289
|
+
` flex-direction: column;`,
|
|
290
|
+
` gap: 6px;`,
|
|
291
|
+
` box-sizing: border-box;`,
|
|
292
|
+
` margin: 0;`,
|
|
293
|
+
` padding: 8px;`,
|
|
294
|
+
` border: 1px solid ${borderL2};`,
|
|
295
|
+
` border-radius: 10px;`,
|
|
296
|
+
` list-style: none;`,
|
|
297
|
+
` overflow-y: auto;`,
|
|
298
|
+
`}`,
|
|
299
|
+
`.hiwork-images-row {`,
|
|
300
|
+
` display: flex;`,
|
|
301
|
+
` flex-direction: column;`,
|
|
302
|
+
` gap: 4px;`,
|
|
303
|
+
` box-sizing: border-box;`,
|
|
304
|
+
` width: 100%;`,
|
|
305
|
+
` padding: 8px 10px;`,
|
|
306
|
+
` border: 1px solid transparent;`,
|
|
307
|
+
` border-radius: 8px;`,
|
|
308
|
+
` background: transparent;`,
|
|
309
|
+
` color: inherit;`,
|
|
310
|
+
` font: inherit;`,
|
|
311
|
+
` text-align: left;`,
|
|
312
|
+
` cursor: pointer;`,
|
|
313
|
+
`}`,
|
|
314
|
+
`.hiwork-images-row:hover {`,
|
|
315
|
+
` background: ${interactiveHover};`,
|
|
316
|
+
`}`,
|
|
317
|
+
`.hiwork-images-row[aria-pressed="true"] {`,
|
|
318
|
+
` border-color: ${brandAccent};`,
|
|
319
|
+
` background: var(--dsw-alias-interactive-bg-active, rgba(15, 17, 21, 0.1));`,
|
|
320
|
+
`}`,
|
|
321
|
+
`.hiwork-images-row-prompt {`,
|
|
322
|
+
` font-size: 12px;`,
|
|
323
|
+
` line-height: 18px;`,
|
|
324
|
+
` color: ${textPrimary};`,
|
|
325
|
+
` overflow: hidden;`,
|
|
326
|
+
` text-overflow: ellipsis;`,
|
|
327
|
+
` white-space: nowrap;`,
|
|
328
|
+
`}`,
|
|
329
|
+
`.hiwork-images-row-meta {`,
|
|
330
|
+
` font-size: 11px;`,
|
|
331
|
+
` color: ${textTertiary};`,
|
|
332
|
+
` font-variant-numeric: tabular-nums;`,
|
|
333
|
+
`}`,
|
|
334
|
+
`.hiwork-images-preview {`,
|
|
335
|
+
` display: flex;`,
|
|
336
|
+
` flex-direction: column;`,
|
|
337
|
+
` gap: 10px;`,
|
|
338
|
+
` box-sizing: border-box;`,
|
|
339
|
+
` min-width: 0;`,
|
|
340
|
+
` min-height: 0;`,
|
|
341
|
+
` padding: 14px;`,
|
|
342
|
+
` border: 1px solid ${borderL2};`,
|
|
343
|
+
` border-radius: 10px;`,
|
|
344
|
+
` background: ${bgLayer2};`,
|
|
345
|
+
` overflow: auto;`,
|
|
346
|
+
`}`,
|
|
347
|
+
`.hiwork-images-image {`,
|
|
348
|
+
` display: block;`,
|
|
349
|
+
` max-width: 100%;`,
|
|
350
|
+
` max-height: 60vh;`,
|
|
351
|
+
` margin: 0 auto;`,
|
|
352
|
+
` border-radius: 8px;`,
|
|
353
|
+
` background: ${bgLayer1};`,
|
|
354
|
+
`}`,
|
|
355
|
+
`.hiwork-images-preview-body {`,
|
|
356
|
+
` display: flex;`,
|
|
357
|
+
` flex-direction: column;`,
|
|
358
|
+
` gap: 10px;`,
|
|
359
|
+
` min-height: 0;`,
|
|
360
|
+
`}`,
|
|
361
|
+
`.hiwork-images-empty {`,
|
|
362
|
+
` display: flex;`,
|
|
363
|
+
` flex-direction: column;`,
|
|
364
|
+
` gap: 6px;`,
|
|
365
|
+
` align-items: flex-start;`,
|
|
366
|
+
` padding: 24px 4px;`,
|
|
367
|
+
`}`,
|
|
368
|
+
`.hiwork-images-preview-prompt {`,
|
|
369
|
+
` margin: 0;`,
|
|
370
|
+
` font-size: 12px;`,
|
|
371
|
+
` line-height: 18px;`,
|
|
372
|
+
` color: ${textSecondary};`,
|
|
373
|
+
` white-space: pre-wrap;`,
|
|
374
|
+
` word-break: break-word;`,
|
|
375
|
+
`}`,
|
|
376
|
+
`.hiwork-images-note {`,
|
|
377
|
+
` margin: 0;`,
|
|
378
|
+
` font-size: 12px;`,
|
|
379
|
+
` line-height: 18px;`,
|
|
380
|
+
` color: ${textTertiary};`,
|
|
381
|
+
`}`,
|
|
382
|
+
`.hiwork-images-empty-title {`,
|
|
383
|
+
` margin: 0;`,
|
|
384
|
+
` font-size: 14px;`,
|
|
385
|
+
` font-weight: 600;`,
|
|
386
|
+
` color: ${textSecondary};`,
|
|
387
|
+
`}`,
|
|
388
|
+
`.hiwork-images-error {`,
|
|
389
|
+
` margin: 0;`,
|
|
390
|
+
` font-size: 12px;`,
|
|
391
|
+
` line-height: 18px;`,
|
|
392
|
+
` color: ${errorText};`,
|
|
393
|
+
`}`,
|
|
394
|
+
`.hiwork-images-banner {`,
|
|
395
|
+
` display: flex;`,
|
|
396
|
+
` align-items: center;`,
|
|
397
|
+
` gap: 8px;`,
|
|
398
|
+
` padding: 8px 10px;`,
|
|
399
|
+
` border-radius: 8px;`,
|
|
400
|
+
` background: ${warnFill};`,
|
|
401
|
+
` color: ${warnText};`,
|
|
402
|
+
` font-size: 12px;`,
|
|
403
|
+
` line-height: 18px;`,
|
|
404
|
+
`}`,
|
|
405
|
+
`.hiwork-images-saved {`,
|
|
406
|
+
` font-size: 11px;`,
|
|
407
|
+
` line-height: 16px;`,
|
|
408
|
+
` color: ${textSecondary};`,
|
|
409
|
+
` word-break: break-all;`,
|
|
410
|
+
`}`,
|
|
411
|
+
`.hiwork-images-actions {`,
|
|
412
|
+
` display: flex;`,
|
|
413
|
+
` align-items: center;`,
|
|
414
|
+
` gap: 8px;`,
|
|
415
|
+
` flex-wrap: wrap;`,
|
|
416
|
+
`}`,
|
|
417
|
+
`.hiwork-images-action {`,
|
|
418
|
+
` padding: 5px 12px;`,
|
|
419
|
+
` border: 1px solid ${borderL2};`,
|
|
420
|
+
` border-radius: 8px;`,
|
|
421
|
+
` background: transparent;`,
|
|
422
|
+
` color: ${textSecondary};`,
|
|
423
|
+
` font: inherit;`,
|
|
424
|
+
` font-size: 12px;`,
|
|
425
|
+
` line-height: 18px;`,
|
|
426
|
+
` cursor: pointer;`,
|
|
427
|
+
`}`,
|
|
428
|
+
`.hiwork-images-action:hover:not(:disabled) {`,
|
|
429
|
+
` background: ${interactiveHover};`,
|
|
430
|
+
` color: ${textPrimary};`,
|
|
431
|
+
`}`,
|
|
432
|
+
`.hiwork-images-action:disabled {`,
|
|
433
|
+
` opacity: 0.45;`,
|
|
434
|
+
` cursor: not-allowed;`,
|
|
435
|
+
`}`,
|
|
436
|
+
`.hiwork-images-more {`,
|
|
437
|
+
` padding: 6px 12px;`,
|
|
438
|
+
` border: 1px solid ${borderL2};`,
|
|
439
|
+
` border-radius: 8px;`,
|
|
440
|
+
` background: transparent;`,
|
|
441
|
+
` color: ${textSecondary};`,
|
|
442
|
+
` font: inherit;`,
|
|
443
|
+
` font-size: 12px;`,
|
|
444
|
+
` cursor: pointer;`,
|
|
445
|
+
`}`,
|
|
446
|
+
`.hiwork-images-more:hover:not(:disabled) {`,
|
|
447
|
+
` background: ${interactiveHover};`,
|
|
448
|
+
`}`
|
|
114
449
|
].join("\n");
|
|
115
450
|
var CORE_STYLE_OWNER = "hiwork-core";
|
|
116
451
|
function ownStyleTag(style, cssTagId) {
|
|
@@ -386,12 +721,12 @@ function openExternalUrl(url) {
|
|
|
386
721
|
function openAuthorizeUrl(url) {
|
|
387
722
|
openExternalUrl(url);
|
|
388
723
|
}
|
|
389
|
-
async function requestBrowserLogin(
|
|
390
|
-
await
|
|
724
|
+
async function requestBrowserLogin(form) {
|
|
725
|
+
await form.set("browserLoginRequest", { at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
391
726
|
}
|
|
392
|
-
async function clearBrowserLogin(
|
|
393
|
-
await
|
|
394
|
-
await
|
|
727
|
+
async function clearBrowserLogin(form) {
|
|
728
|
+
await form.unset("browserLoginRequest");
|
|
729
|
+
await form.unset("browserLogin");
|
|
395
730
|
}
|
|
396
731
|
function project(value) {
|
|
397
732
|
const base = {
|
|
@@ -419,8 +754,8 @@ function project(value) {
|
|
|
419
754
|
return base;
|
|
420
755
|
}
|
|
421
756
|
var AiRouterCardController = class {
|
|
422
|
-
constructor(
|
|
423
|
-
this.
|
|
757
|
+
constructor(form, storeCredential) {
|
|
758
|
+
this.form = form;
|
|
424
759
|
this.storeCredential = storeCredential;
|
|
425
760
|
this.store = (0, import_dsh_client_store.createSnapshotStore)({
|
|
426
761
|
status: "ready",
|
|
@@ -439,10 +774,10 @@ var AiRouterCardController = class {
|
|
|
439
774
|
browserLoginPending: false
|
|
440
775
|
});
|
|
441
776
|
const publish = () => {
|
|
442
|
-
const snapshot = this.
|
|
777
|
+
const snapshot = this.form.getSnapshot();
|
|
443
778
|
if (snapshot.status === "ready") this.store.set(project(snapshot.value));
|
|
444
779
|
};
|
|
445
|
-
this.
|
|
780
|
+
this.form.subscribe(() => publish());
|
|
446
781
|
publish();
|
|
447
782
|
}
|
|
448
783
|
store;
|
|
@@ -462,24 +797,24 @@ var AiRouterCardController = class {
|
|
|
462
797
|
}
|
|
463
798
|
async submitLogin(username, password) {
|
|
464
799
|
await this.storeCredential(PENDING_PASSWORD_REF, password);
|
|
465
|
-
await this.
|
|
800
|
+
await this.form.set("pendingLogin", { username, passwordRef: PENDING_PASSWORD_REF });
|
|
466
801
|
}
|
|
467
802
|
/** 切换 Key:先落 keyId 配置,再触发一次刷新(Host 以新 keyId 重拉额度与模型)。 */
|
|
468
803
|
async selectKey(keyId) {
|
|
469
|
-
await this.
|
|
470
|
-
await this.
|
|
804
|
+
await this.form.set("keyId", keyId);
|
|
805
|
+
await this.form.set("refreshRequest", { at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
471
806
|
}
|
|
472
807
|
async refresh() {
|
|
473
|
-
await this.
|
|
808
|
+
await this.form.set("refreshRequest", { at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
474
809
|
}
|
|
475
810
|
async logout() {
|
|
476
|
-
await this.
|
|
811
|
+
await this.form.set("logoutRequest", { at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
477
812
|
}
|
|
478
813
|
async startBrowserLogin() {
|
|
479
|
-
await requestBrowserLogin(this.
|
|
814
|
+
await requestBrowserLogin(this.form);
|
|
480
815
|
}
|
|
481
816
|
async cancelBrowserLogin() {
|
|
482
|
-
await clearBrowserLogin(this.
|
|
817
|
+
await clearBrowserLogin(this.form);
|
|
483
818
|
}
|
|
484
819
|
/** 重新打开授权页:URL 取自最近一次投影,缺省 no-op(登录门禁另有自动打开)。 */
|
|
485
820
|
reopenBrowserLogin() {
|
|
@@ -1198,7 +1533,7 @@ function UserBadge(props) {
|
|
|
1198
1533
|
title: "\u8BBE\u7F6E",
|
|
1199
1534
|
"aria-label": "\u8BBE\u7F6E",
|
|
1200
1535
|
onClick: openGatewaySettings,
|
|
1201
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives.
|
|
1536
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_dsh_client_ui_primitives.IconSettingsOutlineMedium, { size: 16 })
|
|
1202
1537
|
}
|
|
1203
1538
|
),
|
|
1204
1539
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
@@ -1235,7 +1570,7 @@ var logo_with_text_default = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACSYA
|
|
|
1235
1570
|
var HIWORK_LOGO = logo_default;
|
|
1236
1571
|
var HIWORK_LOGO_TEXT = logo_text_default;
|
|
1237
1572
|
var HIWORK_LOGO_WITH_TEXT = logo_with_text_default;
|
|
1238
|
-
var HIWORK_VERSION = "1.0.
|
|
1573
|
+
var HIWORK_VERSION = "1.0.7";
|
|
1239
1574
|
|
|
1240
1575
|
// src/client/brand.tsx
|
|
1241
1576
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -1321,13 +1656,123 @@ function createAppVersion(rpc) {
|
|
|
1321
1656
|
};
|
|
1322
1657
|
}
|
|
1323
1658
|
|
|
1659
|
+
// src/client/about-section.tsx
|
|
1660
|
+
var import_react4 = require("react");
|
|
1661
|
+
|
|
1662
|
+
// src/client/manual-link.ts
|
|
1663
|
+
var MANUAL_URL = "https://hiworkapp.hivery.cn/manual.html";
|
|
1664
|
+
var MANUAL_STYLE_ID = "hiwork-core-manual-style";
|
|
1665
|
+
var MANUAL_STYLE = `
|
|
1666
|
+
[data-hiwork-core-manual-entry]{box-sizing:border-box;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin-left:auto;padding:0;border:0;border-radius:999px;background:transparent;color:var(--dsw-alias-label-tertiary,#71717a);font-family:inherit;font-size:14px;line-height:1;font-weight:600;cursor:pointer;user-select:none;transition:background .16s ease,color .16s ease}
|
|
1667
|
+
[data-hiwork-core-manual-entry]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(24,24,27,.06));color:var(--dsw-alias-label-secondary,#3f3f46)}
|
|
1668
|
+
[data-hiwork-core-manual-entry]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}
|
|
1669
|
+
body[data-ds-dark-theme] [data-hiwork-core-manual-entry]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}
|
|
1670
|
+
body[data-ds-dark-theme] [data-hiwork-core-manual-entry]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08));color:var(--dsw-alias-label-secondary,#a6adb8)}
|
|
1671
|
+
@media (prefers-reduced-motion: reduce){[data-hiwork-core-manual-entry]{transition:none}}
|
|
1672
|
+
`;
|
|
1673
|
+
function ensureManualStyle(doc) {
|
|
1674
|
+
if (doc.getElementById(MANUAL_STYLE_ID) !== null) return;
|
|
1675
|
+
if (doc.head === null) return;
|
|
1676
|
+
const style = doc.createElement("style");
|
|
1677
|
+
style.id = MANUAL_STYLE_ID;
|
|
1678
|
+
ownStyleTag(style, "hiwork-core/manual-link.css");
|
|
1679
|
+
style.textContent = MANUAL_STYLE;
|
|
1680
|
+
doc.head.append(style);
|
|
1681
|
+
}
|
|
1682
|
+
function openManual() {
|
|
1683
|
+
openExternalUrl(MANUAL_URL);
|
|
1684
|
+
}
|
|
1685
|
+
function findManualMount(doc) {
|
|
1686
|
+
const hero = doc.querySelector('[data-phase="hero"]');
|
|
1687
|
+
if (hero === null) return null;
|
|
1688
|
+
return hero.querySelector('[class*="_heroWorkspaceRow"]');
|
|
1689
|
+
}
|
|
1690
|
+
function buildButton(doc) {
|
|
1691
|
+
const button = doc.createElement("button");
|
|
1692
|
+
button.dataset.hiworkCoreManualEntry = "";
|
|
1693
|
+
button.type = "button";
|
|
1694
|
+
button.title = "\u4F7F\u7528\u624B\u518C";
|
|
1695
|
+
button.setAttribute("aria-label", "\u4F7F\u7528\u624B\u518C");
|
|
1696
|
+
button.append(doc.createTextNode("?"));
|
|
1697
|
+
button.addEventListener("click", (event) => {
|
|
1698
|
+
event.preventDefault();
|
|
1699
|
+
event.stopPropagation();
|
|
1700
|
+
openManual();
|
|
1701
|
+
});
|
|
1702
|
+
return button;
|
|
1703
|
+
}
|
|
1704
|
+
function removeStaleManualEntry(doc) {
|
|
1705
|
+
for (const stale of Array.from(doc.querySelectorAll("[data-hiwork-core-manual-entry]"))) {
|
|
1706
|
+
stale.remove();
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
function decorateManualEntry(doc) {
|
|
1710
|
+
const row = findManualMount(doc);
|
|
1711
|
+
if (row === null) {
|
|
1712
|
+
removeStaleManualEntry(doc);
|
|
1713
|
+
return;
|
|
1714
|
+
}
|
|
1715
|
+
const existing = row.querySelector(":scope > [data-hiwork-core-manual-entry]");
|
|
1716
|
+
if (existing !== null) return;
|
|
1717
|
+
removeStaleManualEntry(doc);
|
|
1718
|
+
row.append(buildButton(doc));
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1324
1721
|
// src/client/about-section.tsx
|
|
1325
1722
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1326
1723
|
var KEYWORDS = ["\u667A\u80FD\u4F1A\u8BDD", "\u5C97\u4F4D\u667A\u80FD\u4F53", "\u6280\u80FD\u4E2D\u5FC3", "\u4F01\u4E1A IM \u96C6\u6210", "\u4F1A\u8BDD\u5F52\u6863", "\u5B9A\u65F6\u81EA\u52A8\u5316"];
|
|
1724
|
+
var WEBSITE_ORIGIN = new URL(MANUAL_URL).origin;
|
|
1725
|
+
var WEBSITE_HOST = new URL(MANUAL_URL).host;
|
|
1726
|
+
function versionClipboardText(version) {
|
|
1727
|
+
return `HiWork v${version}`;
|
|
1728
|
+
}
|
|
1729
|
+
async function copyVersionText(text6) {
|
|
1730
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
1731
|
+
if (clipboard === void 0 || typeof clipboard.writeText !== "function") return false;
|
|
1732
|
+
try {
|
|
1733
|
+
await clipboard.writeText(text6);
|
|
1734
|
+
return true;
|
|
1735
|
+
} catch {
|
|
1736
|
+
return false;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
var COPY_HINTS = { idle: "\u70B9\u51FB\u590D\u5236", copied: "\u5DF2\u590D\u5236", failed: "\u590D\u5236\u5931\u8D25" };
|
|
1740
|
+
var COPY_HINT_REVERT_MS = 1800;
|
|
1741
|
+
function ExternalLinkButton({ url, text: text6 }) {
|
|
1742
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1743
|
+
"button",
|
|
1744
|
+
{
|
|
1745
|
+
type: "button",
|
|
1746
|
+
"data-hiwork-core-linktext": true,
|
|
1747
|
+
style: linkValueStyle,
|
|
1748
|
+
title: `\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\uFF1A${url}`,
|
|
1749
|
+
onClick: () => {
|
|
1750
|
+
openExternalUrl(url);
|
|
1751
|
+
},
|
|
1752
|
+
children: [
|
|
1753
|
+
text6,
|
|
1754
|
+
" \u2197"
|
|
1755
|
+
]
|
|
1756
|
+
}
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1327
1759
|
var sectionStyle2 = { color: "var(--dsw-alias-label-primary)" };
|
|
1328
1760
|
var brandStyle = { display: "block", height: 32, width: "auto" };
|
|
1329
1761
|
var taglineStyle = { margin: "12px 0 0", fontSize: 15, fontWeight: 600, lineHeight: "22px" };
|
|
1330
1762
|
var subnoteStyle = { margin: "4px 0 0", color: "var(--dsw-alias-label-secondary)", fontSize: 12, lineHeight: "18px" };
|
|
1763
|
+
var versionRowStyle = { display: "flex", alignItems: "baseline", gap: 8, margin: "10px 0 0" };
|
|
1764
|
+
var versionValueStyle = {
|
|
1765
|
+
padding: 0,
|
|
1766
|
+
border: 0,
|
|
1767
|
+
background: "transparent",
|
|
1768
|
+
cursor: "pointer",
|
|
1769
|
+
font: "inherit",
|
|
1770
|
+
fontSize: 13,
|
|
1771
|
+
fontWeight: 600,
|
|
1772
|
+
lineHeight: "18px",
|
|
1773
|
+
color: textPrimary
|
|
1774
|
+
};
|
|
1775
|
+
var versionHintStyle = { color: textTertiary, fontSize: 11, lineHeight: "18px" };
|
|
1331
1776
|
var dividerStyle = { margin: "24px 0 16px", border: 0, borderTop: "1px solid var(--dsw-alias-border-l2)" };
|
|
1332
1777
|
var keywordsStyle = { display: "flex", flexWrap: "wrap", gap: 8, margin: 0, padding: 0, listStyle: "none" };
|
|
1333
1778
|
var keywordStyle = {
|
|
@@ -1339,24 +1784,100 @@ var keywordStyle = {
|
|
|
1339
1784
|
lineHeight: "16px"
|
|
1340
1785
|
};
|
|
1341
1786
|
var securityStyle = { margin: "20px 0 0", color: "var(--dsw-alias-label-tertiary)", fontSize: 12, lineHeight: "18px" };
|
|
1787
|
+
var metaListStyle = { display: "flex", flexDirection: "column", gap: 10, margin: 0 };
|
|
1788
|
+
var metaRowStyle = { display: "flex", alignItems: "baseline", gap: 12 };
|
|
1789
|
+
var metaLabelStyle = { flex: "none", width: "4em", color: textTertiary, fontSize: 12, lineHeight: "18px" };
|
|
1790
|
+
var metaValueStyle = { margin: 0, color: textSecondary, fontSize: 12, lineHeight: "18px", minWidth: 0 };
|
|
1791
|
+
var linkValueStyle = {
|
|
1792
|
+
padding: 0,
|
|
1793
|
+
border: 0,
|
|
1794
|
+
background: "transparent",
|
|
1795
|
+
cursor: "pointer",
|
|
1796
|
+
textAlign: "left",
|
|
1797
|
+
font: "inherit",
|
|
1798
|
+
fontSize: 12,
|
|
1799
|
+
lineHeight: "18px",
|
|
1800
|
+
color: brandAccent
|
|
1801
|
+
};
|
|
1342
1802
|
var copyrightStyle = { margin: "8px 0 0", color: "var(--dsw-alias-label-tertiary)", fontSize: 12, lineHeight: "18px" };
|
|
1343
|
-
function AboutSection(
|
|
1803
|
+
function AboutSection({ appVersion }) {
|
|
1804
|
+
const [version, setVersion] = (0, import_react4.useState)(void 0);
|
|
1805
|
+
const [copyHint, setCopyHint] = (0, import_react4.useState)("idle");
|
|
1806
|
+
const revertTimer = (0, import_react4.useRef)(void 0);
|
|
1807
|
+
(0, import_react4.useEffect)(() => {
|
|
1808
|
+
let alive = true;
|
|
1809
|
+
void appVersion.version().then(
|
|
1810
|
+
(value) => {
|
|
1811
|
+
if (alive) setVersion(value ?? null);
|
|
1812
|
+
},
|
|
1813
|
+
() => {
|
|
1814
|
+
if (alive) setVersion(null);
|
|
1815
|
+
}
|
|
1816
|
+
);
|
|
1817
|
+
return () => {
|
|
1818
|
+
alive = false;
|
|
1819
|
+
};
|
|
1820
|
+
}, [appVersion]);
|
|
1821
|
+
(0, import_react4.useEffect)(() => () => {
|
|
1822
|
+
if (revertTimer.current !== void 0) window.clearTimeout(revertTimer.current);
|
|
1823
|
+
}, []);
|
|
1824
|
+
const onCopyVersion = () => {
|
|
1825
|
+
if (version === void 0) return;
|
|
1826
|
+
void copyVersionText(versionClipboardText(version ?? HIWORK_VERSION)).then((ok) => {
|
|
1827
|
+
setCopyHint(ok ? "copied" : "failed");
|
|
1828
|
+
if (revertTimer.current !== void 0) window.clearTimeout(revertTimer.current);
|
|
1829
|
+
revertTimer.current = window.setTimeout(() => {
|
|
1830
|
+
setCopyHint("idle");
|
|
1831
|
+
}, COPY_HINT_REVERT_MS);
|
|
1832
|
+
});
|
|
1833
|
+
};
|
|
1344
1834
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { "aria-label": "\u5173\u4E8E", style: sectionStyle2, children: [
|
|
1345
1835
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("header", { children: [
|
|
1346
1836
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: HIWORK_LOGO_WITH_TEXT, alt: "HiWork", className: "hiwork-brand-wordmark", style: brandStyle }),
|
|
1347
1837
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: taglineStyle, children: "\u667A\u8054\u4E07\u7269\uFF0C\u4ECE\u667A\u8054\u5DE5\u4F5C\u5F00\u59CB\u3002" }),
|
|
1348
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: subnoteStyle, children: "\u6E90\u81EA\u6D77\u666E HiCap \u7684\u4F01\u4E1A\u7EA7 AI \u5DE5\u4F5C\u53F0" })
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: subnoteStyle, children: "\u6E90\u81EA\u6D77\u666E HiCap \u7684\u4F01\u4E1A\u7EA7 AI \u5DE5\u4F5C\u53F0" }),
|
|
1839
|
+
version === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { style: versionRowStyle, children: [
|
|
1840
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
1841
|
+
"button",
|
|
1842
|
+
{
|
|
1843
|
+
type: "button",
|
|
1844
|
+
"data-hiwork-core-linktext": true,
|
|
1845
|
+
style: versionValueStyle,
|
|
1846
|
+
title: "\u70B9\u51FB\u590D\u5236\u7248\u672C\u4FE1\u606F",
|
|
1847
|
+
"aria-label": `\u590D\u5236\u7248\u672C\u4FE1\u606F v${version ?? HIWORK_VERSION}`,
|
|
1848
|
+
onClick: onCopyVersion,
|
|
1849
|
+
children: [
|
|
1850
|
+
"v",
|
|
1851
|
+
version ?? HIWORK_VERSION
|
|
1852
|
+
]
|
|
1853
|
+
}
|
|
1854
|
+
),
|
|
1855
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: versionHintStyle, children: COPY_HINTS[copyHint] })
|
|
1856
|
+
] })
|
|
1349
1857
|
] }),
|
|
1350
1858
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("hr", { style: dividerStyle }),
|
|
1351
1859
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { style: keywordsStyle, children: KEYWORDS.map((keyword) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { style: keywordStyle, children: keyword }, keyword)) }),
|
|
1352
1860
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: securityStyle, children: "\u6570\u636E\u4E0D\u51FA\u57DF \xB7 \u4E0D\u7528\u4E8E\u8BAD\u7EC3 \xB7 \u5168\u94FE\u8DEF\u52A0\u5BC6" }),
|
|
1353
|
-
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("
|
|
1354
|
-
|
|
1355
|
-
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("hr", { style: dividerStyle }),
|
|
1862
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("dl", { style: metaListStyle, children: [
|
|
1863
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: metaRowStyle, children: [
|
|
1864
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dt", { style: metaLabelStyle, children: "\u5B98\u65B9\u7F51\u7AD9" }),
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dd", { style: metaValueStyle, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExternalLinkButton, { url: WEBSITE_ORIGIN, text: WEBSITE_HOST }) })
|
|
1866
|
+
] }),
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: metaRowStyle, children: [
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dt", { style: metaLabelStyle, children: "\u4F7F\u7528\u624B\u518C" }),
|
|
1869
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("dd", { style: metaValueStyle, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExternalLinkButton, { url: MANUAL_URL, text: "\u5728\u7EBF\u67E5\u770B" }) })
|
|
1870
|
+
] })
|
|
1871
|
+
] }),
|
|
1872
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: copyrightStyle, children: "\xA9 2026 \u6D77\u666E\u667A\u8054\u79D1\u6280\u80A1\u4EFD\u6709\u9650\u516C\u53F8 \xB7 \u4EC5\u9650\u5185\u90E8\u5458\u5DE5\u4F7F\u7528" })
|
|
1873
|
+
] });
|
|
1874
|
+
}
|
|
1356
1875
|
|
|
1357
1876
|
// src/client/daily-data.ts
|
|
1358
1877
|
var DAILY_CHANNEL = "/hiwork-daily";
|
|
1359
1878
|
var DAILY_FAILURE_TTL_MS = 5 * 6e4;
|
|
1879
|
+
var DAILY_LOCAL_RETRY_MS = 2e3;
|
|
1880
|
+
var DAILY_LOCAL_FAILURE_CODES = ["SESSION_EXPIRED", "GATEWAY_UNREACHABLE"];
|
|
1360
1881
|
var DailyRpcError = class extends Error {
|
|
1361
1882
|
constructor(code, message) {
|
|
1362
1883
|
super(message);
|
|
@@ -1428,27 +1949,38 @@ var DailyDataStore = class {
|
|
|
1428
1949
|
this.now = options.now ?? (() => Date.now());
|
|
1429
1950
|
this.localDay = options.localDay ?? (() => localDayIndex());
|
|
1430
1951
|
this.failureTtlMs = options.failureTtlMs ?? DAILY_FAILURE_TTL_MS;
|
|
1952
|
+
this.localRetryMs = options.localRetryMs ?? DAILY_LOCAL_RETRY_MS;
|
|
1431
1953
|
}
|
|
1432
1954
|
entry;
|
|
1433
1955
|
failureAt;
|
|
1956
|
+
/** 上一次失败是否为宿主本地前置失败({@link DAILY_LOCAL_FAILURE_CODES})——决定节流用哪个 TTL。 */
|
|
1957
|
+
localFailure = false;
|
|
1434
1958
|
pending;
|
|
1435
1959
|
now;
|
|
1436
1960
|
localDay;
|
|
1437
1961
|
failureTtlMs;
|
|
1962
|
+
localRetryMs;
|
|
1438
1963
|
/** 清空缓存与防抖状态(登出 / 换账号时调用)。 */
|
|
1439
1964
|
clear() {
|
|
1440
1965
|
this.entry = void 0;
|
|
1441
1966
|
this.failureAt = void 0;
|
|
1967
|
+
this.localFailure = false;
|
|
1442
1968
|
this.pending = void 0;
|
|
1443
1969
|
}
|
|
1444
1970
|
/**
|
|
1445
|
-
* 当日条目:同一本地日内命中缓存直接返回(不发 RPC
|
|
1446
|
-
*
|
|
1971
|
+
* 当日条目:同一本地日内命中缓存直接返回(不发 RPC);节流期内返回 null 不重复打网关;
|
|
1972
|
+
* 并发调用合并为同一次 RPC。返回 null 即「暂无可展示内容」。
|
|
1973
|
+
*
|
|
1974
|
+
* 节流时长分两档:宿主本地前置失败(未登录 / 网关未配置)只等 {@link DAILY_LOCAL_RETRY_MS},
|
|
1975
|
+
* 其余失败等 {@link DAILY_FAILURE_TTL_MS}——理由见两个常量的说明(首次安装必先启动后登录)。
|
|
1447
1976
|
*/
|
|
1448
1977
|
async getToday() {
|
|
1449
1978
|
const day = this.localDay();
|
|
1450
1979
|
if (this.entry !== void 0 && this.entry.day === day) return this.entry.view;
|
|
1451
|
-
if (this.failureAt !== void 0
|
|
1980
|
+
if (this.failureAt !== void 0) {
|
|
1981
|
+
const ttl = this.localFailure ? this.localRetryMs : this.failureTtlMs;
|
|
1982
|
+
if (this.now() - this.failureAt < ttl) return null;
|
|
1983
|
+
}
|
|
1452
1984
|
if (this.pending === void 0) {
|
|
1453
1985
|
this.pending = this.fetchToday(day).finally(() => {
|
|
1454
1986
|
this.pending = void 0;
|
|
@@ -1470,17 +2002,25 @@ var DailyDataStore = class {
|
|
|
1470
2002
|
return null;
|
|
1471
2003
|
}
|
|
1472
2004
|
}
|
|
1473
|
-
/**
|
|
2005
|
+
/**
|
|
2006
|
+
* 单次 today 拉取:成功(含池空)写缓存;任何失败进入节流并返回 null。
|
|
2007
|
+
*
|
|
2008
|
+
* 失败分两档记:{@link DAILY_LOCAL_FAILURE_CODES} 是宿主在打网之前判定的(无网关流量、登录后必然变化),
|
|
2009
|
+
* 只记短节流;其余(网关透传失败、形状非法、未知错误)记满 {@link DAILY_FAILURE_TTL_MS}。
|
|
2010
|
+
*/
|
|
1474
2011
|
async fetchToday(day) {
|
|
1475
2012
|
try {
|
|
1476
2013
|
const raw = await this.rpc.call(DAILY_CHANNEL, "today", {});
|
|
1477
2014
|
const response = parseTodayResponse(unwrapDailyResult(raw));
|
|
1478
2015
|
if (response === void 0) throw new DailyRpcError("internal", "\u6BCF\u65E5\u4E00\u53E5\u4E3B\u673A\u8FD4\u56DE\u4E86\u65E0\u6548\u54CD\u5E94\u3002");
|
|
1479
2016
|
this.failureAt = void 0;
|
|
2017
|
+
this.localFailure = false;
|
|
1480
2018
|
const view = response.item === null ? null : { ...response.item, date: response.date };
|
|
1481
2019
|
this.entry = { view, day };
|
|
1482
2020
|
return view;
|
|
1483
|
-
} catch {
|
|
2021
|
+
} catch (error) {
|
|
2022
|
+
const code = error instanceof DailyRpcError ? error.code : "";
|
|
2023
|
+
this.localFailure = DAILY_LOCAL_FAILURE_CODES.includes(code);
|
|
1484
2024
|
this.failureAt = this.now();
|
|
1485
2025
|
return null;
|
|
1486
2026
|
}
|
|
@@ -1551,65 +2091,6 @@ function removeStaleHeroGreeting(doc) {
|
|
|
1551
2091
|
for (const stale of Array.from(doc.querySelectorAll("[data-hiwork-hero-greeting]"))) stale.remove();
|
|
1552
2092
|
}
|
|
1553
2093
|
|
|
1554
|
-
// src/client/manual-link.ts
|
|
1555
|
-
var MANUAL_URL = "https://hiworkapp.hivery.cn/manual.html";
|
|
1556
|
-
var MANUAL_STYLE_ID = "hiwork-core-manual-style";
|
|
1557
|
-
var MANUAL_STYLE = `
|
|
1558
|
-
[data-hiwork-core-manual-entry]{box-sizing:border-box;flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:28px;margin-left:auto;padding:0;border:0;border-radius:999px;background:transparent;color:var(--dsw-alias-label-tertiary,#71717a);font-family:inherit;font-size:14px;line-height:1;font-weight:600;cursor:pointer;user-select:none;transition:background .16s ease,color .16s ease}
|
|
1559
|
-
[data-hiwork-core-manual-entry]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(24,24,27,.06));color:var(--dsw-alias-label-secondary,#3f3f46)}
|
|
1560
|
-
[data-hiwork-core-manual-entry]:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#4c8dff);outline-offset:2px}
|
|
1561
|
-
body[data-ds-dark-theme] [data-hiwork-core-manual-entry]{color:var(--dsw-alias-label-tertiary,#9ba3b4)}
|
|
1562
|
-
body[data-ds-dark-theme] [data-hiwork-core-manual-entry]:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(255,255,255,.08));color:var(--dsw-alias-label-secondary,#a6adb8)}
|
|
1563
|
-
@media (prefers-reduced-motion: reduce){[data-hiwork-core-manual-entry]{transition:none}}
|
|
1564
|
-
`;
|
|
1565
|
-
function ensureManualStyle(doc) {
|
|
1566
|
-
if (doc.getElementById(MANUAL_STYLE_ID) !== null) return;
|
|
1567
|
-
if (doc.head === null) return;
|
|
1568
|
-
const style = doc.createElement("style");
|
|
1569
|
-
style.id = MANUAL_STYLE_ID;
|
|
1570
|
-
ownStyleTag(style, "hiwork-core/manual-link.css");
|
|
1571
|
-
style.textContent = MANUAL_STYLE;
|
|
1572
|
-
doc.head.append(style);
|
|
1573
|
-
}
|
|
1574
|
-
function openManual() {
|
|
1575
|
-
openExternalUrl(MANUAL_URL);
|
|
1576
|
-
}
|
|
1577
|
-
function findManualMount(doc) {
|
|
1578
|
-
const hero = doc.querySelector('[data-phase="hero"]');
|
|
1579
|
-
if (hero === null) return null;
|
|
1580
|
-
return hero.querySelector('[class*="_heroWorkspaceRow"]');
|
|
1581
|
-
}
|
|
1582
|
-
function buildButton(doc) {
|
|
1583
|
-
const button = doc.createElement("button");
|
|
1584
|
-
button.dataset.hiworkCoreManualEntry = "";
|
|
1585
|
-
button.type = "button";
|
|
1586
|
-
button.title = "\u4F7F\u7528\u624B\u518C";
|
|
1587
|
-
button.setAttribute("aria-label", "\u4F7F\u7528\u624B\u518C");
|
|
1588
|
-
button.append(doc.createTextNode("?"));
|
|
1589
|
-
button.addEventListener("click", (event) => {
|
|
1590
|
-
event.preventDefault();
|
|
1591
|
-
event.stopPropagation();
|
|
1592
|
-
openManual();
|
|
1593
|
-
});
|
|
1594
|
-
return button;
|
|
1595
|
-
}
|
|
1596
|
-
function removeStaleManualEntry(doc) {
|
|
1597
|
-
for (const stale of Array.from(doc.querySelectorAll("[data-hiwork-core-manual-entry]"))) {
|
|
1598
|
-
stale.remove();
|
|
1599
|
-
}
|
|
1600
|
-
}
|
|
1601
|
-
function decorateManualEntry(doc) {
|
|
1602
|
-
const row = findManualMount(doc);
|
|
1603
|
-
if (row === null) {
|
|
1604
|
-
removeStaleManualEntry(doc);
|
|
1605
|
-
return;
|
|
1606
|
-
}
|
|
1607
|
-
const existing = row.querySelector(":scope > [data-hiwork-core-manual-entry]");
|
|
1608
|
-
if (existing !== null) return;
|
|
1609
|
-
removeStaleManualEntry(doc);
|
|
1610
|
-
row.append(buildButton(doc));
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
2094
|
// src/client/daily-card.ts
|
|
1614
2095
|
var DAILY_CARD_STYLE_ID = "dcu-daily-card-style";
|
|
1615
2096
|
var DAILY_MAIN_FONT_SIZE_MAX = 64;
|
|
@@ -1991,7 +2472,7 @@ function observeAboutNavIcon(doc = document) {
|
|
|
1991
2472
|
}
|
|
1992
2473
|
|
|
1993
2474
|
// src/client/login-gate.tsx
|
|
1994
|
-
var
|
|
2475
|
+
var import_react5 = require("react");
|
|
1995
2476
|
var import_client = require("react-dom/client");
|
|
1996
2477
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1997
2478
|
var INK = "#111b2f";
|
|
@@ -2105,12 +2586,12 @@ function loginGateVisible(snapshot) {
|
|
|
2105
2586
|
return snapshot.value?.lastLogin === void 0 || errorCode === "SESSION_EXPIRED";
|
|
2106
2587
|
}
|
|
2107
2588
|
function LoginGateModal(props) {
|
|
2108
|
-
const [username, setUsername] = (0,
|
|
2109
|
-
const [password, setPassword] = (0,
|
|
2110
|
-
const [rememberPassword, setRememberPassword] = (0,
|
|
2111
|
-
const [submitError, setSubmitError] = (0,
|
|
2112
|
-
const [formOpen, setFormOpen] = (0,
|
|
2113
|
-
(0,
|
|
2589
|
+
const [username, setUsername] = (0, import_react5.useState)(props.initialUsername ?? "");
|
|
2590
|
+
const [password, setPassword] = (0, import_react5.useState)("");
|
|
2591
|
+
const [rememberPassword, setRememberPassword] = (0, import_react5.useState)(props.initialRememberPassword === true);
|
|
2592
|
+
const [submitError, setSubmitError] = (0, import_react5.useState)();
|
|
2593
|
+
const [formOpen, setFormOpen] = (0, import_react5.useState)(false);
|
|
2594
|
+
(0, import_react5.useEffect)(() => {
|
|
2114
2595
|
openAuthorizeUrl(props.browserLoginUrl);
|
|
2115
2596
|
}, [props.browserLoginUrl]);
|
|
2116
2597
|
const showBrowserLogin = props.loginMode !== "form";
|
|
@@ -2123,7 +2604,7 @@ function LoginGateModal(props) {
|
|
|
2123
2604
|
const submit = () => {
|
|
2124
2605
|
if (loginDisabled) return;
|
|
2125
2606
|
setSubmitError(void 0);
|
|
2126
|
-
void props.storeCredential(PENDING_PASSWORD_REF, password).then(() => props.
|
|
2607
|
+
void props.storeCredential(PENDING_PASSWORD_REF, password).then(() => props.form.set("rememberPassword", rememberPassword)).then(() => props.form.set("pendingLogin", { username, passwordRef: PENDING_PASSWORD_REF })).catch(() => setSubmitError("\u767B\u5F55\u8BF7\u6C42\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5"));
|
|
2127
2608
|
};
|
|
2128
2609
|
const onSubmit = (event) => {
|
|
2129
2610
|
event.preventDefault();
|
|
@@ -2134,9 +2615,9 @@ function LoginGateModal(props) {
|
|
|
2134
2615
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { style: browserHintStyle, children: "\u5DF2\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00\u6388\u6743\u9875\uFF0C\u5B8C\u6210\u767B\u5F55\u540E\u672C\u7A97\u53E3\u4F1A\u81EA\u52A8\u7EE7\u7EED\u3002" }),
|
|
2135
2616
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: browserActionsStyle, children: [
|
|
2136
2617
|
props.browserLoginUrl === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-browser-reopen": true, type: "button", style: linkButton, onClick: () => openAuthorizeUrl(props.browserLoginUrl), children: "\u91CD\u65B0\u6253\u5F00\u6388\u6743\u9875" }),
|
|
2137
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-browser-cancel": true, type: "button", style: linkButton, onClick: () => void clearBrowserLogin(props.
|
|
2618
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-browser-cancel": true, type: "button", style: linkButton, onClick: () => void clearBrowserLogin(props.form), children: "\u53D6\u6D88" })
|
|
2138
2619
|
] })
|
|
2139
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-brand": true, "data-hiwork-core-browser-login": true, type: "button", style: props.loggingIn ? loginButtonDisabled : loginButton, disabled: props.loggingIn, onClick: () => void requestBrowserLogin(props.
|
|
2620
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-brand": true, "data-hiwork-core-browser-login": true, type: "button", style: props.loggingIn ? loginButtonDisabled : loginButton, disabled: props.loggingIn, onClick: () => void requestBrowserLogin(props.form), children: "\u4F7F\u7528\u6D4F\u89C8\u5668\u767B\u5F55" }) : null;
|
|
2140
2621
|
const form = formVisible ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("form", { onSubmit, style: { display: "flex", flexDirection: "column", gap: 14 }, children: [
|
|
2141
2622
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: fieldStyle, children: [
|
|
2142
2623
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("label", { htmlFor: "hiwork-core-username", style: labelStyle, children: "\u7528\u6237\u540D" }),
|
|
@@ -2151,7 +2632,7 @@ function LoginGateModal(props) {
|
|
|
2151
2632
|
"\u8BB0\u4F4F\u5BC6\u7801\uFF0C\u4E0B\u6B21\u81EA\u52A8\u767B\u5F55"
|
|
2152
2633
|
] }),
|
|
2153
2634
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-brand": true, type: "submit", style: loginDisabled ? loginButtonDisabled : loginButton, disabled: loginDisabled, children: props.loggingIn ? "\u767B\u5F55\u4E2D\u2026" : "\u767B \u5F55" }),
|
|
2154
|
-
noKey ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, type: "button", style: linkButton, onClick: () => void props.
|
|
2635
|
+
noKey ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, type: "button", style: linkButton, onClick: () => void props.form.set("refreshRequest", { at: (/* @__PURE__ */ new Date()).toISOString() }), children: "\u6211\u5DF2\u5206\u914D KEY\uFF0C\u5237\u65B0" }) : null
|
|
2155
2636
|
] }) : !passwordFormOffered ? null : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { "data-hiwork-core-secondary": true, "data-hiwork-core-form-toggle": true, type: "button", style: toggleStyle, onClick: () => setFormOpen(true), children: props.loginMode === "browser" ? "\u6D4F\u89C8\u5668\u767B\u5F55\u4E0D\u53EF\u7528\uFF1F\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" : "\u4F7F\u7528\u8D26\u53F7\u5BC6\u7801\u767B\u5F55" });
|
|
2156
2637
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: overlayStyle, children: [
|
|
2157
2638
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(OceanWaves, {}),
|
|
@@ -2192,7 +2673,7 @@ function LoginGateModal(props) {
|
|
|
2192
2673
|
] })
|
|
2193
2674
|
] });
|
|
2194
2675
|
}
|
|
2195
|
-
function mountLoginGate(
|
|
2676
|
+
function mountLoginGate(form, storeCredential) {
|
|
2196
2677
|
let root;
|
|
2197
2678
|
let gateContainer;
|
|
2198
2679
|
const appRoot = typeof document === "undefined" ? null : document.getElementById("root");
|
|
@@ -2208,7 +2689,7 @@ function mountLoginGate(scope, storeCredential) {
|
|
|
2208
2689
|
markReady();
|
|
2209
2690
|
};
|
|
2210
2691
|
const render = () => {
|
|
2211
|
-
const snapshot =
|
|
2692
|
+
const snapshot = form.getSnapshot();
|
|
2212
2693
|
if (!loginGateVisible(snapshot)) {
|
|
2213
2694
|
unmount();
|
|
2214
2695
|
return;
|
|
@@ -2222,10 +2703,10 @@ function mountLoginGate(scope, storeCredential) {
|
|
|
2222
2703
|
root = (0, import_client.createRoot)(gateContainer);
|
|
2223
2704
|
if (appRoot !== null) appRoot.inert = true;
|
|
2224
2705
|
}
|
|
2225
|
-
root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginGateModal, {
|
|
2706
|
+
root.render(/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LoginGateModal, { form, storeCredential, lastError: value?.lastError, loggingIn: value?.pendingLogin !== void 0, initialUsername: value?.username, initialRememberPassword: value?.rememberPassword === true, passwordLogin: passwordLoginOf(value), ...browserLoginViewOf(value) }));
|
|
2226
2707
|
};
|
|
2227
2708
|
render();
|
|
2228
|
-
const dispose =
|
|
2709
|
+
const dispose = form.subscribe(() => render());
|
|
2229
2710
|
return () => {
|
|
2230
2711
|
dispose();
|
|
2231
2712
|
unmount();
|
|
@@ -2233,7 +2714,7 @@ function mountLoginGate(scope, storeCredential) {
|
|
|
2233
2714
|
}
|
|
2234
2715
|
|
|
2235
2716
|
// src/client/usage-section.tsx
|
|
2236
|
-
var
|
|
2717
|
+
var import_react6 = require("react");
|
|
2237
2718
|
|
|
2238
2719
|
// src/client/usage-data.ts
|
|
2239
2720
|
var USAGE_CHANNEL = "/hiwork-usage";
|
|
@@ -2565,16 +3046,16 @@ function RatioRow({ name, value, pct }) {
|
|
|
2565
3046
|
}
|
|
2566
3047
|
function UsageSection(props) {
|
|
2567
3048
|
const card = props.useAiRouterCard((snapshot) => snapshot);
|
|
2568
|
-
const [selectedKeyId, setSelectedKeyId] = (0,
|
|
2569
|
-
const [result, setResult] = (0,
|
|
2570
|
-
const [loading, setLoading] = (0,
|
|
2571
|
-
const alive = (0,
|
|
2572
|
-
const ticket = (0,
|
|
3049
|
+
const [selectedKeyId, setSelectedKeyId] = (0, import_react6.useState)(void 0);
|
|
3050
|
+
const [result, setResult] = (0, import_react6.useState)(void 0);
|
|
3051
|
+
const [loading, setLoading] = (0, import_react6.useState)(true);
|
|
3052
|
+
const alive = (0, import_react6.useRef)(true);
|
|
3053
|
+
const ticket = (0, import_react6.useRef)(0);
|
|
2573
3054
|
const settingsKeyId = card.activeKeyId;
|
|
2574
3055
|
const requestKeyId = selectedKeyId ?? settingsKeyId;
|
|
2575
3056
|
const displayKeyId = requestKeyId ?? result?.data.keys?.[0]?.id;
|
|
2576
3057
|
const loader = props.usage;
|
|
2577
|
-
const load = (0,
|
|
3058
|
+
const load = (0, import_react6.useCallback)(async (force) => {
|
|
2578
3059
|
const current = ticket.current + 1;
|
|
2579
3060
|
ticket.current = current;
|
|
2580
3061
|
setLoading(true);
|
|
@@ -2587,13 +3068,13 @@ function UsageSection(props) {
|
|
|
2587
3068
|
setResult(next);
|
|
2588
3069
|
setLoading(false);
|
|
2589
3070
|
}, [loader, requestKeyId]);
|
|
2590
|
-
(0,
|
|
3071
|
+
(0, import_react6.useEffect)(() => {
|
|
2591
3072
|
alive.current = true;
|
|
2592
3073
|
return () => {
|
|
2593
3074
|
alive.current = false;
|
|
2594
3075
|
};
|
|
2595
3076
|
}, []);
|
|
2596
|
-
(0,
|
|
3077
|
+
(0, import_react6.useEffect)(() => {
|
|
2597
3078
|
void load(false);
|
|
2598
3079
|
}, [load]);
|
|
2599
3080
|
const data = result?.data;
|
|
@@ -2732,247 +3213,229 @@ function UsageSection(props) {
|
|
|
2732
3213
|
] });
|
|
2733
3214
|
}
|
|
2734
3215
|
|
|
2735
|
-
// src/client/
|
|
2736
|
-
var
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
var
|
|
2740
|
-
var
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
3216
|
+
// src/client/menu-section.tsx
|
|
3217
|
+
var import_react8 = require("react");
|
|
3218
|
+
|
|
3219
|
+
// src/client/feature-center.ts
|
|
3220
|
+
var import_react7 = require("react");
|
|
3221
|
+
var import_client2 = require("react-dom/client");
|
|
3222
|
+
|
|
3223
|
+
// src/client/feature-registry.ts
|
|
3224
|
+
var DEFAULT_FEATURE_ORDER = 1e3;
|
|
3225
|
+
var DEFAULT_FEATURE_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.84457 5.06199C11.6605 4.93876 14.7962 6.14848 14.8484 7.76397C14.8875 8.97461 13.1838 10.0696 10.7215 10.5942" stroke="currentColor" stroke-width="1.2"></path><path d="M5.12742 8.07731C5.00419 4.26138 6.21391 1.12568 7.8294 1.07351C9.04004 1.03441 10.135 2.73808 10.6596 5.20037" stroke="currentColor" stroke-width="1.2"></path><path d="M8.02457 10.6802C4.20865 10.8034 1.07294 9.5937 1.02077 7.97821C0.981678 6.76758 2.68535 5.67262 5.14763 5.14798" stroke="currentColor" stroke-width="1.2"></path><path d="M10.7476 7.89535C10.8708 11.7113 9.66109 14.847 8.0456 14.8991C6.83496 14.9382 5.74 13.2346 5.21536 10.7723" stroke="currentColor" stroke-width="1.2"></path></svg>';
|
|
3226
|
+
var NATIVE_PLUGINS_MENU_ID = "__native_plugins__";
|
|
3227
|
+
var NATIVE_PLUGINS_MENU_LABEL = "\u63D2\u4EF6";
|
|
3228
|
+
var NATIVE_PLUGINS_MENU_ORDER = 70;
|
|
3229
|
+
function nativePluginsMenuItem() {
|
|
3230
|
+
return {
|
|
3231
|
+
id: NATIVE_PLUGINS_MENU_ID,
|
|
3232
|
+
label: () => NATIVE_PLUGINS_MENU_LABEL,
|
|
3233
|
+
icon: DEFAULT_FEATURE_ICON,
|
|
3234
|
+
order: NATIVE_PLUGINS_MENU_ORDER
|
|
3235
|
+
};
|
|
2744
3236
|
}
|
|
2745
|
-
function
|
|
2746
|
-
|
|
2747
|
-
const
|
|
2748
|
-
if (
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
const
|
|
2755
|
-
|
|
2756
|
-
const
|
|
3237
|
+
function compareFeatures(left, right) {
|
|
3238
|
+
const leftOrder = left.order ?? DEFAULT_FEATURE_ORDER;
|
|
3239
|
+
const rightOrder = right.order ?? DEFAULT_FEATURE_ORDER;
|
|
3240
|
+
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
3241
|
+
if (left.id === right.id) return 0;
|
|
3242
|
+
return left.id < right.id ? -1 : 1;
|
|
3243
|
+
}
|
|
3244
|
+
function createFeatureRegistry() {
|
|
3245
|
+
const features = /* @__PURE__ */ new Map();
|
|
3246
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
3247
|
+
let activeId = null;
|
|
3248
|
+
const notify = () => {
|
|
3249
|
+
for (const listener of [...listeners]) {
|
|
3250
|
+
try {
|
|
3251
|
+
listener();
|
|
3252
|
+
} catch (error) {
|
|
3253
|
+
console.error("hiwork feature center subscriber failed:", error);
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
};
|
|
3257
|
+
return {
|
|
3258
|
+
register(feature) {
|
|
3259
|
+
const id = feature.id.trim();
|
|
3260
|
+
if (id === "") throw new Error("hiwork feature id must not be blank");
|
|
3261
|
+
if (features.has(id)) throw new Error(`hiwork feature "${id}" is already registered`);
|
|
3262
|
+
const entry = { ...feature, id };
|
|
3263
|
+
features.set(id, entry);
|
|
3264
|
+
notify();
|
|
3265
|
+
let disposed = false;
|
|
3266
|
+
return () => {
|
|
3267
|
+
if (disposed) return;
|
|
3268
|
+
disposed = true;
|
|
3269
|
+
if (features.get(id) !== entry) return;
|
|
3270
|
+
features.delete(id);
|
|
3271
|
+
if (activeId === id) activeId = null;
|
|
3272
|
+
notify();
|
|
3273
|
+
};
|
|
3274
|
+
},
|
|
3275
|
+
list() {
|
|
3276
|
+
return [...features.values()].sort(compareFeatures);
|
|
3277
|
+
},
|
|
3278
|
+
activeId() {
|
|
3279
|
+
return activeId;
|
|
3280
|
+
},
|
|
3281
|
+
open(id) {
|
|
3282
|
+
if (!features.has(id)) return false;
|
|
3283
|
+
if (activeId === id) return true;
|
|
3284
|
+
activeId = id;
|
|
3285
|
+
notify();
|
|
3286
|
+
return true;
|
|
3287
|
+
},
|
|
3288
|
+
openNative() {
|
|
3289
|
+
if (activeId === null) return;
|
|
3290
|
+
activeId = null;
|
|
3291
|
+
notify();
|
|
3292
|
+
},
|
|
3293
|
+
subscribe(listener) {
|
|
3294
|
+
listeners.add(listener);
|
|
3295
|
+
return () => {
|
|
3296
|
+
listeners.delete(listener);
|
|
3297
|
+
};
|
|
3298
|
+
}
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3302
|
+
// src/client/menu-preferences.ts
|
|
3303
|
+
var HIWORK_MENU_NS = "hiwork-menu";
|
|
3304
|
+
var DEFAULT_SIDEBAR_LIMIT = 5;
|
|
3305
|
+
var MORE_MENU_ITEM_ID = "__hiwork_more__";
|
|
3306
|
+
var MORE_MENU_LABEL = "\u66F4\u591A";
|
|
3307
|
+
var MENU_SECTION_LABEL = "\u4FA7\u680F\u83DC\u5355";
|
|
3308
|
+
var MENU_PLACEMENTS = ["sidebar", "more", "hidden"];
|
|
3309
|
+
var EMPTY_MENU_PREFERENCES = { order: [], sidebar: [], more: [], hidden: [] };
|
|
3310
|
+
function uniqueIds(value) {
|
|
2757
3311
|
const seen = /* @__PURE__ */ new Set();
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
if (
|
|
2773
|
-
const
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
// 桶缺失表示「并非每次尝试都上报了该桶」——原样省略,绝不猜 0。
|
|
2780
|
-
...cacheRead === void 0 ? {} : { cache_read_tokens: cacheRead },
|
|
2781
|
-
...cacheWrite === void 0 ? {} : { cache_write_tokens: cacheWrite },
|
|
2782
|
-
...at === void 0 ? {} : { at }
|
|
3312
|
+
const result = [];
|
|
3313
|
+
for (const item of value) {
|
|
3314
|
+
if (typeof item !== "string") continue;
|
|
3315
|
+
const id = item.trim();
|
|
3316
|
+
if (id === "" || seen.has(id)) continue;
|
|
3317
|
+
seen.add(id);
|
|
3318
|
+
result.push(id);
|
|
3319
|
+
}
|
|
3320
|
+
return result;
|
|
3321
|
+
}
|
|
3322
|
+
function idList(value) {
|
|
3323
|
+
return Array.isArray(value) ? uniqueIds(value.filter((item) => typeof item === "string")) : [];
|
|
3324
|
+
}
|
|
3325
|
+
function normalizeMenuPreferences(raw) {
|
|
3326
|
+
if (raw === null || typeof raw !== "object") return EMPTY_MENU_PREFERENCES;
|
|
3327
|
+
const value = raw;
|
|
3328
|
+
return {
|
|
3329
|
+
order: idList(value.order),
|
|
3330
|
+
sidebar: idList(value.sidebar),
|
|
3331
|
+
more: idList(value.more),
|
|
3332
|
+
hidden: idList(value.hidden)
|
|
2783
3333
|
};
|
|
2784
|
-
const ambiguous = pairs.length > 1 || cacheRead === void 0 || cacheWrite === void 0;
|
|
2785
|
-
return { items: [item], turn, ambiguous };
|
|
2786
3334
|
}
|
|
2787
|
-
function
|
|
2788
|
-
if (
|
|
2789
|
-
|
|
2790
|
-
|
|
3335
|
+
function placementOf(preferences, id) {
|
|
3336
|
+
if (preferences.hidden.includes(id)) return "hidden";
|
|
3337
|
+
if (preferences.more.includes(id)) return "more";
|
|
3338
|
+
if (preferences.sidebar.includes(id)) return "sidebar";
|
|
3339
|
+
return void 0;
|
|
2791
3340
|
}
|
|
2792
|
-
function
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
3341
|
+
function compareFeatures2(left, right) {
|
|
3342
|
+
const leftOrder = left.order ?? DEFAULT_FEATURE_ORDER;
|
|
3343
|
+
const rightOrder = right.order ?? DEFAULT_FEATURE_ORDER;
|
|
3344
|
+
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
3345
|
+
if (left.id === right.id) return 0;
|
|
3346
|
+
return left.id < right.id ? -1 : 1;
|
|
3347
|
+
}
|
|
3348
|
+
function orderedFeatureIds(features, preferences) {
|
|
3349
|
+
const known = new Set(features.map((feature) => feature.id));
|
|
3350
|
+
const result = [];
|
|
3351
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3352
|
+
for (const id of preferences.order) {
|
|
3353
|
+
if (!known.has(id) || seen.has(id)) continue;
|
|
3354
|
+
seen.add(id);
|
|
3355
|
+
result.push(id);
|
|
3356
|
+
}
|
|
3357
|
+
for (const feature of [...features].sort(compareFeatures2)) {
|
|
3358
|
+
if (seen.has(feature.id)) continue;
|
|
3359
|
+
seen.add(feature.id);
|
|
3360
|
+
result.push(feature.id);
|
|
3361
|
+
}
|
|
3362
|
+
return result;
|
|
3363
|
+
}
|
|
3364
|
+
function menuLayout(features, preferences) {
|
|
3365
|
+
const hidden = new Set(preferences.hidden);
|
|
3366
|
+
const explicitMore = new Set(preferences.more.filter((id) => !hidden.has(id)));
|
|
3367
|
+
const explicitSidebar = new Set(preferences.sidebar.filter((id) => !hidden.has(id) && !explicitMore.has(id)));
|
|
3368
|
+
const byId = new Map(features.map((feature) => [feature.id, feature]));
|
|
3369
|
+
const markedMore = [...explicitMore].filter((id) => byId.has(id)).length;
|
|
3370
|
+
const budget = Math.max(0, DEFAULT_SIDEBAR_LIMIT - markedMore);
|
|
3371
|
+
const sidebar = [];
|
|
3372
|
+
const more = [];
|
|
3373
|
+
for (const id of orderedFeatureIds(features, preferences)) {
|
|
3374
|
+
const feature = byId.get(id);
|
|
3375
|
+
if (feature === void 0 || hidden.has(id)) continue;
|
|
3376
|
+
if (explicitSidebar.has(id)) {
|
|
3377
|
+
sidebar.push(feature);
|
|
3378
|
+
continue;
|
|
3379
|
+
}
|
|
3380
|
+
if (explicitMore.has(id) || sidebar.length >= budget) {
|
|
3381
|
+
more.push(feature);
|
|
3382
|
+
continue;
|
|
3383
|
+
}
|
|
3384
|
+
sidebar.push(feature);
|
|
2799
3385
|
}
|
|
2800
|
-
return
|
|
3386
|
+
return { sidebar, more };
|
|
2801
3387
|
}
|
|
2802
|
-
function
|
|
2803
|
-
return
|
|
3388
|
+
function withoutPlacement(preferences, id) {
|
|
3389
|
+
return {
|
|
3390
|
+
order: preferences.order,
|
|
3391
|
+
sidebar: preferences.sidebar.filter((item) => item !== id),
|
|
3392
|
+
more: preferences.more.filter((item) => item !== id),
|
|
3393
|
+
hidden: preferences.hidden.filter((item) => item !== id)
|
|
3394
|
+
};
|
|
2804
3395
|
}
|
|
2805
|
-
function
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
if (
|
|
2811
|
-
return {
|
|
2812
|
-
}
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
async request(key, input) {
|
|
2846
|
-
try {
|
|
2847
|
-
const raw = await this.rpc.call(USAGE_CHANNEL, SESSION_COST_ENDPOINT, { items: input.items }, input.signal);
|
|
2848
|
-
const result = normalizeSessionCost(unwrapUsageResult(raw));
|
|
2849
|
-
if (result !== void 0) this.entries.set(key, { result, fetchedAt: this.now() });
|
|
2850
|
-
return result;
|
|
2851
|
-
} catch {
|
|
2852
|
-
return void 0;
|
|
2853
|
-
}
|
|
2854
|
-
}
|
|
2855
|
-
};
|
|
2856
|
-
var costStyle = {
|
|
2857
|
-
// 字号/颜色与同一行的「用量」「用时」胶囊同源变量,保证在动作行里不显得突兀。
|
|
2858
|
-
color: textTertiary,
|
|
2859
|
-
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
2860
|
-
lineHeight: "18px",
|
|
2861
|
-
whiteSpace: "nowrap",
|
|
2862
|
-
cursor: "default"
|
|
2863
|
-
};
|
|
2864
|
-
function SessionCost({ messageId, sessionId, useChat, store }) {
|
|
2865
|
-
const data = useChat((snapshot) => closingTurnTailData(snapshot, messageId));
|
|
2866
|
-
const match = sessionCostMatchOf(data);
|
|
2867
|
-
const key = match === null ? null : sessionCostKey(sessionId, match);
|
|
2868
|
-
const [shown, setShown] = (0, import_react6.useState)(void 0);
|
|
2869
|
-
(0, import_react6.useEffect)(() => {
|
|
2870
|
-
if (match === null || key === null) return;
|
|
2871
|
-
let disposed = false;
|
|
2872
|
-
const controller = new AbortController();
|
|
2873
|
-
void store.estimate({ sessionId, turn: match.turn, items: match.items, signal: controller.signal }).then((value) => {
|
|
2874
|
-
if (!disposed) setShown({ key, result: value });
|
|
2875
|
-
}).catch(() => {
|
|
2876
|
-
if (!disposed) setShown({ key, result: void 0 });
|
|
2877
|
-
});
|
|
2878
|
-
return () => {
|
|
2879
|
-
disposed = true;
|
|
2880
|
-
controller.abort();
|
|
2881
|
-
};
|
|
2882
|
-
}, [store, key]);
|
|
2883
|
-
if (match === null || key === null || shown === void 0 || shown.key !== key || shown.result === void 0) return null;
|
|
2884
|
-
const result = shown.result;
|
|
2885
|
-
const estimated = !result.channelResolved || match.ambiguous;
|
|
2886
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: costStyle, "data-hiwork-session-cost": estimated ? "estimated" : "exact", title: SESSION_COST_HINT, children: `\u2248 ${formatYuan(result.cost)}${estimated ? SESSION_COST_ESTIMATED_SUFFIX : ""}` });
|
|
2887
|
-
}
|
|
2888
|
-
|
|
2889
|
-
// src/client/account-cache.ts
|
|
2890
|
-
function accountIdentityOf(state) {
|
|
2891
|
-
return `${state.username ?? ""}\0${state.activeKeyId ?? ""}`;
|
|
2892
|
-
}
|
|
2893
|
-
function observeAccountChange(source, onChange) {
|
|
2894
|
-
let identity = accountIdentityOf(source.getSnapshot());
|
|
2895
|
-
const unsubscribe = source.subscribe(() => {
|
|
2896
|
-
const next = accountIdentityOf(source.getSnapshot());
|
|
2897
|
-
if (next === identity) return;
|
|
2898
|
-
identity = next;
|
|
2899
|
-
onChange();
|
|
2900
|
-
});
|
|
2901
|
-
return () => {
|
|
2902
|
-
unsubscribe();
|
|
2903
|
-
};
|
|
2904
|
-
}
|
|
2905
|
-
|
|
2906
|
-
// src/client/feature-registry.ts
|
|
2907
|
-
var DEFAULT_FEATURE_ORDER = 1e3;
|
|
2908
|
-
function compareFeatures(left, right) {
|
|
2909
|
-
const leftOrder = left.order ?? DEFAULT_FEATURE_ORDER;
|
|
2910
|
-
const rightOrder = right.order ?? DEFAULT_FEATURE_ORDER;
|
|
2911
|
-
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
|
2912
|
-
if (left.id === right.id) return 0;
|
|
2913
|
-
return left.id < right.id ? -1 : 1;
|
|
2914
|
-
}
|
|
2915
|
-
function createFeatureRegistry() {
|
|
2916
|
-
const features = /* @__PURE__ */ new Map();
|
|
2917
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
2918
|
-
let activeId = null;
|
|
2919
|
-
const notify = () => {
|
|
2920
|
-
for (const listener of [...listeners]) {
|
|
2921
|
-
try {
|
|
2922
|
-
listener();
|
|
2923
|
-
} catch (error) {
|
|
2924
|
-
console.error("hiwork feature center subscriber failed:", error);
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
};
|
|
2928
|
-
return {
|
|
2929
|
-
register(feature) {
|
|
2930
|
-
const id = feature.id.trim();
|
|
2931
|
-
if (id === "") throw new Error("hiwork feature id must not be blank");
|
|
2932
|
-
if (features.has(id)) throw new Error(`hiwork feature "${id}" is already registered`);
|
|
2933
|
-
const entry = { ...feature, id };
|
|
2934
|
-
features.set(id, entry);
|
|
2935
|
-
notify();
|
|
2936
|
-
let disposed = false;
|
|
2937
|
-
return () => {
|
|
2938
|
-
if (disposed) return;
|
|
2939
|
-
disposed = true;
|
|
2940
|
-
if (features.get(id) !== entry) return;
|
|
2941
|
-
features.delete(id);
|
|
2942
|
-
if (activeId === id) activeId = null;
|
|
2943
|
-
notify();
|
|
2944
|
-
};
|
|
2945
|
-
},
|
|
2946
|
-
list() {
|
|
2947
|
-
return [...features.values()].sort(compareFeatures);
|
|
2948
|
-
},
|
|
2949
|
-
activeId() {
|
|
2950
|
-
return activeId;
|
|
2951
|
-
},
|
|
2952
|
-
open(id) {
|
|
2953
|
-
if (!features.has(id)) return false;
|
|
2954
|
-
if (activeId === id) return true;
|
|
2955
|
-
activeId = id;
|
|
2956
|
-
notify();
|
|
2957
|
-
return true;
|
|
2958
|
-
},
|
|
2959
|
-
openNative() {
|
|
2960
|
-
if (activeId === null) return;
|
|
2961
|
-
activeId = null;
|
|
2962
|
-
notify();
|
|
2963
|
-
},
|
|
2964
|
-
subscribe(listener) {
|
|
2965
|
-
listeners.add(listener);
|
|
2966
|
-
return () => {
|
|
2967
|
-
listeners.delete(listener);
|
|
2968
|
-
};
|
|
2969
|
-
}
|
|
2970
|
-
};
|
|
3396
|
+
function setMenuPlacement(preferences, id, placement) {
|
|
3397
|
+
const current = placementOf(preferences, id);
|
|
3398
|
+
if (current === placement) return preferences;
|
|
3399
|
+
const base = withoutPlacement(preferences, id);
|
|
3400
|
+
if (placement === "sidebar") return { ...base, sidebar: [...base.sidebar, id] };
|
|
3401
|
+
if (placement === "more") return { ...base, more: [...base.more, id] };
|
|
3402
|
+
return { ...base, hidden: [...base.hidden, id] };
|
|
3403
|
+
}
|
|
3404
|
+
function moveMenuEntry(preferences, features, id, targetIndex) {
|
|
3405
|
+
const ids = orderedFeatureIds(features, preferences);
|
|
3406
|
+
const from = ids.indexOf(id);
|
|
3407
|
+
if (from < 0) return preferences;
|
|
3408
|
+
const to = Math.max(0, Math.min(ids.length - 1, Math.trunc(targetIndex)));
|
|
3409
|
+
if (to === from) return preferences;
|
|
3410
|
+
const next = [...ids];
|
|
3411
|
+
next.splice(from, 1);
|
|
3412
|
+
next.splice(to, 0, id);
|
|
3413
|
+
return { ...preferences, order: next };
|
|
3414
|
+
}
|
|
3415
|
+
function sameIds(left, right) {
|
|
3416
|
+
return left.length === right.length && left.every((item, index) => item === right[index]);
|
|
3417
|
+
}
|
|
3418
|
+
function moveMenuEntryNear(preferences, features, id, targetId, position) {
|
|
3419
|
+
const ids = orderedFeatureIds(features, preferences);
|
|
3420
|
+
const from = ids.indexOf(id);
|
|
3421
|
+
if (from < 0 || id === targetId || !ids.includes(targetId)) return preferences;
|
|
3422
|
+
const rest = ids.filter((item) => item !== id);
|
|
3423
|
+
const anchor = rest.indexOf(targetId);
|
|
3424
|
+
const next = [...rest];
|
|
3425
|
+
next.splice(position === "before" ? anchor : anchor + 1, 0, id);
|
|
3426
|
+
if (sameIds(next, ids)) return preferences;
|
|
3427
|
+
return { ...preferences, order: next };
|
|
3428
|
+
}
|
|
3429
|
+
function menuPreferenceOps(current, next) {
|
|
3430
|
+
const ops = [];
|
|
3431
|
+
for (const field of ["order", "sidebar", "more", "hidden"]) {
|
|
3432
|
+
if (sameIds(current[field], next[field])) continue;
|
|
3433
|
+
ops.push({ op: "set", path: [field], value: [...next[field]] });
|
|
3434
|
+
}
|
|
3435
|
+
return ops;
|
|
2971
3436
|
}
|
|
2972
3437
|
|
|
2973
3438
|
// src/client/feature-center.ts
|
|
2974
|
-
var import_react7 = require("react");
|
|
2975
|
-
var import_client2 = require("react-dom/client");
|
|
2976
3439
|
var SIDEBAR_SELECTOR = '[data-pane="sidebar"], [class*="sidebarCol"]';
|
|
2977
3440
|
var CONVERSATION_SELECTOR = '[data-pane="conversation"], [class*="centerCol"]';
|
|
2978
3441
|
var REGION_SELECTOR = '[class*="regionArea"]';
|
|
@@ -2983,11 +3446,16 @@ var NEW_SESSION_SELECTOR = [
|
|
|
2983
3446
|
].join(", ");
|
|
2984
3447
|
var MENU_ATTRIBUTE = "data-hiwork-feature-menu";
|
|
2985
3448
|
var MENU_ITEM_ATTRIBUTE = "data-hiwork-feature-menu-item";
|
|
3449
|
+
var MENU_BUTTON_ATTRIBUTE = "data-hiwork-feature-menu-button";
|
|
3450
|
+
var MORE_TOGGLE_ATTRIBUTE = "data-hiwork-feature-more-toggle";
|
|
3451
|
+
var MORE_PANEL_ATTRIBUTE = "data-hiwork-feature-more-panel";
|
|
3452
|
+
var MORE_PANEL_ITEM_ATTRIBUTE = "data-hiwork-feature-more-item";
|
|
2986
3453
|
var MENU_ICON_ATTRIBUTE = "data-hiwork-feature-menu-icon";
|
|
2987
3454
|
var MENU_LABEL_ATTRIBUTE = "data-hiwork-feature-menu-label";
|
|
2988
3455
|
var TOOLTIP_ATTRIBUTE = "data-hiwork-feature-tooltip";
|
|
2989
3456
|
var TOOLTIP_VISIBLE_ATTRIBUTE = "data-hiwork-feature-tooltip-visible";
|
|
2990
3457
|
var NATIVE_HIDDEN_ATTRIBUTE = "data-hiwork-native-new-session";
|
|
3458
|
+
var NATIVE_PANEL_LIST_ATTRIBUTE = "data-hiwork-native-panel-list";
|
|
2991
3459
|
var FEATURE_ROOT_ATTRIBUTE = "data-hiwork-feature-root";
|
|
2992
3460
|
var OWNER_ATTRIBUTE = "data-hiwork-owner";
|
|
2993
3461
|
var ACTIVE_PAGE_ATTRIBUTE = "data-hiwork-active-page";
|
|
@@ -2997,7 +3465,7 @@ var STYLE_ELEMENT_ID = "hiwork-feature-center-styles";
|
|
|
2997
3465
|
var TOOLTIP_DELAY_MS = 500;
|
|
2998
3466
|
var TOOLTIP_GAP_PX = 8;
|
|
2999
3467
|
var DEFAULT_NEW_SESSION_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M8.00003 0.3237C3.76075 0.3237 0.32373 3.76072 0.32373 8C0.32373 9.17603 0.589121 10.2922 1.0632 11.2901L1.35291 11.8989L2.5705 11.3205L2.28079 10.7117C1.89079 9.89074 1.67301 8.97167 1.67301 8C1.67301 4.50546 4.50549 1.67298 8.00003 1.67298C11.4946 1.67298 14.3271 4.50546 14.3271 8C14.3271 11.4945 11.4946 14.327 8.00003 14.327C7.28473 14.327 6.76077 14.277 6.29621 14.1487C5.83857 14.0224 5.40441 13.8109 4.88514 13.4488C4.12569 12.919 3.03778 12.7316 2.141 13.2978L2.12682 13.307L2.11264 13.3171L1.34886 13.854L1.79659 15.188L2.86122 14.4384C3.19068 14.2305 3.68325 14.2542 4.11326 14.5539C4.72789 14.9826 5.30042 15.2724 5.93762 15.4484C6.56803 15.6224 7.22776 15.6763 8.00003 15.6763C12.2393 15.6763 15.6763 12.2393 15.6763 8C15.6763 3.76072 12.2393 0.3237 8.00003 0.3237ZM7.32033 4.82535V7.32536H4.82538V8.67464H7.32033V11.1747H8.6696V8.67464H11.1747V7.32536H8.6696V4.82535H7.32033Z" fill="currentColor"></path></svg>';
|
|
3000
|
-
var
|
|
3468
|
+
var MORE_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="3.2" cy="8" r="1.15" fill="currentColor"></circle><circle cx="8" cy="8" r="1.15" fill="currentColor"></circle><circle cx="12.8" cy="8" r="1.15" fill="currentColor"></circle></svg>';
|
|
3001
3469
|
var SESSION_ROW_SELECTOR = [
|
|
3002
3470
|
'[class*="sessionRow"]',
|
|
3003
3471
|
'[class*="projectRow"]',
|
|
@@ -3007,18 +3475,22 @@ var SESSION_ROW_SELECTOR = [
|
|
|
3007
3475
|
].join(", ");
|
|
3008
3476
|
var featureCenterCss = [
|
|
3009
3477
|
`[${NATIVE_HIDDEN_ATTRIBUTE}="hidden"] { display: none !important; }`,
|
|
3478
|
+
`[${NATIVE_PANEL_LIST_ATTRIBUTE}="hidden"] { display: none !important; }`,
|
|
3010
3479
|
`[${MENU_ATTRIBUTE}] { display: flex; flex-direction: column; gap: 2px; margin: 0 2px 8px; }`,
|
|
3011
|
-
`[${
|
|
3012
|
-
`[${
|
|
3013
|
-
`[${
|
|
3480
|
+
`[${MENU_BUTTON_ATTRIBUTE}] { display: flex; align-items: center; gap: 8px; width: 100%; box-sizing: border-box; padding: 8px 10px; border: 0; border-radius: 10px; background: transparent; color: var(--dsw-alias-label-primary, #0f1115); font-size: 14px; line-height: 22px; text-align: left; cursor: pointer; }`,
|
|
3481
|
+
`[${MENU_BUTTON_ATTRIBUTE}]:hover { background: var(--dsw-alias-interactive-bg-hover, rgba(15, 17, 21, 0.06)); }`,
|
|
3482
|
+
`[${MENU_BUTTON_ATTRIBUTE}][data-hiwork-feature-active="true"] { background: var(--dsw-alias-interactive-bg-active, rgba(15, 17, 21, 0.1)); }`,
|
|
3014
3483
|
`[${MENU_ICON_ATTRIBUTE}] { display: inline-flex; flex: none; width: 16px; height: 16px; }`,
|
|
3015
3484
|
`[${MENU_ICON_ATTRIBUTE}] > svg { display: block; width: 16px; height: 16px; }`,
|
|
3016
3485
|
// 侧栏折叠:宿主在侧栏根元素上加 `*_collapsed`(rc.1 为 hHd-Xa_collapsed),
|
|
3017
3486
|
// 原生控件此时只显示图标;注入菜单必须同形,否则文字会在 56px 宽栏里竖排。
|
|
3018
3487
|
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_ATTRIBUTE}] { margin: 0 4px 8px; }`,
|
|
3019
3488
|
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_LABEL_ATTRIBUTE}] { display: none; }`,
|
|
3020
|
-
`[class*="sidebarCol"] [class*="collapsed"] [${
|
|
3489
|
+
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_BUTTON_ATTRIBUTE}] { justify-content: center; padding: 8px 0; }`,
|
|
3021
3490
|
`[class*="sidebarCol"] [class*="collapsed"] [${MENU_ICON_ATTRIBUTE}], [class*="sidebarCol"] [class*="collapsed"] [${MENU_ICON_ATTRIBUTE}] > svg { width: 18px; height: 18px; }`,
|
|
3491
|
+
// 「更多」浮层:与框架浮层(Tooltip/Popover)同色同圆角,固定定位挂 body(不受侧栏 overflow 裁剪)。
|
|
3492
|
+
`[${MORE_PANEL_ATTRIBUTE}] { position: fixed; z-index: 100; display: flex; flex-direction: column; gap: 2px; box-sizing: border-box; min-width: 168px; max-width: 260px; max-height: 60vh; overflow-y: auto; padding: 4px; border: 1px solid var(--dsw-alias-border-l2, rgba(15, 17, 21, 0.1)); border-radius: 12px; background: var(--dsw-alias-bg-layer-1, #fff); box-shadow: 0 8px 24px rgba(15, 17, 21, 0.16); }`,
|
|
3493
|
+
`[${MORE_PANEL_ATTRIBUTE}] [${MENU_BUTTON_ATTRIBUTE}] { white-space: nowrap; }`,
|
|
3022
3494
|
// 折叠态提示气泡:几何与配色逐项对齐框架 `ui-primitives` 的 `Tooltip.module.css`
|
|
3023
3495
|
// (黑底白字、3px 7px 内边距、8px 圆角、13/20 字号、150ms 淡入、最大半屏宽)。
|
|
3024
3496
|
`[${TOOLTIP_ATTRIBUTE}] { position: fixed; z-index: 100; display: none; width: max-content; max-width: 50vw; padding: 3px 7px; border-radius: 8px; background: var(--dsw-alias-tooltip-bg, #2c2c2e); color: var(--dsw-static-neutral-bluish-00, #fff); font-size: 13px; line-height: 20px; white-space: pre-line; overflow-wrap: break-word; pointer-events: none; transform: translateY(-50%); }`,
|
|
@@ -3061,6 +3533,7 @@ function nativeNewSessionButton(root) {
|
|
|
3061
3533
|
function conversationContainer() {
|
|
3062
3534
|
return document.querySelector(CONVERSATION_SELECTOR) ?? void 0;
|
|
3063
3535
|
}
|
|
3536
|
+
var PANEL_LIST_SELECTOR = 'nav[class*="panelList"]';
|
|
3064
3537
|
function isCollapsedRail(item) {
|
|
3065
3538
|
const host = item.closest('[class*="collapsed"]');
|
|
3066
3539
|
if (host === null) return false;
|
|
@@ -3077,12 +3550,15 @@ function mountFeatureCenter(options) {
|
|
|
3077
3550
|
return { dispose: () => void 0, openNativeHome: () => void 0 };
|
|
3078
3551
|
}
|
|
3079
3552
|
const isActiveOwner = () => activeOwner === owner;
|
|
3080
|
-
for (const stale of Array.from(document.querySelectorAll(`[${MENU_ATTRIBUTE}], [${FEATURE_ROOT_ATTRIBUTE}], [${TOOLTIP_ATTRIBUTE}], #${STYLE_ELEMENT_ID}`))) {
|
|
3553
|
+
for (const stale of Array.from(document.querySelectorAll(`[${MENU_ATTRIBUTE}], [${MORE_PANEL_ATTRIBUTE}], [${FEATURE_ROOT_ATTRIBUTE}], [${TOOLTIP_ATTRIBUTE}], #${STYLE_ELEMENT_ID}`))) {
|
|
3081
3554
|
stale.remove();
|
|
3082
3555
|
}
|
|
3083
3556
|
for (const hidden of Array.from(document.querySelectorAll(`[${NATIVE_HIDDEN_ATTRIBUTE}]`))) {
|
|
3084
3557
|
hidden.removeAttribute(NATIVE_HIDDEN_ATTRIBUTE);
|
|
3085
3558
|
}
|
|
3559
|
+
for (const hidden of Array.from(document.querySelectorAll(`[${NATIVE_PANEL_LIST_ATTRIBUTE}]`))) {
|
|
3560
|
+
hidden.removeAttribute(NATIVE_PANEL_LIST_ATTRIBUTE);
|
|
3561
|
+
}
|
|
3086
3562
|
document.documentElement.removeAttribute(ACTIVE_PAGE_ATTRIBUTE);
|
|
3087
3563
|
activeOwner = owner;
|
|
3088
3564
|
const style = document.createElement("style");
|
|
@@ -3102,6 +3578,87 @@ function mountFeatureCenter(options) {
|
|
|
3102
3578
|
let tooltipElement;
|
|
3103
3579
|
let tooltipTimer;
|
|
3104
3580
|
let tooltipItem;
|
|
3581
|
+
let morePanel;
|
|
3582
|
+
let moreToggle;
|
|
3583
|
+
let morePanelOpen = false;
|
|
3584
|
+
let layout = { sidebar: [], more: [] };
|
|
3585
|
+
const preferences = () => options.preferences?.() ?? EMPTY_MENU_PREFERENCES;
|
|
3586
|
+
const menuFeatures = () => [
|
|
3587
|
+
...registry.list(),
|
|
3588
|
+
...options.additionalFeatures?.() ?? []
|
|
3589
|
+
];
|
|
3590
|
+
const hideMorePanel = (restoreFocus = false) => {
|
|
3591
|
+
const panel = morePanel;
|
|
3592
|
+
const toggle = moreToggle;
|
|
3593
|
+
morePanel = void 0;
|
|
3594
|
+
moreToggle = void 0;
|
|
3595
|
+
if (!morePanelOpen && panel === void 0) return;
|
|
3596
|
+
morePanelOpen = false;
|
|
3597
|
+
panel?.remove();
|
|
3598
|
+
if (toggle !== void 0 && toggle.isConnected) {
|
|
3599
|
+
toggle.setAttribute("aria-expanded", "false");
|
|
3600
|
+
toggle.removeAttribute("data-hiwork-feature-active");
|
|
3601
|
+
if (restoreFocus) toggle.focus();
|
|
3602
|
+
}
|
|
3603
|
+
};
|
|
3604
|
+
const createMorePanelItem = (feature, active) => {
|
|
3605
|
+
const item = createMenuItem(feature.id, feature.label(), feature.icon ?? DEFAULT_FEATURE_ICON, active);
|
|
3606
|
+
item.setAttribute(MORE_PANEL_ITEM_ATTRIBUTE, feature.id);
|
|
3607
|
+
item.setAttribute("role", "menuitem");
|
|
3608
|
+
return item;
|
|
3609
|
+
};
|
|
3610
|
+
const renderMorePanel = () => {
|
|
3611
|
+
const panel = morePanel;
|
|
3612
|
+
if (panel === void 0) return;
|
|
3613
|
+
const active = registry.activeId();
|
|
3614
|
+
const pluginsActive = options.nativePanel?.pluginsActive() ?? false;
|
|
3615
|
+
panel.replaceChildren(...layout.more.map((feature) => createMorePanelItem(feature, feature.id === NATIVE_PLUGINS_MENU_ID ? pluginsActive : feature.id === active)));
|
|
3616
|
+
};
|
|
3617
|
+
const placeMorePanel = (toggle, panel) => {
|
|
3618
|
+
const rect = toggle.getBoundingClientRect();
|
|
3619
|
+
const width = panel.offsetWidth;
|
|
3620
|
+
const height = panel.offsetHeight;
|
|
3621
|
+
const overflowRight = rect.right + TOOLTIP_GAP_PX + width > window.innerWidth;
|
|
3622
|
+
const left = overflowRight ? rect.left - TOOLTIP_GAP_PX - width : rect.right + TOOLTIP_GAP_PX;
|
|
3623
|
+
panel.style.left = `${Math.max(0, left)}px`;
|
|
3624
|
+
panel.style.top = `${Math.max(8, Math.min(rect.top, window.innerHeight - height - 8))}px`;
|
|
3625
|
+
};
|
|
3626
|
+
const showMorePanel = (toggle) => {
|
|
3627
|
+
hideMorePanel();
|
|
3628
|
+
if (layout.more.length === 0) return;
|
|
3629
|
+
const panel = document.createElement("div");
|
|
3630
|
+
panel.setAttribute(MORE_PANEL_ATTRIBUTE, "");
|
|
3631
|
+
panel.setAttribute(OWNER_ATTRIBUTE, owner);
|
|
3632
|
+
panel.setAttribute("role", "menu");
|
|
3633
|
+
panel.addEventListener("click", onMorePanelClick);
|
|
3634
|
+
document.body.appendChild(panel);
|
|
3635
|
+
morePanel = panel;
|
|
3636
|
+
moreToggle = toggle;
|
|
3637
|
+
morePanelOpen = true;
|
|
3638
|
+
toggle.setAttribute("aria-expanded", "true");
|
|
3639
|
+
if (layout.more.some((feature) => feature.id === registry.activeId())) toggle.setAttribute("data-hiwork-feature-active", "true");
|
|
3640
|
+
renderMorePanel();
|
|
3641
|
+
placeMorePanel(toggle, panel);
|
|
3642
|
+
};
|
|
3643
|
+
const toggleMorePanel = (toggle) => {
|
|
3644
|
+
if (morePanelOpen) hideMorePanel();
|
|
3645
|
+
else showMorePanel(toggle);
|
|
3646
|
+
};
|
|
3647
|
+
function onMorePanelClick(event) {
|
|
3648
|
+
const target = event.target;
|
|
3649
|
+
if (!(target instanceof Element)) return;
|
|
3650
|
+
const item = target.closest(`[${MORE_PANEL_ITEM_ATTRIBUTE}]`);
|
|
3651
|
+
if (item === null) return;
|
|
3652
|
+
const id = item.getAttribute(MORE_PANEL_ITEM_ATTRIBUTE);
|
|
3653
|
+
hideMorePanel();
|
|
3654
|
+
if (id === null) return;
|
|
3655
|
+
if (id === NATIVE_PLUGINS_MENU_ID) {
|
|
3656
|
+
if (registry.activeId() !== null) registry.openNative();
|
|
3657
|
+
options.nativePanel?.openPlugins();
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
3660
|
+
registry.open(id);
|
|
3661
|
+
}
|
|
3105
3662
|
const hideTooltip = () => {
|
|
3106
3663
|
if (tooltipTimer !== void 0) {
|
|
3107
3664
|
clearTimeout(tooltipTimer);
|
|
@@ -3203,11 +3760,21 @@ function mountFeatureCenter(options) {
|
|
|
3203
3760
|
renderContext.openNativeHome();
|
|
3204
3761
|
return;
|
|
3205
3762
|
}
|
|
3763
|
+
if (id === MORE_MENU_ITEM_ID) {
|
|
3764
|
+
toggleMorePanel(item);
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
if (id === NATIVE_PLUGINS_MENU_ID) {
|
|
3768
|
+
if (registry.activeId() !== null) registry.openNative();
|
|
3769
|
+
options.nativePanel?.openPlugins();
|
|
3770
|
+
return;
|
|
3771
|
+
}
|
|
3206
3772
|
registry.open(id);
|
|
3207
3773
|
};
|
|
3208
3774
|
const createMenuItem = (id, label, icon, active) => {
|
|
3209
3775
|
const item = document.createElement("button");
|
|
3210
3776
|
item.type = "button";
|
|
3777
|
+
item.setAttribute(MENU_BUTTON_ATTRIBUTE, "");
|
|
3211
3778
|
item.setAttribute(MENU_ITEM_ATTRIBUTE, id);
|
|
3212
3779
|
if (active) item.setAttribute("data-hiwork-feature-active", "true");
|
|
3213
3780
|
item.setAttribute("aria-label", label);
|
|
@@ -3222,6 +3789,13 @@ function mountFeatureCenter(options) {
|
|
|
3222
3789
|
item.append(labelSpan);
|
|
3223
3790
|
return item;
|
|
3224
3791
|
};
|
|
3792
|
+
const createMoreRow = (active) => {
|
|
3793
|
+
const item = createMenuItem(MORE_MENU_ITEM_ID, MORE_MENU_LABEL, MORE_ICON, active);
|
|
3794
|
+
item.setAttribute(MORE_TOGGLE_ATTRIBUTE, "");
|
|
3795
|
+
item.setAttribute("aria-haspopup", "menu");
|
|
3796
|
+
item.setAttribute("aria-expanded", morePanelOpen ? "true" : "false");
|
|
3797
|
+
return item;
|
|
3798
|
+
};
|
|
3225
3799
|
const nativeIcon = (native) => {
|
|
3226
3800
|
const svg = native?.querySelector("svg");
|
|
3227
3801
|
return svg === null || svg === void 0 ? DEFAULT_NEW_SESSION_ICON : svg.outerHTML;
|
|
@@ -3230,7 +3804,12 @@ function mountFeatureCenter(options) {
|
|
|
3230
3804
|
const element = menuElement;
|
|
3231
3805
|
if (element === void 0) return;
|
|
3232
3806
|
const active = registry.activeId();
|
|
3233
|
-
const features =
|
|
3807
|
+
const features = menuFeatures();
|
|
3808
|
+
const prefs = preferences();
|
|
3809
|
+
const next = menuLayout(features, prefs);
|
|
3810
|
+
const nativePanel = options.nativePanel;
|
|
3811
|
+
const pluginsAvailable = nativePanel?.pluginsAvailable() ?? false;
|
|
3812
|
+
const pluginsActive = pluginsAvailable && (nativePanel?.pluginsActive() ?? false);
|
|
3234
3813
|
const sidebar = sidebarRoot();
|
|
3235
3814
|
const native = sidebar === void 0 ? void 0 : nativeNewSessionButton(sidebar);
|
|
3236
3815
|
const newSessionLabel = options.newSessionLabel?.() ?? DEFAULT_NEW_SESSION_LABEL;
|
|
@@ -3238,17 +3817,27 @@ function mountFeatureCenter(options) {
|
|
|
3238
3817
|
native === void 0 ? "no-native" : `native:${nativeIcon(native)}`,
|
|
3239
3818
|
newSessionLabel,
|
|
3240
3819
|
active ?? "",
|
|
3820
|
+
// 官方入口的可用性/激活态进签名:plugin-manager 装载完成后(面板注册进 main 槽)
|
|
3821
|
+
// 与用户在原生面板间切换时,菜单都要跟着重绘一次。
|
|
3822
|
+
`panel:${pluginsAvailable ? pluginsActive ? "active" : "idle" : "off"}`,
|
|
3823
|
+
// 分组与顺序都进签名:偏好一变(设置面板里拖一下)就必须重建,否则侧栏不动。
|
|
3824
|
+
`layout:${next.sidebar.map((item) => item.id).join(",")}>${next.more.map((item) => item.id).join(",")}`,
|
|
3241
3825
|
...features.map((item) => `${item.id}:${item.order ?? ""}:${item.label()}:${item.icon ?? ""}`)
|
|
3242
3826
|
].join("|");
|
|
3243
3827
|
if (signature === menuSignature) return;
|
|
3244
3828
|
menuSignature = signature;
|
|
3829
|
+
layout = next;
|
|
3245
3830
|
hideTooltip();
|
|
3831
|
+
hideMorePanel();
|
|
3246
3832
|
const items = [];
|
|
3247
3833
|
if (native !== void 0) items.push(createMenuItem(NATIVE_MENU_ITEM_ID, newSessionLabel, nativeIcon(native), false));
|
|
3248
|
-
for (const item of
|
|
3249
|
-
|
|
3834
|
+
for (const item of next.sidebar) {
|
|
3835
|
+
const rowActive = item.id === NATIVE_PLUGINS_MENU_ID ? pluginsActive : item.id === active;
|
|
3836
|
+
items.push(createMenuItem(item.id, item.label(), item.icon ?? DEFAULT_FEATURE_ICON, rowActive));
|
|
3250
3837
|
}
|
|
3838
|
+
if (next.more.length > 0) items.push(createMoreRow(next.more.some((item) => item.id === active)));
|
|
3251
3839
|
element.replaceChildren(...items);
|
|
3840
|
+
syncNativePanelList(pluginsAvailable);
|
|
3252
3841
|
};
|
|
3253
3842
|
const ensureMenu = (sidebar, native) => {
|
|
3254
3843
|
let element = menuElement;
|
|
@@ -3274,6 +3863,13 @@ function mountFeatureCenter(options) {
|
|
|
3274
3863
|
if (node.getAttribute(OWNER_ATTRIBUTE) !== owner) node.remove();
|
|
3275
3864
|
}
|
|
3276
3865
|
};
|
|
3866
|
+
const syncNativePanelList = (pluginsAvailable) => {
|
|
3867
|
+
const sidebar = sidebarRoot();
|
|
3868
|
+
const nav = sidebar?.querySelector(PANEL_LIST_SELECTOR) ?? null;
|
|
3869
|
+
if (nav === null) return;
|
|
3870
|
+
if (pluginsAvailable) nav.setAttribute(NATIVE_PANEL_LIST_ATTRIBUTE, "hidden");
|
|
3871
|
+
else nav.removeAttribute(NATIVE_PANEL_LIST_ATTRIBUTE);
|
|
3872
|
+
};
|
|
3277
3873
|
const syncMenu = () => {
|
|
3278
3874
|
dedupe(`[${MENU_ATTRIBUTE}]`);
|
|
3279
3875
|
const sidebar = sidebarRoot();
|
|
@@ -3294,6 +3890,7 @@ function mountFeatureCenter(options) {
|
|
|
3294
3890
|
clearTakeover();
|
|
3295
3891
|
return;
|
|
3296
3892
|
}
|
|
3893
|
+
if (options.nativePanel?.anyActive() === true) options.nativePanel.returnToConversation();
|
|
3297
3894
|
const host = conversationContainer();
|
|
3298
3895
|
if (host === void 0) {
|
|
3299
3896
|
clearTakeover();
|
|
@@ -3312,96 +3909,1435 @@ function mountFeatureCenter(options) {
|
|
|
3312
3909
|
const reactRoot = (0, import_client2.createRoot)(element);
|
|
3313
3910
|
root = reactRoot;
|
|
3314
3911
|
try {
|
|
3315
|
-
reactRoot.render((0, import_react7.createElement)(FeatureErrorBoundary, { onError: onRenderError }, target.render(renderContext)));
|
|
3316
|
-
} catch (error) {
|
|
3317
|
-
onRenderError(error);
|
|
3912
|
+
reactRoot.render((0, import_react7.createElement)(FeatureErrorBoundary, { onError: onRenderError }, target.render(renderContext)));
|
|
3913
|
+
} catch (error) {
|
|
3914
|
+
onRenderError(error);
|
|
3915
|
+
}
|
|
3916
|
+
};
|
|
3917
|
+
const pending = () => {
|
|
3918
|
+
const sidebar = sidebarRoot();
|
|
3919
|
+
if (sidebar === void 0) return true;
|
|
3920
|
+
if (menuElement === void 0 || !sidebar.contains(menuElement)) return true;
|
|
3921
|
+
if (registry.activeId() === null) return false;
|
|
3922
|
+
const host = conversationContainer();
|
|
3923
|
+
if (host === void 0) return true;
|
|
3924
|
+
return container === void 0 || !host.contains(container);
|
|
3925
|
+
};
|
|
3926
|
+
const scheduleRetry = () => {
|
|
3927
|
+
if (disposed || retryTimer !== void 0 || retries >= maxRetries) return;
|
|
3928
|
+
retryTimer = setTimeout(() => {
|
|
3929
|
+
retryTimer = void 0;
|
|
3930
|
+
retries += 1;
|
|
3931
|
+
sync();
|
|
3932
|
+
if (pending()) scheduleRetry();
|
|
3933
|
+
}, interval);
|
|
3934
|
+
};
|
|
3935
|
+
const onDocumentClickCapture = (event) => {
|
|
3936
|
+
const target = event.target;
|
|
3937
|
+
if (morePanelOpen) {
|
|
3938
|
+
const inside = target instanceof Element && (target.closest(`[${MORE_PANEL_ATTRIBUTE}]`) !== null || target.closest(`[${MORE_TOGGLE_ATTRIBUTE}]`) !== null);
|
|
3939
|
+
if (!inside) hideMorePanel();
|
|
3940
|
+
}
|
|
3941
|
+
if (registry.activeId() === null) return;
|
|
3942
|
+
if (!(target instanceof Element)) return;
|
|
3943
|
+
if (target.closest(`[${FEATURE_ROOT_ATTRIBUTE}]`) !== null) return;
|
|
3944
|
+
if (target.closest(`[${MENU_ATTRIBUTE}]`) !== null) return;
|
|
3945
|
+
if (target.closest(SESSION_ROW_SELECTOR) === null) return;
|
|
3946
|
+
registry.openNative();
|
|
3947
|
+
};
|
|
3948
|
+
const onDocumentKeyDown = (event) => {
|
|
3949
|
+
if (event.key !== "Escape" || !morePanelOpen) return;
|
|
3950
|
+
event.stopPropagation();
|
|
3951
|
+
hideMorePanel(true);
|
|
3952
|
+
};
|
|
3953
|
+
const onScrollOrResize = () => {
|
|
3954
|
+
hideTooltip();
|
|
3955
|
+
hideMorePanel();
|
|
3956
|
+
};
|
|
3957
|
+
const unsubscribeRegistry = registry.subscribe(() => {
|
|
3958
|
+
sync();
|
|
3959
|
+
});
|
|
3960
|
+
let unsubscribeLocale;
|
|
3961
|
+
if (options.subscribeLocale !== void 0) {
|
|
3962
|
+
try {
|
|
3963
|
+
unsubscribeLocale = options.subscribeLocale(() => {
|
|
3964
|
+
menuSignature = "";
|
|
3965
|
+
rebuildMenuItems();
|
|
3966
|
+
});
|
|
3967
|
+
} catch (error) {
|
|
3968
|
+
console.warn("hiwork feature center: locale subscription unavailable", error);
|
|
3969
|
+
}
|
|
3970
|
+
}
|
|
3971
|
+
let unsubscribePreferences;
|
|
3972
|
+
if (options.subscribePreferences !== void 0) {
|
|
3973
|
+
try {
|
|
3974
|
+
unsubscribePreferences = options.subscribePreferences(() => {
|
|
3975
|
+
menuSignature = "";
|
|
3976
|
+
rebuildMenuItems();
|
|
3977
|
+
});
|
|
3978
|
+
} catch (error) {
|
|
3979
|
+
console.warn("hiwork feature center: menu preferences subscription unavailable", error);
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
let unsubscribeNativePanel;
|
|
3983
|
+
if (options.nativePanel !== void 0) {
|
|
3984
|
+
try {
|
|
3985
|
+
unsubscribeNativePanel = options.nativePanel.subscribe(() => {
|
|
3986
|
+
if (disposed) return;
|
|
3987
|
+
if (options.nativePanel?.anyActive() === true && registry.activeId() !== null) {
|
|
3988
|
+
registry.openNative();
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
menuSignature = "";
|
|
3992
|
+
rebuildMenuItems();
|
|
3993
|
+
});
|
|
3994
|
+
} catch (error) {
|
|
3995
|
+
console.warn("hiwork feature center: native panel subscription unavailable", error);
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
const observer = new MutationObserver(() => {
|
|
3999
|
+
sync();
|
|
4000
|
+
});
|
|
4001
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
4002
|
+
document.addEventListener("click", onDocumentClickCapture, true);
|
|
4003
|
+
document.addEventListener("keydown", onDocumentKeyDown, true);
|
|
4004
|
+
document.addEventListener("scroll", onScrollOrResize, true);
|
|
4005
|
+
window.addEventListener("resize", onScrollOrResize);
|
|
4006
|
+
const sync = () => {
|
|
4007
|
+
if (disposed || !isActiveOwner()) return;
|
|
4008
|
+
syncMenu();
|
|
4009
|
+
syncTakeover();
|
|
4010
|
+
if (pending()) scheduleRetry();
|
|
4011
|
+
};
|
|
4012
|
+
sync();
|
|
4013
|
+
return {
|
|
4014
|
+
dispose: () => {
|
|
4015
|
+
if (disposed) return;
|
|
4016
|
+
disposed = true;
|
|
4017
|
+
observer.disconnect();
|
|
4018
|
+
if (retryTimer !== void 0) clearTimeout(retryTimer);
|
|
4019
|
+
retryTimer = void 0;
|
|
4020
|
+
unsubscribeRegistry();
|
|
4021
|
+
unsubscribeLocale?.();
|
|
4022
|
+
unsubscribePreferences?.();
|
|
4023
|
+
unsubscribeNativePanel?.();
|
|
4024
|
+
document.removeEventListener("click", onDocumentClickCapture, true);
|
|
4025
|
+
document.removeEventListener("keydown", onDocumentKeyDown, true);
|
|
4026
|
+
document.removeEventListener("scroll", onScrollOrResize, true);
|
|
4027
|
+
window.removeEventListener("resize", onScrollOrResize);
|
|
4028
|
+
hideTooltip();
|
|
4029
|
+
hideMorePanel();
|
|
4030
|
+
const tooltip = tooltipElement;
|
|
4031
|
+
tooltipElement = void 0;
|
|
4032
|
+
if (tooltip !== void 0 && tooltip.getAttribute(OWNER_ATTRIBUTE) === owner) tooltip.remove();
|
|
4033
|
+
for (const panel of Array.from(document.querySelectorAll(`[${MORE_PANEL_ATTRIBUTE}]`))) {
|
|
4034
|
+
if (panel.getAttribute(OWNER_ATTRIBUTE) === owner) panel.remove();
|
|
4035
|
+
}
|
|
4036
|
+
clearTakeover();
|
|
4037
|
+
const element = menuElement;
|
|
4038
|
+
menuElement = void 0;
|
|
4039
|
+
if (element !== void 0) {
|
|
4040
|
+
element.removeEventListener("click", onMenuClick);
|
|
4041
|
+
if (element.getAttribute(OWNER_ATTRIBUTE) === owner) element.remove();
|
|
4042
|
+
}
|
|
4043
|
+
if (!isActiveOwner()) return;
|
|
4044
|
+
activeOwner = null;
|
|
4045
|
+
for (const hidden of Array.from(document.querySelectorAll(`[${NATIVE_HIDDEN_ATTRIBUTE}]`))) {
|
|
4046
|
+
hidden.removeAttribute(NATIVE_HIDDEN_ATTRIBUTE);
|
|
4047
|
+
}
|
|
4048
|
+
for (const hidden of Array.from(document.querySelectorAll(`[${NATIVE_PANEL_LIST_ATTRIBUTE}]`))) {
|
|
4049
|
+
hidden.removeAttribute(NATIVE_PANEL_LIST_ATTRIBUTE);
|
|
4050
|
+
}
|
|
4051
|
+
if (style.getAttribute(OWNER_ATTRIBUTE) === owner) style.remove();
|
|
4052
|
+
},
|
|
4053
|
+
openNativeHome: () => {
|
|
4054
|
+
renderContext.openNativeHome();
|
|
4055
|
+
}
|
|
4056
|
+
};
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
// src/client/menu-section.tsx
|
|
4060
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
4061
|
+
var PLACEMENT_LABELS = {
|
|
4062
|
+
sidebar: "\u4FA7\u680F",
|
|
4063
|
+
more: "\u66F4\u591A",
|
|
4064
|
+
hidden: "\u9690\u85CF"
|
|
4065
|
+
};
|
|
4066
|
+
var sectionStyle4 = { display: "flex", flexDirection: "column", gap: 16 };
|
|
4067
|
+
var headerStyle2 = { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, flexWrap: "wrap" };
|
|
4068
|
+
var titleStyle3 = { margin: 0, fontSize: 18, fontWeight: 600, color: textPrimary };
|
|
4069
|
+
var noteStyle2 = { margin: 0, fontSize: 12, lineHeight: "18px", color: textSecondary };
|
|
4070
|
+
var reasonStyle = { margin: 0, fontSize: 12, lineHeight: "18px", color: textTertiary };
|
|
4071
|
+
var listStyle2 = { display: "flex", flexDirection: "column", gap: 2, margin: 0, padding: 0, listStyle: "none" };
|
|
4072
|
+
var rowStyle = (drop, dragging) => ({
|
|
4073
|
+
display: "flex",
|
|
4074
|
+
alignItems: "center",
|
|
4075
|
+
gap: 10,
|
|
4076
|
+
padding: "6px 8px",
|
|
4077
|
+
borderRadius: 8,
|
|
4078
|
+
// 插入线:拖拽落点用 2px 主题色描边标出(不占位,不引起跳动)。
|
|
4079
|
+
...drop === "before" ? { boxShadow: `inset 0 2px 0 0 ${brandAccent}` } : {},
|
|
4080
|
+
...drop === "after" ? { boxShadow: `inset 0 -2px 0 0 ${brandAccent}` } : {},
|
|
4081
|
+
...dragging ? { opacity: 0.5 } : {}
|
|
4082
|
+
});
|
|
4083
|
+
var handleStyle = (writable) => ({
|
|
4084
|
+
display: "flex",
|
|
4085
|
+
alignItems: "center",
|
|
4086
|
+
justifyContent: "center",
|
|
4087
|
+
width: 18,
|
|
4088
|
+
height: 24,
|
|
4089
|
+
flex: "0 0 auto",
|
|
4090
|
+
borderRadius: 6,
|
|
4091
|
+
color: writable ? textTertiary : textTertiary,
|
|
4092
|
+
cursor: writable ? "grab" : "default"
|
|
4093
|
+
});
|
|
4094
|
+
var iconStyle = {
|
|
4095
|
+
display: "flex",
|
|
4096
|
+
alignItems: "center",
|
|
4097
|
+
justifyContent: "center",
|
|
4098
|
+
width: 16,
|
|
4099
|
+
height: 16,
|
|
4100
|
+
flex: "0 0 auto",
|
|
4101
|
+
color: textSecondary
|
|
4102
|
+
};
|
|
4103
|
+
var labelStyle2 = {
|
|
4104
|
+
flex: 1,
|
|
4105
|
+
minWidth: 0,
|
|
4106
|
+
fontSize: 13,
|
|
4107
|
+
lineHeight: "20px",
|
|
4108
|
+
color: textPrimary,
|
|
4109
|
+
overflow: "hidden",
|
|
4110
|
+
textOverflow: "ellipsis",
|
|
4111
|
+
whiteSpace: "nowrap"
|
|
4112
|
+
};
|
|
4113
|
+
var markedStyle = { flex: "0 0 auto", fontSize: 11, lineHeight: "16px", color: textTertiary };
|
|
4114
|
+
var groupStyle = {
|
|
4115
|
+
display: "flex",
|
|
4116
|
+
alignItems: "center",
|
|
4117
|
+
gap: 2,
|
|
4118
|
+
flex: "0 0 auto",
|
|
4119
|
+
padding: 2,
|
|
4120
|
+
borderRadius: 8,
|
|
4121
|
+
background: bgLayer2,
|
|
4122
|
+
border: `1px solid ${borderL2}`
|
|
4123
|
+
};
|
|
4124
|
+
var optionStyle = (active, writable) => ({
|
|
4125
|
+
border: 0,
|
|
4126
|
+
borderRadius: 6,
|
|
4127
|
+
padding: "3px 10px",
|
|
4128
|
+
font: "inherit",
|
|
4129
|
+
fontSize: 12,
|
|
4130
|
+
lineHeight: "18px",
|
|
4131
|
+
color: active ? textPrimary : textSecondary,
|
|
4132
|
+
background: active ? "var(--dsw-alias-bg-layer-1, #ffffff)" : "transparent",
|
|
4133
|
+
boxShadow: active ? "0 1px 2px rgba(15,17,21,0.08)" : "none",
|
|
4134
|
+
cursor: writable ? "pointer" : "default",
|
|
4135
|
+
opacity: writable ? 1 : 0.6
|
|
4136
|
+
});
|
|
4137
|
+
var HANDLE_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><circle cx="6" cy="4" r="1.1"></circle><circle cx="10" cy="4" r="1.1"></circle><circle cx="6" cy="8" r="1.1"></circle><circle cx="10" cy="8" r="1.1"></circle><circle cx="6" cy="12" r="1.1"></circle><circle cx="10" cy="12" r="1.1"></circle></svg>';
|
|
4138
|
+
function MenuRow({
|
|
4139
|
+
entry,
|
|
4140
|
+
writable,
|
|
4141
|
+
dragging,
|
|
4142
|
+
drop,
|
|
4143
|
+
onPlacement,
|
|
4144
|
+
onDragStart,
|
|
4145
|
+
onDragOver,
|
|
4146
|
+
onDrop,
|
|
4147
|
+
onDragEnd,
|
|
4148
|
+
onKeyDown
|
|
4149
|
+
}) {
|
|
4150
|
+
const rowRef = (0, import_react8.useRef)(null);
|
|
4151
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
4152
|
+
"li",
|
|
4153
|
+
{
|
|
4154
|
+
ref: rowRef,
|
|
4155
|
+
tabIndex: 0,
|
|
4156
|
+
role: "listitem",
|
|
4157
|
+
"aria-label": `${entry.label}\uFF0C\u7B2C ${entry.index + 1} \u9879\uFF0C\u5171 ${entry.total} \u9879`,
|
|
4158
|
+
style: rowStyle(drop, dragging),
|
|
4159
|
+
"data-hiwork-menu-row": entry.id,
|
|
4160
|
+
"data-hiwork-menu-placement": entry.placement,
|
|
4161
|
+
...entry.marked ? { "data-hiwork-menu-marked": "true" } : {},
|
|
4162
|
+
...drop === void 0 ? {} : { "data-hiwork-menu-drop": drop },
|
|
4163
|
+
draggable: writable,
|
|
4164
|
+
onDragStart: (event) => {
|
|
4165
|
+
event.dataTransfer.effectAllowed = "move";
|
|
4166
|
+
event.dataTransfer.setData("text/plain", entry.id);
|
|
4167
|
+
onDragStart(entry.id);
|
|
4168
|
+
},
|
|
4169
|
+
onDragEnd,
|
|
4170
|
+
onKeyDown: (event) => {
|
|
4171
|
+
onKeyDown(event, entry);
|
|
4172
|
+
},
|
|
4173
|
+
onDragOver: (event) => {
|
|
4174
|
+
onDragOver(event, entry.id);
|
|
4175
|
+
},
|
|
4176
|
+
onDrop: (event) => {
|
|
4177
|
+
onDrop(event, entry.id);
|
|
4178
|
+
},
|
|
4179
|
+
children: [
|
|
4180
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4181
|
+
"span",
|
|
4182
|
+
{
|
|
4183
|
+
style: handleStyle(writable),
|
|
4184
|
+
draggable: writable,
|
|
4185
|
+
"aria-hidden": "true",
|
|
4186
|
+
"data-hiwork-menu-handle": entry.id,
|
|
4187
|
+
dangerouslySetInnerHTML: { __html: HANDLE_ICON }
|
|
4188
|
+
}
|
|
4189
|
+
),
|
|
4190
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: iconStyle, dangerouslySetInnerHTML: { __html: entry.icon ?? DEFAULT_FEATURE_ICON } }),
|
|
4191
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: labelStyle2, children: entry.label }),
|
|
4192
|
+
entry.marked ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: markedStyle, children: "\u5DF2\u8BBE\u7F6E" }) : null,
|
|
4193
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: groupStyle, role: "group", "aria-label": `${entry.label} \u663E\u793A\u4F4D\u7F6E`, children: MENU_PLACEMENTS.map((placement) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4194
|
+
"button",
|
|
4195
|
+
{
|
|
4196
|
+
type: "button",
|
|
4197
|
+
style: optionStyle(entry.placement === placement, writable),
|
|
4198
|
+
"aria-pressed": entry.placement === placement,
|
|
4199
|
+
"data-hiwork-menu-option": placement,
|
|
4200
|
+
disabled: !writable,
|
|
4201
|
+
onClick: () => {
|
|
4202
|
+
onPlacement(entry.id, placement);
|
|
4203
|
+
},
|
|
4204
|
+
children: PLACEMENT_LABELS[placement]
|
|
4205
|
+
},
|
|
4206
|
+
placement
|
|
4207
|
+
)) })
|
|
4208
|
+
]
|
|
4209
|
+
}
|
|
4210
|
+
);
|
|
4211
|
+
}
|
|
4212
|
+
function MenuSection(props) {
|
|
4213
|
+
const state = props.useMenu((snapshot) => snapshot);
|
|
4214
|
+
const [dragId, setDragId] = (0, import_react8.useState)(void 0);
|
|
4215
|
+
const [drop, setDrop] = (0, import_react8.useState)(void 0);
|
|
4216
|
+
const { setPlacement, move, moveNear, reset } = props.actions;
|
|
4217
|
+
const writable = state.writable;
|
|
4218
|
+
const onDragOver = (event, id) => {
|
|
4219
|
+
if (dragId === void 0 || dragId === id) return;
|
|
4220
|
+
event.preventDefault();
|
|
4221
|
+
event.dataTransfer.dropEffect = "move";
|
|
4222
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
4223
|
+
const position = event.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
4224
|
+
setDrop((current) => current?.id === id && current.position === position ? current : { id, position });
|
|
4225
|
+
};
|
|
4226
|
+
const onDrop = (event, id) => {
|
|
4227
|
+
if (dragId === void 0 || dragId === id) return;
|
|
4228
|
+
event.preventDefault();
|
|
4229
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
4230
|
+
const position = event.clientY < rect.top + rect.height / 2 ? "before" : "after";
|
|
4231
|
+
const moving = dragId;
|
|
4232
|
+
setDragId(void 0);
|
|
4233
|
+
setDrop(void 0);
|
|
4234
|
+
void moveNear(moving, id, position);
|
|
4235
|
+
};
|
|
4236
|
+
const onKeyDown = (event, entry) => {
|
|
4237
|
+
if (!writable) return;
|
|
4238
|
+
if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
|
|
4239
|
+
if (event.target !== event.currentTarget) return;
|
|
4240
|
+
event.preventDefault();
|
|
4241
|
+
void move(entry.id, event.key === "ArrowUp" ? -1 : 1);
|
|
4242
|
+
};
|
|
4243
|
+
const marked = state.entries.some((entry) => entry.marked);
|
|
4244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("section", { "aria-label": "\u4FA7\u680F\u83DC\u5355", style: sectionStyle4, "data-hiwork-menu-section": "", children: [
|
|
4245
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("header", { style: headerStyle2, children: [
|
|
4246
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h2", { style: titleStyle3, children: "\u4FA7\u680F\u83DC\u5355" }),
|
|
4247
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4248
|
+
"button",
|
|
4249
|
+
{
|
|
4250
|
+
type: "button",
|
|
4251
|
+
style: writable && marked ? secondaryButton : disabledButton,
|
|
4252
|
+
disabled: !writable || !marked,
|
|
4253
|
+
"data-hiwork-menu-reset": "",
|
|
4254
|
+
onClick: () => {
|
|
4255
|
+
void reset();
|
|
4256
|
+
},
|
|
4257
|
+
children: "\u6062\u590D\u9ED8\u8BA4"
|
|
4258
|
+
}
|
|
4259
|
+
)
|
|
4260
|
+
] }),
|
|
4261
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("p", { style: noteStyle2, children: [
|
|
4262
|
+
"\u4FA7\u680F\u53EA\u663E\u793A\u4F60\u9009\u5B9A\u7684\u5165\u53E3\uFF0C\u5176\u4F59\u6536\u8FDB\u300C\u66F4\u591A\u300D\u2014\u2014\u70B9\u4FA7\u680F\u6700\u540E\u4E00\u884C\u300C\u66F4\u591A\u300D\u5C31\u80FD\u770B\u5230\u3002 \u653E\u5230\u300C\u66F4\u591A\u300D\u6216\u300C\u9690\u85CF\u300D\u90FD\u4F1A\u8BA9\u4FA7\u680F\u5C11\u4E00\u884C\uFF1B\u5F53\u524D\u6700\u591A\u663E\u793A ",
|
|
4263
|
+
state.limit,
|
|
4264
|
+
" \u4E2A\u5165\u53E3\u3002"
|
|
4265
|
+
] }),
|
|
4266
|
+
state.reason === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: reasonStyle, "data-hiwork-menu-reason": "", children: state.reason }),
|
|
4267
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("ul", { style: listStyle2, children: state.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
4268
|
+
MenuRow,
|
|
4269
|
+
{
|
|
4270
|
+
entry,
|
|
4271
|
+
writable,
|
|
4272
|
+
dragging: dragId === entry.id,
|
|
4273
|
+
drop: drop?.id === entry.id ? drop.position : void 0,
|
|
4274
|
+
onPlacement: (id, placement) => {
|
|
4275
|
+
void setPlacement(id, placement);
|
|
4276
|
+
},
|
|
4277
|
+
onDragStart: setDragId,
|
|
4278
|
+
onDragOver,
|
|
4279
|
+
onDrop,
|
|
4280
|
+
onDragEnd: () => {
|
|
4281
|
+
setDragId(void 0);
|
|
4282
|
+
setDrop(void 0);
|
|
4283
|
+
},
|
|
4284
|
+
onKeyDown
|
|
4285
|
+
},
|
|
4286
|
+
entry.id
|
|
4287
|
+
)) }),
|
|
4288
|
+
state.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: noteStyle2, children: "\u5F53\u524D\u8FD8\u6CA1\u6709\u53EF\u914D\u7F6E\u7684\u5165\u53E3\u3002" }) : null,
|
|
4289
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { style: reasonStyle, children: "\u63D0\u793A\uFF1A\u62D6\u52A8\u5DE6\u4FA7\u624B\u67C4\u53EF\u4EE5\u8C03\u6574\u987A\u5E8F\uFF1B\u952E\u76D8\u4E0A/\u4E0B\u65B9\u5411\u952E\u4E5F\u80FD\u79FB\u52A8\u3002" })
|
|
4290
|
+
] });
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
// src/client/menu-store.ts
|
|
4294
|
+
var import_dsh_client_store2 = require("@deepseek-ai/dsh-client-store");
|
|
4295
|
+
var MENU_FIELD = "menu";
|
|
4296
|
+
var LOADING_STATE = {
|
|
4297
|
+
status: "loading",
|
|
4298
|
+
writable: false,
|
|
4299
|
+
entries: [],
|
|
4300
|
+
limit: DEFAULT_SIDEBAR_LIMIT,
|
|
4301
|
+
reason: void 0
|
|
4302
|
+
};
|
|
4303
|
+
function reasonFor(status, writable) {
|
|
4304
|
+
if (status === "loading") return "\u6B63\u5728\u8BFB\u53D6\u5F53\u524D\u8BBE\u7F6E\u2026";
|
|
4305
|
+
if (status === "unavailable") return "\u5F53\u524D\u8FDE\u63A5\u4E0D\u652F\u6301\u4FDD\u5B58\u672C\u673A\u504F\u597D\uFF0C\u83DC\u5355\u6682\u6309\u9ED8\u8BA4\u663E\u793A\u3002";
|
|
4306
|
+
if (!writable) return "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u83DC\u5355\u504F\u597D\u3002";
|
|
4307
|
+
return void 0;
|
|
4308
|
+
}
|
|
4309
|
+
function toPathOps(ops) {
|
|
4310
|
+
return ops.map((op) => ({ op: "set", path: [MENU_FIELD, ...op.path], value: [...op.value] }));
|
|
4311
|
+
}
|
|
4312
|
+
var MenuStore = class {
|
|
4313
|
+
/**
|
|
4314
|
+
* @param form - `ai-router` entry 的配置表单(与网关卡片同一个共享实例,见 `src/client/index.ts`)。
|
|
4315
|
+
* @param registry - feature 注册表(列表内容与顺序的另一个来源)。
|
|
4316
|
+
* @param additionalFeatures - 追加的非注册表条目(官方「插件」代理项;可用性由调用方判断)。
|
|
4317
|
+
* 缺省时列表只有注册表成员——与旧版行为一致。
|
|
4318
|
+
*/
|
|
4319
|
+
constructor(form, registry, additionalFeatures) {
|
|
4320
|
+
this.form = form;
|
|
4321
|
+
this.registry = registry;
|
|
4322
|
+
this.additionalFeatures = additionalFeatures;
|
|
4323
|
+
this.store = (0, import_dsh_client_store2.createSnapshotStore)(LOADING_STATE);
|
|
4324
|
+
const publish = () => {
|
|
4325
|
+
this.store.set(this.view());
|
|
4326
|
+
};
|
|
4327
|
+
publish();
|
|
4328
|
+
this.unsubscribe.push(form.subscribe(publish));
|
|
4329
|
+
this.unsubscribe.push(registry.subscribe(publish));
|
|
4330
|
+
}
|
|
4331
|
+
store;
|
|
4332
|
+
unsubscribe = [];
|
|
4333
|
+
/** 当前偏好(未就绪 / 值非法时按「从未自定义」处理)。 */
|
|
4334
|
+
preferences() {
|
|
4335
|
+
const snapshot = this.form.getSnapshot();
|
|
4336
|
+
if (snapshot.status !== "ready") return EMPTY_MENU_PREFERENCES;
|
|
4337
|
+
return normalizeMenuPreferences(snapshot.value?.menu);
|
|
4338
|
+
}
|
|
4339
|
+
/** 订阅偏好变化(菜单适配器用;与 `preferences()` 同源)。 */
|
|
4340
|
+
subscribePreferences(listener) {
|
|
4341
|
+
return this.form.subscribe(listener);
|
|
4342
|
+
}
|
|
4343
|
+
/** 菜单条目全集(注册表 + 追加的代理项),与侧栏适配器的来源同构。 */
|
|
4344
|
+
menuFeatures() {
|
|
4345
|
+
return [...this.registry.list(), ...this.additionalFeatures?.() ?? []];
|
|
4346
|
+
}
|
|
4347
|
+
/** 当前渲染状态。 */
|
|
4348
|
+
view() {
|
|
4349
|
+
const snapshot = this.form.getSnapshot();
|
|
4350
|
+
const value = snapshot.status === "ready" ? normalizeMenuPreferences(snapshot.value?.menu) : EMPTY_MENU_PREFERENCES;
|
|
4351
|
+
const features = this.menuFeatures();
|
|
4352
|
+
const layout = menuLayout(features, value);
|
|
4353
|
+
const placementById = new Map([
|
|
4354
|
+
...layout.sidebar.map((feature) => [feature.id, "sidebar"]),
|
|
4355
|
+
...layout.more.map((feature) => [feature.id, "more"])
|
|
4356
|
+
]);
|
|
4357
|
+
const byId = new Map(features.map((feature) => [feature.id, feature]));
|
|
4358
|
+
const ordered = orderedFeatureIds(features, value);
|
|
4359
|
+
const entries = ordered.flatMap((id, index) => {
|
|
4360
|
+
const feature = byId.get(id);
|
|
4361
|
+
if (feature === void 0) return [];
|
|
4362
|
+
return [{
|
|
4363
|
+
id,
|
|
4364
|
+
label: feature.label(),
|
|
4365
|
+
icon: feature.icon,
|
|
4366
|
+
// 生效去向:不在前两组里的就是被隐藏的(hidden 的项两者都不出现)。
|
|
4367
|
+
placement: placementById.get(id) ?? "hidden",
|
|
4368
|
+
marked: placementOf(value, id) !== void 0,
|
|
4369
|
+
index,
|
|
4370
|
+
total: ordered.length
|
|
4371
|
+
}];
|
|
4372
|
+
});
|
|
4373
|
+
const limit = Math.max(0, DEFAULT_SIDEBAR_LIMIT - value.more.filter((id) => features.some((feature) => feature.id === id)).length);
|
|
4374
|
+
return {
|
|
4375
|
+
status: snapshot.status,
|
|
4376
|
+
writable: snapshot.status === "ready" && snapshot.writable,
|
|
4377
|
+
entries,
|
|
4378
|
+
limit,
|
|
4379
|
+
reason: reasonFor(snapshot.status, snapshot.writable)
|
|
4380
|
+
};
|
|
4381
|
+
}
|
|
4382
|
+
/** 清掉在这份 store 上的全部订阅(插件卸载时调用)。 */
|
|
4383
|
+
dispose() {
|
|
4384
|
+
for (const off of this.unsubscribe.splice(0)) off();
|
|
4385
|
+
}
|
|
4386
|
+
/** 立即重算渲染状态(追加条目的可用性变化等非表单/注册表来源的变化走这里)。 */
|
|
4387
|
+
refresh() {
|
|
4388
|
+
this.store.set(this.view());
|
|
4389
|
+
}
|
|
4390
|
+
/** 注入面:设置分区组件消费。 */
|
|
4391
|
+
inject() {
|
|
4392
|
+
return {
|
|
4393
|
+
hooks: { menu: this.store },
|
|
4394
|
+
actions: {
|
|
4395
|
+
setPlacement: (id, placement) => this.setPlacement(id, placement),
|
|
4396
|
+
move: (id, offset) => this.move(id, offset),
|
|
4397
|
+
moveNear: (id, targetId, position) => this.moveNear(id, targetId, position),
|
|
4398
|
+
reset: () => this.reset()
|
|
4399
|
+
}
|
|
4400
|
+
};
|
|
4401
|
+
}
|
|
4402
|
+
async setPlacement(id, placement) {
|
|
4403
|
+
const current = this.preferences();
|
|
4404
|
+
const effective = this.view().entries.find((entry) => entry.id === id)?.placement;
|
|
4405
|
+
if (effective === placement) return;
|
|
4406
|
+
await this.write(setMenuPlacement(current, id, placement), current);
|
|
4407
|
+
}
|
|
4408
|
+
async move(id, offset) {
|
|
4409
|
+
const current = this.preferences();
|
|
4410
|
+
const ids = this.view().entries.map((entry) => entry.id);
|
|
4411
|
+
const index = ids.indexOf(id);
|
|
4412
|
+
if (index < 0) return;
|
|
4413
|
+
await this.write(moveMenuEntry(current, this.menuFeatures(), id, index + offset), current);
|
|
4414
|
+
}
|
|
4415
|
+
async moveNear(id, targetId, position) {
|
|
4416
|
+
const current = this.preferences();
|
|
4417
|
+
await this.write(moveMenuEntryNear(current, this.menuFeatures(), id, targetId, position), current);
|
|
4418
|
+
}
|
|
4419
|
+
async reset() {
|
|
4420
|
+
const current = this.form.getSnapshot();
|
|
4421
|
+
if (current.status !== "ready" || !current.writable) return;
|
|
4422
|
+
await this.form.mutate([{ op: "unset", path: [MENU_FIELD] }]);
|
|
4423
|
+
}
|
|
4424
|
+
/** 未标记项的生效去向已由 `view()` 给出,这里不再另算一份。 */
|
|
4425
|
+
async write(next, current) {
|
|
4426
|
+
const snapshot = this.form.getSnapshot();
|
|
4427
|
+
if (snapshot.status !== "ready" || !snapshot.writable) return;
|
|
4428
|
+
const ops = menuPreferenceOps(current, next);
|
|
4429
|
+
if (ops.length === 0) return;
|
|
4430
|
+
await this.form.mutate(toPathOps(ops));
|
|
4431
|
+
}
|
|
4432
|
+
};
|
|
4433
|
+
|
|
4434
|
+
// src/client/images-data.ts
|
|
4435
|
+
var IMAGE_CHANNEL = "/hiwork-image";
|
|
4436
|
+
var IMAGE_LIST_PAGE_SIZE = 20;
|
|
4437
|
+
var IMAGE_LIST_MAX_PAGE_SIZE = 100;
|
|
4438
|
+
var IMAGE_DATA_ERROR_TEXT = "\u56FE\u7247\u8BB0\u5F55\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
4439
|
+
var ImageRpcError = class extends Error {
|
|
4440
|
+
constructor(code, message) {
|
|
4441
|
+
super(message);
|
|
4442
|
+
this.code = code;
|
|
4443
|
+
this.name = "ImageRpcError";
|
|
4444
|
+
}
|
|
4445
|
+
};
|
|
4446
|
+
function unwrapImageResult(value) {
|
|
4447
|
+
if (typeof value !== "object" || value === null || !("ok" in value)) {
|
|
4448
|
+
throw new ImageRpcError("internal", "\u56FE\u7247\u4E3B\u673A\u8FD4\u56DE\u4E86\u65E0\u6548\u54CD\u5E94\u3002");
|
|
4449
|
+
}
|
|
4450
|
+
const result = value;
|
|
4451
|
+
if (result.ok === true) return result.value;
|
|
4452
|
+
if (result.ok === false) {
|
|
4453
|
+
const code = typeof result.error?.code === "string" ? result.error.code : "internal";
|
|
4454
|
+
const message = typeof result.error?.message === "string" && result.error.message.trim() !== "" ? result.error.message : IMAGE_DATA_ERROR_TEXT;
|
|
4455
|
+
throw new ImageRpcError(code, message);
|
|
4456
|
+
}
|
|
4457
|
+
throw new ImageRpcError("internal", "\u56FE\u7247\u4E3B\u673A\u8FD4\u56DE\u4E86\u65E0\u6548\u54CD\u5E94\u3002");
|
|
4458
|
+
}
|
|
4459
|
+
function imageErrorOf(error) {
|
|
4460
|
+
if (error instanceof ImageRpcError) return { code: error.code, message: error.message };
|
|
4461
|
+
return { code: "internal", message: IMAGE_DATA_ERROR_TEXT };
|
|
4462
|
+
}
|
|
4463
|
+
function str(value) {
|
|
4464
|
+
return typeof value === "string" ? value : "";
|
|
4465
|
+
}
|
|
4466
|
+
function num(value) {
|
|
4467
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
4468
|
+
}
|
|
4469
|
+
function normalizeImageListPage(value, fallbackPage, fallbackPageSize) {
|
|
4470
|
+
const record = typeof value === "object" && value !== null ? value : {};
|
|
4471
|
+
const rawItems = record["items"];
|
|
4472
|
+
if (!Array.isArray(rawItems)) throw new ImageRpcError("internal", "\u56FE\u7247\u5217\u8868\u8FD4\u56DE\u4E86\u65E0\u6548\u54CD\u5E94\u3002");
|
|
4473
|
+
const items = [];
|
|
4474
|
+
for (const entry of rawItems) {
|
|
4475
|
+
const row = typeof entry === "object" && entry !== null ? entry : {};
|
|
4476
|
+
items.push({
|
|
4477
|
+
id: num(row["id"]),
|
|
4478
|
+
prompt: str(row["prompt"]),
|
|
4479
|
+
...str(row["revisedPrompt"]) === "" ? {} : { revisedPrompt: str(row["revisedPrompt"]) },
|
|
4480
|
+
model: str(row["model"]),
|
|
4481
|
+
// `size` 是网关侧唯一的新增投影(spec §5.2 末);缺失时留空串,视图渲染 `—`。
|
|
4482
|
+
size: str(row["size"]),
|
|
4483
|
+
format: str(row["format"]),
|
|
4484
|
+
mimeType: str(row["mimeType"]),
|
|
4485
|
+
byteSize: num(row["byteSize"]),
|
|
4486
|
+
generationMs: num(row["generationMs"]),
|
|
4487
|
+
createdAt: str(row["createdAt"])
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4490
|
+
return {
|
|
4491
|
+
items,
|
|
4492
|
+
total: num(record["total"]),
|
|
4493
|
+
page: num(record["page"]) > 0 ? num(record["page"]) : fallbackPage,
|
|
4494
|
+
pageSize: num(record["pageSize"]) > 0 ? num(record["pageSize"]) : fallbackPageSize
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
function normalizeImageBytes(value) {
|
|
4498
|
+
const record = typeof value === "object" && value !== null ? value : {};
|
|
4499
|
+
const dataBase64 = str(record["dataBase64"]);
|
|
4500
|
+
if (dataBase64 === "") throw new ImageRpcError("internal", "\u56FE\u7247\u6570\u636E\u4E3A\u7A7A\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5");
|
|
4501
|
+
return {
|
|
4502
|
+
dataBase64,
|
|
4503
|
+
mimeType: str(record["mimeType"]) === "" ? "image/png" : str(record["mimeType"]),
|
|
4504
|
+
byteSize: num(record["byteSize"])
|
|
4505
|
+
};
|
|
4506
|
+
}
|
|
4507
|
+
var ImageGallerySource = class {
|
|
4508
|
+
constructor(rpc) {
|
|
4509
|
+
this.rpc = rpc;
|
|
4510
|
+
}
|
|
4511
|
+
/** 分页拉取历史记录(`page` 从 1 开始;`pageSize` 上限 100)。 */
|
|
4512
|
+
async list(page = 1, pageSize = IMAGE_LIST_PAGE_SIZE) {
|
|
4513
|
+
const size = Math.min(Math.max(1, Math.trunc(pageSize)), IMAGE_LIST_MAX_PAGE_SIZE);
|
|
4514
|
+
const raw = await this.rpc.call(IMAGE_CHANNEL, "list", { page: Math.max(1, Math.trunc(page)), pageSize: size });
|
|
4515
|
+
return normalizeImageListPage(unwrapImageResult(raw), page, size);
|
|
4516
|
+
}
|
|
4517
|
+
/** 取一张图的本体(**点哪张拉哪张**;超 20MiB 由 Host 回 `IMAGE_TOO_LARGE`)。 */
|
|
4518
|
+
async fetch(id) {
|
|
4519
|
+
const raw = await this.rpc.call(IMAGE_CHANNEL, "fetch", { id });
|
|
4520
|
+
return normalizeImageBytes(unwrapImageResult(raw));
|
|
4521
|
+
}
|
|
4522
|
+
/** 把一张图存到工作区(缺省落默认工作区,由 Host 决定)。 */
|
|
4523
|
+
async save(id, workspaceId) {
|
|
4524
|
+
const payload = workspaceId === void 0 || workspaceId === "" ? { id } : { id, workspaceId };
|
|
4525
|
+
const raw = await this.rpc.call(IMAGE_CHANNEL, "save", payload);
|
|
4526
|
+
return { path: str(unwrapImageResult(raw)["path"]) };
|
|
4527
|
+
}
|
|
4528
|
+
};
|
|
4529
|
+
function imageDataUrl(bytes) {
|
|
4530
|
+
const mime = bytes.mimeType === "" ? "image/png" : bytes.mimeType;
|
|
4531
|
+
return `data:${mime};base64,${bytes.dataBase64}`;
|
|
4532
|
+
}
|
|
4533
|
+
function formatImageBytes(bytes) {
|
|
4534
|
+
if (bytes === void 0 || !Number.isFinite(bytes) || bytes <= 0) return "";
|
|
4535
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4536
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4537
|
+
return `${bytes} B`;
|
|
4538
|
+
}
|
|
4539
|
+
function imagePromptSummary(prompt, limit = 42) {
|
|
4540
|
+
const text6 = prompt.trim().replace(/\s+/g, " ");
|
|
4541
|
+
if (text6 === "") return "\uFF08\u65E0\u63D0\u793A\u8BCD\uFF09";
|
|
4542
|
+
return text6.length <= limit ? text6 : `${text6.slice(0, limit)}\u2026`;
|
|
4543
|
+
}
|
|
4544
|
+
function imageTimeLabel(createdAt) {
|
|
4545
|
+
const date = new Date(createdAt);
|
|
4546
|
+
if (Number.isNaN(date.getTime())) return "";
|
|
4547
|
+
return date.toLocaleString("zh-CN", { hour12: false });
|
|
4548
|
+
}
|
|
4549
|
+
|
|
4550
|
+
// src/client/images-page.tsx
|
|
4551
|
+
var import_react9 = require("react");
|
|
4552
|
+
|
|
4553
|
+
// src/client/image-styles.ts
|
|
4554
|
+
var IMAGES_STYLE_ID = "hiwork-core-images-styles";
|
|
4555
|
+
var IMAGES_STYLE_TAG_ID = "hiwork-core/images.css";
|
|
4556
|
+
function ensureImagesStyle(doc) {
|
|
4557
|
+
if (doc.getElementById(IMAGES_STYLE_ID) !== null) return;
|
|
4558
|
+
const style = doc.createElement("style");
|
|
4559
|
+
style.id = IMAGES_STYLE_ID;
|
|
4560
|
+
ownStyleTag(style, IMAGES_STYLE_TAG_ID);
|
|
4561
|
+
style.textContent = [imageCardsCss, imagesPageCss].join("\n");
|
|
4562
|
+
doc.head.appendChild(style);
|
|
4563
|
+
}
|
|
4564
|
+
|
|
4565
|
+
// src/client/images-page.tsx
|
|
4566
|
+
var IMAGES_FEATURE_ID = "images";
|
|
4567
|
+
var IMAGES_FEATURE_ORDER = 60;
|
|
4568
|
+
var IMAGES_FEATURE_LABEL = "\u6211\u7684\u56FE\u7247";
|
|
4569
|
+
var IMAGES_FEATURE_EFFECT_LABEL = "hiwork-core: images feature";
|
|
4570
|
+
var IMAGES_FEATURE_ICON = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1.6" y="2.6" width="12.8" height="10.8" rx="1.8" stroke="currentColor" stroke-width="1.2"></rect><circle cx="5.7" cy="6.3" r="1.05" fill="currentColor"></circle><path d="M2.6 11.4l3.1-2.9c.4-.4 1-.4 1.4 0l2.1 2 1.4-1.3c.4-.4 1-.4 1.4 0l1.4 1.3" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"></path></svg>';
|
|
4571
|
+
var IMAGES_EMPTY_TEXT = "\u8FD8\u6CA1\u6709\u751F\u6210\u8FC7\u56FE\u7247";
|
|
4572
|
+
var IMAGES_EMPTY_HINT = "\u5728\u4F1A\u8BDD\u91CC\u8BF4\u300E\u753B\u4E00\u5F20\u2026\u300F\u5C31\u80FD\u751F\u6210";
|
|
4573
|
+
var IMAGES_LOADING_TEXT = "\u6B63\u5728\u52A0\u8F7D\u56FE\u7247\u8BB0\u5F55\u2026";
|
|
4574
|
+
var IMAGES_PREVIEW_PLACEHOLDER = "\u4ECE\u5DE6\u4FA7\u9009\u4E00\u5F20\u56FE\u7247\u67E5\u770B";
|
|
4575
|
+
var IMAGES_PREVIEW_LOADING_TEXT = "\u6B63\u5728\u52A0\u8F7D\u56FE\u7247\u2026";
|
|
4576
|
+
var IMAGES_DISABLED_NOTE = "\u7BA1\u7406\u5458\u8FD8\u6CA1\u5F00\u542F\u56FE\u7247\u751F\u6210";
|
|
4577
|
+
var IMAGES_MISSING_TEXT = "\u2014";
|
|
4578
|
+
var IMAGES_SAVE_TEXT = "\u5B58\u5230\u5DE5\u4F5C\u533A";
|
|
4579
|
+
var IMAGES_SAVE_SAVED_PREFIX = "\u5DF2\u4FDD\u5B58\u5230 ";
|
|
4580
|
+
var IMAGES_SAVE_RUNNING_TEXT = "\u6B63\u5728\u4FDD\u5B58\u2026";
|
|
4581
|
+
var IMAGES_COPY_TEXT = "\u590D\u5236\u63D0\u793A\u8BCD";
|
|
4582
|
+
var IMAGES_MORE_TEXT = "\u52A0\u8F7D\u66F4\u591A";
|
|
4583
|
+
var IMAGES_RETRY_TEXT = "\u91CD\u8BD5";
|
|
4584
|
+
var IMAGES_SAVE_HINT = "\u56FE\u7247\u4F1A\u5B58\u5230\u4F60\u7684\u9ED8\u8BA4\u5DE5\u4F5C\u533A\uFF08~/HiWork/Default\uFF09\u7684\u300C\u56FE\u7247\u300D\u76EE\u5F55\u3002";
|
|
4585
|
+
var IMAGES_COPY_HINTS = { copied: "\u5DF2\u590D\u5236", failed: "\u590D\u5236\u5931\u8D25" };
|
|
4586
|
+
async function copyText(text6) {
|
|
4587
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
4588
|
+
if (clipboard === void 0 || typeof clipboard.writeText !== "function") return false;
|
|
4589
|
+
try {
|
|
4590
|
+
await clipboard.writeText(text6);
|
|
4591
|
+
return true;
|
|
4592
|
+
} catch {
|
|
4593
|
+
return false;
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4596
|
+
function rowMeta(row) {
|
|
4597
|
+
const time = imageTimeLabel(row.createdAt);
|
|
4598
|
+
return [
|
|
4599
|
+
row.size === "" ? IMAGES_MISSING_TEXT : row.size,
|
|
4600
|
+
time === "" ? IMAGES_MISSING_TEXT : time,
|
|
4601
|
+
formatImageBytes(row.byteSize)
|
|
4602
|
+
].filter((part) => part !== "").join(" \xB7 ");
|
|
4603
|
+
}
|
|
4604
|
+
function ImagesPage({ source }) {
|
|
4605
|
+
const [rows, setRows] = (0, import_react9.useState)([]);
|
|
4606
|
+
const [total, setTotal] = (0, import_react9.useState)(0);
|
|
4607
|
+
const [page, setPage] = (0, import_react9.useState)(1);
|
|
4608
|
+
const [listState, setListState] = (0, import_react9.useState)("loading");
|
|
4609
|
+
const [listFailure, setListFailure] = (0, import_react9.useState)(null);
|
|
4610
|
+
const [selectedId, setSelectedId] = (0, import_react9.useState)(void 0);
|
|
4611
|
+
const [preview, setPreview] = (0, import_react9.useState)({ status: "idle" });
|
|
4612
|
+
const [saveState, setSaveState] = (0, import_react9.useState)({ status: "idle" });
|
|
4613
|
+
const [copyHint, setCopyHint] = (0, import_react9.useState)("");
|
|
4614
|
+
const listTicket = (0, import_react9.useRef)(0);
|
|
4615
|
+
const previewTicket = (0, import_react9.useRef)(0);
|
|
4616
|
+
const alive = (0, import_react9.useRef)(true);
|
|
4617
|
+
(0, import_react9.useEffect)(() => {
|
|
4618
|
+
if (typeof document === "undefined") return;
|
|
4619
|
+
ensureImagesStyle(document);
|
|
4620
|
+
}, []);
|
|
4621
|
+
(0, import_react9.useEffect)(() => {
|
|
4622
|
+
alive.current = true;
|
|
4623
|
+
return () => {
|
|
4624
|
+
alive.current = false;
|
|
4625
|
+
};
|
|
4626
|
+
}, []);
|
|
4627
|
+
const load = (0, import_react9.useCallback)(async (nextPage, append) => {
|
|
4628
|
+
const current = listTicket.current + 1;
|
|
4629
|
+
listTicket.current = current;
|
|
4630
|
+
setListState("loading");
|
|
4631
|
+
try {
|
|
4632
|
+
const result = await source.list(nextPage, IMAGE_LIST_PAGE_SIZE);
|
|
4633
|
+
if (!alive.current || current !== listTicket.current) return;
|
|
4634
|
+
setRows((prev) => append ? [...prev, ...result.items.filter((item) => !prev.some((existing) => existing.id === item.id))] : result.items);
|
|
4635
|
+
setTotal(result.total);
|
|
4636
|
+
setPage(result.page);
|
|
4637
|
+
setListFailure(null);
|
|
4638
|
+
setListState("ready");
|
|
4639
|
+
} catch (error) {
|
|
4640
|
+
if (!alive.current || current !== listTicket.current) return;
|
|
4641
|
+
setListFailure(imageErrorOf(error));
|
|
4642
|
+
setListState("failed");
|
|
4643
|
+
}
|
|
4644
|
+
}, [source]);
|
|
4645
|
+
(0, import_react9.useEffect)(() => {
|
|
4646
|
+
void load(1, false);
|
|
4647
|
+
}, [load]);
|
|
4648
|
+
const select = (0, import_react9.useCallback)(async (row) => {
|
|
4649
|
+
setSelectedId(row.id);
|
|
4650
|
+
setSaveState({ status: "idle" });
|
|
4651
|
+
setCopyHint("");
|
|
4652
|
+
const current = previewTicket.current + 1;
|
|
4653
|
+
previewTicket.current = current;
|
|
4654
|
+
setPreview({ status: "loading" });
|
|
4655
|
+
try {
|
|
4656
|
+
const bytes = await source.fetch(row.id);
|
|
4657
|
+
if (!alive.current || current !== previewTicket.current) return;
|
|
4658
|
+
setPreview({ status: "ready", url: imageDataUrl(bytes), byteSize: bytes.byteSize });
|
|
4659
|
+
} catch (error) {
|
|
4660
|
+
if (!alive.current || current !== previewTicket.current) return;
|
|
4661
|
+
const failure = imageErrorOf(error);
|
|
4662
|
+
setPreview({ status: "failed", code: failure.code, message: failure.message });
|
|
4663
|
+
}
|
|
4664
|
+
}, [source]);
|
|
4665
|
+
const save = (0, import_react9.useCallback)(async () => {
|
|
4666
|
+
if (selectedId === void 0) return;
|
|
4667
|
+
setSaveState({ status: "saving" });
|
|
4668
|
+
try {
|
|
4669
|
+
const { path } = await source.save(selectedId);
|
|
4670
|
+
if (!alive.current) return;
|
|
4671
|
+
setSaveState({ status: "saved", path });
|
|
4672
|
+
} catch (error) {
|
|
4673
|
+
if (!alive.current) return;
|
|
4674
|
+
setSaveState({ status: "failed", message: imageErrorOf(error).message });
|
|
4675
|
+
}
|
|
4676
|
+
}, [source, selectedId]);
|
|
4677
|
+
const selected = rows.find((row) => row.id === selectedId);
|
|
4678
|
+
const disabled = listFailure?.code === "IMAGE_DISABLED" || preview.status === "failed" && preview.code === "IMAGE_DISABLED";
|
|
4679
|
+
const previewBody = (() => {
|
|
4680
|
+
if (preview.status === "idle") {
|
|
4681
|
+
return (0, import_react9.createElement)("p", { className: "hiwork-images-note", "data-hiwork-images-placeholder": "" }, IMAGES_PREVIEW_PLACEHOLDER);
|
|
4682
|
+
}
|
|
4683
|
+
if (preview.status === "loading") {
|
|
4684
|
+
return (0, import_react9.createElement)("p", { className: "hiwork-images-note", "data-hiwork-images-loading": "" }, IMAGES_PREVIEW_LOADING_TEXT);
|
|
4685
|
+
}
|
|
4686
|
+
if (preview.status === "failed") {
|
|
4687
|
+
return (0, import_react9.createElement)(
|
|
4688
|
+
"div",
|
|
4689
|
+
{ className: "hiwork-images-actions", "data-hiwork-images-preview-error": preview.code },
|
|
4690
|
+
(0, import_react9.createElement)("span", { className: "hiwork-images-error" }, preview.message),
|
|
4691
|
+
selected === void 0 ? null : (0, import_react9.createElement)(
|
|
4692
|
+
"button",
|
|
4693
|
+
{ type: "button", className: "hiwork-images-action", "data-hiwork-images-retry-preview": "", onClick: () => {
|
|
4694
|
+
void select(selected);
|
|
4695
|
+
} },
|
|
4696
|
+
IMAGES_RETRY_TEXT
|
|
4697
|
+
)
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
return (0, import_react9.createElement)(
|
|
4701
|
+
"div",
|
|
4702
|
+
{ className: "hiwork-images-preview-body" },
|
|
4703
|
+
// 字节来自本次 fetch:data URL 渲染,**不写盘、不指向任何本地文件**。
|
|
4704
|
+
(0, import_react9.createElement)("img", {
|
|
4705
|
+
className: "hiwork-images-image",
|
|
4706
|
+
src: preview.url,
|
|
4707
|
+
alt: selected === void 0 || selected.prompt.trim() === "" ? "\u5386\u53F2\u56FE\u7247" : selected.prompt.trim(),
|
|
4708
|
+
"data-hiwork-images-image": ""
|
|
4709
|
+
}),
|
|
4710
|
+
selected === void 0 ? null : (0, import_react9.createElement)("p", { className: "hiwork-images-preview-prompt", "data-hiwork-images-prompt": "" }, selected.prompt),
|
|
4711
|
+
selected === void 0 ? null : (0, import_react9.createElement)("p", { className: "hiwork-images-note", "data-hiwork-images-preview-meta": "" }, [
|
|
4712
|
+
rowMeta(selected),
|
|
4713
|
+
formatImageBytes(preview.byteSize)
|
|
4714
|
+
].filter((part) => part !== "").join(" \xB7 ")),
|
|
4715
|
+
(0, import_react9.createElement)(
|
|
4716
|
+
"div",
|
|
4717
|
+
{ className: "hiwork-images-actions" },
|
|
4718
|
+
(0, import_react9.createElement)(
|
|
4719
|
+
"button",
|
|
4720
|
+
{
|
|
4721
|
+
type: "button",
|
|
4722
|
+
className: "hiwork-images-action",
|
|
4723
|
+
"data-hiwork-images-save": "",
|
|
4724
|
+
disabled: saveState.status === "saving",
|
|
4725
|
+
onClick: () => {
|
|
4726
|
+
void save();
|
|
4727
|
+
}
|
|
4728
|
+
},
|
|
4729
|
+
saveState.status === "saving" ? IMAGES_SAVE_RUNNING_TEXT : IMAGES_SAVE_TEXT
|
|
4730
|
+
),
|
|
4731
|
+
(0, import_react9.createElement)(
|
|
4732
|
+
"button",
|
|
4733
|
+
{
|
|
4734
|
+
type: "button",
|
|
4735
|
+
className: "hiwork-images-action",
|
|
4736
|
+
"data-hiwork-images-copy": "",
|
|
4737
|
+
onClick: () => {
|
|
4738
|
+
void copyText(selected?.prompt ?? "").then((ok) => {
|
|
4739
|
+
setCopyHint(ok ? IMAGES_COPY_HINTS.copied : IMAGES_COPY_HINTS.failed);
|
|
4740
|
+
});
|
|
4741
|
+
}
|
|
4742
|
+
},
|
|
4743
|
+
IMAGES_COPY_TEXT
|
|
4744
|
+
),
|
|
4745
|
+
copyHint === "" ? null : (0, import_react9.createElement)("span", { className: "hiwork-images-note", "data-hiwork-images-copy-state": copyHint }, copyHint)
|
|
4746
|
+
),
|
|
4747
|
+
saveState.status === "saved" ? (0, import_react9.createElement)("span", { className: "hiwork-images-saved", "data-hiwork-images-saved": "" }, `${IMAGES_SAVE_SAVED_PREFIX}${saveState.path}`) : null,
|
|
4748
|
+
saveState.status === "failed" ? (0, import_react9.createElement)("span", { className: "hiwork-images-error", "data-hiwork-images-save-error": "" }, saveState.message) : null,
|
|
4749
|
+
(0, import_react9.createElement)("p", { className: "hiwork-images-note" }, IMAGES_SAVE_HINT)
|
|
4750
|
+
);
|
|
4751
|
+
})();
|
|
4752
|
+
const empty = listState === "ready" && rows.length === 0;
|
|
4753
|
+
return (0, import_react9.createElement)(
|
|
4754
|
+
"section",
|
|
4755
|
+
{ className: "hiwork-images", "data-hiwork-images": "", "data-hiwork-images-state": listState },
|
|
4756
|
+
(0, import_react9.createElement)(
|
|
4757
|
+
"div",
|
|
4758
|
+
{ className: "hiwork-images-head" },
|
|
4759
|
+
(0, import_react9.createElement)("h2", { className: "hiwork-images-title" }, IMAGES_FEATURE_LABEL),
|
|
4760
|
+
(0, import_react9.createElement)(
|
|
4761
|
+
"span",
|
|
4762
|
+
{ className: "hiwork-images-count", "data-hiwork-images-count": "" },
|
|
4763
|
+
listState === "loading" && rows.length === 0 ? IMAGES_LOADING_TEXT : `\u5171 ${total} \u5F20`
|
|
4764
|
+
)
|
|
4765
|
+
),
|
|
4766
|
+
// 关开关只关「生成」不关「查看」:历史照常可见,但要说清当前不能再生成(spec §7.2 V11)。
|
|
4767
|
+
disabled ? (0, import_react9.createElement)("div", { className: "hiwork-images-banner", "data-hiwork-images-disabled": "" }, IMAGES_DISABLED_NOTE) : null,
|
|
4768
|
+
// 列表级失败:行内红字 + 重试(成功过一页的话**保留已有行**,不清空)。
|
|
4769
|
+
listFailure === null ? null : (0, import_react9.createElement)(
|
|
4770
|
+
"div",
|
|
4771
|
+
{ className: "hiwork-images-actions", "data-hiwork-images-error": listFailure.code },
|
|
4772
|
+
(0, import_react9.createElement)("span", { className: "hiwork-images-error" }, listFailure.message),
|
|
4773
|
+
(0, import_react9.createElement)(
|
|
4774
|
+
"button",
|
|
4775
|
+
{ type: "button", className: "hiwork-images-action", "data-hiwork-images-retry": "", onClick: () => {
|
|
4776
|
+
void load(page, rows.length > 0);
|
|
4777
|
+
} },
|
|
4778
|
+
IMAGES_RETRY_TEXT
|
|
4779
|
+
)
|
|
4780
|
+
),
|
|
4781
|
+
empty ? (0, import_react9.createElement)(
|
|
4782
|
+
"div",
|
|
4783
|
+
{ className: "hiwork-images-empty", "data-hiwork-images-empty": "" },
|
|
4784
|
+
(0, import_react9.createElement)("p", { className: "hiwork-images-empty-title" }, IMAGES_EMPTY_TEXT),
|
|
4785
|
+
(0, import_react9.createElement)("p", { className: "hiwork-images-note" }, IMAGES_EMPTY_HINT)
|
|
4786
|
+
) : (0, import_react9.createElement)(
|
|
4787
|
+
"div",
|
|
4788
|
+
{ className: "hiwork-images-body" },
|
|
4789
|
+
(0, import_react9.createElement)(
|
|
4790
|
+
"ul",
|
|
4791
|
+
{ className: "hiwork-images-list", "data-hiwork-images-list": "" },
|
|
4792
|
+
...rows.map((row) => (0, import_react9.createElement)(
|
|
4793
|
+
"li",
|
|
4794
|
+
{ key: row.id },
|
|
4795
|
+
(0, import_react9.createElement)(
|
|
4796
|
+
"button",
|
|
4797
|
+
{
|
|
4798
|
+
type: "button",
|
|
4799
|
+
className: "hiwork-images-row",
|
|
4800
|
+
"data-hiwork-images-row": row.id,
|
|
4801
|
+
"aria-pressed": row.id === selectedId,
|
|
4802
|
+
onClick: () => {
|
|
4803
|
+
void select(row);
|
|
4804
|
+
}
|
|
4805
|
+
},
|
|
4806
|
+
(0, import_react9.createElement)("span", { className: "hiwork-images-row-prompt" }, imagePromptSummary(row.prompt)),
|
|
4807
|
+
(0, import_react9.createElement)("span", { className: "hiwork-images-row-meta", "data-hiwork-images-row-meta": "" }, rowMeta(row))
|
|
4808
|
+
)
|
|
4809
|
+
))
|
|
4810
|
+
),
|
|
4811
|
+
(0, import_react9.createElement)("div", { className: "hiwork-images-preview", "data-hiwork-images-preview": "" }, previewBody)
|
|
4812
|
+
),
|
|
4813
|
+
rows.length > 0 && rows.length < total ? (0, import_react9.createElement)(
|
|
4814
|
+
"button",
|
|
4815
|
+
{
|
|
4816
|
+
type: "button",
|
|
4817
|
+
className: "hiwork-images-more",
|
|
4818
|
+
"data-hiwork-images-more": "",
|
|
4819
|
+
disabled: listState === "loading",
|
|
4820
|
+
onClick: () => {
|
|
4821
|
+
void load(page + 1, true);
|
|
4822
|
+
}
|
|
4823
|
+
},
|
|
4824
|
+
IMAGES_MORE_TEXT
|
|
4825
|
+
) : null
|
|
4826
|
+
);
|
|
4827
|
+
}
|
|
4828
|
+
function imagesFeature(source) {
|
|
4829
|
+
return {
|
|
4830
|
+
id: IMAGES_FEATURE_ID,
|
|
4831
|
+
order: IMAGES_FEATURE_ORDER,
|
|
4832
|
+
label: () => IMAGES_FEATURE_LABEL,
|
|
4833
|
+
icon: IMAGES_FEATURE_ICON,
|
|
4834
|
+
render: () => (0, import_react9.createElement)(ImagesPage, { source })
|
|
4835
|
+
};
|
|
4836
|
+
}
|
|
4837
|
+
function installImagesFeature(scope, registry, source) {
|
|
4838
|
+
scope.effect(() => registry.register(imagesFeature(source)), IMAGES_FEATURE_EFFECT_LABEL);
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
// src/client/image-cards.tsx
|
|
4842
|
+
var import_react10 = require("react");
|
|
4843
|
+
var IMAGE_GENERATE_TOOL_NAME = "image_generate";
|
|
4844
|
+
var TOOL_VIEW_SLOT = "tool.call.toolview";
|
|
4845
|
+
var IMAGE_CARD_TITLE = "\u751F\u6210\u56FE\u7247";
|
|
4846
|
+
var IMAGE_CARD_GENERIC_ERROR = "\u56FE\u7247\u751F\u6210\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
4847
|
+
var IMAGE_ERROR_TEXT = {
|
|
4848
|
+
IMAGE_DISABLED: "\u7BA1\u7406\u5458\u8FD8\u6CA1\u5F00\u542F\u56FE\u7247\u751F\u6210",
|
|
4849
|
+
IMAGE_NOT_CONFIGURED: "\u56FE\u7247\u751F\u6210\u8FD8\u6CA1\u6709\u914D\u7F6E\u597D\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458",
|
|
4850
|
+
IMAGE_BUDGET_EXCEEDED: "\u751F\u56FE\u6682\u65F6\u4E0D\u53EF\u7528\uFF1A\u53EF\u80FD\u662F\u672C\u6708\u989D\u5EA6\u7528\u5C3D\uFF0C\u4E5F\u53EF\u80FD\u662F\u8FD9\u4E2A\u6A21\u578B\u8FD8\u6CA1\u914D\u7F6E\u597D\u2014\u2014\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458",
|
|
4851
|
+
IMAGE_BAD_REQUEST: "\u8FD9\u4E2A\u63D0\u793A\u8BCD\u6CA1\u80FD\u751F\u6210\uFF0C\u6362\u4E2A\u8BF4\u6CD5\u518D\u8BD5",
|
|
4852
|
+
IMAGE_TOO_LARGE: "\u751F\u6210\u7684\u56FE\u7247\u592A\u5927\u4E86\uFF0C\u6362\u4E2A\u5C0F\u4E00\u70B9\u7684\u5C3A\u5BF8\uFF08\u5982 1024x1024\uFF09",
|
|
4853
|
+
IMAGE_TIMEOUT: "\u751F\u6210\u8D85\u65F6\u4E86\uFF0C\u518D\u8BD5\u4E00\u6B21\uFF08\u590D\u6742\u753B\u9762\u4F1A\u6BD4\u8F83\u6162\uFF09",
|
|
4854
|
+
IMAGE_UPSTREAM_ERROR: "\u56FE\u7247\u751F\u6210\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
4855
|
+
GATEWAY_UNREACHABLE: "\u56FE\u7247\u751F\u6210\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
4856
|
+
// spec §6.6 把 SESSION_EXPIRED 并入「服务不可用」那一行(**逐字同该行**),只在括号里
|
|
4857
|
+
// 注明例外「提示重新登录」——所以不加自创文案,另给一句 {@link IMAGE_RELOGIN_HINT}。
|
|
4858
|
+
SESSION_EXPIRED: "\u56FE\u7247\u751F\u6210\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5",
|
|
4859
|
+
// Host 侧写盘失败(src/imagegen.ts 的两种原因):图已经生成、也已经计费,绝不能让
|
|
4860
|
+
// 员工以为白花了钱——文案必须说清「图在,只是没落到工作区」。
|
|
4861
|
+
"write-failed": "\u56FE\u7247\u5DF2\u751F\u6210\u4F46\u6CA1\u80FD\u4FDD\u5B58\u5230\u5DE5\u4F5C\u533A",
|
|
4862
|
+
"path-outside-workspace": "\u56FE\u7247\u6CA1\u80FD\u4FDD\u5B58\u5230\u5DE5\u4F5C\u533A\uFF1A\u76EE\u6807\u8DEF\u5F84\u4E0D\u5728\u5DE5\u4F5C\u533A\u91CC",
|
|
4863
|
+
cancelled: "\u5DF2\u53D6\u6D88",
|
|
4864
|
+
// 平台级中断码(`interrupted` 是 shell 自己的停止标记,不是网关错误码)。
|
|
4865
|
+
interrupted: "\u5DF2\u505C\u6B62"
|
|
4866
|
+
};
|
|
4867
|
+
var IMAGE_RETRYABLE_CODES = [
|
|
4868
|
+
"IMAGE_BAD_REQUEST",
|
|
4869
|
+
"IMAGE_TOO_LARGE",
|
|
4870
|
+
"IMAGE_TIMEOUT",
|
|
4871
|
+
"IMAGE_UPSTREAM_ERROR",
|
|
4872
|
+
"GATEWAY_UNREACHABLE"
|
|
4873
|
+
];
|
|
4874
|
+
var IMAGE_RELOGIN_CODES = ["SESSION_EXPIRED"];
|
|
4875
|
+
var IMAGE_RELOGIN_HINT = "\u8BF7\u91CD\u65B0\u767B\u5F55";
|
|
4876
|
+
function needsRelogin(code) {
|
|
4877
|
+
return IMAGE_RELOGIN_CODES.includes(code);
|
|
4878
|
+
}
|
|
4879
|
+
function imageErrorText(code, message = "") {
|
|
4880
|
+
const known = IMAGE_ERROR_TEXT[code];
|
|
4881
|
+
if (known !== void 0) return known;
|
|
4882
|
+
return message.trim() === "" ? IMAGE_CARD_GENERIC_ERROR : message;
|
|
4883
|
+
}
|
|
4884
|
+
function isRetryableImageError(code) {
|
|
4885
|
+
return IMAGE_RETRYABLE_CODES.includes(code);
|
|
4886
|
+
}
|
|
4887
|
+
function asRecord(value) {
|
|
4888
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
4889
|
+
}
|
|
4890
|
+
function str2(value) {
|
|
4891
|
+
return typeof value === "string" ? value : "";
|
|
4892
|
+
}
|
|
4893
|
+
function num2(value) {
|
|
4894
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4895
|
+
}
|
|
4896
|
+
function parseImageArgs(argsRaw) {
|
|
4897
|
+
if (typeof argsRaw !== "string" || argsRaw.trim() === "") return {};
|
|
4898
|
+
try {
|
|
4899
|
+
return asRecord(JSON.parse(argsRaw));
|
|
4900
|
+
} catch {
|
|
4901
|
+
return {};
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
function parseImageOutcome(content) {
|
|
4905
|
+
if (!Array.isArray(content)) return null;
|
|
4906
|
+
for (const block of content) {
|
|
4907
|
+
const text6 = typeof block?.text === "string" ? block.text : "";
|
|
4908
|
+
if (text6 === "") continue;
|
|
4909
|
+
const trimmed = text6.trim();
|
|
4910
|
+
if (!trimmed.startsWith("{")) continue;
|
|
4911
|
+
let parsed;
|
|
4912
|
+
try {
|
|
4913
|
+
parsed = JSON.parse(trimmed);
|
|
4914
|
+
} catch {
|
|
4915
|
+
continue;
|
|
4916
|
+
}
|
|
4917
|
+
const record = asRecord(parsed);
|
|
4918
|
+
if (typeof record["ok"] !== "boolean") continue;
|
|
4919
|
+
return record;
|
|
4920
|
+
}
|
|
4921
|
+
return null;
|
|
4922
|
+
}
|
|
4923
|
+
function parseImagePathsFromText(content) {
|
|
4924
|
+
if (!Array.isArray(content)) return [];
|
|
4925
|
+
const paths = [];
|
|
4926
|
+
for (const block of content) {
|
|
4927
|
+
const text6 = typeof block?.text === "string" ? block.text : "";
|
|
4928
|
+
for (const match of text6.matchAll(/<path>([^<>]+)<\/path>/g)) {
|
|
4929
|
+
const path = (match[1] ?? "").trim();
|
|
4930
|
+
if (path !== "" && !paths.includes(path)) paths.push(path);
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
return paths;
|
|
4934
|
+
}
|
|
4935
|
+
function isAbsoluteImagePath(path) {
|
|
4936
|
+
return path.startsWith("/") || /^[A-Za-z]:[\\/]/.test(path);
|
|
4937
|
+
}
|
|
4938
|
+
function imageFileUrl(path, baseURI) {
|
|
4939
|
+
if (!isAbsoluteImagePath(path)) return "";
|
|
4940
|
+
return new URL(`api/file?path=${encodeURIComponent(path)}`, baseURI).href;
|
|
4941
|
+
}
|
|
4942
|
+
function abbreviateHome(path, home) {
|
|
4943
|
+
if (home === void 0 || home === "") return path;
|
|
4944
|
+
const trimmed = home.replace(/[\\/]+$/, "");
|
|
4945
|
+
if (trimmed === "") return path;
|
|
4946
|
+
if (path === trimmed) return "~";
|
|
4947
|
+
if (path.startsWith(`${trimmed}/`) || path.startsWith(`${trimmed}\\`)) return `~${path.slice(trimmed.length)}`;
|
|
4948
|
+
return path;
|
|
4949
|
+
}
|
|
4950
|
+
function formatGenerationMs(ms) {
|
|
4951
|
+
if (ms === void 0 || ms < 0) return "";
|
|
4952
|
+
if (ms < 1e3) return `${ms} \u6BEB\u79D2`;
|
|
4953
|
+
return `${(ms / 1e3).toFixed(1)} \u79D2`;
|
|
4954
|
+
}
|
|
4955
|
+
function formatByteSize(bytes) {
|
|
4956
|
+
if (bytes === void 0 || bytes < 0) return "";
|
|
4957
|
+
if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
4958
|
+
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
4959
|
+
return `${bytes} B`;
|
|
4960
|
+
}
|
|
4961
|
+
function toImageCardModel(block, baseURI) {
|
|
4962
|
+
const settled = block?.kind === "tool-result" || block?.phase === "result";
|
|
4963
|
+
const errorRecord = asRecord(block?.error);
|
|
4964
|
+
const errorCode = str2(errorRecord["code"]);
|
|
4965
|
+
const state = settled ? errorCode === "interrupted" ? "stopped" : block?.isError === true ? "error" : "ok" : "running";
|
|
4966
|
+
const args = parseImageArgs(settled ? block?.call?.argsRaw : block?.argsRaw);
|
|
4967
|
+
const outcome = settled ? parseImageOutcome(block?.content) : null;
|
|
4968
|
+
const prompt = str2(outcome?.["prompt"]) || str2(args["prompt"]);
|
|
4969
|
+
const size = str2(outcome?.["size"]) || str2(args["size"]);
|
|
4970
|
+
const model = str2(outcome?.["model"]);
|
|
4971
|
+
const generationMs = num2(outcome?.["generationMs"]);
|
|
4972
|
+
const alt = prompt.trim() === "" ? "\u751F\u6210\u7684\u56FE\u7247" : prompt.trim();
|
|
4973
|
+
const outcomeFailed = outcome !== null && outcome["ok"] === false;
|
|
4974
|
+
const transportFailed = !outcomeFailed && (state === "error" || state === "stopped");
|
|
4975
|
+
const rawFailureCode = outcomeFailed ? str2(outcome["code"]) : errorCode;
|
|
4976
|
+
const failureCode = rawFailureCode === "" && transportFailed ? "IMAGE_UPSTREAM_ERROR" : rawFailureCode;
|
|
4977
|
+
const failure = outcomeFailed ? imageErrorText(failureCode, str2(outcome["message"])) : transportFailed ? imageErrorText(failureCode, str2(errorRecord["reason"]) || str2(errorRecord["name"])) : null;
|
|
4978
|
+
const files = [];
|
|
4979
|
+
if (settled && failure === null) {
|
|
4980
|
+
const rawFiles = Array.isArray(outcome?.["files"]) ? outcome["files"] : void 0;
|
|
4981
|
+
if (rawFiles !== void 0) {
|
|
4982
|
+
for (const entry of rawFiles) {
|
|
4983
|
+
const record = asRecord(entry);
|
|
4984
|
+
const path = str2(record["path"]);
|
|
4985
|
+
const url = imageFileUrl(path, baseURI);
|
|
4986
|
+
if (url === "") continue;
|
|
4987
|
+
files.push({
|
|
4988
|
+
path,
|
|
4989
|
+
url,
|
|
4990
|
+
alt,
|
|
4991
|
+
byteSize: num2(record["byteSize"]),
|
|
4992
|
+
mimeType: str2(record["mimeType"]),
|
|
4993
|
+
revisedPrompt: str2(record["revisedPrompt"]) === "" ? void 0 : str2(record["revisedPrompt"])
|
|
4994
|
+
});
|
|
4995
|
+
}
|
|
4996
|
+
} else {
|
|
4997
|
+
for (const path of parseImagePathsFromText(block?.content)) {
|
|
4998
|
+
const url = imageFileUrl(path, baseURI);
|
|
4999
|
+
if (url !== "") {
|
|
5000
|
+
files.push({ path, url, alt, byteSize: void 0, mimeType: "", revisedPrompt: void 0 });
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
return {
|
|
5006
|
+
state,
|
|
5007
|
+
prompt,
|
|
5008
|
+
size,
|
|
5009
|
+
model,
|
|
5010
|
+
generationMs,
|
|
5011
|
+
files,
|
|
5012
|
+
error: failure,
|
|
5013
|
+
errorCode: failureCode,
|
|
5014
|
+
retryable: failure !== null && isRetryableImageError(failureCode)
|
|
5015
|
+
};
|
|
5016
|
+
}
|
|
5017
|
+
var COPY_HINTS2 = { idle: "", copied: "\u5DF2\u590D\u5236", failed: "\u590D\u5236\u5931\u8D25" };
|
|
5018
|
+
async function copyPromptText(text6) {
|
|
5019
|
+
const clipboard = typeof navigator === "undefined" ? void 0 : navigator.clipboard;
|
|
5020
|
+
if (clipboard === void 0 || typeof clipboard.writeText !== "function") return false;
|
|
5021
|
+
try {
|
|
5022
|
+
await clipboard.writeText(text6);
|
|
5023
|
+
return true;
|
|
5024
|
+
} catch {
|
|
5025
|
+
return false;
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
var COPY_HINT_REVERT_MS2 = 1800;
|
|
5029
|
+
function currentBaseURI() {
|
|
5030
|
+
return typeof document === "undefined" ? "" : document.baseURI;
|
|
5031
|
+
}
|
|
5032
|
+
function ImageToolCard(props) {
|
|
5033
|
+
const { block, toolName = IMAGE_GENERATE_TOOL_NAME, home, openFile, inspect } = props;
|
|
5034
|
+
const model = toImageCardModel(block, currentBaseURI());
|
|
5035
|
+
const [zoom, setZoom] = (0, import_react10.useState)(null);
|
|
5036
|
+
const [copyHint, setCopyHint] = (0, import_react10.useState)(COPY_HINTS2.idle);
|
|
5037
|
+
const revertTimer = (0, import_react10.useRef)(void 0);
|
|
5038
|
+
(0, import_react10.useEffect)(() => {
|
|
5039
|
+
if (typeof document === "undefined") return;
|
|
5040
|
+
ensureImagesStyle(document);
|
|
5041
|
+
}, []);
|
|
5042
|
+
(0, import_react10.useEffect)(() => () => {
|
|
5043
|
+
if (revertTimer.current !== void 0) clearTimeout(revertTimer.current);
|
|
5044
|
+
}, []);
|
|
5045
|
+
const copy = (0, import_react10.useCallback)(() => {
|
|
5046
|
+
void copyPromptText(model.prompt).then((ok) => {
|
|
5047
|
+
setCopyHint(ok ? COPY_HINTS2.copied : COPY_HINTS2.failed);
|
|
5048
|
+
if (revertTimer.current !== void 0) clearTimeout(revertTimer.current);
|
|
5049
|
+
revertTimer.current = setTimeout(() => {
|
|
5050
|
+
setCopyHint(COPY_HINTS2.idle);
|
|
5051
|
+
}, COPY_HINT_REVERT_MS2);
|
|
5052
|
+
});
|
|
5053
|
+
}, [model.prompt]);
|
|
5054
|
+
const summary = () => {
|
|
5055
|
+
if (model.state === "running") return "\u6B63\u5728\u751F\u6210\u56FE\u7247\u2026";
|
|
5056
|
+
if (model.error !== null && model.files.length === 0) return "\u5931\u8D25";
|
|
5057
|
+
const count = model.files.length === 0 ? "" : `\u5DF2\u751F\u6210 ${model.files.length} \u5F20`;
|
|
5058
|
+
const elapsed = formatGenerationMs(model.generationMs);
|
|
5059
|
+
return [count, elapsed].filter((part) => part !== "").join(" \xB7 ");
|
|
5060
|
+
};
|
|
5061
|
+
const meta = () => {
|
|
5062
|
+
const parts = [model.size, formatGenerationMs(model.generationMs), model.model].filter((part) => part !== "");
|
|
5063
|
+
return parts.join(" \xB7 ");
|
|
5064
|
+
};
|
|
5065
|
+
return (0, import_react10.createElement)(
|
|
5066
|
+
"div",
|
|
5067
|
+
{
|
|
5068
|
+
className: "hiwork-image-card",
|
|
5069
|
+
"data-hiwork-image-card": toolName,
|
|
5070
|
+
"data-state": model.state
|
|
5071
|
+
},
|
|
5072
|
+
(0, import_react10.createElement)(
|
|
5073
|
+
"div",
|
|
5074
|
+
{ className: "hiwork-image-card-head" },
|
|
5075
|
+
(0, import_react10.createElement)("span", { className: "hiwork-image-card-title" }, IMAGE_CARD_TITLE),
|
|
5076
|
+
(0, import_react10.createElement)("span", { className: "hiwork-image-card-summary" }, summary())
|
|
5077
|
+
),
|
|
5078
|
+
// 提示词:running 阶段(start)就要显示——那是用户唯一的「它在按我说的画」凭据。
|
|
5079
|
+
model.prompt === "" ? null : (0, import_react10.createElement)("p", { className: "hiwork-image-card-prompt", "data-hiwork-image-prompt": "" }, model.prompt),
|
|
5080
|
+
...model.files.map((file) => (0, import_react10.createElement)(
|
|
5081
|
+
"figure",
|
|
5082
|
+
{ key: file.path, className: "hiwork-image-card-figure" },
|
|
5083
|
+
(0, import_react10.createElement)("img", {
|
|
5084
|
+
className: "hiwork-image-card-img",
|
|
5085
|
+
// 只画绝对路径(相对路径与远端 URL 保持惰性,与平台同规则):地址为空时不渲染本块。
|
|
5086
|
+
src: file.url,
|
|
5087
|
+
alt: file.alt,
|
|
5088
|
+
title: file.alt,
|
|
5089
|
+
"data-hiwork-image-file": file.path,
|
|
5090
|
+
onClick: () => {
|
|
5091
|
+
setZoom(file);
|
|
5092
|
+
}
|
|
5093
|
+
}),
|
|
5094
|
+
(0, import_react10.createElement)(
|
|
5095
|
+
"figcaption",
|
|
5096
|
+
{ className: "hiwork-image-card-actions" },
|
|
5097
|
+
(0, import_react10.createElement)("span", { className: "hiwork-image-card-path", title: file.path, "data-hiwork-image-path": "" }, abbreviateHome(file.path, home)),
|
|
5098
|
+
meta() === "" ? null : (0, import_react10.createElement)("span", { className: "hiwork-image-card-meta", "data-hiwork-image-meta": "" }, [meta(), formatByteSize(file.byteSize)].filter((part) => part !== "").join(" \xB7 ")),
|
|
5099
|
+
// 可选能力:宿主没给 openFile 就不渲染按钮(渲染一个点了没反应的控件更差)。
|
|
5100
|
+
typeof openFile === "function" ? (0, import_react10.createElement)(
|
|
5101
|
+
"button",
|
|
5102
|
+
{
|
|
5103
|
+
type: "button",
|
|
5104
|
+
className: "hiwork-image-card-action",
|
|
5105
|
+
"data-hiwork-image-open": file.path,
|
|
5106
|
+
onClick: () => {
|
|
5107
|
+
openFile(file.path);
|
|
5108
|
+
}
|
|
5109
|
+
},
|
|
5110
|
+
"\u6253\u5F00"
|
|
5111
|
+
) : null
|
|
5112
|
+
)
|
|
5113
|
+
)),
|
|
5114
|
+
// 卡片级动作:复制提示词(提示词是卡片级信息,每张图复制一次即可)。
|
|
5115
|
+
model.prompt === "" ? null : (0, import_react10.createElement)(
|
|
5116
|
+
"div",
|
|
5117
|
+
{ className: "hiwork-image-card-actions" },
|
|
5118
|
+
(0, import_react10.createElement)(
|
|
5119
|
+
"button",
|
|
5120
|
+
{ type: "button", className: "hiwork-image-card-action", "data-hiwork-image-copy": "", onClick: copy },
|
|
5121
|
+
"\u590D\u5236\u63D0\u793A\u8BCD"
|
|
5122
|
+
),
|
|
5123
|
+
copyHint === "" ? null : (0, import_react10.createElement)("span", { className: "hiwork-image-card-copy-state", "data-hiwork-image-copy-state": copyHint }, copyHint),
|
|
5124
|
+
inspect === void 0 ? null : (0, import_react10.createElement)(
|
|
5125
|
+
"button",
|
|
5126
|
+
{ type: "button", className: "hiwork-image-card-action", "data-hiwork-image-inspect": "", onClick: inspect },
|
|
5127
|
+
"\u67E5\u770B\u8F68\u8FF9"
|
|
5128
|
+
)
|
|
5129
|
+
),
|
|
5130
|
+
model.error === null ? null : (0, import_react10.createElement)(
|
|
5131
|
+
"p",
|
|
5132
|
+
{ className: "hiwork-image-card-error", role: "alert", "data-hiwork-image-error": model.errorCode },
|
|
5133
|
+
model.error,
|
|
5134
|
+
model.retryable ? (0, import_react10.createElement)("span", { className: "hiwork-image-card-retry-hint", "data-hiwork-image-retry-hint": "" }, "\u53EF\u4EE5\u518D\u8BD5\u4E00\u6B21") : null,
|
|
5135
|
+
needsRelogin(model.errorCode) ? (0, import_react10.createElement)("span", { className: "hiwork-image-card-retry-hint", "data-hiwork-image-relogin-hint": "" }, IMAGE_RELOGIN_HINT) : null
|
|
5136
|
+
),
|
|
5137
|
+
zoom === null ? null : (0, import_react10.createElement)(
|
|
5138
|
+
"div",
|
|
5139
|
+
{
|
|
5140
|
+
className: "hiwork-image-zoom",
|
|
5141
|
+
role: "dialog",
|
|
5142
|
+
"aria-label": zoom.alt,
|
|
5143
|
+
"data-hiwork-image-zoom": "",
|
|
5144
|
+
onClick: () => {
|
|
5145
|
+
setZoom(null);
|
|
5146
|
+
}
|
|
5147
|
+
},
|
|
5148
|
+
(0, import_react10.createElement)("img", { src: zoom.url, alt: zoom.alt }),
|
|
5149
|
+
(0, import_react10.createElement)(
|
|
5150
|
+
"button",
|
|
5151
|
+
{
|
|
5152
|
+
type: "button",
|
|
5153
|
+
className: "hiwork-image-zoom-close",
|
|
5154
|
+
"data-hiwork-image-zoom-close": "",
|
|
5155
|
+
onClick: () => {
|
|
5156
|
+
setZoom(null);
|
|
5157
|
+
}
|
|
5158
|
+
},
|
|
5159
|
+
"\u5173\u95ED"
|
|
5160
|
+
)
|
|
5161
|
+
)
|
|
5162
|
+
);
|
|
5163
|
+
}
|
|
5164
|
+
function installImageToolCard(ctx) {
|
|
5165
|
+
const slots = ctx.slots;
|
|
5166
|
+
if (slots === void 0 || typeof slots.inject !== "function") return;
|
|
5167
|
+
slots.inject(TOOL_VIEW_SLOT, () => slots.register(
|
|
5168
|
+
{ name: TOOL_VIEW_SLOT, key: IMAGE_GENERATE_TOOL_NAME },
|
|
5169
|
+
ImageToolCard
|
|
5170
|
+
));
|
|
5171
|
+
}
|
|
5172
|
+
|
|
5173
|
+
// src/client/session-cost.tsx
|
|
5174
|
+
var import_react11 = require("react");
|
|
5175
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
5176
|
+
var SESSION_COST_ENDPOINT = "estimate";
|
|
5177
|
+
var SESSION_COST_CACHE_TTL_MS = 5 * 6e4;
|
|
5178
|
+
var SESSION_COST_HINT = "\u6309\u7F51\u5173\u4EF7\u683C\u4F30\u7B97\uFF0C\u4EC5\u4F9B\u53C2\u8003\uFF1B\u4EE5\u300C\u7528\u91CF\u4E0E\u6210\u672C\u300D\u4E2D\u7684\u6743\u5A01\u8D26\u5355\u4E3A\u51C6";
|
|
5179
|
+
var SESSION_COST_ESTIMATED_SUFFIX = "\uFF08\u4F30\u7B97\uFF09";
|
|
5180
|
+
function nonNegativeInteger(value) {
|
|
5181
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5182
|
+
}
|
|
5183
|
+
function sessionCostMatchOf(data) {
|
|
5184
|
+
if (typeof data !== "object" || data === null) return null;
|
|
5185
|
+
const turn = nonNegativeInteger(data.turn);
|
|
5186
|
+
if (turn === void 0) return null;
|
|
5187
|
+
const usage = data.tokenUsage;
|
|
5188
|
+
if (typeof usage !== "object" || usage === null) return null;
|
|
5189
|
+
const input = nonNegativeInteger(usage.uncachedInputTokens);
|
|
5190
|
+
const output = nonNegativeInteger(usage.outputTokens);
|
|
5191
|
+
if (input === void 0 || output === void 0) return null;
|
|
5192
|
+
const routes = usage.routes;
|
|
5193
|
+
if (!Array.isArray(routes) || routes.length === 0) return null;
|
|
5194
|
+
const pairs = [];
|
|
5195
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5196
|
+
for (const entry of routes) {
|
|
5197
|
+
if (typeof entry !== "object" || entry === null) return null;
|
|
5198
|
+
const { provider, model } = entry;
|
|
5199
|
+
if (typeof provider !== "string" || provider === "" || typeof model !== "string" || model === "") return null;
|
|
5200
|
+
const key = `${provider}\0${model}`;
|
|
5201
|
+
if (seen.has(key)) continue;
|
|
5202
|
+
seen.add(key);
|
|
5203
|
+
pairs.push({ route: provider, model });
|
|
5204
|
+
}
|
|
5205
|
+
if (pairs.length === 0) return null;
|
|
5206
|
+
const cacheRead = nonNegativeInteger(usage.cacheReadTokens);
|
|
5207
|
+
const cacheWrite = nonNegativeInteger(usage.cacheWriteTokens);
|
|
5208
|
+
if (input === 0 && output === 0 && (cacheRead ?? 0) === 0 && (cacheWrite ?? 0) === 0) return null;
|
|
5209
|
+
const first = pairs[0];
|
|
5210
|
+
if (first === void 0) return null;
|
|
5211
|
+
const at = rfc3339Of(data.time);
|
|
5212
|
+
const item = {
|
|
5213
|
+
route: first.route,
|
|
5214
|
+
model: first.model,
|
|
5215
|
+
input_tokens: input,
|
|
5216
|
+
output_tokens: output,
|
|
5217
|
+
// 桶缺失表示「并非每次尝试都上报了该桶」——原样省略,绝不猜 0。
|
|
5218
|
+
...cacheRead === void 0 ? {} : { cache_read_tokens: cacheRead },
|
|
5219
|
+
...cacheWrite === void 0 ? {} : { cache_write_tokens: cacheWrite },
|
|
5220
|
+
...at === void 0 ? {} : { at }
|
|
5221
|
+
};
|
|
5222
|
+
const ambiguous = pairs.length > 1 || cacheRead === void 0 || cacheWrite === void 0;
|
|
5223
|
+
return { items: [item], turn, ambiguous };
|
|
5224
|
+
}
|
|
5225
|
+
function rfc3339Of(time) {
|
|
5226
|
+
if (typeof time !== "number" || !Number.isFinite(time) || time <= 0) return void 0;
|
|
5227
|
+
const date = new Date(time);
|
|
5228
|
+
return Number.isNaN(date.getTime()) ? void 0 : date.toISOString();
|
|
5229
|
+
}
|
|
5230
|
+
function closingTurnTailData(snapshot, messageId) {
|
|
5231
|
+
for (const key of snapshot.order) {
|
|
5232
|
+
const node = snapshot.nodes.get(key);
|
|
5233
|
+
if (node === void 0 || node.kind !== "turn-tail") continue;
|
|
5234
|
+
const data = node.data;
|
|
5235
|
+
if (data === void 0) continue;
|
|
5236
|
+
if (data.closing?.finalNode?.messageId === messageId) return data;
|
|
5237
|
+
}
|
|
5238
|
+
return void 0;
|
|
5239
|
+
}
|
|
5240
|
+
function sessionCostKey(sessionId, match) {
|
|
5241
|
+
return `${sessionId}\0${match.turn}\0${JSON.stringify(match.items)}`;
|
|
5242
|
+
}
|
|
5243
|
+
function normalizeSessionCost(value) {
|
|
5244
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
5245
|
+
const record = value;
|
|
5246
|
+
if (record.priced !== true) return void 0;
|
|
5247
|
+
const cost = record.cost;
|
|
5248
|
+
if (typeof cost !== "number" || !Number.isFinite(cost) || cost < 0) return void 0;
|
|
5249
|
+
return { cost, channelResolved: record.channelResolved === true };
|
|
5250
|
+
}
|
|
5251
|
+
var SessionCostStore = class {
|
|
5252
|
+
constructor(rpc, options = {}) {
|
|
5253
|
+
this.rpc = rpc;
|
|
5254
|
+
this.now = options.now ?? (() => Date.now());
|
|
5255
|
+
this.ttlMs = options.ttlMs ?? SESSION_COST_CACHE_TTL_MS;
|
|
5256
|
+
}
|
|
5257
|
+
entries = /* @__PURE__ */ new Map();
|
|
5258
|
+
pending = /* @__PURE__ */ new Map();
|
|
5259
|
+
now;
|
|
5260
|
+
ttlMs;
|
|
5261
|
+
/** 当前缓存条目数(测试断言用)。 */
|
|
5262
|
+
get size() {
|
|
5263
|
+
return this.entries.size;
|
|
5264
|
+
}
|
|
5265
|
+
/** 清空缓存(登出 / 切换账号)。 */
|
|
5266
|
+
clear() {
|
|
5267
|
+
this.entries.clear();
|
|
5268
|
+
}
|
|
5269
|
+
async estimate(input) {
|
|
5270
|
+
const key = sessionCostKey(input.sessionId, { turn: input.turn, items: [...input.items] });
|
|
5271
|
+
const cached = this.entries.get(key);
|
|
5272
|
+
if (cached !== void 0 && this.now() - cached.fetchedAt < this.ttlMs) return cached.result;
|
|
5273
|
+
const inflight = this.pending.get(key);
|
|
5274
|
+
if (inflight !== void 0) return await inflight;
|
|
5275
|
+
const task = this.request(key, input);
|
|
5276
|
+
this.pending.set(key, task);
|
|
5277
|
+
try {
|
|
5278
|
+
return await task;
|
|
5279
|
+
} finally {
|
|
5280
|
+
this.pending.delete(key);
|
|
3318
5281
|
}
|
|
3319
|
-
}
|
|
3320
|
-
|
|
3321
|
-
const sidebar = sidebarRoot();
|
|
3322
|
-
if (sidebar === void 0) return true;
|
|
3323
|
-
if (menuElement === void 0 || !sidebar.contains(menuElement)) return true;
|
|
3324
|
-
if (registry.activeId() === null) return false;
|
|
3325
|
-
const host = conversationContainer();
|
|
3326
|
-
if (host === void 0) return true;
|
|
3327
|
-
return container === void 0 || !host.contains(container);
|
|
3328
|
-
};
|
|
3329
|
-
const scheduleRetry = () => {
|
|
3330
|
-
if (disposed || retryTimer !== void 0 || retries >= maxRetries) return;
|
|
3331
|
-
retryTimer = setTimeout(() => {
|
|
3332
|
-
retryTimer = void 0;
|
|
3333
|
-
retries += 1;
|
|
3334
|
-
sync();
|
|
3335
|
-
if (pending()) scheduleRetry();
|
|
3336
|
-
}, interval);
|
|
3337
|
-
};
|
|
3338
|
-
const onDocumentClickCapture = (event) => {
|
|
3339
|
-
if (registry.activeId() === null) return;
|
|
3340
|
-
const target = event.target;
|
|
3341
|
-
if (!(target instanceof Element)) return;
|
|
3342
|
-
if (target.closest(`[${FEATURE_ROOT_ATTRIBUTE}]`) !== null) return;
|
|
3343
|
-
if (target.closest(`[${MENU_ATTRIBUTE}]`) !== null) return;
|
|
3344
|
-
if (target.closest(SESSION_ROW_SELECTOR) === null) return;
|
|
3345
|
-
registry.openNative();
|
|
3346
|
-
};
|
|
3347
|
-
const unsubscribeRegistry = registry.subscribe(() => {
|
|
3348
|
-
sync();
|
|
3349
|
-
});
|
|
3350
|
-
let unsubscribeLocale;
|
|
3351
|
-
if (options.subscribeLocale !== void 0) {
|
|
5282
|
+
}
|
|
5283
|
+
async request(key, input) {
|
|
3352
5284
|
try {
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
} catch
|
|
3358
|
-
|
|
5285
|
+
const raw = await this.rpc.call(USAGE_CHANNEL, SESSION_COST_ENDPOINT, { items: input.items }, input.signal);
|
|
5286
|
+
const result = normalizeSessionCost(unwrapUsageResult(raw));
|
|
5287
|
+
if (result !== void 0) this.entries.set(key, { result, fetchedAt: this.now() });
|
|
5288
|
+
return result;
|
|
5289
|
+
} catch {
|
|
5290
|
+
return void 0;
|
|
3359
5291
|
}
|
|
3360
5292
|
}
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
5293
|
+
};
|
|
5294
|
+
var costStyle = {
|
|
5295
|
+
// 字号/颜色与同一行的「用量」「用时」胶囊同源变量,保证在动作行里不显得突兀。
|
|
5296
|
+
color: textTertiary,
|
|
5297
|
+
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
5298
|
+
lineHeight: "18px",
|
|
5299
|
+
whiteSpace: "nowrap",
|
|
5300
|
+
cursor: "default"
|
|
5301
|
+
};
|
|
5302
|
+
function SessionCost({ messageId, sessionId, useChat, store }) {
|
|
5303
|
+
const data = useChat((snapshot) => closingTurnTailData(snapshot, messageId));
|
|
5304
|
+
const match = sessionCostMatchOf(data);
|
|
5305
|
+
const key = match === null ? null : sessionCostKey(sessionId, match);
|
|
5306
|
+
const [shown, setShown] = (0, import_react11.useState)(void 0);
|
|
5307
|
+
(0, import_react11.useEffect)(() => {
|
|
5308
|
+
if (match === null || key === null) return;
|
|
5309
|
+
let disposed = false;
|
|
5310
|
+
const controller = new AbortController();
|
|
5311
|
+
void store.estimate({ sessionId, turn: match.turn, items: match.items, signal: controller.signal }).then((value) => {
|
|
5312
|
+
if (!disposed) setShown({ key, result: value });
|
|
5313
|
+
}).catch(() => {
|
|
5314
|
+
if (!disposed) setShown({ key, result: void 0 });
|
|
5315
|
+
});
|
|
5316
|
+
return () => {
|
|
3376
5317
|
disposed = true;
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
if (style.getAttribute(OWNER_ATTRIBUTE) === owner) style.remove();
|
|
3401
|
-
},
|
|
3402
|
-
openNativeHome: () => {
|
|
3403
|
-
renderContext.openNativeHome();
|
|
3404
|
-
}
|
|
5318
|
+
controller.abort();
|
|
5319
|
+
};
|
|
5320
|
+
}, [store, key]);
|
|
5321
|
+
if (match === null || key === null || shown === void 0 || shown.key !== key || shown.result === void 0) return null;
|
|
5322
|
+
const result = shown.result;
|
|
5323
|
+
const estimated = !result.channelResolved || match.ambiguous;
|
|
5324
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { style: costStyle, "data-hiwork-session-cost": estimated ? "estimated" : "exact", title: SESSION_COST_HINT, children: `\u2248 ${formatYuan(result.cost)}${estimated ? SESSION_COST_ESTIMATED_SUFFIX : ""}` });
|
|
5325
|
+
}
|
|
5326
|
+
|
|
5327
|
+
// src/client/account-cache.ts
|
|
5328
|
+
function accountIdentityOf(state) {
|
|
5329
|
+
return `${state.username ?? ""}\0${state.activeKeyId ?? ""}`;
|
|
5330
|
+
}
|
|
5331
|
+
function observeAccountChange(source, onChange) {
|
|
5332
|
+
let identity = accountIdentityOf(source.getSnapshot());
|
|
5333
|
+
const unsubscribe = source.subscribe(() => {
|
|
5334
|
+
const next = accountIdentityOf(source.getSnapshot());
|
|
5335
|
+
if (next === identity) return;
|
|
5336
|
+
identity = next;
|
|
5337
|
+
onChange();
|
|
5338
|
+
});
|
|
5339
|
+
return () => {
|
|
5340
|
+
unsubscribe();
|
|
3405
5341
|
};
|
|
3406
5342
|
}
|
|
3407
5343
|
|
|
@@ -4405,17 +6341,17 @@ var PmsTodoStore = class {
|
|
|
4405
6341
|
};
|
|
4406
6342
|
|
|
4407
6343
|
// src/client/voice/MicButton.tsx
|
|
4408
|
-
var
|
|
4409
|
-
var
|
|
6344
|
+
var import_react12 = require("react");
|
|
6345
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
4410
6346
|
function MicIcon({ size }) {
|
|
4411
|
-
return /* @__PURE__ */ (0,
|
|
4412
|
-
/* @__PURE__ */ (0,
|
|
4413
|
-
/* @__PURE__ */ (0,
|
|
4414
|
-
/* @__PURE__ */ (0,
|
|
6347
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
6348
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("rect", { x: "9", y: "2.75", width: "6", height: "11.5", rx: "3", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
6349
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M5.5 11.5a6.5 6.5 0 0 0 13 0", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
|
|
6350
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("path", { d: "M12 18v3.25", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
|
|
4415
6351
|
] });
|
|
4416
6352
|
}
|
|
4417
6353
|
function MicButton({ open }) {
|
|
4418
|
-
return /* @__PURE__ */ (0,
|
|
6354
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
4419
6355
|
"button",
|
|
4420
6356
|
{
|
|
4421
6357
|
type: "button",
|
|
@@ -4424,13 +6360,13 @@ function MicButton({ open }) {
|
|
|
4424
6360
|
"aria-label": "\u8BED\u97F3\u8F93\u5165",
|
|
4425
6361
|
"aria-haspopup": "dialog",
|
|
4426
6362
|
onClick: () => open(),
|
|
4427
|
-
children: /* @__PURE__ */ (0,
|
|
6363
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(MicIcon, { size: 18 })
|
|
4428
6364
|
}
|
|
4429
6365
|
);
|
|
4430
6366
|
}
|
|
4431
6367
|
|
|
4432
6368
|
// src/client/voice/entry.ts
|
|
4433
|
-
var
|
|
6369
|
+
var import_react14 = require("react");
|
|
4434
6370
|
var import_client3 = require("react-dom/client");
|
|
4435
6371
|
|
|
4436
6372
|
// src/client/voice/composer.ts
|
|
@@ -4486,7 +6422,7 @@ function insertDraftText(sessions, text6, sessionId) {
|
|
|
4486
6422
|
}
|
|
4487
6423
|
|
|
4488
6424
|
// src/client/voice/VoiceOverlay.tsx
|
|
4489
|
-
var
|
|
6425
|
+
var import_react13 = require("react");
|
|
4490
6426
|
|
|
4491
6427
|
// src/client/voice/recording.ts
|
|
4492
6428
|
var RecordingError = class extends Error {
|
|
@@ -4769,7 +6705,7 @@ function createVoiceRuntime(rpc) {
|
|
|
4769
6705
|
}
|
|
4770
6706
|
|
|
4771
6707
|
// src/client/voice/VoiceOverlay.tsx
|
|
4772
|
-
var
|
|
6708
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
4773
6709
|
var BAR_COUNT = 28;
|
|
4774
6710
|
var LEVEL_INTERVAL_MS = 80;
|
|
4775
6711
|
var TICK_INTERVAL_MS = 200;
|
|
@@ -4783,25 +6719,25 @@ function rmsToLevel(rms) {
|
|
|
4783
6719
|
return Math.min(1, Math.max(0, rms) * 6);
|
|
4784
6720
|
}
|
|
4785
6721
|
function MicIcon2({ size }) {
|
|
4786
|
-
return /* @__PURE__ */ (0,
|
|
4787
|
-
/* @__PURE__ */ (0,
|
|
4788
|
-
/* @__PURE__ */ (0,
|
|
4789
|
-
/* @__PURE__ */ (0,
|
|
6722
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true", children: [
|
|
6723
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("rect", { x: "9", y: "2.75", width: "6", height: "11.5", rx: "3", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
6724
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M5.5 11.5a6.5 6.5 0 0 0 13 0", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" }),
|
|
6725
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("path", { d: "M12 18v3.25", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round" })
|
|
4790
6726
|
] });
|
|
4791
6727
|
}
|
|
4792
6728
|
function VoiceOverlay(props) {
|
|
4793
6729
|
const { runtime, onClose, insertDraft } = props;
|
|
4794
|
-
const [phase, setPhase] = (0,
|
|
4795
|
-
const [error, setError] = (0,
|
|
4796
|
-
const [levels, setLevels] = (0,
|
|
4797
|
-
const [elapsed, setElapsed] = (0,
|
|
4798
|
-
const [maxDuration, setMaxDuration] = (0,
|
|
4799
|
-
const [copied, setCopied] = (0,
|
|
4800
|
-
const recordingRef = (0,
|
|
4801
|
-
const wavRef = (0,
|
|
4802
|
-
const startedAtRef = (0,
|
|
4803
|
-
const abortRef = (0,
|
|
4804
|
-
const disposedRef = (0,
|
|
6730
|
+
const [phase, setPhase] = (0, import_react13.useState)("checking");
|
|
6731
|
+
const [error, setError] = (0, import_react13.useState)(void 0);
|
|
6732
|
+
const [levels, setLevels] = (0, import_react13.useState)(() => Array.from({ length: BAR_COUNT }, () => 0.06));
|
|
6733
|
+
const [elapsed, setElapsed] = (0, import_react13.useState)(0);
|
|
6734
|
+
const [maxDuration, setMaxDuration] = (0, import_react13.useState)(60);
|
|
6735
|
+
const [copied, setCopied] = (0, import_react13.useState)(false);
|
|
6736
|
+
const recordingRef = (0, import_react13.useRef)(void 0);
|
|
6737
|
+
const wavRef = (0, import_react13.useRef)(void 0);
|
|
6738
|
+
const startedAtRef = (0, import_react13.useRef)(0);
|
|
6739
|
+
const abortRef = (0, import_react13.useRef)(void 0);
|
|
6740
|
+
const disposedRef = (0, import_react13.useRef)(false);
|
|
4805
6741
|
const failWith = (message, opts = {}) => {
|
|
4806
6742
|
if (disposedRef.current) return;
|
|
4807
6743
|
setPhase("error");
|
|
@@ -4898,7 +6834,7 @@ function VoiceOverlay(props) {
|
|
|
4898
6834
|
failWith(err instanceof Error ? err.message : "\u5F55\u97F3\u5904\u7406\u5931\u8D25\u3002", { holdWav: false });
|
|
4899
6835
|
});
|
|
4900
6836
|
};
|
|
4901
|
-
(0,
|
|
6837
|
+
(0, import_react13.useEffect)(() => {
|
|
4902
6838
|
disposedRef.current = false;
|
|
4903
6839
|
let cancelled = false;
|
|
4904
6840
|
runtime.status().then((status) => {
|
|
@@ -4921,7 +6857,7 @@ function VoiceOverlay(props) {
|
|
|
4921
6857
|
recordingRef.current = void 0;
|
|
4922
6858
|
};
|
|
4923
6859
|
}, []);
|
|
4924
|
-
(0,
|
|
6860
|
+
(0, import_react13.useEffect)(() => {
|
|
4925
6861
|
if (phase !== "recording") return;
|
|
4926
6862
|
const levelTimer = window.setInterval(() => {
|
|
4927
6863
|
const rms = recordingRef.current?.amplitude() ?? 0;
|
|
@@ -4935,7 +6871,7 @@ function VoiceOverlay(props) {
|
|
|
4935
6871
|
window.clearInterval(clockTimer);
|
|
4936
6872
|
};
|
|
4937
6873
|
}, [phase]);
|
|
4938
|
-
(0,
|
|
6874
|
+
(0, import_react13.useEffect)(() => {
|
|
4939
6875
|
if (phase !== "recording") return;
|
|
4940
6876
|
if (elapsed < maxDuration) return;
|
|
4941
6877
|
finish();
|
|
@@ -4944,7 +6880,7 @@ function VoiceOverlay(props) {
|
|
|
4944
6880
|
abortRef.current?.abort();
|
|
4945
6881
|
onClose();
|
|
4946
6882
|
};
|
|
4947
|
-
(0,
|
|
6883
|
+
(0, import_react13.useEffect)(() => {
|
|
4948
6884
|
const onKey = (event) => {
|
|
4949
6885
|
if (event.key !== "Escape") return;
|
|
4950
6886
|
event.preventDefault();
|
|
@@ -4962,21 +6898,21 @@ function VoiceOverlay(props) {
|
|
|
4962
6898
|
const idle = phase === "checking" || phase === "requesting" || phase === "transcribing";
|
|
4963
6899
|
const canRetryTranscribe = error?.holdWav === true && error.recognizedText === void 0;
|
|
4964
6900
|
const canRestart = error?.canRestart === true;
|
|
4965
|
-
return /* @__PURE__ */ (0,
|
|
6901
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4966
6902
|
"div",
|
|
4967
6903
|
{
|
|
4968
6904
|
className: "hiwv-backdrop",
|
|
4969
6905
|
onMouseDown: (event) => {
|
|
4970
6906
|
if (event.target === event.currentTarget && !idle) handleClose();
|
|
4971
6907
|
},
|
|
4972
|
-
children: /* @__PURE__ */ (0,
|
|
4973
|
-
/* @__PURE__ */ (0,
|
|
4974
|
-
/* @__PURE__ */ (0,
|
|
4975
|
-
/* @__PURE__ */ (0,
|
|
6908
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "hiwv-panel", role: "dialog", "aria-modal": "true", "aria-label": "\u8BED\u97F3\u8F93\u5165", children: [
|
|
6909
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "hiwv-head", children: [
|
|
6910
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "hiwv-title", children: "\u8BED\u97F3\u8F93\u5165" }),
|
|
6911
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "hiwv-hint", children: recording ? `\u5DF2\u5F55 ${formatClock(elapsed)} / \u4E0A\u9650 ${formatClock(maxDuration)}` : `\u5355\u6B21\u6700\u957F ${formatClock(maxDuration)}` })
|
|
4976
6912
|
] }),
|
|
4977
|
-
/* @__PURE__ */ (0,
|
|
4978
|
-
/* @__PURE__ */ (0,
|
|
4979
|
-
/* @__PURE__ */ (0,
|
|
6913
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `hiwv-stage${recording ? "" : " hiwv-stageIdle"}`, children: recording || phase === "transcribing" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
6914
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: `hiwv-orb${phase === "transcribing" ? " hiwv-orbBusy" : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MicIcon2, { size: 30 }) }),
|
|
6915
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hiwv-bars", "aria-hidden": "true", children: levels.map((level, index) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
4980
6916
|
"i",
|
|
4981
6917
|
{
|
|
4982
6918
|
style: { height: `${Math.round(8 + level * 46)}px` },
|
|
@@ -4984,19 +6920,19 @@ function VoiceOverlay(props) {
|
|
|
4984
6920
|
},
|
|
4985
6921
|
index
|
|
4986
6922
|
)) }),
|
|
4987
|
-
/* @__PURE__ */ (0,
|
|
4988
|
-
] }) : phase === "checking" || phase === "requesting" ? /* @__PURE__ */ (0,
|
|
4989
|
-
/* @__PURE__ */ (0,
|
|
4990
|
-
error?.recognizedText !== void 0 && /* @__PURE__ */ (0,
|
|
4991
|
-
/* @__PURE__ */ (0,
|
|
4992
|
-
/* @__PURE__ */ (0,
|
|
6923
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hiwv-status", children: phase === "transcribing" ? "\u6B63\u5728\u8BC6\u522B\u2026" : "\u6B63\u5728\u8046\u542C\uFF0C\u8BB2\u5B8C\u540E\u70B9\u300C\u5B8C\u6210\u300D" })
|
|
6924
|
+
] }) : phase === "checking" || phase === "requesting" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hiwv-status", children: "\u6B63\u5728\u51C6\u5907\u9EA6\u514B\u98CE\u2026" }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "hiwv-errorBox", children: [
|
|
6925
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hiwv-errorMsg", children: error?.message ?? "\u51FA\u4E86\u70B9\u95EE\u9898\u3002" }),
|
|
6926
|
+
error?.recognizedText !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "hiwv-recognized", children: [
|
|
6927
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "hiwv-recognizedText", children: error.recognizedText }),
|
|
6928
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "hiwv-btn hiwv-btnSecondary", onClick: copyRecognized, children: copied ? "\u5DF2\u590D\u5236" : "\u590D\u5236\u6587\u672C" })
|
|
4993
6929
|
] })
|
|
4994
6930
|
] }) }),
|
|
4995
|
-
/* @__PURE__ */ (0,
|
|
4996
|
-
/* @__PURE__ */ (0,
|
|
4997
|
-
recording && /* @__PURE__ */ (0,
|
|
4998
|
-
phase === "error" && canRetryTranscribe && /* @__PURE__ */ (0,
|
|
4999
|
-
phase === "error" && canRestart && /* @__PURE__ */ (0,
|
|
6931
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "hiwv-actions", children: [
|
|
6932
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "hiwv-btn hiwv-btnSecondary", onClick: handleClose, children: "\u53D6\u6D88" }),
|
|
6933
|
+
recording && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "hiwv-btn hiwv-btnPrimary", onClick: finish, children: "\u5B8C\u6210" }),
|
|
6934
|
+
phase === "error" && canRetryTranscribe && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "hiwv-btn hiwv-btnPrimary", onClick: transcribeWav, children: "\u91CD\u8BD5\u8BC6\u522B" }),
|
|
6935
|
+
phase === "error" && canRestart && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
5000
6936
|
"button",
|
|
5001
6937
|
{
|
|
5002
6938
|
type: "button",
|
|
@@ -5060,7 +6996,7 @@ function createOverlayController(deps) {
|
|
|
5060
6996
|
doc.body.appendChild(container);
|
|
5061
6997
|
root = (0, import_client3.createRoot)(container);
|
|
5062
6998
|
root.render(
|
|
5063
|
-
(0,
|
|
6999
|
+
(0, import_react14.createElement)(VoiceOverlay, {
|
|
5064
7000
|
runtime: deps.runtime,
|
|
5065
7001
|
onClose: close,
|
|
5066
7002
|
// 会话 id 缺省(拿不到槽位注入)时由 insertDraftText 退到当前打开会话;
|
|
@@ -5133,7 +7069,7 @@ async function installVoiceInput(deps) {
|
|
|
5133
7069
|
}
|
|
5134
7070
|
|
|
5135
7071
|
// src/client/index.ts
|
|
5136
|
-
var inject = ["slots", "locale", "remote", "remote.credentials", "
|
|
7072
|
+
var inject = ["slots", "locale", "remote", "remote.credentials", "configForms", "connection", "layout"];
|
|
5137
7073
|
var INTERACTION_STYLE_ID = "hiwork-core-interaction-styles";
|
|
5138
7074
|
function injectInteractionStyles() {
|
|
5139
7075
|
if (typeof document === "undefined") return;
|
|
@@ -5146,12 +7082,12 @@ function injectInteractionStyles() {
|
|
|
5146
7082
|
}
|
|
5147
7083
|
function apply(ctx) {
|
|
5148
7084
|
injectInteractionStyles();
|
|
5149
|
-
const
|
|
7085
|
+
const form = ctx.configForms.get(AI_ROUTER_NS);
|
|
5150
7086
|
const storeCredential = async (ref, value) => {
|
|
5151
7087
|
const response = await ctx.remote.credentials.set(ref, value);
|
|
5152
7088
|
if (!response.ok) throw new Error(response.error.message);
|
|
5153
7089
|
};
|
|
5154
|
-
const controller = new AiRouterCardController(
|
|
7090
|
+
const controller = new AiRouterCardController(form, storeCredential);
|
|
5155
7091
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
5156
7092
|
name: "settings.section",
|
|
5157
7093
|
id: AI_ROUTER_NS,
|
|
@@ -5159,12 +7095,6 @@ function apply(ctx) {
|
|
|
5159
7095
|
label: () => AI_ROUTER_SECTION_LABEL,
|
|
5160
7096
|
inject: () => controller.inject()
|
|
5161
7097
|
}, AiRouterSection));
|
|
5162
|
-
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
5163
|
-
name: "settings.section",
|
|
5164
|
-
id: "hiwork-about",
|
|
5165
|
-
order: Number.MAX_SAFE_INTEGER,
|
|
5166
|
-
label: () => "\u5173\u4E8E"
|
|
5167
|
-
}, AboutSection));
|
|
5168
7098
|
const connection = ctx.get("connection");
|
|
5169
7099
|
if (connection === void 0) throw new Error("connection service unavailable");
|
|
5170
7100
|
const usage = new UsageDataStore(connection.rpc);
|
|
@@ -5175,6 +7105,13 @@ function apply(ctx) {
|
|
|
5175
7105
|
const managedWorkbench = new ManagedWorkbenchStore(connection.rpc);
|
|
5176
7106
|
const pmsTodo = new PmsTodoStore(connection.rpc);
|
|
5177
7107
|
const appVersion = createAppVersion(connection.rpc);
|
|
7108
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
7109
|
+
name: "settings.section",
|
|
7110
|
+
id: "hiwork-about",
|
|
7111
|
+
order: Number.MAX_SAFE_INTEGER,
|
|
7112
|
+
label: () => "\u5173\u4E8E",
|
|
7113
|
+
inject: () => ({ appVersion })
|
|
7114
|
+
}, AboutSection));
|
|
5178
7115
|
const pluginUpdates = createPluginUpdates(connection.rpc);
|
|
5179
7116
|
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
5180
7117
|
name: "settings.section",
|
|
@@ -5183,6 +7120,57 @@ function apply(ctx) {
|
|
|
5183
7120
|
label: () => "\u7528\u91CF\u4E0E\u6210\u672C",
|
|
5184
7121
|
inject: () => ({ hooks: controller.inject().hooks, usage })
|
|
5185
7122
|
}, UsageSection));
|
|
7123
|
+
const featureRegistry = createFeatureRegistry();
|
|
7124
|
+
const images = new ImageGallerySource(connection.rpc);
|
|
7125
|
+
installImagesFeature(ctx, featureRegistry, images);
|
|
7126
|
+
installImageToolCard(ctx);
|
|
7127
|
+
const PLUGINS_PANEL_ID = "plugins";
|
|
7128
|
+
const asPanelId = (id) => id;
|
|
7129
|
+
const layoutFace = () => {
|
|
7130
|
+
try {
|
|
7131
|
+
return ctx.layout ?? void 0;
|
|
7132
|
+
} catch {
|
|
7133
|
+
return void 0;
|
|
7134
|
+
}
|
|
7135
|
+
};
|
|
7136
|
+
const nativePanel = {
|
|
7137
|
+
pluginsAvailable: () => {
|
|
7138
|
+
const layout = layoutFace();
|
|
7139
|
+
if (layout === void 0) return false;
|
|
7140
|
+
try {
|
|
7141
|
+
return ctx.slots.entries("main").some((entry) => entry.options.key === PLUGINS_PANEL_ID);
|
|
7142
|
+
} catch {
|
|
7143
|
+
return false;
|
|
7144
|
+
}
|
|
7145
|
+
},
|
|
7146
|
+
anyActive: () => (layoutFace()?.panelInfo.getSnapshot().activePanelId ?? null) !== null,
|
|
7147
|
+
pluginsActive: () => layoutFace()?.panelInfo.getSnapshot().activePanelId === PLUGINS_PANEL_ID,
|
|
7148
|
+
openPlugins: () => {
|
|
7149
|
+
layoutFace()?.selectPanel(asPanelId(PLUGINS_PANEL_ID));
|
|
7150
|
+
},
|
|
7151
|
+
returnToConversation: () => {
|
|
7152
|
+
layoutFace()?.selectPanel(null);
|
|
7153
|
+
},
|
|
7154
|
+
subscribe: (listener) => {
|
|
7155
|
+
const layout = layoutFace();
|
|
7156
|
+
if (layout === void 0) return () => void 0;
|
|
7157
|
+
const offSlots = ctx.slots.subscribe("main", listener);
|
|
7158
|
+
const offPanel = layout.panelInfo.subscribe(listener);
|
|
7159
|
+
return () => {
|
|
7160
|
+
offSlots();
|
|
7161
|
+
offPanel();
|
|
7162
|
+
};
|
|
7163
|
+
}
|
|
7164
|
+
};
|
|
7165
|
+
const additionalFeatures = () => nativePanel.pluginsAvailable() ? [nativePluginsMenuItem()] : [];
|
|
7166
|
+
const menuStore = new MenuStore(form, featureRegistry, additionalFeatures);
|
|
7167
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
7168
|
+
name: "settings.section",
|
|
7169
|
+
id: HIWORK_MENU_NS,
|
|
7170
|
+
order: 3,
|
|
7171
|
+
label: () => MENU_SECTION_LABEL,
|
|
7172
|
+
inject: () => menuStore.inject()
|
|
7173
|
+
}, MenuSection));
|
|
5186
7174
|
ctx.slots.inject("conversation.chat.assistant-actions", () => ctx.slots.register({
|
|
5187
7175
|
name: "conversation.chat.assistant-actions",
|
|
5188
7176
|
id: "hiwork-session-cost",
|
|
@@ -5228,6 +7216,7 @@ function apply(ctx) {
|
|
|
5228
7216
|
ctx.effect(() => observeAccountChange(controller.inject().hooks.aiRouterCard, () => {
|
|
5229
7217
|
usage.clear();
|
|
5230
7218
|
sessionCost.clear();
|
|
7219
|
+
dailyQuotes.clear();
|
|
5231
7220
|
managedMcp.clear();
|
|
5232
7221
|
managedSkills.clear();
|
|
5233
7222
|
managedWorkbench.clear();
|
|
@@ -5248,12 +7237,11 @@ function apply(ctx) {
|
|
|
5248
7237
|
id: AI_ROUTER_NS,
|
|
5249
7238
|
inject: () => ({ ...controller.inject(), pluginUpdates })
|
|
5250
7239
|
}, UserBadge));
|
|
5251
|
-
ctx.effect(() => mountLoginGate(
|
|
7240
|
+
ctx.effect(() => mountLoginGate(form, storeCredential), "hiwork-core: login gate");
|
|
5252
7241
|
ctx.effect(() => observeHeroDecorations(document, dailyQuotes), "hiwork-core: hero decorations");
|
|
5253
|
-
installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench, pmsTodo);
|
|
7242
|
+
installFeatureCenter(ctx, featureRegistry, menuStore, managedMcp, managedSkills, managedWorkbench, pmsTodo, additionalFeatures, nativePanel);
|
|
5254
7243
|
}
|
|
5255
|
-
function installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench, pmsTodo) {
|
|
5256
|
-
const featureRegistry = createFeatureRegistry();
|
|
7244
|
+
function installFeatureCenter(ctx, featureRegistry, menuStore, managedMcp, managedSkills, managedWorkbench, pmsTodo, additionalFeatures, nativePanel) {
|
|
5257
7245
|
let center;
|
|
5258
7246
|
const resolveSessions = () => ctx.get("sessions");
|
|
5259
7247
|
const locale = ctx.get("locale");
|
|
@@ -5332,12 +7320,20 @@ function installFeatureCenter(ctx, managedMcp, managedSkills, managedWorkbench,
|
|
|
5332
7320
|
openNativeSession: (sessionId) => {
|
|
5333
7321
|
resolveSessions()?.open(sessionId);
|
|
5334
7322
|
},
|
|
5335
|
-
|
|
7323
|
+
additionalFeatures,
|
|
7324
|
+
nativePanel,
|
|
7325
|
+
...locale === void 0 ? {} : { subscribeLocale: (listener) => locale.subscribe(listener) },
|
|
7326
|
+
// 侧栏分组与顺序(`hiwork-menu`,见 menu-preferences.ts / menu-store.ts):
|
|
7327
|
+
// `preferences` 每次重建菜单时现取,`subscribePreferences` 让设置页里的一次改动
|
|
7328
|
+
// 立刻反映到侧栏,不需要重启。
|
|
7329
|
+
preferences: () => menuStore.preferences(),
|
|
7330
|
+
subscribePreferences: (listener) => menuStore.subscribePreferences(listener)
|
|
5336
7331
|
});
|
|
5337
7332
|
return () => {
|
|
5338
7333
|
center?.dispose();
|
|
5339
7334
|
center = void 0;
|
|
5340
7335
|
};
|
|
5341
7336
|
}, "hiwork-core: feature center");
|
|
7337
|
+
ctx.effect(() => () => menuStore.dispose(), "hiwork-core: menu preferences");
|
|
5342
7338
|
}
|
|
5343
7339
|
return module.exports; } });
|