Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
6660dcc
Add Udon Test Kit as a docs project
ToniSeas Oct 10, 2026
3dca161
Keep Udon Test Kit's intro independent of TsVRC
ToniSeas Oct 10, 2026
5eb8537
Add Udon Test Kit reference and explanation pages
ToniSeas Oct 10, 2026
b4a9f38
Add Udon Test Kit how-to guides
ToniSeas Oct 10, 2026
9376f45
Add Udon Test Kit getting-started pages
ToniSeas Oct 10, 2026
b01a50f
Point TsVRC's testing docs at Udon Test Kit
ToniSeas Oct 10, 2026
6ea6532
Document the multi-project sidebar and cross-linking rules
ToniSeas Oct 10, 2026
ebad3f3
Drop the SDK version reason from the kit's requirements
ToniSeas Oct 10, 2026
1f59f9b
Share one install page between projects
ToniSeas Oct 10, 2026
f33b75f
Mention the command-line test script in the kit's intro
ToniSeas Oct 10, 2026
869520c
Add local search
ToniSeas Oct 10, 2026
b51579d
Make the homepage personal and list every project with its badges
ToniSeas Oct 10, 2026
8142db4
Link the homepage avatar to Toni's GitHub
ToniSeas Oct 10, 2026
1f2028a
Scope the homepage to VRChat world creation
ToniSeas Oct 10, 2026
a58a5a5
Drop the first person from the projects intro
ToniSeas Oct 10, 2026
47fba46
Rework the homepage project cards and drop the Framework link
ToniSeas Oct 10, 2026
099e5ce
Give the homepage the logo's colors
ToniSeas Oct 10, 2026
221b8f8
Describe the kit by what it does on the homepage
ToniSeas Oct 10, 2026
f269c30
Rework the kit's intro page
ToniSeas Oct 10, 2026
2b3de05
Explain what automated tests are in the kit's intro
ToniSeas Oct 10, 2026
657bdcb
Link Unity's testing guide from the kit's intro
ToniSeas Oct 10, 2026
c00060b
Remove CLAUDE.md
ToniSeas Oct 10, 2026
622dfd5
Trim AGENTS.md to what CONTRIBUTING.md leaves out
ToniSeas Oct 10, 2026
2d066d5
Drop the commit section from AGENTS.md
ToniSeas Oct 10, 2026
9b0daec
Use the kit's new Tools/TsVRC menu paths
ToniSeas Oct 10, 2026
d61bbe2
Rename Udon Test Kit to Test Kit
ToniSeas Oct 10, 2026
55789fb
Rename Test Kit back to Udon Test Kit, with Tsvrc.UdonTestKit assemblies
ToniSeas Oct 10, 2026
f62610f
Document SetOwner's handoff to the master
ToniSeas Oct 10, 2026
b72db74
Drop TsVRC's own testing helpers from the docs
ToniSeas Oct 10, 2026
e566aef
Rename the Framework docs to Core
ToniSeas Oct 10, 2026
c7ac7f9
Call the framework Core in the docs
ToniSeas Oct 10, 2026
bc7abe1
Turn the pre-1.0 note into a warning
ToniSeas Oct 10, 2026
0861f07
Drop the community-maintained wording
ToniSeas Oct 11, 2026
df1a519
Rework possessive apostrophes
ToniSeas Oct 11, 2026
a38d807
Install unitypackages into Packages
ToniSeas Oct 11, 2026
a26b32a
Add a Git option for changing a package's code
ToniSeas Oct 11, 2026
3a0f2a2
Remove the Git install option
ToniSeas Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/doc_issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ body:
id: project
attributes:
label: Project
description: Which project's docs this is about (e.g. tsvrc).
description: Which project's docs this is about (e.g. tsvrc or udon-test-kit).
validations:
required: true
- type: dropdown
Expand Down
49 changes: 12 additions & 37 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,20 @@
# AGENTS.md

## Project overview
Docusaurus site (TypeScript, Node 20+) for every project in the `tsvrc` org, one folder per project
under `docs/<project>/`. [CONTRIBUTING.md](CONTRIBUTING.md) covers the layout, translations and pull
requests, and [STYLE_GUIDE.md](STYLE_GUIDE.md) covers writing; follow both. This file only adds what
they leave out.

Docusaurus site (TypeScript, Node 20+) documenting every project under the `tsvrc` org,
namespaced under `docs/<project>/` rather than one repo per project. See
[CONTRIBUTING.md](CONTRIBUTING.md) for the multi-project layout and the i18n workflow.
## Verify

## Setup

- `npm install`
- `npm start` for a dev server at `http://localhost:3000`. Content hot-reloads; restart
after changing `docusaurus.config.ts`.

## Build / verify

- `npm run build` before calling any change done. `onBrokenLinks: 'throw'` fails the
build on a broken link, catching mistakes `npm start` won't.

## Code style

Follow [STYLE_GUIDE.md](STYLE_GUIDE.md) for any `.md`/`.mdx` change: Diátaxis categories,
linking instead of duplicating, avoiding AI-sounding prose.
Run `npm run build` before calling a change done. It builds both locales and fails on a broken
link, which `npm start` doesn't catch.

## Gotchas

- `sidebars.ts` is hand-written per project, not a blanket autogenerated tree. A
`dirName` pointed straight at a leaf folder returns an unlabeled flat list, so a
category's `label` has to come from the wrapping `{type: 'category', ...}` node, not a
- `sidebars.ts` is written by hand. A `dirName` pointed at a leaf folder returns an unlabeled flat
list, so a category's `label` comes from its wrapping `{type: 'category', ...}` node, not a
`_category_.json`.
- `tsvrc-core` (code) and this repo (docs) aren't wired together, no submodule, no
build-time fetch, and can drift. Changed public API or behavior in `tsvrc-core`, or
something under `docs/tsvrc/`? Check the other side still matches.

## Commit / PR rules

- Every commit must be signed off (`git commit -s`) per the
[DCO](https://developercertificate.org/).
- Disclose AI assistance in the PR description. You're responsible for checking the
result renders correctly, the same as if you'd written it by hand.

## Security

- Don't commit secrets or API tokens. Report vulnerabilities per `SECURITY.md`, not as a
public issue.
- Each project's code lives in its own repo (`tsvrc-core`, `udon-test-kit`), and nothing keeps it in
sync with this one. When a project's public behavior changes, check its pages here, and the Core
testing pages too when Udon Test Kit changes.
6 changes: 0 additions & 6 deletions CLAUDE.md

This file was deleted.

26 changes: 20 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,42 @@ duplicating, words/phrases to avoid, sentence shape, formatting rules.

Each project under the `tsvrc` org gets its own `docs/<project>/` folder here rather than
its own docs repo. Adding docs for a new project means adding a folder here and a
matching top-level category in `sidebars.ts`, not creating a new repo.
matching top-level category in `sidebars.ts`, not creating a new repo. Every project shares
one sidebar, so a category whose label another project already uses, such as "Reference",
needs a unique `key`; without one, the Spanish build fails on a duplicate translation key.
`docs/udon-test-kit/` is set up this way.

A page every project shares, such as [Install a TsVRC package](docs/install-a-package.md),
sits at the root of `docs/` instead. Each project's own install page links to it and adds only
what's specific to that project: the package name, requirements, and how to check that it
worked.

A project's docs stand on their own. When one project depends on another, as TsVRC does on
Udon Test Kit, the dependent project's pages link to the other's where a reader needs it, and
the project depended on never mentions its consumers.

## Adding a translation

Docs content stays English-only and unversioned under `docs/<project>/`. A translation
lives in a parallel tree at
`i18n/<locale>/docusaurus-plugin-content-docs/current/<project>/`, mirroring the same
relative paths and filenames; adding a language never touches the English files. See
`docs/tsvrc/intro.md` and its Spanish counterpart for a worked example to copy.
`docs/core/intro.md` and its Spanish counterpart for a worked example to copy.
`src/pages/` translates differently: via `@docusaurus/Translate` in the component source,
with strings in `i18n/<locale>/code.json`.

A heading another page links to by anchor needs a stable id that survives translation,
since translating the heading text changes the anchor. Docusaurus's MDX v3 syntax for this
since translating the heading text changes the anchor. The Docusaurus MDX v3 syntax for this
is a comment, not `{#id}`: `## Play Mode tests {/* #play-mode-tests */}`. Sidebar category
labels live in `sidebars.ts` itself, not a translatable file, so they render in English for
every locale regardless of translated content underneath. Don't override Docusaurus's
every locale regardless of translated content underneath. Don't override the Docusaurus
theme chrome (pagination, admonition labels, sidebar buttons) in this site's own
`code.json`; it ships its own translated defaults per locale.
`code.json`; it ships its own translated defaults per locale. The search plugin
(`@easyops-cn/docusaurus-search-local`) ships no Spanish, so its `theme.SearchBar.*` and
`theme.SearchPage.*` strings are the exception and live in `code.json`.

Before translating a technical term, check how the target language's own community or
official docs (Unity's localized manual, GitHub's glossary, VRChat's community) actually
official docs (the localized Unity manual, the GitHub glossary, the VRChat community) actually
render it rather than translating literally. Some terms stay in English even in translated
prose, others have a real idiomatic equivalent that isn't the literal word-for-word choice.
This repo's own code identifiers (`_ts.Memory`, `TsStart`) are never translated, including
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
[![License: CC BY 4.0](https://img.shields.io/badge/license-CC%20BY%204.0-58a6ff?labelColor=161b22)](LICENSE)

The [Docusaurus](https://docusaurus.io/) site behind [tsvrc.com](https://tsvrc.com),
documenting every project under the `tsvrc` GitHub org. Framework code lives in
[`tsvrc/tsvrc-core`](https://github.com/tsvrc/tsvrc-core); this repo is docs and content
only.
documenting every project under the `tsvrc` GitHub org. Each project's code lives in its own
repository, such as [`tsvrc/tsvrc-core`](https://github.com/tsvrc/tsvrc-core) for
Core and [`tsvrc/udon-test-kit`](https://github.com/tsvrc/udon-test-kit) for Udon Test
Kit; this repo is docs and content only.

## Development

Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Reporting a vulnerability

Don't open a public issue. Use [GitHub's private vulnerability
Don't open a public issue. Use [GitHub private vulnerability
reporting](https://github.com/tsvrc/docs/security/advisories/new) for this repository
instead, so the report isn't visible before a fix is available.

Expand Down
42 changes: 42 additions & 0 deletions docs/core/add-to-your-project.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
id: add-to-your-project
title: Add TsVRC Core to your project
sidebar_position: 1
---

# Add TsVRC Core to your project

Install TsVRC Core in a VRChat world project.

## Requirements

- **Unity 2022.3.** The Core package manifest targets this version specifically.
- **The [VRChat Worlds SDK](https://creators.vrchat.com/worlds/) `3.10.x`.**

## Install the package

Install **TsVRC Core** (`com.tsvrc.core`) as described in
[Install a TsVRC package](/docs/install-a-package). Its `.unitypackage` is on
[the Core GitHub releases](https://github.com/tsvrc/tsvrc-core/releases).

Core depends on [Udon Test Kit](/docs/udon-test-kit/intro) (`com.tsvrc.udon-test-kit`) for its
testing helpers. The Creator Companion installs it along with Core. With the `.unitypackage`,
import [the kit's](/docs/udon-test-kit/add-to-your-project) too, unless your project already has
it.

You'll know it worked when the Unity menu bar gains a **Tsvrc** menu. Everything Core does
from here runs from there.

## What you just added

The Core manifest description sums it up: structured initialization, dependency wiring,
and editor codegen tooling for building VRChat worlds with UdonSharp. Concretely, the
package compiles as one runtime assembly (`Tsvrc.Runtime`), the base classes your world's
scripts extend and call into, and one editor-only assembly (`Tsvrc.Editor`) holding all
the codegen and Configure-window tooling. See [How Core fits
together](./core-concepts/how-it-fits-together) for how those two halves relate.

## Next

Continue to [Build your first behaviour](./first-behaviour) to initialize Core in a
scene and get a script running.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ group's direct entries on the right (row rendering delegated to
with different parameters. `memberPrefix`/`memberSuffix` control whether a live member-name
preview is shown at all (`"_ts."` for Globals, `"Create"`/`"(parent)"` for Factories,
neither for Pool or Constructs, since neither generates a name at all). `prefixRespectsToggle`
mirrors [`BuildGroupPrefix`](../internals/ts-module)'s own `respectToggle`
parameter: Factory always prefixes regardless of a group's `IncludeInName`, Global respects
mirrors the `respectToggle` parameter of
[`BuildGroupPrefix`](../internals/ts-module): Factory always prefixes regardless of a group's `IncludeInName`, Global respects
it per-group.

Its `State` class holds purely UI-local state (tree expansion, selection, search text,
pagination), never written back to any config asset, persisted across a domain reload via
`SessionState`. It's keyed by a combination of the target object's type name and the
property name being drawn, specifically so `TsWindow`'s `TsConfig`-backed trees and
`TsBuiltinConfigInspector`'s `TsBuiltinConfig`-backed trees (which share property names like
property name being drawn, specifically so the `TsConfig`-backed trees in `TsWindow` and the
`TsBuiltinConfig`-backed trees in `TsBuiltinConfigInspector` (which share property names like
`"GlobalGroups"`) never collide on the same session key.

`Draw` returns whether a drag-and-drop reparent action already flushed its own nested
Expand All @@ -44,8 +44,8 @@ into: a delete button, and per-row hint text covering three cases a bare `Proper
can't communicate on its own. An empty slot means something used to be assigned here and
was probably deleted; a scene-instance reference where only a persisted prefab asset is
valid (the Pool and Factory tabs, both drawn with `assetsOnly: true`) gets its own hint too;
and a type resolved only via `TsModule.TryResolveObjectType`'s compile-broken fallback is
shown with a note that the compile is currently broken, since Unity's own `PropertyField`
and a type resolved only via the compile-broken fallback in `TsModule.TryResolveObjectType` is
shown with a note that the compile is currently broken, since the Unity `PropertyField`
can't display a component's real type while nothing compiles.

## The three "managed by Configure" inspectors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ scaffold root. It exists to close a real correctness gap. Without it, every code
searched "whatever scene happens to be loaded," which meant switching to look at an
unrelated scene, or a play-mode test's own temporary scene, could silently regenerate a
project's real generated output with an almost-empty result. A scene with no `TsConfig`
compiles clean, and per `TsModule`'s own snapshot-fallback logic, a clean compile with fewer
compiles clean, and per the snapshot-fallback logic in `TsModule`, a clean compile with fewer
entries than before is treated as a deliberate deletion, not an error.

## How it's set
Expand All @@ -36,7 +36,7 @@ Once a scene is linked, every scene-scoped lookup (`Find<T>`, `FindType`, `FindA
`FindAllType`) is scoped strictly to that scene's root objects, rather than searching
whatever's currently loaded. `Find<T>`/`FindType` additionally refuse to guess: if more than
one match exists within the linked scene, they return `null` rather than picking whichever
one Unity's iteration order happens to return first, the same "don't guess" stance
one the Unity iteration order happens to return first, the same "don't guess" stance
`InstanceModule` takes for resolving a project's single scaffold instance.

`IsConfiguredButNotLoaded` is true whenever a scene has been linked but isn't currently
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ two-pass grace period involved.

`TsGroup` is one node in an entry group tree, flat, with a `ParentId` reference rather than
a nested/recursive structure, so it maps directly onto a tree-view's row model and avoids
Unity's serialization depth limit for nested types. `Id` is assigned once and never reused,
the Unity serialization depth limit for nested types. `Id` is assigned once and never reused,
even after deletion. Groups are purely organizational by default; setting `IncludeInName`
turns nesting into namespacing, prefixing an opted-in group's own name (and its
opted-in ancestors') onto the generated member name of everything inside it. `_ts.Spawner`
opted-in ancestors) onto the generated member name of everything inside it. `_ts.Spawner`
under a plain group stays `_ts.Spawner`, but under a group named "Enemies" with
`IncludeInName` set it becomes `_ts.EnemiesSpawner`. Factory groups always prefix
regardless of this toggle; Global groups respect it per-group. Construct groups have no
Expand All @@ -74,7 +74,7 @@ identifier.

`TsBuiltinConfig` is a `ScriptableObject`, not a scene component but a package asset,
holding the same Global/Pool/Factory entry-and-group shape as `TsConfig`, but for
registrations TsVRC itself ships and wants present regardless of what any individual
registrations Core itself ships and wants present regardless of what any individual
project configures. It's merged with each project's own `TsConfig` at generate time, so a
library-provided global or pooled prefab shows up in the generated output the same way a
project's own entry would, without every project needing to register it by hand.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ sidebar_position: 3
# TsPendingConfigEdit

`Tsvrc.Editor.TsPendingConfigEdit` is the batching primitive behind every Apply/Discard
button in TsVRC's editor UI. [`TsWindow`](./ts-window) owns one per `TsConfig` it edits
button in the Core editor UI. [`TsWindow`](./ts-window) owns one per `TsConfig` it edits
(plus a second one for the Settings tab's `TsvrcLogger` fields), and
`TsBuiltinConfigInspector` owns one for whichever `TsBuiltinConfig` asset is selected. One
instance tracks edits to exactly one target object.
Expand All @@ -16,9 +16,9 @@ instance tracks edits to exactly one target object.

Without this, every `SerializedObject.ApplyModifiedProperties()` call, meaning every single
field edit including one keystroke into a name field, would immediately trigger a real
regenerate pass through `TsGenerator`'s own `Undo.postprocessModifications` watch. Typing a
regenerate pass through the `Undo.postprocessModifications` watch in `TsGenerator`. Typing a
ten-character name would fire ten regenerate passes. `TsPendingConfigEdit` suppresses
`TsGenerator`'s automatic triggers for the duration of an in-progress edit and only lets the
the `TsGenerator` automatic triggers for the duration of an in-progress edit and only lets the
accumulated changes trigger one real regenerate when you explicitly click Apply.

## The baseline
Expand All @@ -39,7 +39,7 @@ GameObject to live on at all) or as a bare `ScriptableObject` instance otherwise
Three calls, in order, once per `OnGUI` pass:

1. **`BeginFrame()`** — arms suppression proactively, before any edit could happen this
frame. This matters because some UI code (`TsGroupTreeGUI`'s drag-and-drop reparenting)
frame. This matters because some UI code (the drag-and-drop reparenting in `TsGroupTreeGUI`)
calls `ApplyModifiedProperties()` itself, mid-draw, ahead of the caller's own outer call,
so suppression has to already be active by then.
2. *(the tab draws its fields, and the caller calls `SerializedObject.ApplyModifiedProperties()`
Expand Down Expand Up @@ -67,5 +67,5 @@ state immediately. A discarded edit never reaches `ScheduleRerun()`.
baseline clone and any held suppression scope. It never has data to lose by the time it
runs, since `OnDisable` also fires around an ordinary domain reload, where cleaning up a
pending edit would be wrong because the user hasn't decided anything yet, while `OnDestroy`
only fires once the window or inspector is actually closing for good, by which point Unity's
own save-changes dialog has already forced a real Apply or Discard decision.
only fires once the window or inspector is actually closing for good, by which point the Unity
save-changes dialog has already forced a real Apply or Discard decision.
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ survives a domain reload via a `[SerializeField]` field: `EditorWindow` instance
re-deserialized across a reload, but only serialized fields keep their value across it.

The Settings tab isn't a real module: it's a pseudo-module (`SettingsTabModule`) appended
last, because its content spans two different data sources (`LogModule`'s logging fields,
which live on the scene's `TsvrcLogger`, and `TsConfig`'s tree-shaking fields) that don't
last, because its content spans two different data sources (the `LogModule` logging fields,
which live on the scene's `TsvrcLogger`, and the `TsConfig` tree-shaking fields) that don't
belong to any single registered module.

## Apply / Discard

Every tab's edits are batched behind an explicit Apply/Discard step rather than triggering a
real regenerate on every single property change. That batching is the entire reason
[`TsPendingConfigEdit`](./ts-pending-config-edit) exists. `TsWindow` participates in Unity's
own "unsaved changes" contract (`hasUnsavedChanges`, `SaveChanges`, `DiscardChanges`): it
[`TsPendingConfigEdit`](./ts-pending-config-edit) exists. `TsWindow` participates in the Unity
"unsaved changes" contract (`hasUnsavedChanges`, `SaveChanges`, `DiscardChanges`): it
decorates the window's title/tab and blocks it from closing, including on an editor quit,
until you explicitly apply or discard. `TsWindow` tracks two independent pending-edit sets
at once, the active tab's `TsConfig` edits and the Settings tab's separate `TsvrcLogger`
Expand All @@ -54,7 +54,7 @@ edits, both folded into one shared "you have unapplied changes" footer and one s
Beyond the window-close path, switching the linked scene (or picking a different scene to
link) also risks discarding an in-progress edit silently. `ResolvePendingChangesBeforeSwitch`
guards that specific action with its own Apply/Cancel/Discard dialog, since
`hasUnsavedChanges`'s built-in protection only covers the window actually closing.
the built-in protection of `hasUnsavedChanges` only covers the window actually closing.

## Status reporting

Expand Down
Loading
Loading