ink starting
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "tsx src/index.ts"
|
||||
"start": "tsx src/index.tsx"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@@ -14,6 +14,8 @@
|
||||
"@biomejs/biome": "1.7.0",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.12.7",
|
||||
"@types/react": "^18.3.12",
|
||||
"boxen": "^8.0.1",
|
||||
"cli-spinners": "^3.0.0",
|
||||
"cli-table3": "^0.6.5",
|
||||
"date-fns": "^3.6.0",
|
||||
@@ -22,8 +24,10 @@
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-sheriff": "^18.2.0",
|
||||
"eslint-define-config": "^2.1.0",
|
||||
"ink": "^5.0.1",
|
||||
"ky": "^1.2.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"react": "^18.3.1",
|
||||
"tinyrainbow": "^1.1.1",
|
||||
"tsx": "4.7.2",
|
||||
"typescript": "5.4.5",
|
||||
|
||||
1527
pnpm-lock.yaml
generated
1527
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,82 +0,0 @@
|
||||
import * as w from "wretched";
|
||||
import Table from "cli-table3";
|
||||
import {
|
||||
getOneInvoice,
|
||||
markInvoicePaid,
|
||||
updateInvoice,
|
||||
} from "../services/pancakeApi.js";
|
||||
import { doubleBoxWithTitle } from "./helpers/boxBorders.js";
|
||||
import { isNaN, omit, values } from "lodash-es";
|
||||
import { fromUnixTime, format } from "date-fns";
|
||||
import { InvoiceDetailsResponse } from "../services/invoiceResponse.js";
|
||||
|
||||
export const renderInvoiceDetails = async (invoiceId: string | number) => {
|
||||
const { invoice } = await getOneInvoice(invoiceId.toString());
|
||||
if (process.env.DEBUG) {
|
||||
console.log("🚀 ~ renderInvoiceDetails ~ details:", invoice);
|
||||
}
|
||||
const mainWindow = new w.Scrollable({
|
||||
children: [],
|
||||
});
|
||||
const mainTable = new Table();
|
||||
for (const key in omit(invoice, "items", "partial_payments")) {
|
||||
const rawValue = invoice[key as keyof InvoiceDetailsResponse];
|
||||
if (typeof rawValue !== "object") {
|
||||
let value: Table.Cell = rawValue;
|
||||
if (typeof rawValue !== "boolean") {
|
||||
if (key.indexOf("date") >= 0) {
|
||||
value = Number(rawValue);
|
||||
if (!isNaN(value)) {
|
||||
value = format(fromUnixTime(value), "y-M-d");
|
||||
}
|
||||
}
|
||||
}
|
||||
mainTable.push({
|
||||
[key]: value,
|
||||
} as Table.VerticalTableRow);
|
||||
}
|
||||
}
|
||||
mainWindow.addAll([
|
||||
new w.Flow({
|
||||
direction: "topToBottom",
|
||||
children: [
|
||||
new w.Text({ text: `Unpaid: $${invoice.unpaid_amount}` }),
|
||||
new w.Button({
|
||||
text: `Mark as ${invoice.unpaid_amount > 0 ? "Paid" : "Unpaid"}`,
|
||||
onClick: async () => {
|
||||
const isPaid = invoice.unpaid_amount > 0;
|
||||
if (process.env.DEBUG) {
|
||||
console.log("Switching paid status from", {
|
||||
isPaid,
|
||||
invoiceId,
|
||||
});
|
||||
}
|
||||
if (isPaid) {
|
||||
mainWindow.removeAllChildren();
|
||||
mainWindow.addAll([
|
||||
new w.Flow({
|
||||
direction: "leftToRight",
|
||||
children: [
|
||||
new w.Text({ text: "Paid Amount" }),
|
||||
new w.Input({ text: "Paid Amount" }),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
} else {
|
||||
if (!process.env.DEBUG) {
|
||||
await markInvoicePaid(invoiceId);
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
new w.Text({ text: mainTable.toString() }),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
return new w.Box({
|
||||
border: doubleBoxWithTitle(
|
||||
`${invoice.client_name} 💸 Invoice: ${invoice.id}`,
|
||||
),
|
||||
child: mainWindow,
|
||||
});
|
||||
};
|
||||
93
src/components/detailsInvoice.tsx
Normal file
93
src/components/detailsInvoice.tsx
Normal file
@@ -0,0 +1,93 @@
|
||||
import React, {useState, useEffect} from "react";
|
||||
import { Text, Box, u } from "ink";
|
||||
import Table from "cli-table3";
|
||||
import {
|
||||
getOneInvoice,
|
||||
markInvoicePaid,
|
||||
updateInvoice,
|
||||
} from "../services/pancakeApi.js";
|
||||
|
||||
import { isNaN, omit, values } from "lodash-es";
|
||||
import { fromUnixTime, format } from "date-fns";
|
||||
import { InvoiceDetailsResponse } from "../services/invoiceResponse.js";
|
||||
|
||||
|
||||
|
||||
export const InvoiceDetails = (props: { invoiceId: string | number }) => {
|
||||
const mainTable = new Table();
|
||||
const [ invoice, setInvoice ] = useState<InvoiceDetailsResponse | undefined>();
|
||||
useEffect(() => {
|
||||
getOneInvoice(props.invoiceId.toString()).then(({ status, invoice }) => {
|
||||
if (process.env.DEBUG) {
|
||||
console.log("🚀 ~ renderInvoiceDetails ~ details:", invoice);
|
||||
}
|
||||
setInvoice(invoice);
|
||||
});
|
||||
}, [props.invoiceId]);
|
||||
if (invoice) {
|
||||
|
||||
for (const key in omit(invoice, "items", "partial_payments")) {
|
||||
const rawValue = invoice[key as keyof InvoiceDetailsResponse];
|
||||
if (typeof rawValue !== "object") {
|
||||
let value: Table.Cell = rawValue;
|
||||
if (typeof rawValue !== "boolean") {
|
||||
if (key.indexOf("date") >= 0) {
|
||||
value = Number(rawValue);
|
||||
if (!isNaN(value)) {
|
||||
value = format(fromUnixTime(value), "y-M-d");
|
||||
}
|
||||
}
|
||||
}
|
||||
mainTable.push({
|
||||
[key]: value,
|
||||
} as Table.VerticalTableRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// mainWindow.addAll([
|
||||
// new w.Flow({
|
||||
// direction: "topToBottom",
|
||||
// children: [
|
||||
// new w.Text({ text: `Unpaid: $${invoice.unpaid_amount}` }),
|
||||
// new w.Button({
|
||||
// text: `Mark as ${invoice.unpaid_amount > 0 ? "Paid" : "Unpaid"}`,
|
||||
// onClick: async () => {
|
||||
// const isPaid = invoice.unpaid_amount > 0;
|
||||
// if (process.env.DEBUG) {
|
||||
// console.log("Switching paid status from", {
|
||||
// isPaid,
|
||||
// invoiceId,
|
||||
// });
|
||||
// }
|
||||
// if (isPaid) {
|
||||
// mainWindow.removeAllChildren();
|
||||
// mainWindow.addAll([
|
||||
// new w.Flow({
|
||||
// direction: "leftToRight",
|
||||
// children: [
|
||||
// new w.Text({ text: "Paid Amount" }),
|
||||
// new w.Input({ text: "Paid Amount" }),
|
||||
// ],
|
||||
// }),
|
||||
// ]);
|
||||
// } else {
|
||||
// if (!process.env.DEBUG) {
|
||||
// await markInvoicePaid(invoiceId);
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// }),
|
||||
// new w.Text({ text: mainTable.toString() }),
|
||||
// ],
|
||||
// }),
|
||||
// ]);
|
||||
return <Box flexDirection="column">
|
||||
<Text>{invoice.client_name} 💸 Invoice: ${invoice.id}</Text>
|
||||
<Text>{mainTable.toString()}</Text>
|
||||
</Box>
|
||||
|
||||
|
||||
};
|
||||
16
src/components/layoutHorizontal.ts
Normal file
16
src/components/layoutHorizontal.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import boxen from "boxen";
|
||||
|
||||
export default function (width?: number) {
|
||||
return [
|
||||
boxen("child 1", {
|
||||
borderColor: "red",
|
||||
width,
|
||||
float: "left",
|
||||
}),
|
||||
boxen("child 2", {
|
||||
borderColor: "magentaBright",
|
||||
width,
|
||||
float: "right",
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -1,12 +1,5 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Flex,
|
||||
Screen,
|
||||
ScrollableList,
|
||||
Text,
|
||||
} from "wretched";
|
||||
import React from "react";
|
||||
import { Box, Text } from "ink";
|
||||
import {
|
||||
ClientResponse,
|
||||
InvoiceResponse,
|
||||
@@ -17,7 +10,7 @@ import {
|
||||
import spinners from "cli-spinners";
|
||||
import { compact, find, isNil, isString, reverse, sortBy } from "lodash-es";
|
||||
import { renderInvoiceList } from "./listInvoices.js";
|
||||
import { renderInvoiceDetails } from "./detailsInvoice.js";
|
||||
import { InvoiceDetails } from "./detailsInvoice.js";
|
||||
import { doubleBoxWithTitle } from "./helpers/boxBorders.js";
|
||||
import { AnimatedText } from "./helpers/animatedText.js";
|
||||
|
||||
@@ -50,55 +43,29 @@ const renderInvoicesBox = async (
|
||||
selectedClientId?: string,
|
||||
selectedInvoiceId?: string,
|
||||
) => {
|
||||
let InvoicesBox = new Box({ child: new Text({ text: "« Select a Client" }) });
|
||||
let InvoicesBox = <Box><Text>« Select a Client</Text></Box>;
|
||||
|
||||
if (selectedInvoiceId && selectedClientId) {
|
||||
InvoicesBox = await renderInvoiceDetails(selectedInvoiceId);
|
||||
InvoicesBox = <InvoiceDetails id={selectedInvoiceId}></InvoiceDetails>;
|
||||
} else if (selectedClientId) {
|
||||
const currClient = find(sorted, { id: selectedClientId });
|
||||
const currentClientName = `${currClient?.company} | ${currClient?.first_name} ${currClient?.last_name}`;
|
||||
InvoicesBox = new Box({
|
||||
width: "fill",
|
||||
height: "fill",
|
||||
child: await renderInvoiceList(selectedClientId, (id) => {
|
||||
InvoicesBox = await renderInvoiceList(selectedClientId, (id) => {
|
||||
console.log("clicked invoice", id);
|
||||
reDrawMain(selectedClientId, id.toString());
|
||||
}),
|
||||
border: doubleBoxWithTitle(currentClientName),
|
||||
});
|
||||
});
|
||||
}
|
||||
return InvoicesBox;
|
||||
};
|
||||
|
||||
let Clients = new ScrollableList({
|
||||
minWidth: 20,
|
||||
width: "natural",
|
||||
cellForItem: (client: ClientResponse) => {
|
||||
const cellForItem = (client: ClientResponse) => {
|
||||
if (!client) {
|
||||
// console.log("empty client company name", item);
|
||||
return new Text({ text: `Undefined Client` });
|
||||
return <text>(Undefined Client)</text>
|
||||
}
|
||||
return new Button({
|
||||
border: "none",
|
||||
width: "natural",
|
||||
text: `${client.company}`,
|
||||
onClick: async () => {
|
||||
console.log("🚀 ~ clientInvoicesView ~ clicked client:", client);
|
||||
reDrawMain(client.id);
|
||||
},
|
||||
});
|
||||
},
|
||||
items: sorted,
|
||||
});
|
||||
return <Text>{client.company}</Text>
|
||||
};
|
||||
|
||||
export const Clients = <Box minWidth={20} width={"100%"}>{sorted.map(cellForItem)}</Box>
|
||||
|
||||
|
||||
const renderRightPanel = (box: Box) =>
|
||||
Flex.down({
|
||||
width: "fill",
|
||||
height: "fill",
|
||||
padding: { left: 1, right: 1 },
|
||||
children: [box],
|
||||
});
|
||||
|
||||
export const clientInvoicesView = Flex.right({
|
||||
children: [Clients, renderRightPanel(await renderInvoicesBox())],
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { Box, Button, Flex, Text } from "wretched";
|
||||
import { Style } from "wretched/dist/Style.js";
|
||||
const tabs = {
|
||||
Home: "/",
|
||||
Clients: "/",
|
||||
Invoices: "/",
|
||||
Projects: "/",
|
||||
} as const;
|
||||
export const mainNav = (props: { activeTabName: keyof typeof tabs }) => {
|
||||
return new Flex({
|
||||
direction: "leftToRight",
|
||||
children: Object.keys(tabs).map(
|
||||
(x) =>
|
||||
new Box({
|
||||
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
||||
// child: new Text({ text: x, style: new Style({ bold: true }) }),
|
||||
child: new Button({
|
||||
text: x,
|
||||
border: "none",
|
||||
theme: props.activeTabName === x ? "plain" : "selected",
|
||||
onClick: () => {
|
||||
console.log({ clicked: x });
|
||||
},
|
||||
}),
|
||||
border: [
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
props.activeTabName === x ? "━" : "─",
|
||||
"",
|
||||
],
|
||||
}),
|
||||
),
|
||||
});
|
||||
};
|
||||
21
src/components/mainNav.tsx
Normal file
21
src/components/mainNav.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import { Box, Text } from "ink";
|
||||
|
||||
const tabs = {
|
||||
Home: "/",
|
||||
Clients: "/",
|
||||
Invoices: "/",
|
||||
Projects: "/",
|
||||
} as const;
|
||||
export const MainNav = (props: { activeTabName: keyof typeof tabs }) => {
|
||||
const tabBoxes = Object.keys(tabs).map(x => props.activeTabName === x ?
|
||||
<Text backgroundColor={"blueBright"} bold>[ <Text color={"red"}>({x.charAt(0)})</Text> {`${x}♦`} ]</Text>
|
||||
:<Text backgroundColor={"blue"} >[ <Text color={"red"}>({x.charAt(0)})</Text>{` ${x} `} ]</Text>);
|
||||
|
||||
return <Box width="100%" flexDirection="row" columnGap={0}>
|
||||
<Text backgroundColor={"blueBright"} bold>[ <Text color={"red"}>(h)</Text>Home ]</Text>
|
||||
<Text backgroundColor={"blue"} >[ <Text color={"red"}>(c)</Text> Clients ]</Text>
|
||||
<Text backgroundColor={"blue"} >[ <Text color={"red"}>(p)</Text> Projects ]</Text>
|
||||
<Text backgroundColor={"blue"} >[ <Text color={"red"}>(i)</Text> Invoices ]</Text>
|
||||
</Box>;
|
||||
};
|
||||
47
src/index.ts
47
src/index.ts
@@ -1,47 +0,0 @@
|
||||
import "dotenv/config";
|
||||
import {
|
||||
Screen,
|
||||
Box,
|
||||
Flow,
|
||||
Text,
|
||||
Button,
|
||||
interceptConsoleLog,
|
||||
ConsoleLog,
|
||||
iTerm2,
|
||||
Window,
|
||||
Flex,
|
||||
} from "wretched";
|
||||
import * as utility from "wretched/dist/components/utility";
|
||||
import { clientInvoicesView } from "./components/listClients.js";
|
||||
import { mainNav } from "./components/mainNav.js";
|
||||
import { inspect } from "node:util";
|
||||
|
||||
interceptConsoleLog();
|
||||
process.title = "Wretched";
|
||||
|
||||
const consoleLog = new ConsoleLog({
|
||||
height: 12,
|
||||
});
|
||||
const [screen, program] = await Screen.start(async (program) => {
|
||||
await iTerm2.setBackground(program, [23, 23, 23]);
|
||||
|
||||
return new Window({
|
||||
child: Flex.down({
|
||||
padding: { top: 1 },
|
||||
children: [
|
||||
["natural", mainNav({ activeTabName: "Clients" })],
|
||||
["flex1", clientInvoicesView],
|
||||
["natural", consoleLog],
|
||||
],
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
program.key("escape", function () {
|
||||
consoleLog.clear();
|
||||
screen.render();
|
||||
});
|
||||
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error("\r\nUncaught Exception was Caught!", inspect(error));
|
||||
});
|
||||
95
src/index.tsx
Normal file
95
src/index.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import React from "react";
|
||||
import "dotenv/config";
|
||||
import { interceptConsoleLog, ConsoleLog, iTerm2 } from "wretched";
|
||||
|
||||
import { clientInvoicesView } from "./components/listClients.js";
|
||||
import { MainNav } from "./components/mainNav.js";
|
||||
import { inspect } from "node:util";
|
||||
import { emitKeypressEvents } from "node:readline";
|
||||
import { render, Box, Text, Newline } from "ink";
|
||||
|
||||
const currentGlobalState = { count: 0, logs: [""], };
|
||||
const activeTabName: "Home" | "Clients" = "Home";
|
||||
function log(message: unknown) {
|
||||
currentGlobalState.logs.push(inspect(message));
|
||||
};
|
||||
|
||||
const Main = () =>
|
||||
<Box width="100%" flexDirection="column" alignItems="stretch">
|
||||
<Box width="100%" columnGap={2}>
|
||||
<Text backgroundColor={"blackBright"}>PANCAKE-TUI 🮥 </Text><MainNav activeTabName={activeTabName}/>
|
||||
</Box>
|
||||
<Box width="100%"
|
||||
flexDirection="row" justifyContent="center" alignItems="stretch">
|
||||
<Box width="25%" borderColor="greenBright" borderStyle={{
|
||||
top: "─",
|
||||
topRight: "┬",
|
||||
right: "│",
|
||||
bottomRight: "┴",
|
||||
bottom: "─",
|
||||
bottomLeft: "└",
|
||||
left: "│",
|
||||
topLeft: "┌"
|
||||
}}>
|
||||
<Text bold underline>Left Sidebar</Text><Newline/>
|
||||
|
||||
</Box>
|
||||
<Box flexGrow={1} borderStyle="single" borderColor="greenBright" borderLeft={false}>
|
||||
<Text bold underline>Main Area</Text><Newline/>
|
||||
<Text>Hello World {currentGlobalState.count}</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
<Text backgroundColor={"gray"}>🔔 Status[{currentGlobalState.logs.pop()}]</Text>
|
||||
</Box>;
|
||||
|
||||
|
||||
|
||||
function renderState() {
|
||||
currentGlobalState.count += 1;
|
||||
render(<Main />);
|
||||
}
|
||||
function renderRaw() {
|
||||
console.clear();
|
||||
// process.stdout.write(renderState());
|
||||
}
|
||||
|
||||
interceptConsoleLog();
|
||||
process.title = "TUI demo";
|
||||
|
||||
|
||||
// await iTerm2.setBackground(program, [23, 23, 23]);
|
||||
|
||||
// child: Flex.down({
|
||||
// padding: { top: 1 },
|
||||
// children: [
|
||||
// ["natural", mainNav({ activeTabName: "Clients" })],
|
||||
// ["flex1", clientInvoicesView],
|
||||
// ["natural", consoleLog],
|
||||
// ],
|
||||
// }),
|
||||
// });
|
||||
// });
|
||||
|
||||
emitKeypressEvents(process.stdin);
|
||||
process.stdin.setRawMode(true);
|
||||
|
||||
process.stdin.on("keypress", (str, key) => {
|
||||
if (key.ctrl && key.name === "c") {
|
||||
process.exit();
|
||||
} else if (key.name === "escape") {
|
||||
currentGlobalState.logs = [""];
|
||||
renderState();
|
||||
} else {
|
||||
log(`You pressed ${key.name}`);
|
||||
}
|
||||
});
|
||||
|
||||
renderState();
|
||||
log("Press any key, or Ctrl+C to exit.");
|
||||
// program.key("escape", function () {
|
||||
// consoleLog.clear();
|
||||
// screen.render();
|
||||
// });
|
||||
process.on("uncaughtException", (error) => {
|
||||
console.error("\r\nUncaught Exception was Caught!", inspect(error));
|
||||
});
|
||||
17
src/lib/cols.ts
Normal file
17
src/lib/cols.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export const terminalColumns = () => {
|
||||
const { env, stdout, stderr } = process;
|
||||
|
||||
if (stdout?.columns) {
|
||||
return stdout.columns;
|
||||
}
|
||||
|
||||
if (stderr?.columns) {
|
||||
return stderr.columns;
|
||||
}
|
||||
|
||||
if (env.COLUMNS) {
|
||||
return Number.parseInt(env.COLUMNS, 10);
|
||||
}
|
||||
|
||||
return 80;
|
||||
};
|
||||
53
src/lib/merge.ts
Normal file
53
src/lib/merge.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
export function xorMergeStrings(strings: string[]) {
|
||||
// Split the input strings into arrays of lines
|
||||
const [str1, str2] = strings;
|
||||
const lines1 = str1.split("\n");
|
||||
console.log("🚀 ~ xorMergeStrings ~ lines1:", lines1);
|
||||
const lines2 = str2.split("\n");
|
||||
console.log("🚀 ~ xorMergeStrings ~ lines2:", lines2);
|
||||
|
||||
// Determine the maximum number of lines
|
||||
const maxLines = Math.max(lines1.length, lines2.length);
|
||||
|
||||
// Initialize an array to hold the merged lines
|
||||
const mergedLines = [];
|
||||
|
||||
// Iterate through each line index up to the maximum number of lines
|
||||
for (let i = 0; i < maxLines; i++) {
|
||||
// Get the current line from each string, or an empty string if the line does not exist
|
||||
const maxLength = Math.max(lines1[i].length, lines2[i].length);
|
||||
const line1 = lines1[i].padEnd(maxLength, " ");
|
||||
console.log("🚀 ~ xorMergeStrings ~ line1: \n", line1);
|
||||
const line2 = lines2[i].padEnd(maxLength, " ");
|
||||
console.log("🚀 ~ xorMergeStrings ~ line2: \n", line2);
|
||||
|
||||
// Determine the maximum length of the current lines
|
||||
console.log("🚀 ~ xorMergeStrings ~ maxLength:", maxLength);
|
||||
|
||||
// Initialize an array to hold the merged characters for the current line
|
||||
const mergedLine = [];
|
||||
|
||||
// Iterate through each character index up to the maximum length
|
||||
for (let j = 0; j < maxLength; j++) {
|
||||
// Get the current character from each line, or a space if the character does not exist
|
||||
const char1 = line1[j];
|
||||
const char2 = line2[j];
|
||||
|
||||
// Determine if each character is a whitespace character
|
||||
const isWhitespace1 = char1.trim() === "";
|
||||
const isWhitespace2 = char2.trim() === "";
|
||||
|
||||
// XOR merge the characters based on the whitespace criteria
|
||||
const mergedChar = isWhitespace1 ? char2 : char1;
|
||||
|
||||
// Add the merged character to the merged line
|
||||
mergedLine.push(mergedChar);
|
||||
}
|
||||
|
||||
// Join the merged line array into a string and add it to the merged lines array
|
||||
mergedLines.push(mergedLine.join(""));
|
||||
}
|
||||
|
||||
// Join the merged lines array into a single string with newline characters
|
||||
return mergedLines.join("\n");
|
||||
}
|
||||
@@ -4,18 +4,19 @@
|
||||
"target": "es2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": false,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"incremental": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"tsBuildInfoFile": null
|
||||
"tsBuildInfoFile": null,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts"
|
||||
],
|
||||
, "src/index.tsx", "src/components/mainNav.tsx", "src/components/detailsInvoice.tsx", "src/components/listClients.tsx" ],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/mocks/**/*",
|
||||
|
||||
Reference in New Issue
Block a user