@stakeplate/core 0.1.0 → 0.3.0
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 +16 -0
- package/dist/audio.d.ts +2 -2
- package/dist/audio.js +20 -5
- package/dist/audio.js.map +1 -1
- package/dist/{index-BcTRfis9.d.ts → index-BXD-eK1I.d.ts} +89 -8
- package/dist/index.d.ts +119 -2
- package/dist/index.js +372 -21
- package/dist/index.js.map +1 -1
- package/dist/{bind-BBRxizWR.js → inputs-X3Tc_HTV.js} +26 -2
- package/dist/inputs-X3Tc_HTV.js.map +1 -0
- package/dist/rules.d.ts +5 -3
- package/dist/rules.js +17 -6
- package/dist/rules.js.map +1 -1
- package/dist/vite.d.ts +39 -0
- package/dist/vite.js +57 -0
- package/dist/vite.js.map +1 -0
- package/package.json +12 -6
- package/dist/bind-BBRxizWR.js.map +0 -1
package/dist/vite.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
//#region src/vite.ts
|
|
4
|
+
/** Generate a tiny blurred base64 placeholder from the image, or `null` if `sharp` is absent. */
|
|
5
|
+
async function makeLqip(imgPath, size) {
|
|
6
|
+
try {
|
|
7
|
+
return `data:image/jpeg;base64,${(await (await import("sharp")).default(imgPath).resize(size, null, { fit: "inside" }).jpeg({ quality: 55 }).toBuffer()).toString("base64")}`;
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Inline a blurred placeholder of the loader background into index.html so the first paint
|
|
14
|
+
* shows the backdrop (no black flash), then the loader blurs its full image up over it.
|
|
15
|
+
*/
|
|
16
|
+
function stakeplateBoot(options) {
|
|
17
|
+
const color = options.backgroundColor ?? "#0b0d12";
|
|
18
|
+
const blur = options.blur ?? 28;
|
|
19
|
+
const size = options.size ?? 64;
|
|
20
|
+
let root = process.cwd();
|
|
21
|
+
return {
|
|
22
|
+
name: "stakeplate-boot",
|
|
23
|
+
enforce: "pre",
|
|
24
|
+
configResolved(config) {
|
|
25
|
+
root = config.root;
|
|
26
|
+
},
|
|
27
|
+
async transformIndexHtml(html) {
|
|
28
|
+
const imgPath = isAbsolute(options.background) ? options.background : resolve(root, options.background);
|
|
29
|
+
let lqip = null;
|
|
30
|
+
try {
|
|
31
|
+
await readFile(imgPath);
|
|
32
|
+
lqip = await makeLqip(imgPath, size);
|
|
33
|
+
} catch {
|
|
34
|
+
lqip = null;
|
|
35
|
+
}
|
|
36
|
+
const tags = [{
|
|
37
|
+
tag: "style",
|
|
38
|
+
attrs: { id: "sp-boot-style" },
|
|
39
|
+
children: lqip ? `html{background:${color}}#sp-bootbg{position:fixed;inset:0;z-index:0;background:${color} url("${lqip}") center/cover no-repeat;filter:blur(${blur}px);transform:scale(1.08);pointer-events:none}` : `html{background:${color}}`,
|
|
40
|
+
injectTo: "head-prepend"
|
|
41
|
+
}];
|
|
42
|
+
if (lqip) tags.push({
|
|
43
|
+
tag: "div",
|
|
44
|
+
attrs: { id: "sp-bootbg" },
|
|
45
|
+
injectTo: "body-prepend"
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
html,
|
|
49
|
+
tags
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { stakeplateBoot };
|
|
56
|
+
|
|
57
|
+
//# sourceMappingURL=vite.js.map
|
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","names":[],"sources":["../src/vite.ts"],"sourcesContent":["// @stakeplate/core/vite — a build helper so a game's index.html needs ZERO background setup\n// and there's no black flash on first paint.\n//\n// The problem: the loader (and its backdrop) are created by JS, which only runs after the\n// module bundle downloads + parses. Until then the page is blank/black. This plugin bakes a\n// tiny BLURRED placeholder of the loader background straight INTO index.html at build time,\n// so the very first paint — before any JS — already shows the (blurred) backdrop. The loader\n// then fades its full-resolution image up over the same spot: a seamless blur-up, no flash.\n//\n// import { stakeplateBoot } from '@stakeplate/core/vite';\n// export default defineConfig({ plugins: [stakeplateBoot({ background: 'src/art/bg.png' })] });\n//\n// The placeholder is a ~64px image (a few KB, base64-inlined — no extra request) under a CSS\n// blur, generated with `sharp` if it's installed (add it as a devDependency for the blur-up).\n// Without sharp it still injects the solid `backgroundColor`, so there is never a black flash —\n// you just don't get the blurred image until the loader paints it.\n\nimport { readFile } from 'node:fs/promises';\nimport { resolve, isAbsolute } from 'node:path';\n\nexport interface StakeplateBootOptions {\n /** Path to the loader background image (the same one you pass to `loader.backgroundImage`),\n * relative to the Vite project root (or absolute). PNG/JPEG/WebP — anything `sharp` reads. */\n background: string;\n /** Solid colour painted behind the placeholder (and the fallback when `sharp` is absent).\n * Match `loader.background`. Default `#0b0d12`. */\n backgroundColor?: string;\n /** CSS blur radius (px) applied to the inlined placeholder. Default `28`. */\n blur?: number;\n /** Downscaled placeholder width (px) — smaller = tinier HTML. Default `64`. */\n size?: number;\n}\n\n/** A structural Vite `Plugin` (typed here so the core needn't depend on `vite`). */\nexport interface StakeplateBootPlugin {\n name: string;\n enforce: 'pre';\n configResolved(config: { root: string }): void;\n transformIndexHtml(html: string): Promise<{ html: string; tags: HtmlTag[] }>;\n}\ninterface HtmlTag {\n tag: string;\n attrs?: Record<string, string>;\n children?: string;\n injectTo: 'head' | 'head-prepend' | 'body' | 'body-prepend';\n}\n\n/** Generate a tiny blurred base64 placeholder from the image, or `null` if `sharp` is absent. */\nasync function makeLqip(imgPath: string, size: number): Promise<string | null> {\n try {\n // Non-literal specifier so TS doesn't type-resolve the OPTIONAL `sharp` (games without it\n // still get the solid-colour fallback — never a black flash).\n const spec = 'sharp';\n const mod = (await import(spec)) as { default: (p: string) => SharpLike };\n const buf = await mod.default(imgPath).resize(size, null, { fit: 'inside' }).jpeg({ quality: 55 }).toBuffer();\n return `data:image/jpeg;base64,${buf.toString('base64')}`;\n } catch {\n return null; // sharp not installed / unreadable — caller falls back to the solid colour\n }\n}\ninterface SharpLike {\n resize(w: number, h: null, o: { fit: 'inside' }): SharpLike;\n jpeg(o: { quality: number }): SharpLike;\n toBuffer(): Promise<Buffer>;\n}\n\n/**\n * Inline a blurred placeholder of the loader background into index.html so the first paint\n * shows the backdrop (no black flash), then the loader blurs its full image up over it.\n */\nexport function stakeplateBoot(options: StakeplateBootOptions): StakeplateBootPlugin {\n const color = options.backgroundColor ?? '#0b0d12';\n const blur = options.blur ?? 28;\n const size = options.size ?? 64;\n let root = process.cwd();\n\n return {\n name: 'stakeplate-boot',\n enforce: 'pre',\n configResolved(config) {\n root = config.root;\n },\n async transformIndexHtml(html) {\n const imgPath = isAbsolute(options.background) ? options.background : resolve(root, options.background);\n let lqip: string | null = null;\n try {\n await readFile(imgPath); // fail fast with a clear reason if the path is wrong\n lqip = await makeLqip(imgPath, size);\n } catch {\n lqip = null;\n }\n // `html { background }` guarantees no black even before the placeholder decodes; the\n // #sp-bootbg layer carries the blurred image (removed by the loader once it's done).\n const css = lqip\n ? `html{background:${color}}` +\n `#sp-bootbg{position:fixed;inset:0;z-index:0;background:${color} url(\"${lqip}\") center/cover no-repeat;` +\n `filter:blur(${blur}px);transform:scale(1.08);pointer-events:none}`\n : `html{background:${color}}`;\n const tags: HtmlTag[] = [{ tag: 'style', attrs: { id: 'sp-boot-style' }, children: css, injectTo: 'head-prepend' }];\n if (lqip) tags.push({ tag: 'div', attrs: { id: 'sp-bootbg' }, injectTo: 'body-prepend' });\n return { html, tags };\n },\n };\n}\n"],"mappings":";;;;AAgDA,eAAe,SAAS,SAAiB,MAAsC;CAC7E,IAAI;EAMF,OAAO,2BAA0B,OADf,MADC,OAAO,SAAA,CACJ,QAAQ,OAAO,CAAC,CAAC,OAAO,MAAM,MAAM,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,SAAS,EAAA,CACvE,SAAS,QAAQ;CACxD,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAWA,SAAgB,eAAe,SAAsD;CACnF,MAAM,QAAQ,QAAQ,mBAAmB;CACzC,MAAM,OAAO,QAAQ,QAAQ;CAC7B,MAAM,OAAO,QAAQ,QAAQ;CAC7B,IAAI,OAAO,QAAQ,IAAI;CAEvB,OAAO;EACL,MAAM;EACN,SAAS;EACT,eAAe,QAAQ;GACrB,OAAO,OAAO;EAChB;EACA,MAAM,mBAAmB,MAAM;GAC7B,MAAM,UAAU,WAAW,QAAQ,UAAU,IAAI,QAAQ,aAAa,QAAQ,MAAM,QAAQ,UAAU;GACtG,IAAI,OAAsB;GAC1B,IAAI;IACF,MAAM,SAAS,OAAO;IACtB,OAAO,MAAM,SAAS,SAAS,IAAI;GACrC,QAAQ;IACN,OAAO;GACT;GAQA,MAAM,OAAkB,CAAC;IAAE,KAAK;IAAS,OAAO,EAAE,IAAI,gBAAgB;IAAG,UAL7D,OACR,mBAAmB,MAAM,0DACiC,MAAM,QAAQ,KAAK,wCAC9D,KAAK,kDACpB,mBAAmB,MAAM;IAC2D,UAAU;GAAe,CAAC;GAClH,IAAI,MAAM,KAAK,KAAK;IAAE,KAAK;IAAO,OAAO,EAAE,IAAI,YAAY;IAAG,UAAU;GAAe,CAAC;GACxF,OAAO;IAAE;IAAM;GAAK;EACtB;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stakeplate/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Batteries-included Stake Engine game core — RGS transport, boot, round FSM, HUD binding, audio, i18n and a compliant rules builder. Bring a scene, a Present phase and a pure interpretBook; the core does the rest.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"stake",
|
|
@@ -64,6 +64,11 @@
|
|
|
64
64
|
"types": "./dist/rules.d.ts",
|
|
65
65
|
"import": "./dist/rules.js",
|
|
66
66
|
"default": "./dist/rules.js"
|
|
67
|
+
},
|
|
68
|
+
"./vite": {
|
|
69
|
+
"types": "./dist/vite.d.ts",
|
|
70
|
+
"import": "./dist/vite.js",
|
|
71
|
+
"default": "./dist/vite.js"
|
|
67
72
|
}
|
|
68
73
|
},
|
|
69
74
|
"files": [
|
|
@@ -84,8 +89,8 @@
|
|
|
84
89
|
"typecheck": "tsc --noEmit"
|
|
85
90
|
},
|
|
86
91
|
"peerDependencies": {
|
|
87
|
-
"@open-slot-ui/core": ">=0.8.
|
|
88
|
-
"@open-slot-ui/pixi": ">=0.8.
|
|
92
|
+
"@open-slot-ui/core": ">=0.8.3",
|
|
93
|
+
"@open-slot-ui/pixi": ">=0.8.3",
|
|
89
94
|
"@schmooky/zvuk": ">=1.13",
|
|
90
95
|
"pixi.js": "^8"
|
|
91
96
|
},
|
|
@@ -107,12 +112,13 @@
|
|
|
107
112
|
"mobx": "^6.13.0"
|
|
108
113
|
},
|
|
109
114
|
"devDependencies": {
|
|
110
|
-
"@open-slot-ui/core": "^0.8.
|
|
111
|
-
"@open-slot-ui/pixi": "^0.8.
|
|
115
|
+
"@open-slot-ui/core": "^0.8.3",
|
|
116
|
+
"@open-slot-ui/pixi": "^0.8.3",
|
|
112
117
|
"@schmooky/zvuk": "^1.13.0",
|
|
113
118
|
"pixi.js": "^8",
|
|
114
119
|
"tsdown": "^0.22.4",
|
|
115
120
|
"typescript": "^5.7.2",
|
|
116
|
-
"vitest": "^3.0.5"
|
|
121
|
+
"vitest": "^3.0.5",
|
|
122
|
+
"@types/node": "^22.20.0"
|
|
117
123
|
}
|
|
118
124
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bind-BBRxizWR.js","names":[],"sources":["../src/audio/bind.ts"],"sourcesContent":["// The mixer↔HUD binding, with NO @schmooky/zvuk dependency — so `createStakeGame` can\n// auto-wire audio without pulling zvuk into every game's bundle. It talks to the mixer\n// through a minimal structural port (`MixerLike`), which @stakeplate/core/audio's\n// `GameAudio` satisfies. `@open-slot-ui/pixi` is imported type-only (erased at build).\n\nimport type { BootedHud } from '@open-slot-ui/pixi';\n\n/** The two volume groups the HUD's two sliders drive. */\nexport type MixerGroup = 'music' | 'effects';\n\n/**\n * The minimal mixer surface the HUD binding needs. The HUD has exactly two sliders —\n * **Music** and **Effects** — so the port is group-level, not per-bus. GameAudio satisfies\n * it structurally.\n */\nexport interface MixerLike {\n /** Set a group's level (0..1) — applies to every bus in the group. */\n setGroupLevel(group: MixerGroup, level: number): void;\n /** Read a group's level (average across its buses). */\n getGroupLevel(group: MixerGroup): number;\n /** Mute/unmute everything (the Sound toggle). */\n setMuted(muted: boolean): void;\n /** Resume the AudioContext from a user gesture. Present on GameAudio. */\n unlock?(): Promise<void> | void;\n}\n\n/**\n * Bind the HUD sound controls to a mixer: the **Music slider → `music` group**, the\n * **Effects slider → `effects` group** (both persisted to localStorage + restored), and the\n * **Sound toggle → mute** everything. Returns a disposer.\n */\nexport function bindMixerToHud(mixer: MixerLike, hud: BootedHud, opts: { storageKey?: string } = {}): () => void {\n const key = opts.storageKey ?? 'stakeplate.mixer';\n const save = (): void => {\n try {\n localStorage.setItem(key, JSON.stringify({ music: mixer.getGroupLevel('music'), effects: mixer.getGroupLevel('effects') }));\n } catch {\n /* storage may be unavailable */\n }\n };\n try {\n const saved = JSON.parse(localStorage.getItem(key) || '{}') as { music?: number; effects?: number };\n if (typeof saved.music === 'number') mixer.setGroupLevel('music', saved.music);\n if (typeof saved.effects === 'number') mixer.setGroupLevel('effects', saved.effects);\n } catch {\n /* ignore */\n }\n\n const disposers: Array<() => void> = [];\n disposers.push(\n hud.on('valueChanged', (p) => {\n const v = p as { id?: string; value?: number };\n if (typeof v?.value !== 'number') return;\n // HUD control ids: 'music' → music group, 'sfx' (the Effects slider) → effects group.\n if (v.id === 'music') { mixer.setGroupLevel('music', v.value); save(); }\n if (v.id === 'sfx') { mixer.setGroupLevel('effects', v.value); save(); }\n }),\n );\n disposers.push(hud.ui.muted.subscribe((m: boolean) => mixer.setMuted(m)));\n return () => { for (const d of disposers.splice(0)) d(); };\n}\n"],"mappings":";;;;;;AA+BA,SAAgB,eAAe,OAAkB,KAAgB,OAAgC,CAAC,GAAe;CAC/G,MAAM,MAAM,KAAK,cAAc;CAC/B,MAAM,aAAmB;EACvB,IAAI;GACF,aAAa,QAAQ,KAAK,KAAK,UAAU;IAAE,OAAO,MAAM,cAAc,OAAO;IAAG,SAAS,MAAM,cAAc,SAAS;GAAE,CAAC,CAAC;EAC5H,QAAQ,CAER;CACF;CACA,IAAI;EACF,MAAM,QAAQ,KAAK,MAAM,aAAa,QAAQ,GAAG,KAAK,IAAI;EAC1D,IAAI,OAAO,MAAM,UAAU,UAAU,MAAM,cAAc,SAAS,MAAM,KAAK;EAC7E,IAAI,OAAO,MAAM,YAAY,UAAU,MAAM,cAAc,WAAW,MAAM,OAAO;CACrF,QAAQ,CAER;CAEA,MAAM,YAA+B,CAAC;CACtC,UAAU,KACR,IAAI,GAAG,iBAAiB,MAAM;EAC5B,MAAM,IAAI;EACV,IAAI,OAAO,GAAG,UAAU,UAAU;EAElC,IAAI,EAAE,OAAO,SAAS;GAAE,MAAM,cAAc,SAAS,EAAE,KAAK;GAAG,KAAK;EAAG;EACvE,IAAI,EAAE,OAAO,OAAO;GAAE,MAAM,cAAc,WAAW,EAAE,KAAK;GAAG,KAAK;EAAG;CACzE,CAAC,CACH;CACA,UAAU,KAAK,IAAI,GAAG,MAAM,WAAW,MAAe,MAAM,SAAS,CAAC,CAAC,CAAC;CACxE,aAAa;EAAE,KAAK,MAAM,KAAK,UAAU,OAAO,CAAC,GAAG,EAAE;CAAG;AAC3D"}
|