feat: shell controller, TabStage, layout and theme plugins with repair view

This commit is contained in:
LukeGus committed 2026-10-18 16:37:36 -05:00
1 parent aff29a9581
commit 86161d66e2
120 files changed
+23693 -3381

No files matched your search

+12 -2
View File
@@ -1,7 +1,12 @@
import fs from "node:fs";
import path from "node:path";
import { readManifest } from "../lib/plugin-dir.mjs";
import { collectFiles, createTmxplug } from "../lib/tmxplug.mjs";
import {
collectFiles,
createTmxplug,
themeFiles,
TMXPLUG_CONTENTS,
} from "../lib/tmxplug.mjs";
import { sha256 } from "../lib/signing.mjs";
import { validate } from "./validate.mjs";
@@ -32,7 +37,12 @@ export async function pack({ cwd, args = [] }) {
fs.mkdirSync(outDir, { recursive: true });
const outfile = path.join(outDir, `${pluginId}-${version}.tmxplug`);
const archive = createTmxplug(cwd, collectFiles(cwd));
const archive = createTmxplug(
cwd,
collectFiles(cwd, [
...new Set([...TMXPLUG_CONTENTS, ...themeFiles(manifest)]),
]),
);
fs.writeFileSync(outfile, archive);
console.log(`packed ${path.basename(outfile)}`);
+26 -1
View File
@@ -17,7 +17,7 @@ export async function loadSdkModule(name) {
export async function validate({ cwd }) {
const raw = readManifest(cwd);
const { parseManifest } = await loadSdkModule("manifest");
const { parseManifest, validateThemeCss } = await loadSdkModule("manifest");
const { errors } = parseManifest(raw);
const problems = [...errors];
@@ -34,6 +34,7 @@ export async function validate({ cwd }) {
}
}
problems.push(...validateThemeFiles(cwd, raw, validateThemeCss));
problems.push(
...(await validateMigrations(cwd, raw.id ?? path.basename(cwd))),
);
@@ -53,6 +54,30 @@ export async function validate({ cwd }) {
console.log(`ok ${raw.id ?? path.basename(cwd)}`);
}
/** Every declared theme file exists and holds only CSS a theme may hold. */
export function validateThemeFiles(cwd, raw, validateThemeCss) {
const problems = [];
const themes = raw?.contributes?.themes;
if (!Array.isArray(themes)) return problems;
for (const theme of themes) {
if (typeof theme?.css !== "string" || typeof theme?.id !== "string") {
continue;
}
const file = path.join(cwd, theme.css);
if (!fs.existsSync(file)) {
problems.push(`theme ${theme.id}: ${theme.css} does not exist`);
continue;
}
for (const error of validateThemeCss(
fs.readFileSync(file, "utf8"),
theme.id,
)) {
problems.push(`theme ${theme.id}: ${error}`);
}
}
return problems;
}
/** Env vars core sets for everyone, so a plugin need not list them. */
const CORE_ENV = new Set([
"DATA_DIR",
+9
View File
@@ -2,6 +2,15 @@ import fs from "node:fs";
import path from "node:path";
import zlib from "node:zlib";
/** Theme CSS files a manifest declares, which travel with the plugin too. */
export function themeFiles(manifest) {
const themes = manifest?.contributes?.themes;
if (!Array.isArray(themes)) return [];
return themes
.map((theme) => theme?.css)
.filter((css) => typeof css === "string");
}
/** What a .tmxplug holds, in the order the spec lists it. */
export const TMXPLUG_CONTENTS = [
"manifest.json",