2.0.0 - Overhaul
All checks were successful
Docker Deploy / build-and-push (push) Successful in 4m13s

This commit is contained in:
2025-08-18 11:44:55 -06:00
parent bd71602d95
commit ec58d44b9d
20 changed files with 273 additions and 1345 deletions

View File

@@ -10,20 +10,20 @@ const { title, description: blurb, pubDate } = post.data;
const { slug } = post;
---
<div class="card bg-accent w-full max-w-sm shrink shadow-md">
<div class="card bg-accent text-accent-content w-full max-w-sm shrink shadow-md">
<div class="card-body break-words">
<h2
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words text-base-100"
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words"
>
{title}
</h2>
<p class="text-center break-words my-4 text-base-100">
<p class="text-center break-words my-4">
{blurb || "No description available."}
</p>
<div
class="flex flex-wrap items-center justify-center text-base-100 opacity-75 gap-2 text-sm mb-4"
class="flex flex-wrap items-center justify-center opacity-75 gap-2 text-sm mb-4"
>
<Icon name="mdi:clock" class="text-xl" />
<span>
@@ -41,7 +41,7 @@ const { slug } = post;
post.data.tags && post.data.tags.length > 0 && (
<div class="flex gap-2 flex-wrap mb-4 justify-center">
{post.data.tags.map((tag: string) => (
<div class="badge badge-primary font-bold text-base-100">
<div class="badge badge-primary font-bold">
<Icon name="mdi:tag" class="text-lg" />
{tag}
</div>
@@ -53,7 +53,7 @@ const { slug } = post;
<div class="card-actions justify-end">
<a
href={`/post/${slug}`}
class="btn btn-circle text-accent"
class="btn btn-circle"
aria-label={`Read more about ${title}`}
>
<Icon name="mdi:arrow-right" class="text-lg" />

View File

@@ -9,15 +9,15 @@ interface Props {
const { project } = Astro.props;
---
<div class="card bg-accent w-full max-w-sm shrink shadow-md">
<div class="card bg-accent text-accent-content w-full max-w-sm shrink shadow-md">
<div class="card-body break-words">
<h2
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words text-base-100"
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words"
>
{project.name}
</h2>
<p class="text-center break-words text-base-100">
<p class="text-center break-words">
{project.description}
</p>
@@ -26,7 +26,7 @@ const { project } = Astro.props;
href={project.link}
target="_blank"
rel="noopener noreferrer"
class="btn btn-circle text-accent"
class="btn btn-circle"
aria-label={`Visit ${project.name}`}
>
<Icon name="mdi:link" class="text-lg" />

View File

@@ -52,7 +52,7 @@ export default function ResumeDownloadButton({
class={`btn bg-primary font-bold rounded-full inline-flex items-center gap-2 text-sm sm:text-base ${
isLoading
? "text-primary border-2 border-primary"
: "text-base-100"
: ""
}`}
>
{isLoading ? (

View File

@@ -162,7 +162,7 @@ export default function ResumeSettingsModal({
{/* Floating Settings Button */}
<button
onClick={openModal}
class={`fixed top-4 right-4 z-20 bg-secondary text-base-100 hover:bg-primary btn btn-circle ${className}`}
class={`fixed top-4 right-4 z-20 bg-secondary hover:bg-primary btn btn-circle ${className}`}
aria-label="Resume Settings"
>
<Settings class="text-lg" />
@@ -175,7 +175,7 @@ export default function ResumeSettingsModal({
<h3 class="font-bold text-lg">Resume Generator</h3>
<button
onClick={closeModal}
class="btn btn-circle bg-secondary hover:bg-primary text-base-100"
class="btn btn-circle bg-secondary hover:bg-primary"
>
<X className="text-lg" />
</button>
@@ -189,10 +189,10 @@ export default function ResumeSettingsModal({
{/* Action Buttons */}
<div class="flex flex-wrap gap-2 mb-6">
<button onClick={downloadTemplate} class="btn bg-primary btn-sm font-bold text-base-100">
<button onClick={downloadTemplate} class="btn bg-primary btn-sm font-bold">
Download Template
</button>
<button onClick={loadTemplate} class="btn bg-secondary btn-sm font-bold text-base-100">
<button onClick={loadTemplate} class="btn bg-secondary btn-sm font-bold">
Load Template in Editor
</button>
</div>
@@ -207,7 +207,7 @@ export default function ResumeSettingsModal({
role="tab"
class={`px-4 py-2 rounded-full text-sm transition-all duration-200 font-bold ${
activeTab === "upload"
? "bg-primary shadow-sm text-base-100"
? "bg-primary shadow-sm"
: "text-base-content/70 hover:text-base-content hover:bg-base-200"
}`}
onClick={() => setActiveTab("upload")}
@@ -218,7 +218,7 @@ export default function ResumeSettingsModal({
role="tab"
class={`px-4 py-2 rounded-full text-sm font-bold transition-all duration-200 ${
activeTab === "edit"
? "bg-primary font-bold text-base-100 shadow-sm"
? "bg-primary font-bold shadow-sm"
: "text-base-content/70 hover:text-base-content font-bold hover:bg-base-200"
}`}
onClick={() => setActiveTab("edit")}

View File

@@ -29,7 +29,7 @@ export default function ScrollUpButton() {
return (
<button
onClick={scrollToTop}
class={`fixed bottom-4 right-4 z-20 bg-secondary hover:bg-primary text-base-100
class={`fixed bottom-4 right-4 z-20 bg-secondary hover:bg-primary
btn btn-circle transition-all duration-300
${
isVisible.value

View File

@@ -10,16 +10,16 @@ const { talk } = Astro.props;
---
<div
class="card bg-accent shadow-lg w-full sm:w-[calc(50%-1rem)] md:w-96 min-w-[280px] max-w-sm shrink"
class="card bg-accent text-accent-content shadow-lg w-full sm:w-[calc(50%-1rem)] md:w-96 min-w-[280px] max-w-sm shrink"
>
<div class="card-body p-6 break-words">
<h2
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words text-base-100"
class="card-title text-xl md:text-2xl font-bold justify-center text-center break-words"
>
{talk.name}
</h2>
<p class="text-center break-words my-4 text-base-100">
<p class="text-center break-words my-4">
{talk.description}
</p>
@@ -39,7 +39,7 @@ const { talk } = Astro.props;
href={talk.link}
target="_blank"
rel="noopener noreferrer"
class="btn btn-circle text-accent"
class="btn btn-circle"
aria-label={`Visit ${talk.name}`}
>
<Icon name="mdi:link" class="text-lg" />

View File

@@ -1,234 +0,0 @@
import { useState, useEffect, useRef } from "preact/hooks";
import type { JSX } from "preact";
import type { Command } from "../utils/terminal/types";
import { buildFileSystem } from "../utils/terminal/fs";
import {
executeCommand,
type CommandContext,
} from "../utils/terminal/commands";
import {
getCompletions,
formatOutput,
saveCommandToHistory,
loadCommandHistory,
} from "../utils/terminal/utils";
const Terminal = () => {
const [currentPath, setCurrentPath] = useState("/");
const [commandHistory, setCommandHistory] = useState<Command[]>([
{
input: "",
output:
'Welcome to Atridad\'s Shell!\nType "help" to see available commands.\n',
timestamp: new Date(),
path: "/",
},
]);
const [currentInput, setCurrentInput] = useState("");
const [historyIndex, setHistoryIndex] = useState(-1);
const [fileSystem, setFileSystem] = useState<{ [key: string]: any }>({});
const [isTrainRunning, setIsTrainRunning] = useState(false);
const [trainPosition, setTrainPosition] = useState(100);
const [persistentHistory, setPersistentHistory] = useState<string[]>([]);
const inputRef = useRef<HTMLInputElement>(null);
const terminalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (terminalRef.current && !isTrainRunning) {
terminalRef.current.scrollTop = terminalRef.current.scrollHeight;
}
}, [commandHistory, isTrainRunning]);
// Load command history from localStorage
useEffect(() => {
const history = loadCommandHistory();
setPersistentHistory(history);
}, []);
// Initialize file system
useEffect(() => {
buildFileSystem().then(setFileSystem);
}, []);
const handleSubmit = (e: JSX.TargetedEvent<HTMLFormElement>) => {
e.preventDefault();
const commandContext: CommandContext = {
currentPath,
fileSystem,
setCurrentPath,
setIsTrainRunning,
setTrainPosition,
};
const output = executeCommand(currentInput, commandContext);
const newCommand: Command = {
input: currentInput,
output,
timestamp: new Date(),
path: currentPath,
};
// Save command to persistent history
const updatedHistory = saveCommandToHistory(
currentInput,
persistentHistory,
);
setPersistentHistory(updatedHistory);
if (currentInput.trim().toLowerCase() === "clear") {
setCommandHistory([]);
} else {
setCommandHistory((prev: Command[]) => [...prev, newCommand]);
}
setCurrentInput("");
setHistoryIndex(-1);
};
const handleKeyDown = (e: JSX.TargetedKeyboardEvent<HTMLInputElement>) => {
if (e.key === "Tab") {
e.preventDefault();
const { completion, replaceFrom } = getCompletions(
currentInput,
currentPath,
fileSystem,
);
if (completion) {
const beforeReplacement = currentInput.substring(0, replaceFrom);
const newInput = beforeReplacement + completion;
setCurrentInput(newInput + (completion.endsWith("/") ? "" : " "));
}
} else if (e.key === "ArrowUp") {
e.preventDefault();
if (persistentHistory.length > 0) {
const newIndex =
historyIndex === -1
? persistentHistory.length - 1
: Math.max(0, historyIndex - 1);
setHistoryIndex(newIndex);
setCurrentInput(persistentHistory[newIndex]);
}
} else if (e.key === "ArrowDown") {
e.preventDefault();
if (historyIndex !== -1) {
const newIndex = Math.min(
persistentHistory.length - 1,
historyIndex + 1,
);
if (
newIndex === persistentHistory.length - 1 &&
historyIndex === newIndex
) {
setHistoryIndex(-1);
setCurrentInput("");
} else {
setHistoryIndex(newIndex);
setCurrentInput(persistentHistory[newIndex]);
}
}
}
};
return (
<div className="bg-base-100 text-base-content font-mono text-sm h-full flex flex-col rounded-lg border-2 border-primary shadow-2xl relative">
<div className="bg-base-200 px-4 py-2 rounded-t-lg border-b border-base-300">
<div className="flex items-center space-x-2">
<div className="w-3 h-3 bg-error rounded-full"></div>
<div className="w-3 h-3 bg-warning rounded-full"></div>
<div className="w-3 h-3 bg-success rounded-full"></div>
<span className="ml-4 text-base-content/70 text-xs">
guest@atri.dad: {currentPath}
</span>
</div>
</div>
<div
ref={terminalRef}
className={`flex-1 p-4 overflow-y-auto scrollbar-thin scrollbar-thumb-base-300 scrollbar-track-base-100 relative ${
isTrainRunning ? "opacity-0" : "opacity-100"
}`}
onClick={() => !isTrainRunning && inputRef.current?.focus()}
>
<div className="min-h-full">
{commandHistory.map((command: Command, index: number) => (
<div key={index} className="mb-2">
{command.input && (
<div className="flex items-center">
<span className="text-primary font-semibold">
guest@atri.dad
</span>
<span className="text-base-content">:</span>
<span className="text-secondary font-semibold">
{command.path}
</span>
<span className="text-base-content">$ </span>
<span className="text-accent">{command.input}</span>
</div>
)}
{command.output && (
<div
className="whitespace-pre-wrap text-base-content/80 mt-1"
dangerouslySetInnerHTML={{
__html: formatOutput(command.output),
}}
/>
)}
</div>
))}
{!isTrainRunning && (
<form onSubmit={handleSubmit} className="flex items-center">
<span className="text-primary font-semibold">guest@atri.dad</span>
<span className="text-base-content">:</span>
<span className="text-secondary font-semibold">
{currentPath}
</span>
<span className="text-base-content">$ </span>
<input
ref={inputRef}
type="text"
value={currentInput}
onInput={(e) =>
setCurrentInput((e.target as HTMLInputElement).value)
}
onKeyDown={handleKeyDown}
className="flex-1 bg-transparent border-none outline-none text-accent ml-1"
spellcheck={false}
/>
</form>
)}
</div>
</div>
{/* Train animation overlay - positioned over the content area but outside the opacity div */}
{isTrainRunning && (
<div className="absolute inset-x-0 top-16 bottom-0 flex items-center justify-center overflow-hidden pointer-events-none">
<div
className="text-white font-mono text-xs whitespace-nowrap"
style={{
transform: `translateX(${trainPosition}%)`,
transition: "none",
}}
>
<pre className="leading-none">{`
==== ________ ___________
_D _| |_______/ \\__I_I_____===__|_________|
|(_)--- | H\\________/ | | =|___ ___| _________________
/ | | H | | | | ||_| |_|| _| \\_____A
| | | H |__--------------------| [___] | =| |
| ________|___H__/__|_____/[][]~\\_______| | -| |
|/ | |-----------I_____I [][] [] D |=======|____|________________________|_
__/ =| o |=-O=====O=====O=====O \\ ____Y___________|__|__________________________|_
|/-=|___|= || || || |_____/~\\___/ |_D__D__D_| |_D__D__D_|
\\_/ \\__/ \\__/ \\__/ \\__/ \\_/ \\_/ \\_/ \\_/ \\_/`}</pre>
</div>
</div>
)}
</div>
);
};
export default Terminal;