@karinjs/plugin-basic 1.3.2 → 1.3.4
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 +80 -84
- 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-7LNXK3JJ.js → chunk-3EM2TC2Y.js} +1 -1
- package/dist/{chunk-W3Q26Z2E.js → chunk-KMYEMP7Q.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 +34 -191
- package/resources/logger/index.html +7 -22
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,84 @@
|
|
|
1
1
|
import {
|
|
2
2
|
render
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-KMYEMP7Q.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
|
+
};
|
|
9
50
|
var ansiToHtml = (text) => {
|
|
10
51
|
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
52
|
let result = "";
|
|
15
53
|
let lastIndex = 0;
|
|
16
54
|
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) => {
|
|
55
|
+
text.replace(ANSI_REGEX, (match, codesStr, offset) => {
|
|
36
56
|
const chunk = text.slice(lastIndex, offset);
|
|
37
57
|
if (chunk) {
|
|
38
58
|
result += escapeHtml(chunk);
|
|
39
59
|
}
|
|
40
60
|
lastIndex = offset + match.length;
|
|
41
|
-
const codes = codesStr.split(";").map((n) =>
|
|
61
|
+
const codes = codesStr.split(";").map((n) => parseInt(n, 10));
|
|
42
62
|
if (openSpan) {
|
|
43
63
|
result += "</span>";
|
|
44
64
|
openSpan = "";
|
|
45
65
|
}
|
|
46
|
-
if (codes.includes(0) || codes.includes(39)) {
|
|
47
|
-
return "";
|
|
48
|
-
}
|
|
49
66
|
if (codes[0] === 38 && codes[1] === 2 && codes.length >= 5) {
|
|
50
67
|
const r = codes[2];
|
|
51
68
|
const g = codes[3];
|
|
52
69
|
const b = codes[4];
|
|
53
|
-
|
|
54
|
-
|
|
70
|
+
if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
|
|
71
|
+
openSpan = `color: rgb(${r}, ${g}, ${b})`;
|
|
72
|
+
result += `<span style="${openSpan}">`;
|
|
73
|
+
}
|
|
55
74
|
return "";
|
|
56
75
|
}
|
|
57
|
-
|
|
76
|
+
if (codes.length === 1 && (codes[0] === 0 || codes[0] === 39)) {
|
|
77
|
+
return "";
|
|
78
|
+
}
|
|
79
|
+
const colorCode = codes.find((c) => ANSI_COLOR_MAP[c]);
|
|
58
80
|
if (colorCode !== void 0) {
|
|
59
|
-
const color =
|
|
81
|
+
const color = ANSI_COLOR_MAP[colorCode];
|
|
60
82
|
openSpan = `color: ${color}`;
|
|
61
83
|
result += `<span style="${openSpan}">`;
|
|
62
84
|
return "";
|
|
@@ -72,21 +94,19 @@ var ansiToHtml = (text) => {
|
|
|
72
94
|
}
|
|
73
95
|
return result;
|
|
74
96
|
};
|
|
75
|
-
var
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
97
|
+
var colorizeLog = (line) => {
|
|
98
|
+
return line.replace(LOG_HEADER_REGEX, (match, timePart, levelPart) => {
|
|
99
|
+
const levelKey = levelPart.replace("[", "").replace("]", "");
|
|
100
|
+
const colorCode = LOG_LEVEL_COLOR_CODE[levelKey] ?? "\x1B[37m";
|
|
101
|
+
return `${colorCode}${timePart}${levelPart}\x1B[0m`;
|
|
102
|
+
});
|
|
80
103
|
};
|
|
81
104
|
var groupLogLines = (lines) => {
|
|
82
105
|
const groups = [];
|
|
83
106
|
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
|
-
};
|
|
107
|
+
const isNewEntry = (line) => LOG_TIME_PREFIX_REGEX.test(line);
|
|
89
108
|
for (const line of lines) {
|
|
109
|
+
if (!line.trim()) continue;
|
|
90
110
|
if (isNewEntry(line)) {
|
|
91
111
|
if (current.length) groups.push(current);
|
|
92
112
|
current = [line];
|
|
@@ -99,9 +119,15 @@ var groupLogLines = (lines) => {
|
|
|
99
119
|
}
|
|
100
120
|
}
|
|
101
121
|
if (current.length) groups.push(current);
|
|
102
|
-
return groups.map((
|
|
122
|
+
return groups.map((g) => g.join("\n"));
|
|
103
123
|
};
|
|
104
|
-
var
|
|
124
|
+
var readAndGroupLogFile = (logFile, limit) => {
|
|
125
|
+
const content = fs.readFileSync(logFile, "utf-8");
|
|
126
|
+
const lines = content.split("\n");
|
|
127
|
+
const groups = groupLogLines(lines);
|
|
128
|
+
return groups.slice(-limit);
|
|
129
|
+
};
|
|
130
|
+
var getTodayLogs = async (limit = 100) => {
|
|
105
131
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
106
132
|
const logDir = path.join(process.cwd(), "@karinjs", "logs");
|
|
107
133
|
const logFile = path.join(logDir, `logger.${today}.log`);
|
|
@@ -112,21 +138,15 @@ var getTodayLogs = async (limit = 50) => {
|
|
|
112
138
|
return ["\u6682\u65E0\u65E5\u5FD7\u8BB0\u5F55"];
|
|
113
139
|
}
|
|
114
140
|
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();
|
|
141
|
+
return readAndGroupLogFile(latestLog, limit);
|
|
119
142
|
}
|
|
120
|
-
|
|
121
|
-
const lines = content.split("\n").filter((line) => line.trim());
|
|
122
|
-
const groups = groupLogLines(lines);
|
|
123
|
-
return groups.slice(-limit).reverse();
|
|
143
|
+
return readAndGroupLogFile(logFile, limit);
|
|
124
144
|
} catch (error) {
|
|
125
145
|
logger.error("\u8BFB\u53D6\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", error);
|
|
126
146
|
return ["\u8BFB\u53D6\u65E5\u5FD7\u5931\u8D25"];
|
|
127
147
|
}
|
|
128
148
|
};
|
|
129
|
-
var getErrorLogs = async (limit =
|
|
149
|
+
var getErrorLogs = async (limit = 100) => {
|
|
130
150
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
131
151
|
const errorDir = path.join(process.cwd(), "@karinjs", "logs", "error");
|
|
132
152
|
if (!fs.existsSync(errorDir)) {
|
|
@@ -147,47 +167,23 @@ var getErrorLogs = async (limit = 50) => {
|
|
|
147
167
|
}
|
|
148
168
|
}
|
|
149
169
|
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();
|
|
170
|
+
return readAndGroupLogFile(logFile, limit);
|
|
154
171
|
} catch (error) {
|
|
155
172
|
logger.error("\u8BFB\u53D6\u9519\u8BEF\u65E5\u5FD7\u6587\u4EF6\u5931\u8D25:", error);
|
|
156
173
|
return [];
|
|
157
174
|
}
|
|
158
175
|
};
|
|
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
176
|
var logViewer = karin.command(/^#日志\s*(\d+)?$/, async (e) => {
|
|
181
177
|
const match = e.msg.match(/^#日志\s*(\d+)?$/);
|
|
182
178
|
let limit = match && match[1] ? Number(match[1]) : 50;
|
|
183
179
|
if (!Number.isFinite(limit) || limit <= 0) limit = 50;
|
|
184
|
-
if (limit >
|
|
180
|
+
if (limit > 1e3) limit = 1e3;
|
|
185
181
|
const logs = await getTodayLogs(limit);
|
|
186
|
-
const
|
|
182
|
+
const logsHtml = logs.map((line) => ansiToHtml(colorizeLog(line)));
|
|
187
183
|
try {
|
|
188
184
|
const img = await render("logger/index", {
|
|
189
|
-
logs:
|
|
190
|
-
total:
|
|
185
|
+
logs: logsHtml,
|
|
186
|
+
total: logs.length,
|
|
191
187
|
date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
|
|
192
188
|
});
|
|
193
189
|
await e.reply(img);
|
|
@@ -208,11 +204,11 @@ var errorLogViewer = karin.command(/^#错误日志\s*(\d+)?$/, async (e) => {
|
|
|
208
204
|
await e.reply("\u6682\u65E0\u9519\u8BEF\u65E5\u5FD7");
|
|
209
205
|
return true;
|
|
210
206
|
}
|
|
211
|
-
const
|
|
207
|
+
const logsHtml = logs.map((line) => ansiToHtml(colorizeLog(line)));
|
|
212
208
|
try {
|
|
213
209
|
const img = await render("logger/index", {
|
|
214
|
-
logs:
|
|
215
|
-
total:
|
|
210
|
+
logs: logsHtml,
|
|
211
|
+
total: logs.length,
|
|
216
212
|
date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
|
|
217
213
|
});
|
|
218
214
|
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-3EM2TC2Y.js";
|
|
11
|
+
import "../chunk-KMYEMP7Q.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.4",
|
|
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-3EM2TC2Y.js";
|
|
13
13
|
import {
|
|
14
14
|
plugin
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KMYEMP7Q.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,215 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
:root {
|
|
2
|
+
--bg-color: #000000;
|
|
3
|
+
--text-color: #c1c1c1;
|
|
4
|
+
--font-family: 'Consolas', 'Lucida Console', 'Courier New', monospace;
|
|
5
|
+
--border-color: #333333;
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
body {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
background-color: var(--bg-color);
|
|
12
|
+
color: var(--text-color);
|
|
13
|
+
font-family: var(--font-family);
|
|
14
|
+
font-size: 14px;
|
|
15
|
+
line-height: 1.15;
|
|
16
|
+
min-height: 100vh;
|
|
11
17
|
display: flex;
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.container {
|
|
16
|
-
max-width: 1400px;
|
|
17
|
-
margin: 0 auto;
|
|
18
|
-
background: rgba(255, 255, 255, 0.96);
|
|
19
|
-
border-radius: 18px;
|
|
20
|
-
box-shadow: 0 18px 45px rgba(148, 163, 184, 0.45);
|
|
21
|
-
overflow: hidden;
|
|
18
|
+
flex-direction: column;
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
.header {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
color:
|
|
28
|
-
overflow: hidden;
|
|
29
|
-
background: url('../img/logger.png') center/cover no-repeat, #e0f2ff;
|
|
30
|
-
border-bottom: 1px solid rgba(148, 163, 184, 0.35);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.header h1,
|
|
34
|
-
.header .info {
|
|
35
|
-
position: relative;
|
|
36
|
-
z-index: 2;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.header-bg-text {
|
|
40
|
-
position: absolute;
|
|
41
|
-
inset: 0;
|
|
22
|
+
padding: 8px 15px;
|
|
23
|
+
border-bottom: 1px dashed var(--border-color);
|
|
24
|
+
background-color: var(--bg-color);
|
|
42
25
|
display: flex;
|
|
26
|
+
justify-content: space-between;
|
|
43
27
|
align-items: center;
|
|
44
|
-
justify-content: center;
|
|
45
|
-
font-size: 92px;
|
|
46
|
-
font-weight: 800;
|
|
47
|
-
letter-spacing: 0.12em;
|
|
48
|
-
text-transform: uppercase;
|
|
49
|
-
color: rgba(30, 64, 175, 0.2);
|
|
50
|
-
filter: blur(2px);
|
|
51
|
-
pointer-events: none;
|
|
52
|
-
z-index: 1;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.header h1 {
|
|
56
|
-
font-size: 32px;
|
|
57
|
-
margin-bottom: 15px;
|
|
58
|
-
font-weight: 600;
|
|
59
|
-
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.info {
|
|
63
|
-
display: flex;
|
|
64
|
-
gap: 20px;
|
|
65
|
-
font-size: 14px;
|
|
66
|
-
opacity: 0.95;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
.info span {
|
|
70
|
-
background: rgba(255, 255, 255, 0.2);
|
|
71
|
-
padding: 6px 12px;
|
|
72
|
-
border-radius: 6px;
|
|
73
|
-
backdrop-filter: blur(10px);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.logs-container {
|
|
77
|
-
padding: 20px;
|
|
78
|
-
/* 让截图包含全部日志,不使用内部滚动条 */
|
|
79
|
-
overflow: visible;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.log-item {
|
|
83
|
-
display: flex;
|
|
84
|
-
align-items: flex-start;
|
|
85
|
-
padding: 12px 16px;
|
|
86
|
-
margin-bottom: 8px;
|
|
87
|
-
border-radius: 8px;
|
|
88
|
-
background: #f9fafb;
|
|
89
|
-
border-left: 4px solid #e5e7eb;
|
|
90
|
-
transition: all 0.2s ease;
|
|
91
|
-
font-size: 13px;
|
|
92
|
-
line-height: 1.6;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.log-item:hover {
|
|
96
|
-
transform: translateX(4px);
|
|
97
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.log-time {
|
|
101
|
-
color: #6c757d;
|
|
102
|
-
font-family: 'Consolas', 'Monaco', monospace;
|
|
103
|
-
min-width: 100px;
|
|
104
|
-
margin-right: 12px;
|
|
105
|
-
font-weight: 500;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
.log-level {
|
|
109
|
-
display: inline-block;
|
|
110
|
-
padding: 2px 10px;
|
|
111
|
-
border-radius: 4px;
|
|
112
|
-
font-weight: 600;
|
|
113
|
-
text-transform: uppercase;
|
|
114
|
-
font-size: 11px;
|
|
115
|
-
min-width: 60px;
|
|
116
|
-
text-align: center;
|
|
117
|
-
margin-right: 12px;
|
|
118
28
|
}
|
|
119
29
|
|
|
120
|
-
.
|
|
121
|
-
|
|
122
|
-
color:
|
|
30
|
+
.title {
|
|
31
|
+
font-weight: bold;
|
|
32
|
+
color: #ffffff;
|
|
123
33
|
}
|
|
124
34
|
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
color:
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.level-mark {
|
|
131
|
-
background: #a855f7;
|
|
132
|
-
color: white;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
.level-warn {
|
|
136
|
-
background: #facc15;
|
|
137
|
-
color: #422006;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
.level-error {
|
|
141
|
-
background: #ef4444;
|
|
142
|
-
color: white;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.level-fatal {
|
|
146
|
-
background: #000;
|
|
147
|
-
color: white;
|
|
35
|
+
.meta {
|
|
36
|
+
font-size: 12px;
|
|
37
|
+
color: #888888;
|
|
148
38
|
}
|
|
149
39
|
|
|
150
|
-
.
|
|
40
|
+
.logs {
|
|
151
41
|
flex: 1;
|
|
152
|
-
|
|
153
|
-
color: #212529;
|
|
154
|
-
font-family: 'Consolas', 'Monaco', monospace;
|
|
42
|
+
padding: 15px;
|
|
155
43
|
white-space: pre-wrap;
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
/* 不同日志级别的边框颜色 */
|
|
159
|
-
.log-debug {
|
|
160
|
-
border-left-color: #d1d5db;
|
|
161
|
-
background: #f9fafb;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
.log-info {
|
|
165
|
-
border-left-color: #93c5fd;
|
|
166
|
-
background: #e0f2fe;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
.log-mark {
|
|
170
|
-
border-left-color: #c4b5fd;
|
|
171
|
-
background: #f3e8ff;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.log-warn {
|
|
175
|
-
border-left-color: #fcd34d;
|
|
176
|
-
background: #fef3c7;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
.log-error {
|
|
180
|
-
border-left-color: #fca5a5;
|
|
181
|
-
background: #fee2e2;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
.log-fatal {
|
|
185
|
-
border-left-color: #9ca3af;
|
|
186
|
-
background: #f5f5f5;
|
|
44
|
+
word-break: break-all;
|
|
187
45
|
}
|
|
188
46
|
|
|
189
47
|
.footer {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
text-align: center;
|
|
193
|
-
color: #6c757d;
|
|
48
|
+
padding: 8px 15px;
|
|
49
|
+
border-top: 1px dashed var(--border-color);
|
|
194
50
|
font-size: 12px;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
/* 滚动条样式 */
|
|
199
|
-
.logs-container::-webkit-scrollbar {
|
|
200
|
-
width: 8px;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
.logs-container::-webkit-scrollbar-track {
|
|
204
|
-
background: #f1f1f1;
|
|
205
|
-
border-radius: 4px;
|
|
51
|
+
color: #666666;
|
|
52
|
+
text-align: center;
|
|
53
|
+
background-color: var(--bg-color);
|
|
206
54
|
}
|
|
207
55
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
border-radius: 4px;
|
|
56
|
+
::-webkit-scrollbar {
|
|
57
|
+
display: none;
|
|
211
58
|
}
|
|
212
|
-
|
|
213
|
-
.logs-container::-webkit-scrollbar-thumb:hover {
|
|
214
|
-
background: #555;
|
|
215
|
-
}
|
|
@@ -3,34 +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
|
-
<span class="log-time">{{$value.time}}</span>
|
|
25
|
-
<span class="log-level level-{{$value.level}}">{{$value.level}}</span>
|
|
26
|
-
<span class="log-message">{{@ $value.messageHtml}}</span>
|
|
27
|
-
</div>
|
|
28
|
-
{{/each}}
|
|
29
|
-
</div>
|
|
15
|
+
<div class="logs">{{@ logs.join('\n') }}</div>
|
|
30
16
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
</div>
|
|
17
|
+
<div class="footer">
|
|
18
|
+
{{sys.copyright}}
|
|
34
19
|
</div>
|
|
35
20
|
</body>
|
|
36
21
|
|