diff --git a/.github/scripts/docs-site.mjs b/.github/scripts/docs-site.mjs
index cc9a3f4..9965456 100644
--- a/.github/scripts/docs-site.mjs
+++ b/.github/scripts/docs-site.mjs
@@ -5,20 +5,21 @@ import { spawnSync } from "node:child_process";
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
import { fileURLToPath } from "node:url";
-export const REQUIRED_DOC_FILES = [
+export const COMMON_DOC_FILES = [
"projects/typed-peripheral-digitalitems/build.gradle.kts",
"projects/typed-peripheral-digitalitems/package.json",
"projects/typed-peripheral-digitalitems/package-lock.json",
- "projects/typed-peripheral-digitalitems/typedoc.json",
- "projects/typed-peripheral-digitalitems/tsconfig.docs.json",
"projects/typed-peripheral-digitalitems/shared.ts",
"projects/typed-peripheral-digitalitems/digitizer.ts",
"projects/typed-peripheral-digitalitems/advanced_digitizer.ts",
"projects/typed-peripheral-digitalitems/documentation/index.md",
- "projects/typed-peripheral-digitalitems/documentation/theme/plugin.mjs",
"projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.css",
"projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.js",
];
+const DOC_GENERATORS = [
+ ["projects/typed-peripheral-digitalitems/typedoc.json", "projects/typed-peripheral-digitalitems/documentation/theme/plugin.mjs"],
+ ["projects/typed-peripheral-digitalitems/mkdocs.yml", "projects/typed-peripheral-digitalitems/requirements-docs.txt", "projects/typed-peripheral-digitalitems/documentation/generate-docs.mjs"],
+];
const BRANCHES = ["1.20", "1.21"];
const TAG_RE = /^v\d+(?:\.\d+)+(?:-[0-9A-Za-z]+(?:[.-][0-9A-Za-z]+)*)?$/;
@@ -119,9 +120,10 @@ function show(repository, ref, path) {
}
function refHasDocs(repository, ref) {
- return REQUIRED_DOC_FILES.every((path) =>
- git(repository, ["cat-file", "-e", `${ref}:${path}`], true).status === 0
- ) && /\bgenerateDocs\b/.test(show(repository, ref, REQUIRED_DOC_FILES[0]));
+ const has = (path) => git(repository, ["cat-file", "-e", `${ref}:${path}`], true).status === 0;
+ return COMMON_DOC_FILES.every(has)
+ && DOC_GENERATORS.some((files) => files.every(has))
+ && /\bgenerateDocs\b/.test(show(repository, ref, COMMON_DOC_FILES[0]));
}
function metadata(repository, ref) {
@@ -265,7 +267,10 @@ function localTarget(root, html, rawLink) {
fail(`Malformed local link in ${html}: ${rawLink}`);
}
if (pathname.includes("\\") || pathname.includes("\0")) fail(`Unsafe local link in ${html}: ${rawLink}`);
- const target = pathname.startsWith("/") ? resolve(root, `.${pathname}`) : resolve(dirname(html), pathname);
+ const versionedPath = pathname.match(/^\/.*?\/((?:branch|tag)\/.*)$/)?.[1];
+ const target = pathname.startsWith("/")
+ ? resolve(root, versionedPath ?? `.${pathname}`)
+ : resolve(dirname(html), pathname);
if (!within(root, target)) fail(`Local link escapes site in ${html}: ${rawLink}`);
return target;
}
@@ -290,9 +295,6 @@ export async function validateSite(site) {
for (const entry of plan) {
const versionRoot = resolve(root, entry.path);
await regularFile(resolve(versionRoot, "index.html"), `${entry.path} index.html`);
- for (const asset of ["search.js", "navigation.js", "custom.css", "custom.js"]) {
- await regularFile(resolve(versionRoot, "assets", asset), `${entry.path} ${asset}`);
- }
}
const canonicalRoot = await realpath(root);
for (const html of await htmlFiles(root)) {
diff --git a/.github/scripts/docs-site.test.mjs b/.github/scripts/docs-site.test.mjs
index 33f96cf..5e4a8f8 100644
--- a/.github/scripts/docs-site.test.mjs
+++ b/.github/scripts/docs-site.test.mjs
@@ -23,11 +23,11 @@ function entry(kind, name, sha = SHA_A) {
};
}
-async function fragment(root, path, html = 'asset ') {
+async function fragment(root, path, html = ' ') {
const directory = join(root, path);
await mkdir(join(directory, "assets"), { recursive: true });
await writeFile(join(directory, "index.html"), html);
- for (const asset of ["main.js", "search.js", "navigation.js", "custom.css", "custom.js"]) {
+ for (const asset of ["main.js", "main.css"]) {
await writeFile(join(directory, "assets", asset), "// docs\n");
}
}
@@ -107,14 +107,23 @@ test("validate rejects links escaping the site", async () => {
await assert.rejects(validateSite(output), /escapes site/);
});
-test("validate rejects missing TypeDoc assets", async () => {
+test("validate resolves hosted version paths below the Pages project prefix", async () => {
+ const root = await mkdtemp(join(tmpdir(), "docs-site-prefix-"));
+ const input = join(root, "input");
+ const output = join(root, "output");
+ await fragment(input, "branch/1.20", '');
+ await assembleSite({ input, output, plan: [entry("branch", "1.20")] });
+ await validateSite(output);
+});
+
+test("validate rejects a missing linked asset", async () => {
const root = await mkdtemp(join(tmpdir(), "docs-site-assets-"));
const input = join(root, "input");
const output = join(root, "output");
await fragment(input, "branch/1.20");
await assembleSite({ input, output, plan: [entry("branch", "1.20")] });
- await unlink(join(output, "branch", "1.20", "assets", "search.js"));
- await assert.rejects(validateSite(output), /Missing branch\/1.20 search.js/);
+ await unlink(join(output, "branch", "1.20", "assets", "main.css"));
+ await assert.rejects(validateSite(output), /Broken local link.*assets\/main.css/);
});
test("assemble rejects a symlinked output ancestor", async () => {
diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml
index a253169..90c0ea2 100644
--- a/.github/workflows/docs-check.yml
+++ b/.github/workflows/docs-check.yml
@@ -42,6 +42,16 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ cache: pip
+ cache-dependency-path: projects/typed-peripheral-digitalitems/requirements-docs.txt
+
+ - name: Install documentation dependencies
+ run: timeout --foreground 120s python -m pip install -r projects/typed-peripheral-digitalitems/requirements-docs.txt
+
- name: Build package and documentation
run: timeout --foreground 600s ./gradlew :typed-peripheral-digitalitems:compileTypeScript :typed-peripheral-digitalitems:generateDocs --no-daemon
@@ -55,7 +65,7 @@ jobs:
run: |
set -euo pipefail
NODE="$PWD/.gradle/nodejs/node-v22.14.0-linux-x64/bin/node"
- timeout --foreground 60s "$NODE" --test .github/scripts/docs-site.test.mjs
+ timeout --foreground 60s "$NODE" --test .github/scripts/docs-site.test.mjs projects/typed-peripheral-digitalitems/documentation/generate-docs.test.mjs
mkdir -p build/docs-fragments/branch/1.20
cp -a projects/typed-peripheral-digitalitems/docs/. build/docs-fragments/branch/1.20/
timeout --foreground 60s "$NODE" .github/scripts/docs-site.mjs assemble --input build/docs-fragments --output build/docs-site --plan-json "$DOCS_PLAN"
diff --git a/.github/workflows/docs-pages.yml b/.github/workflows/docs-pages.yml
index ffc47e6..67cdb2c 100644
--- a/.github/workflows/docs-pages.yml
+++ b/.github/workflows/docs-pages.yml
@@ -67,6 +67,17 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Install MkDocs for migrated refs
+ run: |
+ if [[ -f projects/typed-peripheral-digitalitems/requirements-docs.txt ]]; then
+ timeout --foreground 120s python -m pip install -r projects/typed-peripheral-digitalitems/requirements-docs.txt
+ fi
+
- name: Generate documentation with ref-owned task
env:
DOCS_PATH: ${{ matrix.path }}
diff --git a/projects/typed-peripheral-digitalitems/.gitignore b/projects/typed-peripheral-digitalitems/.gitignore
index b4504e2..dff745a 100644
--- a/projects/typed-peripheral-digitalitems/.gitignore
+++ b/projects/typed-peripheral-digitalitems/.gitignore
@@ -2,3 +2,4 @@
*.d.ts
node_modules/
docs/
+.mkdocs-build/
diff --git a/projects/typed-peripheral-digitalitems/build.gradle.kts b/projects/typed-peripheral-digitalitems/build.gradle.kts
index 0eaa4b2..ba09555 100644
--- a/projects/typed-peripheral-digitalitems/build.gradle.kts
+++ b/projects/typed-peripheral-digitalitems/build.gradle.kts
@@ -54,10 +54,11 @@ val generateDocs by tasks.registering(NpmTask::class) {
"package.json",
"package-lock.json",
"tsconfig.json",
- "tsconfig.docs.json",
- "typedoc.json",
+ "mkdocs.yml",
+ "requirements-docs.txt",
"*.ts",
"documentation/**/*.md",
+ "documentation/**/*.mjs",
"documentation/theme/**",
)
exclude("node_modules/**", "*.d.ts")
@@ -71,6 +72,7 @@ tasks.assemble {
tasks.clean {
delete(file("docs"))
+ delete(file(".mkdocs-build"))
delete(fileTree(projectDir) {
include("*.d.ts", "*.lua")
})
diff --git a/projects/typed-peripheral-digitalitems/documentation/generate-docs.mjs b/projects/typed-peripheral-digitalitems/documentation/generate-docs.mjs
new file mode 100644
index 0000000..8cbcc97
--- /dev/null
+++ b/projects/typed-peripheral-digitalitems/documentation/generate-docs.mjs
@@ -0,0 +1,196 @@
+import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
+import { spawnSync } from "node:child_process";
+import { dirname, relative, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+import ts from "typescript";
+
+const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const peripherals = [
+ { interfaceName: "Digitizer", page: "guides/basic-digitizer.md" },
+ { interfaceName: "AdvancedDigitizer", page: "guides/advanced-items.md" },
+];
+
+function text(parts) {
+ return ts.displayPartsToString(parts).trim();
+}
+
+function tagsFor(signature, checker) {
+ const result = { params: new Map(), returns: "", throws: [] };
+ for (const tag of signature.getJsDocTags(checker)) {
+ const value = text(tag.text);
+ if (tag.name === "param") {
+ const match = /^(\S+)\s*-?\s*(.*)$/s.exec(value);
+ if (match) result.params.set(match[1], match[2]);
+ } else if (tag.name === "returns" || tag.name === "return") result.returns = value;
+ else if (tag.name === "throws") result.throws.push(value);
+ }
+ return result;
+}
+
+function sourceInterface(symbol) {
+ const declaration = symbol.valueDeclaration ?? symbol.declarations?.[0];
+ let current = declaration;
+ while (current && !ts.isInterfaceDeclaration(current)) current = current.parent;
+ return current?.name.text;
+}
+
+export function extractPeripheral(program, interfaceName) {
+ const checker = program.getTypeChecker();
+ const matches = [];
+ for (const sourceFile of program.getSourceFiles()) {
+ if (sourceFile.isDeclarationFile && sourceFile.fileName.includes("typescript/lib/")) continue;
+ ts.forEachChild(sourceFile, (node) => {
+ if (ts.isInterfaceDeclaration(node) && node.name.text === interfaceName) matches.push(node);
+ });
+ }
+ if (matches.length !== 1) throw new Error(`Expected one ${interfaceName} interface, found ${matches.length}`);
+ const declaration = matches[0];
+ let peripheralType = null;
+ for (const sourceFile of program.getSourceFiles()) {
+ const visit = (node) => {
+ if (ts.isNewExpression(node)
+ && node.typeArguments?.[0]?.getText() === interfaceName
+ && node.arguments?.[0]
+ && ts.isStringLiteral(node.arguments[0])) {
+ peripheralType = node.arguments[0].text;
+ }
+ ts.forEachChild(node, visit);
+ };
+ ts.forEachChild(sourceFile, visit);
+ }
+ if (!peripheralType) throw new Error(`No peripheral provider found for ${interfaceName}`);
+ const type = checker.getTypeAtLocation(declaration);
+ const methods = checker.getPropertiesOfType(type).flatMap((symbol) => {
+ const methodType = checker.getTypeOfSymbolAtLocation(symbol, declaration);
+ const signatures = checker.getSignaturesOfType(methodType, ts.SignatureKind.Call);
+ if (!signatures.length) return [];
+ return [{
+ name: symbol.name,
+ inheritedFrom: sourceInterface(symbol) === interfaceName ? null : sourceInterface(symbol),
+ signatures: signatures.map((signature) => {
+ const tags = tagsFor(signature, checker);
+ const parameters = signature.getParameters().map((parameter) => {
+ const parameterDeclaration = parameter.valueDeclaration ?? parameter.declarations?.[0] ?? declaration;
+ return {
+ name: parameter.name,
+ type: checker.typeToString(checker.getTypeOfSymbolAtLocation(parameter, parameterDeclaration), declaration, ts.TypeFormatFlags.NoTruncation),
+ optional: Boolean(parameter.flags & ts.SymbolFlags.Optional) || Boolean(parameterDeclaration.questionToken),
+ description: tags.params.get(parameter.name) ?? "",
+ };
+ });
+ return {
+ signature: `${symbol.name}${checker.signatureToString(signature, declaration, ts.TypeFormatFlags.NoTruncation | ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope)}`,
+ summary: text(signature.getDocumentationComment(checker)),
+ parameters,
+ returns: tags.returns,
+ throws: tags.throws,
+ };
+ }),
+ }];
+ });
+ if (!methods.length) throw new Error(`${interfaceName} has no methods`);
+ return { interfaceName, peripheralType, methods };
+}
+
+function escapeCell(value) {
+ return value.replaceAll("|", "\\|").replaceAll("\n", " ");
+}
+
+export function renderPeripheral(peripheral, revision = "") {
+ const lines = [
+ "## Peripheral methods",
+ "",
+ "The reference below is generated from the published TypeScript interface.",
+ "",
+ ];
+ for (const method of peripheral.methods) {
+ lines.push(`### \`${method.name}\``, "");
+ if (method.inheritedFrom) lines.push(`*Inherited from \`${method.inheritedFrom}\`.*`, "");
+ for (const [index, signature] of method.signatures.entries()) {
+ if (method.signatures.length > 1) lines.push(`#### Overload ${index + 1}`, "");
+ lines.push("```typescript", signature.signature, "```", "");
+ if (signature.summary) lines.push(signature.summary, "");
+ if (signature.parameters.length) {
+ lines.push("| Parameter | Type | Description |", "| --- | --- | --- |");
+ for (const parameter of signature.parameters) {
+ const name = `\`${parameter.name}${parameter.optional ? "?" : ""}\``;
+ lines.push(`| ${name} | \`${escapeCell(parameter.type)}\` | ${escapeCell(parameter.description)} |`);
+ }
+ lines.push("");
+ }
+ if (signature.returns) lines.push(`**Returns:** ${signature.returns}`, "");
+ for (const error of signature.throws) lines.push(`**Throws:** ${error}`, "");
+ }
+ }
+ if (revision) lines.push(`[View TypeScript source](https://github.com/SirEdvin/DigitalItems/tree/${revision}/projects/typed-peripheral-digitalitems)`, "");
+ return lines.join("\n");
+}
+
+function options(args) {
+ const result = { out: resolve(projectRoot, "docs"), revision: "", siteUrl: "" };
+ for (let index = 0; index < args.length; index += 2) {
+ const value = args[index + 1];
+ if (!value) throw new Error(`Missing value for ${args[index]}`);
+ if (args[index] === "--out") result.out = resolve(value);
+ else if (args[index] === "--gitRevision") result.revision = value;
+ else if (args[index] === "--hostedBaseUrl") result.siteUrl = value;
+ else throw new Error(`Unknown option: ${args[index]}`);
+ }
+ return result;
+}
+
+export async function buildDocs(args = []) {
+ const config = options(args);
+ const buildRoot = resolve(projectRoot, ".mkdocs-build");
+ await rm(buildRoot, { recursive: true, force: true });
+ await cp(resolve(projectRoot, "documentation"), buildRoot, {
+ recursive: true,
+ filter: (path) => !path.endsWith("generate-docs.mjs") && !path.endsWith("generate-docs.test.mjs") && !path.includes("/theme/"),
+ });
+ await mkdir(resolve(buildRoot, "assets/peripherals"), { recursive: true });
+ const textureRoot = resolve(projectRoot, "../core/src/main/resources/assets/digitalitems/textures/block");
+ await cp(resolve(textureRoot, "digitizer_front_on.png"), resolve(buildRoot, "assets/peripherals/digitizer.png"));
+ await cp(resolve(textureRoot, "advanced_digitizer_front_on.png"), resolve(buildRoot, "assets/peripherals/advanced_digitizer.png"));
+ await mkdir(resolve(buildRoot, "assets/stylesheets"), { recursive: true });
+ await mkdir(resolve(buildRoot, "assets/javascripts"), { recursive: true });
+ await cp(resolve(projectRoot, "documentation/theme/digitalitems.css"), resolve(buildRoot, "assets/stylesheets/digitalitems.css"));
+ await cp(resolve(projectRoot, "documentation/theme/digitalitems.js"), resolve(buildRoot, "assets/javascripts/digitalitems.js"));
+ await cp(resolve(projectRoot, "documentation/theme/favicon.svg"), resolve(buildRoot, "assets/favicon.svg"));
+
+ const parsed = ts.parseJsonConfigFileContent(
+ ts.readConfigFile(resolve(projectRoot, "tsconfig.json"), ts.sys.readFile).config,
+ ts.sys,
+ projectRoot,
+ { noEmit: true, strictNullChecks: true },
+ );
+ const program = ts.createProgram(parsed.fileNames, parsed.options);
+ const errors = ts.getPreEmitDiagnostics(program);
+ if (errors.length) throw new Error(ts.formatDiagnosticsWithColorAndContext(errors, {
+ getCanonicalFileName: (name) => name,
+ getCurrentDirectory: () => projectRoot,
+ getNewLine: () => "\n",
+ }));
+ for (const peripheral of peripherals) {
+ const path = resolve(buildRoot, peripheral.page);
+ const markdown = await readFile(path, "utf8");
+ const extracted = extractPeripheral(program, peripheral.interfaceName);
+ await writeFile(path, `${markdown.replaceAll("{{ peripheralType }}", extracted.peripheralType).trim()}\n\n${renderPeripheral(extracted, config.revision)}\n`);
+ }
+
+ await rm(config.out, { recursive: true, force: true });
+ const result = spawnSync("python3", ["-m", "mkdocs", "build", "--strict", "--clean", "--site-dir", config.out], {
+ cwd: projectRoot,
+ env: config.siteUrl ? { ...process.env, DOCS_SITE_URL: config.siteUrl } : process.env,
+ encoding: "utf8",
+ });
+ if (result.status !== 0) throw new Error((result.stderr || result.stdout).trim());
+ await rm(buildRoot, { recursive: true, force: true });
+ return relative(projectRoot, config.out);
+}
+
+if (resolve(process.argv[1] ?? "") === fileURLToPath(import.meta.url)) {
+ buildDocs(process.argv.slice(2)).catch((error) => {
+ process.stderr.write(`generate-docs: ${error.message}\n`);
+ process.exitCode = 1;
+ });
+}
diff --git a/projects/typed-peripheral-digitalitems/documentation/generate-docs.test.mjs b/projects/typed-peripheral-digitalitems/documentation/generate-docs.test.mjs
new file mode 100644
index 0000000..6c111e6
--- /dev/null
+++ b/projects/typed-peripheral-digitalitems/documentation/generate-docs.test.mjs
@@ -0,0 +1,34 @@
+import assert from "node:assert/strict";
+import { resolve } from "node:path";
+import test from "node:test";
+import ts from "typescript";
+import { extractPeripheral, renderPeripheral } from "./generate-docs.mjs";
+
+const root = resolve(import.meta.dirname, "..");
+const parsed = ts.parseJsonConfigFileContent(
+ ts.readConfigFile(resolve(root, "tsconfig.json"), ts.sys.readFile).config,
+ ts.sys,
+ root,
+ { noEmit: true, strictNullChecks: true },
+);
+const program = ts.createProgram(parsed.fileNames, parsed.options);
+
+test("extracts direct, inherited, and generic digitizer methods", () => {
+ const peripheral = extractPeripheral(program, "Digitizer");
+ assert.equal(peripheral.peripheralType, "digitizer");
+ assert.equal(peripheral.methods.length, 15);
+ assert.match(peripheral.methods.find(({ name }) => name === "getConfiguration").signatures[0].signature, /DigitizerConfiguration/);
+ assert.equal(peripheral.methods.find(({ name }) => name === "size").inheritedFrom, "InventoryViewAPI");
+ assert.match(renderPeripheral(peripheral), /\*\*Throws:\*\* A Lua error if the slot is empty/);
+});
+
+test("preserves advanced digitizer overloads and nullable tuple unions", () => {
+ const peripheral = extractPeripheral(program, "AdvancedDigitizer");
+ assert.equal(peripheral.peripheralType, "advanced_digitizer");
+ assert.equal(peripheral.methods.length, 11);
+ assert.equal(peripheral.methods.find(({ name }) => name === "digitize").signatures.length, 2);
+ assert.equal(peripheral.methods.find(({ name }) => name === "get").signatures.length, 3);
+ const markdown = renderPeripheral(peripheral);
+ assert.match(markdown, /filter\?: number \| string \| LuaTable \| null/);
+ assert.match(markdown, /LuaMultiReturn<\[null, string\] \| \[number, null\]>/);
+});
diff --git a/projects/typed-peripheral-digitalitems/documentation/guides/advanced-items.md b/projects/typed-peripheral-digitalitems/documentation/guides/advanced-items.md
index 42faf03..00d5cd1 100644
--- a/projects/typed-peripheral-digitalitems/documentation/guides/advanced-items.md
+++ b/projects/typed-peripheral-digitalitems/documentation/guides/advanced-items.md
@@ -5,8 +5,8 @@ peripheralInterface: AdvancedDigitizer
# Advanced Digitizer
-
-
MULTI-STORAGE PERIPHERAL The advanced digitizer moves items, fluids, and energy between attached storage and durable digital identifiers.
Peripheral type advanced_digitizer
Storage Items, fluids, energy
Failure style Recoverable result pairs
+
+
MULTI-STORAGE PERIPHERAL The advanced digitizer moves items, fluids, and energy between attached storage and durable digital identifiers.
Peripheral type {{ peripheralType }}
Storage Items, fluids, energy
Failure style Recoverable result pairs
The advanced digitizer can extract from its internal inventory with source
diff --git a/projects/typed-peripheral-digitalitems/documentation/guides/basic-digitizer.md b/projects/typed-peripheral-digitalitems/documentation/guides/basic-digitizer.md
index b3eb5c6..ad1a08a 100644
--- a/projects/typed-peripheral-digitalitems/documentation/guides/basic-digitizer.md
+++ b/projects/typed-peripheral-digitalitems/documentation/guides/basic-digitizer.md
@@ -5,8 +5,8 @@ peripheralInterface: Digitizer
# Basic Digitizer
-
-
ITEM PERIPHERAL The basic digitizer turns physical item stacks into portable digital identifiers and restores them through its internal inventory.
Peripheral type digitizer
Storage Items
Failure style Lua errors
+
+
ITEM PERIPHERAL The basic digitizer turns physical item stacks into portable digital identifiers and restores them through its internal inventory.
Peripheral type {{ peripheralType }}
Storage Items
Failure style Lua errors
Slots are one-based and default to slot 1. `digitize()` removes the complete
diff --git a/projects/typed-peripheral-digitalitems/documentation/index.md b/projects/typed-peripheral-digitalitems/documentation/index.md
index c3dd707..a216b0d 100644
--- a/projects/typed-peripheral-digitalitems/documentation/index.md
+++ b/projects/typed-peripheral-digitalitems/documentation/index.md
@@ -7,12 +7,12 @@ TypeScript with TypeScriptToLua.
## Choose your digitizer
diff --git a/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.css b/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.css
index bba38b1..5b4ff27 100644
--- a/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.css
+++ b/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.css
@@ -1,271 +1,17 @@
-@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap");
+@import url("https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600&family=Manrope:wght@400;600;700;800&display=swap");
:root {
- --di-accent: #4f46e5;
- --di-accent-hover: #4338ca;
- --di-accent-dark: #3730a3;
- --di-accent-subtle: rgba(79, 70, 229, 0.06);
- --di-bg: #ffffff;
- --di-bg-secondary: #f8fafc;
- --di-bg-elevated: #eef2ff;
- --di-bg-code: #f1f5f9;
- --di-border: rgba(79, 70, 229, 0.1);
- --di-border-subtle: rgba(148, 163, 184, 0.12);
- --di-text-primary: #0f172a;
- --di-text-secondary: #475569;
- --di-text-muted: #94a3b8;
- --di-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
- --di-shadow-md: 0 2px 8px rgba(0, 0, 0, 0.06);
- --color-background: var(--di-bg);
- --color-background-secondary: var(--di-bg-secondary);
- --color-background-active: var(--di-bg-elevated);
- --color-accent: var(--di-accent);
- --color-text: var(--di-text-primary);
- --color-text-aside: var(--di-text-secondary);
- --color-link: #4f46e5;
- --color-ts-keyword: #c084fc;
- --color-ts-string: #34d399;
- --color-ts-number: #fb923c;
+ --md-text-font: "Manrope";
+ --md-code-font: "IBM Plex Mono";
+ --di-line: color-mix(in srgb, var(--md-primary-fg-color) 28%, transparent);
+ --di-panel: color-mix(in srgb, var(--md-primary-fg-color) 7%, var(--md-default-bg-color));
+ --di-warm: #e29a32;
}
-html[data-theme="dark"] {
- --di-accent: #6366f1;
- --di-accent-hover: #818cf8;
- --di-accent-dark: #4f46e5;
- --di-accent-subtle: rgba(99, 102, 241, 0.08);
- --di-bg: #0c0e16;
- --di-bg-secondary: #141622;
- --di-bg-elevated: #1a1d2e;
- --di-bg-code: #111322;
- --di-border: rgba(99, 102, 241, 0.12);
- --di-border-subtle: rgba(148, 163, 184, 0.08);
- --di-text-primary: #e2e8f0;
- --di-text-secondary: #8892b0;
- --di-text-muted: #5a6380;
- --di-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.12);
- --di-shadow-md: 0 2px 8px rgba(0, 0, 0, 0.18);
- --color-text: var(--di-text-primary);
- --color-link: #818cf8;
-}
-
-@media (prefers-color-scheme: dark) {
- html[data-theme="os"] {
- --di-accent: #6366f1;
- --di-accent-hover: #818cf8;
- --di-accent-dark: #4f46e5;
- --di-accent-subtle: rgba(99, 102, 241, 0.08);
- --di-bg: #0c0e16;
- --di-bg-secondary: #141622;
- --di-bg-elevated: #1a1d2e;
- --di-bg-code: #111322;
- --di-border: rgba(99, 102, 241, 0.12);
- --di-border-subtle: rgba(148, 163, 184, 0.08);
- --di-text-primary: #e2e8f0;
- --di-text-secondary: #8892b0;
- --di-text-muted: #5a6380;
- --di-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.12);
- --di-shadow-md: 0 2px 8px rgba(0, 0, 0, 0.18);
- --color-text: var(--di-text-primary);
- --color-link: #818cf8;
- }
-}
-
-body {
- font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- line-height: 1.6;
- background-color: var(--di-bg);
-}
-
-.container-main {
- backdrop-filter: none;
-}
-
-.col-content {
- position: relative;
- max-width: 920px;
-}
-
-.col-content::before {
- content: none;
- display: none;
-}
-
-h1, h2, h3, h4, h5, h6,
-.site-menu,
-.tsd-page-title {
- font-family: "Inter", system-ui, -apple-system, sans-serif;
- letter-spacing: -0.025em;
-}
-
-h1 {
- font-weight: 800;
- font-size: 2rem;
- line-height: 1.25;
- text-wrap: balance;
- margin-bottom: 1rem;
-}
-
-h2 {
- font-weight: 700;
- font-size: 1.5rem;
- line-height: 1.3;
- border-bottom: 1px solid var(--di-border);
- padding-bottom: 0.5rem;
- margin-top: 2.5rem;
- margin-bottom: 1rem;
-}
-
-h3 {
- font-weight: 600;
- font-size: 1.25rem;
- line-height: 1.4;
- margin-top: 1.75rem;
- margin-bottom: 0.75rem;
-}
-
-h4 {
- font-weight: 600;
- font-size: 1.1rem;
- margin-top: 1.5rem;
- margin-bottom: 0.5rem;
-}
-
-p {
- margin-bottom: 1rem;
-}
-
-ul, ol {
- margin-bottom: 1rem;
- padding-left: 1.5rem;
-}
-
-li {
- margin-bottom: 0.35rem;
-}
-
-.tsd-panel {
- border: 1px solid var(--di-border);
- border-radius: 10px;
- box-shadow: var(--di-shadow-sm);
- background: var(--di-bg-secondary);
-}
-
-.tsd-member {
- border: 1px solid var(--di-border);
- border-radius: 10px;
- box-shadow: var(--di-shadow-sm);
-}
-
-.tsd-signature,
-code,
-pre {
- font-family: "JetBrains Mono", "Fira Code", "Cascadia Code", Consolas, monospace;
-}
-
-code {
- background: var(--di-bg-code);
- border: 1px solid var(--di-border-subtle);
- border-radius: 5px;
- padding: 0.1em 0.4em;
- font-size: 0.875em;
- color: var(--di-accent-hover);
-}
-
-html[data-theme="light"] code {
- color: var(--di-accent);
-}
-
-pre {
- background: var(--di-bg-code);
- border: 1px solid var(--di-border);
- border-radius: 10px;
- padding: 1rem 1.25rem;
- overflow-x: auto;
- box-shadow: var(--di-shadow-sm);
-}
-
-pre code {
- background: none;
- border: none;
- padding: 0;
- font-size: 0.85rem;
- line-height: 1.65;
- color: var(--di-text-primary);
-}
-
-a {
- color: var(--color-link);
- text-decoration: none;
- text-underline-offset: 0.2em;
- transition: color 0.15s ease;
-}
-
-a:hover {
- color: var(--di-accent-hover);
-}
-
-table {
- border: 1px solid var(--di-border);
- border-radius: 10px;
- overflow: hidden;
- box-shadow: var(--di-shadow-sm);
-}
-
-th, td {
- border-color: var(--di-border-subtle);
-}
-
-.tsd-page-toolbar {
- background: var(--di-bg);
- border-bottom: 1px solid var(--di-border);
-}
-
-.tsd-page-toolbar .title {
- font-family: "Inter", system-ui, sans-serif;
- font-weight: 700;
- color: var(--di-text-primary);
-}
-
-.di-signal-line {
- display: none !important;
-}
-
-.col-content p,
-.col-content li {
- max-width: 78ch;
-}
-
-.col-content table {
- width: 100%;
- border-collapse: collapse;
- overflow: hidden;
-}
-
-.col-content th {
- color: var(--di-accent-hover);
- background: var(--di-bg-elevated);
- text-align: left;
-}
-
-.col-content th,
-.col-content td {
- padding: 0.8rem 1rem;
- border-bottom: 1px solid var(--di-border-subtle);
- vertical-align: top;
-}
-
-.col-content blockquote {
- margin: 1.5rem 0;
- padding: 0.8rem 1.1rem;
- border-left: 4px solid var(--color-ts-number);
- background: color-mix(in srgb, var(--color-ts-number) 7%, var(--di-bg-secondary));
-}
-
-.col-content blockquote p {
- margin: 0;
-}
+.md-grid { max-width: 78rem; }
+.md-typeset h1 { font-weight: 800; letter-spacing: -.04em; }
+.md-typeset h2 { margin-top: 2.5em; padding-bottom: .35em; border-bottom: 1px solid var(--di-line); }
+.md-typeset h3 code { font-size: .9em; color: var(--md-accent-fg-color); }
.di-peripheral-grid {
display: grid;
@@ -276,329 +22,72 @@ th, td {
.di-peripheral-card,
.di-peripheral-hero {
- border: 1px solid var(--di-border);
- background: linear-gradient(145deg, var(--di-bg-elevated), var(--di-bg-secondary));
+ border: 1px solid var(--di-line);
+ background: linear-gradient(145deg, var(--di-panel), var(--md-default-bg-color));
+ box-shadow: 0 14px 35px rgba(0, 0, 0, .08);
}
.di-peripheral-card {
display: flex;
gap: 1.2rem;
- min-height: 190px;
- padding: 1.25rem;
- border-radius: 10px;
- color: var(--color-text);
- text-decoration: none;
- transition: border-color 160ms ease, transform 160ms ease;
+ min-height: 11rem;
+ padding: 1.2rem;
+ border-radius: .55rem;
+ color: var(--md-default-fg-color) !important;
+ transition: transform 160ms ease, border-color 160ms ease;
}
-.di-peripheral-card:hover {
- border-color: var(--di-accent-hover);
- color: var(--color-text);
- transform: translateY(-3px);
-}
-
-.di-peripheral-card--advanced,
-.di-peripheral-hero--advanced {
- border-color: color-mix(in srgb, var(--color-ts-number) 35%, transparent);
-}
+.di-peripheral-card:hover { transform: translateY(-3px); border-color: var(--md-accent-fg-color); }
+.di-peripheral-card--advanced, .di-peripheral-hero--advanced { border-color: color-mix(in srgb, var(--di-warm) 45%, transparent); }
.di-peripheral-image {
display: grid;
- flex: 0 0 128px;
- min-height: 128px;
+ flex: 0 0 7rem;
+ min-height: 7rem;
place-items: center;
- border-radius: 6px;
- background-color: #0a1114;
- background-image: linear-gradient(45deg, rgba(255,255,255,.035) 25%, transparent 25%, transparent 75%, rgba(255,255,255,.035) 75%), linear-gradient(45deg, rgba(255,255,255,.035) 25%, transparent 25%, transparent 75%, rgba(255,255,255,.035) 75%);
- background-position: 0 0, 8px 8px;
- background-size: 16px 16px;
+ border-radius: .35rem;
+ background: #091114 repeating-conic-gradient(rgba(255,255,255,.035) 0 25%, transparent 0 50%) 0 / 16px 16px;
}
-.di-peripheral-image img {
- width: 112px;
- height: 112px;
- image-rendering: pixelated;
-}
-
-.di-peripheral-copy {
- display: flex;
- flex-direction: column;
-}
-
-.di-peripheral-copy strong {
- font-size: 1.25rem;
-}
-
-.di-peripheral-copy small {
- margin: 0.25rem 0 0.75rem;
- color: var(--di-text-secondary);
-}
-
-.di-peripheral-copy b {
- margin-top: auto;
- color: var(--di-accent-hover);
- font-size: 0.82rem;
-}
+.di-peripheral-image img { width: 6rem; height: 6rem; image-rendering: pixelated; }
+.di-peripheral-copy { display: flex; flex-direction: column; }
+.di-peripheral-copy strong { font-size: 1.15rem; }
+.di-peripheral-copy small { margin: .25rem 0 .65rem; opacity: .72; }
+.di-peripheral-copy b { margin-top: auto; color: var(--md-accent-fg-color); font-size: .78rem; }
.di-peripheral-hero {
display: grid;
- grid-template-columns: 176px minmax(0, 1fr);
- gap: 1.75rem;
+ grid-template-columns: 10rem minmax(0, 1fr);
+ gap: 1.5rem;
margin: 1rem 0 2rem;
- padding: 1.5rem;
- border-radius: 10px;
-}
-
-.di-peripheral-hero .di-peripheral-image {
- min-height: 176px;
-}
-
-.di-peripheral-hero .di-peripheral-image img {
- width: 144px;
- height: 144px;
-}
-
-.di-eyebrow {
- color: var(--color-ts-number);
- font: 700 0.7rem/1 "Lucida Console", Monaco, monospace;
- letter-spacing: 0.12em;
-}
-
-.di-peripheral-hero p {
- margin: 0.55rem 0 1rem;
- font-size: 1.05rem;
-}
-
-.di-peripheral-hero dl {
- display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
- gap: 0.7rem;
- margin: 0;
-}
-
-.di-peripheral-hero dl div {
- min-width: 0;
-}
-
-.di-peripheral-hero dt {
- color: var(--di-text-secondary);
- font-size: 0.68rem;
- text-transform: uppercase;
- letter-spacing: 0.06em;
-}
-
-.di-peripheral-hero dd {
- margin: 0.2rem 0 0;
- overflow-wrap: anywhere;
- font-weight: 650;
-}
-
-.di-method-reference {
- margin-top: 3rem;
-}
-
-.di-generated-note {
- margin-bottom: 1.25rem;
- color: var(--di-text-secondary);
- font-size: 0.9rem;
-}
-
-.di-method-card {
- margin: 1rem 0;
- border: 1px solid var(--di-border);
- border-radius: 8px;
- background: var(--di-bg-secondary);
- box-shadow: 0 8px 22px rgba(0, 0, 0, 0.1);
- overflow: hidden;
-}
-
-.di-method-card > h3 {
- margin: 0;
- padding: 0.75rem 1rem;
- border-bottom: 1px solid var(--di-border);
- background: var(--di-bg-elevated);
- color: var(--di-accent-hover);
- font-size: 1rem;
-}
-
-.di-method-overload {
- padding: 1rem;
-}
-
-.di-method-overload + .di-method-overload {
- border-top: 1px dashed var(--di-border);
-}
-
-.di-method-overload .tsd-signature {
- margin: 0 0 1rem;
- padding: 0.8rem;
- box-shadow: none;
- overflow-x: auto;
-}
-
-.di-method-overload .tsd-comment {
- margin: 0.75rem 0;
-}
-
-.di-method-overload h4 {
- margin: 1rem 0 0.35rem;
- color: var(--di-text-secondary);
- font-size: 0.78rem;
- letter-spacing: 0.05em;
- text-transform: uppercase;
-}
-
-.di-version-panel {
- margin: 0.5rem 0 1rem;
- padding: 0.6rem 0.75rem;
- border: 1px solid var(--di-border);
- border-radius: 8px;
- background: var(--di-bg-secondary);
-}
-
-.di-version-label {
- display: block;
- margin-bottom: 0.3rem;
- color: var(--di-accent);
- font: 600 0.68rem/1.2 "Inter", system-ui, sans-serif;
- letter-spacing: 0.05em;
- text-transform: uppercase;
-}
-
-#di-version-select {
- width: 100%;
- padding: 0.35rem 0.5rem;
- border: 1px solid var(--di-border);
- border-radius: 6px;
- color: var(--di-text-primary);
- background: var(--di-bg);
- font-family: "Inter", system-ui, sans-serif;
- font-size: 0.82rem;
-}
-
-#di-version-select:focus-visible {
- outline: 2px solid var(--di-accent);
- outline-offset: 1px;
-}
-
-.tsd-typography {
- line-height: 1.7;
-}
-
-.tsd-anchor-link {
- color: var(--di-accent);
-}
-
-.tsd-anchor-link:hover {
- color: var(--di-accent-hover);
-}
-
-.tsd-kind-icon {
- color: var(--di-accent);
-}
-
-.tsd-navigation a,
-.tsd-nav-link {
- font-family: "Inter", system-ui, sans-serif;
-}
-
-.tsd-accordion-summary {
- font-family: "Inter", system-ui, sans-serif;
-}
-
-.tsd-filter-visibility .settings-label {
- font-family: "Inter", system-ui, sans-serif;
- font-weight: 600;
- font-size: 0.8rem;
- letter-spacing: 0.03em;
- text-transform: uppercase;
- color: var(--di-text-secondary);
-}
-
-#tsd-theme {
- font-family: "Inter", system-ui, sans-serif;
- border: 1px solid var(--di-border);
- border-radius: 6px;
- padding: 0.3rem 0.5rem;
- background: var(--di-bg);
- color: var(--di-text-primary);
- font-size: 0.82rem;
-}
-
-.tsd-search-input {
- font-family: "Inter", system-ui, sans-serif;
- border: 1px solid var(--di-border);
- border-radius: 8px;
-}
-
-.tsd-generator {
- border-top: 1px solid var(--di-border);
- padding-top: 1rem;
- margin-top: 2rem;
- color: var(--di-text-muted);
- font-size: 0.82rem;
-}
-
-footer {
- border-top: 1px solid var(--di-border);
-}
-
-@media (prefers-reduced-motion: no-preference) {
- .col-content > * {
- animation: di-fade-in 180ms ease-out both;
- }
-
- @keyframes di-fade-in {
- from {
- opacity: 0;
- transform: translateY(3px);
- }
- }
-
- a, .tsd-panel, .tsd-member {
- transition: all 0.15s ease;
- }
-}
-
-@media (max-width: 769px) {
- h1 { font-size: 1.5rem; }
- h2 { font-size: 1.25rem; }
- h3 { font-size: 1.1rem; }
-
- .tsd-signature,
- pre,
- table {
- max-width: 100%;
- overflow-x: auto;
- }
-
- .di-peripheral-grid {
- grid-template-columns: 1fr;
- }
-
- .di-peripheral-hero {
- grid-template-columns: 1fr;
- }
-
- .di-peripheral-hero .di-peripheral-image {
- min-height: 144px;
- }
-
- .di-peripheral-hero dl {
- grid-template-columns: 1fr 1fr;
- }
-}
-
-@media (max-width: 480px) {
- .di-peripheral-card {
- flex-direction: column;
- }
-
- .di-peripheral-image {
- flex-basis: auto;
- }
-
- .di-peripheral-hero dl {
- grid-template-columns: 1fr;
- }
+ padding: 1.4rem;
+ border-radius: .55rem;
+}
+
+.di-peripheral-hero .di-peripheral-image { min-height: 10rem; }
+.di-peripheral-hero .di-peripheral-image img { width: 8rem; height: 8rem; }
+.di-eyebrow { color: var(--di-warm); font: 600 .66rem/1 "IBM Plex Mono"; letter-spacing: .12em; }
+.di-peripheral-hero p { margin: .55rem 0 1rem; font-size: .95rem; }
+.di-peripheral-hero dl { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .7rem; margin: 0; }
+.di-peripheral-hero dt { opacity: .65; font-size: .62rem; text-transform: uppercase; letter-spacing: .06em; }
+.di-peripheral-hero dd { margin: .15rem 0 0; overflow-wrap: anywhere; font-weight: 700; }
+
+.md-typeset h2#peripheral-methods ~ h3 {
+ margin: 1.4rem 0 0;
+ padding: .75rem 1rem;
+ border: 1px solid var(--di-line);
+ border-bottom: 0;
+ border-radius: .45rem .45rem 0 0;
+ background: var(--di-panel);
+}
+
+.md-typeset table:not([class]) { display: table; width: 100%; }
+.md-typeset table:not([class]) th { background: var(--di-panel); }
+#di-version-select { max-width: 15rem; padding: .28rem .5rem; border: 1px solid var(--di-line); border-radius: .25rem; color: inherit; background: var(--md-default-bg-color); font: 600 .65rem "IBM Plex Mono"; }
+
+@media (max-width: 760px) {
+ .di-peripheral-grid { grid-template-columns: 1fr; }
+ .di-peripheral-hero { grid-template-columns: 1fr; }
+ .di-peripheral-hero dl { grid-template-columns: 1fr; }
+ .di-peripheral-hero .di-peripheral-image { min-height: 9rem; }
}
diff --git a/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.js b/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.js
index 5e11b2e..9e1f407 100644
--- a/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.js
+++ b/projects/typed-peripheral-digitalitems/documentation/theme/digitalitems.js
@@ -1,28 +1,18 @@
-(() => {
- const themeSelector = document.querySelector("#tsd-theme");
- if (themeSelector instanceof HTMLSelectElement) {
- const systemOption = themeSelector.querySelector('option[value="os"]');
- if (systemOption) systemOption.textContent = "System";
- }
-
- const selector = document.querySelector("#di-version-select");
- if (!(selector instanceof HTMLSelectElement)) return;
-
- const script = document.currentScript ?? [...document.scripts].find((entry) =>
- entry.src.endsWith("/custom.js")
- );
+document.addEventListener("DOMContentLoaded", () => {
+ const script = [...document.scripts].find((entry) => entry.src.endsWith("/assets/javascripts/digitalitems.js"));
if (!script) return;
-
const scriptUrl = new URL(script.src);
- const versionRoot = scriptUrl.pathname.match(
- /^(.*\/)(?:branch|tag)\/[^/]+\/assets\/custom\.js$/
- );
- if (!versionRoot) return;
-
- const siteRoot = new URL(versionRoot[1], scriptUrl.origin);
- const currentPath = decodeURIComponent(
- scriptUrl.pathname.slice(versionRoot[1].length).replace(/assets\/custom\.js$/, "")
- );
+ const match = scriptUrl.pathname.match(/^(.*\/)((?:branch|tag)\/[^/]+)\/assets\/javascripts\/digitalitems\.js$/);
+ if (!match) return;
+
+ const siteRoot = new URL(match[1], scriptUrl.origin);
+ const currentVersion = `${decodeURIComponent(match[2])}/`;
+ const select = document.createElement("select");
+ select.id = "di-version-select";
+ select.ariaLabel = "Documentation version";
+ select.disabled = true;
+ select.append(new Option("Loading versions..."));
+ document.querySelector(".md-header__inner")?.append(select);
fetch(new URL("versions.json", siteRoot))
.then((response) => {
@@ -30,38 +20,15 @@
return response.json();
})
.then((manifest) => {
- if (!manifest || !Array.isArray(manifest.versions)) {
- throw new Error("Unsupported version manifest");
- }
-
- selector.replaceChildren();
- const groups = new Map([
- ["branch", document.createElement("optgroup")],
- ["tag", document.createElement("optgroup")],
- ]);
- groups.get("branch").label = "Development branches";
- groups.get("tag").label = "Releases";
-
- for (const entry of manifest.versions) {
- if (!entry || !groups.has(entry.kind) || typeof entry.path !== "string") continue;
- const option = document.createElement("option");
- option.value = entry.path;
- option.textContent = entry.label || entry.name;
- option.selected = entry.path === currentPath;
- groups.get(entry.kind).append(option);
- }
-
- for (const group of groups.values()) {
- if (group.children.length > 0) selector.append(group);
+ select.replaceChildren();
+ for (const entry of manifest.versions ?? []) {
+ const option = new Option(entry.label || entry.name, entry.path);
+ option.selected = entry.path === currentVersion;
+ select.append(option);
}
- selector.disabled = selector.options.length === 0;
+ select.disabled = select.options.length === 0;
})
- .catch(() => {
- selector.options[0].textContent = "Version list unavailable";
- });
+ .catch(() => { select.options[0].textContent = "Versions unavailable"; });
- selector.addEventListener("change", () => {
- if (!selector.value) return;
- window.location.assign(new URL(selector.value, siteRoot));
- });
-})();
+ select.addEventListener("change", () => window.location.assign(new URL(select.value, siteRoot)));
+});
diff --git a/projects/typed-peripheral-digitalitems/documentation/theme/plugin.mjs b/projects/typed-peripheral-digitalitems/documentation/theme/plugin.mjs
deleted file mode 100644
index 294aa81..0000000
--- a/projects/typed-peripheral-digitalitems/documentation/theme/plugin.mjs
+++ /dev/null
@@ -1,67 +0,0 @@
-import { JSX, ReflectionKind } from "typedoc";
-
-function renderPeripheralMethods(context) {
- if (!context.model.isDocument()) return;
-
- const interfaceName = context.model.frontmatter.peripheralInterface;
- if (typeof interfaceName !== "string") return;
-
- const peripheral = context.page.project
- .getReflectionsByKind(ReflectionKind.Interface)
- .find((reflection) => reflection.name === interfaceName);
- if (!peripheral?.children) return;
-
- const methods = peripheral.children.filter(
- (reflection) => reflection.kindOf(ReflectionKind.Method) && reflection.signatures?.length
- );
- if (methods.length === 0) return;
-
- context.page.pageHeadings.push({
- link: "#peripheral-methods",
- text: "Peripheral methods",
- level: 2,
- });
-
- return JSX.createElement(
- "section",
- { class: "di-method-reference", "aria-labelledby": "peripheral-methods" },
- JSX.createElement("h2", { id: "peripheral-methods" }, "Peripheral methods"),
- JSX.createElement(
- "p",
- { class: "di-generated-note" },
- "Generated from the TypeScript interface. Signatures, parameters, return values, and errors stay synchronized with the published API."
- ),
- ...methods.map((method) =>
- JSX.createElement(
- "article",
- { class: "di-method-card" },
- JSX.createElement("h3", null, method.name),
- ...method.signatures.map((signature) =>
- JSX.createElement(
- "div",
- { class: "di-method-overload" },
- context.memberSignatureTitle(signature),
- context.memberSignatureBody(signature, { hideSources: true })
- )
- )
- )
- )
- );
-}
-
-export function load(app) {
- app.renderer.hooks.on("sidebar.begin", () =>
- JSX.createElement(
- "section",
- { class: "di-version-panel", "aria-label": "Documentation version" },
- JSX.createElement("span", { class: "di-version-label" }, "Version"),
- JSX.createElement(
- "select",
- { id: "di-version-select", disabled: true },
- JSX.createElement("option", null, "Local build")
- )
- )
- );
-
- app.renderer.hooks.on("content.end", renderPeripheralMethods);
-}
diff --git a/projects/typed-peripheral-digitalitems/mkdocs.yml b/projects/typed-peripheral-digitalitems/mkdocs.yml
new file mode 100644
index 0000000..6c1f4a6
--- /dev/null
+++ b/projects/typed-peripheral-digitalitems/mkdocs.yml
@@ -0,0 +1,65 @@
+site_name: Digital Items Peripheral API
+site_description: TypeScript and Lua reference for Digital Items peripherals
+site_url: !ENV [DOCS_SITE_URL, "https://siredvin.github.io/DigitalItems/branch/1.20/"]
+repo_url: https://github.com/SirEdvin/DigitalItems
+repo_name: SirEdvin/DigitalItems
+docs_dir: .mkdocs-build
+site_dir: docs
+strict: true
+
+theme:
+ name: material
+ logo: assets/favicon.svg
+ favicon: assets/favicon.svg
+ palette:
+ - media: "(prefers-color-scheme: light)"
+ scheme: default
+ primary: teal
+ accent: amber
+ toggle:
+ icon: material/weather-night
+ name: Use dark mode
+ - media: "(prefers-color-scheme: dark)"
+ scheme: slate
+ primary: teal
+ accent: amber
+ toggle:
+ icon: material/weather-sunny
+ name: Use light mode
+ features:
+ - content.code.copy
+ - navigation.footer
+ - navigation.indexes
+ - navigation.sections
+ - navigation.top
+ - search.highlight
+
+nav:
+ - Overview: index.md
+ - Getting started: getting-started.md
+ - Digital identifiers: digital-identifiers.md
+ - Peripherals:
+ - Basic digitizer: guides/basic-digitizer.md
+ - Advanced digitizer: guides/advanced-items.md
+ - Guides:
+ - Fluids and energy: guides/fluids-and-energy.md
+
+markdown_extensions:
+ - admonition
+ - attr_list
+ - md_in_html
+ - pymdownx.details
+ - pymdownx.highlight:
+ anchor_linenums: true
+ - pymdownx.superfences
+ - toc:
+ permalink: true
+
+extra_css:
+ - assets/stylesheets/digitalitems.css
+extra_javascript:
+ - assets/javascripts/digitalitems.js
+extra:
+ social:
+ - icon: fontawesome/brands/github
+ link: https://github.com/SirEdvin/DigitalItems
diff --git a/projects/typed-peripheral-digitalitems/package-lock.json b/projects/typed-peripheral-digitalitems/package-lock.json
index 5cac6cb..82e3e95 100644
--- a/projects/typed-peripheral-digitalitems/package-lock.json
+++ b/projects/typed-peripheral-digitalitems/package-lock.json
@@ -17,80 +17,16 @@
"@siredvin/typed-peripheral-base": "*"
},
"devDependencies": {
- "typedoc": "0.28.20",
"typescript": "5.7.2",
"typescript-to-lua": "1.29.1"
}
},
- "node_modules/@gerrit0/mini-shiki": {
- "version": "3.23.0",
- "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz",
- "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/engine-oniguruma": "^3.23.0",
- "@shikijs/langs": "^3.23.0",
- "@shikijs/themes": "^3.23.0",
- "@shikijs/types": "^3.23.0",
- "@shikijs/vscode-textmate": "^10.0.2"
- }
- },
"node_modules/@jackmacwindows/lua-types": {
"version": "2.13.2",
"resolved": "https://registry.npmjs.org/@jackmacwindows/lua-types/-/lua-types-2.13.2.tgz",
"integrity": "sha512-XoqHmgtG1SLWzmMx8wQc0sJtPmrzjCFbiwNiBIZE7HOEXWdJi7NDKVlE1nl9A5745Yb5jIkfz2deOzMtHiHVcQ==",
"license": "MIT"
},
- "node_modules/@shikijs/engine-oniguruma": {
- "version": "3.23.0",
- "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz",
- "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "3.23.0",
- "@shikijs/vscode-textmate": "^10.0.2"
- }
- },
- "node_modules/@shikijs/langs": {
- "version": "3.23.0",
- "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz",
- "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "3.23.0"
- }
- },
- "node_modules/@shikijs/themes": {
- "version": "3.23.0",
- "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz",
- "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/types": "3.23.0"
- }
- },
- "node_modules/@shikijs/types": {
- "version": "3.23.0",
- "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz",
- "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@shikijs/vscode-textmate": "^10.0.2",
- "@types/hast": "^3.0.4"
- }
- },
- "node_modules/@shikijs/vscode-textmate": {
- "version": "10.0.2",
- "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz",
- "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@siredvin/api-types": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@siredvin/api-types/-/api-types-1.1.0.tgz",
@@ -134,23 +70,6 @@
"@siredvin/craftos-types": "1.2.0"
}
},
- "node_modules/@types/hast": {
- "version": "3.0.5",
- "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz",
- "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/unist": "*"
- }
- },
- "node_modules/@types/unist": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
- "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@typescript-to-lua/language-extensions": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@typescript-to-lua/language-extensions/-/language-extensions-1.19.0.tgz",
@@ -158,36 +77,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "license": "Python-2.0"
- },
- "node_modules/balanced-match": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
- "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
- "node_modules/brace-expansion": {
- "version": "5.0.7",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
- "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^4.0.2"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- }
- },
"node_modules/enhanced-resolve": {
"version": "5.8.2",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz",
@@ -202,19 +91,6 @@
"node": ">=10.13.0"
}
},
- "node_modules/entities": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
- "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
- "dev": true,
- "license": "BSD-2-Clause",
- "engines": {
- "node": ">=0.12"
- },
- "funding": {
- "url": "https://github.com/fb55/entities?sponsor=1"
- }
- },
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
@@ -271,84 +147,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/linkify-it": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz",
- "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/puzrin"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/markdown-it"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "uc.micro": "^2.0.0"
- }
- },
- "node_modules/lunr": {
- "version": "2.3.9",
- "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz",
- "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/markdown-it": {
- "version": "14.3.0",
- "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz",
- "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/puzrin"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/markdown-it"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1",
- "entities": "^4.5.0",
- "linkify-it": "^5.0.2",
- "mdurl": "^2.0.0",
- "punycode.js": "^2.3.1",
- "uc.micro": "^2.1.0"
- },
- "bin": {
- "markdown-it": "bin/markdown-it.mjs"
- }
- },
- "node_modules/mdurl": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
- "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/minimatch": {
- "version": "10.2.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
- "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
- "dev": true,
- "license": "BlueOak-1.0.0",
- "dependencies": {
- "brace-expansion": "^5.0.5"
- },
- "engines": {
- "node": "18 || 20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
@@ -369,16 +167,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/punycode.js": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
- "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/resolve": {
"version": "1.22.12",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
@@ -438,30 +226,6 @@
"url": "https://opencollective.com/webpack"
}
},
- "node_modules/typedoc": {
- "version": "0.28.20",
- "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz",
- "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@gerrit0/mini-shiki": "^3.23.0",
- "lunr": "^2.3.9",
- "markdown-it": "^14.3.0",
- "minimatch": "^10.2.5",
- "yaml": "^2.9.0"
- },
- "bin": {
- "typedoc": "bin/typedoc"
- },
- "engines": {
- "node": ">= 18",
- "pnpm": ">= 10"
- },
- "peerDependencies": {
- "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x"
- }
- },
"node_modules/typescript": {
"version": "5.7.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz",
@@ -498,29 +262,6 @@
"peerDependencies": {
"typescript": "5.7.2"
}
- },
- "node_modules/uc.micro": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
- "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/yaml": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz",
- "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/eemeli"
- }
}
}
}
diff --git a/projects/typed-peripheral-digitalitems/package.json b/projects/typed-peripheral-digitalitems/package.json
index 0a8ab07..1741f11 100644
--- a/projects/typed-peripheral-digitalitems/package.json
+++ b/projects/typed-peripheral-digitalitems/package.json
@@ -14,8 +14,8 @@
"scripts": {
"build": "tstl",
"clean": "rm -f *.lua *.d.ts",
- "docs": "typedoc",
- "docs:watch": "typedoc --watch"
+ "docs": "node documentation/generate-docs.mjs",
+ "docs:test": "node --test documentation/generate-docs.test.mjs"
},
"dependencies": {
"@jackmacwindows/lua-types": "^2.13.1",
@@ -26,7 +26,6 @@
"@siredvin/typed-peripheral-base": "*"
},
"devDependencies": {
- "typedoc": "0.28.20",
"typescript": "5.7.2",
"typescript-to-lua": "1.29.1"
}
diff --git a/projects/typed-peripheral-digitalitems/requirements-docs.txt b/projects/typed-peripheral-digitalitems/requirements-docs.txt
new file mode 100644
index 0000000..c8de0c5
--- /dev/null
+++ b/projects/typed-peripheral-digitalitems/requirements-docs.txt
@@ -0,0 +1 @@
+mkdocs-material==9.6.14
diff --git a/projects/typed-peripheral-digitalitems/tsconfig.docs.json b/projects/typed-peripheral-digitalitems/tsconfig.docs.json
deleted file mode 100644
index 35b63ea..0000000
--- a/projects/typed-peripheral-digitalitems/tsconfig.docs.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "extends": "./tsconfig.json",
- "compilerOptions": {
- "skipLibCheck": true
- },
- "include": ["./*.ts"]
-}
diff --git a/projects/typed-peripheral-digitalitems/typedoc.json b/projects/typed-peripheral-digitalitems/typedoc.json
deleted file mode 100644
index 9a3b9c5..0000000
--- a/projects/typed-peripheral-digitalitems/typedoc.json
+++ /dev/null
@@ -1,87 +0,0 @@
-{
- "$schema": "https://typedoc.org/schema.json",
- "entryPoints": ["./shared.ts", "./digitizer.ts", "./advanced_digitizer.ts"],
- "tsconfig": "./tsconfig.docs.json",
- "out": "./docs",
- "name": "@siredvin/typed-peripheral-digitalitems",
- "readme": "./documentation/index.md",
- "projectDocuments": [
- "./documentation/getting-started.md",
- "./documentation/digital-identifiers.md",
- "./documentation/guides/*.md"
- ],
- "plugin": ["./documentation/theme/plugin.mjs"],
- "customCss": "./documentation/theme/digitalitems.css",
- "customJs": "./documentation/theme/digitalitems.js",
- "favicon": "./documentation/theme/favicon.svg",
- "lightHighlightTheme": "material-theme-lighter",
- "darkHighlightTheme": "material-theme-darker",
- "highlightLanguages": ["typescript", "lua", "json", "bash"],
- "excludeExternals": true,
- "basePath": "../..",
- "sourceLinkTemplate": "https://github.com/SirEdvin/DigitalItems/blob/{gitRevision}/{path}#L{line}",
- "navigationLinks": {
- "GitHub": "https://github.com/SirEdvin/DigitalItems"
- },
- "blockTags": [
- "@defaultValue",
- "@deprecated",
- "@example",
- "@jsx",
- "@param",
- "@privateRemarks",
- "@remarks",
- "@returns",
- "@see",
- "@throws",
- "@typeParam",
- "@author",
- "@callback",
- "@category",
- "@categoryDescription",
- "@default",
- "@document",
- "@extends",
- "@augments",
- "@yields",
- "@group",
- "@groupDescription",
- "@import",
- "@inheritDoc",
- "@license",
- "@module",
- "@mergeModuleWith",
- "@prop",
- "@property",
- "@return",
- "@satisfies",
- "@since",
- "@sortStrategy",
- "@template",
- "@this",
- "@type",
- "@typedef",
- "@summary",
- "@preventInline",
- "@inlineType",
- "@preventExpand",
- "@expandType",
- "@noSelf"
- ],
- "excludeTags": [
- "@override",
- "@virtual",
- "@privateRemarks",
- "@satisfies",
- "@overload",
- "@inline",
- "@inlineType",
- "@noSelf"
- ],
- "validation": {
- "notDocumented": true,
- "invalidLink": true,
- "notExported": true
- },
- "treatWarningsAsErrors": true
-}
diff --git a/projects/typescript-tests/package-lock.json b/projects/typescript-tests/package-lock.json
index 9eae9f0..fa73958 100644
--- a/projects/typescript-tests/package-lock.json
+++ b/projects/typescript-tests/package-lock.json
@@ -35,7 +35,6 @@
"@siredvin/typed-peripheral-base": "*"
},
"devDependencies": {
- "typedoc": "^0.28.16",
"typescript": "5.7.2",
"typescript-to-lua": "1.29.1"
}