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 🙏

© 2026 – Pkg Stats / Ryan Hefner

textcat

v1.0.0

Published

A utility for styling content-editable text

Readme

$ npm install textcat
let attribute = {
    name: 'attribute-name',
    value: 'attribute-value'
}
// attached to a tag this would look like: <p attribute-name="attribute-value"></p>
import { CaretPos } from '../dist/TextCat.js'

CaretPos.Start // 'START',
CaretPos.End // 'END',
CaretPos.Middle // 'MIDDLE'
  • blockTag array: stores the block level tag(s) the character is associated with. Often the array is only one item deep like with 'p' or 'h1 - h6'; however, for a list item the array might look like this: ['ul', 'li'], and for a nested list: ['ul', 'li', 'ul', 'li'].

  • styleTag array: stores all the styles applied to the character. All styles need to be tags, so for styles that do not have designated tags use 'span' with appropriate attributes.

let exampleChar = {
  char: 'a',
  blockTags: [{ type: 'p', attributes: [] }],
  styleTags: [{ type: 'strong', attributes: [] }, { type: 'em', attributes: [] }]
}
let exampleTag = {
    type: 'p',
    attributes: [
        { name: 'class', value: 'test-class' }
    ]
}
<link rel="stylesheet" type="text/css" href="textCat/dist/TextCat.css">

or

@import "textCat/dist/TextCat.css";
import { TextAlign } from '../dist/TextCat.js'

TextAlign.Start // 'start',
TextAlign.End // 'end',
TextAlign.Left // 'left',
TextAlign.Right // 'right',
TextAlign.Center // 'center',
TextAlign.Justify // 'justify'
  • chars: is an array of all the characters with a block of text
  • breakpoints: is an array of numbers storing at which character in the chars array one blockTag ends and the next begins.
  • select: is an object with 3 properties:
    • start: stores the beginning of a selection as a number corrisponding to the array position of the character in the chars array. If the carot is active in a block of text but no characters are selected, both start and end will be the same number
    • end: stores the ending of a selection as a number corrisponding to the array position of the character in the chars array.
    • carotPosition: stores the CarotPos. This extra information is necessary because the position at the end of one line, and beginning of the next line are different, but if recorded only as a number representing a position within the chars array, these two positions would be the same number.
  • target: a HTML element matching the type of containerTag, usually also with the contenteditble property set to true.
  • Required: false

  • type: HTML element   An html element matching one of the element types conatined in containerTag which also contains text as children.

import { TextCat } from '../dist/TextCat.js'
let tco = TextCat.create()

Setting a specific element

import { TextCat } from '../dist/TextCat.js'
let element = document.getElementById("elementId");
let tco = TextCat.create(element)
  • Required: true

  • type: TextCatObject   An object representing a textblock including its selection state.

import { TextCat } from '../dist/TextCat.js'
let tco = TextCat.create()
// Do styling stuff to tco
let output = TxtCat.html(tco)
tco.target.innerHTML = output
  • Required: true

  • type: string   a string representing text to be injected into the text block often coming from the clipboard.

tco

  • Required: true

  • type: TextCatObject   An object representing a textblock including its selection state.

import { TextCat } from '../dist/TextCat.js'

//Get all containers with a property of contenteditable set to true. Note the contenteditable property can be placed on most any tag. This example assumes that contenteditable is only placed on container elements matching those of the containerTag property.
editableElements = document.querySelectorAll('[contenteditable="true"]')
//Add an event handler for each conatiner on the page
editableElements.forEach(element => {
    element.addEventListener('paste', pasteHandler)
}
//eventHandler where pasted text is injected into container.
function pasteHandler (event) {
    let pasteText = (event.clipboardData).getData('text')
    let tco = TextCat.create()
    tco = TextCat.insertText(pasteText, tco)
    let output = TextCat.html(tco)
    tco.target.innerHTML = output
}
  • Required: true

  • type: string   A string representing text a string representing a tag like 'p' or 'strong'.

attributes

  • Required: false

  • type: Attribute

import { TextCat } from '../dist/TextCat.js'

let tag = TextCat.createTag('strong')

//or

let attributes = [
    { name: 'style', value: 'color: #ff0000' },
    { name: 'class', value: 'custom-class' }
]
let tagWithAttributes = TextCat.createTag('strong', attributes)
  • Required: true

  • type: tag   a tag object of type 'p', 'h1' - 'h6', 'ul', and 'ol'.

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
let tag = TextCat.createTag('h1')
tco = TextCat.changeBlockTag(tag, tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
let selectedBlockTag = TextCat.getSelectedBlockTags(tco)
console.log(selectedBlockTag) //'p'

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
tco = TextCat.nestIn(tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
tco = TextCat.nestOut(tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output
  • Required: true

  • type: tag   An inline tag object like 'strong', 'em', 'u', 's', 'sup', 'sub', 'a' or 'span'.

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
let tag = TextCat.createTag('span', [{ name: 'style', value: 'color: #ff0000'}])
tco = TextCat.addStyleTag(tag, tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output
  • Required: true

  • type: tag An inline tag object like 'strong', 'em', 'u', 's', 'sup', 'sub', 'a' or 'span'.

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
let tag = TextCat.createTag('strong')
tco = TextCat.removeStyleTag(tag, tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
let selectedStyleTag = TextCat.getSelectedStyleTags(tco)
console.log(selectedStyleTag) //['strong', 'a', 'target|_blank', 'href|textcat.com']

alignment

  • Required: true

  • type: TextAlign

tco

  • Required: true

  • type: TextCatObject

import { TextCat, TextAlign } from '../dist/TextCat.js'

let tco = TextCat.create()
tco = TextCat.addTextAlign(TextAlign.Center, tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output

tco

  • Required: true

  • type: TextCatObject

import { TextCat, TextAlign } from '../dist/TextCat.js'

let tco = TextCat.create()
tco = TextCat.removeTextAlign(tco)
let output = TextCat.html(tco)
tco.target.innerHTML = output

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
let selectedStyleTag = TextCat.getTextAlign(tco)
console.log(selectedStyleTag) //'center'

tco

  • Required: true

  • type: TextCatObject

import { TextCat } from '../dist/TextCat.js'

let tco = TextCat.create()
//Do stuff to tco
let output = TextCat.html(tco)
tco.target.innerHTML = output
//Setting the innerHTML removes the selection
TextCat.setSelection(tco) //Selection is reapplied
import { TextCat } from '../dist/TextCat.js'

let containerTags = TextCat.containerTags
containerTags.push('custom-tag')
TextCat.containerTags = containerTags
import { TextCat } from '../dist/TextCat.js'

let blockTags = TextCat.blockTags
// 'li' needs to be the last item in the array so in this example the new tag is added to the beginning of the array
blockTags.unshift('custom-tag')
TextCat.blockTags = blockTags
import { TextCat } from '../dist/TextCat.js'

let listTags = TextCat.listTags
listTags.push('custom-list')
TextCat.listTags = listTags
import { TextCat } from '../dist/TextCat.js'

let tco
let currentStyleTags

document.addEventListener('selectionchange', selectionChanged) //Listen for selection changes
function selectionChanged (event) {
  let myTCO = TextCat.create() //Create TextCat object from selected text
  if (myTCO === null) {
    tco = null
  } else {
    tco = myTCO //Set TextCat object to global variable
    currentStyleTags = TextCat.getSelectedStyleTags(tco) //Get style tags of current selection
  }
}

document.querySelector('.p-tag').addEventListener('click', setP) //Button event handler
function setP () {  //Set selected block of text to a parageaph.
    let tag = TextCat.createTag('p') //Create new tag
    tco = TextCat.changeBlockTag(tag, tco) //Convert selected block of text to new BlockTag
    let output = TextCat.html(tco) //Generate updated html
    tco.target.innerHTML = output //Replace old html with newly generated html
    TextCat.setSelection(tco) //Reselect previously selected characters
}

document.querySelector('.h1-tag').addEventListener('click', setH1) //Button event handler
function setH1 () { //Set selected block of text to h1.
    let tag = TextCat.createTag('h1') //Create new tag
    tco = TextCat.changeBlockTag(tag, tco) //Convert selected block of text to new BlockTag
    let output = TextCat.html(tco) //Generate updated html
    tco.target.innerHTML = output //Replace old html with newly generated html
    TextCat.setSelection(tco) //Reselect previously selected characters
}

document.querySelector('.bold-tag').addEventListener('click', setBOLD) //Button event handler
function setBOLD () { //Toggle Bold style of selected characters
    if (tco !== null) {
        let tag = TextCat.createTag('strong') //Create new tag
        if (currentStyleTags.indexOf('strong') > -1) { //Check if style is alreadsy applied
            tco = TextCat.removeStyleTag(tag, tco) //Remove if already applied
        } else {
            tco = TextCat.addStyleTag(tag, tco) //Add if not applied
        }
        let output = TextCat.html(tco) //Generate updated html
        tco.target.innerHTML = output //Replace old html with newly generated html
        TextCat.setSelection(tco) //Reselect previously selected characters
    }
}