diff --git a/README.md b/README.md
index 9aa9119..7e45a7b 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,163 @@
-# Personal Site
\ No newline at end of file
+# Personal Website
+
+My personal website built with Astro and Preact!
+
+## Features
+
+- **Resume Management**
+- **Blog Posts**
+- **Projects**
+- **Talks & Presentations**
+- **Terminal View**
+
+## Resume Configuration
+
+The resume system supports multiple sections that can be enabled, disabled, and customized.
+
+### Available Resume Sections
+
+| Section | Required Fields |
+|---------|-----------------|
+| **basics** | `name`, `email`, `profiles` |
+| **summary** | `content` |
+| **experience** | `company`, `position`, `location`, `date`, `description` |
+| **education** | `institution`, `degree`, `field`, `date` |
+| **skills** | `name`, `level` (1-5) |
+| **volunteer** | `organization`, `position`, `date` |
+| **awards** | `title`, `organization`, `date` |
+| **profiles** | `network`, `username`, `url` |
+
+### Section Configuration
+
+Each section can be configured in `src/config/data.ts`:
+
+```typescript
+export const resumeConfig: ResumeConfig = {
+ tomlFile: "/files/resume.toml",
+ sections: {
+ summary: {
+ title: "Professional Summary",
+ enabled: true,
+ },
+ experience: {
+ title: "Work Experience",
+ enabled: true,
+ },
+ awards: {
+ title: "Awards & Recognition",
+ enabled: true,
+ },
+ // ... other sections
+ },
+};
+```
+
+### Resume Data Format (TOML)
+
+Create a `resume.toml` file in the `public/files/` directory:
+
+```toml
+[basics]
+name = "Your Name"
+email = "your.email@example.com"
+
+[[basics.profiles]]
+network = "GitHub"
+username = "yourusername"
+url = "https://github.com/yourusername"
+
+[[basics.profiles]]
+network = "LinkedIn"
+username = "yourname"
+url = "https://linkedin.com/in/yourname"
+
+[summary]
+content = "Your professional summary here..."
+
+[[experience]]
+company = "Company Name"
+position = "Job Title"
+location = "City, State"
+date = "2020 - Present"
+description = [
+ "Achievement or responsibility 1",
+ "Achievement or responsibility 2"
+]
+url = "https://company.com"
+
+[[education]]
+institution = "University Name"
+degree = "Bachelor of Science"
+field = "Computer Science"
+date = "2016 - 2020"
+details = [
+ "Relevant coursework or achievements"
+]
+
+[[skills]]
+name = "JavaScript"
+level = 4
+
+[[skills]]
+name = "Python"
+level = 5
+
+[[volunteer]]
+organization = "Organization Name"
+position = "Volunteer Position"
+date = "2019 - Present"
+
+[[awards]]
+title = "Award Title"
+organization = "Awarding Organization"
+date = "2023"
+description = "Brief description of the award"
+```
+
+### Section Field Details
+
+#### Skills Section
+- `level`: Integer from 1-5 representing proficiency level
+- Displays as progress bars with visual indicators
+
+#### Experience Section
+- `description`: Array of strings for bullet points
+- `url`: Optional company website link
+
+#### Education Section
+- `details`: Optional array of additional information (coursework, achievements, etc.)
+
+#### Awards Section
+- `description`: Optional additional details about the award
+
+#### Profiles Section
+- `network`: Used for icon selection (GitHub, LinkedIn, etc.)
+- Icons automatically selected based on network name
+
+## Usage
+
+1. **Configure Resume**: Edit `src/config/data.ts` to enable/disable sections
+2. **Add Resume Data**: Create `public/files/resume.toml` with your information
+3. **View Resume**: Navigate to `/resume` on your site
+4. **Generate PDF**: Click "Generate PDF Resume" button for downloadable PDF
+
+## Development
+
+```bash
+# Install dependencies
+npm install
+
+# Start development server
+npm run dev
+
+# Build for production
+npm run build
+```
+
+## Resume PDF Generation
+
+The system automatically generates PDFs using Puppeteer with:
+- Optimized layout for A4 paper
+- Print-friendly styling
+- Consistent formatting across sections
+- Proper page breaks and margins
diff --git a/src/config/data.ts b/src/config/data.ts
index 7be6c76..893f0f7 100644
--- a/src/config/data.ts
+++ b/src/config/data.ts
@@ -62,7 +62,7 @@ export const homepageSections: HomepageSections = {
// Resume Configuration
export const resumeConfig: ResumeConfig = {
- jsonFile: "/files/resume.toml",
+ tomlFile: "/files/resume.toml",
pdfFile: {
path: "/files/Atridad_Lahiji_Resume.pdf",
filename: "Atridad_Lahiji_Resume.pdf",
@@ -76,6 +76,7 @@ export const resumeConfig: ResumeConfig = {
"skills",
"volunteer",
"profiles",
+ "awards",
],
summary: {
title: "Summary",
@@ -101,6 +102,10 @@ export const resumeConfig: ResumeConfig = {
title: "Professional Profiles",
enabled: true,
},
+ awards: {
+ title: "Awards & Recognition",
+ enabled: true,
+ },
},
};
@@ -303,7 +308,7 @@ export const navigationItems: NavigationItem[] = [
path: "/resume",
tooltip: "Resume",
icon: BriefcaseBusiness,
- enabled: !!(resumeConfig.jsonFile && resumeConfig.jsonFile.trim()),
+ enabled: !!(resumeConfig.tomlFile && resumeConfig.tomlFile.trim()),
},
{
id: "projects",
diff --git a/src/pages/api/resume.json.ts b/src/pages/api/resume.json.ts
new file mode 100644
index 0000000..0602326
--- /dev/null
+++ b/src/pages/api/resume.json.ts
@@ -0,0 +1,37 @@
+import type { APIRoute } from "astro";
+import * as TOML from "@iarna/toml";
+import { siteConfig } from "../../config/data";
+
+export const GET: APIRoute = async ({ request }) => {
+ try {
+ // Check if resume TOML file is configured
+ if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
+ return new Response("Resume not configured", { status: 404 });
+ }
+
+ const url = new URL(request.url);
+ const baseUrl = `${url.protocol}//${url.host}`;
+
+ // Fetch the TOML file from the public directory
+ const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed to fetch resume TOML: ${response.status} ${response.statusText}`,
+ );
+ }
+
+ const tomlContent = await response.text();
+ const resumeData = TOML.parse(tomlContent);
+
+ return new Response(JSON.stringify(resumeData), {
+ headers: {
+ "Content-Type": "application/json",
+ "Cache-Control": "public, max-age=300", // Cache for 5 minutes
+ },
+ });
+ } catch (error) {
+ console.error("Error parsing resume TOML:", error);
+ return new Response("Error parsing resume data", { status: 500 });
+ }
+};
diff --git a/src/pages/api/resume/pdf.ts b/src/pages/api/resume/pdf.ts
index c8c1dd7..07c89d4 100644
--- a/src/pages/api/resume/pdf.ts
+++ b/src/pages/api/resume/pdf.ts
@@ -41,6 +41,12 @@ interface ResumeData {
position: string;
date: string;
}[];
+ awards: {
+ title: string;
+ organization: string;
+ date: string;
+ description?: string;
+ }[];
}
const generateResumeHTML = (data: ResumeData): string => {
@@ -128,6 +134,23 @@ const generateResumeHTML = (data: ResumeData): string => {
})
.join("") || "";
+ const awardsHTML =
+ data.awards
+ ?.map((award) => {
+ return `
+
+
${award.title}
+
+ ${award.organization}
+ •
+ ${award.date}
+
+ ${award.description ? `
${award.description}
` : ""}
+
+ `;
+ })
+ .join("") || "";
+
return `
@@ -217,6 +240,23 @@ const generateResumeHTML = (data: ResumeData): string => {
`
: ""
}
+
+ ${
+ data.awards &&
+ data.awards.length > 0 &&
+ resumeConfig.sections.awards?.enabled
+ ? `
+
+
+ ${resumeConfig.sections.awards.title || "Awards & Recognition"}
+
+
+ ${awardsHTML}
+
+
+ `
+ : ""
+ }
@@ -263,14 +303,14 @@ const generateResumeHTML = (data: ResumeData): string => {
export const GET: APIRoute = async ({ request }) => {
try {
- if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) {
+ if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
return new Response("Resume not configured", { status: 404 });
}
const url = new URL(request.url);
const baseUrl = `${url.protocol}//${url.host}`;
- const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`);
+ const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
if (!response.ok) {
throw new Error(
@@ -279,7 +319,9 @@ export const GET: APIRoute = async ({ request }) => {
}
const tomlContent = await response.text();
- const resumeData: ResumeData = TOML.parse(tomlContent) as unknown as ResumeData;
+ const resumeData: ResumeData = TOML.parse(
+ tomlContent,
+ ) as unknown as ResumeData;
const htmlContent = generateResumeHTML(resumeData);
diff --git a/src/pages/resume.astro b/src/pages/resume.astro
index 81f1783..48cafab 100644
--- a/src/pages/resume.astro
+++ b/src/pages/resume.astro
@@ -45,13 +45,19 @@ interface ResumeData {
position: string;
date: string;
}[];
+ awards: {
+ title: string;
+ organization: string;
+ date: string;
+ description?: string;
+ }[];
}
let resumeData: ResumeData | undefined = undefined;
let fetchError: string | null = null;
-// Check if resume JSON file is configured before attempting to fetch
-if (!siteConfig.resume.jsonFile || !siteConfig.resume.jsonFile.trim()) {
+// Check if resume TOML file is configured before attempting to fetch
+if (!siteConfig.resume.tomlFile || !siteConfig.resume.tomlFile.trim()) {
return Astro.redirect("/");
}
@@ -60,7 +66,7 @@ try {
const baseUrl = Astro.url.origin;
// Fetch the TOML file from the public directory
- const response = await fetch(`${baseUrl}${siteConfig.resume.jsonFile}`);
+ const response = await fetch(`${baseUrl}${siteConfig.resume.tomlFile}`);
if (!response.ok) {
throw new Error(
@@ -331,5 +337,42 @@ if (!data) {
)
}
+
+ {
+ data.awards &&
+ data.awards.length > 0 &&
+ resumeConfig.sections.awards?.enabled && (
+
+
+
+ {resumeConfig.sections.awards.title ||
+ "Awards & Recognition"}
+
+
+ {data.awards.map((award) => (
+
+
+ {award.title}
+
+
+
+ {award.organization}
+
+
+ {award.date}
+
+
+ {award.description && (
+
+ {award.description}
+
+ )}
+
+ ))}
+
+
+
+ )
+ }
diff --git a/src/types/index.ts b/src/types/index.ts
index 77fc679..aa3b2b7 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -51,7 +51,7 @@ export interface NavigationItem {
}
export interface ResumeConfig {
- jsonFile: string;
+ tomlFile: string;
pdfFile: {
path: string;
filename: string;
@@ -83,6 +83,10 @@ export interface ResumeConfig {
title?: string;
enabled?: boolean;
};
+ awards?: {
+ title?: string;
+ enabled?: boolean;
+ };
};
}
diff --git a/src/utils/terminal/fs.ts b/src/utils/terminal/fs.ts
index aa6d411..1315a0e 100644
--- a/src/utils/terminal/fs.ts
+++ b/src/utils/terminal/fs.ts
@@ -1,18 +1,27 @@
-import type { FileSystemNode, ResumeData } from './types';
-import { talks, projects, socialLinks, techLinks } from '../../config/data';
+import type { FileSystemNode, ResumeData } from "./types";
+import { talks, projects, socialLinks, techLinks } from "../../config/data";
-export async function buildFileSystem(): Promise<{ [key: string]: FileSystemNode }> {
+export async function buildFileSystem(): Promise<{
+ [key: string]: FileSystemNode;
+}> {
try {
- const response = await fetch('/files/resume.json');
- const resumeData: ResumeData = await response.json();
+ const response = await fetch("/api/resume.json");
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed to fetch resume data: ${response.status} ${response.statusText}`,
+ );
+ }
+
+ const resumeData: any = await response.json();
// Fetch blog posts
- const postsResponse = await fetch('/api/posts.json');
+ const postsResponse = await fetch("/api/posts.json");
let postsData = [];
try {
postsData = await postsResponse.json();
} catch (error) {
- console.log('Could not fetch posts data:', error);
+ console.log("Could not fetch posts data:", error);
}
// Build resume files from rxresume json
@@ -25,117 +34,140 @@ export async function buildFileSystem(): Promise<{ [key: string]: FileSystemNode
const contactContent = buildContactContent(resumeData);
const fs: { [key: string]: FileSystemNode } = {
- '/': {
- type: 'directory',
- name: '/',
+ "/": {
+ type: "directory",
+ name: "/",
children: {
- 'about.txt': {
- type: 'file',
- name: 'about.txt',
- content: `${resumeData.basics.name}\nResearcher, Full-Stack Developer, and IT Professional.\n\nExplore the directories:\n- /resume - Professional experience and skills\n- /posts - Blog posts and articles\n- /talks - Conference presentations\n- /projects - Personal and professional projects\n- /social - Social media and contact links\n- /tech - Technologies and tools I use\n\nType "ls" to see all available files and directories.`
+ "about.txt": {
+ type: "file",
+ name: "about.txt",
+ content: `${resumeData.basics.name}\nResearcher, Full-Stack Developer, and IT Professional.\n\nExplore the directories:\n- /resume - Professional experience and skills\n- /posts - Blog posts and articles\n- /talks - Conference presentations\n- /projects - Personal and professional projects\n- /social - Social media and contact links\n- /tech - Technologies and tools I use\n\nType "ls" to see all available files and directories.`,
},
- 'resume': {
- type: 'directory',
- name: 'resume',
- children: resumeFiles
+ resume: {
+ type: "directory",
+ name: "resume",
+ children: resumeFiles,
},
- 'posts': {
- type: 'directory',
- name: 'posts',
- children: postsFiles
+ posts: {
+ type: "directory",
+ name: "posts",
+ children: postsFiles,
},
- 'talks': {
- type: 'directory',
- name: 'talks',
- children: talksFiles
+ talks: {
+ type: "directory",
+ name: "talks",
+ children: talksFiles,
},
- 'projects': {
- type: 'directory',
- name: 'projects',
- children: projectsFiles
+ projects: {
+ type: "directory",
+ name: "projects",
+ children: projectsFiles,
},
- 'social': {
- type: 'directory',
- name: 'social',
- children: socialFiles
+ social: {
+ type: "directory",
+ name: "social",
+ children: socialFiles,
},
- 'tech': {
- type: 'directory',
- name: 'tech',
- children: techFiles
+ tech: {
+ type: "directory",
+ name: "tech",
+ children: techFiles,
},
- 'contact.txt': {
- type: 'file',
- name: 'contact.txt',
- content: contactContent
- }
- }
- }
+ "contact.txt": {
+ type: "file",
+ name: "contact.txt",
+ content: contactContent,
+ },
+ },
+ },
};
return fs;
} catch (error) {
- console.error('Error loading resume data:', error);
+ console.error("Error loading resume data:", error);
return buildFallbackFileSystem();
}
}
-function buildResumeFiles(resumeData: ResumeData): { [key: string]: FileSystemNode } {
+function buildResumeFiles(resumeData: any): { [key: string]: FileSystemNode } {
const resumeFiles: { [key: string]: FileSystemNode } = {};
- if (resumeData.sections.summary) {
- resumeFiles['summary.txt'] = {
- type: 'file',
- name: 'summary.txt',
- content: resumeData.sections.summary.content.replace(/<[^>]*>/g, '')
- };
- }
+ try {
+ if (resumeData.summary) {
+ resumeFiles["summary.txt"] = {
+ type: "file",
+ name: "summary.txt",
+ content: resumeData.summary.content,
+ };
+ }
- if (resumeData.sections.skills?.items) {
- const skillsContent = resumeData.sections.skills.items
- .map(skill => `${skill.name} (Level: ${skill.level}/5)`)
- .join('\n');
- resumeFiles['skills.txt'] = {
- type: 'file',
- name: 'skills.txt',
- content: skillsContent
- };
- }
+ if (resumeData.skills && Array.isArray(resumeData.skills)) {
+ const skillsContent = resumeData.skills
+ .map((skill: any) => `${skill.name} (Level: ${skill.level}/5)`)
+ .join("\n");
+ resumeFiles["skills.txt"] = {
+ type: "file",
+ name: "skills.txt",
+ content: skillsContent,
+ };
+ }
- if (resumeData.sections.experience?.items) {
- const experienceContent = resumeData.sections.experience.items
- .map(exp => {
- const summary = exp.summary.replace(/<[^>]*>/g, '').replace(/ /g, ' ');
- return `${exp.position} at ${exp.company}\n${exp.date} | ${exp.location}\n${summary}\n${exp.url?.href ? `URL: ${exp.url.href}` : ''}\n`;
- })
- .join('\n---\n\n');
- resumeFiles['experience.txt'] = {
- type: 'file',
- name: 'experience.txt',
- content: experienceContent
- };
- }
+ if (resumeData.experience && Array.isArray(resumeData.experience)) {
+ const experienceContent = resumeData.experience
+ .map((exp: any) => {
+ const description = Array.isArray(exp.description)
+ ? exp.description.join("\n• ")
+ : "";
+ return `${exp.position} at ${exp.company}\n${exp.date} | ${exp.location}\n• ${description}\n${exp.url ? `URL: ${exp.url}` : ""}\n`;
+ })
+ .join("\n---\n\n");
+ resumeFiles["experience.txt"] = {
+ type: "file",
+ name: "experience.txt",
+ content: experienceContent,
+ };
+ }
- if (resumeData.sections.education?.items) {
- const educationContent = resumeData.sections.education.items
- .map(edu => `${edu.institution}\n${edu.studyType} - ${edu.area}\n${edu.date}\n${edu.summary ? edu.summary.replace(/<[^>]*>/g, '') : ''}`)
- .join('\n\n---\n\n');
- resumeFiles['education.txt'] = {
- type: 'file',
- name: 'education.txt',
- content: educationContent
- };
- }
+ if (resumeData.education && Array.isArray(resumeData.education)) {
+ const educationContent = resumeData.education
+ .map(
+ (edu: any) =>
+ `${edu.institution}\n${edu.degree} - ${edu.field}\n${edu.date}\n${edu.details && Array.isArray(edu.details) ? edu.details.join("\n• ") : ""}`,
+ )
+ .join("\n\n---\n\n");
+ resumeFiles["education.txt"] = {
+ type: "file",
+ name: "education.txt",
+ content: educationContent,
+ };
+ }
- if (resumeData.sections.volunteer?.items) {
- const volunteerContent = resumeData.sections.volunteer.items
- .map(vol => `${vol.organization}\n${vol.position}\n${vol.date}`)
- .join('\n\n---\n\n');
- resumeFiles['volunteer.txt'] = {
- type: 'file',
- name: 'volunteer.txt',
- content: volunteerContent
- };
+ if (resumeData.volunteer && Array.isArray(resumeData.volunteer)) {
+ const volunteerContent = resumeData.volunteer
+ .map((vol: any) => `${vol.organization}\n${vol.position}\n${vol.date}`)
+ .join("\n\n---\n\n");
+ resumeFiles["volunteer.txt"] = {
+ type: "file",
+ name: "volunteer.txt",
+ content: volunteerContent,
+ };
+ }
+
+ if (resumeData.awards && Array.isArray(resumeData.awards)) {
+ const awardsContent = resumeData.awards
+ .map(
+ (award: any) =>
+ `${award.title}\n${award.organization}\n${award.date}\n${award.description || ""}`,
+ )
+ .join("\n\n---\n\n");
+ resumeFiles["awards.txt"] = {
+ type: "file",
+ name: "awards.txt",
+ content: awardsContent,
+ };
+ }
+ } catch (error) {
+ console.error("Error building resume files:", error);
}
return resumeFiles;
@@ -150,15 +182,15 @@ function buildPostsFiles(postsData: any[]): { [key: string]: FileSystemNode } {
title: "${post.title}"
description: "${post.description}"
pubDate: "${post.pubDate}"
-tags: [${post.tags.map((tag: string) => `"${tag}"`).join(', ')}]
+tags: [${post.tags.map((tag: string) => `"${tag}"`).join(", ")}]
---
${post.content}`;
postsFiles[fileName] = {
- type: 'file',
+ type: "file",
name: fileName,
- content
+ content,
};
});
@@ -168,18 +200,17 @@ ${post.content}`;
function buildTalksFiles(): { [key: string]: FileSystemNode } {
const talksFiles: { [key: string]: FileSystemNode } = {};
- talks.forEach(talk => {
+ talks.forEach((talk) => {
const fileName = `${talk.id}.txt`;
let content = `${talk.name}
${talk.description}
-${talk.venue || ''}
-${talk.date || ''}
+${talk.date || ""}
${talk.link}`;
talksFiles[fileName] = {
- type: 'file',
+ type: "file",
name: fileName,
- content
+ content,
};
});
@@ -189,18 +220,18 @@ ${talk.link}`;
function buildProjectsFiles(): { [key: string]: FileSystemNode } {
const projectsFiles: { [key: string]: FileSystemNode } = {};
- projects.forEach(project => {
+ projects.forEach((project) => {
const fileName = `${project.id}.txt`;
let content = `${project.name}
${project.description}
-${project.status || ''}
-${project.technologies ? project.technologies.join(', ') : ''}
+${project.status || ""}
+${project.technologies ? project.technologies.join(", ") : ""}
${project.link}`;
projectsFiles[fileName] = {
- type: 'file',
+ type: "file",
name: fileName,
- content
+ content,
};
});
@@ -210,15 +241,15 @@ ${project.link}`;
function buildSocialFiles(): { [key: string]: FileSystemNode } {
const socialFiles: { [key: string]: FileSystemNode } = {};
- socialLinks.forEach(link => {
+ socialLinks.forEach((link) => {
const fileName = `${link.id}.txt`;
let content = `${link.name}
${link.url}`;
socialFiles[fileName] = {
- type: 'file',
+ type: "file",
name: fileName,
- content
+ content,
};
});
@@ -228,76 +259,126 @@ ${link.url}`;
function buildTechFiles(): { [key: string]: FileSystemNode } {
const techFiles: { [key: string]: FileSystemNode } = {};
- techLinks.forEach(link => {
+ techLinks.forEach((link) => {
const fileName = `${link.id}.txt`;
let content = `${link.name}
${link.url}`;
techFiles[fileName] = {
- type: 'file',
+ type: "file",
name: fileName,
- content
+ content,
};
});
return techFiles;
}
-function buildContactContent(resumeData: ResumeData): string {
- return [
- `Email: ${resumeData.basics.email}`,
- '',
- 'Social Profiles:',
- ...resumeData.sections.profiles.items.map(profile =>
- `${profile.network}: ${profile.url.href}`
- )
- ].join('\n');
+function buildContactContent(resumeData: any): string {
+ try {
+ const basics = resumeData.basics || {};
+ const email = basics.email || "Not provided";
+ const profiles = basics.profiles || [];
+
+ return [
+ `Email: ${email}`,
+ "",
+ "Social Profiles:",
+ ...profiles.map((profile: any) => `${profile.network}: ${profile.url}`),
+ ].join("\n");
+ } catch (error) {
+ console.error("Error building contact content:", error);
+ return "Contact information unavailable";
+ }
}
function buildFallbackFileSystem(): { [key: string]: FileSystemNode } {
+ const talksFiles = buildTalksFiles();
+ const projectsFiles = buildProjectsFiles();
+ const socialFiles = buildSocialFiles();
+ const techFiles = buildTechFiles();
+
return {
- '/': {
- type: 'directory',
- name: '/',
+ "/": {
+ type: "directory",
+ name: "/",
children: {
- 'about.txt': {
- type: 'file',
- name: 'about.txt',
- content: 'Atridad Lahiji\nResearcher, Full-Stack Developer, and IT Professional.\n\nError loading detailed information. Please check the website directly.'
- }
- }
- }
+ "about.txt": {
+ type: "file",
+ name: "about.txt",
+ content:
+ "Atridad Lahiji\nResearcher, Full-Stack Developer, and IT Professional.\n\nError loading resume data. Basic navigation still available.\n\nExplore the directories:\n- /talks - Conference presentations\n- /projects - Personal and professional projects\n- /social - Social media and contact links\n- /tech - Technologies and tools I use\n\nType 'ls' to see all available files and directories.",
+ },
+ talks: {
+ type: "directory",
+ name: "talks",
+ children: talksFiles,
+ },
+ projects: {
+ type: "directory",
+ name: "projects",
+ children: projectsFiles,
+ },
+ social: {
+ type: "directory",
+ name: "social",
+ children: socialFiles,
+ },
+ tech: {
+ type: "directory",
+ name: "tech",
+ children: techFiles,
+ },
+ "help.txt": {
+ type: "file",
+ name: "help.txt",
+ content:
+ "Available commands:\n- ls - list files\n- cd - change directory\n- cat - view file contents\n- pwd - show current directory\n- clear - clear terminal\n- help - show this help\n- train - run the train animation",
+ },
+ },
+ },
};
}
-export function getCurrentDirectory(fileSystem: { [key: string]: FileSystemNode }, currentPath: string): FileSystemNode {
- const pathParts = currentPath.split('/').filter((part: string) => part !== '');
- let current = fileSystem['/'];
-
+export function getCurrentDirectory(
+ fileSystem: { [key: string]: FileSystemNode },
+ currentPath: string,
+): FileSystemNode {
+ const pathParts = currentPath
+ .split("/")
+ .filter((part: string) => part !== "");
+ let current = fileSystem["/"];
+
for (const part of pathParts) {
- if (current?.children && current.children[part] && current.children[part].type === 'directory') {
+ if (
+ current?.children &&
+ current.children[part] &&
+ current.children[part].type === "directory"
+ ) {
current = current.children[part];
}
}
-
+
return current;
}
export function resolvePath(currentPath: string, path: string): string {
- if (path.startsWith('/')) {
+ if (path.startsWith("/")) {
return path;
}
-
- const currentParts = currentPath.split('/').filter((part: string) => part !== '');
- const pathParts = path.split('/');
-
+
+ const currentParts = currentPath
+ .split("/")
+ .filter((part: string) => part !== "");
+ const pathParts = path.split("/");
+
for (const part of pathParts) {
- if (part === '..') {
+ if (part === "..") {
currentParts.pop();
- } else if (part !== '.' && part !== '') {
+ } else if (part !== "." && part !== "") {
currentParts.push(part);
}
}
-
- return '/' + currentParts.join('/');
-}
\ No newline at end of file
+
+ return "/" + currentParts.join("/");
+}