@klarkxy/dsh-git-commit 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -16
- package/docs/README.zh-CN.md +28 -11
- package/lib/client.inner.cjs +923 -152
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +923 -152
- package/lib/contracts.d.ts +77 -2
- package/lib/contracts.js +38 -1
- package/lib/contracts.js.map +1 -1
- package/lib/index.d.ts +21 -7
- package/lib/index.js +139 -42
- package/lib/index.js.map +1 -1
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +20 -6
- package/lib/contracts2.d.ts +0 -54
package/lib/client.js
CHANGED
|
@@ -170,7 +170,467 @@ let react = require("react");
|
|
|
170
170
|
let react_dom = require("react-dom");
|
|
171
171
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
172
172
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
173
|
+
//#region ../dsh-plugin-kit/src/model-menu.ts
|
|
174
|
+
/**
|
|
175
|
+
* Shared plugin-page model menu.
|
|
176
|
+
*
|
|
177
|
+
* A feature plugin's own settings row exposes a model select. An empty
|
|
178
|
+
* selection means the host default chat model for that feature's own call.
|
|
179
|
+
*
|
|
180
|
+
* Browser-safe and React-optional: pure helpers live here so hosts and clients
|
|
181
|
+
* share one catalog contract without bundling React into the host build.
|
|
182
|
+
*/
|
|
183
|
+
const KEY_SEPARATOR = "";
|
|
184
|
+
function modelMenuChoiceKey(provider, model) {
|
|
185
|
+
return provider && model ? `${provider}${KEY_SEPARATOR}${model}` : "";
|
|
186
|
+
}
|
|
187
|
+
function parseModelMenuChoiceKey(value) {
|
|
188
|
+
const index = value.indexOf(KEY_SEPARATOR);
|
|
189
|
+
if (index <= 0) return void 0;
|
|
190
|
+
const provider = value.slice(0, index);
|
|
191
|
+
const model = value.slice(index + 1);
|
|
192
|
+
if (!provider || !model) return void 0;
|
|
193
|
+
return {
|
|
194
|
+
provider,
|
|
195
|
+
model
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function asRecord(value) {
|
|
199
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
200
|
+
}
|
|
201
|
+
function asText(value) {
|
|
202
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
203
|
+
}
|
|
204
|
+
function parseEffort(value) {
|
|
205
|
+
const row = asRecord(value);
|
|
206
|
+
const id = row ? asText(row.id) : void 0;
|
|
207
|
+
if (!id) return void 0;
|
|
208
|
+
return {
|
|
209
|
+
id,
|
|
210
|
+
name: row ? asText(row.name) ?? id : id
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/** Parse the host session catalog into flat choices, keeping a saved route selectable. */
|
|
214
|
+
function parseModelMenuChoices(value, bound) {
|
|
215
|
+
const envelope = asRecord(value);
|
|
216
|
+
const root = (envelope && envelope.ok === true ? asRecord(envelope.value) : void 0) ?? envelope ?? {};
|
|
217
|
+
const groups = Array.isArray(root.groups) ? root.groups : [];
|
|
218
|
+
const choices = [];
|
|
219
|
+
for (const group of groups) {
|
|
220
|
+
const groupRow = asRecord(group);
|
|
221
|
+
const provider = groupRow ? asText(groupRow.id) : void 0;
|
|
222
|
+
if (!provider || !groupRow || !Array.isArray(groupRow.models)) continue;
|
|
223
|
+
const providerName = asText(groupRow.name) ?? provider;
|
|
224
|
+
for (const entry of groupRow.models) {
|
|
225
|
+
const modelRow = asRecord(entry);
|
|
226
|
+
const model = modelRow ? asText(modelRow.id) : void 0;
|
|
227
|
+
if (!model || !modelRow) continue;
|
|
228
|
+
const name = asText(modelRow.name) ?? model;
|
|
229
|
+
const reasoning = asRecord(modelRow.reasoning);
|
|
230
|
+
const efforts = reasoning && Array.isArray(reasoning.efforts) ? reasoning.efforts.flatMap((effort) => {
|
|
231
|
+
const parsed = parseEffort(effort);
|
|
232
|
+
return parsed ? [parsed] : [];
|
|
233
|
+
}) : [];
|
|
234
|
+
choices.push({
|
|
235
|
+
provider,
|
|
236
|
+
model,
|
|
237
|
+
label: `${providerName} / ${name}`,
|
|
238
|
+
efforts
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (bound?.provider && bound.model && !choices.some((item) => item.provider === bound.provider && item.model === bound.model)) choices.push({
|
|
243
|
+
provider: bound.provider,
|
|
244
|
+
model: bound.model,
|
|
245
|
+
label: `${bound.provider} / ${bound.model}`,
|
|
246
|
+
efforts: []
|
|
247
|
+
});
|
|
248
|
+
return choices;
|
|
249
|
+
}
|
|
250
|
+
/** Reasoning-effort options for one choice; the saved value stays selectable. */
|
|
251
|
+
function modelMenuEffortOptions(choice, current) {
|
|
252
|
+
const listed = (choice?.efforts ?? []).map((item) => ({ ...item }));
|
|
253
|
+
if (current && !listed.some((item) => item.id === current)) listed.push({
|
|
254
|
+
id: current,
|
|
255
|
+
name: current
|
|
256
|
+
});
|
|
257
|
+
return listed;
|
|
258
|
+
}
|
|
259
|
+
Object.values({
|
|
260
|
+
/** Surfaces, from the page up to the topmost nested card. */
|
|
261
|
+
surface: [
|
|
262
|
+
"--dsw-alias-bg-base",
|
|
263
|
+
"--dsw-alias-bg-layer-1",
|
|
264
|
+
"--dsw-alias-bg-layer-2",
|
|
265
|
+
"--dsw-alias-bg-layer-3",
|
|
266
|
+
"--dsw-alias-bg-layer-4",
|
|
267
|
+
"--dsw-alias-bg-module-platform",
|
|
268
|
+
"--dsw-alias-bg-overlay"
|
|
269
|
+
],
|
|
270
|
+
/** Hairlines. l1 is near-invisible on a menu surface, so menus use l2+. */
|
|
271
|
+
border: [
|
|
272
|
+
"--dsw-alias-border-l1",
|
|
273
|
+
"--dsw-alias-border-l2",
|
|
274
|
+
"--dsw-alias-border-l3",
|
|
275
|
+
"--dsw-alias-border-l4"
|
|
276
|
+
],
|
|
277
|
+
/** Text. `caption` is menu shortcut text, `dimmed` is placeholder text. */
|
|
278
|
+
label: [
|
|
279
|
+
"--dsw-alias-label-primary",
|
|
280
|
+
"--dsw-alias-label-secondary",
|
|
281
|
+
"--dsw-alias-label-tertiary",
|
|
282
|
+
"--dsw-alias-label-caption",
|
|
283
|
+
"--dsw-alias-label-dimmed",
|
|
284
|
+
"--dsw-alias-label-error",
|
|
285
|
+
"--dsw-alias-label-primary-foreground"
|
|
286
|
+
],
|
|
287
|
+
/** Interactive fills. The `-danger` hover belongs to destructive rows. */
|
|
288
|
+
interactive: [
|
|
289
|
+
"--dsw-alias-interactive-bg-hover",
|
|
290
|
+
"--dsw-alias-interactive-bg-active",
|
|
291
|
+
"--dsw-alias-interactive-bg-hover-danger"
|
|
292
|
+
],
|
|
293
|
+
/** Accent and status. `business` is the focus ring's default colour. */
|
|
294
|
+
accent: [
|
|
295
|
+
"--dsw-alias-brand-primary",
|
|
296
|
+
"--dsw-alias-button-primary-fill",
|
|
297
|
+
"--dsw-alias-button-primary-hover",
|
|
298
|
+
"--dsw-alias-button-tool-bar-fill",
|
|
299
|
+
"--dsw-alias-button-tool-bar-hover",
|
|
300
|
+
"--dsw-alias-button-ghost-active-fill",
|
|
301
|
+
"--dsw-alias-button-ghost-active-border"
|
|
302
|
+
],
|
|
303
|
+
state: [
|
|
304
|
+
"--dsw-alias-state-business-primary",
|
|
305
|
+
"--dsw-alias-state-success-primary",
|
|
306
|
+
"--dsw-alias-state-warn-primary",
|
|
307
|
+
"--dsw-alias-state-error-primary",
|
|
308
|
+
"--dsw-alias-state-idle-primary"
|
|
309
|
+
],
|
|
310
|
+
/** Corner scale: sm 8, md 12, lg the large card, panel the modal. */
|
|
311
|
+
radius: [
|
|
312
|
+
"--dsw-radius-sm",
|
|
313
|
+
"--dsw-radius-md",
|
|
314
|
+
"--dsw-radius-lg",
|
|
315
|
+
"--dsw-radius-panel"
|
|
316
|
+
],
|
|
317
|
+
/** An elevated surface draws its edge in the shadow, so it keeps border: 0. */
|
|
318
|
+
elevation: [
|
|
319
|
+
"--dsw-elevation-soft",
|
|
320
|
+
"--dsw-elevation-stroke",
|
|
321
|
+
"--dsw-elevation-stroke-color",
|
|
322
|
+
"--dsw-elevation-panel",
|
|
323
|
+
"--dsw-elevation-prominent"
|
|
324
|
+
],
|
|
325
|
+
/** Floats, masks and scrollbars. */
|
|
326
|
+
overlay: [
|
|
327
|
+
"--dsw-menu-surface-fill",
|
|
328
|
+
"--dsw-menu-backdrop-filter",
|
|
329
|
+
"--dsw-mask-blur",
|
|
330
|
+
"--dsw-alias-bg-mask-1",
|
|
331
|
+
"--dsw-alias-menu-icon",
|
|
332
|
+
"--dsw-alias-scrollbar-bg-l2",
|
|
333
|
+
"--dsw-alias-scrollbar-hover-l2",
|
|
334
|
+
"--dsw-shadow-lv3"
|
|
335
|
+
],
|
|
336
|
+
/** Motion, focus and type axis, published by the host rather than by a component. */
|
|
337
|
+
system: [
|
|
338
|
+
"--dsw-focus-ring-width",
|
|
339
|
+
"--dsw-focus-ring-color",
|
|
340
|
+
"--dsw-font-markdown-code-font-family",
|
|
341
|
+
"--ds-transition-duration",
|
|
342
|
+
"--ds-ease-in-out",
|
|
343
|
+
"--ds-font-family-code",
|
|
344
|
+
"--dsh-scrollbar-thumb",
|
|
345
|
+
"--dsh-scrollbar-thumb-hover",
|
|
346
|
+
"--dsh-content-font-size-secondary",
|
|
347
|
+
"--dsh-content-font-delta",
|
|
348
|
+
"--dsh-frame-top-clearance"
|
|
349
|
+
]
|
|
350
|
+
}).flat();
|
|
351
|
+
/**
|
|
352
|
+
* The host's focus ring shorthand, verbatim. Writing this per control is how
|
|
353
|
+
* rings drift apart; a stylesheet that draws its own focus style should use
|
|
354
|
+
* {@link officialFocus} instead. The colour fallback is the host's own default.
|
|
355
|
+
*/
|
|
356
|
+
const OFFICIAL_FOCUS_RING = "var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary))";
|
|
357
|
+
/**
|
|
358
|
+
* A complete `outline` declaration at the official geometry. `offset` defaults
|
|
359
|
+
* to the 2px the native controls use; the host's inline field rules sit at 1px
|
|
360
|
+
* and a segmented tab pulls the ring inside itself at -2px.
|
|
361
|
+
*/
|
|
362
|
+
function officialFocus(offset = "2px") {
|
|
363
|
+
return `outline: ${OFFICIAL_FOCUS_RING}; outline-offset: ${offset}`;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* An elevated surface. The host draws a raised surface's edge inside its
|
|
367
|
+
* shadow rather than as a border, so a card sets `border: 0` and takes the edge
|
|
368
|
+
* from the elevation — which is also why its border stays off the alias-border
|
|
369
|
+
* set that a flat, non-elevated rule would use.
|
|
370
|
+
*/
|
|
371
|
+
function officialElevation(level) {
|
|
372
|
+
return `border: 0; box-shadow: var(--dsw-elevation-${level})`;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* The shared stylesheet for a plugin UI, scoped under that plugin's root class.
|
|
376
|
+
*
|
|
377
|
+
* Scoping is what keeps this safe to always-bundle into every client: the rules
|
|
378
|
+
* can only match inside the plugin's own subtree, so two plugins can both mount
|
|
379
|
+
* `dsh-ui-card` without either styling the other's markup.
|
|
380
|
+
*
|
|
381
|
+
* Each recipe matches the root element *and* its descendants, because the
|
|
382
|
+
* commonest arrangement is one element that is both — a surface that carries
|
|
383
|
+
* the root class and `dsh-ui-card`, or a portaled dialog that is the root and
|
|
384
|
+
* the `dsh-ui-surface`. A descendant-only scope would silently leave those
|
|
385
|
+
* unstyled, with no error anywhere.
|
|
386
|
+
*
|
|
387
|
+
* A caveat worth knowing: `Modal` and `Menu` portal to `document.body`, so their
|
|
388
|
+
* content leaves the plugin's subtree entirely. A portaled surface therefore
|
|
389
|
+
* needs its own root class on an element inside the portal.
|
|
390
|
+
*
|
|
391
|
+
* Only patterns that primitives do not provide live here. If a control appears
|
|
392
|
+
* below that `Button`/`Input`/`Switch`/`Tag`/`Menu` already covers, that is a
|
|
393
|
+
* bug in this file: use the primitive and delete the recipe.
|
|
394
|
+
*/
|
|
395
|
+
function officialUiCss(roots) {
|
|
396
|
+
const scope = (typeof roots === "string" ? [roots] : roots).map((root) => `.${root}`).join(", ");
|
|
397
|
+
return `
|
|
398
|
+
/* Official DSH Web UI contract. Generated by @klarkxy/dsh-plugin-kit/official-ui.
|
|
399
|
+
* Do not hand-edit the copy inside a plugin: change this module so every plugin
|
|
400
|
+
* moves together. Scope: ${scope} */
|
|
401
|
+
/* The scope rule below stays open for the whole sheet: every rule after it
|
|
402
|
+
* nests with the parent selector, so one stylesheet can cover several unrelated
|
|
403
|
+
* roots without leaking into a neighbouring plugin. Its closing brace is last. */
|
|
404
|
+
${scope} {
|
|
405
|
+
color: var(--dsw-alias-label-primary);
|
|
406
|
+
font-family: inherit;
|
|
407
|
+
font-size: 14px;
|
|
408
|
+
line-height: 22px;
|
|
409
|
+
min-width: 0;
|
|
410
|
+
|
|
411
|
+
& *, & *::before, & *::after { box-sizing: border-box; }
|
|
412
|
+
& h1, & h2, & h3, & h4, & p, & figure { margin: 0; }
|
|
413
|
+
/* Keyboard focus only: pointer input keeps the native resting appearance. The
|
|
414
|
+
* ring needs no radius of its own — the outline already follows the control's
|
|
415
|
+
* own border-radius, and overriding it here would flatten every control. */
|
|
416
|
+
& :focus-visible { ${officialFocus()}; }
|
|
417
|
+
& [hidden] { display: none !important; }
|
|
418
|
+
|
|
419
|
+
/* --- Type tiers. The same 14/22 and 13/20 steps the primitives use. --- */
|
|
420
|
+
& .dsh-ui-title { font-size: 16px; line-height: 24px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
421
|
+
& .dsh-ui-heading { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
422
|
+
& .dsh-ui-compact { font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-primary); }
|
|
423
|
+
& .dsh-ui-meta { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
|
|
424
|
+
& .dsh-ui-hint { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
425
|
+
& .dsh-ui-error { font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-error-primary); }
|
|
426
|
+
& .dsh-ui-warn { color: var(--dsw-alias-state-warn-primary); }
|
|
427
|
+
& .dsh-ui-success { color: var(--dsw-alias-state-success-primary); }
|
|
428
|
+
& .dsh-ui-muted { color: var(--dsw-alias-label-tertiary); }
|
|
429
|
+
& .dsh-ui-truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
430
|
+
& .dsh-ui-wrap { min-width: 0; overflow-wrap: anywhere; }
|
|
431
|
+
|
|
432
|
+
/* --- Layout scaffolding. --- */
|
|
433
|
+
& .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
|
|
434
|
+
& .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
|
|
435
|
+
& .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
436
|
+
& .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
|
|
437
|
+
& .dsh-ui-spacer { flex: 1; min-width: 0; }
|
|
438
|
+
/* A 0.5px l2 rule is the host's section divider; reserve the same rhythm. */
|
|
439
|
+
& .dsh-ui-divider { height: 0.5px; background: var(--dsw-alias-border-l2); }
|
|
440
|
+
& .dsh-ui-section { display: flex; flex-direction: column; gap: 12px; padding-top: 16px; border-top: 0.5px solid var(--dsw-alias-border-l2); }
|
|
441
|
+
|
|
442
|
+
/* --- Card: a raised surface whose edge lives in the shadow. --- */
|
|
443
|
+
& .dsh-ui-card {
|
|
444
|
+
${officialElevation("stroke")}
|
|
445
|
+
display: flex;
|
|
446
|
+
flex-direction: column;
|
|
447
|
+
gap: 12px;
|
|
448
|
+
padding: 16px;
|
|
449
|
+
border-radius: var(--dsw-radius-lg);
|
|
450
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
451
|
+
color: var(--dsw-alias-label-primary);
|
|
452
|
+
min-width: 0;
|
|
453
|
+
}
|
|
454
|
+
& .dsh-ui-card--flat { border: 0.5px solid var(--dsw-alias-border-l3); box-shadow: none; }
|
|
455
|
+
& .dsh-ui-card--nested { background: var(--dsw-alias-bg-layer-2); }
|
|
456
|
+
& .dsh-ui-card--plain { background: none; }
|
|
457
|
+
|
|
458
|
+
/* --- Field: label, control, help, error. The control itself is an
|
|
459
|
+
* official primitive, so this only owns the vertical rhythm. --- */
|
|
460
|
+
& .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
461
|
+
& .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
|
|
462
|
+
& .dsh-ui-label { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 20px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
463
|
+
& .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
|
464
|
+
& .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
|
|
465
|
+
& .dsh-ui-help p { margin: 0; }
|
|
466
|
+
& .dsh-ui-help p + p { margin-top: 8px; }
|
|
467
|
+
& .dsh-ui-required { color: var(--dsw-alias-state-error-primary); }
|
|
468
|
+
& .dsh-ui-control { width: 100%; min-width: 0; }
|
|
469
|
+
/* An invalid control is marked on the control, not only in the message. */
|
|
470
|
+
& .dsh-ui-control[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
471
|
+
& .dsh-ui-readonly { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
472
|
+
& .dsh-ui-readonly > dt { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
473
|
+
& .dsh-ui-readonly > dd { margin: 0; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
|
|
474
|
+
|
|
475
|
+
/* --- Buttons row. A no-op container: it exists so actions line up on the
|
|
476
|
+
* trailing edge the way every native panel does. --- */
|
|
477
|
+
& .dsh-ui-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 4px; }
|
|
478
|
+
& .dsh-ui-actions-end { justify-content: flex-end; }
|
|
479
|
+
|
|
480
|
+
/* --- Banners. A banner is a left-rail notice, the shape used for warnings,
|
|
481
|
+
* deprecations and blocking instructions. --- */
|
|
482
|
+
& .dsh-ui-banner {
|
|
483
|
+
display: flex;
|
|
484
|
+
flex-direction: column;
|
|
485
|
+
gap: 8px;
|
|
486
|
+
padding: 10px 14px;
|
|
487
|
+
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
488
|
+
border-left: 3px solid var(--dsw-alias-state-warn-primary);
|
|
489
|
+
border-radius: var(--dsw-radius-md);
|
|
490
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
491
|
+
font-size: 13px;
|
|
492
|
+
line-height: 20px;
|
|
493
|
+
color: var(--dsw-alias-label-secondary);
|
|
494
|
+
}
|
|
495
|
+
& .dsh-ui-banner-title { font-weight: 500; color: var(--dsw-alias-state-warn-primary); }
|
|
496
|
+
& .dsh-ui-banner--info { border-left-color: var(--dsw-alias-state-business-primary); }
|
|
497
|
+
& .dsh-ui-banner--danger { border-left-color: var(--dsw-alias-state-error-primary); }
|
|
498
|
+
& .dsh-ui-banner--success { border-left-color: var(--dsw-alias-state-success-primary); }
|
|
499
|
+
& .dsh-ui-banner p { margin: 0; }
|
|
500
|
+
|
|
501
|
+
/* --- Inline notice: a transient confirmation with no chrome of its own. --- */
|
|
502
|
+
& .dsh-ui-notice { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-success-primary); }
|
|
503
|
+
& .dsh-ui-notice--error { color: var(--dsw-alias-state-error-primary); }
|
|
504
|
+
|
|
505
|
+
/* --- Empty and loading states. Both sit in a dashed well so an empty panel
|
|
506
|
+
* reads as "nothing here yet" rather than as a broken layout. --- */
|
|
507
|
+
& .dsh-ui-empty {
|
|
508
|
+
display: flex;
|
|
509
|
+
flex-direction: column;
|
|
510
|
+
align-items: flex-start;
|
|
511
|
+
gap: 6px;
|
|
512
|
+
padding: 20px 16px;
|
|
513
|
+
border: 0.5px dashed var(--dsw-alias-border-l3);
|
|
514
|
+
border-radius: var(--dsw-radius-md);
|
|
515
|
+
font-size: 13px;
|
|
516
|
+
line-height: 20px;
|
|
517
|
+
color: var(--dsw-alias-label-secondary);
|
|
518
|
+
}
|
|
519
|
+
& .dsh-ui-loading { display: flex; align-items: center; gap: 8px; padding: 20px 0; font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-secondary); }
|
|
520
|
+
|
|
521
|
+
/* --- Selectable list. A row is a button-sized target with a hover fill and
|
|
522
|
+
* a selected fill; the name carries the weight, the description does not. --- */
|
|
523
|
+
& .dsh-ui-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
524
|
+
& .dsh-ui-list-scroll { max-height: min(66vh, 760px); overflow-y: auto; overflow-x: hidden; }
|
|
525
|
+
& .dsh-ui-list-row { border-radius: var(--dsw-radius-md); }
|
|
526
|
+
& .dsh-ui-list-row:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
527
|
+
& .dsh-ui-list-row[aria-selected='true'], & .dsh-ui-list-row[data-selected] { background: var(--dsw-alias-interactive-bg-active); }
|
|
528
|
+
& .dsh-ui-list-item {
|
|
529
|
+
display: flex;
|
|
530
|
+
flex-direction: column;
|
|
531
|
+
align-items: flex-start;
|
|
532
|
+
gap: 4px;
|
|
533
|
+
width: 100%;
|
|
534
|
+
min-width: 0;
|
|
535
|
+
padding: 10px 12px;
|
|
536
|
+
border: 0;
|
|
537
|
+
border-radius: var(--dsw-radius-md);
|
|
538
|
+
background: none;
|
|
539
|
+
font: inherit;
|
|
540
|
+
color: inherit;
|
|
541
|
+
text-align: start;
|
|
542
|
+
cursor: pointer;
|
|
543
|
+
}
|
|
544
|
+
& .dsh-ui-list-item:disabled { cursor: default; }
|
|
545
|
+
& .dsh-ui-list-item:disabled:hover { background: none; }
|
|
546
|
+
& .dsh-ui-list-name { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); overflow-wrap: anywhere; }
|
|
547
|
+
& .dsh-ui-list-desc { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
548
|
+
|
|
549
|
+
/* --- Code and structured output. Wraps rather than scrolls, because a
|
|
550
|
+
* feature panel is read rather than scanned. --- */
|
|
551
|
+
& .dsh-ui-code {
|
|
552
|
+
padding: 10px 12px;
|
|
553
|
+
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
554
|
+
border-radius: var(--dsw-radius-md);
|
|
555
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
556
|
+
font-family: var(--ds-font-family-code, ui-monospace, 'Cascadia Mono', Consolas, monospace);
|
|
557
|
+
font-size: 12px;
|
|
558
|
+
line-height: 20px;
|
|
559
|
+
color: var(--dsw-alias-label-secondary);
|
|
560
|
+
overflow-wrap: anywhere;
|
|
561
|
+
white-space: pre-wrap;
|
|
562
|
+
}
|
|
563
|
+
& .dsh-ui-scroll { max-height: 60vh; overflow: auto; overscroll-behavior: contain; }
|
|
564
|
+
|
|
565
|
+
/* --- A toggle row: label on the left, switch on the trailing edge. This is
|
|
566
|
+
* layout only; the switch itself is the official primitive. --- */
|
|
567
|
+
& .dsh-ui-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-width: 0; padding: 12px 0; }
|
|
568
|
+
& .dsh-ui-toggle-row + .dsh-ui-toggle-row { border-top: 0.5px solid var(--dsw-alias-border-l2); }
|
|
569
|
+
& .dsh-ui-toggle-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
570
|
+
& .dsh-ui-toggle-label { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
571
|
+
|
|
572
|
+
/* --- Floating surface: a panel anchored to a trigger. This is the material
|
|
573
|
+
* a menu card is made of — the translucent fill plus its backdrop blur, with
|
|
574
|
+
* the edge riding inside the prominent elevation. A plugin that hand-rolls this
|
|
575
|
+
* is how tokens like --dsw-specific-menu get invented; use this instead. --- */
|
|
576
|
+
& .dsh-ui-surface {
|
|
577
|
+
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
578
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
579
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
580
|
+
position: relative;
|
|
581
|
+
display: flex;
|
|
582
|
+
flex-direction: column;
|
|
583
|
+
border: 0;
|
|
584
|
+
border-radius: var(--dsw-radius-lg);
|
|
585
|
+
background: var(--dsw-menu-surface-fill);
|
|
586
|
+
backdrop-filter: var(--dsw-menu-backdrop-filter);
|
|
587
|
+
box-shadow: var(--dsw-elevation-prominent);
|
|
588
|
+
color: var(--dsw-alias-label-primary);
|
|
589
|
+
font-size: 13px;
|
|
590
|
+
line-height: 20px;
|
|
591
|
+
min-width: 0;
|
|
592
|
+
}
|
|
593
|
+
& .dsh-ui-surface-body { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px 14px; overflow-y: auto; }
|
|
594
|
+
& .dsh-ui-surface-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
595
|
+
& .dsh-ui-surface-title { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
596
|
+
|
|
597
|
+
/* --- Native select. The primitives ship no select, so a plugin that offers a
|
|
598
|
+
* closed list of choices keeps the platform control and matches the official
|
|
599
|
+
* settings field geometry exactly (34px, 0.5px l4, radius-md, layer-3). A
|
|
600
|
+
* SegmentedControl is the primitive to reach for when the choices are few
|
|
601
|
+
* enough to show at once. --- */
|
|
602
|
+
& .dsh-ui-select {
|
|
603
|
+
box-sizing: border-box;
|
|
604
|
+
width: 100%;
|
|
605
|
+
min-width: 0;
|
|
606
|
+
height: 34px;
|
|
607
|
+
padding: 0 12px;
|
|
608
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
609
|
+
border-radius: var(--dsw-radius-md);
|
|
610
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
611
|
+
font: inherit;
|
|
612
|
+
font-size: 13px;
|
|
613
|
+
line-height: 1.5;
|
|
614
|
+
color: var(--dsw-alias-label-primary);
|
|
615
|
+
cursor: pointer;
|
|
616
|
+
}
|
|
617
|
+
& .dsh-ui-select:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }
|
|
618
|
+
& .dsh-ui-select:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
619
|
+
& .dsh-ui-select[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
620
|
+
|
|
621
|
+
/* Long panels scroll inside themselves instead of growing the host page. */
|
|
622
|
+
& .dsh-ui-panel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
|
|
623
|
+
& .dsh-ui-panel-wide { max-width: 100%; }
|
|
624
|
+
|
|
625
|
+
@media (prefers-reduced-motion: reduce) {
|
|
626
|
+
& *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
|
|
630
|
+
}
|
|
631
|
+
//#endregion
|
|
173
632
|
//#region src/contracts.ts
|
|
633
|
+
const PLUGIN_NAME = "@klarkxy/dsh-git-commit";
|
|
174
634
|
const RPC_CHANNEL = "/dsh-git-commit";
|
|
175
635
|
const GIT_COMMIT_CLIENT_SERVICE = "dshGitCommitClient";
|
|
176
636
|
//#endregion
|
|
@@ -179,6 +639,8 @@ const name = "dsh-git-commit-client";
|
|
|
179
639
|
const inject = [
|
|
180
640
|
"slots",
|
|
181
641
|
"connection",
|
|
642
|
+
"remote",
|
|
643
|
+
"remote.session",
|
|
182
644
|
"locale"
|
|
183
645
|
];
|
|
184
646
|
const NS = "dsh-git-commit.action";
|
|
@@ -189,7 +651,12 @@ const zh = {
|
|
|
189
651
|
files: "个文件",
|
|
190
652
|
model: "模型",
|
|
191
653
|
modelDefault: "默认模型",
|
|
192
|
-
modelCustom: "
|
|
654
|
+
modelCustom: "插件设置",
|
|
655
|
+
filesCount: "({n} 个文件)",
|
|
656
|
+
confirmPrompt: "将把 {n} 个文件的全部修改分次提交到当前分支。",
|
|
657
|
+
confirm: "确认提交",
|
|
658
|
+
cancel: "取消",
|
|
659
|
+
loading: "正在读取仓库状态…",
|
|
193
660
|
running: "正在整理修改并分次提交…",
|
|
194
661
|
done: "已创建提交",
|
|
195
662
|
fallback: "模型不可用,已合并为单个提交",
|
|
@@ -202,6 +669,7 @@ const zh = {
|
|
|
202
669
|
sessionRunning: "会话正在运行,提交已暂停",
|
|
203
670
|
refresh: "刷新",
|
|
204
671
|
failed: "操作失败",
|
|
672
|
+
planUnavailable: "模型没有给出可用的提交方案,已取消本次提交,工作区没有被改动。换一个模型重试,或在插件设置里开启兜底提交。",
|
|
205
673
|
close: "关闭"
|
|
206
674
|
};
|
|
207
675
|
const en = {
|
|
@@ -211,7 +679,12 @@ const en = {
|
|
|
211
679
|
files: "files",
|
|
212
680
|
model: "Model",
|
|
213
681
|
modelDefault: "default model",
|
|
214
|
-
modelCustom: "
|
|
682
|
+
modelCustom: "Plugin settings",
|
|
683
|
+
filesCount: " ({n} files)",
|
|
684
|
+
confirmPrompt: "All changes in {n} files will be committed to the current branch in one or more commits.",
|
|
685
|
+
confirm: "Commit now",
|
|
686
|
+
cancel: "Cancel",
|
|
687
|
+
loading: "Reading repository status…",
|
|
215
688
|
running: "Organizing changes into commits…",
|
|
216
689
|
done: "Commits created",
|
|
217
690
|
fallback: "Model unavailable; used a single commit",
|
|
@@ -224,11 +697,24 @@ const en = {
|
|
|
224
697
|
sessionRunning: "Session is running; commits paused",
|
|
225
698
|
refresh: "Refresh",
|
|
226
699
|
failed: "Operation failed",
|
|
700
|
+
planUnavailable: "The model returned no usable commit plan, so the run was cancelled and your workspace is untouched. Try another model, or turn on the fallback in plugin settings.",
|
|
227
701
|
close: "Close"
|
|
228
702
|
};
|
|
229
|
-
|
|
703
|
+
/** A failed RPC keeps its code so the panel can explain a rejection in its own words. */
|
|
704
|
+
var RpcError = class extends Error {
|
|
705
|
+
code;
|
|
706
|
+
constructor(code, message) {
|
|
707
|
+
super(message);
|
|
708
|
+
this.name = "RpcError";
|
|
709
|
+
this.code = code;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
async function rpc(call, endpoint, payload = {}, fallback = "Operation failed") {
|
|
230
713
|
const result = await call(endpoint, payload);
|
|
231
|
-
if (!result || result.ok !== true)
|
|
714
|
+
if (!result || result.ok !== true) {
|
|
715
|
+
const failed = result && "error" in result ? result.error : void 0;
|
|
716
|
+
throw new RpcError(failed?.code ?? "unknown", failed?.message ? failed.message : fallback);
|
|
717
|
+
}
|
|
232
718
|
return result.value;
|
|
233
719
|
}
|
|
234
720
|
function reasonText(t, status) {
|
|
@@ -240,6 +726,19 @@ function reasonText(t, status) {
|
|
|
240
726
|
default: return t("unavailable");
|
|
241
727
|
}
|
|
242
728
|
}
|
|
729
|
+
/** A refusal the plugin can name gets its own sentence; the rest stay verbatim. */
|
|
730
|
+
function errorText(t, error) {
|
|
731
|
+
return error.code === "plan-unavailable" ? t("planUnavailable") : error.message;
|
|
732
|
+
}
|
|
733
|
+
function panelError(reason) {
|
|
734
|
+
return reason instanceof RpcError ? {
|
|
735
|
+
code: reason.code,
|
|
736
|
+
message: reason.message
|
|
737
|
+
} : {
|
|
738
|
+
code: "unknown",
|
|
739
|
+
message: reason instanceof Error ? reason.message : String(reason)
|
|
740
|
+
};
|
|
741
|
+
}
|
|
243
742
|
function GitBranchIcon() {
|
|
244
743
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
245
744
|
width: "14",
|
|
@@ -289,6 +788,7 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
289
788
|
const [running, setRunning] = (0, react.useState)(false);
|
|
290
789
|
const [result, setResult] = (0, react.useState)(null);
|
|
291
790
|
const [error, setError] = (0, react.useState)(null);
|
|
791
|
+
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
292
792
|
const rootRef = (0, react.useRef)(null);
|
|
293
793
|
const triggerRef = (0, react.useRef)(null);
|
|
294
794
|
const panelRef = (0, react.useRef)(null);
|
|
@@ -303,11 +803,16 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
303
803
|
const sessionRunning = useSession((snapshot) => snapshot.running === true);
|
|
304
804
|
const changeOpen = (0, react.useCallback)((next) => {
|
|
305
805
|
setOpen(next);
|
|
806
|
+
setConfirming(false);
|
|
306
807
|
if (next) {
|
|
307
808
|
setResult(null);
|
|
308
809
|
setError(null);
|
|
309
810
|
}
|
|
310
811
|
}, []);
|
|
812
|
+
const positioned = Boolean(position);
|
|
813
|
+
(0, react.useEffect)(() => {
|
|
814
|
+
if (open && positioned) panelRef.current?.focus();
|
|
815
|
+
}, [open, positioned]);
|
|
311
816
|
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, changeOpen, panelRef);
|
|
312
817
|
(0, react.useEffect)(() => {
|
|
313
818
|
generationRef.current += 1;
|
|
@@ -322,17 +827,21 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
322
827
|
setLoading(true);
|
|
323
828
|
setError(null);
|
|
324
829
|
try {
|
|
325
|
-
const next = await rpc(call, "status", { sessionId });
|
|
830
|
+
const next = await rpc(call, "status", { sessionId }, t("failed"));
|
|
326
831
|
if (generationRef.current !== generation) return;
|
|
327
832
|
setStatus(next);
|
|
328
833
|
} catch (reason) {
|
|
329
834
|
if (generationRef.current !== generation) return;
|
|
330
835
|
setStatus(void 0);
|
|
331
|
-
setError(
|
|
836
|
+
setError(panelError(reason));
|
|
332
837
|
} finally {
|
|
333
838
|
if (generationRef.current === generation) setLoading(false);
|
|
334
839
|
}
|
|
335
|
-
}, [
|
|
840
|
+
}, [
|
|
841
|
+
call,
|
|
842
|
+
sessionId,
|
|
843
|
+
t
|
|
844
|
+
]);
|
|
336
845
|
(0, react.useEffect)(() => {
|
|
337
846
|
if (!open) return;
|
|
338
847
|
loadStatus();
|
|
@@ -350,11 +859,12 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
350
859
|
}, [open, changeOpen]);
|
|
351
860
|
const runCommit = (0, react.useCallback)(async () => {
|
|
352
861
|
const generation = generationRef.current;
|
|
862
|
+
setConfirming(false);
|
|
353
863
|
setRunning(true);
|
|
354
864
|
setResult(null);
|
|
355
865
|
setError(null);
|
|
356
866
|
try {
|
|
357
|
-
const value = await rpc(call, "commit", { sessionId });
|
|
867
|
+
const value = await rpc(call, "commit", { sessionId }, t("failed"));
|
|
358
868
|
if (generationRef.current !== generation) return;
|
|
359
869
|
setResult(value);
|
|
360
870
|
const next = await rpc(call, "status", { sessionId }).catch(() => void 0);
|
|
@@ -362,176 +872,423 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
362
872
|
if (next) setStatus(next);
|
|
363
873
|
} catch (reason) {
|
|
364
874
|
if (generationRef.current !== generation) return;
|
|
365
|
-
setError(
|
|
875
|
+
setError(panelError(reason));
|
|
366
876
|
} finally {
|
|
367
877
|
if (generationRef.current === generation) setRunning(false);
|
|
368
878
|
}
|
|
369
|
-
}, [
|
|
879
|
+
}, [
|
|
880
|
+
call,
|
|
881
|
+
sessionId,
|
|
882
|
+
t
|
|
883
|
+
]);
|
|
370
884
|
const available = status?.available === true;
|
|
371
885
|
const workspaceCount = status?.workspace.files ?? 0;
|
|
372
886
|
const canCommit = available && !running && !sessionRunning && workspaceCount > 0;
|
|
373
|
-
const modelLabel = status?.model ? `${status.model.provider}/${status.model.model} · ${status.model.source === "purpose" || status.model.source === "override" ? t("modelCustom") : t("modelDefault")}` : void 0;
|
|
374
887
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
375
888
|
ref: rootRef,
|
|
376
889
|
className: "gcm-root",
|
|
377
|
-
children: [
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
className: "gcm-trigger",
|
|
387
|
-
"aria-label": t("title"),
|
|
388
|
-
"aria-haspopup": "dialog",
|
|
389
|
-
"aria-expanded": open,
|
|
390
|
-
onClick: () => changeOpen(!open),
|
|
391
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitBranchIcon, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
392
|
-
className: "gcm-triggerLabel",
|
|
393
|
-
children: t("action")
|
|
394
|
-
})]
|
|
395
|
-
})
|
|
396
|
-
}),
|
|
397
|
-
open && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
398
|
-
ref: panelRef,
|
|
399
|
-
className: "gcm-panel",
|
|
400
|
-
style: position ?? {
|
|
401
|
-
visibility: "hidden",
|
|
402
|
-
left: 0,
|
|
403
|
-
top: 0
|
|
404
|
-
},
|
|
405
|
-
role: "dialog",
|
|
406
|
-
tabIndex: -1,
|
|
890
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
891
|
+
label: t("title"),
|
|
892
|
+
side: "bottom",
|
|
893
|
+
gap: 4,
|
|
894
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
895
|
+
ref: triggerRef,
|
|
896
|
+
variant: "ghost",
|
|
897
|
+
size: "sm",
|
|
898
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitBranchIcon, {}),
|
|
407
899
|
"aria-label": t("title"),
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
900
|
+
"aria-haspopup": "dialog",
|
|
901
|
+
"aria-expanded": open,
|
|
902
|
+
onClick: () => changeOpen(!open),
|
|
903
|
+
children: t("action")
|
|
904
|
+
})
|
|
905
|
+
}), open && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
906
|
+
ref: panelRef,
|
|
907
|
+
className: "gcm-root dsh-ui-surface gcm-panel",
|
|
908
|
+
style: position ?? {
|
|
909
|
+
visibility: "hidden",
|
|
910
|
+
left: 0,
|
|
911
|
+
top: 0
|
|
912
|
+
},
|
|
913
|
+
role: "dialog",
|
|
914
|
+
tabIndex: -1,
|
|
915
|
+
"aria-label": t("title"),
|
|
916
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
917
|
+
className: "dsh-ui-surface-body",
|
|
918
|
+
children: [
|
|
919
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
920
|
+
className: "dsh-ui-surface-head",
|
|
921
|
+
children: [
|
|
922
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
923
|
+
className: "dsh-ui-surface-title",
|
|
924
|
+
children: t("title")
|
|
925
|
+
}),
|
|
926
|
+
status?.branch ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
927
|
+
className: "dsh-ui-hint dsh-ui-truncate",
|
|
928
|
+
children: status.branch
|
|
929
|
+
}) : null,
|
|
930
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsh-ui-spacer" }),
|
|
931
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
932
|
+
label: t("refresh"),
|
|
933
|
+
side: "bottom",
|
|
934
|
+
gap: 4,
|
|
935
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
936
|
+
variant: "ghost",
|
|
937
|
+
size: "sm",
|
|
938
|
+
"aria-label": t("refresh"),
|
|
939
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, { size: 14 }),
|
|
940
|
+
disabled: loading || running,
|
|
941
|
+
onClick: () => void loadStatus()
|
|
942
|
+
})
|
|
943
|
+
})
|
|
944
|
+
]
|
|
945
|
+
}),
|
|
946
|
+
loading && !status && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
947
|
+
className: "dsh-ui-hint",
|
|
948
|
+
role: "status",
|
|
949
|
+
children: t("loading")
|
|
950
|
+
}),
|
|
951
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
952
|
+
className: "dsh-ui-error",
|
|
953
|
+
role: "alert",
|
|
954
|
+
children: errorText(t, error)
|
|
955
|
+
}),
|
|
956
|
+
status && !available && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
957
|
+
className: "dsh-ui-meta",
|
|
958
|
+
role: "status",
|
|
959
|
+
children: reasonText(t, status)
|
|
960
|
+
}),
|
|
961
|
+
available && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
962
|
+
status?.model ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
963
|
+
className: "gcm-model",
|
|
413
964
|
children: [
|
|
414
965
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
415
|
-
className: "
|
|
416
|
-
children: t("
|
|
966
|
+
className: "dsh-ui-hint",
|
|
967
|
+
children: t("model")
|
|
417
968
|
}),
|
|
418
|
-
|
|
419
|
-
className: "
|
|
420
|
-
children:
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
children: "
|
|
969
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
970
|
+
className: "dsh-ui-truncate",
|
|
971
|
+
children: [
|
|
972
|
+
status.model.provider,
|
|
973
|
+
"/",
|
|
974
|
+
status.model.model
|
|
975
|
+
]
|
|
976
|
+
}),
|
|
977
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
978
|
+
tone: status.model.source === "page" ? "info" : "neutral",
|
|
979
|
+
children: status.model.source === "page" ? t("modelCustom") : t("modelDefault")
|
|
429
980
|
})
|
|
430
981
|
]
|
|
982
|
+
}) : null,
|
|
983
|
+
confirming && canCommit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
984
|
+
className: "dsh-ui-stack",
|
|
985
|
+
role: "group",
|
|
986
|
+
"aria-label": t("commitAction"),
|
|
987
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
988
|
+
className: "dsh-ui-hint dsh-ui-wrap",
|
|
989
|
+
children: t("confirmPrompt").replace("{n}", String(workspaceCount))
|
|
990
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
991
|
+
className: "dsh-ui-actions",
|
|
992
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
993
|
+
variant: "ghost",
|
|
994
|
+
size: "md",
|
|
995
|
+
onClick: () => setConfirming(false),
|
|
996
|
+
children: t("cancel")
|
|
997
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
998
|
+
variant: "primary",
|
|
999
|
+
size: "md",
|
|
1000
|
+
onClick: () => void runCommit(),
|
|
1001
|
+
children: t("confirm")
|
|
1002
|
+
})]
|
|
1003
|
+
})]
|
|
1004
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1005
|
+
variant: "primary",
|
|
1006
|
+
size: "md",
|
|
1007
|
+
disabled: !canCommit,
|
|
1008
|
+
onClick: () => setConfirming(true),
|
|
1009
|
+
children: [t("commitAction"), t("filesCount").replace("{n}", String(workspaceCount))]
|
|
431
1010
|
}),
|
|
432
|
-
|
|
433
|
-
className: "
|
|
434
|
-
|
|
435
|
-
children: error
|
|
436
|
-
}),
|
|
437
|
-
status && !available && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
438
|
-
className: "gcm-notice",
|
|
439
|
-
role: "status",
|
|
440
|
-
children: reasonText(t, status)
|
|
1011
|
+
workspaceCount === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1012
|
+
className: "dsh-ui-hint",
|
|
1013
|
+
children: t("noWorkspace")
|
|
441
1014
|
}),
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
1015
|
+
sessionRunning && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1016
|
+
className: "dsh-ui-hint",
|
|
1017
|
+
children: t("sessionRunning")
|
|
1018
|
+
})
|
|
1019
|
+
] }),
|
|
1020
|
+
running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1021
|
+
className: "dsh-ui-hint",
|
|
1022
|
+
role: "status",
|
|
1023
|
+
children: t("running")
|
|
1024
|
+
}),
|
|
1025
|
+
result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1026
|
+
className: "dsh-ui-stack",
|
|
1027
|
+
children: [
|
|
1028
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1029
|
+
className: "dsh-ui-hint dsh-ui-wrap",
|
|
456
1030
|
children: [
|
|
457
|
-
t("
|
|
458
|
-
"
|
|
459
|
-
|
|
460
|
-
" ",
|
|
461
|
-
t("files"),
|
|
462
|
-
")"
|
|
1031
|
+
t("done"),
|
|
1032
|
+
" · ",
|
|
1033
|
+
result.branch
|
|
463
1034
|
]
|
|
464
1035
|
}),
|
|
465
|
-
|
|
466
|
-
className: "gcm-
|
|
467
|
-
children:
|
|
1036
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1037
|
+
className: "gcm-commits",
|
|
1038
|
+
children: result.commits.map((commit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
1039
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: commit.hash }),
|
|
1040
|
+
" ",
|
|
1041
|
+
commit.message
|
|
1042
|
+
] }, commit.hash || commit.message))
|
|
468
1043
|
}),
|
|
469
|
-
|
|
470
|
-
className: "
|
|
471
|
-
children: t("
|
|
1044
|
+
result.fallback && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1045
|
+
className: "dsh-ui-hint",
|
|
1046
|
+
children: t("fallback")
|
|
472
1047
|
})
|
|
473
|
-
]
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
1048
|
+
]
|
|
1049
|
+
})
|
|
1050
|
+
]
|
|
1051
|
+
})
|
|
1052
|
+
}), document.body)]
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
1055
|
+
const clientCss = `${officialUiCss("gcm-root")}
|
|
1056
|
+
/* Only the floating panel's geometry is this plugin's own: the material, the
|
|
1057
|
+
* rounded edge and the scrollbar tokens all come from the shared contract.
|
|
1058
|
+
*
|
|
1059
|
+
* The root class is spelled out because the panel is portaled and *is* the
|
|
1060
|
+
* root: the dsh-ui-surface recipe therefore reaches it as a compound
|
|
1061
|
+
* .gcm-root.dsh-ui-surface selector and declares position:relative at the
|
|
1062
|
+
* weight a bare .gcm-panel rule also carries. Left unqualified that recipe
|
|
1063
|
+
* wins, and the anchored left/top degrade into offsets from the panel's static
|
|
1064
|
+
* spot at the end of body — the popover then renders under the page instead
|
|
1065
|
+
* of at the trigger. Naming both classes puts this rule at the same weight,
|
|
1066
|
+
* later in the sheet, so it wins. */
|
|
1067
|
+
.gcm-root.gcm-panel { position: fixed; width: min(360px, calc(100vw - 32px)); max-height: min(560px, calc(100vh - 32px)); overflow: auto; }
|
|
1068
|
+
.gcm-model { display: flex; align-items: center; gap: 6px; min-width: 0; margin: 0; font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-primary); }
|
|
1069
|
+
.gcm-commits { margin: 0; padding-inline-start: 18px; display: grid; gap: 2px; font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
|
|
1070
|
+
.gcm-commits code { font-size: 12px; background: var(--dsw-alias-bg-layer-2); padding: 0 4px; border-radius: var(--dsw-radius-sm); }
|
|
1071
|
+
.gcm-settings { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
|
|
1072
|
+
.gcm-settings-status { display: flex; flex-direction: column; gap: 4px; }
|
|
1073
|
+
`;
|
|
1074
|
+
/** Host UI language, following live switches; falls back to zh when the service lacks a snapshot. */
|
|
1075
|
+
function useHostLocale(client) {
|
|
1076
|
+
const subscribe = (0, react.useCallback)((listener) => client.locale.subscribe?.(listener) ?? (() => {}), [client]);
|
|
1077
|
+
const snapshot = (0, react.useCallback)(() => client.locale.getSnapshot?.().active ?? "zh", [client]);
|
|
1078
|
+
return (0, react.useSyncExternalStore)(subscribe, snapshot, snapshot);
|
|
1079
|
+
}
|
|
1080
|
+
const settingsZh = {
|
|
1081
|
+
pageTitle: "Git 提交",
|
|
1082
|
+
model: "提交规划模型",
|
|
1083
|
+
modelDefault: "默认模型",
|
|
1084
|
+
modelEffort: "思考强度",
|
|
1085
|
+
modelHint: "留空则使用当前会话模型,再回落到宿主默认对话模型。",
|
|
1086
|
+
fallback: "模型不可用时仍然提交",
|
|
1087
|
+
fallbackHint: "默认关闭:模型规划失败会取消本次提交,工作区不变。开启后全部改动会合并成一个提交,由插件代写提交信息。",
|
|
1088
|
+
loading: "正在读取设置…",
|
|
1089
|
+
saved: "已保存。",
|
|
1090
|
+
stale: "设置已更新,请刷新后重试。",
|
|
1091
|
+
failed: "无法读取提交设置。",
|
|
1092
|
+
retry: "重试"
|
|
1093
|
+
};
|
|
1094
|
+
const settingsEn = {
|
|
1095
|
+
pageTitle: "Git Commit",
|
|
1096
|
+
model: "Commit planning model",
|
|
1097
|
+
modelDefault: "Default model",
|
|
1098
|
+
modelEffort: "Reasoning effort",
|
|
1099
|
+
modelHint: "Leave empty to use the current session model, then the host default chat model.",
|
|
1100
|
+
fallback: "Commit even without a model plan",
|
|
1101
|
+
fallbackHint: "Off by default: a failed plan cancels the run and leaves the workspace untouched. When on, every change lands in one commit with a plugin-written message.",
|
|
1102
|
+
loading: "Loading settings…",
|
|
1103
|
+
saved: "Saved.",
|
|
1104
|
+
stale: "Settings changed; refresh and retry.",
|
|
1105
|
+
failed: "Could not load commit settings.",
|
|
1106
|
+
retry: "Retry"
|
|
1107
|
+
};
|
|
1108
|
+
function settingsCopy(locale) {
|
|
1109
|
+
return String(locale).startsWith("zh") ? settingsZh : settingsEn;
|
|
1110
|
+
}
|
|
1111
|
+
/** Plugin-page settings row: pick the model this plugin's planning call uses. */
|
|
1112
|
+
function GitCommitSettingsPanel(props) {
|
|
1113
|
+
const hostLocale = useHostLocale(props.client);
|
|
1114
|
+
const text = settingsCopy(props.locale ?? hostLocale);
|
|
1115
|
+
const [settings, setSettings] = (0, react.useState)(void 0);
|
|
1116
|
+
const [choices, setChoices] = (0, react.useState)([]);
|
|
1117
|
+
const [error, setError] = (0, react.useState)("");
|
|
1118
|
+
const [note, setNote] = (0, react.useState)("");
|
|
1119
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
1120
|
+
const call = (0, react.useCallback)((endpoint, payload = {}) => props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload), [props.client]);
|
|
1121
|
+
(0, react.useEffect)(() => {
|
|
1122
|
+
let live = true;
|
|
1123
|
+
rpc(call, "settings", {}, text.failed).then((next) => {
|
|
1124
|
+
if (live) {
|
|
1125
|
+
setSettings(next);
|
|
1126
|
+
setError("");
|
|
1127
|
+
}
|
|
1128
|
+
}).catch(() => {
|
|
1129
|
+
if (live) setError(text.failed);
|
|
1130
|
+
});
|
|
1131
|
+
return () => {
|
|
1132
|
+
live = false;
|
|
1133
|
+
};
|
|
1134
|
+
}, [call, text.failed]);
|
|
1135
|
+
const boundProvider = settings?.model.provider ?? "";
|
|
1136
|
+
const boundModel = settings?.model.model ?? "";
|
|
1137
|
+
(0, react.useEffect)(() => {
|
|
1138
|
+
let live = true;
|
|
1139
|
+
const bound = boundProvider && boundModel ? {
|
|
1140
|
+
provider: boundProvider,
|
|
1141
|
+
model: boundModel
|
|
1142
|
+
} : void 0;
|
|
1143
|
+
props.client.remote?.session?.modelCatalog?.()?.then((value) => {
|
|
1144
|
+
if (live) setChoices(parseModelMenuChoices(value, bound));
|
|
1145
|
+
}).catch(() => {
|
|
1146
|
+
if (live) setChoices([]);
|
|
1147
|
+
});
|
|
1148
|
+
return () => {
|
|
1149
|
+
live = false;
|
|
1150
|
+
};
|
|
1151
|
+
}, [
|
|
1152
|
+
props.client,
|
|
1153
|
+
boundProvider,
|
|
1154
|
+
boundModel
|
|
1155
|
+
]);
|
|
1156
|
+
const save = (0, react.useCallback)((patch) => {
|
|
1157
|
+
if (!settings) return;
|
|
1158
|
+
setBusy(true);
|
|
1159
|
+
setNote("");
|
|
1160
|
+
setError("");
|
|
1161
|
+
rpc(call, "settings.update", {
|
|
1162
|
+
settings: {
|
|
1163
|
+
model: patch.model ?? settings.model,
|
|
1164
|
+
allowFallback: patch.allowFallback ?? settings.allowFallback
|
|
1165
|
+
},
|
|
1166
|
+
expectedRevision: settings.revision
|
|
1167
|
+
}, text.stale).then((next) => {
|
|
1168
|
+
setSettings(next);
|
|
1169
|
+
setNote(text.saved);
|
|
1170
|
+
setBusy(false);
|
|
1171
|
+
}).catch(() => {
|
|
1172
|
+
setError(text.stale);
|
|
1173
|
+
setBusy(false);
|
|
1174
|
+
});
|
|
1175
|
+
}, [
|
|
1176
|
+
call,
|
|
1177
|
+
settings,
|
|
1178
|
+
text.saved,
|
|
1179
|
+
text.stale
|
|
1180
|
+
]);
|
|
1181
|
+
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
1182
|
+
className: "gcm-root gcm-settings",
|
|
1183
|
+
children: error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1184
|
+
role: "alert",
|
|
1185
|
+
className: "dsh-ui-error",
|
|
1186
|
+
children: error
|
|
1187
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1188
|
+
role: "status",
|
|
1189
|
+
className: "dsh-ui-hint",
|
|
1190
|
+
children: text.loading
|
|
1191
|
+
})
|
|
1192
|
+
});
|
|
1193
|
+
const current = settings.model;
|
|
1194
|
+
const selected = choices.find((item) => item.provider === current.provider && item.model === current.model);
|
|
1195
|
+
const efforts = modelMenuEffortOptions(selected, current.reasoningEffort);
|
|
1196
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1197
|
+
className: "gcm-root gcm-settings",
|
|
1198
|
+
"data-testid": "git-commit-settings",
|
|
1199
|
+
children: [
|
|
1200
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1201
|
+
className: "gcm-settings-status",
|
|
1202
|
+
children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1203
|
+
role: "alert",
|
|
1204
|
+
className: "dsh-ui-error",
|
|
1205
|
+
children: error
|
|
1206
|
+
}), note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1207
|
+
role: "status",
|
|
1208
|
+
className: "dsh-ui-notice",
|
|
1209
|
+
children: note
|
|
1210
|
+
})]
|
|
1211
|
+
}),
|
|
1212
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1213
|
+
className: "dsh-ui-field",
|
|
1214
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1215
|
+
className: "dsh-ui-label",
|
|
1216
|
+
children: text.model
|
|
1217
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1218
|
+
className: "dsh-ui-select",
|
|
1219
|
+
value: modelMenuChoiceKey(current.provider, current.model),
|
|
1220
|
+
disabled: busy,
|
|
1221
|
+
onChange: (event) => {
|
|
1222
|
+
const key = event.target.value;
|
|
1223
|
+
if (!key) {
|
|
1224
|
+
save({ model: {
|
|
1225
|
+
provider: "",
|
|
1226
|
+
model: ""
|
|
1227
|
+
} });
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
const parsed = parseModelMenuChoiceKey(key);
|
|
1231
|
+
if (parsed) save({ model: parsed });
|
|
1232
|
+
},
|
|
1233
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1234
|
+
value: "",
|
|
1235
|
+
children: text.modelDefault
|
|
1236
|
+
}), choices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1237
|
+
value: modelMenuChoiceKey(choice.provider, choice.model),
|
|
1238
|
+
children: choice.label
|
|
1239
|
+
}, modelMenuChoiceKey(choice.provider, choice.model)))]
|
|
1240
|
+
})]
|
|
1241
|
+
}),
|
|
1242
|
+
selected && efforts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1243
|
+
className: "dsh-ui-field",
|
|
1244
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1245
|
+
className: "dsh-ui-label",
|
|
1246
|
+
children: text.modelEffort
|
|
1247
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1248
|
+
className: "dsh-ui-select",
|
|
1249
|
+
value: current.reasoningEffort ?? "",
|
|
1250
|
+
disabled: busy,
|
|
1251
|
+
onChange: (event) => save({ model: {
|
|
1252
|
+
...current,
|
|
1253
|
+
reasoningEffort: event.target.value || void 0
|
|
1254
|
+
} }),
|
|
1255
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1256
|
+
value: "",
|
|
1257
|
+
children: text.modelDefault
|
|
1258
|
+
}), efforts.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1259
|
+
value: effort.id,
|
|
1260
|
+
children: effort.name
|
|
1261
|
+
}, effort.id))]
|
|
1262
|
+
})]
|
|
1263
|
+
}),
|
|
1264
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1265
|
+
className: "dsh-ui-meta",
|
|
1266
|
+
children: text.modelHint
|
|
1267
|
+
}),
|
|
1268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
|
|
1269
|
+
className: "gcm-root dsh-ui-card",
|
|
1270
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
1271
|
+
className: "dsh-ui-toggle-row",
|
|
1272
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1273
|
+
className: "dsh-ui-toggle-text",
|
|
1274
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1275
|
+
className: "dsh-ui-toggle-label",
|
|
1276
|
+
children: text.fallback
|
|
1277
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1278
|
+
className: "dsh-ui-hint",
|
|
1279
|
+
children: text.fallbackHint
|
|
1280
|
+
})]
|
|
1281
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1282
|
+
checked: settings.allowFallback,
|
|
1283
|
+
label: text.fallback,
|
|
1284
|
+
disabled: busy,
|
|
1285
|
+
onChange: (next) => save({ allowFallback: next })
|
|
1286
|
+
})]
|
|
505
1287
|
})
|
|
506
|
-
})
|
|
1288
|
+
})
|
|
507
1289
|
]
|
|
508
1290
|
});
|
|
509
1291
|
}
|
|
510
|
-
const css = `
|
|
511
|
-
.gcm-root{position:relative;display:inline-flex}
|
|
512
|
-
.gcm-trigger{min-height:28px;color:var(--dsw-alias-label-tertiary);cursor:pointer;background:none;border:0;border-radius:6px;align-items:center;gap:5px;padding:3px 7px;font-size:12px;display:inline-flex}
|
|
513
|
-
.gcm-trigger:hover,.gcm-trigger:focus-visible{color:var(--dsw-alias-label-primary)}
|
|
514
|
-
.gcm-triggerLabel{white-space:nowrap}
|
|
515
|
-
.gcm-panel{--dsw-elevation-stroke-color:var(--dsw-alias-border-l1);z-index:100;box-sizing:border-box;width:min(360px,100vw - 32px);max-height:min(560px,100vh - 32px);box-shadow:var(--dsw-elevation-prominent);border:0;border-radius:12px;display:flex;flex-direction:column;position:fixed;overflow:hidden;color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.5;font-family:var(--default-font-family,system-ui,sans-serif)}
|
|
516
|
-
.gcm-panel::before{content:"";z-index:-1;background:var(--dsw-specific-menu);backdrop-filter:var(--dsw-menu-backdrop-filter);border-radius:12px;position:absolute;inset:0}
|
|
517
|
-
.gcm-body{display:grid;gap:8px;padding:12px 14px 14px;overflow-y:auto}
|
|
518
|
-
.gcm-header{display:flex;align-items:center;gap:8px}
|
|
519
|
-
.gcm-title{font-weight:600}
|
|
520
|
-
.gcm-branch{color:var(--dsw-alias-label-caption);overflow-wrap:anywhere;font-size:12px}
|
|
521
|
-
.gcm-iconButton{margin-left:auto;border:0;background:none;color:var(--dsw-alias-label-tertiary);cursor:pointer;font-size:14px;padding:2px 6px;border-radius:6px}
|
|
522
|
-
.gcm-iconButton:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
523
|
-
.gcm-iconButton:disabled{opacity:.45;cursor:not-allowed}
|
|
524
|
-
.gcm-action{--dsw-elevation-stroke-color:var(--dsw-alias-border-l2);display:block;width:100%;text-align:left;padding:7px 10px;border:0;box-shadow:var(--dsw-elevation-stroke);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer}
|
|
525
|
-
.gcm-action:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}
|
|
526
|
-
.gcm-action:disabled{opacity:.45;cursor:not-allowed}
|
|
527
|
-
.gcm-primary{--dsw-elevation-stroke-color:var(--dsw-alias-brand-primary)}
|
|
528
|
-
.gcm-meta{margin:0;font-size:12px;color:var(--dsw-alias-label-caption);overflow-wrap:anywhere}
|
|
529
|
-
.gcm-notice{margin:0;font-size:12px;color:var(--dsw-alias-label-secondary)}
|
|
530
|
-
.gcm-error{margin:0;font-size:12px;color:var(--dsw-alias-state-error-primary)}
|
|
531
|
-
.gcm-result{display:grid;gap:4px}
|
|
532
|
-
.gcm-commits{margin:0;padding-left:18px;display:grid;gap:2px;font-size:12px;overflow-wrap:anywhere}
|
|
533
|
-
.gcm-commits code{font-size:12px;background:var(--dsw-alias-bg-layer-2);padding:0 4px;border-radius:4px}
|
|
534
|
-
`;
|
|
535
1292
|
function apply(ctx) {
|
|
536
1293
|
const client = ctx;
|
|
537
1294
|
ctx.provide(GIT_COMMIT_CLIENT_SERVICE, { active: true });
|
|
@@ -539,6 +1296,18 @@ function apply(ctx) {
|
|
|
539
1296
|
zh: { ...zh },
|
|
540
1297
|
en: { ...en }
|
|
541
1298
|
}), "git-commit.locale");
|
|
1299
|
+
ctx.effect(() => {
|
|
1300
|
+
if (typeof document === "undefined") return () => {};
|
|
1301
|
+
const style = document.createElement("style");
|
|
1302
|
+
style.setAttribute("data-plugin", name);
|
|
1303
|
+
style.textContent = clientCss;
|
|
1304
|
+
document.head.appendChild(style);
|
|
1305
|
+
return () => style.remove();
|
|
1306
|
+
}, "git-commit.styles");
|
|
1307
|
+
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
1308
|
+
name: "plugins.bundle.config",
|
|
1309
|
+
key: PLUGIN_NAME
|
|
1310
|
+
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitCommitSettingsPanel, { client }))), "git-commit.settings");
|
|
542
1311
|
ctx.effect(() => client.slots.inject("conversation.session.header.actions", () => client.slots.register({
|
|
543
1312
|
name: "conversation.session.header.actions",
|
|
544
1313
|
id: "git-commit",
|
|
@@ -549,7 +1318,9 @@ function apply(ctx) {
|
|
|
549
1318
|
}
|
|
550
1319
|
//#endregion
|
|
551
1320
|
exports.GitCommitAction = GitCommitAction;
|
|
1321
|
+
exports.GitCommitSettingsPanel = GitCommitSettingsPanel;
|
|
552
1322
|
exports.apply = apply;
|
|
1323
|
+
exports.clientCss = clientCss;
|
|
553
1324
|
exports.inject = inject;
|
|
554
1325
|
exports.name = name;
|
|
555
1326
|
|