Code Monkey home page Code Monkey logo

temir's Introduction

This repository has been archived, please use Vue TermUI instead.



Temir


English | 简体中文

Vue for CLIs. Build your CLI output using components.

Temir provides the same component-based UI building experience that Vue offers in the browser, but for command-line apps.

It uses Yoga to build Flexbox layouts in the terminal, so most CSS-like props are available in Temir as well. If you are already familiar with Vue, you already know Temir.

Since Temir is a Vue renderer, it means that most of the features of Vue are supported. Head over to Vue website for documentation on how to use it. Only Temir's methods will be documented in this readme.


Install

npm install @temir/core

Usage

<script lang="ts" setup>
import { ref } from '@vue/runtime-core'
import { TBox, TText } from '@temir/core'
const counter = ref(0)
setInterval(() => {
  counter.value++
}, 100)
</script>

<template>
  <TBox>
    <TText color="green">
      {{ counter }} tests passed
    </TText>
  </TBox>
</template>

HMR Support

Contents

Getting Started

Use @temir/cli to quickly scaffold a new Temir-based CLI.

mkdir my-temir-cli

cd my-temir-cli

touch main.ts

npm install @temir/cli

# Dev

temir main.ts

# Build

temir build main.ts

You can also check it out this example to get started.Feel free to play around with the example and fork this repl at repl.it sandbox

Temir uses Yoga - a Flexbox layout engine to build great user interfaces for your CLIs using familiar CSS-like props you've used when building apps for the browser. It's important to remember that each element is a Flexbox container. Think of it as if each

in the browser had display: flex. See built-in component below for documentation on how to use Flexbox layouts in Ink. Note that all text must be wrapped in a component.

Components

<Text>

This component can display text, and change its style to make it bold, underline, italic or strikethrough.

temir-text-props

<TText color="green">
  I am green
</TText>

<TText color="black" background-color="white">
  I am black on white
</TText>

<TText color="white">
  I am white
</TText>

<TText bold>
  I am bold
</TText>

<TText italic>
  I am italic
</TText>

<TText underline>
  I am underline
</TText>

<TText strikethrough>
  I am strikethrough
</TText>

<TText inverse>
  I am inversed
</TText>

Note: <Text> allows only text nodes and nested <Text> components inside of it. For example, <Box> component can't be used inside <Text>.

color

Type: string

Change text color. Temir uses chalk under the hood, so all its functionality is supported.

<TBox flex-direction="column">
  <TText color="green">
    Green
  </TText>
  <TText color="blue">
    Blue
  </TText>
  <TText color="red">
    Red
  </TText>
</TBox>

backgroundColor

Type: string

Same as color above, but for background.

<TBox flex-direction="column">
  <TText background-color="green">
    Green
  </TText>
  <TText background-color="blue">
    Blue
  </TText>
  <TText background-color="red">
    Red
  </TText>
</TBox>

dimColor

Type: boolean
Default: false

Dim the color (emit a small amount of light).

<Text color="red" dimColor>
	Dimmed Red
</Text>

bold

Type: boolean
Default: false

Make the text bold.

italic

Type: boolean
Default: false

Make the text italic.

underline

Type: boolean
Default: false

Make the text underlined.

strikethrough

Type: boolean
Default: false

Make the text crossed with a line.

inverse

Type: boolean
Default: false

Inverse background and foreground colors.

<TText color="yellow" inverse>
  Inversed Yellow
</TText>

wrap

Type: string
Allowed values: wrap truncate truncate-start truncate-middle truncate-end
Default: wrap

This property tells Temir to wrap or truncate text if its width is larger than container. If wrap is passed (by default), Temir will wrap text and split it into multiple lines. If truncate-* is passed, Temir will truncate text instead, which will result in one line of text with the rest cut off.

<template>
  <TBox :width="7">
    <TText>Hello World</TText>
  </TBox>
  //=> 'Hello\nWorld'

  // `truncate` is an alias to `truncate-end`
  <TBox :width="7">
    <TText wrap="truncate">
      Hello World
    </TText>
  </TBox>
  //=> 'Hello…'

  <TBox :width="7">
    <TText wrap="truncate-middle">
      Hello World
    </TText>
  </TBox>
  //=> 'He…ld'

  <TBox :width="7">
    <TText wrap="truncate-start">
      Hello World
    </TText>
  </TBox>
  //=> '…World'
</template>

<Box>

<Box> is an essential Temir component to build your layout. It's like <div style="display: flex"> in the browser.

<script>
import { TBox, TText } from '@temir/core'
</script>

<template>
  <TBox :margin="2">
    <TText>This is a box with margin</TText>
  </TBox>
</template>

Dimensions

width

Type: number string

Width of the element in spaces. You can also set it in percent, which will calculate the width based on the width of parent element.

<template>
  <TBox :width="4">
    <TText>X</TText>
  </TBox>

  //=> 'X   '
</template>
<template>
  <TBox :width="10">
    <TBox width="50%">
      <TText>X</TText>
    </TBox>

    <TText>
      Y
    </TText>
  </TBox>

  //=> 'X    Y'
</template>
height

Type: number string

Height of the element in lines (rows). You can also set it in percent, which will calculate the height based on the height of parent element.

<template>
  <TBox :height="4">
    <TText>X</TText>
  </TBox>
  //=> 'X\n\n\n'
</template>
<template>
  <TBox :height="6" flex-direction="column">
    <TBox height="50%">
      <TText>X</TText>
    </TBox>

    <TText>
      Y
    </TText>
  </TBox>
  //=> 'X\n\n\nY\n\n'
</template>
minWidth

Type: number

Sets a minimum width of the element. Percentages aren't supported yet, see facebook/yoga#872.

minHeight

Type: number

Sets a minimum height of the element. Percentages aren't supported yet, see facebook/yoga#872.

Padding

paddingTop

Type: number
Default: 0

Top padding.

paddingBottom

Type: number
Default: 0

Bottom padding.

paddingLeft

Type: number
Default: 0

Left padding.

paddingRight

Type: number
Default: 0

Right padding.

paddingX

Type: number
Default: 0

Horizontal padding. Equivalent to setting paddingLeft and paddingRight.

paddingY

Type: number
Default: 0

Vertical padding. Equivalent to setting paddingTop and paddingBottom.

padding

Type: number
Default: 0

Padding on all sides. Equivalent to setting paddingTop, paddingBottom, paddingLeft and paddingRight.

<template>
  <TBox :padding-top="2">
    <TText>Top</TText>
  </TBox>

  <TBox :padding-bottom="2">
    <TText>Bottom</TText>
  </TBox>

  <TBox :padding-left="2">
    <TText>Left</TText>
  </TBox>

  <TBox :padding-right="2">
    <TText>Right</TText>
  </TBox>

  <TBox :padding-x="2">
    <TText>Left and right</TText>
  </TBox>

  <TBox :padding-y="2">
    <TText>Top and bottom</TText>
  </TBox>

  <TBox :padding="2">
    <TText>Top, bottom, left and right</TText>
  </TBox>
</template>

Margin

marginTop

Type: number
Default: 0

Top margin.

marginBottom

Type: number
Default: 0

Bottom margin.

marginLeft

Type: number
Default: 0

Left margin.

marginRight

Type: number
Default: 0

Right margin.

marginX

Type: number
Default: 0

Horizontal margin. Equivalent to setting marginLeft and marginRight.

marginY

Type: number
Default: 0

Vertical margin. Equivalent to setting marginTop and marginBottom.

margin

Type: number
Default: 0

Margin on all sides. Equivalent to setting marginTop, marginBottom, marginLeft and marginRight.

<template>
  <TBox :margin-top="2">
    <TText>Top</TText>
  </TBox>

  <TBox :margin-bottom="2">
    <TText>Bottom</TText>
  </TBox>

  <TBox :margin-left="2">
    <TText>Left</TText>
  </TBox>

  <TBox :margin-right="2">
    <TText>Right</TText>
  </TBox>

  <TBox :margin-x="2">
    <TText>Left and right</TText>
  </TBox>

  <TBox :margin-y="2">
    <TText>Top and bottom</TText>
  </TBox>

  <TBox :margin="2">
    <TText>Top, bottom, left and right</TText>
  </TBox>
</template>

Flex

flexGrow

Type: number
Default: 0

See flex-grow.

<template>
  <TBox>
    <TText>Label:</TText>
    <TBox :flex-grow="1">
      <TText>Fills all remaining space</TText>
    </TBox>
  </TBox>
</template>
flexShrink

Type: number
Default: 1

See flex-shrink.

<template>
  <TBox :width="20">
    <TBox :flex-shrink="2" :width="10">
      <TText>Will be 1/4</TText>
    </TBox>

    <TBox :width="10">
      <TText>Will be 3/4</TText>
    </TBox>
  </TBox>
</template>
flexBasis

Type: number string

See flex-basis.

<template>
  <TBox :width="6">
    <TBox :flex-basis="3">
      <TText>X</TText>
    </TBox>

    <TText>
      Y
    </TText>
  </TBox>
  //=> 'X  Y'
</template>
<template>
  <TBox :width="6">
    <TBox flex-basis="50%">
      <TText>X</TText>
    </TBox>

    <TText>
      Y
    </TText>
  </TBox>
  //=> 'X  Y'
</template>
flexDirection

Type: string
Allowed values: row row-reverse column column-reverse

See flex-direction.

<template>
  <TBox>
    <TBox :margin-right="1">
      <TText>X</TText>
    </TBox>

    <TText>
      Y
    </TText>
  </TBox>
  // X Y

  <TBox flex-direction="row-reverse">
    <TText>X</TText>
    <TBox :margin-right="1">
      <TText>Y</TText>
    </TBox>
  </TBox>
  // Y X

  <TBox flex-direction="column">
    <TText>X</TText>
    <TText>Y</TText>
  </TBox>
  // X
  // Y

  <TBox flex-direction="column-reverse">
    <TText>X</TText>
    <TText>Y</TText>
  </TBox>
  // Y
  // X
</template>
alignItems

Type: string
Allowed values: flex-start center flex-end

See align-items.

<template>
  <TBox align-items="flex-start">
    <TBox :margin-right="1">
      <TText>X</TText>
    </TBox>

    <TText>
      A
      <TNewline />
      B
      <TNewline />
      C
    </TText>
  </TBox>
  // X A
  //   B
  //   C

  <TBox align-items="center">
    <TBox margin-right="1">
      <TText>X</TText>
    </TBox>

    <TText>
      A
      <TNewline />
      B
      <TNewline />
      C
    </TText>
  </TBox>
  //   A
  // X B
  //   C

  <TBox align-items="flex-end">
    <TBox margin-right="1">
      <TText>X</TText>
    </TBox>

    <TText>
      A
      <TNewline />
      B
      <TNewline />
      C
    </TText>
  </TBox>
  //   A
  //   B
  // X C
</template>
alignSelf

Type: string
Default: auto
Allowed values: auto flex-start center flex-end

See align-self.

<template>
  <TBox :height="3">
    <TBox align-self="flex-start">
      <TText>X</TText>
    </TBox>
  </TBox>
  // X
  //
  //

  <TBox :height="3">
    <TBox align-self="center">
      <TText>X</TText>
    </TBox>
  </TBox>
  //
  // X
  //

  <TBox :height="3">
    <TBox align-self="flex-end">
      <TText>X</TText>
    </TBox>
  </TBox>
  //
  //
  // X
</template>
justifyContent

Type: string
Allowed values: flex-start center flex-end space-between space-around

See justify-content.

<template>
  <TBox justify-content="flex-start">
    <TText>X</TText>
  </TBox>
  // [X      ]

  <TBox justify-content="center">
    <TText>X</TText>
  </TBox>
  // [   X   ]

  <TBox justify-content="flex-end">
    <TText>X</TText>
  </TBox>
  // [      X]

  <TBox justify-content="space-between">
    <TText>X</TText>
    <TText>Y</TText>
  </TBox>
  // [X      Y]

  <TBox justify-content="space-around">
    <TText>X</TText>
    <TText>Y</TText>
  </TBox>
  // [  X   Y  ]
</template>

Visibility

display

Type: string
Allowed values: flex none
Default: flex

Set this property to none to hide the element.

Borders

borderStyle

Type: string
Allowed values: single double round bold singleDouble doubleSingle classic

Add a border with a specified style. If borderStyle is undefined (which it is by default), no border will be added. Temir uses border styles from cli-boxes module.

<template>
  <TBox flex-direction="column">
    <TBox>
      <TBox border-style="single" :margin-right="2">
        <TText>single</TText>
      </TBox>

      <TBox border-style="double" :margin-right="2">
        <TText>double</TText>
      </TBox>

      <TBox border-style="round" :margin-right="2">
        <TText>round</TText>
      </TBox>

      <TBox border-style="bold">
        <TText>bold</TText>
      </TBox>
    </TBox>

    <TBox :margin-top="1">
      <TBox border-style="singleDouble" :margin-right="2">
        <TText>singleDouble</TText>
      </TBox>

      <TBox border-style="doubleSingle" :margin-right="2">
        <TText>doubleSingle</TText>
      </TBox>

      <TBox border-style="classic">
        <TText>classic</TText>
      </TBox>
    </TBox>
  </TBox>
</template>
borderColor

Type: string

Change border color. Accepts the same values as color in <Text> component.

<template>
  <TBox border-style="round" border-color="green">
    <TText>Green Rounded Box</TText>
  </TBox>
</template>

<Newline>

Adds one or more newline (\n) characters. Must be used within <Text> components.

count

Type: number
Default: 1

Number of newlines to insert.

<script>
import { TBox, TNewline, TText } from '@temir/core'
</script>

<template>
  <TBox>
    <TText>
      <TText color="green">
        Hello
      </TText>
      <TNewline />
      <TText color="red">
        World
      </TText>
    </TText>
  </TBox>
</template>

Output:

Hello
World

<Spacer>

A flexible space that expands along the major axis of its containing layout. It's useful as a shortcut for filling all the available spaces between elements.

For example, using <Spacer> in a <Box> with default flex direction (row) will position "Left" on the left side and will push "Right" to the right side.

<script lang="ts" setup>
import { TBox, TSpacer, TText } from '@temir/core'
</script>

<template>
  <TBox>
    <TText>Left</TText>
    <TSpacer />
    <TText>Right</TText>
  </TBox>
</template>

In a vertical flex direction (column), it will position "Top" to the top of the container and push "Bottom" to the bottom of it. Note, that container needs to be tall to enough to see this in effect.

<script lang="ts" setup>
import { TBox, TSpacer, TText } from '@temir/core'
</script>

<template>
  <TBox flex-direction="column" :height="10">
    <TText>Top</TText>
    <TSpacer />
    <TText>Bottom</TText>
  </TBox>
</template>

Credits

This project is highly inspired by ink

vite-node made the HMR support easily

temir's People

Contributors

godliangcy avatar innei avatar lotwt avatar vaakian avatar webfansplz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

temir's Issues

Can't build example `hi-temir`

First of all: super cool framework.

Now: I am trying to build the examples on my machine (MacOS, Node 18), in particular the examples/hi-temir example. npm run dev works fine, but when I try to build using npm run build I get the following error.

Steps to reproduce:

  1. git clone [email protected]:webfansplz/temir.git
  2. cd ./temir/examples/hi-temir
  3. npm install
  4. npm run build
errors: [
    {
      id: '',
      pluginName: '',
      text: 'Expected value for define "__VUE_OPTIONS_API__" to be a string, got boolean instead',
      location: {
        file: '/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js',
        namespace: 'file',
        line: 299,
        column: 10,
        length: 0,
        lineText: '    throw new Error(`Expected value for ${what}${key !== void 0 ? " " + quote(key) : ""} to be a string, got ${typeof value} instead`);\n' +
          '    at validateStringValue (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:299:11)\n' +
          '    at pushCommonFlags (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:390:37)\n' +
          '    at flagsForBuildOptions (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:424:3)\n' +
          '    at buildOrContextContinue (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:1009:9)\n' +
          '    at /Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:983:11',
        suggestion: ''
      },
      notes: [],
      detail: Error: Expected value for define "__VUE_OPTIONS_API__" to be a string, got boolean instead
          at validateStringValue (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:299:11)
          at pushCommonFlags (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:390:37)
          at flagsForBuildOptions (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:424:3)
          at buildOrContextContinue (/Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:1009:9)
          at /Users/maximillian/Development/temir/examples/hi-temir/node_modules/esbuild/lib/main.js:983:11
    }
  ],
  warnings: []
}

PSA: state of Temir

Temir is a very cool package but has some rough edges. I just want to briefly go over some of the issues I have run into, some of the solutions, and remaining issues. Hopefully you can walk away with a better idea as to whether this library fits your use-case.

I think many of the Pros speak for themselves, this is one of the few (maybe only?) reactive cli tools with automatic dep tracking (courtesy of Vue). I had no issues with the reactivity, that all functions just as you'd expect.

Issues:

Small Community, Few Refences, Mostly Unmaintained:

There is no discussion tab on this repo, no discord, and issues seem to be unaddressed.
If you are having trouble, the best I can do is point you to these few references and maybe you can compare your work or strip their repos and use them as a template.

Undocumented Features:

If you plan to use this repo, it may be beneficial to review Ink's docs as this repo is based on it. It will also be beneficial to look over the Temir's source code. In both cases you'll find things that exist here but are undocumented, like waitUntilExit() and other render/instance methods.

TS Dependency Issues Preventing Build:

The functionality of this repo's examples are currently dependent upon the lock file. If you delete that and rebuild, you will run into issues. Likewise, if you are starting a project from scratch without knowing this, you will likely run into these issues as well. One option is to just not use TS I suppose...

Others have clearly gotten TS to work, so let them be your guide:

Or wait for the maintainer to addresses this issue.
(I'll mention another solution later - tips: building below)

Select input broken

I mainly just wanted to use this repo for a convenient "item selection list", but was disappointed to find it was broken.
If you arrow down then back up, things break. I stripped the original and rolled my own here. Hopefully someone may find it of use or otherwise find the bug in the original.

Double console.log

I was able to avoid it, most probably can as well. It is a little annoying tho.
Depending ones use case, render(app, { patchConsole: false }) may help.

No Text Input Component

If this is something you require, you will have to roll your own. You may wish to use this ink version as a reference. Or, maybe leverage more vanilla libs like Inquirer.

Tips:

App vs Commands

Going into you're project, you should have an idea as to whether you want CLI tool to be an App or Command based.
I don't have, much to say about making a pure app but you can reference this snake game if you need help.

For command-based tools, you will want to use the render function more synchronously as well as pass data:

function ender(renderer) {
  renderer.clear()
  renderer.unmount()
}

let renderWait = async (component, cb) => {
  let r, argsTmp;
  
  let resolvableCB = (...args)=>{ argsTmp=args; ender(r) }
  r = render(component(resolvableCB))
  await r.waitUntilExit() // wait for resolvableCB() to execute ender()

  // We forward args to avoid race condition and double console.log
  if(!Array.isArray(argsTmp)) process.exit(1) // quit without selecting
  return await cb(...argsTmp)
}

program.command('someCommand')
  .action(async (options, cmd) => {
    let outPut = await renderWait(
      cb => <Selector items={items} onSubmit={cb}/>,
      selected => {
        console.log('Selected:', selected.value)
        return selected.value
    })
  })

// this could probably be better
let renderPromise = async (component, cb) => {
  return new Promise(async (resolve, reject) => {
    let r;
    let resolvableCB = (...args)=>{ 
      ender(r); // exit first to prevent double log issue
      resolve(cb(...args))
    }
    r = render(component(resolvableCB))
  })
}

program.command('someCommand')
  .action(async (options, cmd) => {
    let outPut = await renderPromise(
      cb => <Selector items={items} onSubmit={cb}/>,
      selected => {
        console.log('Selected:', selected.value)
        return selected.value
    })
  })

Building:

Because I ran into TS issues with the unbuild version and also prefer to have a transparent build process, I chose Vite to build:

import { defineConfig } from 'vite';

import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'

import { dependencies } from './package.json'

// Node modules will have to be registered manually here
let node_libs = [
  'util', 'os', 'child_process', 'stream', 'path', 'fs' // can prob replace with `import { builtinModules } from "module"`
].flatMap(m=>[m, `node:${m}`])

let pkg_deps = Object.keys(dependencies)
let pkg_dep_map = Object.fromEntries(pkg_deps.map(m => [m, m.replaceAll(/[\@\/\:]/g, '_')]))

export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),

    shebang('./dist/index.js'),
    del('./dist/index.umd.cjs')
  ],
  build: {
    minify: false,
    lib: {
      entry: './index.jsx',
      fileName: 'index',
      name: 'default',
    },
    rollupOptions: {
      external: [ ...pkg_deps, ...node_libs ],
      output: {
        globals: {
          ...pkg_dep_map,
          ...Object.fromEntries(node_libs.map(m=>[m, m]))
        }
      } 
    }
  }
})

import fs from 'fs'
function shebang (pathToBang) { 
  return {
    name: 'remove-main-script',
    enforce: 'post',
    apply: 'build',
    closeBundle: async () => {
      if (!fs.existsSync(pathToBang)) return
      let stringToPrepend = `#! /usr/bin/env node\n\n`
      try {
        const data = fs.readFileSync(pathToBang, 'utf8')
        fs.writeFileSync(pathToBang, stringToPrepend + data)
      } catch (err) {
        console.error(err);
      }
    }
  }
}

function del(pathToRm) {
  return {
    name: 'del',
    enforce: 'post', 
    apply: 'build',
    closeBundle: async () => {
      if (!fs.existsSync(pathToRm)) return
      fs.rmSync(pathToRm, { recursive: true } )
    }
  }
}

I am no Vite expert, so there are probably improvements to be made.

Don't pass entry script to args

If you are taking a command-based approach, you will only want to pass the given args to the program.
This is a problem with the temir cli, where the given file to be served is passed as an arg...

{
  "scripts": {
    "start": "temir entryFile.jsx"
  }
}

To alleviate this, you may want to create a serve file that hardcodes the entry point:

// serve.js
// Allows us to start the dev server without passing index as an cmd-line arg 

import { runDevServer } from '@temir/cli'
runDevServer('./entryFile.jsx')

node serve.js

NOTE: runDevServer() defaults to 'src/main.ts' rather than checking your entry in package.json...

HMR

Again, when dealing with a command-based approach, there are times when HMR gets in the way.
An example might be starting a server on a given port or something that doesn't use the renderer at all:

  • You'll probably want to use process.exit() for renderless commands.
  • For things like a server, it would be nice to use something similar to import.meta?.hot.on('vite:beforeUpdate', ()=>{}) to clean up long running/persistent processes. However, I haven't found a similar function in vite-node as of yet...
     

Closing Statement:

I love how powerful and versatile Vue is and preferer to use it when possible. Despite the issues I ran into, I believe there is still great potential for this repo. However, in its current state I would not use it for something I need done quickly or professionally. Hopefully others may share their tips and trick to build up more of a community around this tool, I would love for it to become an obvious choice over Ink someday.

text 频繁的属性变更会导致性能问题

例子:
https://replit.com/@yangxin9003/temir-list-test?v=1

child.vue 组建切换注释的模版代码
image

现象:
在属性更新时,随着时间推移性能逐渐下降
只有children文本更新时,性能没有下降

排查出的问题:
随着 list 中 数据 的 active 属性的逐一变更,导致了之前变更过的 child.vue 组建发生 update。原因不明

大量组建的update性能也有些问题,排查感觉和 updadte 时 vnode的props 的 diff 导致的频繁的onRender调用有关系。

temir如何在Vue2项目中使用呢?

我看项目中使用的是Vue3语法,
如果想要在Vue2中使用,

现在能想到一个方案:
用Vue3单独将此插件运行起来,通过iframe嵌入到Vue2项目中,
但这样会增加交互环节,

此插件是否直接支持Vue2呢?

向开源致敬!

计划新增 Tree 组件和 Progress 组件吗?

  • Tree组件
    类似于ui组件库中的 Tree 组件,用快捷键操作,可以用于文件夹目录展示等场景。
  • Progress组件
    各种场景下的进度条。

如果有类似计划,我可以尝试 contribute。

can't run examples/hi-temir

Project Info:

- Operating System:  `Windows_NT`
- Node Version:      `v16.16.0`
- Package Manager:   `[email protected]`
- Terminal:          `PowerShell 7.2.5`

Log :

G:\codes\hi-temir via 🌰 v16.16.0
❯ pnpm dev

> hi-temir@ dev G:\codes\hi-temir
> temir src/main.ts

G:\codes\hi-temir\node_modules\.pnpm\y[email protected]\node_modules\yoga-layout-prebuilt\yoga-layout\build\Release\nbind.js:53
        throw ex;
        ^

Error: Failed to resolve import "vue" from "src\App.vue". Does the file exist?
    at formatError (G:\codes\hi-temir\node_modules\.pnpm\v[email protected]\node_modules\vite\dist\node\chunks\dep-c9998dc6.js:39080:46)
    at TransformContext.error (G:\codes\hi-temir\node_modules\.pnpm\v[email protected]\node_modules\vite\dist\node\chunks\dep-c9998dc6.js:39076:19)
    at normalizeUrl (G:\codes\hi-temir\node_modules\.pnpm\v[email protected]\node_modules\vite\dist\node\chunks\dep-c9998dc6.js:58354:26)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async TransformContext.transform (G:\codes\hi-temir\node_modules\.pnpm\v[email protected]\node_modules\vite\dist\node\chunks\dep-c9998dc6.js:58503:57)
    at async Object.transform (G:\codes\hi-temir\node_modules\.pnpm\v[email protected]\node_modules\vite\dist\node\chunks\dep-c9998dc6.js:39317:30)
    at async doTransform (G:\codes\hi-temir\node_modules\.pnpm\v[email protected]\node_modules\vite\dist\node\chunks\dep-c9998dc6.js:50037:29) {
  plugin: 'vite:import-analysis',
  id: 'G:/codes/hi-temir/src/App.vue',
  pluginCode: 'import { defineComponent as _defineComponent } from "vue";\n' +
    'import { ref } from "@vue/runtime-core";\n' +
    'import { TBox, TText } from "@temir/core";\n' +
    'const _sfc_main = /* @__PURE__ */ _defineComponent({\n' +
    '  __name: "App",\n' +
    '  setup(__props, { expose }) {\n' +
    '    expose();\n' +
    '    const count = ref(0);\n' +
    '    setInterval(() => {\n' +
    '      count.value++;\n' +
    '    }, 100);\n' +
    '    const __returned__ = { count, TBox, TText };\n' +
    '    Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true });\n' +
    '    return __returned__;\n' +
    '  }\n' +
    '});\n' +
    'import { toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, withCtx as _withCtx, createVNode as _createVNode, openBlock as _openBlock, createBlock as _createBlock } from "vue";\n' +
    'function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {\n' +
    '  return _openBlock(), _createBlock($setup["TBox"], {\n' +
    '    margin: 5,\n' +
    '    width: 30,\n' +
    '    height: 5,\n' +
    '    "border-style": "double",\n' +
    '    "justify-content": "center",\n' +
    '    "align-items": "center"\n' +
    '  }, {\n' +
    '    default: _withCtx(() => [\n' +
    '      _createVNode($setup["TText"], { color: "#42b883" }, {\n' +
    '        default: _withCtx(() => [\n' +
    '          _createTextVNode(" \\u{1F308} Hi TEMIR !! " + _toDisplayString($setup.count), 1)\n' +
    '        ]),\n' +
    '        _: 1\n' +
    '      })\n' +
    '    ]),\n' +
    '    _: 1\n' +
    '  });\n' +
    '}\n' +
    '_sfc_main.__hmrId = "7ba5bd90";\n' +
    'typeof __VUE_HMR_RUNTIME__ !== "undefined" && __VUE_HMR_RUNTIME__.createRecord(_sfc_main.__hmrId, _sfc_main);\n' +
    'import.meta.hot.accept(({ default: updated, _rerender_only }) => {\n' +
    '  if (_rerender_only) {\n' +
    '    __VUE_HMR_RUNTIME__.rerender(updated.__hmrId, updated.render);\n' +
    '  } else {\n' +
    '    __VUE_HMR_RUNTIME__.reload(updated.__hmrId, updated);\n' +
    '  }\n' +
    '});\n' +
    'import _export_sfc from "plugin-vue:export-helper";\n' +
    'export default /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__file", "G:/codes/hi-temir/src/App.vue"]]);\n',
  loc: { file: 'G:/codes/hi-temir/src/App.vue', line: 1, column: 54 },
  frame: '1  |  import { defineComponent as _defineComponent } from "vue";\n' +
    '   |                                                       ^\n' +
    '2  |  import { ref } from "@vue/runtime-core";\n' +
    '3  |  import { TBox, TText } from "@temir/core";'
}
 ELIFECYCLE  Command failed with exit code 7.

TSelectInput 无法响应式渲染?

在onMounted钩子里将items修改后 Select组件还是只有开始的一个选项

Reproductions

<script lang="ts" setup>
  const items:any = ref([
      {
        label: 'Vue',
        value: 'Vue',
      },
  ])

  onMounted(() => {
    items.value = [
      {
        label: 'Vue',
        value: 'Vue',
      },
      {
        label: 'Vite',
        value: 'Vite',
      },
      {
        label: 'Temir',
        value: 'Temir',
      },
    ]
  })

  </script>
  
  <template>
    <TText>{{ items }}</TText>
    <TSelectInput :items="items" />
  </template>

unocss support?

If temir could support unocss, the ease of use would go up a notch, like slidev

can't run playground

Project Info:

- Operating System:  `Windows_NT`
- Node Version:      `v16.16.0`
- Package Manager:   `[email protected]`
- Terminal:          `PowerShell 7.2.5`

Logs:

temir\playground on  main [?] via 🌰 v16.16.0 
❯ pnpm dev

> @temir/playground@ dev G:\codes\temir\playground
> temir main.ts

G:\codes\temir\node_modules\.pnpm\y[email protected]\node_modules\yoga-layout-prebuilt\yoga-layout\build\Release\nbind.js:53
        throw ex;
        ^

TypeError: Cannot convert a Symbol value to a string
    at Array.join (<anonymous>)
    at warn (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:24:24)
    at patch (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:4987:21)
    at mountChildren (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:5163:13)
    at mountElement (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:5073:17)
    at processElement (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:5055:13)
    at patch (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:4975:21)
    at ReactiveEffect.componentUpdateFn [as fn] (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:5515:21)
    at ReactiveEffect.run (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\reactivity\dist\reactivity.cjs.js:189:25)
    at instance.update (G:\codes\temir\node_modules\.pnpm\@[email protected]\node_modules\@vue\runtime-core\dist\runtime-core.cjs.js:5622:56)
 ELIFECYCLE  Command failed with exit code 7.

Components were rendered twice?

As the title described, it's unexpected.

Minimal repro link

Repro steps

  1. pnpm i
  2. pnpm dev

Type something and see the terminal.It was logged twice(before this PR has been merged, it was 4 times).

Support vue 3 jsx/tsx?

Hello, this is awesome project to write cli toolchain. When I try to write a demo, I run into problems. This is my code.

import { render } from '@temir/core'

import App from './views/App'

render(App)
// App.tsx
import { TText } from '@temir/core'
import { TBox } from '@temir/core'
import { defineComponent } from 'vue'

export default defineComponent({
  setup() {
    return () => (
      <TBox>
        <TText>Hello World!</TText>
      </TBox>
    )
  },
})

And run dev then crashed...


> @innei/[email protected] dev /Users/user/git/bump-version
> temir src/main.ts

/Users/user/git/bump-version/node_modules/.pnpm/[email protected]/node_modules/yoga-layout-prebuilt/yoga-layout/build/Release/nbind.js:53
        throw ex;
        ^

TypeError: Cannot read properties of undefined (reading 'modules')
    at comp.setup (/Users/user/git/bump-version/__vue-jsx-ssr-register-helper:8:18)
    at callWithErrorHandling (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:157:22)
    at setupStatefulComponent (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7084:29)
    at setupComponent (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:7039:11)
    at mountComponent (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5401:13)
    at processComponent (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5376:17)
    at patch (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:4978:21)
    at ReactiveEffect.componentUpdateFn [as fn] (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5515:21)
    at ReactiveEffect.run (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/reactivity/dist/reactivity.cjs.js:189:25)
    at instance.update (/Users/user/git/bump-version/node_modules/.pnpm/@[email protected]/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:5622:56)

Help,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.