Compare commits
1 commit
68ece9f36c
...
e8fc4ce653
Author | SHA1 | Date | |
---|---|---|---|
e8fc4ce653 |
76 changed files with 2810 additions and 534 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -12,6 +12,6 @@ node_modules/
|
||||||
# custom
|
# custom
|
||||||
.DS_Store
|
.DS_Store
|
||||||
# vuepress
|
# vuepress
|
||||||
/docs/.vuepress/.cache
|
docs/.vuepress/.cache
|
||||||
/docs/.vuepress/.temp
|
docs/.vuepress/.temp
|
||||||
/docs/.vuepress/dist
|
docs/.vuepress/dist
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -1,16 +1,14 @@
|
||||||
PREFIX ?= dist
|
NPM ?= npm
|
||||||
|
|
||||||
help:
|
help:
|
||||||
cat Makefile
|
cat makefile
|
||||||
|
|
||||||
build:
|
build:
|
||||||
pnpm run docs:build --dest $(PREFIX)
|
$(NPM) run docs:build
|
||||||
|
|
||||||
install: build
|
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
pnpm run docs:dev
|
$(NPM) run docs:dev
|
||||||
|
|
||||||
deps:
|
deps:
|
||||||
pnpm install
|
$(NPM) install
|
||||||
|
|
||||||
|
|
43
dist/404.html
vendored
43
dist/404.html
vendored
|
@ -1,43 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="ru-RU">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
||||||
<meta name="generator" content="VuePress 2.0.0-rc.14" />
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--c-bg: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
html.dark {
|
|
||||||
--c-bg: #22272e;
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
background-color: var(--c-bg);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
const userMode = localStorage.getItem('vuepress-color-scheme')
|
|
||||||
const systemDarkMode =
|
|
||||||
window.matchMedia &&
|
|
||||||
window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
||||||
|
|
||||||
if (userMode === 'light') {
|
|
||||||
document.documentElement.dataset.theme = 'light'
|
|
||||||
} else if (userMode === 'dark' || systemDarkMode) {
|
|
||||||
document.documentElement.classList.toggle('dark', true)
|
|
||||||
document.documentElement.dataset.theme = 'dark'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<meta property="og:url" content="https://pleshevski.ru/404.html"><meta property="og:site_name" content="Дмитрий Плешевский"><meta property="og:description" content="404 Not Found"><meta property="og:type" content="website"><meta property="og:locale" content="ru-RU"><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","name":"","description":"404 Not Found"}</script><title>Дмитрий Плешевский</title><meta name="description" content="404 Not Found">
|
|
||||||
<link rel="preload" href="/assets/style-BnNdFOI8.css" as="style"><link rel="stylesheet" href="/assets/style-BnNdFOI8.css">
|
|
||||||
<link rel="modulepreload" href="/assets/app-Dw1tezwH.js"><link rel="modulepreload" href="/assets/404.html-C-XD0uTd.js">
|
|
||||||
<link rel="prefetch" href="/assets/index.html-Bgp5oohT.js" as="script"><link rel="prefetch" href="/assets/works.html-sG2zmfnZ.js" as="script"><link rel="prefetch" href="/assets/index.html-Bmcvetlf.js" as="script"><link rel="prefetch" href="/assets/works.html-DIDCwbnz.js" as="script">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="app"><!--[--><div class="vp-theme-container" data-v-99131a8e><main class="page" data-v-99131a8e><div class="theme-default-content" data-v-99131a8e><h1 data-v-99131a8e>404</h1><blockquote data-v-99131a8e>Мы потеряли страницу...</blockquote><a class="route-link" href="/" data-v-99131a8e>Вернуться на главную</a></div></main></div><!--[--><!----><!--]--><!--]--></div>
|
|
||||||
<script type="module" src="/assets/app-Dw1tezwH.js" defer></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
1
dist/assets/404.html-C-XD0uTd.js
vendored
1
dist/assets/404.html-C-XD0uTd.js
vendored
|
@ -1 +0,0 @@
|
||||||
import{_ as t,c as e,o,a as n}from"./app-Dw1tezwH.js";const r={},a=n("p",null,"404 Not Found",-1),c=[a];function s(p,l){return o(),e("div",null,c)}const d=t(r,[["render",s],["__file","404.html.vue"]]),m=JSON.parse('{"path":"/404.html","title":"","lang":"ru-RU","frontmatter":{"layout":"NotFound","description":"404 Not Found","head":[["meta",{"property":"og:url","content":"https://pleshevski.ru/404.html"}],["meta",{"property":"og:site_name","content":"Дмитрий Плешевский"}],["meta",{"property":"og:description","content":"404 Not Found"}],["meta",{"property":"og:type","content":"website"}],["meta",{"property":"og:locale","content":"ru-RU"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"WebPage\\",\\"name\\":\\"\\",\\"description\\":\\"404 Not Found\\"}"]]},"headers":[],"git":{},"autoDesc":true,"filePathRelative":null}');export{d as comp,m as data};
|
|
32
dist/assets/app-Dw1tezwH.js
vendored
32
dist/assets/app-Dw1tezwH.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/index.html-Bgp5oohT.js
vendored
1
dist/assets/index.html-Bgp5oohT.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/index.html-Bmcvetlf.js
vendored
1
dist/assets/index.html-Bmcvetlf.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/style-BnNdFOI8.css
vendored
1
dist/assets/style-BnNdFOI8.css
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/works.html-DIDCwbnz.js
vendored
1
dist/assets/works.html-DIDCwbnz.js
vendored
File diff suppressed because one or more lines are too long
1
dist/assets/works.html-sG2zmfnZ.js
vendored
1
dist/assets/works.html-sG2zmfnZ.js
vendored
File diff suppressed because one or more lines are too long
43
dist/eng/index.html
vendored
43
dist/eng/index.html
vendored
File diff suppressed because one or more lines are too long
43
dist/eng/works.html
vendored
43
dist/eng/works.html
vendored
File diff suppressed because one or more lines are too long
43
dist/index.html
vendored
43
dist/index.html
vendored
File diff suppressed because one or more lines are too long
5
dist/robots.txt
vendored
5
dist/robots.txt
vendored
|
@ -1,5 +0,0 @@
|
||||||
|
|
||||||
User-agent:*
|
|
||||||
Disallow:
|
|
||||||
|
|
||||||
Sitemap: https://pleshevski.ru/sitemap.xml
|
|
17
dist/rus/index.html
vendored
17
dist/rus/index.html
vendored
|
@ -1,17 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
<meta http-equiv="refresh" content="0; url=/index.html">
|
|
||||||
<link rel="canonical" href="/index.html">
|
|
||||||
<title>Redirecting...</title>
|
|
||||||
<script>
|
|
||||||
const anchor = window.location.hash.substring(1);
|
|
||||||
location.href = `/index.html${anchor? `#${anchor}`: ""}`;
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Redirecting...</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
17
dist/rus/works.html
vendored
17
dist/rus/works.html
vendored
|
@ -1,17 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="robots" content="noindex">
|
|
||||||
<meta http-equiv="refresh" content="0; url=/works.html">
|
|
||||||
<link rel="canonical" href="/works.html">
|
|
||||||
<title>Redirecting...</title>
|
|
||||||
<script>
|
|
||||||
const anchor = window.location.hash.substring(1);
|
|
||||||
location.href = `/works.html${anchor? `#${anchor}`: ""}`;
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<p>Redirecting...</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
3
dist/sitemap.xml
vendored
3
dist/sitemap.xml
vendored
|
@ -1,3 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<?xml-stylesheet type="text/xsl" href="/sitemap.xsl"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://pleshevski.ru/</loc><lastmod>2024-07-24T14:15:05.000Z</lastmod><changefreq>daily</changefreq><xhtml:link rel="alternate" hreflang="ru-RU" href="https://pleshevski.ru/"/><xhtml:link rel="alternate" hreflang="en-US" href="https://pleshevski.ru/eng/"/></url><url><loc>https://pleshevski.ru/works.html</loc><lastmod>2024-07-24T14:15:05.000Z</lastmod><changefreq>daily</changefreq><xhtml:link rel="alternate" hreflang="ru-RU" href="https://pleshevski.ru/works.html"/><xhtml:link rel="alternate" hreflang="en-US" href="https://pleshevski.ru/eng/works.html"/></url><url><loc>https://pleshevski.ru/eng/</loc><lastmod>2024-07-24T14:15:05.000Z</lastmod><changefreq>daily</changefreq><xhtml:link rel="alternate" hreflang="ru-RU" href="https://pleshevski.ru/"/><xhtml:link rel="alternate" hreflang="en-US" href="https://pleshevski.ru/eng/"/></url><url><loc>https://pleshevski.ru/eng/works.html</loc><lastmod>2024-07-24T14:15:05.000Z</lastmod><changefreq>daily</changefreq><xhtml:link rel="alternate" hreflang="ru-RU" href="https://pleshevski.ru/works.html"/><xhtml:link rel="alternate" hreflang="en-US" href="https://pleshevski.ru/eng/works.html"/></url></urlset>
|
|
207
dist/sitemap.xsl
vendored
207
dist/sitemap.xsl
vendored
|
@ -1,207 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<xsl:stylesheet version="2.0"
|
|
||||||
xmlns:html="http://www.w3.org/TR/REC-html40"
|
|
||||||
xmlns:sitemap="http://www.sitemaps.org/schemas/sitemap/0.9"
|
|
||||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
|
||||||
<xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes" />
|
|
||||||
<xsl:template match="/">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
||||||
<head>
|
|
||||||
<title>XML Sitemap</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0;" />
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color: #f8f8f8;
|
|
||||||
--bg-color-secondary: #fff;
|
|
||||||
--text-color: #2c3e50;
|
|
||||||
--border-color: #eaecef;
|
|
||||||
--brand-color: #3eaf7c;
|
|
||||||
|
|
||||||
color-scheme: light dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
:root {
|
|
||||||
--bg-color: #0d1117;
|
|
||||||
--bg-color-secondary: #161b22;
|
|
||||||
--text-color: #ccc;
|
|
||||||
--border-color: #30363d;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
background: var(--bg-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
min-height: 100vh;
|
|
||||||
color: var(--text-color);
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content {
|
|
||||||
max-width: 960px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
margin-top: 1rem;
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 419px) {
|
|
||||||
h1 {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--text-color);
|
|
||||||
font-weight: 500;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
border-radius: 8px;
|
|
||||||
border-collapse: collapse;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 419px) {
|
|
||||||
table {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
min-width: 56px;
|
|
||||||
padding: 0.6em 1em;
|
|
||||||
|
|
||||||
background-color: var(--brand-color);
|
|
||||||
color: var(--bg-color);
|
|
||||||
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 719px) {
|
|
||||||
th {
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
th:first-child {
|
|
||||||
text-align: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:nth-child(odd) {
|
|
||||||
background: var(--bg-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
tr:hover {
|
|
||||||
background-color: #e8e8e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
|
||||||
tr:hover {
|
|
||||||
background-color: #333;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
padding: 0.6em 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 719px) {
|
|
||||||
td {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td:first-child {
|
|
||||||
text-align: start;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 4px;
|
|
||||||
|
|
||||||
color: #888;
|
|
||||||
|
|
||||||
font-size: 12px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="content">
|
|
||||||
<h1>Sitemap</h1>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<xsl:value-of select="concat('URL (', count(sitemap:urlset/sitemap:url), ')')" />
|
|
||||||
</th>
|
|
||||||
<th>Priority</th>
|
|
||||||
<th>Change Frequency</th>
|
|
||||||
<th>Last Updated Time</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'" />
|
|
||||||
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
|
|
||||||
<xsl:for-each select="sitemap:urlset/sitemap:url">
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<xsl:variable name="itemURL">
|
|
||||||
<xsl:value-of select="sitemap:loc" />
|
|
||||||
</xsl:variable>
|
|
||||||
<a href="{$itemURL}" target="_blank">
|
|
||||||
<xsl:value-of select="sitemap:loc" />
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:choose>
|
|
||||||
<xsl:when test="sitemap:priority">
|
|
||||||
<xsl:value-of select="concat(sitemap:priority*100,'%a')" />
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:otherwise>
|
|
||||||
<xsl:text>0.5</xsl:text>
|
|
||||||
</xsl:otherwise>
|
|
||||||
</xsl:choose>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:choose>
|
|
||||||
<xsl:when test="sitemap:changefreq">
|
|
||||||
<xsl:value-of select="concat(translate(substring(sitemap:changefreq, 1, 1),concat($lower, $upper),concat($upper, $lower)),substring(sitemap:changefreq, 2))" />
|
|
||||||
</xsl:when>
|
|
||||||
<xsl:otherwise>
|
|
||||||
<xsl:text>-</xsl:text>
|
|
||||||
</xsl:otherwise>
|
|
||||||
</xsl:choose>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<xsl:value-of select="concat(substring(sitemap:lastmod,0,11),concat(' ', substring(sitemap:lastmod,12,5)))" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</xsl:for-each>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<footer>
|
|
||||||
Generatd by <a href="https://ecosystem.vuejs.press/plugins/sitemap/">@vuepress/plugin-sitemap</a>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
</xsl:template>
|
|
||||||
</xsl:stylesheet>
|
|
43
dist/works.html
vendored
43
dist/works.html
vendored
File diff suppressed because one or more lines are too long
25
flake.nix
25
flake.nix
|
@ -9,32 +9,13 @@
|
||||||
outputs = { self, nixpkgs, flake-utils }:
|
outputs = { self, nixpkgs, flake-utils }:
|
||||||
let
|
let
|
||||||
out = system:
|
out = system:
|
||||||
let
|
let pkgs = import nixpkgs { inherit system; };
|
||||||
inherit (builtins) substring;
|
in {
|
||||||
|
|
||||||
pkgs = import nixpkgs { inherit system; };
|
|
||||||
version = "0.0.1+${substring 0 8 self.lastModifiedDate}_${self.shortRev or "dirty"}";
|
|
||||||
in
|
|
||||||
{
|
|
||||||
packages.default = with pkgs; stdenv.mkDerivation (finalAttrs: {
|
|
||||||
pname = "pleshevski_site";
|
|
||||||
inherit version;
|
|
||||||
|
|
||||||
src = ./.;
|
|
||||||
|
|
||||||
dontBuild = true;
|
|
||||||
|
|
||||||
installPhase = ''
|
|
||||||
mkdir -p $out
|
|
||||||
cp -r dist/* $out
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
|
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = with pkgs; [
|
||||||
nodejs_22
|
nodejs_22
|
||||||
gnumake
|
gnumake
|
||||||
pnpm
|
nodePackages.pnpm
|
||||||
nodePackages.typescript-language-server # typescript
|
nodePackages.typescript-language-server # typescript
|
||||||
nodePackages.vscode-langservers-extracted # html, css, json, eslint
|
nodePackages.vscode-langservers-extracted # html, css, json, eslint
|
||||||
];
|
];
|
||||||
|
|
13
old/.dockerignore
Normal file
13
old/.dockerignore
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
!/data
|
||||||
|
!/public
|
||||||
|
!/styles
|
||||||
|
!/translates
|
||||||
|
!/views
|
||||||
|
!/uikit
|
||||||
|
!/modules
|
||||||
|
|
||||||
|
!/*.ts
|
||||||
|
!/*.json
|
||||||
|
|
19
old/.woodpecker.yml
Normal file
19
old/.woodpecker.yml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
when:
|
||||||
|
branch: main
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build-docker-image
|
||||||
|
image: plugins/docker
|
||||||
|
settings:
|
||||||
|
repo: ${ORG_REGISTRY}/${CI_REPO}
|
||||||
|
tags:
|
||||||
|
- "${CI_COMMIT_BRANCH}"
|
||||||
|
- "${CI_COMMIT_SHA:0:8}"
|
||||||
|
|
||||||
|
- name: deploy
|
||||||
|
image: ${ORG_REGISTRY}/drone_plugins/docker_stack
|
||||||
|
pull: true
|
||||||
|
environment:
|
||||||
|
PLESHEVSKI_IMAGE: ${ORG_REGISTRY}/${CI_REPO}:${CI_COMMIT_SHA:0:8}
|
||||||
|
settings:
|
||||||
|
name: pleshevski
|
13
old/Dockerfile
Normal file
13
old/Dockerfile
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
FROM denoland/deno:alpine-1.22.1
|
||||||
|
|
||||||
|
EXPOSE 33334
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
USER deno
|
||||||
|
|
||||||
|
ADD . .
|
||||||
|
# Compile the main app so that it doesn't need to be compiled each startup/entry.
|
||||||
|
RUN deno cache server.ts
|
||||||
|
|
||||||
|
CMD ["run", "-A", "server.ts"]
|
25
old/context.ts
Normal file
25
old/context.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import { Translations } from "./translates/rus.ts";
|
||||||
|
|
||||||
|
export interface Context {
|
||||||
|
title?: string;
|
||||||
|
locPath: string;
|
||||||
|
lang: Lang;
|
||||||
|
tr: Translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLangHref(lang: Lang, url: string): string {
|
||||||
|
return getLangUrlPrefix(lang) + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLangUrlPrefix(lang: Lang): string {
|
||||||
|
return `/${lang}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function iterLangs(): Lang[] {
|
||||||
|
return [Lang.Eng, Lang.Rus];
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Lang {
|
||||||
|
Rus = "rus",
|
||||||
|
Eng = "eng",
|
||||||
|
}
|
105
old/data/about/eng.md
Normal file
105
old/data/about/eng.md
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
###
|
||||||
|
|
||||||
|
Always up-to-date link to [resume](https://pleshevski.ru/eng/).
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
|
||||||
|
My name is Dmitriy Pleshevskiy.
|
||||||
|
|
||||||
|
I'm an open source software enthusiast, a lead software developer, architect,
|
||||||
|
team leader and also mentor.
|
||||||
|
|
||||||
|
### Skills
|
||||||
|
|
||||||
|
Programming Languages:
|
||||||
|
|
||||||
|
- TypeScript (prefer, solid 9-year exp)
|
||||||
|
- SQL (prefer, solid 8-year exp)
|
||||||
|
- Rust (prefer, solid 5-year exp)
|
||||||
|
- Python (solid 9-year exp)
|
||||||
|
- Haskell
|
||||||
|
- Bash
|
||||||
|
- Java
|
||||||
|
- C#
|
||||||
|
- C++
|
||||||
|
|
||||||
|
Databases:
|
||||||
|
|
||||||
|
- PostgreSQL (prefer, solid 7-year exp)
|
||||||
|
- MySQL
|
||||||
|
- Sqlite
|
||||||
|
- MsSQL
|
||||||
|
- MongoDB
|
||||||
|
- Reddis
|
||||||
|
|
||||||
|
I also have extensive experience in creating the following applications:
|
||||||
|
|
||||||
|
- Traditional (SSR + Forms)
|
||||||
|
- API (REST/GraphQL/WebSocket/EventSource)
|
||||||
|
- Dynamic (SPA)
|
||||||
|
- Hybrid (SSR + SPA)
|
||||||
|
- Console
|
||||||
|
- Crossplatform
|
||||||
|
|
||||||
|
### Stack
|
||||||
|
|
||||||
|
Backend (Rust)
|
||||||
|
|
||||||
|
- axum (prefer, solid 2-year exp)
|
||||||
|
- async-graphql (prefer, solid 2-year exp)
|
||||||
|
- shaku (prefer, solid 2-year exp)
|
||||||
|
- bb8 + postgres-types (prefer, solid 5-year exp)
|
||||||
|
- diesel (2-year exp)
|
||||||
|
|
||||||
|
Backend (Node.JS)
|
||||||
|
|
||||||
|
- Apollo (solid 5-year exp)
|
||||||
|
- Express (solid 9-year exp)
|
||||||
|
- Nest.JS
|
||||||
|
- Knex.js / Objection.js (solid 5-year exp)
|
||||||
|
- Sequelize
|
||||||
|
|
||||||
|
Frontend
|
||||||
|
|
||||||
|
- React (solid 8-year exp)
|
||||||
|
- VueJS (prefer, solid 3-year exp)
|
||||||
|
- Cypress (prefer, solid 3-year exp)
|
||||||
|
- JQuery
|
||||||
|
- Antd / Antdv
|
||||||
|
- PostCSS (prefer, solid 5-year exp)
|
||||||
|
- Sass (prefer, solid 8-year exp)
|
||||||
|
- Less (weak 4-year exp)
|
||||||
|
|
||||||
|
DevOps
|
||||||
|
|
||||||
|
- NixOS / NixOps / Nix dev shell (prefer, solid 2-year exp)
|
||||||
|
- Docker Swarm (prefer, solid 5-year exp)
|
||||||
|
- Kubernetes (weak 4-year exp)
|
||||||
|
- Woodpecker CI (prefer, solid 3-year exp)
|
||||||
|
- Drone CI (solid 3-year exp)
|
||||||
|
- Gitlab CI (solid 7-year exp)
|
||||||
|
- GitHub Actions (3-year exp)
|
||||||
|
|
||||||
|
### Interests
|
||||||
|
|
||||||
|
Open-source projects are my passion! I develop, maintain and improve projects in
|
||||||
|
my spare time.
|
||||||
|
|
||||||
|
Besides programming, I love to cook and spend time with my beloved family!
|
||||||
|
|
||||||
|
### Contacts
|
||||||
|
|
||||||
|
SimpleX:
|
||||||
|
[Dmitriy Pleshevskiy](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FSkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w%3D%40smp9.simplex.im%2FLfKyG0YgW5eRO-z8vrEyvnNfV2EKDfBv%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAMRpR3YB10GVzc-asfqY2oIFkipx5RQm4DZRabzjfPHo%253D%26srv%3Djssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion)
|
||||||
|
|
||||||
|
Telegram: [Dmitriy Pleshevskiy](https://telegram.me/da_pranaya)
|
||||||
|
|
||||||
|
Matrix: @pleshevskiy:matrix.org
|
||||||
|
|
||||||
|
Email: dmitriy[at]pleshevski[dot]ru
|
||||||
|
|
||||||
|
### Links
|
||||||
|
|
||||||
|
[My Git Repo](https://git.pleshevski.ru/)
|
||||||
|
|
||||||
|
[My Github (Suspended due to sanctions)](https://github.com/pleshevskiy)
|
107
old/data/about/rus.md
Normal file
107
old/data/about/rus.md
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
###
|
||||||
|
|
||||||
|
Всегда актуальная ссылка на [резюме](https://pleshevski.ru/rus/).
|
||||||
|
|
||||||
|
### Общие сведения
|
||||||
|
|
||||||
|
Меня зовут Дмитрий Плешевский.
|
||||||
|
|
||||||
|
Я энтузиаст программного обеспечения с открытым исходным кодом, ведущий
|
||||||
|
разработчик програмного обеспечения, архитектор, руководитель команды, а так же
|
||||||
|
ментор.
|
||||||
|
|
||||||
|
### Умения
|
||||||
|
|
||||||
|
Языки программирования:
|
||||||
|
|
||||||
|
- TypeScript (предпочитаю, твёрдый 9-летний опыт)
|
||||||
|
- SQL (предпочитаю, твёрдый 8-летний опыт)
|
||||||
|
- Rust (предпочитаю, 5-летний опыт)
|
||||||
|
- Python (твёрдый 9-летний опыт)
|
||||||
|
- Haskell
|
||||||
|
- Bash
|
||||||
|
- Java
|
||||||
|
- C#
|
||||||
|
- C++
|
||||||
|
|
||||||
|
Базы данных:
|
||||||
|
|
||||||
|
- PostgreSQL (предпочитаю, твёрдый 7-летний опыт)
|
||||||
|
- MySQL
|
||||||
|
- Sqlite
|
||||||
|
- MsSQL
|
||||||
|
- MongoDB
|
||||||
|
- Reddis
|
||||||
|
|
||||||
|
Я так же имею большой опыт в создании следующих типов приложений:
|
||||||
|
|
||||||
|
- Традиционные (SSR + Forms)
|
||||||
|
- API (REST/GraphQL/WebSocket/EventSource)
|
||||||
|
- Динамическое (SPA)
|
||||||
|
- Гибридное (SSR + SPA)
|
||||||
|
- Консольные
|
||||||
|
- Кроссплатформенные
|
||||||
|
|
||||||
|
### Stack
|
||||||
|
|
||||||
|
Backend (Rust)
|
||||||
|
|
||||||
|
- axum (предпочитаю, твёрдый 2-летний опыт)
|
||||||
|
- async-graphql (предпочитаю, твёрдый 2-летний опыт)
|
||||||
|
- shaku (предпочитаю, твёрдый 2-летний опыт)
|
||||||
|
- bb8 + postgres-types (предпочитаю, твёрдый 5-летний опыт)
|
||||||
|
- diesel (2-летний опыт)
|
||||||
|
|
||||||
|
Backend (Node.JS)
|
||||||
|
|
||||||
|
- Apollo (твёрдый 5-летний опыт)
|
||||||
|
- Express (твёрдый 9-летний опыт)
|
||||||
|
- Nest.JS
|
||||||
|
- Knex.js / Objection.js (твёрдый 5-летний опыт)
|
||||||
|
- Sequelize
|
||||||
|
|
||||||
|
Frontend
|
||||||
|
|
||||||
|
- React (твёрдый 8-летний опыт)
|
||||||
|
- VueJS (предпочитаю, твёрдый 3-летний опыт)
|
||||||
|
- Cypress (предпочитаю, твёрдый 3-летний опыт)
|
||||||
|
- JQuery
|
||||||
|
- Antd / Antdv
|
||||||
|
- PostCSS (предпочитаю, твёрдый 5-летний опыт)
|
||||||
|
- Sass (предпочитаю, твёрдый 8-летний опыт)
|
||||||
|
- Less (слабый 4-летний опыт)
|
||||||
|
|
||||||
|
DevOps
|
||||||
|
|
||||||
|
- NixOS / NixOps / Nix dev shell (предпочитаю, твёрдый 2-летний опыт)
|
||||||
|
- Docker Swarm (предпочитаю, твёрдый 5-летний опыт)
|
||||||
|
- Kubernetes (слабый 4-летний опыт)
|
||||||
|
- Woodpecker CI (предпочитаю, твёрдый 3-летний опыт)
|
||||||
|
- Drone CI (твёрдый 3-летний опыт)
|
||||||
|
- Gitlab CI (твёрдый 7-летний опыт)
|
||||||
|
- GitHub Actions (3-летний опыт)
|
||||||
|
|
||||||
|
### Интересы
|
||||||
|
|
||||||
|
Open-source проекты - моя страсть! Разрабатываю, поддерживаю и улучшаю проекты в
|
||||||
|
своё свободное время.
|
||||||
|
|
||||||
|
Помимо программирования я люблю готовить и проводить время со своей любимой
|
||||||
|
семьей!
|
||||||
|
|
||||||
|
### Контакты
|
||||||
|
|
||||||
|
SimpleX:
|
||||||
|
[Dmitriy Pleshevskiy](https://simplex.chat/contact#/?v=1-2&smp=smp%3A%2F%2FSkIkI6EPd2D63F4xFKfHk7I1UGZVNn6k1QWZ5rcyr6w%3D%40smp9.simplex.im%2FLfKyG0YgW5eRO-z8vrEyvnNfV2EKDfBv%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAMRpR3YB10GVzc-asfqY2oIFkipx5RQm4DZRabzjfPHo%253D%26srv%3Djssqzccmrcws6bhmn77vgmhfjmhwlyr3u7puw4erkyoosywgl67slqqd.onion)
|
||||||
|
|
||||||
|
Telegram: [Dmitriy Pleshevskiy](https://telegram.me/da_pranaya)
|
||||||
|
|
||||||
|
Matrix: @pleshevskiy:matrix.org
|
||||||
|
|
||||||
|
Email: dmitriy[at]pleshevski[dot]ru
|
||||||
|
|
||||||
|
### Ссылки
|
||||||
|
|
||||||
|
[My Git Repo](https://git.pleshevski.ru/)
|
||||||
|
|
||||||
|
[My Github (Приостановлен из-за санкций)](https://github.com/pleshevskiy)
|
64
old/data/works/eng.md
Normal file
64
old/data/works/eng.md
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
### Highlighted working experience
|
||||||
|
|
||||||
|
#### Binary Management
|
||||||
|
|
||||||
|
- Dates: August 2018 – currently
|
||||||
|
- Roles: Lead Fullstack Developer, Team Lead, Architect
|
||||||
|
|
||||||
|
Development of a project management tool for interior designers
|
||||||
|
|
||||||
|
- Development of the GraphQL API (Node.JS, Apollo, PostgreSQL, Redis, BullMQ).
|
||||||
|
Moved database triggers to business logic. Wrote integration tests on 70% api.
|
||||||
|
- Development of the frontend (React, Antd). Formed uikit, shared components,
|
||||||
|
redesigned the page generation gathering. Completely changed work with API on
|
||||||
|
the frontend. Introduced the practice of writing integration tests using
|
||||||
|
cypress
|
||||||
|
- Completely ported the project to TypeScript. I have formed isolated modules of
|
||||||
|
the system.
|
||||||
|
- As a team leader, I brought the critical chain method, the buffer method, and
|
||||||
|
the planning method to the project from the end. Helped the team get into a
|
||||||
|
rhythm to make releases each week in small batches. A couple of times I also
|
||||||
|
prepared an individual development plan for team members.
|
||||||
|
|
||||||
|
#### Master Progress
|
||||||
|
|
||||||
|
- Dates: May 2018 - currently (Passively maintained)
|
||||||
|
- Role: Tech Lead
|
||||||
|
|
||||||
|
Development web infrastructure of the educational center Master Progress
|
||||||
|
|
||||||
|
- Development of [the main site](https://masterprogress.ru) (Python, Flask).
|
||||||
|
- Development of [Student's cabinet](https://cabinet.masterprogress.ru) (Python,
|
||||||
|
Flask, TypeScript, React).
|
||||||
|
- Development of [a tool for rosmintrud](https://rosmintrud.masterprogress.ru)
|
||||||
|
(Deno, Vue, Typescript)
|
||||||
|
- Created a complete infrastructure on Woodpecker CI and Docker swarm.
|
||||||
|
|
||||||
|
#### Core Spirit
|
||||||
|
|
||||||
|
- Dates: August 2018 - May 2020
|
||||||
|
- Role: Lead Fullstack Developer
|
||||||
|
|
||||||
|
Development of Social platform focusing on human and planetary enhancement
|
||||||
|
|
||||||
|
- Development of the REST API (Node.JS, Express, PostgreSQL) for main site and
|
||||||
|
backoffice.
|
||||||
|
- Development of an auto poster to various social networks and messengers
|
||||||
|
(Facebook, LinkedIn, Twitter, Telegram).
|
||||||
|
- Development of a neural network for automatic categorization of articles.
|
||||||
|
|
||||||
|
#### MERLION
|
||||||
|
|
||||||
|
- Dates: March 2016 – May 2018
|
||||||
|
- Role: Senior Fullstack developer
|
||||||
|
|
||||||
|
In this company there were 6 considerable projects I have successfully
|
||||||
|
completed:
|
||||||
|
|
||||||
|
- optimize the creation of promotional pages (PHP, JavaScript)
|
||||||
|
- support main traditional site <https://citilink.ru> (PHP, JavaScript)
|
||||||
|
- development of parsing to monitor products for changes in price,
|
||||||
|
quantity/availability in stock, rating and other fields based on data from 55
|
||||||
|
websites (Node.JS, Express)
|
||||||
|
- work with neural networks for matching of goods
|
||||||
|
- development face recognition apps for Android (Java)
|
65
old/data/works/rus.md
Normal file
65
old/data/works/rus.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
### Выделенный опыт работы
|
||||||
|
|
||||||
|
#### Binary Management
|
||||||
|
|
||||||
|
- Даты: Август 2018 – по настоящее время
|
||||||
|
- Роли: Lead Fullstack Developer, Team Lead, Architect
|
||||||
|
|
||||||
|
Разработка инструмента управления проектами для дизайнеров интерьера
|
||||||
|
|
||||||
|
- Разработка GraphQL API (Node.JS, Apollo, PostgreSQL, Redis, BullMQ). Перенес
|
||||||
|
триггеры базы данных в бизнес-логику. Написал интеграционные тесты на 70% api.
|
||||||
|
- Разработка фронтенда (React, Antd). Сформировал uikit и общие компоненты,
|
||||||
|
оптимизировал сложные и нагруженные компоненты. Полностью изменил работу с API
|
||||||
|
на фронтенде. Внедрил практику написания интеграционных тестов с помощью
|
||||||
|
cypress.
|
||||||
|
- Полностью перенес проект на TypeScript. Сформировал изолированные модули
|
||||||
|
системы.
|
||||||
|
- Как руководитель команды, я привнес в проект метод критической цепи, метод
|
||||||
|
буфера и метод планирования с конца. Помог команде войти в ритм, чтобы
|
||||||
|
выпускать релизы каждую неделю небольшими партиями. Я также несколько раз
|
||||||
|
составлял индивидуальный план развития для членов команды.
|
||||||
|
|
||||||
|
#### Master Progress
|
||||||
|
|
||||||
|
- Даты: Май 2018 - по настоящее время (Пассивная поддержка)
|
||||||
|
- Роль: Tech Lead
|
||||||
|
|
||||||
|
Разработка веб-инфраструктуры образовательного центра Мастер Прогресс
|
||||||
|
|
||||||
|
- Разработка [главного сайта](https://masterprogress.ru) (Python, Flask).
|
||||||
|
- Разработка [кабинета студента](https://cabinet.masterprogress.ru) (Python,
|
||||||
|
Flask, TypeScript, React).
|
||||||
|
- Разработка
|
||||||
|
[инструмента для работы с rosmintrud](https://rosmintrud.masterprogress.ru)
|
||||||
|
(Deno, Vue, Typescript)
|
||||||
|
- Создана полная инфраструктура на Woodpecker CI и Docker swarm.
|
||||||
|
|
||||||
|
#### Core Spirit
|
||||||
|
|
||||||
|
- Даты: Август 2018 - May 2020
|
||||||
|
- Роль: Lead Fullstack Developer
|
||||||
|
|
||||||
|
Разработка социальной платформы, сфокусированной на улучшении человека и планеты
|
||||||
|
|
||||||
|
- Разработка REST API (Node.JS, Express, PostgreSQL) для основного сайта и
|
||||||
|
бэк-офиса.
|
||||||
|
- Разработка автопостера в различные социальные сети и мессенджеры (Facebook,
|
||||||
|
LinkedIn, Twitter, Telegram).
|
||||||
|
- Разработка нейронной сети для автоматической категоризации статей.
|
||||||
|
|
||||||
|
#### MERLION
|
||||||
|
|
||||||
|
- Dates: March 2016 – May 2018
|
||||||
|
- Role: Senior Fullstack developer
|
||||||
|
|
||||||
|
В этой компании было 6 значительных проектов, которые я успешно завершил:
|
||||||
|
|
||||||
|
- Оптимизация создания рекламных страниц (PHP, JavaScript)
|
||||||
|
- Поддержка основного традиционного сайта <https://citilink.ru> (PHP,
|
||||||
|
JavaScript)
|
||||||
|
- Разработка парсинга для мониторинга товаров на предмет изменения цены,
|
||||||
|
количества/наличия на складе, рейтинга и других полей на основе данных с 55+
|
||||||
|
сайтов (Node.js, Express)
|
||||||
|
- Работа с нейронными сетями для подбора товаров
|
||||||
|
- Разработка приложений для распознавания лиц для Android (Java)
|
6
old/deno.json
Normal file
6
old/deno.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": ["deno.ns", "dom"]
|
||||||
|
},
|
||||||
|
"importMap": "./import_map.json"
|
||||||
|
}
|
13
old/deno.lock
Normal file
13
old/deno.lock
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"version": "2",
|
||||||
|
"remote": {
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/core/node.ts": "0857215f4ddbc5ef661af1eef8010376914373b9040d11ac777923651f59db08",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/core/utils.ts": "136b4b594befe6f1157741932aaea86f00f5ca6b1f7ee3e84059e9140a87e82d",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/par/md.ts": "0d4264ee133a883372664ac4b5e2dcb66a41a10a914de089e96b4ebb6757c641",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/par/types.ts": "f114cafde896121b9db754ffb5f2778edb3d414c78e3782ce1fb50a2ec8e2708",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/ren/attrs.ts": "a8f118423567bc64fd53ffd472bba4417df7de4df726c122c2ae8f37e921329a",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/ren/html_str.ts": "6052fa5b65ae0d8b2d5f6cb82d365e8b9a01f6070d55015db8386175386b8cca",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/ren/node.ts": "85d81ee3adc506f7ae90db31ed52336034766430ec8a8d6ae5d9951206f989fc",
|
||||||
|
"https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/ren/types.ts": "f1f561397a8326ddfcfefb00c3c9ea7e4e5467021fa89795628daa656d90727b"
|
||||||
|
}
|
||||||
|
}
|
29
old/docker-compose.yml
Normal file
29
old/docker-compose.yml
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik_public:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
site:
|
||||||
|
image: $PLESHEVSKI_IMAGE
|
||||||
|
networks:
|
||||||
|
- traefik_public
|
||||||
|
deploy:
|
||||||
|
replicas: 1
|
||||||
|
endpoint_mode: vip
|
||||||
|
update_config:
|
||||||
|
order: start-first
|
||||||
|
rollback_config:
|
||||||
|
order: start-first
|
||||||
|
labels:
|
||||||
|
- traefik.enable=true
|
||||||
|
- traefik.constraint-label=magenta_public
|
||||||
|
- traefik.http.routers.to_pleshevski_site.rule=Host(`pleshevski.ru`)
|
||||||
|
- traefik.http.routers.to_pleshevski_site.entrypoints=https
|
||||||
|
- traefik.http.routers.to_pleshevski_site.tls=true
|
||||||
|
- traefik.http.routers.to_pleshevski_site.tls.certresolver=le
|
||||||
|
- traefik.http.services.pleshevski_site.loadbalancer.server.port=33334
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.role == worker
|
1
old/global.ts
Normal file
1
old/global.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export type NonEmptyArray<T> = [T, ...T[]];
|
6
old/import_map.json
Normal file
6
old/import_map.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"par/": "https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/par/",
|
||||||
|
"ren/": "https://git.pleshevski.ru/pleshevskiy/paren/raw/commit/257079305813c4c0a71c16a89164c20bbce1a5e2/ren/"
|
||||||
|
}
|
||||||
|
}
|
14
old/log.ts
Normal file
14
old/log.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
export function error(...args: unknown[]): void {
|
||||||
|
console.log("[ERROR]", ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function info(...args: unknown[]): void {
|
||||||
|
console.log("[INFO]", ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function debug(...args: unknown[]): void {
|
||||||
|
// choose better name for this env
|
||||||
|
if (Deno.env.get("DEBUG") === "1") {
|
||||||
|
console.log("[DEBUG]", ...args);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { AnyNode, E } from "ren/node.ts";
|
||||||
|
import { WorkLink } from "../mod.ts";
|
||||||
|
import { renderDate } from "../../../render.ts";
|
||||||
|
import { CHRONOLOGICAL_WORKS } from "../data.ts";
|
||||||
|
import { RoleList } from "./RoleList.ts";
|
||||||
|
import { TechnologyList } from "./TechnologyList.ts";
|
||||||
|
|
||||||
|
export type ChronologicalWorksTableTranslations = Readonly<
|
||||||
|
Record<
|
||||||
|
| "Name"
|
||||||
|
| "Description"
|
||||||
|
| "Role"
|
||||||
|
| "Technologies"
|
||||||
|
| "Start"
|
||||||
|
| "Status_or_End",
|
||||||
|
string
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
|
||||||
|
const tr = E.bind(null, "tr", []);
|
||||||
|
const td = E.bind(null, "td", []);
|
||||||
|
const th = E.bind(null, "th", []);
|
||||||
|
|
||||||
|
export const ChronologicalWorksTable = (
|
||||||
|
i18n: ChronologicalWorksTableTranslations,
|
||||||
|
): AnyNode =>
|
||||||
|
E("table", [], [
|
||||||
|
E("thead", [], [
|
||||||
|
tr([
|
||||||
|
th(i18n.Name),
|
||||||
|
th(i18n.Description),
|
||||||
|
th(i18n.Role),
|
||||||
|
th(i18n.Technologies),
|
||||||
|
th(i18n.Start),
|
||||||
|
th(i18n.Status_or_End),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
E(
|
||||||
|
"tbody",
|
||||||
|
[],
|
||||||
|
CHRONOLOGICAL_WORKS.map((work) =>
|
||||||
|
tr([
|
||||||
|
td([WorkLink(work)]),
|
||||||
|
td(work.description),
|
||||||
|
td([RoleList(work.roles)]),
|
||||||
|
td([TechnologyList(work.technologies)]),
|
||||||
|
td(renderDate(work.startDate)),
|
||||||
|
td(
|
||||||
|
work.endDate ? renderDate(work.endDate) : work.status,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]);
|
6
old/modules/work/ChronologicalWorksTable/RoleList.ts
Normal file
6
old/modules/work/ChronologicalWorksTable/RoleList.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { NonEmptyArray } from "../../../global.ts";
|
||||||
|
import { Role } from "../domain/mod.ts";
|
||||||
|
import { AnyNode, TextNode } from "ren/node.ts";
|
||||||
|
|
||||||
|
export const RoleList: (roles: NonEmptyArray<Role>) => AnyNode = (roles) =>
|
||||||
|
new TextNode(roles.join(", "));
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { NonEmptyArray } from "../../../global.ts";
|
||||||
|
import { Technology } from "../domain/mod.ts";
|
||||||
|
import { AnyNode, TextNode } from "ren/node.ts";
|
||||||
|
|
||||||
|
export const TechnologyList: (techs: NonEmptyArray<Technology>) => AnyNode = (
|
||||||
|
techs,
|
||||||
|
) => new TextNode(techs.join(", "));
|
1
old/modules/work/ChronologicalWorksTable/mod.ts
Normal file
1
old/modules/work/ChronologicalWorksTable/mod.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { ChronologicalWorksTable } from "./ChronologicalWorksTable.ts";
|
6
old/modules/work/WorkLink.ts
Normal file
6
old/modules/work/WorkLink.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { AnyNode } from "ren/node.ts";
|
||||||
|
import { Work, work as w } from "./domain/mod.ts";
|
||||||
|
import { Link } from "../../uikit/link.ts";
|
||||||
|
|
||||||
|
export const WorkLink: (work: Work) => AnyNode = (work) =>
|
||||||
|
Link(work.name, { href: w.getExternalLink(work) });
|
368
old/modules/work/data.ts
Normal file
368
old/modules/work/data.ts
Normal file
|
@ -0,0 +1,368 @@
|
||||||
|
import { Role } from "./domain/Role.ts";
|
||||||
|
import { Status } from "./domain/Status.ts";
|
||||||
|
import { Technology } from "./domain/Technology.ts";
|
||||||
|
import { Work } from "./domain/Work.ts";
|
||||||
|
|
||||||
|
export const CHRONOLOGICAL_WORKS: Work[] = [
|
||||||
|
{
|
||||||
|
name: "picsg",
|
||||||
|
url: "/pleshevskiy/picsg",
|
||||||
|
description:
|
||||||
|
"A tool for steganographing information in a picture encoded using the Vernam cipher.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Haskell],
|
||||||
|
startDate: new Date("2024-04-13"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Mindustry tools",
|
||||||
|
url: "/pleshevskiy/mindustry-tools",
|
||||||
|
description: "Tools for the Mindustry game",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [
|
||||||
|
Technology.Nix,
|
||||||
|
Technology.Godot,
|
||||||
|
],
|
||||||
|
startDate: new Date("2024-01-07"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Master Progress Rosmintrud tools",
|
||||||
|
url: "https://rosmintrud.masterprogress.ru",
|
||||||
|
description:
|
||||||
|
"Internal service to prepare documents for the rosmintrud (SPA)",
|
||||||
|
roles: [Role.TechLead],
|
||||||
|
technologies: [
|
||||||
|
Technology.Deno,
|
||||||
|
Technology.Sqlite,
|
||||||
|
Technology.TypeScript,
|
||||||
|
Technology.Vue,
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Woodpecker,
|
||||||
|
Technology.Nix,
|
||||||
|
],
|
||||||
|
startDate: new Date("2023-07-03"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "yandexgpt_tg_bot",
|
||||||
|
url: "/pleshevskiy/yandexgpt_tg_bot",
|
||||||
|
description: "The Telegram bot to describe article with link by YandexGPT.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.JavaScript, Technology.NodeJS, Technology.Nix],
|
||||||
|
startDate: new Date("2023-06-27"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree-sitter-plpgsql",
|
||||||
|
url: "/pleshevskiy/tree-sitter-plpgsql",
|
||||||
|
description: "plpgsql grammar for tree-sitter",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [
|
||||||
|
Technology.C,
|
||||||
|
Technology.JavaScript,
|
||||||
|
Technology.TreeSitter,
|
||||||
|
Technology.Nix,
|
||||||
|
],
|
||||||
|
startDate: new Date("2023-01-05"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wd2",
|
||||||
|
url: "/pleshevskiy/wd2",
|
||||||
|
description:
|
||||||
|
"A wrapper over d2 which allows to use additional configs from d2 file",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Bash, Technology.Nix],
|
||||||
|
startDate: new Date("2022-12-12"),
|
||||||
|
endDate: new Date("2023-07-31"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tree-sitter-d2",
|
||||||
|
url: "/pleshevskiy/tree-sitter-d2",
|
||||||
|
description: "d2 grammar for tree-sitter",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [
|
||||||
|
Technology.C,
|
||||||
|
Technology.JavaScript,
|
||||||
|
Technology.TreeSitter,
|
||||||
|
Technology.Nix,
|
||||||
|
],
|
||||||
|
startDate: new Date("2022-12-04"),
|
||||||
|
status: Status.ActiveDeveloped,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nix2lua",
|
||||||
|
url: "/mynix/nix2lua",
|
||||||
|
description:
|
||||||
|
"This is a small but functional library that converts your nix configurations into lua format.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Nix, Technology.Lua],
|
||||||
|
startDate: new Date("2022-11-22"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vnetod",
|
||||||
|
url: "/pleshevskiy/vnetod",
|
||||||
|
description: "Dotenv section switcher",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2022-07-29"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "estring",
|
||||||
|
url: "/pleshevskiy/estring",
|
||||||
|
description: "A simple way to parse a string using type annotations.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2022-07-23"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "enve",
|
||||||
|
url: "/pleshevskiy/enve",
|
||||||
|
description:
|
||||||
|
"It helps you work with environment variables and convert it to any type using only type annotations",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2022-07-18"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "docker stack drone plugin",
|
||||||
|
url: "/drone_plugins/docker_stack",
|
||||||
|
description: "Deploy to production using `docker stack deploy`",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Drone,
|
||||||
|
Technology.Woodpecker,
|
||||||
|
],
|
||||||
|
startDate: new Date("2022-06-06"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dexios",
|
||||||
|
url: "/github/dexios",
|
||||||
|
description:
|
||||||
|
"Dexios is a fast, secure, and open source command-line encryption tool.",
|
||||||
|
roles: [Role.Collaborator],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2022-06-01"),
|
||||||
|
endDate: new Date("2023-02-28"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "recipes",
|
||||||
|
url: "/pleshevskiy/recipes",
|
||||||
|
description: "Site with recipes which cares about privacy",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.TypeScript, Technology.Deno, Technology.Rust],
|
||||||
|
startDate: new Date("2022-05-04"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pleshevski.ru",
|
||||||
|
url: "/pleshevskiy/pleshevski.ru",
|
||||||
|
description: "Source code of my personal site",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [
|
||||||
|
Technology.TypeScript,
|
||||||
|
Technology.Deno,
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Woodpecker,
|
||||||
|
],
|
||||||
|
startDate: new Date("2022-03-16"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "paren",
|
||||||
|
url: "/pleshevskiy/paren",
|
||||||
|
description: "Library for parsing and rendering information.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.TypeScript, Technology.Deno],
|
||||||
|
startDate: new Date("2022-03-14"),
|
||||||
|
status: Status.Experimental,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hwt",
|
||||||
|
url: "/pleshevskiy/hwt",
|
||||||
|
description:
|
||||||
|
"healthy workaholic timer – A tool that keeps you from breaking your health by working all day.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2022-02-04"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ood_persistence",
|
||||||
|
url: "/pleshevskiy/ood_persistence",
|
||||||
|
description:
|
||||||
|
"Asynchronous and synchronous interfaces and persistence implementations for your OOD architecture ",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2021-10-12"),
|
||||||
|
status: Status.Deprecated,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "migra",
|
||||||
|
url: "/pleshevskiy/migra",
|
||||||
|
description: "Simple SQL migration manager for your project.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2021-01-31"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "espruino-starter",
|
||||||
|
url: "/pleshevskiy/espruino-starter",
|
||||||
|
description:
|
||||||
|
"Quickly start creating your new project on the espruino board or a board based on it.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.JavaScript],
|
||||||
|
startDate: new Date("2021-08-23"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "react-rest-request",
|
||||||
|
url: "/pleshevskiy/react-rest-request",
|
||||||
|
description: "Minimalistic REST API client for React inspired by Apollo.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.TypeScript, Technology.React],
|
||||||
|
startDate: new Date("2020-10-04"),
|
||||||
|
status: Status.Deprecated,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sonic-channel",
|
||||||
|
url: "/pleshevskiy/sonic-channel",
|
||||||
|
description: "Rust client for sonic search backend.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2020-07-18"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "itconfig",
|
||||||
|
url: "/pleshevskiy/itconfig",
|
||||||
|
description:
|
||||||
|
"Easy build a configs from environment variables and use it in globally.",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Rust],
|
||||||
|
startDate: new Date("2019-12-22"),
|
||||||
|
status: Status.Deprecated,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "it-fsm",
|
||||||
|
url: "/pleshevskiy/it-fsm",
|
||||||
|
description: "Simple full-featured finite state machine for your project",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.TypeScript, Technology.NodeJS, Technology.Deno],
|
||||||
|
startDate: new Date("2019"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Cabinet Master Progress",
|
||||||
|
url: "https://cabinet.masterprogress.ru",
|
||||||
|
description:
|
||||||
|
"Student's cabinet of the educational center Master Progress (SSR + SPA)",
|
||||||
|
roles: [Role.TechLead],
|
||||||
|
technologies: [
|
||||||
|
Technology.Python,
|
||||||
|
Technology.Flask,
|
||||||
|
Technology.Postgresql,
|
||||||
|
Technology.TypeScript,
|
||||||
|
Technology.React,
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Woodpecker,
|
||||||
|
Technology.Nix,
|
||||||
|
],
|
||||||
|
startDate: new Date("2019-09-22"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "genrss",
|
||||||
|
url: "/pleshevskiy/genrss",
|
||||||
|
description: "RSS generator for python",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Python],
|
||||||
|
startDate: new Date("2019-07-23"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "marshmallow_pageinfo",
|
||||||
|
url: "/pleshevskiy/marshmallow_pageinfo",
|
||||||
|
description: "Page info marshmallow schema for api",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Python],
|
||||||
|
startDate: new Date("2019-10-05"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Binary Management",
|
||||||
|
url: "https://www.binarymanagement.com",
|
||||||
|
description: "Project management tool for interior designers",
|
||||||
|
roles: [Role.Developer, Role.TechLead, Role.TeamLead],
|
||||||
|
technologies: [
|
||||||
|
Technology.TypeScript,
|
||||||
|
Technology.NodeJS,
|
||||||
|
Technology.React,
|
||||||
|
Technology.Antd,
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Drone,
|
||||||
|
Technology.Rust,
|
||||||
|
Technology.Nix,
|
||||||
|
],
|
||||||
|
startDate: new Date("2018-09-15"),
|
||||||
|
status: Status.ActiveDeveloped,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Core Spirit",
|
||||||
|
url: "https://corespirit.com",
|
||||||
|
description: "Social platform focusing on human and planetary enhancement",
|
||||||
|
roles: [Role.Developer],
|
||||||
|
technologies: [
|
||||||
|
Technology.TypeScript,
|
||||||
|
Technology.NodeJS,
|
||||||
|
Technology.React,
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Drone,
|
||||||
|
],
|
||||||
|
startDate: new Date("2018-09-05"),
|
||||||
|
endDate: new Date("2019-12-31"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Master Progress",
|
||||||
|
url: "https://masterprogress.ru",
|
||||||
|
description:
|
||||||
|
"Main website of the educational center Master Progress (SSR + Forms)",
|
||||||
|
roles: [Role.TechLead],
|
||||||
|
technologies: [
|
||||||
|
Technology.Python,
|
||||||
|
Technology.Flask,
|
||||||
|
Technology.JavaScript,
|
||||||
|
Technology.Docker,
|
||||||
|
Technology.Woodpecker,
|
||||||
|
],
|
||||||
|
startDate: new Date("2018-04-10"),
|
||||||
|
status: Status.PassivelyMaintained,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ictmpl",
|
||||||
|
url: "/pleshevskiy/ictmpl",
|
||||||
|
description: "Generate projects from templates",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Python],
|
||||||
|
startDate: new Date("2018-06-30"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jjcrypto",
|
||||||
|
url: "/pleshevskiy/jjcrypto",
|
||||||
|
description: "Javascript encoder and decoder",
|
||||||
|
roles: [Role.Author],
|
||||||
|
technologies: [Technology.Php],
|
||||||
|
startDate: new Date("2015-11-01"),
|
||||||
|
status: Status.AsIs,
|
||||||
|
},
|
||||||
|
];
|
7
old/modules/work/domain/Role.ts
Normal file
7
old/modules/work/domain/Role.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export enum Role {
|
||||||
|
Collaborator = "collaborator",
|
||||||
|
Author = "author",
|
||||||
|
TechLead = "tech lead",
|
||||||
|
TeamLead = "team lead",
|
||||||
|
Developer = "developer",
|
||||||
|
}
|
25
old/modules/work/domain/Status.ts
Normal file
25
old/modules/work/domain/Status.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export enum Status {
|
||||||
|
// New features are being added and bugs are being fixed.
|
||||||
|
ActiveDeveloped = "actively-developed",
|
||||||
|
|
||||||
|
// There are no plans for new features, but the maintainer intends to respond
|
||||||
|
// to issues that get filed.
|
||||||
|
PassivelyMaintained = "passively-maintained",
|
||||||
|
|
||||||
|
// The package is feature complete, the maintainer does not intend to continue
|
||||||
|
// working on it or providing support, but it works for the purposes it was
|
||||||
|
// designed for.
|
||||||
|
AsIs = "as-is",
|
||||||
|
|
||||||
|
// The author wants to share it with the community but is not intending to
|
||||||
|
// meet anyone's particular use case.
|
||||||
|
Experimental = "experimental",
|
||||||
|
|
||||||
|
// The current maintainer would like to transfer the package to someone else.
|
||||||
|
LookingForMaintainer = "looking-for-maintainer",
|
||||||
|
|
||||||
|
// The maintainer does not recommend using this package (the description of the
|
||||||
|
// package can describe why, there could be a better solution available or
|
||||||
|
// there could be problems with the package that the author does not want to fix).
|
||||||
|
Deprecated = "deprecated",
|
||||||
|
}
|
25
old/modules/work/domain/Technology.ts
Normal file
25
old/modules/work/domain/Technology.ts
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export enum Technology {
|
||||||
|
C = "C",
|
||||||
|
JavaScript = "JS",
|
||||||
|
TypeScript = "TS",
|
||||||
|
Rust = "Rust",
|
||||||
|
Python = "Python",
|
||||||
|
Php = "PHP",
|
||||||
|
Deno = "Deno",
|
||||||
|
NodeJS = "NodeJS",
|
||||||
|
Flask = "Flask",
|
||||||
|
React = "React",
|
||||||
|
Antd = "Antd",
|
||||||
|
Postgresql = "PostgreSQL",
|
||||||
|
Docker = "Docker",
|
||||||
|
Drone = "Drone CI",
|
||||||
|
Woodpecker = "Woodpecker CI",
|
||||||
|
Bash = "Bash",
|
||||||
|
TreeSitter = "TreeSitter",
|
||||||
|
Nix = "Nix",
|
||||||
|
Lua = "Lua",
|
||||||
|
Sqlite = "Sqlite",
|
||||||
|
Vue = "Vue",
|
||||||
|
Godot = "Godot",
|
||||||
|
Haskell = "Haskell",
|
||||||
|
}
|
20
old/modules/work/domain/Work.ts
Normal file
20
old/modules/work/domain/Work.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { NonEmptyArray } from "../../../global.ts";
|
||||||
|
import { Role } from "./Role.ts";
|
||||||
|
import { Status } from "./Status.ts";
|
||||||
|
import { Technology } from "./Technology.ts";
|
||||||
|
|
||||||
|
export interface Work {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
description: string;
|
||||||
|
roles: NonEmptyArray<Role>;
|
||||||
|
technologies: NonEmptyArray<Technology>;
|
||||||
|
startDate: Date;
|
||||||
|
endDate?: Date;
|
||||||
|
status?: Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getExternalLink: (work: Pick<Work, "url">) => string = (work) =>
|
||||||
|
work.url.startsWith("https://")
|
||||||
|
? work.url
|
||||||
|
: new URL(work.url, "https://git.pleshevski.ru").toString();
|
5
old/modules/work/domain/mod.ts
Normal file
5
old/modules/work/domain/mod.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export { Role } from "./Role.ts";
|
||||||
|
export { Status } from "./Status.ts";
|
||||||
|
export { Technology } from "./Technology.ts";
|
||||||
|
export type { Work } from "./Work.ts";
|
||||||
|
export * as work from "./Work.ts";
|
1
old/modules/work/mod.ts
Normal file
1
old/modules/work/mod.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { WorkLink } from "./WorkLink.ts";
|
570
old/public/styles/main.css
Normal file
570
old/public/styles/main.css
Normal file
|
@ -0,0 +1,570 @@
|
||||||
|
@charset "UTF-8";
|
||||||
|
:root {
|
||||||
|
--default-color-black: #000000;
|
||||||
|
--default-color-black-0: hsla(0, 0%, 0%, 0);
|
||||||
|
--default-color-black-0x15: hsla(0, 0%, 0%, 0.15);
|
||||||
|
--default-color-black-0x6: hsla(0, 0%, 0%, 0.6);
|
||||||
|
--default-color-white: #ffffff;
|
||||||
|
--default-color-warning: #ffee58;
|
||||||
|
--default-color-error: #b00008;
|
||||||
|
--default-color-success: #417505;
|
||||||
|
--color-brand-blue: #1966df;
|
||||||
|
--color-brand-faded-blue: #f5f5ff;
|
||||||
|
--color-graphite: #212121;
|
||||||
|
--color-warm-gray: #757575;
|
||||||
|
--color-pale: #b6b6b6;
|
||||||
|
--color-faded: #e0e0e0;
|
||||||
|
--max-content-width: 1440px;
|
||||||
|
--min-content-width: 320px;
|
||||||
|
--rad-std-half: 0.125rem;
|
||||||
|
--rad-std: 0.25rem;
|
||||||
|
--rad-std-x2: 0.5rem;
|
||||||
|
--rad-std-x3: 0.75rem;
|
||||||
|
--default-font-size: 16px;
|
||||||
|
--f-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Roboto,Oxygen-Sans, Ubuntu, Cantarell, "Segoe UI", Verdana, sans-serif;
|
||||||
|
--f-wei-thin: 100;
|
||||||
|
--f-wei-reg: 400;
|
||||||
|
--f-wei-bold: 700;
|
||||||
|
--f-wei-black: 800;
|
||||||
|
--z-ind-background: -100;
|
||||||
|
--z-ind-backward: -1;
|
||||||
|
--z-ind-select: 50;
|
||||||
|
--z-ind-tooltip: 75;
|
||||||
|
--z-ind-high: 100;
|
||||||
|
--z-ind-overlay: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: var(--default-color-white);
|
||||||
|
font-size: var(--default-font-size);
|
||||||
|
line-height: 1;
|
||||||
|
height: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--color-graphite);
|
||||||
|
font-weight: var(--f-wei-regular);
|
||||||
|
font-family: var(--f-family);
|
||||||
|
min-width: var(--min-content-width);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
details > summary {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
details > summary::before, details > summary::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio, canvas, progress, video {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
address, caption, cite, code, dfn, strong, th, var {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: var(--f-wei-regular);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: var(--f-wei-bold);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
b, strong, optgroup {
|
||||||
|
font-weight: var(--f-wei-bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
dfn, em, i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe, abbr, acronym, img {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background: var(--default-color-warning);
|
||||||
|
color: var(--default-color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub, sup {
|
||||||
|
font-size: 80%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
q::before, q::after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
height: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, kbd, pre, samp {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid var(--color-pale);
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
border: 0;
|
||||||
|
color: inherit;
|
||||||
|
display: table;
|
||||||
|
word-spacing: normal;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, input, optgroup, select, textarea {
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, input {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type=button]::-moz-focus-inner,
|
||||||
|
[type=reset]::-moz-focus-inner,
|
||||||
|
[type=submit]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type=button]:-moz-focusring,
|
||||||
|
[type=reset]:-moz-focusring,
|
||||||
|
[type=submit]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, input:focus,
|
||||||
|
button, textarea,
|
||||||
|
select, a:focus {
|
||||||
|
outline: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-webkit-input-placeholder,
|
||||||
|
input:-moz-placeholder,
|
||||||
|
textarea::-webkit-input-placeholder,
|
||||||
|
textarea:-moz-placeholder {
|
||||||
|
color: var(--color-pale);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=number]::-webkit-inner-spin-button,
|
||||||
|
input[type=number]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=search] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type=search]::-webkit-search-cancel-button,
|
||||||
|
input[type=search]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type=button],
|
||||||
|
input[type=reset],
|
||||||
|
input[type=submit] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled], input[disabled] {
|
||||||
|
cursor: no-drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-brand-blue);
|
||||||
|
border-bottom: dashed 1px var(--color-brand-blue);
|
||||||
|
}
|
||||||
|
a:hover, a:focus {
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden], template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color: var(--default-color-black);
|
||||||
|
color: var(--default-color-white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
border-top: 1px solid var(--color-faded);
|
||||||
|
}
|
||||||
|
|
||||||
|
.maw-100p {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maw-content, .content-width {
|
||||||
|
max-width: var(--max-content-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.miw-content, #main {
|
||||||
|
min-width: var(--min-content-width);
|
||||||
|
}
|
||||||
|
|
||||||
|
.w-100p, .responsive-typography ul li, .responsive-typography ol li, .responsive-typography ul, .responsive-typography ol, #root, #main, .content, .content-width {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mah-100p {
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mah-100vh {
|
||||||
|
max-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mih-100vh, #root {
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mar-ha, .content-width {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-0x5, .main-menu > a, .responsive-typography th, .responsive-typography td {
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-h-1x25, .content-width {
|
||||||
|
padding-right: 1.25rem;
|
||||||
|
padding-left: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pad-v-1, .header, .footer {
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-v-1x5 > :not([hidden]):not(.hidden-input) + :not([hidden]):not(.hidden-input), #main > :not([hidden]):not(.hidden-input) + :not([hidden]):not(.hidden-input) {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-h-0x5 > :not(:last-child):not(:only-child) {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gap-h-1 > :not(:last-child):not(:only-child), .main-menu > :not(:last-child):not(:only-child) {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-sta-sta, .main-menu {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-orient: horizontal;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
-ms-flex-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
-webkit-box-align: start;
|
||||||
|
-ms-flex-align: start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row-sta-bet {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-orient: horizontal;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
-webkit-box-pack: justify;
|
||||||
|
-ms-flex-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
-webkit-box-align: start;
|
||||||
|
-ms-flex-align: start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-sta-sta {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
-ms-flex-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
-webkit-box-align: start;
|
||||||
|
-ms-flex-align: start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-str-sta, #root, #main {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
-ms-flex-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
-webkit-box-align: stretch;
|
||||||
|
-ms-flex-align: stretch;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-1, #main, .content {
|
||||||
|
-webkit-box-flex: 1 0;
|
||||||
|
-ms-flex: 1 0;
|
||||||
|
flex: 1 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.responsive-typography h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
.responsive-typography > div, .responsive-typography p, .responsive-typography li {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.responsive-typography ul li, .responsive-typography ol li {
|
||||||
|
position: relative;
|
||||||
|
min-height: 1.5rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
.responsive-typography ul li::before, .responsive-typography ol li::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.responsive-typography ul > li::before, .responsive-typography ol > li::before {
|
||||||
|
background-color: var(--color-brand-blue);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
.responsive-typography ul > * + *, .responsive-typography ol > * + * {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
.responsive-typography p + ul,
|
||||||
|
.responsive-typography ul + p,
|
||||||
|
.responsive-typography p + p {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
.responsive-typography li + li {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
.responsive-typography table {
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.responsive-typography thead {
|
||||||
|
background-color: var(--color-brand-faded-blue);
|
||||||
|
}
|
||||||
|
.responsive-typography tbody tr {
|
||||||
|
border-top: solid 1px var(--color-pale);
|
||||||
|
}
|
||||||
|
.responsive-typography th, .responsive-typography td {
|
||||||
|
text-align: initial;
|
||||||
|
}
|
||||||
|
.responsive-typography th:not(:first-of-type), .responsive-typography td:not(:first-of-type) {
|
||||||
|
border-left: solid 1px var(--color-pale);
|
||||||
|
}
|
||||||
|
.responsive-typography td:nth-child(n+3) {
|
||||||
|
color: var(--color-warm-gray);
|
||||||
|
}
|
||||||
|
|
||||||
|
.anim, .main-menu > a, a, .anim::before, .main-menu > a::before, a::before, .anim::after, .main-menu > a::after, a::after {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-menu > a {
|
||||||
|
color: var(--color-brand-blue);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-brand-blue);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.main-menu > a:hover, .main-menu > a[aria-current]:not([aria-current=""]) {
|
||||||
|
color: var(--default-color-white);
|
||||||
|
background-color: var(--color-brand-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* dropdown
|
||||||
|
* Source: https://codepen.io/markcaron/pen/wdVmpB
|
||||||
|
*
|
||||||
|
* TODO: change styles
|
||||||
|
* */
|
||||||
|
.dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.dropdown > input[type=checkbox] {
|
||||||
|
position: absolute;
|
||||||
|
left: -100vw;
|
||||||
|
}
|
||||||
|
.dropdown > label {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 15px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid #8c8c8c;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.dropdown > label:hover {
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
.dropdown > label:after {
|
||||||
|
content: "▲";
|
||||||
|
font-size: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 6px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.dropdown > ul {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
display: block;
|
||||||
|
left: -100vw;
|
||||||
|
bottom: calc(1.5em + 14px);
|
||||||
|
border: 1px solid #8c8c8c;
|
||||||
|
background: #fff;
|
||||||
|
padding: 6px 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-webkit-box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
-moz-box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
.dropdown > ul a {
|
||||||
|
display: block;
|
||||||
|
padding: 6px 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.dropdown > ul a:hover, .dropdown > ul a:focus {
|
||||||
|
background: #ececec;
|
||||||
|
}
|
||||||
|
.dropdown > input[type=checkbox]:checked ~ ul {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
.dropdown > input[type=checkbox]:checked + label:after {
|
||||||
|
content: "▼";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=main.css.map */
|
1
old/public/styles/main.css.map
Normal file
1
old/public/styles/main.css.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sourceRoot":"","sources":["../../styles/base/reset.scss","../../styles/base/layout.scss","../../styles/atoms/sizes.scss","../../styles/atoms/white-spaces.scss","../../styles/mixins/white-spaces.scss","../../styles/atoms/flex.scss","../../styles/mixins/flex.scss","../../styles/atoms/typography.scss","../../styles/atoms/misc.scss","../../styles/uikit/main-menu.scss","../../styles/uikit/dropdown.scss"],"names":[],"mappings":";AAEA;EAEE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EAEA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;EAEA;EACA;EACA;EACA;EACA;EACA;;;AAGF;AAAA;AAAA;EAGE;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;;;AAGF;EACE;;;AAKF;EACE;EACA;;AAEA;EAEE;;;AAIJ;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;AAAA;AAAA;AAAA;EAIE;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;;;AAGF;AAAA;AAAA;EAGE;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;;;AAGF;AAAA;EAEE;;;AAGF;EACE;EACA;;;AAGF;AAAA;AAAA;AAAA;EAIE;EACA;;;AAGF;EACE;;;AAGF;AAAA;EAEE;EACA;;;AAGF;EACE;EACA;;;AAGF;EACE;EACA;EACA;EACA;;AAKA;EACE;;;AAIJ;EACE;EACA;EACA;;;AAGF;EACE;EACA;EACA;;;AAGF;EACE;;;AAGF;EACE;;;AAGF;EACE;EACA;;;ACzSF;EAEE;;;ACTF;EAAe;;;AACf;EAAe;;;AACf;EAAe;;;AACf;EAAU;;;AAEV;EAAa;;;AACb;EAAa;;;AACb;EAAa;;;ACPb;ECQgB;EAAmB;;;ADNnC;ECQmB;;;ADPnB;ECWqB;EAEA;;;ADZrB;ECSqB;EAEA;;;AAInB;EAfmB;;;AAmBnB;EAlBmB;;;AAkBnB;EAlBmB;;;ACLrB;ECCE;EACA;EACA;EAWA;EACA;EACA;EACA;EA4CA;EACA;EACA;EAoCA;EACA;EACA;;;ADpGF;ECAE;EACA;EACA;EAWA;EACA;EACA;EACA;EAmEA;EACA;EACA;EAaA;EACA;EACA;;;ADnGF;ECDE;EACA;EACA;EAmBA;EACA;EACA;EACA;EAoCA;EACA;EACA;EAoCA;EACA;EACA;;;ADlGF;ECFE;EACA;EACA;EAmBA;EACA;EACA;EACA;EAoCA;EACA;EACA;EAsDA;EACA;EACA;;;ADlHF;ECuCE,kBDvCsB;ECwCtB,UDxCsB;ECyCtB,MDzCsB;;;AEHtB;EACE;EACA;;AAGF;EACE;EACA;;AAMA;EAGE;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;;AAGF;EACE;;AAIJ;AAAA;AAAA;EAGE;;AAGF;EACE;;AAGF;EACE;EACA;EACA;;AAGF;EACE;;AAGF;EACE;;AAGF;EAEE;;AAEA;EACE;;AAIJ;EACE;;;AC9EJ;EACE;;;ACGA;EAIE;EACA;EACA;EACA;;AAEA;EAEE;EACA;;;ACfN;AAAA;AAAA;AAAA;AAAA;AAMA;EACE;EACA;;AAEA;EACE;EACA;;AAGF;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;;AAGF;EACE;EACA;EACA;EACA;EACA;;AAIJ;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACE;EACA;EACA;EACA;;AAEA;EACE;;AAMJ;EACE;;AAGF;EACE","file":"main.css"}
|
7
old/render.ts
Normal file
7
old/render.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export function renderDate(date: Date): string {
|
||||||
|
return date.toLocaleDateString(undefined, {
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
});
|
||||||
|
}
|
246
old/server.ts
Normal file
246
old/server.ts
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
import { MarkdownParser } from "par/md.ts";
|
||||||
|
import { HtmlStrRenderer } from "ren/html_str.ts";
|
||||||
|
import * as log from "./log.ts";
|
||||||
|
import rusTranslates from "./translates/rus.ts";
|
||||||
|
import type { Translations } from "./translates/rus.ts";
|
||||||
|
import { Context, getLangHref, getLangUrlPrefix, Lang } from "./context.ts";
|
||||||
|
import { E404Page } from "./views/pages/e404.ts";
|
||||||
|
import { E500Page } from "./views/pages/e500.ts";
|
||||||
|
import { WorksPage } from "./views/pages/works.ts";
|
||||||
|
import { Layout } from "./views/comp/layout.ts";
|
||||||
|
import { ContentPage } from "./views/pages/content.ts";
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
await main();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
await startServer({ port: 33334 });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startServer(cfg: ServerConfig) {
|
||||||
|
const srv = Deno.listen({ hostname: "0.0.0.0", port: cfg.port });
|
||||||
|
log.info(`Server listening at http://localhost:${cfg.port}`);
|
||||||
|
|
||||||
|
for await (const conn of srv) {
|
||||||
|
serveHttp(conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServerConfig {
|
||||||
|
port: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function serveHttp(conn: Deno.Conn) {
|
||||||
|
const httpConn = Deno.serveHttp(conn);
|
||||||
|
|
||||||
|
for await (const reqEvt of httpConn) {
|
||||||
|
const res = await handleRequest(reqEvt.request);
|
||||||
|
reqEvt.respondWith(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleRequest(req: Request): Promise<Response> {
|
||||||
|
log.info({ method: req.method, url: req.url });
|
||||||
|
|
||||||
|
if (req.method === "GET") {
|
||||||
|
return await handleGet(req);
|
||||||
|
} else {
|
||||||
|
return new Response("Method Not Allowed", { status: 405 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleGet(req: Request) {
|
||||||
|
const restCtx = createRestContextFromRequest(req);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await tryCreateFileResponse(restCtx.locPath);
|
||||||
|
return res;
|
||||||
|
} catch (_) {
|
||||||
|
if (restCtx.lang == null && restCtx.newLang) {
|
||||||
|
return new Response(null, {
|
||||||
|
status: 301,
|
||||||
|
headers: {
|
||||||
|
location: getLangUrlPrefix(restCtx.newLang) + restCtx.locPath,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const ctx = intoAppContext(restCtx);
|
||||||
|
|
||||||
|
if (restCtx.lang !== Lang.Rus) {
|
||||||
|
await loadAndUpdateTranslations(ctx);
|
||||||
|
}
|
||||||
|
log.debug({ context: restCtx });
|
||||||
|
|
||||||
|
const par = new MarkdownParser();
|
||||||
|
const ren = new HtmlStrRenderer({
|
||||||
|
wrapNode: Layout.bind(null, ctx),
|
||||||
|
onVisitAttr: ([key, value]) => {
|
||||||
|
if (key === "lhref" && typeof value === "string") {
|
||||||
|
return ["href", getLangHref(ctx.lang, value)];
|
||||||
|
} else {
|
||||||
|
return [key, value];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (restCtx.locPath === "/" || restCtx.locPath === "/about") {
|
||||||
|
const res = par.parse(
|
||||||
|
await readMarkdownFile("data/about", ctx.lang),
|
||||||
|
);
|
||||||
|
return createHtmlResponse(ren.render(ContentPage(ctx, res)));
|
||||||
|
} else if (restCtx.locPath === "/works") {
|
||||||
|
const res = par.parse(
|
||||||
|
await readMarkdownFile("data/works", ctx.lang),
|
||||||
|
);
|
||||||
|
return createHtmlResponse(ren.render(WorksPage(ctx, res)));
|
||||||
|
} else {
|
||||||
|
return createHtmlResponse(ren.render(E404Page(ctx)), 404);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.error(e);
|
||||||
|
return createHtmlResponse(ren.render(E500Page(ctx)), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readMarkdownFile(dirPath: string, lang: Lang): Promise<string> {
|
||||||
|
return await Deno.readTextFile(`${dirPath}/${lang}.md`)
|
||||||
|
.catch((_) => Deno.readTextFile(`${dirPath}/${Lang.Rus}.md`));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAndUpdateTranslations(ctx: Context) {
|
||||||
|
try {
|
||||||
|
const translates = await import(`./translates/${ctx.lang}.ts`);
|
||||||
|
ctx.tr = Object.entries(translates.default as Partial<Translations>)
|
||||||
|
.reduce(
|
||||||
|
(acc, [key, val]) => ({
|
||||||
|
...acc,
|
||||||
|
[key as keyof Translations]: val,
|
||||||
|
}),
|
||||||
|
{ ...ctx.tr } as Translations,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
log.debug({ err });
|
||||||
|
/* ignore */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function intoAppContext(restCtx: RestContext): Context {
|
||||||
|
return {
|
||||||
|
locPath: restCtx.locPath,
|
||||||
|
lang: restCtx.lang || Lang.Rus,
|
||||||
|
tr: restCtx.tr,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRestContextFromRequest(req: Request): RestContext {
|
||||||
|
log.debug(req.headers);
|
||||||
|
|
||||||
|
const locUrl = new URL(req.url);
|
||||||
|
const lang = tryIntoAppLangFromUrl(locUrl);
|
||||||
|
|
||||||
|
return {
|
||||||
|
lang,
|
||||||
|
newLang: getPreferRequestLang(req.headers) ?? Lang.Rus,
|
||||||
|
locPath: stripPrefix(`/${lang}`, locUrl.pathname),
|
||||||
|
tr: rusTranslates,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RestContext {
|
||||||
|
locPath: string;
|
||||||
|
lang: Lang | null;
|
||||||
|
newLang: Lang;
|
||||||
|
tr: Translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPreferRequestLang(headers: Headers): Lang | null {
|
||||||
|
const acceptLanguageHeader = headers.get("accept-language");
|
||||||
|
if (!acceptLanguageHeader) return null;
|
||||||
|
|
||||||
|
const acceptLanguages = acceptLanguageHeader
|
||||||
|
.split(/,\s*/)
|
||||||
|
.map((part) => part.split(";q=")[0])
|
||||||
|
.map(tryIntoAppLangFromAcceptLangCode)
|
||||||
|
.filter((lang): lang is Lang => !!lang);
|
||||||
|
return acceptLanguages[0] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryIntoAppLangFromAcceptLangCode(lang: string): Lang | null {
|
||||||
|
return lang === "*"
|
||||||
|
? Lang.Rus
|
||||||
|
: lang.startsWith("en")
|
||||||
|
? Lang.Eng
|
||||||
|
: lang.startsWith("ru")
|
||||||
|
? Lang.Rus
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryIntoAppLangFromUrl(url: URL): Lang | null {
|
||||||
|
return url.pathname.startsWith("/eng/")
|
||||||
|
? Lang.Eng
|
||||||
|
: url.pathname.startsWith("/rus/")
|
||||||
|
? Lang.Rus
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripPrefix(prefix: string, val: string): string {
|
||||||
|
return val.startsWith(prefix) ? val.slice(prefix.length) : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createHtmlResponse(body: string, status = 200): Response {
|
||||||
|
return new Response(body, {
|
||||||
|
status,
|
||||||
|
headers: getContentTypeHeader("html"),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tryCreateFileResponse(urlPath: string): Promise<Response> {
|
||||||
|
const filePath = extractFilePath(urlPath);
|
||||||
|
log.debug({ filePath });
|
||||||
|
if (!filePath) throw new SkipFile();
|
||||||
|
|
||||||
|
const content = await Deno.readTextFile(filePath).catch(() => {
|
||||||
|
throw new SkipFile();
|
||||||
|
});
|
||||||
|
|
||||||
|
return createFileResponse(content, getFileExt(filePath));
|
||||||
|
}
|
||||||
|
|
||||||
|
class SkipFile extends Error {}
|
||||||
|
|
||||||
|
function createFileResponse(content: string, fileExt: string): Response {
|
||||||
|
return new Response(content, {
|
||||||
|
headers: getContentTypeHeader(fileExt),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractFilePath(urlPath: string): string | null {
|
||||||
|
const relPath = urlPath.slice(1);
|
||||||
|
if (relPath.startsWith("styles/")) {
|
||||||
|
return `public/${relPath}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContentTypeHeader(fileExt: string): Record<string, string> {
|
||||||
|
return { "content-type": getContentTypeByExt(fileExt) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContentTypeByExt(fileExt: string): string {
|
||||||
|
switch (fileExt) {
|
||||||
|
case "html":
|
||||||
|
return "text/html";
|
||||||
|
case "css":
|
||||||
|
return "text/css";
|
||||||
|
default:
|
||||||
|
return "text/plain";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFileExt(filePath: string): string {
|
||||||
|
return filePath.slice((filePath.lastIndexOf(".") - 1 >>> 0) + 2);
|
||||||
|
}
|
6
old/styles/atoms/flex.scss
Normal file
6
old/styles/atoms/flex.scss
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.row-sta-sta { @include flex-layout(row, sta, sta) }
|
||||||
|
.row-sta-bet { @include flex-layout(row, sta, bet) }
|
||||||
|
.col-sta-sta { @include flex-layout(col, sta, sta) }
|
||||||
|
.col-str-sta { @include flex-layout(col, str, sta) }
|
||||||
|
|
||||||
|
.flex-1 { @include flex(1 0) }
|
3
old/styles/atoms/misc.scss
Normal file
3
old/styles/atoms/misc.scss
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
.anim, .anim::before, .anim::after {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
5
old/styles/atoms/mod.scss
Normal file
5
old/styles/atoms/mod.scss
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@import 'sizes';
|
||||||
|
@import 'white-spaces';
|
||||||
|
@import 'flex';
|
||||||
|
@import 'typography';
|
||||||
|
@import 'misc';
|
9
old/styles/atoms/sizes.scss
Normal file
9
old/styles/atoms/sizes.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.maw-100p { max-width: 100% }
|
||||||
|
.maw-content { max-width: var(--max-content-width) }
|
||||||
|
.miw-content { min-width: var(--min-content-width) }
|
||||||
|
.w-100p { width: 100% }
|
||||||
|
|
||||||
|
.mah-100p { max-height: 100% }
|
||||||
|
.mah-100vh { max-height: 100vh }
|
||||||
|
.mih-100vh { min-height: 100vh }
|
||||||
|
|
86
old/styles/atoms/typography.scss
Normal file
86
old/styles/atoms/typography.scss
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
.responsive-typography {
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
margin: 1.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
> div, p, li {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
@extend .w-100p;
|
||||||
|
|
||||||
|
li {
|
||||||
|
@extend .w-100p;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
min-height: 1.5rem;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> li::before {
|
||||||
|
background-color: var(--color-brand-blue);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * + * {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p + ul,
|
||||||
|
ul + p,
|
||||||
|
p + p {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
li + li {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background-color: var(--color-brand-faded-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody tr {
|
||||||
|
border-top: solid 1px var(--color-pale);
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
@extend .pad-0x5;
|
||||||
|
text-align: initial;
|
||||||
|
|
||||||
|
&:not(:first-of-type) {
|
||||||
|
border-left: solid 1px var(--color-pale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td:nth-child(n+3) {
|
||||||
|
color: var(--color-warm-gray)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
11
old/styles/atoms/white-spaces.scss
Normal file
11
old/styles/atoms/white-spaces.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
.mar-ha { @include mar-ha }
|
||||||
|
|
||||||
|
.pad-0x5 { @include pad(0.5) }
|
||||||
|
.pad-h-1x25 { @include pad-h(1.25) }
|
||||||
|
.pad-v-1 { @include pad-v(1) }
|
||||||
|
|
||||||
|
.gap-v-1x5 { @include gap-v(1.5) }
|
||||||
|
|
||||||
|
.gap-h-0x5 { @include gap-h(0.5) }
|
||||||
|
.gap-h-1 { @include gap-h(1) }
|
||||||
|
|
11
old/styles/base/layout.scss
Normal file
11
old/styles/base/layout.scss
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#root { @extend .col-str-sta, .w-100p, .mih-100vh }
|
||||||
|
#main { @extend .col-str-sta, .flex-1, .miw-content, .w-100p, .gap-v-1x5 }
|
||||||
|
.content { @extend .flex-1, .w-100p }
|
||||||
|
.content-width { @extend .maw-content, .w-100p, .mar-ha, .pad-h-1x25 }
|
||||||
|
|
||||||
|
.header { @extend .pad-v-1 }
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
@extend .pad-v-1;
|
||||||
|
border-top: 1px solid var(--color-faded);
|
||||||
|
}
|
307
old/styles/base/reset.scss
Normal file
307
old/styles/base/reset.scss
Normal file
|
@ -0,0 +1,307 @@
|
||||||
|
@use "sass:math";
|
||||||
|
|
||||||
|
:root {
|
||||||
|
// Default Colors
|
||||||
|
--default-color-black: #000000;
|
||||||
|
--default-color-black-0: hsla(0, 0%, 0%, 0);
|
||||||
|
--default-color-black-0x15: hsla(0, 0%, 0%, 0.15);
|
||||||
|
--default-color-black-0x6: hsla(0, 0%, 0%, 0.6);
|
||||||
|
--default-color-white: #ffffff;
|
||||||
|
--default-color-warning: #ffee58;
|
||||||
|
--default-color-error: #b00008;
|
||||||
|
--default-color-success: #417505;
|
||||||
|
// Project Colors
|
||||||
|
--color-brand-blue: #1966df;
|
||||||
|
--color-brand-faded-blue: #f5f5ff;
|
||||||
|
--color-graphite: #212121;
|
||||||
|
--color-warm-gray: #757575;
|
||||||
|
--color-pale: #b6b6b6;
|
||||||
|
--color-faded: #e0e0e0;
|
||||||
|
// Layout
|
||||||
|
--max-content-width: #{$page-max-width + px};
|
||||||
|
--min-content-width: #{$page-min-width + px};
|
||||||
|
// Borders
|
||||||
|
--rad-std-half: #{math.div($radius, 2)};
|
||||||
|
--rad-std: #{$radius};
|
||||||
|
--rad-std-x2: #{$radius * 2};
|
||||||
|
--rad-std-x3: #{$radius * 3};
|
||||||
|
// Font
|
||||||
|
--default-font-size: 16px;
|
||||||
|
--f-family: system-ui, -apple-system, BlinkMacSystemFont, "Helvetica Neue", Roboto,Oxygen-Sans, Ubuntu, Cantarell, "Segoe UI", Verdana, sans-serif;
|
||||||
|
--f-wei-thin: 100;
|
||||||
|
--f-wei-reg: 400; // Normal = Regular
|
||||||
|
--f-wei-bold: 700;
|
||||||
|
--f-wei-black: 800; // Extra Bold = Black
|
||||||
|
// Z-index
|
||||||
|
--z-ind-background: -100;
|
||||||
|
--z-ind-backward: -1;
|
||||||
|
--z-ind-select: 50;
|
||||||
|
--z-ind-tooltip: 75;
|
||||||
|
--z-ind-high: 100;
|
||||||
|
--z-ind-overlay: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
background-color: var(--default-color-white);
|
||||||
|
font-size: var(--default-font-size);
|
||||||
|
line-height: 1;
|
||||||
|
height: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-moz-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: var(--color-graphite);
|
||||||
|
font-weight: var(--f-wei-regular);
|
||||||
|
font-family: var(--f-family);
|
||||||
|
min-width: var(--min-content-width);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
summary {}
|
||||||
|
|
||||||
|
details > summary {
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::-webkit-details-marker {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
audio, canvas, progress, video {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
address, caption, cite, code, dfn, strong, th, var {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: var(--f-wei-regular);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: var(--f-wei-bold);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
b, strong, optgroup {
|
||||||
|
font-weight: var(--f-wei-bold);
|
||||||
|
}
|
||||||
|
|
||||||
|
dfn, em, i {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe, abbr, acronym, img {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background: var(--default-color-warning);
|
||||||
|
color: var(--default-color-black);
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub, sup {
|
||||||
|
font-size: 80%;
|
||||||
|
vertical-align: baseline;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
q::before, q::after {
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
height: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, kbd, pre, samp {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid var(--color-pale);
|
||||||
|
padding: 0.25rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
legend {
|
||||||
|
border: 0;
|
||||||
|
color: inherit;
|
||||||
|
display: table;
|
||||||
|
word-spacing: normal;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, input, optgroup, select, textarea {
|
||||||
|
color: inherit;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, input {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
button, select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type='button']::-moz-focus-inner,
|
||||||
|
[type='reset']::-moz-focus-inner,
|
||||||
|
[type='submit']::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type='button']:-moz-focusring,
|
||||||
|
[type='reset']:-moz-focusring,
|
||||||
|
[type='submit']:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, input:focus,
|
||||||
|
button, textarea,
|
||||||
|
select, a:focus {
|
||||||
|
outline: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input::-webkit-input-placeholder,
|
||||||
|
input:-moz-placeholder,
|
||||||
|
textarea::-webkit-input-placeholder,
|
||||||
|
textarea:-moz-placeholder {
|
||||||
|
color: var(--color-pale);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='number']::-webkit-inner-spin-button,
|
||||||
|
input[type='number']::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='search'] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='search']::-webkit-search-cancel-button,
|
||||||
|
input[type='search']::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
input[type='button'],
|
||||||
|
input[type='reset'],
|
||||||
|
input[type='submit'] {
|
||||||
|
-webkit-appearance: button;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled], input[disabled] {
|
||||||
|
cursor: no-drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-brand-blue);
|
||||||
|
border-bottom: dashed 1px var(--color-brand-blue);
|
||||||
|
|
||||||
|
// TODO: move to other place
|
||||||
|
@extend .anim;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
border-bottom-style: solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[hidden], template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color: var(--default-color-black);
|
||||||
|
color: var(--default-color-white);
|
||||||
|
}
|
||||||
|
|
6
old/styles/main.scss
Normal file
6
old/styles/main.scss
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
@import 'variables';
|
||||||
|
@import 'mixins/mod';
|
||||||
|
@import 'base/reset';
|
||||||
|
@import 'base/layout';
|
||||||
|
@import 'atoms/mod';
|
||||||
|
@import 'uikit/mod';
|
165
old/styles/mixins/flex.scss
Normal file
165
old/styles/mixins/flex.scss
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
@mixin dis-flex {
|
||||||
|
display: -webkit-box;
|
||||||
|
display: -ms-flexbox;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin dis-inl-flex {
|
||||||
|
display: -webkit-inline-box;
|
||||||
|
display: -ms-inline-flexbox;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-dir-row {
|
||||||
|
@include dis-flex;
|
||||||
|
-webkit-box-orient: horizontal;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: row;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-dir-col {
|
||||||
|
@include dis-flex;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-box-direction: normal;
|
||||||
|
-ms-flex-direction: column;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-dir($val) {
|
||||||
|
@if $val == row {@include flex-dir-row;}
|
||||||
|
@else if $val == col {@include flex-dir-col;}
|
||||||
|
@else {@error 'unknown flex-direction: #{$val}';}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-wrap($val: wrap) {
|
||||||
|
-ms-flex-wrap: $val;
|
||||||
|
flex-wrap: $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-nowrap {
|
||||||
|
@include flex-wrap(nowrap);
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex($val) {
|
||||||
|
-webkit-box-flex: $val;
|
||||||
|
-ms-flex: $val;
|
||||||
|
flex: $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-shrink($val) {
|
||||||
|
-ms-flex-negative: $val;
|
||||||
|
flex-shrink: $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ord($val) {
|
||||||
|
-webkit-box-ordinal-group: $val + 1;
|
||||||
|
-ms-flex-order: $val;
|
||||||
|
order: $val;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-jus-sta {
|
||||||
|
-webkit-box-pack: start;
|
||||||
|
-ms-flex-pack: start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-jus-cen {
|
||||||
|
-webkit-box-pack: center;
|
||||||
|
-ms-flex-pack: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-jus-end {
|
||||||
|
-webkit-box-pack: end;
|
||||||
|
-ms-flex-pack: end;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-jus-aro {
|
||||||
|
-ms-flex-pack: distribute;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-jus-bet {
|
||||||
|
-webkit-box-pack: justify;
|
||||||
|
-ms-flex-pack: justify;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-jus($val) {
|
||||||
|
@if $val == sta {@include flex-jus-sta;}
|
||||||
|
@else if $val == cen {@include flex-jus-cen;}
|
||||||
|
@else if $val == end {@include flex-jus-end;}
|
||||||
|
@else if $val == aro {@include flex-jus-aro;}
|
||||||
|
@else if $val == bet {@include flex-jus-bet;}
|
||||||
|
@else {@error 'unknown flex-jus (justify-content) property: #{$val}';}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-ali-sta {
|
||||||
|
-webkit-box-align: start;
|
||||||
|
-ms-flex-align: start;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-ali-cen {
|
||||||
|
-webkit-box-align: center;
|
||||||
|
-ms-flex-align: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-ali-end {
|
||||||
|
-webkit-box-align: end;
|
||||||
|
-ms-flex-align: end;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-ali-str {
|
||||||
|
-webkit-box-align: stretch;
|
||||||
|
-ms-flex-align: stretch;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-ali($val) {
|
||||||
|
@if $val == sta {@include flex-ali-sta;}
|
||||||
|
@else if $val == cen {@include flex-ali-cen;}
|
||||||
|
@else if $val == end {@include flex-ali-end;}
|
||||||
|
@else if $val == str {@include flex-ali-str;}
|
||||||
|
@else {@error 'unknown flex-ali (align-items) property: #{$val}';}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ali-self-sta {
|
||||||
|
-ms-flex-item-align: start;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ali-self-cen {
|
||||||
|
-ms-flex-item-align: center;
|
||||||
|
-ms-grid-row-align: center;
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ali-self-end {
|
||||||
|
-ms-flex-item-align: end;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ali-self-str {
|
||||||
|
-ms-flex-item-align: stretch;
|
||||||
|
-ms-grid-row-align: stretch;
|
||||||
|
align-self: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin ali-self($val) {
|
||||||
|
@if $val == sta {@include ali-self-sta;}
|
||||||
|
@else if $val == cen {@include ali-self-cen;}
|
||||||
|
@else if $val == end {@include ali-self-end;}
|
||||||
|
@else if $val == str {@include ali-self-str;}
|
||||||
|
@else {@error 'unknown ali-self (align-self) property: #{$val}';}
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin flex-layout($dir: none, $ali: none, $jus: none) {
|
||||||
|
@if $dir != none {@include flex-dir($dir);}
|
||||||
|
@if $jus != none {@include flex-jus($jus);}
|
||||||
|
@if $ali != none {@include flex-ali($ali);}
|
||||||
|
}
|
2
old/styles/mixins/mod.scss
Normal file
2
old/styles/mixins/mod.scss
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
@import 'flex';
|
||||||
|
@import 'white-spaces';
|
29
old/styles/mixins/white-spaces.scss
Normal file
29
old/styles/mixins/white-spaces.scss
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
|
||||||
|
@mixin mar($val) { margin: #{$val}rem }
|
||||||
|
@mixin mar-v($val) { @include mar-t($val); @include mar-b($val) }
|
||||||
|
@mixin mar-h($val) { @include mar-r($val); @include mar-l($val) }
|
||||||
|
@mixin mar-t($val) { margin-top: #{$val}rem }
|
||||||
|
@mixin mar-r($val) { margin-right: #{$val}rem }
|
||||||
|
@mixin mar-b($val) { margin-bottom: #{$val}rem }
|
||||||
|
@mixin mar-l($val) { margin-left: #{$val}rem }
|
||||||
|
@mixin mar-ha { margin-left: auto; margin-right: auto }
|
||||||
|
|
||||||
|
@mixin pad($val) { padding: #{$val}rem }
|
||||||
|
@mixin pad-v($val) { @include pad-t($val); @include pad-b($val) }
|
||||||
|
@mixin pad-h($val) { @include pad-r($val); @include pad-l($val) }
|
||||||
|
@mixin pad-t($val) { padding-top: #{$val}rem }
|
||||||
|
@mixin pad-r($val) { padding-right: #{$val}rem }
|
||||||
|
@mixin pad-b($val) { padding-bottom: #{$val}rem }
|
||||||
|
@mixin pad-l($val) { padding-left: #{$val}rem }
|
||||||
|
|
||||||
|
@mixin gap-v($val) {
|
||||||
|
> :not([hidden]):not(.hidden-input) + :not([hidden]):not(.hidden-input) { @include mar-t($val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin gap-h($val) {
|
||||||
|
> :not(:last-child):not(:only-child) { @include mar-r($val) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin gap-h-l($val) {
|
||||||
|
> :not([hidden]):not(.hidden-input) + :not([hidden]):not(.hidden-input) { @include mar-l($val) }
|
||||||
|
}
|
83
old/styles/uikit/dropdown.scss
Normal file
83
old/styles/uikit/dropdown.scss
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
/* dropdown
|
||||||
|
* Source: https://codepen.io/markcaron/pen/wdVmpB
|
||||||
|
*
|
||||||
|
* TODO: change styles
|
||||||
|
* */
|
||||||
|
|
||||||
|
.dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
& > input[type="checkbox"] {
|
||||||
|
position: absolute;
|
||||||
|
left: -100vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > label {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 6px 15px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-decoration: none;
|
||||||
|
border: 1px solid #8c8c8c;
|
||||||
|
cursor: pointer;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: "▲";
|
||||||
|
font-size: 10px;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 6px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > ul {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
display: block;
|
||||||
|
left: -100vw;
|
||||||
|
bottom: calc(1.5em + 14px);
|
||||||
|
border: 1px solid #8c8c8c;
|
||||||
|
background: #fff;
|
||||||
|
padding: 6px 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
width: 100%;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
-webkit-box-shadow: 0 3px 8px rgba(0,0,0,.15);
|
||||||
|
-moz-box-shadow: 0 3px 8px rgba(0,0,0,.15);
|
||||||
|
box-shadow: 0 3px 8px rgba(0,0,0,.15);
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: block;
|
||||||
|
padding: 6px 15px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
background: #ececec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > input[type="checkbox"]:checked {
|
||||||
|
& ~ ul {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + label:after {
|
||||||
|
content: "▼";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
21
old/styles/uikit/main-menu.scss
Normal file
21
old/styles/uikit/main-menu.scss
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
.main-menu {
|
||||||
|
@extend .row-sta-sta, .gap-h-1;
|
||||||
|
|
||||||
|
> a {
|
||||||
|
@extend .pad-0x5, .anim;
|
||||||
|
|
||||||
|
// TODO: move to atoms
|
||||||
|
color: var(--color-brand-blue);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid var(--color-brand-blue);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&[aria-current]:not([aria-current=""]) {
|
||||||
|
color: var(--default-color-white);
|
||||||
|
background-color: var(--color-brand-blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
2
old/styles/uikit/mod.scss
Normal file
2
old/styles/uikit/mod.scss
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
@import 'main-menu';
|
||||||
|
@import 'dropdown';
|
9
old/styles/variables.scss
Normal file
9
old/styles/variables.scss
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
@use "sass:math";
|
||||||
|
|
||||||
|
$radius: 0.25rem;
|
||||||
|
$col: 12;
|
||||||
|
$col-gutter: 24;
|
||||||
|
$page-max-width: 1440;
|
||||||
|
$page-min-width: 320;
|
||||||
|
$content-width: $page-max-width - ($col-gutter * 2);
|
||||||
|
$col-width: math.div($content-width - $col-gutter * $col - 1, $col);
|
16
old/translates/eng.ts
Normal file
16
old/translates/eng.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Translations } from "./rus.ts";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
About: "About",
|
||||||
|
Works: "Works",
|
||||||
|
Chronological: "Chronological",
|
||||||
|
Source_code: "Source code",
|
||||||
|
Page_not_found: "Page not found",
|
||||||
|
Internal_server_error: "Internal server error",
|
||||||
|
Name: "Name",
|
||||||
|
Description: "Description",
|
||||||
|
Role: "Role",
|
||||||
|
Technologies: "Technologies",
|
||||||
|
Start: "Start",
|
||||||
|
Status_or_End: "Status/End",
|
||||||
|
} as Translations;
|
18
old/translates/rus.ts
Normal file
18
old/translates/rus.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
export const rus = {
|
||||||
|
About: "Обо мне",
|
||||||
|
Works: "Работы",
|
||||||
|
Chronological: "Хронология",
|
||||||
|
Source_code: "Исходный код",
|
||||||
|
Page_not_found: "Страница не найдена",
|
||||||
|
Internal_server_error: "Внутренняя ошибка сервера",
|
||||||
|
Name: "Название",
|
||||||
|
Description: "Описание",
|
||||||
|
Role: "Роль",
|
||||||
|
Technologies: "Технологии",
|
||||||
|
Start: "Начало",
|
||||||
|
Status_or_End: "Статус/Окончание",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default rus;
|
||||||
|
|
||||||
|
export type Translations = typeof rus;
|
20
old/uikit/link.ts
Normal file
20
old/uikit/link.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { AnyNode, Attrs, E } from "ren/node.ts";
|
||||||
|
|
||||||
|
export function Link(
|
||||||
|
text: string,
|
||||||
|
sourceAttrs: Attrs | Attrs[],
|
||||||
|
): AnyNode {
|
||||||
|
const attrs = Array.isArray(sourceAttrs) ? sourceAttrs : [sourceAttrs];
|
||||||
|
const isExternal = attrs.some((attr) =>
|
||||||
|
typeof attr.href === "string" && attr.href?.startsWith("http")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isExternal) {
|
||||||
|
attrs.push({
|
||||||
|
target: "_blank",
|
||||||
|
rel: "external nofollow noopener noreferrer",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return E("a", attrs, text);
|
||||||
|
}
|
6
old/uikit/typo.ts
Normal file
6
old/uikit/typo.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { classNames } from "ren/attrs.ts";
|
||||||
|
import { E, Elem } from "ren/node.ts";
|
||||||
|
|
||||||
|
export function H3(text: string): Elem {
|
||||||
|
return E("h3", classNames("font-h3"), text);
|
||||||
|
}
|
19
old/views/comp/layout.ts
Normal file
19
old/views/comp/layout.ts
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import { AnyNode, E } from "ren/node.ts";
|
||||||
|
import { Context } from "../../context.ts";
|
||||||
|
|
||||||
|
export function Layout(ctx: Context, page: AnyNode): AnyNode {
|
||||||
|
return E("html", { lang: ctx.lang }, [
|
||||||
|
E("head", [], [
|
||||||
|
E("meta", { charset: "utf-8" }),
|
||||||
|
E("meta", {
|
||||||
|
name: "viewport",
|
||||||
|
content: "width=device-width, initial-scale=1",
|
||||||
|
}),
|
||||||
|
E("link", { rel: "stylesheet", href: "/styles/main.css" }),
|
||||||
|
E("title", [], ctx.title ?? "Pleshevski"),
|
||||||
|
]),
|
||||||
|
E("body", [], [
|
||||||
|
E("div", { id: "root" }, [page]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
72
old/views/comp/page_layout.ts
Normal file
72
old/views/comp/page_layout.ts
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import { AnyNode, Attrs, E, Elem } from "ren/node.ts";
|
||||||
|
import { classNames } from "ren/attrs.ts";
|
||||||
|
import { Context, getLangHref, iterLangs, Lang } from "../../context.ts";
|
||||||
|
import { Link } from "../../uikit/link.ts";
|
||||||
|
import { renderDate } from "../../render.ts";
|
||||||
|
|
||||||
|
const SITE_UPDATED_AT = new Date();
|
||||||
|
|
||||||
|
export function PageLayout(ctx: Context, children: AnyNode[]): Elem {
|
||||||
|
return E("div", { id: "main" }, [
|
||||||
|
Header(ctx),
|
||||||
|
E("div", classNames("content"), children),
|
||||||
|
Footer(ctx),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Header(ctx: Context): AnyNode {
|
||||||
|
return E("header", classNames("header gap-v-1x5"), [
|
||||||
|
E("div", classNames("content-width"), [HeaderNav(ctx)]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HeaderNav(ctx: Context): AnyNode {
|
||||||
|
return E("nav", classNames("main-menu"), [
|
||||||
|
Link(ctx.tr.About, navLink("/", ctx)),
|
||||||
|
Link(ctx.tr.Works, navLink("/works", ctx)),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function navLink(lhref: string, ctx?: Context): Attrs {
|
||||||
|
const attrs: Attrs = { lhref };
|
||||||
|
if (ctx?.locPath === lhref) attrs["aria-current"] = "true";
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Footer(ctx: Context): AnyNode {
|
||||||
|
return E("footer", classNames("footer"), [
|
||||||
|
E("div", classNames("content-width row-sta-bet"), [
|
||||||
|
E("div", classNames("gap-v-1x5"), [
|
||||||
|
E("div", [], [
|
||||||
|
E("b", [], "Updated At:"),
|
||||||
|
renderDate(SITE_UPDATED_AT),
|
||||||
|
]),
|
||||||
|
E("div", [], [
|
||||||
|
Link(ctx.tr.Source_code, {
|
||||||
|
href: "https://git.pleshevski.ru/pleshevskiy/pleshevski.ru",
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
ChangeLang(ctx),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChangeLang(ctx: Context): AnyNode {
|
||||||
|
const dropdownId = "change_langs";
|
||||||
|
return E("div", classNames("dropdown"), [
|
||||||
|
E("input", { id: dropdownId, type: "checkbox" }),
|
||||||
|
E("label", { for: dropdownId }, ctx.lang),
|
||||||
|
E(
|
||||||
|
"ul",
|
||||||
|
[],
|
||||||
|
iterLangs().filter((l) => l !== ctx.lang).map((l) =>
|
||||||
|
ChangeLangBtn(ctx, l)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChangeLangBtn(ctx: Context, lang: Lang): AnyNode {
|
||||||
|
return Link(lang, { "href": getLangHref(lang, ctx.locPath) });
|
||||||
|
}
|
12
old/views/pages/content.ts
Normal file
12
old/views/pages/content.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { PageLayout } from "../comp/page_layout.ts";
|
||||||
|
import { AnyNode, E } from "ren/node.ts";
|
||||||
|
import { classNames } from "ren/attrs.ts";
|
||||||
|
import { Context } from "../../context.ts";
|
||||||
|
|
||||||
|
export function ContentPage(ctx: Context, content: AnyNode): AnyNode {
|
||||||
|
ctx.title = "About | Pleshevski";
|
||||||
|
|
||||||
|
return PageLayout(ctx, [
|
||||||
|
E("div", classNames("content-width responsive-typography"), [content]),
|
||||||
|
]);
|
||||||
|
}
|
17
old/views/pages/e404.ts
Normal file
17
old/views/pages/e404.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { PageLayout } from "../comp/page_layout.ts";
|
||||||
|
import { AnyNode, E } from "ren/node.ts";
|
||||||
|
import { classNames } from "ren/attrs.ts";
|
||||||
|
import { Context } from "../../context.ts";
|
||||||
|
import { H3 } from "../../uikit/typo.ts";
|
||||||
|
|
||||||
|
export function E404Page(ctx: Context): AnyNode {
|
||||||
|
ctx.title = "Not Found - 404 | Pleshevski";
|
||||||
|
|
||||||
|
return PageLayout(ctx, [E404(ctx)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function E404(ctx: Context): AnyNode {
|
||||||
|
return E("div", classNames("content-width gap-v-1x5"), [
|
||||||
|
H3(ctx.tr.Page_not_found),
|
||||||
|
]);
|
||||||
|
}
|
17
old/views/pages/e500.ts
Normal file
17
old/views/pages/e500.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { PageLayout } from "../comp/page_layout.ts";
|
||||||
|
import { AnyNode, E } from "ren/node.ts";
|
||||||
|
import { classNames } from "ren/attrs.ts";
|
||||||
|
import { Context } from "../../context.ts";
|
||||||
|
import { H3 } from "../../uikit/typo.ts";
|
||||||
|
|
||||||
|
export function E500Page(ctx: Context): AnyNode {
|
||||||
|
ctx.title = "Internal Server Error - 500 | Pleshevski";
|
||||||
|
|
||||||
|
return PageLayout(ctx, [E500(ctx)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function E500(ctx: Context): AnyNode {
|
||||||
|
return E("div", classNames("content-width gap-v-1x5"), [
|
||||||
|
H3(ctx.tr.Internal_server_error),
|
||||||
|
]);
|
||||||
|
}
|
18
old/views/pages/works.ts
Normal file
18
old/views/pages/works.ts
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import { PageLayout } from "../comp/page_layout.ts";
|
||||||
|
import { AnyNode, E } from "ren/node.ts";
|
||||||
|
import { classNames } from "ren/attrs.ts";
|
||||||
|
import { Context } from "../../context.ts";
|
||||||
|
import { ChronologicalWorksTable } from "../../modules/work/ChronologicalWorksTable/mod.ts";
|
||||||
|
import { H3 } from "../../uikit/typo.ts";
|
||||||
|
|
||||||
|
export function WorksPage(ctx: Context, content: AnyNode): AnyNode {
|
||||||
|
ctx.title = "Works | Pleshevski";
|
||||||
|
|
||||||
|
return PageLayout(ctx, [
|
||||||
|
E("div", classNames("content-width gap-v-1x5 responsive-typography"), [
|
||||||
|
content,
|
||||||
|
H3(ctx.tr.Chronological),
|
||||||
|
ChronologicalWorksTable(ctx.tr),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
}
|
Loading…
Reference in a new issue