@klarkxy/dsh-git-commit 0.2.0 → 0.2.2
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 +14 -11
- package/docs/README.zh-CN.md +11 -10
- package/lib/client.inner.cjs +384 -63
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +384 -63
- package/lib/contracts.d.ts +44 -11
- package/lib/contracts.js +1 -27
- package/lib/contracts.js.map +1 -1
- package/lib/index.d.ts +7 -3
- package/lib/index.js +237 -70
- package/lib/index.js.map +1 -1
- package/package.json +10 -5
package/lib/client.inner.cjs
CHANGED
|
@@ -381,7 +381,7 @@ ${scope} {
|
|
|
381
381
|
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
382
382
|
border-radius: var(--dsw-radius-md);
|
|
383
383
|
background: var(--dsw-alias-bg-layer-1);
|
|
384
|
-
font-family: var(--ds-font-family-code
|
|
384
|
+
font-family: var(--ds-font-family-code);
|
|
385
385
|
font-size: 12px;
|
|
386
386
|
line-height: 20px;
|
|
387
387
|
color: var(--dsw-alias-label-secondary);
|
|
@@ -457,6 +457,162 @@ ${scope} {
|
|
|
457
457
|
`.replace(/^& (\.dsh-ui-[a-z0-9-]+)(?![+>~])/gm, (_match, className) => `&${className}, & ${className}`);
|
|
458
458
|
}
|
|
459
459
|
//#endregion
|
|
460
|
+
//#region ../dsh-plugin-kit/src/contracts.ts
|
|
461
|
+
/**
|
|
462
|
+
* Remote contribution for model-settings change notifications.
|
|
463
|
+
* Provided by `@klarkxy/dsh-model-hub` (`ModelSettingsChangesController`);
|
|
464
|
+
* subscribers degrade silently when that package is not installed.
|
|
465
|
+
* The stream carries invalidations only — `ready` is never a settings snapshot.
|
|
466
|
+
*/
|
|
467
|
+
const MODEL_SETTINGS_CHANGES_REMOTE = {
|
|
468
|
+
package: "@klarkxy/dsh-model-hub",
|
|
469
|
+
descriptors: [{
|
|
470
|
+
id: "modelSettingsChanges/watch",
|
|
471
|
+
service: "modelSettingsChangesController",
|
|
472
|
+
namespace: "modelSettingsChanges",
|
|
473
|
+
method: "watch",
|
|
474
|
+
mode: "stream",
|
|
475
|
+
invocation: { kind: "direct" },
|
|
476
|
+
parameters: [],
|
|
477
|
+
cancellation: { parameter: "signal" }
|
|
478
|
+
}]
|
|
479
|
+
};
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
482
|
+
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
483
|
+
function parseModelSettingsChangeFrame(value) {
|
|
484
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Invalid model settings change frame");
|
|
485
|
+
const frame = value;
|
|
486
|
+
if (frame.kind === "ready" && Object.keys(frame).length === 1) return { kind: "ready" };
|
|
487
|
+
if (frame.kind === "changed" && Object.keys(frame).length === 3 && typeof frame.ns === "string" && frame.ns.length > 0 && typeof frame.revision === "number" && Number.isSafeInteger(frame.revision) && frame.revision >= 0) return {
|
|
488
|
+
kind: "changed",
|
|
489
|
+
ns: frame.ns,
|
|
490
|
+
revision: frame.revision
|
|
491
|
+
};
|
|
492
|
+
throw new Error("Invalid model settings change frame");
|
|
493
|
+
}
|
|
494
|
+
MODEL_SETTINGS_CHANGES_REMOTE.package, MODEL_SETTINGS_CHANGES_REMOTE.descriptors.map((descriptor) => ({
|
|
495
|
+
...descriptor,
|
|
496
|
+
result: {
|
|
497
|
+
mode: "strict",
|
|
498
|
+
typeSymbol: "ModelSettings.ChangeFrame",
|
|
499
|
+
create: () => ({ parse: parseModelSettingsChangeFrame })
|
|
500
|
+
}
|
|
501
|
+
}));
|
|
502
|
+
/** Native RemoteStream owns carrier reconnects; no timers or browser transport.
|
|
503
|
+
* A factory shares one logical stream while at least one subscriber remains. */
|
|
504
|
+
function createModelSettingsSubscriber(ctx, namespace) {
|
|
505
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
506
|
+
let injection;
|
|
507
|
+
let current;
|
|
508
|
+
let lifecycle = 0;
|
|
509
|
+
let failureMessage;
|
|
510
|
+
const report = (message) => {
|
|
511
|
+
failureMessage = message;
|
|
512
|
+
for (const listener of [...listeners]) try {
|
|
513
|
+
listener.error?.(message);
|
|
514
|
+
} catch {}
|
|
515
|
+
};
|
|
516
|
+
const invalidate = () => {
|
|
517
|
+
for (const listener of [...listeners]) {
|
|
518
|
+
if (!listeners.has(listener)) continue;
|
|
519
|
+
try {
|
|
520
|
+
listener.change();
|
|
521
|
+
} catch (error) {
|
|
522
|
+
try {
|
|
523
|
+
listener.error?.(error instanceof Error ? error.message : String(error));
|
|
524
|
+
} catch {}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
};
|
|
528
|
+
const start = () => {
|
|
529
|
+
const life = ++lifecycle;
|
|
530
|
+
injection = ctx.inject(["remote", "remote.modelSettingsChanges"], (injected) => {
|
|
531
|
+
if (life !== lifecycle || !listeners.size) return;
|
|
532
|
+
const scope = injected;
|
|
533
|
+
const stream = scope.remote.$stream({
|
|
534
|
+
name: "Model settings sync",
|
|
535
|
+
open: (signal) => scope.remote.modelSettingsChanges.watch(signal),
|
|
536
|
+
ended: (accepted) => /* @__PURE__ */ new Error(`Model settings sync stream ended ${accepted ? "after" : "before"} ready; refresh or reopen the page`)
|
|
537
|
+
});
|
|
538
|
+
const session = {
|
|
539
|
+
stream,
|
|
540
|
+
active: true
|
|
541
|
+
};
|
|
542
|
+
current = session;
|
|
543
|
+
scope.effect(() => () => {
|
|
544
|
+
session.active = false;
|
|
545
|
+
if (current === session) {
|
|
546
|
+
current = void 0;
|
|
547
|
+
if (life === lifecycle && listeners.size) report("Model settings sync service unavailable");
|
|
548
|
+
}
|
|
549
|
+
return stream.dispose();
|
|
550
|
+
});
|
|
551
|
+
(async () => {
|
|
552
|
+
let generation;
|
|
553
|
+
let ready = false;
|
|
554
|
+
try {
|
|
555
|
+
for await (const item of stream) {
|
|
556
|
+
if (!session.active || life !== lifecycle || !listeners.size) break;
|
|
557
|
+
if (item.signal.aborted) continue;
|
|
558
|
+
if (generation !== item.generation) {
|
|
559
|
+
generation = item.generation;
|
|
560
|
+
ready = false;
|
|
561
|
+
}
|
|
562
|
+
const frame = parseModelSettingsChangeFrame(item.value);
|
|
563
|
+
if (frame.kind === "ready") {
|
|
564
|
+
ready = true;
|
|
565
|
+
failureMessage = void 0;
|
|
566
|
+
item.accept();
|
|
567
|
+
invalidate();
|
|
568
|
+
} else {
|
|
569
|
+
if (!ready) throw new Error("Model settings sync change arrived before ready");
|
|
570
|
+
if (namespace === "*" || frame.ns === namespace) invalidate();
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
} catch (error) {
|
|
574
|
+
if (session.active && life === lifecycle) report(error instanceof Error ? error.message : String(error));
|
|
575
|
+
} finally {
|
|
576
|
+
if (current === session) current = void 0;
|
|
577
|
+
session.active = false;
|
|
578
|
+
await stream.dispose().catch(() => {});
|
|
579
|
+
}
|
|
580
|
+
})();
|
|
581
|
+
});
|
|
582
|
+
queueMicrotask(() => {
|
|
583
|
+
if (life === lifecycle && listeners.size && !current) report("Model settings sync service unavailable");
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
return Object.freeze({ subscribe(onChange, onError) {
|
|
587
|
+
const listener = {
|
|
588
|
+
change: onChange,
|
|
589
|
+
error: onError
|
|
590
|
+
};
|
|
591
|
+
listeners.add(listener);
|
|
592
|
+
if (!injection) start();
|
|
593
|
+
else if (failureMessage) queueMicrotask(() => {
|
|
594
|
+
if (listeners.has(listener) && failureMessage) try {
|
|
595
|
+
listener.error?.(failureMessage);
|
|
596
|
+
} catch {}
|
|
597
|
+
});
|
|
598
|
+
return () => {
|
|
599
|
+
if (!listeners.delete(listener) || listeners.size) return;
|
|
600
|
+
lifecycle++;
|
|
601
|
+
if (current) {
|
|
602
|
+
current.active = false;
|
|
603
|
+
current.stream.dispose().catch(() => {});
|
|
604
|
+
}
|
|
605
|
+
current = void 0;
|
|
606
|
+
failureMessage = void 0;
|
|
607
|
+
const previous = injection;
|
|
608
|
+
injection = void 0;
|
|
609
|
+
try {
|
|
610
|
+
Promise.resolve(previous?.dispose()).catch(() => {});
|
|
611
|
+
} catch {}
|
|
612
|
+
};
|
|
613
|
+
} });
|
|
614
|
+
}
|
|
615
|
+
//#endregion
|
|
460
616
|
//#region src/contracts.ts
|
|
461
617
|
const PLUGIN_NAME = "@klarkxy/dsh-git-commit";
|
|
462
618
|
const RPC_CHANNEL = "/dsh-git-commit";
|
|
@@ -487,6 +643,12 @@ const zh = {
|
|
|
487
643
|
loading: "正在读取仓库状态…",
|
|
488
644
|
running: "正在整理修改并分次提交…",
|
|
489
645
|
done: "已创建提交",
|
|
646
|
+
partial: "本次提交未全部完成;以下提交已保留",
|
|
647
|
+
stopped: "本次操作未确认完成",
|
|
648
|
+
uncertain: "以下提交组的结果不确定。请检查 Git 日志和状态,确认后再操作。",
|
|
649
|
+
hashUnavailable: "提交已创建,哈希未读取",
|
|
650
|
+
remaining: "尚未提交:{n} 组",
|
|
651
|
+
cancelled: "提交操作已取消",
|
|
490
652
|
fallback: "模型不可用,已合并为单个提交",
|
|
491
653
|
noWorkspace: "工作区没有修改",
|
|
492
654
|
unavailable: "不可用",
|
|
@@ -497,6 +659,12 @@ const zh = {
|
|
|
497
659
|
sessionRunning: "会话正在运行,提交已暂停",
|
|
498
660
|
refresh: "刷新",
|
|
499
661
|
failed: "操作失败",
|
|
662
|
+
disabled: "插件已停用",
|
|
663
|
+
busy: "已有提交操作正在进行",
|
|
664
|
+
stale: "设置已更新,请刷新后重试",
|
|
665
|
+
workspaceChanged: "工作区或分支已变更,请刷新后重试提交",
|
|
666
|
+
settingsUnavailable: "无法读取提交设置,请重新加载插件后重试",
|
|
667
|
+
invalidRequest: "提交请求无效,请刷新后重试",
|
|
500
668
|
planUnavailable: "模型没有给出可用的提交方案,已取消本次提交,工作区没有被改动。换一个模型重试,或在插件设置里开启兜底提交。",
|
|
501
669
|
close: "关闭"
|
|
502
670
|
};
|
|
@@ -515,6 +683,12 @@ const en = {
|
|
|
515
683
|
loading: "Reading repository status…",
|
|
516
684
|
running: "Organizing changes into commits…",
|
|
517
685
|
done: "Commits created",
|
|
686
|
+
partial: "This run stopped before completion; these commits were kept",
|
|
687
|
+
stopped: "This run did not finish with a confirmed result",
|
|
688
|
+
uncertain: "The outcome of these groups is uncertain. Check Git log and status before taking further action.",
|
|
689
|
+
hashUnavailable: "Commit created; hash unavailable",
|
|
690
|
+
remaining: "Groups not committed: {n}",
|
|
691
|
+
cancelled: "Commit run cancelled",
|
|
518
692
|
fallback: "Model unavailable; used a single commit",
|
|
519
693
|
noWorkspace: "No workspace changes",
|
|
520
694
|
unavailable: "Unavailable",
|
|
@@ -525,23 +699,31 @@ const en = {
|
|
|
525
699
|
sessionRunning: "Session is running; commits paused",
|
|
526
700
|
refresh: "Refresh",
|
|
527
701
|
failed: "Operation failed",
|
|
702
|
+
disabled: "Plugin disabled",
|
|
703
|
+
busy: "A commit run is already in progress",
|
|
704
|
+
stale: "Settings changed; refresh and retry",
|
|
705
|
+
workspaceChanged: "The workspace or branch changed; refresh and retry the commit",
|
|
706
|
+
settingsUnavailable: "Could not read commit settings; reload the plugin and retry",
|
|
707
|
+
invalidRequest: "Invalid commit request; refresh and retry",
|
|
528
708
|
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.",
|
|
529
709
|
close: "Close"
|
|
530
710
|
};
|
|
531
711
|
/** A failed RPC keeps its code so the panel can explain a rejection in its own words. */
|
|
532
712
|
var RpcError = class extends Error {
|
|
533
713
|
code;
|
|
534
|
-
|
|
714
|
+
partial;
|
|
715
|
+
constructor(code, message, partial) {
|
|
535
716
|
super(message);
|
|
536
717
|
this.name = "RpcError";
|
|
537
718
|
this.code = code;
|
|
719
|
+
this.partial = partial;
|
|
538
720
|
}
|
|
539
721
|
};
|
|
540
|
-
async function rpc(call, endpoint, payload = {}, fallback = "
|
|
722
|
+
async function rpc(call, endpoint, payload = {}, fallback = "") {
|
|
541
723
|
const result = await call(endpoint, payload);
|
|
542
724
|
if (!result || result.ok !== true) {
|
|
543
725
|
const failed = result && "error" in result ? result.error : void 0;
|
|
544
|
-
throw new RpcError(failed?.code ?? "unknown", failed?.message ? failed.message : fallback);
|
|
726
|
+
throw new RpcError(failed?.code ?? "unknown", failed?.message ? failed.message : fallback, failed && "partial" in failed ? failed.partial : void 0);
|
|
545
727
|
}
|
|
546
728
|
return result.value;
|
|
547
729
|
}
|
|
@@ -556,7 +738,25 @@ function reasonText(t, status) {
|
|
|
556
738
|
}
|
|
557
739
|
/** A refusal the plugin can name gets its own sentence; the rest stay verbatim. */
|
|
558
740
|
function errorText(t, error) {
|
|
559
|
-
|
|
741
|
+
const key = {
|
|
742
|
+
"plan-unavailable": "planUnavailable",
|
|
743
|
+
"no-session": "reasonNoSession",
|
|
744
|
+
"no-cwd": "reasonNoCwd",
|
|
745
|
+
"no-git": "reasonNoGit",
|
|
746
|
+
"not-a-repo": "reasonNoRepo",
|
|
747
|
+
"no-changes": "noWorkspace",
|
|
748
|
+
"session-running": "sessionRunning",
|
|
749
|
+
"session-changed": "workspaceChanged",
|
|
750
|
+
"workspace-changed": "workspaceChanged",
|
|
751
|
+
busy: "busy",
|
|
752
|
+
stale: "stale",
|
|
753
|
+
disposed: "disabled",
|
|
754
|
+
cancelled: "cancelled",
|
|
755
|
+
"settings-unavailable": "settingsUnavailable",
|
|
756
|
+
"bad-request": "invalidRequest",
|
|
757
|
+
"not-found": "invalidRequest"
|
|
758
|
+
}[error.code];
|
|
759
|
+
return key ? t(key) : error.message || t("failed");
|
|
560
760
|
}
|
|
561
761
|
function panelError(reason) {
|
|
562
762
|
return reason instanceof RpcError ? {
|
|
@@ -655,7 +855,7 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
655
855
|
setLoading(true);
|
|
656
856
|
setError(null);
|
|
657
857
|
try {
|
|
658
|
-
const next = await rpc(call, "status", { sessionId }
|
|
858
|
+
const next = await rpc(call, "status", { sessionId });
|
|
659
859
|
if (generationRef.current !== generation) return;
|
|
660
860
|
setStatus(next);
|
|
661
861
|
} catch (reason) {
|
|
@@ -665,11 +865,7 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
665
865
|
} finally {
|
|
666
866
|
if (generationRef.current === generation) setLoading(false);
|
|
667
867
|
}
|
|
668
|
-
}, [
|
|
669
|
-
call,
|
|
670
|
-
sessionId,
|
|
671
|
-
t
|
|
672
|
-
]);
|
|
868
|
+
}, [call, sessionId]);
|
|
673
869
|
(0, react.useEffect)(() => {
|
|
674
870
|
if (!open) return;
|
|
675
871
|
loadStatus();
|
|
@@ -692,23 +888,23 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
692
888
|
setResult(null);
|
|
693
889
|
setError(null);
|
|
694
890
|
try {
|
|
695
|
-
const value = await rpc(call, "commit", { sessionId }
|
|
891
|
+
const value = await rpc(call, "commit", { sessionId });
|
|
696
892
|
if (generationRef.current !== generation) return;
|
|
697
893
|
setResult(value);
|
|
698
|
-
const next = await rpc(call, "status", { sessionId }).catch(() => void 0);
|
|
699
|
-
if (generationRef.current !== generation) return;
|
|
700
|
-
if (next) setStatus(next);
|
|
701
894
|
} catch (reason) {
|
|
702
895
|
if (generationRef.current !== generation) return;
|
|
896
|
+
if (reason instanceof RpcError && reason.partial) setResult(reason.partial);
|
|
703
897
|
setError(panelError(reason));
|
|
704
898
|
} finally {
|
|
705
|
-
if (generationRef.current === generation)
|
|
899
|
+
if (generationRef.current === generation) {
|
|
900
|
+
const next = await rpc(call, "status", { sessionId }).catch(() => void 0);
|
|
901
|
+
if (generationRef.current === generation) {
|
|
902
|
+
setStatus(next);
|
|
903
|
+
setRunning(false);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
706
906
|
}
|
|
707
|
-
}, [
|
|
708
|
-
call,
|
|
709
|
-
sessionId,
|
|
710
|
-
t
|
|
711
|
-
]);
|
|
907
|
+
}, [call, sessionId]);
|
|
712
908
|
const available = status?.available === true;
|
|
713
909
|
const workspaceCount = status?.workspace.files ?? 0;
|
|
714
910
|
const canCommit = available && !running && !sessionRunning && workspaceCount > 0;
|
|
@@ -856,23 +1052,48 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
856
1052
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
857
1053
|
className: "dsh-ui-hint dsh-ui-wrap",
|
|
858
1054
|
children: [
|
|
859
|
-
t("done"),
|
|
1055
|
+
t("failure" in result ? result.commits.length ? "partial" : "stopped" : "done"),
|
|
860
1056
|
" · ",
|
|
861
1057
|
result.branch
|
|
862
1058
|
]
|
|
863
1059
|
}),
|
|
864
1060
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
865
1061
|
className: "gcm-commits",
|
|
866
|
-
children: result.commits.map((commit) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
867
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: commit.hash }),
|
|
1062
|
+
children: result.commits.map((commit, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
|
|
1063
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", { children: commit.hash || t("hashUnavailable") }),
|
|
868
1064
|
" ",
|
|
869
|
-
commit.message
|
|
870
|
-
|
|
1065
|
+
commit.message,
|
|
1066
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1067
|
+
className: "dsh-ui-hint",
|
|
1068
|
+
children: commit.files.join(", ")
|
|
1069
|
+
})
|
|
1070
|
+
] }, `${index}:${commit.hash}`))
|
|
871
1071
|
}),
|
|
872
1072
|
result.fallback && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
873
1073
|
className: "dsh-ui-hint",
|
|
874
1074
|
children: t("fallback")
|
|
875
|
-
})
|
|
1075
|
+
}),
|
|
1076
|
+
"uncertain" in result && result.uncertain?.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1077
|
+
className: "dsh-ui-hint",
|
|
1078
|
+
role: "status",
|
|
1079
|
+
children: t("uncertain")
|
|
1080
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1081
|
+
className: "gcm-commits",
|
|
1082
|
+
children: result.uncertain.map((group, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [group.message, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1083
|
+
className: "dsh-ui-hint",
|
|
1084
|
+
children: group.files.join(", ")
|
|
1085
|
+
})] }, index))
|
|
1086
|
+
})] }) : null,
|
|
1087
|
+
"remaining" in result && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1088
|
+
className: "dsh-ui-hint",
|
|
1089
|
+
children: t("remaining").replace("{n}", String(result.remaining.length))
|
|
1090
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1091
|
+
className: "gcm-commits",
|
|
1092
|
+
children: result.remaining.map((group, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [group.message, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1093
|
+
className: "dsh-ui-hint",
|
|
1094
|
+
children: group.files.join(", ")
|
|
1095
|
+
})] }, index))
|
|
1096
|
+
})] })
|
|
876
1097
|
]
|
|
877
1098
|
})
|
|
878
1099
|
]
|
|
@@ -899,10 +1120,10 @@ const clientCss = `${officialUiCss("gcm-root")}
|
|
|
899
1120
|
.gcm-settings { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
|
|
900
1121
|
.gcm-settings-status { display: flex; flex-direction: column; gap: 4px; }
|
|
901
1122
|
`;
|
|
902
|
-
/** Host UI language, following live switches
|
|
1123
|
+
/** Host UI language, following live switches with the host's English fallback. */
|
|
903
1124
|
function useHostLocale(client) {
|
|
904
1125
|
const subscribe = (0, react.useCallback)((listener) => client.locale.subscribe?.(listener) ?? (() => {}), [client]);
|
|
905
|
-
const snapshot = (0, react.useCallback)(() => client.locale.getSnapshot?.().active ?? "
|
|
1126
|
+
const snapshot = (0, react.useCallback)(() => client.locale.getSnapshot?.().active ?? "en", [client]);
|
|
906
1127
|
return (0, react.useSyncExternalStore)(subscribe, snapshot, snapshot);
|
|
907
1128
|
}
|
|
908
1129
|
const settingsZh = {
|
|
@@ -917,7 +1138,9 @@ const settingsZh = {
|
|
|
917
1138
|
saved: "已保存。",
|
|
918
1139
|
stale: "设置已更新,请刷新后重试。",
|
|
919
1140
|
failed: "无法读取提交设置。",
|
|
920
|
-
retry: "重试"
|
|
1141
|
+
retry: "重试",
|
|
1142
|
+
refresh: "刷新设置",
|
|
1143
|
+
syncUnavailable: "自动刷新不可用,请手动刷新设置。"
|
|
921
1144
|
};
|
|
922
1145
|
const settingsEn = {
|
|
923
1146
|
pageTitle: "Git Commit",
|
|
@@ -931,7 +1154,9 @@ const settingsEn = {
|
|
|
931
1154
|
saved: "Saved.",
|
|
932
1155
|
stale: "Settings changed; refresh and retry.",
|
|
933
1156
|
failed: "Could not load commit settings.",
|
|
934
|
-
retry: "Retry"
|
|
1157
|
+
retry: "Retry",
|
|
1158
|
+
refresh: "Refresh settings",
|
|
1159
|
+
syncUnavailable: "Automatic refresh unavailable; refresh settings manually."
|
|
935
1160
|
};
|
|
936
1161
|
function settingsCopy(locale) {
|
|
937
1162
|
return String(locale).startsWith("zh") ? settingsZh : settingsEn;
|
|
@@ -943,23 +1168,77 @@ function GitCommitSettingsPanel(props) {
|
|
|
943
1168
|
const [settings, setSettings] = (0, react.useState)(void 0);
|
|
944
1169
|
const [choices, setChoices] = (0, react.useState)([]);
|
|
945
1170
|
const [error, setError] = (0, react.useState)("");
|
|
946
|
-
const [note, setNote] = (0, react.useState)(
|
|
1171
|
+
const [note, setNote] = (0, react.useState)(false);
|
|
947
1172
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
1173
|
+
const [syncError, setSyncError] = (0, react.useState)("");
|
|
1174
|
+
const operation = (0, react.useRef)({
|
|
1175
|
+
epoch: 0,
|
|
1176
|
+
active: false,
|
|
1177
|
+
reading: false,
|
|
1178
|
+
writing: false,
|
|
1179
|
+
pending: false
|
|
1180
|
+
});
|
|
1181
|
+
const refreshRef = (0, react.useRef)(() => {});
|
|
948
1182
|
const call = (0, react.useCallback)((endpoint, payload = {}) => props.client.connection.rpc.call(RPC_CHANNEL, endpoint, payload), [props.client]);
|
|
949
|
-
|
|
950
|
-
|
|
1183
|
+
const refresh = () => {
|
|
1184
|
+
const state = operation.current;
|
|
1185
|
+
if (!state.active) return;
|
|
1186
|
+
if (state.reading || state.writing) {
|
|
1187
|
+
state.pending = true;
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
state.pending = false;
|
|
1191
|
+
state.reading = true;
|
|
1192
|
+
setBusy(true);
|
|
1193
|
+
const epoch = state.epoch;
|
|
951
1194
|
rpc(call, "settings", {}, text.failed).then((next) => {
|
|
952
|
-
if (
|
|
1195
|
+
if (state.active && state.epoch === epoch && !state.pending) {
|
|
953
1196
|
setSettings(next);
|
|
954
1197
|
setError("");
|
|
955
1198
|
}
|
|
956
1199
|
}).catch(() => {
|
|
957
|
-
if (
|
|
1200
|
+
if (state.active && state.epoch === epoch && !state.pending) setError("failed");
|
|
1201
|
+
}).finally(() => {
|
|
1202
|
+
if (!state.active || state.epoch !== epoch) return;
|
|
1203
|
+
state.reading = false;
|
|
1204
|
+
setBusy(false);
|
|
1205
|
+
if (state.pending) refreshRef.current();
|
|
1206
|
+
});
|
|
1207
|
+
};
|
|
1208
|
+
refreshRef.current = refresh;
|
|
1209
|
+
(0, react.useEffect)(() => {
|
|
1210
|
+
const state = operation.current;
|
|
1211
|
+
state.epoch++;
|
|
1212
|
+
state.active = true;
|
|
1213
|
+
state.reading = false;
|
|
1214
|
+
state.writing = false;
|
|
1215
|
+
state.pending = false;
|
|
1216
|
+
setBusy(false);
|
|
1217
|
+
setSettings(void 0);
|
|
1218
|
+
setError("");
|
|
1219
|
+
setNote(false);
|
|
1220
|
+
refreshRef.current();
|
|
1221
|
+
return () => {
|
|
1222
|
+
state.active = false;
|
|
1223
|
+
state.epoch++;
|
|
1224
|
+
};
|
|
1225
|
+
}, [call]);
|
|
1226
|
+
(0, react.useEffect)(() => {
|
|
1227
|
+
setSyncError("");
|
|
1228
|
+
let live = true;
|
|
1229
|
+
const stop = props.subscribeSettings?.(() => {
|
|
1230
|
+
if (live) {
|
|
1231
|
+
setSyncError("");
|
|
1232
|
+
refreshRef.current();
|
|
1233
|
+
}
|
|
1234
|
+
}, (message) => {
|
|
1235
|
+
if (live) setSyncError(message);
|
|
958
1236
|
});
|
|
959
1237
|
return () => {
|
|
960
1238
|
live = false;
|
|
1239
|
+
stop?.();
|
|
961
1240
|
};
|
|
962
|
-
}, [
|
|
1241
|
+
}, [props.subscribeSettings, props.client]);
|
|
963
1242
|
const boundProvider = settings?.model.provider ?? "";
|
|
964
1243
|
const boundModel = settings?.model.model ?? "";
|
|
965
1244
|
(0, react.useEffect)(() => {
|
|
@@ -982,9 +1261,12 @@ function GitCommitSettingsPanel(props) {
|
|
|
982
1261
|
boundModel
|
|
983
1262
|
]);
|
|
984
1263
|
const save = (0, react.useCallback)((patch) => {
|
|
985
|
-
|
|
1264
|
+
const state = operation.current;
|
|
1265
|
+
if (!settings || !state.active || state.writing || state.reading) return;
|
|
1266
|
+
state.writing = true;
|
|
1267
|
+
const epoch = state.epoch;
|
|
986
1268
|
setBusy(true);
|
|
987
|
-
setNote(
|
|
1269
|
+
setNote(false);
|
|
988
1270
|
setError("");
|
|
989
1271
|
rpc(call, "settings.update", {
|
|
990
1272
|
settings: {
|
|
@@ -993,12 +1275,17 @@ function GitCommitSettingsPanel(props) {
|
|
|
993
1275
|
},
|
|
994
1276
|
expectedRevision: settings.revision
|
|
995
1277
|
}, text.stale).then((next) => {
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1278
|
+
if (state.active && state.epoch === epoch) {
|
|
1279
|
+
setSettings(next);
|
|
1280
|
+
setNote(true);
|
|
1281
|
+
}
|
|
999
1282
|
}).catch(() => {
|
|
1000
|
-
setError(
|
|
1283
|
+
if (state.active && state.epoch === epoch) setError("stale");
|
|
1284
|
+
}).finally(() => {
|
|
1285
|
+
if (!state.active || state.epoch !== epoch) return;
|
|
1286
|
+
state.writing = false;
|
|
1001
1287
|
setBusy(false);
|
|
1288
|
+
if (state.pending) refreshRef.current();
|
|
1002
1289
|
});
|
|
1003
1290
|
}, [
|
|
1004
1291
|
call,
|
|
@@ -1006,17 +1293,31 @@ function GitCommitSettingsPanel(props) {
|
|
|
1006
1293
|
text.saved,
|
|
1007
1294
|
text.stale
|
|
1008
1295
|
]);
|
|
1009
|
-
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
1296
|
+
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1010
1297
|
className: "gcm-root gcm-settings",
|
|
1011
|
-
children:
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1298
|
+
children: [
|
|
1299
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1300
|
+
role: "alert",
|
|
1301
|
+
className: "dsh-ui-error",
|
|
1302
|
+
children: text[error]
|
|
1303
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1304
|
+
role: "status",
|
|
1305
|
+
className: "dsh-ui-hint",
|
|
1306
|
+
children: text.loading
|
|
1307
|
+
}),
|
|
1308
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1309
|
+
role: "status",
|
|
1310
|
+
className: "dsh-ui-hint",
|
|
1311
|
+
children: text.syncUnavailable
|
|
1312
|
+
}),
|
|
1313
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1314
|
+
variant: "ghost",
|
|
1315
|
+
size: "sm",
|
|
1316
|
+
disabled: busy,
|
|
1317
|
+
onClick: () => refreshRef.current(),
|
|
1318
|
+
children: text.retry
|
|
1319
|
+
})
|
|
1320
|
+
]
|
|
1020
1321
|
});
|
|
1021
1322
|
const current = settings.model;
|
|
1022
1323
|
const selected = choices.find((item) => item.provider === current.provider && item.model === current.model);
|
|
@@ -1027,15 +1328,30 @@ function GitCommitSettingsPanel(props) {
|
|
|
1027
1328
|
children: [
|
|
1028
1329
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1029
1330
|
className: "gcm-settings-status",
|
|
1030
|
-
children: [
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1331
|
+
children: [
|
|
1332
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1333
|
+
role: "status",
|
|
1334
|
+
className: "dsh-ui-hint",
|
|
1335
|
+
children: text.syncUnavailable
|
|
1336
|
+
}),
|
|
1337
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1338
|
+
variant: "ghost",
|
|
1339
|
+
size: "sm",
|
|
1340
|
+
disabled: busy,
|
|
1341
|
+
onClick: () => refreshRef.current(),
|
|
1342
|
+
children: text.refresh
|
|
1343
|
+
}),
|
|
1344
|
+
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1345
|
+
role: "alert",
|
|
1346
|
+
className: "dsh-ui-error",
|
|
1347
|
+
children: text[error]
|
|
1348
|
+
}),
|
|
1349
|
+
note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1350
|
+
role: "status",
|
|
1351
|
+
className: "dsh-ui-notice",
|
|
1352
|
+
children: text.saved
|
|
1353
|
+
})
|
|
1354
|
+
]
|
|
1039
1355
|
}),
|
|
1040
1356
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1041
1357
|
className: "dsh-ui-field",
|
|
@@ -1119,6 +1435,8 @@ function GitCommitSettingsPanel(props) {
|
|
|
1119
1435
|
}
|
|
1120
1436
|
function apply(ctx) {
|
|
1121
1437
|
const client = ctx;
|
|
1438
|
+
const settingsSubscriber = createModelSettingsSubscriber(ctx, PLUGIN_NAME);
|
|
1439
|
+
const subscribeSettings = (change, error) => settingsSubscriber.subscribe(change, error);
|
|
1122
1440
|
ctx.provide(GIT_COMMIT_CLIENT_SERVICE, { active: true });
|
|
1123
1441
|
ctx.effect(() => client.locale.register(NS, {
|
|
1124
1442
|
zh: { ...zh },
|
|
@@ -1135,7 +1453,10 @@ function apply(ctx) {
|
|
|
1135
1453
|
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
1136
1454
|
name: "plugins.bundle.config",
|
|
1137
1455
|
key: PLUGIN_NAME
|
|
1138
|
-
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitCommitSettingsPanel, {
|
|
1456
|
+
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitCommitSettingsPanel, {
|
|
1457
|
+
client,
|
|
1458
|
+
subscribeSettings
|
|
1459
|
+
}))), "git-commit.settings");
|
|
1139
1460
|
ctx.effect(() => client.slots.inject("conversation.session.header.actions", () => client.slots.register({
|
|
1140
1461
|
name: "conversation.session.header.actions",
|
|
1141
1462
|
id: "git-commit",
|