dsh-done-sound 0.1.5 → 0.1.7

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/lib/client.js CHANGED
@@ -85,6 +85,7 @@ window.__ModuleLoader__.load({
85
85
  retrySent: '已自动发送「继续」',
86
86
  retryFailed: '自动发送「继续」失败',
87
87
  logPath: '日志文件',
88
+ exportLog: '导出日志',
88
89
  version: '当前版本',
89
90
  updateAvailable: '有新版本',
90
91
  upToDate: '已是最新',
@@ -132,6 +133,7 @@ window.__ModuleLoader__.load({
132
133
  retrySent: 'auto-sent "continue"',
133
134
  retryFailed: 'failed to auto-send "continue"',
134
135
  logPath: 'Log file',
136
+ exportLog: 'Export log',
135
137
  version: 'Version',
136
138
  updateAvailable: 'Update available',
137
139
  upToDate: 'Up to date',
@@ -407,6 +409,7 @@ window.__ModuleLoader__.load({
407
409
  retryDelaySeconds: typeof payload.retryDelaySeconds === 'number' ? payload.retryDelaySeconds : 60,
408
410
  version: typeof payload.version === 'string' ? payload.version : null,
409
411
  logPath: typeof payload.logPath === 'string' && payload.logPath ? payload.logPath : null,
412
+ logFileName: typeof payload.logFileName === 'string' && payload.logFileName ? payload.logFileName : 'dsh-done-sound.log',
410
413
  url: typeof payload.url === 'string' && payload.url ? payload.url : null,
411
414
  defaultUrl: typeof payload.defaultUrl === 'string' && payload.defaultUrl ? payload.defaultUrl : null,
412
415
  fileName: payload.audio && typeof payload.audio.fileName === 'string' ? payload.audio.fileName : '',
@@ -466,6 +469,38 @@ window.__ModuleLoader__.load({
466
469
  },
467
470
  [],
468
471
  );
472
+ // Export today's log file: fetch it from the host, then hand the
473
+ // browser a Blob download — the native save dialog lets the user pick
474
+ // where to store it (filename is today's dated log name).
475
+ const exportLog = () => {
476
+ fetch('/dsh-done-sound/api/log/export')
477
+ .then((res) => {
478
+ if (!res.ok) {
479
+ return res.json().catch(() => null).then((j) => {
480
+ throw new Error((j && j.error) || 'HTTP ' + res.status);
481
+ });
482
+ }
483
+ return res.text();
484
+ })
485
+ .then((text) => {
486
+ const blob = new Blob([text], { type: 'text/plain;charset=utf-8' });
487
+ const url = URL.createObjectURL(blob);
488
+ const a = document.createElement('a');
489
+ a.href = url;
490
+ a.download = cfg.logFileName || 'dsh-done-sound.log';
491
+ document.body.appendChild(a);
492
+ a.click();
493
+ a.remove();
494
+ setTimeout(() => URL.revokeObjectURL(url), 2000);
495
+ })
496
+ .catch((err) => {
497
+ try {
498
+ window.alert('导出日志失败:' + (err && err.message ? err.message : String(err)));
499
+ } catch {
500
+ // ignore
501
+ }
502
+ });
503
+ };
469
504
 
470
505
  react.useEffect(() => {
471
506
  let disposed = false;
@@ -614,6 +649,15 @@ window.__ModuleLoader__.load({
614
649
  react.createElement('span', { className: 'dtc-muted dtc-logpath' }, cfg.logPath),
615
650
  )
616
651
  : null,
652
+ react.createElement(
653
+ 'div',
654
+ { className: 'dtc-row' },
655
+ react.createElement(
656
+ 'button',
657
+ { className: 'dtc-btn', type: 'button', onClick: exportLog },
658
+ L('exportLog'),
659
+ ),
660
+ ),
617
661
 
618
662
  react.createElement(
619
663
  'div',
package/lib/index.js CHANGED
@@ -809,11 +809,14 @@ function pluginVersion() {
809
809
  return cachedPluginVersion;
810
810
  }
811
811
  var LOGS_ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "logs");
812
- function datedLogPath(now = /* @__PURE__ */ new Date()) {
812
+ function datedLogFileName(now = /* @__PURE__ */ new Date()) {
813
813
  const y = now.getFullYear();
814
814
  const m = String(now.getMonth() + 1).padStart(2, "0");
815
815
  const d = String(now.getDate()).padStart(2, "0");
816
- return join(LOGS_ROOT, `${y}${m}${d}`, "dsh-done-sound.log");
816
+ return `${y}${m}${d}-dsh-done-sound.log`;
817
+ }
818
+ function datedLogPath(now = /* @__PURE__ */ new Date()) {
819
+ return join(LOGS_ROOT, datedLogFileName(now));
817
820
  }
818
821
  function writeLog(level, message) {
819
822
  try {
@@ -889,7 +892,8 @@ function apply(ctx) {
889
892
  },
890
893
  url: fileId ? `${ROUTE_PREFIX}/audio/${fileId}` : null,
891
894
  defaultUrl: DEFAULT_AUDIO_URL,
892
- logPath: datedLogPath()
895
+ logPath: datedLogPath(),
896
+ logFileName: datedLogFileName()
893
897
  };
894
898
  };
895
899
  const clearAudio = async () => {
@@ -1101,7 +1105,23 @@ function apply(ctx) {
1101
1105
  return;
1102
1106
  }
1103
1107
  if (pathname === "/dsh-done-sound/api/log" && method === "GET") {
1104
- sendJson(res, 200, { ok: true, logPath: datedLogPath() });
1108
+ sendJson(res, 200, { ok: true, logPath: datedLogPath(), logFileName: datedLogFileName() });
1109
+ return;
1110
+ }
1111
+ if (pathname === "/dsh-done-sound/api/log/export" && method === "GET") {
1112
+ try {
1113
+ const buffer = await readFile(datedLogPath());
1114
+ res.writeHead(200, {
1115
+ "Content-Type": "text/plain; charset=utf-8",
1116
+ "Content-Disposition": `attachment; filename="${datedLogFileName()}"`,
1117
+ "Content-Length": buffer.length,
1118
+ "Cache-Control": "no-cache",
1119
+ "X-Content-Type-Options": "nosniff"
1120
+ });
1121
+ res.end(buffer);
1122
+ } catch {
1123
+ sendJson(res, 404, { ok: false, error: "\u4ECA\u5929\u7684\u65E5\u5FD7\u6587\u4EF6\u8FD8\u4E0D\u5B58\u5728" });
1124
+ }
1105
1125
  return;
1106
1126
  }
1107
1127
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-done-sound",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "Play a user-chosen sound whenever a conversation finishes in the DeepSeek Harness web GUI",
5
5
  "license": "MIT",
6
6
  "type": "module",