@windypro-rourou/dsh-logcat 0.6.0-preview.5 → 0.6.1
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 +4 -5
- package/lib/client.js +37 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,12 +74,11 @@ dsh plugin --profile web update # 升到当前 major 内最新
|
|
|
74
74
|
dsh plugin --profile web add @windypro-rourou/dsh-logcat@preview
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
## 发布策略(main /
|
|
77
|
+
## 发布策略(main / latest 为主)
|
|
78
78
|
|
|
79
|
-
- **`main` 分支 + npm `latest`
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
凑够足够功能后合并回 `main` 批量发布正式版。
|
|
79
|
+
- **`main` 分支 + npm `latest` 标签**:正式版,更新的主要通道 —— 功能攒够一批就发,直接推 main/latest。
|
|
80
|
+
- **`preview` 分支 + npm `preview` 标签**:可选尝鲜通道(高频小步迭代)。正式版发布后如无新的试验特性,
|
|
81
|
+
preview 标签可能停留在上一个预览版本,不必理会。
|
|
83
82
|
- 版本自检会按安装通道提示(正式版用户只看 `latest`,preview 用户只看 `preview`,互不打扰)。
|
|
84
83
|
|
|
85
84
|
```bash
|
package/lib/client.js
CHANGED
|
@@ -90,7 +90,7 @@ window.__ModuleLoader__.load({
|
|
|
90
90
|
.lc-line.crash .tag { color: #ef5350; font-weight: 700; }
|
|
91
91
|
.lc-line.crash .msg { color: #ef5350; }
|
|
92
92
|
.lc-empty { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; color: light-dark(#9aa0a8, #7c838d); font-size: 13px; }
|
|
93
|
-
.lc-status { display: flex; align-items: center; gap: 14px; padding: 5px 14px; border-top: 1px solid light-dark(rgba(0,0,0,.1), rgba(255,255,255,.12)); flex: none; font-size: 11px; color: light-dark(#6b7078, #9aa0a8); }
|
|
93
|
+
.lc-status { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 14px; padding: 5px 14px; border-top: 1px solid light-dark(rgba(0,0,0,.1), rgba(255,255,255,.12)); flex: none; font-size: 11px; color: light-dark(#6b7078, #9aa0a8); }
|
|
94
94
|
.lc-status b { font-weight: 600; color: inherit; }
|
|
95
95
|
.lc-tabs { display: flex; gap: 4px; padding: 6px 12px 0; border-bottom: 1px solid light-dark(rgba(0,0,0,.08), rgba(255,255,255,.1)); flex: none; }
|
|
96
96
|
.lc-tab { border: 0; background: transparent; color: inherit; font: inherit; font-size: 13px; font-weight: 600; padding: 6px 12px; border-radius: 7px 7px 0 0; cursor: pointer; opacity: .75; }
|
|
@@ -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
|
-
|
|
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)
|
|
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
|
|
499
|
-
|
|
500
|
-
pendingRef.current
|
|
501
|
-
|
|
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) },
|
|
@@ -1101,7 +1127,7 @@ window.__ModuleLoader__.load({
|
|
|
1101
1127
|
h("div", { className: "lc-status" },
|
|
1102
1128
|
h("span", null,
|
|
1103
1129
|
h("b", null, adbReady ? "adb 就绪" : "未找到 adb"),
|
|
1104
|
-
" · " + (adbPath || "—"),
|
|
1130
|
+
h("span", { title: adbPath || undefined }, " · " + (adbPath !== "" ? (adbPath || "—").split(/[\\/]/).pop() : "—")),
|
|
1105
1131
|
!adbReady
|
|
1106
1132
|
? h("button", {
|
|
1107
1133
|
type: "button",
|
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.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@11.22.0",
|
|
7
7
|
"engines": {
|