feat: shared list kit, settings search, rail order and ui consistency fixes

This commit is contained in:
LukeGus committed 2026-10-06 13:02:05 -05:00
1 parent 3ed5775ed5
commit 58c3434e58
68 files changed
+2078 -512

No files matched your search

+17
View File
@@ -5,6 +5,10 @@
* dist/backend/backend/plugins -> dist/plugins, which is where this writes.
* Each .tmxplug is downloaded (or read from a path), checked against its
* pinned sha256 and unpacked.
*
* For development, `--local <dir>` (or TERMIX_LOCAL_PLUGINS) points at a
* folder of plugin repos like ../Termix-Plugins. Any plugin built there (has
* dist/) is copied in place of its pin, so local plugin changes show up.
*/
const fs = require("node:fs");
@@ -13,6 +17,8 @@ const {
loadBundledPlugins,
fetchArtifact,
extractArtifact,
findLocalPluginBuilds,
copyLocalBuild,
} = require("./lib/bundled-plugins.cjs");
const root = path.resolve(__dirname, "..");
@@ -20,11 +26,22 @@ const destination = path.join(root, "dist", "plugins");
async function main() {
const plugins = loadBundledPlugins(root);
const flag = process.argv.indexOf("--local");
const localArg =
flag !== -1 ? process.argv[flag + 1] : process.env.TERMIX_LOCAL_PLUGINS;
const localDir = localArg ? path.resolve(root, localArg) : null;
const local = findLocalPluginBuilds(localDir);
fs.rmSync(destination, { recursive: true, force: true });
fs.mkdirSync(destination, { recursive: true });
for (const plugin of plugins) {
const repo = local.get(plugin.id);
if (repo) {
copyLocalBuild(repo, path.join(destination, plugin.id));
console.log(`copied local build of ${plugin.id}`);
continue;
}
const buffer = await fetchArtifact(plugin, root);
await extractArtifact(buffer, path.join(destination, plugin.id), plugin.id);
console.log(`unpacked ${plugin.id}`);
+38
View File
@@ -10,6 +10,8 @@ import {
fetchArtifact,
extractArtifact,
pinsFromIndex,
findLocalPluginBuilds,
copyLocalBuild,
} from "./lib/bundled-plugins.cjs";
const cleanups: Array<() => void> = [];
@@ -192,3 +194,39 @@ describe("pinsFromIndex", () => {
);
});
});
describe("local plugin builds", () => {
it("finds built repos by manifest id and skips unbuilt ones", () => {
const dir = tempDir();
const built = path.join(dir, "Plugin-Files");
fs.mkdirSync(path.join(built, "dist"), { recursive: true });
fs.writeFileSync(
path.join(built, "manifest.json"),
JSON.stringify({ id: "file-manager" }),
);
const unbuilt = path.join(dir, "Plugin-Other");
fs.mkdirSync(unbuilt);
fs.writeFileSync(
path.join(unbuilt, "manifest.json"),
JSON.stringify({ id: "other" }),
);
const builds = findLocalPluginBuilds(dir);
expect([...builds.keys()]).toEqual(["file-manager"]);
expect(findLocalPluginBuilds(null).size).toBe(0);
});
it("copies only the files a bundled plugin needs", () => {
const repo = tempDir();
fs.mkdirSync(path.join(repo, "dist"));
fs.writeFileSync(path.join(repo, "dist", "frontend.js"), "x");
fs.writeFileSync(path.join(repo, "manifest.json"), "{}");
fs.mkdirSync(path.join(repo, "src"));
const out = path.join(tempDir(), "file-manager");
copyLocalBuild(repo, out);
expect(fs.existsSync(path.join(out, "dist", "frontend.js"))).toBe(true);
expect(fs.existsSync(path.join(out, "manifest.json"))).toBe(true);
expect(fs.existsSync(path.join(out, "src"))).toBe(false);
});
});
+8
View File
@@ -20,6 +20,7 @@ const TSCONFIG = path.join(ROOT, "tsconfig.app.json");
const ALLOWED = [
"ActionSlot",
"AddButton",
"Alert",
"AlertDescription",
"AlertDialog",
@@ -100,6 +101,7 @@ const ALLOWED = [
"FakeSwitch",
"Field",
"FieldPair",
"FormFooter",
"FullScreenAppPhase",
"FullScreenAppWrapper",
"GridCardCatalogEntry",
@@ -124,6 +126,11 @@ const ALLOWED = [
"Label",
"LineChart",
"LineChartSeries",
"ListBadge",
"ListRow",
"ListRowAction",
"ListRowFolder",
"ListRowTone",
"LogContext",
"LogEntry",
"MFAPromptMode",
@@ -140,6 +147,7 @@ const ALLOWED = [
"PROMPT_DESTRUCTIVE_BUTTON",
"PROMPT_PRIMARY_BUTTON",
"PanelDensity",
"PanelList",
"PanelSearch",
"PanelShell",
"PanelViewMode",
+44
View File
@@ -172,7 +172,51 @@ function pinsFromIndex(index, ids) {
});
}
const LOCAL_FILES = [
"manifest.json",
"dist",
"locales",
"migrations",
"README.md",
"CHANGELOG.json",
];
/**
* Built plugin repos under dir (each with manifest.json and dist/), by id.
* Used in development to stage local plugin builds instead of the pins.
*/
function findLocalPluginBuilds(dir) {
const builds = new Map();
if (!dir || !fs.existsSync(dir)) return builds;
for (const name of fs.readdirSync(dir)) {
const repo = path.join(dir, name);
const manifestPath = path.join(repo, "manifest.json");
if (!fs.existsSync(manifestPath)) continue;
if (!fs.existsSync(path.join(repo, "dist"))) continue;
try {
const { id } = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
if (typeof id === "string" && ID.test(id)) builds.set(id, repo);
} catch {
// not a plugin repo
}
}
return builds;
}
/** Copies a local plugin build into the staging folder. */
function copyLocalBuild(repo, destination) {
fs.mkdirSync(destination, { recursive: true });
for (const entry of LOCAL_FILES) {
const from = path.join(repo, entry);
if (fs.existsSync(from)) {
fs.cpSync(from, path.join(destination, entry), { recursive: true });
}
}
}
module.exports = {
findLocalPluginBuilds,
copyLocalBuild,
parseBundledPlugins,
pinsFromIndex,
loadBundledPlugins,