mirror of
https://github.com/SikongJueluo/cc-utils.git
synced 2025-11-29 12:57:50 +08:00
refactor(logging): migrate from CCLog to structured logger
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import { Command, createCli } from "@/lib/ccCLI";
|
||||
import { Ok } from "@/lib/thirdparty/ts-result-es";
|
||||
import { CCLog } from "@/lib/ccLog";
|
||||
import {
|
||||
AccessConfig,
|
||||
UserGroupConfig,
|
||||
@@ -8,12 +7,13 @@ import {
|
||||
saveConfig,
|
||||
} from "./config";
|
||||
import { parseBoolean } from "@/lib/common";
|
||||
import { Logger } from "@/lib/ccStructLog";
|
||||
|
||||
// 1. Define AppContext
|
||||
export interface AppContext {
|
||||
configFilepath: string;
|
||||
reloadConfig: () => void;
|
||||
logger: CCLog;
|
||||
logger: Logger;
|
||||
print: (
|
||||
message: string | MinecraftTextComponent | MinecraftTextComponent[],
|
||||
) => void;
|
||||
@@ -48,7 +48,9 @@ const addCommand: Command<AppContext> = {
|
||||
config.adminGroupConfig.groupUsers.push(playerName);
|
||||
}
|
||||
} else {
|
||||
const group = config.usersGroups.find((g) => g.groupName === groupName);
|
||||
const group = config.usersGroups.find(
|
||||
(g) => g.groupName === groupName,
|
||||
);
|
||||
if (!group) {
|
||||
const groupNames = getGroupNames(config);
|
||||
context.print({
|
||||
@@ -107,7 +109,9 @@ const delCommand: Command<AppContext> = {
|
||||
}
|
||||
|
||||
if (group.groupUsers !== undefined) {
|
||||
group.groupUsers = group.groupUsers.filter((user) => user !== playerName);
|
||||
group.groupUsers = group.groupUsers.filter(
|
||||
(user) => user !== playerName,
|
||||
);
|
||||
}
|
||||
|
||||
saveConfig(config, context.configFilepath);
|
||||
@@ -238,7 +242,10 @@ const configCommand: Command<AppContext> = {
|
||||
{ name: "value", description: "要设置的值", required: true },
|
||||
],
|
||||
action: ({ args, context }) => {
|
||||
const [option, valueStr] = [args.option as string, args.value as string];
|
||||
const [option, valueStr] = [
|
||||
args.option as string,
|
||||
args.value as string,
|
||||
];
|
||||
const config = loadConfig(context.configFilepath)!;
|
||||
|
||||
// Check if it's a group property (contains a dot)
|
||||
@@ -251,7 +258,9 @@ const configCommand: Command<AppContext> = {
|
||||
if (groupName === "admin") {
|
||||
groupConfig = config.adminGroupConfig;
|
||||
} else {
|
||||
groupConfig = config.usersGroups.find((g) => g.groupName === groupName);
|
||||
groupConfig = config.usersGroups.find(
|
||||
(g) => g.groupName === groupName,
|
||||
);
|
||||
}
|
||||
|
||||
if (!groupConfig) {
|
||||
@@ -321,7 +330,9 @@ const configCommand: Command<AppContext> = {
|
||||
const value = parseInt(valueStr);
|
||||
|
||||
if (isNaN(value)) {
|
||||
context.print({ text: `无效的值: ${valueStr}. 必须是一个数字。` });
|
||||
context.print({
|
||||
text: `无效的值: ${valueStr}. 必须是一个数字。`,
|
||||
});
|
||||
return Ok.EMPTY;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,12 @@ import {
|
||||
ConsoleStream,
|
||||
DAY,
|
||||
FileStream,
|
||||
Logger,
|
||||
getStructLogger,
|
||||
LoggerOptions,
|
||||
LogLevel,
|
||||
MB,
|
||||
processor,
|
||||
setStructLoggerConfig,
|
||||
textRenderer,
|
||||
} from "@/lib/ccStructLog";
|
||||
|
||||
@@ -22,7 +24,7 @@ const args = [...$vararg];
|
||||
|
||||
// Init Log
|
||||
let isOnConsoleStream = true;
|
||||
const logger = new Logger({
|
||||
const loggerConfig: LoggerOptions = {
|
||||
processors: [
|
||||
processor.filterByLevel(LogLevel.Info),
|
||||
processor.addTimestamp(),
|
||||
@@ -40,7 +42,9 @@ const logger = new Logger({
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
setStructLoggerConfig(loggerConfig);
|
||||
const logger = getStructLogger();
|
||||
|
||||
// Load Config
|
||||
const configFilepath = `${shell.dir()}/access.config.json`;
|
||||
@@ -79,6 +83,11 @@ function reloadConfig() {
|
||||
gWatchPlayersInfo = [];
|
||||
releaser.release();
|
||||
logger.info("Reload config successfully!");
|
||||
const tutorial: string[] = [];
|
||||
tutorial.push("Access Control System started.");
|
||||
tutorial.push("\tPress 'c' to open configuration TUI.");
|
||||
tutorial.push("\tPress 'r' to reload configuration.");
|
||||
print(tutorial.join("\n"));
|
||||
}
|
||||
|
||||
function safeParseTextComponent(
|
||||
|
||||
@@ -99,25 +99,31 @@ const AccessControlTUI = () => {
|
||||
|
||||
// Validate numbers
|
||||
if (
|
||||
validateNumber(currentConfig.detectInterval?.toString() ?? "") === null
|
||||
validateNumber(
|
||||
currentConfig.detectInterval?.toString() ?? "",
|
||||
) === null
|
||||
) {
|
||||
showError("Invalid Detect Interval: must be a number");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
validateNumber(currentConfig.watchInterval?.toString() ?? "") === null
|
||||
validateNumber(
|
||||
currentConfig.watchInterval?.toString() ?? "",
|
||||
) === null
|
||||
) {
|
||||
showError("Invalid Watch Interval: must be a number");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
validateNumber(currentConfig.noticeTimes?.toString() ?? "") === null
|
||||
validateNumber(currentConfig.noticeTimes?.toString() ?? "") ===
|
||||
null
|
||||
) {
|
||||
showError("Invalid Notice Times: must be a number");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
validateNumber(currentConfig.detectRange?.toString() ?? "") === null
|
||||
validateNumber(currentConfig.detectRange?.toString() ?? "") ===
|
||||
null
|
||||
) {
|
||||
showError("Invalid Detect Range: must be a number");
|
||||
return;
|
||||
@@ -153,7 +159,9 @@ const AccessControlTUI = () => {
|
||||
|
||||
for (const toastConfig of toastConfigs) {
|
||||
if (toastConfig.value != undefined) {
|
||||
const serialized = textutils.serialiseJSON(toastConfig.value);
|
||||
const serialized = textutils.serialiseJSON(
|
||||
toastConfig.value,
|
||||
);
|
||||
if (!validateTextComponent(serialized)) {
|
||||
showError(
|
||||
`Invalid ${toastConfig.name}: must be valid MinecraftTextComponent JSON`,
|
||||
@@ -210,7 +218,9 @@ const AccessControlTUI = () => {
|
||||
const currentAdmin = config().adminGroupConfig;
|
||||
setConfig("adminGroupConfig", {
|
||||
...currentAdmin,
|
||||
groupUsers: currentAdmin.groupUsers.filter((user) => user !== userName),
|
||||
groupUsers: currentAdmin.groupUsers.filter(
|
||||
(user) => user !== userName,
|
||||
),
|
||||
});
|
||||
} else {
|
||||
// Regular group
|
||||
@@ -268,7 +278,10 @@ const AccessControlTUI = () => {
|
||||
onFocusChanged: () => {
|
||||
const num = validateNumber(getDetectInterval());
|
||||
if (num !== null) setConfig("detectInterval", num);
|
||||
else setDetectInterval(config().detectInterval.toString());
|
||||
else
|
||||
setDetectInterval(
|
||||
config().detectInterval.toString(),
|
||||
);
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -282,7 +295,8 @@ const AccessControlTUI = () => {
|
||||
onFocusChanged: () => {
|
||||
const num = validateNumber(getWatchInterval());
|
||||
if (num !== null) setConfig("watchInterval", num);
|
||||
else setWatchInterval(config().watchInterval.toString());
|
||||
else
|
||||
setWatchInterval(config().watchInterval.toString());
|
||||
},
|
||||
}),
|
||||
),
|
||||
@@ -347,11 +361,15 @@ const AccessControlTUI = () => {
|
||||
div(
|
||||
{ class: "flex flex-col" },
|
||||
label({}, "Groups:"),
|
||||
For({ each: () => groups, class: "flex flex-col" }, (group, index) =>
|
||||
For(
|
||||
{ each: () => groups, class: "flex flex-col" },
|
||||
(group, index) =>
|
||||
button(
|
||||
{
|
||||
class:
|
||||
selectedGroupIndex() === index() ? "bg-blue text-white" : "",
|
||||
selectedGroupIndex() === index()
|
||||
? "bg-blue text-white"
|
||||
: "",
|
||||
onClick: () => setSelectedGroupIndex(index()),
|
||||
},
|
||||
group.groupName,
|
||||
@@ -489,7 +507,10 @@ const AccessControlTUI = () => {
|
||||
* Toast Configuration Tab Factory
|
||||
*/
|
||||
const createToastTab = (
|
||||
toastType: "welcomeToastConfig" | "warnToastConfig" | "noticeToastConfig",
|
||||
toastType:
|
||||
| "welcomeToastConfig"
|
||||
| "warnToastConfig"
|
||||
| "noticeToastConfig",
|
||||
) => {
|
||||
return () => {
|
||||
const toastConfig = config()[toastType];
|
||||
@@ -533,7 +554,9 @@ const AccessControlTUI = () => {
|
||||
} catch {
|
||||
setTempToastConfig({
|
||||
...getTempToastConfig(),
|
||||
title: textutils.serialiseJSON(currentToastConfig.title),
|
||||
title: textutils.serialiseJSON(
|
||||
currentToastConfig.title,
|
||||
),
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -545,7 +568,10 @@ const AccessControlTUI = () => {
|
||||
type: "text",
|
||||
value: () => getTempToastConfig().msg,
|
||||
onInput: (value) =>
|
||||
setTempToastConfig({ ...getTempToastConfig(), msg: value }),
|
||||
setTempToastConfig({
|
||||
...getTempToastConfig(),
|
||||
msg: value,
|
||||
}),
|
||||
onFocusChanged: () => {
|
||||
const currentToastConfig = config()[toastType];
|
||||
|
||||
@@ -566,7 +592,9 @@ const AccessControlTUI = () => {
|
||||
} catch {
|
||||
setTempToastConfig({
|
||||
...getTempToastConfig(),
|
||||
msg: textutils.serialiseJSON(currentToastConfig.msg),
|
||||
msg: textutils.serialiseJSON(
|
||||
currentToastConfig.msg,
|
||||
),
|
||||
});
|
||||
// Invalid JSON, ignore
|
||||
}
|
||||
@@ -579,13 +607,19 @@ const AccessControlTUI = () => {
|
||||
input({
|
||||
type: "text",
|
||||
value: () => {
|
||||
const str = textutils.serialiseJSON(getTempToastConfig().prefix, {
|
||||
const str = textutils.serialiseJSON(
|
||||
getTempToastConfig().prefix,
|
||||
{
|
||||
unicode_strings: true,
|
||||
});
|
||||
},
|
||||
);
|
||||
return str.substring(1, str.length - 1);
|
||||
},
|
||||
onInput: (value) =>
|
||||
setTempToastConfig({ ...getTempToastConfig(), prefix: value }),
|
||||
setTempToastConfig({
|
||||
...getTempToastConfig(),
|
||||
prefix: value,
|
||||
}),
|
||||
onFocusChanged: () => {
|
||||
const currentToastConfig = config()[toastType];
|
||||
setConfig(toastType, {
|
||||
@@ -603,7 +637,10 @@ const AccessControlTUI = () => {
|
||||
type: "text",
|
||||
value: () => getTempToastConfig().brackets,
|
||||
onInput: (value) =>
|
||||
setTempToastConfig({ ...getTempToastConfig(), brackets: value }),
|
||||
setTempToastConfig({
|
||||
...getTempToastConfig(),
|
||||
brackets: value,
|
||||
}),
|
||||
onFocusChanged: () => {
|
||||
const currentToastConfig = config()[toastType];
|
||||
setConfig(toastType, {
|
||||
@@ -678,7 +715,10 @@ const AccessControlTUI = () => {
|
||||
{ when: () => currentTab() === TABS.WELCOME_TOAST },
|
||||
WelcomeToastTab(),
|
||||
),
|
||||
Match({ when: () => currentTab() === TABS.WARN_TOAST }, WarnToastTab()),
|
||||
Match(
|
||||
{ when: () => currentTab() === TABS.WARN_TOAST },
|
||||
WarnToastTab(),
|
||||
),
|
||||
Match(
|
||||
{ when: () => currentTab() === TABS.NOTICE_TOAST },
|
||||
NoticeToastTab(),
|
||||
@@ -703,7 +743,10 @@ const AccessControlTUI = () => {
|
||||
For({ each: () => tabNames }, (tabName, index) =>
|
||||
button(
|
||||
{
|
||||
class: currentTab() === index() ? "bg-blue text-white" : "",
|
||||
class:
|
||||
currentTab() === index()
|
||||
? "bg-blue text-white"
|
||||
: "",
|
||||
onClick: () => setCurrentTab(index() as TabIndex),
|
||||
},
|
||||
tabName,
|
||||
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
import { Queue } from "@/lib/datatype/Queue";
|
||||
import {
|
||||
ConsoleStream,
|
||||
DAY,
|
||||
FileStream,
|
||||
Logger,
|
||||
LogLevel,
|
||||
processor,
|
||||
@@ -18,7 +20,17 @@ const logger = new Logger({
|
||||
processor.addTimestamp(),
|
||||
],
|
||||
renderer: textRenderer,
|
||||
streams: [new ConsoleStream()],
|
||||
streams: [
|
||||
new ConsoleStream(),
|
||||
new FileStream({
|
||||
filePath: "autocraft.log",
|
||||
rotationInterval: DAY,
|
||||
autoCleanup: {
|
||||
enabled: true,
|
||||
maxFiles: 3,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
const peripheralsNames = {
|
||||
@@ -47,7 +59,8 @@ enum State {
|
||||
}
|
||||
|
||||
function main() {
|
||||
while (true) {
|
||||
let isFinishedInitPeripheral = false;
|
||||
while (!isFinishedInitPeripheral) {
|
||||
try {
|
||||
packsInventory = peripheral.wrap(
|
||||
peripheralsNames.packsInventory,
|
||||
@@ -67,7 +80,7 @@ function main() {
|
||||
turtleLocalName = wiredModem.getNameLocal();
|
||||
|
||||
logger.info("Peripheral initialization complete...");
|
||||
break;
|
||||
isFinishedInitPeripheral = true;
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`Peripheral initialization failed for ${String(error)}, try again...`,
|
||||
|
||||
@@ -398,11 +398,17 @@ export class UIObject {
|
||||
|
||||
const newScrollX = Math.max(
|
||||
0,
|
||||
Math.min(this.scrollProps.maxScrollX, this.scrollProps.scrollX + deltaX),
|
||||
Math.min(
|
||||
this.scrollProps.maxScrollX,
|
||||
this.scrollProps.scrollX + deltaX,
|
||||
),
|
||||
);
|
||||
const newScrollY = Math.max(
|
||||
0,
|
||||
Math.min(this.scrollProps.maxScrollY, this.scrollProps.scrollY + deltaY),
|
||||
Math.min(
|
||||
this.scrollProps.maxScrollY,
|
||||
this.scrollProps.scrollY + deltaY,
|
||||
),
|
||||
);
|
||||
|
||||
this.scrollProps.scrollX = newScrollX;
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
import { UIObject } from "./UIObject";
|
||||
import { calculateLayout } from "./layout";
|
||||
import { render as renderTree, clearScreen } from "./renderer";
|
||||
import { CCLog, DAY, LogLevel } from "../ccLog";
|
||||
import { setLogger } from "./context";
|
||||
import { InputProps } from "./components";
|
||||
import { Setter } from "./reactivity";
|
||||
import { getStructLogger, Logger } from "@/lib/ccStructLog";
|
||||
import { setLogger } from "./context";
|
||||
|
||||
/**
|
||||
* Main application class
|
||||
@@ -21,7 +21,7 @@ export class Application {
|
||||
private focusedNode?: UIObject;
|
||||
private termWidth: number;
|
||||
private termHeight: number;
|
||||
private logger: CCLog;
|
||||
private logger: Logger;
|
||||
private cursorBlinkState = false;
|
||||
private lastBlinkTime = 0;
|
||||
private readonly BLINK_INTERVAL = 0.5; // seconds
|
||||
@@ -30,11 +30,7 @@ export class Application {
|
||||
const [width, height] = term.getSize();
|
||||
this.termWidth = width;
|
||||
this.termHeight = height;
|
||||
this.logger = new CCLog("tui_debug.log", {
|
||||
printTerminal: false,
|
||||
logInterval: DAY,
|
||||
outputMinLevel: LogLevel.Info,
|
||||
});
|
||||
this.logger = getStructLogger("ccTUI");
|
||||
setLogger(this.logger);
|
||||
this.logger.debug("Application constructed.");
|
||||
}
|
||||
@@ -99,7 +95,6 @@ export class Application {
|
||||
this.root.unmount();
|
||||
}
|
||||
|
||||
this.logger.close();
|
||||
clearScreen();
|
||||
}
|
||||
|
||||
@@ -224,8 +219,10 @@ export class Application {
|
||||
| undefined;
|
||||
if (type === "checkbox") {
|
||||
// Toggle checkbox
|
||||
const onChangeProp = (this.focusedNode.props as InputProps).onChange;
|
||||
const checkedProp = (this.focusedNode.props as InputProps).checked;
|
||||
const onChangeProp = (this.focusedNode.props as InputProps)
|
||||
.onChange;
|
||||
const checkedProp = (this.focusedNode.props as InputProps)
|
||||
.checked;
|
||||
|
||||
if (
|
||||
typeof onChangeProp === "function" &&
|
||||
@@ -260,7 +257,10 @@ export class Application {
|
||||
const valueProp = (this.focusedNode.props as InputProps).value;
|
||||
const onInputProp = (this.focusedNode.props as InputProps).onInput;
|
||||
|
||||
if (typeof valueProp !== "function" || typeof onInputProp !== "function") {
|
||||
if (
|
||||
typeof valueProp !== "function" ||
|
||||
typeof onInputProp !== "function"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -273,7 +273,10 @@ export class Application {
|
||||
this.needsRender = true;
|
||||
} else if (key === keys.right) {
|
||||
// Move cursor right
|
||||
this.focusedNode.cursorPos = math.min(currentValue.length, cursorPos + 1);
|
||||
this.focusedNode.cursorPos = math.min(
|
||||
currentValue.length,
|
||||
cursorPos + 1,
|
||||
);
|
||||
this.needsRender = true;
|
||||
} else if (key === keys.backspace) {
|
||||
// Delete character before cursor
|
||||
@@ -301,11 +304,15 @@ export class Application {
|
||||
* Handle character input events
|
||||
*/
|
||||
private handleCharEvent(char: string): void {
|
||||
if (this.focusedNode !== undefined && this.focusedNode.type === "input") {
|
||||
if (
|
||||
this.focusedNode !== undefined &&
|
||||
this.focusedNode.type === "input"
|
||||
) {
|
||||
const type = (this.focusedNode.props as InputProps).type;
|
||||
if (type !== "checkbox") {
|
||||
// Insert character at cursor position
|
||||
const onInputProp = (this.focusedNode.props as InputProps).onInput;
|
||||
const onInputProp = (this.focusedNode.props as InputProps)
|
||||
.onInput;
|
||||
const valueProp = (this.focusedNode.props as InputProps).value;
|
||||
|
||||
if (
|
||||
@@ -338,7 +345,10 @@ export class Application {
|
||||
|
||||
if (clicked !== undefined) {
|
||||
this.logger.debug(
|
||||
string.format("handleMouseClick: Found node of type %s.", clicked.type),
|
||||
string.format(
|
||||
"handleMouseClick: Found node of type %s.",
|
||||
clicked.type,
|
||||
),
|
||||
);
|
||||
// Set focus
|
||||
if (
|
||||
@@ -351,7 +361,8 @@ export class Application {
|
||||
}
|
||||
this.focusedNode = clicked;
|
||||
if (typeof clicked.props.onFocusChanged === "function") {
|
||||
const onFocusChanged = clicked.props.onFocusChanged as Setter<boolean>;
|
||||
const onFocusChanged = clicked.props
|
||||
.onFocusChanged as Setter<boolean>;
|
||||
onFocusChanged(true);
|
||||
}
|
||||
|
||||
@@ -375,11 +386,15 @@ export class Application {
|
||||
"handleMouseClick: onClick handler found, executing.",
|
||||
);
|
||||
(onClick as () => void)();
|
||||
this.logger.debug("handleMouseClick: onClick handler finished.");
|
||||
this.logger.debug(
|
||||
"handleMouseClick: onClick handler finished.",
|
||||
);
|
||||
this.needsRender = true;
|
||||
}
|
||||
} else if (clicked.type === "input") {
|
||||
const type = (clicked.props as InputProps).type as string | undefined;
|
||||
const type = (clicked.props as InputProps).type as
|
||||
| string
|
||||
| undefined;
|
||||
if (type === "checkbox") {
|
||||
const onChangeProp = (clicked.props as InputProps).onChange;
|
||||
const checkedProp = (clicked.props as InputProps).checked;
|
||||
@@ -397,7 +412,9 @@ export class Application {
|
||||
|
||||
this.needsRender = true;
|
||||
} else {
|
||||
this.logger.debug("handleMouseClick: No node found at click position.");
|
||||
this.logger.debug(
|
||||
"handleMouseClick: No node found at click position.",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,7 +449,9 @@ export class Application {
|
||||
);
|
||||
// Only return interactive elements
|
||||
if (node.type === "button" || node.type === "input") {
|
||||
this.logger.debug("findNodeAt: Node is interactive, returning.");
|
||||
this.logger.debug(
|
||||
"findNodeAt: Node is interactive, returning.",
|
||||
);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
@@ -502,7 +521,9 @@ export class Application {
|
||||
);
|
||||
// Only return scrollable elements
|
||||
if (node.type === "scroll-container") {
|
||||
this.logger.debug("findNodeAt: Node is scrollable, returning.");
|
||||
this.logger.debug(
|
||||
"findNodeAt: Node is scrollable, returning.",
|
||||
);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,12 +167,15 @@ export function label(
|
||||
const sentences = createMemo(() => {
|
||||
const words = splitByWhitespace(text());
|
||||
const ret = concatSentence(words, 40);
|
||||
context.logger?.debug(`label words changed : [ ${ret.join(",")} ]`);
|
||||
context.logger?.debug(
|
||||
`label words changed : [ ${ret.join(",")} ]`,
|
||||
);
|
||||
return ret;
|
||||
});
|
||||
|
||||
const forNode = For({ class: `flex flex-col`, each: sentences }, (word) =>
|
||||
label({ class: p.class }, word),
|
||||
const forNode = For(
|
||||
{ class: `flex flex-col`, each: sentences },
|
||||
(word) => label({ class: p.class }, word),
|
||||
);
|
||||
|
||||
return forNode;
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
* to all components without prop drilling.
|
||||
*/
|
||||
|
||||
import type { CCLog } from "../ccLog";
|
||||
import { Logger } from "@/lib/ccStructLog";
|
||||
|
||||
/**
|
||||
* The global context object for the TUI application.
|
||||
* This will be set by the Application instance on creation.
|
||||
*/
|
||||
export const context: { logger: CCLog | undefined } = {
|
||||
export const context: { logger: Logger | undefined } = {
|
||||
logger: undefined,
|
||||
};
|
||||
|
||||
@@ -18,6 +18,6 @@ export const context: { logger: CCLog | undefined } = {
|
||||
* Sets the global logger instance.
|
||||
* @param l The logger instance.
|
||||
*/
|
||||
export function setLogger(l: CCLog): void {
|
||||
export function setLogger(l: Logger): void {
|
||||
context.logger = l;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,10 @@ function measureNode(
|
||||
if (node.styleProps.width === "screen") {
|
||||
const termSize = getTerminalSize();
|
||||
measuredWidth = termSize.width;
|
||||
} else if (node.styleProps.width === "full" && parentWidth !== undefined) {
|
||||
} else if (
|
||||
node.styleProps.width === "full" &&
|
||||
parentWidth !== undefined
|
||||
) {
|
||||
measuredWidth = parentWidth;
|
||||
} else if (typeof node.styleProps.width === "number") {
|
||||
measuredWidth = node.styleProps.width;
|
||||
@@ -297,7 +300,13 @@ export function calculateLayout(
|
||||
const childY = startY + scrollOffsetY;
|
||||
|
||||
// Recursively calculate layout for child with its natural size
|
||||
calculateLayout(child, childSize.width, childSize.height, childX, childY);
|
||||
calculateLayout(
|
||||
child,
|
||||
childSize.width,
|
||||
childSize.height,
|
||||
childX,
|
||||
childY,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -368,7 +377,8 @@ export function calculateLayout(
|
||||
|
||||
// Cross axis (vertical) alignment
|
||||
if (align === "center") {
|
||||
childY = startY + math.floor((availableHeight - measure.height) / 2);
|
||||
childY =
|
||||
startY + math.floor((availableHeight - measure.height) / 2);
|
||||
} else if (align === "end") {
|
||||
childY = startY + (availableHeight - measure.height);
|
||||
} else {
|
||||
@@ -385,7 +395,8 @@ export function calculateLayout(
|
||||
|
||||
// Cross axis (horizontal) alignment
|
||||
if (align === "center") {
|
||||
childX = startX + math.floor((availableWidth - measure.width) / 2);
|
||||
childX =
|
||||
startX + math.floor((availableWidth - measure.width) / 2);
|
||||
} else if (align === "end") {
|
||||
childX = startX + (availableWidth - measure.width);
|
||||
} else {
|
||||
|
||||
@@ -75,10 +75,10 @@ export function createSignal<T>(initialValue: T): Signal<T> {
|
||||
// Notify all subscribed listeners
|
||||
if (batchDepth > 0) {
|
||||
// In batch mode, collect effects to run later
|
||||
listeners.forEach(listener => pendingEffects.add(listener));
|
||||
listeners.forEach((listener) => pendingEffects.add(listener));
|
||||
} else {
|
||||
// Run effects immediately
|
||||
listeners.forEach(listener => {
|
||||
listeners.forEach((listener) => {
|
||||
try {
|
||||
listener();
|
||||
} catch (e) {
|
||||
@@ -152,7 +152,7 @@ export function batch(fn: () => void): void {
|
||||
const effects = Array.from(pendingEffects);
|
||||
pendingEffects.clear();
|
||||
|
||||
effects.forEach(effect => {
|
||||
effects.forEach((effect) => {
|
||||
try {
|
||||
effect();
|
||||
} catch (e) {
|
||||
|
||||
@@ -18,7 +18,10 @@ function getTextContent(node: UIObject): string {
|
||||
}
|
||||
|
||||
// For nodes with text children, get their content
|
||||
if (node.children.length > 0 && node.children[0].textContent !== undefined) {
|
||||
if (
|
||||
node.children.length > 0 &&
|
||||
node.children[0].textContent !== undefined
|
||||
) {
|
||||
const child = node.children[0];
|
||||
if (typeof child.textContent === "function") {
|
||||
return child.textContent();
|
||||
@@ -39,7 +42,11 @@ function isPositionVisible(
|
||||
): boolean {
|
||||
let current = node.parent;
|
||||
while (current) {
|
||||
if (isScrollContainer(current) && current.layout && current.scrollProps) {
|
||||
if (
|
||||
isScrollContainer(current) &&
|
||||
current.layout &&
|
||||
current.scrollProps
|
||||
) {
|
||||
const { x: containerX, y: containerY } = current.layout;
|
||||
const { viewportWidth, viewportHeight } = current.scrollProps;
|
||||
|
||||
@@ -189,7 +196,9 @@ function drawNode(
|
||||
}
|
||||
|
||||
case "input": {
|
||||
const type = (node.props as InputProps).type as string | undefined;
|
||||
const type = (node.props as InputProps).type as
|
||||
| string
|
||||
| undefined;
|
||||
|
||||
if (type === "checkbox") {
|
||||
// Draw checkbox
|
||||
@@ -224,7 +233,11 @@ function drawNode(
|
||||
const focusedBgColor = bgColor ?? colors.white;
|
||||
const unfocusedBgColor = bgColor ?? colors.black;
|
||||
|
||||
if (displayText === "" && placeholder !== undefined && !focused) {
|
||||
if (
|
||||
displayText === "" &&
|
||||
placeholder !== undefined &&
|
||||
!focused
|
||||
) {
|
||||
displayText = placeholder;
|
||||
showPlaceholder = true;
|
||||
currentTextColor = currentTextColor ?? colors.gray;
|
||||
@@ -235,7 +248,9 @@ function drawNode(
|
||||
}
|
||||
|
||||
// Set background and clear the input area, creating a 1-character padding on the left
|
||||
term.setBackgroundColor(focused ? focusedBgColor : unfocusedBgColor);
|
||||
term.setBackgroundColor(
|
||||
focused ? focusedBgColor : unfocusedBgColor,
|
||||
);
|
||||
term.setCursorPos(x, y);
|
||||
term.write(" ".repeat(width));
|
||||
|
||||
@@ -247,7 +262,9 @@ function drawNode(
|
||||
|
||||
// Move text if it's too long for the padded area
|
||||
const startDisPos =
|
||||
cursorPos >= renderWidth ? cursorPos - renderWidth + 1 : 0;
|
||||
cursorPos >= renderWidth
|
||||
? cursorPos - renderWidth + 1
|
||||
: 0;
|
||||
const stopDisPos = startDisPos + renderWidth;
|
||||
|
||||
if (focused && !showPlaceholder && cursorBlinkState) {
|
||||
@@ -271,7 +288,10 @@ function drawNode(
|
||||
}
|
||||
}
|
||||
// Draw cursor at the end of the text if applicable
|
||||
if (cursorPos === textToRender.length && cursorPos < renderWidth) {
|
||||
if (
|
||||
cursorPos === textToRender.length &&
|
||||
cursorPos < renderWidth
|
||||
) {
|
||||
term.setBackgroundColor(currentTextColor);
|
||||
term.setTextColor(focusedBgColor);
|
||||
term.write(" ");
|
||||
@@ -281,7 +301,9 @@ function drawNode(
|
||||
}
|
||||
} else {
|
||||
// Not focused or no cursor, just write the text
|
||||
term.write(textToRender.substring(startDisPos, stopDisPos));
|
||||
term.write(
|
||||
textToRender.substring(startDisPos, stopDisPos),
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -45,7 +45,9 @@ export interface SetStoreFunction<T> {
|
||||
* setTodos([{ title: "First", done: false }]);
|
||||
* ```
|
||||
*/
|
||||
export function createStore<T extends object>(initialValue: T): [Accessor<T>, SetStoreFunction<T>] {
|
||||
export function createStore<T extends object>(
|
||||
initialValue: T,
|
||||
): [Accessor<T>, SetStoreFunction<T>] {
|
||||
// Use a signal to track the entire state
|
||||
const [get, set] = createSignal(initialValue);
|
||||
|
||||
@@ -88,8 +90,11 @@ export function createStore<T extends object>(initialValue: T): [Accessor<T>, Se
|
||||
|
||||
if (Array.isArray(current)) {
|
||||
const newArray = [...current] as unknown[];
|
||||
if (typeof newArray[index] === "object" && newArray[index] !== undefined) {
|
||||
newArray[index] = { ...(newArray[index]!), [key]: value };
|
||||
if (
|
||||
typeof newArray[index] === "object" &&
|
||||
newArray[index] !== undefined
|
||||
) {
|
||||
newArray[index] = { ...newArray[index]!, [key]: value };
|
||||
}
|
||||
set(newArray as T);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user