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

tm-textarea

v0.1.1

Published

textarea with textmate syntax highlighting

Downloads

9

Readme

📄 tm-textarea

pnpm

Textarea with syntax highlighting powered by solid-js, @lume/element and vscode-oniguruma.

https://github.com/user-attachments/assets/6e785c75-75ae-4274-a904-5e1004153b76

Table of Contents

Installation

npm i tm tm-textarea
# or
yarn add tm tm-textarea
# or
pnpm add tm tm-textarea

Custom Element (tm-textarea)

The main export is a custom-element <tm-textarea/> powered by @lume/element

import type { Grammar, Theme } from 'tm-textarea/tm'

interface tmTextareaAttributes extends ComponentProps<'div'> {
  grammar?: Grammar
  theme?: Theme
  value?: string
  editable?: boolean
  stylesheet?: string | CSSStyleSheet
  onInput?: (event: InputEvent & { currentTarget: tmTextareaElement }) => void
}

Usage

import 'tm-textarea'

import { setCDN } from 'tm-textarea/cdn'
setCDN('/tm')

export default () => (
  <tm-textarea
    grammar="tsx"
    theme="andromeeda"
    value="const sum = (a: string, b: string) => a + b"
    editable={true}
    style={{
      padding: '10px',
      'font-size': '16pt',
    }}
    stylesheet="code, code * { font-style:normal; }"
    onInput={e => console.log(e.currentTarget.value)}
  />
)

Styling The Custom Element

Some DOM ::part() are exported.

  • root exposes root container.
  • code exposes the code tag.
  • line exposes the lines.
  • textarea exposes textarea to maybe change the selection color.
tm-textarea {
  min-height: 100%;
  min-width: 100%;
  padding: 10px;
  line-height: 16pt;
}

/* overwrite the theme background-color */
tm-textarea::part(root) {
  background: transparent;
  /* set a  color for meanwhile the theme loads */
  color: grey;
}

/* overwrite the selected text background color */
tm-textarea::part(textarea)::selection {
  background: deepskyblue;
}

/* add line-numbers */
tm-textarea::part(line)::before {
  display: inline-block;
  counter-reset: variable calc(var(--line-number) + 1);
  min-width: 7ch;
  content: counter(variable);
}

tm-textarea::part(textarea) {
  margin-left: 7ch;
}

The attribute stylesheet could be used as a last resort to customize the theme. In the following example we avoid italics in the rendered coded. The stylesheet is created, cached and possibly reused on the different tm-textarea instances.

<tm-textarea
  grammar="tsx"
  theme="andromeeda"
  value="const sum = (a: string, b: string) => a + b"
  stylesheet="code, code * { font-style: normal; }"
/>

Solid Component (tm-textarea/solid)

A solid component of tm-textarea is available at tm-textarea/solid

import { Grammar, Theme } from 'tm-textarea/tm'

interface tmTextareaProps extends Omit<ComponentProps<'div'>, 'style'> {
  grammar: Grammar
  theme: Theme
  value: string
  editable?: boolean
  style?: JSX.CSSProperties
  onInput?: (event: InputEvent & { currentTarget: HTMLTextAreaElement }) => void
}

Usage

import { TmTextarea } from 'tm-textarea/solid'

export default () => (
  <TmTextarea
    grammar="tsx"
    theme="min-light"
    value="const sum = (a: string, b: string) => a + b"
    editable={true}
    style={{
      padding: '10px',
      'font-size': '16pt',
    }}
    onInput={e => console.log(e.currentTarget.value)}
  />
)

CDN (tm-textarea/cdn)

To ease development we provide a way to set themes/grammars by setting the theme or grammar property with a string. Without configuration these are resolved to tm-themes and tm-grammars hosted on esm.sh.

To provide a way to customize how these keys are resolved we provide a global function setCDN, exported from tm-textarea. This function accepts as arguments either a base-url or a callback-function.

When given a base-url, this will be used to fetch

  • ${cdn}/tm-themes/themes/${theme}.json for the themes
  • ${cdn}/tm-grammars/grammars/${grammar}.json for the grammars
  • ${cdn}/vscode-oniguruma/release/onig.wasm for the oniguruma wasm-file

When given a callback, the returned string will be used to fetch instead.

Usage

import { setCDN } from 'tm-textarea/cdn'

// Set absolute base-url
setCDN('https://unpkg.com')

// Set relative base-url (for local hosting)
setCDN('/assets/tm')

// Use the callback-form
setCDN((type, id) => (type === 'oniguruma' ? `./oniguruma.wasm` : `./${type}/${id}.json`))

Themes & Grammars (tm-textarea/tm)

We export a list of textmate grammars and themes that are hosted on tm-grammars and tm-themes. These are used internally and maintained by shiki.

import type { Theme, Grammar } from 'tm-textarea/tm'
import { themes, grammars } from 'tm-textarea/tm'

Bindings

In addition to the core functionality, tm-textarea provides bindings that enhance the text editing experience by introducing keyboard shortcuts and behaviors that are common in code editors.

TabIndentation (tm-textarea/bindings/tab-indentation)

The TabIndentation binding enables tab and shift-tab indentation for a native textarea of tm-textarea. It allows users to easily increase or decrease the indentation level of lines or selected blocks of text.

import { TmTextareaElement } from 'src'

interface TabIndentation {
  /** Adds event listeners to the passed element for handling 'keydown' and 'input' events specific to indentation. */
  binding: (element: HTMLTextAreaElement | TmTextareaElement) => () => void;
  /** Dispatches `formatIndent` and `formatOutdent` event-types when pressing tab */
  onKeyDown: (event: KeyboardEvent & { currentTarget: TmTextareaElement | HTMLTextAreaElement }) => void;
  /** Add indentation on `formatIndent` and `formatOutdent` event-type.*/
  onInput: (event: InputEvent & { currentTarget: TmTextareaElement | HTMLTextAreaElement }) => void;
  /** Format leading whitespace of given string according to given tab-size. */
  format: (source: string, tabSize: number) => string;
  /** Utilities */
  getLeadingWhitespace: (source: string) => string;
  getLineStart: (value: string, position: number) => number;
  getIndentationSegments: (leadingWhitespace: string, tabSize: number) => string[];

Features

  • Indentation and Outdentation: Automatically adjusts the indentation level based on the tab size.
  • Multi-Line Selection: Supports indenting and outdenting multiple lines at once.
  • Customizable: Works with any specified tab size and can be customized further if needed.

Importing and Usage

import { TmTextarea } from 'tm-textarea/solid'
import { TabIndentation } from 'tm-textarea/bindings/tab-indentation'
import source from "./source"

const App = () => {
  return (
    <TmTextarea
      ref={TabIndentation.binding}
      value={TabIndentation.format(source)}
      grammar="tsx"
      theme="andromeeda"
    />
  )
}

export default App