Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions scripts/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ const server = Bun.serve({

async function getFile(pathname: string): Promise<BunFile | null> {
const trimedPath = pathname.replace(/^\/|\/$/g, "");
const indexFile = Bun.file(`dist/${trimedPath}/index.html`);
if (await indexFile.exists()) return indexFile;
const file = Bun.file(`dist/${trimedPath}`);
if (await file.exists()) return file;
const indexFile = Bun.file(`dist/${trimedPath}/index.html`);
if (await indexFile.exists()) return indexFile;
return null;
}

Expand Down
11 changes: 8 additions & 3 deletions scripts/ssg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { minifyGlsl } from "~/utils/minify.js";
// Imports for watch only
import "~/scripts/cool-cursor.js";
import "~/styles/core.css";
import "~/styles/resume.css";
import "~/styles/footer.css";
import "~/styles/header.css";
import "~/styles/icon.css";
Expand All @@ -21,6 +22,10 @@ await Bun.$`rm -rf dist`;
// Build Clients
await Bun.write("dist/assets/background.svg", Background().render());
await Promise.all([buildScripts(), buildStyles()]);
await Bun.$`mkdir -p dist`;
await Bun.$`cp -rf .cache/scripts dist`;
// HACK: Add postfix "src/styles" to solve Bun bundler path when the number of files is >= 9
await Bun.$`cp -rf .cache/styles/src/styles dist`;

const { fetchSite, pageMap } = await import("~/index.js");

Expand All @@ -39,7 +44,7 @@ export async function buildScripts() {
const files = await readdir("src/scripts");
await Bun.build({
entrypoints: files.map((f) => `src/scripts/${f}`),
outdir: "dist/scripts",
outdir: "./.cache/scripts",
minify: true,
plugins: [
{
Expand All @@ -58,8 +63,8 @@ export async function buildScripts() {
export async function buildStyles() {
const files = await readdir("src/styles");
await Bun.build({
entrypoints: files.map((f) => `src/styles/${f}`),
outdir: "dist/styles",
entrypoints: files.map((f) => `./src/styles/${f}`),
outdir: "./.cache/styles",
experimentalCss: true,
minify: true,
});
Expand Down
16 changes: 11 additions & 5 deletions src/components/footer.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { env } from "mini-van-plate/shared";
import { metadata, socials } from "~/profile";
import { EyeButton } from "./eye-button";
import { UndercurlLink } from "./undercurl";

import { metadata, socials } from "~/profile.js";
import { trimProtocol } from "~/utils/profile.js";
import { EyeButton } from "./eye-button.js";
import { UndercurlLink } from "./undercurl.js";

const FootAvatar = () => {
const { div, img, h3, h4 } = env.van.tags;
return div(
{ class: "foot-avatar" },
img({ src: "/assets/profile.webp", alt: "Footer Avatar" }),
div(h3(metadata.name), h4("Software Engineer")),
div(h3(`${metadata.firstName} ${metadata.lastName}`), h4(metadata.job)),
);
};

const FootMail = () => {
const { div, h3, h4 } = env.van.tags;
return div({ class: "foot-mail" }, h4("Get in touch"), h3(metadata.email));
return div(
{ class: "foot-mail" },
h4("Get in touch"),
h3(trimProtocol(metadata.email)),
);
};

const FootSocial = () => {
Expand Down
65 changes: 33 additions & 32 deletions src/components/header.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,56 @@
import { env } from "mini-van-plate/shared";
import type { ChildDom } from "mini-van-plate/van-plate";
import { PrintIcon } from "~/icons";

const CoolLink = ({ href, title }: { href: string; title: string }) => {
const CoolLink = ({ href, label }: { href: string; label: string }) => {
const { a, span } = env.van.tags;
return a({ href, class: "cool-link", hidecoolcursor: true }, span(title));
return a({ href, class: "cool-link" }, span(label));
};

const CoolButton = ({ href, title }: { href: string; title: string }) => {
const CoolButton = ({ href, label }: { href: string; label: string }) => {
const { a, span } = env.van.tags;
return a(
{ href, class: "cool-button shadow", hidecoolcursor: true },
span(title),
);
return a({ href, class: "cool-button shadow" }, span(label));
};

const HeaderLogo = ({ href }: { href: string }, page: string) => {
const { a, span } = env.van.tags;
return a(
{ href, class: "logo", hidecoolcursor: true },
"Binh",
" ",
span({ class: "site" }, page),
);
return a({ href, class: "logo" }, "Binh", " ", span({ class: "site" }, page));
};

const HeaderNav = (
links: { href: string; title: string; isButton?: boolean }[],
links: { href: string; label?: string; isButton?: boolean }[],
...extra: ChildDom[]
) => {
const { input, label, nav } = env.van.tags;
return [
input({ type: "checkbox", id: "navbar-toggle", style: "display:none;" }),
const { div, input, label, nav } = env.van.tags;
return div(
{ class: "wrap-nav" },
...extra,
input({ type: "checkbox", id: "nav-toggle", style: "display:none;" }),
label({
for: "navbar-toggle",
class: "navbar-toggle",
for: "nav-toggle",
class: "nav-toggle",
hidecoolcursor: true,
}),
nav(
{ class: "navbar" },
links.map(({ href, title, isButton }) =>
isButton ? CoolButton({ href, title }) : CoolLink({ href, title }),
links.map(({ href, label, isButton }) =>
isButton
? CoolButton({ href, label: label ?? href })
: CoolLink({ href, label: label ?? href }),
),
),
];
);
};

export const CvHeader = () => {
const { header, div } = env.van.tags;
export const ResumeHeader = () => {
const { header, div, button } = env.van.tags;
return header(
div(
{ class: "container" },
HeaderLogo({ href: "/cv" }, "CV"),
HeaderNav([{ href: "/#contact", title: "Contact", isButton: true }]),
HeaderLogo({ href: "/resume" }, "Resume"),
HeaderNav(
[{ href: "/", label: "Landing", isButton: true }],
button({ class: "print", onclick: "window.print();" }, PrintIcon()),
),
),
);
};
Expand All @@ -61,11 +62,11 @@ export const LandingHeader = () => {
{ class: "container" },
HeaderLogo({ href: "/" }, "Landing"),
HeaderNav([
{ href: "/#intro", title: "Intro" },
{ href: "/#skill", title: "Skills" },
{ href: "/#github-profile", title: "Github Profile" },
{ href: "/#project", title: "Projects" },
{ href: "/#contact", title: "Contact", isButton: true },
{ href: "/#intro", label: "Intro" },
{ href: "/#skill", label: "Skills" },
{ href: "/#github-profile", label: "Github Profile" },
{ href: "/#project", label: "Projects" },
{ href: "/#contact", label: "Contact", isButton: true },
]),
),
);
Expand Down
15 changes: 13 additions & 2 deletions src/components/contact.ts → src/components/landing/contact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export const Contact = () => {
h4("Contact"),
h3("Get in touch"),
form(
{ action: "https://api.staticforms.xyz/submit", method: "post" },
{
action: "https://api.staticforms.xyz/submit",
method: "post",
},
input({
type: "hidden",
name: "accessKey",
Expand All @@ -20,14 +23,20 @@ export const Contact = () => {
div(
label(
"Hey, My name is ",
input({ placeholder: "Type Here", name: "name", required: true }),
input({
placeholder: "Type Here",
name: "name",
required: true,
hidecoolcursor: true,
}),
),
label(
" I'm looking for ",
input({
placeholder: "Type of service",
name: "subject",
required: true,
hidecoolcursor: true,
}),
),
),
Expand All @@ -38,6 +47,7 @@ export const Contact = () => {
placeholder: "Your Email id here",
name: "email",
required: true,
hidecoolcursor: true,
}),
),
input({ type: "hidden", name: "replyTo", value: "@" }),
Expand All @@ -51,6 +61,7 @@ export const Contact = () => {
placeholder: "Your Message",
name: "message",
required: true,
hidecoolcursor: true,
}),
button({ type: "submit", class: "cool-button" }, span("Send")),
),
Expand Down
22 changes: 7 additions & 15 deletions src/components/intro.ts → src/components/landing/intro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,11 @@ export const IntroContent = () => {
};

export const Intro = () => {
const { div, section, canvas } = env.van.tags;
return [
canvas({
class: "background",
id: "background-hover",
width: 1920,
height: 1080,
}),
section(
{ class: "intro container" },
div({ id: "intro" }),
IntroContent(),
Social(),
),
];
const { div, section } = env.van.tags;
return section(
{ class: "intro container" },
div({ id: "intro" }),
IntroContent(),
Social(),
);
};
10 changes: 5 additions & 5 deletions src/components/project.ts → src/components/landing/projects.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { env } from "mini-van-plate/shared";

import { type ProjectData, projects } from "~/profile.js";
import { type DProject, projects } from "~/profile.js";

import { EyeButton } from "./eye-button.js";
import { UndercurlText } from "./undercurl.js";
import { EyeButton } from "~/components/eye-button.js";
import { UndercurlText } from "~/components/undercurl.js";

const Project = (
index: string,
{ title, src, image, previewLink, date, techs }: ProjectData,
{ title, src, image, previewLink, date, techs }: DProject,
) => {
const { div, span, h4, p, article, img, a } = env.van.tags;
return article(
Expand Down Expand Up @@ -50,7 +50,7 @@ const Project = (
export const Projects = () => {
const { div, section, h3 } = env.van.tags;
return section(
{ class: "projects" },
{ class: "projects", hidecoolcursor: true },
div({ id: "project" }),
div(
{ class: "container" },
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ export const Social = () => {
href,
target: "_blank",
"aria-label": `${label} link`,
hidecoolcursor: true,
},
span({ class: `bi-${label}` }),
),
Expand Down
14 changes: 14 additions & 0 deletions src/components/resume/education.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Section } from "./section.js";
import { Timeline } from "./timeline.js";

export const Education = () => {
return Section(
{ class: "education", title: "Education" },
Timeline({
label: "Ho Chi Minh city University of Technology",
sublabel: "Bachelor of Engineering in Computer Science",
timeline: "Sep 2018 - Nov 2024",
location: "Ho Chi Minh, Vietnam",
}),
);
};
38 changes: 38 additions & 0 deletions src/components/resume/opensource-work.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Project } from "./projects.js";
import { Section } from "./section.js";

export const OpenSourceWork = () => {
return Section(
{ class: "projects", title: "Open Source Work" },
Project({
name: "Cucumber Open",
url: "https://github.com/cucumber",
techs: ["TypeScript", "LSP", "NPM", "Diagram"],
details: [
"Fixed critical crash issues related to editor tools startup.",
"Enhanced Vietnamese language support for Gherkin syntax.",
"Created class diagrams to represent Gherkin syntax structure.",
],
}),
Project({
name: "IBus Bamboo - Bộ gõ tiếng Việt cho Linux/BSD",
url: "https://github.com/BambooEngine/ibus-bamboo",
techs: ["Go", "Unicode", "Icons"],
details: [
"Added Unicode typing feature with the shortcut `Ctrl + Shift + U`.",
"Implemented an English mode version.",
"Designed icons for Vietnamese and English modes with various fancy themes.",
],
}),
Project({
name: "Dracula colorscheme for NEOVIM written in Lua",
url: "https://github.com/Mofiqul/dracula.nvim",
techs: ["Lua", "Neovim"],
details: [
"Rewrote the entire theme using Lua API methods to improve startup performance.",
"Adopted a modern configuration style for the theme.",
"Added new demonstration images.",
],
}),
);
};
48 changes: 48 additions & 0 deletions src/components/resume/overview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { env } from "mini-van-plate/shared";
import type { ChildDom } from "mini-van-plate/van-plate";

import {
CallIcon,
EmailIcon,
GithubIcon,
GlobeIcon,
LinkedinIcon,
} from "~/icons.js";
import { contacts, metadata } from "~/profile.js";
import { trimProtocol } from "~/utils/profile.js";

function getContactIcon(url: string): ChildDom {
if (url.startsWith("mailto:")) return EmailIcon();
if (url.startsWith("tel:")) return CallIcon();
if (url.includes("github.com")) return GithubIcon();
if (url.includes("linkedin.com")) return LinkedinIcon();
return GlobeIcon();
}

const Link = (url: string) => {
const { a } = env.van.tags;
return a({ href: url, target: "_blank" }, trimProtocol(url));
};

const ContactLink = (url: string) => {
const { a } = env.van.tags;
return a(
{ href: url, target: "_blank", class: "ctc" },
getContactIcon(url),
trimProtocol(url),
);
};

export const Overview = () => {
const { section, div, h1, p } = env.van.tags;

return section(
{ class: "overview container" },
h1({ class: "name" }, `${metadata.firstName} ${metadata.lastName}`),
div(
{ class: "ctcs" },
Object.values(contacts).map((url) => ContactLink(url)),
),
p("(Live version: ", Link(metadata.resume), ")"),
);
};
Loading