Skip to content

Commit 87b2364

Browse files
committed
feat(ui): improve CSV/schema/results layout, logo dark mode, upload area, and transitions
- Panels now always visible and sized (2/3 CSV, 1/3 results) - Results panel always visible, never scrolls page - Logo is white in dark mode using CSS filter - Upload area compact when CSV loaded - Validation results persist when toggling schema panel - Smoother, much slower schema panel transition - Icon sizes unified in headers - Various minor UI/UX fixes
1 parent 7fbcfd6 commit 87b2364

File tree

17 files changed

+3826
-542
lines changed

17 files changed

+3826
-542
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,6 @@ dist/
115115
.pnp.*
116116

117117
# Local Development/Testing Folders
118-
LocalCsvDataValidator/
118+
LocalCsvDataValidator/
119+
120+
csv
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import { NextResponse } from 'next/server';
2+
// import { JsonSchemaStaticDocs } from 'json-schema-static-docs'; // Original import
3+
import * as JsonSchemaStaticDocsLib from 'json-schema-static-docs'; // Use namespace import
4+
import fs from 'fs/promises';
5+
import path from 'path';
6+
import os from 'os';
7+
import { createRequire } from 'module'; // Import createRequire
8+
9+
// Helper to resolve package paths
10+
const require = createRequire(import.meta.url);
11+
12+
// Recursive copy function (fs.cp might not be available everywhere or handle nested dirs reliably)
13+
async function copyDirRecursive(src: string, dest: string) {
14+
await fs.mkdir(dest, { recursive: true });
15+
const entries = await fs.readdir(src, { withFileTypes: true });
16+
for (let entry of entries) {
17+
const srcPath = path.join(src, entry.name);
18+
const destPath = path.join(dest, entry.name);
19+
if (entry.isDirectory()) {
20+
await copyDirRecursive(srcPath, destPath);
21+
} else {
22+
await fs.copyFile(srcPath, destPath);
23+
}
24+
}
25+
}
26+
27+
// Helper to create a temporary file
28+
async function writeTempSchemaFile(schema: any): Promise<string> {
29+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'schema-'));
30+
const tempFilePath = path.join(tempDir, 'schema.json');
31+
await fs.writeFile(tempFilePath, JSON.stringify(schema, null, 2));
32+
console.log(`API: Wrote temp schema to ${tempFilePath}`);
33+
return tempFilePath;
34+
}
35+
36+
// Helper to read the generated markdown file
37+
async function readGeneratedMarkdown(outputPath: string, schemaFileName: string): Promise<string> {
38+
const mdFileName = schemaFileName.replace(/\.json$/, '.md'); // Assuming input was .json
39+
const mdFilePath = path.join(outputPath, mdFileName);
40+
console.log(`API: Reading generated markdown from ${mdFilePath}`);
41+
try {
42+
const markdownContent = await fs.readFile(mdFilePath, 'utf-8');
43+
return markdownContent;
44+
} catch (error) {
45+
console.error(`API: Error reading generated markdown file ${mdFilePath}:`, error);
46+
throw new Error(`Could not read generated markdown file.`);
47+
}
48+
}
49+
50+
// Helper to clean up temporary directories
51+
async function cleanupTempDirs(pathsToClean: string[]) {
52+
for (const dirPath of pathsToClean) {
53+
try {
54+
if (dirPath) { // Check if path is defined
55+
console.log(`API: Cleaning up temp dir: ${dirPath}`);
56+
await fs.rm(dirPath, { recursive: true, force: true });
57+
}
58+
} catch (error) {
59+
console.error(`API: Error during temp cleanup of ${dirPath}:`, error);
60+
// Don't throw error here, cleanup failure shouldn't fail the request
61+
}
62+
}
63+
}
64+
65+
export async function POST(request: Request) {
66+
let tempInputPath: string | undefined;
67+
let tempOutputPath: string | undefined;
68+
let tempTemplatePath: string | undefined; // Path for copied templates
69+
const cleanupPaths: string[] = []; // Keep track of all paths to clean
70+
71+
try {
72+
const { schema } = await request.json();
73+
74+
if (!schema) {
75+
return NextResponse.json({ error: 'Missing schema in request body' }, { status: 400 });
76+
}
77+
78+
// 1. Write the received schema to a temporary file
79+
tempInputPath = await writeTempSchemaFile(schema);
80+
const tempInputDirectory = path.dirname(tempInputPath);
81+
cleanupPaths.push(tempInputDirectory); // Add for cleanup
82+
console.log(`API: Created temp input dir ${tempInputDirectory}`);
83+
84+
// 2. Create a temporary output directory
85+
tempOutputPath = await fs.mkdtemp(path.join(os.tmpdir(), 'schema-docs-'));
86+
cleanupPaths.push(tempOutputPath); // Add for cleanup
87+
console.log(`API: Created temp output dir ${tempOutputPath}`);
88+
89+
// 3. Resolve source template path and create/copy to temp template path
90+
// Use the path to the copied templates in the public directory
91+
const projectRoot = process.cwd();
92+
const sourceTemplatePath = path.join(projectRoot, 'public', '__schema_templates');
93+
94+
// Check if source template path exists before copying
95+
try {
96+
await fs.access(sourceTemplatePath);
97+
console.log(`API: Source template path found: ${sourceTemplatePath}`);
98+
} catch (accessError) {
99+
console.error(`API: Source template path NOT FOUND: ${sourceTemplatePath}`, accessError);
100+
throw new Error(`Could not find source template directory at ${sourceTemplatePath}. Check path resolution.`);
101+
}
102+
103+
tempTemplatePath = await fs.mkdtemp(path.join(os.tmpdir(), 'schema-templates-'));
104+
cleanupPaths.push(tempTemplatePath); // Add for cleanup
105+
console.log(`API: Copying templates from ${sourceTemplatePath} to ${tempTemplatePath}`);
106+
await copyDirRecursive(sourceTemplatePath, tempTemplatePath);
107+
console.log(`API: Finished copying templates.`);
108+
109+
// 4. Instantiate and run json-schema-static-docs
110+
const Constructor = JsonSchemaStaticDocsLib.default || JsonSchemaStaticDocsLib;
111+
112+
const generator = new Constructor({
113+
inputPath: tempInputDirectory,
114+
outputPath: tempOutputPath,
115+
templatePath: tempTemplatePath, // <-- Use the path with copied templates
116+
createIndex: false, // We only want the single doc
117+
addFrontMatter: false, // No frontmatter needed
118+
// Add any other options needed for styling/generation
119+
// ajvOptions: { allowUnionTypes: true }, // Example ajv option
120+
// enableMetaEnum: true, // Example meta enum option
121+
});
122+
123+
console.log(`API: Running generator. Input: ${tempInputDirectory}, Output: ${tempOutputPath}, Templates: ${tempTemplatePath}`);
124+
await generator.generate();
125+
console.log("API: Generator finished.");
126+
127+
// 5. Read the generated Markdown file
128+
const markdown = await readGeneratedMarkdown(tempOutputPath, path.basename(tempInputPath));
129+
130+
// 6. Clean up temporary files/directories (do this *before* returning)
131+
await cleanupTempDirs(cleanupPaths);
132+
133+
// 7. Return the Markdown content
134+
return NextResponse.json({ markdown });
135+
136+
} catch (error: any) {
137+
console.error("API Error generating schema doc:", error);
138+
139+
// Ensure cleanup happens even on error
140+
await cleanupTempDirs(cleanupPaths);
141+
142+
return NextResponse.json(
143+
{ error: 'Failed to generate schema documentation', details: error.message },
144+
{ status: 500 }
145+
);
146+
}
147+
}

components/code-editor.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ interface CodeEditorProps {
2020
readOnly?: boolean
2121
errorDecorations?: EditorErrorDecoration[] // Changed from errorLines: number[]
2222
highlightedLine?: number // New prop for line highlighting
23+
scrollToLine?: number // New prop for auto-scroll
2324
}
2425

2526
export default function CodeEditor({
@@ -31,6 +32,7 @@ export default function CodeEditor({
3132
readOnly = false,
3233
errorDecorations = [], // Changed from errorLines
3334
highlightedLine,
35+
scrollToLine,
3436
}: CodeEditorProps) {
3537
const { theme } = useTheme()
3638
const editorRef = useRef<monaco.editor.IStandaloneCodeEditor | null>(null)
@@ -131,6 +133,13 @@ export default function CodeEditor({
131133
}
132134
}, [highlightedLine]);
133135

136+
useEffect(() => {
137+
if (!editorRef.current || !monacoRef.current) return;
138+
if (scrollToLine) {
139+
editorRef.current.revealLineInCenter(scrollToLine);
140+
}
141+
}, [scrollToLine]);
142+
134143
const editorTheme = theme === 'dark' ? 'vs-dark' : 'vs'
135144

136145
return (

0 commit comments

Comments
 (0)