@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 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
+ ```
@@ -1,62 +1,84 @@
1
1
  import {
2
2
  render
3
- } from "../chunk-W3Q26Z2E.js";
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
14
52
  let result = "";
15
53
  let lastIndex = 0;
16
54
  let openSpan = "";
17
- const colorMap = {
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) => Number(n) || 0);
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
- openSpan = `color: rgb(${r}, ${g}, ${b})`;
54
- result += `<span style="${openSpan}">`;
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
- const colorCode = codes.find((c) => colorMap[c]);
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 = colorMap[colorCode];
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 trimEmptyLines = (text) => {
76
- const lines = text.split("\n");
77
- while (lines.length && !lines[0].trim()) lines.shift();
78
- while (lines.length && !lines[lines.length - 1].trim()) lines.pop();
79
- return lines.join("\n");
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((item) => item.join("\n"));
122
+ return groups.map((g) => g.join("\n"));
103
123
  };
104
- var getTodayLogs = async (limit = 50) => {
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
- const content2 = fs.readFileSync(latestLog, "utf-8");
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
- const content = fs.readFileSync(logFile, "utf-8");
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 = 50) => {
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
- const content = fs.readFileSync(logFile, "utf-8");
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 > 200) limit = 200;
180
+ if (limit > 1e3) limit = 1e3;
185
181
  const logs = await getTodayLogs(limit);
186
- const parsedLogs = logs.map(parseLogLine);
182
+ const logsHtml = logs.map((line) => ansiToHtml(colorizeLog(line)));
187
183
  try {
188
184
  const img = await render("logger/index", {
189
- logs: parsedLogs,
190
- total: parsedLogs.length,
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 parsedLogs = logs.map(parseLogLine);
207
+ const logsHtml = logs.map((line) => ansiToHtml(colorizeLog(line)));
212
208
  try {
213
209
  const img = await render("logger/index", {
214
- logs: parsedLogs,
215
- total: parsedLogs.length,
210
+ logs: logsHtml,
211
+ total: logs.length,
216
212
  date: (/* @__PURE__ */ new Date()).toLocaleString("zh-CN")
217
213
  });
218
214
  await e.reply(img);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  cfg
3
- } from "../chunk-7LNXK3JJ.js";
4
- import "../chunk-W3Q26Z2E.js";
3
+ } from "../chunk-3EM2TC2Y.js";
4
+ import "../chunk-KMYEMP7Q.js";
5
5
 
6
6
  // src/apps/login.ts
7
7
  import karin, { common, contactFriend, logger, segment } from "node-karin";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  cfg
3
- } from "../chunk-7LNXK3JJ.js";
4
- import "../chunk-W3Q26Z2E.js";
3
+ } from "../chunk-3EM2TC2Y.js";
4
+ import "../chunk-KMYEMP7Q.js";
5
5
 
6
6
  // src/apps/restart.ts
7
7
  import { common, karin, logger, restart } from "node-karin";
@@ -7,8 +7,8 @@ import {
7
7
  } from "../chunk-ODFXVVIE.js";
8
8
  import {
9
9
  cfg
10
- } from "../chunk-7LNXK3JJ.js";
11
- import "../chunk-W3Q26Z2E.js";
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";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  cfg,
3
3
  sendToFirstAdmin
4
- } from "../chunk-7LNXK3JJ.js";
4
+ } from "../chunk-3EM2TC2Y.js";
5
5
  import {
6
6
  render
7
- } from "../chunk-W3Q26Z2E.js";
7
+ } from "../chunk-KMYEMP7Q.js";
8
8
 
9
9
  // src/apps/update.ts
10
10
  import fs from "fs";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  plugin
3
- } from "./chunk-W3Q26Z2E.js";
3
+ } from "./chunk-KMYEMP7Q.js";
4
4
 
5
5
  // src/utils/utils.ts
6
6
  import karin, { config, logger } from "node-karin";
@@ -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.2",
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-7LNXK3JJ.js";
12
+ } from "./chunk-3EM2TC2Y.js";
13
13
  import {
14
14
  plugin
15
- } from "./chunk-W3Q26Z2E.js";
15
+ } from "./chunk-KMYEMP7Q.js";
16
16
 
17
17
  // src/index.ts
18
18
  import { logger, restartDirect } from "node-karin";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  cfg
3
- } from "./chunk-7LNXK3JJ.js";
3
+ } from "./chunk-3EM2TC2Y.js";
4
4
  import {
5
5
  plugin
6
- } from "./chunk-W3Q26Z2E.js";
6
+ } from "./chunk-KMYEMP7Q.js";
7
7
 
8
8
  // src/web.config.ts
9
9
  import { components, defineConfig } from "node-karin";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karinjs/plugin-basic",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Karin的基础插件,提供最基础的功能",
5
5
  "homepage": "https://github.com/KarinJS/karin-plugin-basic",
6
6
  "bugs": {
@@ -1,215 +1,58 @@
1
- * {
2
- margin: 0;
3
- padding: 0;
4
- box-sizing: border-box;
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
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif, 'Microsoft YaHei';
9
- background: linear-gradient(135deg, #c7d2fe 0%, #fbcfe8 50%, #a5f3fc 100%);
10
- padding: 20px;
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
- align-items: flex-start;
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
- position: relative;
26
- padding: 30px;
27
- color: #0f172a;
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
- .level-debug {
121
- background: #9ca3af;
122
- color: white;
30
+ .title {
31
+ font-weight: bold;
32
+ color: #ffffff;
123
33
  }
124
34
 
125
- .level-info {
126
- background: #38bdf8;
127
- color: white;
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
- .log-message {
40
+ .logs {
151
41
  flex: 1;
152
- word-break: break-all;
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
- background: #f8f9fa;
191
- padding: 15px 30px;
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
- border-top: 1px solid #dee2e6;
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
- .logs-container::-webkit-scrollbar-thumb {
209
- background: #888;
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="container">
13
- <div class="header">
14
- <h1>📋 Karin 日志</h1>
15
- <div class="info">
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
- <div class="logs-container">
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
- <div class="footer">
32
- <p>{{sys.copyright}}</p>
33
- </div>
17
+ <div class="footer">
18
+ {{sys.copyright}}
34
19
  </div>
35
20
  </body>
36
21