ink starting
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "tsx src/index.ts"
|
"start": "tsx src/index.tsx"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
@@ -14,6 +14,8 @@
|
|||||||
"@biomejs/biome": "1.7.0",
|
"@biomejs/biome": "1.7.0",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/node": "^20.12.7",
|
"@types/node": "^20.12.7",
|
||||||
|
"@types/react": "^18.3.12",
|
||||||
|
"boxen": "^8.0.1",
|
||||||
"cli-spinners": "^3.0.0",
|
"cli-spinners": "^3.0.0",
|
||||||
"cli-table3": "^0.6.5",
|
"cli-table3": "^0.6.5",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
@@ -22,8 +24,10 @@
|
|||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-sheriff": "^18.2.0",
|
"eslint-config-sheriff": "^18.2.0",
|
||||||
"eslint-define-config": "^2.1.0",
|
"eslint-define-config": "^2.1.0",
|
||||||
|
"ink": "^5.0.1",
|
||||||
"ky": "^1.2.3",
|
"ky": "^1.2.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"react": "^18.3.1",
|
||||||
"tinyrainbow": "^1.1.1",
|
"tinyrainbow": "^1.1.1",
|
||||||
"tsx": "4.7.2",
|
"tsx": "4.7.2",
|
||||||
"typescript": "5.4.5",
|
"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 {
|
import React from "react";
|
||||||
Box,
|
import { Box, Text } from "ink";
|
||||||
Button,
|
|
||||||
Container,
|
|
||||||
Flex,
|
|
||||||
Screen,
|
|
||||||
ScrollableList,
|
|
||||||
Text,
|
|
||||||
} from "wretched";
|
|
||||||
import {
|
import {
|
||||||
ClientResponse,
|
ClientResponse,
|
||||||
InvoiceResponse,
|
InvoiceResponse,
|
||||||
@@ -17,7 +10,7 @@ import {
|
|||||||
import spinners from "cli-spinners";
|
import spinners from "cli-spinners";
|
||||||
import { compact, find, isNil, isString, reverse, sortBy } from "lodash-es";
|
import { compact, find, isNil, isString, reverse, sortBy } from "lodash-es";
|
||||||
import { renderInvoiceList } from "./listInvoices.js";
|
import { renderInvoiceList } from "./listInvoices.js";
|
||||||
import { renderInvoiceDetails } from "./detailsInvoice.js";
|
import { InvoiceDetails } from "./detailsInvoice.js";
|
||||||
import { doubleBoxWithTitle } from "./helpers/boxBorders.js";
|
import { doubleBoxWithTitle } from "./helpers/boxBorders.js";
|
||||||
import { AnimatedText } from "./helpers/animatedText.js";
|
import { AnimatedText } from "./helpers/animatedText.js";
|
||||||
|
|
||||||
@@ -50,55 +43,29 @@ const renderInvoicesBox = async (
|
|||||||
selectedClientId?: string,
|
selectedClientId?: string,
|
||||||
selectedInvoiceId?: 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) {
|
if (selectedInvoiceId && selectedClientId) {
|
||||||
InvoicesBox = await renderInvoiceDetails(selectedInvoiceId);
|
InvoicesBox = <InvoiceDetails id={selectedInvoiceId}></InvoiceDetails>;
|
||||||
} else if (selectedClientId) {
|
} else if (selectedClientId) {
|
||||||
const currClient = find(sorted, { id: selectedClientId });
|
const currClient = find(sorted, { id: selectedClientId });
|
||||||
const currentClientName = `${currClient?.company} | ${currClient?.first_name} ${currClient?.last_name}`;
|
const currentClientName = `${currClient?.company} | ${currClient?.first_name} ${currClient?.last_name}`;
|
||||||
InvoicesBox = new Box({
|
InvoicesBox = await renderInvoiceList(selectedClientId, (id) => {
|
||||||
width: "fill",
|
|
||||||
height: "fill",
|
|
||||||
child: await renderInvoiceList(selectedClientId, (id) => {
|
|
||||||
console.log("clicked invoice", id);
|
console.log("clicked invoice", id);
|
||||||
reDrawMain(selectedClientId, id.toString());
|
reDrawMain(selectedClientId, id.toString());
|
||||||
}),
|
|
||||||
border: doubleBoxWithTitle(currentClientName),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return InvoicesBox;
|
return InvoicesBox;
|
||||||
};
|
};
|
||||||
|
const cellForItem = (client: ClientResponse) => {
|
||||||
let Clients = new ScrollableList({
|
|
||||||
minWidth: 20,
|
|
||||||
width: "natural",
|
|
||||||
cellForItem: (client: ClientResponse) => {
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
// console.log("empty client company name", item);
|
// console.log("empty client company name", item);
|
||||||
return new Text({ text: `Undefined Client` });
|
return <text>(Undefined Client)</text>
|
||||||
}
|
}
|
||||||
return new Button({
|
return <Text>{client.company}</Text>
|
||||||
border: "none",
|
};
|
||||||
width: "natural",
|
|
||||||
text: `${client.company}`,
|
export const Clients = <Box minWidth={20} width={"100%"}>{sorted.map(cellForItem)}</Box>
|
||||||
onClick: async () => {
|
|
||||||
console.log("🚀 ~ clientInvoicesView ~ clicked client:", client);
|
|
||||||
reDrawMain(client.id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
items: sorted,
|
|
||||||
});
|
|
||||||
|
|
||||||
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",
|
"target": "es2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"esModuleInterop": false,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"incremental": true,
|
"incremental": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"tsBuildInfoFile": null
|
"tsBuildInfoFile": null,
|
||||||
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts"
|
"**/*.ts"
|
||||||
],
|
, "src/index.tsx", "src/components/mainNav.tsx", "src/components/detailsInvoice.tsx", "src/components/listClients.tsx" ],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"**/mocks/**/*",
|
"**/mocks/**/*",
|
||||||
|
|||||||
Reference in New Issue
Block a user