feat(i18n): set up glossary (#2198)

This pull request introduces the initial glossary for the project to
improve the translation quality.

Implements #2148.

### TODOs

- [x] Add the definition YAML
- [x] Add a generator to produce Crowdin-compatible (TBX v3) format
- [x] AGENTS.md
- [ ] ~Workflow~ (Split to another PR)
- [ ] ~Skills~ (Same as above)
This commit is contained in:
Makito
2026-08-03 02:01:43 +09:00
committed by GitHub
parent be3621ef39
commit b26ea3123d
11 changed files with 1388 additions and 24 deletions
+4
View File
@@ -143,3 +143,7 @@ apps/stage-tamagotchi/electron.vite.config.*.mjs
docs/ai/context/verifications/
.playwright-mcp/
# Generated from packages/i18n/glossary/terms.yaml at upload time. Never committed, so it
# cannot drift from its source.
packages/i18n/glossary/glossary.tbx
+31
View File
@@ -143,6 +143,37 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
- Add/modify translations in `packages/i18n`; avoid scattering i18n across apps/packages.
### Glossary
`packages/i18n/glossary/terms.yaml` gives the approved English term for each product concept.
`pnpm -F @proj-airi/i18n glossary:build` writes the TBX file that Crowdin imports. `schema.ts`
documents each field.
- Read `terms.yaml` before you write or change a string that a user sees. Use the term it gives.
- Do not edit `packages/i18n/glossary/translations/`. The next Crowdin download replaces it.
- Take terms from the interface. Search the documentation to confirm a term, not to find one.
**Add a term when it prevents one of two failures:**
- A translator who reads only the source string chooses the wrong word.
- Two translators choose different words, and both words are correct.
**Do not add a term when:**
- It is code. A translated control token, JSON key, file name, or package name breaks the
application, and the application shows no error.
- It is a common English word with its usual meaning, such as "Speed" or "Volume".
- Its parts each have an entry and it says no more than its parts, such as "VRM model".
- One feature uses it, and only two translators could disagree about it. But keep a rare term if a
translator can get it wrong: `Tachie` occurs two times and is the strongest entry in the file.
`terms.yaml` holds no rationale, because each field must map to a TBX element. Give the reason in
the pull request.
**Write a definition in one sentence.** Describe the thing, not the word, and put a negative rule in
the `note`. Keep each sentence short and active: translators read them, and most do not read English
as a first language.
## CSS/UNO
- Use/extend UnoCSS shortcuts in `uno.config.ts`.
+2
View File
@@ -52,6 +52,7 @@ words:
- clippy
- clustr
- cmdorctrl
- codenames
- codesign
- collectblock
- colorjs
@@ -189,6 +190,7 @@ words:
- MeshToonMaterial
- meteocons
- micvad
- MikuMikuDance
- mineflayer
- mingcute
- minimizable
+284
View File
@@ -0,0 +1,284 @@
import type { Concept } from './schema'
import { readdir, readFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import { parse } from 'yaml'
import { buildTbx } from './build-tbx'
import { parseGlossary } from './schema'
const here = dirname(fileURLToPath(import.meta.url))
function concept(overrides: Partial<Concept> = {}): Concept {
return {
id: 'stage',
subject: 'application',
translatable: true,
definition: 'The surface of the application that shows the character.',
terms: [{ 'text': 'Stage', 'part-of-speech': 'noun', 'status': 'preferred' }],
...overrides,
}
}
describe('buildTbx', () => {
it('writes the document shape that a Crowdin export uses', () => {
const xml = buildTbx([concept()], 'test')
expect(xml.startsWith('<?xml version="1.0" encoding="UTF-8"?>\n')).toBe(true)
expect(xml).toContain('TBXcoreStructV03_TBX-Basic_integrated.rng')
expect(xml).toContain('TBX-Basic_DCA.sch')
expect(xml).toContain('xmlns="urn:iso:std:iso:30042:ed-2"')
expect(xml).toContain('type="TBX-Basic"')
expect(xml).toContain('style="dca"')
expect(xml).toContain('<p>test</p>')
expect(xml).toContain('<conceptEntry id="stage">')
expect(xml).toContain('<langSec xml:lang="en">')
// Crowdin's header carries a sourceDesc and nothing else.
expect(xml).not.toContain('<titleStmt>')
})
it('indents the elements the way a Crowdin export does', () => {
const xml = buildTbx([concept()], 'test')
expect(xml).toContain('\n <tbxHeader>')
expect(xml).toContain('\n <conceptEntry id="stage">')
// Whitespace inside a definition is content, so it must not gain indentation.
expect(xml).toContain('<descrip type="definition">The surface of the application that shows the character.</descrip>')
})
it('puts langSec after the concept-level children', () => {
const xml = buildTbx([concept()], 'test')
// TBX-Basic fixes this order. A langSec before a descrip is invalid, and a schema
// validator rejects the file rather than importing it partly.
expect(xml.indexOf('subjectField')).toBeLessThan(xml.indexOf('<langSec'))
expect(xml.indexOf('type="definition"')).toBeLessThan(xml.indexOf('<langSec'))
})
it('puts term before the termNote elements', () => {
const xml = buildTbx([concept()], 'test')
expect(xml.indexOf('<term>')).toBeLessThan(xml.indexOf('partOfSpeech'))
expect(xml.indexOf('partOfSpeech')).toBeLessThan(xml.indexOf('administrativeStatus'))
})
it('writes the translatable flag that Crowdin reads', () => {
// A Crowdin export carries the flag as a concept-level descrip, ahead of everything
// else. TBX-Basic has no such data category, so this is a Crowdin extension.
expect(buildTbx([concept({ translatable: false })], 'test'))
.toContain('<descrip type="translatable">no</descrip>')
expect(buildTbx([concept({ translatable: true })], 'test'))
.toContain('<descrip type="translatable">yes</descrip>')
})
it('puts the translatable flag before the other concept children', () => {
const xml = buildTbx([concept()], 'test')
expect(xml.indexOf('type="translatable"')).toBeLessThan(xml.indexOf('type="subjectField"'))
})
it('writes the context of each term that has one', () => {
const xml = buildTbx([concept({
terms: [
{ 'text': 'Stage', 'part-of-speech': 'noun', 'status': 'preferred', 'context': 'Disable Stage Transitions' },
{ 'text': 'Scene', 'part-of-speech': 'noun', 'status': 'deprecated', 'type': 'variant' },
],
})], 'test')
// TBX-Basic allows a context at term level only, so it belongs to the term whose
// sentence it is rather than to the concept.
expect(xml).toContain('<descrip type="context">Disable Stage Transitions</descrip>')
expect(xml.match(/type="context"/g)).toHaveLength(1)
})
it('puts the context and the note after the termNote elements', () => {
const xml = buildTbx([concept({
terms: [{ 'text': 'Stage', 'part-of-speech': 'noun', 'status': 'preferred', 'context': 'A sentence', 'note': 'A note' }],
})], 'test')
expect(xml.indexOf('administrativeStatus')).toBeLessThan(xml.indexOf('type="context"'))
expect(xml.indexOf('type="context"')).toBeLessThan(xml.indexOf('<note>A note</note>'))
})
it('maps each status to the suffixed value that Crowdin writes', () => {
const xml = buildTbx([concept({
terms: [
{ 'text': 'Character Card', 'part-of-speech': 'noun', 'status': 'preferred' },
{ 'text': 'Card', 'part-of-speech': 'noun', 'status': 'admitted', 'type': 'shortForm' },
{ 'text': 'Deck', 'part-of-speech': 'noun', 'status': 'deprecated', 'gender': 'feminine' },
],
})], 'test')
expect(xml).toContain('<termNote type="administrativeStatus">preferredTerm-admn-sts</termNote>')
expect(xml).toContain('<termNote type="administrativeStatus">admittedTerm-admn-sts</termNote>')
expect(xml).toContain('<termNote type="administrativeStatus">deprecatedTerm-admn-sts</termNote>')
expect(xml).toContain('<termNote type="termType">shortForm</termNote>')
expect(xml).toContain('<termNote type="grammaticalGender">feminine</termNote>')
})
it('escapes the characters that would otherwise break the XML', () => {
const xml = buildTbx([concept({ definition: 'A tag <b> & an "attribute".' })], 'test')
expect(xml).toContain('&#x3C;b>')
expect(xml).toContain('&#x26;')
expect(xml).not.toContain('<b>')
})
it('collapses the wrapping of a YAML folded block but keeps the paragraphs', () => {
const note = parse('note: >\n One\n line.\n\n A second\n paragraph.\n').note as string
const xml = buildTbx([concept({ note })], 'test')
expect(note).toBe('One line.\nA second paragraph.\n')
expect(xml).toContain('One line.\n\nA second paragraph.')
})
})
describe('parseGlossary', () => {
it('rejects two concepts that share an id', () => {
expect(() => parseGlossary([concept(), concept()])).toThrow(/unique/)
})
it('allows more than one preferred term, as Crowdin does', () => {
// The export from project 816610 gives both `AIRI` and `Project AIRI` the preferred
// status inside one concept, so nothing here may forbid it.
const twoPreferred = concept({
terms: [
{ 'text': 'AIRI', 'part-of-speech': 'proper noun', 'status': 'preferred' },
{ 'text': 'Project AIRI', 'part-of-speech': 'proper noun', 'status': 'preferred' },
],
})
expect(parseGlossary([twoPreferred])).toHaveLength(1)
})
it('accepts the spelling of proper noun that Crowdin writes', () => {
// ROOT CAUSE:
//
// An earlier version required `properNoun`, which is what the TBX-Basic specification
// says, and rejected "proper noun" with a space. A glossary exported from project 816610
// writes the spaced form on every proper noun, so the requirement was backwards: it
// would have rejected the data Crowdin itself produces.
const spaced = concept({ terms: [{ 'text': 'AIRI', 'part-of-speech': 'proper noun', 'status': 'preferred' }] })
expect(parseGlossary([spaced])).toHaveLength(1)
expect(() => parseGlossary([concept({ terms: [{ 'text': 'AIRI', 'part-of-speech': 'properNoun', 'status': 'preferred' }] })])).toThrow()
})
it('rejects an id that is not kebab case', () => {
expect(() => parseGlossary([concept({ id: 'Stage Profile' })])).toThrow(/kebab/)
})
})
describe('terms.yaml', () => {
async function loadConcepts(): Promise<Concept[]> {
return parseGlossary(parse(await readFile(join(here, 'terms.yaml'), 'utf8')))
}
/** Every scalar leaf of the English locale, which is what a user reads. */
async function loadEnglishStrings(): Promise<string[]> {
const root = join(here, '..', 'src', 'locales', 'en')
const found: string[] = []
function walk(node: unknown): void {
if (typeof node === 'string')
found.push(node)
else if (Array.isArray(node))
node.forEach(walk)
else if (node && typeof node === 'object')
Object.values(node).forEach(walk)
}
async function read(directory: string): Promise<void> {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name)
if (entry.isDirectory())
await read(path)
else if (entry.name.endsWith('.yaml'))
walk(parse(await readFile(path, 'utf8')))
}
}
await read(root)
return found
}
it('validates against the schema', async () => {
const concepts = await loadConcepts()
expect(concepts.length).toBeGreaterThan(0)
})
it('produces TBX for every concept', async () => {
const concepts = await loadConcepts()
const xml = buildTbx(concepts, 'test')
expect(xml.match(/<conceptEntry /g)).toHaveLength(concepts.length)
})
it('holds no term that neither the interface nor the English documentation uses', async () => {
const concepts = await loadConcepts()
// The glossary shares one Crowdin container with the documentation terminology, so a
// term may live in prose rather than in a string. `Project AIRI` appears six times in
// README.md and in no interface string, and `Neuro Sama` only under docs/content/en.
//
// The check still has teeth. A term that appears in none of these places can never match
// in the editor, so it cannot help a translator. `Ayaka Neko` was such a term, and it is
// the reason this check runs over the documentation rather than being deleted.
const repository = join(here, '..', '..', '..')
const prose = [await readFile(join(repository, 'README.md'), 'utf8')]
async function readDocs(directory: string): Promise<void> {
for (const entry of await readdir(directory, { withFileTypes: true })) {
const path = join(directory, entry.name)
if (entry.isDirectory())
await readDocs(path)
else if (entry.name.endsWith('.md'))
prose.push(await readFile(path, 'utf8'))
}
}
await readDocs(join(repository, 'docs', 'content', 'en'))
const haystack = [...await loadEnglishStrings(), ...prose].join('\n').toLowerCase()
// The `documentation` concepts were adopted from the Crowdin glossary, and Crowdin is
// where they are maintained. Repository text is not their source, so checking them
// against it would delete data this file exists to preserve. `Ayaka Neko` is one: the
// reversed name order, which no file here writes.
const missing = concepts
.filter(c => c.subject !== 'documentation')
.flatMap(c => c.terms.filter(t => !haystack.includes(t.text.toLowerCase())).map(t => t.text))
expect(missing).toEqual([])
})
it('gives every context the term it belongs to', async () => {
const concepts = await loadConcepts()
// A context shows the term in use. Usually it is a real interface string, but for the
// two codenames it cannot be: every interface string containing them is one this
// glossary rules against. So the check is not "does the locale contain it" but "does it
// mention its own term", which catches a sentence pasted onto the wrong entry.
const unrelated = concepts.flatMap(c => c.terms
.filter(t => t.context && !t.context.toLowerCase().includes(t.text.toLowerCase()))
.map(t => `${c.id} / ${t.text}`))
expect(unrelated).toEqual([])
})
it('holds no control token from the system prompt', async () => {
const concepts = await loadConcepts()
// base.yaml carries the streaming control tokens to the translators. Those are code.
// A translated token breaks stage playback with no error message, so a token must never
// reach the glossary and become a term a translator is invited to work on.
const code = concepts.flatMap(c => c.terms
.filter(t => ['ACT', 'DELAY', 'CALL'].includes(t.text) || /[/{}<>]/.test(t.text))
.map(t => t.text))
expect(code).toEqual([])
})
})
+211
View File
@@ -0,0 +1,211 @@
// cspell:words descrip
import type { Element } from 'xast'
import type { Concept, Term } from './schema'
import process from 'node:process'
import { readFile, writeFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { toXml } from 'xast-util-to-xml'
import { x } from 'xastscript'
import { parse } from 'yaml'
import { parseGlossary } from './schema'
/**
* The XML namespace of ISO 30042:2019, which the tooling calls TBX version 3. The `ed-2`
* segment is the second edition of the ISO standard, not the second version of TBX.
*/
const TBX_NAMESPACE = 'urn:iso:std:iso:30042:ed-2'
/** The source language of the glossary. Crowdin owns every other language. */
const SOURCE_LANGUAGE = 'en'
/**
* The two schemas that Crowdin names in the exports it produces.
*
* They are processing instructions rather than elements, so an XML editor can validate the
* file. Writing the same pair keeps a diff against a re-download small.
*/
const XML_MODELS = [
'<?xml-model href="https://raw.githubusercontent.com/LTAC-Global/TBX-Basic_dialect/master/DCA/TBXcoreStructV03_TBX-Basic_integrated.rng" type="application/xml" schematypens="http://relaxng.org/ns/structure/1.0"?>',
'<?xml-model href="https://raw.githubusercontent.com/LTAC-Global/TBX-Basic_dialect/master/DCA/TBX-Basic_DCA.sch" type="application/xml" schematypens="http://purl.oclc.org/dsdl/schematron"?>',
]
/**
* How Crowdin writes an acceptability rating.
*
* NOTICE:
* The suffixed forms come from ISO 12620, and a Crowdin export writes them literally. Only
* `preferredTerm-admn-sts` is confirmed by the export we have, because every term in it is
* preferred. The other two follow the same pattern, and the first import should confirm
* them.
*/
const ADMINISTRATIVE_STATUS: Record<Term['status'], string> = {
preferred: 'preferredTerm-admn-sts',
admitted: 'admittedTerm-admn-sts',
deprecated: 'deprecatedTerm-admn-sts',
}
/**
* Turns the text of a YAML folded block into paragraphs.
*
* A folded block (`>`) joins each wrapped line with a space and turns a blank line into one
* newline character. So a newline that survives parsing is always a paragraph break, and
* the line width of the source file never reaches the output.
*
* NOTICE:
* Splitting on two or more newlines looks correct and is wrong. YAML gives a blank line a
* single newline, so that pattern matches nothing and every paragraph merges into one.
*
* Before:
* - "One paragraph.\nA second paragraph."
*
* After:
* - "One paragraph.\n\nA second paragraph."
*/
function collapse(text: string): string {
return text.split(/\n+/).map(paragraph => paragraph.replace(/\s+/g, ' ').trim()).filter(Boolean).join('\n\n')
}
/**
* Builds one `conceptEntry` element.
*
* TBX-Basic fixes the order inside each section, and the order below is not cosmetic:
*
* - inside `conceptEntry`, every `langSec` comes after the other children,
* - inside `langSec`, every `termSec` comes after the other children,
* - inside `termSec`, `term` comes first, then the `termNote` elements, then the rest.
*
*/
function buildConceptEntry(concept: Concept): Element {
const conceptChildren: Element[] = [
// Crowdin's own export writes the flag first, as a concept-level descrip with the value
// "yes" or "no". This is a Crowdin extension: TBX-Basic has no such data category.
x('descrip', { type: 'translatable' }, concept.translatable ? 'yes' : 'no'),
x('descrip', { type: 'subjectField' }, concept.subject),
x('descrip', { type: 'definition' }, collapse(concept.definition)),
]
if (concept.note)
conceptChildren.push(x('note', collapse(concept.note)))
if (concept.url)
conceptChildren.push(x('xref', { type: 'externalCrossReference', target: concept.url }, concept.url))
const termSections = concept.terms.map((term) => {
const termChildren: Element[] = [
x('term', term.text),
x('termNote', { type: 'partOfSpeech' }, term['part-of-speech']),
x('termNote', { type: 'administrativeStatus' }, ADMINISTRATIVE_STATUS[term.status]),
]
if (term.type)
termChildren.push(x('termNote', { type: 'termType' }, term.type))
if (term.gender)
termChildren.push(x('termNote', { type: 'grammaticalGender' }, term.gender))
// TBX-Basic fixes the order inside a termSec: term, then every termNote, then the rest.
if (term.context)
termChildren.push(x('descrip', { type: 'context' }, term.context))
if (term.note)
termChildren.push(x('note', collapse(term.note)))
return x('termSec', termChildren)
})
conceptChildren.push(x('langSec', { 'xml:lang': SOURCE_LANGUAGE }, termSections))
return x('conceptEntry', { id: concept.id }, conceptChildren)
}
/**
* Renders the glossary as a TBX-Basic document.
*
* The result is a complete XML document with the declaration, so a caller writes it to disk
* unchanged. It carries the English terms only; Crowdin owns every other language, and
* uploading target terms would overwrite the work of a translator.
*
* @param concepts Validated concepts, in the order they appear in `terms.yaml`.
* @param source A description of where the data came from, recorded in the TBX header.
*/
export function buildTbx(concepts: Concept[], source: string): string {
const tree = x(
'tbx',
// The root carries all four attributes that TBX-Basic requires. `style="dca"` selects
// the data-category-as-attribute style, which is the one the schema values assume.
{ 'type': 'TBX-Basic', 'style': 'dca', 'xml:lang': SOURCE_LANGUAGE, 'xmlns': TBX_NAMESPACE },
[
// The header carries a sourceDesc and nothing else, which is the shape Crowdin writes.
x('tbxHeader', [x('fileDesc', [x('sourceDesc', [x('p', source)])])]),
x('text', [x('body', concepts.map(buildConceptEntry))]),
],
)
indent(tree, 0)
return ['<?xml version="1.0" encoding="UTF-8"?>', ...XML_MODELS, toXml(tree), ''].join('\n')
}
/**
* Adds whitespace between the children of an element, in place.
*
* Crowdin writes its exports with four-space indentation, and the serializer produces one
* long line. Matching the layout keeps a diff against a re-download readable.
*
* An element whose children include text is left alone, because whitespace inside a
* definition or a note is content, not layout.
*/
function indent(node: Element, depth: number): void {
const children = node.children.filter(child => child.type === 'element')
if (children.length !== node.children.length || children.length === 0)
return
const inner = `\n${' '.repeat(depth + 1)}`
const outer = `\n${' '.repeat(depth)}`
for (const child of children)
indent(child, depth + 1)
node.children = [
...children.flatMap(child => [{ type: 'text' as const, value: inner }, child]),
{ type: 'text' as const, value: outer },
]
}
/**
* Reads `terms.yaml`, validates it, and writes the TBX file.
*
* Call stack:
*
* main
* -> {@link parseGlossary}
* -> {@link buildTbx}
* -> buildConceptEntry
* -> composeNote
*/
async function main(): Promise<void> {
const here = dirname(fileURLToPath(import.meta.url))
const input = join(here, 'terms.yaml')
const output = process.argv[2] ?? join(here, 'glossary.tbx')
const concepts = parseGlossary(parse(await readFile(input, 'utf8')))
await writeFile(output, buildTbx(concepts, 'packages/i18n/glossary/terms.yaml'), 'utf8')
const terms = concepts.reduce((total, concept) => total + concept.terms.length, 0)
process.stdout.write(`wrote ${output}: ${concepts.length} concepts, ${terms} terms\n`)
}
// Only run when this file is the entry point, so that the test can import buildTbx without
// writing a file.
if (process.argv[1] && import.meta.url === `file://${process.argv[1]}`) {
main().catch((error: unknown) => {
process.stderr.write(`${String(error)}\n`)
process.exitCode = 1
})
}
+119
View File
@@ -0,0 +1,119 @@
import * as v from 'valibot'
/**
* Part of speech, spelled the way Crowdin spells it.
*
* NOTICE:
* TBX-Basic writes this value as `properNoun` (DC-0396), and Crowdin writes it as "proper
* noun" with a space. Crowdin reads what we upload, so Crowdin wins. Confirmed against a
* glossary exported from project 816610, where every proper noun reads "proper noun".
*/
const PartOfSpeechSchema = v.picklist([
'adjective',
'adposition',
'adverb',
'auxiliary',
'coordinating conjunction',
'determiner',
'interjection',
'noun',
'numeral',
'particle',
'pronoun',
'proper noun',
'subordinating conjunction',
'verb',
'other',
])
/** Term type. TBX-Basic allows these values and no others (DC-2677). */
const TermTypeSchema = v.picklist(['fullForm', 'acronym', 'abbreviation', 'shortForm', 'variant', 'phrase'])
/**
* How acceptable a term is (DC-0070). ISO 704:2022 calls it an acceptability rating.
*
* These are the readable forms. The generator maps them to the suffixed values Crowdin
* writes, such as `preferredTerm-admn-sts`. Crowdin permits more than one preferred term in
* a concept, so nothing here limits the count.
*/
const UsageStatusSchema = v.picklist(['preferred', 'admitted', 'deprecated'])
/** Grammatical gender (DC-0245). */
const GenderSchema = v.picklist(['masculine', 'feminine', 'neuter', 'other'])
/**
* One term of a concept.
*
* Every field maps to one TBX element, and the field names follow TBX rather than any name
* of our own, so that the mapping stays obvious:
*
* text -> <term>
* part-of-speech -> <termNote type="partOfSpeech">
* status -> <termNote type="administrativeStatus">
* type -> <termNote type="termType">
* gender -> <termNote type="grammaticalGender">
* context -> <descrip type="context">
* note -> <note>
*/
const TermSchema = v.object({
'text': v.pipe(v.string(), v.nonEmpty()),
'part-of-speech': PartOfSpeechSchema,
'status': UsageStatusSchema,
'type': v.optional(TermTypeSchema),
'gender': v.optional(GenderSchema),
/** A sentence that shows the term in use. TBX allows a context at term level only. */
'context': v.optional(v.string()),
'note': v.optional(v.string()),
})
/**
* One concept, which is one entry of the glossary.
*
* As with a term, every field maps to one TBX element:
*
* id -> <conceptEntry id="...">
* subject -> <descrip type="subjectField">
* translatable -> <descrip type="translatable">, a Crowdin extension
* definition -> <descrip type="definition">
* note -> <note>
* url -> <xref type="externalCrossReference">
* terms -> <langSec xml:lang="en"> with one <termSec> for each term
*/
const ConceptSchema = v.object({
/**
* Stable identifier in kebab case. A change here creates a new concept in Crowdin rather
* than updating the existing one, so it must survive a rename of the terms.
*/
id: v.pipe(v.string(), v.regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, 'id must be kebab case')),
subject: v.pipe(v.string(), v.nonEmpty()),
translatable: v.boolean(),
/** One sentence. Write two only when one cannot carry the meaning. */
definition: v.pipe(v.string(), v.nonEmpty()),
note: v.optional(v.string()),
url: v.optional(v.pipe(v.string(), v.url())),
terms: v.pipe(v.array(TermSchema), v.minLength(1)),
})
export const GlossarySchema = v.pipe(
v.array(ConceptSchema),
v.minLength(1),
v.check(
concepts => new Set(concepts.map(c => c.id)).size === concepts.length,
'every concept id must be unique',
),
)
export type Term = v.InferOutput<typeof TermSchema>
export type Concept = v.InferOutput<typeof ConceptSchema>
/**
* Parses and validates the contents of `terms.yaml`.
*
* Throws a `ValiError` naming the failing path when the data does not match, so a broken
* entry fails the build rather than producing invalid TBX.
*
* @param data The result of parsing the YAML file.
*/
export function parseGlossary(data: unknown): Concept[] {
return v.parse(GlossarySchema, data)
}
+476
View File
@@ -0,0 +1,476 @@
# AIRI terminology glossary — English source of truth.
#
# A generator turns this file into TBX version 3 and sends it to the Crowdin glossary, where
# the editor marks each term and offers the approved translation.
#
# Every field maps to one TBX element and carries the TBX name. schema.ts holds the mapping.
# AGENTS.md, under "i18n / Glossary", says when a term belongs here and when it does not.
#
# This repository owns English. Crowdin owns the other 8 languages, and the translated terms
# arrive in packages/i18n/glossary/translations/ through a pull request.
# ---------------------------------------------------------------------------------------
# Names from the documentation
#
# These concepts were already in the Crowdin glossary, added by hand. They live here so that
# this file describes the whole glossary and an upload cannot destroy them.
# ---------------------------------------------------------------------------------------
- id: neuro-sama
subject: documentation
translatable: false
definition: An AI VTuber made by another developer.
note: The hyphenated form is the product's own spelling.
terms:
- text: Neuro-sama
part-of-speech: proper noun
status: preferred
type: fullForm
context: Neuro-sama uses 2D model driven by framework developed by Live2D Inc
- text: Neuro Sama
part-of-speech: proper noun
status: admitted
type: variant
- text: Neuro
part-of-speech: proper noun
status: admitted
type: shortForm
- id: neko-ayaka
subject: documentation
translatable: true
definition: A maintainer of this project.
note: Both name orders name one person.
terms:
- text: Neko Ayaka
part-of-speech: proper noun
status: preferred
gender: feminine
type: fullForm
context: (from Neko Ayaka) Good morning! You are finally awake.
- text: Ayaka Neko
part-of-speech: proper noun
status: admitted
gender: feminine
type: variant
note: The reversed name order.
- text: Neko
part-of-speech: proper noun
status: admitted
gender: feminine
type: shortForm
note: 'The short form, as in "always call her Neko for simplicity".'
- id: devlog
subject: documentation
translatable: false
definition: A development log that the project publishes on its documentation site.
terms:
- text: DevLog
part-of-speech: noun
status: preferred
type: fullForm
context: DevLog @ 2025.08.05
- text: DevLogs
part-of-speech: noun
status: admitted
type: variant
note: The plural; TBX has no plural value, so it is recorded as a variant.
# ---------------------------------------------------------------------------------------
# Product and editions
# ---------------------------------------------------------------------------------------
- id: airi
subject: product
# TODO: open question. Should this concept be translatable? Interface strings in all 8
# locales keep "AIRI". But the project uses the Japanese form アイリ in prose, and also in
# the English README.
translatable: false
definition: The application that this project makes, an animated character that speaks, listens, and sees.
note: >
Keep the Latin letters in every language; only prose may write the Japanese form アイリ.
terms:
- text: AIRI
part-of-speech: proper noun
status: preferred
context: Welcome to AIRI!
- text: Project AIRI
part-of-speech: proper noun
status: preferred
type: fullForm
note: The full name, used on first mention.
- id: tamagotchi
subject: product
translatable: false
definition: The internal codename of the desktop edition, whose package is stage-tamagotchi.
note: >
Keep the Latin letters, write "Desktop ver." in anything an end user reads, and never
write "Stage Tamagotchi" with a space.
terms:
- text: Tamagotchi
part-of-speech: proper noun
status: preferred
context: Desktop ver. (known as stage-tamagotchi internally)
- id: pocket
subject: product
translatable: false
definition: The internal codename of the mobile edition, whose package is stage-pocket.
note: >
Keep the Latin letters, write "Mobile ver." in anything an end user reads, and never
write "Stage Pocket" with a space.
terms:
- text: Pocket
part-of-speech: proper noun
status: preferred
context: Mobile ver. (known as stage-pocket internally)
note: Correct for a developer audience, wrong in the interface.
- text: Stage Pocket
part-of-speech: proper noun
status: deprecated
type: variant
note: Neither the codename nor the name a user sees.
# ---------------------------------------------------------------------------------------
# Payment
# ---------------------------------------------------------------------------------------
- id: flux
subject: payment
translatable: false
definition: The currency inside the application.
terms:
- text: Flux
part-of-speech: proper noun
status: preferred
context: Payment successful! Your Flux has been topped up.
# ---------------------------------------------------------------------------------------
# Rendering
# ---------------------------------------------------------------------------------------
- id: live2d
subject: rendering
translatable: false
definition: A system that animates 2D artwork of a character.
terms:
- text: Live2D
part-of-speech: proper noun
status: preferred
context: Live2D Settings
- id: vrm
subject: rendering
translatable: false
definition: A file format for a 3D avatar.
terms:
- text: VRM
part-of-speech: proper noun
status: preferred
context: Enter VRM model URL
- id: mmd
subject: rendering
translatable: false
definition: The MikuMikuDance file format.
terms:
- text: MMD
part-of-speech: proper noun
status: preferred
context: Remove imported Live2D, VRM, Spine, and MMD models.
- id: spine
subject: rendering
translatable: false
definition: A system for 2D skeletal animation.
terms:
- text: Spine
part-of-speech: proper noun
status: preferred
context: Spine Settings
- id: tachie
subject: rendering
translatable: true
definition: Artwork that shows a character standing.
# TODO: open question. Confirm the Chinese wording with a native reviewer.
note: Japanese uses 立ち絵; a language with no such word keeps "Tachie".
terms:
- text: Tachie
part-of-speech: noun
status: preferred
context: Tachie models use .tachie.zip archives containing emotion-named images.
- id: avatar-model
subject: rendering
translatable: true
definition: The file that holds the appearance of the character, such as a Live2D model or a VRM model.
note: Not the LLM model.
terms:
- text: Model
part-of-speech: noun
status: preferred
context: Remove imported Live2D, VRM, Spine, and MMD models.
- id: motion
subject: rendering
translatable: true
definition: An animation clip that moves the character, such as a wave of the hand.
note: A Motion moves the body and an Expression changes the face; use two different words.
terms:
- text: Motion
part-of-speech: noun
status: preferred
context: Map Motions
- id: expression
subject: rendering
translatable: true
definition: A change of the character's face, such as a smile.
note: Use a different word from Motion, which moves the body.
terms:
- text: Expression
part-of-speech: noun
status: preferred
context: Expressions
- id: motion-map
subject: rendering
translatable: true
definition: The table that connects each emotion to a motion.
terms:
- text: Motion Map
part-of-speech: noun
status: preferred
context: Edit motion map
# ---------------------------------------------------------------------------------------
# The application
# ---------------------------------------------------------------------------------------
- id: stage
subject: application
translatable: true
definition: The surface of the application that shows the character and the widgets.
note: This word does not refer to a step in a process.
terms:
- text: Stage
part-of-speech: noun
status: preferred
context: Disable Stage Transitions
- id: controls-island
subject: application
translatable: true
definition: The group of controls that floats above the stage.
note: An invented name; describe the thing rather than translate the two words.
terms:
- text: Controls Island
part-of-speech: noun
status: preferred
context: Controls Island Icon Size
- id: spotlight
subject: application
translatable: true
definition: A window on the desktop edition that opens with a keyboard shortcut for quick commands.
note: Not the macOS function of the same name, so do not copy the Apple translation.
terms:
- text: Spotlight
part-of-speech: noun
status: preferred
context: Spotlight shortcut
# ---------------------------------------------------------------------------------------
# Models and providers
# ---------------------------------------------------------------------------------------
- id: provider
subject: providers
translatable: true
definition: An external service that supplies a model, such as OpenAI, Ollama, or ElevenLabs.
terms:
- text: Provider
part-of-speech: noun
status: preferred
context: No models were found for this provider
- id: model
subject: providers
translatable: true
definition: A machine-learning model that a provider supplies.
terms:
- text: Model
part-of-speech: noun
status: preferred
context: Select a model
# ---------------------------------------------------------------------------------------
# The modules
#
# These five names are a family. Translate them as a group, so the result reads as one set
# of capabilities and not as five unrelated words.
# ---------------------------------------------------------------------------------------
- id: consciousness
subject: modules
translatable: true
definition: The module that thinks for the character, using a large language model.
note: An unusual word for a software feature; it means the module for the language model.
terms:
- text: Consciousness
part-of-speech: noun
status: preferred
context: Select the suitable LLM provider for consciousness
- id: hearing
subject: modules
translatable: true
definition: The module that changes the speech of the user to text.
note: Opposite direction to Speech; not a word for the ear or a medical test.
terms:
- text: Hearing
part-of-speech: noun
status: preferred
context: Hearing
- id: speech
subject: modules
translatable: true
definition: The module that changes text to the voice of the character.
note: Read the note on "Hearing".
terms:
- text: Speech
part-of-speech: noun
status: preferred
context: Speech
- id: vision
subject: modules
translatable: true
definition: The module that lets the character see images and the screen.
note: This is eyesight, not a plan for the future.
terms:
- text: Vision
part-of-speech: noun
status: preferred
context: Vision
- id: artistry
subject: modules
translatable: true
definition: The module that makes images.
note: Translate the definition, not the English word.
terms:
- text: Artistry
part-of-speech: noun
status: preferred
context: Artistry Provider Configuration
# ---------------------------------------------------------------------------------------
# Memory
# ---------------------------------------------------------------------------------------
- id: memory
subject: memory
translatable: true
definition: What the character keeps and can use again in a later conversation.
note: >
Not the computer's RAM, which the performance overlay means by the same word; use the
word for what a person remembers.
terms:
- text: Memory
part-of-speech: noun
status: preferred
context: Memory
- id: long-term-memory
subject: memory
translatable: true
# TODO: confirm this definition. The module has a settings page and a route, but no
# implementation that describes the behavior.
definition: The module that holds what the character keeps across many conversations.
terms:
- text: Long-Term Memory
part-of-speech: noun
status: preferred
context: Long-term memory specific settings and management
- id: short-term-memory
subject: memory
translatable: true
# TODO: confirm this definition. Read the note on "long-term-memory".
definition: The module that holds what the character keeps inside the current conversation.
terms:
- text: Short-Term Memory
part-of-speech: noun
status: preferred
context: Short-term memory specific settings and management
# ---------------------------------------------------------------------------------------
# The character
# ---------------------------------------------------------------------------------------
- id: character
subject: character
translatable: true
definition: >
A character with everything this product stores for it: the persona, the avatar model,
the background, the provider and model of each module, and the prompt of each agent.
note: >
A rename is in progress, so no term is preferred yet, and the interface still uses
"Character" for a smaller thing: a tab inside the card.
terms:
- text: AIRI Card
part-of-speech: noun
status: admitted
context: Paste this into your AIRI Card artistry config to override these nodes.
note: The name in the interface today; translate "Card" and keep "AIRI".
- text: Character
part-of-speech: noun
status: admitted
note: The planned name, not yet used for this concept.
- text: Profile
part-of-speech: noun
status: admitted
type: variant
note: >
The stage switcher means this concept; the Account page means the user's own details.
- id: character-card
subject: character
translatable: true
definition: The portable file format for a character, from the wider ecosystem.
note: >
A user imports it into a Character, which holds more; never shorten it to "Card".
terms:
- text: Character Card
part-of-speech: noun
status: preferred
context: Configure modules for this character card.
- id: system-prompt
subject: character
translatable: true
definition: The instructions the application sends to the model before the conversation starts.
terms:
- text: System Prompt
part-of-speech: noun
status: preferred
context: 'Error: Please, provide a system prompt.'
# ---------------------------------------------------------------------------------------
# Sound
# ---------------------------------------------------------------------------------------
- id: voice-pack
subject: sound
translatable: true
definition: A prepared voice the user selects for the active character.
terms:
- text: Voice Pack
part-of-speech: noun
status: preferred
context: No Voice Packs available
+8 -1
View File
@@ -61,9 +61,16 @@
"scripts": {
"dev": "pnpm run build",
"build": "tsdown",
"glossary:build": "tsx glossary/build-tbx.ts",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"unplugin-yaml": "catalog:"
"unplugin-yaml": "catalog:",
"valibot": "catalog:",
"xast-util-to-xml": "catalog:",
"xastscript": "catalog:",
"yaml": "catalog:"
}
}
+7
View File
@@ -0,0 +1,7 @@
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['glossary/**/*.test.ts'],
},
})
+245 -23
View File
@@ -2116,7 +2116,7 @@ importers:
version: 3.0.2(electron@41.2.1)
'@electron-toolkit/tsconfig':
specifier: 'catalog:'
version: 2.0.0(@types/node@25.6.0)
version: 2.0.0(@types/node@24.12.2)
'@electron-toolkit/utils':
specifier: 'catalog:'
version: 4.0.0(electron@41.2.1)
@@ -2155,7 +2155,7 @@ importers:
version: 3.1.0
'@intlify/unplugin-vue-i18n':
specifier: 'catalog:'
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
version: 11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@modelcontextprotocol/sdk':
specifier: 'catalog:'
version: 1.29.0(@cfworker/json-schema@4.1.1)(zod@4.3.6)
@@ -2191,10 +2191,10 @@ importers:
version: link:../../packages/ui-transitions
'@proj-airi/unplugin-fetch':
specifier: 'catalog:'
version: 0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@proj-airi/unplugin-live2d-sdk':
specifier: 'catalog:'
version: 0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
version: 0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@types/audioworklet':
specifier: 'catalog:'
version: 0.0.97
@@ -2221,7 +2221,7 @@ importers:
version: 2.10.3
'@vitejs/plugin-vue':
specifier: 'catalog:'
version: 6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
version: 6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/volar':
specifier: 'catalog:'
version: 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
@@ -2254,7 +2254,7 @@ importers:
version: 6.8.3
electron-vite:
specifier: 'catalog:'
version: 5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
get-port-please:
specifier: 'catalog:'
version: 3.2.0
@@ -2275,31 +2275,31 @@ importers:
version: 2.2.6
unocss-preset-scrollbar:
specifier: 'catalog:'
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
version: 4.0.0(unocss@66.6.8(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)))
unplugin-info:
specifier: 'catalog:'
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unplugin-yaml:
specifier: 'catalog:'
version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite:
specifier: 'catalog:'
version: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
version: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-bundle-visualizer:
specifier: 'catalog:'
version: 1.2.1(rolldown@1.0.0-rc.16)(rollup@4.60.1)
vite-plugin-mkcert:
specifier: 'catalog:'
version: 2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
version: 2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-plugin-vue-devtools:
specifier: 'catalog:'
version: 8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
version: 8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))
vite-plugin-vue-layouts:
specifier: 'catalog:'
version: 0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
version: 0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
vue-macros:
specifier: 'catalog:'
version: 3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
version: 3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
vue-tsc:
specifier: 'catalog:'
version: 3.2.6(typescript@5.9.3)
@@ -3664,6 +3664,18 @@ importers:
unplugin-yaml:
specifier: 'catalog:'
version: 4.1.0(@nuxt/kit@3.20.2(magicast@0.5.2))(esbuild@0.27.2)(rolldown@1.0.0-rc.17)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
valibot:
specifier: 'catalog:'
version: 1.3.1(typescript@5.9.3)
xast-util-to-xml:
specifier: 'catalog:'
version: 4.0.0
xastscript:
specifier: 'catalog:'
version: 4.0.0
yaml:
specifier: 'catalog:'
version: 2.8.3
packages/memory-pgvector:
dependencies:
@@ -21050,9 +21062,9 @@ snapshots:
dependencies:
electron: 41.2.1
'@electron-toolkit/tsconfig@2.0.0(@types/node@25.6.0)':
'@electron-toolkit/tsconfig@2.0.0(@types/node@24.12.2)':
dependencies:
'@types/node': 25.6.0
'@types/node': 24.12.2
'@electron-toolkit/utils@4.0.0(electron@41.2.1)':
dependencies:
@@ -21973,6 +21985,31 @@ snapshots:
- supports-color
- typescript
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
'@intlify/bundle-utils': 11.0.7(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))
'@intlify/shared': 11.3.2
'@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.3.2)(@vue/compiler-dom@3.5.32)(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
'@typescript-eslint/scope-manager': 8.63.0
'@typescript-eslint/typescript-estree': 8.63.0(typescript@5.9.3)
debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3
pathe: 2.0.3
picocolors: 1.1.1
unplugin: 2.3.11
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
optionalDependencies:
vue-i18n: 11.3.2(vue@3.5.32(typescript@5.9.3))
transitivePeerDependencies:
- '@vue/compiler-dom'
- eslint
- rollup
- supports-color
- typescript
'@intlify/unplugin-vue-i18n@11.0.7(@vue/compiler-dom@3.5.32)(eslint@10.2.1(jiti@2.6.1))(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-i18n@11.3.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@eslint-community/eslint-utils': 4.9.1(eslint@10.2.1(jiti@2.6.1))
@@ -24032,11 +24069,35 @@ snapshots:
transitivePeerDependencies:
- magicast
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@proj-airi/unplugin-fetch@0.2.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
dependencies:
ofetch: 1.5.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
yauzl: 3.3.0
transitivePeerDependencies:
- '@types/node'
- '@vitejs/devtools'
- esbuild
- jiti
- less
- sass
- sass-embedded
- stylus
- sugarss
- terser
- tsx
- yaml
'@proj-airi/unplugin-live2d-sdk@0.1.7(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)':
dependencies:
ofetch: 1.5.1
@@ -25762,6 +25823,12 @@ snapshots:
transitivePeerDependencies:
- typescript
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.6(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-rc.13
@@ -26037,6 +26104,15 @@ snapshots:
transitivePeerDependencies:
- vue
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
sirv: 3.0.2
vue: 3.5.32(typescript@5.9.3)
optionalDependencies:
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- typescript
'@vue-macros/devtools@3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))':
dependencies:
sirv: 3.0.2
@@ -28116,7 +28192,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
electron-vite@5.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
electron-vite@5.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0)
@@ -28124,7 +28200,7 @@ snapshots:
esbuild: 0.25.12
magic-string: 0.30.21
picocolors: 1.1.1
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
@@ -34153,11 +34229,6 @@ snapshots:
'@unocss/preset-mini': 66.6.8
unocss: 66.6.8(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unocss-preset-scrollbar@4.0.0(unocss@66.6.8(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))):
dependencies:
'@unocss/preset-mini': 66.6.8
unocss: 66.6.8(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unocss@66.6.8(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@unocss/cli': 66.6.8
@@ -34224,6 +34295,14 @@ snapshots:
unplugin: 2.3.11
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
optionalDependencies:
esbuild: 0.27.2
rolldown: 1.0.0-rc.16
rollup: 4.60.1
unplugin: 2.3.11
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-combine@2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
optionalDependencies:
esbuild: 0.27.2
@@ -34245,6 +34324,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ci-info: 4.4.0
git-url-parse: 16.1.0
simple-git: 3.36.0
unplugin: 2.3.11
optionalDependencies:
esbuild: 0.27.2
rollup: 4.60.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
unplugin-info@1.3.2(esbuild@0.27.2)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ci-info: 4.4.0
@@ -34338,6 +34430,17 @@ snapshots:
rollup: 2.80.0
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
unplugin: 3.0.0
yaml: 2.8.3
optionalDependencies:
esbuild: 0.27.2
rolldown: 1.0.0-rc.16
rollup: 4.60.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
unplugin-yaml@4.1.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.60.1)
@@ -34576,12 +34679,22 @@ snapshots:
- rollup
- supports-color
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-dev-rpc@1.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
birpc: 2.9.0
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client: 2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-hot-client@2.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-hot-client@2.1.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
vite: 8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
@@ -34627,6 +34740,21 @@ snapshots:
- tsx
- yaml
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
debug: 4.4.3(supports-color@10.2.2)
error-stack-parser-es: 1.0.5
ohash: 2.0.11
open: 10.2.0
perfect-debounce: 2.1.0
sirv: 3.0.2
unplugin-utils: 0.3.1
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-dev-rpc: 1.1.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- supports-color
vite-plugin-inspect@11.3.3(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
ansis: 4.2.0
@@ -34658,6 +34786,13 @@ snapshots:
- typescript
- ws
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
supports-color: 10.2.2
undici: 8.1.0
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-plugin-mkcert@2.0.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@@ -34676,6 +34811,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
'@vue/devtools-kit': 8.1.1
'@vue/devtools-shared': 8.1.1
sirv: 3.0.2
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vite-plugin-inspect: 11.3.3(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
vite-plugin-vue-inspector: 5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
transitivePeerDependencies:
- '@nuxt/kit'
- supports-color
- vue
vite-plugin-vue-devtools@8.1.1(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue/devtools-core': 8.1.1(vue@3.5.32(typescript@5.9.3))
@@ -34690,6 +34839,21 @@ snapshots:
- supports-color
- vue
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
'@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.29.0)
'@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0)
'@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0)
'@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.29.0)
'@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.29.0)
'@vue/compiler-dom': 3.5.32
kolorist: 1.8.0
magic-string: 0.30.21
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
transitivePeerDependencies:
- supports-color
vite-plugin-vue-inspector@5.3.2(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)):
dependencies:
'@babel/core': 7.29.0
@@ -34705,6 +34869,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
fast-glob: 3.3.3
vite: 8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3)
vue: 3.5.32(typescript@5.9.3)
vue-router: 5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
transitivePeerDependencies:
- supports-color
vite-plugin-vue-layouts@0.11.0(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-router@5.0.4(@pinia/colada@1.2.1(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(@vue/compiler-sfc@3.5.32)(pinia@3.0.4(typescript@5.9.3)(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3)):
dependencies:
debug: 4.4.3(supports-color@10.2.2)
@@ -34989,6 +35163,54 @@ snapshots:
- vue-tsc
- webpack
vue-macros@3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vue@3.5.32(typescript@5.9.3))
'@vue-macros/boolean-prop': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/chain-call': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/common': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/config': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-emit': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-models': 3.1.2(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-prop': 3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-props': 3.1.2(@vue-macros/reactivity-transform@3.1.2(vue@3.5.32(typescript@5.9.3)))(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-props-refs': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-slots': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/define-stylex': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/devtools': 3.1.2(typescript@5.9.3)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
'@vue-macros/export-expose': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/export-props': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/export-render': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/hoist-static': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/jsx-directive': 3.1.2(typescript@5.9.3)
'@vue-macros/named-template': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/reactivity-transform': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/script-lang': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-block': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-component': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/setup-sfc': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-bind': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-emits': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/short-vmodel': 3.1.2(vue@3.5.32(typescript@5.9.3))
'@vue-macros/volar': 3.1.2(typescript@5.9.3)(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3))
unplugin: 2.3.11
unplugin-combine: 2.3.0(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(unplugin@2.3.11)(vite@8.0.8(@types/node@24.12.2)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))
unplugin-vue-define-options: 3.1.2(vue@3.5.32(typescript@5.9.3))
vue: 3.5.32(typescript@5.9.3)
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
- '@rspack/core'
- '@vueuse/core'
- esbuild
- rolldown
- rollup
- typescript
- vite
- vue-tsc
- webpack
vue-macros@3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@vueuse/core@14.2.1(vue@3.5.32(typescript@5.9.3)))(esbuild@0.27.2)(rolldown@1.0.0-rc.16)(rollup@4.60.1)(typescript@5.9.3)(vite@8.0.8(@types/node@25.6.0)(esbuild@0.27.2)(jiti@2.6.1)(less@4.6.4)(terser@5.46.1)(tsx@4.21.0)(yaml@2.8.3))(vue-tsc@3.2.6(typescript@5.9.3))(vue@3.5.32(typescript@5.9.3)):
dependencies:
'@vue-macros/better-define': 3.1.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(vue@3.5.32(typescript@5.9.3))
+1
View File
@@ -9,6 +9,7 @@ export default defineConfig({
'packages/cap-vite',
'packages/ccc',
'packages/core-agent',
'packages/i18n',
'packages/better-ws',
'packages/plugin-sdk',
'packages/plugin-sdk-tamagotchi',