@klarkxy/dsh-git-commit 0.3.1 → 0.3.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 +9 -6
- package/docs/README.zh-CN.md +9 -6
- package/lib/client.inner.cjs +760 -88
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +760 -88
- package/lib/contracts.d.ts +1801 -5
- package/lib/contracts.js +2 -35
- package/lib/contracts2.d.ts +2 -0
- package/lib/contracts2.js +85 -0
- package/lib/contracts2.js.map +1 -0
- package/lib/index.d.ts +10 -3
- package/lib/index.js +3935 -22
- package/lib/index.js.map +1 -1
- package/package.json +3 -3
- package/lib/contracts.js.map +0 -1
package/lib/client.inner.cjs
CHANGED
|
@@ -1406,6 +1406,37 @@ function modelSettingsChangesIdentity(packageName) {
|
|
|
1406
1406
|
namespace: segment
|
|
1407
1407
|
};
|
|
1408
1408
|
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Shared "Session Management" settings section id. The section is provided by
|
|
1411
|
+
* shared infrastructure (see `registerSessionSettingsItem` in `client-utils`),
|
|
1412
|
+
* not by any one business plugin: every participant registers only a row into
|
|
1413
|
+
* {@link SESSION_SETTINGS_ITEM_SLOT}, so any enabled subset yields the same page.
|
|
1414
|
+
*/
|
|
1415
|
+
const SESSION_SETTINGS_SECTION_ID = "session-manager";
|
|
1416
|
+
/**
|
|
1417
|
+
* Child list slot declared by the shared Session Management page. Neutral
|
|
1418
|
+
* naming on purpose: participants never import each other, they only
|
|
1419
|
+
* contribute `{id, order}` rows into this slot while the page is alive.
|
|
1420
|
+
*/
|
|
1421
|
+
const SESSION_SETTINGS_ITEM_SLOT = "dsh.session-settings.item";
|
|
1422
|
+
/**
|
|
1423
|
+
* Host-owned nav icon seat: a root-scoped list where each entry's `id` must
|
|
1424
|
+
* equal the `settings.section` id it decorates. Owner props are
|
|
1425
|
+
* `{size, active}`; icons stay `aria-hidden` so the section label remains the
|
|
1426
|
+
* accessible name. Contributors into a shared section may register the same
|
|
1427
|
+
* icon `id` with different priorities — the ledger keeps every entry while the
|
|
1428
|
+
* host renders one icon per section.
|
|
1429
|
+
*/
|
|
1430
|
+
const SETTINGS_SECTION_ICON_SLOT = "settings.section.icon";
|
|
1431
|
+
/** Authenticated host-RPC channel for get/set of the shared default. */
|
|
1432
|
+
const AUXILIARY_MODEL_CHANNEL = "/dsh-auxiliary-model";
|
|
1433
|
+
/** Model-settings namespace under which the shared default is published. */
|
|
1434
|
+
const AUXILIARY_MODEL_NS = "@klarkxy/dsh-plugin-kit/auxiliary-model";
|
|
1435
|
+
/**
|
|
1436
|
+
* Typert identity of the shared change stream. Mounted on the root context
|
|
1437
|
+
* with ownerNs filtering, so frames only carry {@link AUXILIARY_MODEL_NS}.
|
|
1438
|
+
*/
|
|
1439
|
+
const AUXILIARY_MODEL_CHANGES_IDENTITY = modelSettingsChangesIdentity("@klarkxy/dsh-plugin-kit");
|
|
1409
1440
|
//#endregion
|
|
1410
1441
|
//#region ../dsh-plugin-kit/src/model-settings-client.ts
|
|
1411
1442
|
/** This stream carries invalidations only; ready is never a settings snapshot. */
|
|
@@ -1575,6 +1606,604 @@ function createModelSettingsSubscriber(ctx, packageName, identity = modelSetting
|
|
|
1575
1606
|
} });
|
|
1576
1607
|
}
|
|
1577
1608
|
//#endregion
|
|
1609
|
+
//#region ../dsh-plugin-kit/src/client-utils.ts
|
|
1610
|
+
/** One `<svg>` with the shared icon chrome; children are plain elements. */
|
|
1611
|
+
function lineIcon(children) {
|
|
1612
|
+
return function SettingsSectionIcon(props) {
|
|
1613
|
+
return (0, react.createElement)("svg", {
|
|
1614
|
+
"aria-hidden": "true",
|
|
1615
|
+
width: props.size ?? 16,
|
|
1616
|
+
height: props.size ?? 16,
|
|
1617
|
+
viewBox: "0 0 16 16",
|
|
1618
|
+
fill: "none",
|
|
1619
|
+
stroke: "currentColor",
|
|
1620
|
+
strokeWidth: 1,
|
|
1621
|
+
strokeLinecap: "round",
|
|
1622
|
+
strokeLinejoin: "round"
|
|
1623
|
+
}, ...children.map(([tag, attrs], index) => (0, react.createElement)(tag, {
|
|
1624
|
+
key: index,
|
|
1625
|
+
...attrs
|
|
1626
|
+
})));
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
/** Chat bubble for the shared Session Management section. */
|
|
1630
|
+
const IconSessionSettings = lineIcon([["path", { d: "M8.5 13a5.5 5.5 0 0 0 5.5-5.5A5.5 5.5 0 0 0 8.5 2 5.5 5.5 0 0 0 3 7.5a5.48 5.48 0 0 0 .9 3L2.8 12.6a.5.5 0 0 0 .62.62l2.9-.98a5.48 5.48 0 0 0 2.18.76Z" }]]);
|
|
1631
|
+
/** Private Cordis rendezvous; every bundled copy of this helper resolves the same root service. */
|
|
1632
|
+
const SESSION_SETTINGS_PAGE_SERVICE = "dshSessionSettingsPage";
|
|
1633
|
+
/** The page component renders only its declared child list: items paint their own titles and styles. */
|
|
1634
|
+
function SessionSettingsPageView(props) {
|
|
1635
|
+
return props.renderSlot(SESSION_SETTINGS_ITEM_SLOT, {});
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Acquire the root-owned page service, creating and providing it on first use.
|
|
1639
|
+
* One counter covers participants, the section wait and the provider: the last
|
|
1640
|
+
* release removes the wait and then the provider synchronously (Cordis deletes
|
|
1641
|
+
* the impl before its async disposer awaits), so a later acquire sees a clean
|
|
1642
|
+
* get() miss and can re-establish the page.
|
|
1643
|
+
*/
|
|
1644
|
+
function acquireSessionSettingsPage(root) {
|
|
1645
|
+
const existing = root.get(SESSION_SETTINGS_PAGE_SERVICE, false);
|
|
1646
|
+
if (existing) return existing;
|
|
1647
|
+
const slots = root.slots;
|
|
1648
|
+
let users = 0;
|
|
1649
|
+
let stopPage;
|
|
1650
|
+
let unprovide;
|
|
1651
|
+
const page = {
|
|
1652
|
+
retain() {
|
|
1653
|
+
users += 1;
|
|
1654
|
+
if (!stopPage) stopPage = slots.inject("settings.section", () => slots.register({
|
|
1655
|
+
name: "settings.section",
|
|
1656
|
+
id: SESSION_SETTINGS_SECTION_ID,
|
|
1657
|
+
order: 30,
|
|
1658
|
+
label: () => {
|
|
1659
|
+
const locale = root.get("locale", false);
|
|
1660
|
+
return String(locale?.getSnapshot?.().active ?? "").startsWith("zh") ? "会话管理" : "Session Management";
|
|
1661
|
+
},
|
|
1662
|
+
children: { [SESSION_SETTINGS_ITEM_SLOT]: {
|
|
1663
|
+
kind: "list",
|
|
1664
|
+
scope: "root"
|
|
1665
|
+
} }
|
|
1666
|
+
}, SessionSettingsPageView));
|
|
1667
|
+
},
|
|
1668
|
+
release() {
|
|
1669
|
+
if (users <= 0) return;
|
|
1670
|
+
users -= 1;
|
|
1671
|
+
if (users > 0) return;
|
|
1672
|
+
stopPage?.();
|
|
1673
|
+
stopPage = void 0;
|
|
1674
|
+
unprovide?.();
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
unprovide = root.provide(SESSION_SETTINGS_PAGE_SERVICE, page);
|
|
1678
|
+
return page;
|
|
1679
|
+
}
|
|
1680
|
+
/**
|
|
1681
|
+
* Contribute one row to the shared Session Management settings page.
|
|
1682
|
+
*
|
|
1683
|
+
* `ctx` is the plugin's own context: the row's slot wait unloads with the
|
|
1684
|
+
* plugin, while the page itself is retained on the shared root context for as
|
|
1685
|
+
* long as any contributor lives. There is no fallback seat — when every
|
|
1686
|
+
* contributor is gone the section disappears with the page. Returns a disposer.
|
|
1687
|
+
* Shared root-owned infrastructure (the auxiliary-model card) calls this on
|
|
1688
|
+
* `ctx.root` so its row, icon and page share are owned by the root lifetime
|
|
1689
|
+
* and released by the returned disposer, not by any plugin fiber.
|
|
1690
|
+
*/
|
|
1691
|
+
function registerSessionSettingsItem(ctx, item, component) {
|
|
1692
|
+
const slots = ctx.slots;
|
|
1693
|
+
const root = ctx.root;
|
|
1694
|
+
const page = acquireSessionSettingsPage(root);
|
|
1695
|
+
let stopItem;
|
|
1696
|
+
let stopIcon;
|
|
1697
|
+
try {
|
|
1698
|
+
page.retain();
|
|
1699
|
+
stopItem = slots.inject(SESSION_SETTINGS_ITEM_SLOT, () => slots.register({
|
|
1700
|
+
name: SESSION_SETTINGS_ITEM_SLOT,
|
|
1701
|
+
...item
|
|
1702
|
+
}, component));
|
|
1703
|
+
stopIcon = slots.inject(SETTINGS_SECTION_ICON_SLOT, () => slots.register({
|
|
1704
|
+
name: SETTINGS_SECTION_ICON_SLOT,
|
|
1705
|
+
id: SESSION_SETTINGS_SECTION_ID,
|
|
1706
|
+
order: item.order,
|
|
1707
|
+
priority: item.order
|
|
1708
|
+
}, IconSessionSettings));
|
|
1709
|
+
} catch (error) {
|
|
1710
|
+
stopIcon?.();
|
|
1711
|
+
stopItem?.();
|
|
1712
|
+
page.release();
|
|
1713
|
+
throw error;
|
|
1714
|
+
}
|
|
1715
|
+
let disposed = false;
|
|
1716
|
+
return () => {
|
|
1717
|
+
if (disposed) return;
|
|
1718
|
+
disposed = true;
|
|
1719
|
+
stopIcon?.();
|
|
1720
|
+
stopItem?.();
|
|
1721
|
+
page.release();
|
|
1722
|
+
};
|
|
1723
|
+
}
|
|
1724
|
+
//#endregion
|
|
1725
|
+
//#region ../dsh-plugin-kit/src/auxiliary-model-client.tsx
|
|
1726
|
+
const AUXILIARY_MODEL_PACKAGE = "@klarkxy/dsh-plugin-kit";
|
|
1727
|
+
const AUXILIARY_MODEL_SEAT_ID = "auxiliary-model";
|
|
1728
|
+
/** Private Cordis rendezvous; every bundled copy resolves the same root service. */
|
|
1729
|
+
const AUXILIARY_MODEL_CARD_SERVICE = "dshAuxiliaryModelSettingsCard";
|
|
1730
|
+
/** Theme-token-only stylesheet: officialUiCss scope plus the shared ModelMenu recipe. */
|
|
1731
|
+
const auxiliaryModelCss = `${officialUiCss("ams-root")}${modelMenuCss}
|
|
1732
|
+
.ams-root .ams-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
|
|
1733
|
+
`;
|
|
1734
|
+
const auxiliaryCopy = {
|
|
1735
|
+
zh: {
|
|
1736
|
+
title: "默认辅助模型",
|
|
1737
|
+
help: "各辅助功能(如标题生成、摘要)没有单独指定模型时使用的模型;仅影响声明使用它的功能。",
|
|
1738
|
+
model: "辅助模型",
|
|
1739
|
+
modelDefault: "跟随会话或宿主默认",
|
|
1740
|
+
modelHint: "不选择模型时,各功能按自己的规则决定对话模型。",
|
|
1741
|
+
effortDefault: "跟随模型默认",
|
|
1742
|
+
loading: "正在读取设置…",
|
|
1743
|
+
failed: "无法读取模型设置。",
|
|
1744
|
+
stale: "设置已被其他窗口更新,请刷新后重试。",
|
|
1745
|
+
saveFailed: "保存失败,原设置未更改。请重试。",
|
|
1746
|
+
saved: "已保存。",
|
|
1747
|
+
retry: "重试",
|
|
1748
|
+
refresh: "刷新设置",
|
|
1749
|
+
syncUnavailable: "自动刷新不可用,请手动刷新设置。"
|
|
1750
|
+
},
|
|
1751
|
+
en: {
|
|
1752
|
+
title: "Default Auxiliary Model",
|
|
1753
|
+
help: "The model used by auxiliary features (such as title generation or summarizing) that do not name their own model; only features that opted in are affected.",
|
|
1754
|
+
model: "Auxiliary model",
|
|
1755
|
+
modelDefault: "Follow session or host default",
|
|
1756
|
+
modelHint: "With no model selected, each feature follows its own rules for the conversation model.",
|
|
1757
|
+
effortDefault: "Follow model default",
|
|
1758
|
+
loading: "Loading settings…",
|
|
1759
|
+
failed: "Could not load model settings.",
|
|
1760
|
+
stale: "Settings were changed elsewhere; refresh and retry.",
|
|
1761
|
+
saveFailed: "Save failed; the previous setting was not changed. Try again.",
|
|
1762
|
+
saved: "Saved.",
|
|
1763
|
+
retry: "Retry",
|
|
1764
|
+
refresh: "Refresh settings",
|
|
1765
|
+
syncUnavailable: "Automatic refresh unavailable; refresh settings manually."
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1768
|
+
/** A failed RPC keeps its code so the card can translate known rejections. */
|
|
1769
|
+
var AuxiliaryRpcError = class extends Error {
|
|
1770
|
+
code;
|
|
1771
|
+
constructor(code, message) {
|
|
1772
|
+
super(message);
|
|
1773
|
+
this.code = code;
|
|
1774
|
+
this.name = "AuxiliaryRpcError";
|
|
1775
|
+
}
|
|
1776
|
+
};
|
|
1777
|
+
/** Host UI language, following live switches with the host's English fallback. */
|
|
1778
|
+
function useAuxiliaryLocale(client) {
|
|
1779
|
+
const subscribe = (0, react.useCallback)((listener) => client.locale?.subscribe(listener) ?? (() => {}), [client]);
|
|
1780
|
+
const snapshot = (0, react.useCallback)(() => client.locale?.getSnapshot().active ?? "en", [client]);
|
|
1781
|
+
return String((0, react.useSyncExternalStore)(subscribe, snapshot, snapshot)).startsWith("zh") ? "zh" : "en";
|
|
1782
|
+
}
|
|
1783
|
+
function isAbort(error) {
|
|
1784
|
+
return error instanceof Error && (error.name === "AbortError" || /abort|cancel/i.test(error.message));
|
|
1785
|
+
}
|
|
1786
|
+
function isStaleRejection(error) {
|
|
1787
|
+
if (!(error instanceof AuxiliaryRpcError)) return false;
|
|
1788
|
+
return error.code === "stale" || error.code === "auxiliary-model/conflict" || /changed|revision|reload|stale|conflict/i.test(error.message);
|
|
1789
|
+
}
|
|
1790
|
+
async function callAuxiliaryRpc(client, endpoint, payload = {}) {
|
|
1791
|
+
const result = await client.connection.rpc.call(AUXILIARY_MODEL_CHANNEL, endpoint, payload);
|
|
1792
|
+
if (!result || result.ok !== true) {
|
|
1793
|
+
const error = result && "error" in result ? result.error : void 0;
|
|
1794
|
+
throw new AuxiliaryRpcError(error?.code ?? "unknown", error?.message ?? `RPC ${endpoint} failed`);
|
|
1795
|
+
}
|
|
1796
|
+
return result.value;
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* The shared card. All state comes from the shared host service over the
|
|
1800
|
+
* plugin-kit RPC channel; the card only renders the stored row.
|
|
1801
|
+
*/
|
|
1802
|
+
function AuxiliaryModelCard({ client, subscribe }) {
|
|
1803
|
+
const language = useAuxiliaryLocale(client);
|
|
1804
|
+
const text = auxiliaryCopy[language];
|
|
1805
|
+
const [settings, setSettings] = (0, react.useState)();
|
|
1806
|
+
const catalogState = useModelCatalog((0, react.useCallback)(() => readModelCatalog(client), [client]), { subscribe: (0, react.useCallback)((change) => client.connection.generation?.subscribe(change), [client]) });
|
|
1807
|
+
const { refresh: refreshCatalog } = catalogState;
|
|
1808
|
+
const choices = parseModelMenuChoices(catalogState.catalog, settings?.model ?? void 0);
|
|
1809
|
+
const [menuOpen, setMenuOpen] = (0, react.useState)(false);
|
|
1810
|
+
const [error, setError] = (0, react.useState)("");
|
|
1811
|
+
const [note, setNote] = (0, react.useState)(false);
|
|
1812
|
+
const [busy, setBusy] = (0, react.useState)(false);
|
|
1813
|
+
const [syncError, setSyncError] = (0, react.useState)("");
|
|
1814
|
+
const operation = (0, react.useRef)({
|
|
1815
|
+
epoch: 0,
|
|
1816
|
+
active: false,
|
|
1817
|
+
reading: false,
|
|
1818
|
+
writing: false,
|
|
1819
|
+
pending: false
|
|
1820
|
+
});
|
|
1821
|
+
const refreshRef = (0, react.useRef)(() => {});
|
|
1822
|
+
const refresh = () => {
|
|
1823
|
+
const state = operation.current;
|
|
1824
|
+
if (!state.active) return;
|
|
1825
|
+
if (state.reading || state.writing) {
|
|
1826
|
+
state.pending = true;
|
|
1827
|
+
return;
|
|
1828
|
+
}
|
|
1829
|
+
state.pending = false;
|
|
1830
|
+
state.reading = true;
|
|
1831
|
+
setBusy(true);
|
|
1832
|
+
const epoch = state.epoch;
|
|
1833
|
+
callAuxiliaryRpc(client, "defaults.get").then((next) => {
|
|
1834
|
+
if (state.active && state.epoch === epoch && !state.pending) {
|
|
1835
|
+
setSettings(next);
|
|
1836
|
+
setError("");
|
|
1837
|
+
}
|
|
1838
|
+
}).catch((reason) => {
|
|
1839
|
+
if (state.active && state.epoch === epoch && !state.pending && !isAbort(reason)) setError("failed");
|
|
1840
|
+
}).finally(() => {
|
|
1841
|
+
if (!state.active || state.epoch !== epoch) return;
|
|
1842
|
+
state.reading = false;
|
|
1843
|
+
setBusy(false);
|
|
1844
|
+
if (state.pending) refreshRef.current();
|
|
1845
|
+
});
|
|
1846
|
+
};
|
|
1847
|
+
refreshRef.current = refresh;
|
|
1848
|
+
(0, react.useEffect)(() => {
|
|
1849
|
+
const state = operation.current;
|
|
1850
|
+
state.epoch++;
|
|
1851
|
+
state.active = true;
|
|
1852
|
+
state.reading = false;
|
|
1853
|
+
state.writing = false;
|
|
1854
|
+
state.pending = false;
|
|
1855
|
+
setBusy(false);
|
|
1856
|
+
setSettings(void 0);
|
|
1857
|
+
setError("");
|
|
1858
|
+
setNote(false);
|
|
1859
|
+
refreshRef.current();
|
|
1860
|
+
return () => {
|
|
1861
|
+
state.active = false;
|
|
1862
|
+
state.epoch++;
|
|
1863
|
+
};
|
|
1864
|
+
}, [client]);
|
|
1865
|
+
(0, react.useEffect)(() => {
|
|
1866
|
+
setSyncError("");
|
|
1867
|
+
let live = true;
|
|
1868
|
+
let stop;
|
|
1869
|
+
try {
|
|
1870
|
+
stop = subscribe?.(() => {
|
|
1871
|
+
if (live) {
|
|
1872
|
+
setSyncError("");
|
|
1873
|
+
refreshCatalog();
|
|
1874
|
+
refreshRef.current();
|
|
1875
|
+
}
|
|
1876
|
+
}, (message) => {
|
|
1877
|
+
if (live) setSyncError(message);
|
|
1878
|
+
});
|
|
1879
|
+
} catch {
|
|
1880
|
+
if (live) setSyncError("Model settings sync service unavailable");
|
|
1881
|
+
}
|
|
1882
|
+
return () => {
|
|
1883
|
+
live = false;
|
|
1884
|
+
try {
|
|
1885
|
+
stop?.();
|
|
1886
|
+
} catch {}
|
|
1887
|
+
};
|
|
1888
|
+
}, [subscribe, refreshCatalog]);
|
|
1889
|
+
const save = (0, react.useCallback)((model) => {
|
|
1890
|
+
const state = operation.current;
|
|
1891
|
+
if (!settings || !state.active || state.writing || state.reading) return;
|
|
1892
|
+
state.writing = true;
|
|
1893
|
+
const epoch = state.epoch;
|
|
1894
|
+
setBusy(true);
|
|
1895
|
+
setNote(false);
|
|
1896
|
+
setError("");
|
|
1897
|
+
callAuxiliaryRpc(client, "defaults.set", {
|
|
1898
|
+
model: model ?? {
|
|
1899
|
+
provider: "",
|
|
1900
|
+
model: ""
|
|
1901
|
+
},
|
|
1902
|
+
expectedRevision: settings.revision
|
|
1903
|
+
}).then((next) => {
|
|
1904
|
+
if (state.active && state.epoch === epoch) {
|
|
1905
|
+
setSettings(next);
|
|
1906
|
+
setNote(true);
|
|
1907
|
+
refreshCatalog();
|
|
1908
|
+
}
|
|
1909
|
+
}).catch((reason) => {
|
|
1910
|
+
if (state.active && state.epoch === epoch) setError(isStaleRejection(reason) ? "stale" : "saveFailed");
|
|
1911
|
+
}).finally(() => {
|
|
1912
|
+
if (!state.active || state.epoch !== epoch) return;
|
|
1913
|
+
state.writing = false;
|
|
1914
|
+
setBusy(false);
|
|
1915
|
+
if (state.pending) refreshRef.current();
|
|
1916
|
+
});
|
|
1917
|
+
}, [
|
|
1918
|
+
client,
|
|
1919
|
+
settings,
|
|
1920
|
+
refreshCatalog
|
|
1921
|
+
]);
|
|
1922
|
+
const locked = busy;
|
|
1923
|
+
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1924
|
+
className: "ams-root dsh-ui-card",
|
|
1925
|
+
"data-testid": "auxiliary-model-settings",
|
|
1926
|
+
children: [
|
|
1927
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1928
|
+
className: "dsh-ui-heading",
|
|
1929
|
+
children: text.title
|
|
1930
|
+
}),
|
|
1931
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1932
|
+
className: "dsh-ui-help",
|
|
1933
|
+
children: text.help
|
|
1934
|
+
}),
|
|
1935
|
+
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1936
|
+
role: "alert",
|
|
1937
|
+
className: "dsh-ui-error",
|
|
1938
|
+
children: text[error]
|
|
1939
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1940
|
+
role: "status",
|
|
1941
|
+
className: "dsh-ui-hint",
|
|
1942
|
+
children: text.loading
|
|
1943
|
+
}),
|
|
1944
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1945
|
+
role: "status",
|
|
1946
|
+
className: "dsh-ui-hint",
|
|
1947
|
+
children: text.syncUnavailable
|
|
1948
|
+
}),
|
|
1949
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1950
|
+
className: "dsh-ui-actions",
|
|
1951
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
1952
|
+
variant: "ghost",
|
|
1953
|
+
size: "sm",
|
|
1954
|
+
disabled: locked,
|
|
1955
|
+
onClick: () => refreshRef.current(),
|
|
1956
|
+
children: text.retry
|
|
1957
|
+
})
|
|
1958
|
+
})
|
|
1959
|
+
]
|
|
1960
|
+
});
|
|
1961
|
+
const current = settings.model ?? {
|
|
1962
|
+
provider: "",
|
|
1963
|
+
model: ""
|
|
1964
|
+
};
|
|
1965
|
+
const follow = !current.provider || !current.model;
|
|
1966
|
+
const selected = follow ? void 0 : choices.find((item) => item.provider === current.provider && item.model === current.model);
|
|
1967
|
+
const efforts = follow ? [] : modelMenuEffortOptions(selected, current.reasoningEffort);
|
|
1968
|
+
const currentLabel = follow ? text.modelDefault : selected?.label ?? `${current.provider} / ${current.model}`;
|
|
1969
|
+
const selectedEffort = efforts.find((item) => item.id === (current.reasoningEffort || ""));
|
|
1970
|
+
const effortName = selectedEffort ? modelMenuEffortLabel(selectedEffort, language) : void 0;
|
|
1971
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1972
|
+
className: "ams-root dsh-ui-card",
|
|
1973
|
+
"data-testid": "auxiliary-model-settings",
|
|
1974
|
+
children: [
|
|
1975
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1976
|
+
className: "dsh-ui-heading",
|
|
1977
|
+
children: text.title
|
|
1978
|
+
}),
|
|
1979
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1980
|
+
className: "dsh-ui-help",
|
|
1981
|
+
children: text.help
|
|
1982
|
+
}),
|
|
1983
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1984
|
+
className: "dsh-ui-field",
|
|
1985
|
+
children: [
|
|
1986
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1987
|
+
className: "dsh-ui-label",
|
|
1988
|
+
children: text.model
|
|
1989
|
+
}),
|
|
1990
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelCatalogStatus, {
|
|
1991
|
+
state: catalogState,
|
|
1992
|
+
locale: language
|
|
1993
|
+
}),
|
|
1994
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
1995
|
+
locale: language,
|
|
1996
|
+
open: menuOpen && !locked,
|
|
1997
|
+
onOpenChange: (next) => setMenuOpen(next && !locked),
|
|
1998
|
+
listClassName: "ams-root ams-menu-portal",
|
|
1999
|
+
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2000
|
+
type: "button",
|
|
2001
|
+
className: "dsh-model-menu-trigger",
|
|
2002
|
+
disabled: locked,
|
|
2003
|
+
"aria-label": `${text.model}: ${currentLabel}`,
|
|
2004
|
+
children: [
|
|
2005
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2006
|
+
className: "dsh-model-menu-triggerLabel",
|
|
2007
|
+
children: currentLabel
|
|
2008
|
+
}),
|
|
2009
|
+
!follow && efforts.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2010
|
+
className: "dsh-model-menu-triggerEffort",
|
|
2011
|
+
children: effortName ?? text.effortDefault
|
|
2012
|
+
}) : null,
|
|
2013
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen && !locked ? " dsh-model-menu-chevronOpen" : ""}` })
|
|
2014
|
+
]
|
|
2015
|
+
}),
|
|
2016
|
+
choices,
|
|
2017
|
+
selected: follow ? void 0 : {
|
|
2018
|
+
provider: current.provider,
|
|
2019
|
+
model: current.model
|
|
2020
|
+
},
|
|
2021
|
+
onPick: (picked) => save({
|
|
2022
|
+
provider: picked.provider,
|
|
2023
|
+
model: picked.model
|
|
2024
|
+
}),
|
|
2025
|
+
leading: [{
|
|
2026
|
+
id: "default",
|
|
2027
|
+
label: text.modelDefault,
|
|
2028
|
+
selected: follow
|
|
2029
|
+
}],
|
|
2030
|
+
onPickLeading: () => save(null),
|
|
2031
|
+
efforts,
|
|
2032
|
+
selectedEffort: current.reasoningEffort || "",
|
|
2033
|
+
onPickEffort: (effort) => save(follow ? null : {
|
|
2034
|
+
provider: current.provider,
|
|
2035
|
+
model: current.model,
|
|
2036
|
+
...effort ? { reasoningEffort: effort } : {}
|
|
2037
|
+
}),
|
|
2038
|
+
modelLabel: text.model,
|
|
2039
|
+
modelValue: currentLabel,
|
|
2040
|
+
defaultEffortLabel: text.effortDefault
|
|
2041
|
+
}),
|
|
2042
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2043
|
+
className: "dsh-ui-hint",
|
|
2044
|
+
children: text.modelHint
|
|
2045
|
+
})
|
|
2046
|
+
]
|
|
2047
|
+
}),
|
|
2048
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2049
|
+
className: "ams-row",
|
|
2050
|
+
children: [
|
|
2051
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2052
|
+
role: "status",
|
|
2053
|
+
className: "dsh-ui-hint",
|
|
2054
|
+
children: text.syncUnavailable
|
|
2055
|
+
}),
|
|
2056
|
+
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2057
|
+
role: "alert",
|
|
2058
|
+
className: "dsh-ui-error",
|
|
2059
|
+
children: text[error]
|
|
2060
|
+
}),
|
|
2061
|
+
note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2062
|
+
role: "status",
|
|
2063
|
+
className: "dsh-ui-notice",
|
|
2064
|
+
children: text.saved
|
|
2065
|
+
}),
|
|
2066
|
+
error || syncError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2067
|
+
variant: "ghost",
|
|
2068
|
+
size: "sm",
|
|
2069
|
+
disabled: locked,
|
|
2070
|
+
onClick: () => refreshRef.current(),
|
|
2071
|
+
children: text.refresh
|
|
2072
|
+
}) : null
|
|
2073
|
+
]
|
|
2074
|
+
})
|
|
2075
|
+
]
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
const AUXILIARY_MODEL_WATCH_COORDINATOR = "dshAuxiliaryModelWatchCoordinator";
|
|
2079
|
+
function acquireWatchCoordinator(root) {
|
|
2080
|
+
const existing = root.get(AUXILIARY_MODEL_WATCH_COORDINATOR, false);
|
|
2081
|
+
if (existing) return existing;
|
|
2082
|
+
let unprovide;
|
|
2083
|
+
const coordinator = {
|
|
2084
|
+
tail: Promise.resolve(),
|
|
2085
|
+
active: 0,
|
|
2086
|
+
mount() {
|
|
2087
|
+
coordinator.active += 1;
|
|
2088
|
+
const previousTail = coordinator.tail;
|
|
2089
|
+
const lease = {
|
|
2090
|
+
closed: false,
|
|
2091
|
+
done() {
|
|
2092
|
+
if (lease.closed) return;
|
|
2093
|
+
lease.closed = true;
|
|
2094
|
+
coordinator.active -= 1;
|
|
2095
|
+
const owned = lease.fiber;
|
|
2096
|
+
lease.fiber = void 0;
|
|
2097
|
+
coordinator.tail = coordinator.tail.then(() => Promise.resolve(owned?.dispose()).catch(() => {}));
|
|
2098
|
+
coordinator.tail.then(() => {
|
|
2099
|
+
if (coordinator.active > 0) return;
|
|
2100
|
+
try {
|
|
2101
|
+
if (root.get(AUXILIARY_MODEL_WATCH_COORDINATOR, false) === coordinator) Promise.resolve(unprovide?.()).catch(() => {});
|
|
2102
|
+
} catch {}
|
|
2103
|
+
});
|
|
2104
|
+
},
|
|
2105
|
+
fiber: void 0
|
|
2106
|
+
};
|
|
2107
|
+
previousTail.then(() => {
|
|
2108
|
+
if (lease.closed) return;
|
|
2109
|
+
try {
|
|
2110
|
+
lease.fiber = root.inject(["remote"], (injected) => {
|
|
2111
|
+
const scope = injected;
|
|
2112
|
+
const mounting = scope.remote.$mount(modelSettingsChangesRemote(AUXILIARY_MODEL_PACKAGE, AUXILIARY_MODEL_CHANGES_IDENTITY));
|
|
2113
|
+
mounting.catch(() => {});
|
|
2114
|
+
scope.effect(() => async () => {
|
|
2115
|
+
await (await mounting.catch(() => void 0))?.();
|
|
2116
|
+
});
|
|
2117
|
+
});
|
|
2118
|
+
} catch {}
|
|
2119
|
+
});
|
|
2120
|
+
return lease;
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
unprovide = root.provide(AUXILIARY_MODEL_WATCH_COORDINATOR, coordinator);
|
|
2124
|
+
return coordinator;
|
|
2125
|
+
}
|
|
2126
|
+
/**
|
|
2127
|
+
* Acquire the root-owned card service, creating it on first use. One counter
|
|
2128
|
+
* covers participants, the seat wait and the change-stream mount: the last
|
|
2129
|
+
* release removes the wait, then the watch contribution, then the provider
|
|
2130
|
+
* itself synchronously (Cordis deletes the impl before its async disposer
|
|
2131
|
+
* awaits), so a later acquire sees a clean get() miss and can re-establish.
|
|
2132
|
+
*/
|
|
2133
|
+
function acquireAuxiliaryModelCard(root, ctx) {
|
|
2134
|
+
const existing = root.get(AUXILIARY_MODEL_CARD_SERVICE, false);
|
|
2135
|
+
if (existing) return existing;
|
|
2136
|
+
let users = 0;
|
|
2137
|
+
let styleElement;
|
|
2138
|
+
let stopItem;
|
|
2139
|
+
let unprovide;
|
|
2140
|
+
const watchLease = acquireWatchCoordinator(root).mount();
|
|
2141
|
+
const subscriber = createModelSettingsSubscriber(ctx.root, AUXILIARY_MODEL_PACKAGE, AUXILIARY_MODEL_CHANGES_IDENTITY, (ns) => ns === AUXILIARY_MODEL_NS);
|
|
2142
|
+
const render = () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AuxiliaryModelCard, {
|
|
2143
|
+
client: root,
|
|
2144
|
+
subscribe: subscriber.subscribe
|
|
2145
|
+
});
|
|
2146
|
+
const service = {
|
|
2147
|
+
retain() {
|
|
2148
|
+
users += 1;
|
|
2149
|
+
if (styleElement === void 0 && typeof document !== "undefined") {
|
|
2150
|
+
styleElement = document.createElement("style");
|
|
2151
|
+
styleElement.setAttribute("data-plugin", "dsh-auxiliary-model-shared");
|
|
2152
|
+
styleElement.textContent = auxiliaryModelCss;
|
|
2153
|
+
document.head.appendChild(styleElement);
|
|
2154
|
+
}
|
|
2155
|
+
if (!stopItem) stopItem = registerSessionSettingsItem(ctx.root, {
|
|
2156
|
+
id: AUXILIARY_MODEL_SEAT_ID,
|
|
2157
|
+
order: 55
|
|
2158
|
+
}, render);
|
|
2159
|
+
},
|
|
2160
|
+
release() {
|
|
2161
|
+
if (users <= 0) return;
|
|
2162
|
+
users -= 1;
|
|
2163
|
+
if (users > 0) return;
|
|
2164
|
+
stopItem?.();
|
|
2165
|
+
stopItem = void 0;
|
|
2166
|
+
styleElement?.remove();
|
|
2167
|
+
styleElement = void 0;
|
|
2168
|
+
watchLease.done();
|
|
2169
|
+
try {
|
|
2170
|
+
Promise.resolve(unprovide?.()).catch(() => {});
|
|
2171
|
+
} catch {}
|
|
2172
|
+
unprovide = void 0;
|
|
2173
|
+
}
|
|
2174
|
+
};
|
|
2175
|
+
unprovide = root.provide(AUXILIARY_MODEL_CARD_SERVICE, service);
|
|
2176
|
+
return service;
|
|
2177
|
+
}
|
|
2178
|
+
/**
|
|
2179
|
+
* Show the shared "default auxiliary model" card while the calling plugin is
|
|
2180
|
+
* enabled.
|
|
2181
|
+
*
|
|
2182
|
+
* `ctx` is the plugin's own client context: the card itself is retained on the
|
|
2183
|
+
* shared root context for as long as any participant lives, so an arbitrary
|
|
2184
|
+
* subset of enabled plugins yields the same single card. The mount's slot wait
|
|
2185
|
+
* and the change-stream contribution are owned by the root service — the first
|
|
2186
|
+
* participant's unload cannot take the card down. Returns a disposer; it may
|
|
2187
|
+
* be invoked by both ctx.effect and an explicit caller and releases at most
|
|
2188
|
+
* once, so a double call cannot unbalance the shared refcount.
|
|
2189
|
+
*/
|
|
2190
|
+
function registerAuxiliaryModelSettings(ctx) {
|
|
2191
|
+
const root = ctx.root;
|
|
2192
|
+
const service = acquireAuxiliaryModelCard(root, ctx);
|
|
2193
|
+
try {
|
|
2194
|
+
service.retain();
|
|
2195
|
+
} catch (error) {
|
|
2196
|
+
service.release();
|
|
2197
|
+
throw error;
|
|
2198
|
+
}
|
|
2199
|
+
let disposed = false;
|
|
2200
|
+
return () => {
|
|
2201
|
+
if (disposed) return;
|
|
2202
|
+
disposed = true;
|
|
2203
|
+
service.release();
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
//#endregion
|
|
1578
2207
|
//#region src/contracts.ts
|
|
1579
2208
|
const PLUGIN_NAME = "@klarkxy/dsh-git-commit";
|
|
1580
2209
|
const RPC_CHANNEL = "/dsh-git-commit";
|
|
@@ -1595,12 +2224,11 @@ const zh = {
|
|
|
1595
2224
|
commitAction: "分次提交全部工作区修改",
|
|
1596
2225
|
files: "个文件",
|
|
1597
2226
|
model: "模型",
|
|
1598
|
-
modelDefault: "默认模型",
|
|
1599
|
-
modelCustom: "插件设置",
|
|
1600
2227
|
filesCount: "({n} 个文件)",
|
|
1601
2228
|
confirmPrompt: "将把 {n} 个文件的全部修改分次提交到当前分支。",
|
|
1602
2229
|
confirm: "确认提交",
|
|
1603
2230
|
cancel: "取消",
|
|
2231
|
+
commitBlocked: "正在保存模型设置或上次保存未生效,恢复后才可提交",
|
|
1604
2232
|
loading: "正在读取仓库状态…",
|
|
1605
2233
|
running: "正在整理修改并分次提交…",
|
|
1606
2234
|
done: "已创建提交",
|
|
@@ -1626,7 +2254,7 @@ const zh = {
|
|
|
1626
2254
|
workspaceChanged: "工作区或分支已变更,请刷新后重试提交",
|
|
1627
2255
|
settingsUnavailable: "无法读取提交设置,请重新加载插件后重试",
|
|
1628
2256
|
invalidRequest: "提交请求无效,请刷新后重试",
|
|
1629
|
-
planUnavailable: "
|
|
2257
|
+
planUnavailable: "模型没有给出可用的提交方案,已取消本次提交,工作区没有被改动。换一个模型重试,或开启下方「模型不可用时仍然提交」。",
|
|
1630
2258
|
close: "关闭"
|
|
1631
2259
|
};
|
|
1632
2260
|
const en = {
|
|
@@ -1635,12 +2263,11 @@ const en = {
|
|
|
1635
2263
|
commitAction: "Commit all workspace changes",
|
|
1636
2264
|
files: "files",
|
|
1637
2265
|
model: "Model",
|
|
1638
|
-
modelDefault: "default model",
|
|
1639
|
-
modelCustom: "Plugin settings",
|
|
1640
2266
|
filesCount: " ({n} files)",
|
|
1641
2267
|
confirmPrompt: "All changes in {n} files will be committed to the current branch in one or more commits.",
|
|
1642
2268
|
confirm: "Commit now",
|
|
1643
2269
|
cancel: "Cancel",
|
|
2270
|
+
commitBlocked: "A settings write is pending or unrecovered; commits unlock once it settles",
|
|
1644
2271
|
loading: "Reading repository status…",
|
|
1645
2272
|
running: "Organizing changes into commits…",
|
|
1646
2273
|
done: "Commits created",
|
|
@@ -1666,7 +2293,7 @@ const en = {
|
|
|
1666
2293
|
workspaceChanged: "The workspace or branch changed; refresh and retry the commit",
|
|
1667
2294
|
settingsUnavailable: "Could not read commit settings; reload the plugin and retry",
|
|
1668
2295
|
invalidRequest: "Invalid commit request; refresh and retry",
|
|
1669
|
-
planUnavailable: "The model returned no usable commit plan, so the run was cancelled and your workspace is untouched. Try another model, or
|
|
2296
|
+
planUnavailable: "The model returned no usable commit plan, so the run was cancelled and your workspace is untouched. Try another model, or enable “Commit even without a model plan” below.",
|
|
1670
2297
|
close: "Close"
|
|
1671
2298
|
};
|
|
1672
2299
|
/** A failed RPC keeps its code so the panel can explain a rejection in its own words. */
|
|
@@ -1770,7 +2397,7 @@ function GitBranchIcon() {
|
|
|
1770
2397
|
]
|
|
1771
2398
|
});
|
|
1772
2399
|
}
|
|
1773
|
-
function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
2400
|
+
function GitCommitAction({ sessionId, useSession, call, t, client, subscribeSettings }) {
|
|
1774
2401
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1775
2402
|
const [status, setStatus] = (0, react.useState)();
|
|
1776
2403
|
const [loading, setLoading] = (0, react.useState)(false);
|
|
@@ -1778,6 +2405,29 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
1778
2405
|
const [result, setResult] = (0, react.useState)(null);
|
|
1779
2406
|
const [error, setError] = (0, react.useState)(null);
|
|
1780
2407
|
const [confirming, setConfirming] = (0, react.useState)(false);
|
|
2408
|
+
const pendingWrites = (0, react.useRef)(0);
|
|
2409
|
+
const writeFailed = (0, react.useRef)(false);
|
|
2410
|
+
const settingsDirty = (0, react.useRef)(false);
|
|
2411
|
+
const [settingsPending, setSettingsPending] = (0, react.useState)(false);
|
|
2412
|
+
const [settingsSeen, setSettingsSeen] = (0, react.useState)(false);
|
|
2413
|
+
const syncLock = (0, react.useCallback)(() => {
|
|
2414
|
+
const locked = pendingWrites.current > 0 || writeFailed.current;
|
|
2415
|
+
settingsDirty.current = locked;
|
|
2416
|
+
setSettingsPending(locked);
|
|
2417
|
+
}, []);
|
|
2418
|
+
const markWrite = (0, react.useCallback)((active) => {
|
|
2419
|
+
pendingWrites.current = Math.max(0, pendingWrites.current + (active ? 1 : -1));
|
|
2420
|
+
syncLock();
|
|
2421
|
+
}, [syncLock]);
|
|
2422
|
+
const markWriteFailed = (0, react.useCallback)(() => {
|
|
2423
|
+
writeFailed.current = true;
|
|
2424
|
+
syncLock();
|
|
2425
|
+
}, [syncLock]);
|
|
2426
|
+
const markSeen = (0, react.useCallback)(() => {
|
|
2427
|
+
writeFailed.current = false;
|
|
2428
|
+
setSettingsSeen(true);
|
|
2429
|
+
syncLock();
|
|
2430
|
+
}, [syncLock]);
|
|
1781
2431
|
const rootRef = (0, react.useRef)(null);
|
|
1782
2432
|
const triggerRef = (0, react.useRef)(null);
|
|
1783
2433
|
const panelRef = (0, react.useRef)(null);
|
|
@@ -1790,19 +2440,26 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
1790
2440
|
margin: 16
|
|
1791
2441
|
});
|
|
1792
2442
|
const sessionRunning = useSession((snapshot) => snapshot.running === true);
|
|
2443
|
+
const panelPortalRef = (0, react.useMemo)(() => ({ get current() {
|
|
2444
|
+
const panel = panelRef.current;
|
|
2445
|
+
if (!panel) return null;
|
|
2446
|
+
return { contains: (node) => panel.contains(node) || [...document.querySelectorAll(".gcm-menu-portal")].some((menu) => menu.contains(node)) };
|
|
2447
|
+
} }), []);
|
|
1793
2448
|
const changeOpen = (0, react.useCallback)((next) => {
|
|
2449
|
+
if (!next && pendingWrites.current > 0) return;
|
|
1794
2450
|
setOpen(next);
|
|
1795
2451
|
setConfirming(false);
|
|
1796
2452
|
if (next) {
|
|
1797
2453
|
setResult(null);
|
|
1798
2454
|
setError(null);
|
|
2455
|
+
setSettingsSeen(false);
|
|
1799
2456
|
}
|
|
1800
2457
|
}, []);
|
|
1801
2458
|
const positioned = Boolean(position);
|
|
1802
2459
|
(0, react.useEffect)(() => {
|
|
1803
2460
|
if (open && positioned) panelRef.current?.focus();
|
|
1804
2461
|
}, [open, positioned]);
|
|
1805
|
-
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, changeOpen,
|
|
2462
|
+
(0, _deepseek_ai_dsh_client_ui_primitives.useDismissOnOutsidePointer)(rootRef, open, changeOpen, panelPortalRef);
|
|
1806
2463
|
(0, react.useEffect)(() => {
|
|
1807
2464
|
generationRef.current += 1;
|
|
1808
2465
|
setOpen(false);
|
|
@@ -1810,6 +2467,11 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
1810
2467
|
setResult(null);
|
|
1811
2468
|
setError(null);
|
|
1812
2469
|
setRunning(false);
|
|
2470
|
+
pendingWrites.current = 0;
|
|
2471
|
+
writeFailed.current = false;
|
|
2472
|
+
settingsDirty.current = false;
|
|
2473
|
+
setSettingsPending(false);
|
|
2474
|
+
setSettingsSeen(false);
|
|
1813
2475
|
}, [sessionId]);
|
|
1814
2476
|
const loadStatus = (0, react.useCallback)(async () => {
|
|
1815
2477
|
const generation = generationRef.current;
|
|
@@ -1844,6 +2506,10 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
1844
2506
|
}, [open, changeOpen]);
|
|
1845
2507
|
const runCommit = (0, react.useCallback)(async () => {
|
|
1846
2508
|
const generation = generationRef.current;
|
|
2509
|
+
if (settingsDirty.current || client && !settingsSeen) {
|
|
2510
|
+
setConfirming(false);
|
|
2511
|
+
return;
|
|
2512
|
+
}
|
|
1847
2513
|
setConfirming(false);
|
|
1848
2514
|
setRunning(true);
|
|
1849
2515
|
setResult(null);
|
|
@@ -1865,10 +2531,15 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
1865
2531
|
}
|
|
1866
2532
|
}
|
|
1867
2533
|
}
|
|
1868
|
-
}, [
|
|
2534
|
+
}, [
|
|
2535
|
+
call,
|
|
2536
|
+
sessionId,
|
|
2537
|
+
client,
|
|
2538
|
+
settingsSeen
|
|
2539
|
+
]);
|
|
1869
2540
|
const available = status?.available === true;
|
|
1870
2541
|
const workspaceCount = status?.workspace.files ?? 0;
|
|
1871
|
-
const canCommit = available && !running && !sessionRunning && workspaceCount > 0;
|
|
2542
|
+
const canCommit = available && !running && !sessionRunning && workspaceCount > 0 && !settingsPending && (!client || settingsSeen);
|
|
1872
2543
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1873
2544
|
ref: rootRef,
|
|
1874
2545
|
className: "gcm-root",
|
|
@@ -1938,33 +2609,21 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
1938
2609
|
role: "alert",
|
|
1939
2610
|
children: errorText(t, error)
|
|
1940
2611
|
}),
|
|
2612
|
+
client ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitCommitSettingsPanel, {
|
|
2613
|
+
client,
|
|
2614
|
+
subscribeSettings,
|
|
2615
|
+
disabled: running,
|
|
2616
|
+
onSaving: markWrite,
|
|
2617
|
+
onWriteFailed: markWriteFailed,
|
|
2618
|
+
onReady: markSeen,
|
|
2619
|
+
onSaved: () => void loadStatus()
|
|
2620
|
+
}) : null,
|
|
1941
2621
|
status && !available && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1942
2622
|
className: "dsh-ui-meta",
|
|
1943
2623
|
role: "status",
|
|
1944
2624
|
children: reasonText(t, status)
|
|
1945
2625
|
}),
|
|
1946
2626
|
available && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1947
|
-
status?.model ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
1948
|
-
className: "gcm-model",
|
|
1949
|
-
children: [
|
|
1950
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1951
|
-
className: "dsh-ui-hint",
|
|
1952
|
-
children: t("model")
|
|
1953
|
-
}),
|
|
1954
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1955
|
-
className: "dsh-ui-truncate",
|
|
1956
|
-
children: [
|
|
1957
|
-
status.model.provider,
|
|
1958
|
-
"/",
|
|
1959
|
-
status.model.model
|
|
1960
|
-
]
|
|
1961
|
-
}),
|
|
1962
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tag, {
|
|
1963
|
-
tone: status.model.source === "page" ? "info" : "neutral",
|
|
1964
|
-
children: status.model.source === "page" ? t("modelCustom") : t("modelDefault")
|
|
1965
|
-
})
|
|
1966
|
-
]
|
|
1967
|
-
}) : null,
|
|
1968
2627
|
confirming && canCommit ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1969
2628
|
className: "dsh-ui-stack",
|
|
1970
2629
|
role: "group",
|
|
@@ -2000,6 +2659,11 @@ function GitCommitAction({ sessionId, useSession, call, t }) {
|
|
|
2000
2659
|
sessionRunning && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2001
2660
|
className: "dsh-ui-hint",
|
|
2002
2661
|
children: t("sessionRunning")
|
|
2662
|
+
}),
|
|
2663
|
+
settingsPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2664
|
+
className: "dsh-ui-hint",
|
|
2665
|
+
role: "status",
|
|
2666
|
+
children: t("commitBlocked")
|
|
2003
2667
|
})
|
|
2004
2668
|
] }),
|
|
2005
2669
|
running && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -2075,7 +2739,6 @@ const clientCss = `${officialUiCss("gcm-root")}${modelMenuCss}
|
|
|
2075
2739
|
* of at the trigger. Naming both classes puts this rule at the same weight,
|
|
2076
2740
|
* later in the sheet, so it wins. */
|
|
2077
2741
|
.gcm-root.gcm-panel { position: fixed; width: min(360px, calc(100vw - 32px)); max-height: min(560px, calc(100vh - 32px)); overflow: auto; }
|
|
2078
|
-
.gcm-root .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); }
|
|
2079
2742
|
.gcm-root .gcm-commits { margin: 0; padding-inline-start: 18px; display: grid; gap: 2px; font-size: 12px; line-height: 18px; overflow-wrap: anywhere; }
|
|
2080
2743
|
.gcm-root .gcm-commits code { font-size: 12px; background: var(--dsw-alias-bg-layer-2); padding: 0 4px; border-radius: var(--dsw-radius-sm); }
|
|
2081
2744
|
.gcm-root .gcm-settings-status { display: flex; flex-direction: column; gap: 4px; }
|
|
@@ -2087,31 +2750,31 @@ function useHostLocale(client) {
|
|
|
2087
2750
|
return (0, react.useSyncExternalStore)(subscribe, snapshot, snapshot);
|
|
2088
2751
|
}
|
|
2089
2752
|
const settingsZh = {
|
|
2090
|
-
pageTitle: "Git 提交",
|
|
2091
2753
|
model: "提交规划模型",
|
|
2092
|
-
modelDefault: "
|
|
2093
|
-
modelHint: "
|
|
2754
|
+
modelDefault: "默认辅助模型",
|
|
2755
|
+
modelHint: "留空使用全局默认辅助模型;未设置时跟随当前会话模型,再回落到宿主默认对话模型。",
|
|
2094
2756
|
fallback: "模型不可用时仍然提交",
|
|
2095
2757
|
fallbackHint: "默认关闭:模型规划失败会取消本次提交,工作区不变。开启后全部改动会合并成一个提交,由插件代写提交信息。",
|
|
2096
2758
|
loading: "正在读取设置…",
|
|
2097
2759
|
saved: "已保存。",
|
|
2098
2760
|
stale: "设置已更新,请刷新后重试。",
|
|
2099
2761
|
failed: "无法读取提交设置。",
|
|
2762
|
+
saveFailed: "设置未能保存,已显示的选择未生效。请重试。",
|
|
2100
2763
|
retry: "重试",
|
|
2101
2764
|
refresh: "刷新设置",
|
|
2102
2765
|
syncUnavailable: "自动刷新不可用,请手动刷新设置。"
|
|
2103
2766
|
};
|
|
2104
2767
|
const settingsEn = {
|
|
2105
|
-
pageTitle: "Git Commit",
|
|
2106
2768
|
model: "Commit planning model",
|
|
2107
|
-
modelDefault: "Default model",
|
|
2108
|
-
modelHint: "Leave empty to use the current
|
|
2769
|
+
modelDefault: "Default auxiliary model",
|
|
2770
|
+
modelHint: "Leave empty to use the global default auxiliary model; when none is set, the current conversation model, then the host default chat model.",
|
|
2109
2771
|
fallback: "Commit even without a model plan",
|
|
2110
2772
|
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.",
|
|
2111
2773
|
loading: "Loading settings…",
|
|
2112
2774
|
saved: "Saved.",
|
|
2113
2775
|
stale: "Settings changed; refresh and retry.",
|
|
2114
2776
|
failed: "Could not load commit settings.",
|
|
2777
|
+
saveFailed: "The change could not be saved, so the shown selection did not take effect. Try again.",
|
|
2115
2778
|
retry: "Retry",
|
|
2116
2779
|
refresh: "Refresh settings",
|
|
2117
2780
|
syncUnavailable: "Automatic refresh unavailable; refresh settings manually."
|
|
@@ -2119,7 +2782,8 @@ const settingsEn = {
|
|
|
2119
2782
|
function settingsCopy(locale) {
|
|
2120
2783
|
return String(locale).startsWith("zh") ? settingsZh : settingsEn;
|
|
2121
2784
|
}
|
|
2122
|
-
/**
|
|
2785
|
+
/** Model/fallback block embedded in the commit popover; picks persist as the
|
|
2786
|
+
* plugin's saved defaults and sync through the model-settings change channel. */
|
|
2123
2787
|
function GitCommitSettingsPanel(props) {
|
|
2124
2788
|
const hostLocale = useHostLocale(props.client);
|
|
2125
2789
|
const text = settingsCopy(props.locale ?? hostLocale);
|
|
@@ -2157,6 +2821,7 @@ function GitCommitSettingsPanel(props) {
|
|
|
2157
2821
|
if (state.active && state.epoch === epoch && !state.pending) {
|
|
2158
2822
|
setSettings(next);
|
|
2159
2823
|
setError("");
|
|
2824
|
+
props.onReady?.();
|
|
2160
2825
|
}
|
|
2161
2826
|
}).catch(() => {
|
|
2162
2827
|
if (state.active && state.epoch === epoch && !state.pending) setError("failed");
|
|
@@ -2206,14 +2871,16 @@ function GitCommitSettingsPanel(props) {
|
|
|
2206
2871
|
props.client,
|
|
2207
2872
|
refreshCatalog
|
|
2208
2873
|
]);
|
|
2874
|
+
const controlsLocked = props.disabled === true;
|
|
2209
2875
|
const save = (0, react.useCallback)((patch) => {
|
|
2210
2876
|
const state = operation.current;
|
|
2211
|
-
if (!settings || !state.active || state.writing || state.reading) return;
|
|
2877
|
+
if (!settings || !state.active || state.writing || state.reading || props.disabled) return;
|
|
2212
2878
|
state.writing = true;
|
|
2213
2879
|
const epoch = state.epoch;
|
|
2214
2880
|
setBusy(true);
|
|
2215
2881
|
setNote(false);
|
|
2216
2882
|
setError("");
|
|
2883
|
+
props.onSaving?.(true);
|
|
2217
2884
|
rpc(call, "settings.update", {
|
|
2218
2885
|
settings: {
|
|
2219
2886
|
model: patch.model ?? settings.model,
|
|
@@ -2224,10 +2891,16 @@ function GitCommitSettingsPanel(props) {
|
|
|
2224
2891
|
if (state.active && state.epoch === epoch) {
|
|
2225
2892
|
setSettings(next);
|
|
2226
2893
|
setNote(true);
|
|
2894
|
+
props.onReady?.();
|
|
2895
|
+
props.onSaved?.(next);
|
|
2896
|
+
}
|
|
2897
|
+
}).catch((reason) => {
|
|
2898
|
+
if (state.active && state.epoch === epoch) {
|
|
2899
|
+
props.onWriteFailed?.();
|
|
2900
|
+
setError(reason instanceof RpcError && reason.code === "stale" ? "stale" : "saveFailed");
|
|
2227
2901
|
}
|
|
2228
|
-
}).catch(() => {
|
|
2229
|
-
if (state.active && state.epoch === epoch) setError("stale");
|
|
2230
2902
|
}).finally(() => {
|
|
2903
|
+
props.onSaving?.(false);
|
|
2231
2904
|
if (!state.active || state.epoch !== epoch) return;
|
|
2232
2905
|
state.writing = false;
|
|
2233
2906
|
setBusy(false);
|
|
@@ -2236,11 +2909,12 @@ function GitCommitSettingsPanel(props) {
|
|
|
2236
2909
|
}, [
|
|
2237
2910
|
call,
|
|
2238
2911
|
settings,
|
|
2239
|
-
text.
|
|
2240
|
-
|
|
2912
|
+
text.stale,
|
|
2913
|
+
props.disabled
|
|
2241
2914
|
]);
|
|
2915
|
+
const locked = busy || controlsLocked;
|
|
2242
2916
|
if (!settings) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2243
|
-
className: "gcm-root gcm-settings dsh-ui-
|
|
2917
|
+
className: "gcm-root gcm-settings dsh-ui-stack",
|
|
2244
2918
|
children: [
|
|
2245
2919
|
error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2246
2920
|
role: "alert",
|
|
@@ -2259,7 +2933,7 @@ function GitCommitSettingsPanel(props) {
|
|
|
2259
2933
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2260
2934
|
variant: "ghost",
|
|
2261
2935
|
size: "sm",
|
|
2262
|
-
disabled:
|
|
2936
|
+
disabled: locked,
|
|
2263
2937
|
onClick: () => refreshRef.current(),
|
|
2264
2938
|
children: text.retry
|
|
2265
2939
|
})
|
|
@@ -2273,36 +2947,9 @@ function GitCommitSettingsPanel(props) {
|
|
|
2273
2947
|
const selectedEffort = efforts.find((item) => item.id === (current.reasoningEffort || ""));
|
|
2274
2948
|
const effortName = selectedEffort ? modelMenuEffortLabel(selectedEffort, language) : void 0;
|
|
2275
2949
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2276
|
-
className: "gcm-root gcm-settings dsh-ui-
|
|
2950
|
+
className: "gcm-root gcm-settings dsh-ui-stack",
|
|
2277
2951
|
"data-testid": "git-commit-settings",
|
|
2278
2952
|
children: [
|
|
2279
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2280
|
-
className: "gcm-settings-status",
|
|
2281
|
-
children: [
|
|
2282
|
-
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2283
|
-
role: "status",
|
|
2284
|
-
className: "dsh-ui-hint",
|
|
2285
|
-
children: text.syncUnavailable
|
|
2286
|
-
}),
|
|
2287
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
2288
|
-
variant: "ghost",
|
|
2289
|
-
size: "sm",
|
|
2290
|
-
disabled: busy,
|
|
2291
|
-
onClick: () => refreshRef.current(),
|
|
2292
|
-
children: text.refresh
|
|
2293
|
-
}),
|
|
2294
|
-
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2295
|
-
role: "alert",
|
|
2296
|
-
className: "dsh-ui-error",
|
|
2297
|
-
children: text[error]
|
|
2298
|
-
}),
|
|
2299
|
-
note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2300
|
-
role: "status",
|
|
2301
|
-
className: "dsh-ui-notice",
|
|
2302
|
-
children: text.saved
|
|
2303
|
-
})
|
|
2304
|
-
]
|
|
2305
|
-
}),
|
|
2306
2953
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2307
2954
|
className: "dsh-ui-field",
|
|
2308
2955
|
children: [
|
|
@@ -2316,13 +2963,13 @@ function GitCommitSettingsPanel(props) {
|
|
|
2316
2963
|
}),
|
|
2317
2964
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelMenu, {
|
|
2318
2965
|
locale: language,
|
|
2319
|
-
open: menuOpen && !
|
|
2320
|
-
onOpenChange: (next) => setMenuOpen(next && !
|
|
2321
|
-
listClassName: "gcm-root",
|
|
2966
|
+
open: menuOpen && !locked,
|
|
2967
|
+
onOpenChange: (next) => setMenuOpen(next && !locked),
|
|
2968
|
+
listClassName: "gcm-root gcm-menu-portal",
|
|
2322
2969
|
anchor: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2323
2970
|
type: "button",
|
|
2324
2971
|
className: "dsh-model-menu-trigger",
|
|
2325
|
-
disabled:
|
|
2972
|
+
disabled: locked,
|
|
2326
2973
|
"aria-label": `${text.model}: ${currentLabel}`,
|
|
2327
2974
|
children: [
|
|
2328
2975
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2333,7 +2980,7 @@ function GitCommitSettingsPanel(props) {
|
|
|
2333
2980
|
className: "dsh-model-menu-triggerEffort",
|
|
2334
2981
|
children: effortName ?? text.modelDefault
|
|
2335
2982
|
}) : null,
|
|
2336
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen && !
|
|
2983
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutlineRegular, { className: `dsh-model-menu-chevron${menuOpen && !locked ? " dsh-model-menu-chevronOpen" : ""}` })
|
|
2337
2984
|
]
|
|
2338
2985
|
}),
|
|
2339
2986
|
choices,
|
|
@@ -2374,6 +3021,33 @@ function GitCommitSettingsPanel(props) {
|
|
|
2374
3021
|
className: "dsh-ui-meta",
|
|
2375
3022
|
children: text.modelHint
|
|
2376
3023
|
}),
|
|
3024
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3025
|
+
className: "gcm-settings-status",
|
|
3026
|
+
children: [
|
|
3027
|
+
syncError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3028
|
+
role: "status",
|
|
3029
|
+
className: "dsh-ui-hint",
|
|
3030
|
+
children: text.syncUnavailable
|
|
3031
|
+
}),
|
|
3032
|
+
error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3033
|
+
role: "alert",
|
|
3034
|
+
className: "dsh-ui-error",
|
|
3035
|
+
children: text[error]
|
|
3036
|
+
}),
|
|
3037
|
+
note && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3038
|
+
role: "status",
|
|
3039
|
+
className: "dsh-ui-notice",
|
|
3040
|
+
children: text.saved
|
|
3041
|
+
}),
|
|
3042
|
+
error || syncError ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3043
|
+
variant: "ghost",
|
|
3044
|
+
size: "sm",
|
|
3045
|
+
disabled: locked,
|
|
3046
|
+
onClick: () => refreshRef.current(),
|
|
3047
|
+
children: text.refresh
|
|
3048
|
+
}) : null
|
|
3049
|
+
]
|
|
3050
|
+
}),
|
|
2377
3051
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
|
|
2378
3052
|
className: "gcm-root dsh-ui-card",
|
|
2379
3053
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
@@ -2390,7 +3064,7 @@ function GitCommitSettingsPanel(props) {
|
|
|
2390
3064
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Switch, {
|
|
2391
3065
|
checked: settings.allowFallback,
|
|
2392
3066
|
label: text.fallback,
|
|
2393
|
-
disabled:
|
|
3067
|
+
disabled: locked,
|
|
2394
3068
|
onChange: (next) => save({ allowFallback: next })
|
|
2395
3069
|
})]
|
|
2396
3070
|
})
|
|
@@ -2404,6 +3078,7 @@ function apply(ctx) {
|
|
|
2404
3078
|
const settingsSubscriber = createModelSettingsSubscriber(ctx, PLUGIN_NAME);
|
|
2405
3079
|
const subscribeSettings = (change, error) => settingsSubscriber.subscribe(change, error);
|
|
2406
3080
|
ctx.provide(GIT_COMMIT_CLIENT_SERVICE, { active: true });
|
|
3081
|
+
ctx.effect(() => registerAuxiliaryModelSettings(ctx), "git-commit.auxiliary-model");
|
|
2407
3082
|
ctx.effect(() => client.locale.register(NS, {
|
|
2408
3083
|
zh: { ...zh },
|
|
2409
3084
|
en: { ...en }
|
|
@@ -2416,19 +3091,16 @@ function apply(ctx) {
|
|
|
2416
3091
|
document.head.appendChild(style);
|
|
2417
3092
|
return () => style.remove();
|
|
2418
3093
|
}, "git-commit.styles");
|
|
2419
|
-
ctx.effect(() => client.slots.inject("plugins.bundle.config", () => client.slots.register({
|
|
2420
|
-
name: "plugins.bundle.config",
|
|
2421
|
-
key: PLUGIN_NAME
|
|
2422
|
-
}, () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GitCommitSettingsPanel, {
|
|
2423
|
-
client,
|
|
2424
|
-
subscribeSettings
|
|
2425
|
-
}))), "git-commit.settings");
|
|
2426
3094
|
ctx.effect(() => client.slots.inject("conversation.session.header.actions", () => client.slots.register({
|
|
2427
3095
|
name: "conversation.session.header.actions",
|
|
2428
3096
|
id: "git-commit",
|
|
2429
3097
|
order: 10,
|
|
2430
3098
|
locale: NS,
|
|
2431
|
-
inject: () => ({
|
|
3099
|
+
inject: () => ({
|
|
3100
|
+
call: (endpoint, payload) => client.connection.rpc.call(RPC_CHANNEL, endpoint, payload),
|
|
3101
|
+
client,
|
|
3102
|
+
subscribeSettings
|
|
3103
|
+
})
|
|
2432
3104
|
}, GitCommitAction)), "git-commit.slot");
|
|
2433
3105
|
}
|
|
2434
3106
|
//#endregion
|