系统设置增加系统运行日志

This commit is contained in:
whyour 2023-08-21 00:10:43 +08:00
parent b002cbef3a
commit 4f7649f157
33 changed files with 864 additions and 112 deletions

View File

@ -4,6 +4,7 @@ const CompressionPlugin = require('compression-webpack-plugin');
const baseUrl = process.env.QlBaseUrl || '/';
export default defineConfig({
hash: true,
jsMinifier: 'terser',
antd: {},
locale: {
antd: true,

View File

@ -249,4 +249,17 @@ export default (app: Router) => {
}
},
);
route.get(
'/log',
async (req: Request, res: Response, next: NextFunction) => {
try {
const systemService = Container.get(SystemService);
await systemService.getSystemLog(res);
} catch (e) {
return next(e);
}
},
);
};

View File

@ -18,10 +18,12 @@ async function startServer() {
const server = app
.listen(config.port, () => {
Logger.debug(`✌️ 后端服务启动成功!`);
console.debug(`✌️ 后端服务启动成功!`);
process.send?.('ready');
})
.on('error', (err) => {
Logger.error(err);
console.error(err);
process.exit(1);
});

View File

@ -30,6 +30,7 @@ const logPath = path.join(dataPath, 'log/');
const dbPath = path.join(dataPath, 'db/');
const uploadPath = path.join(dataPath, 'upload/');
const sshdPath = path.join(dataPath, 'ssh.d/');
const systemLogPath = path.join(dataPath, 'syslog/');
const envFile = path.join(configPath, 'env.sh');
const confFile = path.join(configPath, 'config.sh');
@ -110,4 +111,5 @@ export default {
lastVersionFile,
sqliteFile,
sshdPath,
systemLogPath,
};

View File

@ -307,22 +307,28 @@ interface IFile {
type: 'directory' | 'file';
parent: string;
mtime: number;
size?: number;
children?: IFile[];
}
export function dirSort(a: IFile, b: IFile) {
if (a.type !== b.type) return FileType[a.type] < FileType[b.type] ? -1 : 1;
else if (a.mtime !== b.mtime) return a.mtime > b.mtime ? -1 : 1;
export function dirSort(a: IFile, b: IFile): number {
if (a.type !== b.type) {
return FileType[a.type] < FileType[b.type] ? -1 : 1
}else if (a.mtime !== b.mtime) {
return a.mtime > b.mtime ? -1 : 1
} else {
return 0;
}
}
export function readDirs(
dir: string,
baseDir: string = '',
blacklist: string[] = [],
) {
): IFile[] {
const relativePath = path.relative(baseDir, dir);
const files = fs.readdirSync(dir);
const result: any = files
const result: IFile[] = files
.filter((x) => !blacklist.includes(x))
.map((file: string) => {
const subPath = path.join(dir, file);
@ -344,6 +350,7 @@ export function readDirs(
isLeaf: true,
key,
parent: relativePath,
size: stats.size,
mtime: stats.mtime.getTime(),
};
});

View File

@ -10,20 +10,25 @@ export default async ({ expressApp }: { expressApp: Application }) => {
try {
depInjectorLoader();
Logger.info('✌️ Dependency Injector loaded');
console.log('✌️ Dependency Injector loaded');
expressLoader({ app: expressApp });
Logger.info('✌️ Express loaded');
console.log('✌️ Express loaded');
await initData();
Logger.info('✌️ init data loaded');
console.log('✌️ init data loaded');
await linkDeps();
Logger.info('✌️ link deps loaded');
console.log('✌️ link deps loaded');
initTask();
Logger.info('✌️ init task loaded');
console.log('✌️ init task loaded');
} catch (error) {
Logger.info('✌️ depInjectorLoader expressLoader initData linkDeps failed');
Logger.error(error);
Logger.error(`✌️ depInjectorLoader expressLoader initData linkDeps failed, ${error}`);
console.error(`✌️ depInjectorLoader expressLoader initData linkDeps failed ${error}`);
}
};

View File

@ -122,9 +122,10 @@ export default async () => {
});
}
console.log('✌️ DB loaded');
Logger.info('✌️ DB loaded');
} catch (error) {
Logger.info('✌️ DB load failed');
console.error('✌️ DB load failed');
Logger.error(error);
}
};

View File

@ -15,6 +15,7 @@ import { EnvModel } from '../data/env';
import { errors } from 'celebrate';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { serveEnv } from '../config/serverEnv';
import Logger from './logger';
export default ({ app }: { app: Application }) => {
app.enable('trust proxy');
@ -28,6 +29,7 @@ export default ({ app }: { app: Application }) => {
target: `http://localhost:${config.publicPort}/api`,
changeOrigin: true,
pathRewrite: { '/api/public': '' },
logProvider: () => Logger
}),
);

View File

@ -28,6 +28,7 @@ const TaskAfterFile = path.join(configPath, 'task_after.sh');
const homedir = os.homedir();
const sshPath = path.resolve(homedir, '.ssh');
const sshdPath = path.join(dataPath, 'ssh.d');
const systemLogPath = path.join(dataPath, 'syslog');
export default async () => {
const authFileExist = await fileExist(authConfigFile);
@ -39,6 +40,7 @@ export default async () => {
const sshDirExist = await fileExist(sshPath);
const bakDirExist = await fileExist(bakPath);
const sshdDirExist = await fileExist(sshdPath);
const systemLogDirExist = await fileExist(systemLogPath);
const tmpDirExist = await fileExist(tmpPath);
const scriptNotifyJsFileExist = await fileExist(scriptNotifyJsFile);
const scriptNotifyPyFileExist = await fileExist(scriptNotifyPyFile);
@ -77,6 +79,10 @@ export default async () => {
fs.mkdirSync(sshdPath);
}
if (!systemLogDirExist) {
fs.mkdirSync(systemLogPath);
}
// 初始化文件
if (!authFileExist) {
fs.writeFileSync(authConfigFile, fs.readFileSync(sampleAuthFile));
@ -105,4 +111,5 @@ export default async () => {
dotenv.config({ path: confFile });
Logger.info('✌️ Init file down');
console.log('✌️ Init file down');
};

View File

@ -1,32 +1,38 @@
import winston from 'winston';
import 'winston-daily-rotate-file';
import config from '../config';
import path from 'path';
const transports = [];
if (process.env.NODE_ENV !== 'development') {
transports.push(new winston.transports.Console());
} else {
transports.push(
new winston.transports.Console({
format: winston.format.combine(
winston.format.cli(),
winston.format.splat(),
),
}),
);
const levelMap: Record<string, string> = {
info: '🔵',
warn: '🟡',
error: '🔴',
debug: '🔶'
}
const customFormat = winston.format.combine(
winston.format.splat(),
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.align(),
winston.format.printf((i) => `[${levelMap[i.level]}${i.level}] [${[i.timestamp]}]: ${i.message}`),
);
const defaultOptions = {
format: customFormat,
datePattern: "YYYY-MM-DD",
maxSize: "20m",
maxFiles: "7d",
};
const LoggerInstance = winston.createLogger({
level: config.logs.level,
levels: winston.config.npm.levels,
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json(),
),
transports,
transports: [
new winston.transports.DailyRotateFile({
filename: path.join(config.systemLogPath, '%DATE%.log'),
...defaultOptions,
})
],
});
export default LoggerInstance;

View File

@ -29,4 +29,5 @@ export default async ({ expressApp }: { expressApp: Application }) => {
expressApp.use(Sentry.Handlers.tracingHandler());
Logger.info('✌️ Sentry loaded');
console.log('✌️ Sentry loaded');
};

View File

@ -10,9 +10,11 @@ export default async ({ server }: { server: Server }) => {
process.on('SIGINT', (singal) => {
Logger.warn(`Server need close, singal ${singal}`);
console.warn(`Server need close, singal ${singal}`);
exitTime++;
if (exitTime >= 3) {
Logger.warn('Forcing server close');
console.warn('Forcing server close');
clearTimeout(timer);
process.exit(1);
}
@ -28,11 +30,13 @@ export default async ({ server }: { server: Server }) => {
process.on('uncaughtException', (error) => {
Logger.error('Uncaught exception:', error);
console.error('Uncaught exception:', error);
process.exit(1);
});
process.on('unhandledRejection', (reason, promise) => {
Logger.error('Unhandled rejection:', reason);
Logger.error('Unhandled rejection:', reason, promise);
console.error('Unhandled rejection:', reason, promise);
process.exit(1);
});
};

View File

@ -6,7 +6,7 @@ import { credentials } from '@grpc/grpc-js';
const app = express();
const client = new HealthClient(
`localhost:${config.cronPort}`,
`0.0.0.0:${config.cronPort}`,
credentials.createInsecure(),
);
@ -25,9 +25,11 @@ app
await require('./loaders/db').default();
Logger.debug(`✌️ 公共服务启动成功!`);
console.debug(`✌️ 公共服务启动成功!`);
process.send?.('ready');
})
.on('error', (err) => {
Logger.error(err);
console.error(err);
process.exit(1);
});

View File

@ -22,11 +22,18 @@ const addCron = (
cmdStr = `${TASK_PREFIX}${cmdStr}`;
}
Logger.info(
'[schedule][创建定时任务], 任务ID: %s, cron: %s, 执行命令: %s',
id,
schedule,
command,
);
scheduleStacks.set(
id,
nodeSchedule.scheduleJob(id, schedule, async () => {
Logger.info(
`当前时间: ${dayjs().format(
`[schedule] 时间: ${dayjs().format(
'YYYY-MM-DD HH:mm:ss',
)}运行命令: ${cmdStr}`,
);

View File

@ -1,6 +1,7 @@
import { ServerUnaryCall, sendUnaryData } from '@grpc/grpc-js';
import { DeleteCronRequest, DeleteCronResponse } from '../protos/cron';
import { scheduleStacks } from './data';
import Logger from '../loaders/logger';
const delCron = (
call: ServerUnaryCall<DeleteCronRequest, DeleteCronResponse>,
@ -8,6 +9,10 @@ const delCron = (
) => {
for (const id of call.request.ids) {
if (scheduleStacks.has(id)) {
Logger.info(
'[schedule][取消定时任务], 任务ID: %s',
id,
);
scheduleStacks.get(id)?.cancel();
scheduleStacks.delete(id);
}

View File

@ -11,7 +11,7 @@ const server = new Server();
server.addService(HealthService, { check });
server.addService(CronService, { addCron, delCron });
server.bindAsync(
`localhost:${config.cronPort}`,
`0.0.0.0:${config.cronPort}`,
ServerCredentials.createInsecure(),
(err, port) => {
if (err) {
@ -19,6 +19,7 @@ server.bindAsync(
}
server.start();
Logger.debug(`✌️ 定时服务启动成功!`);
console.debug(`✌️ 定时服务启动成功!`);
process.send?.('ready');
},
);

View File

@ -370,7 +370,7 @@ export default class CronService {
try {
await killTask(doc.pid);
} catch (error) {
this.logger.silly(error);
this.logger.error(error);
}
}
}
@ -530,7 +530,6 @@ export default class CronService {
}
});
this.logger.silly(crontab_string);
fs.writeFileSync(config.crontabFile, crontab_string);
execSync(`crontab ${config.crontabFile}`);

View File

@ -66,7 +66,7 @@ export default class ScheduleService {
cp.stderr.on('data', async (data) => {
this.logger.info(
'[执行任务失败] %s,时间:%s, 错误信息:%j',
'[执行任务失败] %s, 时间: %s, 错误信息: %j',
command,
new Date().toLocaleString(),
data.toString(),
@ -76,7 +76,7 @@ export default class ScheduleService {
cp.on('error', async (err) => {
this.logger.error(
'[创建任务失败] %s,时间:%s, 错误信息:%j',
'[创建任务失败] %s, 时间: %s, 错误信息: %j',
command,
new Date().toLocaleString(),
err,
@ -86,7 +86,7 @@ export default class ScheduleService {
cp.on('exit', async (code, signal) => {
this.logger.info(
`[任务退出] ${command} 进程id: ${cp.pid}退出码 ${code}`,
`[任务退出] ${command} 进程id: ${cp.pid}, 退出码 ${code}`,
);
});
@ -101,7 +101,7 @@ export default class ScheduleService {
});
} catch (error) {
await this.logger.error(
'执行任务%s失败时间%s, 错误信息:%j',
'[执行任务失败] 命令: %s, 时间: %s, 错误信息: %j',
command,
new Date().toLocaleString(),
error,
@ -119,7 +119,7 @@ export default class ScheduleService {
) {
const _id = this.formatId(id);
this.logger.info(
'[创建cron任务]任务ID: %scron: %s任务名: %s执行命令: %s',
'[创建cron任务], 任务ID: %s, cron: %s, 任务名: %s, 执行命令: %s',
_id,
schedule,
name,
@ -140,7 +140,7 @@ export default class ScheduleService {
async cancelCronTask({ id = 0, name }: ScheduleTaskType) {
const _id = this.formatId(id);
this.logger.info('[取消定时任务],任务名:%s', name);
this.logger.info('[取消定时任务], 任务名: %s', name);
if (this.scheduleStacks.has(_id)) {
this.scheduleStacks.get(_id)?.cancel();
this.scheduleStacks.delete(_id);
@ -155,7 +155,7 @@ export default class ScheduleService {
) {
const _id = this.formatId(id);
this.logger.info(
'[创建interval任务]任务ID: %s任务名: %s执行命令: %s',
'[创建interval任务], 任务ID: %s, 任务名: %s, 执行命令: %s',
_id,
name,
command,
@ -167,7 +167,7 @@ export default class ScheduleService {
},
(err) => {
this.logger.error(
'执行任务%s失败时间%s, 错误信息:%j',
'[执行任务失败] 命令: %s, 时间: %s, 错误信息: %j',
command,
new Date().toLocaleString(),
err,
@ -190,7 +190,7 @@ export default class ScheduleService {
async cancelIntervalTask({ id = 0, name }: ScheduleTaskType) {
const _id = this.formatId(id);
this.logger.info('[取消interval任务]任务ID: %s任务名%s', _id, name);
this.logger.info('[取消interval任务], 任务ID: %s, 任务名: %s', _id, name);
this.intervalSchedule.removeById(_id);
}

View File

@ -301,7 +301,7 @@ export default class SubscriptionService {
try {
await killTask(doc.pid);
} catch (error) {
this.logger.silly(error);
this.logger.error(error);
}
}
const absolutePath = await handleLogPath(doc.log_path as string);

View File

@ -21,11 +21,14 @@ import {
parseContentVersion,
parseVersion,
promiseExec,
readDirs,
} from '../config/util';
import { TASK_COMMAND } from '../config/const';
import taskLimit from '../shared/pLimit';
import tar from 'tar';
import path from 'path';
import fs from 'fs';
import { sum } from 'lodash';
@Service()
export default class SystemService {
@ -275,4 +278,29 @@ export default class SystemService {
return { code: 400, message: error.message };
}
}
public async getSystemLog(res: Response) {
const result = readDirs(config.systemLogPath, config.systemLogPath);
const logs = result.reverse().filter((x) => x.title.endsWith('.log'));
res.set({
'Content-Length': sum(logs.map((x) => x.size)),
});
(function sendFiles(res, fileNames) {
if (fileNames.length === 0) {
res.end();
return;
}
const currentLog = fileNames.shift();
if (currentLog) {
const currentFileStream = fs.createReadStream(
path.join(config.systemLogPath, currentLog.title),
);
currentFileStream.on('end', () => {
sendFiles(res, fileNames);
});
currentFileStream.pipe(res, { end: false });
}
})(res, logs);
}
}

View File

@ -5,13 +5,13 @@ import Logger from '../loaders/logger';
export function runCron(cmd: string): Promise<number> {
return taskLimit.runWithCpuLimit(() => {
return new Promise(async (resolve: any) => {
Logger.silly('运行命令: ' + cmd);
Logger.info(`[schedule][开始执行任务] 运行命令: ${cmd}`);
const cp = spawn(cmd, { shell: '/bin/bash' });
cp.stderr.on('data', (data) => {
Logger.info(
'[执行任务失败] %s时间%s, 错误信息:%j',
'[schedule][执行任务失败] %s, 时间: %s, 错误信息: %j',
cmd,
new Date().toLocaleString(),
data.toString(),
@ -19,7 +19,7 @@ export function runCron(cmd: string): Promise<number> {
});
cp.on('error', (err) => {
Logger.error(
'[创建任务失败] %s时间%s, 错误信息:%j',
'[schedule][创建任务失败] %s, 时间: %s, 错误信息: %j',
cmd,
new Date().toLocaleString(),
err,
@ -27,7 +27,7 @@ export function runCron(cmd: string): Promise<number> {
});
cp.on('close', async (code) => {
Logger.info(`[任务退出] ${cmd} 进程id: ${cp.pid} 退出退出码 ${code}`);
Logger.info(`[schedule][任务退出] ${cmd} 进程id: ${cp.pid} 退出, 退出码 ${code}`);
resolve();
});
});

View File

@ -96,6 +96,7 @@
"typedi": "^0.10.0",
"uuid": "^8.3.2",
"winston": "^3.6.0",
"winston-daily-rotate-file": "^4.7.1",
"yargs": "^17.3.1"
},
"devDependencies": {
@ -127,6 +128,8 @@
"@types/sockjs-client": "^1.5.1",
"@types/tar": "^6.1.5",
"@types/uuid": "^8.3.4",
"@uiw/codemirror-extensions-langs": "^4.21.9",
"@uiw/react-codemirror": "^4.21.9",
"@umijs/max": "^4.0.72",
"@umijs/ssr-darkreader": "^4.9.45",
"ahooks": "^3.7.8",
@ -134,7 +137,6 @@
"antd": "^4.24.8",
"antd-img-crop": "^4.2.3",
"axios": "^1.4.0",
"codemirror": "^5.65.2",
"compression-webpack-plugin": "9.2.0",
"concurrently": "^7.0.0",
"file-saver": "^2.0.5",
@ -148,7 +150,6 @@
"rc-tween-one": "^3.0.6",
"rc-virtual-list": "3.5.3",
"react": "18.2.0",
"react-codemirror2": "^7.2.1",
"react-copy-to-clipboard": "^5.1.0",
"react-diff-viewer": "^3.1.1",
"react-dnd": "^14.0.2",

View File

@ -124,6 +124,9 @@ dependencies:
winston:
specifier: ^3.6.0
version: 3.9.0
winston-daily-rotate-file:
specifier: ^4.7.1
version: 4.7.1(winston@3.9.0)
yargs:
specifier: ^17.3.1
version: 17.7.2
@ -213,6 +216,12 @@ devDependencies:
'@types/uuid':
specifier: ^8.3.4
version: 8.3.4
'@uiw/codemirror-extensions-langs':
specifier: ^4.21.9
version: 4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/language-data@6.3.1)(@codemirror/language@6.9.0)(@codemirror/legacy-modes@6.3.3)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/javascript@1.4.5)(@lezer/lr@1.3.10)
'@uiw/react-codemirror':
specifier: ^4.21.9
version: 4.21.9(@babel/runtime@7.22.3)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0)
'@umijs/max':
specifier: ^4.0.72
version: 4.0.72(@types/node@17.0.45)(@types/react-dom@18.2.4)(@types/react@18.2.8)(prettier@2.8.8)(react-dom@18.2.0)(react@18.2.0)(sockjs-client@1.6.1)(typescript@4.8.4)(webpack@5.85.1)
@ -234,9 +243,6 @@ devDependencies:
axios:
specifier: ^1.4.0
version: 1.4.0
codemirror:
specifier: ^5.65.2
version: 5.65.13
compression-webpack-plugin:
specifier: 9.2.0
version: 9.2.0(webpack@5.85.1)
@ -276,9 +282,6 @@ devDependencies:
react:
specifier: 18.2.0
version: 18.2.0
react-codemirror2:
specifier: ^7.2.1
version: 7.2.1(codemirror@5.65.13)(react@18.2.0)
react-copy-to-clipboard:
specifier: ^5.1.0
version: 5.1.0(react@18.2.0)
@ -2762,6 +2765,298 @@ packages:
tinycolor2: 1.6.0
dev: true
/@codemirror/autocomplete@6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4):
resolution: {integrity: sha512-Fbwm0V/Wn3BkEJZRhr0hi5BhCo5a7eBL6LYaliPjOSwCyfOpnjXY59HruSxOUNV+1OYer0Tgx1zRNQttjXyDog==}
peerDependencies:
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
dependencies:
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
dev: true
/@codemirror/commands@6.2.4:
resolution: {integrity: sha512-42lmDqVH0ttfilLShReLXsDfASKLXzfyC36bzwcqzox9PlHulMcsUOfHXNo2X2aFMVNUoQ7j+d4q5bnfseYoOA==}
dependencies:
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
dev: true
/@codemirror/lang-angular@0.1.2:
resolution: {integrity: sha512-Nq7lmx9SU+JyoaRcs6SaJs7uAmW2W06HpgJVQYeZptVGNWDzDvzhjwVb/ZuG1rwTlOocY4Y9GwNOBuKCeJbKtw==}
dependencies:
'@codemirror/lang-html': 6.4.5
'@codemirror/lang-javascript': 6.1.9
'@codemirror/language': 6.9.0
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@codemirror/lang-cpp@6.0.2:
resolution: {integrity: sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==}
dependencies:
'@codemirror/language': 6.9.0
'@lezer/cpp': 1.1.1
dev: true
/@codemirror/lang-css@6.2.1(@codemirror/view@6.16.0):
resolution: {integrity: sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@lezer/common': 1.0.4
'@lezer/css': 1.1.3
transitivePeerDependencies:
- '@codemirror/view'
dev: true
/@codemirror/lang-html@6.4.5:
resolution: {integrity: sha512-dUCSxkIw2G+chaUfw3Gfu5kkN83vJQN8gfQDp9iEHsIZluMJA0YJveT12zg/28BJx+uPsbQ6VimKCgx3oJrZxA==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-css': 6.2.1(@codemirror/view@6.16.0)
'@codemirror/lang-javascript': 6.1.9
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/css': 1.1.3
'@lezer/html': 1.3.6
dev: true
/@codemirror/lang-java@6.0.1:
resolution: {integrity: sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==}
dependencies:
'@codemirror/language': 6.9.0
'@lezer/java': 1.0.4
dev: true
/@codemirror/lang-javascript@6.1.9:
resolution: {integrity: sha512-z3jdkcqOEBT2txn2a87A0jSy6Te3679wg/U8QzMeftFt+4KA6QooMwfdFzJiuC3L6fXKfTXZcDocoaxMYfGz0w==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@codemirror/lint': 6.4.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/javascript': 1.4.5
dev: true
/@codemirror/lang-json@6.0.1:
resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==}
dependencies:
'@codemirror/language': 6.9.0
'@lezer/json': 1.0.1
dev: true
/@codemirror/lang-less@6.0.1(@codemirror/view@6.16.0):
resolution: {integrity: sha512-ABcsKBjLbyPZwPR5gePpc8jEKCQrFF4pby2WlMVdmJOOr7OWwwyz8DZonPx/cKDE00hfoSLc8F7yAcn/d6+rTQ==}
dependencies:
'@codemirror/lang-css': 6.2.1(@codemirror/view@6.16.0)
'@codemirror/language': 6.9.0
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
transitivePeerDependencies:
- '@codemirror/view'
dev: true
/@codemirror/lang-lezer@6.0.1:
resolution: {integrity: sha512-WHwjI7OqKFBEfkunohweqA5B/jIlxaZso6Nl3weVckz8EafYbPZldQEKSDb4QQ9H9BUkle4PVELP4sftKoA0uQ==}
dependencies:
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@lezer/common': 1.0.4
'@lezer/lezer': 1.1.2
dev: true
/@codemirror/lang-markdown@6.2.0:
resolution: {integrity: sha512-deKegEQVzfBAcLPqsJEa+IxotqPVwWZi90UOEvQbfa01NTAw8jNinrykuYPTULGUj+gha0ZG2HBsn4s5d64Qrg==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-html': 6.4.5
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/markdown': 1.1.0
dev: true
/@codemirror/lang-php@6.0.1:
resolution: {integrity: sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==}
dependencies:
'@codemirror/lang-html': 6.4.5
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@lezer/common': 1.0.4
'@lezer/php': 1.0.1
dev: true
/@codemirror/lang-python@6.1.3(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4):
resolution: {integrity: sha512-S9w2Jl74hFlD5nqtUMIaXAq9t5WlM0acCkyuQWUUSvZclk1sV+UfnpFiZzuZSG+hfEaOmxKR5UxY/Uxswn7EhQ==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@lezer/python': 1.1.8
transitivePeerDependencies:
- '@codemirror/state'
- '@codemirror/view'
- '@lezer/common'
dev: true
/@codemirror/lang-rust@6.0.1:
resolution: {integrity: sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==}
dependencies:
'@codemirror/language': 6.9.0
'@lezer/rust': 1.0.1
dev: true
/@codemirror/lang-sass@6.0.2(@codemirror/view@6.16.0):
resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==}
dependencies:
'@codemirror/lang-css': 6.2.1(@codemirror/view@6.16.0)
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@lezer/common': 1.0.4
'@lezer/sass': 1.0.3
transitivePeerDependencies:
- '@codemirror/view'
dev: true
/@codemirror/lang-sql@6.5.4(@codemirror/view@6.16.0)(@lezer/common@1.0.4):
resolution: {integrity: sha512-5Gq7fYtT/5HbNyIG7a8vYaqOYQU3JbgtBe3+derkrFUXRVcjkf8WVgz++PIbMFAQsOFMDdDR+uiNM8ZRRuXH+w==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
transitivePeerDependencies:
- '@codemirror/view'
- '@lezer/common'
dev: true
/@codemirror/lang-vue@0.1.2:
resolution: {integrity: sha512-D4YrefiRBAr+CfEIM4S3yvGSbYW+N69mttIfGMEf7diHpRbmygDxS+R/5xSqjgtkY6VO6qmUrre1GkRcWeZa9A==}
dependencies:
'@codemirror/lang-html': 6.4.5
'@codemirror/lang-javascript': 6.1.9
'@codemirror/language': 6.9.0
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@codemirror/lang-wast@6.0.1:
resolution: {integrity: sha512-sQLsqhRjl2MWG3rxZysX+2XAyed48KhLBHLgq9xcKxIJu3npH/G+BIXW5NM5mHeDUjG0jcGh9BcjP0NfMStuzA==}
dependencies:
'@codemirror/language': 6.9.0
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@codemirror/lang-xml@6.0.2(@codemirror/view@6.16.0):
resolution: {integrity: sha512-JQYZjHL2LAfpiZI2/qZ/qzDuSqmGKMwyApYmEUUCTxLM4MWS7sATUEfIguZQr9Zjx/7gcdnewb039smF6nC2zw==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@lezer/common': 1.0.4
'@lezer/xml': 1.0.2
transitivePeerDependencies:
- '@codemirror/view'
dev: true
/@codemirror/language-data@6.3.1(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4):
resolution: {integrity: sha512-p6jhJmvhGe1TG1EGNhwH7nFWWFSTJ8NDKnB2fVx5g3t+PpO0+63R7GJNxjS0TmmH3cdMxZbzejsik+rlEh1EyQ==}
dependencies:
'@codemirror/lang-angular': 0.1.2
'@codemirror/lang-cpp': 6.0.2
'@codemirror/lang-css': 6.2.1(@codemirror/view@6.16.0)
'@codemirror/lang-html': 6.4.5
'@codemirror/lang-java': 6.0.1
'@codemirror/lang-javascript': 6.1.9
'@codemirror/lang-json': 6.0.1
'@codemirror/lang-less': 6.0.1(@codemirror/view@6.16.0)
'@codemirror/lang-markdown': 6.2.0
'@codemirror/lang-php': 6.0.1
'@codemirror/lang-python': 6.1.3(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-rust': 6.0.1
'@codemirror/lang-sass': 6.0.2(@codemirror/view@6.16.0)
'@codemirror/lang-sql': 6.5.4(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-vue': 0.1.2
'@codemirror/lang-wast': 6.0.1
'@codemirror/lang-xml': 6.0.2(@codemirror/view@6.16.0)
'@codemirror/language': 6.9.0
'@codemirror/legacy-modes': 6.3.3
transitivePeerDependencies:
- '@codemirror/state'
- '@codemirror/view'
- '@lezer/common'
dev: true
/@codemirror/language@6.9.0:
resolution: {integrity: sha512-nFu311/0ne/qGuGCL3oKuktBgzVOaxCHZPZv1tLSZkNjPYxxvkjSbzno3MlErG2tgw1Yw1yF8BxMCegeMXqpiw==}
dependencies:
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
style-mod: 4.0.3
dev: true
/@codemirror/legacy-modes@6.3.3:
resolution: {integrity: sha512-X0Z48odJ0KIoh/HY8Ltz75/4tDYc9msQf1E/2trlxFaFFhgjpVHjZ/BCXe1Lk7s4Gd67LL/CeEEHNI+xHOiESg==}
dependencies:
'@codemirror/language': 6.9.0
dev: true
/@codemirror/lint@6.4.0:
resolution: {integrity: sha512-6VZ44Ysh/Zn07xrGkdtNfmHCbGSHZzFBdzWi0pbd7chAQ/iUcpLGX99NYRZTa7Ugqg4kEHCqiHhcZnH0gLIgSg==}
dependencies:
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
crelt: 1.0.6
dev: true
/@codemirror/search@6.5.1:
resolution: {integrity: sha512-4jupk4JwkeVbrN2pStY74q6OJEYqwosB4koA66nyLeVedadtX9MHI38j2vbYmnfDGurDApP3OZO46MrWalcjiQ==}
dependencies:
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
crelt: 1.0.6
dev: true
/@codemirror/state@6.2.1:
resolution: {integrity: sha512-RupHSZ8+OjNT38zU9fKH2sv+Dnlr8Eb8sl4NOnnqz95mCFTZUaiRP8Xv5MeeaG0px2b8Bnfe7YGwCV3nsBhbuw==}
dev: true
/@codemirror/theme-one-dark@6.1.2:
resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==}
dependencies:
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/highlight': 1.1.6
dev: true
/@codemirror/view@6.16.0:
resolution: {integrity: sha512-1Z2HkvkC3KR/oEZVuW9Ivmp8TWLzGEd8T8TA04TTwPvqogfkHBdYSlflytDOqmkUxM2d1ywTg7X2dU5mC+SXvg==}
dependencies:
'@codemirror/state': 6.2.1
style-mod: 4.0.3
w3c-keyname: 2.2.8
dev: true
/@colors/colors@1.5.0:
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'}
@ -3562,6 +3857,114 @@ packages:
resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==}
dev: true
/@lezer/common@1.0.4:
resolution: {integrity: sha512-lZHlk8p67x4aIDtJl6UQrXSOP6oi7dQR3W/geFVrENdA1JDaAJWldnVqVjPMJupbTKbzDfFcePfKttqVidS/dg==}
dev: true
/@lezer/cpp@1.1.1:
resolution: {integrity: sha512-eS1M3L3U2mDowoFVPG7tEp01SWu9/68Nx3HEBgLJVn3N9ku7g5S7WdFv0jzmcTipAyONYfZJ+7x4WRkfdB2Ung==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/css@1.1.3:
resolution: {integrity: sha512-SjSM4pkQnQdJDVc80LYzEaMiNy9txsFbI7HsMgeVF28NdLaAdHNtQ+kB/QqDUzRBV/75NTXjJ/R5IdC8QQGxMg==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/highlight@1.1.6:
resolution: {integrity: sha512-cmSJYa2us+r3SePpRCjN5ymCqCPv+zyXmDl0ciWtVaNiORT/MxM7ZgOMQZADD0o51qOaOg24qc/zBViOIwAjJg==}
dependencies:
'@lezer/common': 1.0.4
dev: true
/@lezer/html@1.3.6:
resolution: {integrity: sha512-Kk9HJARZTc0bAnMQUqbtuhFVsB4AnteR2BFUWfZV7L/x1H0aAKz6YabrfJ2gk/BEgjh9L3hg5O4y2IDZRBdzuQ==}
dependencies:
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/java@1.0.4:
resolution: {integrity: sha512-POc53LHf2AuNeRXjqZbXNu88GKj0KZTjjSx0L7tYeXlrEHF+3NAQx+dEwKVuCbkl0ZMtpRy2VsDYOV7KKV0oyg==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/javascript@1.4.5:
resolution: {integrity: sha512-FmBUHz8K1V22DgjTd6SrIG9owbzOYZ1t3rY6vGEmw+e2RVBd7sqjM8uXEVRFmfxKFn1Mx2ABJehHjrN3G2ZpmA==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/json@1.0.1:
resolution: {integrity: sha512-nkVC27qiEZEjySbi6gQRuMwa2sDu2PtfjSgz0A4QF81QyRGm3kb2YRzLcOPcTEtmcwvrX/cej7mlhbwViA4WJw==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/lezer@1.1.2:
resolution: {integrity: sha512-O8yw3CxPhzYHB1hvwbdozjnAslhhR8A5BH7vfEMof0xk3p+/DFDfZkA9Tde6J+88WgtwaHy4Sy6ThZSkaI0Evw==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/lr@1.3.10:
resolution: {integrity: sha512-BZfVvf7Re5BIwJHlZXbJn9L8lus5EonxQghyn+ih8Wl36XMFBPTXC0KM0IdUtj9w/diPHsKlXVgL+AlX2jYJ0Q==}
dependencies:
'@lezer/common': 1.0.4
dev: true
/@lezer/markdown@1.1.0:
resolution: {integrity: sha512-JYOI6Lkqbl83semCANkO3CKbKc0pONwinyagBufWBm+k4yhIcqfCF8B8fpEpvJLmIy7CAfwiq7dQ/PzUZA340g==}
dependencies:
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
dev: true
/@lezer/php@1.0.1:
resolution: {integrity: sha512-aqdCQJOXJ66De22vzdwnuC502hIaG9EnPK2rSi+ebXyUd+j7GAX1mRjWZOVOmf3GST1YUfUCu6WXDiEgDGOVwA==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/python@1.1.8:
resolution: {integrity: sha512-1T/XsmeF57ijrjpC0Zmrf9YeO5mn2zC1XeSNrOnc0KB+6PgxJ5m7kWKt0CnwyS74oHQXbJxUUL+QDQJR26c1Gw==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/rust@1.0.1:
resolution: {integrity: sha512-j+ToFKM6Wpglv3OQ4ebHYdYIMT2dh0ziCCV0rTf47AWiHOVhR0WjaKrBq+yuvDQNEhr5sxPxVI7+naJIgpqcsQ==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/sass@1.0.3:
resolution: {integrity: sha512-n4l2nVOB7gWiGU/Cg2IVxpt2Ic9Hgfgy/7gk+p/XJibAsPXs0lSbsfGwQgwsAw9B/euYo3oS6lEFr9WytoqcZg==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@lezer/xml@1.0.2:
resolution: {integrity: sha512-dlngsWceOtQBMuBPw5wtHpaxdPJ71aVntqjbpGkFtWsp4WtQmCnuTjQGocviymydN6M18fhj6UQX3oiEtSuY7w==}
dependencies:
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@loadable/component@5.15.2(react@18.1.0):
resolution: {integrity: sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==}
engines: {node: '>=8'}
@ -3644,6 +4047,19 @@ packages:
state-local: 1.0.7
dev: true
/@nextjournal/lang-clojure@1.0.0:
resolution: {integrity: sha512-gOCV71XrYD0DhwGoPMWZmZ0r92/lIHsqQu9QWdpZYYBwiChNwMO4sbVMP7eTuAqffFB2BTtCSC+1skSH9d3bNg==}
dependencies:
'@codemirror/language': 6.9.0
'@nextjournal/lezer-clojure': 1.0.0
dev: true
/@nextjournal/lezer-clojure@1.0.0:
resolution: {integrity: sha512-VZyuGu4zw5mkTOwQBTaGVNWmsOZAPw5ZRxu1/Knk/Xfs7EDBIogwIs5UXTYkuECX5ZQB8eOB+wKA2pc7VyqaZQ==}
dependencies:
'@lezer/lr': 1.3.10
dev: true
/@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3:
resolution: {integrity: sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ==}
requiresBuild: true
@ -3878,6 +4294,82 @@ packages:
react: 18.2.0
dev: true
/@replit/codemirror-lang-csharp@6.1.0(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/lr@1.3.10):
resolution: {integrity: sha512-Dtyk9WVrdPPgkgTp8MUX9HyXd87O7UZnFrE647gjHUZY8p0UN+z0m6dPfk6rJMsTTvMcl7YbDUykxfeqB6EQOQ==}
peerDependencies:
'@codemirror/autocomplete': ^6.0.0
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
'@lezer/highlight': ^1.0.0
'@lezer/lr': ^1.0.0
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@replit/codemirror-lang-nix@6.0.1(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/lr@1.3.10):
resolution: {integrity: sha512-lvzjoYn9nfJzBD5qdm3Ut6G3+Or2wEacYIDJ49h9+19WSChVnxv4ojf+rNmQ78ncuxIt/bfbMvDLMeMP0xze6g==}
peerDependencies:
'@codemirror/autocomplete': ^6.0.0
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
'@lezer/highlight': ^1.0.0
'@lezer/lr': ^1.0.0
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/@replit/codemirror-lang-solidity@6.0.1(@codemirror/language@6.9.0):
resolution: {integrity: sha512-kDnak0xZelGmvzJwKTpMTl6gYSfFq9hnxrkbLaMV0CARq/MFvDQJmcmYon/k8uZqXy6DfzewKDV8tx9kY2WUZg==}
peerDependencies:
'@codemirror/language': ^6.0.0
dependencies:
'@codemirror/language': 6.9.0
dev: true
/@replit/codemirror-lang-svelte@6.0.0(@codemirror/autocomplete@6.9.0)(@codemirror/lang-css@6.2.1)(@codemirror/lang-html@6.4.5)(@codemirror/lang-javascript@6.1.9)(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/javascript@1.4.5)(@lezer/lr@1.3.10):
resolution: {integrity: sha512-U2OqqgMM6jKelL0GNWbAmqlu1S078zZNoBqlJBW+retTc5M4Mha6/Y2cf4SVg6ddgloJvmcSpt4hHrVoM4ePRA==}
peerDependencies:
'@codemirror/autocomplete': ^6.0.0
'@codemirror/lang-css': ^6.0.1
'@codemirror/lang-html': ^6.2.0
'@codemirror/lang-javascript': ^6.1.1
'@codemirror/language': ^6.0.0
'@codemirror/state': ^6.0.0
'@codemirror/view': ^6.0.0
'@lezer/common': ^1.0.0
'@lezer/highlight': ^1.0.0
'@lezer/javascript': ^1.2.0
'@lezer/lr': ^1.0.0
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-css': 6.2.1(@codemirror/view@6.16.0)
'@codemirror/lang-html': 6.4.5
'@codemirror/lang-javascript': 6.1.9
'@codemirror/language': 6.9.0
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
'@lezer/common': 1.0.4
'@lezer/highlight': 1.1.6
'@lezer/javascript': 1.4.5
'@lezer/lr': 1.3.10
dev: true
/@sentry-internal/tracing@7.54.0:
resolution: {integrity: sha512-JsyhZ0wWZ+VqbHJg+azqRGdYJDkcI5R9+pnkO6SzbzxrRewqMAIwzkpPee3oI7vG99uhMEkOkMjHu0nQGwkOQw==}
engines: {node: '>=8'}
@ -4828,6 +5320,101 @@ packages:
eslint-visitor-keys: 3.4.1
dev: true
/@uiw/codemirror-extensions-basic-setup@4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0):
resolution: {integrity: sha512-TQT6aF8brxZpFnk/K4fm/K/9k9eF3PMav/KKjHlYrGUT8BTNk/qL+ximLtIzvTUhmBFchjM1lrqSJdvpVom7/w==}
peerDependencies:
'@codemirror/autocomplete': '>=6.0.0'
'@codemirror/commands': '>=6.0.0'
'@codemirror/language': '>=6.0.0'
'@codemirror/lint': '>=6.0.0'
'@codemirror/search': '>=6.0.0'
'@codemirror/state': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/commands': 6.2.4
'@codemirror/language': 6.9.0
'@codemirror/lint': 6.4.0
'@codemirror/search': 6.5.1
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
dev: true
/@uiw/codemirror-extensions-langs@4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/language-data@6.3.1)(@codemirror/language@6.9.0)(@codemirror/legacy-modes@6.3.3)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/javascript@1.4.5)(@lezer/lr@1.3.10):
resolution: {integrity: sha512-s1VT1rss0iyvrtRl7BZtC5H7U5uQtCKTaD8wxjQrgZz5un9wHVvy9twU97aJGQR0FwbKWqK8/1iiICRJTRCoZA==}
peerDependencies:
'@codemirror/language-data': '>=6.0.0'
'@codemirror/legacy-modes': '>=6.0.0'
dependencies:
'@codemirror/lang-angular': 0.1.2
'@codemirror/lang-cpp': 6.0.2
'@codemirror/lang-css': 6.2.1(@codemirror/view@6.16.0)
'@codemirror/lang-html': 6.4.5
'@codemirror/lang-java': 6.0.1
'@codemirror/lang-javascript': 6.1.9
'@codemirror/lang-json': 6.0.1
'@codemirror/lang-less': 6.0.1(@codemirror/view@6.16.0)
'@codemirror/lang-lezer': 6.0.1
'@codemirror/lang-markdown': 6.2.0
'@codemirror/lang-php': 6.0.1
'@codemirror/lang-python': 6.1.3(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-rust': 6.0.1
'@codemirror/lang-sass': 6.0.2(@codemirror/view@6.16.0)
'@codemirror/lang-sql': 6.5.4(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/lang-vue': 0.1.2
'@codemirror/lang-wast': 6.0.1
'@codemirror/lang-xml': 6.0.2(@codemirror/view@6.16.0)
'@codemirror/language-data': 6.3.1(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/legacy-modes': 6.3.3
'@nextjournal/lang-clojure': 1.0.0
'@replit/codemirror-lang-csharp': 6.1.0(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/lr@1.3.10)
'@replit/codemirror-lang-nix': 6.0.1(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/lr@1.3.10)
'@replit/codemirror-lang-solidity': 6.0.1(@codemirror/language@6.9.0)
'@replit/codemirror-lang-svelte': 6.0.0(@codemirror/autocomplete@6.9.0)(@codemirror/lang-css@6.2.1)(@codemirror/lang-html@6.4.5)(@codemirror/lang-javascript@6.1.9)(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)(@lezer/highlight@1.1.6)(@lezer/javascript@1.4.5)(@lezer/lr@1.3.10)
codemirror-lang-mermaid: 0.2.2
transitivePeerDependencies:
- '@codemirror/autocomplete'
- '@codemirror/language'
- '@codemirror/state'
- '@codemirror/view'
- '@lezer/common'
- '@lezer/highlight'
- '@lezer/javascript'
- '@lezer/lr'
dev: true
/@uiw/react-codemirror@4.21.9(@babel/runtime@7.22.3)(@codemirror/autocomplete@6.9.0)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.16.0)(codemirror@6.0.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-aeLegPz2iCvqJjhzXp2WUMqpMZDqxsTnF3rX9kGRlfY6vQLsrjoctj0cQ29uxEtFYJChOVjtCOtnQUlyIuNAHQ==}
peerDependencies:
'@babel/runtime': '>=7.11.0'
'@codemirror/state': '>=6.0.0'
'@codemirror/theme-one-dark': '>=6.0.0'
'@codemirror/view': '>=6.0.0'
codemirror: '>=6.0.0'
react: '>=16.8.0 || 18'
react-dom: '>=16.8.0 || 18'
peerDependenciesMeta:
react:
optional: true
react-dom:
optional: true
dependencies:
'@babel/runtime': 7.22.3
'@codemirror/commands': 6.2.4
'@codemirror/state': 6.2.1
'@codemirror/theme-one-dark': 6.1.2
'@codemirror/view': 6.16.0
'@uiw/codemirror-extensions-basic-setup': 4.21.9(@codemirror/autocomplete@6.9.0)(@codemirror/commands@6.2.4)(@codemirror/language@6.9.0)(@codemirror/lint@6.4.0)(@codemirror/search@6.5.1)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)
codemirror: 6.0.1(@lezer/common@1.0.4)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies:
- '@codemirror/autocomplete'
- '@codemirror/language'
- '@codemirror/lint'
- '@codemirror/search'
dev: true
/@umijs/ast@4.0.72:
resolution: {integrity: sha512-WatRvU09vsx4Hlu5hemPA7a+QK4pJvzmQz/9LxN/KVgn+wZXi717qHFLu5eoV6XO7HlFZaEBGq2aHpDj0ngA8w==}
dependencies:
@ -6657,8 +7244,26 @@ packages:
mimic-response: 1.0.1
dev: false
/codemirror@5.65.13:
resolution: {integrity: sha512-SVWEzKXmbHmTQQWaz03Shrh4nybG0wXx2MEu3FO4ezbPW8IbnZEd5iGHGEffSUaitKYa3i+pHpBsSvw8sPHtzg==}
/codemirror-lang-mermaid@0.2.2:
resolution: {integrity: sha512-AqSzkQgfWsjBbifio3dy/zDj6WXEw4g52Mq6bltIWLMWryWWRMpFwjQSlHtCGOol1FENYObUF5KI4ofiv8bjXA==}
dependencies:
'@codemirror/language': 6.9.0
'@lezer/highlight': 1.1.6
'@lezer/lr': 1.3.10
dev: true
/codemirror@6.0.1(@lezer/common@1.0.4):
resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
dependencies:
'@codemirror/autocomplete': 6.9.0(@codemirror/language@6.9.0)(@codemirror/state@6.2.1)(@codemirror/view@6.16.0)(@lezer/common@1.0.4)
'@codemirror/commands': 6.2.4
'@codemirror/language': 6.9.0
'@codemirror/lint': 6.4.0
'@codemirror/search': 6.5.1
'@codemirror/state': 6.2.1
'@codemirror/view': 6.16.0
transitivePeerDependencies:
- '@lezer/common'
dev: true
/color-convert@1.9.3:
@ -6970,6 +7575,10 @@ packages:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/crelt@1.0.6:
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
dev: true
/cron-parser@4.8.1:
resolution: {integrity: sha512-jbokKWGcyU4gl6jAfX97E1gDpY12DJ1cLJZmoDzaAln/shZ+S3KBFBuA2Q6WeUN4gJf/8klnV1EfvhA2lK5IRQ==}
engines: {node: '>=12.0.0'}
@ -8273,6 +8882,12 @@ packages:
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
dev: true
/file-stream-rotator@0.6.1:
resolution: {integrity: sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==}
dependencies:
moment: 2.29.4
dev: false
/file-uri-to-path@2.0.0:
resolution: {integrity: sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==}
engines: {node: '>= 6'}
@ -10787,6 +11402,11 @@ packages:
engines: {node: '>= 0.10.0'}
dev: true
/object-hash@2.2.0:
resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==}
engines: {node: '>= 6'}
dev: false
/object-inspect@1.12.3:
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
@ -12903,19 +13523,6 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: true
/react-codemirror2@7.2.1(codemirror@5.65.13)(react@18.2.0):
resolution: {integrity: sha512-t7YFmz1AXdlImgHXA9Ja0T6AWuopilub24jRaQdPVbzUJVNKIYuy3uCFZYa7CE5S3UW6SrSa5nAqVQvtzRF9gw==}
peerDependencies:
codemirror: 5.x
react: '>=15.5 <=16.x || 18'
peerDependenciesMeta:
react:
optional: true
dependencies:
codemirror: 5.65.13
react: 18.2.0
dev: true
/react-copy-to-clipboard@5.1.0(react@18.2.0):
resolution: {integrity: sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A==}
peerDependencies:
@ -14307,6 +14914,10 @@ packages:
engines: {node: '>=8'}
dev: true
/style-mod@4.0.3:
resolution: {integrity: sha512-78Jv8kYJdjbvRwwijtCevYADfsI0lGzYJe4mMFdceO8l75DFFDoqBhR1jVDicDRRaX4//g1u9wKeo+ztc2h1Rw==}
dev: true
/style-search@0.1.0:
resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==}
dev: true
@ -15308,6 +15919,10 @@ packages:
acorn-walk: 8.2.0
dev: true
/w3c-keyname@2.2.8:
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
dev: true
/walker@1.0.8:
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
dependencies:
@ -15489,6 +16104,19 @@ packages:
semver: 5.7.1
dev: true
/winston-daily-rotate-file@4.7.1(winston@3.9.0):
resolution: {integrity: sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==}
engines: {node: '>=8'}
peerDependencies:
winston: ^3
dependencies:
file-stream-rotator: 0.6.1
object-hash: 2.2.0
triple-beam: 1.3.0
winston: 3.9.0
winston-transport: 4.5.0
dev: false
/winston-transport@4.5.0:
resolution: {integrity: sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==}
engines: {node: '>= 6.4.0'}

View File

@ -296,7 +296,7 @@ git_clone_scripts() {
set_proxy "$proxy"
git clone --depth=1 $part_cmd $url $dir
git clone --depth=1 $part_cmd $url $dir 2>&1
exit_status=$?
unset_proxy

View File

@ -1,6 +1,5 @@
@import '~antd/es/style/themes/default.less';
@import '~@/styles/variable.less';
@import '~codemirror/lib/codemirror.css';
@font-face {
font-family: 'Source Code Pro';

View File

@ -32,9 +32,6 @@ import {
import SockJS from 'sockjs-client';
import * as Sentry from '@sentry/react';
import { init } from '../utils/init';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/python/python';
import 'codemirror/mode/shell/shell';
export interface SharedContext {
headerStyle: React.CSSProperties;

View File

@ -448,5 +448,7 @@
"编辑订阅": "Edit Subscription",
"Subscription表达式格式有误": "Incorrect Subscription Expression Format",
"一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "The 'Group Code' for One-to-Many Push (Below One-to-Many Push->Your Group (if not, create one)->Group Code, if you are the creator of the group, you also need to click 'View QR Code' to scan and bind, otherwise you cannot receive group messages)",
"登录已过期,请重新登录": "Login session has expired, please log in again"
"登录已过期,请重新登录": "Login session has expired, please log in again",
"系统日志": "System Logs",
"主题": "Theme"
}

View File

@ -448,5 +448,7 @@
"编辑订阅": "编辑订阅",
"Subscription表达式格式有误": "Subscription表达式格式有误",
"一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)": "一对多推送的“群组编码”(一对多推送下面->您的群组(如无则新建)->群组编码,如果您是创建群组人。也需点击“查看二维码”扫描绑定,否则不能接受群组消息推送)",
"登录已过期,请重新登录": "登录已过期,请重新登录"
"登录已过期,请重新登录": "登录已过期,请重新登录",
"系统日志": "系统日志",
"主题": "主题"
}

View File

@ -11,9 +11,10 @@ import config from '@/utils/config';
import { PageContainer } from '@ant-design/pro-layout';
import { request } from '@/utils/http';
import Editor from '@monaco-editor/react';
import { Controlled as CodeMirror } from 'react-codemirror2';
import CodeMirror from '@uiw/react-codemirror';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
import { langs } from '@uiw/codemirror-extensions-langs';
const Config = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
@ -104,16 +105,11 @@ const Config = () => {
{isPhone ? (
<CodeMirror
value={value}
options={{
lineNumbers: true,
styleActiveLine: true,
matchBrackets: true,
mode: 'shell',
}}
onBeforeChange={(editor, data, value) => {
theme={theme.includes('dark') ? 'dark' : 'light'}
extensions={[langs.shell()]}
onChange={(value) => {
setValue(value);
}}
onChange={(editor, data, value) => {}}
/>
) : (
<Editor

View File

@ -16,7 +16,7 @@ import { PageContainer } from '@ant-design/pro-layout';
import Editor from '@monaco-editor/react';
import { request } from '@/utils/http';
import styles from './index.module.less';
import { Controlled as CodeMirror } from 'react-codemirror2';
import CodeMirror from '@uiw/react-codemirror';
import SplitPane from 'react-split-pane';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
@ -278,17 +278,11 @@ const Log = () => {
{isPhone && (
<CodeMirror
value={value}
options={{
lineNumbers: true,
lineWrapping: true,
styleActiveLine: true,
matchBrackets: true,
readOnly: true,
}}
onBeforeChange={(editor, data, value) => {
readOnly={true}
theme={theme.includes('dark') ? 'dark' : 'light'}
onChange={(value, viewUpdate) => {
setValue(value);
}}
onChange={(editor, data, value) => {}}
/>
)}
</div>

View File

@ -20,7 +20,7 @@ import Editor from '@monaco-editor/react';
import { request } from '@/utils/http';
import styles from './index.module.less';
import EditModal from './editModal';
import { Controlled as CodeMirror } from 'react-codemirror2';
import CodeMirror from '@uiw/react-codemirror';
import SplitPane from 'react-split-pane';
import {
DeleteOutlined,
@ -43,6 +43,7 @@ import useFilterTreeData from '@/hooks/useFilterTreeData';
import uniq from 'lodash/uniq';
import IconFont from '@/components/iconfont';
import RenameModal from './renameModal';
import { langs } from '@uiw/codemirror-extensions-langs';
const { Text } = Typography;
@ -580,18 +581,14 @@ const Script = () => {
{isPhone && (
<CodeMirror
value={value}
options={{
lineNumbers: true,
lineWrapping: true,
styleActiveLine: true,
matchBrackets: true,
mode,
readOnly: !isEditing,
}}
onBeforeChange={(editor, data, value) => {
extensions={
mode ? [langs[mode as keyof typeof langs]()] : undefined
}
theme={theme.includes('dark') ? 'dark' : 'light'}
readOnly={!isEditing}
onChange={(value) => {
setValue(value);
}}
onChange={(editor, data, value) => {}}
/>
)}
<EditModal

View File

@ -1,5 +1,5 @@
import intl from 'react-intl-universal';
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import {
Button,
InputNumber,
@ -32,6 +32,7 @@ import About from './about';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
import './index.less';
import CodeMirror from '@uiw/react-codemirror';
const { Text } = Typography;
const isDemoEnv = window.__ENV__DeployEnv === 'demo';
@ -41,6 +42,7 @@ const Setting = () => {
headerStyle,
isPhone,
user,
theme,
reloadUser,
reloadTheme,
socketMessage,
@ -113,7 +115,9 @@ const Setting = () => {
const [editedApp, setEditedApp] = useState<any>();
const [tabActiveKey, setTabActiveKey] = useState('security');
const [loginLogData, setLoginLogData] = useState<any[]>([]);
const [systemLogData, setSystemLogData] = useState<string>('');
const [notificationInfo, setNotificationInfo] = useState<any>();
const systemLogRef = useRef<any>();
const getApps = () => {
setLoading(true);
@ -232,6 +236,19 @@ const Setting = () => {
});
};
const getSystemLog = () => {
request
.get<Blob>(`${config.apiPrefix}system/log`, {
responseType: 'blob',
})
.then(async (res) => {
setSystemLogData(await res.text());
})
.catch((error: any) => {
console.log(error);
});
};
const tabChange = (activeKey: string) => {
setTabActiveKey(activeKey);
if (activeKey === 'app') {
@ -240,6 +257,8 @@ const Setting = () => {
getLoginLog();
} else if (activeKey === 'notification') {
getNotification();
} else if (activeKey === 'syslog') {
getSystemLog();
}
};
@ -332,6 +351,30 @@ const Setting = () => {
/>
),
},
{
key: 'syslog',
label: intl.get('系统日志'),
children: (
<CodeMirror
ref={systemLogRef}
maxHeight={`calc(100vh - ${
systemLogRef.current?.editor?.getBoundingClientRect()?.top +
16
}px)`}
value={systemLogData}
onCreateEditor={(view) => {
setTimeout(() => {
view.scrollDOM.scrollTo({
top: view.scrollDOM.scrollHeight,
behavior: 'smooth',
});
}, 300);
}}
readOnly={true}
theme={theme.includes('dark') ? 'dark' : 'light'}
/>
),
},
{
key: 'about',
label: intl.get('关于'),

View File

@ -19,7 +19,7 @@ enum LoginStatusColor {
const columns = [
{
title: intl.get('序号'),
width: 40,
width: 50,
render: (text: string, record: any, index: number) => {
return index + 1;
},
@ -75,7 +75,7 @@ const LoginLog = ({ data }: any) => {
dataSource={data}
rowKey="id"
size="middle"
scroll={{ x: 768 }}
scroll={{ x: 1000 }}
sticky
/>
</>