SkillAgentSearch skills...

Ithkuil

A complete toolkit for New Ithkuil, converting to and from native text, glosses, both scripts, and JSON objects.

Install / Use

/learn @zsakowitz/Ithkuil
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

@zsnout/ithkuil

@zsnout/ithkuil is a package containing several utilities for working with New Ithkuil text. It can generate romanized text from JSON objects, parse text into JSON objects, and write in Ithkuil's writing system using SVG paths.

Custom Character Syntax

In addition to standard romanized Ithkuil, this package adds many new word kinds for writing script characters. See this Google Slide explaining the syntax for more information. Examples include ho1 for forcing a particlar kind of register, Q2world for forcing the transcription of "world" to be included, and QA27o'u to force a particular quaternary character.

Stability

This package is in active development, so expect change. That said, this package is completely suitable for use, and has even been tested somewhat.

Features

@zsnout/ithkuil can generate (as of May 29, 2023), parse (as of July 6, 2023), create block script for (as of August 1, 2023), and gloss (as of August 25, 2023) every kind of word in Ithkuil, including:

  • Formatives
  • Specialized Cs-root formatives
  • Specialized personal-reference formatives
  • Single- and dual- referentials
  • Combination referentials
  • Affixual adjuncts
  • Bias adjuncts
  • Modular adjuncts
  • Parsing adjuncts
  • Register adjuncts
  • Suppletive adjuncts
  • Referentials with suppletive adjuncts as heads

It also has many, many more functions. It can:

  • Map the values of various categories to their names
  • Provide descriptions for certain forms
  • Check whether a consonant form is phonotactically legal
  • Insert glottal stops into vowel forms
  • Compute Ca forms and their geminated counterparts
  • Validate incoming objects containing Ithkuilic word data

@zsnout/ithkuil has many more functions. However, these are too numerous to place into a single document. To experiment with all of @zsnout/ithkuil's functionality, install it and use your code editor's tools to see the available functions it exports.

Important Note

Unlike most packages available online, @zsnout/ithkuil does not have a top-level export. That is, directly importing from @zsnout/ithkuil will fail.

// INCORRECT:
import { parseWord } from "@zsnout/ithkuil"

Instead, always make sure to import from the proper sub-package. Currently, there are four sub-packages:

  • @zsnout/ithkuil/data, which has JSON information about all roots and affixes.
  • @zsnout/ithkuil/generate, which generates romanized text.
  • @zsnout/ithkuil/gloss, which turns Ithkuil words from the JSON format into gloss strings.
  • @zsnout/ithkuil/parse, which parses romanized text.
  • @zsnout/ithkuil/script, which generates SVG block script.
  • @zsnout/ithkuil/zod, which provides Zod validators for this project.
// CORRECT:
import { parseWord } from "@zsnout/ithkuil/parse"

JSX

If you're generating block script using the script module, you may find it helpful to use our mini JSX library to make things easier to work with. It's completely optional, but it makes writing code much simpler. Here's an example without JSX.

import {
  Anchor,
  CharacterRow,
  fitViewBox,
  textToScript,
} from "@zsnout/ithkuil/script"

function displayText(text: string) {
  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")

  const result = textToScript(text)

  if (result.ok) {
    const row = CharacterRow({
      children: result.value,
      compact: true,
    })

    const anchored = Anchor({
      at: "cc",
      children: row,
    })

    svg.appendChild(anchored)
  } else {
    const text = document.createElementNS("http://www.w3.org/2000/svg", "text")

    text.textContent = result.reason

    svg.appendChild(text)
  }

  document.body.append(svg)

  fitViewBox(svg)
}

displayText("Wattunkí ruyün")

And here's the same example with JSX:

import {
  Anchor,
  CharacterRow,
  HandleResult,
  fitViewBox,
  textToScript,
} from "@zsnout/ithkuil/script"

function displayText(text: string) {
  const svg = (
    <svg>
      <HandleResult
        ok={(value) => (
          <Anchor at="cc">
            <CharacterRow compact>{value}</CharacterRow>
          </Anchor>
        )}
        error={(reason) => <text>{reason}</text>}
      >
        {textToScript(text)}
      </HandleResult>
    </svg>
  ) as SVGSVGElement

  document.body.append(svg)

  fitViewBox(svg)
}

displayText("Wattunkí ruyün")

Before setting up JSX, make sure you're in a TypeScript project.

If you don't already have a JSX transform, add these lines to your tsconfig's compilerOptions to enable JSX:

{
  "compilerOptions": {
    ...
    "jsx": "react-jsx",
    "jsxImportSource": "@zsnout/ithkuil/script"
    ...
  }
}

If you're already using a JSX transform, write this at the top of any files you want to use @zsnout/ithkuil's JSX in.

/* @jsx react-jsx */
/* @jsxRuntime automatic */
/* @jsxImportSource @zsnout/ithkuil/script */

That's it!

Note that the JSX runtime is intentionally very minimal. There are no event listeners, no signals, no hooks, very few type definitions, and it only generates SVG elements.

Example 1

This example compiles a simple formative.

import { formativeToIthkuil } from "@zsnout/ithkuil/generate"

const result = formativeToIthkuil({
  root: "kš",
  type: "UNF/C",
})

console.log(result)
// kšala

Notice how the function formativeToIthkuil is able to infer default values for most of its arguments.

Example 2

Just being able to convert formative roots into Ithkuil isn't very exciting. Let's try an example with many options.

import { formativeToIthkuil } from "@zsnout/ithkuil/generate"

const result = formativeToIthkuil({
  type: "UNF/C",

  // Type-2 concatenation
  concatenationType: 2,

  // Completive Version
  version: "CPT",

  // Stem II
  stem: 2,

  // "kš" root
  root: "kš",

  // Dynamic Function
  function: "DYN",

  // Objective Specification
  specification: "OBJ",

  // Amalgamative Context
  context: "AMG",

  slotVAffixes: [
    // Referential Affix
    {
      // 1m:BEN Referent
      referents: ["1m:BEN"],

      // Ergative Case
      case: "ERG",
    },
  ],

  ca: {
    // Multiplex/Fuzzy/Connected Configuration
    configuration: "MFC",

    // Coalescent Affiliation
    affiliation: "COA",

    // Graduative Extension
    extension: "GRA",
  },

  // Repetitive Phase
  vn: "REP",
})

console.log(result)
// hwikšöeroeržžgeiha

Notice that in the above example, we didn't have to say whether the Vn slot was a Valence, Phase, etc. We simply specified "REP" as the Vn slot and @zsnout/ithkuil figured out the appropriate category automatically.

In addition, we only specified part of the Ca complex. We left out Monadic Perspective and Normal Essence, but @zsnout/ithkuil inferred those.

Example 3

Not only can @zsnout/ithkuil generate formatives, but it can also parse them using the parseFormative function. Let's try it out.

import { parseFormative } from "@zsnout/ithkuil/parse"

const result = parseFormative("malëuţřait")

console.log(result)

// {
//   type: "UNF/C",
//   concatenationType: undefined,
//   shortcut: false,
//   stem: 1,
//   version: "PRC",
//   root: "m",
//   context: "EXS",
//   specification: "BSC",
//   function: "STA",
//   slotVAffixes: [],
//   ca: {},
//   slotVIIAffixes: [
//     { type: 2, degree: 5, cs: "ţř" },
//     { type: 2, degree: 1, cs: "t" },
//   ],
//   mood: undefined,
//   caseScope: undefined,
//   vn: undefined,
//   case: undefined,
//   illocutionValidation: undefined,
// }

As you can see, parseFormative quickly turns formative strings into programmatically analyzable objects.

Note that concatenated formatives must be parsed separately from their parents, as shown below with the example "hlarrau-laza" (a tribe of people owned by a cat).

import { parseFormative } from "@zsnout/ithkuil/parse"

// Correct:

const result1 = parseFormative("hlarrau")
const result2 = parseFormative("laza")

console.log(result1, result2) // { type: "UNF/C", ... }, { type: "UNF/C", ... }

// Incorrect:

const result = parseFormative("hlarrau-laza")

console.log(result) // undefined

Note that parseFormative can output three types of values.

  1. If the formative doesn't have a valid slot structure (e.g. an invalid number of C and V forms), undefined is returned.

  2. If the formative has a valid slot structure but its slots are filled with invalid values (such as üö in the Vc slot), an error is thrown.

  3. If the formative is a valid formative, the parsed formative is returned.

Example 4

@zsnout/ithkuil doesn't just handle formatives; it can work with all types of Ithkuilic words. Let's try generating an affixual adjunct, as those are used frequently to move affixes out of formatives.

import { affixualAdjunctToIthkuil } from "@zsnout/ithkuil/generate"

const result = affixualAdjunctToIthkuil({
  affixes: [
    {
      type: 1,
      degree: 2,
      cs: "c",
    },
  ],
  scope: "VII:DOM",
})

console.log(result)
// äce

Example 5

This example creates a referential (1m.BEN-CTE-GID₁/3).

import { referentialToIthkuil } from "@zsnout/ithkuil/generate"

const result = referentialToIthkuil({
  referents: ["1m:BEN"],
  specification: "CTE",
  affixes: [
    {
      type: 1,
      degree: 3,
      cs: "c",
    },
  ],
})

console.log(result)
// raxtec

Example 6

This example shows the glossing capabilities of @zsnout/ithkuil.

import { glossWord } from "@zsnout/ithkuil/gloss/index.js"
import { parseWord } from "@zsnout/ithkuil/parse/index.js"

const result2 = parseWord("wetace")

if (result2) {
  const { short, full } = glossWord(result2)

  // S2-‘that one’-‘female’₁-ABS
  console.log(short)

  // stem_two-‘that one’-‘female’₁-absolutive
  console.log(full)
}

As you can see, `@zsnou

View on GitHub
GitHub Stars18
CategoryDevelopment
Updated1d ago
Forks2

Languages

TypeScript

Security Score

90/100

Audited on Mar 30, 2026

No findings