ridgeline 0.7.4 → 0.7.11
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 +2 -0
- package/dist/agents/core/designer.md +18 -0
- package/dist/agents/core/planner.md +36 -0
- package/dist/agents/core/refiner.md +4 -0
- package/dist/agents/core/researcher.md +4 -0
- package/dist/agents/core/retrospective.md +64 -0
- package/dist/agents/core/specifier.md +4 -0
- package/dist/catalog/build-catalog.d.ts +21 -0
- package/dist/catalog/build-catalog.js +305 -0
- package/dist/catalog/build-catalog.js.map +1 -0
- package/dist/catalog/classify.d.ts +16 -0
- package/dist/catalog/classify.js +189 -0
- package/dist/catalog/classify.js.map +1 -0
- package/dist/catalog/extract-metadata.d.ts +41 -0
- package/dist/catalog/extract-metadata.js +175 -0
- package/dist/catalog/extract-metadata.js.map +1 -0
- package/dist/catalog/pack-sprites.d.ts +8 -0
- package/dist/catalog/pack-sprites.js +106 -0
- package/dist/catalog/pack-sprites.js.map +1 -0
- package/dist/catalog/parse-conventions.d.ts +25 -0
- package/dist/catalog/parse-conventions.js +86 -0
- package/dist/catalog/parse-conventions.js.map +1 -0
- package/dist/catalog/resolve-asset-dir.d.ts +15 -0
- package/dist/catalog/resolve-asset-dir.js +96 -0
- package/dist/catalog/resolve-asset-dir.js.map +1 -0
- package/dist/catalog/types.d.ts +74 -0
- package/dist/catalog/types.js +3 -0
- package/dist/catalog/types.js.map +1 -0
- package/dist/catalog/vision-describe.d.ts +12 -0
- package/dist/catalog/vision-describe.js +158 -0
- package/dist/catalog/vision-describe.js.map +1 -0
- package/dist/cli.js +54 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/build.d.ts +2 -2
- package/dist/commands/build.js +156 -30
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/catalog.d.ts +6 -0
- package/dist/commands/catalog.js +141 -0
- package/dist/commands/catalog.js.map +1 -0
- package/dist/commands/design.js +73 -0
- package/dist/commands/design.js.map +1 -1
- package/dist/commands/retrospective.d.ts +7 -0
- package/dist/commands/retrospective.js +119 -0
- package/dist/commands/retrospective.js.map +1 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/engine/discovery/agent.registry.d.ts +3 -0
- package/dist/engine/discovery/agent.registry.js +15 -2
- package/dist/engine/discovery/agent.registry.js.map +1 -1
- package/dist/engine/pipeline/build.exec.d.ts +1 -1
- package/dist/engine/pipeline/build.exec.js +24 -27
- package/dist/engine/pipeline/build.exec.js.map +1 -1
- package/dist/engine/pipeline/ensemble.exec.d.ts +16 -0
- package/dist/engine/pipeline/ensemble.exec.js +128 -31
- package/dist/engine/pipeline/ensemble.exec.js.map +1 -1
- package/dist/engine/pipeline/phase.graph.d.ts +31 -0
- package/dist/engine/pipeline/phase.graph.js +102 -0
- package/dist/engine/pipeline/phase.graph.js.map +1 -0
- package/dist/engine/pipeline/phase.sequence.d.ts +3 -1
- package/dist/engine/pipeline/phase.sequence.js +50 -21
- package/dist/engine/pipeline/phase.sequence.js.map +1 -1
- package/dist/engine/pipeline/pipeline.shared.d.ts +12 -5
- package/dist/engine/pipeline/pipeline.shared.js +50 -26
- package/dist/engine/pipeline/pipeline.shared.js.map +1 -1
- package/dist/engine/pipeline/plan.exec.d.ts +4 -1
- package/dist/engine/pipeline/plan.exec.js +15 -12
- package/dist/engine/pipeline/plan.exec.js.map +1 -1
- package/dist/engine/pipeline/prompt.document.d.ts +28 -0
- package/dist/engine/pipeline/prompt.document.js +50 -0
- package/dist/engine/pipeline/prompt.document.js.map +1 -0
- package/dist/engine/pipeline/refine.exec.js +16 -14
- package/dist/engine/pipeline/refine.exec.js.map +1 -1
- package/dist/engine/pipeline/research.exec.d.ts +0 -2
- package/dist/engine/pipeline/research.exec.js +32 -58
- package/dist/engine/pipeline/research.exec.js.map +1 -1
- package/dist/engine/pipeline/review.exec.d.ts +1 -1
- package/dist/engine/pipeline/review.exec.js +23 -31
- package/dist/engine/pipeline/review.exec.js.map +1 -1
- package/dist/engine/pipeline/specify.exec.js +16 -24
- package/dist/engine/pipeline/specify.exec.js.map +1 -1
- package/dist/engine/pipeline/worktree.parallel.d.ts +22 -0
- package/dist/engine/pipeline/worktree.parallel.js +122 -0
- package/dist/engine/pipeline/worktree.parallel.js.map +1 -0
- package/dist/flavours/web-game/core/builder.md +5 -2
- package/dist/flavours/web-game/core/designer.md +157 -0
- package/dist/git.js +11 -4
- package/dist/git.js.map +1 -1
- package/dist/stores/budget.js +21 -17
- package/dist/stores/budget.js.map +1 -1
- package/dist/stores/handoff.d.ts +4 -0
- package/dist/stores/handoff.js +28 -1
- package/dist/stores/handoff.js.map +1 -1
- package/dist/stores/phases.d.ts +8 -0
- package/dist/stores/phases.js +23 -2
- package/dist/stores/phases.js.map +1 -1
- package/dist/stores/settings.d.ts +1 -0
- package/dist/stores/settings.js.map +1 -1
- package/dist/stores/state.d.ts +6 -1
- package/dist/stores/state.js +101 -19
- package/dist/stores/state.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/ui/logger.d.ts +11 -0
- package/dist/ui/logger.js +71 -0
- package/dist/ui/logger.js.map +1 -0
- package/dist/ui/output.d.ts +1 -0
- package/dist/ui/output.js +11 -1
- package/dist/ui/output.js.map +1 -1
- package/dist/utils/file-lock.d.ts +5 -0
- package/dist/utils/file-lock.js +95 -0
- package/dist/utils/file-lock.js.map +1 -0
- package/package.json +6 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.classifyWithAI = exports.classifyByHeuristics = void 0;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const node_child_process_1 = require("node:child_process");
|
|
40
|
+
// --- Heuristic patterns (filename prefix → category) ---
|
|
41
|
+
const IMAGE_HEURISTICS = [
|
|
42
|
+
[/^(bg|background|backdrop|sky|scenery)[_-]/i, "backgrounds"],
|
|
43
|
+
[/^(btn|button|icon|hud|bar|frame|panel|menu)[_-]/i, "ui"],
|
|
44
|
+
[/^(tile|floor|wall|ground|terrain|grass|stone|brick)[_-]/i, "tiles"],
|
|
45
|
+
[/^(char|character|player|enemy|npc|hero|sprite)[_-]/i, "characters"],
|
|
46
|
+
[/^(item|pickup|loot|potion|coin|gem|key|weapon|sword|shield)[_-]/i, "items"],
|
|
47
|
+
[/^(fx|effect|particle|explosion|spark|flash|smoke)[_-]/i, "effects"],
|
|
48
|
+
[/^(layout|mockup|wireframe|screen)[_-]/i, "layouts"],
|
|
49
|
+
];
|
|
50
|
+
const AUDIO_HEURISTICS = [
|
|
51
|
+
[/^(bgm|music|soundtrack|theme|ost|track|song)[_-]/i, "music"],
|
|
52
|
+
[/^(sfx|click|hit|boom|slash|jump|land|step|beep|ding|whoosh|pop|crash)[_-]/i, "sfx"],
|
|
53
|
+
[/^(ambient|ambience|wind|rain|water|forest|cave|city|night|crowd)[_-]/i, "ambience"],
|
|
54
|
+
[/^(voice|dialogue|narrat|speech|line)[_-]/i, "dialogue"],
|
|
55
|
+
];
|
|
56
|
+
const VIDEO_HEURISTICS = [
|
|
57
|
+
[/^(cutscene|cinematic|intro|outro|ending|opening)[_-]/i, "cinematics"],
|
|
58
|
+
];
|
|
59
|
+
const TEXT_HEURISTICS = [
|
|
60
|
+
[/^(dialogue|conversation|script|line|speech)[_-]/i, "dialogue"],
|
|
61
|
+
[/^(doc|readme|guide|manual|help|note)[_-]/i, "docs"],
|
|
62
|
+
];
|
|
63
|
+
/** Categories valid for each media type. */
|
|
64
|
+
const CATEGORIES_BY_MEDIA = {
|
|
65
|
+
image: ["characters", "tiles", "items", "ui", "backgrounds", "effects", "layouts"],
|
|
66
|
+
audio: ["music", "sfx", "ambience", "dialogue"],
|
|
67
|
+
video: ["cinematics"],
|
|
68
|
+
text: ["dialogue", "data", "docs"],
|
|
69
|
+
};
|
|
70
|
+
const HEURISTICS_BY_MEDIA = {
|
|
71
|
+
image: IMAGE_HEURISTICS,
|
|
72
|
+
audio: AUDIO_HEURISTICS,
|
|
73
|
+
video: VIDEO_HEURISTICS,
|
|
74
|
+
text: TEXT_HEURISTICS,
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Attempt to classify a file using filename pattern matching.
|
|
78
|
+
* Returns null if no heuristic matches confidently.
|
|
79
|
+
*/
|
|
80
|
+
const classifyByHeuristics = (filename, ext, mediaType) => {
|
|
81
|
+
const basename = path.basename(filename, ext);
|
|
82
|
+
// Text data files by extension
|
|
83
|
+
if (mediaType === "text" && [".json", ".csv", ".yaml", ".yml"].includes(ext)) {
|
|
84
|
+
return { category: "data", confidence: "medium" };
|
|
85
|
+
}
|
|
86
|
+
const patterns = HEURISTICS_BY_MEDIA[mediaType];
|
|
87
|
+
for (const [pattern, category] of patterns) {
|
|
88
|
+
if (pattern.test(basename)) {
|
|
89
|
+
return { category, confidence: "high" };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
exports.classifyByHeuristics = classifyByHeuristics;
|
|
95
|
+
// --- AI classification prompts ---
|
|
96
|
+
const IMAGE_CLASSIFY_PROMPT = `You are classifying game assets into organized categories. Analyze this image and determine the best category.
|
|
97
|
+
|
|
98
|
+
File: {filename}
|
|
99
|
+
|
|
100
|
+
Available categories:
|
|
101
|
+
- characters: Player characters, NPCs, enemies, character sprites, portraits
|
|
102
|
+
- tiles: Ground tiles, wall tiles, terrain, tileable textures
|
|
103
|
+
- items: Inventory items, pickups, powerups, weapons, equipment
|
|
104
|
+
- ui: HUD elements, buttons, frames, health bars, menu components
|
|
105
|
+
- backgrounds: Scenery, parallax layers, sky, environment backdrops
|
|
106
|
+
- effects: Particles, explosions, magic effects, weather, visual FX
|
|
107
|
+
- layouts: UI mockups, screen layouts, wireframes
|
|
108
|
+
- uncategorized: Does not clearly fit any category above
|
|
109
|
+
|
|
110
|
+
Respond with ONLY valid JSON:
|
|
111
|
+
{
|
|
112
|
+
"category": "one of the categories above",
|
|
113
|
+
"confidence": "high" | "medium" | "low"
|
|
114
|
+
}`;
|
|
115
|
+
const buildNonImagePrompt = (filename, ext, mediaType, fileSizeBytes, contentPreview) => {
|
|
116
|
+
const categories = CATEGORIES_BY_MEDIA[mediaType];
|
|
117
|
+
const categoryList = categories
|
|
118
|
+
.map((c) => `- ${c}`)
|
|
119
|
+
.join("\n");
|
|
120
|
+
const preview = contentPreview
|
|
121
|
+
? `\nContent preview (first 500 chars):\n\`\`\`\n${contentPreview}\n\`\`\``
|
|
122
|
+
: "";
|
|
123
|
+
return `You are classifying game asset files into organized categories based on filename, extension, and file content.
|
|
124
|
+
|
|
125
|
+
File: ${filename}
|
|
126
|
+
Extension: ${ext}
|
|
127
|
+
Media type: ${mediaType}
|
|
128
|
+
File size: ${Math.round(fileSizeBytes / 1024)} KB${preview}
|
|
129
|
+
|
|
130
|
+
Available categories for ${mediaType}:
|
|
131
|
+
${categoryList}
|
|
132
|
+
- uncategorized: Does not clearly fit any category above
|
|
133
|
+
|
|
134
|
+
Respond with ONLY valid JSON:
|
|
135
|
+
{
|
|
136
|
+
"category": "one of the categories above",
|
|
137
|
+
"confidence": "high" | "medium" | "low"
|
|
138
|
+
}`;
|
|
139
|
+
};
|
|
140
|
+
/** Invoke Claude and parse the classification result. */
|
|
141
|
+
const invokeClaude = (prompt, model, timeoutMs, filename, filePath) => {
|
|
142
|
+
const args = ["-p", "--model", model, "--output-format", "json", prompt];
|
|
143
|
+
if (filePath)
|
|
144
|
+
args.push("--file", filePath);
|
|
145
|
+
try {
|
|
146
|
+
const result = (0, node_child_process_1.execFileSync)("claude", args, {
|
|
147
|
+
timeout: timeoutMs,
|
|
148
|
+
encoding: "utf-8",
|
|
149
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
150
|
+
});
|
|
151
|
+
const parsed = JSON.parse(result);
|
|
152
|
+
const text = parsed.result ?? parsed;
|
|
153
|
+
const data = typeof text === "string" ? JSON.parse(text) : text;
|
|
154
|
+
return {
|
|
155
|
+
category: data.category ?? "uncategorized",
|
|
156
|
+
confidence: data.confidence ?? "low",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
161
|
+
process.stderr.write(`\x1b[33mClassification failed for ${filename}: ${msg}\x1b[0m\n`);
|
|
162
|
+
return { category: "uncategorized", confidence: "low" };
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Classify a single file using AI.
|
|
167
|
+
* Dispatches to vision (images) or text-only (audio/video/text).
|
|
168
|
+
*/
|
|
169
|
+
const classifyWithAI = (absPath, filename, ext, mediaType, model, timeoutMs) => {
|
|
170
|
+
if (mediaType === "image") {
|
|
171
|
+
const prompt = IMAGE_CLASSIFY_PROMPT.replace("{filename}", filename);
|
|
172
|
+
return invokeClaude(prompt, model, timeoutMs, filename, absPath);
|
|
173
|
+
}
|
|
174
|
+
const stat = fs.statSync(absPath);
|
|
175
|
+
let contentPreview = null;
|
|
176
|
+
if (mediaType === "text") {
|
|
177
|
+
try {
|
|
178
|
+
const content = fs.readFileSync(absPath, "utf-8");
|
|
179
|
+
contentPreview = content.slice(0, 500);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Binary or unreadable — skip preview
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const prompt = buildNonImagePrompt(filename, ext, mediaType, stat.size, contentPreview);
|
|
186
|
+
return invokeClaude(prompt, model, timeoutMs, filename);
|
|
187
|
+
};
|
|
188
|
+
exports.classifyWithAI = classifyWithAI;
|
|
189
|
+
//# sourceMappingURL=classify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classify.js","sourceRoot":"","sources":["../../src/catalog/classify.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
type ImageMetadata = {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
format: string;
|
|
5
|
+
hasAlpha: boolean;
|
|
6
|
+
channels: number;
|
|
7
|
+
};
|
|
8
|
+
type PaletteResult = {
|
|
9
|
+
dominantColour: string;
|
|
10
|
+
palette: string[];
|
|
11
|
+
};
|
|
12
|
+
type SpritesheetInfo = {
|
|
13
|
+
isSpritesheet: boolean;
|
|
14
|
+
frameCount: number;
|
|
15
|
+
frameSize: {
|
|
16
|
+
w: number;
|
|
17
|
+
h: number;
|
|
18
|
+
} | null;
|
|
19
|
+
frameDirection: "horizontal" | "vertical" | null;
|
|
20
|
+
};
|
|
21
|
+
/** Extract image dimensions, format, alpha, and channel info via sharp. */
|
|
22
|
+
export declare const extractImageMetadata: (filepath: string) => Promise<ImageMetadata>;
|
|
23
|
+
/** Extract dominant colour and 6-colour palette via colorthief. */
|
|
24
|
+
export declare const extractPalette: (filepath: string) => Promise<PaletteResult>;
|
|
25
|
+
/** Detect spritesheet from dimensions — one dimension is an exact multiple of the other. */
|
|
26
|
+
export declare const detectSpritesheet: (width: number, height: number) => SpritesheetInfo;
|
|
27
|
+
/**
|
|
28
|
+
* Detect tilability by comparing edge pixel strips.
|
|
29
|
+
* If the top row matches the bottom row (and left col matches right col),
|
|
30
|
+
* the image is likely tileable.
|
|
31
|
+
*/
|
|
32
|
+
export declare const detectTileable: (filepath: string, width: number, height: number) => Promise<boolean>;
|
|
33
|
+
/** Compute MD5 content hash for change detection (not security-sensitive). */
|
|
34
|
+
export declare const computeContentHash: (filepath: string) => string;
|
|
35
|
+
type BasicMetadata = {
|
|
36
|
+
fileSizeBytes: number;
|
|
37
|
+
extension: string;
|
|
38
|
+
};
|
|
39
|
+
/** Extract basic file metadata for non-image assets. */
|
|
40
|
+
export declare const extractBasicMetadata: (filepath: string) => BasicMetadata;
|
|
41
|
+
export {};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.extractBasicMetadata = exports.computeContentHash = exports.detectTileable = exports.detectSpritesheet = exports.extractPalette = exports.extractImageMetadata = void 0;
|
|
37
|
+
const crypto = __importStar(require("node:crypto"));
|
|
38
|
+
const fs = __importStar(require("node:fs"));
|
|
39
|
+
const path = __importStar(require("node:path"));
|
|
40
|
+
// Dynamic imports for native deps — only loaded when catalog command runs
|
|
41
|
+
let sharpModule = null;
|
|
42
|
+
let colorThiefModule = null;
|
|
43
|
+
const loadSharp = async () => {
|
|
44
|
+
if (!sharpModule)
|
|
45
|
+
sharpModule = (await Promise.resolve().then(() => __importStar(require("sharp")))).default;
|
|
46
|
+
return sharpModule;
|
|
47
|
+
};
|
|
48
|
+
const loadColorThief = async () => {
|
|
49
|
+
if (!colorThiefModule)
|
|
50
|
+
colorThiefModule = (await Promise.resolve().then(() => __importStar(require("colorthief")))).default;
|
|
51
|
+
return colorThiefModule;
|
|
52
|
+
};
|
|
53
|
+
/** Extract image dimensions, format, alpha, and channel info via sharp. */
|
|
54
|
+
const extractImageMetadata = async (filepath) => {
|
|
55
|
+
const sharp = await loadSharp();
|
|
56
|
+
const meta = await sharp(filepath).metadata();
|
|
57
|
+
return {
|
|
58
|
+
width: meta.width ?? 0,
|
|
59
|
+
height: meta.height ?? 0,
|
|
60
|
+
format: meta.format ?? "unknown",
|
|
61
|
+
hasAlpha: meta.hasAlpha ?? false,
|
|
62
|
+
channels: meta.channels ?? 3,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
exports.extractImageMetadata = extractImageMetadata;
|
|
66
|
+
/** Extract dominant colour and 6-colour palette via colorthief. */
|
|
67
|
+
const extractPalette = async (filepath) => {
|
|
68
|
+
const ColorThief = await loadColorThief();
|
|
69
|
+
try {
|
|
70
|
+
const dominant = await ColorThief.getColor(filepath);
|
|
71
|
+
const palette = await ColorThief.getPalette(filepath, 6);
|
|
72
|
+
return {
|
|
73
|
+
dominantColour: rgbToHex(dominant),
|
|
74
|
+
palette: palette.map(rgbToHex),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// colorthief can fail on very small or single-colour images
|
|
79
|
+
return { dominantColour: "#000000", palette: [] };
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.extractPalette = extractPalette;
|
|
83
|
+
/** Detect spritesheet from dimensions — one dimension is an exact multiple of the other. */
|
|
84
|
+
const detectSpritesheet = (width, height) => {
|
|
85
|
+
if (width <= 0 || height <= 0 || width === height) {
|
|
86
|
+
return { isSpritesheet: false, frameCount: 1, frameSize: null, frameDirection: null };
|
|
87
|
+
}
|
|
88
|
+
const isWideStrip = width > height && width % height === 0;
|
|
89
|
+
const isTallStrip = height > width && height % width === 0;
|
|
90
|
+
if (isWideStrip) {
|
|
91
|
+
return {
|
|
92
|
+
isSpritesheet: true,
|
|
93
|
+
frameCount: width / height,
|
|
94
|
+
frameSize: { w: height, h: height },
|
|
95
|
+
frameDirection: "horizontal",
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (isTallStrip) {
|
|
99
|
+
return {
|
|
100
|
+
isSpritesheet: true,
|
|
101
|
+
frameCount: height / width,
|
|
102
|
+
frameSize: { w: width, h: width },
|
|
103
|
+
frameDirection: "vertical",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return { isSpritesheet: false, frameCount: 1, frameSize: null, frameDirection: null };
|
|
107
|
+
};
|
|
108
|
+
exports.detectSpritesheet = detectSpritesheet;
|
|
109
|
+
/**
|
|
110
|
+
* Detect tilability by comparing edge pixel strips.
|
|
111
|
+
* If the top row matches the bottom row (and left col matches right col),
|
|
112
|
+
* the image is likely tileable.
|
|
113
|
+
*/
|
|
114
|
+
const detectTileable = async (filepath, width, height) => {
|
|
115
|
+
if (width < 2 || height < 2)
|
|
116
|
+
return false;
|
|
117
|
+
const sharp = await loadSharp();
|
|
118
|
+
try {
|
|
119
|
+
// Extract top and bottom edge strips (1px high)
|
|
120
|
+
const topPixels = await sharp(filepath)
|
|
121
|
+
.extract({ left: 0, top: 0, width, height: 1 })
|
|
122
|
+
.raw()
|
|
123
|
+
.toBuffer();
|
|
124
|
+
const bottomPixels = await sharp(filepath)
|
|
125
|
+
.extract({ left: 0, top: height - 1, width, height: 1 })
|
|
126
|
+
.raw()
|
|
127
|
+
.toBuffer();
|
|
128
|
+
// Extract left and right edge strips (1px wide)
|
|
129
|
+
const leftPixels = await sharp(filepath)
|
|
130
|
+
.extract({ left: 0, top: 0, width: 1, height })
|
|
131
|
+
.raw()
|
|
132
|
+
.toBuffer();
|
|
133
|
+
const rightPixels = await sharp(filepath)
|
|
134
|
+
.extract({ left: width - 1, top: 0, width: 1, height })
|
|
135
|
+
.raw()
|
|
136
|
+
.toBuffer();
|
|
137
|
+
const hSimilarity = bufferSimilarity(topPixels, bottomPixels);
|
|
138
|
+
const vSimilarity = bufferSimilarity(leftPixels, rightPixels);
|
|
139
|
+
// Threshold: edges must be >85% similar
|
|
140
|
+
return hSimilarity > 0.85 && vSimilarity > 0.85;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
exports.detectTileable = detectTileable;
|
|
147
|
+
/** Compute MD5 content hash for change detection (not security-sensitive). */
|
|
148
|
+
const computeContentHash = (filepath) => {
|
|
149
|
+
const data = fs.readFileSync(filepath);
|
|
150
|
+
return crypto.createHash("md5").update(data).digest("hex");
|
|
151
|
+
};
|
|
152
|
+
exports.computeContentHash = computeContentHash;
|
|
153
|
+
/** Extract basic file metadata for non-image assets. */
|
|
154
|
+
const extractBasicMetadata = (filepath) => {
|
|
155
|
+
const stat = fs.statSync(filepath);
|
|
156
|
+
return {
|
|
157
|
+
fileSizeBytes: stat.size,
|
|
158
|
+
extension: path.extname(filepath).toLowerCase(),
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
exports.extractBasicMetadata = extractBasicMetadata;
|
|
162
|
+
// --- Helpers ---
|
|
163
|
+
const rgbToHex = (rgb) => "#" + rgb.map((c) => c.toString(16).padStart(2, "0")).join("");
|
|
164
|
+
/** Compare two raw pixel buffers and return similarity ratio (0..1). */
|
|
165
|
+
const bufferSimilarity = (a, b) => {
|
|
166
|
+
if (a.length !== b.length || a.length === 0)
|
|
167
|
+
return 0;
|
|
168
|
+
let matching = 0;
|
|
169
|
+
for (let i = 0; i < a.length; i++) {
|
|
170
|
+
if (Math.abs(a[i] - b[i]) <= 16)
|
|
171
|
+
matching++; // tolerance of 16/255
|
|
172
|
+
}
|
|
173
|
+
return matching / a.length;
|
|
174
|
+
};
|
|
175
|
+
//# sourceMappingURL=extract-metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extract-metadata.js","sourceRoot":"","sources":["../../src/catalog/extract-metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AssetCatalog } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Pack sprites into texture atlases grouped by category.
|
|
4
|
+
* Uses free-tex-packer-core to produce PixiJS-compatible JSON + PNG atlas files.
|
|
5
|
+
*
|
|
6
|
+
* Output: <assetDir>/packed/<category>.png + <category>.json
|
|
7
|
+
*/
|
|
8
|
+
export declare const packAtlases: (assetDir: string, catalog: AssetCatalog) => Promise<void>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.packAtlases = void 0;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
/**
|
|
40
|
+
* Pack sprites into texture atlases grouped by category.
|
|
41
|
+
* Uses free-tex-packer-core to produce PixiJS-compatible JSON + PNG atlas files.
|
|
42
|
+
*
|
|
43
|
+
* Output: <assetDir>/packed/<category>.png + <category>.json
|
|
44
|
+
*/
|
|
45
|
+
const packAtlases = async (assetDir, catalog) => {
|
|
46
|
+
// Dynamic import — only loaded when --pack is used
|
|
47
|
+
const { packAsync } = await Promise.resolve().then(() => __importStar(require("free-tex-packer-core")));
|
|
48
|
+
const outputDir = path.join(assetDir, "packed");
|
|
49
|
+
if (!fs.existsSync(outputDir)) {
|
|
50
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
// Group assets by category (skip layouts — they're reference-only)
|
|
53
|
+
const byCategory = new Map();
|
|
54
|
+
for (const asset of catalog.assets) {
|
|
55
|
+
if (asset.isReferenceOnly || asset.category === "layouts")
|
|
56
|
+
continue;
|
|
57
|
+
// Skip backgrounds — typically too large for atlases
|
|
58
|
+
if (asset.category === "backgrounds")
|
|
59
|
+
continue;
|
|
60
|
+
const list = byCategory.get(asset.category) ?? [];
|
|
61
|
+
list.push(asset.file);
|
|
62
|
+
byCategory.set(asset.category, list);
|
|
63
|
+
}
|
|
64
|
+
for (const [category, files] of byCategory) {
|
|
65
|
+
if (files.length === 0)
|
|
66
|
+
continue;
|
|
67
|
+
const images = files
|
|
68
|
+
.map((f) => {
|
|
69
|
+
const absPath = path.join(assetDir, f);
|
|
70
|
+
if (!fs.existsSync(absPath))
|
|
71
|
+
return null;
|
|
72
|
+
return {
|
|
73
|
+
path: path.basename(f, path.extname(f)),
|
|
74
|
+
contents: fs.readFileSync(absPath),
|
|
75
|
+
};
|
|
76
|
+
})
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
if (images.length === 0)
|
|
79
|
+
continue;
|
|
80
|
+
try {
|
|
81
|
+
const result = await packAsync(images, {
|
|
82
|
+
textureName: category,
|
|
83
|
+
width: 2048,
|
|
84
|
+
height: 2048,
|
|
85
|
+
fixedSize: false,
|
|
86
|
+
padding: 1,
|
|
87
|
+
allowRotation: false,
|
|
88
|
+
detectIdentical: true,
|
|
89
|
+
allowTrim: true,
|
|
90
|
+
exporter: "Pixi",
|
|
91
|
+
removeFileExtension: true,
|
|
92
|
+
});
|
|
93
|
+
for (const file of result) {
|
|
94
|
+
const outPath = path.join(outputDir, file.name);
|
|
95
|
+
fs.writeFileSync(outPath, file.buffer);
|
|
96
|
+
}
|
|
97
|
+
process.stderr.write(`\x1b[90m Packed ${category}: ${images.length} sprites\x1b[0m\n`);
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
101
|
+
process.stderr.write(`\x1b[33mFailed to pack ${category}: ${msg}\x1b[0m\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
exports.packAtlases = packAtlases;
|
|
106
|
+
//# sourceMappingURL=pack-sprites.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pack-sprites.js","sourceRoot":"","sources":["../../src/catalog/pack-sprites.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type ConventionResult = {
|
|
2
|
+
category: string;
|
|
3
|
+
name: string;
|
|
4
|
+
subject: string;
|
|
5
|
+
state: string | null;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Parse category, subject, and state from the filesystem path.
|
|
9
|
+
*
|
|
10
|
+
* "characters/knight-walk.png" →
|
|
11
|
+
* { category: "characters", name: "knight-walk", subject: "knight", state: "walk" }
|
|
12
|
+
*
|
|
13
|
+
* "export_003.png" (no subdirectory) →
|
|
14
|
+
* { category: "uncategorized", name: "export_003", subject: "export_003", state: null }
|
|
15
|
+
*/
|
|
16
|
+
export declare const parseConventions: (relativePath: string) => ConventionResult;
|
|
17
|
+
type CategoryDefaults = {
|
|
18
|
+
zLayer: string;
|
|
19
|
+
anchor: string;
|
|
20
|
+
};
|
|
21
|
+
/** Infer default z-layer and anchor from category name. */
|
|
22
|
+
export declare const inferDefaults: (category: string) => CategoryDefaults;
|
|
23
|
+
/** Categories that always get vision descriptions regardless of --describe flag. */
|
|
24
|
+
export declare const AUTO_DESCRIBE_CATEGORIES: Set<string>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AUTO_DESCRIBE_CATEGORIES = exports.inferDefaults = exports.parseConventions = void 0;
|
|
37
|
+
const path = __importStar(require("node:path"));
|
|
38
|
+
/**
|
|
39
|
+
* Parse category, subject, and state from the filesystem path.
|
|
40
|
+
*
|
|
41
|
+
* "characters/knight-walk.png" →
|
|
42
|
+
* { category: "characters", name: "knight-walk", subject: "knight", state: "walk" }
|
|
43
|
+
*
|
|
44
|
+
* "export_003.png" (no subdirectory) →
|
|
45
|
+
* { category: "uncategorized", name: "export_003", subject: "export_003", state: null }
|
|
46
|
+
*/
|
|
47
|
+
const parseConventions = (relativePath) => {
|
|
48
|
+
const parts = relativePath.split(path.sep);
|
|
49
|
+
const category = parts.length > 1 ? parts[parts.length - 2] : "uncategorized";
|
|
50
|
+
const filename = path.basename(relativePath, path.extname(relativePath));
|
|
51
|
+
const segments = filename.split("-");
|
|
52
|
+
return {
|
|
53
|
+
category,
|
|
54
|
+
name: filename,
|
|
55
|
+
subject: segments[0] || filename,
|
|
56
|
+
state: segments.length > 1 ? segments.slice(1).join("-") : null,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
exports.parseConventions = parseConventions;
|
|
60
|
+
const CATEGORY_DEFAULTS = {
|
|
61
|
+
// Image categories
|
|
62
|
+
characters: { zLayer: "entity", anchor: "bottom-center" },
|
|
63
|
+
tiles: { zLayer: "ground", anchor: "top-left" },
|
|
64
|
+
items: { zLayer: "entity", anchor: "center" },
|
|
65
|
+
ui: { zLayer: "ui", anchor: "center" },
|
|
66
|
+
backgrounds: { zLayer: "background", anchor: "top-left" },
|
|
67
|
+
effects: { zLayer: "foreground", anchor: "center" },
|
|
68
|
+
layouts: { zLayer: "ui", anchor: "top-left" },
|
|
69
|
+
// Audio categories
|
|
70
|
+
music: { zLayer: "background", anchor: "center" },
|
|
71
|
+
sfx: { zLayer: "entity", anchor: "center" },
|
|
72
|
+
ambience: { zLayer: "background", anchor: "center" },
|
|
73
|
+
dialogue: { zLayer: "entity", anchor: "center" },
|
|
74
|
+
// Video categories
|
|
75
|
+
cinematics: { zLayer: "foreground", anchor: "center" },
|
|
76
|
+
// Text/data categories
|
|
77
|
+
data: { zLayer: "entity", anchor: "center" },
|
|
78
|
+
docs: { zLayer: "entity", anchor: "center" },
|
|
79
|
+
};
|
|
80
|
+
const DEFAULT_FALLBACK = { zLayer: "entity", anchor: "center" };
|
|
81
|
+
/** Infer default z-layer and anchor from category name. */
|
|
82
|
+
const inferDefaults = (category) => CATEGORY_DEFAULTS[category] ?? DEFAULT_FALLBACK;
|
|
83
|
+
exports.inferDefaults = inferDefaults;
|
|
84
|
+
/** Categories that always get vision descriptions regardless of --describe flag. */
|
|
85
|
+
exports.AUTO_DESCRIBE_CATEGORIES = new Set(["layouts", "ui"]);
|
|
86
|
+
//# sourceMappingURL=parse-conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse-conventions.js","sourceRoot":"","sources":["../../src/catalog/parse-conventions.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAiC;AASjC;;;;;;;;GAQG;AACI,MAAM,gBAAgB,GAAG,CAAC,YAAoB,EAAoB,EAAE;IACzE,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAA;IAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAA;IACxE,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAEpC,OAAO;QACL,QAAQ;QACR,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ;QAChC,KAAK,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI;KAChE,CAAA;AACH,CAAC,CAAA;AAZY,QAAA,gBAAgB,oBAY5B;AAOD,MAAM,iBAAiB,GAAqC;IAC1D,mBAAmB;IACnB,UAAU,EAAG,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,eAAe,EAAE;IAC9D,KAAK,EAAQ,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,UAAU,EAAE;IACzD,KAAK,EAAQ,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,QAAQ,EAAE;IACvD,EAAE,EAAW,EAAE,MAAM,EAAE,IAAI,EAAU,MAAM,EAAE,QAAQ,EAAE;IACvD,WAAW,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE;IACzD,OAAO,EAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;IACvD,OAAO,EAAM,EAAE,MAAM,EAAE,IAAI,EAAU,MAAM,EAAE,UAAU,EAAE;IACzD,mBAAmB;IACnB,KAAK,EAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;IACvD,GAAG,EAAU,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,QAAQ,EAAE;IACvD,QAAQ,EAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;IACvD,QAAQ,EAAK,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,QAAQ,EAAE;IACvD,mBAAmB;IACnB,UAAU,EAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE;IACvD,uBAAuB;IACvB,IAAI,EAAS,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,QAAQ,EAAE;IACvD,IAAI,EAAS,EAAE,MAAM,EAAE,QAAQ,EAAM,MAAM,EAAE,QAAQ,EAAE;CACxD,CAAA;AAED,MAAM,gBAAgB,GAAqB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAA;AAEjF,2DAA2D;AACpD,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAoB,EAAE,CAClE,iBAAiB,CAAC,QAAQ,CAAC,IAAI,gBAAgB,CAAA;AADpC,QAAA,aAAa,iBACuB;AAEjD,oFAAoF;AACvE,QAAA,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAA"}
|