Skip to content

Commit

Permalink
Timeline for zemn.me
Browse files Browse the repository at this point in the history
  • Loading branch information
Zemnmez committed Jun 16, 2023
1 parent fdd1325 commit 870bd5f
Show file tree
Hide file tree
Showing 11 changed files with 335 additions and 21 deletions.
7 changes: 7 additions & 0 deletions project/zemn.me/bio/bio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ export const Bio: Bio = {
],
timeline: [
// BEGIN TOOL ASSISTED SORT
{
date: date(23, 'nov', 2022),
description: en`Google research; exploit to remotely take over VSCode and any attached cloud systems`,
tags: [security, disclosure],
title: en`Visual Studio Code: Remote Code Execution`,
url: url`https://github.com/google/security-research/security/advisories/GHSA-pw56-c55x-cm9m`
},

{
date: date(14, 'jan', 2019),
Expand Down
2 changes: 1 addition & 1 deletion project/zemn.me/next/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ ts_project(
assets = glob(["**/*.css"]),
deps = [
"//:node_modules/@types/react",
"//:node_modules/immutable",
"//:node_modules/next",
"//:node_modules/react",
"//:node_modules/immutable",
"//project/zemn.me/bio",
"//project/zemn.me/elements/TimeEye",
"//ts/next.js",
Expand Down
6 changes: 6 additions & 0 deletions project/zemn.me/next/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'project/zemn.me/next/pages/base.css';

import type { AppProps } from 'next/app';
import Head from 'next/head';
import { HeaderTags } from 'ts/next.js';
Expand All @@ -11,6 +13,10 @@ export function App({ Component, pageProps }: AppProps) {
<meta content="@zemnmez" name="twitter:site" />
<meta content="@zemnnmez" name="twitter:creator" />
<meta content="zemnmez" name="author" />
<meta
content="width=device-width,initial-scale=1,shrink-to-fit=no"
name="viewport"
/>
</Head>
</>
);
Expand Down
24 changes: 24 additions & 0 deletions project/zemn.me/next/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Head, Html, Main, NextScript } from 'next/document';

export default function Document() {
return (
<Html>
<Head>
<link href="https://fonts.googleapis.com" rel="preconnect" />
<link
crossOrigin="anonymous"
href="https://fonts.gstatic.com"
rel="preconnect"
/>
<link
href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,700;1,400;1,700&display=swap"
rel="stylesheet"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
13 changes: 13 additions & 0 deletions project/zemn.me/next/pages/base.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
html, body, :root {
margin: 0;
padding: 0;
}

body {
font-family: 'Lora', serif;
}

a {
font-style: italic;
color: currentColor;
}
25 changes: 25 additions & 0 deletions project/zemn.me/next/pages/dotted_divider.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.dottedDivider {
display: grid;
grid: ". line-left . text . line-right ." min-content/.2em 1fr 2rem min-content 2rem 1fr .2em
}

.dottedDivider::before, .dottedDivider::after {
border-top: 1px solid currentColor;
content: "";
height: 0;
margin: auto;
width: 100%;
}

.dottedDivider::before {
grid-area: line-left;
}

.dottedDivider::after {
grid-area: line-right;
}

.dottedDivider > * {
font-weight: initial;
grid-area: text;
}
13 changes: 9 additions & 4 deletions project/zemn.me/next/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Head from 'next/head';
import * as bio from 'project/zemn.me/bio';
import { TimeEye } from 'project/zemn.me/elements/TimeEye';
import Timeline from 'project/zemn.me/next/pages/timeline';
import * as lang from 'ts/react/lang';

export default function Main() {
Expand All @@ -24,17 +25,19 @@ export default function Main() {
<main>
<section>
<header>
<TimeEye />
<h2 lang={lang.get(bio.Bio.who.fullName)}>
{lang.text(bio.Bio.who.fullName)}
</h2>
<TimeEye />
<p>I am an internationally recognised international expert on computer security, with specialisms in web security, security program construction, and automated security analysis.</p>
<p>I am interested in consulting on legal cases. For business, email me at <a href="mailto:thomas@shadwell.im">thomas@shadwell.im</a>.</p>
</header>
{bio.Bio.links !== undefined ? (
<nav>
{bio.Bio.links.map(([text, url]) => (
<a
href={url.toString()}
key={lang.get(text)}
key={url.toString()}
lang={lang.get(text)}
>
{lang.text(text)}
Expand All @@ -43,8 +46,10 @@ export default function Main() {
</nav>
) : null}
</section>
<TimeEye />
<section>
<Timeline />
</section>
</main>
</>
);
);;
}
75 changes: 75 additions & 0 deletions project/zemn.me/next/pages/timeline.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
.year {
display: grid;
grid:
'....... ......... ........ ........... ....... .......... .....' 1em
'....... line-left ........ year ....... line-right .....'
'....... line-left ........ age ....... line-right .....'
'....... ......... ........ ........... ....... .......... .....' 1em
'....... content content content content content .....'
/ .2em 1fr 1em min-content 1em 1fr .2em
}

.year::before, .year::after {
border-top: 1px solid currentColor;
content: "";
height: 0;
margin: auto;
width: 100%;
}

.year::before {
grid-area: line-left;
}

.year::after {
grid-area: line-right;
}



.month {
display: grid;
grid:

'........ ....... ..... ....... ......' .2em
'........ ....... month ....... ......'
'........ ....... ....... ....... ......' .2em
'........ content content content ......'
'........ ....... ....... ....... ......' .2em
/.5em 1fr min-content 1fr .5em;
width: 30em;
}

.monthName {
grid-area: month;
font-style: italic;
}

.event {
max-width: 30em;
margin-bottom: 1em;
width: 100%;
}

.event::before {
margin-left: 3em;
}

.yearIndicator, .ageIndicator {
margin: auto;
}

.yearIndicator {
grid-area: year;
}

.ageIndicator {
grid-area: age;
}

.content {
grid-area: content;
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
150 changes: 150 additions & 0 deletions project/zemn.me/next/pages/timeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import Immutable from 'immutable';
import React from 'react';
import * as Bio from 'project/zemn.me/bio';
import * as lang from 'ts/react/lang';
import style from 'project/zemn.me/next/pages/timeline.module.css';


const numerals = [
[3000, 'MMM'],
[2000, 'MM'],
[1000, 'M'],
[900, 'CM'],
[800, 'DCCC'],
[700, 'DCC'],
[600, 'DC'],
[500, 'D'],
[400, 'CD'],
[300, 'CCC'],
[200, 'CC'],
[100, 'C'],
[90, 'XC'],
[80, 'LXXX'],
[70, 'LXX'],
[60, 'LX'],
[50, 'L'],
[40, 'XL'],
[30, 'XXX'],
[20, 'XX'],
[10, 'X'],
[9, 'IX'],
[8, 'VIII'],
[7, 'VII'],
[6, 'VI'],
[5, 'V'],
[4, 'IV'],
[3, 'III'],
[2, 'II'],
[1, 'I'],
] as const;

const romanize = (n: number) => {
const on = n;
const parts: string[] = [];
while (n > 0) {
for (const [value, sym] of numerals) {
if (n < value) continue;
parts.push(sym);
n -= value;
break;
}
}

return parts.join('');
};


/**
* Given a map that contains a set, set a single item, expanding or creating
* the set if needed.
* @param v the map
* @param key the key in the map
* @param value the item to set
* @returns the same map that was put in
*/
function setManyMap<K, V>(
v: Immutable.OrderedMap<K, Immutable.List<V>>,
key: K,
value: V
): Immutable.OrderedMap<K, Immutable.List<V>> {
return v.set(key, v.get(key, Immutable.List<V>()).concat(value));
}

function groupBy<T, Q>(
i: Iterable<T>,
select: (v: T) => Q
): Immutable.Map<Q, Immutable.List<T>> {
let m = Immutable.OrderedMap<Q, Immutable.List<T>>();
for (const v of i) m = setManyMap(m, select(v), v);

return m;
}

function Event({event: e}: {event: Bio.Event}) {
return <div className={style.event}>
<a href={e.url?.toString()} lang={lang.get(e.title)}>
{lang.text(e.title)}
</a>

{" "}

{
e.description
? <span lang={lang.get(e.description)}>
{lang.text(e.description)}
</span>
: null
}
</div>
}

function Month({month, events}: { month: string, events: Iterable<Bio.Event>}) {
return <div className={style.month}>
<header className={style.monthName} lang="en-GB"> { /* <- this needs to be set to the correct language */ }
{month}
</header>
<div className={style.content}>
{
[...events].map((e, i) => <Event event={e} key={i}/>)
}
</div>
</div>
}

function Year({year, months}: {year: string, months: Immutable.OrderedMap<string, Immutable.List<Bio.Event>> }) {
return <div className={style.year}>
<div className={style.yearIndicator}>{year}</div>
<div className={style.ageIndicator}>{romanize((months.first(undefined)?.first()?.date.getFullYear() ?? 0) - 1994)}</div>

<div className={style.content}>
{
[...months].map(([month, events], i) => <Month month={month} events={events} key={month}/>)
}
</div>
</div>
}

export default function Timeline() {
// this spread is just because Intl.DateTimeFormat expects a mutable array.
const locale = [...lang.useLocale()];

const years = React.useMemo(() => groupBy(Immutable.List(Bio.Bio.timeline).map(
event => {
// depending on locale there may be different numbers of months etc.
const month = Intl.DateTimeFormat(locale, { month: 'long' }).format(event.date);
const year = Intl.DateTimeFormat(locale, { year: 'numeric' }).format(event.date);

return {...event, month, year}
}
).sort((a, b) => +b.date - +a.date), v => v.year).map(
v => groupBy(v, i => i.month)
), [ locale]);

return <>
{
[...years].map(([year, months], i) =>
<Year year={year} months={months} key={year}/>
)
}
</>
}
Loading

0 comments on commit 870bd5f

Please sign in to comment.