Move beta into stable
|
|
@ -0,0 +1,20 @@
|
||||||
|
{"match": "\"Arch\"*", "color": "6 6 7 1"}
|
||||||
|
${c1} -`
|
||||||
|
.o+`
|
||||||
|
`ooo/
|
||||||
|
`+oooo:
|
||||||
|
`+oooooo:
|
||||||
|
-+oooooo+:
|
||||||
|
`/:-:++oooo+:
|
||||||
|
`/++++/+++++++:
|
||||||
|
`/++++++++++++++:
|
||||||
|
`/+++o${c2}oooooooo${c1}oooo/`
|
||||||
|
${c2} ${c1}./${c2}ooosssso++osssssso${c1}+`
|
||||||
|
${c2} .oossssso-````/ossssss+`
|
||||||
|
-osssssso. :ssssssso.
|
||||||
|
:osssssss/ osssso+++.
|
||||||
|
/ossssssss/ +ssssooo/-
|
||||||
|
`/ossssso+/:- -:/+osssso+-
|
||||||
|
`+sso+:-` `.-/+oso:
|
||||||
|
`++:. `-/+/
|
||||||
|
.` `/
|
||||||
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 925 B |
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 995 KiB After Width: | Height: | Size: 240 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 423 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 114 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="#000000" class="bi bi-github" viewBox="0 0 16 16">
|
|
||||||
<path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 703 B |
|
|
@ -0,0 +1,177 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Cool People</title>
|
||||||
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
<link rel="stylesheet" href="/css/themes/mocha.css">
|
||||||
|
<link rel="stylesheet" href="/css/themes/macchiato.css">
|
||||||
|
<link rel="stylesheet" href="/css/themes/frappe.css">
|
||||||
|
<link rel="stylesheet" href="/css/themes/latte.css">
|
||||||
|
<link rel="stylesheet" href="/api/api.css">
|
||||||
|
<script>try { var f = localStorage.getItem('ctpFlavor'); document.documentElement.setAttribute('data-flavor', ['mocha', 'macchiato', 'frappe', 'latte'].indexOf(f) >= 0 ? f : 'mocha'); } catch (e) { document.documentElement.setAttribute('data-flavor', 'mocha'); }</script>
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/assets/favicon/favicon.svg">
|
||||||
|
|
||||||
|
<!-- SEO Meta Tags -->
|
||||||
|
<meta name="description" content="Link Center for Clove Twilight" />
|
||||||
|
<meta name="keywords" content="Portfolio, Personal, Developer" />
|
||||||
|
<meta name="author" content="doughmination" />
|
||||||
|
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
||||||
|
|
||||||
|
<!-- Canonical URL -->
|
||||||
|
<link rel="canonical" href="https://clove.is-a.dev/cool-people" />
|
||||||
|
|
||||||
|
<!-- Alternate for mobile -->
|
||||||
|
<link rel="alternate" media="only screen and (max-width: 640px)" href="https://clove.is-a.dev" />
|
||||||
|
|
||||||
|
<!-- Theme Color -->
|
||||||
|
<meta name="theme-color" content="#f5c2e7" />
|
||||||
|
|
||||||
|
<!-- Open Graph / Discord / Facebook -->
|
||||||
|
<meta property="og:image" content="https://clove.is-a.dev/assets/favicon/favicon.png" />
|
||||||
|
<meta property="og:site_name" content="clove.is-a.dev" />
|
||||||
|
<meta property="og:title" content="Cool People | Clove Twilight" />
|
||||||
|
<meta property="og:description" content="Cool people Clove knows!" />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:url" content="https://clove.is-a.dev/cool-people" />
|
||||||
|
<meta property="og:locale" content="en_GB" />
|
||||||
|
|
||||||
|
<!-- Twitter Card -->
|
||||||
|
<meta name="twitter:image" content="https://clove.is-a.dev/assets/favicon/favicon.png" />
|
||||||
|
<meta name="twitter:card" content="summary" />
|
||||||
|
<meta name="twitter:title" content="Cool People | Clove Twilight" />
|
||||||
|
<meta name="twitter:description" content="Cool people Clove knows!" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="nav">
|
||||||
|
<nav class="nav-links">
|
||||||
|
<a class="nav-link" data-href="/">Link Center</a>
|
||||||
|
<a class="nav-link selected" data-href="/cool-people">Cool People</a>
|
||||||
|
<a class="nav-link" data-href="/dev-info">Coding Stats</a>
|
||||||
|
<a class="nav-link" data-href="/discord-bots">Discord Bots</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="now-playing"></div>
|
||||||
|
|
||||||
|
<div class="hub">
|
||||||
|
<header class="hub-header">
|
||||||
|
<h1>Cool People</h1>
|
||||||
|
<p class="tagline">This is where people I know can be put up on my site, click their profiles for their pages</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Girlfriend -->
|
||||||
|
<section class="section">
|
||||||
|
<h2 class="section-title">Fiancee</h2>
|
||||||
|
<div class="friend-grid">
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp wife" src="/assets/friends/ari.png">
|
||||||
|
<span class="friend-name wife">Aria</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Close Friends -->
|
||||||
|
<section class="section">
|
||||||
|
<h2 class="section-title">Close Friends</h2>
|
||||||
|
<div class="friend-grid">
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp close" src="/assets/friends/ria.png">
|
||||||
|
<span class="friend-name close">Ria</span>
|
||||||
|
</a>
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp close"
|
||||||
|
src="/assets/friends/lilly.png">
|
||||||
|
<span class="friend-name close">Lilly</span>
|
||||||
|
</a>
|
||||||
|
<a class="friend" href="https://cammy-the-cat.com">
|
||||||
|
<img class="friend-pfp close"
|
||||||
|
src="/assets/friends/camilla.png">
|
||||||
|
<span class="friend-name close">Camilla</span>
|
||||||
|
</a>
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp close"
|
||||||
|
src="/assets/friends/saphie.png">
|
||||||
|
<span class="friend-name close">Saphie</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<h2 class="section-title">Friends</h2>
|
||||||
|
<div class="friend-grid">
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp" src="/assets/friends/meme.png">
|
||||||
|
<span class="friend-name">Memeancia</span>
|
||||||
|
</a>
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp" src="/assets/friends/n.png">
|
||||||
|
<span class="friend-name">N</span>
|
||||||
|
</a>
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp" src="/assets/friends/lylla.png">
|
||||||
|
<span class="friend-name">Lylla</span>
|
||||||
|
</a>
|
||||||
|
<a class="friend">
|
||||||
|
<img class="friend-pfp" src="/assets/friends/simon.png">
|
||||||
|
<span class="friend-name">Simon</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="section">
|
||||||
|
<h2 class="section-title">Other Peeps</h2>
|
||||||
|
<p class="section-subtitle">You can request to be added here!</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="button-wall" aria-label="88x31 buttons">
|
||||||
|
<img src="/assets/88x31/linux.gif" alt="Linux" loading="lazy">
|
||||||
|
<img src="/assets/88x31/microslop.gif" alt="Microslop" loading="lazy">
|
||||||
|
<img src="/assets/88x31/estrogen.gif" alt="Estrogen" loading="lazy">
|
||||||
|
<img src="/assets/88x31/girlsnow.png" alt="Girls Network" loading="lazy">
|
||||||
|
<img src="/assets/88x31/skirt.gif" alt="Skirt" loading="lazy">
|
||||||
|
<img src="/assets/88x31/gitgay.png" alt="GitGay" loading="lazy">
|
||||||
|
<img src="/assets/88x31/blink.gif" alt="Blink" loading="lazy">
|
||||||
|
<img src="/assets/88x31/firefox.gif" alt="Firefox" loading="lazy">
|
||||||
|
<img src="/assets/88x31/nft.gif" alt="NFT" loading="lazy">
|
||||||
|
<img src="/assets/88x31/noweb32.gif" alt="No Web 3.2" loading="lazy">
|
||||||
|
<img src="/assets/88x31/meltice.gif" alt="Melt Ice" loading="lazy">
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<aside class="badges" aria-label="System badges">
|
||||||
|
<span class="badge">
|
||||||
|
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
||||||
|
AMD Purist
|
||||||
|
</span>
|
||||||
|
<span class="badge">
|
||||||
|
<img class="badge-icon" src="/assets/misc/arch.svg" alt="">
|
||||||
|
I use arch btw
|
||||||
|
</span>
|
||||||
|
<span class="badge">
|
||||||
|
<img class="badge-icon" src="/assets/misc/debian.svg" alt="">
|
||||||
|
Debian Professional
|
||||||
|
</span>
|
||||||
|
<span class="badge">
|
||||||
|
<img class="badge-icon" src="/assets/misc/apple.svg" alt="">
|
||||||
|
Apple Ecosystem Enthusiast
|
||||||
|
</span>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
|
||||||
|
<script src="/js/cat.js" data-cat="/assets/oneko/classics/classic.png"></script>
|
||||||
|
<script src="/js/nav.js"></script>
|
||||||
|
<script src="/api/now-playing.js" data-user="1464890289922641993"></script>
|
||||||
|
<script src="/js/flavors.js"></script>
|
||||||
|
<script src="/js/dev-mode.js"></script>
|
||||||
|
<script src="/js/terminal.js"></script>
|
||||||
|
<script src="/js/site-switcher.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
1006
css/main.css
|
|
@ -4,27 +4,22 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Clove Twilight - Tech Stack</title>
|
<title>Clove Twilight - Coding Stats</title>
|
||||||
<link rel="stylesheet" href="/css/main.css">
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
<link rel="stylesheet" href="/css/themes/mocha.css">
|
|
||||||
<link rel="stylesheet" href="/css/themes/macchiato.css">
|
|
||||||
<link rel="stylesheet" href="/css/themes/frappe.css">
|
|
||||||
<link rel="stylesheet" href="/css/themes/latte.css">
|
|
||||||
<link rel="stylesheet" href="/api/api.css">
|
|
||||||
<script>try { var f = localStorage.getItem('ctpFlavor'); document.documentElement.setAttribute('data-flavor', ['mocha', 'macchiato', 'frappe', 'latte'].indexOf(f) >= 0 ? f : 'mocha'); } catch (e) { document.documentElement.setAttribute('data-flavor', 'mocha'); }</script>
|
<script>try { var f = localStorage.getItem('ctpFlavor'); document.documentElement.setAttribute('data-flavor', ['mocha', 'macchiato', 'frappe', 'latte'].indexOf(f) >= 0 ? f : 'mocha'); } catch (e) { document.documentElement.setAttribute('data-flavor', 'mocha'); }</script>
|
||||||
<link rel="icon" type="image/svg+xml" href="/assets/favicon/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/assets/favicon/favicon.svg">
|
||||||
|
|
||||||
<!-- SEO Meta Tags -->
|
<!-- SEO Meta Tags -->
|
||||||
<meta name="description" content="Tech Stack for Clove Twilight" />
|
<meta name="description" content="What Clove has been coding lately, tracked by dev-info" />
|
||||||
<meta name="keywords" content="Clove Twilight, Portfolio, Personal, Developer" />
|
<meta name="keywords" content="Portfolio, Personal, Developer, dev-info, Coding Stats" />
|
||||||
<meta name="author" content="doughmination" />
|
<meta name="author" content="doughmination" />
|
||||||
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" />
|
||||||
|
|
||||||
<!-- Canonical URL -->
|
<!-- Canonical URL -->
|
||||||
<link rel="canonical" href="https://clove.is-a.dev/tech-stack" />
|
<link rel="canonical" href="https://clove.is-a.dev/dev-info" />
|
||||||
|
|
||||||
<!-- Alternate for mobile -->
|
<!-- Alternate for mobile -->
|
||||||
<link rel="alternate" media="only screen and (max-width: 640px)" href="https://clove.is-a.dev/tech-stack" />
|
<link rel="alternate" media="only screen and (max-width: 640px)" href="https://clove.is-a.dev/dev-info" />
|
||||||
|
|
||||||
<!-- Theme Color -->
|
<!-- Theme Color -->
|
||||||
<meta name="theme-color" content="#f5c2e7" />
|
<meta name="theme-color" content="#f5c2e7" />
|
||||||
|
|
@ -32,56 +27,62 @@
|
||||||
<!-- Open Graph / Discord / Facebook -->
|
<!-- Open Graph / Discord / Facebook -->
|
||||||
<meta property="og:image" content="https://clove.is-a.dev/assets/favicon/favicon.png" />
|
<meta property="og:image" content="https://clove.is-a.dev/assets/favicon/favicon.png" />
|
||||||
<meta property="og:site_name" content="clove.is-a.dev" />
|
<meta property="og:site_name" content="clove.is-a.dev" />
|
||||||
<meta property="og:title" content="Clove Twilight | Tech Stack" />
|
<meta property="og:title" content="Clove Twilight | Coding Stats" />
|
||||||
<meta property="og:description" content="Tech Stack for Clove Twilight" />
|
<meta property="og:description" content="What Clove has been coding lately, tracked by dev-info" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:url" content="https://clove.is-a.dev/tech-stack" />
|
<meta property="og:url" content="https://clove.is-a.dev/dev-info" />
|
||||||
<meta property="og:locale" content="en_GB" />
|
<meta property="og:locale" content="en_GB" />
|
||||||
|
|
||||||
<!-- Twitter Card -->
|
<!-- Twitter Card -->
|
||||||
<meta name="twitter:image" content="https://clove.is-a.dev/assets/favicon/favicon.png" />
|
<meta name="twitter:image" content="https://clove.is-a.dev/assets/favicon/favicon.png" />
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
<meta name="twitter:title" content="Clove Twilight | Tech Stack" />
|
<meta name="twitter:title" content="Clove Twilight | Coding Stats" />
|
||||||
<meta name="twitter:description" content="Tech Stack for Clove Twilight" />
|
<meta name="twitter:description" content="What Clove has been coding lately, tracked by dev-info" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
<div id="now-playing"></div>
|
||||||
|
|
||||||
<header class="nav">
|
<header class="nav">
|
||||||
<nav class="nav-links">
|
<nav class="nav-links">
|
||||||
<a class="nav-link" data-href="/">Link Center</a>
|
<a class="nav-link" data-href="/">Link Center</a>
|
||||||
<a class="nav-link selected" data-href="/tech-stack">Tech Stack</a>
|
<a class="nav-link" data-href="/cool-people">Cool People</a>
|
||||||
|
<a class="nav-link selected" data-href="/dev-info">Coding Stats</a>
|
||||||
<a class="nav-link" data-href="/discord-bots">Discord Bots</a>
|
<a class="nav-link" data-href="/discord-bots">Discord Bots</a>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<a class="now-playing" id="now-playing" target="_blank" rel="noopener" hidden>
|
<main class="waka">
|
||||||
<img class="np-art" alt="" referrerpolicy="no-referrer" crossorigin="anonymous">
|
|
||||||
<span class="np-bars" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
|
|
||||||
<span class="np-text">
|
|
||||||
<span class="np-head">
|
|
||||||
<span class="np-status" aria-hidden="true"></span>
|
|
||||||
<span class="np-status-label"></span>
|
|
||||||
<span class="np-label">Now playing</span>
|
|
||||||
</span>
|
|
||||||
<span class="np-track"></span>
|
|
||||||
<span class="np-artist"></span>
|
|
||||||
<span class="np-progress" aria-hidden="true">
|
|
||||||
<span class="np-bar"><span class="np-fill"></span></span>
|
|
||||||
<span class="np-times"><span class="np-cur">0:00</span><span class="np-dur">0:00</span></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<main class="hub">
|
|
||||||
<header class="hub-header">
|
<header class="hub-header">
|
||||||
<img class="pfp" src="/assets/favicon/avatar.png" alt="Clove Twilight avatar">
|
<img class="pfp" src="/assets/favicon/avatar.png" alt="Clove Twilight avatar">
|
||||||
<h1>Clove Twilight</h1>
|
<h1>Clove Twilight</h1>
|
||||||
<h2 class="pronouns">(fae/faer)</h2>
|
<h2 class="pronouns">(fae/faer)</h2>
|
||||||
<p class="tagline">Tech Stack</p>
|
<p class="tagline">Dev Info</p>
|
||||||
|
<p class="waka-meta" id="waka-meta" hidden></p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section class="tech-stack" aria-label="Tech stack">
|
<!-- shown only until the dev-info share URLs are filled in -->
|
||||||
|
<section class="waka-setup" id="waka-setup" hidden>
|
||||||
|
<h2 class="section-title">Almost there</h2>
|
||||||
|
<p class="waka-empty">
|
||||||
|
This page is wired up and ready — it just needs your dev-info
|
||||||
|
<strong>embeddable JSON</strong> share URLs.
|
||||||
|
</p>
|
||||||
|
<ol class="waka-steps">
|
||||||
|
<li>Open <a href="https://dev-info.com/share/embed" target="_blank"
|
||||||
|
rel="noopener">dev-info.com/share/embed</a>.</li>
|
||||||
|
<li>Create an embed for each of: <em>Coding Activity, Languages, Projects, Editors, Operating
|
||||||
|
Systems</em>.</li>
|
||||||
|
<li>Choose the <strong>JSON</strong> format and copy each <code>.json</code> URL.</li>
|
||||||
|
<li>Paste them into the <code>dev-info</code> block at the top of <code>/js/dev-info.js</code>.</li>
|
||||||
|
</ol>
|
||||||
|
<a class="waka-setup-btn" href="https://dev-info.com/share/embed" target="_blank" rel="noopener">Open
|
||||||
|
dev-info Share</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="dev-info" aria-label="Tech stack">
|
||||||
<span class="tech-icon pink" style="--si:url('https://cdn.simpleicons.org/javascript')" role="img"
|
<span class="tech-icon pink" style="--si:url('https://cdn.simpleicons.org/javascript')" role="img"
|
||||||
aria-label="JavaScript"></span>
|
aria-label="JavaScript"></span>
|
||||||
<span class="tech-icon mauve" style="--si:url('https://cdn.simpleicons.org/openjdk')" role="img"
|
<span class="tech-icon mauve" style="--si:url('https://cdn.simpleicons.org/openjdk')" role="img"
|
||||||
|
|
@ -171,8 +172,57 @@
|
||||||
<span class="tech-icon blue" style="--si:url('https://cdn.simpleicons.org/vscodium')" role="img"
|
<span class="tech-icon blue" style="--si:url('https://cdn.simpleicons.org/vscodium')" role="img"
|
||||||
aria-label="VSCodium"></span>
|
aria-label="VSCodium"></span>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- the real content; revealed once at least one URL is configured -->
|
||||||
|
<div id="waka-content" hidden>
|
||||||
|
|
||||||
|
<section class="waka-section waka-total" id="waka-total">
|
||||||
|
<div class="waka-total-num" id="waka-total-val">—</div>
|
||||||
|
<div class="waka-total-sub" id="waka-total-sub">total coding time</div>
|
||||||
|
<div class="waka-week" id="waka-week"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="waka-section" id="waka-section-languages" hidden>
|
||||||
|
<h2 class="section-title">Languages</h2>
|
||||||
|
<div class="waka-bars" id="waka-languages"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="waka-section" id="waka-section-categories" hidden>
|
||||||
|
<h2 class="section-title">Categories</h2>
|
||||||
|
<div class="waka-bars" id="waka-categories"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="waka-grid2">
|
||||||
|
<section class="waka-section" id="waka-section-editors" hidden>
|
||||||
|
<h2 class="section-title">Editors</h2>
|
||||||
|
<div class="waka-bars" id="waka-editors"></div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="waka-section" id="waka-section-os" hidden>
|
||||||
|
<h2 class="section-title">Operating Systems</h2>
|
||||||
|
<div class="waka-bars" id="waka-os"></div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="waka-credit">Tracked automatically with <a href="https://wakatime.com" target="_blank"
|
||||||
|
rel="noopener">wakatime</a>.</p>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<aside class="button-wall" aria-label="88x31 buttons">
|
||||||
|
<img src="/assets/88x31/linux.gif" alt="Linux" loading="lazy">
|
||||||
|
<img src="/assets/88x31/microslop.gif" alt="Microslop" loading="lazy">
|
||||||
|
<img src="/assets/88x31/estrogen.gif" alt="Estrogen" loading="lazy">
|
||||||
|
<img src="/assets/88x31/girlsnow.png" alt="Girls Network" loading="lazy">
|
||||||
|
<img src="/assets/88x31/skirt.gif" alt="Skirt" loading="lazy">
|
||||||
|
<img src="/assets/88x31/gitgay.png" alt="GitGay" loading="lazy">
|
||||||
|
<img src="/assets/88x31/blink.gif" alt="Blink" loading="lazy">
|
||||||
|
<img src="/assets/88x31/firefox.gif" alt="Firefox" loading="lazy">
|
||||||
|
<img src="/assets/88x31/nft.gif" alt="NFT" loading="lazy">
|
||||||
|
<img src="/assets/88x31/noweb32.gif" alt="No Web 3.2" loading="lazy">
|
||||||
|
<img src="/assets/88x31/meltice.gif" alt="Melt Ice" loading="lazy">
|
||||||
|
</aside>
|
||||||
|
|
||||||
<aside class="badges" aria-label="System badges">
|
<aside class="badges" aria-label="System badges">
|
||||||
<span class="badge">
|
<span class="badge">
|
||||||
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
||||||
|
|
@ -194,9 +244,10 @@
|
||||||
|
|
||||||
<script src="/js/cat.js" data-cat="/assets/oneko/classics/classic.png"></script>
|
<script src="/js/cat.js" data-cat="/assets/oneko/classics/classic.png"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/api/now-playing.js"></script>
|
<script src="/api/now-playing.js" data-user="1464890289922641993"></script>
|
||||||
<script src="/js/flavors.js"></script>
|
<script src="/js/flavors.js"></script>
|
||||||
<script src="/js/dev-mode.js" user-data="1464890289922641993"></script>
|
<script src="/js/dev-mode.js"></script>
|
||||||
|
<script src="/js/dev-info.js"></script>
|
||||||
<script src="/js/site-switcher.js"></script>
|
<script src="/js/site-switcher.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
@ -47,28 +47,13 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<a class="now-playing" id="now-playing" target="_blank" rel="noopener" hidden>
|
div id="now-playing"></div>
|
||||||
<img class="np-art" alt="" referrerpolicy="no-referrer" crossorigin="anonymous">
|
|
||||||
<span class="np-bars" aria-hidden="true"><i></i><i></i><i></i><i></i></span>
|
|
||||||
<span class="np-text">
|
|
||||||
<span class="np-head">
|
|
||||||
<span class="np-status" aria-hidden="true"></span>
|
|
||||||
<span class="np-status-label"></span>
|
|
||||||
<span class="np-label">Now playing</span>
|
|
||||||
</span>
|
|
||||||
<span class="np-track"></span>
|
|
||||||
<span class="np-artist"></span>
|
|
||||||
<span class="np-progress" aria-hidden="true">
|
|
||||||
<span class="np-bar"><span class="np-fill"></span></span>
|
|
||||||
<span class="np-times"><span class="np-cur">0:00</span><span class="np-dur">0:00</span></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<header class="nav">
|
<header class="nav">
|
||||||
<nav class="nav-links">
|
<nav class="nav-links">
|
||||||
<a class="nav-link" data-href="/">Link Center</a>
|
<a class="nav-link" data-href="/">Link Center</a>
|
||||||
<a class="nav-link" data-href="/tech-stack">Tech Stack</a>
|
<a class="nav-link" data-href="/cool-people">Cool People</a>
|
||||||
|
<a class="nav-link" data-href="/dev-info">Coding Stats</a>
|
||||||
<a class="nav-link selected" data-href="/discord-bots">Discord Bots</a>
|
<a class="nav-link selected" data-href="/discord-bots">Discord Bots</a>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -185,9 +170,9 @@
|
||||||
|
|
||||||
<a class="bot" href="https://github.com/doughmination/widget-script">
|
<a class="bot" href="https://github.com/doughmination/widget-script">
|
||||||
<img class="bot-pfp" src="/assets/favicon/avatar.png" alt="" />
|
<img class="bot-pfp" src="/assets/favicon/avatar.png" alt="" />
|
||||||
<span class="link-text">
|
<span class="bot-link-text">
|
||||||
<span class="link-title">Widget-v2 Script</span>
|
<span class="bot-link-title">Widget-v2 Script</span>
|
||||||
<span class="link-sub">Open Source</span>
|
<span class="bot-link-sub">Open Source</span>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
@ -195,6 +180,20 @@
|
||||||
</section>
|
</section>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<aside class="button-wall" aria-label="88x31 buttons">
|
||||||
|
<img src="/assets/88x31/linux.gif" alt="Linux" loading="lazy">
|
||||||
|
<img src="/assets/88x31/microslop.gif" alt="Microslop" loading="lazy">
|
||||||
|
<img src="/assets/88x31/estrogen.gif" alt="Estrogen" loading="lazy">
|
||||||
|
<img src="/assets/88x31/girlsnow.png" alt="Girls Network" loading="lazy">
|
||||||
|
<img src="/assets/88x31/skirt.gif" alt="Skirt" loading="lazy">
|
||||||
|
<img src="/assets/88x31/gitgay.png" alt="GitGay" loading="lazy">
|
||||||
|
<img src="/assets/88x31/blink.gif" alt="Blink" loading="lazy">
|
||||||
|
<img src="/assets/88x31/firefox.gif" alt="Firefox" loading="lazy">
|
||||||
|
<img src="/assets/88x31/nft.gif" alt="NFT" loading="lazy">
|
||||||
|
<img src="/assets/88x31/noweb32.gif" alt="No Web 3.2" loading="lazy">
|
||||||
|
<img src="/assets/88x31/meltice.gif" alt="Melt Ice" loading="lazy">
|
||||||
|
</aside>
|
||||||
|
|
||||||
<aside class="badges" aria-label="System badges">
|
<aside class="badges" aria-label="System badges">
|
||||||
<span class="badge">
|
<span class="badge">
|
||||||
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
||||||
|
|
@ -219,7 +218,7 @@
|
||||||
|
|
||||||
<script src="/js/cat.js" data-cat="/assets/oneko/classics/classic.png"></script>
|
<script src="/js/cat.js" data-cat="/assets/oneko/classics/classic.png"></script>
|
||||||
<script src="/js/nav.js"></script>
|
<script src="/js/nav.js"></script>
|
||||||
<script src="/api/now-playing.js" user-data="1464890289922641993"></script>
|
<script src="/api/now-playing.js" data-user="1464890289922641993"></script>
|
||||||
<script src="/js/flavors.js"></script>
|
<script src="/js/flavors.js"></script>
|
||||||
<script src="/js/dev-mode.js"></script>
|
<script src="/js/dev-mode.js"></script>
|
||||||
<script src="/js/site-switcher.js"></script>
|
<script src="/js/site-switcher.js"></script>
|
||||||
|
|
|
||||||
135
index.html
|
|
@ -49,7 +49,8 @@
|
||||||
<header class="nav">
|
<header class="nav">
|
||||||
<nav class="nav-links">
|
<nav class="nav-links">
|
||||||
<a class="nav-link selected" data-href="/">Link Center</a>
|
<a class="nav-link selected" data-href="/">Link Center</a>
|
||||||
<a class="nav-link" data-href="/tech-stack">Tech Stack</a>
|
<a class="nav-link" data-href="/cool-people">Cool People</a>
|
||||||
|
<a class="nav-link" data-href="/dev-info">Coding Stats</a>
|
||||||
<a class="nav-link" data-href="/discord-bots">Discord Bots</a>
|
<a class="nav-link" data-href="/discord-bots">Discord Bots</a>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
|
|
@ -61,125 +62,24 @@
|
||||||
<img class="pfp" src="/assets/favicon/avatar.png" alt="Clove Twilight avatar">
|
<img class="pfp" src="/assets/favicon/avatar.png" alt="Clove Twilight avatar">
|
||||||
<h1>Clove Twilight</h1>
|
<h1>Clove Twilight</h1>
|
||||||
<h2 class="pronouns">(fae/faer)</h2>
|
<h2 class="pronouns">(fae/faer)</h2>
|
||||||
<p class="tagline">Link Center</p>
|
|
||||||
</header>
|
</header>
|
||||||
|
<div class="terminal" id="terminal"></div>
|
||||||
<nav class="links">
|
|
||||||
<a class="link-card" href="https://github.com/doughmination">
|
|
||||||
<img class="icon" src="/assets/socials/github.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">GitHub</span>
|
|
||||||
<span class="link-sub">@doughmination</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://git.gay/doughmination">
|
|
||||||
<img class="icon" src="/assets/socials/git-gay.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Git.Gay</span>
|
|
||||||
<span class="link-sub">@doughmination</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://x.com/DoughminCEO">
|
|
||||||
<img class="icon" src="/assets/socials/twitter.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Twitter</span>
|
|
||||||
<span class="link-sub">@DoughminCEO</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://bsky.app/profile/doughmination.win">
|
|
||||||
<img class="icon" src="/assets/socials/bluesky.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Bluesky</span>
|
|
||||||
<span class="link-sub">@doughmination.win</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://www.linkedin.com/in/estrogen/">
|
|
||||||
<img class="icon" src="/assets/socials/linkedin.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">LinkedIn</span>
|
|
||||||
<span class="link-sub">Clove Twilight</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://open.spotify.com/user/x060f5w4ftwv8zc8fi9662t70">
|
|
||||||
<img class="icon" src="/assets/socials/spotify.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Spotify</span>
|
|
||||||
<span class="link-sub">doughmination</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://discord.gg/TransRights">
|
|
||||||
<img class="icon" src="/assets/socials/discord.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Discord</span>
|
|
||||||
<span class="link-sub">Girls Discord Server</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://www.twitch.tv/doughminationgaming">
|
|
||||||
<img class="icon" src="/assets/socials/twitch.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Twitch</span>
|
|
||||||
<span class="link-sub">@doughminationgaming</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://www.reddit.com/user/XerinDotZero/">
|
|
||||||
<img class="icon" src="/assets/socials/reddit.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Reddit</span>
|
|
||||||
<span class="link-sub">u/XerinDotZero</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://www.youtube.com/@CloveTwiGaming">
|
|
||||||
<img class="icon" src="/assets/socials/youtube.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">YouTube</span>
|
|
||||||
<span class="link-sub">@CloveTwiGaming</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" rel="me" href="https://mastodon.social/@doughmination">
|
|
||||||
<img class="icon" src="/assets/socials/mastodon.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Mastodon</span>
|
|
||||||
<span class="link-sub">@doughmination@mastodon.social</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="mailto:admin@doughmination.win">
|
|
||||||
<img class="icon" src="/assets/socials/email.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">Email</span>
|
|
||||||
<span class="link-sub">admin@doughmination.win</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://doughmination.win/">
|
|
||||||
<img class="icon" src="/assets/socials/site.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">My Company Website</span>
|
|
||||||
<span class="link-sub">doughmination.win</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="link-card" href="https://doughmination.co.uk/">
|
|
||||||
<img class="icon" src="/assets/socials/site.svg" alt="">
|
|
||||||
<span class="link-text">
|
|
||||||
<span class="link-title">My Portfolio</span>
|
|
||||||
<span class="link-sub">doughmination.co.uk</span>
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</nav>
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<aside class="button-wall" aria-label="88x31 buttons">
|
||||||
|
<img src="/assets/88x31/linux.gif" alt="Linux" loading="lazy">
|
||||||
|
<img src="/assets/88x31/microslop.gif" alt="Microslop" loading="lazy">
|
||||||
|
<img src="/assets/88x31/estrogen.gif" alt="Estrogen" loading="lazy">
|
||||||
|
<img src="/assets/88x31/girlsnow.png" alt="Girls Network" loading="lazy">
|
||||||
|
<img src="/assets/88x31/skirt.gif" alt="Skirt" loading="lazy">
|
||||||
|
<img src="/assets/88x31/gitgay.png" alt="GitGay" loading="lazy">
|
||||||
|
<img src="/assets/88x31/blink.gif" alt="Blink" loading="lazy">
|
||||||
|
<img src="/assets/88x31/firefox.gif" alt="Firefox" loading="lazy">
|
||||||
|
<img src="/assets/88x31/nft.gif" alt="NFT" loading="lazy">
|
||||||
|
<img src="/assets/88x31/noweb32.gif" alt="No Web 3.2" loading="lazy">
|
||||||
|
<img src="/assets/88x31/meltice.gif" alt="Melt Ice" loading="lazy">
|
||||||
|
</aside>
|
||||||
|
|
||||||
<aside class="badges" aria-label="System badges">
|
<aside class="badges" aria-label="System badges">
|
||||||
<span class="badge">
|
<span class="badge">
|
||||||
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
<img class="badge-icon" src="/assets/misc/amd.svg" alt="">
|
||||||
|
|
@ -204,6 +104,7 @@
|
||||||
<script src="/api/now-playing.js" data-user="1464890289922641993"></script>
|
<script src="/api/now-playing.js" data-user="1464890289922641993"></script>
|
||||||
<script src="/js/flavors.js"></script>
|
<script src="/js/flavors.js"></script>
|
||||||
<script src="/js/dev-mode.js"></script>
|
<script src="/js/dev-mode.js"></script>
|
||||||
|
<script src="/js/terminal.js"></script>
|
||||||
<script src="/js/site-switcher.js"></script>
|
<script src="/js/site-switcher.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,294 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// ====== PASTE YOUR EMBEDDABLE JSON SHARE URLS HERE ===================
|
||||||
|
var WAKATIME = {
|
||||||
|
codingActivity: "https://wakatime.com/share/@doughmination/9dcc5b5c-ed3d-4896-bfa3-87737fa70930.json",
|
||||||
|
languages: "https://wakatime.com/share/@doughmination/8354e3f8-b458-452b-aa06-839f303d4904.json",
|
||||||
|
categories: "https://wakatime.com/share/@doughmination/c54fcd4e-91b3-46ab-8e7e-82226491ec0f.json",
|
||||||
|
editors: "https://wakatime.com/share/@doughmination/38dba24b-d2de-4d50-9b09-83642c01c33e.json",
|
||||||
|
operatingSystems: "https://wakatime.com/share/@doughmination/a69f00cb-e38e-4de1-aa42-eec71dc6d658.json"
|
||||||
|
};
|
||||||
|
// How many rows to show in each ranked list.
|
||||||
|
var MAX_ROWS = 8;
|
||||||
|
// =====================================================================
|
||||||
|
|
||||||
|
// ---- JSONP loader ---------------------------------------------------
|
||||||
|
var seq = 0;
|
||||||
|
function jsonp(url, timeoutMs) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var cb = "__wakatime_cb_" + (++seq);
|
||||||
|
var script = document.createElement("script");
|
||||||
|
var timer = setTimeout(function () { cleanup(); reject(new Error("timed out")); }, timeoutMs || 12000);
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
clearTimeout(timer);
|
||||||
|
try { delete window[cb]; } catch (e) { window[cb] = undefined; }
|
||||||
|
if (script.parentNode) script.parentNode.removeChild(script);
|
||||||
|
}
|
||||||
|
|
||||||
|
window[cb] = function (data) { cleanup(); resolve(data); };
|
||||||
|
script.onerror = function () { cleanup(); reject(new Error("failed to load")); };
|
||||||
|
|
||||||
|
var sep = url.indexOf("?") === -1 ? "?" : "&";
|
||||||
|
script.src = url + sep + "callback=" + cb;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- helpers --------------------------------------------------------
|
||||||
|
function fmt(seconds) {
|
||||||
|
seconds = Math.max(0, Math.round(seconds || 0));
|
||||||
|
var h = Math.floor(seconds / 3600);
|
||||||
|
var m = Math.round((seconds % 3600) / 60);
|
||||||
|
if (h && m) return h + " hr" + (h > 1 ? "s" : "") + " " + m + " min" + (m > 1 ? "s" : "");
|
||||||
|
if (h) return h + " hr" + (h > 1 ? "s" : "");
|
||||||
|
return m + " min" + (m === 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
|
||||||
|
function pctLabel(p) {
|
||||||
|
return (p < 10 ? Math.round(p * 10) / 10 : Math.round(p)) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value shown at the end of a bar: real time when the embed provides it,
|
||||||
|
// otherwise the percentage share.
|
||||||
|
function valueLabel(d, hasSeconds) {
|
||||||
|
if (hasSeconds && (d.total_seconds || 0) > 0) return d.text || fmt(d.total_seconds);
|
||||||
|
if (typeof d.percent === "number") return pctLabel(d.percent);
|
||||||
|
if (d.text) return d.text;
|
||||||
|
return fmt(d.total_seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function el(id) { return document.getElementById(id); }
|
||||||
|
|
||||||
|
function setMeta(text) {
|
||||||
|
var m = el("waka-meta");
|
||||||
|
if (m && text) { m.textContent = text; m.hidden = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render a ranked list of horizontal bars into a container.
|
||||||
|
function renderBars(containerId, items) {
|
||||||
|
var box = el(containerId);
|
||||||
|
if (!box) return;
|
||||||
|
if (!items || !items.length) { failSection(box, "No data yet."); return; }
|
||||||
|
|
||||||
|
// Share embeds for languages/categories/editors/OS often return only
|
||||||
|
// {name, percent, color} with no seconds — so fall back to percent for
|
||||||
|
// both the bar width and the value label when time isn't provided.
|
||||||
|
var hasSeconds = items.some(function (d) { return d && (d.total_seconds || 0) > 0; });
|
||||||
|
|
||||||
|
var rows = items
|
||||||
|
.filter(function (d) { return d && ((d.total_seconds || 0) > 0 || (d.percent || 0) > 0); })
|
||||||
|
.sort(function (a, b) {
|
||||||
|
return hasSeconds
|
||||||
|
? (b.total_seconds || 0) - (a.total_seconds || 0)
|
||||||
|
: (b.percent || 0) - (a.percent || 0);
|
||||||
|
})
|
||||||
|
.slice(0, MAX_ROWS);
|
||||||
|
|
||||||
|
var max = rows.reduce(function (acc, d) {
|
||||||
|
return Math.max(acc, hasSeconds ? (d.total_seconds || 0) : (d.percent || 0));
|
||||||
|
}, 0) || 1;
|
||||||
|
|
||||||
|
box.innerHTML = "";
|
||||||
|
rows.forEach(function (d) {
|
||||||
|
var basis = hasSeconds ? (d.total_seconds || 0) : (d.percent || 0);
|
||||||
|
var pct = Math.max(2, Math.round((basis / max) * 100));
|
||||||
|
var row = document.createElement("div");
|
||||||
|
row.className = "waka-bar-row";
|
||||||
|
|
||||||
|
var name = document.createElement("span");
|
||||||
|
name.className = "waka-bar-name";
|
||||||
|
name.textContent = d.name || "Unknown";
|
||||||
|
name.title = d.name || "";
|
||||||
|
|
||||||
|
var track = document.createElement("span");
|
||||||
|
track.className = "waka-bar-track";
|
||||||
|
var fill = document.createElement("span");
|
||||||
|
fill.className = "waka-bar-fill";
|
||||||
|
fill.style.width = pct + "%";
|
||||||
|
if (d.color) fill.style.background = d.color;
|
||||||
|
track.appendChild(fill);
|
||||||
|
|
||||||
|
var val = document.createElement("span");
|
||||||
|
val.className = "waka-bar-val";
|
||||||
|
val.textContent = valueLabel(d, hasSeconds);
|
||||||
|
|
||||||
|
row.appendChild(name);
|
||||||
|
row.appendChild(track);
|
||||||
|
row.appendChild(val);
|
||||||
|
box.appendChild(row);
|
||||||
|
});
|
||||||
|
showSection(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render the 7-day vertical bar chart + headline total.
|
||||||
|
function renderWeek(days) {
|
||||||
|
var box = el("waka-week");
|
||||||
|
if (!box) return;
|
||||||
|
if (!days || !days.length) { failSection(box, "No activity data yet."); return; }
|
||||||
|
|
||||||
|
var max = days.reduce(function (acc, d) { return Math.max(acc, d.total); }, 0) || 1;
|
||||||
|
var total = days.reduce(function (acc, d) { return acc + d.total; }, 0);
|
||||||
|
|
||||||
|
var headline = el("waka-total-val");
|
||||||
|
if (headline) headline.textContent = fmt(total);
|
||||||
|
var sub = el("waka-total-sub");
|
||||||
|
if (sub) sub.textContent = "across the last " + days.length + " days";
|
||||||
|
|
||||||
|
box.innerHTML = "";
|
||||||
|
days.forEach(function (d) {
|
||||||
|
var h = Math.max(3, Math.round((d.total / max) * 100));
|
||||||
|
var col = document.createElement("div");
|
||||||
|
col.className = "waka-day";
|
||||||
|
|
||||||
|
var barWrap = document.createElement("div");
|
||||||
|
barWrap.className = "waka-day-track";
|
||||||
|
var bar = document.createElement("div");
|
||||||
|
bar.className = "waka-day-fill";
|
||||||
|
bar.style.height = h + "%";
|
||||||
|
bar.title = d.label + ": " + fmt(d.total);
|
||||||
|
barWrap.appendChild(bar);
|
||||||
|
|
||||||
|
var lbl = document.createElement("span");
|
||||||
|
lbl.className = "waka-day-label";
|
||||||
|
lbl.textContent = d.short;
|
||||||
|
|
||||||
|
col.appendChild(barWrap);
|
||||||
|
col.appendChild(lbl);
|
||||||
|
box.appendChild(col);
|
||||||
|
});
|
||||||
|
showSection(box);
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSection(box) {
|
||||||
|
var sec = box.closest(".waka-section");
|
||||||
|
if (sec) sec.hidden = false;
|
||||||
|
}
|
||||||
|
function failSection(box, msg) {
|
||||||
|
box.innerHTML = '<p class="waka-empty">' + msg + "</p>";
|
||||||
|
showSection(box);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- shape parsers (defensive: WakaTime embed shapes vary) ----------
|
||||||
|
// Categorical embeds (languages/categories/editors/OS) -> data:[{name,total_seconds,percent,color,text}]
|
||||||
|
function asCategorical(json) {
|
||||||
|
var data = json && json.data;
|
||||||
|
if (!Array.isArray(data)) return [];
|
||||||
|
// Some embeds nest under data.<key>; flatten the first array we find.
|
||||||
|
if (data.length && data[0] && data[0].name === undefined && Array.isArray(data[0])) {
|
||||||
|
data = data[0];
|
||||||
|
}
|
||||||
|
return data.map(function (d) {
|
||||||
|
return {
|
||||||
|
name: d.name,
|
||||||
|
total_seconds: typeof d.total_seconds === "number" ? d.total_seconds : (d.seconds || 0),
|
||||||
|
percent: d.percent,
|
||||||
|
color: d.color,
|
||||||
|
text: d.text
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coding-activity embed -> array of {label, short, total}
|
||||||
|
function asDays(json) {
|
||||||
|
var data = json && json.data;
|
||||||
|
if (!Array.isArray(data)) return [];
|
||||||
|
var out = [];
|
||||||
|
data.forEach(function (d) {
|
||||||
|
var seconds = 0, dateStr = "";
|
||||||
|
if (d.grand_total && typeof d.grand_total.total_seconds === "number") {
|
||||||
|
seconds = d.grand_total.total_seconds; // daily-summaries shape
|
||||||
|
} else if (typeof d.total_seconds === "number") {
|
||||||
|
seconds = d.total_seconds; // flat shape
|
||||||
|
}
|
||||||
|
if (d.range && (d.range.date || d.range.text)) {
|
||||||
|
dateStr = d.range.date || d.range.text;
|
||||||
|
} else if (d.date) {
|
||||||
|
dateStr = d.date;
|
||||||
|
}
|
||||||
|
// Anchor bare YYYY-MM-DD to local noon so the weekday label doesn't
|
||||||
|
// slip a day in timezones west of UTC (where it'd parse as UTC midnight).
|
||||||
|
var dateForParse = /^\d{4}-\d{2}-\d{2}$/.test(dateStr) ? dateStr + "T12:00:00" : dateStr;
|
||||||
|
var dt = dateStr ? new Date(dateForParse) : null;
|
||||||
|
var label = dt && !isNaN(dt) ? dt.toDateString() : (dateStr || "");
|
||||||
|
var short = dt && !isNaN(dt)
|
||||||
|
? dt.toLocaleDateString(undefined, { weekday: "short" })
|
||||||
|
: (label.slice(0, 3) || "?");
|
||||||
|
out.push({ label: label, short: short, total: seconds });
|
||||||
|
});
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- orchestration --------------------------------------------------
|
||||||
|
function load(url, onData, fallbackBoxId) {
|
||||||
|
if (!url) return Promise.resolve(null);
|
||||||
|
return jsonp(url).then(function (json) {
|
||||||
|
if (json && json.human_readable_range) setMeta("Range: " + json.human_readable_range);
|
||||||
|
else if (json && json.range && json.range.text) setMeta("Range: " + json.range.text);
|
||||||
|
onData(json);
|
||||||
|
return json;
|
||||||
|
}).catch(function (err) {
|
||||||
|
console.warn("[wakatime] failed to load", url, err);
|
||||||
|
if (fallbackBoxId) { var b = el(fallbackBoxId); if (b) failSection(b, "Couldn't load this chart."); }
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
var configured = Object.keys(WAKATIME).some(function (k) { return !!WAKATIME[k]; });
|
||||||
|
var setup = el("waka-setup");
|
||||||
|
var content = el("waka-content");
|
||||||
|
|
||||||
|
if (!configured) {
|
||||||
|
if (setup) setup.hidden = false;
|
||||||
|
if (content) content.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (setup) setup.hidden = true;
|
||||||
|
if (content) content.hidden = false;
|
||||||
|
|
||||||
|
var jobs = [];
|
||||||
|
|
||||||
|
if (WAKATIME.codingActivity) {
|
||||||
|
jobs.push(load(WAKATIME.codingActivity, function (json) {
|
||||||
|
renderWeek(asDays(json));
|
||||||
|
}, "waka-week"));
|
||||||
|
}
|
||||||
|
if (WAKATIME.languages) {
|
||||||
|
jobs.push(load(WAKATIME.languages, function (json) {
|
||||||
|
renderBars("waka-languages", asCategorical(json));
|
||||||
|
}, "waka-languages"));
|
||||||
|
}
|
||||||
|
if (WAKATIME.categories) {
|
||||||
|
jobs.push(load(WAKATIME.categories, function (json) {
|
||||||
|
renderBars("waka-categories", asCategorical(json));
|
||||||
|
}, "waka-categories"));
|
||||||
|
}
|
||||||
|
if (WAKATIME.editors) {
|
||||||
|
jobs.push(load(WAKATIME.editors, function (json) {
|
||||||
|
renderBars("waka-editors", asCategorical(json));
|
||||||
|
}, "waka-editors"));
|
||||||
|
}
|
||||||
|
if (WAKATIME.operatingSystems) {
|
||||||
|
jobs.push(load(WAKATIME.operatingSystems, function (json) {
|
||||||
|
renderBars("waka-os", asCategorical(json));
|
||||||
|
}, "waka-os"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no coding-activity embed was set, hide the headline total card.
|
||||||
|
if (!WAKATIME.codingActivity) {
|
||||||
|
var totEl = el("waka-total");
|
||||||
|
if (totEl) totEl.hidden = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(jobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.readyState === "loading") {
|
||||||
|
document.addEventListener("DOMContentLoaded", init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
@ -0,0 +1,433 @@
|
||||||
|
/* =====================================================================
|
||||||
|
* terminal.js — the homepage's interactive terminal.
|
||||||
|
*
|
||||||
|
* Flow: a short boot log streams in, the side chrome fades in alongside
|
||||||
|
* it, then the banner + a pinned prompt appear. You type a command (or a
|
||||||
|
* social's name) and the output is appended to the scrollback BELOW the
|
||||||
|
* input — the input itself never moves.
|
||||||
|
* ===================================================================== */
|
||||||
|
(function terminal() {
|
||||||
|
const root = document.getElementById("terminal");
|
||||||
|
if (!root) return;
|
||||||
|
|
||||||
|
// ---- socials (keyword -> destination) ----------------------------------
|
||||||
|
const SOCIALS = {
|
||||||
|
gitgay: { label: "Git.Gay", sub: "@doughmination", url: "https://git.gay/doughmination", aliases: ["git.gay", "gitea", "github", "git"] },
|
||||||
|
twitter: { label: "Twitter", sub: "@DoughminCEO", url: "https://x.com/DoughminCEO", aliases: ["x"] },
|
||||||
|
bluesky: { label: "Bluesky", sub: "@doughmination.win", url: "https://bsky.app/profile/doughmination.win", aliases: ["bsky"] },
|
||||||
|
linkedin: { label: "LinkedIn", sub: "Clove Twilight", url: "https://www.linkedin.com/in/estrogen/" },
|
||||||
|
spotify: { label: "Spotify", sub: "doughmination", url: "https://open.spotify.com/user/x060f5w4ftwv8zc8fi9662t70" },
|
||||||
|
discord: { label: "Discord", sub: "Doughmination", url: "https://discord.com/users/1464890289922641993" },
|
||||||
|
twitch: { label: "Twitch", sub: "@doughminationgaming", url: "https://www.twitch.tv/doughminationgaming" },
|
||||||
|
reddit: { label: "Reddit", sub: "u/XerinDotZero", url: "https://www.reddit.com/user/XerinDotZero/" },
|
||||||
|
youtube: { label: "YouTube", sub: "@CloveTwiGaming", url: "https://www.youtube.com/@CloveTwiGaming", aliases: ["yt"] },
|
||||||
|
mastodon: { label: "Mastodon", sub: "@doughmination@mastodon.social", url: "https://mastodon.social/@doughmination" },
|
||||||
|
email: { label: "Email", sub: "admin@doughmination.win", url: "mailto:admin@doughmination.win", aliases: ["mail"] },
|
||||||
|
portfolio: { label: "Portfolio", sub: "doughmination.co.uk", url: "https://doughmination.co.uk/", aliases: ["website", "site"] }
|
||||||
|
};
|
||||||
|
const ALIASES = {};
|
||||||
|
Object.keys(SOCIALS).forEach((k) => {
|
||||||
|
(SOCIALS[k].aliases || []).forEach((a) => { ALIASES[a] = k; });
|
||||||
|
});
|
||||||
|
// keyword -> svg filename in /assets/socials
|
||||||
|
const SOCIAL_ICON = {
|
||||||
|
github: "github", gitgay: "git-gay", twitter: "twitter", bluesky: "bluesky",
|
||||||
|
linkedin: "linkedin", spotify: "spotify", discord: "discord", twitch: "twitch",
|
||||||
|
reddit: "reddit", youtube: "youtube", mastodon: "mastodon", email: "email",
|
||||||
|
company: "site", portfolio: "site"
|
||||||
|
};
|
||||||
|
function iconImg(key) {
|
||||||
|
return '<img class="t-social-ic" src="/assets/socials/' + (SOCIAL_ICON[key] || "site") + '.svg" alt="">';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- friends (keyword -> who they are) ---------------------------------
|
||||||
|
const FRIENDS = {
|
||||||
|
ari: { name: "Ari", desc: "🩵 My wifey 🩵 the best 🩵 Her corner of the web:", url: "https://ariare.es", urlLabel: "ariare.es 🩵" },
|
||||||
|
saphie: { name: "Saphie", desc: "🩷 Cammy's partner, loves linguistics🩷", url: "" },
|
||||||
|
camilla: { name: "Camillia (Cammy)", desc: "🖤 Close friend who shares a passion for coding 🖤", url: "https://cammy-the-cat.com", urlLabel: "cammy-the-cat.com 🖤" },
|
||||||
|
ria: { name: "Ria", desc: "🤍 Close friend/platonic daughter who means a lot to me 🤍", url: "" },
|
||||||
|
lilly: { name: "Lilly (Lils)", desc: "💖 Pookie, really cool person 💖", url: "" },
|
||||||
|
primrose: { name: "Nimnose", desc: "💜 Lil's partner 💜", url: "" },
|
||||||
|
fin: { name: "Fin", desc: "💛 Ari's friend who is really nice and who I can be unfilered with 💛", url: "" }
|
||||||
|
};
|
||||||
|
|
||||||
|
let cache = null;
|
||||||
|
async function checkDomain(subdomain) {
|
||||||
|
if (!cache) {
|
||||||
|
const response = await fetch("https://raw.is-a.dev/v2.json");
|
||||||
|
cache = await response.json();
|
||||||
|
}
|
||||||
|
return cache.some((d) => d.subdomain === subdomain);
|
||||||
|
}
|
||||||
|
|
||||||
|
// arch.ascii (hyfetch format) is fetched once at startup for `hyfetch`.
|
||||||
|
let archLines = null;
|
||||||
|
function loadArt() {
|
||||||
|
fetch("/arch.ascii").then(function (r) { return r.ok ? r.text() : ""; }).then(function (t) {
|
||||||
|
if (!t) return;
|
||||||
|
var lines = t.replace(/\r/g, "").split("\n");
|
||||||
|
if (lines[0] && lines[0].trim().charAt(0) === "{") lines.shift();
|
||||||
|
lines = lines.map(function (l) { return l.replace(/\$\{c\d\}/g, ""); });
|
||||||
|
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
||||||
|
archLines = lines;
|
||||||
|
}).catch(function () { });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- ascii banner -------------------------------------------------------
|
||||||
|
const BANNER = [
|
||||||
|
" ██████╗██╗ ██████╗ ██╗ ██╗███████╗",
|
||||||
|
"██╔════╝██║ ██╔═══██╗██║ ██║██╔════╝",
|
||||||
|
"██║ ██║ ██║ ██║██║ ██║█████╗ ",
|
||||||
|
"██║ ██║ ██║ ██║╚██╗ ██╔╝██╔══╝ ",
|
||||||
|
"╚██████╗███████╗╚██████╔╝ ╚████╔╝ ███████╗",
|
||||||
|
" ╚═════╝╚══════╝ ╚═════╝ ╚═══╝ ╚══════╝"
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
// ---- boot log -----------------------------------------------------------
|
||||||
|
const BOOT = [
|
||||||
|
["info", "starting clovesh..."],
|
||||||
|
["info", "mounting /dev/estrogen..."],
|
||||||
|
["ok", "estrogen levels nominal"],
|
||||||
|
["info", "loading kernel modules (catppuccin)..."],
|
||||||
|
["ok", "modules loaded"],
|
||||||
|
["info", "summoning cats..."],
|
||||||
|
["ok", "oneko ready"],
|
||||||
|
["info", "connecting to discord via lanyard..."],
|
||||||
|
["ok", "presence online"],
|
||||||
|
["info", "mounting button wall..."],
|
||||||
|
["ok", "88x31 buttons hung"],
|
||||||
|
["info", "starting terminal..."],
|
||||||
|
["ok", "ready — type 'help'"]
|
||||||
|
];
|
||||||
|
|
||||||
|
// ---- build DOM ----------------------------------------------------------
|
||||||
|
root.innerHTML =
|
||||||
|
'<pre class="t-boot" id="t-boot" aria-hidden="true"></pre>' +
|
||||||
|
'<div class="t-main" id="t-main" hidden>' +
|
||||||
|
'<pre class="t-banner">' + esc(BANNER) + "</pre>" +
|
||||||
|
'<div class="t-greet">Type <b>help</b> for commands, or <b>socials</b> to browse.</div>' +
|
||||||
|
'<div class="t-inputline">' +
|
||||||
|
'<span class="t-prompt">arch@arch<span class="t-path">:[~]$</span></span>' +
|
||||||
|
'<input class="t-input" id="t-input" type="text" autocomplete="off" autocapitalize="off" spellcheck="false">' +
|
||||||
|
"</div>" +
|
||||||
|
'<div class="t-output" id="t-output"></div>' +
|
||||||
|
"</div>";
|
||||||
|
|
||||||
|
const bootEl = root.querySelector("#t-boot");
|
||||||
|
const mainEl = root.querySelector("#t-main");
|
||||||
|
const input = root.querySelector("#t-input");
|
||||||
|
const output = root.querySelector("#t-output");
|
||||||
|
|
||||||
|
function esc(s) {
|
||||||
|
return String(s == null ? "" : s)
|
||||||
|
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
||||||
|
}
|
||||||
|
function stamp() {
|
||||||
|
return new Date().toLocaleTimeString("en-GB", { hour12: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- command handlers ---------------------------------------------------
|
||||||
|
const COMMANDS = {
|
||||||
|
help() {
|
||||||
|
const rows = [
|
||||||
|
["help", "show this list"],
|
||||||
|
["socials", "list all socials"],
|
||||||
|
["<social> [-open]", "show a social & ask to open it (append -open to do directly)"],
|
||||||
|
["system [person]", "open my system website (append a person's name to open their page)"],
|
||||||
|
["about", "a little about me"],
|
||||||
|
["hyfetch", "system info, with flair"]
|
||||||
|
];
|
||||||
|
let out = "Available commands:\n";
|
||||||
|
out += rows.map((r) => " " + r[0].padEnd(12) + r[1]).join("\n");
|
||||||
|
out += "\n\nTip: type a social's name (try 'socials') to open it.";
|
||||||
|
return { text: out };
|
||||||
|
},
|
||||||
|
ls() {
|
||||||
|
const rows = [
|
||||||
|
["help", "show this list"],
|
||||||
|
["socials", "list all socials"],
|
||||||
|
["<social>", "show a social & ask to open it (append -open to do directly)"],
|
||||||
|
["system [person]", "open my system website (append a person's name to open their page)"],
|
||||||
|
["about", "a little about me"],
|
||||||
|
["hyfetch", "system info, with flair"]
|
||||||
|
];
|
||||||
|
let out = "Available commands:\n";
|
||||||
|
out += rows.map((r) => " " + r[0].padEnd(12) + r[1]).join("\n");
|
||||||
|
out += "\n\nTip: type a social's name (try 'socials') to open it.";
|
||||||
|
return { text: out };
|
||||||
|
},
|
||||||
|
async system(args) {
|
||||||
|
const who = (args[0] || "").toLowerCase();
|
||||||
|
if (!who) {
|
||||||
|
window.open("https://system.doughmination.co.uk/", "_blank");
|
||||||
|
return { text: "Opening system site..." };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await fetch(
|
||||||
|
`https://system.doughmination.co.uk/api/member/${encodeURIComponent(who)}`
|
||||||
|
);
|
||||||
|
if (response.status === 200) {
|
||||||
|
window.open(`https://system.doughmination.co.uk/member/${encodeURIComponent(who)}`, "_blank");
|
||||||
|
return { text: `Opening ${who}'s profile...` };
|
||||||
|
}
|
||||||
|
if (response.status === 404) {
|
||||||
|
return { text: "That person doesn't exist." };
|
||||||
|
}
|
||||||
|
if (response.status === 502) {
|
||||||
|
return { text: "The server is currently having issues." };
|
||||||
|
}
|
||||||
|
return { text: `Unexpected response (${response.status}).` };
|
||||||
|
} catch (error) {
|
||||||
|
return { text: "Failed to contact the server." };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
socials() {
|
||||||
|
const items = Object.keys(SOCIALS)
|
||||||
|
.map((k) => '<span class="t-ls-item">' + esc(k) + "</span>").join("");
|
||||||
|
return {
|
||||||
|
html: '<div class="t-ls">' + items + "</div>" +
|
||||||
|
'<div class="t-dim t-ls-foot">type one to view it, or <b><name> -open</b> to open</div>'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
about() {
|
||||||
|
return {
|
||||||
|
text:
|
||||||
|
"Clove Twilight — fae/faer\n" +
|
||||||
|
"Transfem developer from Southampton, UK. I make Discord bots,\n" +
|
||||||
|
"personal-site nonsense, and run a small corner of the internet\n" +
|
||||||
|
"under the trade mark 'doughmination'. Big on Linux, Catppuccin, and cats.\n\n" +
|
||||||
|
"This site is the beta playground for clove.is-a.dev — expect things\n" +
|
||||||
|
"to break in funny ways. Type 'socials' to find me elsewhere."
|
||||||
|
};
|
||||||
|
},
|
||||||
|
hyfetch() {
|
||||||
|
const info = [
|
||||||
|
'<b class="t-accent">arch</b>@<b class="t-accent">arch</b>',
|
||||||
|
"-----------------------",
|
||||||
|
"OS........ Arch Linux x86_64",
|
||||||
|
"GPU....... AMD ATI SPEEDSTER MERC 310 RX 7900 XTX",
|
||||||
|
"CPU....... AMD Ryzen 9 9950X3D (8) @ 5.7GHz",
|
||||||
|
"Host...... B850M AORUS ELITE WIFI6E ICE -CF-WCP-ADO",
|
||||||
|
"Kernel.... 7.0.11-arch1-1",
|
||||||
|
"Shell..... bash 5.3.12",
|
||||||
|
"Theme..... Breeze-Dark [GTK2/3]",
|
||||||
|
"Pronouns.. fae/faer",
|
||||||
|
"Uptime.... " + uptime(),
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
if (!archLines || !archLines.length) {
|
||||||
|
return { html: '<pre class="hf-info">' + info + "</pre>" };
|
||||||
|
}
|
||||||
|
const colors = ["#5bcefa", "#f5a9b8", "#ffffff", "#f5a9b8", "#5bcefa"];
|
||||||
|
const n = archLines.length;
|
||||||
|
const logo = archLines.map(function (ln, i) {
|
||||||
|
const c = colors[Math.min(colors.length - 1, Math.floor((i / n) * colors.length))];
|
||||||
|
return '<span style="color:' + c + '">' + esc(ln) + "</span>";
|
||||||
|
}).join("\n");
|
||||||
|
return {
|
||||||
|
html: '<div class="hf"><pre class="hf-logo">' + logo + "</pre>" +
|
||||||
|
'<pre class="hf-info">' + info + "</pre></div>"
|
||||||
|
};
|
||||||
|
},
|
||||||
|
async isadotdev(parts) {
|
||||||
|
const arg = (parts[0] || "").toLowerCase().replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
||||||
|
if (!arg || !arg.endsWith(".is-a.dev")) {
|
||||||
|
return { text: "usage: isadotdev <subdomain>.is-a.dev", error: true };
|
||||||
|
}
|
||||||
|
const sub = arg.replace(/\.is-a\.dev$/, "");
|
||||||
|
if (["clove", "doughmination"].indexOf(sub) >= 0) {
|
||||||
|
return { text: "nice try 👀", error: true };
|
||||||
|
}
|
||||||
|
showResult({ text: "checking " + arg + "…" });
|
||||||
|
let found;
|
||||||
|
try { found = await checkDomain(sub); }
|
||||||
|
catch (e) { return { text: "couldn't reach the is-a.dev registry — try again later.", error: true }; }
|
||||||
|
if (!found) return { text: arg + " isn't registered on is-a.dev.", error: true };
|
||||||
|
window.open("https://" + arg, "_blank", "noopener");
|
||||||
|
return { html: 'opening <b class="t-accent">' + esc(arg) + "</b> …" };
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ---- runtime ------------------------------------------------------------
|
||||||
|
const startedAt = Date.now();
|
||||||
|
function uptime() {
|
||||||
|
let s = Math.floor((Date.now() - startedAt) / 1000);
|
||||||
|
const h = Math.floor(s / 3600); s -= h * 3600;
|
||||||
|
const m = Math.floor(s / 60); s -= m * 60;
|
||||||
|
const parts = [];
|
||||||
|
if (h) parts.push(h + "h");
|
||||||
|
if (m) parts.push(m + "m");
|
||||||
|
parts.push(s + "s");
|
||||||
|
return parts.join(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = [];
|
||||||
|
let histIdx = -1;
|
||||||
|
let pendingSocial = null;
|
||||||
|
|
||||||
|
function showResult(result) {
|
||||||
|
output.innerHTML = "";
|
||||||
|
if (!result) return;
|
||||||
|
const box = document.createElement("div");
|
||||||
|
box.className = "t-result";
|
||||||
|
if (result.error) box.classList.add("is-error");
|
||||||
|
if (result.html != null) box.innerHTML = result.html;
|
||||||
|
else if (result.text != null) box.textContent = result.text;
|
||||||
|
output.appendChild(box);
|
||||||
|
output.scrollTop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function runCommand(fn, args) {
|
||||||
|
let r;
|
||||||
|
try { r = fn(args); }
|
||||||
|
catch (e) { showResult({ text: "error running that command.", error: true }); return; }
|
||||||
|
if (r && typeof r.then === "function") {
|
||||||
|
r.then(showResult).catch(function () { showResult({ text: "something went wrong.", error: true }); });
|
||||||
|
} else {
|
||||||
|
showResult(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(raw) {
|
||||||
|
const cmd = raw.trim();
|
||||||
|
output.innerHTML = "";
|
||||||
|
if (!cmd) { pendingSocial = null; return; }
|
||||||
|
history.push(cmd); histIdx = history.length;
|
||||||
|
|
||||||
|
const parts = cmd.split(/\s+/);
|
||||||
|
const name = parts[0].toLowerCase();
|
||||||
|
const flags = parts.slice(1).map((p) => p.toLowerCase());
|
||||||
|
const wantsOpen = flags.indexOf("-open") >= 0 || flags.indexOf("--open") >= 0 || flags.indexOf("-o") >= 0;
|
||||||
|
|
||||||
|
if (pendingSocial) {
|
||||||
|
if (["y", "yes", "open", "o"].indexOf(name) >= 0) { openSocial(pendingSocial); return; }
|
||||||
|
if (["n", "no"].indexOf(name) >= 0) { pendingSocial = null; showResult({ text: "okay, leaving it closed." }); return; }
|
||||||
|
pendingSocial = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const socialKey = resolveSocial(name === "open" ? flags[0] : name);
|
||||||
|
if (socialKey) {
|
||||||
|
if (wantsOpen || name === "open") openSocial(socialKey);
|
||||||
|
else promptSocial(socialKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.endsWith(".is-a.dev")) {
|
||||||
|
runCommand(COMMANDS.isadotdev, [name]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COMMANDS[name]) { runCommand(COMMANDS[name], parts.slice(1)); return; }
|
||||||
|
|
||||||
|
showResult({ text: "clovesh: command not found: " + name + "\nType 'help' for a list, or 'socials' to browse.", error: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
function promptSocial(key) {
|
||||||
|
const s = SOCIALS[key];
|
||||||
|
pendingSocial = key;
|
||||||
|
showResult({
|
||||||
|
html:
|
||||||
|
'<div class="t-social-card">' +
|
||||||
|
'<div class="t-sc-head">' + iconImg(key) +
|
||||||
|
'<span><b class="t-accent">' + esc(s.label) + "</b> " +
|
||||||
|
'<span class="t-dim">' + esc(s.sub) + "</span></span></div>" +
|
||||||
|
'<a class="t-sc-url" href="' + esc(s.url) + '"' +
|
||||||
|
(s.url.startsWith("mailto:") ? "" : ' target="_blank" rel="noopener"') + ">" + esc(s.url) + "</a>" +
|
||||||
|
'<div class="t-sc-ask t-dim">open it? type <b>y</b> · or run <b>' + esc(key) + " -open</b> · <b>n</b> to cancel</div>" +
|
||||||
|
"</div>"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveSocial(key) {
|
||||||
|
if (!key) return null;
|
||||||
|
if (SOCIALS[key]) return key;
|
||||||
|
if (ALIASES[key]) return ALIASES[key];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSocial(key) {
|
||||||
|
pendingSocial = null;
|
||||||
|
const s = SOCIALS[key];
|
||||||
|
showResult({
|
||||||
|
html: '<a class="t-social-open" href="' + esc(s.url) + '"' +
|
||||||
|
(s.url.startsWith("mailto:") ? "" : ' target="_blank" rel="noopener"') + ">" +
|
||||||
|
iconImg(key) + "opening <b class=\"t-accent\">" + esc(s.label) + "</b> " +
|
||||||
|
'<span class="t-dim">' + esc(s.url) + "</span> …</a>"
|
||||||
|
});
|
||||||
|
if (s.url.startsWith("mailto:")) { window.location.href = s.url; }
|
||||||
|
else { window.open(s.url, "_blank", "noopener"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- tab-complete + history --------------------------------------------
|
||||||
|
const COMPLETIONS = Object.keys(COMMANDS).concat(["open", "socials", "isadotdev"], Object.keys(SOCIALS), Object.keys(ALIASES));
|
||||||
|
function complete(prefix) {
|
||||||
|
if (!prefix) return null;
|
||||||
|
const hits = COMPLETIONS.filter((c) => c.indexOf(prefix) === 0);
|
||||||
|
if (hits.length === 1) return hits[0];
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
const v = input.value;
|
||||||
|
input.value = "";
|
||||||
|
run(v);
|
||||||
|
} else if (e.key === "ArrowUp") {
|
||||||
|
e.preventDefault();
|
||||||
|
if (histIdx > 0) { histIdx--; input.value = history[histIdx] || ""; moveCaretEnd(); }
|
||||||
|
} else if (e.key === "ArrowDown") {
|
||||||
|
e.preventDefault();
|
||||||
|
if (histIdx < history.length - 1) { histIdx++; input.value = history[histIdx] || ""; }
|
||||||
|
else { histIdx = history.length; input.value = ""; }
|
||||||
|
moveCaretEnd();
|
||||||
|
} else if (e.key === "Tab") {
|
||||||
|
e.preventDefault();
|
||||||
|
const c = complete(input.value.trim().toLowerCase());
|
||||||
|
if (c) input.value = c;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function moveCaretEnd() {
|
||||||
|
requestAnimationFrame(() => { input.selectionStart = input.selectionEnd = input.value.length; });
|
||||||
|
}
|
||||||
|
|
||||||
|
root.addEventListener("click", () => {
|
||||||
|
if ((window.getSelection() + "") === "") input.focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ---- boot then reveal ---------------------------------------------------
|
||||||
|
document.body.classList.add("term-booting");
|
||||||
|
|
||||||
|
let booted = false;
|
||||||
|
function finishBoot() {
|
||||||
|
if (booted) return;
|
||||||
|
booted = true;
|
||||||
|
bootEl.hidden = true;
|
||||||
|
mainEl.hidden = false;
|
||||||
|
document.body.classList.remove("term-booting");
|
||||||
|
document.body.classList.add("term-ready");
|
||||||
|
input.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
function streamBoot(i) {
|
||||||
|
if (booted) return;
|
||||||
|
if (i >= BOOT.length) { setTimeout(finishBoot, 350); return; }
|
||||||
|
const [kind, msg] = BOOT[i];
|
||||||
|
const tag = kind === "ok"
|
||||||
|
? '<span class="b-ok"> OK </span>'
|
||||||
|
: '<span class="b-info"> INFO </span>';
|
||||||
|
bootEl.insertAdjacentHTML("beforeend",
|
||||||
|
'<span class="b-line">[<span class="b-time">' + stamp() + "</span>] [" + tag + "] " + esc(msg) + "</span>\n");
|
||||||
|
bootEl.scrollTop = bootEl.scrollHeight;
|
||||||
|
setTimeout(() => streamBoot(i + 1), 120 + Math.random() * 120);
|
||||||
|
}
|
||||||
|
|
||||||
|
function skipHandler(e) {
|
||||||
|
if (e.type === "keydown" || e.type === "click") finishBoot();
|
||||||
|
}
|
||||||
|
document.addEventListener("keydown", skipHandler, { once: false });
|
||||||
|
|
||||||
|
loadArt();
|
||||||
|
requestAnimationFrame(() => document.body.classList.add("term-chrome-in"));
|
||||||
|
streamBoot(0);
|
||||||
|
})();
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div align="center">
|
<div align="center">
|
||||||
<img src="https://doughmination.is-a.dev/assets/favicon/avatar.png" alt="Clove Twilight avatar" height="100">
|
<img src="https://clove.is-a.dev/assets/favicon/avatar.png" alt="Clove Twilight avatar" height="100">
|
||||||
|
|
||||||
# clove.is-a.dev
|
# clove.is-a.dev
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||