@klarkxy/dsh-git-commit 0.1.3
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/LICENSE +42 -0
- package/README.md +38 -0
- package/cordis.patch.yml +5 -0
- package/docs/README.zh-CN.md +30 -0
- package/icon.svg +16 -0
- package/lib/client.inner.cjs +389 -0
- package/lib/client.inner.cjs.map +1 -0
- package/lib/client.js +559 -0
- package/lib/contracts.d.ts +2 -0
- package/lib/contracts.js +24 -0
- package/lib/contracts.js.map +1 -0
- package/lib/contracts2.d.ts +54 -0
- package/lib/index.d.ts +101 -0
- package/lib/index.js +846 -0
- package/lib/index.js.map +1 -0
- package/package.json +108 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
function __createDshNodeShims() {
|
|
2
|
+
class EventEmitter {
|
|
3
|
+
constructor() {
|
|
4
|
+
this._e = Object.create(null)
|
|
5
|
+
}
|
|
6
|
+
on(type, fn) {
|
|
7
|
+
(this._e[type] ||= []).push(fn)
|
|
8
|
+
return this
|
|
9
|
+
}
|
|
10
|
+
addListener(type, fn) {
|
|
11
|
+
return this.on(type, fn)
|
|
12
|
+
}
|
|
13
|
+
once(type, fn) {
|
|
14
|
+
const wrap = (...args) => {
|
|
15
|
+
this.off(type, wrap)
|
|
16
|
+
fn(...args)
|
|
17
|
+
}
|
|
18
|
+
return this.on(type, wrap)
|
|
19
|
+
}
|
|
20
|
+
off(type, fn) {
|
|
21
|
+
const list = this._e[type]
|
|
22
|
+
if (list) this._e[type] = list.filter((item) => item !== fn)
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
removeListener(type, fn) {
|
|
26
|
+
return this.off(type, fn)
|
|
27
|
+
}
|
|
28
|
+
emit(type, ...args) {
|
|
29
|
+
const list = this._e[type]
|
|
30
|
+
if (!list) return false
|
|
31
|
+
for (const fn of list.slice()) fn(...args)
|
|
32
|
+
return true
|
|
33
|
+
}
|
|
34
|
+
removeAllListeners(type) {
|
|
35
|
+
if (type) delete this._e[type]
|
|
36
|
+
else this._e = Object.create(null)
|
|
37
|
+
return this
|
|
38
|
+
}
|
|
39
|
+
listeners(type) {
|
|
40
|
+
return (this._e[type] || []).slice()
|
|
41
|
+
}
|
|
42
|
+
setMaxListeners() {
|
|
43
|
+
return this
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class Buffer extends Uint8Array {
|
|
48
|
+
static from(value, encodingOrOffset, length) {
|
|
49
|
+
if (typeof value === 'string') return new Buffer(new TextEncoder().encode(value))
|
|
50
|
+
if (value instanceof ArrayBuffer) return new Buffer(new Uint8Array(value, encodingOrOffset, length))
|
|
51
|
+
if (ArrayBuffer.isView(value)) return new Buffer(new Uint8Array(value.buffer, value.byteOffset, value.byteLength))
|
|
52
|
+
if (Array.isArray(value) || value && typeof value.length === 'number') return new Buffer(Uint8Array.from(value))
|
|
53
|
+
return new Buffer(0)
|
|
54
|
+
}
|
|
55
|
+
static alloc(size, fill) {
|
|
56
|
+
const buf = new Buffer(size)
|
|
57
|
+
if (fill !== undefined && fill !== 0) {
|
|
58
|
+
if (typeof fill === 'string') {
|
|
59
|
+
const bytes = new TextEncoder().encode(fill)
|
|
60
|
+
for (let i = 0; i < size; i += 1) buf[i] = bytes[i % bytes.length]
|
|
61
|
+
} else buf.fill(fill)
|
|
62
|
+
}
|
|
63
|
+
return buf
|
|
64
|
+
}
|
|
65
|
+
static allocUnsafe(size) {
|
|
66
|
+
return new Buffer(size)
|
|
67
|
+
}
|
|
68
|
+
static allocUnsafeSlow(size) {
|
|
69
|
+
return new Buffer(size)
|
|
70
|
+
}
|
|
71
|
+
static concat(list, totalLength) {
|
|
72
|
+
const length = totalLength ?? list.reduce((sum, item) => sum + item.length, 0)
|
|
73
|
+
const out = new Buffer(length)
|
|
74
|
+
let offset = 0
|
|
75
|
+
for (const item of list) {
|
|
76
|
+
out.set(item, offset)
|
|
77
|
+
offset += item.length
|
|
78
|
+
}
|
|
79
|
+
return out
|
|
80
|
+
}
|
|
81
|
+
static isBuffer(value) {
|
|
82
|
+
return value instanceof Buffer
|
|
83
|
+
}
|
|
84
|
+
static byteLength(value) {
|
|
85
|
+
if (typeof value === 'string') return new TextEncoder().encode(value).length
|
|
86
|
+
return value?.byteLength ?? value?.length ?? 0
|
|
87
|
+
}
|
|
88
|
+
static isEncoding(encoding) {
|
|
89
|
+
return /^(utf8|utf-8|ascii|latin1|binary|hex|base64|base64url)$/i.test(String(encoding || ''))
|
|
90
|
+
}
|
|
91
|
+
toString(encoding) {
|
|
92
|
+
if (encoding === 'hex') return [...this].map((byte) => byte.toString(16).padStart(2, '0')).join('')
|
|
93
|
+
if (encoding === 'base64') {
|
|
94
|
+
let binary = ''
|
|
95
|
+
this.forEach((byte) => {
|
|
96
|
+
binary += String.fromCharCode(byte)
|
|
97
|
+
})
|
|
98
|
+
return btoa(binary)
|
|
99
|
+
}
|
|
100
|
+
return new TextDecoder().decode(this)
|
|
101
|
+
}
|
|
102
|
+
slice(start, end) {
|
|
103
|
+
return Buffer.from(super.subarray(start, end))
|
|
104
|
+
}
|
|
105
|
+
copy(target, targetStart = 0, start = 0, end = this.length) {
|
|
106
|
+
const sliced = this.subarray(start, end)
|
|
107
|
+
target.set(sliced, targetStart)
|
|
108
|
+
return sliced.length
|
|
109
|
+
}
|
|
110
|
+
write(string, offset = 0) {
|
|
111
|
+
const bytes = new TextEncoder().encode(string)
|
|
112
|
+
const written = Math.min(bytes.length, this.length - offset)
|
|
113
|
+
this.set(bytes.subarray(0, written), offset)
|
|
114
|
+
return written
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function inherits(ctor, superCtor) {
|
|
119
|
+
ctor.super_ = superCtor
|
|
120
|
+
ctor.prototype = Object.create(superCtor.prototype, {
|
|
121
|
+
constructor: { value: ctor, writable: true, configurable: true },
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!globalThis.Buffer) globalThis.Buffer = Buffer
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
buffer: { Buffer, SlowBuffer: Buffer, INSPECT_MAX_BYTES: 50, kMaxLength: 0x7fffffff },
|
|
129
|
+
stream: {
|
|
130
|
+
Stream: EventEmitter,
|
|
131
|
+
Readable: EventEmitter,
|
|
132
|
+
Writable: EventEmitter,
|
|
133
|
+
Duplex: EventEmitter,
|
|
134
|
+
Transform: EventEmitter,
|
|
135
|
+
PassThrough: EventEmitter,
|
|
136
|
+
},
|
|
137
|
+
events: { EventEmitter },
|
|
138
|
+
util: {
|
|
139
|
+
deprecate(fn) {
|
|
140
|
+
return fn
|
|
141
|
+
},
|
|
142
|
+
inherits,
|
|
143
|
+
inspect() {
|
|
144
|
+
return ''
|
|
145
|
+
},
|
|
146
|
+
format(value) {
|
|
147
|
+
return String(value)
|
|
148
|
+
},
|
|
149
|
+
types: { isBuffer: Buffer.isBuffer },
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
window.__ModuleLoader__.load({
|
|
155
|
+
id: "@klarkxy/dsh-git-commit",
|
|
156
|
+
factory: (dshRequire) => {
|
|
157
|
+
var __nodeShims;
|
|
158
|
+
var process = globalThis.process || { env: {}, nextTick: function (fn) { var args = [].slice.call(arguments, 1); queueMicrotask(function () { fn.apply(null, args); }); } };
|
|
159
|
+
var require = function (id) {
|
|
160
|
+
if (id === 'buffer' || id === 'stream' || id === 'util' || id === 'events') {
|
|
161
|
+
if (!__nodeShims) __nodeShims = __createDshNodeShims();
|
|
162
|
+
return __nodeShims[id];
|
|
163
|
+
}
|
|
164
|
+
return dshRequire(id);
|
|
165
|
+
};
|
|
166
|
+
var module = { exports: {} };
|
|
167
|
+
var exports = module.exports;
|
|
168
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
169
|
+
let react = require("react");
|
|
170
|
+
let react_dom = require("react-dom");
|
|
171
|
+
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
172
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
173
|
+
//#region src/contracts.ts
|
|
174
|
+
const RPC_CHANNEL = "/dsh-git-commit";
|
|
175
|
+
const GIT_COMMIT_CLIENT_SERVICE = "dshGitCommitClient";
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/client.tsx
|
|
178
|
+
const name = "dsh-git-commit-client";
|
|
179
|
+
const inject = [
|
|
180
|
+
"slots",
|
|
181
|
+
"connection",
|
|
182
|
+
"locale"
|
|
183
|
+
];
|
|
184
|
+
const NS = "dsh-git-commit.action";
|
|
185
|
+
const zh = {
|
|
186
|
+
action: "提交",
|
|
187
|
+
title: "提交到 Git",
|
|
188
|
+
commitAction: "分次提交全部工作区修改",
|
|
189
|
+
files: "个文件",
|
|
190
|
+
model: "模型",
|
|
191
|
+
modelDefault: "默认模型",
|
|
192
|
+
modelCustom: "AI 服务配置",
|
|
193
|
+
running: "正在整理修改并分次提交…",
|
|
194
|
+
done: "已创建提交",
|
|
195
|
+
fallback: "模型不可用,已合并为单个提交",
|
|
196
|
+
noWorkspace: "工作区没有修改",
|
|
197
|
+
unavailable: "不可用",
|
|
198
|
+
reasonNoRepo: "当前目录不在 Git 仓库中",
|
|
199
|
+
reasonNoGit: "未找到 git 命令",
|
|
200
|
+
reasonNoCwd: "会话没有工作目录",
|
|
201
|
+
reasonNoSession: "会话不在线",
|
|
202
|
+
sessionRunning: "会话正在运行,提交已暂停",
|
|
203
|
+
refresh: "刷新",
|
|
204
|
+
failed: "操作失败",
|
|
205
|
+
close: "关闭"
|
|
206
|
+
};
|
|
207
|
+
const en = {
|
|
208
|
+
action: "Commit",
|
|
209
|
+
title: "Commit to Git",
|
|
210
|
+
commitAction: "Commit all workspace changes",
|
|
211
|
+
files: "files",
|
|
212
|
+
model: "Model",
|
|
213
|
+
modelDefault: "default model",
|
|
214
|
+
modelCustom: "AI services route",
|
|
215
|
+
running: "Organizing changes into commits…",
|
|
216
|
+
done: "Commits created",
|
|
217
|
+
fallback: "Model unavailable; used a single commit",
|
|
218
|
+
noWorkspace: "No workspace changes",
|
|
219
|
+
unavailable: "Unavailable",
|
|
220
|
+
reasonNoRepo: "Current directory is not inside a Git repository",
|
|
221
|
+
reasonNoGit: "git executable not found",
|
|
222
|
+
reasonNoCwd: "Session has no working directory",
|
|
223
|
+
reasonNoSession: "Session is not live",
|
|
224
|
+
sessionRunning: "Session is running; commits paused",
|
|
225
|
+
refresh: "Refresh",
|
|
226
|
+
failed: "Operation failed",
|
|
227
|
+
close: "Close"
|
|
228
|
+
};
|
|
229
|
+
async function rpc(call, endpoint, payload) {
|
|
230
|
+
const result = await call(endpoint, payload);
|
|
231
|
+
if (!result || result.ok !== true) throw new Error(result && "error" in result ? result.error.message : "request failed");
|
|
232
|
+
return result.value;
|
|
233
|
+
}
|
|
234
|
+
function reasonText(t, status) {
|
|
235
|
+
switch (status.reason) {
|
|
236
|
+
case "not-a-repo": return t("reasonNoRepo");
|
|
237
|
+
case "no-git": return t("reasonNoGit");
|
|
238
|
+
case "no-cwd": return t("reasonNoCwd");
|
|
239
|
+
case "no-session": return t("reasonNoSession");
|
|
240
|
+
default: return t("unavailable");
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
function GitBranchIcon() {
|
|
244
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
245
|
+
width: "14",
|
|
246
|
+
height: "14",
|
|
247
|
+
viewBox: "0 0 16 16",
|
|
248
|
+
fill: "none",
|
|
249
|
+
"aria-hidden": "true",
|
|
250
|
+
children: [
|
|
251
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
252
|
+
cx: "4.5",
|
|
253
|
+
cy: "3.5",
|
|
254
|
+
r: "1.6",
|
|
255
|
+
stroke: "currentColor",
|
|
256
|
+
strokeWidth: "1.2"
|
|
257
|
+
}),
|
|
258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
259
|
+
cx: "4.5",
|
|
260
|
+
cy: "12.5",
|
|
261
|
+
r: "1.6",
|
|
262
|
+
stroke: "currentColor",
|
|
263
|
+
strokeWidth: "1.2"
|
|
264
|
+
}),
|
|
265
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
266
|
+
cx: "11.5",
|
|
267
|
+
cy: "6.5",
|
|
268
|
+
r: "1.6",
|
|
269
|
+
stroke: "currentColor",
|
|
270
|
+
strokeWidth: "1.2"
|
|
271
|
+
}),
|
|
272
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
273
|
+
d: "M4.5 5.1v5.8",
|
|
274
|
+
stroke: "currentColor",
|
|
275
|
+
strokeWidth: "1.2"
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
278
|
+
d: "M11.5 8.1c0 2-1.6 2.9-3.4 3",
|
|
279
|
+
stroke: "currentColor",
|
|
280
|
+
strokeWidth: "1.2"
|
|
281
|
+
})
|
|
282
|
+
]
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
286
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
287
|
+
const [status, setStatus] = (0, react.useState)();
|
|
288
|
+
const [loading, setLoading] = (0, react.useState)(false);
|
|
289
|
+
const [running, setRunning] = (0, react.useState)(false);
|
|
290
|
+
const [result, setResult] = (0, react.useState)(null);
|
|
291
|
+
const [error, setError] = (0, react.useState)(null);
|
|
292
|
+
const rootRef = (0, react.useRef)(null);
|
|
293
|
+
const triggerRef = (0, react.useRef)(null);
|
|
294
|
+
const panelRef = (0, react.useRef)(null);
|
|
295
|
+
const generationRef = (0, react.useRef)(0);
|
|
296
|
+
const position = (0, _deepseek_ai_dsh_client_ui_primitives.useAnchoredPosition)({
|
|
297
|
+
open,
|
|
298
|
+
anchorRef: triggerRef,
|
|
299
|
+
panelRef,
|
|
300
|
+
gap: 5,
|
|
301
|
+
margin: 16
|
|
302
|
+
});
|
|
303
|
+
const sessionRunning = useSession((snapshot) => snapshot.running === true);
|
|
304
|
+
const changeOpen = (0, react.useCallback)((next) => {
|
|
305
|
+
setOpen(next);
|
|
306
|
+
if (next) {
|
|
307
|
+
setResult(null);
|
|
308
|
+
setError(null);
|
|
309
|
+
}
|
|
310
|
+
}, []);
|
|
311
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, changeOpen, panelRef);
|
|
312
|
+
(0, react.useEffect)(() => {
|
|
313
|
+
generationRef.current += 1;
|
|
314
|
+
setOpen(false);
|
|
315
|
+
setStatus(void 0);
|
|
316
|
+
setResult(null);
|
|
317
|
+
setError(null);
|
|
318
|
+
setRunning(false);
|
|
319
|
+
}, [sessionId]);
|
|
320
|
+
const loadStatus = (0, react.useCallback)(async () => {
|
|
321
|
+
const generation = generationRef.current;
|
|
322
|
+
setLoading(true);
|
|
323
|
+
setError(null);
|
|
324
|
+
try {
|
|
325
|
+
const next = await rpc(call, "status", { sessionId });
|
|
326
|
+
if (generationRef.current !== generation) return;
|
|
327
|
+
setStatus(next);
|
|
328
|
+
} catch (reason) {
|
|
329
|
+
if (generationRef.current !== generation) return;
|
|
330
|
+
setStatus(void 0);
|
|
331
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
332
|
+
} finally {
|
|
333
|
+
if (generationRef.current === generation) setLoading(false);
|
|
334
|
+
}
|
|
335
|
+
}, [call, sessionId]);
|
|
336
|
+
(0, react.useEffect)(() => {
|
|
337
|
+
if (!open) return;
|
|
338
|
+
loadStatus();
|
|
339
|
+
}, [open, loadStatus]);
|
|
340
|
+
(0, react.useEffect)(() => {
|
|
341
|
+
if (!open) return;
|
|
342
|
+
const dismiss = (event) => {
|
|
343
|
+
if (event.key !== "Escape") return;
|
|
344
|
+
event.preventDefault();
|
|
345
|
+
changeOpen(false);
|
|
346
|
+
triggerRef.current?.focus();
|
|
347
|
+
};
|
|
348
|
+
document.addEventListener("keydown", dismiss);
|
|
349
|
+
return () => document.removeEventListener("keydown", dismiss);
|
|
350
|
+
}, [open, changeOpen]);
|
|
351
|
+
const runCommit = (0, react.useCallback)(async () => {
|
|
352
|
+
const generation = generationRef.current;
|
|
353
|
+
setRunning(true);
|
|
354
|
+
setResult(null);
|
|
355
|
+
setError(null);
|
|
356
|
+
try {
|
|
357
|
+
const value = await rpc(call, "commit", { sessionId });
|
|
358
|
+
if (generationRef.current !== generation) return;
|
|
359
|
+
setResult(value);
|
|
360
|
+
const next = await rpc(call, "status", { sessionId }).catch(() => void 0);
|
|
361
|
+
if (generationRef.current !== generation) return;
|
|
362
|
+
if (next) setStatus(next);
|
|
363
|
+
} catch (reason) {
|
|
364
|
+
if (generationRef.current !== generation) return;
|
|
365
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
366
|
+
} finally {
|
|
367
|
+
if (generationRef.current === generation) setRunning(false);
|
|
368
|
+
}
|
|
369
|
+
}, [call, sessionId]);
|
|
370
|
+
const available = status?.available === true;
|
|
371
|
+
const workspaceCount = status?.workspace.files ?? 0;
|
|
372
|
+
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
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
375
|
+
ref: rootRef,
|
|
376
|
+
className: "gcm-root",
|
|
377
|
+
children: [
|
|
378
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: css }),
|
|
379
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
|
|
380
|
+
label: t("title"),
|
|
381
|
+
side: "bottom",
|
|
382
|
+
gap: 4,
|
|
383
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
384
|
+
type: "button",
|
|
385
|
+
ref: triggerRef,
|
|
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,
|
|
407
|
+
"aria-label": t("title"),
|
|
408
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
409
|
+
className: "gcm-body",
|
|
410
|
+
children: [
|
|
411
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
412
|
+
className: "gcm-header",
|
|
413
|
+
children: [
|
|
414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
415
|
+
className: "gcm-title",
|
|
416
|
+
children: t("title")
|
|
417
|
+
}),
|
|
418
|
+
status?.branch ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
419
|
+
className: "gcm-branch",
|
|
420
|
+
children: status.branch
|
|
421
|
+
}) : null,
|
|
422
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
423
|
+
type: "button",
|
|
424
|
+
className: "gcm-iconButton",
|
|
425
|
+
"aria-label": t("refresh"),
|
|
426
|
+
disabled: loading || running,
|
|
427
|
+
onClick: () => void loadStatus(),
|
|
428
|
+
children: "⟳"
|
|
429
|
+
})
|
|
430
|
+
]
|
|
431
|
+
}),
|
|
432
|
+
error !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
433
|
+
className: "gcm-error",
|
|
434
|
+
role: "alert",
|
|
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)
|
|
441
|
+
}),
|
|
442
|
+
available && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
443
|
+
modelLabel ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
444
|
+
className: "gcm-meta",
|
|
445
|
+
children: [
|
|
446
|
+
t("model"),
|
|
447
|
+
": ",
|
|
448
|
+
modelLabel
|
|
449
|
+
]
|
|
450
|
+
}) : null,
|
|
451
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
452
|
+
type: "button",
|
|
453
|
+
className: "gcm-action gcm-primary",
|
|
454
|
+
disabled: !canCommit,
|
|
455
|
+
onClick: () => void runCommit(),
|
|
456
|
+
children: [
|
|
457
|
+
t("commitAction"),
|
|
458
|
+
"(",
|
|
459
|
+
workspaceCount,
|
|
460
|
+
" ",
|
|
461
|
+
t("files"),
|
|
462
|
+
")"
|
|
463
|
+
]
|
|
464
|
+
}),
|
|
465
|
+
workspaceCount === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
466
|
+
className: "gcm-notice",
|
|
467
|
+
children: t("noWorkspace")
|
|
468
|
+
}),
|
|
469
|
+
sessionRunning && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
470
|
+
className: "gcm-notice",
|
|
471
|
+
children: t("sessionRunning")
|
|
472
|
+
})
|
|
473
|
+
] }),
|
|
474
|
+
running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
475
|
+
className: "gcm-notice",
|
|
476
|
+
role: "status",
|
|
477
|
+
children: t("running")
|
|
478
|
+
}),
|
|
479
|
+
result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
480
|
+
className: "gcm-result",
|
|
481
|
+
children: [
|
|
482
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
483
|
+
className: "gcm-meta",
|
|
484
|
+
children: [
|
|
485
|
+
t("done"),
|
|
486
|
+
" · ",
|
|
487
|
+
result.branch
|
|
488
|
+
]
|
|
489
|
+
}),
|
|
490
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
491
|
+
className: "gcm-commits",
|
|
492
|
+
children: result.commits.map((commit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
493
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: commit.hash }),
|
|
494
|
+
" ",
|
|
495
|
+
commit.message
|
|
496
|
+
] }, commit.hash || commit.message))
|
|
497
|
+
}),
|
|
498
|
+
result.fallback && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
499
|
+
className: "gcm-notice",
|
|
500
|
+
children: t("fallback")
|
|
501
|
+
})
|
|
502
|
+
]
|
|
503
|
+
})
|
|
504
|
+
]
|
|
505
|
+
})
|
|
506
|
+
}), document.body)
|
|
507
|
+
]
|
|
508
|
+
});
|
|
509
|
+
}
|
|
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
|
+
function apply(ctx) {
|
|
536
|
+
const client = ctx;
|
|
537
|
+
ctx.provide(GIT_COMMIT_CLIENT_SERVICE, { active: true });
|
|
538
|
+
ctx.effect(() => client.locale.register(NS, {
|
|
539
|
+
zh: { ...zh },
|
|
540
|
+
en: { ...en }
|
|
541
|
+
}), "git-commit.locale");
|
|
542
|
+
ctx.effect(() => client.slots.inject("conversation.session.header.actions", () => client.slots.register({
|
|
543
|
+
name: "conversation.session.header.actions",
|
|
544
|
+
id: "git-commit",
|
|
545
|
+
order: 10,
|
|
546
|
+
locale: NS,
|
|
547
|
+
inject: () => ({ call: (endpoint, payload) => client.connection.rpc.call(RPC_CHANNEL, endpoint, payload) })
|
|
548
|
+
}, GitCommitAction)), "git-commit.slot");
|
|
549
|
+
}
|
|
550
|
+
//#endregion
|
|
551
|
+
exports.GitCommitAction = GitCommitAction;
|
|
552
|
+
exports.apply = apply;
|
|
553
|
+
exports.inject = inject;
|
|
554
|
+
exports.name = name;
|
|
555
|
+
|
|
556
|
+
//# sourceMappingURL=client.inner.cjs.map
|
|
557
|
+
return module.exports;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as RPC_CHANNEL, a as DEFAULT_MAX_OUTPUT_TOKENS, c as GIT_TIMEOUT_MS, d as MAX_MESSAGE_CHARS, f as MAX_UNTRACKED_PREVIEW_CHARS, g as RECENT_LOG_COUNT, h as PURPOSE_ID, i as CommitRunResult, l as GitCommitStatus, m as PROMPT_VERSION, n as CommitGroupResult, o as FEATURE_ID, p as PLUGIN_NAME, r as CommitModelInfo, s as GIT_COMMIT_CLIENT_SERVICE, t as CONTRACT_VERSION, u as MAX_MANIFEST_CHARS, v as RpcResult, y as SCHEMA_VERSION } from "./contracts2.js";
|
|
2
|
+
export { CONTRACT_VERSION, CommitGroupResult, CommitModelInfo, CommitRunResult, DEFAULT_MAX_OUTPUT_TOKENS, FEATURE_ID, GIT_COMMIT_CLIENT_SERVICE, GIT_TIMEOUT_MS, GitCommitStatus, MAX_MANIFEST_CHARS, MAX_MESSAGE_CHARS, MAX_UNTRACKED_PREVIEW_CHARS, PLUGIN_NAME, PROMPT_VERSION, PURPOSE_ID, RECENT_LOG_COUNT, RPC_CHANNEL, type RpcResult, SCHEMA_VERSION };
|
package/lib/contracts.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
//#region src/contracts.ts
|
|
2
|
+
const PLUGIN_NAME = "@klarkxy/dsh-git-commit";
|
|
3
|
+
const FEATURE_ID = "git-commit";
|
|
4
|
+
const PURPOSE_ID = "git-commit.plan";
|
|
5
|
+
const RPC_CHANNEL = "/dsh-git-commit";
|
|
6
|
+
const GIT_COMMIT_CLIENT_SERVICE = "dshGitCommitClient";
|
|
7
|
+
const PROMPT_VERSION = "git-commit.v1";
|
|
8
|
+
const SCHEMA_VERSION = "commit-plan-json.v1";
|
|
9
|
+
const CONTRACT_VERSION = 1;
|
|
10
|
+
const DEFAULT_MAX_OUTPUT_TOKENS = 2048;
|
|
11
|
+
/** Total character budget for the change manifest sent to the model. */
|
|
12
|
+
const MAX_MANIFEST_CHARS = 48e3;
|
|
13
|
+
/** Per-file preview cap for untracked files. */
|
|
14
|
+
const MAX_UNTRACKED_PREVIEW_CHARS = 2e3;
|
|
15
|
+
/** Recent commit subjects supplied as style reference. */
|
|
16
|
+
const RECENT_LOG_COUNT = 10;
|
|
17
|
+
/** Hard cap for one generated commit message line. */
|
|
18
|
+
const MAX_MESSAGE_CHARS = 120;
|
|
19
|
+
/** git subprocess timeout. */
|
|
20
|
+
const GIT_TIMEOUT_MS = 3e4;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { CONTRACT_VERSION, DEFAULT_MAX_OUTPUT_TOKENS, FEATURE_ID, GIT_COMMIT_CLIENT_SERVICE, GIT_TIMEOUT_MS, MAX_MANIFEST_CHARS, MAX_MESSAGE_CHARS, MAX_UNTRACKED_PREVIEW_CHARS, PLUGIN_NAME, PROMPT_VERSION, PURPOSE_ID, RECENT_LOG_COUNT, RPC_CHANNEL, SCHEMA_VERSION };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contracts.js","names":[],"sources":["../src/contracts.ts"],"sourcesContent":["/** Browser-safe contracts. Shared RPC/result types stay on @klarkxy/dsh-ai-services. */\nexport type { RpcResult } from '@klarkxy/dsh-ai-services/contracts'\n\nexport const PLUGIN_NAME = '@klarkxy/dsh-git-commit'\nexport const FEATURE_ID = 'git-commit'\nexport const PURPOSE_ID = 'git-commit.plan'\nexport const RPC_CHANNEL = '/dsh-git-commit'\nexport const GIT_COMMIT_CLIENT_SERVICE = 'dshGitCommitClient'\nexport const PROMPT_VERSION = 'git-commit.v1'\nexport const SCHEMA_VERSION = 'commit-plan-json.v1'\nexport const CONTRACT_VERSION = 1\n\nexport const DEFAULT_MAX_OUTPUT_TOKENS = 2048\n/** Total character budget for the change manifest sent to the model. */\nexport const MAX_MANIFEST_CHARS = 48_000\n/** Per-file preview cap for untracked files. */\nexport const MAX_UNTRACKED_PREVIEW_CHARS = 2_000\n/** Recent commit subjects supplied as style reference. */\nexport const RECENT_LOG_COUNT = 10\n/** Hard cap for one generated commit message line. */\nexport const MAX_MESSAGE_CHARS = 120\n/** git subprocess timeout. */\nexport const GIT_TIMEOUT_MS = 30_000\n\nexport interface CommitModelInfo {\n readonly provider: string\n readonly model: string\n /** 'purpose' means the user configured an AI-services route; 'default' means the fallback default model. */\n readonly source: 'override' | 'purpose' | 'default'\n}\n\nexport interface GitCommitStatus {\n readonly available: boolean\n /** Machine-readable reason when unavailable: 'no-session' | 'no-cwd' | 'not-a-repo' | 'no-git'. */\n readonly reason?: string\n readonly detail?: string\n readonly root?: string\n readonly branch?: string\n readonly workspace: { readonly files: number }\n readonly model?: CommitModelInfo\n}\n\nexport interface CommitGroupResult {\n readonly hash: string\n readonly message: string\n readonly files: readonly string[]\n}\n\nexport interface CommitRunResult {\n readonly branch: string\n readonly commits: readonly CommitGroupResult[]\n /** True when the model plan was unavailable and a single fallback commit was used. */\n readonly fallback: boolean\n readonly model?: CommitModelInfo\n}\n"],"mappings":";AAGA,MAAa,cAAc;AAC3B,MAAa,aAAa;AAC1B,MAAa,aAAa;AAC1B,MAAa,cAAc;AAC3B,MAAa,4BAA4B;AACzC,MAAa,iBAAiB;AAC9B,MAAa,iBAAiB;AAC9B,MAAa,mBAAmB;AAEhC,MAAa,4BAA4B;;AAEzC,MAAa,qBAAqB;;AAElC,MAAa,8BAA8B;;AAE3C,MAAa,mBAAmB;;AAEhC,MAAa,oBAAoB;;AAEjC,MAAa,iBAAiB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { RpcResult as RpcResult$1 } from "@klarkxy/dsh-ai-services/contracts";
|
|
2
|
+
//#region src/contracts.d.ts
|
|
3
|
+
declare const PLUGIN_NAME = "@klarkxy/dsh-git-commit";
|
|
4
|
+
declare const FEATURE_ID = "git-commit";
|
|
5
|
+
declare const PURPOSE_ID = "git-commit.plan";
|
|
6
|
+
declare const RPC_CHANNEL = "/dsh-git-commit";
|
|
7
|
+
declare const GIT_COMMIT_CLIENT_SERVICE = "dshGitCommitClient";
|
|
8
|
+
declare const PROMPT_VERSION = "git-commit.v1";
|
|
9
|
+
declare const SCHEMA_VERSION = "commit-plan-json.v1";
|
|
10
|
+
declare const CONTRACT_VERSION = 1;
|
|
11
|
+
declare const DEFAULT_MAX_OUTPUT_TOKENS = 2048;
|
|
12
|
+
/** Total character budget for the change manifest sent to the model. */
|
|
13
|
+
declare const MAX_MANIFEST_CHARS = 48000;
|
|
14
|
+
/** Per-file preview cap for untracked files. */
|
|
15
|
+
declare const MAX_UNTRACKED_PREVIEW_CHARS = 2000;
|
|
16
|
+
/** Recent commit subjects supplied as style reference. */
|
|
17
|
+
declare const RECENT_LOG_COUNT = 10;
|
|
18
|
+
/** Hard cap for one generated commit message line. */
|
|
19
|
+
declare const MAX_MESSAGE_CHARS = 120;
|
|
20
|
+
/** git subprocess timeout. */
|
|
21
|
+
declare const GIT_TIMEOUT_MS = 30000;
|
|
22
|
+
interface CommitModelInfo {
|
|
23
|
+
readonly provider: string;
|
|
24
|
+
readonly model: string;
|
|
25
|
+
/** 'purpose' means the user configured an AI-services route; 'default' means the fallback default model. */
|
|
26
|
+
readonly source: 'override' | 'purpose' | 'default';
|
|
27
|
+
}
|
|
28
|
+
interface GitCommitStatus {
|
|
29
|
+
readonly available: boolean;
|
|
30
|
+
/** Machine-readable reason when unavailable: 'no-session' | 'no-cwd' | 'not-a-repo' | 'no-git'. */
|
|
31
|
+
readonly reason?: string;
|
|
32
|
+
readonly detail?: string;
|
|
33
|
+
readonly root?: string;
|
|
34
|
+
readonly branch?: string;
|
|
35
|
+
readonly workspace: {
|
|
36
|
+
readonly files: number;
|
|
37
|
+
};
|
|
38
|
+
readonly model?: CommitModelInfo;
|
|
39
|
+
}
|
|
40
|
+
interface CommitGroupResult {
|
|
41
|
+
readonly hash: string;
|
|
42
|
+
readonly message: string;
|
|
43
|
+
readonly files: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
interface CommitRunResult {
|
|
46
|
+
readonly branch: string;
|
|
47
|
+
readonly commits: readonly CommitGroupResult[];
|
|
48
|
+
/** True when the model plan was unavailable and a single fallback commit was used. */
|
|
49
|
+
readonly fallback: boolean;
|
|
50
|
+
readonly model?: CommitModelInfo;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { RPC_CHANNEL as _, DEFAULT_MAX_OUTPUT_TOKENS as a, GIT_TIMEOUT_MS as c, MAX_MESSAGE_CHARS as d, MAX_UNTRACKED_PREVIEW_CHARS as f, RECENT_LOG_COUNT as g, PURPOSE_ID as h, CommitRunResult as i, GitCommitStatus as l, PROMPT_VERSION as m, CommitGroupResult as n, FEATURE_ID as o, PLUGIN_NAME as p, CommitModelInfo as r, GIT_COMMIT_CLIENT_SERVICE as s, CONTRACT_VERSION as t, MAX_MANIFEST_CHARS as u, RpcResult$1 as v, SCHEMA_VERSION as y };
|
|
54
|
+
//# sourceMappingURL=contracts2.d.ts.map
|