New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

kashi

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kashi

Singing at the top of my lungs

latest
Source
npmnpm
Version
4.0.0
Version published
Maintainers
1
Created
Source
GitLabNPMCDNKo-fi

Kashi

🎤 Kashi

🎯 Goal

This project is a dependency-free library that aims to provide a way to correctly read, format, structure, and display song lyrics.

📜 Features

  • 🎵 Process and list song lyrics in .lrc files
  • 💪 Easily integrates with React and other frameworks
  • ✉️ Implements the Observer pattern and emits events at each step of the process whenever something changes
  • ✏️ Allows you to enter custom text when the lyrics line is empty
  • 🎤 Synchronizes the lyrics with the music that is playing
  • 🎩 Supports multiple lyrics for the same song (useful to keep track of the original lyrics and their translation)
  • 🧞 Supports the Walaoke extension
  • 🕖️ Supports the A2 extension

Note: Support for fetching lyrics using a URL has been removed because it only supported public URLs.

Therefore, instead of refactoring to handle requests to private APIs, I opted to support only files. If a request is necessary, you can make it externally and then pass the returned file to Kashi!

🎨 How to use it in my project?

🙌 Classic scripts

The project is also exported using UMD, which means that all variables and classes such as the Kashi class is exposed globally, and can be used in any script imported after the library.

Note: You should change X.Y.Z to the library version number according to your needs, this is just an example. It is recommended to always use the latest version.

It is recommended that you pin to the latest stable version of the lib. However, if you always want to use the latest version, you can also use latest instead of a predefined version number. Be careful though, breaking changes may be introduced and your project may need to adapt to the changes.

<!DOCTYPE html>

<html>
  <head>
    <!-- ... -->
    <script src="https://unpkg.com/kashi@X.Y.Z/kashi.js" defer></script>
    <!-- ... -->
  </head>

  <body>
    <div id="kashi"></div>
  </body>
</html>
"use strict";

new Kashi({
  files. // Loaded from some input[type="file"] or anywhere else
  container: document.getElementById("kashi"),
});

📦️ ES6 Modules

The HTML is very similar to the classic scripts version, just change the .js extension to .mjs (and maybe will be necessary to adds a type="module" too), which will result in something similar to this:

Note: Please read the previous section to get the details involved in importing and choosing a package version, they are the same here.

<script
  src="https://unpkg.com/kashi@X.Y.Z/kashi.mjs"
  type="module"
  defer
></script>
import { Kashi } from "kashi";

// Usage is essentially the same as in the previous section

new Kashi({
  files. // Loaded from some input[type="file"] or anywhere else
  container: document.getElementById("kashi"),
});

⚛️ React

Install the lib using your favorite package manager.

npm install kashi

Since the library was designed primarily to be used with vanilla JS, a helper component needs to be created to encapsulate Kashi's behavior and make it simple to reuse throughout the application.

Note: There is TypeScript support, and even if your project doesn't use the JS superset, it should help VSCode and other editors provide autocomplete/code suggestions.

import { memo, useEffect, useRef } from "react";
import { Kashi, KashiProps } from "kashi";

import { api } from "@/services/axios";

// Example using Vite, React and TypeScript
export const KashiWrapper = memo(
  (props: Omit<KashiProps, "container"> & { url: string }) => {
    const ref = useRef<HTMLDivElement>(null);

    useEffect(() => {
      async function loadKashi() {
        if (!ref.current) {
          return;
        }

        if (!props.url) {
          ref.current.innerHTML = "";
          return;
        }

        try {
          const { url, ...rest } = props;

          const response = await api.get(url, { responseType: "blob" });

          new Kashi({
            ...rest,
            files: [response.data],
            container: ref.current,
          });
        } catch (error) {
          console.error("Error loading Kashi:", error);
        }
      }

      loadKashi();

      return () => {
        // Required to avoid duplication when React is in Strict Mode
        if (ref.current) {
          ref.current.innerHTML = "";
        }
      };
    }, [ref.current, props]);

    return <div className="kashi-wrapper" ref={ref} />;
  },
  (prevProps, nextProps) => {
    function compareArrays(arr1: unknown[], arr2: unknown[]) {
      if (arr1.length !== arr2.length) {
        return false;
      }

      return arr1.every((item, index) => item === arr2[index]);
    }

    function compareObjects(
      obj1: Omit<KashiProps, "container">,
      obj2: Omit<KashiProps, "container">,
    ) {
      const keys1 = Object.keys(obj1) as Array<
        keyof Omit<KashiProps, "container">
      >;
      const keys2 = Object.keys(obj2) as Array<
        keyof Omit<KashiProps, "container">
      >;

      if (keys1.length !== keys2.length) {
        return false;
      }

      for (const key of keys1) {
        const val1 = obj1[key];
        const val2 = obj2[key];

        if (Array.isArray(val1) && Array.isArray(val2)) {
          if (!compareArrays(val1, val2)) {
            return false;
          }
        } else if (val1 !== val2) {
          return false;
        }
      }

      return true;
    }

    return compareObjects(prevProps, nextProps);
  },
);

🧐 Constructor properties

You must pass some properties to Kashi to define what lyrics display and where. Here are its specifications:

PropertyTypeDefault valueIs required?Description
filesBlob[]-YesLyrics files
containerHTMLDivElement-YesElement where the lyrics will be inserted
emptyLineTextstring...NoCustom text for empty lines of the lyrics
noLyricsTextstring-NoCustom text for when there are no lyrics

👾 Generated HTML structure

The div#kashi represents the container passed to Kashi where the song lyrics will be inserted.

Each line of lyrics present in the lrc files will be wrapped by a <div></div> tag and inserted into the container.

Here's an example:

<div id="kashi">
  <div
    data-time="00:17.55"
    data-ms-time="17550"
    data-empty="false"
    data-aria-current="false"
  >
    <span>Telling myself, "I won't go there"</span>
    <br />
    <span> Dizendo a mim mesmo, "eu não vou lá" </span>
  </div>

  <div
    data-time="00:21.24"
    data-ms-time="21240"
    data-empty="false"
    data-aria-current="false"
  >
    <span>Oh, but I know that I won't care</span>
    <br />
    <span>Oh, mas eu sei que não vou me importar</span>
  </div>

  <!-- ... -->
</div>

📂 Methods and attributes

The instance generated by Kashi has some public methods and attributes that can be used to query or change properties on the fly.

NameTypeDescription
filesAttributeReturns the files from the current lyrics
emptyLineTextAttributeReturns the text set for empty lines
noLyricsTextAttributeReturns the text set for when there are no lyrics
setFilesMethodFunction capable of changing the current lyrics files by passing the the new files
setEmptyLineTextMethodFunction capable of changing the text defined for empty lines
setNoLyricsTextMethodFunction capable of changing the text defined for when there are no lyrics
subscribeMethodFunction capable of defining a callback to be executed when a given event is triggered
unsubscribeMethodFunction capable of making a callback to stop listening to an event
notifyMethodFunction capable of triggering an event

🍾 Events

When creating a new instance using Kashi you will have access to the subscribe, unsubscribe and notify methods, these methods can be used respectively to listen for an event, stop listening for an event and manually trigger an event. Below is the list of events triggered internally:

EventDataTrigger
filesSet{ files: Blob[] }When calling the setFiles method
emptyLineTextSet{ emptyLineText: string }When calling the setEmptyLineText method
noLyricsTextSet{ noLyricsText: text }When calling the setNoLyricsText method
lyricLinesUpdated{ lyricLines: string[] }When inserting/updating lyrics in HTML

🤔 How do I run the project on my machine?

The first step is to clone the project, either via terminal or even by downloading the compressed file (.zip). After that, go ahead.

🛠️ Requirements

  • NodeJS and NPM

✨ Running the project

With the dependencies properly installed, still in the terminal, run npm start.

Create a simple demo project using vanilla HTML/JS and use the files in the dist folder for testing.

You can also create a demo project using React and use npm link.

🎉 If everything went well...

Now you are running the project beautifully!

✏️ License

This project is under the GPL v3 license. See the LICENSE for more information.

Made with 💙 by lucasmc64 👋 Get in touch!

Keywords

lyrics

FAQs

Package last updated on 07 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts