@klarkxy/dsh-git-commit 0.1.3 → 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.inner.cjs
CHANGED
|
@@ -3,7 +3,467 @@ let react = require("react");
|
|
|
3
3
|
let react_dom = require("react-dom");
|
|
4
4
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
5
5
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
6
|
+
//#region ../dsh-plugin-kit/src/model-menu.ts
|
|
7
|
+
/**
|
|
8
|
+
* Shared plugin-page model menu.
|
|
9
|
+
*
|
|
10
|
+
* A feature plugin's own settings row exposes a model select. An empty
|
|
11
|
+
* selection means the host default chat model for that feature's own call.
|
|
12
|
+
*
|
|
13
|
+
* Browser-safe and React-optional: pure helpers live here so hosts and clients
|
|
14
|
+
* share one catalog contract without bundling React into the host build.
|
|
15
|
+
*/
|
|
16
|
+
const KEY_SEPARATOR = "";
|
|
17
|
+
function modelMenuChoiceKey(provider, model) {
|
|
18
|
+
return provider && model ? `${provider}${KEY_SEPARATOR}${model}` : "";
|
|
19
|
+
}
|
|
20
|
+
function parseModelMenuChoiceKey(value) {
|
|
21
|
+
const index = value.indexOf(KEY_SEPARATOR);
|
|
22
|
+
if (index <= 0) return void 0;
|
|
23
|
+
const provider = value.slice(0, index);
|
|
24
|
+
const model = value.slice(index + 1);
|
|
25
|
+
if (!provider || !model) return void 0;
|
|
26
|
+
return {
|
|
27
|
+
provider,
|
|
28
|
+
model
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function asRecord(value) {
|
|
32
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
33
|
+
}
|
|
34
|
+
function asText(value) {
|
|
35
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
36
|
+
}
|
|
37
|
+
function parseEffort(value) {
|
|
38
|
+
const row = asRecord(value);
|
|
39
|
+
const id = row ? asText(row.id) : void 0;
|
|
40
|
+
if (!id) return void 0;
|
|
41
|
+
return {
|
|
42
|
+
id,
|
|
43
|
+
name: row ? asText(row.name) ?? id : id
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Parse the host session catalog into flat choices, keeping a saved route selectable. */
|
|
47
|
+
function parseModelMenuChoices(value, bound) {
|
|
48
|
+
const envelope = asRecord(value);
|
|
49
|
+
const root = (envelope && envelope.ok === true ? asRecord(envelope.value) : void 0) ?? envelope ?? {};
|
|
50
|
+
const groups = Array.isArray(root.groups) ? root.groups : [];
|
|
51
|
+
const choices = [];
|
|
52
|
+
for (const group of groups) {
|
|
53
|
+
const groupRow = asRecord(group);
|
|
54
|
+
const provider = groupRow ? asText(groupRow.id) : void 0;
|
|
55
|
+
if (!provider || !groupRow || !Array.isArray(groupRow.models)) continue;
|
|
56
|
+
const providerName = asText(groupRow.name) ?? provider;
|
|
57
|
+
for (const entry of groupRow.models) {
|
|
58
|
+
const modelRow = asRecord(entry);
|
|
59
|
+
const model = modelRow ? asText(modelRow.id) : void 0;
|
|
60
|
+
if (!model || !modelRow) continue;
|
|
61
|
+
const name = asText(modelRow.name) ?? model;
|
|
62
|
+
const reasoning = asRecord(modelRow.reasoning);
|
|
63
|
+
const efforts = reasoning && Array.isArray(reasoning.efforts) ? reasoning.efforts.flatMap((effort) => {
|
|
64
|
+
const parsed = parseEffort(effort);
|
|
65
|
+
return parsed ? [parsed] : [];
|
|
66
|
+
}) : [];
|
|
67
|
+
choices.push({
|
|
68
|
+
provider,
|
|
69
|
+
model,
|
|
70
|
+
label: `${providerName} / ${name}`,
|
|
71
|
+
efforts
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (bound?.provider && bound.model && !choices.some((item) => item.provider === bound.provider && item.model === bound.model)) choices.push({
|
|
76
|
+
provider: bound.provider,
|
|
77
|
+
model: bound.model,
|
|
78
|
+
label: `${bound.provider} / ${bound.model}`,
|
|
79
|
+
efforts: []
|
|
80
|
+
});
|
|
81
|
+
return choices;
|
|
82
|
+
}
|
|
83
|
+
/** Reasoning-effort options for one choice; the saved value stays selectable. */
|
|
84
|
+
function modelMenuEffortOptions(choice, current) {
|
|
85
|
+
const listed = (choice?.efforts ?? []).map((item) => ({ ...item }));
|
|
86
|
+
if (current && !listed.some((item) => item.id === current)) listed.push({
|
|
87
|
+
id: current,
|
|
88
|
+
name: current
|
|
89
|
+
});
|
|
90
|
+
return listed;
|
|
91
|
+
}
|
|
92
|
+
Object.values({
|
|
93
|
+
/** Surfaces, from the page up to the topmost nested card. */
|
|
94
|
+
surface: [
|
|
95
|
+
"--dsw-alias-bg-base",
|
|
96
|
+
"--dsw-alias-bg-layer-1",
|
|
97
|
+
"--dsw-alias-bg-layer-2",
|
|
98
|
+
"--dsw-alias-bg-layer-3",
|
|
99
|
+
"--dsw-alias-bg-layer-4",
|
|
100
|
+
"--dsw-alias-bg-module-platform",
|
|
101
|
+
"--dsw-alias-bg-overlay"
|
|
102
|
+
],
|
|
103
|
+
/** Hairlines. l1 is near-invisible on a menu surface, so menus use l2+. */
|
|
104
|
+
border: [
|
|
105
|
+
"--dsw-alias-border-l1",
|
|
106
|
+
"--dsw-alias-border-l2",
|
|
107
|
+
"--dsw-alias-border-l3",
|
|
108
|
+
"--dsw-alias-border-l4"
|
|
109
|
+
],
|
|
110
|
+
/** Text. `caption` is menu shortcut text, `dimmed` is placeholder text. */
|
|
111
|
+
label: [
|
|
112
|
+
"--dsw-alias-label-primary",
|
|
113
|
+
"--dsw-alias-label-secondary",
|
|
114
|
+
"--dsw-alias-label-tertiary",
|
|
115
|
+
"--dsw-alias-label-caption",
|
|
116
|
+
"--dsw-alias-label-dimmed",
|
|
117
|
+
"--dsw-alias-label-error",
|
|
118
|
+
"--dsw-alias-label-primary-foreground"
|
|
119
|
+
],
|
|
120
|
+
/** Interactive fills. The `-danger` hover belongs to destructive rows. */
|
|
121
|
+
interactive: [
|
|
122
|
+
"--dsw-alias-interactive-bg-hover",
|
|
123
|
+
"--dsw-alias-interactive-bg-active",
|
|
124
|
+
"--dsw-alias-interactive-bg-hover-danger"
|
|
125
|
+
],
|
|
126
|
+
/** Accent and status. `business` is the focus ring's default colour. */
|
|
127
|
+
accent: [
|
|
128
|
+
"--dsw-alias-brand-primary",
|
|
129
|
+
"--dsw-alias-button-primary-fill",
|
|
130
|
+
"--dsw-alias-button-primary-hover",
|
|
131
|
+
"--dsw-alias-button-tool-bar-fill",
|
|
132
|
+
"--dsw-alias-button-tool-bar-hover",
|
|
133
|
+
"--dsw-alias-button-ghost-active-fill",
|
|
134
|
+
"--dsw-alias-button-ghost-active-border"
|
|
135
|
+
],
|
|
136
|
+
state: [
|
|
137
|
+
"--dsw-alias-state-business-primary",
|
|
138
|
+
"--dsw-alias-state-success-primary",
|
|
139
|
+
"--dsw-alias-state-warn-primary",
|
|
140
|
+
"--dsw-alias-state-error-primary",
|
|
141
|
+
"--dsw-alias-state-idle-primary"
|
|
142
|
+
],
|
|
143
|
+
/** Corner scale: sm 8, md 12, lg the large card, panel the modal. */
|
|
144
|
+
radius: [
|
|
145
|
+
"--dsw-radius-sm",
|
|
146
|
+
"--dsw-radius-md",
|
|
147
|
+
"--dsw-radius-lg",
|
|
148
|
+
"--dsw-radius-panel"
|
|
149
|
+
],
|
|
150
|
+
/** An elevated surface draws its edge in the shadow, so it keeps border: 0. */
|
|
151
|
+
elevation: [
|
|
152
|
+
"--dsw-elevation-soft",
|
|
153
|
+
"--dsw-elevation-stroke",
|
|
154
|
+
"--dsw-elevation-stroke-color",
|
|
155
|
+
"--dsw-elevation-panel",
|
|
156
|
+
"--dsw-elevation-prominent"
|
|
157
|
+
],
|
|
158
|
+
/** Floats, masks and scrollbars. */
|
|
159
|
+
overlay: [
|
|
160
|
+
"--dsw-menu-surface-fill",
|
|
161
|
+
"--dsw-menu-backdrop-filter",
|
|
162
|
+
"--dsw-mask-blur",
|
|
163
|
+
"--dsw-alias-bg-mask-1",
|
|
164
|
+
"--dsw-alias-menu-icon",
|
|
165
|
+
"--dsw-alias-scrollbar-bg-l2",
|
|
166
|
+
"--dsw-alias-scrollbar-hover-l2",
|
|
167
|
+
"--dsw-shadow-lv3"
|
|
168
|
+
],
|
|
169
|
+
/** Motion, focus and type axis, published by the host rather than by a component. */
|
|
170
|
+
system: [
|
|
171
|
+
"--dsw-focus-ring-width",
|
|
172
|
+
"--dsw-focus-ring-color",
|
|
173
|
+
"--dsw-font-markdown-code-font-family",
|
|
174
|
+
"--ds-transition-duration",
|
|
175
|
+
"--ds-ease-in-out",
|
|
176
|
+
"--ds-font-family-code",
|
|
177
|
+
"--dsh-scrollbar-thumb",
|
|
178
|
+
"--dsh-scrollbar-thumb-hover",
|
|
179
|
+
"--dsh-content-font-size-secondary",
|
|
180
|
+
"--dsh-content-font-delta",
|
|
181
|
+
"--dsh-frame-top-clearance"
|
|
182
|
+
]
|
|
183
|
+
}).flat();
|
|
184
|
+
/**
|
|
185
|
+
* The host's focus ring shorthand, verbatim. Writing this per control is how
|
|
186
|
+
* rings drift apart; a stylesheet that draws its own focus style should use
|
|
187
|
+
* {@link officialFocus} instead. The colour fallback is the host's own default.
|
|
188
|
+
*/
|
|
189
|
+
const OFFICIAL_FOCUS_RING = "var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary))";
|
|
190
|
+
/**
|
|
191
|
+
* A complete `outline` declaration at the official geometry. `offset` defaults
|
|
192
|
+
* to the 2px the native controls use; the host's inline field rules sit at 1px
|
|
193
|
+
* and a segmented tab pulls the ring inside itself at -2px.
|
|
194
|
+
*/
|
|
195
|
+
function officialFocus(offset = "2px") {
|
|
196
|
+
return `outline: ${OFFICIAL_FOCUS_RING}; outline-offset: ${offset}`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* An elevated surface. The host draws a raised surface's edge inside its
|
|
200
|
+
* shadow rather than as a border, so a card sets `border: 0` and takes the edge
|
|
201
|
+
* from the elevation — which is also why its border stays off the alias-border
|
|
202
|
+
* set that a flat, non-elevated rule would use.
|
|
203
|
+
*/
|
|
204
|
+
function officialElevation(level) {
|
|
205
|
+
return `border: 0; box-shadow: var(--dsw-elevation-${level})`;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* The shared stylesheet for a plugin UI, scoped under that plugin's root class.
|
|
209
|
+
*
|
|
210
|
+
* Scoping is what keeps this safe to always-bundle into every client: the rules
|
|
211
|
+
* can only match inside the plugin's own subtree, so two plugins can both mount
|
|
212
|
+
* `dsh-ui-card` without either styling the other's markup.
|
|
213
|
+
*
|
|
214
|
+
* Each recipe matches the root element *and* its descendants, because the
|
|
215
|
+
* commonest arrangement is one element that is both — a surface that carries
|
|
216
|
+
* the root class and `dsh-ui-card`, or a portaled dialog that is the root and
|
|
217
|
+
* the `dsh-ui-surface`. A descendant-only scope would silently leave those
|
|
218
|
+
* unstyled, with no error anywhere.
|
|
219
|
+
*
|
|
220
|
+
* A caveat worth knowing: `Modal` and `Menu` portal to `document.body`, so their
|
|
221
|
+
* content leaves the plugin's subtree entirely. A portaled surface therefore
|
|
222
|
+
* needs its own root class on an element inside the portal.
|
|
223
|
+
*
|
|
224
|
+
* Only patterns that primitives do not provide live here. If a control appears
|
|
225
|
+
* below that `Button`/`Input`/`Switch`/`Tag`/`Menu` already covers, that is a
|
|
226
|
+
* bug in this file: use the primitive and delete the recipe.
|
|
227
|
+
*/
|
|
228
|
+
function officialUiCss(roots) {
|
|
229
|
+
const scope = (typeof roots === "string" ? [roots] : roots).map((root) => `.${root}`).join(", ");
|
|
230
|
+
return `
|
|
231
|
+
/* Official DSH Web UI contract. Generated by @klarkxy/dsh-plugin-kit/official-ui.
|
|
232
|
+
* Do not hand-edit the copy inside a plugin: change this module so every plugin
|
|
233
|
+
* moves together. Scope: ${scope} */
|
|
234
|
+
/* The scope rule below stays open for the whole sheet: every rule after it
|
|
235
|
+
* nests with the parent selector, so one stylesheet can cover several unrelated
|
|
236
|
+
* roots without leaking into a neighbouring plugin. Its closing brace is last. */
|
|
237
|
+
${scope} {
|
|
238
|
+
color: var(--dsw-alias-label-primary);
|
|
239
|
+
font-family: inherit;
|
|
240
|
+
font-size: 14px;
|
|
241
|
+
line-height: 22px;
|
|
242
|
+
min-width: 0;
|
|
243
|
+
|
|
244
|
+
& *, & *::before, & *::after { box-sizing: border-box; }
|
|
245
|
+
& h1, & h2, & h3, & h4, & p, & figure { margin: 0; }
|
|
246
|
+
/* Keyboard focus only: pointer input keeps the native resting appearance. The
|
|
247
|
+
* ring needs no radius of its own — the outline already follows the control's
|
|
248
|
+
* own border-radius, and overriding it here would flatten every control. */
|
|
249
|
+
& :focus-visible { ${officialFocus()}; }
|
|
250
|
+
& [hidden] { display: none !important; }
|
|
251
|
+
|
|
252
|
+
/* --- Type tiers. The same 14/22 and 13/20 steps the primitives use. --- */
|
|
253
|
+
& .dsh-ui-title { font-size: 16px; line-height: 24px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
254
|
+
& .dsh-ui-heading { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
255
|
+
& .dsh-ui-compact { font-size: 13px; line-height: 20px; color: var(--dsw-alias-label-primary); }
|
|
256
|
+
& .dsh-ui-meta { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
|
|
257
|
+
& .dsh-ui-hint { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
258
|
+
& .dsh-ui-error { font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-error-primary); }
|
|
259
|
+
& .dsh-ui-warn { color: var(--dsw-alias-state-warn-primary); }
|
|
260
|
+
& .dsh-ui-success { color: var(--dsw-alias-state-success-primary); }
|
|
261
|
+
& .dsh-ui-muted { color: var(--dsw-alias-label-tertiary); }
|
|
262
|
+
& .dsh-ui-truncate { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
263
|
+
& .dsh-ui-wrap { min-width: 0; overflow-wrap: anywhere; }
|
|
264
|
+
|
|
265
|
+
/* --- Layout scaffolding. --- */
|
|
266
|
+
& .dsh-ui-stack { display: flex; flex-direction: column; gap: 12px; }
|
|
267
|
+
& .dsh-ui-stack-lg { display: flex; flex-direction: column; gap: 20px; }
|
|
268
|
+
& .dsh-ui-row { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
269
|
+
& .dsh-ui-row-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
|
|
270
|
+
& .dsh-ui-spacer { flex: 1; min-width: 0; }
|
|
271
|
+
/* A 0.5px l2 rule is the host's section divider; reserve the same rhythm. */
|
|
272
|
+
& .dsh-ui-divider { height: 0.5px; background: var(--dsw-alias-border-l2); }
|
|
273
|
+
& .dsh-ui-section { display: flex; flex-direction: column; gap: 12px; padding-top: 16px; border-top: 0.5px solid var(--dsw-alias-border-l2); }
|
|
274
|
+
|
|
275
|
+
/* --- Card: a raised surface whose edge lives in the shadow. --- */
|
|
276
|
+
& .dsh-ui-card {
|
|
277
|
+
${officialElevation("stroke")}
|
|
278
|
+
display: flex;
|
|
279
|
+
flex-direction: column;
|
|
280
|
+
gap: 12px;
|
|
281
|
+
padding: 16px;
|
|
282
|
+
border-radius: var(--dsw-radius-lg);
|
|
283
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
284
|
+
color: var(--dsw-alias-label-primary);
|
|
285
|
+
min-width: 0;
|
|
286
|
+
}
|
|
287
|
+
& .dsh-ui-card--flat { border: 0.5px solid var(--dsw-alias-border-l3); box-shadow: none; }
|
|
288
|
+
& .dsh-ui-card--nested { background: var(--dsw-alias-bg-layer-2); }
|
|
289
|
+
& .dsh-ui-card--plain { background: none; }
|
|
290
|
+
|
|
291
|
+
/* --- Field: label, control, help, error. The control itself is an
|
|
292
|
+
* official primitive, so this only owns the vertical rhythm. --- */
|
|
293
|
+
& .dsh-ui-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
|
|
294
|
+
& .dsh-ui-field + .dsh-ui-field { border-top: 0.5px solid var(--dsw-alias-border-l2); padding-top: 12px; }
|
|
295
|
+
& .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); }
|
|
296
|
+
& .dsh-ui-label-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
|
|
297
|
+
& .dsh-ui-help { font-size: 12px; line-height: 1.6; color: var(--dsw-alias-label-secondary); }
|
|
298
|
+
& .dsh-ui-help p { margin: 0; }
|
|
299
|
+
& .dsh-ui-help p + p { margin-top: 8px; }
|
|
300
|
+
& .dsh-ui-required { color: var(--dsw-alias-state-error-primary); }
|
|
301
|
+
& .dsh-ui-control { width: 100%; min-width: 0; }
|
|
302
|
+
/* An invalid control is marked on the control, not only in the message. */
|
|
303
|
+
& .dsh-ui-control[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
304
|
+
& .dsh-ui-readonly { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
305
|
+
& .dsh-ui-readonly > dt { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
306
|
+
& .dsh-ui-readonly > dd { margin: 0; font-size: 13px; line-height: 20px; overflow-wrap: anywhere; }
|
|
307
|
+
|
|
308
|
+
/* --- Buttons row. A no-op container: it exists so actions line up on the
|
|
309
|
+
* trailing edge the way every native panel does. --- */
|
|
310
|
+
& .dsh-ui-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-top: 4px; }
|
|
311
|
+
& .dsh-ui-actions-end { justify-content: flex-end; }
|
|
312
|
+
|
|
313
|
+
/* --- Banners. A banner is a left-rail notice, the shape used for warnings,
|
|
314
|
+
* deprecations and blocking instructions. --- */
|
|
315
|
+
& .dsh-ui-banner {
|
|
316
|
+
display: flex;
|
|
317
|
+
flex-direction: column;
|
|
318
|
+
gap: 8px;
|
|
319
|
+
padding: 10px 14px;
|
|
320
|
+
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
321
|
+
border-left: 3px solid var(--dsw-alias-state-warn-primary);
|
|
322
|
+
border-radius: var(--dsw-radius-md);
|
|
323
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
324
|
+
font-size: 13px;
|
|
325
|
+
line-height: 20px;
|
|
326
|
+
color: var(--dsw-alias-label-secondary);
|
|
327
|
+
}
|
|
328
|
+
& .dsh-ui-banner-title { font-weight: 500; color: var(--dsw-alias-state-warn-primary); }
|
|
329
|
+
& .dsh-ui-banner--info { border-left-color: var(--dsw-alias-state-business-primary); }
|
|
330
|
+
& .dsh-ui-banner--danger { border-left-color: var(--dsw-alias-state-error-primary); }
|
|
331
|
+
& .dsh-ui-banner--success { border-left-color: var(--dsw-alias-state-success-primary); }
|
|
332
|
+
& .dsh-ui-banner p { margin: 0; }
|
|
333
|
+
|
|
334
|
+
/* --- Inline notice: a transient confirmation with no chrome of its own. --- */
|
|
335
|
+
& .dsh-ui-notice { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 18px; color: var(--dsw-alias-state-success-primary); }
|
|
336
|
+
& .dsh-ui-notice--error { color: var(--dsw-alias-state-error-primary); }
|
|
337
|
+
|
|
338
|
+
/* --- Empty and loading states. Both sit in a dashed well so an empty panel
|
|
339
|
+
* reads as "nothing here yet" rather than as a broken layout. --- */
|
|
340
|
+
& .dsh-ui-empty {
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-direction: column;
|
|
343
|
+
align-items: flex-start;
|
|
344
|
+
gap: 6px;
|
|
345
|
+
padding: 20px 16px;
|
|
346
|
+
border: 0.5px dashed var(--dsw-alias-border-l3);
|
|
347
|
+
border-radius: var(--dsw-radius-md);
|
|
348
|
+
font-size: 13px;
|
|
349
|
+
line-height: 20px;
|
|
350
|
+
color: var(--dsw-alias-label-secondary);
|
|
351
|
+
}
|
|
352
|
+
& .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); }
|
|
353
|
+
|
|
354
|
+
/* --- Selectable list. A row is a button-sized target with a hover fill and
|
|
355
|
+
* a selected fill; the name carries the weight, the description does not. --- */
|
|
356
|
+
& .dsh-ui-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
357
|
+
& .dsh-ui-list-scroll { max-height: min(66vh, 760px); overflow-y: auto; overflow-x: hidden; }
|
|
358
|
+
& .dsh-ui-list-row { border-radius: var(--dsw-radius-md); }
|
|
359
|
+
& .dsh-ui-list-row:hover { background: var(--dsw-alias-interactive-bg-hover); }
|
|
360
|
+
& .dsh-ui-list-row[aria-selected='true'], & .dsh-ui-list-row[data-selected] { background: var(--dsw-alias-interactive-bg-active); }
|
|
361
|
+
& .dsh-ui-list-item {
|
|
362
|
+
display: flex;
|
|
363
|
+
flex-direction: column;
|
|
364
|
+
align-items: flex-start;
|
|
365
|
+
gap: 4px;
|
|
366
|
+
width: 100%;
|
|
367
|
+
min-width: 0;
|
|
368
|
+
padding: 10px 12px;
|
|
369
|
+
border: 0;
|
|
370
|
+
border-radius: var(--dsw-radius-md);
|
|
371
|
+
background: none;
|
|
372
|
+
font: inherit;
|
|
373
|
+
color: inherit;
|
|
374
|
+
text-align: start;
|
|
375
|
+
cursor: pointer;
|
|
376
|
+
}
|
|
377
|
+
& .dsh-ui-list-item:disabled { cursor: default; }
|
|
378
|
+
& .dsh-ui-list-item:disabled:hover { background: none; }
|
|
379
|
+
& .dsh-ui-list-name { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); overflow-wrap: anywhere; }
|
|
380
|
+
& .dsh-ui-list-desc { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-tertiary); }
|
|
381
|
+
|
|
382
|
+
/* --- Code and structured output. Wraps rather than scrolls, because a
|
|
383
|
+
* feature panel is read rather than scanned. --- */
|
|
384
|
+
& .dsh-ui-code {
|
|
385
|
+
padding: 10px 12px;
|
|
386
|
+
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
387
|
+
border-radius: var(--dsw-radius-md);
|
|
388
|
+
background: var(--dsw-alias-bg-layer-1);
|
|
389
|
+
font-family: var(--ds-font-family-code, ui-monospace, 'Cascadia Mono', Consolas, monospace);
|
|
390
|
+
font-size: 12px;
|
|
391
|
+
line-height: 20px;
|
|
392
|
+
color: var(--dsw-alias-label-secondary);
|
|
393
|
+
overflow-wrap: anywhere;
|
|
394
|
+
white-space: pre-wrap;
|
|
395
|
+
}
|
|
396
|
+
& .dsh-ui-scroll { max-height: 60vh; overflow: auto; overscroll-behavior: contain; }
|
|
397
|
+
|
|
398
|
+
/* --- A toggle row: label on the left, switch on the trailing edge. This is
|
|
399
|
+
* layout only; the switch itself is the official primitive. --- */
|
|
400
|
+
& .dsh-ui-toggle-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; min-width: 0; padding: 12px 0; }
|
|
401
|
+
& .dsh-ui-toggle-row + .dsh-ui-toggle-row { border-top: 0.5px solid var(--dsw-alias-border-l2); }
|
|
402
|
+
& .dsh-ui-toggle-text { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
|
|
403
|
+
& .dsh-ui-toggle-label { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
404
|
+
|
|
405
|
+
/* --- Floating surface: a panel anchored to a trigger. This is the material
|
|
406
|
+
* a menu card is made of — the translucent fill plus its backdrop blur, with
|
|
407
|
+
* the edge riding inside the prominent elevation. A plugin that hand-rolls this
|
|
408
|
+
* is how tokens like --dsw-specific-menu get invented; use this instead. --- */
|
|
409
|
+
& .dsh-ui-surface {
|
|
410
|
+
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
411
|
+
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
412
|
+
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
413
|
+
position: relative;
|
|
414
|
+
display: flex;
|
|
415
|
+
flex-direction: column;
|
|
416
|
+
border: 0;
|
|
417
|
+
border-radius: var(--dsw-radius-lg);
|
|
418
|
+
background: var(--dsw-menu-surface-fill);
|
|
419
|
+
backdrop-filter: var(--dsw-menu-backdrop-filter);
|
|
420
|
+
box-shadow: var(--dsw-elevation-prominent);
|
|
421
|
+
color: var(--dsw-alias-label-primary);
|
|
422
|
+
font-size: 13px;
|
|
423
|
+
line-height: 20px;
|
|
424
|
+
min-width: 0;
|
|
425
|
+
}
|
|
426
|
+
& .dsh-ui-surface-body { display: flex; flex-direction: column; gap: 8px; padding: 12px 14px 14px; overflow-y: auto; }
|
|
427
|
+
& .dsh-ui-surface-head { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
428
|
+
& .dsh-ui-surface-title { font-size: 14px; line-height: 22px; font-weight: 500; color: var(--dsw-alias-label-primary); }
|
|
429
|
+
|
|
430
|
+
/* --- Native select. The primitives ship no select, so a plugin that offers a
|
|
431
|
+
* closed list of choices keeps the platform control and matches the official
|
|
432
|
+
* settings field geometry exactly (34px, 0.5px l4, radius-md, layer-3). A
|
|
433
|
+
* SegmentedControl is the primitive to reach for when the choices are few
|
|
434
|
+
* enough to show at once. --- */
|
|
435
|
+
& .dsh-ui-select {
|
|
436
|
+
box-sizing: border-box;
|
|
437
|
+
width: 100%;
|
|
438
|
+
min-width: 0;
|
|
439
|
+
height: 34px;
|
|
440
|
+
padding: 0 12px;
|
|
441
|
+
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
442
|
+
border-radius: var(--dsw-radius-md);
|
|
443
|
+
background: var(--dsw-alias-bg-layer-3);
|
|
444
|
+
font: inherit;
|
|
445
|
+
font-size: 13px;
|
|
446
|
+
line-height: 1.5;
|
|
447
|
+
color: var(--dsw-alias-label-primary);
|
|
448
|
+
cursor: pointer;
|
|
449
|
+
}
|
|
450
|
+
& .dsh-ui-select:focus-visible { outline: none; border-color: var(--dsw-alias-state-business-primary); }
|
|
451
|
+
& .dsh-ui-select:disabled { color: var(--dsw-alias-label-tertiary); cursor: default; }
|
|
452
|
+
& .dsh-ui-select[aria-invalid='true'] { border-color: var(--dsw-alias-state-error-primary); }
|
|
453
|
+
|
|
454
|
+
/* Long panels scroll inside themselves instead of growing the host page. */
|
|
455
|
+
& .dsh-ui-panel { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
|
|
456
|
+
& .dsh-ui-panel-wide { max-width: 100%; }
|
|
457
|
+
|
|
458
|
+
@media (prefers-reduced-motion: reduce) {
|
|
459
|
+
& *, & *::before, & *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
|
|
463
|
+
}
|
|
464
|
+
//#endregion
|
|
6
465
|
//#region src/contracts.ts
|
|
466
|
+
const PLUGIN_NAME = "@klarkxy/dsh-git-commit";
|
|
7
467
|
const RPC_CHANNEL = "/dsh-git-commit";
|
|
8
468
|
const GIT_COMMIT_CLIENT_SERVICE = "dshGitCommitClient";
|
|
9
469
|
//#endregion
|
|
@@ -12,6 +472,8 @@ const name = "dsh-git-commit-client";
|
|
|
12
472
|
const inject = [
|
|
13
473
|
"slots",
|
|
14
474
|
"connection",
|
|
475
|
+
"remote",
|
|
476
|
+
"remote.session",
|
|
15
477
|
"locale"
|
|
16
478
|
];
|
|
17
479
|
const NS = "dsh-git-commit.action";
|
|
@@ -22,7 +484,12 @@ const zh = {
|
|
|
22
484
|
files: "个文件",
|
|
23
485
|
model: "模型",
|
|
24
486
|
modelDefault: "默认模型",
|
|
25
|
-
modelCustom: "
|
|
487
|
+
modelCustom: "插件设置",
|
|
488
|
+
filesCount: "({n} 个文件)",
|
|
489
|
+
confirmPrompt: "将把 {n} 个文件的全部修改分次提交到当前分支。",
|
|
490
|
+
confirm: "确认提交",
|
|
491
|
+
cancel: "取消",
|
|
492
|
+
loading: "正在读取仓库状态…",
|
|
26
493
|
running: "正在整理修改并分次提交…",
|
|
27
494
|
done: "已创建提交",
|
|
28
495
|
fallback: "模型不可用,已合并为单个提交",
|
|
@@ -35,6 +502,7 @@ const zh = {
|
|
|
35
502
|
sessionRunning: "会话正在运行,提交已暂停",
|
|
36
503
|
refresh: "刷新",
|
|
37
504
|
failed: "操作失败",
|
|
505
|
+
planUnavailable: "模型没有给出可用的提交方案,已取消本次提交,工作区没有被改动。换一个模型重试,或在插件设置里开启兜底提交。",
|
|
38
506
|
close: "关闭"
|
|
39
507
|
};
|
|
40
508
|
const en = {
|
|
@@ -44,7 +512,12 @@ const en = {
|
|
|
44
512
|
files: "files",
|
|
45
513
|
model: "Model",
|
|
46
514
|
modelDefault: "default model",
|
|
47
|
-
modelCustom: "
|
|
515
|
+
modelCustom: "Plugin settings",
|
|
516
|
+
filesCount: " ({n} files)",
|
|
517
|
+
confirmPrompt: "All changes in {n} files will be committed to the current branch in one or more commits.",
|
|
518
|
+
confirm: "Commit now",
|
|
519
|
+
cancel: "Cancel",
|
|
520
|
+
loading: "Reading repository status…",
|
|
48
521
|
running: "Organizing changes into commits…",
|
|
49
522
|
done: "Commits created",
|
|
50
523
|
fallback: "Model unavailable; used a single commit",
|
|
@@ -57,11 +530,24 @@ const en = {
|
|
|
57
530
|
sessionRunning: "Session is running; commits paused",
|
|
58
531
|
refresh: "Refresh",
|
|
59
532
|
failed: "Operation failed",
|
|
533
|
+
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.",
|
|
60
534
|
close: "Close"
|
|
61
535
|
};
|
|
62
|
-
|
|
536
|
+
/** A failed RPC keeps its code so the panel can explain a rejection in its own words. */
|
|
537
|
+
var RpcError = class extends Error {
|
|
538
|
+
code;
|
|
539
|
+
constructor(code, message) {
|
|
540
|
+
super(message);
|
|
541
|
+
this.name = "RpcError";
|
|
542
|
+
this.code = code;
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
async function rpc(call, endpoint, payload = {}, fallback = "Operation failed") {
|
|
63
546
|
const result = await call(endpoint, payload);
|
|
64
|
-
if (!result || result.ok !== true)
|
|
547
|
+
if (!result || result.ok !== true) {
|
|
548
|
+
const failed = result && "error" in result ? result.error : void 0;
|
|
549
|
+
throw new RpcError(failed?.code ?? "unknown", failed?.message ? failed.message : fallback);
|
|
550
|
+
}
|
|
65
551
|
return result.value;
|
|
66
552
|
}
|
|
67
553
|
function reasonText(t, status) {
|
|
@@ -73,6 +559,19 @@ function reasonText(t, status) {
|
|
|
73
559
|
default: return t("unavailable");
|
|
74
560
|
}
|
|
75
561
|
}
|
|
562
|
+
/** A refusal the plugin can name gets its own sentence; the rest stay verbatim. */
|
|
563
|
+
function errorText(t, error) {
|
|
564
|
+
return error.code === "plan-unavailable" ? t("planUnavailable") : error.message;
|
|
565
|
+
}
|
|
566
|
+
function panelError(reason) {
|
|
567
|
+
return reason instanceof RpcError ? {
|
|
568
|
+
code: reason.code,
|
|
569
|
+
message: reason.message
|
|
570
|
+
} : {
|
|
571
|
+
code: "unknown",
|
|
572
|
+
message: reason instanceof Error ? reason.message : String(reason)
|
|
573
|
+
};
|
|
574
|
+
}
|
|
76
575
|
function GitBranchIcon() {
|
|
77
576
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
78
577
|
width: "14",
|
|
@@ -122,6 +621,7 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
122
621
|
const [running, setRunning] = (0, react.useState)(false);
|
|
123
622
|
const [result, setResult] = (0, react.useState)(null);
|
|
124
623
|
const [error, setError] = (0, react.useState)(null);
|
|
624
|
+
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
125
625
|
const rootRef = (0, react.useRef)(null);
|
|
126
626
|
const triggerRef = (0, react.useRef)(null);
|
|
127
627
|
const panelRef = (0, react.useRef)(null);
|
|
@@ -136,11 +636,16 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
136
636
|
const sessionRunning = useSession((snapshot) => snapshot.running === true);
|
|
137
637
|
const changeOpen = (0, react.useCallback)((next) => {
|
|
138
638
|
setOpen(next);
|
|
639
|
+
setConfirming(false);
|
|
139
640
|
if (next) {
|
|
140
641
|
setResult(null);
|
|
141
642
|
setError(null);
|
|
142
643
|
}
|
|
143
644
|
}, []);
|
|
645
|
+
const positioned = Boolean(position);
|
|
646
|
+
(0, react.useEffect)(() => {
|
|
647
|
+
if (open && positioned) panelRef.current?.focus();
|
|
648
|
+
}, [open, positioned]);
|
|
144
649
|
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, changeOpen, panelRef);
|
|
145
650
|
(0, react.useEffect)(() => {
|
|
146
651
|
generationRef.current += 1;
|
|
@@ -155,17 +660,21 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
155
660
|
setLoading(true);
|
|
156
661
|
setError(null);
|
|
157
662
|
try {
|
|
158
|
-
const next = await rpc(call, "status", { sessionId });
|
|
663
|
+
const next = await rpc(call, "status", { sessionId }, t("failed"));
|
|
159
664
|
if (generationRef.current !== generation) return;
|
|
160
665
|
setStatus(next);
|
|
161
666
|
} catch (reason) {
|
|
162
667
|
if (generationRef.current !== generation) return;
|
|
163
668
|
setStatus(void 0);
|
|
164
|
-
setError(
|
|
669
|
+
setError(panelError(reason));
|
|
165
670
|
} finally {
|
|
166
671
|
if (generationRef.current === generation) setLoading(false);
|
|
167
672
|
}
|
|
168
|
-
}, [
|
|
673
|
+
}, [
|
|
674
|
+
call,
|
|
675
|
+
sessionId,
|
|
676
|
+
t
|
|
677
|
+
]);
|
|
169
678
|
(0, react.useEffect)(() => {
|
|
170
679
|
if (!open) return;
|
|
171
680
|
loadStatus();
|
|
@@ -183,11 +692,12 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
183
692
|
}, [open, changeOpen]);
|
|
184
693
|
const runCommit = (0, react.useCallback)(async () => {
|
|
185
694
|
const generation = generationRef.current;
|
|
695
|
+
setConfirming(false);
|
|
186
696
|
setRunning(true);
|
|
187
697
|
setResult(null);
|
|
188
698
|
setError(null);
|
|
189
699
|
try {
|
|
190
|
-
const value = await rpc(call, "commit", { sessionId });
|
|
700
|
+
const value = await rpc(call, "commit", { sessionId }, t("failed"));
|
|
191
701
|
if (generationRef.current !== generation) return;
|
|
192
702
|
setResult(value);
|
|
193
703
|
const next = await rpc(call, "status", { sessionId }).catch(() => void 0);
|
|
@@ -195,176 +705,423 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
195
705
|
if (next) setStatus(next);
|
|
196
706
|
} catch (reason) {
|
|
197
707
|
if (generationRef.current !== generation) return;
|
|
198
|
-
setError(
|
|
708
|
+
setError(panelError(reason));
|
|
199
709
|
} finally {
|
|
200
710
|
if (generationRef.current === generation) setRunning(false);
|
|
201
711
|
}
|
|
202
|
-
}, [
|
|
712
|
+
}, [
|
|
713
|
+
call,
|
|
714
|
+
sessionId,
|
|
715
|
+
t
|
|
716
|
+
]);
|
|
203
717
|
const available = status?.available === true;
|
|
204
718
|
const workspaceCount = status?.workspace.files ?? 0;
|
|
205
719
|
const canCommit = available && !running && !sessionRunning && workspaceCount > 0;
|
|
206
|
-
const modelLabel = status?.model ? `${status.model.provider}/${status.model.model} · ${status.model.source === "purpose" || status.model.source === "override" ? t("modelCustom") : t("modelDefault")}` : void 0;
|
|
207
720
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
208
721
|
ref: rootRef,
|
|
209
722
|
className: "gcm-root",
|
|
210
|
-
children: [
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
className: "gcm-trigger",
|
|
220
|
-
"aria-label": t("title"),
|
|
221
|
-
"aria-haspopup": "dialog",
|
|
222
|
-
"aria-expanded": open,
|
|
223
|
-
onClick: () => changeOpen(!open),
|
|
224
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitBranchIcon, {}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
225
|
-
className: "gcm-triggerLabel",
|
|
226
|
-
children: t("action")
|
|
227
|
-
})]
|
|
228
|
-
})
|
|
229
|
-
}),
|
|
230
|
-
open && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
231
|
-
ref: panelRef,
|
|
232
|
-
className: "gcm-panel",
|
|
233
|
-
style: position ?? {
|
|
234
|
-
visibility: "hidden",
|
|
235
|
-
left: 0,
|
|
236
|
-
top: 0
|
|
237
|
-
},
|
|
238
|
-
role: "dialog",
|
|
239
|
-
tabIndex: -1,
|
|
723
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
724
|
+
label: t("title"),
|
|
725
|
+
side: "bottom",
|
|
726
|
+
gap: 4,
|
|
727
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
728
|
+
ref: triggerRef,
|
|
729
|
+
variant: "ghost",
|
|
730
|
+
size: "sm",
|
|
731
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitBranchIcon, {}),
|
|
240
732
|
"aria-label": t("title"),
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
733
|
+
"aria-haspopup": "dialog",
|
|
734
|
+
"aria-expanded": open,
|
|
735
|
+
onClick: () => changeOpen(!open),
|
|
736
|
+
children: t("action")
|
|
737
|
+
})
|
|
738
|
+
}), open && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
739
|
+
ref: panelRef,
|
|
740
|
+
className: "gcm-root dsh-ui-surface gcm-panel",
|
|
741
|
+
style: position ?? {
|
|
742
|
+
visibility: "hidden",
|
|
743
|
+
left: 0,
|
|
744
|
+
top: 0
|
|
745
|
+
},
|
|
746
|
+
role: "dialog",
|
|
747
|
+
tabIndex: -1,
|
|
748
|
+
"aria-label": t("title"),
|
|
749
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
750
|
+
className: "dsh-ui-surface-body",
|
|
751
|
+
children: [
|
|
752
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
753
|
+
className: "dsh-ui-surface-head",
|
|
754
|
+
children: [
|
|
755
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
756
|
+
className: "dsh-ui-surface-title",
|
|
757
|
+
children: t("title")
|
|
758
|
+
}),
|
|
759
|
+
status?.branch ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
760
|
+
className: "dsh-ui-hint dsh-ui-truncate",
|
|
761
|
+
children: status.branch
|
|
762
|
+
}) : null,
|
|
763
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "dsh-ui-spacer" }),
|
|
764
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
765
|
+
label: t("refresh"),
|
|
766
|
+
side: "bottom",
|
|
767
|
+
gap: 4,
|
|
768
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
769
|
+
variant: "ghost",
|
|
770
|
+
size: "sm",
|
|
771
|
+
"aria-label": t("refresh"),
|
|
772
|
+
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconRefreshOutlineRegular, { size: 14 }),
|
|
773
|
+
disabled: loading || running,
|
|
774
|
+
onClick: () => void loadStatus()
|
|
775
|
+
})
|
|
776
|
+
})
|
|
777
|
+
]
|
|
778
|
+
}),
|
|
779
|
+
loading && !status && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
780
|
+
className: "dsh-ui-hint",
|
|
781
|
+
role: "status",
|
|
782
|
+
children: t("loading")
|
|
783
|
+
}),
|
|
784
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
785
|
+
className: "dsh-ui-error",
|
|
786
|
+
role: "alert",
|
|
787
|
+
children: errorText(t, error)
|
|
788
|
+
}),
|
|
789
|
+
status && !available && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
790
|
+
className: "dsh-ui-meta",
|
|
791
|
+
role: "status",
|
|
792
|
+
children: reasonText(t, status)
|
|
793
|
+
}),
|
|
794
|
+
available && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
795
|
+
status?.model ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
796
|
+
className: "gcm-model",
|
|
246
797
|
children: [
|
|
247
798
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
248
|
-
className: "
|
|
249
|
-
children: t("
|
|
799
|
+
className: "dsh-ui-hint",
|
|
800
|
+
children: t("model")
|
|
250
801
|
}),
|
|
251
|
-
|
|
252
|
-
className: "
|
|
253
|
-
children:
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
children: "
|
|
802
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
803
|
+
className: "dsh-ui-truncate",
|
|
804
|
+
children: [
|
|
805
|
+
status.model.provider,
|
|
806
|
+
"/",
|
|
807
|
+
status.model.model
|
|
808
|
+
]
|
|
809
|
+
}),
|
|
810
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
811
|
+
tone: status.model.source === "page" ? "info" : "neutral",
|
|
812
|
+
children: status.model.source === "page" ? t("modelCustom") : t("modelDefault")
|
|
262
813
|
})
|
|
263
814
|
]
|
|
815
|
+
}) : null,
|
|
816
|
+
confirming && canCommit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
817
|
+
className: "dsh-ui-stack",
|
|
818
|
+
role: "group",
|
|
819
|
+
"aria-label": t("commitAction"),
|
|
820
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
821
|
+
className: "dsh-ui-hint dsh-ui-wrap",
|
|
822
|
+
children: t("confirmPrompt").replace("{n}", String(workspaceCount))
|
|
823
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
824
|
+
className: "dsh-ui-actions",
|
|
825
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
826
|
+
variant: "ghost",
|
|
827
|
+
size: "md",
|
|
828
|
+
onClick: () => setConfirming(false),
|
|
829
|
+
children: t("cancel")
|
|
830
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
831
|
+
variant: "primary",
|
|
832
|
+
size: "md",
|
|
833
|
+
onClick: () => void runCommit(),
|
|
834
|
+
children: t("confirm")
|
|
835
|
+
})]
|
|
836
|
+
})]
|
|
837
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
838
|
+
variant: "primary",
|
|
839
|
+
size: "md",
|
|
840
|
+
disabled: !canCommit,
|
|
841
|
+
onClick: () => setConfirming(true),
|
|
842
|
+
children: [t("commitAction"), t("filesCount").replace("{n}", String(workspaceCount))]
|
|
264
843
|
}),
|
|
265
|
-
|
|
266
|
-
className: "
|
|
267
|
-
|
|
268
|
-
children: error
|
|
269
|
-
}),
|
|
270
|
-
status && !available && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
271
|
-
className: "gcm-notice",
|
|
272
|
-
role: "status",
|
|
273
|
-
children: reasonText(t, status)
|
|
844
|
+
workspaceCount === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
845
|
+
className: "dsh-ui-hint",
|
|
846
|
+
children: t("noWorkspace")
|
|
274
847
|
}),
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
848
|
+
sessionRunning && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
849
|
+
className: "dsh-ui-hint",
|
|
850
|
+
children: t("sessionRunning")
|
|
851
|
+
})
|
|
852
|
+
] }),
|
|
853
|
+
running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
854
|
+
className: "dsh-ui-hint",
|
|
855
|
+
role: "status",
|
|
856
|
+
children: t("running")
|
|
857
|
+
}),
|
|
858
|
+
result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
859
|
+
className: "dsh-ui-stack",
|
|
860
|
+
children: [
|
|
861
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
862
|
+
className: "dsh-ui-hint dsh-ui-wrap",
|
|
289
863
|
children: [
|
|
290
|
-
t("
|
|
291
|
-
"
|
|
292
|
-
|
|
293
|
-
" ",
|
|
294
|
-
t("files"),
|
|
295
|
-
")"
|
|
864
|
+
t("done"),
|
|
865
|
+
" · ",
|
|
866
|
+
result.branch
|
|
296
867
|
]
|
|
297
868
|
}),
|
|
298
|
-
|
|
299
|
-
className: "gcm-
|
|
300
|
-
children:
|
|
869
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
870
|
+
className: "gcm-commits",
|
|
871
|
+
children: result.commits.map((commit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
872
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: commit.hash }),
|
|
873
|
+
" ",
|
|
874
|
+
commit.message
|
|
875
|
+
] }, commit.hash || commit.message))
|
|
301
876
|
}),
|
|
302
|
-
|
|
303
|
-
className: "
|
|
304
|
-
children: t("
|
|
877
|
+
result.fallback && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
878
|
+
className: "dsh-ui-hint",
|
|
879
|
+
children: t("fallback")
|
|
305
880
|
})
|
|
306
|
-
]
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
881
|
+
]
|
|
882
|
+
})
|
|
883
|
+
]
|
|
884
|
+
})
|
|
885
|
+
}), document.body)]
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
const clientCss = `${officialUiCss("gcm-root")}
|
|
889
|
+
/* Only the floating panel's geometry is this plugin's own: the material, the
|
|
890
|
+
* rounded edge and the scrollbar tokens all come from the shared contract.
|
|
891
|
+
*
|
|
892
|
+
* The root class is spelled out because the panel is portaled and *is* the
|
|
893
|
+
* root: the dsh-ui-surface recipe therefore reaches it as a compound
|
|
894
|
+
* .gcm-root.dsh-ui-surface selector and declares position:relative at the
|
|
895
|
+
* weight a bare .gcm-panel rule also carries. Left unqualified that recipe
|
|
896
|
+
* wins, and the anchored left/top degrade into offsets from the panel's static
|
|
897
|
+
* spot at the end of body — the popover then renders under the page instead
|
|
898
|
+
* of at the trigger. Naming both classes puts this rule at the same weight,
|
|
899
|
+
* later in the sheet, so it wins. */
|
|
900
|
+
.gcm-root.gcm-panel { position: fixed; width: min(360px, calc(100vw - 32px)); max-height: min(560px, calc(100vh - 32px)); overflow: auto; }
|
|
901
|
+
.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); }
|
|
902
|
+
.gcm-commits { margin: 0; padding-inline-start: 18px; display: grid; gap: 2px; font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
|
|
903
|
+
.gcm-commits code { font-size: 12px; background: var(--dsw-alias-bg-layer-2); padding: 0 4px; border-radius: var(--dsw-radius-sm); }
|
|
904
|
+
.gcm-settings { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
|
|
905
|
+
.gcm-settings-status { display: flex; flex-direction: column; gap: 4px; }
|
|
906
|
+
`;
|
|
907
|
+
/** Host UI language, following live switches; falls back to zh when the service lacks a snapshot. */
|
|
908
|
+
function useHostLocale(client) {
|
|
909
|
+
const subscribe = (0, react.useCallback)((listener) => client.locale.subscribe?.(listener) ?? (() => {}), [client]);
|
|
910
|
+
const snapshot = (0, react.useCallback)(() => client.locale.getSnapshot?.().active ?? "zh", [client]);
|
|
911
|
+
return (0, react.useSyncExternalStore)(subscribe, snapshot, snapshot);
|
|
912
|
+
}
|
|
913
|
+
const settingsZh = {
|
|
914
|
+
pageTitle: "Git 提交",
|
|
915
|
+
model: "提交规划模型",
|
|
916
|
+
modelDefault: "默认模型",
|
|
917
|
+
modelEffort: "思考强度",
|
|
918
|
+
modelHint: "留空则使用当前会话模型,再回落到宿主默认对话模型。",
|
|
919
|
+
fallback: "模型不可用时仍然提交",
|
|
920
|
+
fallbackHint: "默认关闭:模型规划失败会取消本次提交,工作区不变。开启后全部改动会合并成一个提交,由插件代写提交信息。",
|
|
921
|
+
loading: "正在读取设置…",
|
|
922
|
+
saved: "已保存。",
|
|
923
|
+
stale: "设置已更新,请刷新后重试。",
|
|
924
|
+
failed: "无法读取提交设置。",
|
|
925
|
+
retry: "重试"
|
|
926
|
+
};
|
|
927
|
+
const settingsEn = {
|
|
928
|
+
pageTitle: "Git Commit",
|
|
929
|
+
model: "Commit planning model",
|
|
930
|
+
modelDefault: "Default model",
|
|
931
|
+
modelEffort: "Reasoning effort",
|
|
932
|
+
modelHint: "Leave empty to use the current session model, then the host default chat model.",
|
|
933
|
+
fallback: "Commit even without a model plan",
|
|
934
|
+
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.",
|
|
935
|
+
loading: "Loading settings…",
|
|
936
|
+
saved: "Saved.",
|
|
937
|
+
stale: "Settings changed; refresh and retry.",
|
|
938
|
+
failed: "Could not load commit settings.",
|
|
939
|
+
retry: "Retry"
|
|
940
|
+
};
|
|
941
|
+
function settingsCopy(locale) {
|
|
942
|
+
return String(locale).startsWith("zh") ? settingsZh : settingsEn;
|
|
943
|
+
}
|
|
944
|
+
/** Plugin-page settings row: pick the model this plugin's planning call uses. */
|
|
945
|
+
function GitCommitSettingsPanel(props) {
|
|
946
|
+
const hostLocale = useHostLocale(props.client);
|
|
947
|
+
const text = settingsCopy(props.locale ?? hostLocale);
|
|
948
|
+
const [settings, setSettings] = (0, react.useState)(void 0);
|
|
949
|
+
const [choices, setChoices] = (0, react.useState)([]);
|
|
950
|
+
const [error, setError] = (0, react.useState)("");
|
|
951
|
+
const [note, setNote] = (0, react.useState)("");
|
|
952
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
953
|
+
const call = (0, react.useCallback)((endpoint, payload = {}) => props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload), [props.client]);
|
|
954
|
+
(0, react.useEffect)(() => {
|
|
955
|
+
let live = true;
|
|
956
|
+
rpc(call, "settings", {}, text.failed).then((next) => {
|
|
957
|
+
if (live) {
|
|
958
|
+
setSettings(next);
|
|
959
|
+
setError("");
|
|
960
|
+
}
|
|
961
|
+
}).catch(() => {
|
|
962
|
+
if (live) setError(text.failed);
|
|
963
|
+
});
|
|
964
|
+
return () => {
|
|
965
|
+
live = false;
|
|
966
|
+
};
|
|
967
|
+
}, [call, text.failed]);
|
|
968
|
+
const boundProvider = settings?.model.provider ?? "";
|
|
969
|
+
const boundModel = settings?.model.model ?? "";
|
|
970
|
+
(0, react.useEffect)(() => {
|
|
971
|
+
let live = true;
|
|
972
|
+
const bound = boundProvider && boundModel ? {
|
|
973
|
+
provider: boundProvider,
|
|
974
|
+
model: boundModel
|
|
975
|
+
} : void 0;
|
|
976
|
+
props.client.remote?.session?.modelCatalog?.()?.then((value) => {
|
|
977
|
+
if (live) setChoices(parseModelMenuChoices(value, bound));
|
|
978
|
+
}).catch(() => {
|
|
979
|
+
if (live) setChoices([]);
|
|
980
|
+
});
|
|
981
|
+
return () => {
|
|
982
|
+
live = false;
|
|
983
|
+
};
|
|
984
|
+
}, [
|
|
985
|
+
props.client,
|
|
986
|
+
boundProvider,
|
|
987
|
+
boundModel
|
|
988
|
+
]);
|
|
989
|
+
const save = (0, react.useCallback)((patch) => {
|
|
990
|
+
if (!settings) return;
|
|
991
|
+
setBusy(true);
|
|
992
|
+
setNote("");
|
|
993
|
+
setError("");
|
|
994
|
+
rpc(call, "settings.update", {
|
|
995
|
+
settings: {
|
|
996
|
+
model: patch.model ?? settings.model,
|
|
997
|
+
allowFallback: patch.allowFallback ?? settings.allowFallback
|
|
998
|
+
},
|
|
999
|
+
expectedRevision: settings.revision
|
|
1000
|
+
}, text.stale).then((next) => {
|
|
1001
|
+
setSettings(next);
|
|
1002
|
+
setNote(text.saved);
|
|
1003
|
+
setBusy(false);
|
|
1004
|
+
}).catch(() => {
|
|
1005
|
+
setError(text.stale);
|
|
1006
|
+
setBusy(false);
|
|
1007
|
+
});
|
|
1008
|
+
}, [
|
|
1009
|
+
call,
|
|
1010
|
+
settings,
|
|
1011
|
+
text.saved,
|
|
1012
|
+
text.stale
|
|
1013
|
+
]);
|
|
1014
|
+
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
1015
|
+
className: "gcm-root gcm-settings",
|
|
1016
|
+
children: error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1017
|
+
role: "alert",
|
|
1018
|
+
className: "dsh-ui-error",
|
|
1019
|
+
children: error
|
|
1020
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1021
|
+
role: "status",
|
|
1022
|
+
className: "dsh-ui-hint",
|
|
1023
|
+
children: text.loading
|
|
1024
|
+
})
|
|
1025
|
+
});
|
|
1026
|
+
const current = settings.model;
|
|
1027
|
+
const selected = choices.find((item) => item.provider === current.provider && item.model === current.model);
|
|
1028
|
+
const efforts = modelMenuEffortOptions(selected, current.reasoningEffort);
|
|
1029
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1030
|
+
className: "gcm-root gcm-settings",
|
|
1031
|
+
"data-testid": "git-commit-settings",
|
|
1032
|
+
children: [
|
|
1033
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1034
|
+
className: "gcm-settings-status",
|
|
1035
|
+
children: [error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1036
|
+
role: "alert",
|
|
1037
|
+
className: "dsh-ui-error",
|
|
1038
|
+
children: error
|
|
1039
|
+
}), note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1040
|
+
role: "status",
|
|
1041
|
+
className: "dsh-ui-notice",
|
|
1042
|
+
children: note
|
|
1043
|
+
})]
|
|
1044
|
+
}),
|
|
1045
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1046
|
+
className: "dsh-ui-field",
|
|
1047
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1048
|
+
className: "dsh-ui-label",
|
|
1049
|
+
children: text.model
|
|
1050
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1051
|
+
className: "dsh-ui-select",
|
|
1052
|
+
value: modelMenuChoiceKey(current.provider, current.model),
|
|
1053
|
+
disabled: busy,
|
|
1054
|
+
onChange: (event) => {
|
|
1055
|
+
const key = event.target.value;
|
|
1056
|
+
if (!key) {
|
|
1057
|
+
save({ model: {
|
|
1058
|
+
provider: "",
|
|
1059
|
+
model: ""
|
|
1060
|
+
} });
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
const parsed = parseModelMenuChoiceKey(key);
|
|
1064
|
+
if (parsed) save({ model: parsed });
|
|
1065
|
+
},
|
|
1066
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1067
|
+
value: "",
|
|
1068
|
+
children: text.modelDefault
|
|
1069
|
+
}), choices.map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1070
|
+
value: modelMenuChoiceKey(choice.provider, choice.model),
|
|
1071
|
+
children: choice.label
|
|
1072
|
+
}, modelMenuChoiceKey(choice.provider, choice.model)))]
|
|
1073
|
+
})]
|
|
1074
|
+
}),
|
|
1075
|
+
selected && efforts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1076
|
+
className: "dsh-ui-field",
|
|
1077
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1078
|
+
className: "dsh-ui-label",
|
|
1079
|
+
children: text.modelEffort
|
|
1080
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
1081
|
+
className: "dsh-ui-select",
|
|
1082
|
+
value: current.reasoningEffort ?? "",
|
|
1083
|
+
disabled: busy,
|
|
1084
|
+
onChange: (event) => save({ model: {
|
|
1085
|
+
...current,
|
|
1086
|
+
reasoningEffort: event.target.value || void 0
|
|
1087
|
+
} }),
|
|
1088
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1089
|
+
value: "",
|
|
1090
|
+
children: text.modelDefault
|
|
1091
|
+
}), efforts.map((effort) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1092
|
+
value: effort.id,
|
|
1093
|
+
children: effort.name
|
|
1094
|
+
}, effort.id))]
|
|
1095
|
+
})]
|
|
1096
|
+
}),
|
|
1097
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1098
|
+
className: "dsh-ui-meta",
|
|
1099
|
+
children: text.modelHint
|
|
1100
|
+
}),
|
|
1101
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
|
|
1102
|
+
className: "gcm-root dsh-ui-card",
|
|
1103
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
1104
|
+
className: "dsh-ui-toggle-row",
|
|
1105
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1106
|
+
className: "dsh-ui-toggle-text",
|
|
1107
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1108
|
+
className: "dsh-ui-toggle-label",
|
|
1109
|
+
children: text.fallback
|
|
1110
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1111
|
+
className: "dsh-ui-hint",
|
|
1112
|
+
children: text.fallbackHint
|
|
1113
|
+
})]
|
|
1114
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
1115
|
+
checked: settings.allowFallback,
|
|
1116
|
+
label: text.fallback,
|
|
1117
|
+
disabled: busy,
|
|
1118
|
+
onChange: (next) => save({ allowFallback: next })
|
|
1119
|
+
})]
|
|
338
1120
|
})
|
|
339
|
-
})
|
|
1121
|
+
})
|
|
340
1122
|
]
|
|
341
1123
|
});
|
|
342
1124
|
}
|
|
343
|
-
const css = `
|
|
344
|
-
.gcm-root{position:relative;display:inline-flex}
|
|
345
|
-
.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}
|
|
346
|
-
.gcm-trigger:hover,.gcm-trigger:focus-visible{color:var(--dsw-alias-label-primary)}
|
|
347
|
-
.gcm-triggerLabel{white-space:nowrap}
|
|
348
|
-
.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)}
|
|
349
|
-
.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}
|
|
350
|
-
.gcm-body{display:grid;gap:8px;padding:12px 14px 14px;overflow-y:auto}
|
|
351
|
-
.gcm-header{display:flex;align-items:center;gap:8px}
|
|
352
|
-
.gcm-title{font-weight:600}
|
|
353
|
-
.gcm-branch{color:var(--dsw-alias-label-caption);overflow-wrap:anywhere;font-size:12px}
|
|
354
|
-
.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}
|
|
355
|
-
.gcm-iconButton:hover:not(:disabled){color:var(--dsw-alias-label-primary)}
|
|
356
|
-
.gcm-iconButton:disabled{opacity:.45;cursor:not-allowed}
|
|
357
|
-
.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}
|
|
358
|
-
.gcm-action:hover:not(:disabled){background:var(--dsw-alias-bg-layer-2)}
|
|
359
|
-
.gcm-action:disabled{opacity:.45;cursor:not-allowed}
|
|
360
|
-
.gcm-primary{--dsw-elevation-stroke-color:var(--dsw-alias-brand-primary)}
|
|
361
|
-
.gcm-meta{margin:0;font-size:12px;color:var(--dsw-alias-label-caption);overflow-wrap:anywhere}
|
|
362
|
-
.gcm-notice{margin:0;font-size:12px;color:var(--dsw-alias-label-secondary)}
|
|
363
|
-
.gcm-error{margin:0;font-size:12px;color:var(--dsw-alias-state-error-primary)}
|
|
364
|
-
.gcm-result{display:grid;gap:4px}
|
|
365
|
-
.gcm-commits{margin:0;padding-left:18px;display:grid;gap:2px;font-size:12px;overflow-wrap:anywhere}
|
|
366
|
-
.gcm-commits code{font-size:12px;background:var(--dsw-alias-bg-layer-2);padding:0 4px;border-radius:4px}
|
|
367
|
-
`;
|
|
368
1125
|
function apply(ctx) {
|
|
369
1126
|
const client = ctx;
|
|
370
1127
|
ctx.provide(GIT_COMMIT_CLIENT_SERVICE, { active: true });
|
|
@@ -372,6 +1129,18 @@ function apply(ctx) {
|
|
|
372
1129
|
zh: { ...zh },
|
|
373
1130
|
en: { ...en }
|
|
374
1131
|
}), "git-commit.locale");
|
|
1132
|
+
ctx.effect(() => {
|
|
1133
|
+
if (typeof document === "undefined") return () => {};
|
|
1134
|
+
const style = document.createElement("style");
|
|
1135
|
+
style.setAttribute("data-plugin", name);
|
|
1136
|
+
style.textContent = clientCss;
|
|
1137
|
+
document.head.appendChild(style);
|
|
1138
|
+
return () => style.remove();
|
|
1139
|
+
}, "git-commit.styles");
|
|
1140
|
+
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
1141
|
+
name: "plugins.bundle.config",
|
|
1142
|
+
key: PLUGIN_NAME
|
|
1143
|
+
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitCommitSettingsPanel, { client }))), "git-commit.settings");
|
|
375
1144
|
ctx.effect(() => client.slots.inject("conversation.session.header.actions", () => client.slots.register({
|
|
376
1145
|
name: "conversation.session.header.actions",
|
|
377
1146
|
id: "git-commit",
|
|
@@ -382,7 +1151,9 @@ function apply(ctx) {
|
|
|
382
1151
|
}
|
|
383
1152
|
//#endregion
|
|
384
1153
|
exports.GitCommitAction = GitCommitAction;
|
|
1154
|
+
exports.GitCommitSettingsPanel = GitCommitSettingsPanel;
|
|
385
1155
|
exports.apply = apply;
|
|
1156
|
+
exports.clientCss = clientCss;
|
|
386
1157
|
exports.inject = inject;
|
|
387
1158
|
exports.name = name;
|
|
388
1159
|
|