wuira 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -18
- package/cli/wuira.mjs +67 -0
- package/config/tsconfig.json +27 -0
- package/lib/Console.d.ts +5 -0
- package/lib/Console.lua +20 -0
- package/lib/console-polyfill.d.ts +9 -0
- package/lib/console-polyfill.lua +39 -0
- package/lib/datasources/DataSource.d.ts +17 -0
- package/lib/datasources/DataSource.lua +199 -0
- package/lib/datasources/NativeNumberSource.d.ts +7 -0
- package/lib/datasources/NativeNumberSource.lua +50 -0
- package/lib/datasources/ProgressSource.d.ts +23 -0
- package/lib/datasources/ProgressSource.lua +79 -0
- package/lib/datasources/Resources.d.ts +27 -0
- package/lib/datasources/Resources.lua +102 -0
- package/lib/datasources/index.d.ts +4 -0
- package/lib/datasources/index.lua +18 -0
- package/lib/index.d.ts +6 -0
- package/lib/index.lua +12 -0
- package/{src → lib}/platform/forever.d.ts +17 -17
- package/{src/types/Anchor.ts → lib/types/Anchor.d.ts} +6 -7
- package/lib/types/Anchor.lua +8 -0
- package/lib/types/Color.lua +3 -0
- package/lib/types/Position.d.ts +21 -0
- package/lib/types/Position.lua +3 -0
- package/lib/types/Spacing.d.ts +14 -0
- package/lib/types/Spacing.lua +20 -0
- package/{src/types/Texture.ts → lib/types/Texture.d.ts} +0 -2
- package/lib/types/Texture.lua +3 -0
- package/{src/ui/Cell.tsx → lib/ui/Cell.d.ts} +6 -8
- package/lib/ui/Cell.lua +9 -0
- package/{src/ui/FlowLayout.tsx → lib/ui/FlowLayout.d.ts} +3 -8
- package/lib/ui/FlowLayout.lua +8 -0
- package/{src/ui/GridLayout.tsx → lib/ui/GridLayout.d.ts} +3 -8
- package/lib/ui/GridLayout.lua +8 -0
- package/{src/ui/Layout.ts → lib/ui/Layout.d.ts} +4 -3
- package/lib/ui/Layout.lua +3 -0
- package/{src/ui/ProgressBar.tsx → lib/ui/ProgressBar.d.ts} +4 -7
- package/lib/ui/ProgressBar.lua +8 -0
- package/lib/ui/decorations/Background.d.ts +10 -0
- package/lib/ui/decorations/Background.lua +76 -0
- package/lib/ui/decorations/Border.d.ts +18 -0
- package/lib/ui/decorations/Border.lua +188 -0
- package/lib/ui/decorations/Decoration.d.ts +13 -0
- package/lib/ui/decorations/Decoration.lua +81 -0
- package/{src/ui/decorations/Decorations.ts → lib/ui/decorations/Decorations.d.ts} +0 -1
- package/lib/ui/decorations/Decorations.lua +3 -0
- package/lib/ui/decorations/Glow.d.ts +14 -0
- package/lib/ui/decorations/Glow.lua +63 -0
- package/lib/ui/decorations/backgroundTextures.d.ts +25 -0
- package/lib/ui/decorations/backgroundTextures.lua +10 -0
- package/lib/ui/decorations/borderTextures.d.ts +41 -0
- package/lib/ui/decorations/borderTextures.lua +12 -0
- package/lib/ui/decorations/statusBarTextures.d.ts +12 -0
- package/lib/ui/decorations/statusBarTextures.lua +9 -0
- package/lib/ui/index.d.ts +11 -0
- package/lib/ui/index.lua +35 -0
- package/lib/ui/layout/calculateFlow.d.ts +12 -0
- package/lib/ui/layout/calculateFlow.lua +36 -0
- package/lib/ui/layout/calculateGrid.d.ts +9 -0
- package/lib/ui/layout/calculateGrid.lua +47 -0
- package/lib/ui/layout/measureLayout.d.ts +19 -0
- package/lib/ui/layout/measureLayout.lua +215 -0
- package/lib/ui/mount.d.ts +5 -0
- package/lib/ui/mount.lua +486 -0
- package/lib/ui/primitives/jsx.d.ts +38 -0
- package/lib/ui/primitives/jsx.lua +66 -0
- package/lib/wuira.d.ts +8 -0
- package/lib/wuira.lua +36 -0
- package/package.json +55 -5
- package/templates/addon/README.md +13 -0
- package/templates/addon/addon.toc.in +4 -0
- package/templates/addon/gitignore +3 -0
- package/templates/addon/package.json +11 -0
- package/templates/addon/src/index.tsx +7 -0
- package/templates/addon/tsconfig.json +8 -0
- package/templates/addon/wuira.config.mjs +4 -0
- package/tools/build-library.mjs +15 -0
- package/tools/module-loader.lua +3 -3
- package/tools/{wow-modules.cjs → wow-modules.mjs} +27 -11
- package/tsconfig.json +1 -1
- package/tsconfig.lib.json +8 -0
- package/addon.toc.in +0 -4
- package/cli/uira.cjs +0 -55
- package/src/Console.ts +0 -10
- package/src/UIra.ts +0 -18
- package/src/console-polyfill.ts +0 -25
- package/src/datasources/DataSource.ts +0 -42
- package/src/datasources/NativeNumberSource.ts +0 -7
- package/src/datasources/ProgressSource.ts +0 -46
- package/src/datasources/Resources.ts +0 -41
- package/src/examples/ProgressBars.tsx +0 -24
- package/src/index.tsx +0 -3
- package/src/playground/PlayerFrame.ts +0 -271
- package/src/types/Position.ts +0 -21
- package/src/types/Spacing.ts +0 -14
- package/src/ui/decorations/Background.ts +0 -29
- package/src/ui/decorations/Border.ts +0 -71
- package/src/ui/decorations/Decoration.ts +0 -59
- package/src/ui/decorations/Glow.ts +0 -25
- package/src/ui/decorations/backgroundTextures.ts +0 -10
- package/src/ui/decorations/borderTextures.ts +0 -13
- package/src/ui/decorations/statusBarTextures.ts +0 -11
- package/src/ui/layout/calculateFlow.ts +0 -33
- package/src/ui/layout/calculateGrid.ts +0 -22
- package/src/ui/layout/measureLayout.ts +0 -87
- package/src/ui/mount.ts +0 -138
- package/src/ui/primitives/jsx.ts +0 -75
- /package/{src/types/Color.ts → lib/types/Color.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,17 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wuira",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "Kerim Cetinbas",
|
|
6
6
|
"email": "a.cetinbas@icloud.com"
|
|
7
7
|
},
|
|
8
8
|
"description": "TypeScript and JSX UI framework and addon CLI for WoW Forever",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
9
|
+
"types": "lib/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./lib/index.d.ts",
|
|
13
|
+
"default": "./lib/index.lua"
|
|
14
|
+
},
|
|
15
|
+
"./ui": {
|
|
16
|
+
"types": "./lib/ui/index.d.ts",
|
|
17
|
+
"default": "./lib/ui/index.lua"
|
|
18
|
+
},
|
|
19
|
+
"./datasources": {
|
|
20
|
+
"types": "./lib/datasources/index.d.ts",
|
|
21
|
+
"default": "./lib/datasources/index.lua"
|
|
22
|
+
},
|
|
23
|
+
"./console-polyfill": {
|
|
24
|
+
"types": "./lib/console-polyfill.d.ts",
|
|
25
|
+
"default": "./lib/console-polyfill.lua"
|
|
26
|
+
},
|
|
27
|
+
"./config/tsconfig.json": "./config/tsconfig.json",
|
|
28
|
+
"./tools/wow-modules.mjs": "./tools/wow-modules.mjs",
|
|
29
|
+
"./package.json": "./package.json"
|
|
30
|
+
},
|
|
31
|
+
"typesVersions": {
|
|
32
|
+
"*": {
|
|
33
|
+
"ui": [
|
|
34
|
+
"lib/ui/index.d.ts"
|
|
35
|
+
],
|
|
36
|
+
"datasources": [
|
|
37
|
+
"lib/datasources/index.d.ts"
|
|
38
|
+
],
|
|
39
|
+
"console-polyfill": [
|
|
40
|
+
"lib/console-polyfill.d.ts"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"cli/",
|
|
46
|
+
"lib/",
|
|
47
|
+
"tools/",
|
|
48
|
+
"config/",
|
|
49
|
+
"templates/",
|
|
50
|
+
"tsconfig.json",
|
|
51
|
+
"tsconfig.lib.json",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
],
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=22.13"
|
|
57
|
+
},
|
|
11
58
|
"license": "MIT",
|
|
12
|
-
"bin": {
|
|
59
|
+
"bin": {
|
|
60
|
+
"wuira": "cli/wuira.mjs"
|
|
61
|
+
},
|
|
13
62
|
"scripts": {
|
|
14
|
-
"build": "
|
|
63
|
+
"build": "node tools/build-library.mjs",
|
|
64
|
+
"prepack": "npm run build",
|
|
15
65
|
"watch": "tstl -p tsconfig.json --watch",
|
|
16
66
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
17
67
|
"test": "node --test tests/*.test.cjs"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# {{addonName}}
|
|
2
|
+
|
|
3
|
+
Run `npm install`, then `npm run build` (or `npx wuira build`).
|
|
4
|
+
|
|
5
|
+
Edit `src/index.tsx` to build your UI. The initial empty fragment creates no native frame and logs only to the local WoW chat.
|
|
6
|
+
|
|
7
|
+
Copy the contents of `dist/` to `Interface/AddOns/{{addonName}}/`. The output includes its own TOC and all required Lua modules.
|
|
8
|
+
|
|
9
|
+
Import components from `wuira/ui`, data sources from `wuira/datasources`, and `UI` and `wuira` from `wuira`. Generated declarations and compiled Lua come from the npm package; no framework sources are copied into your project.
|
|
10
|
+
|
|
11
|
+
Set `outDir` in `wuira.config.mjs` to a relative or absolute path to build directly into your game's addon folder. Node.js and this configuration file are development tools only; they are not needed by the game.
|
|
12
|
+
|
|
13
|
+
Interface 16001 targets Forever. Edit `addon.toc.in` to change addon metadata.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
const require = createRequire(import.meta.url);
|
|
6
|
+
const root = path.resolve(import.meta.dirname, "..");
|
|
7
|
+
const result = spawnSync(process.execPath, [require.resolve("typescript-to-lua/dist/tstl.js"), "-p", "tsconfig.lib.json"], { cwd: root, stdio: "inherit" });
|
|
8
|
+
if (result.status !== 0) process.exit(result.status || 1);
|
|
9
|
+
fs.mkdirSync(path.join(root, "lib/platform"), { recursive: true });
|
|
10
|
+
fs.copyFileSync(path.join(root, "src/platform/forever.d.ts"), path.join(root, "lib/platform/forever.d.ts"));
|
|
11
|
+
for (const file of ["index.d.ts", "ui/index.d.ts", "datasources/index.d.ts"]) {
|
|
12
|
+
const target = path.join(root, "lib", file);
|
|
13
|
+
const reference = file === "index.d.ts" ? "./platform/forever.d.ts" : "../platform/forever.d.ts";
|
|
14
|
+
fs.writeFileSync(target, `/// <reference path="${reference}" />\n` + fs.readFileSync(target, "utf8"));
|
|
15
|
+
}
|
package/tools/module-loader.lua
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
local factories, cache, loading = {}, {}, {}
|
|
3
3
|
local function requireModule(name)
|
|
4
4
|
if cache[name] ~= nil then return cache[name] end
|
|
5
|
-
if loading[name] then error("
|
|
5
|
+
if loading[name] then error("wuira: circular import: " .. name) end
|
|
6
6
|
local factory = factories[name]
|
|
7
|
-
if not factory then error("
|
|
7
|
+
if not factory then error("wuira: missing module: " .. name) end
|
|
8
8
|
loading[name] = true
|
|
9
9
|
local ok, result = pcall(factory, requireModule)
|
|
10
10
|
loading[name] = nil
|
|
@@ -13,4 +13,4 @@ local function requireModule(name)
|
|
|
13
13
|
cache[name] = result
|
|
14
14
|
return result
|
|
15
15
|
end
|
|
16
|
-
|
|
16
|
+
__wuiraModules = { factories = factories, require = requireModule }
|
|
@@ -1,15 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
3
5
|
|
|
4
6
|
// WoW loads files through the TOC, rather than a filesystem-based require.
|
|
5
7
|
// Register each emitted module first; execute the entry only after registration.
|
|
6
|
-
|
|
8
|
+
export default {
|
|
9
|
+
moduleResolution(dependency, requiringFile) {
|
|
10
|
+
if (requiringFile.endsWith(".lua")) return;
|
|
11
|
+
if (dependency === "wuira" || dependency.startsWith("wuira/")) {
|
|
12
|
+
const packageFile = require.resolve("wuira/package.json", { paths: [path.dirname(requiringFile)] });
|
|
13
|
+
const subpath = dependency === "wuira" ? "index" : dependency.slice(6);
|
|
14
|
+
const base = path.join(path.dirname(packageFile), "lib", subpath);
|
|
15
|
+
for (const file of [base + ".lua", path.join(base, "index.lua")]) {
|
|
16
|
+
if (fs.existsSync(file)) return file;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
7
20
|
beforeEmit(program, options, host, files) {
|
|
8
21
|
const root = path.dirname(options.configFilePath);
|
|
9
|
-
const configPath = path.join(root, "
|
|
10
|
-
const addon = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, "utf8")) : { name: "
|
|
22
|
+
const configPath = path.join(root, "wuira.json");
|
|
23
|
+
const addon = fs.existsSync(configPath) ? JSON.parse(fs.readFileSync(configPath, "utf8")) : { name: "wuira" };
|
|
24
|
+
if (process.env.WUIRA_ADDON_NAME) addon.name = process.env.WUIRA_ADDON_NAME;
|
|
11
25
|
if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(addon.name)) throw new Error("Invalid addon name");
|
|
12
|
-
const registry = "
|
|
26
|
+
const registry = "__wuiraModules_" + addon.name.replace(/-/g, "_");
|
|
13
27
|
const out = path.resolve(root, options.outDir);
|
|
14
28
|
const moduleFiles = files.filter(file => file.outputPath.endsWith(".lua"));
|
|
15
29
|
for (const file of moduleFiles) {
|
|
@@ -27,14 +41,16 @@ module.exports = {
|
|
|
27
41
|
if (!modules.includes(relative)) modules.push(relative);
|
|
28
42
|
}
|
|
29
43
|
modules.sort();
|
|
30
|
-
const loader = fs.readFileSync(path.join(
|
|
31
|
-
const actualRegistry = addon.name === "
|
|
44
|
+
const loader = fs.readFileSync(path.join(import.meta.dirname, "module-loader.lua"), "utf8");
|
|
45
|
+
const actualRegistry = addon.name === "wuira" && !fs.existsSync(configPath) ? "__wuiraModules" : registry;
|
|
32
46
|
if (actualRegistry !== registry) for (const file of moduleFiles) file.code = file.code.replace(registry, actualRegistry);
|
|
33
|
-
files.push({ outputPath: path.join(out, "__loader.lua"), code: loader.replace(/
|
|
47
|
+
files.push({ outputPath: path.join(out, "__loader.lua"), code: loader.replace(/__wuiraModules/g, actualRegistry) });
|
|
48
|
+
// Framework builds register modules only; addon projects own their startup and TOC.
|
|
49
|
+
if (!fs.existsSync(path.join(root, "addon.toc.in"))) return;
|
|
34
50
|
files.push({ outputPath: path.join(out, "__start.lua"), code: `${actualRegistry}.require("index")\n` });
|
|
35
51
|
const toc = fs.readFileSync(path.join(root, "addon.toc.in"), "utf8");
|
|
36
52
|
const entries = ["__loader.lua", ...modules, "__start.lua"]
|
|
37
|
-
.map(file => path.relative(root, path.join(out, file)).replace(/\//g, "\\"));
|
|
38
|
-
files.push({ outputPath: path.join(root, `${addon.name}.toc`), code: toc + "\n" + entries.join("\n") + "\n" });
|
|
53
|
+
.map(file => (process.env.WUIRA_STANDALONE ? file : path.relative(root, path.join(out, file))).replace(/\//g, "\\"));
|
|
54
|
+
files.push({ outputPath: path.join(process.env.WUIRA_STANDALONE ? out : root, `${addon.name}.toc`), code: toc + "\n" + entries.join("\n") + "\n" });
|
|
39
55
|
},
|
|
40
56
|
};
|
package/tsconfig.json
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"luaTarget": "universal",
|
|
17
17
|
"luaLibImport": "inline",
|
|
18
18
|
"noImplicitGlobalVariables": true,
|
|
19
|
-
"luaPlugins": [{ "name": "./tools/wow-modules.
|
|
19
|
+
"luaPlugins": [{ "name": "./tools/wow-modules.mjs" }]
|
|
20
20
|
},
|
|
21
21
|
"include": ["src/**/*.ts", "src/**/*.tsx"]
|
|
22
22
|
}
|
package/addon.toc.in
DELETED
package/cli/uira.cjs
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require("node:fs");
|
|
3
|
-
const path = require("node:path");
|
|
4
|
-
const { spawnSync } = require("node:child_process");
|
|
5
|
-
const root = path.resolve(__dirname, "..");
|
|
6
|
-
|
|
7
|
-
function create(name, cwd = process.cwd()) {
|
|
8
|
-
if (!name || !/^[A-Za-z][A-Za-z0-9_-]*$/.test(name)) throw new Error("Use an addon name containing letters, numbers, underscores or hyphens, starting with a letter.");
|
|
9
|
-
const target = path.join(cwd, name);
|
|
10
|
-
if (fs.existsSync(target)) throw new Error(`Destination already exists: ${target}`);
|
|
11
|
-
fs.mkdirSync(target, { recursive: true });
|
|
12
|
-
const write = (file, value) => fs.writeFileSync(path.join(target, file), value);
|
|
13
|
-
fs.mkdirSync(path.join(target, "src"));
|
|
14
|
-
fs.cpSync(path.join(root, "src"), path.join(target, "src/uira"), {
|
|
15
|
-
recursive: true,
|
|
16
|
-
filter: source => !["index.tsx", "playground", "examples"].includes(path.basename(source)),
|
|
17
|
-
});
|
|
18
|
-
fs.cpSync(path.join(root, "tools"), path.join(target, "tools"), { recursive: true });
|
|
19
|
-
fs.mkdirSync(path.join(target, "cli"));
|
|
20
|
-
fs.copyFileSync(__filename, path.join(target, "cli/uira.cjs"));
|
|
21
|
-
const config = JSON.parse(fs.readFileSync(path.join(root, "tsconfig.json"), "utf8"));
|
|
22
|
-
write("tsconfig.json", JSON.stringify(config, null, 2) + "\n");
|
|
23
|
-
write("uira.json", JSON.stringify({ name }, null, 2) + "\n");
|
|
24
|
-
write("addon.toc.in", `## Interface: 16001\n## Title: ${name}\n## Notes: Built with Uira\n## Version: 0.1.0\n`);
|
|
25
|
-
write("src/index.tsx", `import "./uira/console-polyfill";\nimport { UI } from "./uira/ui/primitives/jsx";\nimport { UIra } from "./uira/UIra";\n\nfunction App() {\n console.log("${name} loaded");\n return <></>;\n}\n\nUIra.Mount(App);\n`);
|
|
26
|
-
const sourcePackage = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
27
|
-
const dependencies = { ...sourcePackage.devDependencies, ...sourcePackage.dependencies };
|
|
28
|
-
write("package.json", JSON.stringify({ name: name.toLowerCase(), version: "0.1.0", private: true,
|
|
29
|
-
license: "MIT", scripts: { build: "node cli/uira.cjs build", check: "tsc -p tsconfig.json --noEmit" },
|
|
30
|
-
devDependencies: { typescript: dependencies.typescript, "typescript-to-lua": dependencies["typescript-to-lua"] },
|
|
31
|
-
}, null, 2) + "\n");
|
|
32
|
-
fs.copyFileSync(path.join(root, "LICENSE"), path.join(target, "LICENSE"));
|
|
33
|
-
write(".gitignore", "node_modules/\ndist/\n*.toc\n");
|
|
34
|
-
write("README.md", `# ${name}\n\nInstall dependencies with \`npm install\`, then run \`wuira build\` or \`npm run build\`.\n\nThe entry is \`src/index.tsx\`. Its empty JSX creates no native frame; \`console.log\` writes only to the local WoW chat.\n\nCopy \`${name}.toc\` and \`dist/\` into \`Interface/AddOns/${name}/\`, then reload the game. Interface 16001 targets the current Forever playground; edit \`addon.toc.in\` for other clients.\n\nThis development scaffold includes Uira sources under \`src/uira\`; no separate in-game Uira addon is required.\n`);
|
|
35
|
-
return target;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function build(cwd = process.cwd()) {
|
|
39
|
-
if (!fs.existsSync(path.join(cwd, "tsconfig.json"))) throw new Error("Run wuira build inside an addon project.");
|
|
40
|
-
const compiler = require.resolve("typescript-to-lua/dist/tstl.js", { paths: [cwd, root] });
|
|
41
|
-
const result = spawnSync(process.execPath, [compiler, "-p", "tsconfig.json"], { cwd, stdio: "inherit" });
|
|
42
|
-
if (result.error) throw result.error;
|
|
43
|
-
if (result.status !== 0) throw new Error("Addon build failed.");
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (require.main === module) {
|
|
47
|
-
try {
|
|
48
|
-
const [command, name] = process.argv.slice(2);
|
|
49
|
-
if (command === "create") console.log(`Created ${create(name)}\nNext: cd ${name}, npm install, wuira build`);
|
|
50
|
-
else if (command === "build") build();
|
|
51
|
-
else if (!command || command === "--help") console.log("wuira create <addonName>\nwuira build");
|
|
52
|
-
else throw new Error(`Unknown command: ${command}`);
|
|
53
|
-
} catch (error) { console.error(error.message); process.exitCode = 1; }
|
|
54
|
-
}
|
|
55
|
-
module.exports = { create, build };
|
package/src/Console.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
declare function print(this: void, message: string): void;
|
|
2
|
-
|
|
3
|
-
/** Local client output only; never sends chat messages to other players. */
|
|
4
|
-
export class Console {
|
|
5
|
-
private constructor() {}
|
|
6
|
-
|
|
7
|
-
public static Echo(message: string): void {
|
|
8
|
-
print(`|cff60d9c5[Uira]|r ${message}`);
|
|
9
|
-
}
|
|
10
|
-
}
|
package/src/UIra.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { Element } from "./ui/primitives/jsx";
|
|
2
|
-
import type { Anchor } from "./types/Position";
|
|
3
|
-
import { mount, unmount } from "./ui/mount";
|
|
4
|
-
|
|
5
|
-
/** Public entry point for mounting JSX UI trees. */
|
|
6
|
-
export class UIra {
|
|
7
|
-
private constructor() {}
|
|
8
|
-
|
|
9
|
-
public static Mount(frame: Element | (() => Element), anchor?: Anchor, parent?: UiraNativeFrame): UiraNativeFrame | undefined {
|
|
10
|
-
const view = typeof frame === "function" ? frame() : frame;
|
|
11
|
-
if (view.kind === "empty") return undefined;
|
|
12
|
-
return mount(view, parent, anchor);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public static Unmount(frame: UiraNativeFrame): void {
|
|
16
|
-
unmount(frame);
|
|
17
|
-
}
|
|
18
|
-
}
|
package/src/console-polyfill.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
declare const _G: Record<string, unknown>;
|
|
2
|
-
declare function print(this: void, message: string): void;
|
|
3
|
-
declare function tostring(this: void, value: unknown): string;
|
|
4
|
-
|
|
5
|
-
declare global {
|
|
6
|
-
const console: {
|
|
7
|
-
log(this: void, ...values: unknown[]): void;
|
|
8
|
-
info(this: void, ...values: unknown[]): void;
|
|
9
|
-
warn(this: void, ...values: unknown[]): void;
|
|
10
|
-
error(this: void, ...values: unknown[]): void;
|
|
11
|
-
};
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function write(color: string, values: unknown[]): void {
|
|
15
|
-
print(`${color}${values.map(value => tostring(value)).join(" ")}|r`);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Explicit side-effect import installs the WoW console; no Node runtime is used.
|
|
19
|
-
_G.console = {
|
|
20
|
-
log: function(this: void, ...values: unknown[]) { write("|cffffffff", values); },
|
|
21
|
-
info: function(this: void, ...values: unknown[]) { write("|cff60d9c5", values); },
|
|
22
|
-
warn: function(this: void, ...values: unknown[]) { write("|cffffcc00", values); },
|
|
23
|
-
error: function(this: void, ...values: unknown[]) { write("|cffff5555", values); },
|
|
24
|
-
};
|
|
25
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/** A value-producing source. Secrecy describes data, not native frame protection. */
|
|
2
|
-
export abstract class DataSource<T> {
|
|
3
|
-
protected constructor(private value: T, private secret: boolean = false) {}
|
|
4
|
-
|
|
5
|
-
/** Checked when consumed, rather than inferred from combat state. */
|
|
6
|
-
public IsSecret(): boolean {
|
|
7
|
-
return this.IsValueSecret(this.GetValue());
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
public get Value(): T {
|
|
11
|
-
const value = this.GetValue();
|
|
12
|
-
this.AssertReadable(value);
|
|
13
|
-
return value;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
public Compute<TResult>(compute: (value: T) => TResult): TResult {
|
|
17
|
-
const value = this.GetValue();
|
|
18
|
-
this.AssertReadable(value);
|
|
19
|
-
return compute(value);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
protected GetValue(): T { return this.value; }
|
|
23
|
-
|
|
24
|
-
private IsValueSecret(value: T): boolean {
|
|
25
|
-
return issecretvalue(value) || this.secret || this.HasSecretFields(value);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Replace the value and its explicit restriction together, clearing stale flags. */
|
|
29
|
-
protected SetValue(value: T, secret: boolean = false): void {
|
|
30
|
-
this.value = value;
|
|
31
|
-
this.secret = secret;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/** Aggregate sources override this to check known fields without comparing them. */
|
|
35
|
-
protected HasSecretFields(_value: T): boolean {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
private AssertReadable(value: T): void {
|
|
40
|
-
if (this.IsValueSecret(value)) error("DataSource: secret data cannot be read or used in computations");
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { DataSource } from "./DataSource";
|
|
2
|
-
|
|
3
|
-
/** Read-only API value; no cached assignment or game-state mutation. */
|
|
4
|
-
export class NativeNumberSource extends DataSource<number> {
|
|
5
|
-
public constructor(private readonly read: () => number) { super(0); }
|
|
6
|
-
protected override GetValue(): number { return this.read(); }
|
|
7
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { DataSource } from "./DataSource";
|
|
2
|
-
import { NativeNumberSource } from "./NativeNumberSource";
|
|
3
|
-
|
|
4
|
-
export interface ProgressOverrides {
|
|
5
|
-
min?: number;
|
|
6
|
-
max?: number;
|
|
7
|
-
current?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
/** Shared contract for health, power, cooldowns, and other progress values. */
|
|
11
|
-
export abstract class ProgressSource extends DataSource<number> {
|
|
12
|
-
public readonly Min: DataSource<number>;
|
|
13
|
-
public readonly Max: DataSource<number>;
|
|
14
|
-
public readonly Percent: DataSource<number>;
|
|
15
|
-
|
|
16
|
-
protected constructor(private readonly readers: {
|
|
17
|
-
current: () => number; min: () => number; max: () => number; percent: () => number;
|
|
18
|
-
}) {
|
|
19
|
-
super(0);
|
|
20
|
-
this.Min = new NativeNumberSource(readers.min);
|
|
21
|
-
this.Max = new NativeNumberSource(readers.max);
|
|
22
|
-
this.Percent = new NativeNumberSource(readers.percent);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
protected override GetValue(): number { return this.readers.current(); }
|
|
26
|
-
|
|
27
|
-
public override IsSecret(): boolean {
|
|
28
|
-
return super.IsSecret() || this.Min.IsSecret() || this.Max.IsSecret() || this.Percent.IsSecret();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
public BindTo(frame: UiraNativeStatusBar, options: ProgressOverrides = {}): void {
|
|
32
|
-
const refresh = () => {
|
|
33
|
-
// Do not compare or calculate opaque native values in the direct path.
|
|
34
|
-
const current = options.current !== undefined ? options.current : this.readers.current();
|
|
35
|
-
frame.SetMinMaxValues(
|
|
36
|
-
options.min !== undefined ? options.min : this.readers.min(),
|
|
37
|
-
options.max !== undefined ? options.max : this.readers.max(),
|
|
38
|
-
);
|
|
39
|
-
frame.SetValue(current);
|
|
40
|
-
};
|
|
41
|
-
this.Listen(frame, refresh);
|
|
42
|
-
refresh();
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
protected abstract Listen(frame: UiraNativeFrame, refresh: () => void): void;
|
|
46
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { ProgressSource } from "./ProgressSource";
|
|
2
|
-
|
|
3
|
-
export const Unit = { Player: "player" } as const;
|
|
4
|
-
|
|
5
|
-
/** Direct API-to-widget binding; opaque values are never compared or calculated. */
|
|
6
|
-
abstract class Resource extends ProgressSource {
|
|
7
|
-
protected constructor(
|
|
8
|
-
current: () => number,
|
|
9
|
-
maximum: () => number,
|
|
10
|
-
percent: () => number,
|
|
11
|
-
private readonly unitToken: string,
|
|
12
|
-
private readonly events: string[],
|
|
13
|
-
) {
|
|
14
|
-
super({ current, min: () => 0, max: maximum, percent });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
protected Listen(frame: UiraNativeFrame, refresh: () => void): void {
|
|
18
|
-
frame.SetScript("OnEvent", (_frame, event, unit) => {
|
|
19
|
-
if (event === "PLAYER_ENTERING_WORLD" || unit === this.unitToken) refresh();
|
|
20
|
-
});
|
|
21
|
-
frame.RegisterEvent("PLAYER_ENTERING_WORLD");
|
|
22
|
-
for (const event of this.events) frame.RegisterEvent(event);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export class HP extends Resource {
|
|
27
|
-
public constructor(public readonly unit: string) {
|
|
28
|
-
super(() => UnitHealth(unit), () => UnitHealthMax(unit),
|
|
29
|
-
() => UnitHealthPercent(unit, true, CurveConstants.ScaleTo100), unit, ["UNIT_HEALTH", "UNIT_MAXHEALTH"]);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Explicit mana (power type 0), even while the unit uses rage/energy. */
|
|
34
|
-
export class Mana extends Resource {
|
|
35
|
-
public constructor(public readonly unit: string) {
|
|
36
|
-
super(() => UnitPower(unit, 0), () => UnitPowerMax(unit, 0),
|
|
37
|
-
() => UnitPowerPercent(unit, 0, true, CurveConstants.ScaleTo100), unit, ["UNIT_POWER_UPDATE", "UNIT_MAXPOWER", "UNIT_DISPLAYPOWER"]);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export const DataSources = { Resources: { HP, Mana } };
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { UIra } from "../UIra";
|
|
2
|
-
import { HP, Mana } from "../datasources/Resources";
|
|
3
|
-
import { Cell } from "../ui/Cell";
|
|
4
|
-
import { FlowLayout } from "../ui/FlowLayout";
|
|
5
|
-
import { ProgressBar } from "../ui/ProgressBar";
|
|
6
|
-
import { UI } from "../ui/primitives/jsx";
|
|
7
|
-
|
|
8
|
-
function App() {
|
|
9
|
-
|
|
10
|
-
const mana = new Mana("player")
|
|
11
|
-
const hp = new HP("player")
|
|
12
|
-
return (
|
|
13
|
-
<FlowLayout width={160} height={80} >
|
|
14
|
-
<ProgressBar source={mana} />
|
|
15
|
-
<ProgressBar foreground={{
|
|
16
|
-
texture: "blizzard",
|
|
17
|
-
color: { r: 0.1, g: 0.1, b: 0.1 }
|
|
18
|
-
}} source={hp} />
|
|
19
|
-
</FlowLayout>
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
UIra.Mount(App, "CENTER");
|
|
24
|
-
|
package/src/index.tsx
DELETED