aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--blog/xesite-unwrapped-2022.markdown16
-rw-r--r--css/shim.css172
-rw-r--r--dhall/stories.dhall47
-rw-r--r--dhall/types/Story.dhall5
-rw-r--r--dhall/types/StoryStep.dhall3
-rw-r--r--dhall/types/package.dhall2
-rw-r--r--lib/xesite_markdown/src/lib.rs8
-rw-r--r--lib/xesite_templates/src/lib.rs20
-rwxr-xr-xsrc/frontend/build.sh1
-rw-r--r--src/frontend/stories.tsx119
-rw-r--r--static/unwrapped-2022.json25
11 files changed, 418 insertions, 0 deletions
diff --git a/blog/xesite-unwrapped-2022.markdown b/blog/xesite-unwrapped-2022.markdown
new file mode 100644
index 0000000..677538e
--- /dev/null
+++ b/blog/xesite-unwrapped-2022.markdown
@@ -0,0 +1,16 @@
+---
+title: Xesite Unwrapped 2022
+date: 2022-12-31
+tags:
+ - JavaScript
+ - shitposting
+ - WordArt
+---
+
+<noscript>
+ <p>I'm sorry but you will need to enable JavaScript support for this post. I
+ use advanced JavaScript features to create the wonderment that is this
+ creation.</p>
+</noscript>
+
+<xeblog-story name="unwrapped-2022"></xeblog-story>
diff --git a/css/shim.css b/css/shim.css
index 2e1df90..7a3ff89 100644
--- a/css/shim.css
+++ b/css/shim.css
@@ -112,3 +112,175 @@ figcaption {
background-color: #fbf1c7;
}
}
+
+.stories {
+ display: grid;
+ grid: 1fr / auto-flow 100%;
+ gap: 1ch;
+ overflow-x: auto;
+ scroll-snap-type: x mandatory;
+ overscroll-behavior: contain;
+ touch-action: pan-x;
+
+ min-width: 512px;
+ min-height: 512px;
+ max-width: 1024px;
+ max-height: 1024px;
+
+ border-radius: 3ch;
+
+ box-shadow:
+ 0 5px 2.5px hsla(200, 95%, 3%, .037),
+ 0 12px 6.5px hsla(200, 95%, 3%, .053),
+ 0 22.5px 13px hsla(200, 95%, 3%, .065),
+ 0 40.2px 24px hsla(200, 95%, 3%, .077),
+ 0 75.2px 44px hsla(200, 95%, 3%, .093),
+ 0 180px 80px hsla(200, 95%, 3%, .13)
+}
+
+.user {
+ scroll-snap-align: start;
+ scroll-snap-stop: always;
+ display: grid;
+ grid: [story] 1fr / [story] 1fr;
+}
+
+.story {
+ grid-area: story;
+
+ background-size: cover;
+ background-image:
+ var(--bg),
+ linear-gradient(to top, lch(98 0 0), lch(90 0 0));
+
+ user-select: none;
+ touch-action: manipulation;
+
+ transition: opacity .3s cubic-bezier(0.4, 0.0, 1, 1);
+
+ &.seen {
+ opacity: 0;
+ pointer-events: none;
+ }
+}
+
+*.wordart {
+ border: 0;
+ padding: 0;
+ margin: 0;
+ box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+}
+
+.wordart {
+ font-family: Arial, sans-serif;
+ font-size: 4em;
+ font-weight: bold;
+ position: relative;
+ z-index: 1;
+ display: inline-block;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.wordart.superhero {
+ transform: skew(0, -15deg) scale(1, 1.5);
+ -webkit-transform: skew(0, -15deg) scale(1, 1.5);
+ -moz-transform: skew(0, -15deg) scale(1, 1.5);
+ -o-transform: skew(0, -15deg) scale(1, 1.5);
+ -ms-transform: skew(0, -15deg) scale(1, 1.5);
+}
+
+.wordart {
+ font-family: Arial, sans-serif;
+ font-size: 4em;
+ font-weight: bold;
+ position: relative;
+ z-index: 1;
+ display: inline-block;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+.wordart.superhero {
+ transform: skew(0, -15deg) scale(1, 1.5);
+ -webkit-transform: skew(0, -15deg) scale(1, 1.5);
+ -moz-transform: skew(0, -15deg) scale(1, 1.5);
+ -o-transform: skew(0, -15deg) scale(1, 1.5);
+ -ms-transform: skew(0, -15deg) scale(1, 1.5);
+}
+
+.wordart.superhero .text {
+ font-family: Impact;
+ background: #fdea00;
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJod…EiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
+ background: -moz-linear-gradient(top, #fdea00 0%, #fdcf00 44%, #fc2700 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fdea00), color-stop(44%, #fdcf00), color-stop(100%, #fc2700));
+ background: -webkit-linear-gradient(top, #fdea00 0%, #fdcf00 44%, #fc2700 100%);
+ background: -o-linear-gradient(top, #fdea00 0%, #fdcf00 44%, #fc2700 100%);
+ background: -ms-linear-gradient(top, #fdea00 0%, #fdcf00 44%, #fc2700 100%);
+ background: linear-gradient(to bottom, #fdea00 0%, #fdcf00 44%, #fc2700 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdea00', endColorstr='#fc2700', GradientType=0);
+ background-clip: text;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.wordart.superhero .text:before {
+ content: attr(data-text);
+ position: absolute;
+ z-index: -1;
+ text-shadow: 0.01em 0em 0 #802700, 0em 0.01em 0 #c23d00, 0.02em 0.01em 0 #802700, 0.01em 0.02em 0 #c23d00, 0.03em 0.02em 0 #802700, 0.02em 0.03em 0 #c23d00, 0.04em 0.03em 0 #802700, 0.03em 0.04em 0 #c23d00, 0.05em 0.04em 0 #802700, 0.04em 0.05em 0 #c23d00, 0.06em 0.05em 0 #802700, 0.05em 0.06em 0 #c23d00, 0.07em 0.06em 0 #802700, 0.06em 0.07em 0 #c23d00, 0.08em 0.07em 0 #802700, 0.07em 0.08em 0 #c23d00;
+}
+
+.wordart.blues.text {
+ font-family: Impact, sans-serif;
+ color: #24c0fd;
+ text-shadow: 0.13em -0.13em 0px #0000aa;
+ letter-spacing: -0.05em;
+}
+
+.story-text.text {
+ font-family: "Comic Sans MS", "Comic Sans", cursive;
+ position: relative;
+ font-size: calc(34px + (36 + 36 * 0.7) * ((100vw - 320px) / 1920));
+ line-height: calc(46px + (65 + 65 * 0.7) * ((100vw - 320px) / 1920));
+ font-weight: 300;
+ background-color: #22012D;
+ color: #F603AA;
+ display: inline-block;
+ padding-left: 30px;
+ padding-right: 30px;
+}
+
+.story-text.box {
+ border-top-left-radius: 1rem;
+ border-top-right-radius: 1rem;
+ border-bottom-left-radius: 1rem;
+ border-bottom-right-radius: 1rem;
+}
+
+.story-text.box::after{
+ content: "";
+ position: absolute;
+ width: 2rem;
+ height: 2rem;
+ background-color: white;
+ z-index:5;
+ right: -2rem;
+ bottom:0rem;
+ border-radius: 100%;
+}
+
+.story-text.box::before{
+ content: "";
+ position: absolute;
+ width: 1rem;
+ height: 1rem;
+ background-color: #F603AA;
+ z-index:4;
+ right: -1rem;
+ bottom:0rem;
+ overflow: hidden;
+}
diff --git a/dhall/stories.dhall b/dhall/stories.dhall
new file mode 100644
index 0000000..8900269
--- /dev/null
+++ b/dhall/stories.dhall
@@ -0,0 +1,47 @@
+let xesite = ./types/package.dhall
+
+let Prelude = ./Prelude.dhall
+
+let Story = xesite.Story
+
+let Step = xesite.StoryStep
+
+let stories =
+ [ Story::{
+ , name = "unwrapped-2022"
+ , steps =
+ [ Step::{
+ , file = "title"
+ , title = "Xesite 2022"
+ , text = "The last year all wrapped up!"
+ }
+ , Step::{
+ , file = "posts"
+ , title = "Blogposts"
+ , text = "Xe wrote \$NUMBER posts this year!"
+ }
+ , Step::{
+ , file = "talks"
+ , title = "Talks"
+ , text = "Xe gave 4 talks this year! Which one was your favorite?"
+ }
+ , Step::{
+ , file = "commits"
+ , title = "340 commits"
+ , text = "There were 340 commits to Xesite this year!"
+ }
+ ]
+ }
+ ]
+
+let storyToMapValue =
+ \(story : Story.Type) -> { mapKey = story.name, mapValue = story }
+
+let map =
+ Prelude.List.map
+ Story.Type
+ (Prelude.Map.Entry Text Story.Type)
+ storyToMapValue
+ stories
+
+in { stories, map }
diff --git a/dhall/types/Story.dhall b/dhall/types/Story.dhall
new file mode 100644
index 0000000..1f11a2e
--- /dev/null
+++ b/dhall/types/Story.dhall
@@ -0,0 +1,5 @@
+let Step = ./StoryStep.dhall
+
+in { Type = { name : Text, steps : List Step.Type }
+ , default = { name = "", steps = [] : List Step.Type }
+ }
diff --git a/dhall/types/StoryStep.dhall b/dhall/types/StoryStep.dhall
new file mode 100644
index 0000000..3c7ac4f
--- /dev/null
+++ b/dhall/types/StoryStep.dhall
@@ -0,0 +1,3 @@
+{ Type = { file : Text, title : Text, text : Text }
+, default = { file = "", text = "", title = "" }
+}
diff --git a/dhall/types/package.dhall b/dhall/types/package.dhall
index 4f4166e..75abd4a 100644
--- a/dhall/types/package.dhall
+++ b/dhall/types/package.dhall
@@ -10,5 +10,7 @@
, Salary = ./Salary.dhall
, SeriesDescription = ./SeriesDescription.dhall
, Stock = ./Stock.dhall
+, Story = ./Story.dhall
+, StoryStep = ./StoryStep.dhall
, StockKind = ./StockKind.dhall
}
diff --git a/lib/xesite_markdown/src/lib.rs b/lib/xesite_markdown/src/lib.rs
index 941761d..0c6900a 100644
--- a/lib/xesite_markdown/src/lib.rs
+++ b/lib/xesite_markdown/src/lib.rs
@@ -175,6 +175,14 @@ pub fn render(inp: &str) -> Result<String> {
el.replace(&xesite_templates::talk_warning().0, ContentType::Html);
Ok(())
}),
+ element!("xeblog-story", |el| {
+ let name = el
+ .get_attribute("name")
+ .ok_or(Error::MissingElementAttribute("name".to_string()))?;
+
+ el.replace(&xesite_templates::story(name).0, ContentType::Html);
+ Ok(())
+ }),
element!("xeblog-video", |el| {
let path = el
.get_attribute("path")
diff --git a/lib/xesite_templates/src/lib.rs b/lib/xesite_templates/src/lib.rs
index 1149aaa..2f830f8 100644
--- a/lib/xesite_templates/src/lib.rs
+++ b/lib/xesite_templates/src/lib.rs
@@ -96,6 +96,26 @@ pub fn sticker(name: String, mood: String) -> Markup {
}
}
+pub fn story(name: String) -> Markup {
+ let uuid = uuid::Uuid::new_v4();
+ let uuid = format!("{uuid}").replace("-", "");
+ let story_script = PreEscaped(format!(
+ r#"
+<script type="module">
+import {{ init }} from "/static/js/stories.js?cachebust={uuid}";
+
+init("{uuid}", "{name}");
+</script>
+"#
+ ));
+
+ html! {
+ div id=(uuid) {
+ (story_script)
+ }
+ }
+}
+
pub fn video(path: String) -> Markup {
let stream_url = format!(
"https://cdn.xeiaso.net/file/christine-static/{}/index.m3u8",
diff --git a/src/frontend/build.sh b/src/frontend/build.sh
index 33577ae..81b4485 100755
--- a/src/frontend/build.sh
+++ b/src/frontend/build.sh
@@ -10,3 +10,4 @@ set -e
export RUST_LOG=info
denobuild ./mastodon_share_button.tsx ../../static/js/mastodon_share_button.js
denobuild ./wasiterm.tsx ../../static/js/wasiterm.js
+denobuild ./stories.tsx ../../static/js/stories.js
diff --git a/src/frontend/stories.tsx b/src/frontend/stories.tsx
new file mode 100644
index 0000000..cb52439
--- /dev/null
+++ b/src/frontend/stories.tsx
@@ -0,0 +1,119 @@
+import { g, u, x } from "xeact";
+
+type Story = {
+ steps: Step[],
+};
+
+type Step = {
+ file: string,
+ title: string,
+ text: string,
+};
+
+const init = async (elemID: string, story: string) => {
+ let root = g(elemID);
+ x(root);
+ let resp = await fetch(u(`/static/${story}.json`));
+ if (!resp.ok) {
+ root.appendChild(<div>
+ <big>Oopsie-whoopsie!</big>
+ <p>Oopsie-whoopsie uwu we made a fucky-wucky! A wittle fucko boingo! The code monkeys at our headquarters are working reawy hard to fix this!</p>
+ <code>
+ Wanted response to be ok, but unexpectedly got: {resp.status}
+ </code>
+ </div>);
+ return;
+ }
+
+ let data: Story = await resp.json();
+ console.log(data);
+
+ const steps = data.steps.map(s => {
+ console.log(s);
+ //<article class="story" style="--bg: url(https://picsum.photos/480/840);"></article>
+ const style = `--bg: url(https://cdn.xeiaso.net/file/christine-static/stories/${story}/${s.file}.jpg)`;
+ console.log(style);
+ const result = <article class="story" style={style}>
+ <br />
+ <br />
+ <center><div class="wordart superhero"><span class="text">{s.title}</span></div></center>
+ <br />
+ <br />
+ <div class="story-text text box">{s.text}</div>
+ </article>;
+ console.log(result.style.cssText);
+
+ result.style.cssText = result.style.cssText.replace("\\", "");
+ console.log(result.style.cssText);
+
+ return result;
+ });
+
+ const stories = <div class="stories">
+ <section class="user">
+ {steps}
+ </section>
+ </div>;
+
+ root.appendChild(stories);
+
+ const median = stories.offsetLeft + (stories.clientWidth / 2);
+ const state = {
+ current_story: stories?.firstElementChild?.lastElementChild,
+ };
+
+ const navigateStories = (direction: "next" | "prev") => {
+ const story = state.current_story;
+ const lastItemInUserStory = story?.parentNode?.firstElementChild;
+ const firstItemInUserStory = story?.parentNode?.lastElementChild;
+ const hasNextUserStory = story?.parentElement?.nextElementSibling;
+ const hasPrevUserStory = story?.parentElement?.previousElementSibling;
+
+ if (direction === "next") {
+ if (lastItemInUserStory === story && !hasNextUserStory) return;
+ else if (lastItemInUserStory === story && hasNextUserStory) {
+ state.current_story =
+ story.parentElement.nextElementSibling.lastElementChild;
+ story?.parentElement.nextElementSibling.scrollIntoView({
+ behavior: "smooth",
+ });
+ } else {
+ story?.classList.add("seen");
+ state.current_story = story?.previousElementSibling;
+ }
+ } else if (direction === "prev") {
+ if (firstItemInUserStory === story && !hasPrevUserStory) return;
+ else if (firstItemInUserStory === story && hasPrevUserStory) {
+ state.current_story =
+ story.parentElement.previousElementSibling.firstElementChild;
+ story.parentElement.previousElementSibling.scrollIntoView({
+ behavior: "smooth",
+ });
+ } else {
+ story?.nextElementSibling?.classList.remove("seen");
+ state.current_story = story?.nextElementSibling;
+ }
+ }
+
+ console.log(state.current_story.innerText);
+ };
+
+ root.addEventListener("click", (e) => {
+ if (!(e.target instanceof HTMLElement)) {
+ return;
+ }
+ if (e.target?.nodeName !== "ARTICLE") {
+ return;
+ }
+
+ navigateStories(e.clientX > median ? "next" : "prev");
+ });
+
+ document.addEventListener("keydown", ({ key }) => {
+ if (key === "ArrowDown" || key === "ArrowUp") {
+ navigateStories(key === "ArrowDown" ? "next" : "prev");
+ }
+ });
+};
+
+export { init };
diff --git a/static/unwrapped-2022.json b/static/unwrapped-2022.json
new file mode 100644
index 0000000..80caef6
--- /dev/null
+++ b/static/unwrapped-2022.json
@@ -0,0 +1,25 @@
+{
+ "name": "unwrapped-2022",
+ "steps": [
+ {
+ "file": "title",
+ "text": "The last year all wrapped up!",
+ "title": "Xesite 2022"
+ },
+ {
+ "file": "posts",
+ "text": "Xe wrote $NUMBER posts this year!",
+ "title": "Blogposts"
+ },
+ {
+ "file": "talks",
+ "text": "Xe gave 4 talks this year! Which one was your favorite?",
+ "title": "Talks"
+ },
+ {
+ "file": "commits",
+ "text": "There were 340 commits to Xesite this year!",
+ "title": "340 commits"
+ }
+ ]
+}