@windypro-rourou/dsh-logcat 0.6.0-preview.5 → 0.6.0

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.
Files changed (2) hide show
  1. package/lib/client.js +35 -9
  2. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -139,6 +139,10 @@ window.__ModuleLoader__.load({
139
139
  .lc-screen-img { flex: 1; min-height: 0; overflow: auto; display: flex; align-items: center; justify-content: center; cursor: crosshair; background: light-dark(rgba(0,0,0,.03), rgba(255,255,255,.03)); position: relative; }
140
140
  .lc-screen-img img, .lc-screen-img canvas { max-width: 100%; max-height: 100%; object-fit: contain; user-select: none; }
141
141
  .lc-screen-hint { color: light-dark(#6b7078, #9aa0a8); }
142
+ .lc-pausedbar { display: flex; align-items: center; gap: 8px; padding: 5px 12px; background: light-dark(rgba(251,192,45,.16), rgba(251,192,45,.18)); border-bottom: 1px solid rgba(251,192,45,.45); flex: none; font-size: 12px; color: #fbc02d; }
143
+ .lc-pausedbar .lc-btn { padding: 2px 10px; }
144
+ .lc-fab { position: absolute; right: 14px; bottom: 14px; z-index: 4; border: 1px solid rgba(66,133,244,.45); background: light-dark(rgba(66,133,244,.14), rgba(66,133,244,.24)); color: #4285f4; font: inherit; font-size: 12px; font-weight: 700; padding: 8px 14px; border-radius: 999px; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,.22); }
145
+ .lc-fab:hover { background: light-dark(rgba(66,133,244,.22), rgba(66,133,244,.34)); }
142
146
  `;
143
147
  //#endregion
144
148
 
@@ -183,6 +187,7 @@ window.__ModuleLoader__.load({
183
187
  const [entries, setEntries] = useState([]);
184
188
  const [connected, setConnected] = useState(false);
185
189
  const [paused, setPaused] = useState(false);
190
+ const [pendingCount, setPendingCount] = useState(0);
186
191
  const [currentPackage, setCurrentPackage] = useState("");
187
192
  const [pkgInput, setPkgInput] = useState("");
188
193
  const [installingAdb, setInstallingAdb] = useState(false);
@@ -299,10 +304,19 @@ window.__ModuleLoader__.load({
299
304
 
300
305
  const appendEntries = (incoming) => {
301
306
  const next = entriesRef.current.concat(incoming);
302
- if (next.length > 2000) next.splice(0, next.length - 2000);
307
+ let removed = 0;
308
+ if (next.length > 5000) { removed = next.length - 5000; next.splice(0, removed); }
303
309
  entriesRef.current = next;
304
310
  setEntries(next);
305
- if (autoScrollRef.current) requestAnimationFrame(() => scrollToBottom());
311
+ if (autoScrollRef.current) {
312
+ requestAnimationFrame(() => scrollToBottom());
313
+ } else if (removed > 0) {
314
+ // Anchor the viewport: trimming old rows shifts array indexes, so
315
+ // compensate the scroll position to keep what the user is reading
316
+ // visually in place (no jumping while auto-scroll is off).
317
+ const body = bodyRef.current;
318
+ if (body !== null) body.scrollTop = Math.max(0, body.scrollTop - removed * 20);
319
+ }
306
320
  };
307
321
 
308
322
  const applyFrame = (frame) => {
@@ -334,6 +348,7 @@ window.__ModuleLoader__.load({
334
348
  if (pausedRef.current) {
335
349
  pendingRef.current.push(frame);
336
350
  if (pendingRef.current.length > 800) pendingRef.current.splice(0, pendingRef.current.length - 800);
351
+ setPendingCount(pendingRef.current.length);
337
352
  return;
338
353
  }
339
354
  if (frame.serial !== serialRef.current) return;
@@ -495,10 +510,13 @@ window.__ModuleLoader__.load({
495
510
  const togglePause = () => {
496
511
  const next = !paused;
497
512
  setPaused(next);
498
- if (!next && pendingRef.current.length > 0) {
499
- const replay = pendingRef.current.filter((f) => f.serial === serialRef.current);
500
- pendingRef.current = [];
501
- if (replay.length > 0) appendEntries(replay.map((f) => f.entry));
513
+ if (!next) {
514
+ setPendingCount(0);
515
+ if (pendingRef.current.length > 0) {
516
+ const replay = pendingRef.current.filter((f) => f.serial === serialRef.current);
517
+ pendingRef.current = [];
518
+ if (replay.length > 0) appendEntries(replay.map((f) => f.entry));
519
+ }
502
520
  }
503
521
  };
504
522
 
@@ -953,7 +971,7 @@ window.__ModuleLoader__.load({
953
971
  }),
954
972
  h("button", { type: "button", className: "lc-btn", onClick: () => fileInputRef.current?.click(), title: "选择本地 APK 安装到当前设备", disabled: installingApk ? true : undefined }, installingApk ? "安装中…" : "装APK"),
955
973
  h("button", { type: "button", className: "lc-btn", "data-on": paused ? "" : undefined, onClick: togglePause },
956
- paused ? "继续" : "暂停"),
974
+ paused ? "继续(" + pendingCount + ")" : "暂停"),
957
975
  h("button", { type: "button", className: "lc-btn", onClick: clearLog }, "清空"),
958
976
  h("button", { type: "button", className: "lc-btn", onClick: copyLog }, "复制"),
959
977
  h("button", { type: "button", className: "lc-btn", onClick: exportLog }, "导出"),
@@ -986,10 +1004,15 @@ window.__ModuleLoader__.load({
986
1004
  h("button", { type: "button", className: "lc-btn", onClick: () => setHistoryNote("") }, "关闭"),
987
1005
  )
988
1006
  : null,
1007
+ paused && tab === "log" && !eventsOn
1008
+ ? h("div", { className: "lc-pausedbar" },
1009
+ h("span", { style: { flex: 1 } }, "⏸ 已暂停 — 新日志缓冲中(" + pendingCount + " 条),列表已冻结"),
1010
+ h("button", { type: "button", className: "lc-btn", onClick: togglePause, title: "恢复实时,回放缓冲日志" }, "继续"),
1011
+ )
1012
+ : null,
989
1013
  h("div", { className: "lc-body", ref: bodyRef },
990
1014
  tab === "re"
991
- ? h("div", { className: "lc-re" },
992
- h("div", { className: "lc-re-top" },
1015
+ ? h("div", { className: "lc-re" }, h("div", { className: "lc-re-top" },
993
1016
  h("input", { placeholder: "进程过滤…", value: reFilter, onChange: (e) => setReFilter(e.target.value), onKeyDown: (e) => { if (e.key === "Enter") loadProcesses(); }, style: { width: 120 } }),
994
1017
  h("button", { type: "button", className: "lc-btn", onClick: loadProcesses }, "进程"),
995
1018
  h("input", { placeholder: "搜索: hello 或 hex:48656c6c6f", value: rePattern, onChange: (e) => setRePattern(e.target.value), onKeyDown: (e) => { if (e.key === "Enter") reSearch(); }, style: { flex: 1, minWidth: 140 } }),
@@ -1041,6 +1064,9 @@ window.__ModuleLoader__.load({
1041
1064
  : (filtered.length === 0
1042
1065
  ? h("div", { className: "lc-empty" }, paused ? "已暂停(" + entries.length + " 条已缓冲)" : "暂无日志")
1043
1066
  : h(VirtualLog, { entries: filtered, scrollTop, onScrollTop: setScrollTop, bodyRef })),
1067
+ tab === "log" && !eventsOn && live && !paused
1068
+ ? h("button", { type: "button", className: "lc-fab", onClick: togglePause, title: "暂停:冻结当前视图(新日志进入缓冲)" }, "⏸ 暂停")
1069
+ : null,
1044
1070
  ),
1045
1071
  crashModal
1046
1072
  ? h("div", { className: "lc-overlay", onClick: () => setCrashModal(false) },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@windypro-rourou/dsh-logcat",
3
3
  "description": "Android Logcat viewer for the dsh web GUI: auto-connects to any adb device in debug mode, live logcat stream with level/keyword filters, pause/clear/export, plus agent tools (logcat_recent). Hot-pluggable — mounted via ~/.dsh/cordis.patch.yml + a profile node_modules copy, no dsh source changes.",
4
- "version": "0.6.0-preview.5",
4
+ "version": "0.6.0",
5
5
  "type": "module",
6
6
  "packageManager": "pnpm@11.22.0",
7
7
  "engines": {