video
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_
|
||||
=$ sa a y sa_gwaanggug, :
|
||||
? ?F fY ` @F?YFZ@~?f r~ :
|
||||
`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
yyyy_ yagggm aggr _ygggs
|
||||
_@@@@@_ @@@~~~ _ __ __ _ __ __ _ 4@@F $@@~~~ __ _
|
||||
$@@~@@$ $@@@@@@L$@@y@@@L4@@@@@@g_ $@@W@@@@yg@@@@y yg@@@@@gy "@@$ $@@, y@@E _a@@@@@@y 4@@$g@@F4@@F y@@@F 4@@@@@@$_a@@@@@@g, @@$g@@$
|
||||
g@@F ~@@@ ``@@@`` $@@@~`` `yyyyy@@@ $@@F``@@@F `@@@ u@@@yyy$@@L @@@,y@B@@ $@@ @@@~ 4@@@ 4@@@~``~4@@@@@P~ `@@@```$@@~ 7@@$ @@@~``'
|
||||
g@@@$$$@@@y @@@ $@@L y@@@FFF@@@ $@@ @@@ @@@ 4@@@PPPPPPT "@@$$@~$@W@@F @@@ y@@@ 4@@$ 4@@@@@g_ @@@ @@@ @@@ @@$
|
||||
y@@@~~~~~$@@L @@@ $@@L "@@$yy$@@@ $@@ @@@ @@@ R@@gyyyg@ 4@@@E "@@@@ 4@@$yg@@@~ 4@@$ 4@@F~R@@g_ @@@ 7@@@ga@@@~ @@$
|
||||
^~~^ "~~~ ~~~ ~~~~ `~FF~~~~~ 7~~ ~~~ ~~~ `~FFFF~~ ~~~ ~~~~ `~FFFF~ "~~~ ~~~~ ~~~T ~~~ ~FFFF~ ~~~
|
||||
|
||||
|
||||
`:: ::: ::: ::: ::: ::::::::::: ::: ::: :::: ::: ::::::::. ::
|
||||
`:: ..... ... ... ... ::: ..... ::: ... ... ..... .... ... ````:::```` ::: ::: `::: ...... ... .. ..... :::```::: ... ... ....... ..::...
|
||||
`:::```::: ::: ::: ::: ::: :::```:::: ::: ::::```::. :::```::: ::: ::: ::: `::: ::````` ::: :::```::: :::...::` ::: ::: :::````` ``::```
|
||||
`::' ::: ::: ::: ::: ::: ::: `::: ::: ::: ::: ::: ::: ::: ::: ::: `::: `:::::.. ::: ::' ::: :::``:::. ::: ::: `::::::. ::
|
||||
`::: .::: ::: .::: ::: ::: :::. :::: ::: ::: ::: `:::. .::: ::: `::: .:::' `::: ```::: ::: :: ::: ::: `:::. :::. .::: . ````:: ::. :::
|
||||
`````:::`` ``::````` ``' ``` ```::````' ``` ``` ``' ``````::: ``` ```:::``` ```' ``:::``` ``` `` ``` ``` ```. ``::````` ``::::`` ````` ```
|
||||
:....:::`
|
||||
````````
|
||||
|
||||
.y + - i + .
|
||||
4W*MF~EZK,,4~3 $~f~%g~M $~_Ta F~K~L4~$~$uEZLf~$ Q~a~3 F~ [ E4E_ $='EZ%4_z F~K~LyT% F~L4Z, TM #~$uM7L B~%_T3 F~$yED F~LyT% yy^%uT7LyT%?F Fg~3 F~L
|
||||
" ?=! 4rf ~ ?=~`g9 F ?=? ~ ~ ~f ^ f h= ?=F `=F ?= ^rF:=^J F~r?=:_$~ ~ ~ ~?=T M=~==~z "=f f f hf $=~"=? ]gF"h= ~ ~?=? ?^ f _g ^=T h ~`=F ~ ~
|
||||
` ` ` ` `` `
|
||||
y== y_ _ __ __ _ _ _ ,___ y_= __ __ __ __ __ __ __ __ _ __ __ __ __a__ ;y_ __ _,___ _ __ _ yg _, __ __ _ _ __ ___ _ ___ ____ _ ____
|
||||
`Th $`$ $ L`L $`$=="yMy~$`4 $`$ F`L$ 9 `L F`La== "=yy=g $ F la== =d $ E `4`4 $4`4-= E $`4 4 F`$== 4`# 3 F` P=\4_F4== E ~yF 4~J $`$ $4 $==4```L
|
||||
`T~ ~~`T` ~T ~ `T" ~ ~ " " ~~" ~ ~`Z4 ~ ~ ~ TT `T~`T~ ~ ~ ~ ~T T~ ^ `T'~ ~ ~ ~ ~T ~T"`~`T~ ~ `T^ ~ `T` ~T~ ~ ~T ~ .F 4~~ ~ ~T`J `T^ ~T ~ ~
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,60 +1,46 @@
|
||||
/* ==========================================================================
|
||||
tui-pages website - animated ASCII art layer (chafa-rendered)
|
||||
Loaded AFTER site.css. Provides:
|
||||
- .tp-ascii-rain full-bleed body background, slow drifting pattern
|
||||
sourced from static/img/og-image-bg.svg
|
||||
All animations respect prefers-reduced-motion.
|
||||
tui-pages website - animated ASCII mountain background (chafa-rendered)
|
||||
|
||||
The mountain background is JS-driven: tools/mountain.py renders 60
|
||||
procedural 3D mountain-flyover PNG frames; chafa converts each to 80x24
|
||||
ASCII; tools/build_mountain_js.py bundles them into static/js/mountain.js.
|
||||
static/js/mountain-bg.js cycles the frames at 12 fps into a single
|
||||
<pre class="tp-mountain-frame"> element mounted at the bottom of <body>.
|
||||
|
||||
This CSS file:
|
||||
- positions the frame as a full-bleed fixed background
|
||||
- tints the dense characters rust-orange with text-shadow + glow
|
||||
- adds a CRT scanline overlay
|
||||
- disables the animation under prefers-reduced-motion
|
||||
========================================================================== */
|
||||
|
||||
/* Body-wide ASCII art background ---------------------------------------- */
|
||||
/* Fixed full-bleed layer behind all content. Renders a chafa output of the
|
||||
og-image-bg.svg (black background + "tui-pages" wordmark + headline +
|
||||
subtitle), drawn as ASCII glyphs in a single full-width tile. Two copies
|
||||
of the tile are stacked vertically and the whole track drifts slowly
|
||||
upward, looping seamlessly when the top copy exits the viewport.
|
||||
|
||||
The hero is a static <img src="static/img/terminal-default.svg"> as
|
||||
designed - this file does NOT touch the hero.
|
||||
|
||||
Regenerate the source tile with `make ascii` (uses chafa on
|
||||
static/img/og-image-bg.svg). The chafa output is in static/ascii/rain.txt
|
||||
and is 240 columns wide. */
|
||||
|
||||
.tp-ascii-rain {
|
||||
/* Full-bleed ASCII mountain background ---------------------------------- */
|
||||
.tp-mountain-frame {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 0; /* sit above the page background, below the content */
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.14;
|
||||
overflow: hidden;
|
||||
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: clamp(6px, 0.9vw, 9px);
|
||||
line-height: 1.05;
|
||||
color: #f4a26b; /* warm rust-orange tint, picks up the brand */
|
||||
white-space: pre;
|
||||
text-shadow: 0 0 6px rgba(183, 65, 14, 0.25); /* faint phosphor glow */
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* The track holds two byte-identical copies of rain.txt stacked vertically.
|
||||
translateY(0 -> -50%) is a perfect seamless loop. */
|
||||
.tp-ascii-rain-track {
|
||||
display: block;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
animation: tp-ascii-drift 90s linear infinite;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.tp-ascii-rain-cell {
|
||||
display: block;
|
||||
white-space: pre; /* preserve the chafa line breaks */
|
||||
font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
font-size: clamp(8px, 1.2vw, 14px);
|
||||
line-height: 1.0;
|
||||
letter-spacing: 0;
|
||||
color: #f4a26b; /* warm rust-orange tint, picks up the brand */
|
||||
text-shadow: 0 0 8px rgba(183, 65, 14, 0.35); /* phosphor glow */
|
||||
opacity: 0.45;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/* CRT scanlines overlay - a thin moving line every 2px. Faint so it doesn't
|
||||
fight the page content. */
|
||||
.tp-ascii-rain::after {
|
||||
.tp-mountain-frame::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
@@ -62,36 +48,28 @@
|
||||
to bottom,
|
||||
transparent 0,
|
||||
transparent 2px,
|
||||
rgba(0, 0, 0, 0.18) 2px,
|
||||
rgba(0, 0, 0, 0.18) 3px
|
||||
rgba(0, 0, 0, 0.20) 2px,
|
||||
rgba(0, 0, 0, 0.20) 3px
|
||||
);
|
||||
pointer-events: none;
|
||||
animation: tp-ascii-scanline 8s linear infinite;
|
||||
animation: tp-mtn-scanline 8s linear infinite;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
@keyframes tp-ascii-drift {
|
||||
0% { transform: translateY(0); }
|
||||
100% { transform: translateY(-50%); }
|
||||
}
|
||||
|
||||
@keyframes tp-ascii-scanline {
|
||||
@keyframes tp-mtn-scanline {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 0 6px; }
|
||||
}
|
||||
|
||||
/* The hero text is high-contrast on its own (zinc-50 / accent), and the
|
||||
body ASCII rain is at 14% opacity. No overlay needed — the rain shows
|
||||
through everywhere. */
|
||||
|
||||
/* Light theme: invert to dark text on light, dimmer, no glow. */
|
||||
:root[data-theme="light"] .tp-ascii-rain {
|
||||
opacity: 0.10;
|
||||
color: #1f1f23;
|
||||
/* Light theme: dim the background, drop the glow, switch to dark text. */
|
||||
:root[data-theme="winter"] .tp-mountain-frame {
|
||||
opacity: 0.20;
|
||||
color: #2a1810;
|
||||
text-shadow: none;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .tp-ascii-rain::after {
|
||||
:root[data-theme="winter"] .tp-mountain-frame::after {
|
||||
background: repeating-linear-gradient(
|
||||
to bottom,
|
||||
transparent 0,
|
||||
@@ -99,13 +77,12 @@
|
||||
rgba(255, 255, 255, 0.12) 2px,
|
||||
rgba(255, 255, 255, 0.12) 3px
|
||||
);
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
/* Reduced motion: pause the drift, keep the static background. */
|
||||
/* Reduced motion: keep the first frame static, no scanline drift. */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.tp-ascii-rain-track,
|
||||
.tp-ascii-rain::after {
|
||||
.tp-mountain-frame::after {
|
||||
animation: none !important;
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ html {
|
||||
--tp-hero-to: #18181b;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
:root[data-theme="winter"] {
|
||||
--tp-accent: #93330c;
|
||||
--tp-accent-fg: #fff5f0;
|
||||
--tp-grid-line: rgba(228, 228, 231, 0.7);
|
||||
@@ -55,7 +55,7 @@ body {
|
||||
|
||||
/* ---------- Hero --------------------------------------------------------- */
|
||||
.tp-hero {
|
||||
background: transparent; /* let the body ASCII rain show through */
|
||||
background: transparent; /* let the mountain ASCII show through */
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
}
|
||||
@@ -119,7 +119,7 @@ body {
|
||||
-webkit-backdrop-filter: saturate(160%) blur(12px);
|
||||
border-bottom: 1px solid rgba(63, 63, 70, 0.4);
|
||||
}
|
||||
:root[data-theme="light"] .tp-nav {
|
||||
:root[data-theme="winter"] .tp-nav {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border-bottom-color: rgba(228, 228, 231, 0.7);
|
||||
}
|
||||
@@ -138,11 +138,11 @@ body {
|
||||
background: rgba(24, 24, 27, 0.75);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
:root[data-theme="light"] .tp-card {
|
||||
:root[data-theme="winter"] .tp-card {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
border-color: rgba(228, 228, 231, 0.8);
|
||||
}
|
||||
:root[data-theme="light"] .tp-card:hover {
|
||||
:root[data-theme="winter"] .tp-card:hover {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-color: rgba(183, 65, 14, 0.4);
|
||||
}
|
||||
@@ -195,7 +195,7 @@ body {
|
||||
color: #f4f4f5;
|
||||
line-height: 1;
|
||||
}
|
||||
:root[data-theme="light"] .tp-kbd {
|
||||
:root[data-theme="winter"] .tp-kbd {
|
||||
background: #fff;
|
||||
color: #18181b;
|
||||
border-color: #d4d4d8;
|
||||
@@ -254,7 +254,7 @@ body {
|
||||
|
||||
/* ---------- No-FOUC theme flash ----------------------------------------- */
|
||||
html:not([data-theme]) body { background: #09090b; }
|
||||
:root[data-theme="light"] body { background: #fafafa; }
|
||||
:root[data-theme="winter"] body { background: #fafafa; }
|
||||
|
||||
/* ---------- Alpine x-cloak (prevents flash of un-initialised content) --- */
|
||||
[x-cloak] { display: none !important; }
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<svg width="1200" height="520" viewBox="0 0 1200 520" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="tui-pages terminal art (chafa source)">
|
||||
<defs>
|
||||
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
|
||||
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#27272a" stroke-width="1" stroke-opacity="0.3"/>
|
||||
</pattern>
|
||||
</defs>
|
||||
|
||||
<rect width="1200" height="520" fill="#000000"/>
|
||||
<rect width="1200" height="520" fill="url(#grid)"/>
|
||||
|
||||
<g transform="translate(80, 90)">
|
||||
<text font-family="ui-monospace, 'JetBrains Mono', monospace" font-weight="700" font-size="22" fill="#f4f4f5" x="0" y="0">tui-pages</text>
|
||||
<rect x="140" y="-15" width="4" height="24" fill="#b7410e"/>
|
||||
</g>
|
||||
|
||||
<text font-family="Inter, system-ui, sans-serif" font-weight="700" font-size="84" fill="#f4f4f5" x="80" y="280" letter-spacing="-2">
|
||||
A framework for
|
||||
</text>
|
||||
<text font-family="Inter, system-ui, sans-serif" font-weight="700" font-size="84" fill="#b7410e" x="80" y="370" letter-spacing="-2">
|
||||
building TUIs in Rust.
|
||||
</text>
|
||||
|
||||
<text font-family="Inter, system-ui, sans-serif" font-weight="400" font-size="28" fill="#a1a1aa" x="80" y="430">
|
||||
Pre-programmed focus, keymaps, and page navigation.
|
||||
</text>
|
||||
<text font-family="Inter, system-ui, sans-serif" font-weight="400" font-size="28" fill="#a1a1aa" x="80" y="470">
|
||||
Stop rewriting the same architecture for every project.
|
||||
</text>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
75
static/js/mountain-bg.js
Normal file
75
static/js/mountain-bg.js
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Mountain background animation.
|
||||
*
|
||||
* Cycles the 60 ASCII frames from window.TP_MOUNTAIN_FRAMES at 12 fps.
|
||||
* Mounts a single <pre> in the body that gets its textContent swapped
|
||||
* on each frame. Uses requestAnimationFrame to stay smooth and yields
|
||||
* cleanly to the browser's frame budget. Pauses on prefers-reduced-motion
|
||||
* and on document.hidden.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function init() {
|
||||
if (typeof window.TP_MOUNTAIN_FRAMES === "undefined") return;
|
||||
if (document.querySelector("[data-tp-mountain]")) return;
|
||||
|
||||
var pre = document.createElement("pre");
|
||||
pre.className = "tp-mountain-frame";
|
||||
pre.setAttribute("data-tp-mountain", "");
|
||||
pre.setAttribute("aria-hidden", "true");
|
||||
pre.textContent = window.TP_MOUNTAIN_FRAMES[0];
|
||||
document.body.appendChild(pre);
|
||||
|
||||
var n = window.TP_MOUNTAIN_N_FRAMES;
|
||||
var fps = window.TP_MOUNTAIN_FPS;
|
||||
var i = 0;
|
||||
var last = 0;
|
||||
var acc = 0;
|
||||
var interval = 1000 / fps;
|
||||
|
||||
var reduceMotion =
|
||||
window.matchMedia &&
|
||||
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
|
||||
if (reduceMotion) {
|
||||
// Show a single representative frame; no animation.
|
||||
pre.textContent = window.TP_MOUNTAIN_FRAMES[Math.floor(n / 2)];
|
||||
return;
|
||||
}
|
||||
|
||||
function step(ts) {
|
||||
if (!last) last = ts;
|
||||
acc += ts - last;
|
||||
last = ts;
|
||||
while (acc >= interval) {
|
||||
i = (i + 1) % n;
|
||||
pre.textContent = window.TP_MOUNTAIN_FRAMES[i];
|
||||
acc -= interval;
|
||||
}
|
||||
if (!document.hidden) {
|
||||
requestAnimationFrame(step);
|
||||
} else {
|
||||
// Resume on next visibility change.
|
||||
document.addEventListener(
|
||||
"visibilitychange",
|
||||
function onVisible() {
|
||||
if (!document.hidden) {
|
||||
last = 0;
|
||||
acc = 0;
|
||||
document.removeEventListener("visibilitychange", onVisible);
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(step);
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
3969
static/js/mountain.js
Normal file
3969
static/js/mountain.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user