@jgengine/assets 0.9.0 → 0.11.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +34 -3
  2. package/README.md +103 -50
  3. package/dist/aliases.d.ts +5 -0
  4. package/dist/aliases.js +28 -9
  5. package/dist/catalogs/build.js +9 -0
  6. package/dist/catalogs/buildSprites.d.ts +10 -0
  7. package/dist/catalogs/buildSprites.js +15 -0
  8. package/dist/catalogs/starter.d.ts +5 -0
  9. package/dist/catalogs/starter.js +10 -1
  10. package/dist/cli/paths.d.ts +9 -1
  11. package/dist/cli/paths.js +11 -1
  12. package/dist/cli/pull.d.ts +12 -0
  13. package/dist/cli/pull.js +210 -15
  14. package/dist/download.d.ts +46 -3
  15. package/dist/download.js +158 -9
  16. package/dist/find.d.ts +20 -1
  17. package/dist/find.js +52 -2
  18. package/dist/generated/index.js +18 -6
  19. package/dist/generated/kaykit-adventurers.json +674 -0
  20. package/dist/generated/kaykit-city-builder.json +863 -0
  21. package/dist/generated/kaykit-dungeon.json +4265 -0
  22. package/dist/generated/kaykit-furniture.json +1115 -0
  23. package/dist/generated/kaykit-skeletons.json +410 -0
  24. package/dist/generated/kaykit-space-base.json +1199 -0
  25. package/dist/generated/quaternius-medieval-village.json +3698 -0
  26. package/dist/generated/quaternius-modular-scifi.json +4013 -0
  27. package/dist/generated/quaternius-stylized-nature.json +1430 -0
  28. package/dist/generated-sprites/index.d.ts +5 -0
  29. package/dist/generated-sprites/index.js +4 -0
  30. package/dist/index.d.ts +9 -2
  31. package/dist/index.js +9 -2
  32. package/dist/indexGen.js +7 -5
  33. package/dist/manifest.d.ts +12 -1
  34. package/dist/materials.d.ts +61 -0
  35. package/dist/materials.js +98 -0
  36. package/dist/packs/starter.d.ts +46 -0
  37. package/dist/packs/starter.js +248 -0
  38. package/dist/provisioning.d.ts +88 -0
  39. package/dist/provisioning.js +74 -0
  40. package/dist/registry-catalog.json +40 -0
  41. package/dist/snippet.d.ts +4 -0
  42. package/dist/snippet.js +27 -0
  43. package/dist/sources/ambientcg.d.ts +5 -0
  44. package/dist/sources/ambientcg.js +64 -0
  45. package/dist/sources/gameicons.d.ts +10 -0
  46. package/dist/sources/gameicons.js +21 -0
  47. package/dist/sources/index.d.ts +9 -2
  48. package/dist/sources/index.js +11 -3
  49. package/dist/sources/kaykit.js +55 -6
  50. package/dist/sources/quaternius.js +60 -9
  51. package/dist/spriteIndexGen.d.ts +17 -0
  52. package/dist/spriteIndexGen.js +108 -0
  53. package/dist/verify.d.ts +1 -0
  54. package/dist/verify.js +38 -1
  55. package/package.json +4 -4
  56. package/skills/jgengine/SKILL.md +51 -0
  57. package/skills/jgengine/api.md +434 -0
  58. package/skills/jgengine/capabilities.md +29 -0
  59. package/skills/jgengine/reference-cartridge.md +72 -0
  60. package/skills/jgengine-assets/SKILL.md +41 -0
  61. package/skills/jgengine-assets/api.md +273 -0
  62. package/skills/jgengine-assets/capabilities.md +13 -0
  63. package/dist/generated/kenney-blaster.json +0 -362
  64. package/dist/generated/kenney-nature.json +0 -3292
  65. package/dist/generated/kenney-space.json +0 -1532
  66. package/dist/sources/kenney.d.ts +0 -2
  67. package/dist/sources/kenney.js +0 -36
  68. package/llms.txt +0 -997
@@ -0,0 +1,5 @@
1
+ import type { IndexEntry } from "../manifest.js";
2
+ /** Pulled sprite/icon entries grouped by source id. */
3
+ export declare const generatedSpriteBySource: Record<string, IndexEntry[]>;
4
+ /** Every pulled sprite/icon entry across all sources, flattened. */
5
+ export declare const generatedSpriteIndex: IndexEntry[];
@@ -0,0 +1,4 @@
1
+ /** Pulled sprite/icon entries grouped by source id. */
2
+ export const generatedSpriteBySource = {};
3
+ /** Every pulled sprite/icon entry across all sources, flattened. */
4
+ export const generatedSpriteIndex = Object.values(generatedSpriteBySource).flat();
package/dist/index.d.ts CHANGED
@@ -1,12 +1,19 @@
1
1
  export * from "./manifest.js";
2
- export { sources, sourceById, kenneySources, quaterniusSources, kaykitSources } from "./sources/index.js";
2
+ export { sources, sourceById, modelSources, materialSources, spriteSources, ambientcgSources, gameiconsSources, quaterniusSources, kaykitSources, } from "./sources/index.js";
3
+ export { buildMaterialCatalog, extractMaterialMaps, materialAliases, MATERIAL_MAP_FILES, type BuildMaterialCatalogOptions, type ExtractedMaterialMap, type MaterialCatalog, type MaterialMapRole, type MaterialMaps, type MaterialRef, } from "./materials.js";
4
+ export { extractSpriteFiles, type ExtractedSpriteFile } from "./download.js";
5
+ export { keyFromSpriteFile, entryForSpriteFile, indexSpriteSourceDir, reindexSprites, type ReindexSpritesResult, } from "./spriteIndexGen.js";
3
6
  export { singles } from "./singles.js";
4
7
  export { aliases } from "./aliases.js";
5
8
  export { generatedIndex, generatedBySource } from "./generated/index.js";
9
+ export { generatedSpriteIndex, generatedSpriteBySource } from "./generated-sprites/index.js";
6
10
  export { readGlbDims } from "./dims.js";
7
11
  export { buildCatalog, entryUrl, type BuildCatalogOptions } from "./catalogs/build.js";
12
+ export { buildSpriteCatalog, type BuildSpriteCatalogOptions } from "./catalogs/buildSprites.js";
8
13
  export { createStarterCatalog } from "./catalogs/starter.js";
14
+ export { STARTER_ASSETS, STARTER_PACKS, STARTER_SOURCE_PACKS, STARTER_THEMES, type StarterAsset, type StarterTheme, } from "./packs/starter.js";
9
15
  export { findAssets, rankAssets, type AssetKind, type AssetMatch, type FindOptions, type RankedMatch, } from "./find.js";
10
16
  export { registryCatalog, componentInstallUrl, type RegistryCatalog, type RegistryComponent } from "./registry.js";
11
- export { modelWiringSnippet, componentWiringSnippet, iconWiringSnippet, type ModelSnippetOptions } from "./snippet.js";
17
+ export { modelWiringSnippet, materialWiringSnippet, componentWiringSnippet, iconWiringSnippet, spriteWiringSnippet, type ModelSnippetOptions, } from "./snippet.js";
12
18
  export { verifyManifest, type VerifyResult } from "./verify.js";
19
+ export { resolveProvenance, validateAssetReferences, type AssetProvenance, type AssetProvenanceKind, type AssetReference, type ReferenceValidation, type ResolveProvenanceOptions, type ValidateAssetReferencesOptions, type ValidateAssetReferencesResult, } from "./provisioning.js";
package/dist/index.js CHANGED
@@ -1,12 +1,19 @@
1
1
  export * from "./manifest.js";
2
- export { sources, sourceById, kenneySources, quaterniusSources, kaykitSources } from "./sources/index.js";
2
+ export { sources, sourceById, modelSources, materialSources, spriteSources, ambientcgSources, gameiconsSources, quaterniusSources, kaykitSources, } from "./sources/index.js";
3
+ export { buildMaterialCatalog, extractMaterialMaps, materialAliases, MATERIAL_MAP_FILES, } from "./materials.js";
4
+ export { extractSpriteFiles } from "./download.js";
5
+ export { keyFromSpriteFile, entryForSpriteFile, indexSpriteSourceDir, reindexSprites, } from "./spriteIndexGen.js";
3
6
  export { singles } from "./singles.js";
4
7
  export { aliases } from "./aliases.js";
5
8
  export { generatedIndex, generatedBySource } from "./generated/index.js";
9
+ export { generatedSpriteIndex, generatedSpriteBySource } from "./generated-sprites/index.js";
6
10
  export { readGlbDims } from "./dims.js";
7
11
  export { buildCatalog, entryUrl } from "./catalogs/build.js";
12
+ export { buildSpriteCatalog } from "./catalogs/buildSprites.js";
8
13
  export { createStarterCatalog } from "./catalogs/starter.js";
14
+ export { STARTER_ASSETS, STARTER_PACKS, STARTER_SOURCE_PACKS, STARTER_THEMES, } from "./packs/starter.js";
9
15
  export { findAssets, rankAssets, } from "./find.js";
10
16
  export { registryCatalog, componentInstallUrl } from "./registry.js";
11
- export { modelWiringSnippet, componentWiringSnippet, iconWiringSnippet } from "./snippet.js";
17
+ export { modelWiringSnippet, materialWiringSnippet, componentWiringSnippet, iconWiringSnippet, spriteWiringSnippet, } from "./snippet.js";
12
18
  export { verifyManifest } from "./verify.js";
19
+ export { resolveProvenance, validateAssetReferences, } from "./provisioning.js";
package/dist/indexGen.js CHANGED
@@ -3,7 +3,7 @@ import { join } from "node:path";
3
3
  import { readGlbDims } from "./dims.js";
4
4
  import { sourceById } from "./sources/index.js";
5
5
  export function keyFromFile(file) {
6
- return file.replace(/\.glb$/i, "");
6
+ return file.replace(/\.gltf\.glb$/i, "").replace(/\.glb$/i, "");
7
7
  }
8
8
  export function entryForFile(source, file, dims) {
9
9
  return {
@@ -87,7 +87,6 @@ export const generatedIndex = Object.values(generatedBySource).flat();
87
87
  export function reindex(modelsDir, outDir) {
88
88
  mkdirSync(outDir, { recursive: true });
89
89
  const perSource = [];
90
- const sourceIds = [];
91
90
  const subdirs = existsSync(modelsDir)
92
91
  ? readdirSync(modelsDir).filter((name) => statSync(join(modelsDir, name)).isDirectory())
93
92
  : [];
@@ -99,14 +98,17 @@ export function reindex(modelsDir, outDir) {
99
98
  }
100
99
  const entries = indexSourceDir(source, join(modelsDir, sourceId));
101
100
  writeFileSync(join(outDir, `${sourceId}.json`), `${JSON.stringify(entries, null, 2)}\n`);
102
- sourceIds.push(sourceId);
103
101
  perSource.push({ source: sourceId, count: entries.length });
104
102
  }
105
- writeFileSync(join(outDir, "index.ts"), renderTsBarrel(sourceIds));
103
+ const barrelIds = readdirSync(outDir)
104
+ .filter((name) => name.endsWith(".json"))
105
+ .map((name) => name.slice(0, -".json".length))
106
+ .filter((id) => sourceById.has(id));
107
+ writeFileSync(join(outDir, "index.ts"), renderTsBarrel(barrelIds));
106
108
  // Published installs only ship dist/; write a JS barrel there so buildCatalog can re-import after reindex.
107
109
  // Source-tree reindex keeps the TypeScript barrel that the package build compiles.
108
110
  if (!/[/\\]src[/\\]generated$/i.test(outDir)) {
109
- writeFileSync(join(outDir, "index.js"), renderJsBarrel(sourceIds));
111
+ writeFileSync(join(outDir, "index.js"), renderJsBarrel(barrelIds));
110
112
  }
111
113
  return { perSource, total: perSource.reduce((sum, entry) => sum + entry.count, 0) };
112
114
  }
@@ -1,6 +1,13 @@
1
1
  import type { ModelDims } from "@jgengine/core/scene/assetCatalog";
2
+ import type { AssetSpace } from "@jgengine/core/scene/assetSpace";
2
3
  export type { ModelDims };
3
- export type AssetProvider = "kenney" | "quaternius" | "kaykit" | "polypizza" | "itch" | "custom";
4
+ export type { AssetSpace };
5
+ export type AssetProvider = "quaternius" | "kaykit" | "polypizza" | "itch" | "ambientcg" | "gameicons" | "custom";
6
+ /**
7
+ * What a source's archive contains: GLB models (default), one PBR material's
8
+ * texture maps, or a pack of individual 2D sprite/icon files (SVG/PNG).
9
+ */
10
+ export type AssetSourceKind = "model" | "material" | "sprite";
4
11
  export interface PinnedDownload {
5
12
  url: string;
6
13
  sha256?: string;
@@ -12,6 +19,8 @@ export type AssetDownload = PinnedDownload | ScrapeDownload;
12
19
  export declare function isScrapeDownload(download: AssetDownload): download is ScrapeDownload;
13
20
  export interface AssetSource {
14
21
  id: string;
22
+ /** What the archive contains: GLB models (default), a PBR material's texture maps, or a sprite/icon pack. */
23
+ kind?: AssetSourceKind;
15
24
  provider: AssetProvider;
16
25
  title: string;
17
26
  license: string;
@@ -29,6 +38,8 @@ export interface IndexEntry {
29
38
  file: string;
30
39
  /** Footprint/center/minY measured from the GLB at reindex; absent when the model could not be read. */
31
40
  dims?: ModelDims;
41
+ /** Authored asset-space metadata (canonical facing, unit scale, footprint, anchor, bounds, rotation policy); absent unless a curator overrides the measured defaults. */
42
+ space?: AssetSpace;
32
43
  }
33
44
  export interface AssetAlias {
34
45
  key: string;
@@ -0,0 +1,61 @@
1
+ import type { AssetAlias } from "./manifest.js";
2
+ /** Normalized filenames a pulled material directory contains, keyed by map role. */
3
+ export declare const MATERIAL_MAP_FILES: {
4
+ readonly color: "color.jpg";
5
+ readonly normal: "normal.jpg";
6
+ readonly roughness: "roughness.jpg";
7
+ readonly ao: "ao.jpg";
8
+ readonly displacement: "displacement.jpg";
9
+ };
10
+ /** One PBR map's role within a material: color, normal, roughness, ao, or displacement. */
11
+ export type MaterialMapRole = keyof typeof MATERIAL_MAP_FILES;
12
+ /** One normalized map pulled out of a material archive by `extractMaterialMaps`. */
13
+ export interface ExtractedMaterialMap {
14
+ /** Normalized output filename (`color.jpg`, `normal.jpg`, …). */
15
+ file: string;
16
+ role: MaterialMapRole;
17
+ bytes: Uint8Array;
18
+ }
19
+ /**
20
+ * Pulls the recognized PBR maps out of a material archive (ambientCG's flat
21
+ * `<Asset>_<Res>_<Map>.jpg` layout) and normalizes their names so resolved
22
+ * URLs never depend on the provider's naming or the pulled resolution.
23
+ */
24
+ export declare function extractMaterialMaps(archive: Uint8Array): ExtractedMaterialMap[];
25
+ /** URLs of one material's PBR maps; `ao`/`displacement` files may be absent from a rare pack. */
26
+ export interface MaterialMaps {
27
+ color: string;
28
+ normal: string;
29
+ roughness: string;
30
+ ao: string;
31
+ displacement: string;
32
+ }
33
+ /** A resolved material: identity, attribution, and its normalized map URLs. */
34
+ export interface MaterialRef {
35
+ id: string;
36
+ title: string;
37
+ license: string;
38
+ author: string;
39
+ categories: readonly string[];
40
+ maps: MaterialMaps;
41
+ }
42
+ /** Semantic keys onto the ambientCG catalog, mirroring the model alias layer. */
43
+ export declare const materialAliases: readonly AssetAlias[];
44
+ /** Options for `buildMaterialCatalog`. */
45
+ export interface BuildMaterialCatalogOptions {
46
+ /** URL prefix where pulled material maps live (consumer's `public/materials`). */
47
+ basePath?: string;
48
+ includeAliases?: boolean;
49
+ }
50
+ /** Resolves material ids and `material/…` aliases to `MaterialRef`s. */
51
+ export interface MaterialCatalog {
52
+ resolve(idOrAlias: string): MaterialRef | null;
53
+ ids(): readonly string[];
54
+ }
55
+ /**
56
+ * A resolvable catalog over every `kind: "material"` source. Ids are source
57
+ * ids (`ambientcg-grass001`) plus the `material/…` aliases; every resolve
58
+ * returns the normalized map URLs under `basePath`, matching what
59
+ * `assets pull` writes into `<dir>/materials/<id>/`.
60
+ */
61
+ export declare function buildMaterialCatalog(options?: BuildMaterialCatalogOptions): MaterialCatalog;
@@ -0,0 +1,98 @@
1
+ import { unzipSync } from "fflate";
2
+ import { materialSources, sourceById } from "./sources/index.js";
3
+ /** Normalized filenames a pulled material directory contains, keyed by map role. */
4
+ export const MATERIAL_MAP_FILES = {
5
+ color: "color.jpg",
6
+ normal: "normal.jpg",
7
+ roughness: "roughness.jpg",
8
+ ao: "ao.jpg",
9
+ displacement: "displacement.jpg",
10
+ };
11
+ const MAP_SUFFIXES = [
12
+ { role: "color", pattern: /_Color\.(jpe?g|png)$/i },
13
+ { role: "normal", pattern: /_NormalGL\.(jpe?g|png)$/i },
14
+ { role: "roughness", pattern: /_Roughness\.(jpe?g|png)$/i },
15
+ { role: "ao", pattern: /_AmbientOcclusion\.(jpe?g|png)$/i },
16
+ { role: "displacement", pattern: /_Displacement\.(jpe?g|png)$/i },
17
+ ];
18
+ /**
19
+ * Pulls the recognized PBR maps out of a material archive (ambientCG's flat
20
+ * `<Asset>_<Res>_<Map>.jpg` layout) and normalizes their names so resolved
21
+ * URLs never depend on the provider's naming or the pulled resolution.
22
+ */
23
+ export function extractMaterialMaps(archive) {
24
+ const entries = unzipSync(archive);
25
+ const byRole = new Map();
26
+ for (const [path, bytes] of Object.entries(entries)) {
27
+ for (const { role, pattern } of MAP_SUFFIXES) {
28
+ if (pattern.test(path) && !byRole.has(role)) {
29
+ byRole.set(role, { file: MATERIAL_MAP_FILES[role], role, bytes });
30
+ }
31
+ }
32
+ }
33
+ return Array.from(byRole.values()).sort((a, b) => a.file.localeCompare(b.file));
34
+ }
35
+ /** Semantic keys onto the ambientCG catalog, mirroring the model alias layer. */
36
+ export const materialAliases = [
37
+ { key: "material/grass", target: "ambientcg-grass001" },
38
+ { key: "material/dirt", target: "ambientcg-ground001" },
39
+ { key: "material/rock", target: "ambientcg-rock001" },
40
+ { key: "material/gravel", target: "ambientcg-gravel001" },
41
+ { key: "material/snow", target: "ambientcg-snow001" },
42
+ { key: "material/sand", target: "ambientcg-ground002" },
43
+ { key: "material/wood", target: "ambientcg-wood001" },
44
+ { key: "material/planks", target: "ambientcg-planks001" },
45
+ { key: "material/brick", target: "ambientcg-bricks001" },
46
+ { key: "material/paving", target: "ambientcg-pavingstones001" },
47
+ { key: "material/tile", target: "ambientcg-tiles001" },
48
+ { key: "material/concrete", target: "ambientcg-concrete001" },
49
+ { key: "material/asphalt", target: "ambientcg-asphalt001" },
50
+ { key: "material/metal", target: "ambientcg-metal001" },
51
+ { key: "material/fabric", target: "ambientcg-fabric001" },
52
+ { key: "material/lava", target: "ambientcg-lava001" },
53
+ { key: "material/ice", target: "ambientcg-ice001" },
54
+ { key: "material/marble", target: "ambientcg-marble001" },
55
+ ];
56
+ function refFor(source, basePath) {
57
+ const base = `${basePath.replace(/\/+$/, "")}/${source.id}`;
58
+ return {
59
+ id: source.id,
60
+ title: source.title,
61
+ license: source.license,
62
+ author: source.author,
63
+ categories: source.categories,
64
+ maps: {
65
+ color: `${base}/${MATERIAL_MAP_FILES.color}`,
66
+ normal: `${base}/${MATERIAL_MAP_FILES.normal}`,
67
+ roughness: `${base}/${MATERIAL_MAP_FILES.roughness}`,
68
+ ao: `${base}/${MATERIAL_MAP_FILES.ao}`,
69
+ displacement: `${base}/${MATERIAL_MAP_FILES.displacement}`,
70
+ },
71
+ };
72
+ }
73
+ /**
74
+ * A resolvable catalog over every `kind: "material"` source. Ids are source
75
+ * ids (`ambientcg-grass001`) plus the `material/…` aliases; every resolve
76
+ * returns the normalized map URLs under `basePath`, matching what
77
+ * `assets pull` writes into `<dir>/materials/<id>/`.
78
+ */
79
+ export function buildMaterialCatalog(options = {}) {
80
+ const basePath = options.basePath ?? "/materials";
81
+ const includeAliases = options.includeAliases ?? true;
82
+ const byKey = new Map();
83
+ for (const source of materialSources) {
84
+ byKey.set(source.id, refFor(source, basePath));
85
+ }
86
+ if (includeAliases) {
87
+ for (const alias of materialAliases) {
88
+ const target = sourceById.get(alias.target);
89
+ if (target === undefined || target.kind !== "material")
90
+ continue;
91
+ byKey.set(alias.key, refFor(target, basePath));
92
+ }
93
+ }
94
+ return {
95
+ resolve: (idOrAlias) => byKey.get(idOrAlias) ?? null,
96
+ ids: () => materialSources.map((source) => source.id),
97
+ };
98
+ }
@@ -0,0 +1,46 @@
1
+ import type { AssetAlias } from "../manifest.js";
2
+ /**
3
+ * Curated drop-in starter themes for new games. Each entry maps a short semantic
4
+ * id (also registered as `asset:<id>`) to a live catalog GLB that is already
5
+ * measured (dims/anchor flow from the generated index via the alias target).
6
+ * Games wire them as `entityModels: { guest: "asset:person_casual" }`.
7
+ * Never Kenney — Quaternius / KayKit only.
8
+ * @capability starter-packs themed CC0 model ids (people/props/nature/urban) ready for entityModels
9
+ */
10
+ /** people | props | nature | urban — the four curated starter themes. */
11
+ export type StarterTheme = "people" | "props" | "nature" | "urban";
12
+ /** One curated starter model: short id, theme, live catalog target, license, suggested height. */
13
+ export interface StarterAsset {
14
+ /** Short id — also registered as `asset:<id>` and `starter/<id>`. */
15
+ id: string;
16
+ theme: StarterTheme;
17
+ /** Live catalog id (pack/file); must exist in the generated index. */
18
+ target: string;
19
+ /** Suggested height when used as a character/prop figure. */
20
+ targetHeight?: number;
21
+ title: string;
22
+ license: "CC0-1.0";
23
+ author: string;
24
+ provider: "kaykit" | "quaternius";
25
+ }
26
+ /** Flat list of every curated starter asset across themes. */
27
+ export declare const STARTER_ASSETS: readonly StarterAsset[];
28
+ /** Theme → assets table for browse/docs (`STARTER_PACKS.people`, …). */
29
+ export declare const STARTER_PACKS: Readonly<Record<StarterTheme, readonly StarterAsset[]>>;
30
+ /** Ordered starter theme ids: people, props, nature, urban. */
31
+ export declare const STARTER_THEMES: readonly StarterTheme[];
32
+ /** Source pack ids a game must pull so every starter asset URL resolves on disk. */
33
+ export declare const STARTER_SOURCE_PACKS: readonly string[];
34
+ /**
35
+ * Alias rows for the curated starter ids. Registered in the package aliases table so
36
+ * `buildCatalog` resolves `asset:person_casual`, `person_casual`, and
37
+ * `starter/person_casual` to the same measured GLB.
38
+ * @internal
39
+ */
40
+ export declare function starterAliases(): readonly AssetAlias[];
41
+ /** @internal */
42
+ export declare function starterAssetById(id: string): StarterAsset | undefined;
43
+ /** @internal */
44
+ export declare function starterStyle(id: string): {
45
+ targetHeight?: number;
46
+ } | undefined;
@@ -0,0 +1,248 @@
1
+ const PEOPLE = [
2
+ {
3
+ id: "person_casual",
4
+ theme: "people",
5
+ target: "kaykit-adventurers/Rogue",
6
+ targetHeight: 1.8,
7
+ title: "Casual person (rogue kit)",
8
+ license: "CC0-1.0",
9
+ author: "Kay Lousberg (KayKit)",
10
+ provider: "kaykit",
11
+ },
12
+ {
13
+ id: "person_hooded",
14
+ theme: "people",
15
+ target: "kaykit-adventurers/Rogue_Hooded",
16
+ targetHeight: 1.8,
17
+ title: "Hooded person",
18
+ license: "CC0-1.0",
19
+ author: "Kay Lousberg (KayKit)",
20
+ provider: "kaykit",
21
+ },
22
+ {
23
+ id: "person_armored",
24
+ theme: "people",
25
+ target: "kaykit-adventurers/Knight",
26
+ targetHeight: 1.85,
27
+ title: "Armored person",
28
+ license: "CC0-1.0",
29
+ author: "Kay Lousberg (KayKit)",
30
+ provider: "kaykit",
31
+ },
32
+ {
33
+ id: "person_mage",
34
+ theme: "people",
35
+ target: "kaykit-adventurers/Mage",
36
+ targetHeight: 1.8,
37
+ title: "Robe person",
38
+ license: "CC0-1.0",
39
+ author: "Kay Lousberg (KayKit)",
40
+ provider: "kaykit",
41
+ },
42
+ {
43
+ id: "person_heavy",
44
+ theme: "people",
45
+ target: "kaykit-adventurers/Barbarian",
46
+ targetHeight: 1.9,
47
+ title: "Heavy person",
48
+ license: "CC0-1.0",
49
+ author: "Kay Lousberg (KayKit)",
50
+ provider: "kaykit",
51
+ },
52
+ ];
53
+ const PROPS = [
54
+ {
55
+ id: "prop_crate",
56
+ theme: "props",
57
+ target: "kaykit-dungeon/crates_stacked",
58
+ targetHeight: 1.2,
59
+ title: "Stacked crates",
60
+ license: "CC0-1.0",
61
+ author: "Kay Lousberg (KayKit)",
62
+ provider: "kaykit",
63
+ },
64
+ {
65
+ id: "prop_barrel",
66
+ theme: "props",
67
+ target: "kaykit-dungeon/barrel_large",
68
+ targetHeight: 1.1,
69
+ title: "Barrel",
70
+ license: "CC0-1.0",
71
+ author: "Kay Lousberg (KayKit)",
72
+ provider: "kaykit",
73
+ },
74
+ {
75
+ id: "prop_chest",
76
+ theme: "props",
77
+ target: "kaykit-dungeon/chest",
78
+ targetHeight: 0.9,
79
+ title: "Chest",
80
+ license: "CC0-1.0",
81
+ author: "Kay Lousberg (KayKit)",
82
+ provider: "kaykit",
83
+ },
84
+ {
85
+ id: "prop_table",
86
+ theme: "props",
87
+ target: "kaykit-furniture/table_medium",
88
+ targetHeight: 0.85,
89
+ title: "Table",
90
+ license: "CC0-1.0",
91
+ author: "Kay Lousberg (KayKit)",
92
+ provider: "kaykit",
93
+ },
94
+ {
95
+ id: "prop_bench",
96
+ theme: "props",
97
+ target: "kaykit-city-builder/bench",
98
+ targetHeight: 0.7,
99
+ title: "Bench",
100
+ license: "CC0-1.0",
101
+ author: "Kay Lousberg (KayKit)",
102
+ provider: "kaykit",
103
+ },
104
+ ];
105
+ const NATURE = [
106
+ {
107
+ id: "nature_tree",
108
+ theme: "nature",
109
+ target: "quaternius-stylized-nature/CommonTree_1",
110
+ targetHeight: 4.5,
111
+ title: "Common tree",
112
+ license: "CC0-1.0",
113
+ author: "Quaternius",
114
+ provider: "quaternius",
115
+ },
116
+ {
117
+ id: "nature_pine",
118
+ theme: "nature",
119
+ target: "quaternius-stylized-nature/Pine_1",
120
+ targetHeight: 5,
121
+ title: "Pine tree",
122
+ license: "CC0-1.0",
123
+ author: "Quaternius",
124
+ provider: "quaternius",
125
+ },
126
+ {
127
+ id: "nature_bush",
128
+ theme: "nature",
129
+ target: "quaternius-stylized-nature/Bush_Common",
130
+ targetHeight: 1.2,
131
+ title: "Bush",
132
+ license: "CC0-1.0",
133
+ author: "Quaternius",
134
+ provider: "quaternius",
135
+ },
136
+ {
137
+ id: "nature_rock",
138
+ theme: "nature",
139
+ target: "quaternius-stylized-nature/Rock_Medium_1",
140
+ targetHeight: 1.4,
141
+ title: "Rock",
142
+ license: "CC0-1.0",
143
+ author: "Quaternius",
144
+ provider: "quaternius",
145
+ },
146
+ {
147
+ id: "nature_dead_tree",
148
+ theme: "nature",
149
+ target: "quaternius-stylized-nature/DeadTree_1",
150
+ targetHeight: 4,
151
+ title: "Dead tree",
152
+ license: "CC0-1.0",
153
+ author: "Quaternius",
154
+ provider: "quaternius",
155
+ },
156
+ ];
157
+ const URBAN = [
158
+ {
159
+ id: "urban_building",
160
+ theme: "urban",
161
+ target: "kaykit-city-builder/building_A",
162
+ targetHeight: 8,
163
+ title: "City building",
164
+ license: "CC0-1.0",
165
+ author: "Kay Lousberg (KayKit)",
166
+ provider: "kaykit",
167
+ },
168
+ {
169
+ id: "urban_car",
170
+ theme: "urban",
171
+ target: "kaykit-city-builder/car_sedan",
172
+ targetHeight: 1.4,
173
+ title: "Sedan",
174
+ license: "CC0-1.0",
175
+ author: "Kay Lousberg (KayKit)",
176
+ provider: "kaykit",
177
+ },
178
+ {
179
+ id: "urban_streetlight",
180
+ theme: "urban",
181
+ target: "kaykit-city-builder/streetlight",
182
+ targetHeight: 4,
183
+ title: "Streetlight",
184
+ license: "CC0-1.0",
185
+ author: "Kay Lousberg (KayKit)",
186
+ provider: "kaykit",
187
+ },
188
+ {
189
+ id: "urban_box",
190
+ theme: "urban",
191
+ target: "kaykit-city-builder/box_A",
192
+ targetHeight: 0.8,
193
+ title: "Street box",
194
+ license: "CC0-1.0",
195
+ author: "Kay Lousberg (KayKit)",
196
+ provider: "kaykit",
197
+ },
198
+ ];
199
+ /** Flat list of every curated starter asset across themes. */
200
+ export const STARTER_ASSETS = [...PEOPLE, ...PROPS, ...NATURE, ...URBAN];
201
+ /** Theme → assets table for browse/docs (`STARTER_PACKS.people`, …). */
202
+ export const STARTER_PACKS = {
203
+ people: PEOPLE,
204
+ props: PROPS,
205
+ nature: NATURE,
206
+ urban: URBAN,
207
+ };
208
+ /** Ordered starter theme ids: people, props, nature, urban. */
209
+ export const STARTER_THEMES = ["people", "props", "nature", "urban"];
210
+ /** Source pack ids a game must pull so every starter asset URL resolves on disk. */
211
+ export const STARTER_SOURCE_PACKS = [
212
+ "kaykit-adventurers",
213
+ "kaykit-dungeon",
214
+ "kaykit-furniture",
215
+ "kaykit-city-builder",
216
+ "quaternius-stylized-nature",
217
+ ];
218
+ /**
219
+ * Alias rows for the curated starter ids. Registered in the package aliases table so
220
+ * `buildCatalog` resolves `asset:person_casual`, `person_casual`, and
221
+ * `starter/person_casual` to the same measured GLB.
222
+ * @internal
223
+ */
224
+ export function starterAliases() {
225
+ const rows = [];
226
+ for (const asset of STARTER_ASSETS) {
227
+ rows.push({ key: asset.id, target: asset.target });
228
+ rows.push({ key: `asset:${asset.id}`, target: asset.target });
229
+ rows.push({ key: `starter/${asset.id}`, target: asset.target });
230
+ }
231
+ return rows;
232
+ }
233
+ /** @internal */
234
+ export function starterAssetById(id) {
235
+ const bare = id.startsWith("asset:")
236
+ ? id.slice("asset:".length)
237
+ : id.startsWith("starter/")
238
+ ? id.slice("starter/".length)
239
+ : id;
240
+ return STARTER_ASSETS.find((entry) => entry.id === bare);
241
+ }
242
+ /** @internal */
243
+ export function starterStyle(id) {
244
+ const asset = starterAssetById(id);
245
+ if (asset === undefined)
246
+ return undefined;
247
+ return asset.targetHeight === undefined ? {} : { targetHeight: asset.targetHeight };
248
+ }