@jgengine/assets 0.8.0 → 0.9.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/dist/indexGen.js CHANGED
@@ -49,7 +49,7 @@ export function indexSourceDir(source, dir) {
49
49
  function safeIdentifier(sourceId) {
50
50
  return `s_${sourceId.replace(/[^a-zA-Z0-9]/g, "_")}`;
51
51
  }
52
- function renderBarrel(sourceIds) {
52
+ function renderTsBarrel(sourceIds) {
53
53
  const ordered = [...sourceIds].sort((a, b) => a.localeCompare(b));
54
54
  const imports = ordered
55
55
  .map((id) => `import ${safeIdentifier(id)} from "./${id}.json" with { type: "json" };`)
@@ -68,6 +68,22 @@ ${table}
68
68
  export const generatedIndex: IndexEntry[] = Object.values(generatedBySource).flat();
69
69
  `;
70
70
  }
71
+ function renderJsBarrel(sourceIds) {
72
+ const ordered = [...sourceIds].sort((a, b) => a.localeCompare(b));
73
+ const imports = ordered
74
+ .map((id) => `import ${safeIdentifier(id)} from "./${id}.json" with { type: "json" };`)
75
+ .join("\n");
76
+ const table = ordered.map((id) => ` ${JSON.stringify(id)}: ${safeIdentifier(id)},`).join("\n");
77
+ return `// AUTO-GENERATED by \`assets reindex\`. Do not edit by hand.
78
+ ${imports}
79
+
80
+ export const generatedBySource = {
81
+ ${table}
82
+ };
83
+
84
+ export const generatedIndex = Object.values(generatedBySource).flat();
85
+ `;
86
+ }
71
87
  export function reindex(modelsDir, outDir) {
72
88
  mkdirSync(outDir, { recursive: true });
73
89
  const perSource = [];
@@ -86,6 +102,11 @@ export function reindex(modelsDir, outDir) {
86
102
  sourceIds.push(sourceId);
87
103
  perSource.push({ source: sourceId, count: entries.length });
88
104
  }
89
- writeFileSync(join(outDir, "index.ts"), renderBarrel(sourceIds));
105
+ writeFileSync(join(outDir, "index.ts"), renderTsBarrel(sourceIds));
106
+ // Published installs only ship dist/; write a JS barrel there so buildCatalog can re-import after reindex.
107
+ // Source-tree reindex keeps the TypeScript barrel that the package build compiles.
108
+ if (!/[/\\]src[/\\]generated$/i.test(outDir)) {
109
+ writeFileSync(join(outDir, "index.js"), renderJsBarrel(sourceIds));
110
+ }
90
111
  return { perSource, total: perSource.reduce((sum, entry) => sum + entry.count, 0) };
91
112
  }
@@ -0,0 +1,455 @@
1
+ {
2
+ "homepage": "https://jgengine.com",
3
+ "components": [
4
+ {
5
+ "name": "ability-action-bar",
6
+ "title": "Ability Action Bar",
7
+ "description": "Action Bar wired to a live AbilityKit — derives slot state, cooldown, and charges from engine snapshots each tick."
8
+ },
9
+ {
10
+ "name": "ability-slot",
11
+ "title": "Ability Slot",
12
+ "description": "Four-state ability slot button — ready, cooldown (conic sweep + timer), no-resource, locked — with charge pips, just-cast flash, and a corner keybind badge."
13
+ },
14
+ {
15
+ "name": "accent-rule",
16
+ "title": "Accent Rule",
17
+ "description": "Thin glowing gradient divider used under panel headers and section breaks."
18
+ },
19
+ {
20
+ "name": "action-bar",
21
+ "title": "Action Bar",
22
+ "description": "Row of ability slot buttons driven by a plain slot-data array — presentational, value-driven."
23
+ },
24
+ {
25
+ "name": "ammo-counter",
26
+ "title": "Ammo Counter",
27
+ "description": "Magazine/reserve ammo readout with a low-ammo pulse, a reloading label, and a slanted divider between magazine and reserve counts."
28
+ },
29
+ {
30
+ "name": "announcement-banner",
31
+ "title": "Announcement Banner",
32
+ "description": "Centered match-event banner (victory/defeat/warning/neutral) with an accent rule and optional keybind hint."
33
+ },
34
+ {
35
+ "name": "arc-gauge",
36
+ "title": "Arc Gauge",
37
+ "description": "SVG sweep-arc gauge with tick marks, a glowing progress stroke in accent/danger/warning tones, and a centered readout + label."
38
+ },
39
+ {
40
+ "name": "boss-bar",
41
+ "title": "Boss Bar",
42
+ "description": "Wide boss health bar with finial end-caps, phase-transition ticks, a name, and a sub-label."
43
+ },
44
+ {
45
+ "name": "break-meter",
46
+ "title": "Break Meter",
47
+ "description": "Compact stagger/poise break meter that ramps color with fill and shakes and pulses when broken."
48
+ },
49
+ {
50
+ "name": "buff-tray",
51
+ "title": "Buff Tray",
52
+ "description": "Row of chamfered buff/debuff chips with stack counts and a remaining-duration underline, driven by a plain chip array."
53
+ },
54
+ {
55
+ "name": "cast-bar",
56
+ "title": "Cast Bar",
57
+ "description": "Slant-clipped cast/channel progress bar with a bright leading edge and an interrupted state."
58
+ },
59
+ {
60
+ "name": "charge-meter",
61
+ "title": "Charge Meter",
62
+ "description": "Slant-clipped charge or ultimate meter with tier ticks and a ready-state glow animation."
63
+ },
64
+ {
65
+ "name": "combat-float-layer",
66
+ "title": "Combat Float Layer",
67
+ "description": "Absolute-positioned layer of animated damage/heal/crit/info/error text floats, each jittered around a screen position — presentational, list-driven."
68
+ },
69
+ {
70
+ "name": "combat-log-panel",
71
+ "title": "Combat Log Panel",
72
+ "description": "Auto-scrolling tone-colored combat log in a titled panel — damage/heal/system lines fade with age, value-driven."
73
+ },
74
+ {
75
+ "name": "combo-counter",
76
+ "title": "Combo Counter",
77
+ "description": "Big pop-animated combo count with a HUD label and an optional decaying progress bar."
78
+ },
79
+ {
80
+ "name": "crafting-panel",
81
+ "title": "Crafting Panel",
82
+ "description": "Recipe list panel with per-input have/need coloring and an in-progress craft-fraction bar under each recipe."
83
+ },
84
+ {
85
+ "name": "currency-display",
86
+ "title": "Currency Display",
87
+ "description": "Coin-glyph currency readout with optional compact k/m/b formatting and a currency name label — presentational, value-driven."
88
+ },
89
+ {
90
+ "name": "damage-direction-indicator",
91
+ "title": "Damage Direction Indicator",
92
+ "description": "Screen-edge arc that flashes toward the source of incoming damage, scaled by intensity."
93
+ },
94
+ {
95
+ "name": "death-overlay-bound",
96
+ "title": "Death Overlay Bound",
97
+ "description": "DeathScreenView bound to the local player's dead state via useLocalPlayerDead — renders nothing while alive."
98
+ },
99
+ {
100
+ "name": "death-screen-view",
101
+ "title": "Death Screen View",
102
+ "description": "Presentational death overlay with a popping title, respawn countdown or respawn MenuButton — value-driven, no engine reads."
103
+ },
104
+ {
105
+ "name": "dialogue-panel",
106
+ "title": "Dialogue Panel",
107
+ "description": "Notched dialogue box with speaker nameplate, portrait well, and skill-check-annotated branching choices."
108
+ },
109
+ {
110
+ "name": "entity-vital-bar",
111
+ "title": "Entity Vital Bar",
112
+ "description": "VitalBar bound to a live entity stat via useEntityStat — the one-line health bar for any entity instance."
113
+ },
114
+ {
115
+ "name": "equipment-doll",
116
+ "title": "Equipment Doll",
117
+ "description": "Paper-doll figure flanked by left/right/bottom equipment slot wells, rarity-tinted by occupied item."
118
+ },
119
+ {
120
+ "name": "event-charge-meter",
121
+ "title": "Event Charge Meter",
122
+ "description": "ChargeMeter bound to a live EventMeter via useEventMeter — the one-line charge bar for combo/ultimate/hype meters."
123
+ },
124
+ {
125
+ "name": "feed-combat-log",
126
+ "title": "Feed Combat Log",
127
+ "description": "CombatLogPanel bound to a live game feed action via useFeed, mapped through a mapLine() projector."
128
+ },
129
+ {
130
+ "name": "feed-kill-feed",
131
+ "title": "Feed Kill Feed",
132
+ "description": "KillFeed bound to a live game feed action via useFeed, mapping raw feed entries to kill-feed rows."
133
+ },
134
+ {
135
+ "name": "feed-pickup-toasts",
136
+ "title": "Feed Pickup Toasts",
137
+ "description": "PickupToastStack bound to a live game feed action via useFeed, mapping raw feed entries to pickup toasts."
138
+ },
139
+ {
140
+ "name": "game-event-floats",
141
+ "title": "Game Event Floats",
142
+ "description": "CombatFloatLayer wired to the entity.floatText game event via useGame — spawns and auto-expires floats from live combat."
143
+ },
144
+ {
145
+ "name": "heart-row",
146
+ "title": "Heart Row",
147
+ "description": "Row of heart pips for discrete lives or health, with an optional custom icon renderer."
148
+ },
149
+ {
150
+ "name": "hit-marker",
151
+ "title": "Hit Marker",
152
+ "description": "Crosshair X-mark flash that pulses on a hit, brighter and larger on crit — presentational, active/crit-driven."
153
+ },
154
+ {
155
+ "name": "hotbar-selector",
156
+ "title": "Hotbar Selector",
157
+ "description": "Numbered hotbar slots with a selection caret and scale-up highlight for the active index."
158
+ },
159
+ {
160
+ "name": "hud-label",
161
+ "title": "Hud Label",
162
+ "description": "Small uppercase tracked-letterspacing caption for HUD field headers and table columns."
163
+ },
164
+ {
165
+ "name": "hud-marker-layer",
166
+ "title": "Hud Marker Layer",
167
+ "description": "Renders a full-screen layer of WaypointMarker instances from a list of HUD markers."
168
+ },
169
+ {
170
+ "name": "hud-panel",
171
+ "title": "Hud Panel",
172
+ "description": "Corner-bracketed HUD panel shell with a chamfer-free titled header — the base chrome every panel/overlay stacks on."
173
+ },
174
+ {
175
+ "name": "interaction-ring",
176
+ "title": "Interaction Ring",
177
+ "description": "Circular interact-hold progress ring with a center keybind badge and optional label."
178
+ },
179
+ {
180
+ "name": "inventory-slot-grid",
181
+ "title": "Inventory Slot Grid",
182
+ "description": "Item Grid wired to a live inventory container — maps engine InventorySlot state to grid slots each render."
183
+ },
184
+ {
185
+ "name": "item-grid",
186
+ "title": "Item Grid",
187
+ "description": "Rarity-tinted inventory/loot grid of clickable slots, driven by a plain slot-data array — presentational, value-driven."
188
+ },
189
+ {
190
+ "name": "journal-quest-tracker",
191
+ "title": "Journal Quest Tracker",
192
+ "description": "QuestTracker bound to the live quest journal via useQuestJournal, mapped through a describe() projector."
193
+ },
194
+ {
195
+ "name": "keybind-badge",
196
+ "title": "Keybind Badge",
197
+ "description": "Chamfered key-cap badge for showing a binding on its own control — hotbar slot corners, panel toggles."
198
+ },
199
+ {
200
+ "name": "kill-feed",
201
+ "title": "Kill Feed",
202
+ "description": "Right-aligned scrolling feed of kill/elimination rows with friendly/hostile coloring and an accent highlight bar — presentational, list-driven."
203
+ },
204
+ {
205
+ "name": "level-up-splash",
206
+ "title": "Level Up Splash",
207
+ "description": "Radial-glow level-up splash that flashes in on the stat.levelUp game event via useGame and auto-dismisses."
208
+ },
209
+ {
210
+ "name": "loading-screen",
211
+ "title": "Loading Screen",
212
+ "description": "Full-screen loading card with a slant-clipped determinate progress bar or an indeterminate sheen sweep, plus an optional rotating tip line."
213
+ },
214
+ {
215
+ "name": "lock-on-marker",
216
+ "title": "Lock-On Marker",
217
+ "description": "Rotating corner-bracket lock-on reticle that snaps into a diamond frame when a target is acquired, with an optional label."
218
+ },
219
+ {
220
+ "name": "loot-card",
221
+ "title": "Loot Card",
222
+ "description": "Rarity-bordered item tooltip card with icon, stat lines, italic affixes, and flavor text."
223
+ },
224
+ {
225
+ "name": "match-timer",
226
+ "title": "Match Timer",
227
+ "description": "MM:SS countdown clock with tick flanks and warning/critical color states, bundled with CountdownPips, a large pop-in number for a pre-round countdown."
228
+ },
229
+ {
230
+ "name": "menu-button",
231
+ "title": "Menu Button",
232
+ "description": "Slant-clipped menu button with primary/ghost/danger variants and an optional corner keybind badge."
233
+ },
234
+ {
235
+ "name": "menu-list",
236
+ "title": "Menu List",
237
+ "description": "Keyboard/pointer-navigable vertical menu with a selection wedge, accent underline, and per-entry keybind badges."
238
+ },
239
+ {
240
+ "name": "moodle-tray",
241
+ "title": "Moodle Tray",
242
+ "description": "Buff Tray adapter for survival-game Moodle records — maps severity to buff/debuff tint and fraction to the duration ring."
243
+ },
244
+ {
245
+ "name": "objective-channel",
246
+ "title": "Objective Channel",
247
+ "description": "Slant-clipped capture/channel progress bar with friendly/hostile/neutral ownership tinting and a contested pulse."
248
+ },
249
+ {
250
+ "name": "pause-screen",
251
+ "title": "Pause Screen",
252
+ "description": "Blurred full-screen pause overlay with a title and a MenuList of entries."
253
+ },
254
+ {
255
+ "name": "pickup-toast-stack",
256
+ "title": "Pickup Toast Stack",
257
+ "description": "Stacked, fading pickup toasts with rarity-tinted icon/label/count — presentational, list-driven."
258
+ },
259
+ {
260
+ "name": "quest-tracker",
261
+ "title": "Quest Tracker",
262
+ "description": "Stacked objective-list HUD widget — quest title rule plus checkable/countable objective rows, value-driven."
263
+ },
264
+ {
265
+ "name": "race-position",
266
+ "title": "Race Position",
267
+ "description": "Large ordinal position readout (1st/2nd/3rd) with an optional field-size suffix and a lap counter label."
268
+ },
269
+ {
270
+ "name": "rank-list",
271
+ "title": "Rank List",
272
+ "description": "Ranked leaderboard rows in a titled panel — gold star on first place, alternating row shading, value-driven."
273
+ },
274
+ {
275
+ "name": "resource-orb",
276
+ "title": "Resource Orb",
277
+ "description": "Circular fill-from-bottom resource orb (mana, stamina, and other tones) with a percentage readout."
278
+ },
279
+ {
280
+ "name": "results-screen",
281
+ "title": "Results Screen",
282
+ "description": "Victory/defeat/draw results overlay with an animated outcome title, a two-column stat grid, and an optional MenuList."
283
+ },
284
+ {
285
+ "name": "reticle",
286
+ "title": "Reticle",
287
+ "description": "Configurable crosshair reticle — dot, cross, circle, chevron, or shotgun spread pattern — with an optional hit-flash tick overlay."
288
+ },
289
+ {
290
+ "name": "score-readout",
291
+ "title": "Score Readout",
292
+ "description": "Zero-padded score counter that splits leading zeros from significant digits, with a pop-in animation on value change."
293
+ },
294
+ {
295
+ "name": "scoreboard-overlay",
296
+ "title": "Scoreboard Overlay",
297
+ "description": "Full-screen modal scoreboard with configurable columns, numeric-aware alignment, and highlight/team-color row accents."
298
+ },
299
+ {
300
+ "name": "settings-group",
301
+ "title": "Settings Group",
302
+ "description": "Titled settings section with an accent rule underline, wrapping SliderRow/ToggleRow children in a vertical stack."
303
+ },
304
+ {
305
+ "name": "slider-row",
306
+ "title": "Slider Row",
307
+ "description": "Labeled pointer-draggable slant-clipped slider row with a formatted value readout — settings-menu style."
308
+ },
309
+ {
310
+ "name": "stat-leaderboard",
311
+ "title": "Stat Leaderboard",
312
+ "description": "RankList bound to a live stat leaderboard via useLeaderboard, with optional userId-to-display-name resolution."
313
+ },
314
+ {
315
+ "name": "streak-callout",
316
+ "title": "Streak Callout",
317
+ "description": "Italic, skewed streak callout that intensifies size/color/pulse at higher tiers."
318
+ },
319
+ {
320
+ "name": "target-frame",
321
+ "title": "Target Frame",
322
+ "description": "Reversed UnitFrame with hostile/friendly/neutral relation coloring for an enemy or NPC target."
323
+ },
324
+ {
325
+ "name": "team-score-board",
326
+ "title": "Team Score Board",
327
+ "description": "Two-team score comparison with a diamond divider, per-team name labels, and an optional round label — presentational, value-driven."
328
+ },
329
+ {
330
+ "name": "title-screen",
331
+ "title": "Title Screen",
332
+ "description": "Full-screen title card with headline, subtitle, optional backdrop layer, version tag, and a MenuList of entries."
333
+ },
334
+ {
335
+ "name": "toggle-row",
336
+ "title": "Toggle Row",
337
+ "description": "Labeled two-segment on/off toggle row rendered as slant-clipped chips — settings-menu style."
338
+ },
339
+ {
340
+ "name": "unit-frame",
341
+ "title": "Unit Frame",
342
+ "description": "Portrait, name, level rosette, and stacked vital bars for a player or party-member HUD frame."
343
+ },
344
+ {
345
+ "name": "vendor-panel",
346
+ "title": "Vendor Panel",
347
+ "description": "Shop listing panel with rarity-colored item names, stock counts, affordability-aware pricing, and a balance readout."
348
+ },
349
+ {
350
+ "name": "vital-bar",
351
+ "title": "Vital Bar",
352
+ "description": "Slant-clipped stat bar with damage ghost-trail, segment ticks, and health/mana/stamina/xp/shield tones — presentational, value-driven."
353
+ },
354
+ {
355
+ "name": "wallet-currency-display",
356
+ "title": "Wallet Currency Display",
357
+ "description": "CurrencyDisplay bound to a live wallet balance via useCurrency — the one-line currency readout for any currency id."
358
+ },
359
+ {
360
+ "name": "wave-indicator",
361
+ "title": "Wave Indicator",
362
+ "description": "Wave-number readout with an accent underline and an optional remaining-hostiles counter for horde/survival modes."
363
+ },
364
+ {
365
+ "name": "waypoint-marker",
366
+ "title": "Waypoint Marker",
367
+ "description": "World-space HUD marker for objectives, danger, allies, or loot, with an off-screen clamp arrow, label, and distance readout."
368
+ },
369
+ {
370
+ "name": "xp-bar",
371
+ "title": "Xp Bar",
372
+ "description": "Thin tick-marked experience bar with an optional level readout."
373
+ }
374
+ ],
375
+ "icons": [
376
+ "amulet",
377
+ "apple",
378
+ "arrow",
379
+ "arrowDown",
380
+ "arrowLeft",
381
+ "arrowRight",
382
+ "arrowUp",
383
+ "axe",
384
+ "backpack",
385
+ "bomb",
386
+ "boots",
387
+ "bow",
388
+ "bread",
389
+ "buffArrow",
390
+ "check",
391
+ "chest",
392
+ "chestplate",
393
+ "cloak",
394
+ "coin",
395
+ "cross",
396
+ "crossbow",
397
+ "crosshairIcon",
398
+ "crouch",
399
+ "crystal",
400
+ "dagger",
401
+ "debuffArrow",
402
+ "eye",
403
+ "feather",
404
+ "fire",
405
+ "fish",
406
+ "fist",
407
+ "flag",
408
+ "frost",
409
+ "gauntlet",
410
+ "gear",
411
+ "gem",
412
+ "gun",
413
+ "hammer",
414
+ "hand",
415
+ "hardDrop",
416
+ "heart",
417
+ "helmet",
418
+ "hourglass",
419
+ "ingot",
420
+ "jump",
421
+ "key",
422
+ "leaf",
423
+ "lightning",
424
+ "map",
425
+ "meat",
426
+ "menu",
427
+ "ore",
428
+ "pause",
429
+ "pickaxe",
430
+ "pin",
431
+ "poison",
432
+ "potionBlue",
433
+ "potionRed",
434
+ "restart",
435
+ "ring",
436
+ "rotateCcw",
437
+ "rotateCw",
438
+ "scroll",
439
+ "shield",
440
+ "skip",
441
+ "skull",
442
+ "spear",
443
+ "sprint",
444
+ "staff",
445
+ "star",
446
+ "stone",
447
+ "swap",
448
+ "sword",
449
+ "tome",
450
+ "torch",
451
+ "wand",
452
+ "wing",
453
+ "wood"
454
+ ]
455
+ }
@@ -0,0 +1,14 @@
1
+ export interface RegistryComponent {
2
+ name: string;
3
+ title: string;
4
+ description: string;
5
+ }
6
+ export interface RegistryCatalog {
7
+ /** Base site the shadcn registry is published under, e.g. `https://jgengine.com`. */
8
+ homepage: string;
9
+ components: readonly RegistryComponent[];
10
+ icons: readonly string[];
11
+ }
12
+ export declare const registryCatalog: RegistryCatalog;
13
+ /** The `shadcn add` URL for a HUD component, e.g. `https://jgengine.com/r/vital-bar.json`. */
14
+ export declare function componentInstallUrl(name: string): string;
@@ -0,0 +1,6 @@
1
+ import catalog from "./registry-catalog.json" with { type: "json" };
2
+ export const registryCatalog = catalog;
3
+ /** The `shadcn add` URL for a HUD component, e.g. `https://jgengine.com/r/vital-bar.json`. */
4
+ export function componentInstallUrl(name) {
5
+ return `${registryCatalog.homepage.replace(/\/+$/, "")}/r/${name}.json`;
6
+ }
@@ -0,0 +1,13 @@
1
+ import { type RegistryComponent } from "./registry.js";
2
+ export interface ModelSnippetOptions {
3
+ /** URL prefix the app serves pulled GLBs from (matches `buildCatalog({ basePath })`). */
4
+ basePath?: string;
5
+ /** Which `defineGame` model seam to wire the id into. */
6
+ seam?: "objectModels" | "entityModels";
7
+ }
8
+ /** Copy-paste wiring for a pulled GLB id: resolve through the catalog, drop into a model seam. */
9
+ export declare function modelWiringSnippet(id: string, options?: ModelSnippetOptions): string;
10
+ /** Copy-paste wiring for a HUD component: the `shadcn add` command plus import + usage. */
11
+ export declare function componentWiringSnippet(component: RegistryComponent): string;
12
+ /** Copy-paste wiring for a HUD glyph from the registry `game-icon` catalog. */
13
+ export declare function iconWiringSnippet(name: string): string;
@@ -0,0 +1,47 @@
1
+ import { componentInstallUrl } from "./registry.js";
2
+ function pascalCase(name) {
3
+ return name
4
+ .split(/[^a-zA-Z0-9]+/)
5
+ .filter((part) => part.length > 0)
6
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
7
+ .join("");
8
+ }
9
+ /** Copy-paste wiring for a pulled GLB id: resolve through the catalog, drop into a model seam. */
10
+ export function modelWiringSnippet(id, options = {}) {
11
+ const basePath = options.basePath ?? "/models";
12
+ const seam = options.seam ?? "objectModels";
13
+ const key = seam === "entityModels" ? "hero" : id;
14
+ return [
15
+ `// src/game/assets.ts`,
16
+ `import { buildCatalog } from "@jgengine/assets";`,
17
+ `export const catalog = buildCatalog({ basePath: ${JSON.stringify(basePath)} });`,
18
+ ``,
19
+ `// in defineGame({ ... }):`,
20
+ `${seam}: {`,
21
+ ` ${JSON.stringify(key)}: { url: catalog.resolve(${JSON.stringify(id)})!.url, scale: 1 },`,
22
+ `}`,
23
+ ].join("\n");
24
+ }
25
+ /** Copy-paste wiring for a HUD component: the `shadcn add` command plus import + usage. */
26
+ export function componentWiringSnippet(component) {
27
+ const symbol = pascalCase(component.name);
28
+ return [
29
+ `# install into components/ui/${component.name}.tsx`,
30
+ `npx shadcn@latest add ${componentInstallUrl(component.name)}`,
31
+ ``,
32
+ `// then in your HUD:`,
33
+ `import { ${symbol} } from "@/components/ui/${component.name}";`,
34
+ `// <${symbol} … /> — ${component.description}`,
35
+ ].join("\n");
36
+ }
37
+ /** Copy-paste wiring for a HUD glyph from the registry `game-icon` catalog. */
38
+ export function iconWiringSnippet(name) {
39
+ return [
40
+ `# once per project, install the icon catalog:`,
41
+ `npx shadcn@latest add ${componentInstallUrl("game-icon")}`,
42
+ ``,
43
+ `// then render the glyph:`,
44
+ `import { GameIcon } from "@/components/ui/game-icon";`,
45
+ `// <GameIcon name=${JSON.stringify(name)} />`,
46
+ ].join("\n");
47
+ }
@@ -1,6 +1,6 @@
1
1
  const KAYKIT_PACKS = [
2
2
  { id: "kaykit-adventurers", slug: "kaykit-adventurers", title: "KayKit Adventurers", categories: ["character", "rigged", "fantasy"] },
3
- { id: "kaykit-skeletons", slug: "kaykit-skeletons", title: "KayKit Skeletons", categories: ["character", "rigged", "halloween"] },
3
+ { id: "kaykit-skeletons", slug: "kaykit-skeletons", title: "KayKit Skeletons", categories: ["character", "monster", "creature", "undead", "rigged", "halloween"] },
4
4
  { id: "kaykit-dungeon", slug: "kaykit-dungeon-remastered", title: "KayKit Dungeon (Remastered)", categories: ["dungeon", "environment", "prop"] },
5
5
  { id: "kaykit-medieval-hexagon", slug: "kaykit-medieval-hexagon-pack", title: "KayKit Medieval Hexagon Pack", categories: ["hexagon", "medieval", "environment"] },
6
6
  ];
@@ -5,6 +5,8 @@ const QUATERNIUS_PACKS = [
5
5
  { id: "quaternius-modular-scifi", slug: "modularscifimegakit", title: "Modular SciFi MegaKit", categories: ["scifi", "environment", "prop"] },
6
6
  { id: "quaternius-fantasy-props", slug: "fantasypropsmegakit", title: "Fantasy Props MegaKit", categories: ["fantasy", "prop"] },
7
7
  { id: "quaternius-base-characters", slug: "universalbasecharacters", title: "Universal Base Characters", categories: ["character", "rigged"] },
8
+ { id: "quaternius-animated-animals", slug: "ultimateanimatedanimals", title: "Ultimate Animated Animal Pack", categories: ["animal", "creature", "wildlife", "rigged", "animated"] },
9
+ { id: "quaternius-monsters", slug: "ultimatemonsters", title: "Ultimate Monsters", categories: ["monster", "creature", "fantasy", "rigged", "animated"] },
8
10
  ];
9
11
  export const quaterniusSources = QUATERNIUS_PACKS.map((pack) => ({
10
12
  id: pack.id,