@neonwatty/limner 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/capture.d.ts +12 -0
- package/dist/commands/capture.js +66 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/compare-image-app.d.ts +12 -0
- package/dist/commands/compare-image-app.js +45 -0
- package/dist/commands/compare-image-app.js.map +1 -0
- package/dist/commands/compare-image-reference.d.ts +28 -0
- package/dist/commands/compare-image-reference.js +82 -0
- package/dist/commands/compare-image-reference.js.map +1 -0
- package/dist/commands/compare.d.ts +15 -0
- package/dist/commands/compare.js +168 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.js +65 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/preview.d.ts +2 -0
- package/dist/commands/preview.js +27 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +14 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/runs.d.ts +2 -0
- package/dist/commands/runs.js +63 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/core/dom-metrics.d.ts +18 -0
- package/dist/core/dom-metrics.js +54 -0
- package/dist/core/dom-metrics.js.map +1 -0
- package/dist/core/playwright-capture.d.ts +27 -0
- package/dist/core/playwright-capture.js +46 -0
- package/dist/core/playwright-capture.js.map +1 -0
- package/dist/core/playwright-dom.d.ts +16 -0
- package/dist/core/playwright-dom.js +64 -0
- package/dist/core/playwright-dom.js.map +1 -0
- package/dist/core/reference-dom-facts.d.ts +65 -0
- package/dist/core/reference-dom-facts.js +71 -0
- package/dist/core/reference-dom-facts.js.map +1 -0
- package/dist/core/report-writer.d.ts +43 -0
- package/dist/core/report-writer.js +181 -0
- package/dist/core/report-writer.js.map +1 -0
- package/dist/core/run-logger.d.ts +22 -0
- package/dist/core/run-logger.js +67 -0
- package/dist/core/run-logger.js.map +1 -0
- package/dist/core/side-by-side.d.ts +8 -0
- package/dist/core/side-by-side.js +33 -0
- package/dist/core/side-by-side.js.map +1 -0
- package/dist/core/static-server.d.ts +6 -0
- package/dist/core/static-server.js +73 -0
- package/dist/core/static-server.js.map +1 -0
- package/dist/core/visual-spec-agent-pack.d.ts +27 -0
- package/dist/core/visual-spec-agent-pack.js +143 -0
- package/dist/core/visual-spec-agent-pack.js.map +1 -0
- package/dist/core/visual-spec-prompts.d.ts +32 -0
- package/dist/core/visual-spec-prompts.js +129 -0
- package/dist/core/visual-spec-prompts.js.map +1 -0
- package/dist/core/workspace.d.ts +31 -0
- package/dist/core/workspace.js +97 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/contract.d.ts +43 -0
- package/dist/schemas/contract.js +19 -0
- package/dist/schemas/contract.js.map +1 -0
- package/dist/schemas/events.d.ts +34 -0
- package/dist/schemas/events.js +29 -0
- package/dist/schemas/events.js.map +1 -0
- package/dist/schemas/visual-spec.d.ts +410 -0
- package/dist/schemas/visual-spec.js +201 -0
- package/dist/schemas/visual-spec.js.map +1 -0
- package/docs/agent-workflow.md +45 -0
- package/package.json +64 -0
- package/skills/limner/SKILL.md +51 -0
- package/templates/target/AGENT_GUIDE.md +24 -0
- package/templates/target/contract/acceptance.md +23 -0
- package/templates/target/contract/regions.json +11 -0
- package/templates/target/contract/tokens.json +21 -0
- package/templates/target/reference/index.html +22 -0
- package/templates/target/reference/styles.css +53 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RegionMetrics } from './dom-metrics.js';
|
|
2
|
+
import type { TargetPaths } from './workspace.js';
|
|
3
|
+
export declare function writeImageReferenceReport(input: {
|
|
4
|
+
target: TargetPaths;
|
|
5
|
+
idealPath: string;
|
|
6
|
+
referencePath: string;
|
|
7
|
+
sideBySidePath: string;
|
|
8
|
+
specPaths?: {
|
|
9
|
+
idealSpecPath: string;
|
|
10
|
+
referenceSpecPath: string;
|
|
11
|
+
diffPath: string;
|
|
12
|
+
};
|
|
13
|
+
specHighlights?: string[];
|
|
14
|
+
agentSpec?: {
|
|
15
|
+
promptPath: string;
|
|
16
|
+
codexPromptPath: string;
|
|
17
|
+
claudePromptPath: string;
|
|
18
|
+
factsPath: string;
|
|
19
|
+
examplePath: string;
|
|
20
|
+
schemaPath: string;
|
|
21
|
+
instructionsPath: string;
|
|
22
|
+
responsePath: string;
|
|
23
|
+
status: 'awaiting-agent' | 'validated' | 'invalid';
|
|
24
|
+
validationErrors?: string[];
|
|
25
|
+
};
|
|
26
|
+
}): Promise<string>;
|
|
27
|
+
export declare function writeReferenceAppReport(input: {
|
|
28
|
+
target: TargetPaths;
|
|
29
|
+
referencePath: string;
|
|
30
|
+
appPath: string;
|
|
31
|
+
sideBySidePath: string;
|
|
32
|
+
referenceMetrics: RegionMetrics[];
|
|
33
|
+
appMetrics: RegionMetrics[];
|
|
34
|
+
appUrl: string;
|
|
35
|
+
}): Promise<string>;
|
|
36
|
+
export declare function writeImageAppReport(input: {
|
|
37
|
+
target: TargetPaths;
|
|
38
|
+
idealPath: string;
|
|
39
|
+
appPath: string;
|
|
40
|
+
sideBySidePath: string;
|
|
41
|
+
appUrl: string;
|
|
42
|
+
}): Promise<string>;
|
|
43
|
+
export declare function writeTargetSummary(target: TargetPaths): Promise<string>;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { mkdir, readdir, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export async function writeImageReferenceReport(input) {
|
|
4
|
+
const reportPath = path.join(input.target.reportsDir, 'image-reference.md');
|
|
5
|
+
await mkdir(path.dirname(reportPath), { recursive: true });
|
|
6
|
+
await writeFile(reportPath, `# Image To Reference Report
|
|
7
|
+
|
|
8
|
+
Target: \`${input.target.name}\`
|
|
9
|
+
|
|
10
|
+
## Artifacts
|
|
11
|
+
|
|
12
|
+
- Ideal image: \`${relative(input.target.workspace.root, input.idealPath)}\`
|
|
13
|
+
- Reference screenshot: \`${relative(input.target.workspace.root, input.referencePath)}\`
|
|
14
|
+
- Side-by-side: \`${relative(input.target.workspace.root, input.sideBySidePath)}\`
|
|
15
|
+
${formatSpecArtifacts(input.target.workspace.root, input.specPaths, input.agentSpec)}
|
|
16
|
+
|
|
17
|
+
## Agent Critique
|
|
18
|
+
|
|
19
|
+
- Hierarchy:
|
|
20
|
+
- Density:
|
|
21
|
+
- Typography:
|
|
22
|
+
- Color and tokens:
|
|
23
|
+
- Component anatomy:
|
|
24
|
+
- Intentional deviations:
|
|
25
|
+
|
|
26
|
+
## Next Fix
|
|
27
|
+
|
|
28
|
+
- Record the highest-value scoped fix here.
|
|
29
|
+
${formatAgentSpecStatus(input.agentSpec)}
|
|
30
|
+
${formatSpecHighlights(input.specHighlights)}
|
|
31
|
+
`);
|
|
32
|
+
return reportPath;
|
|
33
|
+
}
|
|
34
|
+
export async function writeReferenceAppReport(input) {
|
|
35
|
+
const reportPath = path.join(input.target.reportsDir, 'reference-app.md');
|
|
36
|
+
await mkdir(path.dirname(reportPath), { recursive: true });
|
|
37
|
+
await writeFile(reportPath, `# Reference To App Report
|
|
38
|
+
|
|
39
|
+
Target: \`${input.target.name}\`
|
|
40
|
+
|
|
41
|
+
App URL: ${input.appUrl}
|
|
42
|
+
|
|
43
|
+
## Artifacts
|
|
44
|
+
|
|
45
|
+
- Reference screenshot: \`${relative(input.target.workspace.root, input.referencePath)}\`
|
|
46
|
+
- App screenshot: \`${relative(input.target.workspace.root, input.appPath)}\`
|
|
47
|
+
- Side-by-side: \`${relative(input.target.workspace.root, input.sideBySidePath)}\`
|
|
48
|
+
|
|
49
|
+
## Region Metrics
|
|
50
|
+
|
|
51
|
+
${formatMetrics(input.referenceMetrics, input.appMetrics)}
|
|
52
|
+
|
|
53
|
+
## Agent Findings
|
|
54
|
+
|
|
55
|
+
- Highest mismatch:
|
|
56
|
+
- Evidence:
|
|
57
|
+
- Suggested app fix:
|
|
58
|
+
- Intentional deviations:
|
|
59
|
+
`);
|
|
60
|
+
return reportPath;
|
|
61
|
+
}
|
|
62
|
+
export async function writeImageAppReport(input) {
|
|
63
|
+
const reportPath = path.join(input.target.reportsDir, 'image-app.md');
|
|
64
|
+
await mkdir(path.dirname(reportPath), { recursive: true });
|
|
65
|
+
await writeFile(reportPath, `# Image To App Report
|
|
66
|
+
|
|
67
|
+
Target: \`${input.target.name}\`
|
|
68
|
+
|
|
69
|
+
App URL: ${input.appUrl}
|
|
70
|
+
|
|
71
|
+
## Artifacts
|
|
72
|
+
|
|
73
|
+
- Ideal image: \`${relative(input.target.workspace.root, input.idealPath)}\`
|
|
74
|
+
- App screenshot: \`${relative(input.target.workspace.root, input.appPath)}\`
|
|
75
|
+
- Side-by-side: \`${relative(input.target.workspace.root, input.sideBySidePath)}\`
|
|
76
|
+
|
|
77
|
+
## Agent Findings
|
|
78
|
+
|
|
79
|
+
- Highest visual mismatch:
|
|
80
|
+
- Evidence:
|
|
81
|
+
- Suggested app fix:
|
|
82
|
+
- Intentional deviations:
|
|
83
|
+
`);
|
|
84
|
+
return reportPath;
|
|
85
|
+
}
|
|
86
|
+
export async function writeTargetSummary(target) {
|
|
87
|
+
const lines = [`# Limner Target Summary`, '', `Target: \`${target.name}\``, ''];
|
|
88
|
+
for (const dir of ['captures/image-reference', 'captures/reference-app', 'captures/image-app', 'reports']) {
|
|
89
|
+
const absoluteDir = path.join(target.root, dir);
|
|
90
|
+
lines.push(`## ${dir}`, '');
|
|
91
|
+
try {
|
|
92
|
+
const entries = await readdir(absoluteDir);
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
const entryPath = path.join(absoluteDir, entry);
|
|
95
|
+
const entryStat = await stat(entryPath);
|
|
96
|
+
lines.push(`- \`${dir}/${entry}\` (${entryStat.size} bytes)`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
lines.push('- No artifacts yet.');
|
|
101
|
+
}
|
|
102
|
+
lines.push('');
|
|
103
|
+
}
|
|
104
|
+
return lines.join('\n');
|
|
105
|
+
}
|
|
106
|
+
function formatMetrics(referenceMetrics, appMetrics) {
|
|
107
|
+
const appById = new Map(appMetrics.map((metric) => [metric.id, metric]));
|
|
108
|
+
const rows = referenceMetrics.map((reference) => {
|
|
109
|
+
const app = appById.get(reference.id);
|
|
110
|
+
const referenceBounds = reference.bounds ? `${reference.bounds.width}x${reference.bounds.height}` : 'missing';
|
|
111
|
+
const appBounds = app?.bounds ? `${app.bounds.width}x${app.bounds.height}` : 'missing';
|
|
112
|
+
return `| ${reference.id} | ${reference.found ? 'yes' : 'no'} | ${app?.found ? 'yes' : 'no'} | ${referenceBounds} | ${appBounds} |`;
|
|
113
|
+
});
|
|
114
|
+
return [
|
|
115
|
+
'| Region | Reference Found | App Found | Reference Bounds | App Bounds |',
|
|
116
|
+
'| --- | --- | --- | --- | --- |',
|
|
117
|
+
...rows,
|
|
118
|
+
].join('\n');
|
|
119
|
+
}
|
|
120
|
+
function relative(root, filePath) {
|
|
121
|
+
return path.relative(root, filePath);
|
|
122
|
+
}
|
|
123
|
+
function formatSpecArtifacts(root, specPaths, agentSpec) {
|
|
124
|
+
const lines = [];
|
|
125
|
+
if (agentSpec) {
|
|
126
|
+
lines.push(`- Shared agent prompt: \`${relative(root, agentSpec.promptPath)}\``);
|
|
127
|
+
lines.push(`- Codex prompt: \`${relative(root, agentSpec.codexPromptPath)}\``);
|
|
128
|
+
lines.push(`- Claude prompt: \`${relative(root, agentSpec.claudePromptPath)}\``);
|
|
129
|
+
lines.push(`- Reference DOM facts: \`${relative(root, agentSpec.factsPath)}\``);
|
|
130
|
+
lines.push(`- Agent response example: \`${relative(root, agentSpec.examplePath)}\``);
|
|
131
|
+
lines.push(`- Agent response schema: \`${relative(root, agentSpec.schemaPath)}\``);
|
|
132
|
+
lines.push(`- Custom instructions: \`${relative(root, agentSpec.instructionsPath)}\``);
|
|
133
|
+
lines.push(`- Agent response target: \`${relative(root, agentSpec.responsePath)}\``);
|
|
134
|
+
}
|
|
135
|
+
if (specPaths) {
|
|
136
|
+
lines.push(`- Ideal visual spec: \`${relative(root, specPaths.idealSpecPath)}\``);
|
|
137
|
+
lines.push(`- Reference visual spec: \`${relative(root, specPaths.referenceSpecPath)}\``);
|
|
138
|
+
lines.push(`- Visual spec diff: \`${relative(root, specPaths.diffPath)}\``);
|
|
139
|
+
}
|
|
140
|
+
return lines.length > 0 ? `\n${lines.join('\n')}` : '';
|
|
141
|
+
}
|
|
142
|
+
function formatAgentSpecStatus(agentSpec) {
|
|
143
|
+
if (!agentSpec) {
|
|
144
|
+
return '';
|
|
145
|
+
}
|
|
146
|
+
if (agentSpec.status === 'awaiting-agent') {
|
|
147
|
+
return `
|
|
148
|
+
|
|
149
|
+
## Visual Spec Status
|
|
150
|
+
|
|
151
|
+
- Agent-required spec pack generated.
|
|
152
|
+
- Next step: have an agent inspect the ideal image and reference artifacts, then write \`captures/image-reference/spec/agent-response.json\`.
|
|
153
|
+
- Rerun \`limn compare image-reference --target <target> --spec\` after the response file exists.`;
|
|
154
|
+
}
|
|
155
|
+
if (agentSpec.status === 'invalid') {
|
|
156
|
+
const errors = agentSpec.validationErrors?.map((error) => `- ${error}`).join('\n') ?? '- Unknown validation error.';
|
|
157
|
+
return `
|
|
158
|
+
|
|
159
|
+
## Visual Spec Status
|
|
160
|
+
|
|
161
|
+
- Agent response was found but did not match the required structure.
|
|
162
|
+
|
|
163
|
+
${errors}`;
|
|
164
|
+
}
|
|
165
|
+
return `
|
|
166
|
+
|
|
167
|
+
## Visual Spec Status
|
|
168
|
+
|
|
169
|
+
- Agent response validated and split into standalone spec artifacts.`;
|
|
170
|
+
}
|
|
171
|
+
function formatSpecHighlights(highlights) {
|
|
172
|
+
if (!highlights || highlights.length === 0) {
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
return `
|
|
176
|
+
|
|
177
|
+
## Visual Spec Highlights
|
|
178
|
+
|
|
179
|
+
${highlights.map((highlight) => `- ${highlight}`).join('\n')}`;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=report-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report-writer.js","sourceRoot":"","sources":["../../src/core/report-writer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { LimnerRunEvent } from '../schemas/events.js';
|
|
2
|
+
import type { WorkspacePaths } from './workspace.js';
|
|
3
|
+
export type RunLogger = {
|
|
4
|
+
runId: string;
|
|
5
|
+
runDir: string;
|
|
6
|
+
manifestPath: string;
|
|
7
|
+
eventsPath: string;
|
|
8
|
+
notesPath: string;
|
|
9
|
+
event(event: Omit<LimnerRunEvent, 'ts'>): Promise<void>;
|
|
10
|
+
artifact(filePath: string, kind: string): Promise<void>;
|
|
11
|
+
complete(status?: 'ok' | 'failed', message?: string): Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
export declare function createRunLogger(workspace: WorkspacePaths, input: {
|
|
14
|
+
command: string;
|
|
15
|
+
target?: string;
|
|
16
|
+
mode?: string;
|
|
17
|
+
viewport?: {
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
};
|
|
21
|
+
}): Promise<RunLogger>;
|
|
22
|
+
export declare function createRunId(date?: Date): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
export async function createRunLogger(workspace, input) {
|
|
4
|
+
const runId = createRunId();
|
|
5
|
+
const runDir = path.join(workspace.runsDir, runId);
|
|
6
|
+
const manifestPath = path.join(runDir, 'manifest.json');
|
|
7
|
+
const eventsPath = path.join(runDir, 'events.jsonl');
|
|
8
|
+
const notesPath = path.join(runDir, 'agent-notes.md');
|
|
9
|
+
const manifest = {
|
|
10
|
+
runId,
|
|
11
|
+
command: input.command,
|
|
12
|
+
target: input.target,
|
|
13
|
+
mode: input.mode,
|
|
14
|
+
startedAt: new Date().toISOString(),
|
|
15
|
+
status: 'running',
|
|
16
|
+
artifacts: [],
|
|
17
|
+
viewport: input.viewport,
|
|
18
|
+
};
|
|
19
|
+
await mkdir(runDir, { recursive: true });
|
|
20
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
|
|
21
|
+
await writeFile(eventsPath, '');
|
|
22
|
+
await writeFile(notesPath, `# Limner Run Notes\n\nRun: ${runId}\n\n`);
|
|
23
|
+
const writeManifest = async (next) => {
|
|
24
|
+
await writeFile(manifestPath, JSON.stringify(next, null, 2) + '\n');
|
|
25
|
+
};
|
|
26
|
+
const readManifest = async () => JSON.parse(await readFile(manifestPath, 'utf8'));
|
|
27
|
+
const logEvent = async (event) => {
|
|
28
|
+
const completeEvent = {
|
|
29
|
+
...event,
|
|
30
|
+
command: event.command ?? input.command,
|
|
31
|
+
target: event.target ?? input.target,
|
|
32
|
+
mode: event.mode ?? input.mode,
|
|
33
|
+
ts: new Date().toISOString(),
|
|
34
|
+
};
|
|
35
|
+
await appendFile(eventsPath, JSON.stringify(completeEvent) + '\n');
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
runId,
|
|
39
|
+
runDir,
|
|
40
|
+
manifestPath,
|
|
41
|
+
eventsPath,
|
|
42
|
+
notesPath,
|
|
43
|
+
event: logEvent,
|
|
44
|
+
async artifact(filePath, kind) {
|
|
45
|
+
const manifest = await readManifest();
|
|
46
|
+
const relativePath = path.relative(workspace.root, filePath);
|
|
47
|
+
if (!manifest.artifacts.includes(relativePath)) {
|
|
48
|
+
manifest.artifacts.push(relativePath);
|
|
49
|
+
await writeManifest(manifest);
|
|
50
|
+
}
|
|
51
|
+
await logEvent({ type: 'artifact.written', path: relativePath, kind });
|
|
52
|
+
},
|
|
53
|
+
async complete(status = 'ok', message) {
|
|
54
|
+
const manifest = await readManifest();
|
|
55
|
+
manifest.status = status;
|
|
56
|
+
manifest.completedAt = new Date().toISOString();
|
|
57
|
+
await writeManifest(manifest);
|
|
58
|
+
await logEvent({ type: 'command.completed', status, message });
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function createRunId(date = new Date()) {
|
|
63
|
+
const stamp = date.toISOString().replace(/[:.]/g, '').replace('Z', 'Z');
|
|
64
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
65
|
+
return `${stamp}-${random}`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=run-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-logger.js","sourceRoot":"","sources":["../../src/core/run-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { mkdir } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import sharp from 'sharp';
|
|
4
|
+
export async function createSideBySideImage(input) {
|
|
5
|
+
const height = input.height ?? 900;
|
|
6
|
+
const gap = input.gap ?? 16;
|
|
7
|
+
const background = input.background ?? '#f8fafc';
|
|
8
|
+
const left = await resizeToHeight(input.leftPath, height);
|
|
9
|
+
const right = await resizeToHeight(input.rightPath, height);
|
|
10
|
+
const width = left.info.width + right.info.width + gap;
|
|
11
|
+
await mkdir(path.dirname(input.outputPath), { recursive: true });
|
|
12
|
+
await sharp({
|
|
13
|
+
create: {
|
|
14
|
+
width,
|
|
15
|
+
height,
|
|
16
|
+
channels: 4,
|
|
17
|
+
background,
|
|
18
|
+
},
|
|
19
|
+
})
|
|
20
|
+
.composite([
|
|
21
|
+
{ input: left.buffer, left: 0, top: 0 },
|
|
22
|
+
{ input: right.buffer, left: left.info.width + gap, top: 0 },
|
|
23
|
+
])
|
|
24
|
+
.png()
|
|
25
|
+
.toFile(input.outputPath);
|
|
26
|
+
return input.outputPath;
|
|
27
|
+
}
|
|
28
|
+
async function resizeToHeight(filePath, height) {
|
|
29
|
+
const image = sharp(filePath).resize({ height, fit: 'inside', withoutEnlargement: false }).png();
|
|
30
|
+
const { data, info } = await image.toBuffer({ resolveWithObject: true });
|
|
31
|
+
return { buffer: data, info: { width: info.width, height: info.height } };
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=side-by-side.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"side-by-side.js","sourceRoot":"","sources":["../../src/core/side-by-side.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,KAO3C;IACC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,IAAI,GAAG,CAAC;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,SAAS,CAAC;IACjD,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC5D,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC;IAEvD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,MAAM,KAAK,CAAC;QACV,MAAM,EAAE;YACN,KAAK;YACL,MAAM;YACN,QAAQ,EAAE,CAAC;YACX,UAAU;SACX;KACF,CAAC;SACC,SAAS,CAAC;QACT,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;QACvC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE;KAC7D,CAAC;SACD,GAAG,EAAE;SACL,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAE5B,OAAO,KAAK,CAAC,UAAU,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,MAAc;IAC5D,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;IACjG,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC;IACzE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAC5E,CAAC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createReadStream } from 'node:fs';
|
|
2
|
+
import { stat } from 'node:fs/promises';
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
export async function startStaticServer(rootDir, preferredPort = 0) {
|
|
6
|
+
const root = path.resolve(rootDir);
|
|
7
|
+
const server = createServer(async (request, response) => {
|
|
8
|
+
try {
|
|
9
|
+
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
|
|
10
|
+
const pathname = decodeURIComponent(requestUrl.pathname === '/' ? '/index.html' : requestUrl.pathname);
|
|
11
|
+
const filePath = path.resolve(root, `.${pathname}`);
|
|
12
|
+
if (!filePath.startsWith(root)) {
|
|
13
|
+
response.writeHead(403);
|
|
14
|
+
response.end('Forbidden');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const fileStat = await stat(filePath);
|
|
18
|
+
if (!fileStat.isFile()) {
|
|
19
|
+
response.writeHead(404);
|
|
20
|
+
response.end('Not found');
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
response.writeHead(200, { 'content-type': contentType(filePath) });
|
|
24
|
+
createReadStream(filePath).pipe(response);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
response.writeHead(404);
|
|
28
|
+
response.end('Not found');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
await new Promise((resolve, reject) => {
|
|
32
|
+
server.once('error', reject);
|
|
33
|
+
server.listen(preferredPort, '127.0.0.1', () => resolve());
|
|
34
|
+
});
|
|
35
|
+
const address = server.address();
|
|
36
|
+
if (typeof address !== 'object' || !address) {
|
|
37
|
+
throw new Error('Unable to determine static server port.');
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
url: `http://127.0.0.1:${address.port}`,
|
|
41
|
+
port: address.port,
|
|
42
|
+
close: () => closeServer(server),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function closeServer(server) {
|
|
46
|
+
return new Promise((resolve, reject) => {
|
|
47
|
+
server.close((error) => {
|
|
48
|
+
if (error)
|
|
49
|
+
reject(error);
|
|
50
|
+
else
|
|
51
|
+
resolve();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function contentType(filePath) {
|
|
56
|
+
const extension = path.extname(filePath).toLowerCase();
|
|
57
|
+
if (extension === '.html')
|
|
58
|
+
return 'text/html; charset=utf-8';
|
|
59
|
+
if (extension === '.css')
|
|
60
|
+
return 'text/css; charset=utf-8';
|
|
61
|
+
if (extension === '.js')
|
|
62
|
+
return 'text/javascript; charset=utf-8';
|
|
63
|
+
if (extension === '.json')
|
|
64
|
+
return 'application/json; charset=utf-8';
|
|
65
|
+
if (extension === '.png')
|
|
66
|
+
return 'image/png';
|
|
67
|
+
if (extension === '.jpg' || extension === '.jpeg')
|
|
68
|
+
return 'image/jpeg';
|
|
69
|
+
if (extension === '.svg')
|
|
70
|
+
return 'image/svg+xml';
|
|
71
|
+
return 'application/octet-stream';
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=static-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.js","sourceRoot":"","sources":["../../src/core/static-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { TargetPaths } from './workspace.js';
|
|
2
|
+
export declare function writeImageReferenceSpecPack(input: {
|
|
3
|
+
target: TargetPaths;
|
|
4
|
+
captureDir: string;
|
|
5
|
+
referenceUrl: string;
|
|
6
|
+
referencePath: string;
|
|
7
|
+
sideBySidePath: string;
|
|
8
|
+
headed?: boolean;
|
|
9
|
+
instructionsPath?: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
promptPath: string;
|
|
12
|
+
codexPromptPath: string;
|
|
13
|
+
claudePromptPath: string;
|
|
14
|
+
factsPath: string;
|
|
15
|
+
examplePath: string;
|
|
16
|
+
schemaPath: string;
|
|
17
|
+
instructionsPath: string;
|
|
18
|
+
responsePath: string;
|
|
19
|
+
status: 'awaiting-agent' | 'validated' | 'invalid';
|
|
20
|
+
validationErrors?: string[];
|
|
21
|
+
specPaths?: {
|
|
22
|
+
idealSpecPath: string;
|
|
23
|
+
referenceSpecPath: string;
|
|
24
|
+
diffPath: string;
|
|
25
|
+
};
|
|
26
|
+
diffHighlights?: string[];
|
|
27
|
+
}>;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { visualSpecBundleSchema, createVisualSpecBundleExample, createVisualSpecJsonSchema, } from '../schemas/visual-spec.js';
|
|
4
|
+
import { buildClaudePrompt, buildCodexPrompt, buildSharedPrompt } from './visual-spec-prompts.js';
|
|
5
|
+
import { writeIfMissing } from './workspace.js';
|
|
6
|
+
import { collectReferenceDomFacts } from './reference-dom-facts.js';
|
|
7
|
+
export async function writeImageReferenceSpecPack(input) {
|
|
8
|
+
const specDir = path.join(input.captureDir, 'spec');
|
|
9
|
+
const promptPath = path.join(specDir, 'agent-prompt.md');
|
|
10
|
+
const codexPromptPath = path.join(specDir, 'agent-prompt.codex.md');
|
|
11
|
+
const claudePromptPath = path.join(specDir, 'agent-prompt.claude.md');
|
|
12
|
+
const factsPath = path.join(specDir, 'reference-dom-facts.json');
|
|
13
|
+
const examplePath = path.join(specDir, 'agent-response.example.json');
|
|
14
|
+
const schemaPath = path.join(specDir, 'agent-response.schema.json');
|
|
15
|
+
const responsePath = path.join(specDir, 'agent-response.json');
|
|
16
|
+
const instructionsPath = input.instructionsPath ?? path.join(input.target.contractDir, 'visual-spec-instructions.md');
|
|
17
|
+
const facts = await collectReferenceDomFacts(input.referenceUrl, input.headed);
|
|
18
|
+
await writeIfMissing(instructionsPath, defaultInstructions());
|
|
19
|
+
const customInstructions = await readFile(instructionsPath, 'utf8');
|
|
20
|
+
await mkdir(specDir, { recursive: true });
|
|
21
|
+
await Promise.all([
|
|
22
|
+
writeJson(factsPath, facts),
|
|
23
|
+
writeJson(schemaPath, createVisualSpecJsonSchema()),
|
|
24
|
+
writeIfMissing(examplePath, JSON.stringify(createVisualSpecBundleExample(), null, 2) + '\n'),
|
|
25
|
+
writeFile(promptPath, buildSharedPrompt({
|
|
26
|
+
target: input.target,
|
|
27
|
+
referencePath: input.referencePath,
|
|
28
|
+
sideBySidePath: input.sideBySidePath,
|
|
29
|
+
factsPath,
|
|
30
|
+
examplePath,
|
|
31
|
+
schemaPath,
|
|
32
|
+
responsePath,
|
|
33
|
+
instructionsPath,
|
|
34
|
+
customInstructions,
|
|
35
|
+
})),
|
|
36
|
+
writeFile(codexPromptPath, buildCodexPrompt({
|
|
37
|
+
target: input.target,
|
|
38
|
+
promptPath,
|
|
39
|
+
referencePath: input.referencePath,
|
|
40
|
+
sideBySidePath: input.sideBySidePath,
|
|
41
|
+
factsPath,
|
|
42
|
+
schemaPath,
|
|
43
|
+
responsePath,
|
|
44
|
+
instructionsPath,
|
|
45
|
+
})),
|
|
46
|
+
writeFile(claudePromptPath, buildClaudePrompt({
|
|
47
|
+
target: input.target,
|
|
48
|
+
promptPath,
|
|
49
|
+
referencePath: input.referencePath,
|
|
50
|
+
sideBySidePath: input.sideBySidePath,
|
|
51
|
+
factsPath,
|
|
52
|
+
schemaPath,
|
|
53
|
+
responsePath,
|
|
54
|
+
instructionsPath,
|
|
55
|
+
})),
|
|
56
|
+
]);
|
|
57
|
+
const response = await readBundle(responsePath);
|
|
58
|
+
if (response.status === 'missing') {
|
|
59
|
+
return { promptPath, codexPromptPath, claudePromptPath, factsPath, examplePath, schemaPath, instructionsPath, responsePath, status: 'awaiting-agent' };
|
|
60
|
+
}
|
|
61
|
+
if (response.status === 'invalid-json') {
|
|
62
|
+
return {
|
|
63
|
+
promptPath,
|
|
64
|
+
codexPromptPath,
|
|
65
|
+
claudePromptPath,
|
|
66
|
+
factsPath,
|
|
67
|
+
examplePath,
|
|
68
|
+
schemaPath,
|
|
69
|
+
instructionsPath,
|
|
70
|
+
responsePath,
|
|
71
|
+
status: 'invalid',
|
|
72
|
+
validationErrors: ['agent-response.json: Invalid JSON.'],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const parsed = visualSpecBundleSchema.safeParse(response.value);
|
|
76
|
+
if (!parsed.success) {
|
|
77
|
+
return {
|
|
78
|
+
promptPath,
|
|
79
|
+
codexPromptPath,
|
|
80
|
+
claudePromptPath,
|
|
81
|
+
factsPath,
|
|
82
|
+
examplePath,
|
|
83
|
+
schemaPath,
|
|
84
|
+
instructionsPath,
|
|
85
|
+
responsePath,
|
|
86
|
+
status: 'invalid',
|
|
87
|
+
validationErrors: parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const specPaths = {
|
|
91
|
+
idealSpecPath: path.join(input.captureDir, 'ideal-visual-spec.json'),
|
|
92
|
+
referenceSpecPath: path.join(input.captureDir, 'reference-visual-spec.json'),
|
|
93
|
+
diffPath: path.join(input.captureDir, 'visual-spec-diff.json'),
|
|
94
|
+
};
|
|
95
|
+
await Promise.all([
|
|
96
|
+
writeJson(specPaths.idealSpecPath, parsed.data.idealSpec),
|
|
97
|
+
writeJson(specPaths.referenceSpecPath, parsed.data.referenceSpec),
|
|
98
|
+
writeJson(specPaths.diffPath, parsed.data.diff),
|
|
99
|
+
]);
|
|
100
|
+
return {
|
|
101
|
+
promptPath,
|
|
102
|
+
codexPromptPath,
|
|
103
|
+
claudePromptPath,
|
|
104
|
+
factsPath,
|
|
105
|
+
examplePath,
|
|
106
|
+
schemaPath,
|
|
107
|
+
instructionsPath,
|
|
108
|
+
responsePath,
|
|
109
|
+
status: 'validated',
|
|
110
|
+
specPaths,
|
|
111
|
+
diffHighlights: parsed.data.diff.mismatches.slice(0, 4).map((finding) => `${finding.area}: ${finding.impact}`),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
async function readBundle(filePath) {
|
|
115
|
+
try {
|
|
116
|
+
return { status: 'ok', value: JSON.parse(await readFile(filePath, 'utf8')) };
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
try {
|
|
120
|
+
await readFile(filePath, 'utf8');
|
|
121
|
+
return { status: 'invalid-json' };
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return { status: 'missing' };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function writeJson(filePath, value) {
|
|
129
|
+
await writeFile(filePath, JSON.stringify(value, null, 2) + '\n');
|
|
130
|
+
}
|
|
131
|
+
function defaultInstructions() {
|
|
132
|
+
return `# Visual Spec Instructions
|
|
133
|
+
|
|
134
|
+
Add target-specific guidance for Codex or Claude here.
|
|
135
|
+
|
|
136
|
+
Examples:
|
|
137
|
+
|
|
138
|
+
- Preserve the product stance around side-by-side screenshots as visual ground truth.
|
|
139
|
+
- Treat copy, disabled state semantics, and mobile behavior as first-class evidence.
|
|
140
|
+
- Call out uncertainty instead of inventing exact measurements from the images.
|
|
141
|
+
`;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=visual-spec-agent-pack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-spec-agent-pack.js","sourceRoot":"","sources":["../../src/core/visual-spec-agent-pack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,0BAA0B,GAE3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElG,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAAC,KAQjD;IAcC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;IACzD,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;IACpE,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,0BAA0B,CAAC,CAAC;IACjE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,6BAA6B,CAAC,CAAC;IACtE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,4BAA4B,CAAC,CAAC;IACpE,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,6BAA6B,CAAC,CAAC;IACtH,MAAM,KAAK,GAAG,MAAM,wBAAwB,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/E,MAAM,cAAc,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC9D,MAAM,kBAAkB,GAAG,MAAM,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IAEpE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC;QAC3B,SAAS,CAAC,UAAU,EAAE,0BAA0B,EAAE,CAAC;QACnD,cAAc,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,6BAA6B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;QAC5F,SAAS,CAAC,UAAU,EAAE,iBAAiB,CAAC;YACtC,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,SAAS;YACT,WAAW;YACX,UAAU;YACV,YAAY;YACZ,gBAAgB;YAChB,kBAAkB;SACnB,CAAC,CAAC;QACH,SAAS,CAAC,eAAe,EAAE,gBAAgB,CAAC;YAC1C,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU;YACV,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,SAAS;YACT,UAAU;YACV,YAAY;YACZ,gBAAgB;SACjB,CAAC,CAAC;QACH,SAAS,CAAC,gBAAgB,EAAE,iBAAiB,CAAC;YAC5C,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU;YACV,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,cAAc,EAAE,KAAK,CAAC,cAAc;YACpC,SAAS;YACT,UAAU;YACV,YAAY;YACZ,gBAAgB;SACjB,CAAC,CAAC;KACJ,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,YAAY,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IACzJ,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;QACvC,OAAO;YACL,UAAU;YACV,eAAe;YACf,gBAAgB;YAChB,SAAS;YACT,WAAW;YACX,UAAU;YACV,gBAAgB;YAChB,YAAY;YACZ,MAAM,EAAE,SAAS;YACjB,gBAAgB,EAAE,CAAC,oCAAoC,CAAC;SACzD,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,OAAO;YACL,UAAU;YACV,eAAe;YACf,gBAAgB;YAChB,SAAS;YACT,WAAW;YACX,UAAU;YACV,gBAAgB;YAChB,YAAY;YACZ,MAAM,EAAE,SAAS;YACjB,gBAAgB,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;SAClG,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG;QAChB,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,wBAAwB,CAAC;QACpE,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,4BAA4B,CAAC;QAC5E,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,uBAAuB,CAAC;KAC/D,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,SAAS,CAAC,SAAS,CAAC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QACzD,SAAS,CAAC,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;QACjE,SAAS,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;KAChD,CAAC,CAAC;IACH,OAAO;QACL,UAAU;QACV,eAAe;QACf,gBAAgB;QAChB,SAAS;QACT,WAAW;QACX,UAAU;QACV,gBAAgB;QAChB,YAAY;QACZ,MAAM,EAAE,WAAW;QACnB,SAAS;QACT,cAAc,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;KAC/G,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,QAAgB;IAKxC,IAAI,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAqB,EAAE,CAAC;IACnG,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;YACjC,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,KAAc;IACvD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,mBAAmB;IAC1B,OAAO;;;;;;;;;CASR,CAAC;AACF,CAAC"}
|