feat: new website
This commit is contained in:
commit
003752be95
53 changed files with 2532 additions and 0 deletions
1
src/ambient.d.ts
vendored
Normal file
1
src/ambient.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
import "@hypeup/lexicon"
|
||||
1
src/routes/experience.html.ts
Normal file
1
src/routes/experience.html.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "ui/Experience"
|
||||
1
src/routes/index.html.ts
Normal file
1
src/routes/index.html.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "ui/Home"
|
||||
1
src/routes/projects.html.ts
Normal file
1
src/routes/projects.html.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "ui/Projects"
|
||||
1
src/routes/tech.html.ts
Normal file
1
src/routes/tech.html.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "ui/Tech"
|
||||
4
src/types.d.ts
vendored
Normal file
4
src/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
declare type Content = any
|
||||
|
||||
declare module "*?raw" { const src: string; export default src }
|
||||
declare module "*?url" { const src: string; export default src }
|
||||
22
src/ui/Experience/Experience.ts
Normal file
22
src/ui/Experience/Experience.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import Layout from "ui/Layout"
|
||||
import Window from "ui/Layout/Window"
|
||||
import Markdown from "ui/shared/Markdown"
|
||||
import experience from "./experience.md?raw"
|
||||
|
||||
const title = "Dan Finch - Experience"
|
||||
|
||||
export default function Experience() {
|
||||
return [
|
||||
backgroundColor.white,
|
||||
Layout(
|
||||
{ title, url: "/experience" },
|
||||
Window(
|
||||
{ title },
|
||||
div(
|
||||
padding("16px"),
|
||||
Markdown(experience),
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
}
|
||||
95
src/ui/Experience/experience.md
Normal file
95
src/ui/Experience/experience.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# My Experience
|
||||
|
||||
---
|
||||
|
||||
## Dynatrace - Senior Software Engineer
|
||||
> July 2023 - Present (Graz, Austria - Hybrid)
|
||||
|
||||
- Implemented an array of features and bug fixes for several Business Analytics applications using **React** and **TypeScript**.
|
||||
- Designed and implemented a strongly-typed embedded DSL to generate the data warehouse queries which power the apps, significantly reducing maintenance overhead.
|
||||
- Created an end-to-end testing framework suite based on **Playwright** that substantially streamlined writing and running tests.
|
||||
- Created and integrated a state management framework based on **Zustand** and **Immer** which greatly simplified and optimized front-end code.
|
||||
- Initiated ongoing effort to improve code quality and project structure.
|
||||
- Migrated a large project from Bitbucket+Jenkins to Github+Actions.
|
||||
|
||||
---
|
||||
|
||||
## Independent Consultant
|
||||
> Dec 2014 - July 2023 (Remote)
|
||||
|
||||
My freelance work in this time included:
|
||||
|
||||
- Created a mobile social network app for a startup conference and shared working space provider. This was written in functional **TypeScript** and **React Native**, with some Python/Django and Postgres on a Heroku backend.
|
||||
- Consulted on JavaScript and front-end tech for Bitbanger Labs for their ColorSpike hybrid mobile app built on Cordova.
|
||||
- Wrote a Chrome extension using the **Mithril** framework to inspect advertisements embedded in web pages for Bringhub.
|
||||
- Designed and prototyped an app and backend for a IOT equipment monitoring startup in Sandpoint, ID using **React**, **ElasticSearch**, and **Postgres**.
|
||||
- Created an **Electron** app connected to the QuickBooks API to build quotes for a greenhouse manufacturer.
|
||||
|
||||
---
|
||||
|
||||
## RiskLens - Senior Software Engineer
|
||||
> Jun 2017 - Dec 2017 (Spokane, WA)
|
||||
|
||||
Helped lead the front-end development of FAIR-U - a **React** + **Redux**-based app backed by **C\#**, Sql Server, and
|
||||
Azure & transitioned the team to TypeScript and other modern front-end tools. Gained experience with a
|
||||
security-focused and process-intensive team.
|
||||
|
||||
---
|
||||
|
||||
## TenX Logic - Contract Developer
|
||||
> Oct 2015 - Mar 2016 (Remote)
|
||||
|
||||
This project for the oil + gas industry targeted the Mono platform on ARM processors. I designed and
|
||||
implemented a **C\#**-based driver model to control equipment in the field, with a frontend written in AngularJS.
|
||||
|
||||
---
|
||||
|
||||
## Kochava - Senior Developer
|
||||
> Apr 2014 - Nov 2014 (Sandpoint, ID)
|
||||
|
||||
Worked on infrastructure and systems to manage large volumes of real-time analytics data. I tackled
|
||||
complex challenges requiring constant inventiveness and collaboration to help the company scale at
|
||||
a rapid rate.
|
||||
|
||||
My work centered around **Node.js**, **Redis**, Linux, and **MongoDB**. I also led the early implementation of a
|
||||
scalable high-traffic analytics backend.
|
||||
|
||||
---
|
||||
|
||||
## Coldwater Creek - Web Developer
|
||||
> Oct 2012 - Apr 2014 (Sandpoint, ID)
|
||||
|
||||
Worked with many teams to finish an amazing number of projects. My work on both internal tooling and
|
||||
customer experience put the company ahead of its industry in many respects.
|
||||
|
||||
- Implemented a product search which outperformed all competitors, including a novel infinite scrolling method.
|
||||
- Provided an array of features and user interfaces widgets for website with around one million hits per day.
|
||||
- Wrote or re-wrote around 30 internal web-driven tools for e-commerce & marketing teams.
|
||||
- Wrote custom jQuery UI powered framework and widgets for tool suite.
|
||||
- Initiated effort to significantly improve client-side page load performance.
|
||||
- Created a framework allowing mutual embedding between MVC and WebForms to allow a smooth transition off of legacy code.
|
||||
|
||||
---
|
||||
|
||||
## Promitheia - Technical Director
|
||||
> Sep 2006 - Sep 2012 (Austin, TX)
|
||||
|
||||
As co-founder of this startup I was responsible for software design, programming, project management,
|
||||
operations, testing, maintenance, and security.
|
||||
|
||||
- Created complex e-commerce website featuring retail and affiliate marketing management. Owner of dozens of projects within the company.
|
||||
- Configured and managed production web servers including Microsoft IIS and LAMP stack.
|
||||
- Worked closely with DBA on data-driven applications powered by SQL Server 2008 and ADO.NET.
|
||||
- Analyzed and selected supporting software and services, including early adoption of technologies such as ASP.NET MVC, jQuery, F#, Amazon Web Services, and distributed source control.
|
||||
- Developed solutions using Service Oriented Architecture (SOA), WCF, and Windows services. Integrated software with various cloud data and web service providers, including AWS, Authorize.NET, and Zencoder.
|
||||
- Studied and implemented e-commerce best practices, web security standards, and PCI compliance.
|
||||
|
||||
---
|
||||
|
||||
## Mercury ML - Founder
|
||||
> 2002 - 2004 (Lapeer, MI)
|
||||
|
||||
I got my start right out of high school helping to scale two companies out of working with unwieldy spreadsheets.
|
||||
|
||||
- Released two major versions of a VB6 application to catalog marketing affiliates, track orders, and calculate payouts.
|
||||
- Wrote and administered affiliate websites in Classic ASP and VBScript.
|
||||
1
src/ui/Experience/index.ts
Normal file
1
src/ui/Experience/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Experience"
|
||||
13
src/ui/Home/Home.ts
Normal file
13
src/ui/Home/Home.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import Layout from "ui/Layout"
|
||||
import Markdown from "ui/shared/Markdown"
|
||||
import about from "./about.md?raw"
|
||||
import Window from "ui/Layout/Window"
|
||||
|
||||
const title = "Dan Finch - About Me"
|
||||
|
||||
export default function Home() {
|
||||
return Layout(
|
||||
{ title, url: "/" },
|
||||
Window({ title }, div(padding("8px"), Markdown(about))),
|
||||
)
|
||||
}
|
||||
29
src/ui/Home/about.md
Normal file
29
src/ui/Home/about.md
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# About Me
|
||||
|
||||
I own software end-to-end: building or renewing stacks and evaluating the tools and techniques that will serve the product well. I assist my teams in creating frameworks, automation, standards, and guidance that clear the path to delivering good software and user experiences. I am comfortable working hands-on across code, architecture, UX, and product.
|
||||
|
||||
<aside class="field-border-disabled"><img src="/pfp.jpg" width="200"></aside>
|
||||
|
||||
My first principle is **user experience**. Software is interesting because of what it can do for people, and we all put up with mediocre software every day. This dissatisfaction drives me to set a high standard for our work.
|
||||
|
||||
As a programmer, I've learned to make code **small, simple, and readable**. My aim is prolific and expeditious code output while maintaining quality. I make loosely-coupled components, understandable abstractions, and low-ceremony frameworks.
|
||||
|
||||
As a teammate and leader, I am devoted to **friendliness** and **respect**. I am a good communicator, love sharing my knowledge, and learning from others. I've given talks at local user groups and mentored at work and in my free time.
|
||||
|
||||
## Skills
|
||||
|
||||
- Consciousness of mainstream & emerging technology and trends.
|
||||
- Quickly vetting and adapting to new technologies - building and improving stacks.
|
||||
- High standards for great user and developer experiences.
|
||||
- Interacting directly and empathetically with stakeholders and end users.
|
||||
- Managing work through all phases of a development cycle.
|
||||
- Eliminating friction in processes through automation.
|
||||
- Understanding all the areas of a codebase and recognizing where waste can be reduced.
|
||||
- Coordinating large, complex upgrades and refactors.
|
||||
|
||||
## Interests
|
||||
|
||||
- Compilers, interpreters & parsers.
|
||||
- Metaprogramming, language-oriented programming.
|
||||
- Developer tools - hacking on build tools and bundlers.
|
||||
- Automating self-hosted cloud tech.
|
||||
1
src/ui/Home/index.ts
Normal file
1
src/ui/Home/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Home"
|
||||
19
src/ui/Layout/Head.ts
Normal file
19
src/ui/Layout/Head.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
const DEFAULT_TITLE = "Dan Finch"
|
||||
|
||||
export default function Head(props: { title: string }, ...contents: Content[]) {
|
||||
return head(
|
||||
meta({ charset: "UTF-8" }),
|
||||
meta({
|
||||
name: "viewport",
|
||||
content: "width=device-width",
|
||||
"initial-scale": "1.0",
|
||||
}),
|
||||
link({
|
||||
rel: "shortcut icon",
|
||||
type: "image/png",
|
||||
href: "/favicon.png",
|
||||
}),
|
||||
title(props.title || DEFAULT_TITLE),
|
||||
contents,
|
||||
)
|
||||
}
|
||||
50
src/ui/Layout/Layout.ts
Normal file
50
src/ui/Layout/Layout.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
import Head from "./Head"
|
||||
import background from "assets/background.js?raw"
|
||||
import css98 from "98se.css?url"
|
||||
import Taskbar from "ui/Layout/Taskbar"
|
||||
import Theme from "ui/Layout/Theme"
|
||||
|
||||
export default function Layout(
|
||||
props: { title: string, url: string },
|
||||
...contents: Content[]
|
||||
) {
|
||||
return [
|
||||
doctype.html5,
|
||||
html(
|
||||
Head(props, [
|
||||
link({ rel: "stylesheet", href: css98 }),
|
||||
style(Theme()),
|
||||
]),
|
||||
body(
|
||||
backgroundColor.black,
|
||||
canvas(
|
||||
{ id: "background" },
|
||||
display.block,
|
||||
position.fixed,
|
||||
width("100%"),
|
||||
height("100%"),
|
||||
left(0),
|
||||
top(0),
|
||||
zIndex(-1),
|
||||
),
|
||||
div(
|
||||
display.flex,
|
||||
flexDirection.column,
|
||||
height("100vh"),
|
||||
nav(
|
||||
height.maxContent,
|
||||
Taskbar({ url: props.url }),
|
||||
),
|
||||
main(
|
||||
flex(1),
|
||||
overflowY.hidden,
|
||||
display.flex,
|
||||
justifyContent.center,
|
||||
contents,
|
||||
)
|
||||
)
|
||||
),
|
||||
script(raw(background)),
|
||||
),
|
||||
]
|
||||
}
|
||||
14
src/ui/Layout/Reset.ts
Normal file
14
src/ui/Layout/Reset.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export default function Reset() {
|
||||
return [
|
||||
rule("*, *::before, *::after", [boxSizing.borderBox]),
|
||||
rule("*:not(dialog)", [margin(0)]),
|
||||
rule(
|
||||
"input, button, textarea, select",
|
||||
font("inherit"),
|
||||
),
|
||||
rule(
|
||||
"p, h1, h2, h3, h4, h5, h6",
|
||||
overflowWrap.breakWord,
|
||||
),
|
||||
]
|
||||
}
|
||||
101
src/ui/Layout/Taskbar.ts
Normal file
101
src/ui/Layout/Taskbar.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
import favicon from "public/favicon.png?url"
|
||||
import linkedin from "public/icons/linkedin.svg?raw"
|
||||
import github from "public/icons/github.svg?raw"
|
||||
import aboutIcon from "public/icons/about.png?url"
|
||||
import experienceIcon from "public/icons/experience.png?url"
|
||||
import projectsIcon from "public/icons/projects.png?url"
|
||||
import techIcon from "public/icons/tech.png?url"
|
||||
|
||||
const vr = hr(height("20px"), margin("0px 2px"))
|
||||
|
||||
export default function Taskbar({ url }: { url: string }) {
|
||||
return div.window(
|
||||
textWrapMode.nowrap,
|
||||
overflowX.hidden,
|
||||
margin("10px"),
|
||||
div(
|
||||
display.flex,
|
||||
alignItems.center,
|
||||
gap("2px"),
|
||||
div(
|
||||
display.flex,
|
||||
paddingLeft("4px"),
|
||||
img(
|
||||
{ src: favicon },
|
||||
width("12px"),
|
||||
height("12px"),
|
||||
marginRight("4px"),
|
||||
),
|
||||
div(fontWeight.bold, "Dan Finch"),
|
||||
),
|
||||
vr,
|
||||
a(
|
||||
{ title: "LinkedIn" },
|
||||
{ href: "https://www.linkedin.com/in/danfinch/" },
|
||||
raw(linkedin),
|
||||
),
|
||||
a(
|
||||
{ title: "GitHub" },
|
||||
{ href: "https://github.com/errilaz" },
|
||||
raw(github),
|
||||
),
|
||||
vr,
|
||||
Task({
|
||||
icon: aboutIcon,
|
||||
title: "About",
|
||||
href: "/",
|
||||
active: url === "/",
|
||||
}),
|
||||
Task({
|
||||
icon: projectsIcon,
|
||||
title: "Projects",
|
||||
href: "/projects",
|
||||
active: url === "/projects",
|
||||
}),
|
||||
Task({
|
||||
icon: experienceIcon,
|
||||
title: "Experience",
|
||||
href: "/experience",
|
||||
active: url === "/experience",
|
||||
}),
|
||||
Task({
|
||||
icon: techIcon,
|
||||
title: "Tech",
|
||||
href: "/tech",
|
||||
active: url === "/tech",
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function Task({
|
||||
icon,
|
||||
title,
|
||||
href,
|
||||
active,
|
||||
}: {
|
||||
icon: string
|
||||
title: Content
|
||||
href?: string
|
||||
active?: boolean
|
||||
}) {
|
||||
return a.button(
|
||||
{ href },
|
||||
active && className("active"),
|
||||
padding("0 4px"),
|
||||
textAlign.left,
|
||||
div(
|
||||
width("100%"),
|
||||
pad("4px"),
|
||||
display.flex,
|
||||
justifyContent.flexStart,
|
||||
img(
|
||||
{ src: icon },
|
||||
width("12px"),
|
||||
height("12px"),
|
||||
marginRight("4px"),
|
||||
),
|
||||
span(title),
|
||||
),
|
||||
)
|
||||
}
|
||||
34
src/ui/Layout/Theme.ts
Normal file
34
src/ui/Layout/Theme.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import Reset from "./Reset"
|
||||
|
||||
export default function Theme() {
|
||||
return [
|
||||
Reset(),
|
||||
rule("a.button", [
|
||||
textDecoration("none"),
|
||||
cursor("default"),
|
||||
display.flex,
|
||||
alignItems.center,
|
||||
]),
|
||||
rule("h1", fontSize("20px")),
|
||||
rule("h2", fontSize("16px")),
|
||||
rule("ul",
|
||||
paddingInlineStart("30px"),
|
||||
),
|
||||
rule(
|
||||
".markdown",
|
||||
textAlign.justify,
|
||||
fontFamily("Arial"),
|
||||
fontSize("14px"),
|
||||
rule("h1", fontSize("28px")),
|
||||
rule("h2", fontSize("20px")),
|
||||
rule(
|
||||
"aside",
|
||||
float.right,
|
||||
marginLeft("20px"),
|
||||
paddingBottom(0),
|
||||
),
|
||||
rule("p, h1, h2, h3, ul", margin("12px 0")),
|
||||
),
|
||||
rule("iframe", border(0)),
|
||||
]
|
||||
}
|
||||
33
src/ui/Layout/Window.ts
Normal file
33
src/ui/Layout/Window.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
const DEFAULT_WIDTH = "770px"
|
||||
|
||||
export default function Window(
|
||||
props: { title: string; width?: Content; flat?: boolean },
|
||||
...contents: Content[]
|
||||
) {
|
||||
return article.window(
|
||||
opacity(0.9),
|
||||
marginTop("16px"),
|
||||
marginBottom("24px"),
|
||||
marginLeft("10px"),
|
||||
marginRight("10px"),
|
||||
width(props.width ?? DEFAULT_WIDTH),
|
||||
maxWidth(props.width ?? DEFAULT_WIDTH),
|
||||
minHeight("200px"),
|
||||
display.flex,
|
||||
flexDirection.column,
|
||||
div.titleBar(
|
||||
div.titleBarText(props.title),
|
||||
div.titleBarControls(
|
||||
button({ "aria-label": "Minimize" }),
|
||||
button({ "aria-label": "Maximize" }),
|
||||
button({ "aria-label": "Close" }),
|
||||
),
|
||||
),
|
||||
div.windowBody(
|
||||
props.flat ? null : className("field-border"),
|
||||
flex(1),
|
||||
overflowY.auto,
|
||||
contents,
|
||||
),
|
||||
)
|
||||
}
|
||||
1
src/ui/Layout/index.ts
Normal file
1
src/ui/Layout/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Layout"
|
||||
17
src/ui/Projects/Projects.ts
Normal file
17
src/ui/Projects/Projects.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import Layout from "ui/Layout"
|
||||
import Window from "ui/Layout/Window"
|
||||
|
||||
const title = "Dan Finch - Projects"
|
||||
|
||||
export default function Projects() {
|
||||
return Layout(
|
||||
{ title, url: "/projects" },
|
||||
Window(
|
||||
{ title },
|
||||
display.flex,
|
||||
iframe({ src: "https://sigitex.com" },
|
||||
flex(1)
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
1
src/ui/Projects/index.ts
Normal file
1
src/ui/Projects/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Projects"
|
||||
119
src/ui/Tech/Tech.ts
Normal file
119
src/ui/Tech/Tech.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
import Layout from "ui/Layout"
|
||||
import Window from "ui/Layout/Window"
|
||||
import Markdown from "ui/shared/Markdown"
|
||||
|
||||
const title = "Dan Finch - Tech"
|
||||
|
||||
export default function Tech() {
|
||||
return Layout(
|
||||
{ title, url: "/tech" },
|
||||
Window(
|
||||
{ title, flat: true },
|
||||
div(
|
||||
display.flex,
|
||||
flexWrap.wrap,
|
||||
marginRight("8px"),
|
||||
paddingBottom("16px"),
|
||||
gap("8px"),
|
||||
Skillset("Languages", languages),
|
||||
Skillset("Platforms", platforms),
|
||||
Skillset("Frontend", frontend),
|
||||
Skillset("Backend", backend),
|
||||
Skillset("Data", data),
|
||||
Skillset("Cloud / Infra / DevOps", cloud),
|
||||
Skillset("Methodologies & Concepts", concepts),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
function Skillset(title: string, markdown: Content) {
|
||||
return div(
|
||||
flex(1),
|
||||
minWidth("250px"),
|
||||
display.flex,
|
||||
flexDirection.column,
|
||||
h2(title),
|
||||
div.fieldBorder(flex(1), Markdown(markdown)),
|
||||
)
|
||||
}
|
||||
|
||||
const languages = `
|
||||
- ⭐ TypeScript
|
||||
- ⭐ JavaScript
|
||||
- ⭐ C#
|
||||
- F#
|
||||
- SQL
|
||||
`
|
||||
|
||||
const platforms = `
|
||||
- ⭐ Node.js / Deno / Bun
|
||||
- ⭐ HTML + CSS
|
||||
- ⭐ .NET & .NET Core
|
||||
- Linux
|
||||
- Electron
|
||||
- Browser Extensions
|
||||
- React Native
|
||||
- Cloudflare Workers
|
||||
`
|
||||
|
||||
const frontend = `
|
||||
- ⭐ React
|
||||
- ⭐ Vite
|
||||
- ⭐ Zustand
|
||||
- ⭐ Immer
|
||||
- ⭐ Webpack
|
||||
- ⭐ Parcel
|
||||
- ⭐ Emotion / Styled Components
|
||||
- ⭐ Mithril
|
||||
- Redux
|
||||
- React Router
|
||||
- TanStack Query, React Query
|
||||
- Vanilla Extract
|
||||
- SCSS, Stylus, Less, CSS Modules
|
||||
- Lodash, Immer, RxJS, Kefir
|
||||
`
|
||||
|
||||
const backend = `
|
||||
- ⭐ Express
|
||||
- ⭐ Koa
|
||||
- ⭐ ASP.NET
|
||||
- Fastify
|
||||
- Hapi
|
||||
- Nest
|
||||
`
|
||||
|
||||
const data = `
|
||||
- ⭐ Postgres
|
||||
- ⭐ Redis
|
||||
- Sqlite
|
||||
- MongoDB
|
||||
- MySQL
|
||||
- ElasticSearch
|
||||
- SQL Server
|
||||
`
|
||||
|
||||
const cloud = `
|
||||
- Docker
|
||||
- Cloudflare
|
||||
- Firebase
|
||||
- Supabase
|
||||
- AWS
|
||||
- GitHub Actions
|
||||
`
|
||||
|
||||
const concepts = `
|
||||
- Domain-driven design (DDD)
|
||||
- TDD & BDD, unit testing, & E2E testing
|
||||
- Object-Oriented Programming (OOP)
|
||||
- Functional Programming (FP)
|
||||
- SOLID Principles
|
||||
- REST & Microservices
|
||||
- CI/CD, Infrastructure as Code
|
||||
- RDBMS
|
||||
- NoSQL
|
||||
- Agile & Scrum
|
||||
- Unix philosophy
|
||||
- Responsive design
|
||||
- IoC & Dependency Injection
|
||||
`
|
||||
1
src/ui/Tech/index.ts
Normal file
1
src/ui/Tech/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./Tech"
|
||||
9
src/ui/shared/Markdown.ts
Normal file
9
src/ui/shared/Markdown.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Marked } from "marked"
|
||||
import { gfmHeadingId } from "marked-gfm-heading-id"
|
||||
|
||||
export default function Markdown(md: string) {
|
||||
return div.markdown(raw(marked.parse(md)))
|
||||
}
|
||||
|
||||
const marked = new Marked({ gfm: true })
|
||||
marked.use(gfmHeadingId())
|
||||
Loading…
Add table
Add a link
Reference in a new issue