This commit is contained in:
Josh Patra
2025-04-11 20:16:37 -04:00
parent 82204d6586
commit b97bbb5ed9
4 changed files with 58 additions and 59 deletions

View File

@@ -1,18 +1,33 @@
import { defineConfig } from "eslint/config"; import { defineConfig } from "eslint/config";
import svelte from "eslint-plugin-svelte"; import svelte from "eslint-plugin-svelte";
import svelteParser from "svelte-eslint-parser"; import svelteParser from "svelte-eslint-parser";
import { dirname } from "path";
import { fileURLToPath } from "url";
// Define __dirname for ES modules
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export default defineConfig([ export default defineConfig([
{ {
files: ["**/*.svelte"], files: ["**/*.svelte"],
languageOptions: { languageOptions: {
parser: svelteParser, parser: svelteParser,
parserOptions: {
ecmaVersion: 2021,
sourceType: "module",
tsconfigRootDir: __dirname,
project: ["./tsconfig.json"],
},
}, },
plugins: { plugins: {
svelte, svelte,
}, },
settings: {
"svelte3/typescript": require("typescript"),
},
rules: { rules: {
// Add or override rules here // Add or override Svelte-specific rules here
}, },
}, },
{ {

14
package-lock.json generated
View File

@@ -19,7 +19,7 @@
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"svelte-eslint-parser": "^1.1.2", "svelte-eslint-parser": "^1.1.2",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
"typescript": "^5.0.0", "typescript": "^5.8.3",
"vite": "^6.2.5" "vite": "^6.2.5"
} }
}, },
@@ -1043,9 +1043,9 @@
} }
}, },
"node_modules/@sveltejs/kit": { "node_modules/@sveltejs/kit": {
"version": "2.20.4", "version": "2.20.5",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.4.tgz", "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.5.tgz",
"integrity": "sha512-B3Y1mb1Qjt57zXLVch5tfqsK/ebHe6uYTcFSnGFNwRpId3+fplLgQK6Z2zhDVBezSsPuhDq6Pry+9PA88ocN6Q==", "integrity": "sha512-zT/97KvVUo19jEGZa972ls7KICjPCB53j54TVxnEFT5VEwL16G+YFqRVwJbfxh7AmS7/Ptr1rKF7Qt4FBMDNlw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -3186,9 +3186,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.2.5", "version": "6.2.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz",
"integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -23,7 +23,7 @@
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"svelte-eslint-parser": "^1.1.2", "svelte-eslint-parser": "^1.1.2",
"tailwindcss": "^4.0.0", "tailwindcss": "^4.0.0",
"typescript": "^5.0.0", "typescript": "^5.8.3",
"vite": "^6.2.5" "vite": "^6.2.5"
} }
} }

View File

@@ -1,4 +1,4 @@
<script> <script lang="ts">
import { onMount } from "svelte"; import { onMount } from "svelte";
// Portfolio Data // Portfolio Data
@@ -58,7 +58,7 @@
}, },
{ {
name: "BlueBubbles Contribution", name: "BlueBubbles Contribution",
link: "https://github.com/BlueBubblesApp/BlueBubbles", link: "https://github.com/BlueBubblesApp/bluebubbles-app",
description: description:
"Contributed to BlueBubbles, an open-source project bringing iMessage-like functionality to non-Apple platforms. Focused on UI development and infrastructure improvements.", "Contributed to BlueBubbles, an open-source project bringing iMessage-like functionality to non-Apple platforms. Focused on UI development and infrastructure improvements.",
techStack: ["Android", "Dart/Flutter", "MongoDB"], techStack: ["Android", "Dart/Flutter", "MongoDB"],
@@ -182,13 +182,8 @@
const currentYear = new Date().getFullYear(); const currentYear = new Date().getFullYear();
// Active section for navigation // Active section for navigation
/** @type {string} */ let activeSection: string = "home";
let activeSection = "home"; function navigateTo(section: string) {
/**
* @param {string} section
*/
function navigateTo(section) {
activeSection = section; activeSection = section;
const el = document.getElementById(section); const el = document.getElementById(section);
if (el) { if (el) {
@@ -197,30 +192,32 @@
} }
// Terminal // Terminal
/** @type {Array<{command: string, output: string}>} */ let terminalHistory: Array<{ command: string; output: string }> = [
let terminalHistory = [
{ command: "whoami", output: profile.name }, { command: "whoami", output: profile.name },
{ {
command: "ls -la", command: "ls -la",
output: "projects education achievements experience skills contact", output: "projects education achievements experience skills contact",
}, },
]; ];
/** @type {string} */ let currentCommand: string = "";
let currentCommand = ""; let terminalContainer: HTMLDivElement | null = null;
let terminalInput: HTMLInputElement | null = null;
/** @type {HTMLDivElement | null} */
let terminalContainer = null;
/** @type {HTMLInputElement | null} */
let terminalInput = null;
// Variables for typewriter effect // Variables for typewriter effect
let typedName = ""; let typedName: string = "";
let typedRole = ""; let typedRole: string = "";
let bioVisible = false; // controls when the bio fades in let bioVisible: boolean = false;
// Typewriter function for the text /**
function typeWriter(text, setter, delay) { * A typewriter function that types `text` at the given `delay` and calls
return new Promise((resolve) => { * `setter` with the current substring. Returns a Promise that resolves when finished.
*/
function typeWriter(
text: string,
setter: (val: string) => void,
delay: number,
): Promise<void> {
return new Promise<void>((resolve: () => void) => {
let i = 0; let i = 0;
const interval = setInterval(() => { const interval = setInterval(() => {
setter(text.slice(0, i + 1)); setter(text.slice(0, i + 1));
@@ -237,22 +234,23 @@
if (terminalInput) { if (terminalInput) {
terminalInput.focus(); terminalInput.focus();
} }
// Faster typing speed: delay set to 50ms // Type the name (slower speed for dramatic effect)
await typeWriter( await typeWriter(
profile.name, profile.name,
(val) => { (val: string) => {
typedName = val; typedName = val;
}, },
150, 150,
); );
// Type the role
await typeWriter( await typeWriter(
profile.role, profile.role,
(val) => { (val: string) => {
typedRole = val; typedRole = val;
}, },
50, 50,
); );
// Once typing is done, fade in the bio // Once typing is done, show the bio
bioVisible = true; bioVisible = true;
}); });
@@ -311,7 +309,6 @@
terminalHistory = [...terminalHistory, { command: currentCommand, output }]; terminalHistory = [...terminalHistory, { command: currentCommand, output }];
currentCommand = ""; currentCommand = "";
// Scroll the terminal to bottom after command execution
setTimeout(() => { setTimeout(() => {
if (terminalContainer) { if (terminalContainer) {
terminalContainer.scrollTop = terminalContainer.scrollHeight; terminalContainer.scrollTop = terminalContainer.scrollHeight;
@@ -319,20 +316,13 @@
}, 0); }, 0);
} }
/** function handleKeyPress(e: KeyboardEvent) {
* @param {KeyboardEvent} e
*/
function handleKeyPress(e) {
if (e.key === "Enter") { if (e.key === "Enter") {
executeCommand(); executeCommand();
} }
} }
/** function bindTerminalContainer(node: HTMLDivElement) {
* @param {HTMLDivElement} node
* @returns {{ destroy: () => void }}
*/
function bindTerminalContainer(node) {
terminalContainer = node; terminalContainer = node;
return { return {
destroy() { destroy() {
@@ -341,18 +331,14 @@
}; };
} }
// Store user input // Store user input for contact form
let userName = ""; let userName: string = "";
let userEmail = ""; let userEmail: string = "";
let userMessage = ""; let userMessage: string = "";
/** Opens user's default mail client with a prefilled message. */
function sendMail() { function sendMail() {
const subject = `Portfolio Contact from ${userName}`; const subject = `Portfolio Contact from ${userName}`;
const body = `Name: ${userName}, Email: ${userEmail}\n\n${userMessage}`; const body = `Name: ${userName}, Email: ${userEmail}\n\n${userMessage}`;
const mailtoUrl = `mailto:joshpatra12@gmail.com?subject=${encodeURIComponent( const mailtoUrl = `mailto:joshpatra12@gmail.com?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(body)}`;
subject,
)}&body=${encodeURIComponent(body)}`;
window.location.href = mailtoUrl; window.location.href = mailtoUrl;
userName = ""; userName = "";
userEmail = ""; userEmail = "";
@@ -450,7 +436,6 @@
<div <div
class="bg-black border border-gray-700 rounded-md p-3 font-mono text-sm" class="bg-black border border-gray-700 rounded-md p-3 font-mono text-sm"
> >
<!-- Terminal container with bind: directive -->
<div class="h-48 overflow-y-auto" use:bindTerminalContainer> <div class="h-48 overflow-y-auto" use:bindTerminalContainer>
{#each terminalHistory as entry} {#each terminalHistory as entry}
<div class="mb-2"> <div class="mb-2">
@@ -499,11 +484,10 @@
<h2 class="text-xl md:text-2xl text-gray-400 mb-6"> <h2 class="text-xl md:text-2xl text-gray-400 mb-6">
{typedRole} {typedRole}
</h2> </h2>
<!-- Fade in the bio after typewriting is complete --> <!-- Static bio (no fade in effect) -->
<p class="text-gray-300 leading-relaxed mb-8"> <p class="text-gray-300 leading-relaxed mb-8">
{profile.bio} {profile.bio}
</p> </p>
<div class="flex flex-wrap gap-4"> <div class="flex flex-wrap gap-4">
<a <a
href="/Josh_Patra_Resume.pdf" href="/Josh_Patra_Resume.pdf"