npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

y-webterminal

v1.3.0

Published

web终端,在前端实现一个终端,与服务器进行交互。

Downloads

46

Readme

使用文档

github

演示

安装

使用npm

npm install y-webterminal

使用unpkg

<link rel="stylesheet" href="https://unpkg.com/y-webterminal/style/index.css">
<link rel="stylesheet" href="https://unpkg.com/y-webterminal/style/dark.css">
<script src="https://unpkg.com/y-webterminal/lib/index.js"></script>

概念

WebTerminal

可通过WebTerminal类创建终端、写入日志、监听用户输入。

widget部件

向终端写入的每一行日志都是通过widget创建的,不同的widget输出不同。例如调用writeln方法使用的是weblog部件,用户输入使用的是userInput部件。内置的部件还有tablelog,他可以输出网格日志,当用户执行 ls 命令输出当前文件夹下所有文件信息时,使用tablelog很合适。当widget不满足要求时,开发者也可以自己拓展新的widget。

systemInfo

系统信息,由 usernamehostdirmark 构成,拼接后如 user@localhost ~ #

示例

初始化

<template>
  <div class="container">
	<!-- 为终端准备一个容器 -->
    <div id="webTerminal"></div>
  </div>
</template>

<style lang="scss" scoped>  
#webTerminal {
  // 设置宽高
  margin: 10px;
  width: 80%;
  height: 300px;
  border-radius: 4px;
}
</style>
<script setup lang="ts">
import {WebTerminal} from "y-webterminal";
// 引入样式文件和需要的主题文件
import "y-webterminal/style/index.css";
import "y-webterminal/style/dark.css";
import "y-webterminal/style/light.css";

// 创建应用实例
const webTerminal = new WebTerminal({
  // 指定主题,名称与主题文件名一样
  theme: "dark",
  // 系统信息
  systemInfo: {
    username: "yanxias",
    host: "@localhost",
    dir: "~",
    mark: "#",
    // 自定义信息,当custom存在时以上配置失效
    custom: ""
  }
})

onMounted(() => {
   // 页面挂载完成,渲染终端
  const webTerminalEl = document.getElementById("webTerminal")!
  webTerminal.render(webTerminalEl)
})
</script>

监听回车,与服务器交互并输出日志

webTerminal.on("enter", function ({command, onQuit, offQuit}) {
  // 获取到用户输入的命令,模拟与服务器交互
  console.log("onenter", command)
  const log = webTerminal.writeln("0")
  let count = 0
  // 模拟服务器不断传输日志
  const timer = setInterval(() => {
    count++
    // 在原来的日志上修改
    log.set(count + "")
    if(count >= 10){
      // 日志输出完毕
      clearInterval(timer)
      // 设置新的系统信息
      webTerminal.setSystemInfo({
        username: "root"
      })
      // 回车后会自动隐藏用户输入行,所以日志输出完毕后要显示用户输入行
      webTerminal.showUserRow()
      return
    }
  }, 500)
})

执行任务期间强制退出

// 1. 在回车事件中监听退出事件
webTerminal.on("enter", function ({command, onQuit, offQuit}) {
  let quitCount = 0
  // 监听退出事件
  onQuit(() => {
    quitCount++
    // 用户退出3次后开始退出任务
    if(quitCount >= 3) {
      // 取消监听退出
      offQuit()
      // ......退出任务的代码......
      // 显示用户输入行
      webTerminal.showUserRow()
    }
  })
})
// 2. 直接监听退出事件
webTerminal.on("quit" function() {
  // ......退出任务的代码......
})

监听用户输入tab自动补齐命令

webTerminal.on("tabulator", function (command) {
  console.log("command", command)
  // 设置用户输入的内容
  webTerminal.setUserInput(command + "new")
  // 创建列表日志,显示提示的命令列表
  const listLog = new ListLog()
  listLog.set(["help", "publish", "run", "test", "build"])
  // 写入提示
  webTerminal.writeHelp(listLog)
})

写入

追加

// 写入一行文本日志
webTerminal.writeln("Hello word!")
// 创建表格日志部件
const tableLog = new TableLog()
tableLog.set([
  ["yx", 18, "xz"],
  ["gc", 17, "yz"],
])
// 写入widget部件
webTerminal.writeWidget(tableLog)

查询

// 通过下标查询最后一行widget部件
const row = webTerminal.getRow(webTerminal.logs.length - 1)
// 或者通过id查询
const row = webTerminal.getRow(id)

修改

if(row.type === WidgetType.weblog){
  row.set("new log")
}

删除

// 通过下标或id删除
webTerminal.deleteRow(webTerminal.logs.length - 1)
webTerminal.deleteRow(id)
// 清空全部日志
webTerminal.clearLogs()
// 根据条件清空日志
webTerminal.clearLogs((widget) => {
  return widget.type !== WidgetType.userInput
})

API

WebTerminal

属性

  • systemInfo 系统信息
  • logs 所有日志
  • on 监听用户输入事件

构造函数

constructor(options: Options);
  • options.theme 使用的主题
  • options.systemInfo 系统信息,由 usernamehostdirmark 构成,拼接后如 user@localhost ~ #
  • options.historyLength 用户输入的历史命令最大存储条数
  • options.hiddenUserInput 初始化隐藏用户输入框

render

渲染终端

render(el: HTMLElement);

writeln

写入文本并换行

writeln(text: string, id?: string): WebLog;
  • text 写入的内容
  • id 记录id,可通过id查找这一行
  • return 返回WebLog对象

writeWidget

写入一个widget

writeWidget(widget: WidgetInter<unknown>);
  • widget 创建的widget对象,继承至WidgetInter

writeHelp

向用户输入行下面写入输入提示,例如用户按tab键获取输入提示

writeHelp(widget: WidgetInter<unknown>)
  • widget 创建的widget对象,继承至WidgetInter

clearHelpWidget

清空输入提示

clearHelpWidget()

getRow

获取一行日志

getRow(cursor: number | string): WidgetInter<unknown> | null;
  • cursor 下标或id,当cursor为number类型时按下标查询,当cursor为string类型时按id查询
  • 返回查到的 widget 或 null

deleteRow

删除一行日志

deleteRow(cursor: number | string)
  • cursor 下标或id,当cursor为number类型时按下标查询,当cursor为string类型时按id查询

clearLogs

清空日志

clearLogs(filter?: (widget: WidgetInter<unknown>) => boolean)
  • filter 过滤函数

setSystemInfo

设置系统信息

setSystemInfo(systemInfo: Partial<SystemInfo>)

setUserInput

设置用户输入框的内容

setUserInput(command: string)
  • command 输入的内容

hiddenUserRow

隐藏用户输入行,在回车后会自动隐藏

hiddenUserRow(): void

showUserRow

显示用户输入行

showUserRow(): void

focus

使终端获取焦点

focus(): void

setTheme

设置主题,前提是先引入对应的主题文件

setTheme(theme: string): void

scrollBottom

将滚动条滚动到底部

scrollBottom(): void

事件

事件系统使用mitt,事件类型及参数参考下方 Events

change

用户输入的内容发生变化时触发,参数:输入的值。

focus

获取焦点时触发

blur

失去焦点时触发

quit

用户输入 ctrl + c 时触发,用来退出当前任务

enter

用户输入回车执行命令时触发,参数 { command, onQuit, offQuit}

  • command 输入的命令
  • onQuit 监听退出事件
  • offQuit 取消监听退出,如果在enter事件中调用了onQuit方法,记得在合适的时机取消监听,否则多次enter事件重复监听退出事件

tabulator

用户输入 tab 制表符,参数command

  • command 输入的命令

keydown

键盘按下事件,参数 { event, stop}

  • event 事件参数
  • stop 调用stop方法,如果是控制键(例如:回车、删除、tab、上下左右等),y-webterminal将不会处理

类型

export interface Options {
  theme?: string;
  historyLength?: number;
  systemInfo?: Partial<SystemInfo>;
  hiddenUserInput?: boolean;
}

export interface SystemInfo {
  username: string ;
  host: string;
  dir: string;
  mark: string;
  custom?: string | null;
}

export type Events = {
  change: string;
  enter: {
    command: string;
    onQuit: (cb: () => void) => void;
    offQuit: () => void;
  };
  tabulator: string;
  focus: void;
  blur: void;
  quit: void;
  keydown: {
    event: KeyboardEvent,
    stop: () => void;
  };
}

export enum WidgetType {
  userInput,
  weblog,
  progress,
  table
}

export enum InnerType {
  text,
  html
}

主题

设置主题

// 引入样式文件和需要的主题文件
import "y-webterminal/style/index.css";
import "y-webterminal/style/dark.css";
import "y-webterminal/style/light.css";
// 初始化时指定主题
const webTerminal = new WebTerminal({theme: "dark"});
// 后续修改主题
webTerminal.setTheme("light");

自定义主题

  1. 创建myDark.css文件并引入
  2. 在myDark.css文件中定义样式变量
// my-dark 为主题名
.my-dark {
  // 窗口背景
  --web-shell-bg: #1E1E1E;
  // 主要的文字颜色
  --primary-color: #ffffff;
  // 文字大小
  --font-size: 12px;
  // 光标颜色
  --cursor-color: rgba(173, 173, 173, 0.6);
  // 用户和主机名文字颜色
  --host-color: --primary-color;
  // 所在文件夹文字颜色
  --dir-color: --primary-color;
  // 标记颜色
  --mark-color: --primary-color;
}
// 引入样式文件和自定义主题文件
import "y-webterminal/style/index.css";
import "@/style/myDark.css";
// 指定主题
const webTerminal = new WebTerminal({theme: "my-dark"});

widget的使用及拓展

  • userLog 用户输入命令回车后,会将系统信息及输入的命令生成一条日志,追加到日志列表
  • webLog 普通的网络日志
  • tableLog 以表格的形式输出日志
  • listLog 以列表的形式输出日志

webLog部件的使用

// 直接调用
webTerminal.writeln("hello world");
// 或先创建webLog对象,再设置值,最后调用writeWidget写入
const weblog = new WebLog();
weblog.set("hello world");
webTerminal.writeWidget(weblog);

set方法:

set(value: string)

tablelog部件的使用

// 创建widget
const tableLog = new TableLog();
// 设置一个二维数组
tableLog.set([
  ["yx", 18, "xz"],
  ["gc", 17, "yz"],
])
// 设置对象数组
tableLog.set([
  {name: "yxx", age: 18, address: "xz"},
  {name: "gcc", age: 17, address: "yz"}
],[
  {prop: "age", label: "年龄"},
  {prop: "name", label: "姓名"},
  {prop: "address", label: "地区", align: "right"}
]);
webTerminal.writeWidget(tableLog);

set方法:

interface Column{
  prop: string;
  label: string;
  width?: string;
  align?: string
}
set(value: Array<Array<keyof any>>): void;
set(value: Array<Record<string, keyof any>>, columns: Array<Column>): void;

listLog部件的使用

const listLog = new ListLog()
// 参数为一个数组,数组中可以是字符串或者对象
listLog.set([
  "index.html", 
  "index.ts", 
  {
    value: "static",
  	style: "color: red"
  }
])
webTerminal.writeHelp(listLog)

set方法:

// class: 元素的类名, style: 元素的样式
set(value: Array<string | {value: string, class?: string, style?: string}>)

拓展

当widget不满足要求时,或开发者想拓展新的widget时,可通过继承WidgetInter抽象类来开发自己的widget,所有widget都是继承至WidgetInter

export default abstract class WidgetInter<S> {
  // wedget的唯一标识
  id: string;

  // wedget的类型,拓展时可以是string类型
  abstract type: WidgetType | string;

  // 以哪种方式插入到节点,文本或html字符串
  abstract innerType: InnerType;

  // 数据
  value?: S;

  // 插入的dom节点
  rowEl: HTMLDivElement | null = null;

  protected constructor(id?: string) {
    this.id = id || nanoid();
  }

  get() {
    return this.value;
  };

  set(value: S) {
    this.value = value;
    this.updateInner()
  };

  // 更新dom节点
  updateInner() {
    if (!this.rowEl) return;
    switch (this.innerType) {
      case InnerType.text:
        this.rowEl.innerText = this.render();
        break;
      case InnerType.html:
        this.rowEl.innerHTML = this.render();
        break;
    }
  }

  // 每次插入dom节点都会调用render函数,它应该返回一个字符串。当innerType为InnerType.html时,可以返回html字符串。
  abstract render(): string;

  // 当dom节点插入到文档后调用onMount,此时可以访问rowEl
  onMount() {}
}

例如weblog部件

export default class WebLog extends WidgetInter<string> {
  type: WidgetType = WidgetType.weblog;

  innerType: InnerType = InnerType.text;

  constructor(id?: string) {
    super(id)
  }

  render(): string {
    return this.value || "";
  }
}