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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-xrxs-lingting",
3
- "version": "0.4.0-dev.50",
3
+ "version": "0.4.0-dev.52",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite --host 127.0.0.1",
@@ -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.fail('\u7CFB\u7EDF\u58F0\u97F3\u91C7\u6837\u7387\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u5F55\u97F3\u3002'); return; }
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 >= 5e3) {
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(() => fail("\u97F3\u9891\u5904\u7406\u65E0\u6CD5\u6062\u590D\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u5F55\u97F3\u3002")).finally(() => {
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
- recoveryWarning = "\u7CFB\u7EDF\u58F0\u97F3\u5904\u7406\u66FE\u77ED\u6682\u79EF\u538B\uFF0C\u5DF2\u6062\u590D\u5B9E\u65F6\u5F55\u97F3\uFF1B\u90E8\u5206\u7CFB\u7EDF\u58F0\u97F3\u53EF\u80FD\u7F3A\u5931\u3002";
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` : `\u68C0\u6D4B\u5230${appName}\u4F1A\u8BAE`;
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 }) })
@@ -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 !== "none" && (restartSuppression.get(session.appId) ?? 0) <= at && (config.apps.length === 0 || config.apps.includes(session.appId)) && !config.mutedApps.includes(session.appId)
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: `\u68C0\u6D4B\u5230${record.appName}\u4F1A\u8BAE`,
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",