@jgengine/react 0.13.0 → 0.15.0-next.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/CHANGELOG.md +529 -1
- package/dist/accessibility.d.ts +39 -0
- package/dist/accessibility.js +61 -0
- package/dist/achievements.d.ts +50 -0
- package/dist/achievements.js +94 -0
- package/dist/cameraShake.d.ts +52 -0
- package/dist/cameraShake.js +53 -0
- package/dist/coachMarks.d.ts +72 -0
- package/dist/coachMarks.js +136 -0
- package/dist/codex.d.ts +25 -0
- package/dist/codex.js +62 -0
- package/dist/comboMeter.d.ts +63 -0
- package/dist/comboMeter.js +93 -0
- package/dist/components.js +4 -3
- package/dist/creditsScreen.d.ts +19 -0
- package/dist/creditsScreen.js +31 -0
- package/dist/cutscene.d.ts +66 -0
- package/dist/cutscene.js +132 -0
- package/dist/damageDirection.d.ts +51 -0
- package/dist/damageDirection.js +103 -0
- package/dist/dialogueView.d.ts +69 -0
- package/dist/dialogueView.js +111 -0
- package/dist/entityFrames.d.ts +114 -0
- package/dist/entityFrames.js +55 -0
- package/dist/fastTravel.d.ts +36 -0
- package/dist/fastTravel.js +103 -0
- package/dist/floatingText.d.ts +84 -0
- package/dist/floatingText.js +61 -0
- package/dist/gameViewport.js +8 -12
- package/dist/hooks.d.ts +42 -0
- package/dist/hooks.js +85 -0
- package/dist/hudLayout.js +138 -9
- package/dist/hudTheme.d.ts +104 -7
- package/dist/hudTheme.js +300 -2
- package/dist/i18n.d.ts +29 -0
- package/dist/i18n.js +44 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +34 -0
- package/dist/interactionPrompt.d.ts +80 -0
- package/dist/interactionPrompt.js +112 -0
- package/dist/keybindingMenu.d.ts +59 -0
- package/dist/keybindingMenu.js +146 -0
- package/dist/killFeed.d.ts +63 -0
- package/dist/killFeed.js +88 -0
- package/dist/map.d.ts +123 -2
- package/dist/map.js +239 -36
- package/dist/menuRouter.d.ts +51 -0
- package/dist/menuRouter.js +49 -0
- package/dist/modals.d.ts +131 -0
- package/dist/modals.js +168 -0
- package/dist/notifications.d.ts +39 -0
- package/dist/notifications.js +111 -0
- package/dist/objectiveBanner.d.ts +62 -0
- package/dist/objectiveBanner.js +129 -0
- package/dist/panels.d.ts +10 -2
- package/dist/panels.js +9 -3
- package/dist/photoMode.d.ts +21 -0
- package/dist/photoMode.js +39 -0
- package/dist/previewFixtures.d.ts +30 -0
- package/dist/previewFixtures.js +46 -0
- package/dist/provider.d.ts +15 -0
- package/dist/provider.js +17 -1
- package/dist/questTracker.d.ts +24 -0
- package/dist/questTracker.js +31 -0
- package/dist/quickMenu.d.ts +39 -0
- package/dist/quickMenu.js +79 -0
- package/dist/radialMenu.d.ts +50 -0
- package/dist/radialMenu.js +69 -0
- package/dist/saveSlots.d.ts +81 -0
- package/dist/saveSlots.js +138 -0
- package/dist/scoreboard.d.ts +67 -0
- package/dist/scoreboard.js +71 -0
- package/dist/settings.d.ts +9 -3
- package/dist/settings.js +10 -4
- package/dist/shopGrid.d.ts +70 -0
- package/dist/shopGrid.js +129 -0
- package/dist/statusEffectBar.d.ts +52 -0
- package/dist/statusEffectBar.js +57 -0
- package/dist/streetGeometryPreview.d.ts +7 -0
- package/dist/streetGeometryPreview.js +119 -0
- package/dist/talentTree.d.ts +60 -0
- package/dist/talentTree.js +217 -0
- package/dist/timerReadout.d.ts +90 -0
- package/dist/timerReadout.js +111 -0
- package/dist/useDebouncedCommit.d.ts +54 -0
- package/dist/useDebouncedCommit.js +81 -0
- package/dist/waveHud.d.ts +49 -0
- package/dist/waveHud.js +53 -0
- package/dist/waypointMarkers.d.ts +80 -0
- package/dist/waypointMarkers.js +116 -0
- package/package.json +3 -3
- package/skills/ce-handoff/SKILL.md +149 -0
- package/skills/jgengine/SKILL.md +7 -3
- package/skills/jgengine/api.md +17 -7
- package/skills/jgengine/capabilities.md +12 -0
- package/skills/jgengine/recipes/minimal-game.md +7 -4
- package/skills/jgengine-ui/SKILL.md +8 -3
- package/skills/jgengine-ui/api.md +732 -23
- package/skills/jgengine-ui/capabilities.md +371 -2
- package/skills/jgengine-ui/recipes/overhead-entity-frames.md +136 -0
- package/skills/jgengine-ui/reference.md +7 -4
package/dist/hudTheme.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import {} from "react";
|
|
2
2
|
/** Emits the CSS custom properties for a theme — spread onto any HUD ancestor to re-skin the subtree. */
|
|
3
3
|
export function hudThemeVars(theme) {
|
|
4
|
-
const { palette, frame, bar, slot } = theme;
|
|
4
|
+
const { palette, surface, status, font, rarity, frame, bar, slot } = theme;
|
|
5
5
|
return {
|
|
6
6
|
// atomic-bar tokens (same names the bars read — #1033)
|
|
7
7
|
"--jg-health": palette.health,
|
|
8
8
|
"--jg-health-low": palette.healthLow,
|
|
9
|
+
"--jg-health-deep": palette.healthDeep,
|
|
9
10
|
"--jg-mana": palette.mana,
|
|
11
|
+
"--jg-mana-deep": palette.manaDeep,
|
|
10
12
|
"--jg-stamina": palette.stamina,
|
|
13
|
+
"--jg-stamina-deep": palette.staminaDeep,
|
|
11
14
|
"--jg-shield": palette.shield,
|
|
15
|
+
"--jg-shield-deep": palette.shieldDeep,
|
|
12
16
|
"--jg-xp": palette.xp,
|
|
17
|
+
"--jg-xp-deep": palette.xpDeep,
|
|
13
18
|
"--jg-soul": palette.soul,
|
|
14
19
|
"--jg-ammo": palette.ammo,
|
|
15
20
|
"--jg-boss": palette.boss,
|
|
@@ -21,6 +26,30 @@ export function hudThemeVars(theme) {
|
|
|
21
26
|
"--jg-bar-text": bar.text,
|
|
22
27
|
// shared chrome tokens
|
|
23
28
|
"--jg-accent": palette.accent,
|
|
29
|
+
"--jg-accent-deep": palette.accentDeep,
|
|
30
|
+
"--jg-accent-glow": palette.accentGlow,
|
|
31
|
+
"--jg-accent-dim": palette.accentDim,
|
|
32
|
+
"--jg-surface": surface.surface,
|
|
33
|
+
"--jg-surface-deep": surface.surfaceDeep,
|
|
34
|
+
"--jg-edge": surface.edge,
|
|
35
|
+
"--jg-edge-bright": surface.edgeBright,
|
|
36
|
+
"--jg-text": surface.text,
|
|
37
|
+
"--jg-text-dim": surface.textDim,
|
|
38
|
+
"--jg-backdrop": surface.backdrop,
|
|
39
|
+
"--jg-danger": status.danger,
|
|
40
|
+
"--jg-warning": status.warning,
|
|
41
|
+
"--jg-success": status.success,
|
|
42
|
+
"--jg-hostile": status.hostile,
|
|
43
|
+
"--jg-friendly": status.friendly,
|
|
44
|
+
"--jg-neutral": status.neutral,
|
|
45
|
+
"--jg-font-display": font.display,
|
|
46
|
+
"--jg-font-numeric": font.numeric,
|
|
47
|
+
"--jg-font-body": font.body,
|
|
48
|
+
"--jg-rarity-common": rarity.common,
|
|
49
|
+
"--jg-rarity-uncommon": rarity.uncommon,
|
|
50
|
+
"--jg-rarity-rare": rarity.rare,
|
|
51
|
+
"--jg-rarity-epic": rarity.epic,
|
|
52
|
+
"--jg-rarity-legendary": rarity.legendary,
|
|
24
53
|
"--jg-frame-bg": frame.bg,
|
|
25
54
|
"--jg-frame-border": frame.border,
|
|
26
55
|
"--jg-frame-radius": frame.radius,
|
|
@@ -44,6 +73,43 @@ export const defaultHudTheme = {
|
|
|
44
73
|
ammo: "#f8b84e",
|
|
45
74
|
boss: "#ef4444",
|
|
46
75
|
accent: "#38bdf8",
|
|
76
|
+
healthDeep: "#7f1d1f",
|
|
77
|
+
manaDeep: "#1d4ed8",
|
|
78
|
+
staminaDeep: "#3f6212",
|
|
79
|
+
shieldDeep: "#0e7490",
|
|
80
|
+
xpDeep: "#6b21a8",
|
|
81
|
+
accentDeep: "#0b5f88",
|
|
82
|
+
accentGlow: "rgba(56, 189, 248, 0.5)",
|
|
83
|
+
accentDim: "rgba(56, 189, 248, 0.18)",
|
|
84
|
+
},
|
|
85
|
+
surface: {
|
|
86
|
+
surface: "#141820",
|
|
87
|
+
surfaceDeep: "#0a0c10",
|
|
88
|
+
edge: "#2c3340",
|
|
89
|
+
edgeBright: "#4a5566",
|
|
90
|
+
text: "#f5f7fa",
|
|
91
|
+
textDim: "#94a3b8",
|
|
92
|
+
backdrop: "rgba(4,7,12,0.62)",
|
|
93
|
+
},
|
|
94
|
+
status: {
|
|
95
|
+
danger: "#e5484d",
|
|
96
|
+
warning: "#f8b84e",
|
|
97
|
+
success: "#3fa34d",
|
|
98
|
+
hostile: "#e5484d",
|
|
99
|
+
friendly: "#38bdf8",
|
|
100
|
+
neutral: "#c9b458",
|
|
101
|
+
},
|
|
102
|
+
font: {
|
|
103
|
+
display: '"Segoe UI", system-ui, sans-serif',
|
|
104
|
+
numeric: 'Consolas, "Cascadia Mono", "SF Mono", "Roboto Mono", monospace',
|
|
105
|
+
body: '"Segoe UI", system-ui, sans-serif',
|
|
106
|
+
},
|
|
107
|
+
rarity: {
|
|
108
|
+
common: "#b4b2a8",
|
|
109
|
+
uncommon: "#57c14f",
|
|
110
|
+
rare: "#4a86d8",
|
|
111
|
+
epic: "#a04fd0",
|
|
112
|
+
legendary: "#e0862e",
|
|
47
113
|
},
|
|
48
114
|
frame: {
|
|
49
115
|
bg: "linear-gradient(180deg, rgba(20,24,32,0.86), rgba(10,12,16,0.9))",
|
|
@@ -62,15 +128,73 @@ export const defaultHudTheme = {
|
|
|
62
128
|
slot: { bg: "rgba(12,14,20,0.7)", border: "1px solid rgba(255,255,255,0.12)", radius: "8px" },
|
|
63
129
|
ring: "rgba(140,170,200,0.35)",
|
|
64
130
|
};
|
|
65
|
-
|
|
131
|
+
/**
|
|
132
|
+
* Builds a full {@link HudTheme} from per-group partials, filling the rest from
|
|
133
|
+
* {@link defaultHudTheme}. Reach for this when a game wants to name exact values; use
|
|
134
|
+
* {@link deriveHudTheme} when a seed color pair is enough.
|
|
135
|
+
*
|
|
136
|
+
* @capability hud-theme-author fill a full HudTheme from per-group partial overrides
|
|
137
|
+
*/
|
|
138
|
+
export function hudTheme(overrides) {
|
|
66
139
|
return {
|
|
67
140
|
palette: { ...defaultHudTheme.palette, ...overrides.palette },
|
|
141
|
+
surface: { ...defaultHudTheme.surface, ...overrides.surface },
|
|
142
|
+
status: { ...defaultHudTheme.status, ...overrides.status },
|
|
143
|
+
font: { ...defaultHudTheme.font, ...overrides.font },
|
|
144
|
+
rarity: { ...defaultHudTheme.rarity, ...overrides.rarity },
|
|
68
145
|
frame: { ...defaultHudTheme.frame, ...overrides.frame },
|
|
69
146
|
bar: { ...defaultHudTheme.bar, ...overrides.bar },
|
|
70
147
|
slot: { ...defaultHudTheme.slot, ...overrides.slot },
|
|
71
148
|
ring: overrides.ring ?? defaultHudTheme.ring,
|
|
72
149
|
};
|
|
73
150
|
}
|
|
151
|
+
const theme = hudTheme;
|
|
152
|
+
/** Builds a chrome-led preset: the named surface/edge values drive frames, troughs, and slots. */
|
|
153
|
+
function chrome(spec) {
|
|
154
|
+
return theme({
|
|
155
|
+
palette: {
|
|
156
|
+
health: spec.fills.health,
|
|
157
|
+
healthLow: spec.deeps.health,
|
|
158
|
+
healthDeep: spec.deeps.health,
|
|
159
|
+
mana: spec.fills.mana,
|
|
160
|
+
manaDeep: spec.deeps.mana,
|
|
161
|
+
stamina: spec.fills.stamina,
|
|
162
|
+
staminaDeep: spec.deeps.stamina,
|
|
163
|
+
shield: spec.fills.shield,
|
|
164
|
+
shieldDeep: spec.deeps.shield,
|
|
165
|
+
xp: spec.fills.xp,
|
|
166
|
+
xpDeep: spec.deeps.xp,
|
|
167
|
+
soul: mix(spec.fills.xp, "#ffffff", 0.25),
|
|
168
|
+
ammo: spec.status.warning,
|
|
169
|
+
boss: spec.status.danger,
|
|
170
|
+
accent: spec.accent,
|
|
171
|
+
accentDeep: spec.accentDeep,
|
|
172
|
+
accentGlow: spec.accentGlow,
|
|
173
|
+
accentDim: alpha(spec.accent, 0.18),
|
|
174
|
+
},
|
|
175
|
+
surface: {
|
|
176
|
+
surface: spec.surface,
|
|
177
|
+
surfaceDeep: spec.surfaceDeep,
|
|
178
|
+
edge: spec.edge,
|
|
179
|
+
edgeBright: spec.edgeBright,
|
|
180
|
+
text: spec.text,
|
|
181
|
+
textDim: spec.textDim,
|
|
182
|
+
backdrop: alpha(spec.surfaceDeep, 0.72),
|
|
183
|
+
},
|
|
184
|
+
status: spec.status,
|
|
185
|
+
font: { display: spec.display },
|
|
186
|
+
rarity: spec.rarity,
|
|
187
|
+
frame: {
|
|
188
|
+
bg: `linear-gradient(180deg, ${alpha(spec.surface, 0.92)}, ${alpha(spec.surfaceDeep, 0.94)})`,
|
|
189
|
+
border: `1px solid ${spec.edge}`,
|
|
190
|
+
radius: "6px",
|
|
191
|
+
glow: `0 6px 20px ${alpha(spec.surfaceDeep, 0.7)}`,
|
|
192
|
+
},
|
|
193
|
+
bar: { track: alpha(spec.surfaceDeep, 0.8), frame: spec.surfaceDeep, text: spec.text },
|
|
194
|
+
slot: { bg: alpha(spec.surface, 0.8), border: `1px solid ${spec.edge}`, radius: "6px" },
|
|
195
|
+
ring: alpha(spec.edgeBright, 0.55),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
74
198
|
/** Built-in theme presets for demos/scaffold only — not product identity; games author their own theme. */
|
|
75
199
|
export const HUD_THEME_PRESETS = {
|
|
76
200
|
"arcane-stone": theme({
|
|
@@ -145,7 +269,181 @@ export const HUD_THEME_PRESETS = {
|
|
|
145
269
|
slot: { bg: "#4a4a4a", border: "3px solid #2a2a2a", radius: "0px" },
|
|
146
270
|
ring: "rgba(240,192,64,0.5)",
|
|
147
271
|
}),
|
|
272
|
+
ember: chrome({
|
|
273
|
+
accent: "#e3b054",
|
|
274
|
+
accentGlow: "rgba(227, 176, 84, 0.55)",
|
|
275
|
+
accentDeep: "#8a6425",
|
|
276
|
+
surface: "#17120d",
|
|
277
|
+
surfaceDeep: "#0b0906",
|
|
278
|
+
edge: "#57452c",
|
|
279
|
+
edgeBright: "#93794c",
|
|
280
|
+
text: "#f2e7d0",
|
|
281
|
+
textDim: "#a3947a",
|
|
282
|
+
fills: { health: "#5cc35f", mana: "#4a86d8", stamina: "#d9c33f", xp: "#a566d9", shield: "#9fb9c9" },
|
|
283
|
+
deeps: { health: "#26662c", mana: "#20406f", stamina: "#6e621c", xp: "#4e2c6e", shield: "#48606e" },
|
|
284
|
+
status: {
|
|
285
|
+
danger: "#e0483e",
|
|
286
|
+
warning: "#e8a33d",
|
|
287
|
+
success: "#6fce6f",
|
|
288
|
+
hostile: "#d84a3a",
|
|
289
|
+
friendly: "#4fa9e0",
|
|
290
|
+
neutral: "#d9d04f",
|
|
291
|
+
},
|
|
292
|
+
display: '"Palatino Linotype", Palatino, "Book Antiqua", Georgia, serif',
|
|
293
|
+
rarity: {
|
|
294
|
+
common: "#b4b2a8",
|
|
295
|
+
uncommon: "#57c14f",
|
|
296
|
+
rare: "#4a86d8",
|
|
297
|
+
epic: "#a04fd0",
|
|
298
|
+
legendary: "#e0862e",
|
|
299
|
+
},
|
|
300
|
+
}),
|
|
301
|
+
synthwave: chrome({
|
|
302
|
+
accent: "#41e6f0",
|
|
303
|
+
accentGlow: "rgba(65, 230, 240, 0.55)",
|
|
304
|
+
accentDeep: "#136b74",
|
|
305
|
+
surface: "#12101f",
|
|
306
|
+
surfaceDeep: "#080711",
|
|
307
|
+
edge: "#3c3467",
|
|
308
|
+
edgeBright: "#6a5daa",
|
|
309
|
+
text: "#e9ecff",
|
|
310
|
+
textDim: "#8d8bb3",
|
|
311
|
+
fills: { health: "#3ee08a", mana: "#5a7bff", stamina: "#f0e341", xp: "#e050d8", shield: "#8ad4f0" },
|
|
312
|
+
deeps: { health: "#136b44", mana: "#243487", stamina: "#77701a", xp: "#6e2069", shield: "#2f6d85" },
|
|
313
|
+
status: {
|
|
314
|
+
danger: "#ff4d6a",
|
|
315
|
+
warning: "#ffb347",
|
|
316
|
+
success: "#3ee08a",
|
|
317
|
+
hostile: "#ff4d6a",
|
|
318
|
+
friendly: "#41e6f0",
|
|
319
|
+
neutral: "#f0e341",
|
|
320
|
+
},
|
|
321
|
+
display: '"Segoe UI", system-ui, sans-serif',
|
|
322
|
+
rarity: {
|
|
323
|
+
common: "#9fa4bd",
|
|
324
|
+
uncommon: "#3ee08a",
|
|
325
|
+
rare: "#5a7bff",
|
|
326
|
+
epic: "#e050d8",
|
|
327
|
+
legendary: "#ffb347",
|
|
328
|
+
},
|
|
329
|
+
}),
|
|
330
|
+
fieldkit: chrome({
|
|
331
|
+
accent: "#d8c169",
|
|
332
|
+
accentGlow: "rgba(216, 193, 105, 0.45)",
|
|
333
|
+
accentDeep: "#6e6230",
|
|
334
|
+
surface: "#14160f",
|
|
335
|
+
surfaceDeep: "#0a0b07",
|
|
336
|
+
edge: "#454a35",
|
|
337
|
+
edgeBright: "#6f7657",
|
|
338
|
+
text: "#e6e8d8",
|
|
339
|
+
textDim: "#95997f",
|
|
340
|
+
fills: { health: "#7fb84a", mana: "#57a8b8", stamina: "#c9b73e", xp: "#b88f4a", shield: "#a8b4a0" },
|
|
341
|
+
deeps: { health: "#3d5c1f", mana: "#265059", stamina: "#645a1c", xp: "#5c451f", shield: "#4e594a" },
|
|
342
|
+
status: {
|
|
343
|
+
danger: "#d84f35",
|
|
344
|
+
warning: "#d8952f",
|
|
345
|
+
success: "#7fb84a",
|
|
346
|
+
hostile: "#d84f35",
|
|
347
|
+
friendly: "#57a8b8",
|
|
348
|
+
neutral: "#c9b73e",
|
|
349
|
+
},
|
|
350
|
+
display: '"Arial Narrow", "Roboto Condensed", "Segoe UI", sans-serif',
|
|
351
|
+
rarity: {
|
|
352
|
+
common: "#a8ab99",
|
|
353
|
+
uncommon: "#7fb84a",
|
|
354
|
+
rare: "#57a8b8",
|
|
355
|
+
epic: "#b06fc9",
|
|
356
|
+
legendary: "#d8952f",
|
|
357
|
+
},
|
|
358
|
+
}),
|
|
148
359
|
};
|
|
360
|
+
function hexToRgb(hex) {
|
|
361
|
+
const raw = hex.replace("#", "");
|
|
362
|
+
const full = raw.length === 3 ? raw.split("").map((c) => c + c).join("") : raw;
|
|
363
|
+
return {
|
|
364
|
+
r: parseInt(full.slice(0, 2), 16),
|
|
365
|
+
g: parseInt(full.slice(2, 4), 16),
|
|
366
|
+
b: parseInt(full.slice(4, 6), 16),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
function rgbToHex({ r, g, b }) {
|
|
370
|
+
const channel = (v) => Math.round(Math.min(255, Math.max(0, v))).toString(16).padStart(2, "0");
|
|
371
|
+
return `#${channel(r)}${channel(g)}${channel(b)}`;
|
|
372
|
+
}
|
|
373
|
+
function mix(from, to, amount) {
|
|
374
|
+
const a = hexToRgb(from);
|
|
375
|
+
const b = hexToRgb(to);
|
|
376
|
+
return rgbToHex({
|
|
377
|
+
r: a.r + (b.r - a.r) * amount,
|
|
378
|
+
g: a.g + (b.g - a.g) * amount,
|
|
379
|
+
b: a.b + (b.b - a.b) * amount,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
function alpha(hex, value) {
|
|
383
|
+
const { r, g, b } = hexToRgb(hex);
|
|
384
|
+
return `rgba(${r}, ${g}, ${b}, ${value})`;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Derives a coherent full theme from an accent/surface pair — shades, edges, dim text, frames,
|
|
388
|
+
* bar troughs, and slots all fall out of the seed, so a game gets one consistent look across the
|
|
389
|
+
* `packages/react` primitives and the `registry/jgengine` blocks without naming 50 tokens.
|
|
390
|
+
* Hex seeds only (`#rgb` or `#rrggbb`).
|
|
391
|
+
*
|
|
392
|
+
* @capability hud-theme-derive build a full HudTheme from an accent + surface seed pair
|
|
393
|
+
*/
|
|
394
|
+
export function deriveHudTheme(seed) {
|
|
395
|
+
const { accent, surface } = seed;
|
|
396
|
+
const text = seed.text ?? mix("#f4f4f0", accent, 0.12);
|
|
397
|
+
const health = seed.health ?? "#e0483e";
|
|
398
|
+
const mana = seed.mana ?? "#4a86d8";
|
|
399
|
+
const stamina = seed.stamina ?? "#d9c33f";
|
|
400
|
+
const xp = seed.xp ?? "#a566d9";
|
|
401
|
+
const shield = seed.shield ?? "#9fb9c9";
|
|
402
|
+
const danger = seed.danger ?? "#e0483e";
|
|
403
|
+
const surfaceDeep = mix(surface, "#000000", 0.5);
|
|
404
|
+
const edge = mix(surface, accent, 0.22);
|
|
405
|
+
const edgeBright = mix(surface, accent, 0.42);
|
|
406
|
+
return hudTheme({
|
|
407
|
+
palette: {
|
|
408
|
+
health,
|
|
409
|
+
healthLow: mix(health, "#000000", 0.3),
|
|
410
|
+
healthDeep: mix(health, "#000000", 0.5),
|
|
411
|
+
mana,
|
|
412
|
+
manaDeep: mix(mana, "#000000", 0.5),
|
|
413
|
+
stamina,
|
|
414
|
+
staminaDeep: mix(stamina, "#000000", 0.5),
|
|
415
|
+
shield,
|
|
416
|
+
shieldDeep: mix(shield, "#000000", 0.5),
|
|
417
|
+
xp,
|
|
418
|
+
xpDeep: mix(xp, "#000000", 0.5),
|
|
419
|
+
soul: mix(xp, "#ffffff", 0.25),
|
|
420
|
+
ammo: seed.warning ?? "#e8a33d",
|
|
421
|
+
boss: danger,
|
|
422
|
+
accent,
|
|
423
|
+
accentDeep: mix(accent, "#000000", 0.55),
|
|
424
|
+
accentGlow: alpha(accent, 0.5),
|
|
425
|
+
accentDim: alpha(accent, 0.18),
|
|
426
|
+
},
|
|
427
|
+
surface: { surface, surfaceDeep, edge, edgeBright, text, textDim: mix(text, surface, 0.42), backdrop: alpha(surfaceDeep, 0.72) },
|
|
428
|
+
status: {
|
|
429
|
+
danger,
|
|
430
|
+
warning: seed.warning ?? "#e8a33d",
|
|
431
|
+
success: accent,
|
|
432
|
+
hostile: danger,
|
|
433
|
+
friendly: seed.friendly ?? mana,
|
|
434
|
+
neutral: stamina,
|
|
435
|
+
},
|
|
436
|
+
font: { display: seed.fontDisplay ?? defaultHudTheme.font.display },
|
|
437
|
+
frame: {
|
|
438
|
+
bg: `linear-gradient(180deg, ${alpha(surface, 0.92)}, ${alpha(surfaceDeep, 0.94)})`,
|
|
439
|
+
border: `1px solid ${edge}`,
|
|
440
|
+
glow: `0 6px 20px ${alpha(surfaceDeep, 0.7)}`,
|
|
441
|
+
},
|
|
442
|
+
bar: { track: alpha(surfaceDeep, 0.8), frame: surfaceDeep, text },
|
|
443
|
+
slot: { bg: alpha(surface, 0.8), border: `1px solid ${edge}`, radius: "6px" },
|
|
444
|
+
ring: alpha(edgeBright, 0.55),
|
|
445
|
+
});
|
|
446
|
+
}
|
|
149
447
|
/** Resolves a preset name (or a full theme) to a `HudTheme`; falls back to the default theme. */
|
|
150
448
|
export function resolveHudTheme(theme) {
|
|
151
449
|
if (theme === undefined)
|
package/dist/i18n.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { I18n, Locale, TParams } from "@jgengine/core/i18n/i18n";
|
|
3
|
+
/** Provide an {@link I18n} instance to the tree so `useT`/`useLocale` can read it. */
|
|
4
|
+
export declare function I18nProvider({ i18n, children }: {
|
|
5
|
+
i18n: I18n;
|
|
6
|
+
children: ReactNode;
|
|
7
|
+
}): ReactNode;
|
|
8
|
+
/** Read the current {@link I18n} from context; throws when no {@link I18nProvider} is above. */
|
|
9
|
+
export declare function useI18n(): I18n;
|
|
10
|
+
/** Subscribe to the active locale — re-renders on `setLocale`. */
|
|
11
|
+
export declare function useLocale(): Locale;
|
|
12
|
+
/**
|
|
13
|
+
* Return the translator bound to the current locale; the component re-renders
|
|
14
|
+
* when `setLocale` is called, so returned strings stay live.
|
|
15
|
+
*
|
|
16
|
+
* @capability use-translate live `t(key, params)` translator bound to the active locale, re-rendering on locale change
|
|
17
|
+
*/
|
|
18
|
+
export declare function useT(): (key: string, params?: TParams) => string;
|
|
19
|
+
/** Return the plural-aware translator bound to the current locale. */
|
|
20
|
+
export declare function usePlural(): (key: string, count: number, params?: TParams) => string;
|
|
21
|
+
/**
|
|
22
|
+
* Render a translated message inline: `<Trans k="hud.score" params={{ value }} />`.
|
|
23
|
+
*
|
|
24
|
+
* @capability trans inline translated-message component bound to the active locale
|
|
25
|
+
*/
|
|
26
|
+
export declare function Trans({ k, params }: {
|
|
27
|
+
k: string;
|
|
28
|
+
params?: TParams;
|
|
29
|
+
}): ReactNode;
|
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useSyncExternalStore } from "react";
|
|
3
|
+
const I18nContext = createContext(null);
|
|
4
|
+
/** Provide an {@link I18n} instance to the tree so `useT`/`useLocale` can read it. */
|
|
5
|
+
export function I18nProvider({ i18n, children }) {
|
|
6
|
+
return _jsx(I18nContext.Provider, { value: i18n, children: children });
|
|
7
|
+
}
|
|
8
|
+
/** Read the current {@link I18n} from context; throws when no {@link I18nProvider} is above. */
|
|
9
|
+
export function useI18n() {
|
|
10
|
+
const i18n = useContext(I18nContext);
|
|
11
|
+
if (i18n === null)
|
|
12
|
+
throw new Error("useI18n must be used within an <I18nProvider>");
|
|
13
|
+
return i18n;
|
|
14
|
+
}
|
|
15
|
+
/** Subscribe to the active locale — re-renders on `setLocale`. */
|
|
16
|
+
export function useLocale() {
|
|
17
|
+
const i18n = useI18n();
|
|
18
|
+
return useSyncExternalStore(i18n.subscribe, i18n.locale, i18n.locale);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Return the translator bound to the current locale; the component re-renders
|
|
22
|
+
* when `setLocale` is called, so returned strings stay live.
|
|
23
|
+
*
|
|
24
|
+
* @capability use-translate live `t(key, params)` translator bound to the active locale, re-rendering on locale change
|
|
25
|
+
*/
|
|
26
|
+
export function useT() {
|
|
27
|
+
const i18n = useI18n();
|
|
28
|
+
useLocale(); // re-render on locale change
|
|
29
|
+
return i18n.t;
|
|
30
|
+
}
|
|
31
|
+
/** Return the plural-aware translator bound to the current locale. */
|
|
32
|
+
export function usePlural() {
|
|
33
|
+
const i18n = useI18n();
|
|
34
|
+
useLocale();
|
|
35
|
+
return i18n.plural;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Render a translated message inline: `<Trans k="hud.score" params={{ value }} />`.
|
|
39
|
+
*
|
|
40
|
+
* @capability trans inline translated-message component bound to the active locale
|
|
41
|
+
*/
|
|
42
|
+
export function Trans({ k, params }) {
|
|
43
|
+
return useT()(k, params);
|
|
44
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,23 +5,53 @@ export * from "./chatBubbles.js";
|
|
|
5
5
|
export * from "./voice.js";
|
|
6
6
|
export * from "./social.js";
|
|
7
7
|
export * from "./hooks.js";
|
|
8
|
+
export * from "./useDebouncedCommit.js";
|
|
8
9
|
export * from "./store.js";
|
|
9
10
|
export * from "./settings.js";
|
|
10
11
|
export * from "./save.js";
|
|
12
|
+
export * from "./achievements.js";
|
|
13
|
+
export * from "./i18n.js";
|
|
14
|
+
export * from "./accessibility.js";
|
|
15
|
+
export * from "./photoMode.js";
|
|
16
|
+
export * from "./radialMenu.js";
|
|
17
|
+
export * from "./quickMenu.js";
|
|
18
|
+
export * from "./questTracker.js";
|
|
19
|
+
export * from "./codex.js";
|
|
20
|
+
export * from "./notifications.js";
|
|
21
|
+
export * from "./coachMarks.js";
|
|
22
|
+
export * from "./objectiveBanner.js";
|
|
23
|
+
export * from "./keybindingMenu.js";
|
|
24
|
+
export * from "./shopGrid.js";
|
|
25
|
+
export * from "./killFeed.js";
|
|
26
|
+
export * from "./waveHud.js";
|
|
27
|
+
export * from "./comboMeter.js";
|
|
28
|
+
export * from "./waypointMarkers.js";
|
|
29
|
+
export * from "./scoreboard.js";
|
|
30
|
+
export * from "./timerReadout.js";
|
|
31
|
+
export * from "./dialogueView.js";
|
|
32
|
+
export * from "./cutscene.js";
|
|
33
|
+
export * from "./damageDirection.js";
|
|
34
|
+
export * from "./modals.js";
|
|
35
|
+
export * from "./fastTravel.js";
|
|
36
|
+
export * from "./floatingText.js";
|
|
11
37
|
export * from "./components.js";
|
|
12
38
|
export * from "./hud.js";
|
|
13
39
|
export * from "./bars.js";
|
|
40
|
+
export * from "./entityFrames.js";
|
|
14
41
|
export * from "./barsPreview.js";
|
|
15
42
|
export * from "./iconTreatment.js";
|
|
43
|
+
export * from "./statusEffectBar.js";
|
|
16
44
|
export * from "./iconsPreview.js";
|
|
17
45
|
export * from "./slots.js";
|
|
18
46
|
export * from "./inventoryGrid.js";
|
|
19
47
|
export * from "./panels.js";
|
|
20
48
|
export * from "./characterSheet.js";
|
|
49
|
+
export * from "./talentTree.js";
|
|
21
50
|
export * from "./hudTheme.js";
|
|
22
51
|
export * from "./hudThemePreview.js";
|
|
23
52
|
export * from "./hudFrame.js";
|
|
24
53
|
export * from "./keyHint.js";
|
|
54
|
+
export * from "./interactionPrompt.js";
|
|
25
55
|
export * from "./startScreen.js";
|
|
26
56
|
export * from "./engineStore.js";
|
|
27
57
|
export * from "./dragLayer.js";
|
|
@@ -36,3 +66,7 @@ export * from "./rotateDevice.js";
|
|
|
36
66
|
export * from "./display.js";
|
|
37
67
|
export * from "./map.js";
|
|
38
68
|
export * from "./preview.js";
|
|
69
|
+
export * from "./previewFixtures.js";
|
|
70
|
+
export * from "./streetGeometryPreview.js";
|
|
71
|
+
export * from "./saveSlots.js";
|
|
72
|
+
export * from "./cameraShake.js";
|
package/dist/index.js
CHANGED
|
@@ -5,23 +5,53 @@ export * from "./chatBubbles.js";
|
|
|
5
5
|
export * from "./voice.js";
|
|
6
6
|
export * from "./social.js";
|
|
7
7
|
export * from "./hooks.js";
|
|
8
|
+
export * from "./useDebouncedCommit.js";
|
|
8
9
|
export * from "./store.js";
|
|
9
10
|
export * from "./settings.js";
|
|
10
11
|
export * from "./save.js";
|
|
12
|
+
export * from "./achievements.js";
|
|
13
|
+
export * from "./i18n.js";
|
|
14
|
+
export * from "./accessibility.js";
|
|
15
|
+
export * from "./photoMode.js";
|
|
16
|
+
export * from "./radialMenu.js";
|
|
17
|
+
export * from "./quickMenu.js";
|
|
18
|
+
export * from "./questTracker.js";
|
|
19
|
+
export * from "./codex.js";
|
|
20
|
+
export * from "./notifications.js";
|
|
21
|
+
export * from "./coachMarks.js";
|
|
22
|
+
export * from "./objectiveBanner.js";
|
|
23
|
+
export * from "./keybindingMenu.js";
|
|
24
|
+
export * from "./shopGrid.js";
|
|
25
|
+
export * from "./killFeed.js";
|
|
26
|
+
export * from "./waveHud.js";
|
|
27
|
+
export * from "./comboMeter.js";
|
|
28
|
+
export * from "./waypointMarkers.js";
|
|
29
|
+
export * from "./scoreboard.js";
|
|
30
|
+
export * from "./timerReadout.js";
|
|
31
|
+
export * from "./dialogueView.js";
|
|
32
|
+
export * from "./cutscene.js";
|
|
33
|
+
export * from "./damageDirection.js";
|
|
34
|
+
export * from "./modals.js";
|
|
35
|
+
export * from "./fastTravel.js";
|
|
36
|
+
export * from "./floatingText.js";
|
|
11
37
|
export * from "./components.js";
|
|
12
38
|
export * from "./hud.js";
|
|
13
39
|
export * from "./bars.js";
|
|
40
|
+
export * from "./entityFrames.js";
|
|
14
41
|
export * from "./barsPreview.js";
|
|
15
42
|
export * from "./iconTreatment.js";
|
|
43
|
+
export * from "./statusEffectBar.js";
|
|
16
44
|
export * from "./iconsPreview.js";
|
|
17
45
|
export * from "./slots.js";
|
|
18
46
|
export * from "./inventoryGrid.js";
|
|
19
47
|
export * from "./panels.js";
|
|
20
48
|
export * from "./characterSheet.js";
|
|
49
|
+
export * from "./talentTree.js";
|
|
21
50
|
export * from "./hudTheme.js";
|
|
22
51
|
export * from "./hudThemePreview.js";
|
|
23
52
|
export * from "./hudFrame.js";
|
|
24
53
|
export * from "./keyHint.js";
|
|
54
|
+
export * from "./interactionPrompt.js";
|
|
25
55
|
export * from "./startScreen.js";
|
|
26
56
|
export * from "./engineStore.js";
|
|
27
57
|
export * from "./dragLayer.js";
|
|
@@ -36,3 +66,7 @@ export * from "./rotateDevice.js";
|
|
|
36
66
|
export * from "./display.js";
|
|
37
67
|
export * from "./map.js";
|
|
38
68
|
export * from "./preview.js";
|
|
69
|
+
export * from "./previewFixtures.js";
|
|
70
|
+
export * from "./streetGeometryPreview.js";
|
|
71
|
+
export * from "./saveSlots.js";
|
|
72
|
+
export * from "./cameraShake.js";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
import type { PositionedPrompt, PromptPoint } from "@jgengine/core/interaction/proximityPrompt";
|
|
3
|
+
import type { PromptRegistry } from "@jgengine/core/interaction/promptRegistry";
|
|
4
|
+
/**
|
|
5
|
+
* Subscribe to a {@link PromptRegistry} and re-`resolve` it whenever the player
|
|
6
|
+
* moves, returning the prompt to draw right now — or `null` when the player is
|
|
7
|
+
* out of range of every interactable. The registry notifies only on active-prompt
|
|
8
|
+
* *changes*, so this re-renders on transitions rather than every frame the hero
|
|
9
|
+
* moves. Feed it the player's `{ x, z }` position each frame; identical positions
|
|
10
|
+
* do no work.
|
|
11
|
+
*
|
|
12
|
+
* @capability use-interaction-prompt React hook binding a prompt registry to a moving player — resolves the nearest interactable prompt and returns the active one
|
|
13
|
+
*/
|
|
14
|
+
export declare function useInteractionPrompt(registry: PromptRegistry, playerPosition: PromptPoint): PositionedPrompt | null;
|
|
15
|
+
/** Reskin tokens for {@link InteractionPrompt}. */
|
|
16
|
+
export interface InteractionPromptTheme {
|
|
17
|
+
/** Accent for the key cap and glow. Default reads `--jg-accent`. */
|
|
18
|
+
accent?: string;
|
|
19
|
+
/** Callout background. */
|
|
20
|
+
background?: string;
|
|
21
|
+
/** Callout border color. */
|
|
22
|
+
border?: string;
|
|
23
|
+
/** Label text color. */
|
|
24
|
+
text?: string;
|
|
25
|
+
/** Font family. */
|
|
26
|
+
fontFamily?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Where the callout sits over the scene. */
|
|
29
|
+
export type InteractionPromptAnchor = "bottom-center" | "top-center" | "center";
|
|
30
|
+
/** Props for {@link InteractionPrompt}. */
|
|
31
|
+
export interface InteractionPromptProps {
|
|
32
|
+
/** The registry that owns the prompts and the active selection. */
|
|
33
|
+
registry: PromptRegistry;
|
|
34
|
+
/**
|
|
35
|
+
* The player's ground position. When supplied the host drives `resolve` on
|
|
36
|
+
* every position change; omit it if the game already calls `registry.resolve`
|
|
37
|
+
* from its own tick (the host still subscribes and renders the active prompt).
|
|
38
|
+
*/
|
|
39
|
+
playerPosition?: PromptPoint;
|
|
40
|
+
/** Base reskin tokens. */
|
|
41
|
+
theme?: InteractionPromptTheme;
|
|
42
|
+
/**
|
|
43
|
+
* Per-prompt accent chooser. The registry never interprets a prompt's meaning,
|
|
44
|
+
* so the game decides the accent from the active prompt (its id, action id, or
|
|
45
|
+
* label) — e.g. a warm hue for a chest, cool for a door. Returning `undefined`
|
|
46
|
+
* falls back to the theme accent.
|
|
47
|
+
*/
|
|
48
|
+
accentFor?: (prompt: PositionedPrompt) => string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Key glyph shown on the cap for a `keybind` prompt, chosen from the prompt's
|
|
51
|
+
* free-string `actionId`. Default `"E"`.
|
|
52
|
+
*/
|
|
53
|
+
keyFor?: (actionId: string) => string;
|
|
54
|
+
/**
|
|
55
|
+
* Hold progress `0..1` for a `gauge` prompt, by its free-string `gaugeId` —
|
|
56
|
+
* fills the ring/bar as the player holds. Default `0`.
|
|
57
|
+
*/
|
|
58
|
+
gaugeProgress?: (gaugeId: string) => number;
|
|
59
|
+
/** Where the callout sits. Default `"bottom-center"`. */
|
|
60
|
+
anchor?: InteractionPromptAnchor;
|
|
61
|
+
/** Overlay `z-index`. Default `40`. */
|
|
62
|
+
zIndex?: number;
|
|
63
|
+
className?: string;
|
|
64
|
+
style?: CSSProperties;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A screen-anchored "Press E to …" interaction callout that renders the active
|
|
68
|
+
* prompt of a {@link PromptRegistry}. It subscribes to the registry and — when
|
|
69
|
+
* given `playerPosition` — re-resolves as the hero moves, showing the nearest
|
|
70
|
+
* in-range interactable and switching to a closer/higher-priority one as they
|
|
71
|
+
* cross radii. Each display kind renders as itself: a `keybind` shows a key cap
|
|
72
|
+
* plus its label ("Press [E] Open"), a `gauge` shows a hold bar filling with
|
|
73
|
+
* `gaugeProgress`, and a `label` shows plain text. Presentation only — the
|
|
74
|
+
* registry never branches on a prompt's meaning; the game colors each via
|
|
75
|
+
* `accentFor` and maps action ids to key glyphs via `keyFor`. HudTheme-skinnable
|
|
76
|
+
* through `--jg-accent` and {@link InteractionPromptTheme}.
|
|
77
|
+
*
|
|
78
|
+
* @capability interaction-prompt-host screen-anchored "press E to…" callout rendering a prompt registry's active interactable — keybind cap+label, gauge hold bar, or plain label, theme- and per-prompt-accent skinnable
|
|
79
|
+
*/
|
|
80
|
+
export declare function InteractionPrompt({ registry, playerPosition, theme, accentFor, keyFor, gaugeProgress, anchor, zIndex, className, style, }: InteractionPromptProps): ReactNode;
|