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

@dev-jelly/tinytipy-vue

v0.1.2

Published

Vue 3 adapter for tinytipy — diff-driven text morphing with a typing cursor.

Readme

@dev-jelly/tinytipy-vue

Vue 3 adapter for tinytipy — diff-driven text morphing with a typing cursor (Korean IME aware). Built on the framework-agnostic @dev-jelly/tinytipy.

  • <TextMorph from="..." to="..." /> — drop-in component.
  • useTextMorph({ from, to, ... }) — composable returning a reactive RenderState plus imperative controls, for full-markup control.

Install

pnpm add @dev-jelly/tinytipy-vue @dev-jelly/tinytipy vue

vue (>= 3.3) is a peer dependency.

One-time CSS import (required)

The adapter does not import CSS itself. Import the canonical stylesheet from core exactly once in your app entry:

// main.ts / app entry
import '@dev-jelly/tinytipy/styles.css';

The classes are: .tm-root, .tm-reserve, .tm-layer, .tm-run, .tm-run--keep | --remove | --add, .tm-cursor, .tm-sr-only.

<TextMorph> component

<script setup lang="ts">
import { ref } from 'vue';
import { TextMorph } from '@dev-jelly/tinytipy-vue';

const text = ref('현재 3초');
function correct() {
  text.value = '현재 300ms';
}
</script>

<template>
  <TextMorph from="현재 3초" :to="text" class="my-text" @click="correct" />
</template>

Props

| Prop | Type | Default | Notes | | --------------------- | ------------------------------------------------ | ----------- | -------------------------------------------------- | | from | string | required | Source text. | | to | string | required | Target text. | | timing | Partial<MorphTiming> | undefined | Per-step cadence overrides. | | autoPlay | boolean | true | Play on mount and after from/to change. | | instant | boolean | undefined | Skip straight to to. | | prefersReducedMotion| boolean | undefined | Force on/off; omit to auto-detect via matchMedia. | | reserveLayout | 'both' \| 'to' \| 'from' \| 'none' | 'both' | Hidden reserve text(s) to prevent reflow. | | cursorLayout | 'overlay' \| 'inline' | 'overlay' | Zero-width overlay cursor or legacy inline cursor. | | onDone | () => void | undefined | Fires once when the morph reaches to. |

A class / style / attrs passthrough lands on the root .tm-root element (Vue single-root fallthrough keeps tm-root first).

Imperative controls

Grab a template ref and call play() / pause() / reset() / finish():

<script setup lang="ts">
import { ref } from 'vue';
import { TextMorph } from '@dev-jelly/tinytipy-vue';
import type { TextMorphExposed } from '@dev-jelly/tinytipy-vue';

const morph = ref<TextMorphExposed>();
</script>

<template>
  <TextMorph ref="morph" from="a" to="b" />
  <button @click="morph?.finish()">Finish now</button>
</template>

useTextMorph() composable

For full control over the markup, drive it yourself from the live snapshot:

<script setup lang="ts">
import { ref } from 'vue';
import { useTextMorph } from '@dev-jelly/tinytipy-vue';

const from = ref('Hello');
const to = ref('Help');
const { state, play, pause, reset, finish } = useTextMorph({
  from,
  to,
  onDone: () => console.log('done'),
});
</script>

<template>
  <span class="my-root">
    <!-- map state.runs yourself, or use getRenderTokens(state) -->
    {{ state.runs.map((r) => r.text).join('') }}
  </span>
</template>

from / to / timing / instant / prefersReducedMotion each accept a plain value, a ref, or a getter. The composable creates one controller, re-plans on changes, and destroys it when the owning scope is disposed (component unmount or effectScope.stop()).

Rendering model

The component renders (styled by @dev-jelly/tinytipy/styles.css):

<span class="tm-root" data-cursor-layout="overlay">
  <span class="tm-reserve" aria-hidden="true">…</span>
  <span class="tm-layer" aria-hidden="true">
    <span class="tm-run tm-run--{kind}" data-status="{status}">…</span>
    <span class="tm-cursor" aria-hidden="true"></span>
    …
  </span>
  <span class="tm-sr-only">{final `to` text}</span>
</span>

The cursor trails the active run while editing and the full resolved text when done; the reserve layer sizes the box to prevent layout shift; .tm-sr-only always holds the final to text for assistive tech. Overlay is the default and does not consume horizontal space; inline preserves the legacy cursor spacing. Both modes support empty resolved text, inherit currentColor, and become steady under reduced motion.

License

MIT