mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-05 19:42:15 +02:00
feat(demo): support multi-select in logcat and packet log
related to #425
This commit is contained in:
parent
055da71f6c
commit
52abdd146b
8 changed files with 1075 additions and 835 deletions
|
@ -33,7 +33,7 @@
|
|||
"@yume-chan/struct": "workspace:^0.0.18",
|
||||
"mobx": "^6.7.0",
|
||||
"mobx-react-lite": "^3.4.0",
|
||||
"next": "13.1.1",
|
||||
"next": "13.1.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"webm-muxer": "^1.1.0",
|
||||
|
@ -46,9 +46,9 @@
|
|||
"@mdx-js/loader": "^2.2.1",
|
||||
"@mdx-js/react": "^2.2.1",
|
||||
"@next/mdx": "^13.1.1",
|
||||
"@types/react": "18.0.26",
|
||||
"@types/react": "18.0.27",
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-config-next": "13.1.1",
|
||||
"eslint-config-next": "13.1.5",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"typescript": "^4.9.4"
|
||||
}
|
||||
|
|
|
@ -4,23 +4,22 @@ import { withDisplayName } from "../utils";
|
|||
|
||||
const useClasses = makeStyles({
|
||||
root: {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
overflowY: 'auto',
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
overflowY: "auto",
|
||||
},
|
||||
flex: {
|
||||
|
||||
display: 'flex',
|
||||
display: "flex",
|
||||
},
|
||||
cell: {
|
||||
fontFamily: '"Cascadia Code", Consolas, monospace',
|
||||
},
|
||||
lineNumber: {
|
||||
textAlign: 'right',
|
||||
textAlign: "right",
|
||||
},
|
||||
hex: {
|
||||
marginLeft: '40px',
|
||||
fontVariantLigatures: 'none',
|
||||
marginLeft: "40px",
|
||||
fontVariantLigatures: "none",
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -32,20 +31,19 @@ const PRINTABLE_CHARACTERS: [number, number][] = [
|
|||
|
||||
export function isPrintableCharacter(code: number) {
|
||||
return PRINTABLE_CHARACTERS.some(
|
||||
([start, end]) =>
|
||||
code >= start &&
|
||||
code <= end
|
||||
([start, end]) => code >= start && code <= end
|
||||
);
|
||||
}
|
||||
|
||||
export function toCharacter(code: number) {
|
||||
if (isPrintableCharacter(code))
|
||||
if (isPrintableCharacter(code)) {
|
||||
return String.fromCharCode(code);
|
||||
return '.';
|
||||
}
|
||||
return ".";
|
||||
}
|
||||
|
||||
export function toText(data: Uint8Array) {
|
||||
let result = '';
|
||||
let result = "";
|
||||
for (const code of data) {
|
||||
result += toCharacter(code);
|
||||
}
|
||||
|
@ -59,10 +57,8 @@ export interface HexViewerProps {
|
|||
data: Uint8Array;
|
||||
}
|
||||
|
||||
export const HexViewer = withDisplayName('HexViewer')(({
|
||||
className,
|
||||
data
|
||||
}: HexViewerProps) => {
|
||||
export const HexViewer = withDisplayName("HexViewer")(
|
||||
({ className, data }: HexViewerProps) => {
|
||||
const classes = useClasses();
|
||||
|
||||
// Because ADB packets are usually small,
|
||||
|
@ -73,26 +69,16 @@ export const HexViewer = withDisplayName('HexViewer')(({
|
|||
const hexRows: ReactNode[] = [];
|
||||
const textRows: ReactNode[] = [];
|
||||
for (let i = 0; i < data.length; i += PER_ROW) {
|
||||
lineNumbers.push(
|
||||
<div>
|
||||
{i.toString(16)}
|
||||
</div>
|
||||
);
|
||||
lineNumbers.push(<div key={i}>{i.toString(16)}</div>);
|
||||
|
||||
let hex = '';
|
||||
let hex = "";
|
||||
for (let j = i; j < i + PER_ROW && j < data.length; j++) {
|
||||
hex += data[j].toString(16).padStart(2, '0') + ' ';
|
||||
hex += data[j].toString(16).padStart(2, "0") + " ";
|
||||
}
|
||||
hexRows.push(
|
||||
<div>
|
||||
{hex}
|
||||
</div>
|
||||
);
|
||||
hexRows.push(<div key={i}>{hex}</div>);
|
||||
|
||||
textRows.push(
|
||||
<div>
|
||||
{toText(data.slice(i, i + PER_ROW))}
|
||||
</div>
|
||||
<div key={i}>{toText(data.slice(i, i + PER_ROW))}</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -106,7 +92,12 @@ export const HexViewer = withDisplayName('HexViewer')(({
|
|||
return (
|
||||
<div className={mergeClasses(classes.root, className)}>
|
||||
<div className={classes.flex}>
|
||||
<div className={mergeClasses(classes.cell, classes.lineNumber)}>
|
||||
<div
|
||||
className={mergeClasses(
|
||||
classes.cell,
|
||||
classes.lineNumber
|
||||
)}
|
||||
>
|
||||
{children.lineNumbers}
|
||||
</div>
|
||||
<div className={mergeClasses(classes.cell, classes.hex)}>
|
||||
|
@ -118,4 +109,5 @@ export const HexViewer = withDisplayName('HexViewer')(({
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
import {
|
||||
Checkbox,
|
||||
ICommandBarItemProps,
|
||||
Stack,
|
||||
StackItem,
|
||||
} from "@fluentui/react";
|
||||
import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react";
|
||||
import { makeStyles, mergeClasses, shorthands } from "@griffel/react";
|
||||
import {
|
||||
AndroidLogEntry,
|
||||
|
@ -12,7 +7,6 @@ import {
|
|||
LogcatFormat,
|
||||
formatAndroidLogEntry,
|
||||
} from "@yume-chan/android-bin";
|
||||
import { BTree } from "@yume-chan/b-tree";
|
||||
import {
|
||||
AbortController,
|
||||
ReadableStream,
|
||||
|
@ -28,7 +22,7 @@ import {
|
|||
import { observer } from "mobx-react-lite";
|
||||
import { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import { FormEvent, useEffect, useState } from "react";
|
||||
import { PointerEvent } from "react";
|
||||
|
||||
import {
|
||||
CommandBar,
|
||||
|
@ -39,6 +33,7 @@ import {
|
|||
} from "../components";
|
||||
import { GLOBAL_STATE } from "../state";
|
||||
import { Icons, RouteStackProps, useStableCallback } from "../utils";
|
||||
import { ObservableListSelection } from "./packet-log";
|
||||
|
||||
const LINE_HEIGHT = 32;
|
||||
|
||||
|
@ -85,11 +80,10 @@ const state = makeAutoObservable(
|
|||
buffer: [] as LogRow[],
|
||||
flushRequested: false,
|
||||
list: [] as LogRow[],
|
||||
selection: new BTree(6),
|
||||
selection: new ObservableListSelection(),
|
||||
count: 0,
|
||||
stream: undefined as ReadableStream<AndroidLogEntry> | undefined,
|
||||
stopSignal: undefined as AbortController | undefined,
|
||||
selectedCount: 0,
|
||||
animationFrameId: undefined as number | undefined,
|
||||
start() {
|
||||
if (this.running) {
|
||||
|
@ -131,7 +125,6 @@ const state = makeAutoObservable(
|
|||
clear() {
|
||||
this.list = [];
|
||||
this.selection.clear();
|
||||
this.selectedCount = 0;
|
||||
},
|
||||
get empty() {
|
||||
return this.list.length === 0;
|
||||
|
@ -162,7 +155,7 @@ const state = makeAutoObservable(
|
|||
{
|
||||
key: "copyAll",
|
||||
text: "Copy Rows",
|
||||
disabled: this.selectedCount === 0,
|
||||
disabled: this.selection.size === 0,
|
||||
iconProps: { iconName: Icons.Copy },
|
||||
onClick: () => {
|
||||
let text = "";
|
||||
|
@ -181,7 +174,7 @@ const state = makeAutoObservable(
|
|||
{
|
||||
key: "copyText",
|
||||
text: "Copy Messages",
|
||||
disabled: this.selectedCount === 0,
|
||||
disabled: this.selection.size === 0,
|
||||
iconProps: { iconName: Icons.Copy },
|
||||
onClick: () => {
|
||||
let text = "";
|
||||
|
@ -197,54 +190,6 @@ const state = makeAutoObservable(
|
|||
},
|
||||
get columns(): Column[] {
|
||||
return [
|
||||
{
|
||||
width: 40,
|
||||
title: "",
|
||||
CellComponent: ({
|
||||
rowIndex,
|
||||
columnIndex,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
const [checked, setChecked] = useState(false);
|
||||
useEffect(() => {
|
||||
setChecked(this.selection.has(rowIndex));
|
||||
}, [rowIndex]);
|
||||
|
||||
const handleChange = useStableCallback(
|
||||
(e?: FormEvent<EventTarget>, checked?: boolean) => {
|
||||
if (checked === undefined) {
|
||||
return;
|
||||
}
|
||||
if (checked) {
|
||||
this.selection.add(rowIndex);
|
||||
setChecked(true);
|
||||
} else {
|
||||
this.selection.delete(rowIndex);
|
||||
setChecked(false);
|
||||
}
|
||||
runInAction(() => {
|
||||
// Trigger mobx
|
||||
this.selectedCount = this.selection.size;
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack
|
||||
className={className}
|
||||
verticalAlign="center"
|
||||
horizontalAlign="center"
|
||||
{...rest}
|
||||
>
|
||||
<Checkbox
|
||||
checked={checked}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
width: 200,
|
||||
title: "Time",
|
||||
|
@ -445,22 +390,35 @@ const Header = observer(function Header({
|
|||
);
|
||||
});
|
||||
|
||||
const Row = function Row({ className, rowIndex, ...rest }: GridRowProps) {
|
||||
const item = state.list[rowIndex];
|
||||
const Row = observer(function Row({
|
||||
className,
|
||||
rowIndex,
|
||||
...rest
|
||||
}: GridRowProps) {
|
||||
const classes = useClasses();
|
||||
|
||||
const handleClick = useStableCallback(() => {
|
||||
runInAction(() => {});
|
||||
const handlePointerDown = useStableCallback(
|
||||
(e: PointerEvent<HTMLDivElement>) => {
|
||||
runInAction(() => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
state.selection.select(rowIndex, e.ctrlKey, e.shiftKey);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={mergeClasses(className, classes.row)}
|
||||
onClick={handleClick}
|
||||
className={mergeClasses(
|
||||
className,
|
||||
classes.row,
|
||||
state.selection.has(rowIndex) && classes.selected
|
||||
)}
|
||||
onPointerDown={handlePointerDown}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
const LogcatPage: NextPage = () => {
|
||||
const classes = useClasses();
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
import { ICommandBarItemProps, Stack, StackItem } from "@fluentui/react";
|
||||
import { makeStyles, mergeClasses, shorthands } from "@griffel/react";
|
||||
import { AdbCommand, decodeUtf8 } from "@yume-chan/adb";
|
||||
import { autorun, makeAutoObservable, observable, runInAction } from "mobx";
|
||||
import { BTree, BTreeNode } from "@yume-chan/b-tree";
|
||||
import {
|
||||
IAtom,
|
||||
IObservableValue,
|
||||
action,
|
||||
autorun,
|
||||
createAtom,
|
||||
makeAutoObservable,
|
||||
observable,
|
||||
onBecomeUnobserved,
|
||||
runInAction,
|
||||
} from "mobx";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import { PointerEvent } from "react";
|
||||
import {
|
||||
CommandBar,
|
||||
Grid,
|
||||
|
@ -15,7 +27,7 @@ import {
|
|||
HexViewer,
|
||||
toText,
|
||||
} from "../components";
|
||||
import { GLOBAL_STATE, PacketLogItem } from "../state";
|
||||
import { GLOBAL_STATE } from "../state";
|
||||
import {
|
||||
Icons,
|
||||
RouteStackProps,
|
||||
|
@ -23,6 +35,139 @@ import {
|
|||
withDisplayName,
|
||||
} from "../utils";
|
||||
|
||||
export class ObservableBTree implements BTree {
|
||||
data: BTree;
|
||||
hasMap: Map<number, IObservableValue<boolean>>;
|
||||
keys: IAtom;
|
||||
|
||||
constructor(order: number) {
|
||||
this.data = new BTree(order);
|
||||
this.hasMap = new Map();
|
||||
this.keys = createAtom("ObservableBTree.keys");
|
||||
}
|
||||
|
||||
get order(): number {
|
||||
return this.data.order;
|
||||
}
|
||||
|
||||
get root(): BTreeNode {
|
||||
return this.data.root;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
this.keys.reportObserved();
|
||||
return this.data.size;
|
||||
}
|
||||
|
||||
has(value: number): boolean {
|
||||
if (!this.hasMap.has(value)) {
|
||||
const observableHasValue = observable.box(this.data.has(value));
|
||||
onBecomeUnobserved(observableHasValue, () =>
|
||||
this.hasMap.delete(value)
|
||||
);
|
||||
this.hasMap.set(value, observableHasValue);
|
||||
}
|
||||
return this.hasMap.get(value)!.get();
|
||||
}
|
||||
|
||||
add(value: number): boolean {
|
||||
if (this.data.add(value)) {
|
||||
this.hasMap.get(value)?.set(true);
|
||||
this.keys.reportChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
delete(value: number): boolean {
|
||||
if (this.data.delete(value)) {
|
||||
this.hasMap.get(value)?.set(false);
|
||||
this.keys.reportChanged();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
if (this.data.size === 0) {
|
||||
return;
|
||||
}
|
||||
this.data.clear();
|
||||
for (const entry of this.hasMap) {
|
||||
entry[1].set(false);
|
||||
}
|
||||
this.keys.reportChanged();
|
||||
}
|
||||
|
||||
[Symbol.iterator](): Generator<number, void, void> {
|
||||
this.keys.reportObserved();
|
||||
return this.data[Symbol.iterator]();
|
||||
}
|
||||
}
|
||||
|
||||
export class ObservableListSelection {
|
||||
selected = new ObservableBTree(6);
|
||||
rangeStart = 0;
|
||||
selectedIndex: number | null = null;
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
get size() {
|
||||
return this.selected.size;
|
||||
}
|
||||
|
||||
has(index: number) {
|
||||
return this.selected.has(index);
|
||||
}
|
||||
|
||||
select(index: number, ctrlKey: boolean, shiftKey: boolean) {
|
||||
if (this.rangeStart !== null && shiftKey) {
|
||||
if (!ctrlKey) {
|
||||
this.selected.clear();
|
||||
}
|
||||
|
||||
let [start, end] = [this.rangeStart, index];
|
||||
if (start > end) {
|
||||
[start, end] = [end, start];
|
||||
}
|
||||
for (let i = start; i <= end; i += 1) {
|
||||
this.selected.add(i);
|
||||
}
|
||||
this.selectedIndex = index;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrlKey) {
|
||||
if (this.selected.has(index)) {
|
||||
this.selected.delete(index);
|
||||
this.selectedIndex = null;
|
||||
} else {
|
||||
this.selected.add(index);
|
||||
this.selectedIndex = index;
|
||||
}
|
||||
this.rangeStart = index;
|
||||
return;
|
||||
}
|
||||
|
||||
this.selected.clear();
|
||||
this.selected.add(index);
|
||||
this.rangeStart = index;
|
||||
this.selectedIndex = index;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.selected.clear();
|
||||
this.rangeStart = 0;
|
||||
this.selectedIndex = null;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
return this.selected[Symbol.iterator]();
|
||||
}
|
||||
}
|
||||
|
||||
const ADB_COMMAND_NAME = {
|
||||
[AdbCommand.Auth]: "AUTH",
|
||||
[AdbCommand.Close]: "CLSE",
|
||||
|
@ -38,6 +183,12 @@ interface Column extends GridColumn {
|
|||
|
||||
const LINE_HEIGHT = 32;
|
||||
|
||||
function uint8ArrayToHexString(array: Uint8Array) {
|
||||
return Array.from(array)
|
||||
.map((byte) => byte.toString(16).padStart(2, "0"))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
const state = new (class {
|
||||
get empty() {
|
||||
return !GLOBAL_STATE.logs.length;
|
||||
|
@ -50,21 +201,60 @@ const state = new (class {
|
|||
disabled: this.empty,
|
||||
iconProps: { iconName: Icons.Delete },
|
||||
text: "Clear",
|
||||
onClick: () => GLOBAL_STATE.clearLog(),
|
||||
onClick: action(() => GLOBAL_STATE.clearLog()),
|
||||
},
|
||||
{
|
||||
key: "select-all",
|
||||
disabled: this.empty,
|
||||
iconProps: { iconName: Icons.Wand },
|
||||
text: "Select All",
|
||||
onClick: action(() => {
|
||||
this.selection.clear();
|
||||
this.selection.select(
|
||||
GLOBAL_STATE.logs.length - 1,
|
||||
false,
|
||||
true
|
||||
);
|
||||
}),
|
||||
},
|
||||
{
|
||||
key: "copy",
|
||||
disabled: this.selection.size === 0,
|
||||
iconProps: { iconName: Icons.Copy },
|
||||
text: "Copy",
|
||||
onClick: () => {
|
||||
let text = "";
|
||||
for (const index of this.selection) {
|
||||
const entry = GLOBAL_STATE.logs[index];
|
||||
// prettier-ignore
|
||||
text += `${
|
||||
entry.timestamp!.toISOString()
|
||||
}\t${
|
||||
entry.direction === 'in' ? "IN" : "OUT"
|
||||
}\t${
|
||||
ADB_COMMAND_NAME[entry.command as keyof typeof ADB_COMMAND_NAME]
|
||||
}\t${
|
||||
entry.arg0.toString(16).padStart(8,'0')
|
||||
}\t${
|
||||
entry.arg1.toString(16).padStart(8,'0')
|
||||
}\t${
|
||||
uint8ArrayToHexString(entry.payload)
|
||||
}\n`;
|
||||
}
|
||||
navigator.clipboard.writeText(text);
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
selectedPacket: PacketLogItem | undefined = undefined;
|
||||
selection = new ObservableListSelection();
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this, {
|
||||
selectedPacket: observable.ref,
|
||||
});
|
||||
makeAutoObservable(this, {});
|
||||
|
||||
autorun(() => {
|
||||
if (GLOBAL_STATE.logs.length === 0) {
|
||||
this.selectedPacket = undefined;
|
||||
runInAction(() => this.selection.clear());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -244,21 +434,24 @@ const Row = observer(function Row({
|
|||
}: GridRowProps) {
|
||||
const classes = useClasses();
|
||||
|
||||
const handleClick = useStableCallback(() => {
|
||||
const handlePointerDown = useStableCallback(
|
||||
(e: PointerEvent<HTMLDivElement>) => {
|
||||
runInAction(() => {
|
||||
state.selectedPacket = GLOBAL_STATE.logs[rowIndex];
|
||||
});
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
state.selection.select(rowIndex, e.ctrlKey, e.shiftKey);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={mergeClasses(
|
||||
className,
|
||||
classes.row,
|
||||
state.selectedPacket === GLOBAL_STATE.logs[rowIndex] &&
|
||||
classes.selected
|
||||
state.selection.has(rowIndex) && classes.selected
|
||||
)}
|
||||
onClick={handleClick}
|
||||
onPointerDown={handlePointerDown}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
|
@ -286,12 +479,16 @@ const PacketLog: NextPage = () => {
|
|||
/>
|
||||
</StackItem>
|
||||
|
||||
{state.selectedPacket &&
|
||||
state.selectedPacket.payload.length > 0 && (
|
||||
{state.selection.selectedIndex !== null &&
|
||||
GLOBAL_STATE.logs[state.selection.selectedIndex].payload
|
||||
.length > 0 && (
|
||||
<StackItem className={classes.grow} grow>
|
||||
<HexViewer
|
||||
className={classes.hexViewer}
|
||||
data={state.selectedPacket.payload}
|
||||
data={
|
||||
GLOBAL_STATE.logs[state.selection.selectedIndex]
|
||||
.payload
|
||||
}
|
||||
/>
|
||||
</StackItem>
|
||||
)}
|
||||
|
|
|
@ -6,6 +6,7 @@ export type PacketLogItemDirection = "in" | "out";
|
|||
export interface PacketLogItem extends AdbPacketData {
|
||||
direction: PacketLogItemDirection;
|
||||
|
||||
timestamp?: Date;
|
||||
commandString?: string;
|
||||
arg0String?: string;
|
||||
arg1String?: string;
|
||||
|
@ -48,11 +49,12 @@ export class GlobalState {
|
|||
|
||||
appendLog(direction: PacketLogItemDirection, packet: AdbPacketData) {
|
||||
(packet as PacketLogItem).direction = direction;
|
||||
(packet as PacketLogItem).timestamp = new Date();
|
||||
this.logs.push(packet as PacketLogItem);
|
||||
}
|
||||
|
||||
clearLog() {
|
||||
this.logs = [];
|
||||
this.logs.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
1423
common/config/rush/pnpm-lock.yaml
generated
1423
common/config/rush/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
// DO NOT MODIFY THIS FILE MANUALLY BUT DO COMMIT IT. It is generated and used by Rush.
|
||||
{
|
||||
"pnpmShrinkwrapHash": "cc0a83ec8632b8d767ce54866717a4770246d3dd",
|
||||
"pnpmShrinkwrapHash": "a4548759bf683ee48a45a7367030c615f7b33683",
|
||||
"preferredVersionsHash": "bf21a9e8fbc5a3846fb05b4fa0859e0917b2202f"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// cspell: ignore logcat
|
||||
// cspell: ignore usec
|
||||
|
||||
import { AdbCommandBase, AdbSubprocessNoneProtocol } from "@yume-chan/adb";
|
||||
import {
|
||||
|
@ -124,12 +125,21 @@ export function formatAndroidLogEntry(
|
|||
|
||||
switch (format) {
|
||||
// TODO: implement other formats
|
||||
default:
|
||||
return `${
|
||||
default: {
|
||||
// prettier-ignore
|
||||
const text=`${
|
||||
AndroidLogPriorityToCharacter[entry.priority]
|
||||
}/${entry.tag.padEnd(8)}(${uid}${entry.pid
|
||||
.toString()
|
||||
.padStart(5)}): ${entry.message}`;
|
||||
}/${
|
||||
entry.tag.padEnd(8)
|
||||
}(${
|
||||
uid
|
||||
}${
|
||||
entry.pid.toString().padStart(5)
|
||||
}): ${
|
||||
entry.message
|
||||
}`;
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue