feat: skeleton
This commit is contained in:
336
src/app.css
336
src/app.css
@@ -1,329 +1,47 @@
|
||||
@import url('https://fonts.cdnfonts.com/css/jetbrains-mono-2');
|
||||
@import "tailwind";
|
||||
|
||||
:root {
|
||||
--line-height: 1.2rem;
|
||||
--border-thickness: 2px;
|
||||
--text-color: black;
|
||||
font-optical-sizing: auto;
|
||||
font-variant-numeric: tabular-nums lining-nums;
|
||||
}
|
||||
|
||||
.dark {
|
||||
:root {
|
||||
--text-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
text-decoration-thickness: var(--border-thickness);
|
||||
}
|
||||
|
||||
*::selection {
|
||||
@apply bg-black text-white dark:bg-white dark:text-black;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: var(--border-thickness) solid;
|
||||
padding:
|
||||
calc(var(--line-height) / 2 - var(--border-thickness)) calc(1ch - var(--border-thickness));
|
||||
margin: 0;
|
||||
height: calc(var(--line-height) * 2);
|
||||
width: auto;
|
||||
overflow: visible;
|
||||
line-height: normal;
|
||||
-webkit-font-smoothing: inherit;
|
||||
-moz-osx-font-smoothing: inherit;
|
||||
-webkit-appearance: none;
|
||||
|
||||
@apply select-none bg-white dark:bg-black px-1h shadow-box active:shadow-none active:translate-x-[3px] active:translate-y-[3px];
|
||||
}
|
||||
|
||||
.button:focus:not(:active) {
|
||||
--border-thickness: 3px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
@apply h-2v block relative text-black dark:text-white border-none my-1v;
|
||||
}
|
||||
|
||||
hr:after {
|
||||
@apply block absolute left-0 h-0 w-full border-black dark:border-white;
|
||||
content: "";
|
||||
top: calc(var(--line-height) - var(--border-thickness));
|
||||
border-top: calc(var(--border-thickness) * 3) double;
|
||||
}
|
||||
|
||||
.jump-text:hover>.jump-text {
|
||||
animation: jump 0.25s ease-in-out;
|
||||
animation-delay: var(--animation-delay);
|
||||
}
|
||||
|
||||
@keyframes jump {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateY(-7px);
|
||||
}
|
||||
}
|
||||
|
||||
details {
|
||||
border: var(--border-thickness) solid var(--text-color);
|
||||
padding: calc(var(--line-height) - var(--border-thickness)) 1ch;
|
||||
margin-bottom: var(--line-height);
|
||||
margin-top: var(--line-height);
|
||||
}
|
||||
|
||||
summary {
|
||||
font-weight: var(--font-weight-medium);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
details[open] summary {
|
||||
margin-bottom: var(--line-height);
|
||||
}
|
||||
|
||||
details ::marker {
|
||||
display: inline-block;
|
||||
content: '▶';
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
details[open] ::marker {
|
||||
content: '▼';
|
||||
}
|
||||
|
||||
details :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
/* DITHER ANIMATION */
|
||||
.dither {
|
||||
background-repeat: repeat;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 50;
|
||||
image-rendering: optimizeSpeed;
|
||||
/* STOP SMOOTHING, GIVE ME SPEED */
|
||||
image-rendering: -moz-crisp-edges;
|
||||
/* Firefox */
|
||||
image-rendering: -o-crisp-edges;
|
||||
/* Opera */
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
/* Chrome (and eventually Safari) */
|
||||
image-rendering: pixelated;
|
||||
/* Universal support since 2021 */
|
||||
image-rendering: optimize-contrast;
|
||||
/* CSS3 Proposed */
|
||||
-ms-interpolation-mode: nearest-neighbor;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.dither {
|
||||
filter: invert(1)
|
||||
}
|
||||
.wave-image {
|
||||
filter: invert(1)
|
||||
}
|
||||
}
|
||||
|
||||
.dither-1 {
|
||||
background-image: url(/images/dither_light_3.png);
|
||||
}
|
||||
|
||||
.dither-2 {
|
||||
background-image: url(/images/dither_light_3.png);
|
||||
background-position: 50px 50px;
|
||||
}
|
||||
|
||||
.dither-3 {
|
||||
background-image: url(/images/dither_light_3.png);
|
||||
background-position: 100px 100px;
|
||||
}
|
||||
@import url("https://fonts.cdnfonts.com/css/jetbrains-mono-2");
|
||||
@import "tailwindcss";
|
||||
|
||||
@config "../tailwind.config.ts";
|
||||
|
||||
.tree,
|
||||
.tree ul {
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
line-height: var(--line-height);
|
||||
position: relative;
|
||||
padding-left: 0;
|
||||
list-style-type: none;
|
||||
line-height: var(--line-height);
|
||||
}
|
||||
|
||||
.tree ul {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.tree ul li {
|
||||
position: relative;
|
||||
padding-left: 1.5ch;
|
||||
margin-left: 1.5ch;
|
||||
border-left: var(--border-thickness) solid var(--text-color);
|
||||
position: relative;
|
||||
padding-left: 1.5ch;
|
||||
margin-left: 1.5ch;
|
||||
border-left: var(--border-thickness) solid var(--text-color);
|
||||
}
|
||||
|
||||
.tree ul li:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: calc(var(--line-height) / 2);
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 1ch;
|
||||
border-bottom: var(--border-thickness) solid var(--text-color);
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: calc(var(--line-height) / 2);
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 1ch;
|
||||
border-bottom: var(--border-thickness) solid var(--text-color);
|
||||
}
|
||||
|
||||
.tree ul li:last-child {
|
||||
border-left: none;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.tree ul li:last-child:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
height: calc(var(--line-height) / 2);
|
||||
border-left: var(--border-thickness) solid var(--text-color);
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
height: calc(var(--line-height) / 2);
|
||||
border-left: var(--border-thickness) solid var(--text-color);
|
||||
}
|
||||
|
||||
|
||||
/* DEBUG UTILITIES */
|
||||
|
||||
.debug .debug-grid {
|
||||
--line-height: 1.2rem;
|
||||
--text-color: #000000;
|
||||
--background-color: #FFFFFF;
|
||||
--color: color-mix(in srgb, var(--text-color) 10%, var(--background-color) 90%);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: -1;
|
||||
background-image:
|
||||
repeating-linear-gradient(var(--color) 0 1px, transparent 1px 100%),
|
||||
repeating-linear-gradient(90deg, var(--color) 0 1px, transparent 1px 100%);
|
||||
background-size: 1ch var(--line-height);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
code:not(pre code) {
|
||||
@apply bg-black text-white dark:bg-white dark:text-black px-1h;
|
||||
@apply selection:dark:bg-black selection:dark:text-white selection:bg-white selection:text-black;
|
||||
}
|
||||
|
||||
.debug .off-grid {
|
||||
background: rgba(255, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.debug-toggle-label {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.debug img {
|
||||
/* border: 1px solid black; */
|
||||
}
|
||||
|
||||
|
||||
.formkit-form {
|
||||
font-family: "JetBrains Mono", monospace;
|
||||
}
|
||||
|
||||
.formkit-form [data-style="clean"] {
|
||||
@apply !pt-2v !px-0 !pb-1v;
|
||||
}
|
||||
|
||||
.formkit-fields {
|
||||
@apply !m-0;
|
||||
}
|
||||
|
||||
.formkit-field {
|
||||
@apply !m-0 !mr-2h;
|
||||
}
|
||||
|
||||
.formkit-input {
|
||||
border: var(--border-thickness) solid !important;
|
||||
padding:
|
||||
calc(var(--line-height) / 2 - var(--border-thickness)) calc(1ch - var(--border-thickness)) !important;
|
||||
margin: 0 !important;
|
||||
height: calc(var(--line-height) * 2) !important;
|
||||
width: 100% !important;
|
||||
overflow: visible !important;
|
||||
line-height: normal !important;
|
||||
-webkit-font-smoothing: inherit !important;
|
||||
-moz-osx-font-smoothing: inherit !important;
|
||||
-webkit-appearance: none !important;
|
||||
@apply !font-medium;
|
||||
}
|
||||
|
||||
.formkit-input:focus:not(:active) {
|
||||
--border-thickness: 3px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.formkit-submit {
|
||||
border: var(--border-thickness) solid !important;
|
||||
padding:
|
||||
calc(var(--line-height) / 2 - var(--border-thickness)) calc(1ch - var(--border-thickness)) !important;
|
||||
margin: 0 !important;
|
||||
height: calc(var(--line-height) * 2) !important;
|
||||
width: auto !important;
|
||||
overflow: visible !important;
|
||||
line-height: normal !important;
|
||||
-webkit-font-smoothing: inherit !important;
|
||||
-moz-osx-font-smoothing: inherit !important;
|
||||
-webkit-appearance: none !important;
|
||||
@apply !select-none !bg-white !text-black !px-1h !shadow-box !py-0;
|
||||
}
|
||||
|
||||
.formkit-submit:active {
|
||||
@apply !shadow-none !translate-x-[3px] !translate-y-[3px]
|
||||
}
|
||||
|
||||
.formkit-alert-success {
|
||||
border-width: 0 !important;
|
||||
@apply !bg-transparent !text-black !m-0 !p-0 !font-bold;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.formkit-input {
|
||||
@apply !border-white !bg-black !text-white;
|
||||
}
|
||||
|
||||
.formkit-submit {
|
||||
@apply !bg-black !text-white;
|
||||
}
|
||||
|
||||
.formkit-alert-success {
|
||||
@apply !text-white;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.formkit-submit span {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.formkit-submit:hover span {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.formkit-submit:focus:not(:active) {
|
||||
--border-thickness: 3px !important;
|
||||
outline: none !important;
|
||||
}
|
||||
|
||||
.pixelated {
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
/* <div id="a6d9b30e24" class="formkit-alert formkit-alert-success" data-element="success" data-group="alert">Success! Check your email to confirm the subscription.</div> */
|
||||
|
5
src/app.css.d.ts
vendored
5
src/app.css.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
declare const styles: {
|
||||
readonly "visually-hidden": string;
|
||||
};
|
||||
export = styles;
|
||||
|
33
src/components/Experience.jsx
Normal file
33
src/components/Experience.jsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { For } from "solid-js";
|
||||
import JobCard from "./JobCard";
|
||||
import { createSignal } from "solid-js";
|
||||
|
||||
const Experience = () => {
|
||||
const [jobs, setJobs] = createSignal([
|
||||
{
|
||||
title: "Software Developer Engineer Intern",
|
||||
company: "Amazon (Delivery Extensions Team)",
|
||||
location: "Toronto, Ontario, Canada",
|
||||
range: "May 2025 - July 2025",
|
||||
url: "",
|
||||
},
|
||||
{
|
||||
title: "Software Engineer Intern",
|
||||
company: "Cisco Systems",
|
||||
location: "Remote",
|
||||
range: "January 2023 - May 2023",
|
||||
url: "https://developer.cisco.com/docs/modeling-labs/cat-9000v/",
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<section class="mt-6 mx-auto px-4">
|
||||
<h2 class="mt-6 text-xl font-bold mb-8">Experience</h2>
|
||||
<div>
|
||||
<For each={jobs()}>{(job) => <JobCard job={job} />}</For>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Experience;
|
17
src/components/JobCard.jsx
Normal file
17
src/components/JobCard.jsx
Normal file
@@ -0,0 +1,17 @@
|
||||
const JobCard = (props) => {
|
||||
return (
|
||||
<div class="mb-8">
|
||||
<h3 class="font-bold text-xl">
|
||||
{props.job.title} @
|
||||
<a href={props.job.url} class="text-blue-500 hover:text-blue-600">
|
||||
{props.job.company}
|
||||
</a>
|
||||
</h3>
|
||||
<p class="text-gray-600 mb-4">
|
||||
{props.job.range} | {props.job.location}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default JobCard;
|
@@ -1,12 +1,5 @@
|
||||
import { A } from "@solidjs/router";
|
||||
import type { ParentComponent } from "solid-js";
|
||||
import { clientOnly } from "@solidjs/start";
|
||||
|
||||
const DarkModeToggle = clientOnly(() =>
|
||||
import("./DarkModeToggle").then((r) => ({
|
||||
default: r.DarkModeToggle,
|
||||
})),
|
||||
);
|
||||
|
||||
function changeFavicon(newFaviconPath: string) {
|
||||
const link = document.querySelector("link[rel~='icon']") as HTMLLinkElement;
|
||||
@@ -26,16 +19,14 @@ export const Layout: ParentComponent = (props) => {
|
||||
<a href="#main-content" class="sr-only">
|
||||
Skip to main content
|
||||
</a>
|
||||
<div class="flex flex-col min-h-screen pt-2v py-1v px-2h max-w-thread mx-auto relative overflow-x-hidden leading-1 box-border decoration-2 underline-offset-2">
|
||||
<div class="bg-nord-6 flex flex-col min-h-screen pt-2v py-1v px-2h max-w-full mx-auto relative overflow-x-hidden leading-1 box-border decoration-2 underline-offset-2">
|
||||
<header class="flex flex-col items-center justify-center gap-2v px-4h py-2v">
|
||||
<a href="/" class="text-2v leading-2 font-bold">
|
||||
<a href="/" class="text-nord-3 text-2v leading-2 font-bold">
|
||||
~/minhtrannhat
|
||||
</a>
|
||||
|
||||
<DarkModeToggle />
|
||||
|
||||
<nav>
|
||||
<ul class="flex items-center gap-7h">
|
||||
<nav class="container mx-auto px-4 py-4">
|
||||
<ul class="flex flex-wrap justify-center items-center gap-6">
|
||||
<A end class="hover:underline" activeClass="font-bold" href={"/"}>
|
||||
Home
|
||||
</A>
|
||||
@@ -70,8 +61,6 @@ export const Layout: ParentComponent = (props) => {
|
||||
<main id="main-content" class="mt-1v flex-auto">
|
||||
{props.children}
|
||||
</main>
|
||||
|
||||
<div class="debug-grid" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
@@ -1,199 +0,0 @@
|
||||
/**
|
||||
* Shades of Purple Theme for Prism.js
|
||||
*
|
||||
* @author Ahmad Awais <https://twitter.com/MrAhmadAwais/>
|
||||
* @support Follow/tweet at https://twitter.com/MrAhmadAwais/
|
||||
*/
|
||||
|
||||
code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
@apply text-black dark:text-white font-medium;
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*='language-']::-moz-selection,
|
||||
pre[class*='language-'] ::-moz-selection,
|
||||
code[class*='language-']::-moz-selection,
|
||||
code[class*='language-'] ::-moz-selection,
|
||||
pre[class*='language-']::selection,
|
||||
pre[class*='language-'] ::selection,
|
||||
code[class*='language-']::selection,
|
||||
code[class*='language-'] ::selection {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Code blocks. */
|
||||
pre[class*='language-'] {
|
||||
@apply box-border border-black dark:border-white border-2;
|
||||
padding: calc(var(--line-height) - var(--border-thickness)) calc(2ch - var(--border-thickness));
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/*
|
||||
:not(pre)>code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
|
||||
:not(pre)>code[class*='language-'],
|
||||
pre[class*='language-'] {
|
||||
@apply border-white;
|
||||
}
|
||||
} */
|
||||
|
||||
|
||||
/* Inline code */
|
||||
/* :not(pre)>code[class*='language-'] {
|
||||
|
||||
} */
|
||||
|
||||
.token {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.cdata {
|
||||
@apply text-slate-600 dark:text-slate-400;
|
||||
}
|
||||
|
||||
.token.delimiter,
|
||||
.token.keyword,
|
||||
.token.selector,
|
||||
.token.important,
|
||||
.token.atrule {
|
||||
@apply text-black dark:text-white;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.attr-name {
|
||||
@apply font-medium text-black dark:text-white;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
@apply text-slate-500;
|
||||
}
|
||||
|
||||
.token.boolean {
|
||||
@apply font-medium text-black dark:text-white;
|
||||
}
|
||||
|
||||
.token.tag,
|
||||
.token.tag .punctuation,
|
||||
.token.doctype,
|
||||
.token.builtin {
|
||||
@apply font-medium text-black dark:text-white;
|
||||
}
|
||||
|
||||
.token.entity,
|
||||
.token.symbol {
|
||||
@apply font-medium text-black dark:text-white;
|
||||
|
||||
}
|
||||
|
||||
.token.number {
|
||||
@apply font-medium text-black dark:text-white;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.constant,
|
||||
.token.variable {
|
||||
@apply font-medium text-black dark:text-white;
|
||||
}
|
||||
|
||||
.token.string,
|
||||
.token.char {
|
||||
@apply text-slate-800 dark:text-slate-200;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.attr-value,
|
||||
.token.attr-value .punctuation {
|
||||
color: #a5c261;
|
||||
}
|
||||
|
||||
.token.attr-value .punctuation:first-child {
|
||||
color: #a9b7c6;
|
||||
}
|
||||
|
||||
.token.url {
|
||||
color: #287bde;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.token.function {
|
||||
@apply font-bold text-black dark:text-white;
|
||||
}
|
||||
|
||||
/* .token.regex {
|
||||
|
||||
} */
|
||||
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.inserted {
|
||||
background: #00ff00;
|
||||
}
|
||||
|
||||
.token.deleted {
|
||||
background: #ff000d;
|
||||
}
|
||||
|
||||
code.language-css .token.property,
|
||||
code.language-css .token.property+.token.punctuation {
|
||||
color: #a9b7c6;
|
||||
}
|
||||
|
||||
code.language-css .token.id {
|
||||
color: #ffc66d;
|
||||
}
|
||||
|
||||
code.language-css .token.selector>.token.class,
|
||||
code.language-css .token.selector>.token.attribute,
|
||||
code.language-css .token.selector>.token.pseudo-class,
|
||||
code.language-css .token.selector>.token.pseudo-element {
|
||||
color: #ffc66d;
|
||||
}
|
||||
|
||||
.token.class-name {
|
||||
@apply font-medium text-slate-700 dark:text-slate-300;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.line-highlight.line-highlight {
|
||||
margin-top: 36px;
|
||||
background: linear-gradient(to right, rgba(179, 98, 255, 0.17), transparent);
|
||||
}
|
||||
|
||||
.line-highlight.line-highlight:before,
|
||||
.line-highlight.line-highlight[data-end]:after {
|
||||
content: '';
|
||||
}
|
@@ -1,4 +1,3 @@
|
||||
// @ts-expect-error
|
||||
import Posts from "./posts.json";
|
||||
import type { Post } from "~/types";
|
||||
|
||||
|
@@ -9,12 +9,9 @@ export default createHandler(() => (
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||||
<script>
|
||||
{`document.documentElement.classList.toggle('dark', window.matchMedia('(prefers-color-scheme: dark)').matches)`}
|
||||
</script>
|
||||
{assets}
|
||||
</head>
|
||||
<body class="font-mono bg-white dark:bg-black dark:text-white">
|
||||
<body class="font-mono">
|
||||
<div id="app">{children}</div>
|
||||
{scripts}
|
||||
</body>
|
||||
|
14
src/jobs/cisco.md
Normal file
14
src/jobs/cisco.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
date: 2022-12-27
|
||||
title: Software Engineer Intern
|
||||
company: Cisco Systems
|
||||
location: Remote
|
||||
range: January - May 2023
|
||||
url: https://www.cisco.com/c/en/us/products/cloud-systems-management/modeling-labs/index.html
|
||||
---
|
||||
|
||||
- Developed a **multi-threaded Python microservice** for a Cisco internal SaaS ingesting usage telemetry data of Cisco Cat9kv switches from thousands of GNS3 networking projects to be stored in **Elastic Search**.
|
||||
- Containerized and integrated **Elastic Search** and **Kibana** into existing microservices system.
|
||||
- Created insightful **Kibana** charts highlighting Cat9kv switch flavors daily usage and number of daily users to present to Cisco Enterprise Networking Group executives.
|
||||
- Wrote custom **Ansible** modules to synchronize folders across Cisco managed servers while ensuring 100% folder structure and files integrity with hashing algorithm utilizing Python SHA1 crytographic library.
|
||||
- Automated custom **Ansible** modules testing with **Bash** scripts. Testing environment made with **Vagrant** and **Docker** to simulate production servers
|
@@ -1,6 +1,5 @@
|
||||
import { For } from "solid-js";
|
||||
import { posts } from "~/data/posts";
|
||||
import { Posts } from "~/components/Posts";
|
||||
import Experience from "../components/Experience";
|
||||
|
||||
const links = [
|
||||
"https://github.com/minhtrannhat",
|
||||
@@ -10,19 +9,21 @@ const links = [
|
||||
const Homepage = () => {
|
||||
return (
|
||||
<div>
|
||||
<section class="flex flex-col sm:flex-row gap-2v sm:gap-3h">
|
||||
<section class="mx-4 flex flex-col sm:flex-row gap-2v sm:gap-3h">
|
||||
<div class="font-medium">
|
||||
<div class="flex items-end mb-1v gap-1h">
|
||||
<div class="text-nord-1 flex items-end mb-1v gap-1h">
|
||||
<p>Hi, Minh here.</p>
|
||||
</div>
|
||||
<p class="mb-1v">
|
||||
I'm Minh Tran, a Computer Engineering student at Concordia
|
||||
University, Montreal, Canada.
|
||||
<p class="mb-1v text-nord-1">
|
||||
I'm a Computer Engineering student at Concordia University,
|
||||
Montreal, Canada.
|
||||
<br />
|
||||
<br />
|
||||
I'm most passionate about designing distributed systems that scales
|
||||
but I'm also interested in compilers and systems programming. When
|
||||
I'm not coding, I read books, listen to podcasts or study music
|
||||
but I'm also interested in compilers and systems programming.
|
||||
<br />
|
||||
<br />
|
||||
When I'm not coding, I read books, listen to podcasts or study music
|
||||
theory.
|
||||
</p>
|
||||
<p>
|
||||
@@ -37,10 +38,10 @@ const Homepage = () => {
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<ul class="sm:mt-3v text-slate-600 dark:text-slate-200 text-base sm:text-sm leading-1">
|
||||
<ul class="mx-4 sm:mx-6 sm:mt-3v text-slate-600 text-base sm:text-sm leading-1">
|
||||
<For each={links}>
|
||||
{(link) => (
|
||||
<li class="list-square hover:text-black dark:hover:text-white ml-2h leading-1">
|
||||
<li class="list-square hover:text-black ml-2h leading-1">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
@@ -55,7 +56,8 @@ const Homepage = () => {
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<hr />
|
||||
<br />
|
||||
<Experience />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@@ -5,7 +5,6 @@ import { posts } from "~/data/posts";
|
||||
import { MDXProvider } from "solid-mdx";
|
||||
import { markdownComponents, PostImage } from "~/components/Markdown";
|
||||
import dayjs from "dayjs";
|
||||
import "../css/prism-theme.css";
|
||||
import type { Post } from "~/types";
|
||||
|
||||
const Blog = (props: RouteSectionProps<unknown>) => {
|
||||
|
@@ -1,73 +0,0 @@
|
||||
import type { Accessor } from "solid-js";
|
||||
import { isServer } from "solid-js/web";
|
||||
import { useRouteTransitionTiming } from "./useRouteTransitionTiming";
|
||||
|
||||
export const useDitherAnimation = (ref: Accessor<HTMLElement | undefined>) => {
|
||||
if (!isServer) {
|
||||
const d1 = document.createElement("div");
|
||||
d1.classList.add("dither", "dither-1");
|
||||
const d2 = document.createElement("div");
|
||||
d2.classList.add("dither", "dither-2");
|
||||
const d3 = document.createElement("div");
|
||||
d3.classList.add("dither", "dither-3");
|
||||
|
||||
let started = false;
|
||||
useRouteTransitionTiming(
|
||||
300,
|
||||
() => {
|
||||
ref()?.appendChild(d1);
|
||||
setTimeout(() => {
|
||||
ref()?.appendChild(d2);
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
ref()?.appendChild(d3);
|
||||
}, 200);
|
||||
started = true;
|
||||
},
|
||||
() => {
|
||||
const rnd = () =>
|
||||
setTimeout(() => {
|
||||
try {
|
||||
d1.style.backgroundPosition = `${Math.round(
|
||||
Math.random() * 100,
|
||||
)}px ${Math.round(Math.random() * 100)}px`;
|
||||
d2.style.backgroundPosition = `${Math.round(
|
||||
Math.random() * 100,
|
||||
)}px ${Math.round(Math.random() * 100)}px`;
|
||||
d3.style.backgroundPosition = `${Math.round(
|
||||
Math.random() * 100,
|
||||
)}px ${Math.round(Math.random() * 100)}px`;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
if (started) {
|
||||
rnd();
|
||||
}
|
||||
}, 100);
|
||||
rnd();
|
||||
},
|
||||
() => {
|
||||
started = false;
|
||||
try {
|
||||
ref()?.removeChild(d3);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
setTimeout(() => {
|
||||
try {
|
||||
ref()?.removeChild(d2);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, 100);
|
||||
setTimeout(() => {
|
||||
try {
|
||||
ref()?.removeChild(d1);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}, 200);
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
@@ -1,24 +0,0 @@
|
||||
import { useIsRouting, useBeforeLeave } from "@solidjs/router";
|
||||
import { createEffect } from "solid-js";
|
||||
|
||||
export const useRouteTransitionTiming = (
|
||||
transitionTime: number,
|
||||
onEnter: () => void,
|
||||
onLoading: () => void,
|
||||
onExit: () => void,
|
||||
) => {
|
||||
const isRouting = useIsRouting();
|
||||
createEffect((oldR: boolean | undefined) => {
|
||||
const r = isRouting();
|
||||
if (oldR && !r) onExit();
|
||||
return r;
|
||||
});
|
||||
useBeforeLeave((e) => {
|
||||
e.preventDefault();
|
||||
onEnter();
|
||||
setTimeout(() => {
|
||||
e.retry(true);
|
||||
onLoading();
|
||||
}, transitionTime);
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user