dsh-xrxs-lingting 0.4.0-dev.50 → 0.4.0-dev.52
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
CHANGED
|
@@ -110,3 +110,5 @@ npm run smoke:client -- /path/to/cinlyn-desktop
|
|
|
110
110
|
该检查不代替安装后对授权、录音、转写和回放的人工验证。
|
|
111
111
|
|
|
112
112
|
布局回归验证:`node scripts/smoke-layout-browser.mjs <playwright/index.mjs 路径>`,使用 Chrome 无头浏览器运行真实插件产物,验证页面边界、弹窗居中、转写面板层级、窗口缩放与卸载后聊天恢复。使用合成录音状态,不访问业务数据、不采集声音。
|
|
113
|
+
|
|
114
|
+
设备切换:支持的桌面原生模块会在默认输出变化时重新连接系统采集,插件重新采样并替换默认麦克风输入,保留同一录音与转写连接。操作系统切换设备期间可能存在短暂音频缺口;持续无法恢复才报错暂停。
|
package/package.json
CHANGED
package/plugin-dist/client.js
CHANGED
|
@@ -1384,7 +1384,21 @@ var XinLingtingService = class {
|
|
|
1384
1384
|
this.emit({ operation: null });
|
|
1385
1385
|
}
|
|
1386
1386
|
};
|
|
1387
|
-
finish = async () => {
|
|
1387
|
+
finish = async (recordId) => {
|
|
1388
|
+
if (recordId && this.snapshot.active?.id !== recordId) {
|
|
1389
|
+
if (this.snapshot.operation) throw new Error("\u6B63\u5728\u5904\u7406\u5176\u4ED6\u5F55\u97F3\u64CD\u4F5C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
|
1390
|
+
this.emit({ operation: "finishing" });
|
|
1391
|
+
try {
|
|
1392
|
+
const record = await this.loadDetail(recordId);
|
|
1393
|
+
if (record.id !== recordId || record.status !== "paused") throw new Error("\u8BB0\u5F55\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u5237\u65B0\u540E\u91CD\u8BD5\u3002");
|
|
1394
|
+
await this.http.send(this.requests.stop(recordId, msToSeconds(record.durationMs)));
|
|
1395
|
+
this.emit({ catalogVersion: this.snapshot.catalogVersion + 1 });
|
|
1396
|
+
recordProgress(this).track(recordId);
|
|
1397
|
+
} finally {
|
|
1398
|
+
this.emit({ operation: null });
|
|
1399
|
+
}
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1388
1402
|
const active = this.snapshot.active;
|
|
1389
1403
|
if (this.snapshot.operation || !active || active.status === "finishing") return;
|
|
1390
1404
|
const needTeardown = active.status !== "finish-error";
|
|
@@ -1606,6 +1620,117 @@ function toSegment(segment) {
|
|
|
1606
1620
|
return { id: segment.id, speaker: speakerLabel(segment.speakerId), text: segment.text, final: segment.final };
|
|
1607
1621
|
}
|
|
1608
1622
|
|
|
1623
|
+
// src/audio/microphone-switch.ts
|
|
1624
|
+
function followDefaultMicrophone(options) {
|
|
1625
|
+
let current = options.initial;
|
|
1626
|
+
let stopped = false;
|
|
1627
|
+
let running = false;
|
|
1628
|
+
let queued = false;
|
|
1629
|
+
let cancelAcquire;
|
|
1630
|
+
let cancelDelay;
|
|
1631
|
+
const delay = () => new Promise((resolve) => {
|
|
1632
|
+
const timer = setTimeout(() => {
|
|
1633
|
+
cancelDelay = void 0;
|
|
1634
|
+
resolve();
|
|
1635
|
+
}, 250);
|
|
1636
|
+
cancelDelay = () => {
|
|
1637
|
+
clearTimeout(timer);
|
|
1638
|
+
cancelDelay = void 0;
|
|
1639
|
+
resolve();
|
|
1640
|
+
};
|
|
1641
|
+
});
|
|
1642
|
+
const watch = (stream) => {
|
|
1643
|
+
stream.getAudioTracks().forEach((track) => {
|
|
1644
|
+
track.onended = () => {
|
|
1645
|
+
void reconnect();
|
|
1646
|
+
};
|
|
1647
|
+
});
|
|
1648
|
+
};
|
|
1649
|
+
const reconnect = async () => {
|
|
1650
|
+
if (stopped) return;
|
|
1651
|
+
if (running) {
|
|
1652
|
+
queued = true;
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1655
|
+
running = true;
|
|
1656
|
+
const began = Date.now();
|
|
1657
|
+
try {
|
|
1658
|
+
do {
|
|
1659
|
+
queued = false;
|
|
1660
|
+
try {
|
|
1661
|
+
const next = await new Promise((resolve, reject) => {
|
|
1662
|
+
let settled = false;
|
|
1663
|
+
const finishError = (error) => {
|
|
1664
|
+
if (settled) return;
|
|
1665
|
+
settled = true;
|
|
1666
|
+
clearTimeout(timer);
|
|
1667
|
+
cancelAcquire = void 0;
|
|
1668
|
+
reject(error);
|
|
1669
|
+
};
|
|
1670
|
+
const timer = setTimeout(() => finishError(new Error("Microphone recovery timed out")), Math.max(0, 8e3 - (Date.now() - began)));
|
|
1671
|
+
cancelAcquire = () => finishError(new Error("Recording stopped"));
|
|
1672
|
+
Promise.resolve().then(options.acquire).then((stream) => {
|
|
1673
|
+
if (settled || stopped) {
|
|
1674
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
settled = true;
|
|
1678
|
+
clearTimeout(timer);
|
|
1679
|
+
cancelAcquire = void 0;
|
|
1680
|
+
resolve(stream);
|
|
1681
|
+
}, finishError);
|
|
1682
|
+
});
|
|
1683
|
+
if (stopped) {
|
|
1684
|
+
next.getTracks().forEach((track) => track.stop());
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
if (!next.getAudioTracks().some((track) => track.readyState === "live")) {
|
|
1688
|
+
next.getTracks().forEach((track) => track.stop());
|
|
1689
|
+
throw new Error("Microphone is not live");
|
|
1690
|
+
}
|
|
1691
|
+
const previous = current;
|
|
1692
|
+
try {
|
|
1693
|
+
options.replace(next, previous);
|
|
1694
|
+
} catch (error) {
|
|
1695
|
+
next.getTracks().forEach((track) => track.stop());
|
|
1696
|
+
throw error;
|
|
1697
|
+
}
|
|
1698
|
+
current = next;
|
|
1699
|
+
previous.getTracks().forEach((track) => {
|
|
1700
|
+
track.onended = null;
|
|
1701
|
+
track.stop();
|
|
1702
|
+
});
|
|
1703
|
+
watch(current);
|
|
1704
|
+
} catch {
|
|
1705
|
+
if (stopped) return;
|
|
1706
|
+
if (Date.now() - began >= 8e3) {
|
|
1707
|
+
if (!current.getAudioTracks().some((track) => track.readyState === "live")) options.failed();
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
await delay();
|
|
1711
|
+
queued = true;
|
|
1712
|
+
}
|
|
1713
|
+
} while (queued && !stopped);
|
|
1714
|
+
} finally {
|
|
1715
|
+
running = false;
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
const changed = () => {
|
|
1719
|
+
void reconnect();
|
|
1720
|
+
};
|
|
1721
|
+
watch(current);
|
|
1722
|
+
options.devices.addEventListener?.("devicechange", changed);
|
|
1723
|
+
return () => {
|
|
1724
|
+
stopped = true;
|
|
1725
|
+
cancelDelay?.();
|
|
1726
|
+
cancelAcquire?.();
|
|
1727
|
+
options.devices.removeEventListener?.("devicechange", changed);
|
|
1728
|
+
current.getTracks().forEach((track) => {
|
|
1729
|
+
track.onended = null;
|
|
1730
|
+
});
|
|
1731
|
+
};
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1609
1734
|
// src/audio/worklet-source.ts
|
|
1610
1735
|
var captureWorkletSource = `
|
|
1611
1736
|
class LingtingCaptureProcessor extends AudioWorkletProcessor {
|
|
@@ -1663,6 +1788,12 @@ function publishAudioStatus(next) {
|
|
|
1663
1788
|
status = next;
|
|
1664
1789
|
listeners.forEach((listener) => listener());
|
|
1665
1790
|
}
|
|
1791
|
+
var recoveries = [];
|
|
1792
|
+
function noteAudioRecovery(samples, rate) {
|
|
1793
|
+
if (!Number.isFinite(samples) || samples <= 0 || !Number.isFinite(rate) || rate < 8e3) return;
|
|
1794
|
+
recoveries.push({ at: Date.now(), droppedMs: samples / rate * 1e3 });
|
|
1795
|
+
if (recoveries.length > 50) recoveries.shift();
|
|
1796
|
+
}
|
|
1666
1797
|
|
|
1667
1798
|
// src/audio/native-system.ts
|
|
1668
1799
|
function nativeSystemAudio() {
|
|
@@ -1691,7 +1822,7 @@ class NativeSystemSource extends AudioWorkletProcessor {
|
|
|
1691
1822
|
accept(data) {
|
|
1692
1823
|
if (this.failed) return;
|
|
1693
1824
|
if (!data || !(data.samples instanceof Float32Array) || !Number.isFinite(data.sampleRate) || data.sampleRate < 8000 || data.sampleRate > 192000) return;
|
|
1694
|
-
if (this.rate && this.rate !== data.sampleRate) { this.
|
|
1825
|
+
if (this.rate && this.rate !== data.sampleRate) { this.read = 0; this.write = 0; this.started = false; this.buffer.fill(0); }
|
|
1695
1826
|
this.rate = data.sampleRate;
|
|
1696
1827
|
// Bound latency, not just memory. After a render stall, retain the newest
|
|
1697
1828
|
// 100 ms instead of spending minutes playing stale audio or killing capture.
|
|
@@ -1784,6 +1915,7 @@ var MeetingCapture = class {
|
|
|
1784
1915
|
emitting = false;
|
|
1785
1916
|
stopSystem = null;
|
|
1786
1917
|
systemNode = null;
|
|
1918
|
+
stopMicrophoneSwitch = null;
|
|
1787
1919
|
async start(onChunk, onError) {
|
|
1788
1920
|
if (this.context || this.starting) throw new Error("\u91C7\u96C6\u5DF2\u5728\u8FDB\u884C\u4E2D\u3002");
|
|
1789
1921
|
this.starting = true;
|
|
@@ -1822,7 +1954,6 @@ var MeetingCapture = class {
|
|
|
1822
1954
|
for (const stream of this.streams) {
|
|
1823
1955
|
for (const track of stream.getAudioTracks()) {
|
|
1824
1956
|
if (track.readyState !== "live") throw new Error("\u97F3\u9891\u8BBE\u5907\u5DF2\u65AD\u5F00\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u5F55\u97F3\u3002");
|
|
1825
|
-
track.onended = () => fail(stream === microphone ? "\u9EA6\u514B\u98CE\u5DF2\u65AD\u5F00\uFF0C\u5F55\u97F3\u5DF2\u6682\u505C\uFF0C\u8BF7\u68C0\u67E5\u8BBE\u5907\u540E\u7EE7\u7EED\u3002" : "\u7CFB\u7EDF\u58F0\u97F3\u91C7\u96C6\u5DF2\u505C\u6B62\uFF0C\u5F55\u97F3\u5DF2\u6682\u505C\uFF0C\u8BF7\u91CD\u65B0\u6388\u6743\u540E\u7EE7\u7EED\u3002");
|
|
1826
1957
|
}
|
|
1827
1958
|
}
|
|
1828
1959
|
const context = new AudioContext();
|
|
@@ -1836,7 +1967,7 @@ var MeetingCapture = class {
|
|
|
1836
1967
|
return;
|
|
1837
1968
|
}
|
|
1838
1969
|
stalledSince ??= Date.now();
|
|
1839
|
-
if (Date.now() - stalledSince >=
|
|
1970
|
+
if (Date.now() - stalledSince >= 8e3) {
|
|
1840
1971
|
fail("\u97F3\u9891\u5904\u7406\u6062\u590D\u8D85\u65F6\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u5F55\u97F3\u3002");
|
|
1841
1972
|
return;
|
|
1842
1973
|
}
|
|
@@ -1846,7 +1977,8 @@ var MeetingCapture = class {
|
|
|
1846
1977
|
return;
|
|
1847
1978
|
}
|
|
1848
1979
|
recovering = true;
|
|
1849
|
-
void context.resume().catch(() =>
|
|
1980
|
+
void context.resume().catch(() => {
|
|
1981
|
+
}).finally(() => {
|
|
1850
1982
|
recovering = false;
|
|
1851
1983
|
});
|
|
1852
1984
|
};
|
|
@@ -1870,7 +2002,6 @@ var MeetingCapture = class {
|
|
|
1870
2002
|
const sources = [context.createMediaStreamSource(microphone)];
|
|
1871
2003
|
this.nodes.push(sources[0]);
|
|
1872
2004
|
let startSystem;
|
|
1873
|
-
let recoveryWarning;
|
|
1874
2005
|
if (system) {
|
|
1875
2006
|
const sessionId = crypto.randomUUID();
|
|
1876
2007
|
const source = new AudioWorkletNode(context, "lingting-native-system", {
|
|
@@ -1884,8 +2015,7 @@ var MeetingCapture = class {
|
|
|
1884
2015
|
if (generation !== this.generation) return;
|
|
1885
2016
|
if (event.data?.error) fail(event.data.error);
|
|
1886
2017
|
if (event.data?.droppedSamples > 0) {
|
|
1887
|
-
|
|
1888
|
-
publishAudioStatus({ mode: this.mode, microphone: 0, system: 0, warning: recoveryWarning });
|
|
2018
|
+
noteAudioRecovery(event.data.droppedSamples, event.data.sampleRate);
|
|
1889
2019
|
}
|
|
1890
2020
|
};
|
|
1891
2021
|
const receivePort = (event) => {
|
|
@@ -1924,6 +2054,24 @@ var MeetingCapture = class {
|
|
|
1924
2054
|
this.nodes.push(source, analyser, gain);
|
|
1925
2055
|
analysers.push(analyser);
|
|
1926
2056
|
}
|
|
2057
|
+
this.stopMicrophoneSwitch = followDefaultMicrophone({
|
|
2058
|
+
devices,
|
|
2059
|
+
initial: microphone,
|
|
2060
|
+
acquire: () => devices.getUserMedia({ audio: { channelCount: 1, echoCancellation: true, noiseSuppression: true, autoGainControl: true } }),
|
|
2061
|
+
replace: (next, previous) => {
|
|
2062
|
+
if (generation !== this.generation) throw new Error("\u5F55\u97F3\u5DF2\u53D6\u6D88\u3002");
|
|
2063
|
+
const replacement = context.createMediaStreamSource(next);
|
|
2064
|
+
replacement.connect(analysers[0]);
|
|
2065
|
+
const oldSource = sources[0];
|
|
2066
|
+
oldSource.disconnect();
|
|
2067
|
+
sources[0] = replacement;
|
|
2068
|
+
this.nodes = this.nodes.filter((item) => item !== oldSource);
|
|
2069
|
+
this.nodes.push(replacement);
|
|
2070
|
+
this.streams = this.streams.filter((item) => item !== previous);
|
|
2071
|
+
this.streams.push(next);
|
|
2072
|
+
},
|
|
2073
|
+
failed: () => fail("\u9EA6\u514B\u98CE\u5207\u6362\u540E\u672A\u80FD\u6062\u590D\uFF0C\u5F55\u97F3\u5DF2\u6682\u505C\uFF0C\u8BF7\u68C0\u67E5\u8BBE\u5907\u8FDE\u63A5\u3002")
|
|
2074
|
+
});
|
|
1927
2075
|
const mute = context.createGain();
|
|
1928
2076
|
mute.gain.value = 0;
|
|
1929
2077
|
node.connect(mute);
|
|
@@ -1944,12 +2092,7 @@ var MeetingCapture = class {
|
|
|
1944
2092
|
analyser.getFloatTimeDomainData(samples);
|
|
1945
2093
|
return Math.min(1, Math.sqrt(samples.reduce((sum, sample) => sum + sample * sample, 0) / samples.length) * 4);
|
|
1946
2094
|
});
|
|
1947
|
-
publishAudioStatus({
|
|
1948
|
-
mode: this.mode,
|
|
1949
|
-
microphone: this.emitting ? levels[0] : 0,
|
|
1950
|
-
system: this.emitting ? levels[1] ?? 0 : 0,
|
|
1951
|
-
warning: recoveryWarning
|
|
1952
|
-
});
|
|
2095
|
+
publishAudioStatus({ mode: this.mode, microphone: this.emitting ? levels[0] : 0, system: this.emitting ? levels[1] ?? 0 : 0 });
|
|
1953
2096
|
}, 200);
|
|
1954
2097
|
} catch (error) {
|
|
1955
2098
|
if (generation === this.generation) await this.stop();
|
|
@@ -1970,6 +2113,8 @@ var MeetingCapture = class {
|
|
|
1970
2113
|
}
|
|
1971
2114
|
async stop() {
|
|
1972
2115
|
++this.generation;
|
|
2116
|
+
this.stopMicrophoneSwitch?.();
|
|
2117
|
+
this.stopMicrophoneSwitch = null;
|
|
1973
2118
|
this.emitting = false;
|
|
1974
2119
|
if (this.timer) clearInterval(this.timer);
|
|
1975
2120
|
this.timer = null;
|
|
@@ -3008,6 +3153,23 @@ function RecordViewer({ service, recordId, onLoaded }) {
|
|
|
3008
3153
|
const [refreshing, setRefreshing] = (0, import_react13.useState)(false);
|
|
3009
3154
|
const [summaryError, setSummaryError] = (0, import_react13.useState)("");
|
|
3010
3155
|
const [resuming, setResuming] = (0, import_react13.useState)(false);
|
|
3156
|
+
const [confirmFinish, setConfirmFinish] = (0, import_react13.useState)(false);
|
|
3157
|
+
const [finishing, setFinishing] = (0, import_react13.useState)(false);
|
|
3158
|
+
const finishRecording = async () => {
|
|
3159
|
+
if (finishing || resuming) return;
|
|
3160
|
+
setFinishing(true);
|
|
3161
|
+
setResumeError("");
|
|
3162
|
+
try {
|
|
3163
|
+
await service.finish(recordId);
|
|
3164
|
+
setLoaded((current) => current ? { ...current, status: "finishing" } : current);
|
|
3165
|
+
setConfirmFinish(false);
|
|
3166
|
+
} catch (e) {
|
|
3167
|
+
setResumeError(e instanceof Error ? e.message : "\u7ED3\u675F\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
3168
|
+
setConfirmFinish(false);
|
|
3169
|
+
} finally {
|
|
3170
|
+
setFinishing(false);
|
|
3171
|
+
}
|
|
3172
|
+
};
|
|
3011
3173
|
const [resumeError, setResumeError] = (0, import_react13.useState)("");
|
|
3012
3174
|
const [shareOpen, setShareOpen] = (0, import_react13.useState)(false);
|
|
3013
3175
|
(0, import_react13.useEffect)(() => {
|
|
@@ -3140,12 +3302,20 @@ function RecordViewer({ service, recordId, onLoaded }) {
|
|
|
3140
3302
|
] })
|
|
3141
3303
|
] }),
|
|
3142
3304
|
loaded.status === "paused" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "lt-resume", children: [
|
|
3143
|
-
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: "lt-primary", onClick: () => void resumeRecording(), disabled: resuming, children: [
|
|
3305
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: "lt-primary", onClick: () => void resumeRecording(), disabled: resuming || finishing, children: [
|
|
3144
3306
|
resuming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, { size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Mic, { size: 15 }),
|
|
3145
3307
|
resuming ? "\u6B63\u5728\u7EE7\u7EED\u2026" : "\u7EE7\u7EED\u5F55\u97F3"
|
|
3146
3308
|
] }),
|
|
3309
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "button danger", disabled: resuming || finishing, onClick: () => setConfirmFinish(true), children: "\u7ED3\u675F\u7075\u542C" }),
|
|
3147
3310
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u7EE7\u7EED\u540E\u5B9E\u65F6\u8F6C\u5199\u56DE\u5230\u8FD9\u4E2A\u9762\u677F" })
|
|
3148
3311
|
] }),
|
|
3312
|
+
confirmFinish && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Modal, { title: "\u7ED3\u675F\u8FD9\u573A\u7075\u542C\uFF1F", busy: finishing, onClose: () => setConfirmFinish(false), children: [
|
|
3313
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { children: "\u7ED3\u675F\u540E\u5C06\u6574\u7406\u5DF2\u6709\u8F6C\u5199\uFF0C\u65E0\u6CD5\u7EE7\u7EED\u8FD9\u573A\u5F55\u97F3\u3002" }),
|
|
3314
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "modal-actions", children: [
|
|
3315
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "button", disabled: finishing, onClick: () => setConfirmFinish(false), children: "\u6682\u4E0D\u7ED3\u675F" }),
|
|
3316
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "button danger", disabled: finishing, onClick: () => void finishRecording(), children: finishing ? "\u6B63\u5728\u7ED3\u675F\u2026" : "\u7ED3\u675F\u7075\u542C" })
|
|
3317
|
+
] })
|
|
3318
|
+
] }),
|
|
3149
3319
|
resumeError && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "lt-resume-error", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ErrorNotice, { action: "\u77E5\u9053\u4E86", onAction: () => setResumeError(""), children: resumeError }) }),
|
|
3150
3320
|
(loaded.status === "finishing" || loaded.status === "interrupted") && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "lt-settling", children: [
|
|
3151
3321
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, { size: 13 }),
|
|
@@ -4292,7 +4462,7 @@ function MeetingPrompt({ service }) {
|
|
|
4292
4462
|
const meeting = useMeetingPrompt({ service });
|
|
4293
4463
|
if (!meeting.prompt) return null;
|
|
4294
4464
|
const appName = meeting.prompt.appName;
|
|
4295
|
-
const headline = meeting.pending ? "\u6B63\u5728\u5F00\u542F\u7075\u542C" : meeting.error ? "\u7075\u542C\u542F\u52A8\u672A\u5B8C\u6210" : meeting.confidence === "suspected" ? `\u68C0\u6D4B\u5230\u53EF\u80FD\u7684\u901A\u8BDD\u6D3B\u52A8\uFF08${appName}\uFF09` :
|
|
4465
|
+
const headline = meeting.pending ? "\u6B63\u5728\u5F00\u542F\u7075\u542C" : meeting.error ? "\u7075\u542C\u542F\u52A8\u672A\u5B8C\u6210" : meeting.confidence === "suspected" ? `\u68C0\u6D4B\u5230\u53EF\u80FD\u7684\u901A\u8BDD\u6D3B\u52A8\uFF08${appName}\uFF09` : `${appName}\u53EF\u80FD\u6B63\u5728\u901A\u8BDD`;
|
|
4296
4466
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("aside", { className: "lingting-root lt-meeting", "aria-label": "\u4F1A\u8BAE\u63D0\u9192", children: [
|
|
4297
4467
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "lt-meeting-head", children: [
|
|
4298
4468
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Headphones, { size: 16 }),
|
|
@@ -5450,7 +5620,7 @@ function mountBusinessSurface(doc) {
|
|
|
5450
5620
|
else center.style.removeProperty("position");
|
|
5451
5621
|
} };
|
|
5452
5622
|
}
|
|
5453
|
-
function BusinessSurface({ children }) {
|
|
5623
|
+
function BusinessSurface({ children, onClose }) {
|
|
5454
5624
|
const [target, setTarget] = (0, import_react22.useState)(null);
|
|
5455
5625
|
(0, import_react22.useLayoutEffect)(() => {
|
|
5456
5626
|
let mounted = null;
|
|
@@ -5461,9 +5631,18 @@ function BusinessSurface({ children }) {
|
|
|
5461
5631
|
setTarget(mounted?.target ?? null);
|
|
5462
5632
|
};
|
|
5463
5633
|
attach();
|
|
5634
|
+
const onSidebarClick = (event) => {
|
|
5635
|
+
const element = event.target instanceof Element ? event.target : null;
|
|
5636
|
+
const button = element?.closest("button");
|
|
5637
|
+
if (!button?.closest('[data-slot="sidebar"]')) return;
|
|
5638
|
+
const label = button.getAttribute("aria-label")?.trim();
|
|
5639
|
+
if (label && /^(新会话|新建会话|New session)$/i.test(label)) onClose();
|
|
5640
|
+
};
|
|
5641
|
+
document.addEventListener("click", onSidebarClick, true);
|
|
5464
5642
|
const observer = new MutationObserver(attach);
|
|
5465
5643
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
5466
5644
|
return () => {
|
|
5645
|
+
document.removeEventListener("click", onSidebarClick, true);
|
|
5467
5646
|
observer.disconnect();
|
|
5468
5647
|
mounted?.dispose();
|
|
5469
5648
|
};
|
|
@@ -5537,7 +5716,7 @@ async function apply(ctx) {
|
|
|
5537
5716
|
function Overlay() {
|
|
5538
5717
|
const page = (0, import_react23.useSyncExternalStore)(business.subscribe, business.getSnapshot, business.getSnapshot);
|
|
5539
5718
|
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
5540
|
-
!hostBusiness && page.activeId === ID && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BusinessSurface, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Page, { activeId: page.activeId }) }),
|
|
5719
|
+
!hostBusiness && page.activeId === ID && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(BusinessSurface, { onClose: () => business.close(), children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Page, { activeId: page.activeId }) }),
|
|
5541
5720
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PanelBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(XinHostNotice, { store: hostState }) }),
|
|
5542
5721
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PanelBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(LingtingSidebar, { service, controller, onOpenPage: () => business.open(ID) }) }),
|
|
5543
5722
|
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(PanelBoundary, { children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(MeetingPrompt, { service }) })
|
package/plugin-dist/index.js
CHANGED
|
@@ -756,7 +756,7 @@ function createMeetingPolicy(options = {}) {
|
|
|
756
756
|
}
|
|
757
757
|
if (!config.enabled) return { prompt: null, suppressed: "disabled" };
|
|
758
758
|
const candidates = sessions.filter(
|
|
759
|
-
(session) => session.state === "meeting" && session.confidence
|
|
759
|
+
(session) => session.state === "meeting" && session.confidence === "high" && (restartSuppression.get(session.appId) ?? 0) <= at && (config.apps.length === 0 || config.apps.includes(session.appId)) && !config.mutedApps.includes(session.appId)
|
|
760
760
|
);
|
|
761
761
|
if (candidates.length === 0) return { prompt: null, suppressed: null };
|
|
762
762
|
if (inQuietHours(config, new Date(at))) return { prompt: null, suppressed: "quiet-hours" };
|
|
@@ -929,7 +929,7 @@ function createMeetingRuntime(options = {}) {
|
|
|
929
929
|
const result = await supervisor.command("prompt.show", {
|
|
930
930
|
promptId: record.promptId,
|
|
931
931
|
title: "\u7075\u542C",
|
|
932
|
-
subtitle:
|
|
932
|
+
subtitle: `${record.appName}\u53EF\u80FD\u6B63\u5728\u901A\u8BDD`,
|
|
933
933
|
message: "\u9700\u8981\u6211\u5E2E\u4F60\u5F00\u542F\u5F55\u97F3\u8F6C\u5199\u5417\uFF1F\u53EF\u4EE5\u751F\u6210\u5F55\u97F3\u7EAA\u8981\u5E76\u5BF9\u5185\u5BB9\u63D0\u95EE",
|
|
934
934
|
acceptLabel: "\u5F00\u542F\u5F55\u97F3\u8F6C\u5199",
|
|
935
935
|
ignoreLabel: "\u5FFD\u7565",
|
|
Binary file
|
|
Binary file
|