vue-terminal / vue-termui Goto Github PK
View Code? Open in Web Editor NEWThe Modern Terminal UI Framework
Home Page: https://vue-termui.dev
License: MIT License
The Modern Terminal UI Framework
Home Page: https://vue-termui.dev
License: MIT License
yoga-layout
offers a lot of features out of the box, but I'm curious if it's possible to move yoga-layout
out of the core runtime and abstract it into a tree-shakable component (eg. Box
/FlexBox
) or a 2nd party component (separate package)?
For context, the main reasons why I want to move away from Ink are:
yoga-layout-prebuilt
alone is around 89.3 kB gzipped (379 kB minified). For reference, Vue 3 is 128 kB minified.
Currently, that means any CLI script that uses this tool will be at least 379 kB minified. This might deter users that are looking to create something simple.
I have two tools that rely on Ink right now, and they're both bloated because of this:
They don't actually do that much. And I don't think they even use the flexbox features. So ideally, I can get them down to less than 50 kB max and I was hoping moving to Vue for SFC compilation + treeshaking could help with that.
Let me know if I'm oversimplifying what yoga-layout
does, but AFAIK it implements flexbox in any environment (eg. terminal). This sounds like a great feature, but I'm wondering how often this will be used.
Personally, I don't use any CLI tools that draw boxes, are full screen, need to align content with flexbox powers, etc. Most of the ones I use or make usually have simple and inline UI.
However, it is a great building block to offer. Especially as I read Ink's documentation, it seems like a big selling point to have as a core component. I'm curious if it we can still have it as a tree-shakable component? If the installation size were to deter users, possibly as a separate package?
As I investigated this, I realized there are a few factors that impact rendering speed, and may not even be caused by yoga-layout
.
I'll include it anyway since I still found it relevant and insightful for this project.
Rendering performance factors:
The Terminal Emulator used. Native Terminal app is fastest, iTerm 2 is slow, and Hyper is even slower (although, less glitchy re-renders).
Page size. Seems like the larger the render payload, the slower it is. This sounds expected, but it's dramatically slower, which I found unexpected. For example, if my Ink app renders 3 pages worth of data, each re-render is significantly slower than 1 page of data. In iTerm 2, it's glitches as well.
As a side, I wondered about the possibility of a Page/Document component that overflows content and implements custom scroll so re-renders can be more efficient and less glitchy.
Content change speed. I noticed my Ink app gets notably slower when using ink-spinner. Probably because it changes the render output on every tick, so the render cycle is constantly running.
I originally noticed slowness in my own scripts that use Tasuku. They were running dramatically slower when it was rendering a lot. (ie it was a lot faster to run the script when removing Tasuku). I eventually got an user reporting it: privatenumber/tasuku#5 (comment)
I created isolated experiments with multiple components running 1 second timeouts in parallel to check if it the script will exit in 1s.
Code for Ink:
import React, { useState, useEffect } from 'react';
import {render, Text} from 'ink';
const Counter = () => {
const [counter, setCounter] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
setCounter(previousCounter => previousCounter + 1);
}, 1000);
return () => {
clearInterval(timer);
};
}, []);
return <Text color="green">{counter}</Text>;
};
render(
React.createElement(
React.Fragment,
null,
...Array.from(
{ length: 300 },
() => React.createElement(Counter),
),
),
);
Result:
Terminal: 300 parallel => 4s
Note, because this is 300 1s timers in parallel, exit time should still happen in ~1s. This might be the real cost of yoga-layout. I would like to remove the factor of page size here by rendering them all in one line, but Ink seems to require all text to be wrapped in
<Text>
, which separates them into new lines.
npx esno src/ink.tsx 2.53s user 0.27s system 80% cpu 3.502 total
iTerm 2: 300 parallel => 7s
npx esno src/ink.tsx 5.55s user 0.39s system 86% cpu 6.858 total
Adding ink-spinner in the mix seems to slow down the script dramatically:
Terminal: 300 parallel => 36s
npx esno src/ink.tsx 34.79s user 0.94s system 99% cpu 35.809 total
iTerm 300 parallel => 46s (and major CPU lag)
npx esno src/ink.tsx 42.48s user 1.24s system 94% cpu 46.394 total
import task from 'tasuku';
const sleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms));
task.group(
(task) => Array.from(
{ length: 100 },
() => task(
'sleep 1s',
() => sleep(1000),
),
),
{
concurrency: Infinity,
},
);
Result: 100 parallel -> ~6s
npx esno src/index.ts 5.71s user 0.38s system 107% cpu 5.656 total
Result: 300 parallel -> ~ 2m 🤯
npx esno src/index.ts 86.37s user 2.81s system 77% cpu 1:54.47 total
As a separate optimization feature, I wonder how much faster only re-rendering changed lines would be. Alternatively, skipping line clearing and just overwriting instead (and only clearing the extra text at the end, if the new render is shorter than the previous).
_events: [Object: null prototype] {
end: [Function: onReadableStreamEnd],
error: [Function: bound onceWrapper] { listener: [Function: noop] },
resize: [Function: l]
},
_eventsCount: 3,
_maxListeners: undefined,
_writableState: WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: false,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: null,
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 0,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
errored: null,
closed: false,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
columns: 120,
rows: 30,
_type: 'tty',
fd: 1,
_isStdio: true,
destroySoon: [Function: destroy],
_destroy: [Function: dummyDestroy],
[Symbol(async_id_symbol)]: 58,
[Symbol(kHandle)]: TTY { [Symbol(owner_symbol)]: [Circular *1] },
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kSetNoDelay)]: false,
[Symbol(kSetKeepAlive)]: false,
[Symbol(kSetKeepAliveInitialDelay)]: 0,
[Symbol(kBytesRead)]: 0,
[Symbol(kBytesWritten)]: 0
} swapScreens=undefined >
Done in 25.88s.
clone vue-termui and mount input component, the inputDemo can't input and have removed disabled status
import Input from './InputDemo.vue'
createApp(Input, {
// swapScreens: true,
}).mount({
// TODO: is this option really useful? when rendering once, any change should do a full reload one could just call
// exitApp when they are done rendering on onMounted and it would would handle everything
// renderOnce: true,
})
#26
I ask for help on Discord, but it seems that no one continues to help me solve it.
After build, run esno ./dist/main.mjs
lead to the error occurred
There is a related issue here. vadimdemedes/yoga-layout-prebuilt#2
<script lang="ts" setup>
const whatShow = ref('Bar')
onKeyData(['k'],() => {
whatShow.value = whatShow.value === 'Foo' ? 'Bar' : 'Foo'
console.log('value changed:',whatShow.value)
})
</script>
<template>
<Box>
<Text color="red" v-if="whatShow === 'Foo'">Foo</Text>
<Text color="green" v-else>Bar</Text>
</Box>
</template>
K
whatShow
has changed but template render is not correct.<script lang="ts" setup>
import path from 'path'
console.log("path",path)
</script>
...
Creating a new project works fine, but as soon as you want to install the dependencies you get errors about dependencies that cannot be resolved. See the error log below:
npm install ✔ 16.15.0 Node 10:56:46
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/vite
npm ERR! dev vite@"^3.1.3" from the root project
npm ERR! peer vite@"^3.1.3" from @vue-termui/[email protected]
npm ERR! node_modules/@vue-termui/cli
npm ERR! dev @vue-termui/cli@"*" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer vite@"^2.5.10" from @vitejs/[email protected]
npm ERR! node_modules/@vitejs/plugin-vue
npm ERR! dev @vitejs/plugin-vue@"^2.2.4" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR! See /home/user/.npm/eresolve-report.txt for a full report.npm ERR! A complete log of this run can be found in:
npm ERR! /home/user/.npm/_logs/2022-10-27T08_56_48_899Z-debug-0.log
A format like xx-xx
will not work
<Text bg-color="red">Label </Text>
<Box
:padding="1"
:margin-x="2"
width="100%"
:max-width="100"
flex-direction="column"
border-color="yellowBright"
border-style="round"
></Box>
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.