Skip to content

Commit 0ed59ca

Browse files
committed
Improve repo tree structure for clarity and readability
1 parent 036da76 commit 0ed59ca

File tree

4 files changed

+267
-7
lines changed

4 files changed

+267
-7
lines changed

src/app/generator/customization-options.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,15 @@ const CustomizationOptions: React.FC<CustomizationOptionsProps> = ({ options, on
4949
onCheckedChange={(checked: boolean) => onChange({ showLineNumbers: checked })}
5050
/>
5151
</div>
52+
{/* Show Descriptions */}
53+
<div className="flex items-center justify-between">
54+
<Label htmlFor="show-descriptions">Show Descriptions</Label>
55+
<Switch
56+
id="show-descriptions"
57+
checked={options.showDescriptions}
58+
onCheckedChange={(checked: boolean) => onChange({ showDescriptions: checked })}
59+
/>
60+
</div>
5261
{/* Root Directory */}
5362
<div className="flex items-center justify-between">
5463
<Label htmlFor="show-root-directory">Root Directory</Label>

src/app/generator/repo-tree-generator.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const DEFAULT_CUSTOMIZATION_OPTIONS: TreeCustomizationOptions = {
6767
asciiStyle: "basic",
6868
useIcons: false,
6969
showLineNumbers: false,
70+
showDescriptions: false,
7071
showRootDirectory: false,
7172
showTrailingSlash: false,
7273
}

src/lib/repo-tree-utils.ts

Lines changed: 256 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,233 @@ export const generateStructure = (tree: TreeItem[]): DirectoryMap => {
189189
return structureMap
190190
}
191191

192-
export const buildStructureString = (map: DirectoryMap, prefix = "", options: TreeCustomizationOptions): string => {
192+
// Get description for files and directories
193+
const getDescription = (name: string, isDirectory: boolean, path?: string): string => {
194+
if (isDirectory) {
195+
return getDirectoryDescription(name, path || "")
196+
} else {
197+
return getFileDescription(name)
198+
}
199+
}
200+
201+
// Directory descriptions based on common patterns
202+
const getDirectoryDescription = (dirName: string, fullPath: string): string => {
203+
const lowerName = dirName.toLowerCase()
204+
const lowerPath = fullPath.toLowerCase()
205+
206+
// Common directory patterns
207+
const directoryDescriptions: { [key: string]: string } = {
208+
// Build/Config directories
209+
".github": "GitHub workflows and templates",
210+
".vscode": "VS Code workspace settings",
211+
".next": "Next.js build output",
212+
"dist": "Distribution/build files",
213+
"build": "Compiled application files",
214+
"out": "Output directory",
215+
"public": "Static assets and public files",
216+
"static": "Static assets",
217+
"assets": "Project assets and resources",
218+
219+
// Source directories
220+
"src": "Source code",
221+
"app": "Application pages and routing",
222+
"pages": "Application pages",
223+
"components": "React components",
224+
"lib": "Utility functions and libraries",
225+
"utils": "Utility functions",
226+
"helpers": "Helper functions",
227+
"hooks": "Custom React hooks",
228+
"context": "React context providers",
229+
"store": "State management",
230+
"styles": "CSS and styling files",
231+
"css": "Stylesheets",
232+
"scss": "Sass stylesheets",
233+
"images": "Image assets",
234+
"fonts": "Font files",
235+
"icons": "Icon assets",
236+
237+
// API and backend
238+
"api": "API routes and endpoints",
239+
"server": "Server-side code",
240+
"backend": "Backend application code",
241+
"routes": "Application routes",
242+
"controllers": "Route controllers",
243+
"models": "Data models",
244+
"middleware": "Express middleware",
245+
"database": "Database files and migrations",
246+
"migrations": "Database migrations",
247+
"seeds": "Database seed files",
248+
249+
// Testing
250+
"test": "Test files",
251+
"tests": "Test files",
252+
"__tests__": "Jest test files",
253+
"spec": "Test specifications",
254+
"e2e": "End-to-end tests",
255+
"cypress": "Cypress test files",
256+
257+
// Documentation
258+
"docs": "Documentation files",
259+
"documentation": "Project documentation",
260+
261+
// Configuration
262+
"config": "Configuration files",
263+
"configs": "Configuration files",
264+
265+
// Dependencies
266+
"node_modules": "NPM dependencies",
267+
"vendor": "Third-party libraries",
268+
269+
// UI specific
270+
"ui": "UI components",
271+
"layout": "Layout components",
272+
"layouts": "Page layouts",
273+
"templates": "Component templates",
274+
275+
// Types
276+
"types": "TypeScript type definitions",
277+
"@types": "TypeScript declarations",
278+
279+
// Workflows
280+
"workflows": "CI/CD workflow files"
281+
}
282+
283+
// Check exact matches first
284+
if (directoryDescriptions[lowerName]) {
285+
return directoryDescriptions[lowerName]
286+
}
287+
288+
// Check for patterns in the full path
289+
if (lowerPath.includes("workflow") || lowerPath.includes(".github")) {
290+
return "CI/CD workflows"
291+
}
292+
if (lowerPath.includes("component")) {
293+
return "Component files"
294+
}
295+
if (lowerPath.includes("page")) {
296+
return "Page components"
297+
}
298+
if (lowerPath.includes("api")) {
299+
return "API endpoints"
300+
}
301+
302+
return "Directory"
303+
}
304+
305+
// File descriptions based on extensions and names
306+
const getFileDescription = (fileName: string): string => {
307+
const lowerName = fileName.toLowerCase()
308+
const extension = fileName.split(".").pop()?.toLowerCase() || ""
309+
310+
// Specific file names
311+
const specificFiles: { [key: string]: string } = {
312+
"readme.md": "Project documentation",
313+
"license": "Project license",
314+
"license.txt": "Project license",
315+
"license.md": "Project license",
316+
"changelog.md": "Version history",
317+
"contributing.md": "Contribution guidelines",
318+
"package.json": "NPM package configuration",
319+
"package-lock.json": "Dependency lock file",
320+
"yarn.lock": "Yarn dependency lock file",
321+
"tsconfig.json": "TypeScript configuration",
322+
"next.config.js": "Next.js configuration",
323+
"next.config.ts": "Next.js configuration",
324+
"tailwind.config.js": "Tailwind CSS configuration",
325+
"tailwind.config.ts": "Tailwind CSS configuration",
326+
"postcss.config.js": "PostCSS configuration",
327+
"eslint.config.js": "ESLint configuration",
328+
".eslintrc.json": "ESLint rules",
329+
".gitignore": "Git ignore rules",
330+
".env": "Environment variables",
331+
".env.example": "Environment variables template",
332+
".env.local": "Local environment variables",
333+
"vercel.json": "Vercel deployment config",
334+
"dockerfile": "Docker container config",
335+
"docker-compose.yml": "Docker compose config",
336+
"makefile": "Build automation",
337+
"components.json": "Component configuration",
338+
"prettier.config.js": "Code formatting rules"
339+
}
340+
341+
// Check specific file names first
342+
if (specificFiles[lowerName]) {
343+
return specificFiles[lowerName]
344+
}
345+
346+
// Extension-based descriptions
347+
const extensionDescriptions: { [key: string]: string } = {
348+
// Web technologies
349+
"js": "JavaScript file",
350+
"jsx": "React component",
351+
"ts": "TypeScript file",
352+
"tsx": "React TypeScript component",
353+
"html": "HTML page",
354+
"css": "Stylesheet",
355+
"scss": "Sass stylesheet",
356+
"sass": "Sass stylesheet",
357+
"less": "Less stylesheet",
358+
359+
// Configuration
360+
"json": "JSON configuration",
361+
"yaml": "YAML configuration",
362+
"yml": "YAML configuration",
363+
"toml": "TOML configuration",
364+
"xml": "XML file",
365+
366+
// Documentation
367+
"md": "Markdown documentation",
368+
"txt": "Text file",
369+
"pdf": "PDF document",
370+
371+
// Images
372+
"png": "PNG image",
373+
"jpg": "JPEG image",
374+
"jpeg": "JPEG image",
375+
"gif": "GIF image",
376+
"svg": "SVG vector image",
377+
"webp": "WebP image",
378+
"ico": "Icon file",
379+
380+
// Video/Audio
381+
"mp4": "MP4 video",
382+
"webm": "WebM video",
383+
"avi": "AVI video",
384+
"mov": "QuickTime video",
385+
"mp3": "MP3 audio",
386+
"wav": "WAV audio",
387+
388+
// Other programming languages
389+
"py": "Python script",
390+
"java": "Java source file",
391+
"cpp": "C++ source file",
392+
"c": "C source file",
393+
"php": "PHP script",
394+
"rb": "Ruby script",
395+
"go": "Go source file",
396+
"rs": "Rust source file",
397+
"swift": "Swift source file",
398+
"kt": "Kotlin source file",
399+
400+
// Database
401+
"sql": "SQL script",
402+
"db": "Database file",
403+
404+
// Archives
405+
"zip": "ZIP archive",
406+
"tar": "TAR archive",
407+
"gz": "Gzip archive",
408+
409+
// Others
410+
"sh": "Shell script",
411+
"bat": "Batch script",
412+
"ps1": "PowerShell script"
413+
}
414+
415+
return extensionDescriptions[extension] || "File"
416+
}
417+
418+
export const buildStructureString = (map: DirectoryMap, prefix = "", options: TreeCustomizationOptions, currentPath = ""): string => {
193419
let result = ""
194420

195421
// Add root directory indicator if enabled
@@ -198,20 +424,43 @@ export const buildStructureString = (map: DirectoryMap, prefix = "", options: Tr
198424
}
199425

200426
const entries = Array.from(map.entries())
201-
const lastIndex = entries.length - 1
427+
428+
// Sort entries: directories first, then files
429+
// Within each group, sort alphabetically
430+
const sortedEntries = entries.sort(([keyA, valueA], [keyB, valueB]) => {
431+
const isDirectoryA = valueA instanceof Map
432+
const isDirectoryB = valueB instanceof Map
433+
434+
// If one is directory and other is file, directory comes first
435+
if (isDirectoryA && !isDirectoryB) return -1
436+
if (!isDirectoryA && isDirectoryB) return 1
437+
438+
// If both are same type (both directories or both files), sort alphabetically
439+
return keyA.localeCompare(keyB)
440+
})
441+
442+
const lastIndex = sortedEntries.length - 1
202443

203-
entries.forEach(([key, value], index) => {
444+
sortedEntries.forEach(([key, value], index) => {
204445
const isLast = index === lastIndex
205446
const connector = getConnector(isLast, options.asciiStyle)
206447
const childPrefix = getChildPrefix(isLast, options.asciiStyle)
207448
const icon = options.useIcons ? getIcon(value instanceof Map) : ""
449+
const isDirectory = value instanceof Map
450+
451+
// Build current file/directory path
452+
const itemPath = currentPath ? `${currentPath}/${key}` : key
208453

209454
// Add trailing slash for directories if enabled
210-
const displayName = (value instanceof Map && options.showTrailingSlash) ? `${key}/` : key
455+
const displayName = (isDirectory && options.showTrailingSlash) ? `${key}/` : key
456+
457+
// Get description for this item
458+
const description = getDescription(key, isDirectory, itemPath)
459+
const descriptionText = options.showDescriptions && description ? ` # ${description}` : ""
211460

212-
result += `${prefix}${connector}${icon}${displayName}\n`
213-
if (value instanceof Map) {
214-
result += buildStructureString(value, `${prefix}${childPrefix}`, options)
461+
result += `${prefix}${connector}${icon}${displayName}${descriptionText}\n`
462+
if (isDirectory) {
463+
result += buildStructureString(value, `${prefix}${childPrefix}`, options, itemPath)
215464
}
216465
})
217466

src/types/tree-customization.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export interface TreeCustomizationOptions {
22
asciiStyle: 'basic' | 'detailed' | 'minimal';
33
useIcons: boolean;
44
showLineNumbers: boolean;
5+
showDescriptions: boolean
56
showRootDirectory: boolean;
67
showTrailingSlash: boolean;
78
}

0 commit comments

Comments
 (0)