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