@karinjs/plugin-basic 1.3.3 → 1.3.5
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 +9 -5
- package/dist/apps/logger.js +138 -83
- package/dist/apps/login.js +2 -2
- package/dist/apps/restart.js +2 -2
- package/dist/apps/status.js +2 -2
- package/dist/apps/update.js +2 -2
- package/dist/{chunk-AAM3ZLYA.js → chunk-4GSJ5FLI.js} +1 -1
- package/dist/{chunk-ZRGOU5NN.js → chunk-TUC5A7K4.js} +1 -1
- package/dist/index.js +2 -2
- package/dist/web.config.js +2 -2
- package/package.json +1 -1
- package/resources/logger/index.css +25 -167
- package/resources/logger/index.html +7 -24
package/README.md
CHANGED
|
@@ -10,22 +10,26 @@ pnpm add @karinjs/plugin-basic -w
|
|
|
10
10
|
|
|
11
11
|
## 基本指令
|
|
12
12
|
|
|
13
|
-
```
|
|
13
|
+
```bash
|
|
14
14
|
# 关机
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
```
|
|
17
|
+
```text
|
|
18
18
|
# 重启
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
```
|
|
21
|
+
```text
|
|
22
22
|
# 状态
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
```
|
|
25
|
+
```text
|
|
26
26
|
# 插件列表
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
```
|
|
29
|
+
```text
|
|
30
30
|
#web登录
|
|
31
31
|
```
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
#日志
|
|
35
|
+
```
|
package/dist/apps/logger.js
CHANGED
|
@@ -1,62 +1,143 @@
|
|
|
1
1
|
import {
|
|
2
2
|
render
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-4GSJ5FLI.js";
|
|
4
4
|
|
|
5
5
|
// src/apps/logger.ts
|
|
6
6
|
import fs from "fs";
|
|
7
7
|
import path from "path";
|
|
8
8
|
import { karin, logger } from "node-karin";
|
|
9
|
+
var ESC = "\x1B";
|
|
10
|
+
var ANSI_REGEX = new RegExp(`${ESC}\\[([0-9;]+)m`, "g");
|
|
11
|
+
var escapeHtml = (str) => str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
12
|
+
var ANSI_COLOR_MAP = {
|
|
13
|
+
30: "#020617",
|
|
14
|
+
31: "#ef4444",
|
|
15
|
+
32: "#22c55e",
|
|
16
|
+
33: "#eab308",
|
|
17
|
+
34: "#3b82f6",
|
|
18
|
+
35: "#ec4899",
|
|
19
|
+
36: "#06b6d4",
|
|
20
|
+
37: "#e5e7eb",
|
|
21
|
+
90: "#6b7280",
|
|
22
|
+
91: "#f97373",
|
|
23
|
+
92: "#4ade80",
|
|
24
|
+
93: "#fde047",
|
|
25
|
+
94: "#60a5fa",
|
|
26
|
+
95: "#a855f7",
|
|
27
|
+
96: "#38bdf8",
|
|
28
|
+
97: "#f9fafb"
|
|
29
|
+
};
|
|
30
|
+
var LOG_TIME_PREFIX_REGEX = /^\[\d{2}:\d{2}:\d{2}\.\d{3}\]/;
|
|
31
|
+
var LOG_HEADER_REGEX = /^(\[\d{2}:\d{2}:\d{2}\.\d{3}\])(\[(?:INFO|WARN|ERROR|ERRO|FATAL|DEBUG|TRACE|MARK)\])/;
|
|
32
|
+
var LOG_LEVEL_COLOR_CODE = {
|
|
33
|
+
INFO: "\x1B[32m",
|
|
34
|
+
// 绿色
|
|
35
|
+
WARN: "\x1B[33m",
|
|
36
|
+
// 黄色
|
|
37
|
+
ERROR: "\x1B[31m",
|
|
38
|
+
// 红色
|
|
39
|
+
ERRO: "\x1B[31m",
|
|
40
|
+
// 兼容旧字段
|
|
41
|
+
FATAL: "\x1B[35m",
|
|
42
|
+
// 紫色
|
|
43
|
+
DEBUG: "\x1B[90m",
|
|
44
|
+
// 灰色
|
|
45
|
+
TRACE: "\x1B[90m",
|
|
46
|
+
// 灰色
|
|
47
|
+
MARK: "\x1B[90m"
|
|
48
|
+
// 灰色
|
|
49
|
+
};
|
|
50
|
+
var ansi256ToRgb = (idx) => {
|
|
51
|
+
if (idx < 0 || idx > 255 || Number.isNaN(idx)) return null;
|
|
52
|
+
const basicPalette = [
|
|
53
|
+
"#020617",
|
|
54
|
+
// 0: black
|
|
55
|
+
"#ef4444",
|
|
56
|
+
// 1: red
|
|
57
|
+
"#22c55e",
|
|
58
|
+
// 2: green
|
|
59
|
+
"#eab308",
|
|
60
|
+
// 3: yellow
|
|
61
|
+
"#3b82f6",
|
|
62
|
+
// 4: blue
|
|
63
|
+
"#ec4899",
|
|
64
|
+
// 5: magenta
|
|
65
|
+
"#06b6d4",
|
|
66
|
+
// 6: cyan
|
|
67
|
+
"#e5e7eb",
|
|
68
|
+
// 7: white (light gray)
|
|
69
|
+
"#6b7280",
|
|
70
|
+
// 8: bright black (gray)
|
|
71
|
+
"#f97373",
|
|
72
|
+
// 9: bright red
|
|
73
|
+
"#4ade80",
|
|
74
|
+
// 10: bright green
|
|
75
|
+
"#fde047",
|
|
76
|
+
// 11: bright yellow
|
|
77
|
+
"#60a5fa",
|
|
78
|
+
// 12: bright blue
|
|
79
|
+
"#a855f7",
|
|
80
|
+
// 13: bright magenta
|
|
81
|
+
"#38bdf8",
|
|
82
|
+
// 14: bright cyan
|
|
83
|
+
"#f9fafb"
|
|
84
|
+
// 15: bright white
|
|
85
|
+
];
|
|
86
|
+
if (idx <= 15) {
|
|
87
|
+
return basicPalette[idx];
|
|
88
|
+
}
|
|
89
|
+
if (idx >= 16 && idx <= 231) {
|
|
90
|
+
const n = idx - 16;
|
|
91
|
+
const r = Math.floor(n / 36);
|
|
92
|
+
const g = Math.floor(n % 36 / 6);
|
|
93
|
+
const b = n % 6;
|
|
94
|
+
const conv = (c) => c === 0 ? 0 : 55 + c * 40;
|
|
95
|
+
return `rgb(${conv(r)}, ${conv(g)}, ${conv(b)})`;
|
|
96
|
+
}
|
|
97
|
+
const gray = 8 + (idx - 232) * 10;
|
|
98
|
+
return `rgb(${gray}, ${gray}, ${gray})`;
|
|
99
|
+
};
|
|
9
100
|
var ansiToHtml = (text) => {
|
|
10
101
|
if (!text) return "";
|
|
11
|
-
const ESC = "\x1B";
|
|
12
|
-
const ansiRegex = new RegExp(`${ESC}\\[([0-9;]+)m`, "g");
|
|
13
|
-
const escapeHtml = (str) => str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
14
102
|
let result = "";
|
|
15
103
|
let lastIndex = 0;
|
|
16
104
|
let openSpan = "";
|
|
17
|
-
|
|
18
|
-
30: "#020617",
|
|
19
|
-
31: "#ef4444",
|
|
20
|
-
32: "#22c55e",
|
|
21
|
-
33: "#eab308",
|
|
22
|
-
34: "#3b82f6",
|
|
23
|
-
35: "#ec4899",
|
|
24
|
-
36: "#06b6d4",
|
|
25
|
-
37: "#e5e7eb",
|
|
26
|
-
90: "#6b7280",
|
|
27
|
-
91: "#f97373",
|
|
28
|
-
92: "#4ade80",
|
|
29
|
-
93: "#fde047",
|
|
30
|
-
94: "#60a5fa",
|
|
31
|
-
95: "#a855f7",
|
|
32
|
-
96: "#38bdf8",
|
|
33
|
-
97: "#f9fafb"
|
|
34
|
-
};
|
|
35
|
-
text.replace(ansiRegex, (match, codesStr, offset) => {
|
|
105
|
+
text.replace(ANSI_REGEX, (match, codesStr, offset) => {
|
|
36
106
|
const chunk = text.slice(lastIndex, offset);
|
|
37
107
|
if (chunk) {
|
|
38
108
|
result += escapeHtml(chunk);
|
|
39
109
|
}
|
|
40
110
|
lastIndex = offset + match.length;
|
|
41
|
-
const codes = codesStr.split(";").map((n) =>
|
|
111
|
+
const codes = codesStr.split(";").map((n) => parseInt(n, 10));
|
|
42
112
|
if (openSpan) {
|
|
43
113
|
result += "</span>";
|
|
44
114
|
openSpan = "";
|
|
45
115
|
}
|
|
46
|
-
if (codes.includes(0) || codes.includes(39)) {
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
49
116
|
if (codes[0] === 38 && codes[1] === 2 && codes.length >= 5) {
|
|
50
117
|
const r = codes[2];
|
|
51
118
|
const g = codes[3];
|
|
52
119
|
const b = codes[4];
|
|
53
|
-
|
|
54
|
-
|
|
120
|
+
if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
|
|
121
|
+
openSpan = `color: rgb(${r}, ${g}, ${b})`;
|
|
122
|
+
result += `<span style="${openSpan}">`;
|
|
123
|
+
}
|
|
55
124
|
return "";
|
|
56
125
|
}
|
|
57
|
-
|
|
126
|
+
if (codes[0] === 38 && codes[1] === 5 && codes.length >= 3) {
|
|
127
|
+
const idx = codes[2];
|
|
128
|
+
const rgb = ansi256ToRgb(idx);
|
|
129
|
+
if (rgb) {
|
|
130
|
+
openSpan = `color: ${rgb}`;
|
|
131
|
+
result += `<span style="${openSpan}">`;
|
|
132
|
+
}
|
|
133
|
+
return "";
|
|
134
|
+
}
|
|
135
|
+
if (codes.length === 1 && (codes[0] === 0 || codes[0] === 39)) {
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
const colorCode = codes.find((c) => ANSI_COLOR_MAP[c]);
|
|
58
139
|
if (colorCode !== void 0) {
|
|
59
|
-
const color =
|
|
140
|
+
const color = ANSI_COLOR_MAP[colorCode];
|
|
60
141
|
openSpan = `color: ${color}`;
|
|
61
142
|
result += `<span style="${openSpan}">`;
|
|
62
143
|
return "";
|
|
@@ -72,21 +153,19 @@ var ansiToHtml = (text) => {
|
|
|
72
153
|
}
|
|
73
154
|
return result;
|
|
74
155
|
};
|
|
75
|
-
var
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
156
|
+
var colorizeLog = (line) => {
|
|
157
|
+
return line.replace(LOG_HEADER_REGEX, (match, timePart, levelPart) => {
|
|
158
|
+
const levelKey = levelPart.replace("[", "").replace("]", "");
|
|
159
|
+
const colorCode = LOG_LEVEL_COLOR_CODE[levelKey] ?? "\x1B[37m";
|
|
160
|
+
return `${colorCode}${timePart}${levelPart}\x1B[0m`;
|
|
161
|
+
});
|
|
80
162
|
};
|
|
81
163
|
var groupLogLines = (lines) => {
|
|
82
164
|
const groups = [];
|
|
83
165
|
let current = [];
|
|
84
|
-
const isNewEntry = (line) =>
|
|
85
|
-
const trimmed = line.trim();
|
|
86
|
-
if (!trimmed) return false;
|
|
87
|
-
return /^\[\d{2}:\d{2}:\d{2}\.\d{3}\]\[[A-Z]+]/.test(trimmed);
|
|
88
|
-
};
|
|
166
|
+
const isNewEntry = (line) => LOG_TIME_PREFIX_REGEX.test(line);
|
|
89
167
|
for (const line of lines) {
|
|
168
|
+
if (!line.trim()) continue;
|
|
90
169
|
if (isNewEntry(line)) {
|
|
91
170
|
if (current.length) groups.push(current);
|
|
92
171
|
current = [line];
|
|
@@ -99,9 +178,15 @@ var groupLogLines = (lines) => {
|
|
|
99
178
|
}
|
|
100
179
|
}
|
|
101
180
|
if (current.length) groups.push(current);
|
|
102
|
-
return groups.map((
|
|
181
|
+
return groups.map((g) => g.join("\n"));
|
|
103
182
|
};
|
|
104
|
-
var
|
|
183
|
+
var readAndGroupLogFile = (logFile, limit) => {
|
|
184
|
+
const content = fs.readFileSync(logFile, "utf-8");
|
|
185
|
+
const lines = content.split("\n");
|
|
186
|
+
const groups = groupLogLines(lines);
|
|
187
|
+
return groups.slice(-limit);
|
|
188
|
+
};
|
|
189
|
+
var getTodayLogs = async (limit = 100) => {
|
|
105
190
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
106
191
|
const logDir = path.join(process.cwd(), "@karinjs", "logs");
|
|
107
192
|
const logFile = path.join(logDir, `logger.${today}.log`);
|
|
@@ -112,21 +197,15 @@ var getTodayLogs = async (limit = 50) => {
|
|
|
112
197
|
return ["\u6682\u65E0\u65E5\u5FD7\u8BB0\u5F55"];
|
|
113
198
|
}
|
|
114
199
|
const latestLog = path.join(logDir, files[0]);
|
|
115
|
-
|
|
116
|
-
const lines2 = content2.split("\n").filter((line) => line.trim());
|
|
117
|
-
const groups2 = groupLogLines(lines2);
|
|
118
|
-
return groups2.slice(-limit).reverse();
|
|
200
|
+
return readAndGroupLogFile(latestLog, limit);
|
|
119
201
|
}
|
|
120
|
-
|
|
121
|
-
const lines = content.split("\n").filter((line) => line.trim());
|
|
122
|
-
const groups = groupLogLines(lines);
|
|
123
|
-
return groups.slice(-limit).reverse();
|
|
202
|
+
return readAndGroupLogFile(logFile, limit);
|
|
124
203
|
} catch (error) {
|
|
125
204
|
logger.error("\u8BFB\u53D6\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", error);
|
|
126
205
|
return ["\u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25"];
|
|
127
206
|
}
|
|
128
207
|
};
|
|
129
|
-
var getErrorLogs = async (limit =
|
|
208
|
+
var getErrorLogs = async (limit = 100) => {
|
|
130
209
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
131
210
|
const errorDir = path.join(process.cwd(), "@karinjs", "logs", "error");
|
|
132
211
|
if (!fs.existsSync(errorDir)) {
|
|
@@ -147,47 +226,23 @@ var getErrorLogs = async (limit = 50) => {
|
|
|
147
226
|
}
|
|
148
227
|
}
|
|
149
228
|
if (!logFile) return [];
|
|
150
|
-
|
|
151
|
-
const lines = content.split("\n").filter((line) => line.trim());
|
|
152
|
-
const groups = groupLogLines(lines);
|
|
153
|
-
return groups.slice(-limit).reverse();
|
|
229
|
+
return readAndGroupLogFile(logFile, limit);
|
|
154
230
|
} catch (error) {
|
|
155
231
|
logger.error("\u8BFB\u53D6\u9519\u8BEF\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", error);
|
|
156
232
|
return [];
|
|
157
233
|
}
|
|
158
234
|
};
|
|
159
|
-
var parseLogLine = (line) => {
|
|
160
|
-
const [firstLine, ...rest] = line.split("\n");
|
|
161
|
-
let time = "";
|
|
162
|
-
let levelRaw = "INFO";
|
|
163
|
-
const timeMatch = firstLine.match(/^\[(\d{2}:\d{2}:\d{2}\.\d{3})]/);
|
|
164
|
-
const levelMatch = firstLine.match(/\[([A-Z]+)]/);
|
|
165
|
-
if (timeMatch) time = timeMatch[1];
|
|
166
|
-
if (levelMatch) levelRaw = levelMatch[1];
|
|
167
|
-
const levelKey = levelRaw.toLowerCase();
|
|
168
|
-
const level = levelKey === "erro" ? "error" : levelKey;
|
|
169
|
-
const messageHead = firstLine.replace(/^\[\d{2}:\d{2}:\d{2}\.\d{3}]\[[A-Z]+]\s*/, "");
|
|
170
|
-
const messageTail = rest.length ? "\n" + rest.join("\n") : "";
|
|
171
|
-
const fullMessage = trimEmptyLines(`${messageHead}${messageTail}`);
|
|
172
|
-
const plainMessage = fullMessage.replace(/\u001b\[[0-9;]+m/g, "");
|
|
173
|
-
return {
|
|
174
|
-
time,
|
|
175
|
-
level,
|
|
176
|
-
message: plainMessage,
|
|
177
|
-
messageHtml: ansiToHtml(fullMessage)
|
|
178
|
-
};
|
|
179
|
-
};
|
|
180
235
|
var logViewer = karin.command(/^#日志\s*(\d+)?$/, async (e) => {
|
|
181
236
|
const match = e.msg.match(/^#日志\s*(\d+)?$/);
|
|
182
237
|
let limit = match && match[1] ? Number(match[1]) : 50;
|
|
183
238
|
if (!Number.isFinite(limit) || limit <= 0) limit = 50;
|
|
184
239
|
if (limit > 1e3) limit = 1e3;
|
|
185
240
|
const logs = await getTodayLogs(limit);
|
|
186
|
-
const
|
|
241
|
+
const logsHtml = logs.map((line) => ansiToHtml(colorizeLog(line)));
|
|
187
242
|
try {
|
|
188
243
|
const img = await render("logger/index", {
|
|
189
|
-
logs:
|
|
190
|
-
total:
|
|
244
|
+
logs: logsHtml,
|
|
245
|
+
total: logs.length,
|
|
191
246
|
date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
|
|
192
247
|
});
|
|
193
248
|
await e.reply(img);
|
|
@@ -208,11 +263,11 @@ var errorLogViewer = karin.command(/^#错误日志\s*(\d+)?$/, async (e) => {
|
|
|
208
263
|
await e.reply("\u6682\u65E0\u9519\u8BEF\u65E5\u5FD7");
|
|
209
264
|
return true;
|
|
210
265
|
}
|
|
211
|
-
const
|
|
266
|
+
const logsHtml = logs.map((line) => ansiToHtml(colorizeLog(line)));
|
|
212
267
|
try {
|
|
213
268
|
const img = await render("logger/index", {
|
|
214
|
-
logs:
|
|
215
|
-
total:
|
|
269
|
+
logs: logsHtml,
|
|
270
|
+
total: logs.length,
|
|
216
271
|
date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
|
|
217
272
|
});
|
|
218
273
|
await e.reply(img);
|
package/dist/apps/login.js
CHANGED
package/dist/apps/restart.js
CHANGED
package/dist/apps/status.js
CHANGED
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
} from "../chunk-ODFXVVIE.js";
|
|
8
8
|
import {
|
|
9
9
|
cfg
|
|
10
|
-
} from "../chunk-
|
|
11
|
-
import "../chunk-
|
|
10
|
+
} from "../chunk-TUC5A7K4.js";
|
|
11
|
+
import "../chunk-4GSJ5FLI.js";
|
|
12
12
|
|
|
13
13
|
// src/apps/status.ts
|
|
14
14
|
import { karin } from "node-karin";
|
package/dist/apps/update.js
CHANGED
|
@@ -7,7 +7,7 @@ import { karinPathBase } from "node-karin";
|
|
|
7
7
|
// package.json
|
|
8
8
|
var package_default = {
|
|
9
9
|
name: "@karinjs/plugin-basic",
|
|
10
|
-
version: "1.3.
|
|
10
|
+
version: "1.3.5",
|
|
11
11
|
description: "Karin\u7684\u57FA\u7840\u63D2\u4EF6,\u63D0\u4F9B\u6700\u57FA\u7840\u7684\u529F\u80FD",
|
|
12
12
|
homepage: "https://github.com/KarinJS/karin-plugin-basic",
|
|
13
13
|
bugs: {
|
package/dist/index.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
} from "./chunk-ODFXVVIE.js";
|
|
10
10
|
import {
|
|
11
11
|
cfg
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-TUC5A7K4.js";
|
|
13
13
|
import {
|
|
14
14
|
plugin
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-4GSJ5FLI.js";
|
|
16
16
|
|
|
17
17
|
// src/index.ts
|
|
18
18
|
import { logger, restartDirect } from "node-karin";
|
package/dist/web.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,200 +1,58 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
--bg-color: #
|
|
3
|
-
--
|
|
4
|
-
--
|
|
5
|
-
--border-color: #
|
|
6
|
-
--header-bg: #333333;
|
|
7
|
-
--accent-color: #007acc;
|
|
8
|
-
|
|
9
|
-
--level-debug: #b5cea8;
|
|
10
|
-
--level-info: #569cd6;
|
|
11
|
-
--level-mark: #c586c0;
|
|
12
|
-
--level-warn: #ce9178;
|
|
13
|
-
--level-error: #f44747;
|
|
14
|
-
--level-fatal: #d16969;
|
|
2
|
+
--bg-color: #000000;
|
|
3
|
+
--text-color: #c1c1c1;
|
|
4
|
+
--font-family: 'Consolas', 'Lucida Console', 'Courier New', monospace;
|
|
5
|
+
--border-color: #333333;
|
|
15
6
|
}
|
|
16
7
|
|
|
17
|
-
|
|
8
|
+
body {
|
|
18
9
|
margin: 0;
|
|
19
10
|
padding: 0;
|
|
20
|
-
box-sizing: border-box;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
body {
|
|
24
|
-
font-family: 'Fira Code', 'Consolas', 'Monaco', 'Courier New', monospace;
|
|
25
11
|
background-color: var(--bg-color);
|
|
26
12
|
color: var(--text-color);
|
|
27
|
-
|
|
28
|
-
margin: 0;
|
|
13
|
+
font-family: var(--font-family);
|
|
29
14
|
font-size: 14px;
|
|
30
|
-
line-height: 1.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.container {
|
|
34
|
-
max-width: 100%;
|
|
35
|
-
margin: 0;
|
|
36
|
-
background: var(--bg-color);
|
|
15
|
+
line-height: 1.15;
|
|
16
|
+
min-height: 100vh;
|
|
37
17
|
display: flex;
|
|
38
18
|
flex-direction: column;
|
|
39
19
|
}
|
|
40
20
|
|
|
41
21
|
.header {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
22
|
+
padding: 8px 15px;
|
|
23
|
+
border-bottom: 1px dashed var(--border-color);
|
|
24
|
+
background-color: var(--bg-color);
|
|
45
25
|
display: flex;
|
|
46
26
|
justify-content: space-between;
|
|
47
27
|
align-items: center;
|
|
48
|
-
position: sticky;
|
|
49
|
-
top: 0;
|
|
50
|
-
z-index: 100;
|
|
51
|
-
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.header h1 {
|
|
55
|
-
font-size: 16px;
|
|
56
|
-
font-weight: 600;
|
|
57
|
-
color: #fff;
|
|
58
|
-
margin: 0;
|
|
59
|
-
display: flex;
|
|
60
|
-
align-items: center;
|
|
61
|
-
gap: 10px;
|
|
62
28
|
}
|
|
63
29
|
|
|
64
|
-
.
|
|
65
|
-
content: '>';
|
|
66
|
-
color: var(--accent-color);
|
|
30
|
+
.title {
|
|
67
31
|
font-weight: bold;
|
|
32
|
+
color: #ffffff;
|
|
68
33
|
}
|
|
69
34
|
|
|
70
|
-
.
|
|
71
|
-
display: flex;
|
|
72
|
-
gap: 15px;
|
|
73
|
-
font-size: 12px;
|
|
74
|
-
color: #cccccc;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
.info span {
|
|
78
|
-
background: rgba(255, 255, 255, 0.1);
|
|
79
|
-
padding: 2px 8px;
|
|
80
|
-
border-radius: 3px;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
.header-bg-text {
|
|
84
|
-
display: none;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.logs-container {
|
|
88
|
-
padding: 10px 0;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.log-item {
|
|
92
|
-
padding: 2px 10px;
|
|
93
|
-
border-left: 3px solid transparent;
|
|
94
|
-
transition: background-color 0.1s;
|
|
95
|
-
font-size: 13px;
|
|
96
|
-
line-height: 1.6;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
.log-item:hover {
|
|
100
|
-
background-color: #2a2d2e;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.log-content {
|
|
104
|
-
display: block;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
.log-prefix {
|
|
108
|
-
color: #858585;
|
|
35
|
+
.meta {
|
|
109
36
|
font-size: 12px;
|
|
110
|
-
|
|
111
|
-
opacity: 0.9;
|
|
112
|
-
margin-right: 8px;
|
|
37
|
+
color: #888888;
|
|
113
38
|
}
|
|
114
39
|
|
|
115
|
-
.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
.log-level {
|
|
120
|
-
font-weight: bold;
|
|
121
|
-
text-transform: uppercase;
|
|
122
|
-
font-size: 12px;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.level-debug {
|
|
126
|
-
color: var(--level-debug);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.level-info {
|
|
130
|
-
color: var(--level-info);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
.level-mark {
|
|
134
|
-
color: var(--level-mark);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.level-warn {
|
|
138
|
-
color: var(--level-warn);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
.level-error {
|
|
142
|
-
color: var(--level-error);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.level-fatal {
|
|
146
|
-
color: var(--level-fatal);
|
|
147
|
-
background: #5a1d1d;
|
|
148
|
-
padding: 0 4px;
|
|
149
|
-
border-radius: 2px;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.log-message {
|
|
40
|
+
.logs {
|
|
41
|
+
flex: 1;
|
|
42
|
+
padding: 15px;
|
|
153
43
|
white-space: pre-wrap;
|
|
154
|
-
word-break: break-
|
|
155
|
-
color: #cccccc;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/* Log item border indicators */
|
|
159
|
-
.log-item.log-error {
|
|
160
|
-
border-left-color: var(--level-error);
|
|
161
|
-
background: rgba(244, 71, 71, 0.05);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.log-item.log-warn {
|
|
165
|
-
border-left-color: var(--level-warn);
|
|
166
|
-
background: rgba(206, 145, 120, 0.05);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.log-item.log-fatal {
|
|
170
|
-
border-left-color: var(--level-fatal);
|
|
171
|
-
background: rgba(209, 105, 105, 0.1);
|
|
44
|
+
word-break: break-all;
|
|
172
45
|
}
|
|
173
46
|
|
|
174
47
|
.footer {
|
|
175
|
-
padding:
|
|
48
|
+
padding: 8px 15px;
|
|
49
|
+
border-top: 1px dashed var(--border-color);
|
|
50
|
+
font-size: 12px;
|
|
51
|
+
color: #666666;
|
|
176
52
|
text-align: center;
|
|
177
|
-
color:
|
|
178
|
-
font-size: 11px;
|
|
179
|
-
border-top: 1px solid var(--border-color);
|
|
180
|
-
background: var(--container-bg);
|
|
53
|
+
background-color: var(--bg-color);
|
|
181
54
|
}
|
|
182
55
|
|
|
183
|
-
/* Scrollbar */
|
|
184
56
|
::-webkit-scrollbar {
|
|
185
|
-
|
|
186
|
-
height: 10px;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
::-webkit-scrollbar-track {
|
|
190
|
-
background: var(--bg-color);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
::-webkit-scrollbar-thumb {
|
|
194
|
-
background: #424242;
|
|
195
|
-
border-radius: 5px;
|
|
57
|
+
display: none;
|
|
196
58
|
}
|
|
197
|
-
|
|
198
|
-
::-webkit-scrollbar-thumb:hover {
|
|
199
|
-
background: #4f4f4f;
|
|
200
|
-
}
|
|
@@ -3,36 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
<head>
|
|
5
5
|
<meta charset="UTF-8">
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title>Karin 日志</title>
|
|
8
6
|
<link rel="stylesheet" href="{{pluResPath}}logger/index.css">
|
|
9
7
|
</head>
|
|
10
8
|
|
|
11
9
|
<body>
|
|
12
|
-
<div class="
|
|
13
|
-
<div class="
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
<span class="date">{{date}}</span>
|
|
17
|
-
<span class="count">共 {{total}} 条日志</span>
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
10
|
+
<div class="header">
|
|
11
|
+
<div class="title">Karin 日志</div>
|
|
12
|
+
<div class="meta">{{date}} | 共 {{total}} 条日志</div>
|
|
13
|
+
</div>
|
|
20
14
|
|
|
21
|
-
|
|
22
|
-
{{each logs}}
|
|
23
|
-
<div class="log-item log-{{$value.level}}">
|
|
24
|
-
<div class="log-content">
|
|
25
|
-
<span class="log-prefix">[<span class="log-time">{{$value.time}}</span>][<span
|
|
26
|
-
class="log-level level-{{$value.level}}">{{$value.level}}</span>]</span>
|
|
27
|
-
<span class="log-message">{{@ $value.messageHtml}}</span>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
{{/each}}
|
|
31
|
-
</div>
|
|
15
|
+
<div class="logs">{{@ logs.join('\n') }}</div>
|
|
32
16
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
</div>
|
|
17
|
+
<div class="footer">
|
|
18
|
+
{{sys.copyright}}
|
|
36
19
|
</div>
|
|
37
20
|
</body>
|
|
38
21
|
|