dsh-xrxs-lingting 0.4.0-dev.51 → 0.4.0-dev.53

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-xrxs-lingting",
3
- "version": "0.4.0-dev.51",
3
+ "version": "0.4.0-dev.53",
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";
@@ -1565,20 +1579,16 @@ var XinLingtingService = class {
1565
1579
  }
1566
1580
  };
1567
1581
  remove = async (id) => {
1568
- await this.http.send(this.requests.remove(id));
1569
- if (this.snapshot.active?.id === id) {
1570
- this.stopTicking();
1571
- this.closeWs();
1572
- const capture = this.capture;
1573
- this.capture = null;
1574
- if (capture) {
1575
- capture.pause();
1576
- void capture.stop().catch(() => {
1577
- });
1578
- }
1579
- this.emit({ active: null, catalogVersion: this.snapshot.catalogVersion + 1 });
1580
- } else {
1582
+ if (this.snapshot.operation) throw new Error("\u6B63\u5728\u5904\u7406\u5176\u4ED6\u64CD\u4F5C\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
1583
+ if (this.snapshot.active?.id === id) throw new Error("\u8BF7\u5148\u7ED3\u675F\u8FD9\u573A\u7075\u542C\uFF0C\u518D\u5220\u9664\u8BB0\u5F55\u3002");
1584
+ this.emit({ operation: "deleting" });
1585
+ try {
1586
+ const record = await this.loadDetail(id);
1587
+ if (record.id !== id || !isSessionEnded(record.status)) throw new Error("\u8FD9\u573A\u7075\u542C\u5C1A\u672A\u7ED3\u675F\uFF0C\u8BF7\u5148\u7ED3\u675F\u540E\u518D\u5220\u9664\u3002");
1588
+ await this.http.send(this.requests.remove(id));
1581
1589
  this.emit({ catalogVersion: this.snapshot.catalogVersion + 1 });
1590
+ } finally {
1591
+ this.emit({ operation: null });
1582
1592
  }
1583
1593
  };
1584
1594
  dispose = () => {
@@ -1774,6 +1784,12 @@ function publishAudioStatus(next) {
1774
1784
  status = next;
1775
1785
  listeners.forEach((listener) => listener());
1776
1786
  }
1787
+ var recoveries = [];
1788
+ function noteAudioRecovery(samples, rate) {
1789
+ if (!Number.isFinite(samples) || samples <= 0 || !Number.isFinite(rate) || rate < 8e3) return;
1790
+ recoveries.push({ at: Date.now(), droppedMs: samples / rate * 1e3 });
1791
+ if (recoveries.length > 50) recoveries.shift();
1792
+ }
1777
1793
 
1778
1794
  // src/audio/native-system.ts
1779
1795
  function nativeSystemAudio() {
@@ -1982,7 +1998,6 @@ var MeetingCapture = class {
1982
1998
  const sources = [context.createMediaStreamSource(microphone)];
1983
1999
  this.nodes.push(sources[0]);
1984
2000
  let startSystem;
1985
- let recoveryWarning;
1986
2001
  if (system) {
1987
2002
  const sessionId = crypto.randomUUID();
1988
2003
  const source = new AudioWorkletNode(context, "lingting-native-system", {
@@ -1996,8 +2011,7 @@ var MeetingCapture = class {
1996
2011
  if (generation !== this.generation) return;
1997
2012
  if (event.data?.error) fail(event.data.error);
1998
2013
  if (event.data?.droppedSamples > 0) {
1999
- 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";
2000
- publishAudioStatus({ mode: this.mode, microphone: 0, system: 0, warning: recoveryWarning });
2014
+ noteAudioRecovery(event.data.droppedSamples, event.data.sampleRate);
2001
2015
  }
2002
2016
  };
2003
2017
  const receivePort = (event) => {
@@ -2074,12 +2088,7 @@ var MeetingCapture = class {
2074
2088
  analyser.getFloatTimeDomainData(samples);
2075
2089
  return Math.min(1, Math.sqrt(samples.reduce((sum, sample) => sum + sample * sample, 0) / samples.length) * 4);
2076
2090
  });
2077
- publishAudioStatus({
2078
- mode: this.mode,
2079
- microphone: this.emitting ? levels[0] : 0,
2080
- system: this.emitting ? levels[1] ?? 0 : 0,
2081
- warning: recoveryWarning
2082
- });
2091
+ publishAudioStatus({ mode: this.mode, microphone: this.emitting ? levels[0] : 0, system: this.emitting ? levels[1] ?? 0 : 0 });
2083
2092
  }, 200);
2084
2093
  } catch (error) {
2085
2094
  if (generation === this.generation) await this.stop();
@@ -3140,6 +3149,23 @@ function RecordViewer({ service, recordId, onLoaded }) {
3140
3149
  const [refreshing, setRefreshing] = (0, import_react13.useState)(false);
3141
3150
  const [summaryError, setSummaryError] = (0, import_react13.useState)("");
3142
3151
  const [resuming, setResuming] = (0, import_react13.useState)(false);
3152
+ const [confirmFinish, setConfirmFinish] = (0, import_react13.useState)(false);
3153
+ const [finishing, setFinishing] = (0, import_react13.useState)(false);
3154
+ const finishRecording = async () => {
3155
+ if (finishing || resuming) return;
3156
+ setFinishing(true);
3157
+ setResumeError("");
3158
+ try {
3159
+ await service.finish(recordId);
3160
+ setLoaded((current) => current ? { ...current, status: "finishing" } : current);
3161
+ setConfirmFinish(false);
3162
+ } catch (e) {
3163
+ setResumeError(e instanceof Error ? e.message : "\u7ED3\u675F\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
3164
+ setConfirmFinish(false);
3165
+ } finally {
3166
+ setFinishing(false);
3167
+ }
3168
+ };
3143
3169
  const [resumeError, setResumeError] = (0, import_react13.useState)("");
3144
3170
  const [shareOpen, setShareOpen] = (0, import_react13.useState)(false);
3145
3171
  (0, import_react13.useEffect)(() => {
@@ -3272,12 +3298,20 @@ function RecordViewer({ service, recordId, onLoaded }) {
3272
3298
  ] })
3273
3299
  ] }),
3274
3300
  loaded.status === "paused" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "lt-resume", children: [
3275
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: "lt-primary", onClick: () => void resumeRecording(), disabled: resuming, children: [
3301
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { className: "lt-primary", onClick: () => void resumeRecording(), disabled: resuming || finishing, children: [
3276
3302
  resuming ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, { size: 15 }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Mic, { size: 15 }),
3277
3303
  resuming ? "\u6B63\u5728\u7EE7\u7EED\u2026" : "\u7EE7\u7EED\u5F55\u97F3"
3278
3304
  ] }),
3305
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "button danger", disabled: resuming || finishing, onClick: () => setConfirmFinish(true), children: "\u7ED3\u675F\u7075\u542C" }),
3279
3306
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "\u7EE7\u7EED\u540E\u5B9E\u65F6\u8F6C\u5199\u56DE\u5230\u8FD9\u4E2A\u9762\u677F" })
3280
3307
  ] }),
3308
+ confirmFinish && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Modal, { title: "\u7ED3\u675F\u8FD9\u573A\u7075\u542C\uFF1F", busy: finishing, onClose: () => setConfirmFinish(false), children: [
3309
+ /* @__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" }),
3310
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "modal-actions", children: [
3311
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { className: "button", disabled: finishing, onClick: () => setConfirmFinish(false), children: "\u6682\u4E0D\u7ED3\u675F" }),
3312
+ /* @__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" })
3313
+ ] })
3314
+ ] }),
3281
3315
  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 }) }),
3282
3316
  (loaded.status === "finishing" || loaded.status === "interrupted") && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "lt-settling", children: [
3283
3317
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Spinner, { size: 13 }),
@@ -4424,7 +4458,7 @@ function MeetingPrompt({ service }) {
4424
4458
  const meeting = useMeetingPrompt({ service });
4425
4459
  if (!meeting.prompt) return null;
4426
4460
  const appName = meeting.prompt.appName;
4427
- 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`;
4461
+ 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`;
4428
4462
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("aside", { className: "lingting-root lt-meeting", "aria-label": "\u4F1A\u8BAE\u63D0\u9192", children: [
4429
4463
  /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "lt-meeting-head", children: [
4430
4464
  /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Headphones, { size: 16 }),
@@ -4765,6 +4799,26 @@ function RecordList({ service, snapshot, state, onState, onOpen, onStart, onOpen
4765
4799
  const [renameError, setRenameError] = (0, import_react19.useState)("");
4766
4800
  const [shareLoadingId, setShareLoadingId] = (0, import_react19.useState)(null);
4767
4801
  const [shareTarget, setShareTarget] = (0, import_react19.useState)(null);
4802
+ const [deleting, setDeleting] = (0, import_react19.useState)(null);
4803
+ const [deleteBusy, setDeleteBusy] = (0, import_react19.useState)(false);
4804
+ const [deleteError, setDeleteError] = (0, import_react19.useState)("");
4805
+ const deleteLock = (0, import_react19.useRef)(false);
4806
+ const submitDelete = async () => {
4807
+ if (!deleting || deleteLock.current) return;
4808
+ deleteLock.current = true;
4809
+ setDeleteBusy(true);
4810
+ setDeleteError("");
4811
+ try {
4812
+ await service.remove(deleting.id);
4813
+ setDeleting(null);
4814
+ setToast("\u8BB0\u5F55\u5DF2\u5220\u9664");
4815
+ } catch (e) {
4816
+ setDeleteError(e instanceof Error ? e.message : "\u5220\u9664\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
4817
+ } finally {
4818
+ deleteLock.current = false;
4819
+ setDeleteBusy(false);
4820
+ }
4821
+ };
4768
4822
  const [toast, setToast] = (0, import_react19.useState)("");
4769
4823
  const queryKey = `${state.search}\0${state.status}\0${state.page}`;
4770
4824
  const retainContent = !!result && queryKey === resultQuery;
@@ -4782,6 +4836,11 @@ function RecordList({ service, snapshot, state, onState, onOpen, onStart, onOpen
4782
4836
  setError("");
4783
4837
  service.list({ search: state.search, status: state.status, page: state.page, pageSize: PAGE_SIZE }).then((data) => {
4784
4838
  if (!stale) {
4839
+ const lastPage = Math.max(1, Math.ceil(data.total / PAGE_SIZE));
4840
+ if (state.page > lastPage) {
4841
+ onState({ page: lastPage, scroll: 0 });
4842
+ return;
4843
+ }
4785
4844
  setResult(data);
4786
4845
  setResultQuery(queryKey);
4787
4846
  setLoading(false);
@@ -4970,6 +5029,10 @@ function RecordList({ service, snapshot, state, onState, onOpen, onStart, onOpen
4970
5029
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "row-actions", children: [
4971
5030
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "row-open", disabled: detailsDisabled, "data-tip": detailsDisabled ? "\u5F55\u97F3\u4E2D\u6682\u4E0D\u53EF\u67E5\u770B\u8BE6\u60C5" : "\u53BB\u8BE6\u60C5", "aria-label": `\u67E5\u770B ${r.title}`, onClick: () => open(r.id), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ArrowUpRight, { size: 19 }) }),
4972
5031
  isSessionEnded(r.status) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "row-open", "data-tip": "\u5206\u4EAB", "aria-label": `\u5206\u4EAB ${r.title}`, disabled: shareLoadingId === r.id, onClick: () => void openShare(r), children: shareLoadingId === r.id ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Spinner, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Share2, { size: 17 }) }),
5032
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "row-open", "data-tip": isSessionEnded(r.status) ? "\u5220\u9664" : "\u8BF7\u5148\u7ED3\u675F\u7075\u542C\u540E\u518D\u5220\u9664", "aria-label": `\u5220\u9664 ${r.title}`, disabled: !isSessionEnded(r.status) || !!snapshot.operation, onClick: () => {
5033
+ setDeleting(r);
5034
+ setDeleteError("");
5035
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Trash2, { size: 17 }) }),
4973
5036
  isSessionEnded(r.status) && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "row-open", "data-tip": "\u7F16\u8F91", "aria-label": `\u91CD\u547D\u540D ${r.title}`, onClick: () => openRename(r), children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Pencil, { size: 17 }) })
4974
5037
  ] }) })
4975
5038
  ] }, r.id);
@@ -5018,6 +5081,23 @@ function RecordList({ service, snapshot, state, onState, onOpen, onStart, onOpen
5018
5081
  onCopied: setToast
5019
5082
  }
5020
5083
  ),
5084
+ deleting && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Modal, { title: "\u5220\u9664\u8FD9\u6761\u7075\u542C\u8BB0\u5F55\uFF1F", busy: deleteBusy, onClose: () => {
5085
+ if (!deleteLock.current) setDeleting(null);
5086
+ }, children: [
5087
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("p", { className: "modal-description", children: [
5088
+ "\u786E\u5B9A\u5220\u9664\u300C",
5089
+ deleting.title,
5090
+ "\u300D\uFF1F\u5220\u9664\u540E\u65E0\u6CD5\u5728\u7075\u542C\u5217\u8868\u4E2D\u6062\u590D\uFF0C\u8BF7\u786E\u8BA4\u4E0D\u518D\u9700\u8981\u8FD9\u6761\u8BB0\u5F55\u3002"
5091
+ ] }),
5092
+ deleteError && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ErrorNotice, { children: deleteError }),
5093
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "modal-actions", children: [
5094
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { className: "button", disabled: deleteBusy, onClick: () => setDeleting(null), children: "\u53D6\u6D88" }),
5095
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("button", { className: "button danger", disabled: deleteBusy, onClick: () => void submitDelete(), children: [
5096
+ deleteBusy ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Spinner, {}) : null,
5097
+ deleteBusy ? "\u6B63\u5728\u5220\u9664\u2026" : "\u786E\u8BA4\u5220\u9664"
5098
+ ] })
5099
+ ] })
5100
+ ] }),
5021
5101
  renaming && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(Modal, { title: "\u91CD\u547D\u540D\u8BB0\u5F55", onClose: () => setRenaming(null), busy: renameBusy, children: [
5022
5102
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("label", { className: "field-label", htmlFor: "lt-rename-input", children: "\u8BB0\u5F55\u540D\u79F0" }),
5023
5103
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("input", { id: "lt-rename-input", className: "text-input", value: renameInput, maxLength: TITLE_MAX_LENGTH, disabled: renameBusy, onChange: (e) => setRenameInput(e.target.value) }),
@@ -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",