From 4233c431028f5f655ea4a2f169833dda71846783 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 15:31:47 +0000 Subject: [PATCH 01/55] Starting blog post on stackoverflow And loads of supporting graph code --- GRAPHS.md | 262 +++++++++++++ assets/sass/pages/blog.scss | 5 +- assets/sass/pages/resources.scss | 6 +- assets/sass/partials/_graphs.scss | 174 +++++++++ assets/sass/style.scss | 1 + config.yml | 23 -- .../ai-coding-trend.csv | 86 +++++ .../ai-trend.csv | 86 +++++ .../the-downfall-of-stackoverflow/index.md | 80 ++++ .../stackoverflow_questions_after_2019.csv | 86 +++++ .../stackoverflow_questions_over_time.csv | 212 +++++++++++ content/resources/_index.md | 3 +- layouts/_default/baseof.html | 3 + layouts/partials/site-header.html | 16 +- layouts/resources/list.html | 9 +- layouts/shortcodes/graph.html | 350 ++++++++++++++++++ 16 files changed, 1352 insertions(+), 50 deletions(-) create mode 100644 GRAPHS.md create mode 100644 assets/sass/partials/_graphs.scss create mode 100644 content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv create mode 100644 content/blog/the-downfall-of-stackoverflow/ai-trend.csv create mode 100644 content/blog/the-downfall-of-stackoverflow/index.md create mode 100644 content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv create mode 100644 content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv create mode 100644 layouts/shortcodes/graph.html diff --git a/GRAPHS.md b/GRAPHS.md new file mode 100644 index 0000000..b2dc1de --- /dev/null +++ b/GRAPHS.md @@ -0,0 +1,262 @@ +# Graph Shortcode Documentation + +This Hugo site includes a custom shortcode for creating terminal-styled graphs that match the hacker green aesthetic. + +## Features + +- **Server-side processing**: Data is processed when Hugo builds the site +- **Terminal styling**: Automatic green color scheme matching the site theme +- **Multiple chart types**: Line, bar, pie, doughnut, radar, and more +- **Responsive**: Adapts to different screen sizes +- **Customizable**: Override colors and styling as needed + +## Basic Usage + +The shortcode supports two modes: +1. **Inline JSON data** - Embed data directly in your markdown +2. **CSV file data** - Load data from a CSV file in your page bundle + +### CSV File Mode (Recommended) + +Place your CSV file in the same directory as your blog post's `index.md`: + +```markdown +{{< graph id="my-chart" type="line" title="My Data" height="500" csv="data.csv" labelColumn="Date" dataColumn="Value" dateFormat="2006-01" >}} +{{< /graph >}} +``` + +**CSV Parameters:** +- `csv` - Filename of the CSV in the page bundle (required for CSV mode) +- `labelColumn` - Column name for x-axis labels (default: "Month") +- `dataColumn` - Column name for y-axis data (default: "Questions") +- `dateFormat` - Go date format for parsing timestamps (default: "2006-01") +- `skipRows` - Number of rows to skip from the start (default: 0) +- `maxRows` - Maximum number of rows to display (default: 0 = all) + +**Example CSV file** (`data.csv`): +```csv +Date,Value +2024-01,100 +2024-02,150 +2024-03,175 +``` + +### Inline JSON Mode + +### Line Chart + +```markdown +{{< graph id="my-chart" type="line" title="My Data" height="400" >}} +{ + "labels": ["Jan", "Feb", "Mar", "Apr", "May"], + "datasets": [{ + "label": "Sales", + "data": [12, 19, 3, 5, 2] + }] +} +{{< /graph >}} +``` + +### Bar Chart + +```markdown +{{< graph id="bar-example" type="bar" title="Monthly Revenue" height="350" >}} +{ + "labels": ["Q1", "Q2", "Q3", "Q4"], + "datasets": [{ + "label": "Revenue ($k)", + "data": [65, 59, 80, 81] + }] +} +{{< /graph >}} +``` + +### Multiple Datasets + +```markdown +{{< graph id="multi-line" type="line" title="Comparison" height="400" >}} +{ + "labels": ["2020", "2021", "2022", "2023", "2024"], + "datasets": [ + { + "label": "Product A", + "data": [30, 45, 60, 70, 85] + }, + { + "label": "Product B", + "data": [20, 35, 55, 65, 75] + } + ] +} +{{< /graph >}} +``` + +### Pie Chart + +```markdown +{{< graph id="pie-chart" type="pie" title="Market Share" height="400" >}} +{ + "labels": ["Chrome", "Firefox", "Safari", "Edge"], + "datasets": [{ + "data": [65, 15, 12, 8] + }] +} +{{< /graph >}} +``` + +## Parameters + +### Common Parameters + +| Parameter | Required | Default | Description | +|-----------|----------|---------|-------------| +| `id` | No | Auto-generated | Unique identifier for the chart | +| `type` | No | `line` | Chart type: `line`, `bar`, `pie`, `doughnut`, `radar`, `polarArea` | +| `title` | No | Empty | Chart title displayed above the graph | +| `height` | No | `400` | Height of the chart container in pixels | + +### CSV Mode Parameters + +| Parameter | Required | Default | Description | +|-----------|----------|---------|-------------| +| `csv` | Yes (for CSV mode) | - | Filename of CSV in page bundle | +| `labelColumn` | No | `"Month"` | CSV column name for labels (x-axis) | +| `dataColumn` | No | `"Questions"` | CSV column name for data (y-axis) | +| `dateFormat` | No | `"2006-01"` | Go date format string for parsing dates | +| `skipRows` | No | `0` | Number of data rows to skip | +| `maxRows` | No | `0` | Max rows to display (0 = all) | + +## Data Format + +The shortcode expects JSON data in Chart.js format. The basic structure is: + +```json +{ + "labels": ["Label 1", "Label 2", "Label 3"], + "datasets": [{ + "label": "Dataset Name", + "data": [value1, value2, value3] + }] +} +``` + +### Advanced Data Options + +You can override the automatic styling by providing Chart.js dataset properties: + +```markdown +{{< graph id="custom-style" type="line" title="Custom Colors" >}} +{ + "labels": ["A", "B", "C"], + "datasets": [{ + "label": "Custom", + "data": [10, 20, 30], + "borderColor": "rgb(255, 99, 132)", + "backgroundColor": "rgba(255, 99, 132, 0.2)", + "borderWidth": 3 + }] +} +{{< /graph >}} +``` + +## Color Scheme + +The shortcode automatically applies terminal-style colors: + +- **Primary**: `rgb(173, 255, 47)` (greenyellow) +- **Secondary**: `rgb(0, 255, 0)` (green) +- **Tertiary**: `rgb(0, 200, 0)` (darker green) +- **Grid**: `rgba(0, 255, 0, 0.2)` +- **Background**: `rgba(0, 255, 0, 0.1)` + +Multiple datasets automatically cycle through these colors. + +## CSS Classes + +The graph container has the class `.graph-container` with the following variants: + +- `.graph-container.full-width` - Full width graph (extends to edges) +- `.graph-container.compact` - Smaller padding and title + +To use variants, wrap the shortcode in a div: + +```html +
+{{< graph ... >}} +... +{{< /graph >}} +
+``` + +## Examples + +### StackOverflow Decline + +```markdown +{{< graph id="stackoverflow" type="line" title="Stack Overflow Activity" height="400" >}} +{ + "labels": ["2018", "2019", "2020", "2021", "2022", "2023", "2024"], + "datasets": [{ + "label": "Questions (thousands/month)", + "data": [170, 180, 195, 175, 150, 120, 85], + "fill": true + }] +} +{{< /graph >}} +``` + +### Technology Adoption + +```markdown +{{< graph id="tech-adoption" type="bar" title="Framework Adoption (%)" height="350" >}} +{ + "labels": ["React", "Vue", "Angular", "Svelte"], + "datasets": [{ + "label": "2023", + "data": [67, 45, 42, 18] + }, { + "label": "2024", + "data": [71, 48, 38, 25] + }] +} +{{< /graph >}} +``` + +### Response Time Distribution + +```markdown +{{< graph id="response-times" type="doughnut" title="API Response Times" height="400" >}} +{ + "labels": ["< 100ms", "100-500ms", "500ms-1s", "> 1s"], + "datasets": [{ + "data": [45, 35, 15, 5] + }] +} +{{< /graph >}} +``` + +## Tips + +1. **Keep IDs unique**: Each chart on a page needs a unique ID +2. **Data validation**: The shortcode validates JSON at build time - invalid JSON will cause build errors +3. **Responsive**: Charts are responsive by default, but fixed heights work better for consistency +4. **Performance**: Charts are rendered client-side but data is embedded at build time +5. **Testing**: Set `draft: false` and run `hugo server -D` to preview graphs + +## Troubleshooting + +**Graph not showing?** +- Check browser console for JavaScript errors +- Verify Chart.js is loaded (should be in page ``) +- Ensure JSON data is valid +- Check that the ID is unique on the page + +**Styling issues?** +- Graphs inherit the terminal theme automatically +- Custom colors can be added per dataset +- Container styling can be modified in `assets/sass/partials/_graphs.scss` + +**Build errors?** +- Invalid JSON will cause Hugo build failures +- Check for unclosed braces or missing commas +- Ensure the shortcode tags are properly formatted diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 249cbe9..4667a44 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -271,14 +271,12 @@ } } - .tag-filter-link { background: rgba(255, 153, 0, 0.2); border-color: rgba(255, 153, 0, 0.5); color: #ff9900; text-shadow: 0 0 5px rgba(255, 153, 0, 0.5); } - } // Post title @@ -555,7 +553,8 @@ background: linear-gradient(#000, #000) padding-box, linear-gradient(180deg, #0f0, #000) border-box; - filter: grayscale(100%) contrast(1.2) brightness(0.9) sepia(100%) hue-rotate(60deg) saturate(300%); + filter: grayscale(100%) contrast(1.2) brightness(0.9) sepia(100%) + hue-rotate(60deg) saturate(300%); transition: all 0.3s ease; &:hover { diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 65c6a81..3735953 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -52,8 +52,10 @@ } .whiteboard-description { - color: #546e7a; - font-size: 1.1rem; + font-family: "Caveat", cursive; + font-size: 28px; + font-weight: bold; + color: #2c3e50; max-width: 600px; margin: 0 auto; } diff --git a/assets/sass/partials/_graphs.scss b/assets/sass/partials/_graphs.scss new file mode 100644 index 0000000..c241f52 --- /dev/null +++ b/assets/sass/partials/_graphs.scss @@ -0,0 +1,174 @@ +// Graph container styling to match terminal aesthetic +.graph-container { + margin: 2rem 0; + padding: 20px 20px 100px 20px; // Extra bottom padding for rotated x-axis labels + background: rgba(0, 255, 0, 0.05); + border: 2px solid rgba(0, 255, 0, 0.3); + border-radius: 8px; + position: relative; + box-shadow: + 0 0 20px rgba(0, 255, 0, 0.1), + inset 0 0 40px rgba(0, 255, 0, 0.05); + overflow: visible; // Prevent clipping of labels + + @include media-down(lg) { + padding: 15px 15px 120px 15px; + margin: 1.5rem 0; + } + + // Terminal-style border glow on hover + &:hover { + border-color: rgba(0, 255, 0, 0.5); + box-shadow: + 0 0 30px rgba(0, 255, 0, 0.2), + inset 0 0 40px rgba(0, 255, 0, 0.08); + } + + // Scanline effect overlay + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.1) 0px, + rgba(0, 0, 0, 0.1) 1px, + transparent 1px, + transparent 3px + ); + pointer-events: none; + z-index: 1; + border-radius: 6px; + } + + // Graph title + .graph-title { + color: greenyellow; + font-family: monospace; + font-size: 1.3rem; + margin: 0 0 15px 0; + text-align: center; + text-shadow: 0 0 10px rgba(173, 255, 47, 0.5); + text-transform: uppercase; + letter-spacing: 2px; + position: relative; + z-index: 2; + + @include media-down(lg) { + font-size: 1.1rem; + margin-bottom: 10px; + } + + &::after { + content: ""; + display: block; + width: 60%; + height: 2px; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 255, 0, 0.6), + transparent + ); + margin: 8px auto 0; + box-shadow: 0 0 10px rgba(0, 255, 0, 0.4); + } + } + + // Canvas element + canvas { + position: relative; + z-index: 2; + filter: drop-shadow(0 0 8px rgba(0, 255, 0, 0.3)); + } + + // CRT flicker effect (subtle) + @keyframes graph-flicker { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.98; + } + } + + animation: graph-flicker 3s ease-in-out infinite; +} + +// Special styling for graphs in blog posts +.blog-summary, +.blogs-content { + .graph-container { + // Ensure proper spacing in blog context + margin: 2.5rem 0; + + @include media-down(lg) { + margin: 1.5rem 0; + } + } +} + +// Full-width graph variant +.graph-container.full-width { + margin-left: -20px; + margin-right: -20px; + border-radius: 0; + + @include media-down(lg) { + margin-left: -15px; + margin-right: -15px; + } +} + +// Compact graph variant +.graph-container.compact { + padding: 15px; + + .graph-title { + font-size: 1.1rem; + margin-bottom: 10px; + } + + @include media-down(lg) { + padding: 10px; + + .graph-title { + font-size: 1rem; + } + } +} + +// Loading state +.graph-container.loading { + &::after { + content: "LOADING DATA..."; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: #0f0; + font-family: monospace; + font-size: 1.2rem; + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + animation: pulse 1.5s ease-in-out infinite; + z-index: 3; + } + + canvas { + opacity: 0.3; + } +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} diff --git a/assets/sass/style.scss b/assets/sass/style.scss index 31ff094..56bb0e1 100644 --- a/assets/sass/style.scss +++ b/assets/sass/style.scss @@ -14,6 +14,7 @@ @import "partials/lcd-display"; @import "partials/window"; @import "partials/crt-tv"; +@import "partials/graphs"; @import "partials/content-screens"; diff --git a/config.yml b/config.yml index f9b9050..73ee525 100644 --- a/config.yml +++ b/config.yml @@ -42,29 +42,6 @@ params: limit: 10 keys: ["title", "permalink", "summary", "content"] -menu: - main: - - identifier: about - name: about - url: /about/ - weight: 5 - - identifier: gear - name: gear & edc - url: /gear/ - weight: 10 - - identifier: resources - name: resources - url: /resources/ - weight: 12 - - identifier: archives - name: archives - url: /archives/ - weight: 15 - - identifier: tags - name: tags - url: /tags/ - weight: 20 - markup: highlight: noClasses: false diff --git a/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv b/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv new file mode 100644 index 0000000..e993347 --- /dev/null +++ b/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv @@ -0,0 +1,86 @@ +Month,ai coding: (Worldwide) +2019-01,1 +2019-02,1 +2019-03,1 +2019-04,1 +2019-05,1 +2019-06,1 +2019-07,1 +2019-08,1 +2019-09,1 +2019-10,1 +2019-11,1 +2019-12,1 +2020-01,1 +2020-02,1 +2020-03,1 +2020-04,1 +2020-05,1 +2020-06,1 +2020-07,1 +2020-08,1 +2020-09,1 +2020-10,1 +2020-11,1 +2020-12,1 +2021-01,1 +2021-02,1 +2021-03,1 +2021-04,1 +2021-05,1 +2021-06,1 +2021-07,1 +2021-08,1 +2021-09,1 +2021-10,1 +2021-11,1 +2021-12,1 +2022-01,1 +2022-02,1 +2022-03,1 +2022-04,1 +2022-05,1 +2022-06,1 +2022-07,1 +2022-08,1 +2022-09,1 +2022-10,1 +2022-11,1 +2022-12,3 +2023-01,4 +2023-02,5 +2023-03,5 +2023-04,6 +2023-05,7 +2023-06,7 +2023-07,7 +2023-08,7 +2023-09,7 +2023-10,7 +2023-11,8 +2023-12,8 +2024-01,9 +2024-02,10 +2024-03,14 +2024-04,12 +2024-05,11 +2024-06,11 +2024-07,10 +2024-08,11 +2024-09,14 +2024-10,16 +2024-11,18 +2024-12,19 +2025-01,21 +2025-02,27 +2025-03,31 +2025-04,32 +2025-05,36 +2025-06,53 +2025-07,58 +2025-08,100 +2025-09,89 +2025-10,82 +2025-11,92 +2025-12,82 +2026-01,41 diff --git a/content/blog/the-downfall-of-stackoverflow/ai-trend.csv b/content/blog/the-downfall-of-stackoverflow/ai-trend.csv new file mode 100644 index 0000000..a694ed2 --- /dev/null +++ b/content/blog/the-downfall-of-stackoverflow/ai-trend.csv @@ -0,0 +1,86 @@ +Month,ai: (Worldwide) +2019-01,5 +2019-02,5 +2019-03,5 +2019-04,5 +2019-05,5 +2019-06,5 +2019-07,5 +2019-08,5 +2019-09,5 +2019-10,5 +2019-11,5 +2019-12,5 +2020-01,5 +2020-02,5 +2020-03,5 +2020-04,6 +2020-05,6 +2020-06,5 +2020-07,5 +2020-08,5 +2020-09,5 +2020-10,5 +2020-11,5 +2020-12,5 +2021-01,5 +2021-02,5 +2021-03,6 +2021-04,6 +2021-05,6 +2021-06,6 +2021-07,5 +2021-08,6 +2021-09,6 +2021-10,6 +2021-11,6 +2021-12,6 +2022-01,7 +2022-02,7 +2022-03,7 +2022-04,7 +2022-05,7 +2022-06,7 +2022-07,7 +2022-08,7 +2022-09,8 +2022-10,8 +2022-11,8 +2022-12,12 +2023-01,12 +2023-02,15 +2023-03,16 +2023-04,21 +2023-05,28 +2023-06,27 +2023-07,26 +2023-08,26 +2023-09,25 +2023-10,29 +2023-11,30 +2023-12,30 +2024-01,32 +2024-02,33 +2024-03,36 +2024-04,34 +2024-05,36 +2024-06,34 +2024-07,34 +2024-08,39 +2024-09,39 +2024-10,42 +2024-11,43 +2024-12,43 +2025-01,47 +2025-02,51 +2025-03,54 +2025-04,55 +2025-05,52 +2025-06,57 +2025-07,62 +2025-08,73 +2025-09,100 +2025-10,92 +2025-11,90 +2025-12,80 +2026-01,66 diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md new file mode 100644 index 0000000..20e4ae8 --- /dev/null +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -0,0 +1,80 @@ +--- +title: "The Downfall of StackOverflow" +date: 2026-02-04T13:58:02Z +tags: + - analysis + - ai +draft: true +--- + +This post was inspired by a post on [Hacker News](https://news.ycombinator.com/item?id=46482345) that linked to this [StackOverflow data](https://data.stackexchange.com/stackoverflow/query/1926661#graph). + +My kneejerk reaction was that the rise in AI and its code analysis capabilities have caused the downfall of StackOverflow. We can see a peak after a gradual decline in early 2020 (COVID bedroom coders?) which then returns to a roughly normal level by 2021, before starting a stark decline into obscurity, very much accelerating at the end of 2022. + +{{< graph id="stackoverflow-trend" type="line" title="Stack Overflow Questions Over Time" height="500" csv="stackoverflow_questions_over_time.csv" labelColumn="Month" dataColumn="" dateFormat="2006-01" >}} +{{< /graph >}} + +## The rise and fall + +StackOverflow had a hell of a run. From just 4 questions monthly in July 2008 to over 207,000 in March 2014—that's six years of basically uninterrupted growth. It became _the_ place every developer went when they were stuck. + +Then around 2014-2015, it plateaued. About 170,000-190,000 questions per month, which held steady for a few years before starting to slip. By 2019 we're down to around 150,000 per month. Still solid, but the writing was on the wall. + +Then it properly falls off a cliff. January 2023: 97,209 questions. December 2023: 42,601. January 2026: 321. That's a 99.8% drop from the 2020 peak. Three hundred and twenty-one questions. In a month. + +## Is it AI? + +Looking at Google Trends[^1] for AI-related searches, there's a bit of a gap between when StackOverflow started dying and when AI actually took off. + +{{< graph id="ai-trends" type="line" title="AI Search Trends" height="500" + csv="ai-coding-trend.csv,ai-trend.csv" + labelColumn="Month" + dataColumns="ai coding: (Worldwide),ai: (Worldwide)" + datasetLabels="AI Coding,AI General" + dateFormat="2006-01" >}} +{{< /graph >}} + +Both general AI interest and AI coding searches were basically flat from 2019 through most of 2022. Then in December 2022 it spikes—that's ChatGPT launching[^2]. + +Zooming in on 2019 onwards makes it clearer: + +{{< graph id="ai-trends-vs-questions" type="line" title="AI Search Trends vs StackOverflow Questions" height="500" + csv="stackoverflow_questions_after_2019.csv,ai-coding-trend.csv,ai-trend.csv" + labelColumn="Month" + dataColumns="Questions,ai coding: (Worldwide),ai: (Worldwide)" + datasetLabels="StackOverflow Questions,AI Coding,AI General" + yAxisIDs="y,y1,y1" + dateFormat="2006-01" >}} +{{< /graph >}} + +## The timeline doesn't quite add up + +Here's the thing though: StackOverflow's accelerated decline starts in 2021, well before anyone gave a shit about AI coding. From January 2021 (140,009 questions) to December 2022 (96,767 questions), it lost 31% of its traffic while "AI coding" searches sat at baseline. + +The AI coding surge doesn't really kick off until early 2023, then explodes through 2025, peaking in August. But by then StackOverflow was already down to 5,885 questions per month. + +## So what's actually going on? + +The data suggests AI accelerated something that was already happening. A few things probably contributed: + +**The saturation effect**: By 2021, StackOverflow had 16+ years of answered questions. How many times can you ask "how do I parse JSON in Python" before every variation is covered? The "just Google it" response became the correct answer because everything _had_ been Googled already. + +**The pre-AI decline (2021-2022)**: 31% drop over 18 months while AI coding searches were dead flat. This points to other shifts—better documentation, clearer error messages, frameworks maturing and becoming less footgun-y. Developers were finding answers without needing to ask. + +**The AI acceleration (2023-2025)**: ChatGPT launches November 30, 2022. By March 2023, StackOverflow drops from 123,614 questions to 87,543. AI tools give you instant answers without needing to wade through ten variations of your question that are marked as duplicates and locked. + +**The collapse (2025-2026)**: By mid-2025, AI coding tools are just... everywhere. GitHub Copilot, ChatGPT, Claude, all of them baked into every IDE and workflow. The August 2025 peak in "AI coding" searches lines up with StackOverflow hitting 5,885 questions. That's a 96.8% decline from five years earlier. + +## The bottom line + +AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up—content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask? + +The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By January 2026 we're at 321 questions. That's about the same as August 2008, when it was brand new. + +Twelve years to build it. Five years to tear it down. And yeah, the last three were almost certainly AI finishing what was already started. + +--- + +[^1]: Google Trends data provides relative search interest rather than absolute numbers, which may not capture the full picture. + +[^2]: [ChatGPT Launch Announcement](https://openai.com/index/chatgpt/) diff --git a/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv new file mode 100644 index 0000000..8f7dc97 --- /dev/null +++ b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv @@ -0,0 +1,86 @@ +Month,Questions +"2019-01-01 00:00:00","149628" +"2019-02-01 00:00:00","146421" +"2019-03-01 00:00:00","161159" +"2019-04-01 00:00:00","153558" +"2019-05-01 00:00:00","151393" +"2019-06-01 00:00:00","135935" +"2019-07-01 00:00:00","151081" +"2019-08-01 00:00:00","137181" +"2019-09-01 00:00:00","136935" +"2019-10-01 00:00:00","152721" +"2019-11-01 00:00:00","148338" +"2019-12-01 00:00:00","132623" +"2020-01-01 00:00:00","146735" +"2020-02-01 00:00:00","145191" +"2020-03-01 00:00:00","156037" +"2020-04-01 00:00:00","183016" +"2020-05-01 00:00:00","186573" +"2020-06-01 00:00:00","172002" +"2020-07-01 00:00:00","166135" +"2020-08-01 00:00:00","148452" +"2020-09-01 00:00:00","141887" +"2020-10-01 00:00:00","141948" +"2020-11-01 00:00:00","135088" +"2020-12-01 00:00:00","134035" +"2021-01-01 00:00:00","140009" +"2021-02-01 00:00:00","131810" +"2021-03-01 00:00:00","148900" +"2021-04-01 00:00:00","136022" +"2021-05-01 00:00:00","133911" +"2021-06-01 00:00:00","129106" +"2021-07-01 00:00:00","124130" +"2021-08-01 00:00:00","122273" +"2021-09-01 00:00:00","119882" +"2021-10-01 00:00:00","119008" +"2021-11-01 00:00:00","119260" +"2021-12-01 00:00:00","112278" +"2022-01-01 00:00:00","119459" +"2022-02-01 00:00:00","114114" +"2022-03-01 00:00:00","123614" +"2022-04-01 00:00:00","114422" +"2022-05-01 00:00:00","116346" +"2022-06-01 00:00:00","111741" +"2022-07-01 00:00:00","111059" +"2022-08-01 00:00:00","113048" +"2022-09-01 00:00:00","103965" +"2022-10-01 00:00:00","106366" +"2022-11-01 00:00:00","109719" +"2022-12-01 00:00:00","96767" +"2023-01-01 00:00:00","97209" +"2023-02-01 00:00:00","85973" +"2023-03-01 00:00:00","87543" +"2023-04-01 00:00:00","68746" +"2023-05-01 00:00:00","66749" +"2023-06-01 00:00:00","63858" +"2023-07-01 00:00:00","62938" +"2023-08-01 00:00:00","60319" +"2023-09-01 00:00:00","53046" +"2023-10-01 00:00:00","52743" +"2023-11-01 00:00:00","50646" +"2023-12-01 00:00:00","42601" +"2024-01-01 00:00:00","47854" +"2024-02-01 00:00:00","46292" +"2024-03-01 00:00:00","45070" +"2024-04-01 00:00:00","42776" +"2024-05-01 00:00:00","40485" +"2024-06-01 00:00:00","32243" +"2024-07-01 00:00:00","31740" +"2024-08-01 00:00:00","28059" +"2024-09-01 00:00:00","24947" +"2024-10-01 00:00:00","23319" +"2024-11-01 00:00:00","20891" +"2024-12-01 00:00:00","18029" +"2025-01-01 00:00:00","22394" +"2025-02-01 00:00:00","19340" +"2025-03-01 00:00:00","18965" +"2025-04-01 00:00:00","14138" +"2025-05-01 00:00:00","11824" +"2025-06-01 00:00:00","9392" +"2025-07-01 00:00:00","7841" +"2025-08-01 00:00:00","5885" +"2025-09-01 00:00:00","6132" +"2025-10-01 00:00:00","5415" +"2025-11-01 00:00:00","4366" +"2025-12-01 00:00:00","3862" +"2026-01-01 00:00:00","321" \ No newline at end of file diff --git a/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv new file mode 100644 index 0000000..edfdc83 --- /dev/null +++ b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv @@ -0,0 +1,212 @@ +Month,Questions +"2008-07-01 00:00:00","4" +"2008-08-01 00:00:00","3749" +"2008-09-01 00:00:00","14040" +"2008-10-01 00:00:00","14578" +"2008-11-01 00:00:00","12717" +"2008-12-01 00:00:00","12081" +"2009-01-01 00:00:00","15833" +"2009-02-01 00:00:00","17581" +"2009-03-01 00:00:00","20482" +"2009-04-01 00:00:00","21336" +"2009-05-01 00:00:00","25814" +"2009-06-01 00:00:00","28326" +"2009-07-01 00:00:00","32483" +"2009-08-01 00:00:00","32775" +"2009-09-01 00:00:00","33054" +"2009-10-01 00:00:00","36331" +"2009-11-01 00:00:00","38518" +"2009-12-01 00:00:00","37795" +"2010-01-01 00:00:00","44938" +"2010-02-01 00:00:00","44811" +"2010-03-01 00:00:00","52236" +"2010-04-01 00:00:00","50084" +"2010-05-01 00:00:00","51996" +"2010-06-01 00:00:00","55750" +"2010-07-01 00:00:00","60848" +"2010-08-01 00:00:00","63660" +"2010-09-01 00:00:00","61230" +"2010-10-01 00:00:00","63777" +"2010-11-01 00:00:00","69693" +"2010-12-01 00:00:00","69573" +"2011-01-01 00:00:00","79911" +"2011-02-01 00:00:00","82942" +"2011-03-01 00:00:00","100970" +"2011-04-01 00:00:00","95562" +"2011-05-01 00:00:00","100229" +"2011-06-01 00:00:00","99027" +"2011-07-01 00:00:00","100445" +"2011-08-01 00:00:00","106917" +"2011-09-01 00:00:00","101722" +"2011-10-01 00:00:00","101032" +"2011-11-01 00:00:00","108450" +"2011-12-01 00:00:00","103172" +"2012-01-01 00:00:00","115434" +"2012-02-01 00:00:00","123299" +"2012-03-01 00:00:00","133000" +"2012-04-01 00:00:00","128064" +"2012-05-01 00:00:00","133515" +"2012-06-01 00:00:00","130323" +"2012-07-01 00:00:00","141906" +"2012-08-01 00:00:00","141528" +"2012-09-01 00:00:00","132241" +"2012-10-01 00:00:00","150083" +"2012-11-01 00:00:00","148310" +"2012-12-01 00:00:00","135471" +"2013-01-01 00:00:00","157850" +"2013-02-01 00:00:00","153402" +"2013-03-01 00:00:00","173814" +"2013-04-01 00:00:00","171546" +"2013-05-01 00:00:00","166468" +"2013-06-01 00:00:00","158334" +"2013-07-01 00:00:00","176636" +"2013-08-01 00:00:00","170215" +"2013-09-01 00:00:00","165374" +"2013-10-01 00:00:00","184133" +"2013-11-01 00:00:00","176033" +"2013-12-01 00:00:00","165192" +"2014-01-01 00:00:00","188797" +"2014-02-01 00:00:00","187751" +"2014-03-01 00:00:00","207493" +"2014-04-01 00:00:00","194022" +"2014-05-01 00:00:00","176017" +"2014-06-01 00:00:00","162354" +"2014-07-01 00:00:00","177066" +"2014-08-01 00:00:00","163406" +"2014-09-01 00:00:00","165866" +"2014-10-01 00:00:00","172887" +"2014-11-01 00:00:00","166598" +"2014-12-01 00:00:00","155299" +"2015-01-01 00:00:00","165978" +"2015-02-01 00:00:00","168513" +"2015-03-01 00:00:00","189769" +"2015-04-01 00:00:00","189967" +"2015-05-01 00:00:00","185878" +"2015-06-01 00:00:00","187915" +"2015-07-01 00:00:00","195439" +"2015-08-01 00:00:00","181413" +"2015-09-01 00:00:00","177859" +"2015-10-01 00:00:00","186418" +"2015-11-01 00:00:00","178205" +"2015-12-01 00:00:00","172586" +"2016-01-01 00:00:00","181380" +"2016-02-01 00:00:00","188742" +"2016-03-01 00:00:00","201941" +"2016-04-01 00:00:00","197067" +"2016-05-01 00:00:00","189772" +"2016-06-01 00:00:00","184660" +"2016-07-01 00:00:00","176974" +"2016-08-01 00:00:00","181960" +"2016-09-01 00:00:00","171676" +"2016-10-01 00:00:00","175399" +"2016-11-01 00:00:00","175326" +"2016-12-01 00:00:00","162421" +"2017-01-01 00:00:00","176969" +"2017-02-01 00:00:00","175280" +"2017-03-01 00:00:00","201543" +"2017-04-01 00:00:00","178508" +"2017-05-01 00:00:00","186511" +"2017-06-01 00:00:00","178175" +"2017-07-01 00:00:00","179856" +"2017-08-01 00:00:00","177742" +"2017-09-01 00:00:00","162369" +"2017-10-01 00:00:00","170083" +"2017-11-01 00:00:00","169012" +"2017-12-01 00:00:00","145330" +"2018-01-01 00:00:00","160544" +"2018-02-01 00:00:00","153155" +"2018-03-01 00:00:00","173466" +"2018-04-01 00:00:00","162981" +"2018-05-01 00:00:00","168104" +"2018-06-01 00:00:00","154885" +"2018-07-01 00:00:00","160095" +"2018-08-01 00:00:00","158544" +"2018-09-01 00:00:00","144581" +"2018-10-01 00:00:00","160501" +"2018-11-01 00:00:00","149820" +"2018-12-01 00:00:00","132269" +"2019-01-01 00:00:00","149628" +"2019-02-01 00:00:00","146421" +"2019-03-01 00:00:00","161159" +"2019-04-01 00:00:00","153558" +"2019-05-01 00:00:00","151393" +"2019-06-01 00:00:00","135935" +"2019-07-01 00:00:00","151081" +"2019-08-01 00:00:00","137181" +"2019-09-01 00:00:00","136935" +"2019-10-01 00:00:00","152721" +"2019-11-01 00:00:00","148338" +"2019-12-01 00:00:00","132623" +"2020-01-01 00:00:00","146735" +"2020-02-01 00:00:00","145191" +"2020-03-01 00:00:00","156037" +"2020-04-01 00:00:00","183016" +"2020-05-01 00:00:00","186573" +"2020-06-01 00:00:00","172002" +"2020-07-01 00:00:00","166135" +"2020-08-01 00:00:00","148452" +"2020-09-01 00:00:00","141887" +"2020-10-01 00:00:00","141948" +"2020-11-01 00:00:00","135088" +"2020-12-01 00:00:00","134035" +"2021-01-01 00:00:00","140009" +"2021-02-01 00:00:00","131810" +"2021-03-01 00:00:00","148900" +"2021-04-01 00:00:00","136022" +"2021-05-01 00:00:00","133911" +"2021-06-01 00:00:00","129106" +"2021-07-01 00:00:00","124130" +"2021-08-01 00:00:00","122273" +"2021-09-01 00:00:00","119882" +"2021-10-01 00:00:00","119008" +"2021-11-01 00:00:00","119260" +"2021-12-01 00:00:00","112278" +"2022-01-01 00:00:00","119459" +"2022-02-01 00:00:00","114114" +"2022-03-01 00:00:00","123614" +"2022-04-01 00:00:00","114422" +"2022-05-01 00:00:00","116346" +"2022-06-01 00:00:00","111741" +"2022-07-01 00:00:00","111059" +"2022-08-01 00:00:00","113048" +"2022-09-01 00:00:00","103965" +"2022-10-01 00:00:00","106366" +"2022-11-01 00:00:00","109719" +"2022-12-01 00:00:00","96767" +"2023-01-01 00:00:00","97209" +"2023-02-01 00:00:00","85973" +"2023-03-01 00:00:00","87543" +"2023-04-01 00:00:00","68746" +"2023-05-01 00:00:00","66749" +"2023-06-01 00:00:00","63858" +"2023-07-01 00:00:00","62938" +"2023-08-01 00:00:00","60319" +"2023-09-01 00:00:00","53046" +"2023-10-01 00:00:00","52743" +"2023-11-01 00:00:00","50646" +"2023-12-01 00:00:00","42601" +"2024-01-01 00:00:00","47854" +"2024-02-01 00:00:00","46292" +"2024-03-01 00:00:00","45070" +"2024-04-01 00:00:00","42776" +"2024-05-01 00:00:00","40485" +"2024-06-01 00:00:00","32243" +"2024-07-01 00:00:00","31740" +"2024-08-01 00:00:00","28059" +"2024-09-01 00:00:00","24947" +"2024-10-01 00:00:00","23319" +"2024-11-01 00:00:00","20891" +"2024-12-01 00:00:00","18029" +"2025-01-01 00:00:00","22394" +"2025-02-01 00:00:00","19340" +"2025-03-01 00:00:00","18965" +"2025-04-01 00:00:00","14138" +"2025-05-01 00:00:00","11824" +"2025-06-01 00:00:00","9392" +"2025-07-01 00:00:00","7841" +"2025-08-01 00:00:00","5885" +"2025-09-01 00:00:00","6132" +"2025-10-01 00:00:00","5415" +"2025-11-01 00:00:00","4366" +"2025-12-01 00:00:00","3862" +"2026-01-01 00:00:00","321" \ No newline at end of file diff --git a/content/resources/_index.md b/content/resources/_index.md index 192e39c..8a2654c 100644 --- a/content/resources/_index.md +++ b/content/resources/_index.md @@ -1,7 +1,8 @@ --- title: "Resources" -description: "A collection of useful tools, scripts, and experiments" draft: false --- +# Resources + Welcome to my whiteboard of resources. Here you'll find various tools, scripts, and experiments I've built and wanted to share. diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index d5fb00f..123dde1 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -17,6 +17,9 @@ {{ end }} {{ block "favicon" . }} {{ partial "site-favicon.html" . }} {{ end }} + + + diff --git a/layouts/partials/site-header.html b/layouts/partials/site-header.html index e8b6cc4..72ff1f1 100755 --- a/layouts/partials/site-header.html +++ b/layouts/partials/site-header.html @@ -1,15 +1,3 @@
-
- {{ partial "site-navigation.html" . }} -
- - {{ with .Params.description }} -

- {{ . }} -

- {{ end }} -
-
-
\ No newline at end of file +
{{ partial "site-navigation.html" . }}
+ diff --git a/layouts/resources/list.html b/layouts/resources/list.html index 7b25983..dad3c37 100644 --- a/layouts/resources/list.html +++ b/layouts/resources/list.html @@ -3,16 +3,11 @@
-

{{ .Title }}

-
- {{ .Content }} -
+
{{ .Content }}
- {{ range .Pages }} - {{ .Render "summary" }} - {{ end }} + {{ range .Pages }} {{ .Render "summary" }} {{ end }}
diff --git a/layouts/shortcodes/graph.html b/layouts/shortcodes/graph.html new file mode 100644 index 0000000..f7a48e6 --- /dev/null +++ b/layouts/shortcodes/graph.html @@ -0,0 +1,350 @@ +{{- $id := .Get "id" | default (printf "chart-%d" now.UnixNano) -}} +{{- $type := .Get "type" | default "line" -}} +{{- $title := .Get "title" | default "" -}} +{{- $height := .Get "height" | default "400" -}} +{{- $csvFile := .Get "csv" -}} +{{- $labelCol := .Get "labelColumn" | default "Month" -}} +{{- $dataCol := .Get "dataColumn" | default "Questions" -}} +{{- $dataColumns := .Get "dataColumns" | default "" -}} +{{- $datasetLabels := .Get "datasetLabels" | default "" -}} +{{- $yAxisIDs := .Get "yAxisIDs" | default "" -}} +{{- $dateFormat := .Get "dateFormat" | default "2006-01" -}} +{{- $skipRows := .Get "skipRows" | default 0 | int -}} +{{- $maxRows := .Get "maxRows" | default 0 | int -}} + +{{- $chartData := dict -}} + +{{- if $csvFile -}} + {{/* CSV file mode - supports multiple CSV files */}} + {{- $csvFiles := strings.Split $csvFile "," -}} + {{- $dataColsList := slice -}} + {{- if ne $dataColumns "" -}} + {{- $dataColsList = strings.Split $dataColumns "," -}} + {{- end -}} + {{- $labelsList := slice -}} + {{- if ne $datasetLabels "" -}} + {{- $labelsList = strings.Split $datasetLabels "," -}} + {{- end -}} + {{- $yAxisList := slice -}} + {{- if ne $yAxisIDs "" -}} + {{- $yAxisList = strings.Split $yAxisIDs "," -}} + {{- end -}} + + {{- $labels := slice -}} + {{- $datasets := slice -}} + + {{/* Process each CSV file */}} + {{- range $fileIdx, $csvFileName := $csvFiles -}} + {{- $csvFileName = strings.TrimSpace $csvFileName -}} + {{- $csvResource := $.Page.Resources.GetMatch $csvFileName -}} + {{- if not $csvResource -}} + {{- errorf "CSV file '%s' not found in page bundle for %s. Make sure the file exists in the same directory as index.md" $csvFileName $.Page.File.Path -}} + {{- end -}} + + {{- $csvData := $csvResource | transform.Unmarshal -}} + {{- $data := slice -}} + + {{/* Determine which data column to use */}} + {{- $currentDataCol := $dataCol -}} + {{- if ge $fileIdx (len $dataColsList) -}} + {{- $currentDataCol = $dataCol -}} + {{- else -}} + {{- $currentDataCol = strings.TrimSpace (index $dataColsList $fileIdx) -}} + {{- end -}} + + {{/* Determine dataset label */}} + {{- $datasetLabel := $currentDataCol -}} + {{- if lt $fileIdx (len $labelsList) -}} + {{- $datasetLabel = strings.TrimSpace (index $labelsList $fileIdx) -}} + {{- end -}} + + {{/* Determine Y-axis ID */}} + {{- $yAxisID := "y" -}} + {{- if lt $fileIdx (len $yAxisList) -}} + {{- $yAxisID = strings.TrimSpace (index $yAxisList $fileIdx) -}} + {{- end -}} + + {{/* Process CSV rows */}} + {{- range $idx, $row := $csvData -}} + {{- if gt $idx $skipRows -}} + {{- if or (eq $maxRows 0) (le (len $data) $maxRows) -}} + {{/* Only set labels once from the first CSV file */}} + {{- if eq $fileIdx 0 -}} + {{/* Get label value */}} + {{- $labelRaw := "" -}} + {{- if reflect.IsMap $row -}} + {{- $labelRaw = index $row $labelCol -}} + {{- else -}} + {{/* Handle as slice/array - first column is label */}} + {{- $labelRaw = index $row 0 -}} + {{- end -}} + + {{- $label := $labelRaw -}} + + {{/* Try to parse and format date if it looks like a timestamp */}} + {{- $labelStr := printf "%v" $labelRaw -}} + {{- if strings.Contains $labelStr " 00:00:00" -}} + {{- $parsedTime := time.AsTime $labelStr -}} + {{- $label = $parsedTime.Format $dateFormat -}} + {{- end -}} + + {{- $labels = $labels | append $label -}} + {{- end -}} + + {{/* Get data value */}} + {{- $dataValue := 0 -}} + {{- if reflect.IsMap $row -}} + {{- $dataValue = index $row $currentDataCol | int -}} + {{- else -}} + {{/* Handle as slice/array - second column is data */}} + {{- $dataValue = index $row 1 | int -}} + {{- end -}} + + {{- $data = $data | append $dataValue -}} + {{- end -}} + {{- end -}} + {{- end -}} + + {{/* Add this dataset to the datasets array */}} + {{- $datasets = $datasets | append (dict "label" $datasetLabel "data" $data "yAxisID" $yAxisID) -}} + {{- end -}} + + {{- $chartData = dict "labels" $labels "datasets" $datasets -}} +{{- else -}} + {{/* JSON inline mode */}} + {{- if .Inner -}} + {{- $chartData = .Inner | transform.Unmarshal -}} + {{- else -}} + {{- errorf "Graph shortcode requires either CSV file or inline JSON data" -}} + {{- end -}} +{{- end -}} + +
+ {{- if $title -}} +

{{ $title }}

+ {{- end -}} + +
+ + From 50ea13e1e00408e2009d8fc147bacdd39a248da2 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 15:32:38 +0000 Subject: [PATCH 02/55] Adding total issues csv --- .../total_issues_by_date.csv | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv diff --git a/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv b/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv new file mode 100644 index 0000000..1e75fd3 --- /dev/null +++ b/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv @@ -0,0 +1,196 @@ +Date,Total Issues +2009-10,1 +2009-11,199 +2009-12,88 +2010-01,57 +2010-02,36 +2010-03,42 +2010-04,34 +2010-05,45 +2010-06,41 +2010-07,54 +2010-08,46 +2010-09,41 +2010-10,51 +2010-11,35 +2010-12,35 +2011-01,50 +2011-02,71 +2011-03,42 +2011-04,59 +2011-05,73 +2011-06,70 +2011-07,50 +2011-08,49 +2011-09,46 +2011-10,48 +2011-11,52 +2011-12,60 +2012-01,81 +2012-02,148 +2012-03,112 +2012-04,54 +2012-05,46 +2012-06,38 +2012-07,47 +2012-08,40 +2012-09,63 +2012-10,56 +2012-11,72 +2012-12,62 +2013-01,47 +2013-02,70 +2013-03,70 +2013-04,76 +2013-05,75 +2013-06,93 +2013-07,75 +2013-08,97 +2013-09,90 +2013-10,78 +2013-11,85 +2013-12,98 +2014-01,112 +2014-02,105 +2014-03,152 +2014-04,135 +2014-05,123 +2014-06,141 +2014-07,138 +2014-08,157 +2014-09,211 +2014-10,269 +2014-11,272 +2014-12,348 +2015-01,391 +2015-02,452 +2015-03,495 +2015-04,666 +2015-05,739 +2015-06,875 +2015-07,1024 +2015-08,838 +2015-09,710 +2015-10,839 +2015-11,1287 +2015-12,1222 +2016-01,1326 +2016-02,1576 +2016-03,1490 +2016-04,1404 +2016-05,1456 +2016-06,1502 +2016-07,1647 +2016-08,1765 +2016-09,1796 +2016-10,1542 +2016-11,1473 +2016-12,1465 +2017-01,1551 +2017-02,1725 +2017-03,2055 +2017-04,1847 +2017-05,2063 +2017-06,2057 +2017-07,1805 +2017-08,1973 +2017-09,2051 +2017-10,2115 +2017-11,2510 +2017-12,2353 +2018-01,3100 +2018-02,3097 +2018-03,3790 +2018-04,3662 +2018-05,3875 +2018-06,4247 +2018-07,4377 +2018-08,4741 +2018-09,4459 +2018-10,5292 +2018-11,5458 +2018-12,6048 +2019-01,8605 +2019-02,8408 +2019-03,9698 +2019-04,8991 +2019-05,9687 +2019-06,9013 +2019-07,9963 +2019-08,10381 +2019-09,9459 +2019-10,10923 +2019-11,10365 +2019-12,9743 +2020-01,10240 +2020-02,9782 +2020-03,11336 +2020-04,12978 +2020-05,12955 +2020-06,11921 +2020-07,11547 +2020-08,11006 +2020-09,10252 +2020-10,10560 +2020-11,9827 +2020-12,9509 +2021-01,9788 +2021-02,10228 +2021-03,11503 +2021-04,10537 +2021-05,10048 +2021-06,10273 +2021-07,9494 +2021-08,9496 +2021-09,9584 +2021-10,9652 +2021-11,9500 +2021-12,8836 +2022-01,9202 +2022-02,9612 +2022-03,10661 +2022-04,9016 +2022-05,9552 +2022-06,9207 +2022-07,9633 +2022-08,10136 +2022-09,11260 +2022-10,11668 +2022-11,10974 +2022-12,9605 +2023-01,11124 +2023-02,10993 +2023-03,12906 +2023-04,12436 +2023-05,12798 +2023-06,11041 +2023-07,11009 +2023-08,11467 +2023-09,10751 +2023-10,11316 +2023-11,11278 +2023-12,9598 +2024-01,10656 +2024-02,10658 +2024-03,10949 +2024-04,10735 +2024-05,10996 +2024-06,10195 +2024-07,11183 +2024-08,11632 +2024-09,11180 +2024-10,11319 +2024-11,10850 +2024-12,10091 +2025-01,11259 +2025-02,12612 +2025-03,13429 +2025-04,11680 +2025-05,11367 +2025-06,10888 +2025-07,14019 +2025-08,13127 +2025-09,12489 +2025-10,13904 +2025-11,13730 +2025-12,12652 From 8ff42e5990882ff1f3b0cafd095d0433fa676df5 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 16:25:28 +0000 Subject: [PATCH 03/55] Finished (?) stack overflow blog post --- assets/sass/pages/blog.scss | 18 ++++-- .../the-downfall-of-stackoverflow/index.md | 63 ++++++++++++++++--- layouts/_default/baseof.html | 4 +- layouts/blog/single.html | 3 + static/js/footnote-scroll.js | 32 ++++++++++ 5 files changed, 108 insertions(+), 12 deletions(-) create mode 100644 static/js/footnote-scroll.js diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 4667a44..c156a01 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -4,7 +4,7 @@ position: relative; @include media-down(lg) { - padding: 1rem; + padding: 0rem; } // Main container with CRT monitor styling @@ -21,8 +21,17 @@ position: relative; @include media-down(lg) { - padding: 15px 15px 35px 15px; - border-radius: 8px; + padding: 0px; + border-radius: 0px; + border: 0px; + margin: 0px; + width: 100%; + max-width: 100%; + + &::before, + &::after { + display: none; + } } .blog-header { @@ -108,6 +117,7 @@ .blogs-screen { width: 100%; min-height: 500px; + height: auto; // Allow content to expand background: #000; border-radius: 8px; position: relative; @@ -119,7 +129,7 @@ inset -3px -3px 8px rgba(0, 0, 0, 0.5); @include media-down(lg) { - border-radius: 6px; + border-radius: 0px; min-height: 400px; } diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md index 20e4ae8..f190add 100644 --- a/content/blog/the-downfall-of-stackoverflow/index.md +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -1,12 +1,14 @@ --- title: "The Downfall of StackOverflow" -date: 2026-02-04T13:58:02Z +date: 2026-01-04T13:58:02Z tags: - analysis - ai -draft: true +draft: false --- +_Quick note: This isn't an "AI bad" rant. AI tools are a useful thing to have in your kit. This is just an observational look at what the data shows about StackOverflow's decline. That being said, the sooner the AI bubble bursts the better._ + This post was inspired by a post on [Hacker News](https://news.ycombinator.com/item?id=46482345) that linked to this [StackOverflow data](https://data.stackexchange.com/stackoverflow/query/1926661#graph). My kneejerk reaction was that the rise in AI and its code analysis capabilities have caused the downfall of StackOverflow. We can see a peak after a gradual decline in early 2020 (COVID bedroom coders?) which then returns to a roughly normal level by 2021, before starting a stark decline into obscurity, very much accelerating at the end of 2022. @@ -16,7 +18,7 @@ My kneejerk reaction was that the rise in AI and its code analysis capabilities ## The rise and fall -StackOverflow had a hell of a run. From just 4 questions monthly in July 2008 to over 207,000 in March 2014—that's six years of basically uninterrupted growth. It became _the_ place every developer went when they were stuck. +StackOverflow had a hell of a run. From just 4 questions monthly in July 2008 to over 207,000 in March 2014: six years of basically uninterrupted growth. It became _the_ place every developer went when they were stuck. Then around 2014-2015, it plateaued. About 170,000-190,000 questions per month, which held steady for a few years before starting to slip. By 2019 we're down to around 150,000 per month. Still solid, but the writing was on the wall. @@ -34,7 +36,7 @@ Looking at Google Trends[^1] for AI-related searches, there's a bit of a gap bet dateFormat="2006-01" >}} {{< /graph >}} -Both general AI interest and AI coding searches were basically flat from 2019 through most of 2022. Then in December 2022 it spikes—that's ChatGPT launching[^2]. +Both general AI interest and AI coding searches were basically flat from 2019 through most of 2022. Then in December 2022 it spikes. That's ChatGPT launching[^2]. This is seen conversely in the data on StackOverflow Questions declining at the same point. Zooming in on 2019 onwards makes it clearer: @@ -49,17 +51,50 @@ Zooming in on 2019 onwards makes it clearer: ## The timeline doesn't quite add up -Here's the thing though: StackOverflow's accelerated decline starts in 2021, well before anyone gave a shit about AI coding. From January 2021 (140,009 questions) to December 2022 (96,767 questions), it lost 31% of its traffic while "AI coding" searches sat at baseline. +StackOverflow's real decline starts in 2021, well before anyone gave a shit about AI coding. From January 2021 (140,009 questions) to December 2022 (96,767 questions), it lost 31% of its traffic while "AI coding" searches sat at baseline. The AI coding surge doesn't really kick off until early 2023, then explodes through 2025, peaking in August. But by then StackOverflow was already down to 5,885 questions per month. +## Did the rise of GitHub contribute? + +A few comments[^3] on Hacker News suggested that the rise of GitHub could have been a contributor to the decline of StackOverflow. On the surface that sounds reasonable, a lot of support happens directly on GitHub nowadays, which wasn't really a thing during StackOverflow's peak. + +To check this I grabbed data from the top 100 starred GitHub repositories and tracked issues opened per month. This isn't an ideal metric[^4], but it gives us something to work with. + +{{< graph id="stackoverflow-vs-github-issues" type="line" title="StackOverflow vs GitHub Issues" height="500" + csv="stackoverflow_questions_over_time.csv,total_issues_by_date.csv" + labelColumn="Month" + dataColumns="Questions,Total Issues" + datasetLabels="StackOverflow Questions,GitHub Issues" + yAxisIDs="y,y1" + dateFormat="2006-01" >}} +{{< /graph >}} + +GitHub issues start slow in these specific repositories, but by 2019 we're seeing around 9,000-10,000 issues per month, which plateaus and holds relatively steady through 2025 (hovering between 10,000-14,000). + +The timeline doesn't really match StackOverflow's decline either. GitHub issues hit a high in 2018-2019 (when they jumped to 8,000-10,000 per month), but StackOverflow was still holding steady at 150,000+ questions. The platform only started its real nosedive in 2021-2023, while GitHub issues stayed flat. + +If GitHub issues were the culprit, you'd expect to see an inverse relationship: as issues go up, questions go down. Instead, both coexisted just fine from 2015-2020. GitHub issues plateaued in 2019 and have stayed relatively constant since, even slightly increasing in 2025. Meanwhile, StackOverflow collapsed by 99.8%. + +So no, I don't believe GitHub issues killed StackOverflow. It may have been a contributing factor, with people going elsewhere to find answers. Developers were using both for years without real conflict though. + +## What about toxicity? + +Several comments[^5] brought up StackOverflow's harsh moderation and toxic environment as a major factor. The "marked as duplicate," "this question doesn't belong here," or outright hostile responses to newbies are easier to find than actual answers most of the time. + +Wasn't StackOverflow _always_ like that? The platform had a reputation for being unwelcoming since at least 2012-2013[^6], right during its peak growth years. If toxicity was the primary killer, you'd expect to see the decline start much earlier, not suddenly accelerate in 2021-2023. + +Could it be a contributing factor? Absolutely. If you had a choice between asking a question on StackOverflow (and risking getting dunked on by some pedantic asshole) versus just asking ChatGPT (which never judges you 😘), the choice is obvious. But toxicity alone doesn't explain the timing or the scale of the collapse. + +To actually test this properly you'd need to pull years of StackOverflow comments and run sentiment analysis to see if moderation got worse over time. That's way more effort than I'm putting into this blog post. Maybe one for another day. + ## So what's actually going on? The data suggests AI accelerated something that was already happening. A few things probably contributed: **The saturation effect**: By 2021, StackOverflow had 16+ years of answered questions. How many times can you ask "how do I parse JSON in Python" before every variation is covered? The "just Google it" response became the correct answer because everything _had_ been Googled already. -**The pre-AI decline (2021-2022)**: 31% drop over 18 months while AI coding searches were dead flat. This points to other shifts—better documentation, clearer error messages, frameworks maturing and becoming less footgun-y. Developers were finding answers without needing to ask. +**The pre-AI decline (2021-2022)**: 31% drop over 18 months while AI coding searches were dead flat. This points to other shifts: better documentation, clearer error messages, frameworks maturing and becoming less footgun-y. Developers were finding answers without needing to ask. **The AI acceleration (2023-2025)**: ChatGPT launches November 30, 2022. By March 2023, StackOverflow drops from 123,614 questions to 87,543. AI tools give you instant answers without needing to wade through ten variations of your question that are marked as duplicates and locked. @@ -67,14 +102,28 @@ The data suggests AI accelerated something that was already happening. A few thi ## The bottom line -AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up—content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask? +AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up: content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask? The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By January 2026 we're at 321 questions. That's about the same as August 2008, when it was brand new. Twelve years to build it. Five years to tear it down. And yeah, the last three were almost certainly AI finishing what was already started. +### Footnote + +This is the hardest and longest I have thought about anything in a long time. I was an avid StackOverflow user 10~ years ago, and the graph showing up on Hacker News showing the current state absolutely blew my mind and inspired me to dig a bit deeper. + +I hope you found this interesting. Rock on. + --- [^1]: Google Trends data provides relative search interest rather than absolute numbers, which may not capture the full picture. [^2]: [ChatGPT Launch Announcement](https://openai.com/index/chatgpt/) + +[^3]: [Comment](https://news.ycombinator.com/item?id=46486171), [Comment](https://news.ycombinator.com/item?id=46487601), [Comment](https://news.ycombinator.com/item?id=46482758) + More. + +[^4]: Read: It's bad. There's a couple of massive outlier projects with a LOT more issues raised than others, but that's a topic for another blog. + +[^5]: [Comment](https://news.ycombinator.com/item?id=46482624) + +[^6]: In 2013, Tim Schreiber wrote about ["StackOverlords"](https://timschreiber.com/2013/10/30/beware-the-stackoverlords/) who "ruthlessly wield" their privileges against newcomers. By 2018, StackOverflow officially acknowledged the problem in their blog post ["Stack Overflow Isn't Very Welcoming. It's Time for That to Change."](https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/) diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 123dde1..8aa279c 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -18,8 +18,10 @@ {{ end }} {{ block "favicon" . }} {{ partial "site-favicon.html" . }} {{ end }} - + + {{ if or (findRE "{{<\\s*graph" .RawContent) (findRE "{{%\\s*graph" .RawContent) }} + {{ end }} diff --git a/layouts/blog/single.html b/layouts/blog/single.html index 4a399ba..48ffd6c 100644 --- a/layouts/blog/single.html +++ b/layouts/blog/single.html @@ -46,4 +46,7 @@
{{ partial "elements/lavalamp.html" . }}
+ + + {{ end }} diff --git a/static/js/footnote-scroll.js b/static/js/footnote-scroll.js new file mode 100644 index 0000000..0c58b59 --- /dev/null +++ b/static/js/footnote-scroll.js @@ -0,0 +1,32 @@ +(function() { + // Handle smooth scrolling for all footnote links (both directions) + document.addEventListener('DOMContentLoaded', function() { + // Get all footnote links (both references and backlinks) + const footnoteLinks = document.querySelectorAll('a[href^="#fn:"], a[href^="#fnref:"]'); + + footnoteLinks.forEach(function(link) { + link.addEventListener('click', function(e) { + e.preventDefault(); + + const targetId = this.getAttribute('href').substring(1); + const targetElement = document.getElementById(targetId); + + if (targetElement) { + // Calculate position with offset + const offset = 100; // pixels from top + const elementPosition = targetElement.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - offset; + + // Smooth scroll to position + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth' + }); + + // Update URL hash without jumping + history.pushState(null, null, this.getAttribute('href')); + } + }); + }); + }); +})(); From c0c7ac360de732a2de2f7416c4d3ea961cadd155 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 16:41:53 +0000 Subject: [PATCH 04/55] Fixing maths --- content/blog/the-downfall-of-stackoverflow/index.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md index f190add..ef2b4f1 100644 --- a/content/blog/the-downfall-of-stackoverflow/index.md +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -22,7 +22,7 @@ StackOverflow had a hell of a run. From just 4 questions monthly in July 2008 to Then around 2014-2015, it plateaued. About 170,000-190,000 questions per month, which held steady for a few years before starting to slip. By 2019 we're down to around 150,000 per month. Still solid, but the writing was on the wall. -Then it properly falls off a cliff. January 2023: 97,209 questions. December 2023: 42,601. January 2026: 321. That's a 99.8% drop from the 2020 peak. Three hundred and twenty-one questions. In a month. +Then it properly falls off a cliff. January 2023: 97,209 questions. December 2023: 42,601. December 2025: 3,862. That's a 98.1% drop from the 2020 peak. ## Is it AI? @@ -74,7 +74,7 @@ GitHub issues start slow in these specific repositories, but by 2019 we're seein The timeline doesn't really match StackOverflow's decline either. GitHub issues hit a high in 2018-2019 (when they jumped to 8,000-10,000 per month), but StackOverflow was still holding steady at 150,000+ questions. The platform only started its real nosedive in 2021-2023, while GitHub issues stayed flat. -If GitHub issues were the culprit, you'd expect to see an inverse relationship: as issues go up, questions go down. Instead, both coexisted just fine from 2015-2020. GitHub issues plateaued in 2019 and have stayed relatively constant since, even slightly increasing in 2025. Meanwhile, StackOverflow collapsed by 99.8%. +If GitHub issues were the culprit, you'd expect to see an inverse relationship: as issues go up, questions go down. Instead, both coexisted just fine from 2015-2020. GitHub issues plateaued in 2019 and have stayed relatively constant since, even slightly increasing in 2025. Meanwhile, StackOverflow collapsed by 98.1%. So no, I don't believe GitHub issues killed StackOverflow. It may have been a contributing factor, with people going elsewhere to find answers. Developers were using both for years without real conflict though. @@ -98,13 +98,13 @@ The data suggests AI accelerated something that was already happening. A few thi **The AI acceleration (2023-2025)**: ChatGPT launches November 30, 2022. By March 2023, StackOverflow drops from 123,614 questions to 87,543. AI tools give you instant answers without needing to wade through ten variations of your question that are marked as duplicates and locked. -**The collapse (2025-2026)**: By mid-2025, AI coding tools are just... everywhere. GitHub Copilot, ChatGPT, Claude, all of them baked into every IDE and workflow. The August 2025 peak in "AI coding" searches lines up with StackOverflow hitting 5,885 questions. That's a 96.8% decline from five years earlier. +**The collapse (2025-2026)**: By mid-2025, AI coding tools are just... everywhere. GitHub Copilot, ChatGPT, Claude, all of them baked into every IDE and workflow. The August 2025 peak in "AI coding" searches lines up with StackOverflow hitting 5,885 questions. That's a 96% decline from five years earlier. ## The bottom line AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up: content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask? -The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By January 2026 we're at 321 questions. That's about the same as August 2008, when it was brand new. +The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By December 2025 we're at 3,862 questions. That's roughly the same as September 2008, just a few months after the platform launched. Twelve years to build it. Five years to tear it down. And yeah, the last three were almost certainly AI finishing what was already started. @@ -112,6 +112,8 @@ Twelve years to build it. Five years to tear it down. And yeah, the last three w This is the hardest and longest I have thought about anything in a long time. I was an avid StackOverflow user 10~ years ago, and the graph showing up on Hacker News showing the current state absolutely blew my mind and inspired me to dig a bit deeper. +The CSV data for this post can be found on my [GitHub](https://github.com/unbolt/ritual.sh/tree/main/content/blog/the-downfall-of-stackoverflow). + I hope you found this interesting. Rock on. --- From 7f38596f562661bb75c85f7914a0040a9ebfceea Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 18:33:46 +0000 Subject: [PATCH 05/55] Corrections to blog post --- assets/sass/pages/blog.scss | 8 ++++++++ .../ai-coding-trend.csv | 1 - .../the-downfall-of-stackoverflow/ai-trend.csv | 3 +-- .../blog/the-downfall-of-stackoverflow/index.md | 6 ++++++ .../stackoverflow_questions_after_2019.csv | 3 +-- .../stackoverflow_questions_over_time.csv | 3 +-- .../total_issues_by_date.csv | 15 +++++++++++++++ layouts/shortcodes/graph.html | 2 +- 8 files changed, 33 insertions(+), 8 deletions(-) diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index c156a01..6bf08e6 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -466,6 +466,14 @@ } } + // Horizontal rules + hr { + border: none; + border-top: 2px dashed #0f0; + margin: 2em 0; + opacity: 0.6; + } + .read-more { display: inline-block; margin-top: 10px; diff --git a/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv b/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv index e993347..572fca0 100644 --- a/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv +++ b/content/blog/the-downfall-of-stackoverflow/ai-coding-trend.csv @@ -83,4 +83,3 @@ Month,ai coding: (Worldwide) 2025-10,82 2025-11,92 2025-12,82 -2026-01,41 diff --git a/content/blog/the-downfall-of-stackoverflow/ai-trend.csv b/content/blog/the-downfall-of-stackoverflow/ai-trend.csv index a694ed2..5b65a00 100644 --- a/content/blog/the-downfall-of-stackoverflow/ai-trend.csv +++ b/content/blog/the-downfall-of-stackoverflow/ai-trend.csv @@ -82,5 +82,4 @@ Month,ai: (Worldwide) 2025-09,100 2025-10,92 2025-11,90 -2025-12,80 -2026-01,66 +2025-12,80 \ No newline at end of file diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md index ef2b4f1..7508c5c 100644 --- a/content/blog/the-downfall-of-stackoverflow/index.md +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -118,6 +118,10 @@ I hope you found this interesting. Rock on. --- +### Corrections and Updates + +- 04/01/2025 - Removed the January 2026 data from all graphs as we're only 4 days in and it made it look weird. Also added some 0 value months to the GitHub data at the start so the graphs line up properly visually. + [^1]: Google Trends data provides relative search interest rather than absolute numbers, which may not capture the full picture. [^2]: [ChatGPT Launch Announcement](https://openai.com/index/chatgpt/) @@ -129,3 +133,5 @@ I hope you found this interesting. Rock on. [^5]: [Comment](https://news.ycombinator.com/item?id=46482624) [^6]: In 2013, Tim Schreiber wrote about ["StackOverlords"](https://timschreiber.com/2013/10/30/beware-the-stackoverlords/) who "ruthlessly wield" their privileges against newcomers. By 2018, StackOverflow officially acknowledged the problem in their blog post ["Stack Overflow Isn't Very Welcoming. It's Time for That to Change."](https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/) + + diff --git a/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv index 8f7dc97..75aef63 100644 --- a/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv +++ b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_after_2019.csv @@ -82,5 +82,4 @@ Month,Questions "2025-09-01 00:00:00","6132" "2025-10-01 00:00:00","5415" "2025-11-01 00:00:00","4366" -"2025-12-01 00:00:00","3862" -"2026-01-01 00:00:00","321" \ No newline at end of file +"2025-12-01 00:00:00","3862" \ No newline at end of file diff --git a/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv index edfdc83..5071cfe 100644 --- a/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv +++ b/content/blog/the-downfall-of-stackoverflow/stackoverflow_questions_over_time.csv @@ -208,5 +208,4 @@ Month,Questions "2025-09-01 00:00:00","6132" "2025-10-01 00:00:00","5415" "2025-11-01 00:00:00","4366" -"2025-12-01 00:00:00","3862" -"2026-01-01 00:00:00","321" \ No newline at end of file +"2025-12-01 00:00:00","3862" \ No newline at end of file diff --git a/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv b/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv index 1e75fd3..5275e2e 100644 --- a/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv +++ b/content/blog/the-downfall-of-stackoverflow/total_issues_by_date.csv @@ -1,4 +1,19 @@ Date,Total Issues +2008-07,0 +2008-08,0 +2008-09,0 +2008-10,0 +2008-11,0 +2008-12,0 +2009-01,0 +2009-02,0 +2009-03,0 +2009-04,0 +2009-05,0 +2009-06,0 +2009-07,0 +2009-08,0 +2009-09,0 2009-10,1 2009-11,199 2009-12,88 diff --git a/layouts/shortcodes/graph.html b/layouts/shortcodes/graph.html index f7a48e6..cb4c445 100644 --- a/layouts/shortcodes/graph.html +++ b/layouts/shortcodes/graph.html @@ -220,7 +220,7 @@ maxRotation: 45, minRotation: 45, autoSkip: true, - maxTicksLimit: 20, + maxTicksLimit: 30, padding: 8 }, afterFit: function(scale) { From 8da2a19228ab5a4615515e9c36ff22a06537bda1 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 18:43:54 +0000 Subject: [PATCH 06/55] phrasing tweak --- content/blog/the-downfall-of-stackoverflow/index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md index 7508c5c..15da05a 100644 --- a/content/blog/the-downfall-of-stackoverflow/index.md +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -11,7 +11,9 @@ _Quick note: This isn't an "AI bad" rant. AI tools are a useful thing to have in This post was inspired by a post on [Hacker News](https://news.ycombinator.com/item?id=46482345) that linked to this [StackOverflow data](https://data.stackexchange.com/stackoverflow/query/1926661#graph). -My kneejerk reaction was that the rise in AI and its code analysis capabilities have caused the downfall of StackOverflow. We can see a peak after a gradual decline in early 2020 (COVID bedroom coders?) which then returns to a roughly normal level by 2021, before starting a stark decline into obscurity, very much accelerating at the end of 2022. +My kneejerk reaction to the data was that the rise in AI and its code analysis capabilities have caused the downfall of StackOverflow, but I needed some data to back it up. + +We can see a peak after a gradual decline in early 2020 (COVID bedroom coders?) which then returns to a roughly normal level by 2021, before starting a stark decline into obscurity, very much accelerating at the end of 2022. {{< graph id="stackoverflow-trend" type="line" title="Stack Overflow Questions Over Time" height="500" csv="stackoverflow_questions_over_time.csv" labelColumn="Month" dataColumn="" dateFormat="2006-01" >}} {{< /graph >}} From 430ebf0113b83bf6ebcb746ae1843dececa2af53 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 4 Jan 2026 18:49:55 +0000 Subject: [PATCH 07/55] fixing graph title --- content/blog/the-downfall-of-stackoverflow/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md index 15da05a..d4f97c4 100644 --- a/content/blog/the-downfall-of-stackoverflow/index.md +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -63,7 +63,7 @@ A few comments[^3] on Hacker News suggested that the rise of GitHub could have b To check this I grabbed data from the top 100 starred GitHub repositories and tracked issues opened per month. This isn't an ideal metric[^4], but it gives us something to work with. -{{< graph id="stackoverflow-vs-github-issues" type="line" title="StackOverflow vs GitHub Issues" height="500" +{{< graph id="stackoverflow-vs-github-issues" type="line" title="StackOverflow Questions vs GitHub Issues" height="500" csv="stackoverflow_questions_over_time.csv,total_issues_by_date.csv" labelColumn="Month" dataColumns="Questions,Total Issues" From 66383c9c55f847799f45b4f2f198d6ac84d4b7f1 Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 6 Jan 2026 08:08:19 +0000 Subject: [PATCH 08/55] Bit of a mobile pass on the audio page --- assets/sass/pages/audio.scss | 35 ++++++++++++++++++++++++++++++++--- assets/sass/pages/blog.scss | 8 ++++++++ layouts/_default/baseof.html | 10 +++++++--- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/assets/sass/pages/audio.scss b/assets/sass/pages/audio.scss index 6a1733c..3613893 100644 --- a/assets/sass/pages/audio.scss +++ b/assets/sass/pages/audio.scss @@ -21,7 +21,8 @@ padding-top: 330px; @include media-down(lg) { - padding-top: 400px; + padding-top: 50px; + width: 95%; } .audio-intro { @@ -102,6 +103,13 @@ width: 100%; pointer-events: none; + @include media-down(lg) { + left: auto; + top: auto; + position: relative; + margin-bottom: 50px; + } + .neon-text { @include media-down(lg) { font-size: 5rem; @@ -159,12 +167,12 @@ gap: 20px; @include media-down(lg) { - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(2, 1fr); gap: 15px; } @include media-down(md) { - grid-template-columns: repeat(2, 1fr); + grid-template-columns: 1fr; } } @@ -190,6 +198,27 @@ color: inherit; position: relative; + @include media-down(lg) { + display: grid; + grid-template-columns: auto 1fr; + } + + .track-cover { + @include media-down(lg) { + max-width: 100px; + } + @include media-down(md) { + max-width: 60px; + } + } + + .track-details { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + &:hover { border-color: #00ffff; box-shadow: 0 0 20px rgba(0, 255, 255, 0.3); diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 6bf08e6..1879456 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -37,6 +37,10 @@ .blog-header { text-align: center; padding-bottom: 20px; + + @include media-down(lg) { + font-size: 0.75rem; + } } // Brand on bezel @@ -607,6 +611,10 @@ margin-bottom: 20px; padding-bottom: 15px; border-bottom: 1px solid rgba(0, 255, 0, 0.2); + + @include media-down(lg) { + text-align: right; + } } .blog-post-navigation { diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 8aa279c..5546f2c 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -2,7 +2,6 @@ - {{ block "title" . }}{{ .Site.Title }} {{ with .Params.Title }} | {{ . @@ -19,8 +18,13 @@ }} <!-- Chart.js for graphs - only load if page content contains graph shortcode --> - {{ if or (findRE "{{<\\s*graph" .RawContent) (findRE "{{%\\s*graph" .RawContent) }} - <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" integrity="sha384-9nhczxUqK87bcKHh20fSQcTGD4qq5GhayNYSYWqwBkINBhOfQLg/P5HG5lF1urn4" crossorigin="anonymous"></script> + {{ if or (findRE "{{<\\s*graph" .RawContent) (findRE "{{%\\s*graph" + .RawContent) }} + <script + src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js" + integrity="sha384-9nhczxUqK87bcKHh20fSQcTGD4qq5GhayNYSYWqwBkINBhOfQLg/P5HG5lF1urn4" + crossorigin="anonymous" + ></script> {{ end }} </head> From 989241fc836c4c9daa0015128c3792e697690b4f Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Tue, 6 Jan 2026 08:43:53 +0000 Subject: [PATCH 09/55] More mobile updates --- assets/sass/pages/homepage.scss | 10 ++++++++++ assets/sass/partials/_neon-sign.scss | 27 ++++++++++++++++++++------- assets/sass/partials/_vu-meter.scss | 5 ++++- layouts/index.html | 2 +- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/assets/sass/pages/homepage.scss b/assets/sass/pages/homepage.scss index 4d0fa43..9675016 100644 --- a/assets/sass/pages/homepage.scss +++ b/assets/sass/pages/homepage.scss @@ -7,6 +7,12 @@ padding-top: 200px; + @include media-up(lg) { + position: absolute; + bottom: 28%; + width: 100%; + } + > div { display: grid; align-items: center; @@ -158,6 +164,7 @@ .navigation { position: relative; + z-index: 999; display: grid; @@ -165,6 +172,9 @@ @include media-up(lg) { grid-template-columns: repeat(4, 1fr); + position: absolute; + bottom: 5%; + width: 100%; } > div { diff --git a/assets/sass/partials/_neon-sign.scss b/assets/sass/partials/_neon-sign.scss index 747dcd8..edaf3f4 100644 --- a/assets/sass/partials/_neon-sign.scss +++ b/assets/sass/partials/_neon-sign.scss @@ -1,8 +1,27 @@ +// Function for standard neon glow effect +@function neon-glow($color) { + @return 0 0 5px #fff, 0 0 5px #fff, 0 0 21px $color, 0 0 42px $color, + 0 0 82px $color, 0 0 92px $color, 0 0 142px $color, 0 0 181px $color; +} + /* Neon sign styling */ .neon-sign { text-align: center; line-height: 5rem; - transform: rotate(-10deg); + + .homepage-neon & { + transform: rotate(-5deg); + width: 90%; + margin: auto; + + @include media-up(lg) { + position: absolute; + top: 5%; + left: 60%; + transform: translateX(-50%) rotate(-10deg); + z-index: 1; + } + } @include media-up(lg) { position: absolute; @@ -62,12 +81,6 @@ } } -// Function for standard neon glow effect -@function neon-glow($color) { - @return 0 0 5px #fff, 0 0 5px #fff, 0 0 21px $color, 0 0 42px $color, - 0 0 82px $color, 0 0 92px $color, 0 0 142px $color, 0 0 181px $color; -} - // Mixin for pulse animation - generates keyframes for any color @mixin neon-pulse-animation($name, $color) { @keyframes #{$name} { diff --git a/assets/sass/partials/_vu-meter.scss b/assets/sass/partials/_vu-meter.scss index 05ac633..fb1f578 100644 --- a/assets/sass/partials/_vu-meter.scss +++ b/assets/sass/partials/_vu-meter.scss @@ -1,11 +1,14 @@ /* VU Meter on desk */ .vu-meter { position: absolute; - left: 50%; top: 10px; width: 120px; height: 60px; z-index: 8; + + @include media-up(md) { + left: 50%; + } } .vu-meter-body { diff --git a/layouts/index.html b/layouts/index.html index 109f8ef..3d3a5cb 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -6,7 +6,7 @@ <div class="window hidden-lg-down">{{ partial "elements/window.html" . }}</div> <!-- Neon sign above monitor --> -{{ partial "elements/neon-sign.html" . }} +<div class="homepage-neon">{{ partial "elements/neon-sign.html" . }}</div> <!-- Sticky notes --> <div class="sticky-note note1 hidden-xl-down">fix bugs</div> From 2c84ae31847187871033fc56b8a36694ddc7eec7 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Tue, 6 Jan 2026 08:57:01 +0000 Subject: [PATCH 10/55] More mobile tweaks --- assets/sass/pages/homepage.scss | 2 +- assets/sass/partials/_neon-sign.scss | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/assets/sass/pages/homepage.scss b/assets/sass/pages/homepage.scss index 9675016..fc7b75a 100644 --- a/assets/sass/pages/homepage.scss +++ b/assets/sass/pages/homepage.scss @@ -34,7 +34,7 @@ } @include media-down(lg) { - padding-top: 50px; + padding-top: 130px; grid-template-columns: 1fr 1fr; } diff --git a/assets/sass/partials/_neon-sign.scss b/assets/sass/partials/_neon-sign.scss index edaf3f4..cc14dcd 100644 --- a/assets/sass/partials/_neon-sign.scss +++ b/assets/sass/partials/_neon-sign.scss @@ -11,8 +11,12 @@ .homepage-neon & { transform: rotate(-5deg); - width: 90%; - margin: auto; + margin-left: 50%; + transform: translateX(-50%); + overflow: hidden; + width: 100%; + height: 100vh; + position: absolute; @include media-up(lg) { position: absolute; @@ -20,6 +24,9 @@ left: 60%; transform: translateX(-50%) rotate(-10deg); z-index: 1; + margin-left: auto; + overflow: visible; + width: 50%; } } From c109391073a114b0d55ae7e4d2e4b71604551dd5 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Tue, 6 Jan 2026 09:19:57 +0000 Subject: [PATCH 11/55] Mobile tweaks, prepping post for sunday --- assets/sass/partials/_neon-sign.scss | 2 +- .../2026-01-06-week-3-bit-chilly-out/index.md | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 content/blog/2026-01-06-week-3-bit-chilly-out/index.md diff --git a/assets/sass/partials/_neon-sign.scss b/assets/sass/partials/_neon-sign.scss index cc14dcd..931c36a 100644 --- a/assets/sass/partials/_neon-sign.scss +++ b/assets/sass/partials/_neon-sign.scss @@ -15,7 +15,7 @@ transform: translateX(-50%); overflow: hidden; width: 100%; - height: 100vh; + height: 200px; position: absolute; @include media-up(lg) { diff --git a/content/blog/2026-01-06-week-3-bit-chilly-out/index.md b/content/blog/2026-01-06-week-3-bit-chilly-out/index.md new file mode 100644 index 0000000..0106157 --- /dev/null +++ b/content/blog/2026-01-06-week-3-bit-chilly-out/index.md @@ -0,0 +1,23 @@ +--- +title: "Week 3 - Bit Chilly Out" +date: 2026-01-11 +tags: + - weeknote + - weekly update +draft: true +--- + +- ❄️ It's been pretty cold and we've had the lightest sprinkling of snow. +- 📱 I did a mobile pass on some of the pages on this website, it works a bit better on phones now. + +## Links I Found Interesting + +- [enclose.horse](https://enclose.horse/) - A daily game where you have to enclose a horse in the largest possible field. + +## Music + +## Next Week + +Not necessary + +Until next week! From 80b1d7634afc15ff5b42121026fcc5b1eaf53ec3 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Tue, 6 Jan 2026 09:51:41 +0000 Subject: [PATCH 12/55] Mobile pass on media and script cleanup --- assets/js/lastfm-utils.js | 68 +++++++++++++++ assets/js/pages/audio.js | 46 +---------- assets/js/pages/media.js | 46 ++--------- assets/sass/pages/media.scss | 49 +++++++++-- assets/sass/partials/_neon-sign.scss | 1 + content/media/fackham-hall/cover.jpg | Bin 0 -> 105067 bytes content/media/fackham-hall/index.md | 13 +++ .../cover.jpg | Bin 0 -> 403252 bytes .../index.md | 13 +++ layouts/media/list.html | 78 ++++++++++-------- 10 files changed, 194 insertions(+), 120 deletions(-) create mode 100644 assets/js/lastfm-utils.js create mode 100644 content/media/fackham-hall/cover.jpg create mode 100644 content/media/fackham-hall/index.md create mode 100644 content/media/taskmaster-champion-of-champions-4/cover.jpg create mode 100644 content/media/taskmaster-champion-of-champions-4/index.md diff --git a/assets/js/lastfm-utils.js b/assets/js/lastfm-utils.js new file mode 100644 index 0000000..37f4a2d --- /dev/null +++ b/assets/js/lastfm-utils.js @@ -0,0 +1,68 @@ +// Shared Last.fm utilities - using global namespace pattern +(function(window) { + 'use strict'; + + // Create global LastFmUtils namespace + window.LastFmUtils = { + // Last.fm API configuration + LASTFM_API_URL: "https://ws.audioscrobbler.com/2.0/", + LASTFM_USER: "ritualplays", + LASTFM_API_KEY: "3a4fef48fecc593d25e0f9a40df1fefe", + + // Format time difference + getTimeAgo: function(timestamp) { + const now = Date.now() / 1000; + const diff = now - timestamp; + + if (diff < 60) return "Just now"; + if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; + if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; + if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`; + return `${Math.floor(diff / 604800)}w ago`; + }, + + // Fetch recent tracks from Last.fm + fetchRecentTracks: async function(limit = 10) { + const url = `${this.LASTFM_API_URL}?method=user.getrecenttracks&user=${this.LASTFM_USER}&api_key=${this.LASTFM_API_KEY}&format=json&limit=${limit}`; + + try { + const response = await fetch(url); + if (!response.ok) throw new Error("Failed to fetch tracks"); + + const data = await response.json(); + return data.recenttracks.track; + } catch (error) { + console.error("Error fetching Last.fm data:", error); + return null; + } + }, + + // Filter tracks to remove duplicates of now playing track + // Returns a limited number of unique tracks + filterAndLimitTracks: function(tracks, maxTracks = 5) { + if (!tracks || tracks.length === 0) { + return []; + } + + // Check if first track is now playing + const hasNowPlaying = tracks[0] && tracks[0]["@attr"] && tracks[0]["@attr"].nowplaying; + + if (hasNowPlaying) { + // Show now playing + (maxTracks - 1) latest (excluding duplicates of now playing) + const nowPlayingTrack = tracks[0]; + const nowPlayingId = `${nowPlayingTrack.name}-${nowPlayingTrack.artist["#text"]}`; + + // Get remaining tracks, excluding duplicates of now playing + const remainingTracks = tracks.slice(1).filter(track => { + const trackId = `${track.name}-${track.artist["#text"]}`; + return trackId !== nowPlayingId; + }); + + return [nowPlayingTrack, ...remainingTracks.slice(0, maxTracks - 1)]; + } else { + // No now playing, show maxTracks latest + return tracks.slice(0, maxTracks); + } + } + }; +})(window); diff --git a/assets/js/pages/audio.js b/assets/js/pages/audio.js index 07c36c7..51c2062 100644 --- a/assets/js/pages/audio.js +++ b/assets/js/pages/audio.js @@ -83,27 +83,8 @@ if (document.getElementById("starfield")) { const container = document.getElementById("recent-tracks"); if (!container) return; - const AUDIO_LASTFM_API_URL = "https://ws.audioscrobbler.com/2.0/"; - const AUDIO_LASTFM_USER = "ritualplays"; - const AUDIO_LASTFM_API_KEY = "3a4fef48fecc593d25e0f9a40df1fefe"; const AUDIO_TRACK_LIMIT = 6; // Fetch 6 to have enough after filtering - // Fetch recent tracks from Last.fm for audio page - async function fetchAudioRecentTracks() { - const url = `${AUDIO_LASTFM_API_URL}?method=user.getrecenttracks&user=${AUDIO_LASTFM_USER}&api_key=${AUDIO_LASTFM_API_KEY}&format=json&limit=${AUDIO_TRACK_LIMIT}`; - - try { - const response = await fetch(url); - if (!response.ok) throw new Error("Failed to fetch tracks"); - - const data = await response.json(); - return data.recenttracks.track; - } catch (error) { - console.error("Error fetching Last.fm data:", error); - return null; - } - } - // Render recent tracks for audio page (horizontal layout) function renderRecentTracks(tracks) { if (!tracks || tracks.length === 0) { @@ -114,27 +95,8 @@ if (document.getElementById("starfield")) { container.innerHTML = ""; - // Check if first track is now playing - const hasNowPlaying = tracks[0] && tracks[0]["@attr"] && tracks[0]["@attr"].nowplaying; - - // Filter and limit tracks - let tracksToShow; - if (hasNowPlaying) { - // Show now playing + 4 latest (excluding duplicates of now playing) - const nowPlayingTrack = tracks[0]; - const nowPlayingId = `${nowPlayingTrack.name}-${nowPlayingTrack.artist["#text"]}`; - - // Get remaining tracks, excluding duplicates of now playing - const remainingTracks = tracks.slice(1).filter(track => { - const trackId = `${track.name}-${track.artist["#text"]}`; - return trackId !== nowPlayingId; - }); - - tracksToShow = [nowPlayingTrack, ...remainingTracks.slice(0, 4)]; - } else { - // No now playing, show 5 latest - tracksToShow = tracks.slice(0, 5); - } + // Filter and limit tracks to 5 (excluding duplicates) + const tracksToShow = LastFmUtils.filterAndLimitTracks(tracks, 5); tracksToShow.forEach((track) => { const isNowPlaying = track["@attr"] && track["@attr"].nowplaying; @@ -164,12 +126,12 @@ if (document.getElementById("starfield")) { // Initialize Last.fm feed for audio page async function initAudioRecentTracks() { - const tracks = await fetchAudioRecentTracks(); + const tracks = await LastFmUtils.fetchRecentTracks(AUDIO_TRACK_LIMIT); renderRecentTracks(tracks); // Update every 30 seconds setInterval(async () => { - const updatedTracks = await fetchAudioRecentTracks(); + const updatedTracks = await LastFmUtils.fetchRecentTracks(AUDIO_TRACK_LIMIT); renderRecentTracks(updatedTracks); }, 30000); } diff --git a/assets/js/pages/media.js b/assets/js/pages/media.js index f4548b6..c57c5b1 100644 --- a/assets/js/pages/media.js +++ b/assets/js/pages/media.js @@ -82,39 +82,8 @@ function initMatrixRain() { }); } -// Last.fm API configuration -const LASTFM_API_URL = "https://ws.audioscrobbler.com/2.0/"; -const LASTFM_USER = "ritualplays"; -const LASTFM_API_KEY = "3a4fef48fecc593d25e0f9a40df1fefe"; -const TRACK_LIMIT = 10; - -// Format time difference -function getTimeAgo(timestamp) { - const now = Date.now() / 1000; - const diff = now - timestamp; - - if (diff < 60) return "Just now"; - if (diff < 3600) return `${Math.floor(diff / 60)}m ago`; - if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`; - if (diff < 604800) return `${Math.floor(diff / 86400)}d ago`; - return `${Math.floor(diff / 604800)}w ago`; -} - -// Fetch recent tracks from Last.fm -async function fetchRecentTracks() { - const url = `${LASTFM_API_URL}?method=user.getrecenttracks&user=${LASTFM_USER}&api_key=${LASTFM_API_KEY}&format=json&limit=${TRACK_LIMIT}`; - - try { - const response = await fetch(url); - if (!response.ok) throw new Error("Failed to fetch tracks"); - - const data = await response.json(); - return data.recenttracks.track; - } catch (error) { - console.error("Error fetching Last.fm data:", error); - return null; - } -} +// Media page configuration +const TRACK_LIMIT = 12; // Fetch 12 to have enough after filtering // Render tracks to the DOM function renderTracks(tracks) { @@ -129,7 +98,10 @@ function renderTracks(tracks) { container.innerHTML = ""; - tracks.forEach((track) => { + // Filter and limit tracks to 10 (excluding duplicates of now playing) + const tracksToShow = LastFmUtils.filterAndLimitTracks(tracks, 10); + + tracksToShow.forEach((track) => { const isNowPlaying = track["@attr"] && track["@attr"].nowplaying; const trackElement = document.createElement("a"); trackElement.href = track.url; @@ -143,7 +115,7 @@ function renderTracks(tracks) { // Get timestamp const timestamp = track.date ? track.date.uts : null; - const timeAgo = timestamp ? getTimeAgo(timestamp) : ""; + const timeAgo = timestamp ? LastFmUtils.getTimeAgo(timestamp) : ""; trackElement.innerHTML = ` <div class="track-art ${!hasArt ? "no-art" : ""}"> @@ -162,12 +134,12 @@ function renderTracks(tracks) { // Initialize Last.fm feed async function initLastFmFeed() { - const tracks = await fetchRecentTracks(); + const tracks = await LastFmUtils.fetchRecentTracks(TRACK_LIMIT); renderTracks(tracks); // Update every 30 seconds setInterval(async () => { - const updatedTracks = await fetchRecentTracks(); + const updatedTracks = await LastFmUtils.fetchRecentTracks(TRACK_LIMIT); renderTracks(updatedTracks); }, 30000); } diff --git a/assets/sass/pages/media.scss b/assets/sass/pages/media.scss index 4bcda70..6ef18a0 100644 --- a/assets/sass/pages/media.scss +++ b/assets/sass/pages/media.scss @@ -54,11 +54,19 @@ margin-bottom: 50px; padding-bottom: 20px; border-bottom: 2px solid transparent; - border-image: linear-gradient(90deg, transparent, #ff00ff 20%, #00ffff 50%, #ff00ff 80%, transparent) 1; + border-image: linear-gradient( + 90deg, + transparent, + #ff00ff 20%, + #00ffff 50%, + #ff00ff 80%, + transparent + ) + 1; display: flex; justify-content: center; - pre { + > pre { background: linear-gradient(90deg, #ff00ff, #00ffff, #ff00ff); -webkit-background-clip: text; background-clip: text; @@ -70,7 +78,7 @@ margin: 0; @include media-down(lg) { - font-size: 12px; + font-size: 0.5rem; } } } @@ -98,6 +106,16 @@ gap: 25px; } + > h3 { + display: none; + padding-bottom: 15px; + border-bottom: 2px solid transparent; + border-image: linear-gradient(90deg, #ff00ff, #00ffff) 1; + @include media-down(lg) { + display: block; + } + } + // Subtle divider between items .media-item + .media-item { position: relative; @@ -109,7 +127,13 @@ left: 0; right: 0; height: 1px; - background: linear-gradient(90deg, transparent, #333 20%, #333 80%, transparent); + background: linear-gradient( + 90deg, + transparent, + #333 20%, + #333 80%, + transparent + ); } } } @@ -120,13 +144,16 @@ gap: 25px; padding: 20px; border: 1px solid #333; - background: rgba(0,0,0,0.6); + background: rgba(0, 0, 0, 0.6); border-radius: 8px; transition: all 0.3s ease; @include media-down(lg) { gap: 20px; flex-direction: column; + + display: grid; + grid-template-columns: auto 1fr; } &:hover { @@ -154,9 +181,9 @@ transition: all 0.3s ease; @include media-down(lg) { - width: 100%; - max-width: 300px; - height: 450px; + max-width: 100px; + max-height: 100px; + height: auto; margin: 0 auto; } @@ -394,6 +421,12 @@ display: flex; flex-direction: column; gap: 15px; + + @include media-down(lg) { + .lastfm-track:nth-child(n + 4) { + display: none; + } + } } .lastfm-loading { diff --git a/assets/sass/partials/_neon-sign.scss b/assets/sass/partials/_neon-sign.scss index 931c36a..7787410 100644 --- a/assets/sass/partials/_neon-sign.scss +++ b/assets/sass/partials/_neon-sign.scss @@ -17,6 +17,7 @@ width: 100%; height: 200px; position: absolute; + pointer-events: none; @include media-up(lg) { position: absolute; diff --git a/content/media/fackham-hall/cover.jpg b/content/media/fackham-hall/cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f6207dde5011b7f61d1ae2f575b28cdd132b6bdd GIT binary patch literal 105067 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 z%0A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 e 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ƫ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‹{{X&zr>$q~SE2Jca2TGnu*%+YV?4Gi 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 T)Bck;{rogmcYVjp9>@RL3m8tB literal 0 HcmV?d00001 diff --git a/content/media/fackham-hall/index.md b/content/media/fackham-hall/index.md new file mode 100644 index 0000000..cad11f1 --- /dev/null +++ b/content/media/fackham-hall/index.md @@ -0,0 +1,13 @@ +--- +title: "Fackham Hall" +date: 2025-12-24 +tags: ["film"] # album, film, show, book, game +format: "stream" # stream, cd, vinyl, cassette, dvd, bluray, digital +description: "" +cover: "cover.jpg" +rating: 8 # out of 10 +build: + render: never +--- + +Review here :) diff --git a/content/media/taskmaster-champion-of-champions-4/cover.jpg b/content/media/taskmaster-champion-of-champions-4/cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d40b04d5019a68a978e30de363eb8cd6f9d8b2d6 GIT binary patch literal 403252 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%0A$02=8gh7Rz?5ztM>aEAU=PGuhYbUk#+3wTA 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 w~d(&Ft`9iXXBJ{8>@(FJ~e*h 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%0A@v+IkyN8#I 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 YR8qBERl^BnLaNGQZuwJj)m4fA*~>2jk^lez literal 0 HcmV?d00001 diff --git a/content/media/taskmaster-champion-of-champions-4/index.md b/content/media/taskmaster-champion-of-champions-4/index.md new file mode 100644 index 0000000..56c1efe --- /dev/null +++ b/content/media/taskmaster-champion-of-champions-4/index.md @@ -0,0 +1,13 @@ +--- +title: "Taskmaster - Champion of Champions 4" +date: 2025-12-26 +tags: ["show"] # album, film, show, book, game +format: "stream" # stream, cd, vinyl, cassette, dvd, bluray, digital +description: "" +cover: "cover.jpg" +rating: 9 # out of 10 +build: + render: never +--- + +Review here :) diff --git a/layouts/media/list.html b/layouts/media/list.html index 4f03ef8..af98f68 100644 --- a/layouts/media/list.html +++ b/layouts/media/list.html @@ -14,49 +14,63 @@ ▀██▀ ▀██▄▄▀█▄▄▄▄█▀███▄██▄▀█▄██ ████████▄▀███▀ ▀████ ██ ▀▀▀ -</pre> +</pre + > </div> <div class="media-layout"> <div class="media-list"> + <h3>Recent Media</h3> {{ range .Paginator.Pages }} - <div class="media-item" data-type="{{ index .Params.tags 0 }}"> - <div class="media-cover"> - {{ with .Resources.GetMatch "cover.*" }} - {{ $image := .Resize "320x webp q85" }} - <img src="{{ $image.RelPermalink }}" alt="{{ $.Title }}" loading="lazy" width="{{ $image.Width }}" height="{{ $image.Height }}"> - {{ else }} - <div class="no-cover"> - <span class="cover-placeholder">NO COVER</span> - </div> - {{ end }} - </div> - <div class="media-info"> - <div class="media-meta"> - <div class="media-type">{{ index .Params.tags 0 | upper }}</div> - {{ if .Params.format }} - <div class="media-format">{{ .Params.format | upper }}</div> - {{ end }} - <div class="media-date">{{ .Date.Format "02-01-2006" }}</div> - </div> - <h3 class="media-title">{{ .Title }}</h3> - {{ if .Params.rating }} - <div class="media-rating"> - <span class="rating-value">{{ .Params.rating }}</span> - <span class="rating-max">/10</span> - </div> - {{ end }} + <div class="media-item" data-type="{{ index .Params.tags 0 }}"> + <div class="media-cover"> + {{ with .Resources.GetMatch "cover.*" }} {{ $image := .Resize + "320x webp q85" }} + <img + src="{{ $image.RelPermalink }}" + alt="{{ $.Title }}" + loading="lazy" + width="{{ $image.Width }}" + height="{{ $image.Height }}" + /> + {{ else }} + <div class="no-cover"> + <span class="cover-placeholder">NO COVER</span> </div> + {{ end }} </div> - {{ end }} - - {{ partial "pagination.html" .Paginator }} + <div class="media-info"> + <div class="media-meta"> + <div class="media-type"> + {{ index .Params.tags 0 | upper }} + </div> + {{ if .Params.format }} + <div class="media-format">{{ .Params.format | upper }}</div> + {{ end }} + <div class="media-date">{{ .Date.Format "02-01-2006" }}</div> + </div> + <h3 class="media-title">{{ .Title }}</h3> + {{ if .Params.rating }} + <div class="media-rating"> + <span class="rating-value">{{ .Params.rating }}</span> + <span class="rating-max">/10</span> + </div> + {{ end }} + </div> + </div> + {{ end }} {{ partial "pagination.html" .Paginator }} </div> <div class="lastfm-sidebar"> <div class="lastfm-header"> <h3>Recently Listened</h3> - <a href="https://www.last.fm/user/ritualplays" target="_blank" rel="noopener noreferrer" class="lastfm-profile-link">last.fm →</a> + <a + href="https://www.last.fm/user/ritualplays" + target="_blank" + rel="noopener noreferrer" + class="lastfm-profile-link" + >last.fm →</a + > </div> <div class="lastfm-tracks" id="lastfm-tracks"> <div class="lastfm-loading">Loading tracks...</div> @@ -67,6 +81,4 @@ </div> </div> </div> - -<script src="/js/pages/media.js"></script> {{ end }} From 0041a48bae67dfc947b3192875c2a118c2f94233 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Tue, 6 Jan 2026 14:54:30 +0000 Subject: [PATCH 13/55] Adding contact information, updating about page --- assets/sass/pages/about.scss | 144 ++++++++++++++++++ assets/sass/pages/blog.scss | 136 +++++++++++++++++ assets/sass/pages/resources.scss | 69 ++++++--- assets/sass/partials/_content-screens.scss | 6 +- assets/sass/partials/_global-styles.scss | 4 + assets/sass/partials/_vu-meter.scss | 4 + assets/sass/style.scss | 4 +- .../the-downfall-of-stackoverflow/index.md | 14 +- layouts/about/single.html | 108 ++++++++++++- layouts/blog/single.html | 63 ++++++++ static/publickey.asc | 17 +++ 11 files changed, 526 insertions(+), 43 deletions(-) create mode 100644 static/publickey.asc diff --git a/assets/sass/pages/about.scss b/assets/sass/pages/about.scss index 0156fe9..84c2552 100644 --- a/assets/sass/pages/about.scss +++ b/assets/sass/pages/about.scss @@ -2,6 +2,10 @@ color: white; margin: auto; + .content-screen { + position: relative !important; + } + > .about-content { width: 50%; margin: auto; @@ -14,6 +18,125 @@ grid-column: 1 / -1; } + .about-contact-section { + margin-top: 1rem; + + p { + margin-bottom: 1.5rem; + line-height: 1.6; + } + + .contact-info { + display: flex; + flex-direction: column; + gap: 1rem; + } + + .contact-item { + display: flex; + flex-direction: column; + gap: 0.5rem; + + @media (min-width: 600px) { + flex-direction: row; + align-items: center; + flex-wrap: wrap; + } + } + + .contact-label { + font-weight: bold; + white-space: nowrap; + } + + .info { + color: #0f0; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + text-decoration: none; + border-bottom: 1px dotted rgba(0, 255, 0, 0.5); + transition: all 0.3s ease; + + &:hover { + border-bottom-style: solid; + border-bottom-color: rgba(0, 255, 0, 0.8); + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + } + } + + .pgp-actions { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin-left: 0; + + @media (min-width: 600px) { + margin-left: 0.5rem; + } + } + + .pgp-button { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 4px 10px; + background: rgba(0, 255, 0, 0.1); + border: 1px solid rgba(0, 255, 0, 0.3); + color: #0f0; + text-decoration: none; + font-family: monospace; + font-size: 0.8rem; + cursor: pointer; + transition: all 0.3s ease; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + border-radius: 3px; + + .button-icon { + font-size: 1em; + line-height: 1; + } + + &:hover { + background: rgba(0, 255, 0, 0.2); + border-color: rgba(0, 255, 0, 0.6); + box-shadow: 0 0 10px rgba(0, 255, 0, 0.4); + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + } + + &:active { + transform: translateY(1px); + } + } + + .copy-feedback { + margin-top: 0.5rem; + font-size: 0.85rem; + padding: 6px 10px; + border-radius: 3px; + opacity: 0; + transform: translateY(-10px); + transition: all 0.3s ease; + + &.show { + opacity: 1; + transform: translateY(0); + } + + &.success { + color: #0f0; + background: rgba(0, 255, 0, 0.1); + border: 1px solid rgba(0, 255, 0, 0.3); + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + + &.error { + color: #f00; + background: rgba(255, 0, 0, 0.1); + border: 1px solid rgba(255, 0, 0, 0.3); + text-shadow: 0 0 5px rgba(255, 0, 0, 0.5); + } + } + } + .about-header { position: absolute; left: -80px; @@ -37,6 +160,10 @@ } } + > .about-container { + position: relative; + } + > .info-badges { display: grid; grid-template-columns: 1fr 1fr; @@ -55,6 +182,23 @@ transform: rotate(5deg); } } + + .about-music { + pointer-events: none; + + > .music { + scale: 1.6; + + .ipod-group { + top: 50%; + left: 25%; + } + + .vu-meter { + position: relative; + } + } + } } } diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 1879456..4ed1a18 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -558,6 +558,142 @@ } } + // Contact section styling + .blog-contact-section { + margin-top: 40px; + padding: 20px; + background: rgba(0, 255, 0, 0.05); + border: 1px solid rgba(0, 255, 0, 0.3); + border-radius: 4px; + + @include media-down(lg) { + margin-top: 30px; + padding: 15px; + } + + .contact-title { + font-size: 1.3rem; + margin-bottom: 15px; + color: greenyellow; + text-shadow: 0 0 10px rgba(173, 255, 47, 0.5); + + @include media-down(lg) { + font-size: 1.1rem; + } + } + + .contact-content { + display: flex; + flex-direction: column; + gap: 15px; + } + + .contact-email, + .contact-pgp { + font-size: 0.95rem; + line-height: 1.6; + + @include media-down(lg) { + font-size: 0.9rem; + } + } + + .contact-label { + color: greenyellow; + font-weight: bold; + margin-right: 10px; + text-shadow: 0 0 5px rgba(173, 255, 47, 0.5); + } + + .contact-email a { + color: #0f0; + text-decoration: none; + border-bottom: 1px dotted rgba(0, 255, 0, 0.5); + transition: all 0.3s ease; + + &:hover { + border-bottom-style: solid; + border-bottom-color: rgba(0, 255, 0, 0.8); + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + background: rgba(0, 255, 0, 0.05); + } + } + + .pgp-actions { + display: flex; + gap: 10px; + margin-top: 10px; + flex-wrap: wrap; + } + + .pgp-button { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 8px 15px; + background: rgba(0, 255, 0, 0.1); + border: 1px solid rgba(0, 255, 0, 0.3); + color: #0f0; + text-decoration: none; + font-family: monospace; + font-size: 0.85rem; + cursor: pointer; + transition: all 0.3s ease; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + border-radius: 3px; + + @include media-down(lg) { + font-size: 0.8rem; + padding: 6px 12px; + } + + .button-icon { + font-size: 1.1em; + line-height: 1; + } + + &:hover { + background: rgba(0, 255, 0, 0.2); + border-color: rgba(0, 255, 0, 0.6); + box-shadow: 0 0 10px rgba(0, 255, 0, 0.4); + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + } + + &:active { + transform: translateY(1px); + } + } + + .copy-feedback { + margin-top: 10px; + font-size: 0.85rem; + padding: 8px 12px; + border-radius: 3px; + opacity: 0; + transform: translateY(-10px); + transition: all 0.3s ease; + + &.show { + opacity: 1; + transform: translateY(0); + } + + &.success { + color: #0f0; + background: rgba(0, 255, 0, 0.1); + border: 1px solid rgba(0, 255, 0, 0.3); + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + + &.error { + color: #f00; + background: rgba(255, 0, 0, 0.1); + border: 1px solid rgba(255, 0, 0, 0.3); + text-shadow: 0 0 5px rgba(255, 0, 0, 0.5); + } + } + } + // Blog image styling .blog-img-container { position: relative; diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 3735953..d615573 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -16,7 +16,7 @@ .whiteboard { background: linear-gradient(135deg, #f5f5f0 0%, #e8e8dd 100%); border-radius: 8px; - padding: 3rem 2rem; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1), inset 0 0 100px rgba(0, 0, 0, 0.02); @@ -24,15 +24,27 @@ // Subtle texture overlay &::before { - content: ''; + content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-image: - repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(0, 0, 0, 0.01) 2px, rgba(0, 0, 0, 0.01) 4px), - repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(0, 0, 0, 0.01) 2px, rgba(0, 0, 0, 0.01) 4px); + repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.01) 2px, + rgba(0, 0, 0, 0.01) 4px + ), + repeating-linear-gradient( + 90deg, + transparent, + transparent 2px, + rgba(0, 0, 0, 0.01) 2px, + rgba(0, 0, 0, 0.01) 4px + ); pointer-events: none; border-radius: 8px; } @@ -77,18 +89,20 @@ .resource-pin { position: relative; transform: rotate(0deg); - transition: transform 0.3s ease, box-shadow 0.3s ease; + transition: + transform 0.3s ease, + box-shadow 0.3s ease; // Random slight rotations for pins - &:nth-child(3n+1) { + &:nth-child(3n + 1) { transform: rotate(-1deg); } - &:nth-child(3n+2) { + &:nth-child(3n + 2) { transform: rotate(1deg); } - &:nth-child(3n+3) { + &:nth-child(3n + 3) { transform: rotate(-0.5deg); } @@ -120,7 +134,7 @@ transition: transform 0.3s ease; &::after { - content: ''; + content: ""; position: absolute; top: 50%; left: 50%; @@ -147,19 +161,19 @@ gap: 1rem; // Vary note colors - .resource-pin:nth-child(4n+1) & { + .resource-pin:nth-child(4n + 1) & { background: linear-gradient(135deg, #fef9c3 0%, #fde68a 100%); // Yellow } - .resource-pin:nth-child(4n+2) & { + .resource-pin:nth-child(4n + 2) & { background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); // Blue } - .resource-pin:nth-child(4n+3) & { + .resource-pin:nth-child(4n + 3) & { background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%); // Green } - .resource-pin:nth-child(4n+4) & { + .resource-pin:nth-child(4n + 4) & { background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%); // Pink } } @@ -172,7 +186,13 @@ // Lavalamp icon &.lavalamp { - background: linear-gradient(180deg, transparent 0%, transparent 20%, #9333ea 20%, #7c3aed 100%); + background: linear-gradient( + 180deg, + transparent 0%, + transparent 20%, + #9333ea 20%, + #7c3aed 100% + ); border-radius: 10px 10px 40px 40px; border: 3px solid #6b21a8; position: relative; @@ -180,7 +200,7 @@ // Lamp base &::before { - content: ''; + content: ""; position: absolute; bottom: -8px; left: 50%; @@ -193,7 +213,7 @@ // Lava blobs &::after { - content: ''; + content: ""; position: absolute; top: 30%; left: 30%; @@ -219,7 +239,7 @@ // Equalizer bars &::before, &::after { - content: ''; + content: ""; position: absolute; bottom: 15px; width: 8px; @@ -280,7 +300,8 @@ } @keyframes float-blob { - 0%, 100% { + 0%, + 100% { transform: translateY(0) scale(1); } 50% { @@ -289,7 +310,8 @@ } @keyframes float-blob-2 { - 0%, 100% { + 0%, + 100% { transform: translateY(0) scale(1); } 50% { @@ -298,7 +320,8 @@ } @keyframes equalizer-1 { - 0%, 100% { + 0%, + 100% { height: 35px; } 50% { @@ -307,7 +330,8 @@ } @keyframes equalizer-2 { - 0%, 100% { + 0%, + 100% { height: 25px; } 50% { @@ -316,7 +340,8 @@ } @keyframes equalizer-3 { - 0%, 100% { + 0%, + 100% { height: 30px; } 50% { diff --git a/assets/sass/partials/_content-screens.scss b/assets/sass/partials/_content-screens.scss index 2cf6910..61e57fc 100644 --- a/assets/sass/partials/_content-screens.scss +++ b/assets/sass/partials/_content-screens.scss @@ -6,7 +6,7 @@ aspect-ratio: 300 / 245; background: linear-gradient(145deg, #b8b8b0, #989888); box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.7), + 0 8px 20px #000000b3, inset 0 2px 4px rgba(255, 255, 255, 0.3), inset 0 -2px 4px rgba(0, 0, 0, 0.3); padding: 6px 8px 18px 8px; @@ -20,7 +20,7 @@ &::before { content: " "; display: block; - position: fixed; + position: absolute; top: 6px; left: 8px; bottom: 18px; @@ -44,7 +44,7 @@ &::after { content: " "; display: block; - position: fixed; + position: absolute; top: 6px; left: 8px; bottom: 18px; diff --git a/assets/sass/partials/_global-styles.scss b/assets/sass/partials/_global-styles.scss index 97880d1..cff4408 100644 --- a/assets/sass/partials/_global-styles.scss +++ b/assets/sass/partials/_global-styles.scss @@ -12,6 +12,10 @@ footer[role="contentinfo"] { border-left: 1px solid #0f0; border-top-left-radius: 5px; + > .crt { + position: relative; + } + @include media-down(lg) { display: none; } diff --git a/assets/sass/partials/_vu-meter.scss b/assets/sass/partials/_vu-meter.scss index fb1f578..9d640bd 100644 --- a/assets/sass/partials/_vu-meter.scss +++ b/assets/sass/partials/_vu-meter.scss @@ -43,6 +43,10 @@ justify-content: space-around; height: 100%; gap: 2px; + + &.crt { + position: relative; + } } .vu-bar { diff --git a/assets/sass/style.scss b/assets/sass/style.scss index 56bb0e1..bdde189 100644 --- a/assets/sass/style.scss +++ b/assets/sass/style.scss @@ -103,6 +103,7 @@ body { pointer-events: none; } .crt { + position: absolute; animation: textShadow 1.6s infinite; background: black; color: greenyellow; @@ -813,7 +814,6 @@ body { text-decoration: none !important; } - /* Desk-mounted CRT - on desk left side */ .desk-monitor { bottom: 10%; @@ -1036,8 +1036,6 @@ body { } } - - @keyframes pulse-slow { 0%, 100% { diff --git a/content/blog/the-downfall-of-stackoverflow/index.md b/content/blog/the-downfall-of-stackoverflow/index.md index d4f97c4..95410a3 100644 --- a/content/blog/the-downfall-of-stackoverflow/index.md +++ b/content/blog/the-downfall-of-stackoverflow/index.md @@ -11,7 +11,7 @@ _Quick note: This isn't an "AI bad" rant. AI tools are a useful thing to have in This post was inspired by a post on [Hacker News](https://news.ycombinator.com/item?id=46482345) that linked to this [StackOverflow data](https://data.stackexchange.com/stackoverflow/query/1926661#graph). -My kneejerk reaction to the data was that the rise in AI and its code analysis capabilities have caused the downfall of StackOverflow, but I needed some data to back it up. +My kneejerk reaction to the data was that the rise in AI and its code analysis capabilities have caused the downfall of StackOverflow, but I needed some data to back it up. We can see a peak after a gradual decline in early 2020 (COVID bedroom coders?) which then returns to a roughly normal level by 2021, before starting a stark decline into obscurity, very much accelerating at the end of 2022. @@ -55,7 +55,7 @@ Zooming in on 2019 onwards makes it clearer: StackOverflow's real decline starts in 2021, well before anyone gave a shit about AI coding. From January 2021 (140,009 questions) to December 2022 (96,767 questions), it lost 31% of its traffic while "AI coding" searches sat at baseline. -The AI coding surge doesn't really kick off until early 2023, then explodes through 2025, peaking in August. But by then StackOverflow was already down to 5,885 questions per month. +The AI coding surge doesn't really kick off until early 2023, then explodes through 2025, peaking in August. By then StackOverflow was already down to 5,885 questions per month. ## Did the rise of GitHub contribute? @@ -90,7 +90,7 @@ Could it be a contributing factor? Absolutely. If you had a choice between askin To actually test this properly you'd need to pull years of StackOverflow comments and run sentiment analysis to see if moderation got worse over time. That's way more effort than I'm putting into this blog post. Maybe one for another day. -## So what's actually going on? +## So what's was the actual cause? The data suggests AI accelerated something that was already happening. A few things probably contributed: @@ -102,13 +102,13 @@ The data suggests AI accelerated something that was already happening. A few thi **The collapse (2025-2026)**: By mid-2025, AI coding tools are just... everywhere. GitHub Copilot, ChatGPT, Claude, all of them baked into every IDE and workflow. The August 2025 peak in "AI coding" searches lines up with StackOverflow hitting 5,885 questions. That's a 96% decline from five years earlier. -## The bottom line +## Summary AI didn't kill StackOverflow, but it's definitely finishing the job. The platform was already bleeding out when ChatGPT showed up: content saturation, better tooling, the ecosystem maturing. But AI coding tools changed the game completely. Why search through old forum posts when you can just ask? The inverse relationship is stark: as AI coding interest hits its peak in 2025, StackOverflow craters. By December 2025 we're at 3,862 questions. That's roughly the same as September 2008, just a few months after the platform launched. -Twelve years to build it. Five years to tear it down. And yeah, the last three were almost certainly AI finishing what was already started. +Twelve years to build it. Five years to watch it end. The last three were almost certainly AI finishing what was already started. ### Footnote @@ -116,7 +116,7 @@ This is the hardest and longest I have thought about anything in a long time. I The CSV data for this post can be found on my [GitHub](https://github.com/unbolt/ritual.sh/tree/main/content/blog/the-downfall-of-stackoverflow). -I hope you found this interesting. Rock on. +I hope you found this interesting. Please get in touch if you have any further insights. --- @@ -135,5 +135,3 @@ I hope you found this interesting. Rock on. [^5]: [Comment](https://news.ycombinator.com/item?id=46482624) [^6]: In 2013, Tim Schreiber wrote about ["StackOverlords"](https://timschreiber.com/2013/10/30/beware-the-stackoverlords/) who "ruthlessly wield" their privileges against newcomers. By 2018, StackOverflow officially acknowledged the problem in their blog post ["Stack Overflow Isn't Very Welcoming. It's Time for That to Change."](https://stackoverflow.blog/2018/04/26/stack-overflow-isnt-very-welcoming-its-time-for-that-to-change/) - - diff --git a/layouts/about/single.html b/layouts/about/single.html index 8f8061d..72dc3dd 100644 --- a/layouts/about/single.html +++ b/layouts/about/single.html @@ -1,17 +1,15 @@ -{{ define "header" }}{{ partial "page-header.html" . }}{{ end }} {{ define -"main" }} +{{ define "main" }} <article class="about-page"> <div class="about-content"> <div class="content-screen"> <div class="about-header">{{ partial "elements/lavalamp.html" . }}</div> <div class="screen-display crt no-scroll"> - > about -v <br /> + > about -s <br /> <span>Name</span> / <span class="info">Dan (He/Him)</span><br /> <span>Age</span> / <span class="info">40-something</span><br /> <span>Location</span> / <span class="info">UK 🇬🇧</span><br /><br /> <span>Interests</span> / - <span class="info"> - Programming. Music. Movies. Tech. Photography. </span + <span class="info"> Programming. Music. Movies. Tech. Photography.</span ><br /> <span class="cursor-blink">_</span> </div> @@ -23,6 +21,16 @@ partial "elements/lcd-screen.html" (dict "text" .) }} {{ end }} </div> + <div class="wide-item"> + <div class="content-screen"> + <div class="screen-display crt"> + > cat manifesto + + <span class="cursor-blink">_</span> + </div> + </div> + </div> + <div class="wide-item manifesto-container"> <div class="content-screen"> <div class="screen-display crt"> @@ -105,10 +113,96 @@ ><br /> Camera / <span class="info">Fuji X-T5</span><br /> Audio / <span class="info">Hiby R4 EVA, Fiio FT-1</span> <br /><br /> - More info coming soon... - <br /><span class="cursor-blink">_</span> + Check out my <a href="/now">/now</a> page to see more hardware and + software information. <br /><span class="cursor-blink">_</span> + </div> + </div> + + <div class="about-music hidden-xxl-down"> + <div class="music"> + {{ partial "elements/ipod.html" . }} + + <div class="">{{ partial "elements/vu-meter.html" . }}</div> + </div> + </div> + + <div class="content-screen wide-item"> + <div class="screen-display crt"> + > cat contact_info<br /> + <div class="about-contact-section"> + <p> + If you have any comments, questions, corrections, or just fancy + saying "hello" please feel free to get in touch. + </p> + <p> + I am still debating with myself if I want to put more effort into a + mastodon/bluesky type thing, so for now email is the best way to + talk to me. PGP is available if that's your kind of thing. + </p> + <div class="contact-info"> + <div class="contact-item"> + <span class="contact-label">Email</span> / + <a href="mailto:dan@ritual.sh" class="info">dan@ritual.sh</a> + </div> + <div class="contact-item"> + <span class="contact-label">PGP Public Key</span> / + <div class="pgp-actions"> + <a href="/publickey.asc" download class="pgp-button"> + <span class="button-icon">↓</span> Download + </a> + <button id="copy-pgp-key-about" class="pgp-button"> + <span class="button-icon">⎘</span> Copy to Clipboard + </button> + </div> + </div> + <div id="copy-feedback-about" class="copy-feedback"></div> + </div> + </div> + <pre> +| .-. +| / \ .-. +| / \ / \ .-. .-. _ _ ++--/-------\-----/-----\-----/---\---/---\---/-\-/-\/\/--- +| / \ / \ / '-' '-' +|/ '-' '-' + + </pre> + <span class="cursor-blink">_</span> </div> </div> </div> </article> + +<script> + document.addEventListener("DOMContentLoaded", function () { + const copyButton = document.getElementById("copy-pgp-key-about"); + const feedback = document.getElementById("copy-feedback-about"); + + if (copyButton) { + copyButton.addEventListener("click", async function () { + try { + const response = await fetch("/publickey.asc"); + const pgpKey = await response.text(); + + await navigator.clipboard.writeText(pgpKey); + + feedback.textContent = "PGP key copied to clipboard!"; + feedback.classList.add("show", "success"); + + setTimeout(() => { + feedback.classList.remove("show"); + }, 3000); + } catch (err) { + feedback.textContent = "Failed to copy key"; + feedback.classList.add("show", "error"); + + setTimeout(() => { + feedback.classList.remove("show"); + }, 3000); + } + }); + } + }); +</script> + {{ end }} diff --git a/layouts/blog/single.html b/layouts/blog/single.html index 48ffd6c..1a9cec6 100644 --- a/layouts/blog/single.html +++ b/layouts/blog/single.html @@ -23,6 +23,37 @@ <div class="blog-summary">{{ .Content }}</div> </article> + <section class="blog-contact-section"> + <h2 class="contact-title">Contact</h2> + <div class="contact-content"> + <p> + If you found this interesting, have any comments, questions, + corrections, or just fancy saying "hello" please feel free to get + in touch. + </p> + <div class="contact-email"> + <span class="contact-label">Email:</span> + <a href="mailto:dan@ritual.sh">dan@ritual.sh</a> + </div> + <div class="contact-pgp"> + <span class="contact-label">PGP Public Key:</span> + <div class="pgp-actions"> + <a + href="/publickey.asc" + download + class="pgp-button download-key" + > + <span class="button-icon">↓</span> Download + </a> + <button id="copy-pgp-key" class="pgp-button copy-key"> + <span class="button-icon">⎘</span> Copy to Clipboard + </button> + </div> + <div id="copy-feedback" class="copy-feedback"></div> + </div> + </div> + </section> + <nav class="blog-post-navigation"> <div class="post-nav-links"> {{ with .PrevInSection }} @@ -49,4 +80,36 @@ <script src="/js/footnote-scroll.js"></script> +<script> + document.addEventListener("DOMContentLoaded", function () { + const copyButton = document.getElementById("copy-pgp-key"); + const feedback = document.getElementById("copy-feedback"); + + if (copyButton) { + copyButton.addEventListener("click", async function () { + try { + const response = await fetch("/publickey.asc"); + const pgpKey = await response.text(); + + await navigator.clipboard.writeText(pgpKey); + + feedback.textContent = "PGP key copied to clipboard!"; + feedback.classList.add("show", "success"); + + setTimeout(() => { + feedback.classList.remove("show"); + }, 3000); + } catch (err) { + feedback.textContent = "Failed to copy key"; + feedback.classList.add("show", "error"); + + setTimeout(() => { + feedback.classList.remove("show"); + }, 3000); + } + }); + } + }); +</script> + {{ end }} diff --git a/static/publickey.asc b/static/publickey.asc new file mode 100644 index 0000000..2f54b8a --- /dev/null +++ b/static/publickey.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEaVzgdxYJKwYBBAHaRw8BAQdADvLcBAhz3La0tovwPlJ2Z5uKHufb9MS9 +6CGKBIgssOfNHWRhbkByaXR1YWwuc2ggPGRhbkByaXR1YWwuc2g+wsARBBMW +CgCDBYJpXOB3AwsJBwkQhk08tBmPAUJFFAAAAAAAHAAgc2FsdEBub3RhdGlv +bnMub3BlbnBncGpzLm9yZ2lm/k+MdxoNH3+THDnkaLgtXLiV1AuTGyJ0ZlAe +4ngSAxUKCAQWAAIBAhkBApsDAh4BFiEEFv8cu2aJ5iyvXhJ0hk08tBmPAUIA +ABB0AP0ZvUva/yf6ZR8T7Zwp8+RtqyYfiDlJbpqt3hEGALw0EgD6AxO3O4tm +f2IguB2kuUfrH223xGEzIOHYz1Ciwt6haAPOOARpXOB3EgorBgEEAZdVAQUB +AQdAjllaNe/Z1No5rRxVz6SBsSN4o2xDHPWb0PnGxZAsT3EDAQgHwr4EGBYK +AHAFgmlc4HcJEIZNPLQZjwFCRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9w +ZW5wZ3Bqcy5vcmfxjGz++fxlZxuZLgSTOTjWRrtwvPuLEFJSN1VxonqqcQKb +DBYhBBb/HLtmieYsr14SdIZNPLQZjwFCAABdiAD8Cz5dxfHSm1mBwQ0jKjZd +sktUeaa3Ksw2NsMFU3sbnoIBAMBoAaQVq+q5RoLn1ZOT/DIeDU+1o5HVAL0k +sb/b4NYN +=EIiw +-----END PGP PUBLIC KEY BLOCK----- From fd015bd52c0793b1abe5e9f052c2d10b83bd69ef Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Tue, 6 Jan 2026 15:04:31 +0000 Subject: [PATCH 14/55] Writing some more about text --- layouts/about/single.html | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/layouts/about/single.html b/layouts/about/single.html index 72dc3dd..8f52f8c 100644 --- a/layouts/about/single.html +++ b/layouts/about/single.html @@ -24,8 +24,45 @@ <div class="wide-item"> <div class="content-screen"> <div class="screen-display crt"> - > cat manifesto + > about -v + <br /><br /> + <p> + <span>Intro</span> / + <span class="info"> + How do you do? Welcome to my little corner of the internet. I've + been building websites since the original IndieWeb, so am happy to + see the movement taking off and people embracing building and + curating their own websites. This is my virtual garden, you can + read more about why of it all in my "manifesto" below.</span + ><br /> + </p> + <p> + <span>What's here</span> / + <span class="info"> + I'm working on creating pages on my varied interests, so far you + can find a section on the things I have been + <a href="/audio"> listening to and enjoying</a>, and what I have + been listening to them on. There's also a + <a href="/blog">blog(gish)</a> where I am going to write whatever + comes to mind, and theoretically a brief weekly update*.<br /><br /> + I am working on a resources section to share some of my CSS + artwork and various random scripts with the world. Please look + foward to it. + <br /><br /> + <em + >* I have never kept up with a blog before, don't expect + much.</em + ></span + ><br /> + </p> + <p> + <span>Click around</span> / + <span class="info"> + It's probably best if you just have a little click around for + yourself. Good luck.</span + ><br /> + </p> <span class="cursor-blink">_</span> </div> </div> From a3b9bd26804aad931ac0eac9e0ba41c0c7cf02b1 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Wed, 7 Jan 2026 14:32:06 +0000 Subject: [PATCH 15/55] Loads of stuff --- assets/js/code-copy.js | 70 + assets/js/lastfm-stats.js | 260 +++ assets/js/pgp-copy.js | 33 + assets/sass/pages/blog.scss | 2 +- assets/sass/pages/resources.scss | 1776 +++++++++++++++++++-- content/resources/_index.md | 4 +- content/resources/lastfm-stats/index.md | 123 +- content/resources/lavalamp/index.md | 2 +- lastfm-week.sh | 17 +- layouts/blog/single.html | 63 +- layouts/partials/contact-section.html | 30 + layouts/resources/list.html | 15 +- layouts/resources/single.html | 12 +- layouts/shortcodes/lastfm-stats-form.html | 51 + static/lastfm-week.sh | 60 + 15 files changed, 2310 insertions(+), 208 deletions(-) create mode 100644 assets/js/code-copy.js create mode 100644 assets/js/lastfm-stats.js create mode 100644 assets/js/pgp-copy.js create mode 100644 layouts/partials/contact-section.html create mode 100644 layouts/shortcodes/lastfm-stats-form.html create mode 100755 static/lastfm-week.sh diff --git a/assets/js/code-copy.js b/assets/js/code-copy.js new file mode 100644 index 0000000..f55f123 --- /dev/null +++ b/assets/js/code-copy.js @@ -0,0 +1,70 @@ +// Add copy buttons to all code blocks +document.addEventListener("DOMContentLoaded", function () { + // Find all <pre> elements that contain <code> + const codeBlocks = document.querySelectorAll("pre code"); + + codeBlocks.forEach((codeBlock) => { + const pre = codeBlock.parentElement; + + // Create wrapper for positioning + const wrapper = document.createElement("div"); + wrapper.style.position = "relative"; + + // Wrap the pre element + pre.parentNode.insertBefore(wrapper, pre); + wrapper.appendChild(pre); + + // Create copy button + const copyButton = document.createElement("button"); + copyButton.className = "code-copy-btn"; + copyButton.innerHTML = ` + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> + <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect> + <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path> + </svg> + `; + copyButton.setAttribute("aria-label", "Copy code to clipboard"); + + // Add click handler + copyButton.addEventListener("click", async () => { + const code = codeBlock.textContent; + + try { + await navigator.clipboard.writeText(code); + + // Show success feedback + copyButton.classList.add("copied"); + + // Reset after 2 seconds + setTimeout(() => { + copyButton.classList.remove("copied"); + }, 2000); + } catch (err) { + console.error("Failed to copy code:", err); + + // Fallback for older browsers + const textArea = document.createElement("textarea"); + textArea.value = code; + textArea.style.position = "fixed"; + textArea.style.opacity = "0"; + document.body.appendChild(textArea); + textArea.select(); + + try { + document.execCommand("copy"); + copyButton.classList.add("copied"); + + setTimeout(() => { + copyButton.classList.remove("copied"); + }, 2000); + } catch (err2) { + console.error("Fallback copy failed:", err2); + } + + document.body.removeChild(textArea); + } + }); + + wrapper.appendChild(copyButton); + }); +}); diff --git a/assets/js/lastfm-stats.js b/assets/js/lastfm-stats.js new file mode 100644 index 0000000..92b9192 --- /dev/null +++ b/assets/js/lastfm-stats.js @@ -0,0 +1,260 @@ +// Last.fm Stats Interactive Module +(function () { + "use strict"; + + const LASTFM_API_URL = "https://ws.audioscrobbler.com/2.0/"; + const LASTFM_API_KEY = "3a4fef48fecc593d25e0f9a40df1fefe"; + + // Store current stats for export + let currentStats = { + artists: [], + totalTracks: 0, + period: "", + username: "", + }; + + // Calculate timestamps based on period + function getTimestamps(period) { + const now = Math.floor(Date.now() / 1000); + let from; + + if (period === "7day") { + from = now - 7 * 24 * 60 * 60; // 7 days + } else if (period === "1month") { + from = now - 30 * 24 * 60 * 60; // 30 days + } + + return { from, to: now }; + } + + // Fetch top artists for the specified period + async function fetchTopArtists(username, period) { + const url = `${LASTFM_API_URL}?method=user.gettopartists&user=${username}&api_key=${LASTFM_API_KEY}&format=json&period=${period}&limit=5`; + + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch top artists: ${response.statusText}`); + } + + const data = await response.json(); + + // Check for Last.fm API errors + if (data.error) { + throw new Error(data.message || "Last.fm API error"); + } + + return data.topartists?.artist || []; + } + + // Fetch recent tracks to count total scrobbles in period + async function fetchTrackCount(username, period) { + const { from, to } = getTimestamps(period); + const url = `${LASTFM_API_URL}?method=user.getrecenttracks&user=${username}&api_key=${LASTFM_API_KEY}&format=json&from=${from}&to=${to}&limit=1`; + + const response = await fetch(url); + if (!response.ok) { + throw new Error(`Failed to fetch track count: ${response.statusText}`); + } + + const data = await response.json(); + + // Check for Last.fm API errors + if (data.error) { + throw new Error(data.message || "Last.fm API error"); + } + + return data.recenttracks?.["@attr"]?.total || 0; + } + + // Generate markdown format + function generateMarkdown() { + const periodText = + currentStats.period === "7day" ? "Past Week" : "Past Month"; + let markdown = `## Last.fm Stats - ${periodText}\n\n`; + markdown += `**Total Tracks:** ${currentStats.totalTracks}\n\n`; + markdown += `**Top 5 Artists:**\n\n`; + + currentStats.artists.forEach((artist) => { + markdown += `- [${artist.name}](${artist.url}) - ${artist.playcount} plays\n`; + }); + + return markdown; + } + + // Generate plain text format + function generatePlainText() { + const periodText = + currentStats.period === "7day" ? "Past Week" : "Past Month"; + let text = `Last.fm Stats - ${periodText}\n\n`; + text += `Total Tracks: ${currentStats.totalTracks}\n\n`; + text += `Top 5 Artists:\n\n`; + + currentStats.artists.forEach((artist) => { + text += `- ${artist.name} - ${artist.playcount} plays\n`; + }); + + return text; + } + + // Copy to clipboard + async function copyToClipboard(text, button) { + try { + await navigator.clipboard.writeText(text); + const originalText = button.textContent; + button.textContent = "Copied!"; + button.classList.add("copied"); + + setTimeout(() => { + button.textContent = originalText; + button.classList.remove("copied"); + }, 2000); + } catch (err) { + console.error("Failed to copy:", err); + alert("Failed to copy to clipboard"); + } + } + + // Display the stats + function displayStats(artists, totalTracks, period, username) { + const artistsList = document.getElementById("top-artists"); + const totalTracksEl = document.getElementById("total-tracks"); + + // Store current stats for export + currentStats = { artists, totalTracks, period, username }; + + // Update total tracks + totalTracksEl.textContent = totalTracks; + + // Clear and populate artists list + artistsList.innerHTML = ""; + + if (artists.length === 0) { + artistsList.innerHTML = "<li>No artists found for this period</li>"; + return; + } + + artists.forEach((artist) => { + const li = document.createElement("li"); + li.innerHTML = `<a href="${artist.url}" target="_blank">${artist.name}</a> - ${artist.playcount} plays`; + artistsList.appendChild(li); + }); + + // Show export buttons + const exportButtons = document.getElementById("export-buttons"); + if (exportButtons) { + exportButtons.style.display = "flex"; + } + } + + // Show/hide UI elements + function setLoadingState(isLoading) { + const loading = document.getElementById("stats-loading"); + const content = document.getElementById("stats-content"); + const error = document.getElementById("stats-error"); + const results = document.getElementById("stats-results"); + + results.style.display = "block"; + + if (isLoading) { + loading.style.display = "block"; + content.style.display = "none"; + error.style.display = "none"; + } else { + loading.style.display = "none"; + } + } + + function showError(message) { + const error = document.getElementById("stats-error"); + const errorMessage = document.getElementById("error-message"); + const content = document.getElementById("stats-content"); + + error.style.display = "block"; + content.style.display = "none"; + errorMessage.textContent = message; + } + + function showContent() { + const error = document.getElementById("stats-error"); + const content = document.getElementById("stats-content"); + + error.style.display = "none"; + content.style.display = "block"; + } + + // Main fetch function + async function fetchStats() { + const username = document.getElementById("lastfm-username").value.trim(); + const period = document.getElementById("time-period").value; + + if (!username) { + showError("Please enter a Last.fm username"); + return; + } + + setLoadingState(true); + + try { + // Fetch both stats in parallel + const [artists, totalTracks] = await Promise.all([ + fetchTopArtists(username, period), + fetchTrackCount(username, period), + ]); + + displayStats(artists, totalTracks, period, username); + showContent(); + } catch (error) { + console.error("Error fetching Last.fm stats:", error); + showError( + error.message || + "Failed to fetch stats. Please check the username and try again.", + ); + } finally { + setLoadingState(false); + } + } + + // Initialize when DOM is ready + function init() { + const fetchButton = document.getElementById("fetch-stats"); + const usernameInput = document.getElementById("lastfm-username"); + const copyMarkdownBtn = document.getElementById("copy-markdown"); + const copyPlainTextBtn = document.getElementById("copy-plaintext"); + + if (!fetchButton || !usernameInput) { + return; // Not on the stats page + } + + // Fetch stats on button click + fetchButton.addEventListener("click", fetchStats); + + // Also fetch on Enter key in username input + usernameInput.addEventListener("keypress", (e) => { + if (e.key === "Enter") { + fetchStats(); + } + }); + + // Copy buttons + if (copyMarkdownBtn) { + copyMarkdownBtn.addEventListener("click", () => { + const markdown = generateMarkdown(); + copyToClipboard(markdown, copyMarkdownBtn); + }); + } + + if (copyPlainTextBtn) { + copyPlainTextBtn.addEventListener("click", () => { + const plainText = generatePlainText(); + copyToClipboard(plainText, copyPlainTextBtn); + }); + } + } + + // Run init when DOM is loaded + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); diff --git a/assets/js/pgp-copy.js b/assets/js/pgp-copy.js new file mode 100644 index 0000000..843662f --- /dev/null +++ b/assets/js/pgp-copy.js @@ -0,0 +1,33 @@ +// PGP Key copy functionality +document.addEventListener('DOMContentLoaded', function() { + const copyButtons = document.querySelectorAll('.pgp-copy-trigger'); + + copyButtons.forEach(button => { + button.addEventListener('click', async function() { + const feedback = button.closest('.contact-pgp').querySelector('.pgp-copy-feedback'); + + try { + const response = await fetch('/publickey.asc'); + const pgpKey = await response.text(); + + await navigator.clipboard.writeText(pgpKey); + + feedback.textContent = 'PGP key copied to clipboard!'; + feedback.classList.add('show', 'success'); + + setTimeout(() => { + feedback.classList.remove('show'); + }, 3000); + } catch (err) { + console.error('Failed to copy PGP key:', err); + + feedback.textContent = 'Failed to copy key'; + feedback.classList.add('show', 'error'); + + setTimeout(() => { + feedback.classList.remove('show'); + }, 3000); + } + }); + }); +}); diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 4ed1a18..0d2d9a2 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -559,7 +559,7 @@ } // Contact section styling - .blog-contact-section { + .contact-section { margin-top: 40px; padding: 20px; background: rgba(0, 255, 0, 0.05); diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index d615573..fd17440 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -1,8 +1,80 @@ -// Resources Whiteboard Page -.resources-page { +.test-chamber-grid { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + + background-image: + linear-gradient(rgba(58, 58, 58, 0.6) 2px, transparent 2px), + linear-gradient(90deg, rgba(58, 58, 58, 0.6) 2px, transparent 2px), + radial-gradient( + ellipse 80% 20% at 50% 100%, + rgba(200, 220, 255, 0.25) 0%, + transparent 50% + ), + linear-gradient(180deg, #6a6d75 0%, #7a7d85 50%, #3f4146 100%); + background-size: + 150px 200px, + 150px 200px, + 100% 100%, + 100% 100%; + background-position: + 0 0, + 0 0, + 0 0, + 0 0; + + &::before { + content: "TEST 19"; + position: absolute; + top: 55%; + right: 8%; + transform: translateY(-50%); + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-size: 6rem; + font-weight: 700; + color: rgba(255, 255, 255, 0.3); + letter-spacing: 0.2em; + text-transform: uppercase; + pointer-events: none; + z-index: 1; + writing-mode: vertical-rl; + text-orientation: mixed; + + @include media-up(md) { + font-size: 7rem; + } + } + + &::after { + position: absolute; + z-index: 12; + top: -180px; + left: -200px; + width: 600px; + height: 600px; + background: radial-gradient( + ellipse 60% 70% at 40% 45%, + rgba(0, 0, 0, 0.65) 0%, + rgba(0, 0, 0, 0.4) 30%, + rgba(0, 0, 0, 0.3) 50%, + rgba(0, 0, 0, 0.15) 70%, + transparent 100% + ); + content: " "; + filter: blur(8px); + } +} + +.resources-page, +.resouce-single { min-height: 100vh; padding: 2rem 1rem; +} +// Resources Whiteboard Page +.resources-page { @include media-up(md) { padding: 3rem 2rem; } @@ -11,42 +83,230 @@ .resources-container { max-width: 1400px; margin: 0 auto; + position: relative; + z-index: 1; +} + +// Portal Header with portals on either side +.portal-header { + display: flex; + align-items: center; + justify-content: center; + gap: 3rem; + margin-bottom: 3rem; + position: relative; + + .portal-title { + font-size: 3.5rem; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.15em; + color: #ffffff; + text-shadow: + 0 0 20px rgba(255, 255, 255, 0.8), + 0 0 40px rgba(255, 255, 255, 0.4), + 0 2px 4px rgba(0, 0, 0, 0.3); + margin: 0; + position: relative; + z-index: 2; + + @include media-up(md) { + font-size: 4rem; + } + } + + .portal-icon { + width: 100px; + height: 160px; + position: relative; + overflow: visible; + z-index: 1; + + // Portal ring (the colored edge) + &::before { + content: ""; + position: absolute; + inset: 0; + border-radius: 50%; + border: 8px solid; + z-index: -1; + animation: portal-pulse 2s ease-in-out infinite; + } + + // Black center void + &::after { + content: ""; + position: absolute; + inset: 16px; + border-radius: 50%; + background: radial-gradient( + ellipse at center, + rgba(0, 0, 0, 0.95) 0%, + rgba(0, 0, 0, 0.85) 60%, + rgba(0, 0, 0, 0.6) 100% + ); + z-index: 1; + } + + &.blue-portal { + left: 100px; + + &::before { + border-color: #0096ff; + box-shadow: + 0 0 30px 8px rgba(0, 150, 255, 0.8), + 0 0 60px 12px rgba(0, 150, 255, 0.5), + inset 0 0 30px rgba(0, 150, 255, 0.6); + } + + &::after { + box-shadow: + inset 0 0 40px rgba(0, 150, 255, 0.3), + inset 0 0 20px rgba(0, 150, 255, 0.5); + } + } + + &.orange-portal { + left: -100px; + &::before { + border-color: #ff7800; + box-shadow: + 0 0 30px 8px rgba(255, 120, 0, 0.8), + 0 0 60px 12px rgba(255, 120, 0, 0.5), + inset 0 0 30px rgba(255, 120, 0, 0.6); + } + + &::after { + box-shadow: + inset 0 0 40px rgba(255, 120, 0, 0.3), + inset 0 0 20px rgba(255, 120, 0, 0.5); + } + } + } +} + +// Companion Cube +.companion-cube { + position: fixed; + bottom: 40px; + right: 40px; + width: 80px; + height: 80px; + background: linear-gradient( + 135deg, + rgba(200, 200, 200, 0.2) 0%, + rgba(150, 150, 150, 0.3) 100% + ); + border: 2px solid rgba(200, 200, 200, 0.4); + border-radius: 8px; + box-shadow: + 0 10px 30px rgba(0, 0, 0, 0.5), + inset 0 0 20px rgba(255, 255, 255, 0.1); + transform: rotateX(15deg) rotateY(-15deg); + transform-style: preserve-3d; + animation: cube-float 4s ease-in-out infinite; + position: relative; + + // Heart symbol in center + &::before { + content: "♥"; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 32px; + color: rgba(255, 100, 150, 0.6); + text-shadow: 0 0 10px rgba(255, 100, 150, 0.8); + } + + // Corner circles + &::after { + content: ""; + position: absolute; + width: 12px; + height: 12px; + border-radius: 50%; + background: rgba(200, 200, 200, 0.4); + top: 8px; + left: 8px; + box-shadow: + 60px 0 0 rgba(200, 200, 200, 0.4), + 0 60px 0 rgba(200, 200, 200, 0.4), + 60px 60px 0 rgba(200, 200, 200, 0.4); + } + + @include media-up(md) { + width: 100px; + height: 100px; + + &::before { + font-size: 40px; + } + } +} + +@keyframes portal-pulse { + 0%, + 100% { + transform: scaleX(0.75) scaleY(0.85); + opacity: 1; + } + 50% { + transform: scaleX(0.8) scaleY(0.9); + opacity: 0.9; + } +} + +@keyframes portal-ring-pulse { + 0%, + 100% { + transform: scale(1); + opacity: 0.3; + } + 50% { + transform: scale(1.2); + opacity: 0.1; + } +} + +@keyframes cube-float { + 0%, + 100% { + transform: rotateX(15deg) rotateY(-15deg) translateY(0); + } + 50% { + transform: rotateX(15deg) rotateY(-15deg) translateY(-10px); + } +} + +@keyframes portal-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} + +@keyframes portal-vortex { + 0% { + transform: rotate(0deg) scale(1); + } + 50% { + transform: rotate(180deg) scale(1.1); + } + 100% { + transform: rotate(360deg) scale(1); + } } .whiteboard { - background: linear-gradient(135deg, #f5f5f0 0%, #e8e8dd 100%); - border-radius: 8px; - - box-shadow: - 0 10px 40px rgba(0, 0, 0, 0.1), - inset 0 0 100px rgba(0, 0, 0, 0.02); position: relative; + padding: 3rem 2rem; - // Subtle texture overlay - &::before { - content: ""; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: - repeating-linear-gradient( - 0deg, - transparent, - transparent 2px, - rgba(0, 0, 0, 0.01) 2px, - rgba(0, 0, 0, 0.01) 4px - ), - repeating-linear-gradient( - 90deg, - transparent, - transparent 2px, - rgba(0, 0, 0, 0.01) 2px, - rgba(0, 0, 0, 0.01) 4px - ); - pointer-events: none; - border-radius: 8px; + @include media-up(md) { + padding: 4rem 3rem; } } @@ -58,18 +318,26 @@ h1 { font-size: 3rem; - color: #2c3e50; + color: #0096ff; margin-bottom: 1rem; font-weight: 700; + text-shadow: 0 0 20px rgba(0, 150, 255, 0.6); } .whiteboard-description { - font-family: "Caveat", cursive; - font-size: 28px; - font-weight: bold; - color: #2c3e50; - max-width: 600px; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-size: 1.1rem; + font-weight: 400; + color: rgba(255, 255, 255, 0.9); + max-width: 700px; margin: 0 auto; + line-height: 1.7; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + + p { + color: rgba(255, 255, 255, 0.85); + margin: 0.5rem 0; + } } } @@ -117,21 +385,39 @@ } } -// Pin tack at the top +// Aperture Science indicator at the top .pin-tack { position: absolute; - top: -15px; + top: -20px; left: 50%; transform: translateX(-50%); - width: 20px; - height: 20px; - background: radial-gradient(circle at 30% 30%, #ff6b6b, #c92a2a); + width: 30px; + height: 30px; + background: radial-gradient( + circle at center, + rgba(255, 255, 255, 0.1) 0%, + rgba(255, 255, 255, 0.05) 50%, + transparent 100% + ); border-radius: 50%; - box-shadow: - 0 2px 5px rgba(0, 0, 0, 0.3), - inset -2px -2px 4px rgba(0, 0, 0, 0.2); + border: 2px solid rgba(255, 255, 255, 0.3); z-index: 2; - transition: transform 0.3s ease; + transition: all 0.3s ease; + + // Aperture Science logo segments + &::before { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 14px; + height: 14px; + border-radius: 50%; + border: 2px solid rgba(255, 255, 255, 0.6); + border-top-color: transparent; + border-left-color: transparent; + } &::after { content: ""; @@ -141,40 +427,105 @@ transform: translate(-50%, -50%); width: 6px; height: 6px; - background: #fff; + background: rgba(255, 255, 255, 0.8); border-radius: 50%; - opacity: 0.5; } } -// Resource card (the note itself) +// Resource panel (Aperture Science test chamber panel) .resource-card { - background: linear-gradient(135deg, #fef9c3 0%, #fde68a 100%); + background: linear-gradient( + 145deg, + rgba(255, 255, 255, 0.95) 0%, + rgba(240, 245, 250, 0.9) 100% + ); padding: 2rem 1.5rem 1.5rem; - border-radius: 4px; + border-radius: 8px; box-shadow: - 0 4px 8px rgba(0, 0, 0, 0.1), - inset 0 -40px 40px -20px rgba(0, 0, 0, 0.05); + 0 8px 24px rgba(0, 0, 0, 0.2), + inset 0 1px 0 rgba(255, 255, 255, 0.9), + 0 0 40px rgba(0, 0, 0, 0.1); min-height: 280px; display: flex; flex-direction: column; gap: 1rem; + border: 3px solid; + position: relative; + backdrop-filter: blur(10px); - // Vary note colors + // Vary border colors with Portal theme .resource-pin:nth-child(4n + 1) & { - background: linear-gradient(135deg, #fef9c3 0%, #fde68a 100%); // Yellow + border-color: rgba(0, 150, 255, 0.5); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.2), + 0 0 30px rgba(0, 150, 255, 0.3), + inset 0 1px 0 rgba(255, 255, 255, 0.9); + + &::before { + background: linear-gradient(180deg, rgba(0, 150, 255, 0.15) 0%, transparent 100%); + } } .resource-pin:nth-child(4n + 2) & { - background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); // Blue + border-color: rgba(255, 120, 0, 0.5); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.2), + 0 0 30px rgba(255, 120, 0, 0.3), + inset 0 1px 0 rgba(255, 255, 255, 0.9); + + &::before { + background: linear-gradient(180deg, rgba(255, 120, 0, 0.15) 0%, transparent 100%); + } } .resource-pin:nth-child(4n + 3) & { - background: linear-gradient(135deg, #dcfce7 0%, #bbf7d0 100%); // Green + border-color: rgba(0, 150, 255, 0.6); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.2), + 0 0 35px rgba(0, 150, 255, 0.35), + inset 0 1px 0 rgba(255, 255, 255, 0.9); + + &::before { + background: linear-gradient(180deg, rgba(0, 150, 255, 0.18) 0%, transparent 100%); + } } .resource-pin:nth-child(4n + 4) & { - background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%); // Pink + border-color: rgba(255, 120, 0, 0.6); + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.2), + 0 0 35px rgba(255, 120, 0, 0.35), + inset 0 1px 0 rgba(255, 255, 255, 0.9); + + &::before { + background: linear-gradient(180deg, rgba(255, 120, 0, 0.18) 0%, transparent 100%); + } + } + + // Subtle gradient overlay + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 60%; + pointer-events: none; + border-radius: 6px 6px 0 0; + } + + // Tech panel grid pattern + &::after { + content: ""; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(0, 0, 0, 0.02) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 0, 0, 0.02) 1px, transparent 1px); + background-size: 20px 20px; + pointer-events: none; + border-radius: 8px; + opacity: 0.5; } } @@ -353,31 +704,40 @@ flex: 1; display: flex; flex-direction: column; + position: relative; + z-index: 2; h2 { - font-size: 1.5rem; + font-size: 1.4rem; margin: 0 0 0.5rem 0; - color: #1f2937; - font-weight: 600; + color: #1a1a1a; + font-weight: 700; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + text-transform: uppercase; + letter-spacing: 0.08em; a { color: inherit; text-decoration: none; - transition: color 0.2s ease; + transition: all 0.3s ease; &:hover { - color: #3b82f6; + color: #0096ff; + text-shadow: 0 0 15px rgba(0, 150, 255, 0.5); } } } } .resource-description { - color: #4b5563; + color: #2c3e50; font-size: 0.95rem; - line-height: 1.5; + line-height: 1.6; margin: 0 0 1rem 0; flex: 1; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + position: relative; + z-index: 2; } .resource-tags { @@ -385,18 +745,29 @@ flex-wrap: wrap; gap: 0.5rem; margin-bottom: 1rem; + position: relative; + z-index: 2; .tag { - background: rgba(0, 0, 0, 0.1); - padding: 0.25rem 0.75rem; - border-radius: 12px; - font-size: 0.8rem; - color: #374151; + background: linear-gradient(135deg, rgba(0, 150, 255, 0.2) 0%, rgba(0, 120, 200, 0.15) 100%); + padding: 0.35rem 0.9rem; + border-radius: 4px; + font-size: 0.7rem; + color: #0080dd; text-decoration: none; - transition: background 0.2s ease; + transition: all 0.3s ease; + border: 2px solid rgba(0, 150, 255, 0.4); + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 700; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; &:hover { - background: rgba(0, 0, 0, 0.2); + background: linear-gradient(135deg, rgba(0, 150, 255, 0.3) 0%, rgba(255, 120, 0, 0.2) 100%); + border-color: rgba(0, 150, 255, 0.6); + color: #0096ff; + box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); + transform: translateY(-1px); } } } @@ -405,46 +776,70 @@ display: flex; gap: 0.75rem; margin-top: auto; + position: relative; + z-index: 2; .resource-link { flex: 1; - padding: 0.5rem 1rem; + padding: 0.7rem 1.3rem; text-align: center; text-decoration: none; - border-radius: 4px; - font-weight: 500; - font-size: 0.9rem; - transition: all 0.2s ease; + border-radius: 6px; + font-weight: 700; + font-size: 0.8rem; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 0.1em; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + border: 3px solid; &.demo { - background: #3b82f6; + background: linear-gradient(135deg, #0096ff 0%, #0075cc 100%); color: #fff; + border-color: #0096ff; + box-shadow: + 0 4px 12px rgba(0, 150, 255, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.3); &:hover { - background: #2563eb; - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(59, 130, 246, 0.4); + background: linear-gradient(135deg, #00aaff 0%, #0096ff 100%); + transform: translateY(-3px); + box-shadow: + 0 8px 20px rgba(0, 150, 255, 0.5), + 0 0 30px rgba(0, 150, 255, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.4); + border-color: #66ccff; } } &.source { - background: #1f2937; + background: linear-gradient(135deg, #ff7800 0%, #dd6600 100%); color: #fff; + border-color: #ff7800; + box-shadow: + 0 4px 12px rgba(255, 120, 0, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.3); &:hover { - background: #111827; - transform: translateY(-2px); - box-shadow: 0 4px 8px rgba(31, 41, 55, 0.4); + background: linear-gradient(135deg, #ff8800 0%, #ff7800 100%); + transform: translateY(-3px); + box-shadow: + 0 8px 20px rgba(255, 120, 0, 0.5), + 0 0 30px rgba(255, 120, 0, 0.4), + inset 0 1px 0 rgba(255, 255, 255, 0.4); + border-color: #ffaa66; } } + + &:active { + transform: translateY(0); + } } } // Single Resource Page .resource-single { - max-width: 900px; margin: 0 auto; - padding: 2rem 1rem; @include media-up(md) { padding: 3rem 2rem; @@ -452,10 +847,113 @@ } .resource-content { - background: #fff; + max-width: 900px; + margin: auto; + background: + linear-gradient( + 145deg, + rgba(10, 20, 40, 0.95) 0%, + rgba(15, 25, 45, 0.98) 100% + ) + padding-box, + linear-gradient( + 135deg, + rgba(0, 150, 255, 0.5), + rgba(255, 120, 0, 0.3), + rgba(0, 150, 255, 0.5) + ) + border-box; border-radius: 8px; + border: 2px solid transparent; padding: 3rem 2rem; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + box-shadow: + 0 0 40px rgba(0, 150, 255, 0.2), + 0 0 80px rgba(255, 120, 0, 0.1), + inset 0 0 100px rgba(0, 100, 200, 0.05), + 0 8px 32px rgba(0, 0, 0, 0.4); + position: relative; + overflow: hidden; + + // Subtle tech panel grid pattern + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-image: + linear-gradient(rgba(0, 150, 255, 0.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 150, 255, 0.03) 1px, transparent 1px); + background-size: 50px 50px; + pointer-events: none; + opacity: 0.3; + border-radius: 8px; + } + + // Corner accents (Aperture Science style) + &::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + radial-gradient( + circle at top left, + rgba(0, 150, 255, 0.15) 0%, + transparent 30% + ), + radial-gradient( + circle at bottom right, + rgba(255, 120, 0, 0.15) 0%, + transparent 30% + ); + pointer-events: none; + } + + // Animated energy particles on border + .energy-particles { + position: absolute; + inset: -2px; + border-radius: 8px; + pointer-events: none; + z-index: 1; + overflow: hidden; + + &::before, + &::after { + content: ""; + position: absolute; + width: 20px; + height: 20px; + border-radius: 50%; + filter: blur(4px); + } + + // Blue particle + &::before { + background: radial-gradient( + circle, + rgba(0, 150, 255, 0.8) 0%, + transparent 70% + ); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.8); + animation: particle-travel-blue 8s linear infinite; + } + + // Orange particle + &::after { + background: radial-gradient( + circle, + rgba(255, 120, 0, 0.8) 0%, + transparent 70% + ); + box-shadow: 0 0 15px rgba(255, 120, 0, 0.8); + animation: particle-travel-orange 8s linear infinite 4s; + } + } @include media-up(md) { padding: 4rem 3rem; @@ -466,70 +964,294 @@ text-align: center; margin-bottom: 3rem; padding-bottom: 2rem; - border-bottom: 2px solid #e5e7eb; + border-bottom: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.6), + transparent + ) + 1; + position: relative; + z-index: 1; h1 { font-size: 2.5rem; - color: #1f2937; + color: rgba(240, 245, 250, 0.95); margin: 1rem 0; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.15em; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.3); + position: relative; + display: inline-block; + + // Blue line on the left + &::before { + content: ""; + position: absolute; + left: -4rem; + top: 50%; + transform: translateY(-50%); + width: 3rem; + height: 3px; + background: linear-gradient(90deg, transparent, #0096ff); + box-shadow: 0 0 10px rgba(0, 150, 255, 0.8); + } + + // Orange line on the right + &::after { + content: ""; + position: absolute; + right: -4rem; + top: 50%; + transform: translateY(-50%); + width: 3rem; + height: 3px; + background: linear-gradient(90deg, #ff7800, transparent); + box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); + } @include media-up(md) { font-size: 3rem; + + &::before, + &::after { + width: 5rem; + } + + &::before { + left: -6rem; + } + + &::after { + right: -6rem; + } } } .lead { font-size: 1.25rem; - color: #6b7280; + color: rgba(150, 200, 255, 0.9); margin: 1rem 0; - } -} - -.resource-icon-large { - width: 120px; - height: 120px; - margin: 0 auto; - - &.lavalamp, - &.lastfm-stats { - // Inherit styles from smaller icons + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + font-weight: 300; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.3); } } .resource-body { - color: #374151; + color: rgba(200, 220, 255, 0.95); line-height: 1.8; font-size: 1.1rem; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + position: relative; + z-index: 1; h2 { - color: #1f2937; + color: #0096ff; margin-top: 2rem; margin-bottom: 1rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 600; + text-shadow: 0 0 15px rgba(0, 150, 255, 0.5); + border-bottom: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.5), + transparent + ) + 1; + padding-bottom: 0.5rem; + padding-left: 1rem; + position: relative; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 4px; + height: 70%; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.8), + rgba(255, 120, 0, 0.6) + ); + box-shadow: 0 0 10px rgba(0, 150, 255, 0.6); + } + } + + h3 { + color: rgba(255, 140, 50, 0.9); + margin-top: 1.5rem; + margin-bottom: 0.75rem; + text-transform: uppercase; + font-size: 1rem; + font-weight: 600; + letter-spacing: 0.06em; + text-shadow: 0 0 10px rgba(255, 120, 0, 0.4); } p { margin-bottom: 1rem; } + a { + color: #0096ff; + text-decoration: none; + border-bottom: 1px solid rgba(0, 150, 255, 0.3); + transition: all 0.3s ease; + position: relative; + + &:hover { + color: #ff7800; + border-bottom-color: rgba(255, 120, 0, 0.6); + text-shadow: 0 0 8px rgba(0, 150, 255, 0.5); + } + } + + ul, + ol { + margin-bottom: 1rem; + padding-left: 2rem; + + li { + margin-bottom: 0.5rem; + position: relative; + + &::marker { + color: rgba(0, 150, 255, 0.6); + } + } + } + + strong { + color: #fff; + font-weight: 600; + text-shadow: 0 0 5px rgba(0, 150, 255, 0.3); + } + code { - background: #f3f4f6; - padding: 0.2rem 0.4rem; + background: rgba(0, 100, 180, 0.15); + padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.9em; + color: #66ccff; + border: 1px solid rgba(0, 150, 255, 0.3); + font-family: "Consolas", "Monaco", monospace; + box-shadow: 0 0 10px rgba(0, 150, 255, 0.1); } pre { - background: #1f2937; - color: #e5e7eb; + background: linear-gradient( + 145deg, + rgba(5, 15, 30, 0.9) 0%, + rgba(10, 20, 40, 0.95) 100% + ); + color: #66ccff; padding: 1.5rem; - border-radius: 8px; + border-radius: 6px; overflow-x: auto; margin: 1.5rem 0; + border: 1px solid rgba(0, 150, 255, 0.3); + box-shadow: + inset 0 0 20px rgba(0, 100, 200, 0.1), + 0 4px 20px rgba(0, 0, 0, 0.3); + position: relative; + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 4px; + height: 100%; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.4) + ); + } code { background: none; padding: 0; color: inherit; + border: none; + box-shadow: none; + } + } + + // Code copy button + .code-copy-btn { + position: absolute; + top: 0.75rem; + right: 0.75rem; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.3) 0%, + rgba(0, 80, 150, 0.2) 100% + ); + color: #66ccff; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + font-size: 0.85rem; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 0.05em; + z-index: 10; + + svg { + width: 14px; + height: 14px; + transition: transform 0.2s ease; + } + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.4) 0%, + rgba(255, 100, 0, 0.25) 100% + ); + border-color: rgba(0, 150, 255, 0.7); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); + color: #fff; + transform: translateY(-1px); + + svg { + transform: scale(1.1); + } + } + + &:active { + transform: translateY(0); + } + + &.copied { + background: linear-gradient( + 135deg, + rgba(0, 200, 150, 0.4) 0%, + rgba(0, 150, 100, 0.3) 100% + ); + border-color: rgba(0, 255, 180, 0.6); + color: #66ffcc; + + svg { + transform: scale(0.9); + } } } } @@ -540,30 +1262,89 @@ align-items: center; margin-top: 3rem; padding-top: 2rem; - border-top: 2px solid #e5e7eb; + border-top: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.4), + rgba(255, 120, 0, 0.4), + transparent + ) + 1; gap: 1rem; flex-wrap: wrap; + position: relative; + z-index: 1; .nav-link { padding: 0.75rem 1.5rem; - background: #f3f4f6; - color: #374151; + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.2) 0%, + rgba(0, 80, 150, 0.15) 100% + ); + color: #66ccff; text-decoration: none; - border-radius: 6px; + border-radius: 4px; font-weight: 500; - transition: all 0.2s ease; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + border: 1px solid rgba(0, 150, 255, 0.4); + transition: all 0.3s ease; + text-transform: uppercase; + font-size: 0.85rem; + letter-spacing: 0.08em; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.2), + transparent + ); + transition: left 0.5s ease; + } &:hover { - background: #e5e7eb; + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(255, 100, 0, 0.2) 100% + ); + border-color: rgba(0, 150, 255, 0.7); + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.4), + 0 0 30px rgba(255, 120, 0, 0.2); + color: #fff; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.6); transform: translateY(-2px); + + &::before { + left: 100%; + } } &.back { - background: #3b82f6; - color: #fff; + background: linear-gradient( + 135deg, + rgba(255, 100, 0, 0.2) 0%, + rgba(200, 80, 0, 0.15) 100% + ); + border-color: rgba(255, 120, 0, 0.5); + color: #ffaa66; &:hover { - background: #2563eb; + border-color: rgba(255, 120, 0, 0.8); + box-shadow: + 0 0 20px rgba(255, 120, 0, 0.4), + 0 0 30px rgba(0, 150, 255, 0.2); } } @@ -574,3 +1355,756 @@ } } } + +.resource-tags { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + justify-content: center; + margin: 1rem 0; + position: relative; + z-index: 1; + + .tag { + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.2) 0%, + rgba(0, 80, 150, 0.1) 100% + ); + padding: 0.35rem 0.85rem; + border-radius: 4px; + font-size: 0.75rem; + color: rgba(100, 200, 255, 0.9); + text-decoration: none; + transition: all 0.3s ease; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + border: 1px solid rgba(0, 150, 255, 0.3); + text-transform: uppercase; + letter-spacing: 0.06em; + font-weight: 500; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 2px; + height: 100%; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.4) + ); + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(255, 100, 0, 0.2) 100% + ); + border-color: rgba(0, 150, 255, 0.6); + color: #fff; + box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); + transform: translateY(-1px); + + &::before { + opacity: 1; + } + } + } +} + +// Contact section styling for resources +.contact-section { + margin: 3rem 0; + padding: 2rem; + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.15) 0%, + rgba(0, 80, 150, 0.1) 100% + ); + border-radius: 6px; + border: 1px solid rgba(0, 150, 255, 0.3); + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.1), + inset 0 0 40px rgba(0, 100, 200, 0.05); + position: relative; + z-index: 1; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.7), + rgba(255, 120, 0, 0.5) + ); + border-radius: 6px 0 0 6px; + } + + .contact-title { + color: #0096ff; + font-size: 1.8rem; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + margin-bottom: 1.5rem; + text-shadow: 0 0 15px rgba(0, 150, 255, 0.5); + } + + .contact-content { + color: rgba(200, 220, 255, 0.95); + + p { + margin-bottom: 1.5rem; + line-height: 1.6; + } + } + + .contact-email, + .contact-pgp { + margin-bottom: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + + @include media-up(md) { + flex-direction: row; + align-items: center; + gap: 1rem; + } + } + + .contact-label { + font-weight: 600; + color: rgba(100, 180, 255, 0.9); + text-transform: uppercase; + font-size: 0.85rem; + letter-spacing: 0.08em; + } + + .contact-email a { + color: #66ccff; + text-decoration: none; + border-bottom: 1px solid rgba(0, 150, 255, 0.3); + transition: all 0.3s ease; + font-family: "Consolas", "Monaco", monospace; + + &:hover { + color: #ff7800; + border-bottom-color: rgba(255, 120, 0, 0.6); + text-shadow: 0 0 8px rgba(0, 150, 255, 0.5); + } + } + + .pgp-actions { + display: flex; + gap: 0.75rem; + flex-wrap: wrap; + } + + .pgp-button { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.3) 0%, + rgba(0, 80, 150, 0.2) 100% + ); + color: #66ccff; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + font-size: 0.85rem; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 0.05em; + text-decoration: none; + + .button-icon { + font-size: 1.1rem; + } + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.4) 0%, + rgba(255, 100, 0, 0.25) 100% + ); + border-color: rgba(0, 150, 255, 0.7); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); + color: #fff; + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + } + } + + .copy-feedback { + margin-top: 0.5rem; + font-size: 0.85rem; + font-weight: 500; + opacity: 0; + transition: opacity 0.3s ease; + + &.show { + opacity: 1; + } + + &.success { + color: #66ffcc; + text-shadow: 0 0 10px rgba(0, 255, 180, 0.6); + } + + &.error { + color: #ff9966; + text-shadow: 0 0 10px rgba(255, 120, 0, 0.6); + } + } +} + +// Last.fm Stats Interactive Form +#lastfm-stats-app { + margin: 2rem 0; + padding: 2rem; + background: linear-gradient( + 145deg, + rgba(5, 15, 30, 0.9) 0%, + rgba(10, 20, 40, 0.95) 100% + ); + border-radius: 6px; + border: 1px solid rgba(0, 150, 255, 0.4); + box-shadow: + 0 0 30px rgba(0, 150, 255, 0.15), + 0 0 50px rgba(255, 120, 0, 0.08), + inset 0 0 60px rgba(0, 100, 200, 0.05); + position: relative; + z-index: 1; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.6), + transparent + ); + } +} + +.stats-form { + display: flex; + flex-direction: column; + gap: 1.5rem; + margin-bottom: 2rem; + position: relative; + z-index: 1; + + @include media-up(md) { + flex-direction: row; + align-items: flex-end; + } + + .form-group { + flex: 1; + display: flex; + flex-direction: column; + gap: 0.5rem; + + label { + font-weight: 600; + color: #66ccff; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + text-shadow: 0 0 8px rgba(0, 150, 255, 0.4); + } + + input, + select { + padding: 0.75rem 1rem; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + font-size: 1rem; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + background: rgba(5, 15, 30, 0.7); + color: rgba(200, 220, 255, 0.95); + transition: all 0.3s ease; + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.3); + + &:focus { + outline: none; + border-color: rgba(0, 150, 255, 0.7); + box-shadow: + 0 0 15px rgba(0, 150, 255, 0.3), + inset 0 2px 8px rgba(0, 0, 0, 0.4); + background: rgba(5, 15, 30, 0.9); + } + + &::placeholder { + color: rgba(100, 150, 200, 0.5); + } + } + + select { + cursor: pointer; + + option { + background: rgba(5, 15, 30, 0.95); + color: rgba(200, 220, 255, 0.95); + } + } + } + + .btn-primary { + padding: 0.75rem 2rem; + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(0, 100, 180, 0.2) 100% + ); + color: #66ccff; + border: 1px solid rgba(0, 150, 255, 0.5); + border-radius: 4px; + font-size: 0.95rem; + font-weight: 600; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + cursor: pointer; + transition: all 0.3s ease; + align-self: flex-end; + text-transform: uppercase; + letter-spacing: 0.08em; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.3), + transparent + ); + transition: left 0.5s ease; + } + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 150, 255, 0.4) 0%, + rgba(255, 100, 0, 0.3) 100% + ); + border-color: rgba(0, 150, 255, 0.8); + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.4), + 0 0 30px rgba(255, 120, 0, 0.2); + color: #fff; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.6); + transform: translateY(-2px); + + &::before { + left: 100%; + } + } + + &:active { + transform: translateY(0); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); + } + } +} + +.stats-results { + position: relative; + z-index: 1; + + .loading { + text-align: center; + padding: 2rem; + color: #66ccff; + font-size: 1.1rem; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + + p { + animation: pulse 1.5s ease-in-out infinite; + text-shadow: 0 0 15px rgba(0, 150, 255, 0.6); + } + } + + #stats-content { + h2 { + color: #0096ff; + margin-bottom: 1.5rem; + font-size: 1.8rem; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + text-shadow: 0 0 15px rgba(0, 150, 255, 0.5); + border-bottom: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.6), + transparent + ) + 1; + padding-bottom: 0.75rem; + } + } + + .stat-box { + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.15) 0%, + rgba(0, 80, 150, 0.1) 100% + ); + padding: 1.5rem; + border-radius: 6px; + margin-bottom: 1.5rem; + border: 1px solid rgba(0, 150, 255, 0.3); + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.1), + inset 0 0 40px rgba(0, 100, 200, 0.05); + transition: all 0.3s ease; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 3px; + height: 100%; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.7), + rgba(255, 120, 0, 0.5) + ); + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.25) 0%, + rgba(255, 100, 0, 0.15) 100% + ); + border-color: rgba(0, 150, 255, 0.5); + box-shadow: + 0 0 30px rgba(0, 150, 255, 0.2), + 0 0 40px rgba(255, 120, 0, 0.1); + + &::before { + opacity: 1; + } + } + + h3 { + color: rgba(100, 180, 255, 0.8); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.12em; + margin-bottom: 0.75rem; + font-weight: 600; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + text-shadow: 0 0 8px rgba(0, 150, 255, 0.4); + } + + .stat-number { + font-size: 3rem; + font-weight: 700; + color: #66ccff; + margin: 0; + font-family: "DSEG7-Classic", "Segoe UI", monospace; + text-shadow: + 0 0 20px rgba(0, 150, 255, 0.8), + 0 0 40px rgba(0, 150, 255, 0.4); + letter-spacing: 0.08em; + background: linear-gradient(135deg, #66ccff 0%, #0096ff 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + filter: drop-shadow(0 0 15px rgba(0, 150, 255, 0.5)); + } + } + + .artist-list { + list-style: none; + padding: 0; + margin: 0; + + li { + padding: 0.75rem 0; + padding-left: 1rem; + border-bottom: 1px solid rgba(0, 150, 255, 0.2); + font-size: 1.05rem; + color: rgba(200, 220, 255, 0.9); + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + position: relative; + transition: all 0.3s ease; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 3px; + height: 60%; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.4) + ); + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover { + padding-left: 1.2rem; + + &::before { + opacity: 1; + } + } + + &:last-child { + border-bottom: none; + } + + a { + color: #66ccff; + text-decoration: none; + font-weight: 500; + transition: all 0.3s ease; + border-bottom: 1px solid rgba(0, 150, 255, 0.2); + + &:hover { + color: #ff7800; + border-bottom-color: rgba(255, 120, 0, 0.5); + text-shadow: 0 0 10px rgba(0, 150, 255, 0.5); + } + } + } + } + + .export-buttons { + display: flex; + gap: 1rem; + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.3), + transparent + ) + 1; + flex-wrap: wrap; + + @include media-up(md) { + flex-wrap: nowrap; + } + + .btn-export { + flex: 1; + padding: 0.75rem 1.5rem; + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.2) 0%, + rgba(0, 80, 150, 0.15) 100% + ); + color: #66ccff; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + font-size: 0.85rem; + font-weight: 600; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + cursor: pointer; + transition: all 0.3s ease; + min-width: 150px; + text-transform: uppercase; + letter-spacing: 0.06em; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.2), + transparent + ); + transition: left 0.5s ease; + } + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(255, 100, 0, 0.2) 100% + ); + border-color: rgba(0, 150, 255, 0.6); + transform: translateY(-2px); + box-shadow: 0 0 20px rgba(0, 150, 255, 0.3); + color: #fff; + text-shadow: 0 0 8px rgba(0, 150, 255, 0.5); + + &::before { + left: 100%; + } + } + + &:active { + transform: translateY(0); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.2); + } + + &.copied { + background: linear-gradient( + 135deg, + rgba(0, 200, 150, 0.3) 0%, + rgba(0, 150, 100, 0.2) 100% + ); + border-color: rgba(0, 255, 180, 0.6); + color: #66ffcc; + text-shadow: 0 0 10px rgba(0, 255, 180, 0.6); + box-shadow: 0 0 20px rgba(0, 255, 180, 0.3); + } + } + } + + .error { + background: linear-gradient( + 135deg, + rgba(180, 50, 0, 0.2) 0%, + rgba(150, 30, 0, 0.15) 100% + ); + border: 1px solid rgba(255, 80, 0, 0.5); + border-radius: 6px; + padding: 1.5rem; + color: #ff9966; + text-align: center; + font-family: "Segoe UI", "Helvetica Neue", Arial, sans-serif; + box-shadow: + 0 0 20px rgba(255, 80, 0, 0.2), + inset 0 0 40px rgba(255, 50, 0, 0.1); + position: relative; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: linear-gradient( + 180deg, + rgba(255, 120, 0, 0.8), + rgba(255, 80, 0, 0.6) + ); + } + + p { + margin: 0; + font-weight: 500; + text-shadow: 0 0 10px rgba(255, 120, 0, 0.5); + } + } +} + +@keyframes pulse { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } +} + +// Energy particle animations - travel around the border +@keyframes particle-travel-blue { + 0% { + top: -10px; + left: 0; + } + 25% { + top: -10px; + left: 100%; + } + 50% { + top: 100%; + left: 100%; + } + 75% { + top: 100%; + left: -10px; + } + 100% { + top: -10px; + left: -10px; + } +} + +@keyframes particle-travel-orange { + 0% { + top: -10px; + left: 100%; + } + 25% { + top: 100%; + left: 100%; + } + 50% { + top: 100%; + left: -10px; + } + 75% { + top: -10px; + left: -10px; + } + 100% { + top: -10px; + left: 100%; + } +} diff --git a/content/resources/_index.md b/content/resources/_index.md index 8a2654c..d4c4af7 100644 --- a/content/resources/_index.md +++ b/content/resources/_index.md @@ -3,6 +3,4 @@ title: "Resources" draft: false --- -# Resources - -Welcome to my whiteboard of resources. Here you'll find various tools, scripts, and experiments I've built and wanted to share. +Welcome to my test chamber, here you'll find various experiements, tests and resources. Take a look around. diff --git a/content/resources/lastfm-stats/index.md b/content/resources/lastfm-stats/index.md index b33e843..3cdbb31 100644 --- a/content/resources/lastfm-stats/index.md +++ b/content/resources/lastfm-stats/index.md @@ -1,5 +1,5 @@ --- -title: "Last.fm Weekly Stats Script" +title: "Last.fm Weekly Stats" date: 2026-01-04 tags: ["javascript", "api", "last.fm"] description: "Fetch and display your weekly listening stats from Last.fm" @@ -9,26 +9,115 @@ source_url: "" draft: false --- -A handy script for pulling your weekly listening statistics from Last.fm's API. Perfect for tracking your music habits and creating weekly listening reports. +Get your weekly listening statistics from Last.fm's API. Enter your username below to see your top artists and track counts for different time periods. -## Features +I made this so I could easily add in listening stats to my [weekly posts](/tags/weekly-update/), if you find it useful please let me know. -- Fetches weekly top tracks from Last.fm -- Displays track count and listening time -- Formats data in a clean, readable format -- Easy to integrate into blogs or dashboards +{{< lastfm-stats-form >}} -## Setup +## Download the Shell Script -1. Get your Last.fm API key from [Last.fm API](https://www.last.fm/api) -2. Configure your username in the script -3. Run the script to fetch your weekly stats +Want to run this locally or integrate it into your own workflows? Here is a bash script I was using to generate this before I decided to make a web version. -## Output +``` +#!/bin/bash -The script returns your top tracks for the week, including: -- Track name and artist -- Play count -- Listening duration +# Last.fm Weekly Stats Script +# Fetches your Last.fm listening statistics for the past week +# +# Requirements: +# - curl (for API requests) +# - jq (for JSON parsing) +# +# Usage: ./lastfm-week.sh +# +# Setup: +# Create a .env file with: +# LASTFM_API_KEY=your_api_key_here +# LASTFM_USERNAME=your_username_here +# +# Output: Markdown-formatted stats with top artists and track counts +# +# Download from: https://ritual.sh/resources/lastfm-stats/ -Great for weekly blog posts or personal music tracking! +# Load environment variables from .env file +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) +else + echo "Error: .env file not found" + exit 1 +fi + +# Check required variables +if [ -z "$LASTFM_API_KEY" ] || [ -z "$LASTFM_USERNAME" ]; then + echo "Error: LASTFM_API_KEY and LASTFM_USERNAME must be set in .env file" + exit 1 +fi + +API_BASE="http://ws.audioscrobbler.com/2.0/" + +# Get current timestamp +NOW=$(date +%s) +# Get timestamp from 7 days ago +WEEK_AGO=$((NOW - 604800)) + +# Fetch top artists for the week +TOP_ARTISTS=$(curl -s "${API_BASE}?method=user.gettopartists&user=${LASTFM_USERNAME}&api_key=${LASTFM_API_KEY}&format=json&period=7day&limit=5") + +# Fetch recent tracks to count this week's scrobbles +RECENT_TRACKS=$(curl -s "${API_BASE}?method=user.getrecenttracks&user=${LASTFM_USERNAME}&api_key=${LASTFM_API_KEY}&format=json&from=${WEEK_AGO}&to=${NOW}&limit=1") + +# Get total track count +TOTAL_TRACKS=$(echo "$RECENT_TRACKS" | jq -r '.recenttracks["@attr"].total') + +# Output in markdown format +echo "## Last.fm Weekly Stats" +echo "" +echo "**Total Tracks:** ${TOTAL_TRACKS}" +echo "" +echo "**Top 5 Artists:**" +echo "" + +# Parse and display top 5 artists as markdown links +echo "$TOP_ARTISTS" | jq -r '.topartists.artist[] | "- [\(.name)](\(.url)) - \(.playcount) plays"' +``` + +### Shell Script Usage + +The script fetches your Last.fm stats and outputs them in markdown format. + +**Requirements:** + +- `curl` for API requests +- `jq` for JSON parsing + +**Setup:** + +1. Create a `.env` file with your credentials: + +```bash +LASTFM_API_KEY=your_api_key_here +LASTFM_USERNAME=your_username_here +``` + +2. Make the script executable: + +```bash +chmod +x lastfm-week.sh +``` + +3. Run it: + +```bash +./lastfm-week.sh +``` + +**Output:** + +The script prints markdown-formatted stats including: + +- Total tracks scrobbled this week +- Top 5 artists with play counts +- Direct links to artist pages + +Enjoy! diff --git a/content/resources/lavalamp/index.md b/content/resources/lavalamp/index.md index 1660430..eba821c 100644 --- a/content/resources/lavalamp/index.md +++ b/content/resources/lavalamp/index.md @@ -6,7 +6,7 @@ description: "A pure CSS lavalamp animation with floating blobs" icon: "lavalamp" demo_url: "" source_url: "" -draft: false +draft: true --- A mesmerizing lavalamp effect created entirely with HTML and CSS. Features smooth floating animations and gradient blobs that rise and fall like a classic lava lamp. diff --git a/lastfm-week.sh b/lastfm-week.sh index c208320..1d64688 100755 --- a/lastfm-week.sh +++ b/lastfm-week.sh @@ -1,7 +1,22 @@ #!/bin/bash # Last.fm Weekly Stats Script -# Usage: ./lastfm-weekly.sh +# Fetches your Last.fm listening statistics for the past week +# +# Requirements: +# - curl (for API requests) +# - jq (for JSON parsing) +# +# Usage: ./lastfm-week.sh +# +# Setup: +# Create a .env file with: +# LASTFM_API_KEY=your_api_key_here +# LASTFM_USERNAME=your_username_here +# +# Output: Markdown-formatted stats with top artists and track counts +# +# Download from: https://ritual.sh/resources/lastfm-stats/ # Load environment variables from .env file if [ -f .env ]; then diff --git a/layouts/blog/single.html b/layouts/blog/single.html index 1a9cec6..b8ea142 100644 --- a/layouts/blog/single.html +++ b/layouts/blog/single.html @@ -23,36 +23,7 @@ <div class="blog-summary">{{ .Content }}</div> </article> - <section class="blog-contact-section"> - <h2 class="contact-title">Contact</h2> - <div class="contact-content"> - <p> - If you found this interesting, have any comments, questions, - corrections, or just fancy saying "hello" please feel free to get - in touch. - </p> - <div class="contact-email"> - <span class="contact-label">Email:</span> - <a href="mailto:dan@ritual.sh">dan@ritual.sh</a> - </div> - <div class="contact-pgp"> - <span class="contact-label">PGP Public Key:</span> - <div class="pgp-actions"> - <a - href="/publickey.asc" - download - class="pgp-button download-key" - > - <span class="button-icon">↓</span> Download - </a> - <button id="copy-pgp-key" class="pgp-button copy-key"> - <span class="button-icon">⎘</span> Copy to Clipboard - </button> - </div> - <div id="copy-feedback" class="copy-feedback"></div> - </div> - </div> - </section> + {{ partial "contact-section.html" . }} <nav class="blog-post-navigation"> <div class="post-nav-links"> @@ -80,36 +51,4 @@ <script src="/js/footnote-scroll.js"></script> -<script> - document.addEventListener("DOMContentLoaded", function () { - const copyButton = document.getElementById("copy-pgp-key"); - const feedback = document.getElementById("copy-feedback"); - - if (copyButton) { - copyButton.addEventListener("click", async function () { - try { - const response = await fetch("/publickey.asc"); - const pgpKey = await response.text(); - - await navigator.clipboard.writeText(pgpKey); - - feedback.textContent = "PGP key copied to clipboard!"; - feedback.classList.add("show", "success"); - - setTimeout(() => { - feedback.classList.remove("show"); - }, 3000); - } catch (err) { - feedback.textContent = "Failed to copy key"; - feedback.classList.add("show", "error"); - - setTimeout(() => { - feedback.classList.remove("show"); - }, 3000); - } - }); - } - }); -</script> - {{ end }} diff --git a/layouts/partials/contact-section.html b/layouts/partials/contact-section.html new file mode 100644 index 0000000..f06baa7 --- /dev/null +++ b/layouts/partials/contact-section.html @@ -0,0 +1,30 @@ +<section class="contact-section"> + <h2 class="contact-title">Contact</h2> + <div class="contact-content"> + <p> + If you found this interesting, have any comments, questions, + corrections, or just fancy saying "hello" please feel free to get + in touch. + </p> + <div class="contact-email"> + <span class="contact-label">Email:</span> + <a href="mailto:dan@ritual.sh">dan@ritual.sh</a> + </div> + <div class="contact-pgp"> + <span class="contact-label">PGP Public Key:</span> + <div class="pgp-actions"> + <a + href="/publickey.asc" + download + class="pgp-button download-key" + > + <span class="button-icon">↓</span> Download + </a> + <button class="pgp-button copy-key pgp-copy-trigger"> + <span class="button-icon">⎘</span> Copy to Clipboard + </button> + </div> + <div class="copy-feedback pgp-copy-feedback"></div> + </div> + </div> +</section> diff --git a/layouts/resources/list.html b/layouts/resources/list.html index dad3c37..02b5847 100644 --- a/layouts/resources/list.html +++ b/layouts/resources/list.html @@ -1,6 +1,17 @@ {{ define "main" }} -<div class="resources-page"> +<div class="resources-page portal-theme"> + <div class="portal-bg-elements"> + <div class="aperture-logo"></div> + <div class="test-chamber-grid"></div> + </div> + <div class="resources-container"> + <div class="portal-header"> + <div class="portal-icon blue-portal hidden-lg-down"></div> + <h1 class="portal-title">Resources</h1> + <div class="portal-icon orange-portal hidden-md-down"></div> + </div> + <div class="whiteboard"> <div class="whiteboard-header"> <div class="whiteboard-description">{{ .Content }}</div> @@ -10,6 +21,8 @@ {{ range .Pages }} {{ .Render "summary" }} {{ end }} </div> </div> + + <div class="companion-cube"></div> </div> </div> {{ end }} diff --git a/layouts/resources/single.html b/layouts/resources/single.html index 1229678..dc3de9f 100644 --- a/layouts/resources/single.html +++ b/layouts/resources/single.html @@ -1,8 +1,16 @@ {{ define "main" }} <div class="resource-single"> + <div class="test-chamber-grid"> + </div> + <article class="resource-content"> + <!-- Aperture Science hazard stripes in corners --> + + + <!-- Animated energy particles --> + <div class="energy-particles"></div> + <header class="resource-header"> - <div class="resource-icon-large {{ .Params.icon }}"></div> <h1>{{ .Title }}</h1> {{ if .Params.description }} <p class="lead">{{ .Params.description }}</p> @@ -28,6 +36,8 @@ {{ .Content }} </div> + {{ partial "contact-section.html" . }} + <nav class="resource-navigation"> {{ with .PrevInSection }} <a href="{{ .Permalink }}" class="nav-link prev">← {{ .Title }}</a> diff --git a/layouts/shortcodes/lastfm-stats-form.html b/layouts/shortcodes/lastfm-stats-form.html new file mode 100644 index 0000000..e7cd3e9 --- /dev/null +++ b/layouts/shortcodes/lastfm-stats-form.html @@ -0,0 +1,51 @@ +<div id="lastfm-stats-app"> + <div class="stats-form"> + <div class="form-group"> + <label for="lastfm-username">Last.fm Username</label> + <input + type="text" + id="lastfm-username" + placeholder="Enter your username" + /> + </div> + + <div class="form-group"> + <label for="time-period">Time Period</label> + <select id="time-period"> + <option value="7day">Past Week (7 days)</option> + <option value="1month">Past Month (30 days)</option> + </select> + </div> + + <button id="fetch-stats" class="btn-primary">Get Stats</button> + </div> + + <div id="stats-results" class="stats-results" style="display: none"> + <div id="stats-loading" class="loading" style="display: none"> + <p>Loading your stats...</p> + </div> + + <div id="stats-content" style="display: none"> + <h2>Your Last.fm Stats</h2> + + <div class="stat-box"> + <h3>Total Tracks</h3> + <p id="total-tracks" class="stat-number">-</p> + </div> + + <div class="stat-box"> + <h3>Top 5 Artists</h3> + <ul id="top-artists" class="artist-list"></ul> + </div> + </div> + + <div id="export-buttons" class="export-buttons" style="display: none"> + <button id="copy-markdown" class="btn-export">Copy as Markdown</button> + <button id="copy-plaintext" class="btn-export">Copy as Plain Text</button> + </div> + + <div id="stats-error" class="error" style="display: none"> + <p id="error-message"></p> + </div> + </div> +</div> diff --git a/static/lastfm-week.sh b/static/lastfm-week.sh new file mode 100755 index 0000000..1d64688 --- /dev/null +++ b/static/lastfm-week.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +# Last.fm Weekly Stats Script +# Fetches your Last.fm listening statistics for the past week +# +# Requirements: +# - curl (for API requests) +# - jq (for JSON parsing) +# +# Usage: ./lastfm-week.sh +# +# Setup: +# Create a .env file with: +# LASTFM_API_KEY=your_api_key_here +# LASTFM_USERNAME=your_username_here +# +# Output: Markdown-formatted stats with top artists and track counts +# +# Download from: https://ritual.sh/resources/lastfm-stats/ + +# Load environment variables from .env file +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) +else + echo "Error: .env file not found" + exit 1 +fi + +# Check required variables +if [ -z "$LASTFM_API_KEY" ] || [ -z "$LASTFM_USERNAME" ]; then + echo "Error: LASTFM_API_KEY and LASTFM_USERNAME must be set in .env file" + exit 1 +fi + +API_BASE="http://ws.audioscrobbler.com/2.0/" + +# Get current timestamp +NOW=$(date +%s) +# Get timestamp from 7 days ago +WEEK_AGO=$((NOW - 604800)) + +# Fetch top artists for the week +TOP_ARTISTS=$(curl -s "${API_BASE}?method=user.gettopartists&user=${LASTFM_USERNAME}&api_key=${LASTFM_API_KEY}&format=json&period=7day&limit=5") + +# Fetch recent tracks to count this week's scrobbles +RECENT_TRACKS=$(curl -s "${API_BASE}?method=user.getrecenttracks&user=${LASTFM_USERNAME}&api_key=${LASTFM_API_KEY}&format=json&from=${WEEK_AGO}&to=${NOW}&limit=1") + +# Get total track count +TOTAL_TRACKS=$(echo "$RECENT_TRACKS" | jq -r '.recenttracks["@attr"].total') + +# Output in markdown format +echo "## Last.fm Weekly Stats" +echo "" +echo "**Total Tracks:** ${TOTAL_TRACKS}" +echo "" +echo "**Top 5 Artists:**" +echo "" + +# Parse and display top 5 artists as markdown links +echo "$TOP_ARTISTS" | jq -r '.topartists.artist[] | "- [\(.name)](\(.url)) - \(.playcount) plays"' \ No newline at end of file From 569f64e1da810048506ae1ca6c4bb7d10500ea68 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Wed, 7 Jan 2026 16:06:49 +0000 Subject: [PATCH 16/55] More resource section --- assets/sass/pages/resources.scss | 538 +++++++----------------- assets/sass/style.scss | 2 +- content/resources/_index.md | 2 + content/resources/lastfm-stats/index.md | 1 - content/resources/lavalamp/index.md | 1 - layouts/resources/list.html | 19 +- layouts/resources/summary.html | 27 +- 7 files changed, 168 insertions(+), 422 deletions(-) diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index fd17440..06ec85d 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -26,12 +26,12 @@ 0 0; &::before { - content: "TEST 19"; + content: "TEST 06"; position: absolute; top: 55%; right: 8%; transform: translateY(-50%); - font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-family: "Barlow Condensed", sans-serif; font-size: 6rem; font-weight: 700; color: rgba(255, 255, 255, 0.3); @@ -301,16 +301,124 @@ } } -.whiteboard { +.portal-sign { position: relative; padding: 3rem 2rem; + border: 10px solid black; + background: white; + + max-width: 900px; + margin: auto; + + box-shadow: + 0 8px 24px rgba(0, 0, 0, 0.6), + 0 0 40px rgba(0, 0, 0, 0.6); + @include media-up(md) { padding: 4rem 3rem; } + + .portal-sign-decor { + border-top: 10px solid black; + border-bottom: 10px solid black; + margin-left: 6rem; + position: relative; + + @include media-down(lg) { + margin-left: auto; + } + + .portal-sign-text { + p { + margin-bottom: 1rem; + } + + font-family: "caveat", cursive; + font-size: 1.5rem; + position: absolute; + text-align: right; + right: 0; + top: 50px; + width: 50%; + transform: rotate(10deg); + + @include media-down(md) { + position: relative; + transform: none; + top: auto; + left: auto; + width: 100%; + text-align: left; + margin-top: 1em; + } + } + + .portal-sign-number { + position: relative; + font-family: "Barlow Condensed", sans-serif; + font-size: 30rem; + + @include media-down(md) { + display: none; + } + } + + .portal-sign-sub { + font-family: "Barlow Condensed", sans-serif; + font-size: 3rem; + font-weight: bold; + + @include media-down(md) { + display: none; + } + } + } + + .portal-sign-lines { + margin-left: 6rem; + margin-top: 1rem; + padding-bottom: 5rem; + display: block; + position: relative; + border-bottom: 10px solid black; + + @include media-down(md) { + display: none; + } + + @include media-down(lg) { + margin-left: auto; + } + + &::before { + content: ""; + height: 50px; + width: 10px; + background-color: black; + display: block; + + /* repeat the line 5 more times */ + box-shadow: + 20px 0 0 black, + 40px 0 0 black, + 60px 0 0 black, + 80px 0 0 black, + 100px 0 0 black; + } + } + + .portal-sign-content { + margin-left: 6rem; + margin-top: 1rem; + + @include media-down(lg) { + margin-left: auto; + } + } } -.whiteboard-header { +.portal-header { text-align: center; margin-bottom: 4rem; position: relative; @@ -323,215 +431,58 @@ font-weight: 700; text-shadow: 0 0 20px rgba(0, 150, 255, 0.6); } - - .whiteboard-description { - font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; - font-size: 1.1rem; - font-weight: 400; - color: rgba(255, 255, 255, 0.9); - max-width: 700px; - margin: 0 auto; - line-height: 1.7; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); - - p { - color: rgba(255, 255, 255, 0.85); - margin: 0.5rem 0; - } - } } -.whiteboard-pins { +.portal-sign-items { display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - gap: 3rem; - position: relative; - z-index: 1; - - @include media-up(lg) { - grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); - } + grid-template-columns: 1fr 1fr 1fr; + gap: 1em; } -// Resource Pin (Post-it note style) -.resource-pin { - position: relative; - transform: rotate(0deg); - transition: - transform 0.3s ease, - box-shadow 0.3s ease; - - // Random slight rotations for pins - &:nth-child(3n + 1) { - transform: rotate(-1deg); - } - - &:nth-child(3n + 2) { - transform: rotate(1deg); - } - - &:nth-child(3n + 3) { - transform: rotate(-0.5deg); - } - - &:hover { - transform: rotate(0deg) translateY(-5px) !important; - box-shadow: 0 15px 30px rgba(0, 0, 0, 0.2); - z-index: 10; - - .pin-tack { - transform: translateY(-2px); - } - } +.resource-pin a { + color: #000; } -// Aperture Science indicator at the top -.pin-tack { - position: absolute; - top: -20px; - left: 50%; - transform: translateX(-50%); - width: 30px; - height: 30px; - background: radial-gradient( - circle at center, - rgba(255, 255, 255, 0.1) 0%, - rgba(255, 255, 255, 0.05) 50%, - transparent 100% - ); - border-radius: 50%; - border: 2px solid rgba(255, 255, 255, 0.3); - z-index: 2; - transition: all 0.3s ease; - - // Aperture Science logo segments - &::before { - content: ""; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 14px; - height: 14px; - border-radius: 50%; - border: 2px solid rgba(255, 255, 255, 0.6); - border-top-color: transparent; - border-left-color: transparent; - } - - &::after { - content: ""; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 6px; - height: 6px; - background: rgba(255, 255, 255, 0.8); - border-radius: 50%; - } -} - -// Resource panel (Aperture Science test chamber panel) .resource-card { - background: linear-gradient( - 145deg, - rgba(255, 255, 255, 0.95) 0%, - rgba(240, 245, 250, 0.9) 100% - ); padding: 2rem 1.5rem 1.5rem; - border-radius: 8px; - box-shadow: - 0 8px 24px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.9), - 0 0 40px rgba(0, 0, 0, 0.1); - min-height: 280px; + aspect-ratio: 1/1; display: flex; flex-direction: column; gap: 1rem; border: 3px solid; position: relative; - backdrop-filter: blur(10px); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; - // Vary border colors with Portal theme - .resource-pin:nth-child(4n + 1) & { - border-color: rgba(0, 150, 255, 0.5); - box-shadow: - 0 8px 24px rgba(0, 0, 0, 0.2), - 0 0 30px rgba(0, 150, 255, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - - &::before { - background: linear-gradient(180deg, rgba(0, 150, 255, 0.15) 0%, transparent 100%); - } - } - - .resource-pin:nth-child(4n + 2) & { - border-color: rgba(255, 120, 0, 0.5); - box-shadow: - 0 8px 24px rgba(0, 0, 0, 0.2), - 0 0 30px rgba(255, 120, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - - &::before { - background: linear-gradient(180deg, rgba(255, 120, 0, 0.15) 0%, transparent 100%); - } - } - - .resource-pin:nth-child(4n + 3) & { - border-color: rgba(0, 150, 255, 0.6); - box-shadow: - 0 8px 24px rgba(0, 0, 0, 0.2), - 0 0 35px rgba(0, 150, 255, 0.35), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - - &::before { - background: linear-gradient(180deg, rgba(0, 150, 255, 0.18) 0%, transparent 100%); - } - } - - .resource-pin:nth-child(4n + 4) & { - border-color: rgba(255, 120, 0, 0.6); - box-shadow: - 0 8px 24px rgba(0, 0, 0, 0.2), - 0 0 35px rgba(255, 120, 0, 0.35), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - - &::before { - background: linear-gradient(180deg, rgba(255, 120, 0, 0.18) 0%, transparent 100%); - } - } - - // Subtle gradient overlay - &::before { - content: ""; + .resource-info { position: absolute; top: 0; left: 0; right: 0; - height: 60%; - pointer-events: none; - border-radius: 6px 6px 0 0; - } + bottom: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; - // Tech panel grid pattern - &::after { - content: ""; - position: absolute; - inset: 0; - background-image: - linear-gradient(rgba(0, 0, 0, 0.02) 1px, transparent 1px), - linear-gradient(90deg, rgba(0, 0, 0, 0.02) 1px, transparent 1px); - background-size: 20px 20px; - pointer-events: none; - border-radius: 8px; - opacity: 0.5; + background: rgba(255, 255, 255, 0.6); + font-size: 2rem; + text-align: center; + font-family: "caveat", cursive; + + opacity: 0; + + &:hover { + opacity: 1; + } } } .resource-icon { - width: 80px; - height: 80px; + width: 100px; + height: 100px; margin: 0 auto 1rem; position: relative; @@ -541,13 +492,12 @@ 180deg, transparent 0%, transparent 20%, - #9333ea 20%, - #7c3aed 100% + #000 20%, + #000 100% ); border-radius: 10px 10px 40px 40px; - border: 3px solid #6b21a8; + border: 3px solid #000; position: relative; - box-shadow: inset 0 0 20px rgba(147, 51, 234, 0.3); // Lamp base &::before { @@ -556,7 +506,7 @@ bottom: -8px; left: 50%; transform: translateX(-50%); - width: 90%; + width: 70%; height: 12px; background: #1f2937; border-radius: 4px; @@ -570,16 +520,15 @@ left: 30%; width: 25px; height: 30px; - background: radial-gradient(circle, #ec4899 0%, #db2777 100%); + background: #fff; border-radius: 50% 50% 40% 60%; - opacity: 0.9; animation: float-blob 4s ease-in-out infinite; } } // Last.fm stats icon &.lastfm-stats { - background: linear-gradient(135deg, #d32323 0%, #b71c1c 100%); + background: black; border-radius: 8px; display: flex; align-items: center; @@ -612,44 +561,6 @@ } } -// Additional lava blob for lavalamp -.resource-icon.lavalamp { - & > span { - position: absolute; - top: 50%; - right: 25%; - width: 20px; - height: 25px; - background: radial-gradient(circle, #f97316 0%, #ea580c 100%); - border-radius: 60% 40% 50% 50%; - opacity: 0.85; - animation: float-blob-2 3.5s ease-in-out infinite; - } - - // Create the blob with a pseudo-element instead - &:not(:empty)::before { - // Override for when we add content - animation: float-blob 4s ease-in-out infinite; - } -} - -// Last.fm additional bars -.resource-icon.lastfm-stats { - & > span { - position: absolute; - bottom: 15px; - width: 8px; - background: #fff; - border-radius: 2px; - - &:nth-child(1) { - left: 50px; - height: 30px; - animation: equalizer-3 0.8s ease-in-out infinite 0.4s; - } - } -} - @keyframes float-blob { 0%, 100% { @@ -660,16 +571,6 @@ } } -@keyframes float-blob-2 { - 0%, - 100% { - transform: translateY(0) scale(1); - } - 50% { - transform: translateY(-20px) scale(0.9); - } -} - @keyframes equalizer-1 { 0%, 100% { @@ -690,153 +591,6 @@ } } -@keyframes equalizer-3 { - 0%, - 100% { - height: 30px; - } - 50% { - height: 15px; - } -} - -.resource-info { - flex: 1; - display: flex; - flex-direction: column; - position: relative; - z-index: 2; - - h2 { - font-size: 1.4rem; - margin: 0 0 0.5rem 0; - color: #1a1a1a; - font-weight: 700; - font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; - text-transform: uppercase; - letter-spacing: 0.08em; - - a { - color: inherit; - text-decoration: none; - transition: all 0.3s ease; - - &:hover { - color: #0096ff; - text-shadow: 0 0 15px rgba(0, 150, 255, 0.5); - } - } - } -} - -.resource-description { - color: #2c3e50; - font-size: 0.95rem; - line-height: 1.6; - margin: 0 0 1rem 0; - flex: 1; - font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; - position: relative; - z-index: 2; -} - -.resource-tags { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; - margin-bottom: 1rem; - position: relative; - z-index: 2; - - .tag { - background: linear-gradient(135deg, rgba(0, 150, 255, 0.2) 0%, rgba(0, 120, 200, 0.15) 100%); - padding: 0.35rem 0.9rem; - border-radius: 4px; - font-size: 0.7rem; - color: #0080dd; - text-decoration: none; - transition: all 0.3s ease; - border: 2px solid rgba(0, 150, 255, 0.4); - text-transform: uppercase; - letter-spacing: 0.08em; - font-weight: 700; - font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; - - &:hover { - background: linear-gradient(135deg, rgba(0, 150, 255, 0.3) 0%, rgba(255, 120, 0, 0.2) 100%); - border-color: rgba(0, 150, 255, 0.6); - color: #0096ff; - box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); - transform: translateY(-1px); - } - } -} - -.resource-links { - display: flex; - gap: 0.75rem; - margin-top: auto; - position: relative; - z-index: 2; - - .resource-link { - flex: 1; - padding: 0.7rem 1.3rem; - text-align: center; - text-decoration: none; - border-radius: 6px; - font-weight: 700; - font-size: 0.8rem; - transition: all 0.3s ease; - text-transform: uppercase; - letter-spacing: 0.1em; - font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; - border: 3px solid; - - &.demo { - background: linear-gradient(135deg, #0096ff 0%, #0075cc 100%); - color: #fff; - border-color: #0096ff; - box-shadow: - 0 4px 12px rgba(0, 150, 255, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.3); - - &:hover { - background: linear-gradient(135deg, #00aaff 0%, #0096ff 100%); - transform: translateY(-3px); - box-shadow: - 0 8px 20px rgba(0, 150, 255, 0.5), - 0 0 30px rgba(0, 150, 255, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.4); - border-color: #66ccff; - } - } - - &.source { - background: linear-gradient(135deg, #ff7800 0%, #dd6600 100%); - color: #fff; - border-color: #ff7800; - box-shadow: - 0 4px 12px rgba(255, 120, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.3); - - &:hover { - background: linear-gradient(135deg, #ff8800 0%, #ff7800 100%); - transform: translateY(-3px); - box-shadow: - 0 8px 20px rgba(255, 120, 0, 0.5), - 0 0 30px rgba(255, 120, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.4); - border-color: #ffaa66; - } - } - - &:active { - transform: translateY(0); - } - } -} - // Single Resource Page .resource-single { margin: 0 auto; diff --git a/assets/sass/style.scss b/assets/sass/style.scss index bdde189..b5a53f8 100644 --- a/assets/sass/style.scss +++ b/assets/sass/style.scss @@ -26,7 +26,7 @@ @import "pages/media"; @import "pages/resources"; -@import url("https://fonts.bunny.net/css2?family=Caveat:wght@400..700&family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Neonderthaw&display=swap"); +@import url(https://fonts.bunny.net/css?family=abel:400|barlow-condensed:400,500|caveat:400|lato:300,300i,400,400i|neonderthaw:400); @font-face { font-family: "DSEG7-Classic"; diff --git a/content/resources/_index.md b/content/resources/_index.md index d4c4af7..28c733f 100644 --- a/content/resources/_index.md +++ b/content/resources/_index.md @@ -4,3 +4,5 @@ draft: false --- Welcome to my test chamber, here you'll find various experiements, tests and resources. Take a look around. + +The cake isn't a lie. diff --git a/content/resources/lastfm-stats/index.md b/content/resources/lastfm-stats/index.md index 3cdbb31..d4b199b 100644 --- a/content/resources/lastfm-stats/index.md +++ b/content/resources/lastfm-stats/index.md @@ -1,7 +1,6 @@ --- title: "Last.fm Weekly Stats" date: 2026-01-04 -tags: ["javascript", "api", "last.fm"] description: "Fetch and display your weekly listening stats from Last.fm" icon: "lastfm-stats" demo_url: "" diff --git a/content/resources/lavalamp/index.md b/content/resources/lavalamp/index.md index eba821c..e18dc73 100644 --- a/content/resources/lavalamp/index.md +++ b/content/resources/lavalamp/index.md @@ -1,7 +1,6 @@ --- title: "HTML/CSS Lavalamp" date: 2026-01-04 -tags: ["css", "html", "animation"] description: "A pure CSS lavalamp animation with floating blobs" icon: "lavalamp" demo_url: "" diff --git a/layouts/resources/list.html b/layouts/resources/list.html index 02b5847..c33b3cf 100644 --- a/layouts/resources/list.html +++ b/layouts/resources/list.html @@ -12,13 +12,22 @@ <div class="portal-icon orange-portal hidden-md-down"></div> </div> - <div class="whiteboard"> - <div class="whiteboard-header"> - <div class="whiteboard-description">{{ .Content }}</div> + <div class="portal-sign"> + <div class="portal-sign-decor"> + <div class="portal-sign-text">{{ .Content }}</div> + <div class="portal-sign-number">06</div> + <div class="portal-sign-sub">06/19</div> </div> + <div class="portal-sign-lines"></div> - <div class="whiteboard-pins"> - {{ range .Pages }} {{ .Render "summary" }} {{ end }} + <div class="portal-sign-content"> + <div class="portal-sign-header"> + <div class="portal-description"></div> + </div> + + <div class="portal-sign-items"> + {{ range .Pages }} {{ .Render "summary" }} {{ end }} + </div> </div> </div> diff --git a/layouts/resources/summary.html b/layouts/resources/summary.html index 4962fdf..2896b2b 100644 --- a/layouts/resources/summary.html +++ b/layouts/resources/summary.html @@ -1,25 +1,8 @@ <div class="resource-pin" data-icon="{{ .Params.icon }}"> - <div class="pin-tack"></div> - <div class="resource-card"> - <div class="resource-icon {{ .Params.icon }}"></div> - <div class="resource-info"> - <h2><a href="{{ .Permalink }}">{{ .Title }}</a></h2> - <p class="resource-description">{{ .Params.description }}</p> - {{ if .Params.tags }} - <div class="resource-tags"> - {{ range .Params.tags }} - <span class="tag">{{ . }}</span> - {{ end }} - </div> - {{ end }} - <div class="resource-links"> - {{ if .Params.demo_url }} - <a href="{{ .Params.demo_url }}" class="resource-link demo" target="_blank">Demo</a> - {{ end }} - {{ if .Params.source_url }} - <a href="{{ .Params.source_url }}" class="resource-link source" target="_blank">Source</a> - {{ end }} - </div> + <a href="{{ .Permalink }}"> + <div class="resource-card"> + <div class="resource-icon {{ .Params.icon }}"></div> + <div class="resource-info">{{ .Title }}</div> </div> - </div> + </a> </div> From ea2b1d0072bb9658141dae9f5c290d206a86d121 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Wed, 7 Jan 2026 16:13:04 +0000 Subject: [PATCH 17/55] typo --- content/resources/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/resources/_index.md b/content/resources/_index.md index 28c733f..3d7aa1c 100644 --- a/content/resources/_index.md +++ b/content/resources/_index.md @@ -3,6 +3,6 @@ title: "Resources" draft: false --- -Welcome to my test chamber, here you'll find various experiements, tests and resources. Take a look around. +Welcome to my test chamber, here you'll find various experiments, tests and resources. Take a look around. The cake isn't a lie. From ae073fddce3977d03b2b000706dba2d30234126b Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Wed, 7 Jan 2026 19:29:50 +0000 Subject: [PATCH 18/55] Adding companion cube --- assets/sass/pages/homepage.scss | 45 +++- assets/sass/pages/resources.scss | 65 +----- assets/sass/partials/_companion-cube.scss | 198 ++++++++++++++++++ assets/sass/style.scss | 1 + layouts/index.html | 12 +- layouts/partials/elements/companion-cube.html | 20 ++ layouts/resources/list.html | 6 +- 7 files changed, 287 insertions(+), 60 deletions(-) create mode 100644 assets/sass/partials/_companion-cube.scss create mode 100644 layouts/partials/elements/companion-cube.html diff --git a/assets/sass/pages/homepage.scss b/assets/sass/pages/homepage.scss index fc7b75a..8be4488 100644 --- a/assets/sass/pages/homepage.scss +++ b/assets/sass/pages/homepage.scss @@ -171,7 +171,7 @@ grid-template-columns: 1fr 1fr; @include media-up(lg) { - grid-template-columns: repeat(4, 1fr); + grid-template-columns: repeat(5, 1fr); position: absolute; bottom: 5%; width: 100%; @@ -186,6 +186,49 @@ justify-content: center; } + .nav-cube { + width: 106.5px; + position: relative; + + &:hover { + .nav-cube-text { + opacity: 1; + } + } + + .heart-icon { + font-size: 1.5em; + } + + .nav-cube-text { + position: absolute; + display: block; + bottom: 0; + right: 0; + color: white; + font-size: 20px; + font-weight: bold; + z-index: 8000; + transform: rotate(-20deg); + border: 1px solid #0f0; + padding: 2px; + padding-left: 5px; + padding-right: 5px; + border-radius: 5px; + background-color: rgba(0, 0, 0, 0.7); + opacity: 0; + transition: opacity 0.3s ease; + text-align: center; + + @include media-down(lg) { + opacity: 1; + //transform: rotate(0deg); + bottom: 0; + font-size: 14px; + } + } + } + .time-display { width: 150px; margin: auto; diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 06ec85d..46983d9 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -186,63 +186,16 @@ } } -// Companion Cube -.companion-cube { - position: fixed; - bottom: 40px; - right: 40px; - width: 80px; - height: 80px; - background: linear-gradient( - 135deg, - rgba(200, 200, 200, 0.2) 0%, - rgba(150, 150, 150, 0.3) 100% - ); - border: 2px solid rgba(200, 200, 200, 0.4); - border-radius: 8px; - box-shadow: - 0 10px 30px rgba(0, 0, 0, 0.5), - inset 0 0 20px rgba(255, 255, 255, 0.1); - transform: rotateX(15deg) rotateY(-15deg); - transform-style: preserve-3d; - animation: cube-float 4s ease-in-out infinite; - position: relative; +.background-cube { + position: absolute; + left: 10%; + bottom: 10%; + width: 150px; + z-index: -1; + transform: rotate(5deg); - // Heart symbol in center - &::before { - content: "♥"; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - font-size: 32px; - color: rgba(255, 100, 150, 0.6); - text-shadow: 0 0 10px rgba(255, 100, 150, 0.8); - } - - // Corner circles - &::after { - content: ""; - position: absolute; - width: 12px; - height: 12px; - border-radius: 50%; - background: rgba(200, 200, 200, 0.4); - top: 8px; - left: 8px; - box-shadow: - 60px 0 0 rgba(200, 200, 200, 0.4), - 0 60px 0 rgba(200, 200, 200, 0.4), - 60px 60px 0 rgba(200, 200, 200, 0.4); - } - - @include media-up(md) { - width: 100px; - height: 100px; - - &::before { - font-size: 40px; - } + .heart-icon { + font-size: 2em; } } diff --git a/assets/sass/partials/_companion-cube.scss b/assets/sass/partials/_companion-cube.scss new file mode 100644 index 0000000..f3d983f --- /dev/null +++ b/assets/sass/partials/_companion-cube.scss @@ -0,0 +1,198 @@ +.companion-cube-container { + width: 100%; + aspect-ratio: 1/1; + display: flex; + align-items: center; + justify-content: center; + margin: 2rem auto; + position: relative; + + // Ground shadow beneath the cube + &::after { + content: ""; + position: absolute; + bottom: -30%; + left: 50%; + transform: translateX(-50%); + width: 250%; + height: 50%; + background: radial-gradient(ellipse, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 40%, transparent 70%); + border-radius: 50%; + filter: blur(8px); + z-index: -1; + + @include media-down(md) { + bottom: -4%; + filter: blur(6px); + } + + @include media-down(sm) { + bottom: -3%; + filter: blur(4px); + } + } +} + + +.companion-cube-face { + width: 100%; + height: 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, #e8e0d5 0%, #d0c8bd 50%, #beb6ab 100%); + border-radius: 8px; + box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.3); + + clip-path: polygon( + /* Top left corner */ + 0% 0%, + 0% 35%, + /* Left indent */ + 6% 37.5%, + 6% 62.5%, + 0% 65%, + /* Bottom left corner */ + 0% 100%, + 35% 100%, + /* Bottom indent */ + 37.5% 94%, + 62.5% 94%, + 65% 100%, + /* Bottom right corner */ + 100% 100%, + 100% 65%, + /* Right indent */ + 94% 62.5%, + 94% 37.5%, + 100% 35%, + /* Top right corner */ + 100% 0%, + 65% 0%, + /* Top indent */ + 62.5% 6%, + 37.5% 6%, + 35% 0% + ); + + @include media-down(md) { + border-radius: 6px; + } +} + +.cube-center-circle { + position: absolute; + width: 70%; + height: 70%; + background: linear-gradient(135deg, #6a6a6a 0%, #4a4a4a 50%, #3a3a3a 100%); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; +} + +.center-inner-ring { + position: absolute; + width: 60%; + height: 60%; + border: 2px solid #5a5a5a; + border-radius: 50%; + background: linear-gradient(135deg, #e8e0d5 0%, #d0c8bd 50%, #beb6ab 100%); + border-radius: 50%; + + @include media-down(md) { + border-width: 1.5px; + } +} + +.grey-bar { + position: absolute; + background-color: #5a5a5a; + + &.horizontal-top, &.horizontal-bottom { + height: 2.5%; + } + + &.vertical-left, &.vertical-right { + width: 2.5%; + } + + &.horizontal-top { + left: 0; right: 0; + top: 37.5%; + } + + &.horizontal-bottom { + left: 0; right: 0; + bottom: 37.5%; + } + + &.vertical-left { + left: 37.5%; + top: 0; bottom: 0; + } + + &.vertical-right { + right: 37.5%; + top: 0; bottom: 0; + } +} + +// Pink connector bars extending from center +.pink-bar { + position: absolute; + background: #ff80bf; + + &.vertical { + top: 0; bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 5%; + } + + &.horizontal { + top: 50%; + right: 0; left: 0; + transform: translateY(-50%); + height: 5%; + } + +} + +.heart-icon { + font-size: 5rem; + z-index: 3; + color: #ffc0e5; + pointer-events: none; + -webkit-text-stroke: 2px #8b5a6f; + animation: heartPulse 3s ease-in-out infinite; + + @include media-down(lg) { + font-size: 4rem; + } + + @include media-down(md) { + font-size: 3rem; + -webkit-text-stroke: 1.5px #8b5a6f; + } + + @include media-down(sm) { + font-size: 2rem; + -webkit-text-stroke: 1px #8b5a6f; + } +} + +// Pulsing heart animation +@keyframes heartPulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.3); + opacity: 0.85; + } +} + diff --git a/assets/sass/style.scss b/assets/sass/style.scss index b5a53f8..e165389 100644 --- a/assets/sass/style.scss +++ b/assets/sass/style.scss @@ -15,6 +15,7 @@ @import "partials/window"; @import "partials/crt-tv"; @import "partials/graphs"; +@import "partials/companion-cube"; @import "partials/content-screens"; diff --git a/layouts/index.html b/layouts/index.html index 3d3a5cb..8b05950 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -239,7 +239,7 @@ <div> <a href="/audio/"> <div class="music"> - <div class="music-text">MUSIC & AUDIO GEAR</div> + <div class="music-text">Music & Audio Gear</div> {{ partial "elements/ipod.html" . }} @@ -265,6 +265,16 @@ </a> </div> + <div> + <a href="/resources/"> + <div class="nav-cube"> + {{ partial "elements/companion-cube.html" . }} + <div class="nav-cube-text">Experiments & Resources</div> + </div> + </a> + </div> + + <div>{{ partial "elements/crt-tv.html" . }}</div> </div> diff --git a/layouts/partials/elements/companion-cube.html b/layouts/partials/elements/companion-cube.html new file mode 100644 index 0000000..741d192 --- /dev/null +++ b/layouts/partials/elements/companion-cube.html @@ -0,0 +1,20 @@ +<div class="companion-cube-container"> + <div class="companion-cube-face"> + + <div class="grey-bar horizontal-top"></div> + <div class="grey-bar horizontal-bottom"></div> + <div class="grey-bar vertical-left"></div> + <div class="grey-bar vertical-right"></div> + + + <div class="cube-center-circle"> + + <div class="pink-bar vertical"></div> + <div class="pink-bar horizontal"></div> + + <div class="center-inner-ring"></div> + + <div class="heart-icon">♥︎</div> + </div> + </div> +</div> diff --git a/layouts/resources/list.html b/layouts/resources/list.html index c33b3cf..b207e6f 100644 --- a/layouts/resources/list.html +++ b/layouts/resources/list.html @@ -31,7 +31,9 @@ </div> </div> - <div class="companion-cube"></div> - </div> + <div class="background-cube"> + {{ partial "elements/companion-cube.html" . }} + </div> + </div> </div> {{ end }} From 832bf2d34d44ed94549e7e8f53afa6737a21d3eb Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 09:17:23 +0000 Subject: [PATCH 19/55] Adding portal animation to cube --- assets/sass/pages/homepage.scss | 4 + assets/sass/pages/resources.scss | 8 +- assets/sass/partials/_companion-cube.scss | 180 +++++++++++++++--- layouts/partials/elements/companion-cube.html | 43 +++-- 4 files changed, 191 insertions(+), 44 deletions(-) diff --git a/assets/sass/pages/homepage.scss b/assets/sass/pages/homepage.scss index 8be4488..0aa1a23 100644 --- a/assets/sass/pages/homepage.scss +++ b/assets/sass/pages/homepage.scss @@ -200,6 +200,10 @@ font-size: 1.5em; } + .portal { + border-width: 3px; + } + .nav-cube-text { position: absolute; display: block; diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 46983d9..b7aa8a7 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -187,12 +187,12 @@ } .background-cube { - position: absolute; - left: 10%; - bottom: 10%; + width: 300px; + position: fixed; + left: 5%; + bottom: 5%; width: 150px; z-index: -1; - transform: rotate(5deg); .heart-icon { font-size: 2em; diff --git a/assets/sass/partials/_companion-cube.scss b/assets/sass/partials/_companion-cube.scss index f3d983f..a3babe0 100644 --- a/assets/sass/partials/_companion-cube.scss +++ b/assets/sass/partials/_companion-cube.scss @@ -16,7 +16,12 @@ transform: translateX(-50%); width: 250%; height: 50%; - background: radial-gradient(ellipse, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.5) 40%, transparent 70%); + background: radial-gradient( + ellipse, + rgba(0, 0, 0, 0.7) 0%, + rgba(0, 0, 0, 0.5) 40%, + transparent 70% + ); border-radius: 50%; filter: blur(8px); z-index: -1; @@ -33,6 +38,43 @@ } } +.cube-one, +.cube-two { + position: absolute; + top: 0; + left: 0; + width: 100%; + aspect-ratio: 1/1; + overflow: hidden; + z-index: 30; + + .companion-cube-face { + animation: dropCycle 24s cubic-bezier(0.68, -0.1, 0.265, 1.1) infinite; + } +} + +.cube-two .companion-cube-face { + animation-delay: -12s; +} + +@keyframes dropCycle { + 0% { + transform: translateY(-150%); + } + 12.5% { + transform: translateY(0); + } + 50% { + transform: translateY(0); + } + 62.5% { + transform: translateY(150%); + } + 62.51%, + 100% { + transform: translateY(-150%); + } +} .companion-cube-face { width: 100%; @@ -46,32 +88,24 @@ box-shadow: inset 0 2px 4px rgba(255, 255, 255, 0.3); clip-path: polygon( - /* Top left corner */ - 0% 0%, + /* Top left corner */ 0% 0%, 0% 35%, - /* Left indent */ - 6% 37.5%, + /* Left indent */ 6% 37.5%, 6% 62.5%, 0% 65%, - /* Bottom left corner */ - 0% 100%, + /* Bottom left corner */ 0% 100%, 35% 100%, - /* Bottom indent */ - 37.5% 94%, + /* Bottom indent */ 37.5% 94%, 62.5% 94%, 65% 100%, - /* Bottom right corner */ - 100% 100%, + /* Bottom right corner */ 100% 100%, 100% 65%, - /* Right indent */ - 94% 62.5%, + /* Right indent */ 94% 62.5%, 94% 37.5%, 100% 35%, - /* Top right corner */ - 100% 0%, + /* Top right corner */ 100% 0%, 65% 0%, - /* Top indent */ - 62.5% 6%, + /* Top indent */ 62.5% 6%, 37.5% 6%, 35% 0% ); @@ -111,32 +145,38 @@ position: absolute; background-color: #5a5a5a; - &.horizontal-top, &.horizontal-bottom { + &.horizontal-top, + &.horizontal-bottom { height: 2.5%; } - &.vertical-left, &.vertical-right { + &.vertical-left, + &.vertical-right { width: 2.5%; } &.horizontal-top { - left: 0; right: 0; + left: 0; + right: 0; top: 37.5%; } &.horizontal-bottom { - left: 0; right: 0; + left: 0; + right: 0; bottom: 37.5%; } &.vertical-left { left: 37.5%; - top: 0; bottom: 0; + top: 0; + bottom: 0; } - &.vertical-right { + &.vertical-right { right: 37.5%; - top: 0; bottom: 0; + top: 0; + bottom: 0; } } @@ -146,7 +186,8 @@ background: #ff80bf; &.vertical { - top: 0; bottom: 0; + top: 0; + bottom: 0; left: 50%; transform: translateX(-50%); width: 5%; @@ -154,11 +195,11 @@ &.horizontal { top: 50%; - right: 0; left: 0; + right: 0; + left: 0; transform: translateY(-50%); height: 5%; } - } .heart-icon { @@ -184,9 +225,91 @@ } } +.portal-icon { + width: 100%; + aspect-ratio: 1/1; + position: relative; + overflow: visible; + z-index: 1; + + .portal { + content: ""; + position: absolute; + + transform: translateX(-10%) scaleY(0); + width: 120%; + height: 20%; + + border-radius: 50%; + border: 8px solid; + + background: radial-gradient( + ellipse at center, + rgba(0, 0, 0, 0.95) 0%, + rgba(0, 0, 0, 0.85) 60%, + rgba(0, 0, 0, 0.6) 100% + ); + + @include media-down(lg) { + border-width: 6px; + } + + @include media-down(md) { + border-width: 4px; + } + + @include media-down(sm) { + border-width: 2px; + } + } + + .blue-portal { + bottom: -10%; + z-index: 20; + + border-color: #0096ff; + box-shadow: + inset 0 0 40px rgba(0, 150, 255, 0.3), + inset 0 0 20px rgba(0, 150, 255, 0.5); + animation: portalCycle 24s cubic-bezier(0.68, -0.1, 0.265, 1.1) infinite; + } + + .orange-portal { + top: -10%; + z-index: 40; + border-color: #ff7800; + box-shadow: + 0 0 30px 8px rgba(255, 120, 0, 0.8), + 0 0 60px 12px rgba(255, 120, 0, 0.5), + inset 0 0 30px rgba(255, 120, 0, 0.6); + animation: portalCycle 24s cubic-bezier(0.68, -0.1, 0.265, 1.1) infinite; + animation-delay: 0.2s; + } +} + +@keyframes portalCycle { + 0%, + 12% { + transform: translateX(-10%) scaleY(1); + } + 12.5%, + 48% { + transform: translateX(-10%) scaleY(0); + } + 49%, + 60.5% { + transform: translateX(-10%) scaleY(1); + } + 61%, + 100% { + transform: translateX(-10%) scaleY(0); + } +} + // Pulsing heart animation @keyframes heartPulse { - 0%, 100% { + 0%, + 100% { transform: scale(1); opacity: 1; } @@ -195,4 +318,3 @@ opacity: 0.85; } } - diff --git a/layouts/partials/elements/companion-cube.html b/layouts/partials/elements/companion-cube.html index 741d192..0e6327a 100644 --- a/layouts/partials/elements/companion-cube.html +++ b/layouts/partials/elements/companion-cube.html @@ -1,20 +1,41 @@ <div class="companion-cube-container"> - <div class="companion-cube-face"> + <div class="portal-icon"> + <div class="portal blue-portal"></div> + <div class="portal orange-portal"></div> + </div> + <div class="cube-one"> + <div class="companion-cube-face"> + <div class="grey-bar horizontal-top"></div> + <div class="grey-bar horizontal-bottom"></div> + <div class="grey-bar vertical-left"></div> + <div class="grey-bar vertical-right"></div> - <div class="grey-bar horizontal-top"></div> - <div class="grey-bar horizontal-bottom"></div> - <div class="grey-bar vertical-left"></div> - <div class="grey-bar vertical-right"></div> + <div class="cube-center-circle"> + <div class="pink-bar vertical"></div> + <div class="pink-bar horizontal"></div> + <div class="center-inner-ring"></div> - <div class="cube-center-circle"> + <div class="heart-icon">♥︎</div> + </div> + </div> + </div> - <div class="pink-bar vertical"></div> - <div class="pink-bar horizontal"></div> - - <div class="center-inner-ring"></div> + <div class="cube-two"> + <div class="companion-cube-face"> + <div class="grey-bar horizontal-top"></div> + <div class="grey-bar horizontal-bottom"></div> + <div class="grey-bar vertical-left"></div> + <div class="grey-bar vertical-right"></div> - <div class="heart-icon">♥︎</div> + <div class="cube-center-circle"> + <div class="pink-bar vertical"></div> + <div class="pink-bar horizontal"></div> + + <div class="center-inner-ring"></div> + + <div class="heart-icon">♥︎</div> + </div> </div> </div> </div> From d22d127caccbf4cc60ef0c5efd483424b00fe47a Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 09:26:13 +0000 Subject: [PATCH 20/55] Adding update --- content/updates/2026-01-08-resources-section.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 content/updates/2026-01-08-resources-section.md diff --git a/content/updates/2026-01-08-resources-section.md b/content/updates/2026-01-08-resources-section.md new file mode 100644 index 0000000..cf28c43 --- /dev/null +++ b/content/updates/2026-01-08-resources-section.md @@ -0,0 +1,10 @@ +--- +title: "2026 01 08 Resources Section" +date: 2026-01-08T09:24:45Z +tags: [] +description: "" +build: + render: never +--- + +Added the resources section with a tool to pull weekly listening stats from last.fm From bdee635df0671a0452f77f5fbc38dfb268418084 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 12:04:45 +0000 Subject: [PATCH 21/55] Adding button generator --- assets/js/button-generator.js | 594 ++++++++++++++++++ assets/sass/pages/button-generator.scss | 503 +++++++++++++++ assets/sass/pages/homepage.scss | 3 +- assets/sass/pages/resources.scss | 12 +- assets/sass/style.scss | 1 + .../2026-01-06-week-3-bit-chilly-out/index.md | 2 + content/resources/button-generator/index.md | 23 + layouts/_default/baseof.html | 6 + layouts/partials/site-scripts.html | 2 +- layouts/resources/single.html | 3 + layouts/shortcodes/button-generator.html | 315 ++++++++++ 11 files changed, 1458 insertions(+), 6 deletions(-) create mode 100644 assets/js/button-generator.js create mode 100644 assets/sass/pages/button-generator.scss create mode 100644 content/resources/button-generator/index.md create mode 100644 layouts/shortcodes/button-generator.html diff --git a/assets/js/button-generator.js b/assets/js/button-generator.js new file mode 100644 index 0000000..62acb20 --- /dev/null +++ b/assets/js/button-generator.js @@ -0,0 +1,594 @@ +(function () { + const canvas = document.getElementById("button-canvas"); + const ctx = canvas.getContext("2d"); + + // Preload all web fonts for canvas rendering + const fonts = [ + "Lato", + "Roboto", + "Open Sans", + "Montserrat", + "Oswald", + "Bebas Neue", + "Roboto Mono", + "VT323", + "Press Start 2P", + "DSEG7-Classic", + ]; + + // Load fonts using CSS Font Loading API + const fontPromises = fonts.flatMap((font) => [ + document.fonts.load(`400 12px "${font}"`), + document.fonts.load(`700 12px "${font}"`), + document.fonts.load(`italic 400 12px "${font}"`), + ]); + + Promise.all(fontPromises).then(() => { + console.log("All fonts loaded for canvas"); + drawButton(); + }); + + // Collapsible sections functionality + document.querySelectorAll(".control-group-header").forEach((header) => { + header.addEventListener("click", () => { + const controlGroup = header.closest(".control-group"); + controlGroup.classList.toggle("collapsed"); + }); + }); + + // Get all controls + const controls = { + text: document.getElementById("button-text"), + textEnabled: document.getElementById("text-enabled"), + fontSize: document.getElementById("font-size"), + textX: document.getElementById("text-x"), + textY: document.getElementById("text-y"), + textColorType: document.getElementById("text-color-type"), + textColor: document.getElementById("text-color"), + textGradientColor1: document.getElementById("text-gradient-color1"), + textGradientColor2: document.getElementById("text-gradient-color2"), + textGradientAngle: document.getElementById("text-gradient-angle"), + textOutline: document.getElementById("text-outline"), + outlineColor: document.getElementById("outline-color"), + fontFamily: document.getElementById("font-family"), + fontBold: document.getElementById("font-bold"), + fontItalic: document.getElementById("font-italic"), + text2: document.getElementById("button-text2"), + text2Enabled: document.getElementById("text2-enabled"), + fontSize2: document.getElementById("font-size2"), + text2X: document.getElementById("text2-x"), + text2Y: document.getElementById("text2-y"), + text2ColorType: document.getElementById("text2-color-type"), + text2Color: document.getElementById("text2-color"), + text2GradientColor1: document.getElementById("text2-gradient-color1"), + text2GradientColor2: document.getElementById("text2-gradient-color2"), + text2GradientAngle: document.getElementById("text2-gradient-angle"), + text2Outline: document.getElementById("text2-outline"), + outline2Color: document.getElementById("outline2-color"), + fontFamily2: document.getElementById("font-family2"), + fontBold2: document.getElementById("font-bold2"), + fontItalic2: document.getElementById("font-italic2"), + bgType: document.getElementById("bg-type"), + bgColor: document.getElementById("bg-color"), + gradientColor1: document.getElementById("gradient-color1"), + gradientColor2: document.getElementById("gradient-color2"), + gradientAngle: document.getElementById("gradient-angle"), + textureType: document.getElementById("texture-type"), + textureColor1: document.getElementById("texture-color1"), + textureColor2: document.getElementById("texture-color2"), + textureScale: document.getElementById("texture-scale"), + borderWidth: document.getElementById("border-width"), + borderColor: document.getElementById("border-color"), + borderStyle: document.getElementById("border-style"), + }; + + // Update value displays + const updateValueDisplay = (id, value) => { + const display = document.getElementById(id + "-value"); + if (display) display.textContent = value; + }; + + // Show/hide controls based on background type + controls.bgType.addEventListener("change", () => { + const solidControls = document.getElementById("solid-controls"); + const gradientControls = document.getElementById("gradient-controls"); + const textureControls = document.getElementById("texture-controls"); + + solidControls.style.display = "none"; + gradientControls.style.display = "none"; + textureControls.style.display = "none"; + + if (controls.bgType.value === "solid") { + solidControls.style.display = "block"; + } else if (controls.bgType.value === "gradient") { + gradientControls.style.display = "block"; + } else if (controls.bgType.value === "texture") { + textureControls.style.display = "block"; + } + + drawButton(); + }); + + // Show/hide text color controls + controls.textColorType.addEventListener("change", () => { + const textSolidColor = document.getElementById("text-solid-color"); + const textGradientColor = document.getElementById("text-gradient-color"); + + if (controls.textColorType.value === "solid") { + textSolidColor.style.display = "block"; + textGradientColor.style.display = "none"; + } else { + textSolidColor.style.display = "none"; + textGradientColor.style.display = "block"; + } + drawButton(); + }); + + controls.text2ColorType.addEventListener("change", () => { + const text2SolidColor = document.getElementById("text2-solid-color"); + const text2GradientColor = document.getElementById("text2-gradient-color"); + + if (controls.text2ColorType.value === "solid") { + text2SolidColor.style.display = "block"; + text2GradientColor.style.display = "none"; + } else { + text2SolidColor.style.display = "none"; + text2GradientColor.style.display = "block"; + } + drawButton(); + }); + + // Show/hide outline color + controls.textOutline.addEventListener("change", () => { + controls.outlineColor.style.display = controls.textOutline.checked + ? "block" + : "none"; + drawButton(); + }); + + controls.text2Outline.addEventListener("change", () => { + controls.outline2Color.style.display = controls.text2Outline.checked + ? "block" + : "none"; + drawButton(); + }); + + // Draw texture patterns + function drawTexture(type, color1, color2, scale) { + const tempCanvas = document.createElement("canvas"); + tempCanvas.width = 88; + tempCanvas.height = 31; + const tempCtx = tempCanvas.getContext("2d"); + + tempCtx.fillStyle = color1; + tempCtx.fillRect(0, 0, 88, 31); + + tempCtx.fillStyle = color2; + const size = Math.max(2, Math.floor(scale / 10)); + + switch (type) { + case "dots": + for (let y = 0; y < 31; y += size * 2) { + for (let x = 0; x < 88; x += size * 2) { + tempCtx.beginPath(); + tempCtx.arc(x, y, size / 2, 0, Math.PI * 2); + tempCtx.fill(); + } + } + break; + case "grid": + for (let x = 0; x < 88; x += size) { + tempCtx.fillRect(x, 0, 1, 31); + } + for (let y = 0; y < 31; y += size) { + tempCtx.fillRect(0, y, 88, 1); + } + break; + case "diagonal": + for (let i = -31; i < 88; i += size) { + tempCtx.fillRect(i, 0, 2, 31); + tempCtx.save(); + tempCtx.translate(i + 1, 0); + tempCtx.rotate(Math.PI / 4); + tempCtx.fillRect(0, 0, 2, 100); + tempCtx.restore(); + } + break; + case "checkerboard": + for (let y = 0; y < 31; y += size) { + for (let x = 0; x < 88; x += size) { + if ((x / size + y / size) % 2 === 0) { + tempCtx.fillRect(x, y, size, size); + } + } + } + break; + case "noise": + for (let y = 0; y < 31; y++) { + for (let x = 0; x < 88; x++) { + if (Math.random() > 0.5) { + tempCtx.fillRect(x, y, 1, 1); + } + } + } + break; + case "stars": + for (let i = 0; i < scale; i++) { + const x = Math.floor(Math.random() * 88); + const y = Math.floor(Math.random() * 31); + tempCtx.fillRect(x, y, 1, 1); + tempCtx.fillRect(x - 1, y, 1, 1); + tempCtx.fillRect(x + 1, y, 1, 1); + tempCtx.fillRect(x, y - 1, 1, 1); + tempCtx.fillRect(x, y + 1, 1, 1); + } + break; + } + + return tempCanvas; + } + + // Helper function to draw to a single context + function drawToContext(context) { + context.clearRect(0, 0, 88, 31); + + // Draw background + if (controls.bgType.value === "solid") { + context.fillStyle = controls.bgColor.value; + context.fillRect(0, 0, 88, 31); + } else if (controls.bgType.value === "gradient") { + const angle = parseFloat(controls.gradientAngle.value) * (Math.PI / 180); + const x1 = 44 + Math.cos(angle) * 44; + const y1 = 15.5 + Math.sin(angle) * 15.5; + const x2 = 44 - Math.cos(angle) * 44; + const y2 = 15.5 - Math.sin(angle) * 15.5; + + const gradient = context.createLinearGradient(x1, y1, x2, y2); + gradient.addColorStop(0, controls.gradientColor1.value); + gradient.addColorStop(1, controls.gradientColor2.value); + context.fillStyle = gradient; + context.fillRect(0, 0, 88, 31); + } else if (controls.bgType.value === "texture") { + const texture = drawTexture( + controls.textureType.value, + controls.textureColor1.value, + controls.textureColor2.value, + parseFloat(controls.textureScale.value), + ); + context.drawImage(texture, 0, 0); + } + + // Draw border + const borderWidth = parseFloat(controls.borderWidth.value); + if (borderWidth > 0) { + const style = controls.borderStyle.value; + + if (style === "solid") { + context.strokeStyle = controls.borderColor.value; + context.lineWidth = borderWidth; + context.strokeRect( + borderWidth / 2, + borderWidth / 2, + 88 - borderWidth, + 31 - borderWidth, + ); + } else if (style === "inset" || style === "outset") { + const light = style === "outset"; + context.strokeStyle = light ? "#ffffff" : "#000000"; + context.lineWidth = borderWidth; + context.beginPath(); + context.moveTo(0, 31); + context.lineTo(0, 0); + context.lineTo(88, 0); + context.stroke(); + + context.strokeStyle = light ? "#000000" : "#ffffff"; + context.beginPath(); + context.moveTo(88, 0); + context.lineTo(88, 31); + context.lineTo(0, 31); + context.stroke(); + } else if (style === "ridge") { + context.strokeStyle = "#ffffff"; + context.lineWidth = borderWidth / 2; + context.strokeRect( + borderWidth / 4, + borderWidth / 4, + 88 - borderWidth / 2, + 31 - borderWidth / 2, + ); + + context.strokeStyle = "#000000"; + context.strokeRect( + (borderWidth * 3) / 4, + (borderWidth * 3) / 4, + 88 - borderWidth * 1.5, + 31 - borderWidth * 1.5, + ); + } + } + + // Draw text line 1 + const text = controls.text.value; + if (text && controls.textEnabled.checked) { + const fontSize = parseFloat(controls.fontSize.value); + const fontWeight = controls.fontBold.checked ? "bold" : "normal"; + const fontStyle = controls.fontItalic.checked ? "italic" : "normal"; + const fontFamily = controls.fontFamily.value; + + context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; + context.textAlign = "center"; + context.textBaseline = "middle"; + + const x = (parseFloat(controls.textX.value) / 100) * 88; + const y = (parseFloat(controls.textY.value) / 100) * 31; + + if (controls.textOutline.checked) { + context.strokeStyle = controls.outlineColor.value; + context.lineWidth = 2; + context.strokeText(text, x, y); + } + + // Apply text color or gradient + if (controls.textColorType.value === "solid") { + context.fillStyle = controls.textColor.value; + } else { + const angle = + parseFloat(controls.textGradientAngle.value) * (Math.PI / 180); + const textWidth = context.measureText(text).width; + const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2; + const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2; + const x2 = x + textWidth / 2 - (Math.cos(angle) * textWidth) / 2; + const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2; + + const textGradient = context.createLinearGradient(x1, y1, x2, y2); + textGradient.addColorStop(0, controls.textGradientColor1.value); + textGradient.addColorStop(1, controls.textGradientColor2.value); + context.fillStyle = textGradient; + } + + context.fillText(text, x, y); + } + + // Draw text line 2 + const text2 = controls.text2.value; + if (text2 && controls.text2Enabled.checked) { + const fontSize2 = parseFloat(controls.fontSize2.value); + const fontWeight2 = controls.fontBold2.checked ? "bold" : "normal"; + const fontStyle2 = controls.fontItalic2.checked ? "italic" : "normal"; + const fontFamily2 = controls.fontFamily2.value; + + context.font = `${fontStyle2} ${fontWeight2} ${fontSize2}px "${fontFamily2}"`; + context.textAlign = "center"; + context.textBaseline = "middle"; + + const x2 = (parseFloat(controls.text2X.value) / 100) * 88; + const y2 = (parseFloat(controls.text2Y.value) / 100) * 31; + + if (controls.text2Outline.checked) { + context.strokeStyle = controls.outline2Color.value; + context.lineWidth = 2; + context.strokeText(text2, x2, y2); + } + + // Apply text color or gradient + if (controls.text2ColorType.value === "solid") { + context.fillStyle = controls.text2Color.value; + } else { + const angle2 = + parseFloat(controls.text2GradientAngle.value) * (Math.PI / 180); + const text2Width = context.measureText(text2).width; + const x1_2 = x2 - text2Width / 2 + (Math.cos(angle2) * text2Width) / 2; + const y1_2 = y2 - fontSize2 / 2 + (Math.sin(angle2) * fontSize2) / 2; + const x2_2 = x2 + text2Width / 2 - (Math.cos(angle2) * text2Width) / 2; + const y2_2 = y2 + fontSize2 / 2 - (Math.sin(angle2) * fontSize2) / 2; + + const text2Gradient = context.createLinearGradient( + x1_2, + y1_2, + x2_2, + y2_2, + ); + text2Gradient.addColorStop(0, controls.text2GradientColor1.value); + text2Gradient.addColorStop(1, controls.text2GradientColor2.value); + context.fillStyle = text2Gradient; + } + + context.fillText(text2, x2, y2); + } + } + + // Main draw function + function drawButton() { + drawToContext(ctx); + } + + // Download function + document.getElementById("download-button").addEventListener("click", () => { + const link = document.createElement("a"); + link.download = "button-88x31.png"; + link.href = canvas.toDataURL(); + link.click(); + }); + + // Preset buttons + document.getElementById("preset-random").addEventListener("click", () => { + const randomColor = () => + "#" + + Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, "0"); + + // Random background + controls.bgType.value = ["solid", "gradient", "texture"][ + Math.floor(Math.random() * 3) + ]; + controls.bgColor.value = randomColor(); + controls.gradientColor1.value = randomColor(); + controls.gradientColor2.value = randomColor(); + controls.gradientAngle.value = Math.floor(Math.random() * 360); + controls.textureColor1.value = randomColor(); + controls.textureColor2.value = randomColor(); + controls.textureType.value = [ + "dots", + "grid", + "diagonal", + "checkerboard", + "noise", + "stars", + ][Math.floor(Math.random() * 6)]; + + // Random text 1 color (50% chance of gradient) + controls.textColorType.value = Math.random() > 0.5 ? "gradient" : "solid"; + controls.textColor.value = randomColor(); + controls.textGradientColor1.value = randomColor(); + controls.textGradientColor2.value = randomColor(); + controls.textGradientAngle.value = Math.floor(Math.random() * 360); + + // Random text 2 color (50% chance of gradient) + controls.text2ColorType.value = Math.random() > 0.5 ? "gradient" : "solid"; + controls.text2Color.value = randomColor(); + controls.text2GradientColor1.value = randomColor(); + controls.text2GradientColor2.value = randomColor(); + controls.text2GradientAngle.value = Math.floor(Math.random() * 360); + + // Random border + controls.borderColor.value = randomColor(); + controls.borderWidth.value = Math.floor(Math.random() * 6); + controls.borderStyle.value = ["solid", "inset", "outset", "ridge"][ + Math.floor(Math.random() * 4) + ]; + + // Random text styles + controls.fontBold.checked = Math.random() > 0.5; + controls.fontItalic.checked = Math.random() > 0.5; + controls.fontBold2.checked = Math.random() > 0.5; + controls.fontItalic2.checked = Math.random() > 0.5; + + // Update displays + updateValueDisplay("gradient-angle", controls.gradientAngle.value); + updateValueDisplay("text-gradient-angle", controls.textGradientAngle.value); + updateValueDisplay( + "text2-gradient-angle", + controls.text2GradientAngle.value, + ); + updateValueDisplay("border-width", controls.borderWidth.value); + + controls.bgType.dispatchEvent(new Event("change")); + controls.textColorType.dispatchEvent(new Event("change")); + controls.text2ColorType.dispatchEvent(new Event("change")); + drawButton(); + }); + + document.getElementById("preset-classic").addEventListener("click", () => { + // Classic 90s web button style + controls.bgType.value = "gradient"; + controls.gradientColor1.value = "#0066cc"; + controls.gradientColor2.value = "#0099ff"; + controls.gradientAngle.value = 90; + + controls.textColorType.value = "solid"; + controls.textColor.value = "#ffffff"; + controls.text2ColorType.value = "solid"; + controls.text2Color.value = "#ffffff"; + + controls.borderWidth.value = 2; + controls.borderColor.value = "#000000"; + controls.borderStyle.value = "outset"; + + controls.fontFamily.value = "Oswald"; + controls.fontFamily2.value = "Lato"; + controls.fontBold.checked = true; + controls.fontBold2.checked = false; + controls.fontItalic.checked = false; + controls.fontItalic2.checked = false; + + controls.text.value = "RITUAL.SH"; + controls.text2.value = "FREE THE WEB"; + controls.textEnabled.checked = true; + controls.text2Enabled.checked = true; + controls.fontSize.value = 12; + controls.fontSize2.value = 8; + controls.textY.value = 35; + controls.text2Y.value = 65; + + updateValueDisplay("font-size", 12); + updateValueDisplay("font-size2", 8); + updateValueDisplay("gradient-angle", 90); + updateValueDisplay("text-y", 35); + updateValueDisplay("text2-y", 65); + + controls.bgType.dispatchEvent(new Event("change")); + controls.textColorType.dispatchEvent(new Event("change")); + controls.text2ColorType.dispatchEvent(new Event("change")); + drawButton(); + }); + + document.getElementById("preset-modern").addEventListener("click", () => { + // Modern cyberpunk style with gradient text + controls.bgType.value = "gradient"; + controls.gradientColor1.value = "#0a0a0a"; + controls.gradientColor2.value = "#1a0a2e"; + controls.gradientAngle.value = 135; + + controls.textColorType.value = "gradient"; + controls.textGradientColor1.value = "#00ffaa"; + controls.textGradientColor2.value = "#00ffff"; + controls.textGradientAngle.value = 90; + + controls.text2ColorType.value = "gradient"; + controls.text2GradientColor1.value = "#ff00ff"; + controls.text2GradientColor2.value = "#ff6600"; + controls.text2GradientAngle.value = 0; + + controls.borderWidth.value = 1; + controls.borderColor.value = "#00ffaa"; + controls.borderStyle.value = "solid"; + + controls.fontFamily.value = "Roboto Mono"; + controls.fontFamily2.value = "Roboto Mono"; + controls.fontBold.checked = true; + controls.fontBold2.checked = false; + controls.fontItalic.checked = false; + controls.fontItalic2.checked = false; + + controls.text.value = "RITUAL.SH"; + controls.text2.value = "EST. 2024"; + controls.textEnabled.checked = true; + controls.text2Enabled.checked = true; + controls.fontSize.value = 11; + controls.fontSize2.value = 9; + controls.textY.value = 35; + controls.text2Y.value = 65; + + updateValueDisplay("font-size", 11); + updateValueDisplay("font-size2", 9); + updateValueDisplay("gradient-angle", 135); + updateValueDisplay("text-gradient-angle", 90); + updateValueDisplay("text2-gradient-angle", 0); + updateValueDisplay("text-y", 35); + updateValueDisplay("text2-y", 65); + + controls.textColorType.dispatchEvent(new Event("change")); + controls.text2ColorType.dispatchEvent(new Event("change")); + controls.bgType.dispatchEvent(new Event("change")); + drawButton(); + }); + + // Add event listeners to all controls + Object.values(controls).forEach((control) => { + if (control) { + control.addEventListener("input", drawButton); + control.addEventListener("change", drawButton); + + if (control.type === "range") { + control.addEventListener("input", (e) => { + updateValueDisplay(e.target.id, e.target.value); + }); + } + } + }); + + // Initial draw + drawButton(); +})(); diff --git a/assets/sass/pages/button-generator.scss b/assets/sass/pages/button-generator.scss new file mode 100644 index 0000000..a6a17ec --- /dev/null +++ b/assets/sass/pages/button-generator.scss @@ -0,0 +1,503 @@ +@import url(https://fonts.bunny.net/css?family=bebas-neue:400|lato:400,400i,700,700i|montserrat:400,400i,700,700i|open-sans:400,400i,700,700i|oswald:400,700|press-start-2p:400|roboto:400,400i,700,700i|roboto-mono:400,400i,700,700i|vt323:400); + +#button-generator-app { + margin: 2rem 0; + padding: 2rem; + background: linear-gradient( + 145deg, + rgba(5, 15, 30, 0.9) 0%, + rgba(10, 20, 40, 0.95) 100% + ); + border-radius: 6px; + border: 1px solid rgba(0, 150, 255, 0.4); + box-shadow: + 0 0 30px rgba(0, 150, 255, 0.15), + 0 0 50px rgba(255, 120, 0, 0.08), + inset 0 0 60px rgba(0, 100, 200, 0.05); + position: relative; + overflow: visible; + + &::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 3px; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.6), + transparent + ); + } + + .generator-container { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 2rem; + margin-top: 1rem; + position: relative; + z-index: 1; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } + } + + .preview-section { + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.15) 0%, + rgba(0, 80, 150, 0.1) 100% + ); + padding: 1.5rem; + border-radius: 6px; + border: 1px solid rgba(0, 150, 255, 0.3); + display: flex; + flex-direction: column; + gap: 1rem; + position: sticky; + top: 1rem; + align-self: flex-start; + max-height: calc(100vh - 2rem); + overflow-y: auto; + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.1), + inset 0 0 40px rgba(0, 100, 200, 0.05); + + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 4px; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.7), + rgba(255, 120, 0, 0.5) + ); + border-radius: 6px 0 0 6px; + } + + h3 { + margin-top: 0; + color: #0096ff; + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + text-shadow: 0 0 15px rgba(0, 150, 255, 0.5); + font-size: 1.2rem; + } + } + + .presets-container { + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.5), + transparent + ) + 1; + + h3 { + margin-top: 0; + margin-bottom: 1rem; + color: rgba(100, 180, 255, 0.9); + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.4); + font-size: 1rem; + } + } + + .preview-container { + display: flex; + flex-direction: column; + gap: 1rem; + } + + .preview-wrapper { + background: repeating-conic-gradient( + rgba(20, 30, 50, 0.8) 0% 25%, + rgba(10, 20, 40, 0.9) 0% 50% + ) + 50% / 20px 20px; + padding: 2rem; + display: flex; + justify-content: center; + align-items: center; + border-radius: 4px; + border: 1px solid rgba(0, 150, 255, 0.2); + } + + #button-canvas { + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + border: 1px solid rgba(0, 150, 255, 0.4); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.2); + } + + .controls-section { + display: flex; + flex-direction: column; + gap: 1.5rem; + } + + .control-group { + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.15) 0%, + rgba(0, 80, 150, 0.1) 100% + ); + padding: 1.5rem; + border-radius: 6px; + border: 1px solid rgba(0, 150, 255, 0.3); + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.1), + inset 0 0 40px rgba(0, 100, 200, 0.05); + position: relative; + + &::before { + content: ""; + position: absolute; + left: 0; + top: 0; + width: 3px; + height: 100%; + background: linear-gradient( + 180deg, + rgba(0, 150, 255, 0.7), + rgba(255, 120, 0, 0.5) + ); + opacity: 0; + transition: opacity 0.3s ease; + } + + &:hover::before { + opacity: 1; + } + + h3 { + margin-top: 0; + margin-bottom: 1rem; + } + + label { + display: block; + margin-top: 0.75rem; + margin-bottom: 0.25rem; + font-weight: 500; + font-size: 0.85rem; + color: rgba(100, 180, 255, 0.8); + text-transform: uppercase; + letter-spacing: 0.05em; + } + + input[type="text"], + select { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + font-size: 0.9rem; + background: rgba(5, 15, 30, 0.7); + color: rgba(200, 220, 255, 0.95); + transition: all 0.3s ease; + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.3); + + &:focus { + outline: none; + border-color: rgba(0, 150, 255, 0.7); + box-shadow: + 0 0 15px rgba(0, 150, 255, 0.3), + inset 0 2px 8px rgba(0, 0, 0, 0.4); + background: rgba(5, 15, 30, 0.9); + } + } + + select { + cursor: pointer; + + option { + background: rgba(5, 15, 30, 0.95); + color: rgba(200, 220, 255, 0.95); + } + } + + // Font preview in dropdowns + select#font-family, + select#font-family2 { + option[value="Lato"] { + font-family: "Lato", sans-serif; + } + option[value="Roboto"] { + font-family: "Roboto", sans-serif; + } + option[value="Open Sans"] { + font-family: "Open Sans", sans-serif; + } + option[value="Montserrat"] { + font-family: "Montserrat", sans-serif; + } + option[value="Oswald"] { + font-family: "Oswald", sans-serif; + } + option[value="Bebas Neue"] { + font-family: "Bebas Neue", display; + } + option[value="Roboto Mono"] { + font-family: "Roboto Mono", monospace; + } + option[value="VT323"] { + font-family: "VT323", monospace; + } + option[value="Press Start 2P"] { + font-family: "Press Start 2P", display; + } + } + + input[type="range"] { + width: 100%; + margin-top: 0.25rem; + -webkit-appearance: none; + appearance: none; + background: rgba(0, 100, 180, 0.2); + border-radius: 4px; + height: 6px; + outline: none; + + &::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + background: linear-gradient(135deg, #0096ff, #66ccff); + cursor: pointer; + border-radius: 50%; + box-shadow: 0 0 10px rgba(0, 150, 255, 0.6); + border: 2px solid rgba(0, 150, 255, 0.8); + } + + &::-moz-range-thumb { + width: 18px; + height: 18px; + background: linear-gradient(135deg, #0096ff, #66ccff); + cursor: pointer; + border-radius: 50%; + box-shadow: 0 0 10px rgba(0, 150, 255, 0.6); + border: 2px solid rgba(0, 150, 255, 0.8); + } + } + + input[type="color"] { + width: 100%; + height: 50px; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + cursor: pointer; + background: rgba(5, 15, 30, 0.7); + padding: 4px; + transition: all 0.3s ease; + + &:hover { + border-color: rgba(0, 150, 255, 0.7); + box-shadow: 0 0 15px rgba(0, 150, 255, 0.3); + } + } + + input[type="checkbox"] { + width: auto; + margin-left: 0.5rem; + cursor: pointer; + accent-color: #0096ff; + } + } + + .control-group-header { + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + user-select: none; + margin: 0; + padding: 0.5rem 0; + border-bottom: 2px solid; + border-image: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.5), + transparent + ) + 1; + margin-bottom: 1rem; + color: rgba(100, 180, 255, 0.9); + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + transition: all 0.3s ease; + + &:hover { + color: #66ccff; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.5); + } + } + + .toggle-icon { + font-size: 1.5rem; + font-weight: bold; + transition: transform 0.3s ease; + color: rgba(0, 150, 255, 0.8); + + .control-group.collapsed & { + transform: rotate(180deg); + } + } + + .control-group-content { + overflow: hidden; + transition: + max-height 0.3s ease, + opacity 0.3s ease; + + .control-group.collapsed & { + max-height: 0; + opacity: 0; + pointer-events: none; + } + } + + .checkbox-row { + display: flex; + gap: 1.5rem; + margin-top: 0.75rem; + } + + .checkbox-label { + display: flex; + align-items: center; + gap: 0.5rem; + font-weight: 500; + font-size: 0.85rem; + cursor: pointer; + margin: 0; + color: rgba(100, 180, 255, 0.8); + text-transform: uppercase; + letter-spacing: 0.05em; + transition: color 0.3s ease; + + &:hover { + color: #66ccff; + } + + input[type="checkbox"] { + margin: 0; + cursor: pointer; + } + + span { + user-select: none; + } + } + + .btn-primary, + .btn-secondary { + width: 100%; + padding: 0.75rem 1.5rem; + font-size: 0.95rem; + font-weight: 600; + border: 1px solid rgba(0, 150, 255, 0.5); + border-radius: 4px; + cursor: pointer; + transition: all 0.3s ease; + text-transform: uppercase; + letter-spacing: 0.08em; + position: relative; + overflow: hidden; + + &::before { + content: ""; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.3), + transparent + ); + transition: left 0.5s ease; + } + + &:hover::before { + left: 100%; + } + } + + .btn-primary { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(0, 100, 180, 0.2) 100% + ); + color: #66ccff; + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 150, 255, 0.4) 0%, + rgba(255, 100, 0, 0.3) 100% + ); + border-color: rgba(0, 150, 255, 0.8); + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.4), + 0 0 30px rgba(255, 120, 0, 0.2); + color: #fff; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.6); + transform: translateY(-2px); + } + + &:active { + transform: translateY(0); + } + } + + .btn-secondary { + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 0.2) 0%, + rgba(0, 80, 150, 0.15) 100% + ); + color: #66ccff; + margin-top: 0.5rem; + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(255, 100, 0, 0.2) 100% + ); + border-color: rgba(0, 150, 255, 0.6); + transform: translateY(-2px); + box-shadow: 0 0 20px rgba(0, 150, 255, 0.3); + color: #fff; + text-shadow: 0 0 8px rgba(0, 150, 255, 0.5); + } + + &:active { + transform: translateY(0); + } + } +} diff --git a/assets/sass/pages/homepage.scss b/assets/sass/pages/homepage.scss index 0aa1a23..e06bfd3 100644 --- a/assets/sass/pages/homepage.scss +++ b/assets/sass/pages/homepage.scss @@ -244,6 +244,7 @@ inset 0 1px 2px rgba(255, 255, 255, 0.1); border-radius: 1em; cursor: pointer; + position: relative; &::after { content: "Interests and Tools"; @@ -267,7 +268,7 @@ @include media-down(lg) { margin-left: -0.5em; opacity: 1; - bottom: 0; + bottom: -40px; font-size: 20px; } } diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index b7aa8a7..f70ee2c 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -84,7 +84,7 @@ max-width: 1400px; margin: 0 auto; position: relative; - z-index: 1; + z-index: 10; } // Portal Header with portals on either side @@ -97,7 +97,7 @@ position: relative; .portal-title { - font-size: 3.5rem; + font-size: 2.5rem; font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; font-weight: 700; text-transform: uppercase; @@ -108,11 +108,13 @@ 0 0 40px rgba(255, 255, 255, 0.4), 0 2px 4px rgba(0, 0, 0, 0.3); margin: 0; + margin-top: 50px; position: relative; z-index: 2; @include media-up(md) { font-size: 4rem; + margin-top: 0; } } @@ -192,7 +194,7 @@ left: 5%; bottom: 5%; width: 150px; - z-index: -1; + z-index: 1; .heart-icon { font-size: 2em; @@ -257,6 +259,7 @@ .portal-sign { position: relative; padding: 3rem 2rem; + z-index: 10; border: 10px solid black; background: white; @@ -579,7 +582,8 @@ inset 0 0 100px rgba(0, 100, 200, 0.05), 0 8px 32px rgba(0, 0, 0, 0.4); position: relative; - overflow: hidden; + overflow: visible; + z-index: 10; // Subtle tech panel grid pattern &::before { diff --git a/assets/sass/style.scss b/assets/sass/style.scss index e165389..e5591e9 100644 --- a/assets/sass/style.scss +++ b/assets/sass/style.scss @@ -26,6 +26,7 @@ @import "pages/blog"; @import "pages/media"; @import "pages/resources"; +@import "pages/button-generator"; @import url(https://fonts.bunny.net/css?family=abel:400|barlow-condensed:400,500|caveat:400|lato:300,300i,400,400i|neonderthaw:400); diff --git a/content/blog/2026-01-06-week-3-bit-chilly-out/index.md b/content/blog/2026-01-06-week-3-bit-chilly-out/index.md index 0106157..f0dd967 100644 --- a/content/blog/2026-01-06-week-3-bit-chilly-out/index.md +++ b/content/blog/2026-01-06-week-3-bit-chilly-out/index.md @@ -9,6 +9,8 @@ draft: true - ❄️ It's been pretty cold and we've had the lightest sprinkling of snow. - 📱 I did a mobile pass on some of the pages on this website, it works a bit better on phones now. +- 🟠 Added the [resources](/resources) section with a script to pull weekly last.fm stats (see example output below!) +- 🆒 Started working on an [88x31 button creator](/resources/button-generator/), it's got a decent amount of functionality so far. ## Links I Found Interesting diff --git a/content/resources/button-generator/index.md b/content/resources/button-generator/index.md new file mode 100644 index 0000000..0dbad1b --- /dev/null +++ b/content/resources/button-generator/index.md @@ -0,0 +1,23 @@ +--- +title: "88x31 Button Creator" +date: 2026-01-08 +description: "Make custom 88x31 pixel buttons with text, colors, gradients, and textures" +icon: "button" +demo_url: "" +source_url: "" +draft: false +--- + +Welcome to my 88x31 button creator, this is a pretty rough and ready implementation so it could be buggy, please let me know if you find any issues. + +Currently this only supports static images and exports as png due to the basic `canvas` tag limitations. I have approximate plans for how to make this export gifs and potentially make animated buttons, please look forward to it. + +Big thanks to [neonaut's 88x31 archive](https://neonaut.neocities.org/cyber/88x31) and everyone who made the buttons that appear there. You should check it out if you need inspiration for your button! + +{{< button-generator >}} + +--- + +### Changelog + +- 08/01/2025 - Initial release. diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 5546f2c..e3146ce 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -33,5 +33,11 @@ <main role="main">{{ block "main" . }}{{ end }}</main> {{ block "footer" . }}{{ partial "site-footer.html" . }}{{ end }} {{ block "scripts" . }}{{ partial "site-scripts.html" . }}{{ end }} + + <!-- Button Generator - only load if page content contains button-generator shortcode --> + {{ if or (findRE "{{<\\s*button-generator" .RawContent) (findRE "{{%\\s*button-generator" .RawContent) }} + {{ $buttonGenerator := resources.Get "js/button-generator.js" | resources.Minify | resources.Fingerprint }} + <script src="{{ $buttonGenerator.RelPermalink }}" integrity="{{ $buttonGenerator.Data.Integrity }}"></script> + {{ end }} </body> </html> diff --git a/layouts/partials/site-scripts.html b/layouts/partials/site-scripts.html index 5f3690d..b8ee8b1 100644 --- a/layouts/partials/site-scripts.html +++ b/layouts/partials/site-scripts.html @@ -7,7 +7,7 @@ {{ $filtered := slice }} {{ range $remaining }} {{ $path := .RelPermalink }} - {{ if and (not (strings.Contains $path "/terminal.js")) (not (strings.Contains $path "/init.js")) }} + {{ if and (not (strings.Contains $path "/terminal.js")) (not (strings.Contains $path "/init.js")) (not (strings.Contains $path "/button-generator.js")) }} {{ $filtered = $filtered | append . }} {{ end }} {{ end }} diff --git a/layouts/resources/single.html b/layouts/resources/single.html index dc3de9f..3554198 100644 --- a/layouts/resources/single.html +++ b/layouts/resources/single.html @@ -48,5 +48,8 @@ {{ end }} </nav> </article> + <div class="background-cube"> + {{ partial "elements/companion-cube.html" . }} + </div> </div> {{ end }} diff --git a/layouts/shortcodes/button-generator.html b/layouts/shortcodes/button-generator.html new file mode 100644 index 0000000..0572f2f --- /dev/null +++ b/layouts/shortcodes/button-generator.html @@ -0,0 +1,315 @@ +<div id="button-generator-app"> + <div class="generator-container"> + <div class="preview-section"> + <h3>Preview</h3> + <div class="preview-container"> + <div class="preview-wrapper"> + <canvas id="button-canvas" width="88" height="31"></canvas> + </div> + </div> + <button id="download-button" class="btn-primary">Download Button</button> + + <div class="presets-container"> + <h3>Presets</h3> + <button id="preset-random" class="btn-secondary">Random Button</button> + <button id="preset-classic" class="btn-secondary">Classic Style</button> + <button id="preset-modern" class="btn-secondary">Modern Style</button> + </div> + </div> + + <div class="controls-section"> + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 1</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="button-text">Text</label> + <input + type="text" + id="button-text" + value="RITUAL.SH" + maxlength="20" + /> + + <label class="checkbox-label"> + <input type="checkbox" id="text-enabled" checked /> + <span>Enable Text Line 1</span> + </label> + + <label for="font-size" + >Font Size: <span id="font-size-value">14</span>px</label + > + <input type="range" id="font-size" min="6" max="24" value="14" /> + + <label for="text-x" + >Horizontal Position: <span id="text-x-value">50</span>%</label + > + <input type="range" id="text-x" min="0" max="100" value="50" /> + + <label for="text-y" + >Vertical Position: <span id="text-y-value">35</span>%</label + > + <input type="range" id="text-y" min="0" max="100" value="35" /> + + <label for="text-color-type">Text Color Type</label> + <select id="text-color-type"> + <option value="solid">Solid Color</option> + <option value="gradient">Gradient</option> + </select> + + <div id="text-solid-color"> + <label for="text-color">Text Color</label> + <input type="color" id="text-color" value="#ffffff" /> + </div> + + <div id="text-gradient-color" style="display: none"> + <label for="text-gradient-color1">Gradient Color 1</label> + <input type="color" id="text-gradient-color1" value="#ffffff" /> + + <label for="text-gradient-color2">Gradient Color 2</label> + <input type="color" id="text-gradient-color2" value="#00ffff" /> + + <label for="text-gradient-angle" + >Gradient Angle: + <span id="text-gradient-angle-value">0</span>°</label + > + <input + type="range" + id="text-gradient-angle" + min="0" + max="360" + value="0" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="text-outline" /> + <span>Outline</span> + </label> + <input + type="color" + id="outline-color" + value="#000000" + style="display: none" + /> + + <label for="font-family">Font</label> + <select id="font-family"> + <option value="Lato">Lato</option> + <option value="Roboto">Roboto</option> + <option value="Open Sans">Open Sans</option> + <option value="Montserrat">Montserrat</option> + <option value="Oswald">Oswald</option> + <option value="Bebas Neue">Bebas Neue</option> + <option value="Roboto Mono">Roboto Mono</option> + <option value="VT323">VT323</option> + <option value="Press Start 2P">Press Start 2P</option> + <option value="DSEG7-Classic">DSEG7</option> + </select> + + <div class="checkbox-row"> + <label class="checkbox-label"> + <input type="checkbox" id="font-bold" /> + <span>Bold</span> + </label> + <label class="checkbox-label"> + <input type="checkbox" id="font-italic" /> + <span>Italic</span> + </label> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 2</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="button-text2">Text</label> + <input type="text" id="button-text2" value="" maxlength="20" /> + + <label class="checkbox-label"> + <input type="checkbox" id="text2-enabled" /> + <span>Enable Text Line 2</span> + </label> + + <label for="font-size2" + >Font Size: <span id="font-size2-value">12</span>px</label + > + <input type="range" id="font-size2" min="6" max="24" value="12" /> + + <label for="text2-x" + >Horizontal Position: <span id="text2-x-value">50</span>%</label + > + <input type="range" id="text2-x" min="0" max="100" value="50" /> + + <label for="text2-y" + >Vertical Position: <span id="text2-y-value">65</span>%</label + > + <input type="range" id="text2-y" min="0" max="100" value="65" /> + + <label for="text2-color-type">Text Color Type</label> + <select id="text2-color-type"> + <option value="solid">Solid Color</option> + <option value="gradient">Gradient</option> + </select> + + <div id="text2-solid-color"> + <label for="text2-color">Text Color</label> + <input type="color" id="text2-color" value="#ffffff" /> + </div> + + <div id="text2-gradient-color" style="display: none"> + <label for="text2-gradient-color1">Gradient Color 1</label> + <input type="color" id="text2-gradient-color1" value="#ffffff" /> + + <label for="text2-gradient-color2">Gradient Color 2</label> + <input type="color" id="text2-gradient-color2" value="#00ffff" /> + + <label for="text2-gradient-angle" + >Gradient Angle: + <span id="text2-gradient-angle-value">0</span>°</label + > + <input + type="range" + id="text2-gradient-angle" + min="0" + max="360" + value="0" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="text2-outline" /> + <span>Outline</span> + </label> + <input + type="color" + id="outline2-color" + value="#000000" + style="display: none" + /> + + <label for="font-family2">Font</label> + <select id="font-family2"> + <option value="Lato">Lato</option> + <option value="Roboto">Roboto</option> + <option value="Open Sans">Open Sans</option> + <option value="Montserrat">Montserrat</option> + <option value="Oswald">Oswald</option> + <option value="Bebas Neue">Bebas Neue</option> + <option value="Roboto Mono">Roboto Mono</option> + <option value="VT323">VT323</option> + <option value="Press Start 2P">Press Start 2P</option> + <option value="DSEG7-Classic">DSEG7</option> + </select> + + <div class="checkbox-row"> + <label class="checkbox-label"> + <input type="checkbox" id="font-bold2" /> + <span>Bold</span> + </label> + <label class="checkbox-label"> + <input type="checkbox" id="font-italic2" /> + <span>Italic</span> + </label> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Background</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="bg-type">Background Type</label> + <select id="bg-type"> + <option value="solid">Solid Color</option> + <option value="gradient">Gradient</option> + <option value="texture">Texture</option> + </select> + + <div id="solid-controls"> + <label for="bg-color">Background Color</label> + <input type="color" id="bg-color" value="#0066cc" /> + </div> + + <div id="gradient-controls" style="display: none"> + <label for="gradient-color1">Color 1</label> + <input type="color" id="gradient-color1" value="#0066cc" /> + + <label for="gradient-color2">Color 2</label> + <input type="color" id="gradient-color2" value="#00ccff" /> + + <label for="gradient-angle" + >Angle: <span id="gradient-angle-value">90</span>°</label + > + <input + type="range" + id="gradient-angle" + min="0" + max="360" + value="90" + /> + </div> + + <div id="texture-controls" style="display: none"> + <label for="texture-type">Texture Pattern</label> + <select id="texture-type"> + <option value="dots">Dots</option> + <option value="grid">Grid</option> + <option value="diagonal">Diagonal Lines</option> + <option value="checkerboard">Checkerboard</option> + <option value="noise">Noise</option> + <option value="stars">Stars</option> + </select> + + <label for="texture-color1">Base Color</label> + <input type="color" id="texture-color1" value="#0066cc" /> + + <label for="texture-color2">Pattern Color</label> + <input type="color" id="texture-color2" value="#0099ff" /> + + <label for="texture-scale" + >Pattern Scale: <span id="texture-scale-value">50</span>%</label + > + <input + type="range" + id="texture-scale" + min="10" + max="100" + value="50" + /> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Border</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="border-width" + >Border Width: <span id="border-width-value">2</span>px</label + > + <input type="range" id="border-width" min="0" max="5" value="2" /> + + <label for="border-color">Border Color</label> + <input type="color" id="border-color" value="#000000" /> + + <label for="border-style">Border Style</label> + <select id="border-style"> + <option value="solid">Solid</option> + <option value="inset">Inset (3D)</option> + <option value="outset">Outset (3D)</option> + <option value="ridge">Ridge</option> + </select> + </div> + </div> + </div> + </div> +</div> From 79a7557ea54f6393c33d26fa001785579c2a0be9 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 14:07:50 +0000 Subject: [PATCH 22/55] Much more button gen stuff --- assets/js/button-generator.js | 1759 ++++++++++++----- assets/sass/pages/button-generator.scss | 45 +- assets/sass/pages/resources.scss | 14 +- .../canvas.png | Bin 0 -> 1625 bytes content/resources/button-generator/index.md | 2 +- layouts/_default/baseof.html | 1 + layouts/shortcodes/button-generator.html | 292 +++ static/js/gif.js | 3 + static/js/gif.worker.js | 3 + 9 files changed, 1574 insertions(+), 545 deletions(-) create mode 100644 content/media/taskmaster-champion-of-champions-4/canvas.png create mode 100644 static/js/gif.js create mode 100644 static/js/gif.worker.js diff --git a/assets/js/button-generator.js b/assets/js/button-generator.js index 62acb20..f33b460 100644 --- a/assets/js/button-generator.js +++ b/assets/js/button-generator.js @@ -1,340 +1,483 @@ (function () { - const canvas = document.getElementById("button-canvas"); - const ctx = canvas.getContext("2d"); - - // Preload all web fonts for canvas rendering - const fonts = [ - "Lato", - "Roboto", - "Open Sans", - "Montserrat", - "Oswald", - "Bebas Neue", - "Roboto Mono", - "VT323", - "Press Start 2P", - "DSEG7-Classic", - ]; - - // Load fonts using CSS Font Loading API - const fontPromises = fonts.flatMap((font) => [ - document.fonts.load(`400 12px "${font}"`), - document.fonts.load(`700 12px "${font}"`), - document.fonts.load(`italic 400 12px "${font}"`), - ]); - - Promise.all(fontPromises).then(() => { - console.log("All fonts loaded for canvas"); - drawButton(); - }); - - // Collapsible sections functionality - document.querySelectorAll(".control-group-header").forEach((header) => { - header.addEventListener("click", () => { - const controlGroup = header.closest(".control-group"); - controlGroup.classList.toggle("collapsed"); - }); - }); - - // Get all controls - const controls = { - text: document.getElementById("button-text"), - textEnabled: document.getElementById("text-enabled"), - fontSize: document.getElementById("font-size"), - textX: document.getElementById("text-x"), - textY: document.getElementById("text-y"), - textColorType: document.getElementById("text-color-type"), - textColor: document.getElementById("text-color"), - textGradientColor1: document.getElementById("text-gradient-color1"), - textGradientColor2: document.getElementById("text-gradient-color2"), - textGradientAngle: document.getElementById("text-gradient-angle"), - textOutline: document.getElementById("text-outline"), - outlineColor: document.getElementById("outline-color"), - fontFamily: document.getElementById("font-family"), - fontBold: document.getElementById("font-bold"), - fontItalic: document.getElementById("font-italic"), - text2: document.getElementById("button-text2"), - text2Enabled: document.getElementById("text2-enabled"), - fontSize2: document.getElementById("font-size2"), - text2X: document.getElementById("text2-x"), - text2Y: document.getElementById("text2-y"), - text2ColorType: document.getElementById("text2-color-type"), - text2Color: document.getElementById("text2-color"), - text2GradientColor1: document.getElementById("text2-gradient-color1"), - text2GradientColor2: document.getElementById("text2-gradient-color2"), - text2GradientAngle: document.getElementById("text2-gradient-angle"), - text2Outline: document.getElementById("text2-outline"), - outline2Color: document.getElementById("outline2-color"), - fontFamily2: document.getElementById("font-family2"), - fontBold2: document.getElementById("font-bold2"), - fontItalic2: document.getElementById("font-italic2"), - bgType: document.getElementById("bg-type"), - bgColor: document.getElementById("bg-color"), - gradientColor1: document.getElementById("gradient-color1"), - gradientColor2: document.getElementById("gradient-color2"), - gradientAngle: document.getElementById("gradient-angle"), - textureType: document.getElementById("texture-type"), - textureColor1: document.getElementById("texture-color1"), - textureColor2: document.getElementById("texture-color2"), - textureScale: document.getElementById("texture-scale"), - borderWidth: document.getElementById("border-width"), - borderColor: document.getElementById("border-color"), - borderStyle: document.getElementById("border-style"), - }; - - // Update value displays - const updateValueDisplay = (id, value) => { - const display = document.getElementById(id + "-value"); - if (display) display.textContent = value; - }; - - // Show/hide controls based on background type - controls.bgType.addEventListener("change", () => { - const solidControls = document.getElementById("solid-controls"); - const gradientControls = document.getElementById("gradient-controls"); - const textureControls = document.getElementById("texture-controls"); - - solidControls.style.display = "none"; - gradientControls.style.display = "none"; - textureControls.style.display = "none"; - - if (controls.bgType.value === "solid") { - solidControls.style.display = "block"; - } else if (controls.bgType.value === "gradient") { - gradientControls.style.display = "block"; - } else if (controls.bgType.value === "texture") { - textureControls.style.display = "block"; - } - - drawButton(); - }); - - // Show/hide text color controls - controls.textColorType.addEventListener("change", () => { - const textSolidColor = document.getElementById("text-solid-color"); - const textGradientColor = document.getElementById("text-gradient-color"); - - if (controls.textColorType.value === "solid") { - textSolidColor.style.display = "block"; - textGradientColor.style.display = "none"; - } else { - textSolidColor.style.display = "none"; - textGradientColor.style.display = "block"; - } - drawButton(); - }); - - controls.text2ColorType.addEventListener("change", () => { - const text2SolidColor = document.getElementById("text2-solid-color"); - const text2GradientColor = document.getElementById("text2-gradient-color"); - - if (controls.text2ColorType.value === "solid") { - text2SolidColor.style.display = "block"; - text2GradientColor.style.display = "none"; - } else { - text2SolidColor.style.display = "none"; - text2GradientColor.style.display = "block"; - } - drawButton(); - }); - - // Show/hide outline color - controls.textOutline.addEventListener("change", () => { - controls.outlineColor.style.display = controls.textOutline.checked - ? "block" - : "none"; - drawButton(); - }); - - controls.text2Outline.addEventListener("change", () => { - controls.outline2Color.style.display = controls.text2Outline.checked - ? "block" - : "none"; - drawButton(); - }); - - // Draw texture patterns - function drawTexture(type, color1, color2, scale) { - const tempCanvas = document.createElement("canvas"); - tempCanvas.width = 88; - tempCanvas.height = 31; - const tempCtx = tempCanvas.getContext("2d"); - - tempCtx.fillStyle = color1; - tempCtx.fillRect(0, 0, 88, 31); - - tempCtx.fillStyle = color2; - const size = Math.max(2, Math.floor(scale / 10)); - - switch (type) { - case "dots": - for (let y = 0; y < 31; y += size * 2) { - for (let x = 0; x < 88; x += size * 2) { - tempCtx.beginPath(); - tempCtx.arc(x, y, size / 2, 0, Math.PI * 2); - tempCtx.fill(); - } - } - break; - case "grid": - for (let x = 0; x < 88; x += size) { - tempCtx.fillRect(x, 0, 1, 31); - } - for (let y = 0; y < 31; y += size) { - tempCtx.fillRect(0, y, 88, 1); - } - break; - case "diagonal": - for (let i = -31; i < 88; i += size) { - tempCtx.fillRect(i, 0, 2, 31); - tempCtx.save(); - tempCtx.translate(i + 1, 0); - tempCtx.rotate(Math.PI / 4); - tempCtx.fillRect(0, 0, 2, 100); - tempCtx.restore(); - } - break; - case "checkerboard": - for (let y = 0; y < 31; y += size) { - for (let x = 0; x < 88; x += size) { - if ((x / size + y / size) % 2 === 0) { - tempCtx.fillRect(x, y, size, size); - } - } - } - break; - case "noise": - for (let y = 0; y < 31; y++) { - for (let x = 0; x < 88; x++) { - if (Math.random() > 0.5) { - tempCtx.fillRect(x, y, 1, 1); - } - } - } - break; - case "stars": - for (let i = 0; i < scale; i++) { - const x = Math.floor(Math.random() * 88); - const y = Math.floor(Math.random() * 31); - tempCtx.fillRect(x, y, 1, 1); - tempCtx.fillRect(x - 1, y, 1, 1); - tempCtx.fillRect(x + 1, y, 1, 1); - tempCtx.fillRect(x, y - 1, 1, 1); - tempCtx.fillRect(x, y + 1, 1, 1); - } - break; - } - - return tempCanvas; + // Wait for DOM to be ready + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); } - // Helper function to draw to a single context - function drawToContext(context) { - context.clearRect(0, 0, 88, 31); + function init() { + setupCollapsible(); + setupButtonGenerator(); + } - // Draw background - if (controls.bgType.value === "solid") { - context.fillStyle = controls.bgColor.value; - context.fillRect(0, 0, 88, 31); - } else if (controls.bgType.value === "gradient") { - const angle = parseFloat(controls.gradientAngle.value) * (Math.PI / 180); - const x1 = 44 + Math.cos(angle) * 44; - const y1 = 15.5 + Math.sin(angle) * 15.5; - const x2 = 44 - Math.cos(angle) * 44; - const y2 = 15.5 - Math.sin(angle) * 15.5; + // Collapsible sections functionality + function setupCollapsible() { + const headers = document.querySelectorAll(".control-group-header"); + console.log("Found", headers.length, "collapsible headers"); - const gradient = context.createLinearGradient(x1, y1, x2, y2); - gradient.addColorStop(0, controls.gradientColor1.value); - gradient.addColorStop(1, controls.gradientColor2.value); - context.fillStyle = gradient; - context.fillRect(0, 0, 88, 31); - } else if (controls.bgType.value === "texture") { - const texture = drawTexture( - controls.textureType.value, - controls.textureColor1.value, - controls.textureColor2.value, - parseFloat(controls.textureScale.value), - ); - context.drawImage(texture, 0, 0); - } + headers.forEach((header) => { + header.addEventListener("click", () => { + const controlGroup = header.closest(".control-group"); + if (controlGroup) { + controlGroup.classList.toggle("collapsed"); + console.log("Toggled collapsed on", header.textContent.trim()); + } + }); + }); + } - // Draw border - const borderWidth = parseFloat(controls.borderWidth.value); - if (borderWidth > 0) { - const style = controls.borderStyle.value; + function setupButtonGenerator() { + const canvas = document.getElementById("button-canvas"); + const ctx = canvas.getContext("2d"); - if (style === "solid") { - context.strokeStyle = controls.borderColor.value; - context.lineWidth = borderWidth; - context.strokeRect( - borderWidth / 2, - borderWidth / 2, - 88 - borderWidth, - 31 - borderWidth, - ); - } else if (style === "inset" || style === "outset") { - const light = style === "outset"; - context.strokeStyle = light ? "#ffffff" : "#000000"; - context.lineWidth = borderWidth; - context.beginPath(); - context.moveTo(0, 31); - context.lineTo(0, 0); - context.lineTo(88, 0); - context.stroke(); + // Preload all web fonts for canvas rendering + const fonts = [ + "Lato", + "Roboto", + "Open Sans", + "Montserrat", + "Oswald", + "Bebas Neue", + "Roboto Mono", + "VT323", + "Press Start 2P", + "DSEG7-Classic", + ]; - context.strokeStyle = light ? "#000000" : "#ffffff"; - context.beginPath(); - context.moveTo(88, 0); - context.lineTo(88, 31); - context.lineTo(0, 31); - context.stroke(); - } else if (style === "ridge") { - context.strokeStyle = "#ffffff"; - context.lineWidth = borderWidth / 2; - context.strokeRect( - borderWidth / 4, - borderWidth / 4, - 88 - borderWidth / 2, - 31 - borderWidth / 2, - ); + // Load fonts using CSS Font Loading API + const fontPromises = fonts.flatMap((font) => [ + document.fonts.load(`400 12px "${font}"`), + document.fonts.load(`700 12px "${font}"`), + document.fonts.load(`italic 400 12px "${font}"`), + ]); - context.strokeStyle = "#000000"; - context.strokeRect( - (borderWidth * 3) / 4, - (borderWidth * 3) / 4, - 88 - borderWidth * 1.5, - 31 - borderWidth * 1.5, - ); + Promise.all(fontPromises).then(() => { + console.log("All fonts loaded for canvas"); + drawButton(); + }); + + // Animation configuration + const ANIMATION_CONFIG = { + fps: 20, + duration: 2, // seconds + get totalFrames() { + return this.fps * this.duration; + }, // 40 frames + }; + + // Animation state class - passed to drawToContext for frame-based rendering + class AnimationState { + constructor(frameNumber = 0, totalFrames = 40) { + this.frame = frameNumber; + this.totalFrames = totalFrames; + this.progress = frameNumber / totalFrames; // 0 to 1 + this.time = (frameNumber / ANIMATION_CONFIG.fps) * 1000; // milliseconds + } + + // Helper to get phase for periodic animations (0 to 2π) + getPhase(speed = 1.0) { + return this.progress * speed * Math.PI * 2; } } - // Draw text line 1 - const text = controls.text.value; - if (text && controls.textEnabled.checked) { - const fontSize = parseFloat(controls.fontSize.value); - const fontWeight = controls.fontBold.checked ? "bold" : "normal"; - const fontStyle = controls.fontItalic.checked ? "italic" : "normal"; - const fontFamily = controls.fontFamily.value; + // Get all controls + const controls = { + text: document.getElementById("button-text"), + textEnabled: document.getElementById("text-enabled"), + fontSize: document.getElementById("font-size"), + textX: document.getElementById("text-x"), + textY: document.getElementById("text-y"), + textColorType: document.getElementById("text-color-type"), + textColor: document.getElementById("text-color"), + textGradientColor1: document.getElementById("text-gradient-color1"), + textGradientColor2: document.getElementById("text-gradient-color2"), + textGradientAngle: document.getElementById("text-gradient-angle"), + textOutline: document.getElementById("text-outline"), + outlineColor: document.getElementById("outline-color"), + fontFamily: document.getElementById("font-family"), + fontBold: document.getElementById("font-bold"), + fontItalic: document.getElementById("font-italic"), + text2: document.getElementById("button-text2"), + text2Enabled: document.getElementById("text2-enabled"), + fontSize2: document.getElementById("font-size2"), + text2X: document.getElementById("text2-x"), + text2Y: document.getElementById("text2-y"), + text2ColorType: document.getElementById("text2-color-type"), + text2Color: document.getElementById("text2-color"), + text2GradientColor1: document.getElementById("text2-gradient-color1"), + text2GradientColor2: document.getElementById("text2-gradient-color2"), + text2GradientAngle: document.getElementById("text2-gradient-angle"), + text2Outline: document.getElementById("text2-outline"), + outline2Color: document.getElementById("outline2-color"), + fontFamily2: document.getElementById("font-family2"), + fontBold2: document.getElementById("font-bold2"), + fontItalic2: document.getElementById("font-italic2"), + bgType: document.getElementById("bg-type"), + bgColor: document.getElementById("bg-color"), + gradientColor1: document.getElementById("gradient-color1"), + gradientColor2: document.getElementById("gradient-color2"), + gradientAngle: document.getElementById("gradient-angle"), + textureType: document.getElementById("texture-type"), + textureColor1: document.getElementById("texture-color1"), + textureColor2: document.getElementById("texture-color2"), + textureScale: document.getElementById("texture-scale"), + borderWidth: document.getElementById("border-width"), + borderColor: document.getElementById("border-color"), + borderStyle: document.getElementById("border-style"), + // Animation controls + animateTextWave: document.getElementById("animate-text-wave"), + waveAmplitude: document.getElementById("wave-amplitude"), + waveSpeed: document.getElementById("wave-speed"), + animateTextWave2: document.getElementById("animate-text-wave2"), + waveAmplitude2: document.getElementById("wave-amplitude2"), + waveSpeed2: document.getElementById("wave-speed2"), + animateBgRainbow: document.getElementById("animate-bg-rainbow"), + rainbowSpeed: document.getElementById("rainbow-speed"), + animateBgRainbowGradient: document.getElementById( + "animate-bg-rainbow-gradient", + ), + animateTextRainbow: document.getElementById("animate-text-rainbow"), + textRainbowSpeed: document.getElementById("text-rainbow-speed"), + animateTextRainbow2: document.getElementById("animate-text-rainbow2"), + textRainbowSpeed2: document.getElementById("text-rainbow-speed2"), + animateGlitch: document.getElementById("animate-glitch"), + glitchIntensity: document.getElementById("glitch-intensity"), + animatePulse: document.getElementById("animate-pulse"), + pulseScale: document.getElementById("pulse-scale"), + animateShimmer: document.getElementById("animate-shimmer"), + animateScanline: document.getElementById("animate-scanline"), + scanlineIntensity: document.getElementById("scanline-intensity"), + scanlineSpeed: document.getElementById("scanline-speed"), + animateRgbSplit: document.getElementById("animate-rgb-split"), + rgbSplitIntensity: document.getElementById("rgb-split-intensity"), + animateNoise: document.getElementById("animate-noise"), + noiseIntensity: document.getElementById("noise-intensity"), + animateRotate: document.getElementById("animate-rotate"), + rotateAngle: document.getElementById("rotate-angle"), + rotateSpeed: document.getElementById("rotate-speed"), + }; + + // Update value displays + const updateValueDisplay = (id, value) => { + const display = document.getElementById(id + "-value"); + if (display) display.textContent = value; + }; + + // Show/hide controls based on background type + controls.bgType.addEventListener("change", () => { + const solidControls = document.getElementById("solid-controls"); + const gradientControls = document.getElementById("gradient-controls"); + const textureControls = document.getElementById("texture-controls"); + + solidControls.style.display = "none"; + gradientControls.style.display = "none"; + textureControls.style.display = "none"; + + if (controls.bgType.value === "solid") { + solidControls.style.display = "block"; + } else if (controls.bgType.value === "gradient") { + gradientControls.style.display = "block"; + } else if (controls.bgType.value === "texture") { + textureControls.style.display = "block"; + } + + drawButton(); + }); + + // Show/hide text color controls + controls.textColorType.addEventListener("change", () => { + const textSolidColor = document.getElementById("text-solid-color"); + const textGradientColor = document.getElementById("text-gradient-color"); + + if (controls.textColorType.value === "solid") { + textSolidColor.style.display = "block"; + textGradientColor.style.display = "none"; + } else { + textSolidColor.style.display = "none"; + textGradientColor.style.display = "block"; + } + drawButton(); + }); + + controls.text2ColorType.addEventListener("change", () => { + const text2SolidColor = document.getElementById("text2-solid-color"); + const text2GradientColor = document.getElementById( + "text2-gradient-color", + ); + + if (controls.text2ColorType.value === "solid") { + text2SolidColor.style.display = "block"; + text2GradientColor.style.display = "none"; + } else { + text2SolidColor.style.display = "none"; + text2GradientColor.style.display = "block"; + } + drawButton(); + }); + + // Show/hide outline color + controls.textOutline.addEventListener("change", () => { + controls.outlineColor.style.display = controls.textOutline.checked + ? "block" + : "none"; + drawButton(); + }); + + controls.text2Outline.addEventListener("change", () => { + controls.outline2Color.style.display = controls.text2Outline.checked + ? "block" + : "none"; + drawButton(); + }); + + // Draw texture patterns + function drawTexture(type, color1, color2, scale) { + const tempCanvas = document.createElement("canvas"); + tempCanvas.width = 88; + tempCanvas.height = 31; + const tempCtx = tempCanvas.getContext("2d"); + + tempCtx.fillStyle = color1; + tempCtx.fillRect(0, 0, 88, 31); + + tempCtx.fillStyle = color2; + const size = Math.max(2, Math.floor(scale / 10)); + + switch (type) { + case "dots": + for (let y = 0; y < 31; y += size * 2) { + for (let x = 0; x < 88; x += size * 2) { + tempCtx.beginPath(); + tempCtx.arc(x, y, size / 2, 0, Math.PI * 2); + tempCtx.fill(); + } + } + break; + case "grid": + for (let x = 0; x < 88; x += size) { + tempCtx.fillRect(x, 0, 1, 31); + } + for (let y = 0; y < 31; y += size) { + tempCtx.fillRect(0, y, 88, 1); + } + break; + case "diagonal": + for (let i = -31; i < 88; i += size) { + tempCtx.fillRect(i, 0, 2, 31); + tempCtx.save(); + tempCtx.translate(i + 1, 0); + tempCtx.rotate(Math.PI / 4); + tempCtx.fillRect(0, 0, 2, 100); + tempCtx.restore(); + } + break; + case "checkerboard": + for (let y = 0; y < 31; y += size) { + for (let x = 0; x < 88; x += size) { + if ((x / size + y / size) % 2 === 0) { + tempCtx.fillRect(x, y, size, size); + } + } + } + break; + case "noise": + for (let y = 0; y < 31; y++) { + for (let x = 0; x < 88; x++) { + if (Math.random() > 0.5) { + tempCtx.fillRect(x, y, 1, 1); + } + } + } + break; + case "stars": + for (let i = 0; i < scale; i++) { + const x = Math.floor(Math.random() * 88); + const y = Math.floor(Math.random() * 31); + tempCtx.fillRect(x, y, 1, 1); + tempCtx.fillRect(x - 1, y, 1, 1); + tempCtx.fillRect(x + 1, y, 1, 1); + tempCtx.fillRect(x, y - 1, 1, 1); + tempCtx.fillRect(x, y + 1, 1, 1); + } + break; + } + + return tempCanvas; + } + + // Helper function to draw text line with optional wave animation + function drawTextLine(context, lineNumber, animState) { + const prefix = lineNumber === 1 ? "" : "2"; + const text = controls[`text${prefix}`].value; + const enabled = controls[`text${prefix}Enabled`].checked; + + if (!text || !enabled) return; + + const fontSize = parseFloat(controls[`fontSize${prefix}`].value); + const fontWeight = controls[`fontBold${prefix}`].checked + ? "bold" + : "normal"; + const fontStyle = controls[`fontItalic${prefix}`].checked + ? "italic" + : "normal"; + const fontFamily = controls[`fontFamily${prefix}`].value; context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; context.textAlign = "center"; context.textBaseline = "middle"; - const x = (parseFloat(controls.textX.value) / 100) * 88; - const y = (parseFloat(controls.textY.value) / 100) * 31; + const baseX = (parseFloat(controls[`text${prefix}X`].value) / 100) * 88; + const baseY = (parseFloat(controls[`text${prefix}Y`].value) / 100) * 31; - if (controls.textOutline.checked) { - context.strokeStyle = controls.outlineColor.value; + // Check if wave animation is enabled + const waveEnabled = + animState && controls[`animateTextWave${prefix}`]?.checked; + + if (waveEnabled) { + drawWaveText( + context, + text, + baseX, + baseY, + fontSize, + animState, + lineNumber, + ); + } else { + drawStandardText( + context, + text, + baseX, + baseY, + fontSize, + animState, + lineNumber, + ); + } + } + + // Draw standard (non-wave) text + function drawStandardText( + context, + text, + x, + y, + fontSize, + animState, + lineNumber, + ) { + const prefix = lineNumber === 1 ? "" : "2"; + + // Get colors (with potential rainbow animation) + const colors = getTextColors( + lineNumber, + animState, + context, + text, + x, + y, + fontSize, + ); + + if (controls[`text${prefix}Outline`].checked) { + context.strokeStyle = colors.strokeStyle; context.lineWidth = 2; context.strokeText(text, x, y); } - // Apply text color or gradient - if (controls.textColorType.value === "solid") { - context.fillStyle = controls.textColor.value; + context.fillStyle = colors.fillStyle; + context.fillText(text, x, y); + } + + // Draw wave-animated text (character by character) + function drawWaveText( + context, + text, + baseX, + baseY, + fontSize, + animState, + lineNumber, + ) { + const prefix = lineNumber === 1 ? "" : "2"; + const amplitude = parseFloat(controls[`waveAmplitude${prefix}`].value); + const speed = parseFloat(controls[`waveSpeed${prefix}`].value); + + // Measure total width for centering + const totalWidth = context.measureText(text).width; + let currentX = baseX - totalWidth / 2; + + for (let i = 0; i < text.length; i++) { + const char = text[i]; + const charWidth = context.measureText(char).width; + + // Calculate wave offset for this character + const phase = animState.getPhase(speed); + const charOffset = i / text.length; // 0 to 1 + const waveY = Math.sin(phase + charOffset * Math.PI * 2) * amplitude; + + const charX = currentX + charWidth / 2; + const charY = baseY + waveY; + + // Get colors for this character + const colors = getTextColors( + lineNumber, + animState, + context, + char, + charX, + charY, + fontSize, + ); + + // Draw outline if enabled + if (controls[`text${prefix}Outline`].checked) { + context.strokeStyle = colors.strokeStyle; + context.lineWidth = 2; + context.strokeText(char, charX, charY); + } + + // Draw character + context.fillStyle = colors.fillStyle; + context.fillText(char, charX, charY); + + currentX += charWidth; + } + } + + // Get text colors (solid, gradient, or rainbow) + function getTextColors( + lineNumber, + animState, + context, + text, + x, + y, + fontSize, + ) { + const prefix = lineNumber === 1 ? "" : "2"; + const colorType = controls[`text${prefix}ColorType`].value; + + let fillStyle, strokeStyle; + + // Rainbow text animation overrides other colors + if (animState && controls[`animateTextRainbow${prefix}`]?.checked) { + const hue = + (animState.progress * + parseFloat(controls[`textRainbowSpeed${prefix}`].value) * + 360) % + 360; + fillStyle = `hsl(${hue}, 80%, 60%)`; + strokeStyle = `hsl(${hue}, 80%, 30%)`; + } else if (colorType === "solid") { + fillStyle = controls[`text${prefix}Color`].value; + strokeStyle = controls[`outline${prefix}Color`].value; } else { + // Gradient const angle = - parseFloat(controls.textGradientAngle.value) * (Math.PI / 180); + parseFloat(controls[`text${prefix}GradientAngle`].value) * + (Math.PI / 180); const textWidth = context.measureText(text).width; const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2; const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2; @@ -342,253 +485,815 @@ const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2; const textGradient = context.createLinearGradient(x1, y1, x2, y2); - textGradient.addColorStop(0, controls.textGradientColor1.value); - textGradient.addColorStop(1, controls.textGradientColor2.value); - context.fillStyle = textGradient; - } - - context.fillText(text, x, y); - } - - // Draw text line 2 - const text2 = controls.text2.value; - if (text2 && controls.text2Enabled.checked) { - const fontSize2 = parseFloat(controls.fontSize2.value); - const fontWeight2 = controls.fontBold2.checked ? "bold" : "normal"; - const fontStyle2 = controls.fontItalic2.checked ? "italic" : "normal"; - const fontFamily2 = controls.fontFamily2.value; - - context.font = `${fontStyle2} ${fontWeight2} ${fontSize2}px "${fontFamily2}"`; - context.textAlign = "center"; - context.textBaseline = "middle"; - - const x2 = (parseFloat(controls.text2X.value) / 100) * 88; - const y2 = (parseFloat(controls.text2Y.value) / 100) * 31; - - if (controls.text2Outline.checked) { - context.strokeStyle = controls.outline2Color.value; - context.lineWidth = 2; - context.strokeText(text2, x2, y2); - } - - // Apply text color or gradient - if (controls.text2ColorType.value === "solid") { - context.fillStyle = controls.text2Color.value; - } else { - const angle2 = - parseFloat(controls.text2GradientAngle.value) * (Math.PI / 180); - const text2Width = context.measureText(text2).width; - const x1_2 = x2 - text2Width / 2 + (Math.cos(angle2) * text2Width) / 2; - const y1_2 = y2 - fontSize2 / 2 + (Math.sin(angle2) * fontSize2) / 2; - const x2_2 = x2 + text2Width / 2 - (Math.cos(angle2) * text2Width) / 2; - const y2_2 = y2 + fontSize2 / 2 - (Math.sin(angle2) * fontSize2) / 2; - - const text2Gradient = context.createLinearGradient( - x1_2, - y1_2, - x2_2, - y2_2, + textGradient.addColorStop( + 0, + controls[`text${prefix}GradientColor1`].value, ); - text2Gradient.addColorStop(0, controls.text2GradientColor1.value); - text2Gradient.addColorStop(1, controls.text2GradientColor2.value); - context.fillStyle = text2Gradient; + textGradient.addColorStop( + 1, + controls[`text${prefix}GradientColor2`].value, + ); + fillStyle = textGradient; + strokeStyle = controls[`outline${prefix}Color`].value; } - context.fillText(text2, x2, y2); + return { fillStyle, strokeStyle }; } - } - // Main draw function - function drawButton() { - drawToContext(ctx); - } + // Helper function to draw to a single context + function drawToContext(context, animState = null) { + context.clearRect(0, 0, 88, 31); - // Download function - document.getElementById("download-button").addEventListener("click", () => { - const link = document.createElement("a"); - link.download = "button-88x31.png"; - link.href = canvas.toDataURL(); - link.click(); - }); + // Apply rotate effect (must be before drawing) + if (animState && controls.animateRotate?.checked) { + const maxAngle = parseFloat(controls.rotateAngle.value); + const speed = parseFloat(controls.rotateSpeed.value); + const angle = + Math.sin(animState.getPhase(speed)) * maxAngle * (Math.PI / 180); - // Preset buttons - document.getElementById("preset-random").addEventListener("click", () => { - const randomColor = () => - "#" + - Math.floor(Math.random() * 16777215) - .toString(16) - .padStart(6, "0"); + context.save(); + context.translate(44, 15.5); + context.rotate(angle); + context.translate(-44, -15.5); + } - // Random background - controls.bgType.value = ["solid", "gradient", "texture"][ - Math.floor(Math.random() * 3) - ]; - controls.bgColor.value = randomColor(); - controls.gradientColor1.value = randomColor(); - controls.gradientColor2.value = randomColor(); - controls.gradientAngle.value = Math.floor(Math.random() * 360); - controls.textureColor1.value = randomColor(); - controls.textureColor2.value = randomColor(); - controls.textureType.value = [ - "dots", - "grid", - "diagonal", - "checkerboard", - "noise", - "stars", - ][Math.floor(Math.random() * 6)]; + // Draw background + if (controls.bgType.value === "solid") { + // Rainbow flash for solid colors + if (animState && controls.animateBgRainbow?.checked) { + const hue = + (animState.progress * + parseFloat(controls.rainbowSpeed.value) * + 360) % + 360; + context.fillStyle = `hsl(${hue}, 70%, 50%)`; + } else { + context.fillStyle = controls.bgColor.value; + } + context.fillRect(0, 0, 88, 31); + } else if (controls.bgType.value === "gradient") { + const angle = + parseFloat(controls.gradientAngle.value) * (Math.PI / 180); + const x1 = 44 + Math.cos(angle) * 44; + const y1 = 15.5 + Math.sin(angle) * 15.5; + const x2 = 44 - Math.cos(angle) * 44; + const y2 = 15.5 - Math.sin(angle) * 15.5; - // Random text 1 color (50% chance of gradient) - controls.textColorType.value = Math.random() > 0.5 ? "gradient" : "solid"; - controls.textColor.value = randomColor(); - controls.textGradientColor1.value = randomColor(); - controls.textGradientColor2.value = randomColor(); - controls.textGradientAngle.value = Math.floor(Math.random() * 360); + const gradient = context.createLinearGradient(x1, y1, x2, y2); - // Random text 2 color (50% chance of gradient) - controls.text2ColorType.value = Math.random() > 0.5 ? "gradient" : "solid"; - controls.text2Color.value = randomColor(); - controls.text2GradientColor1.value = randomColor(); - controls.text2GradientColor2.value = randomColor(); - controls.text2GradientAngle.value = Math.floor(Math.random() * 360); + // Rainbow flash for gradients + if (animState && controls.animateBgRainbow?.checked) { + const hue = + (animState.progress * + parseFloat(controls.rainbowSpeed.value) * + 360) % + 360; + gradient.addColorStop(0, `hsl(${hue}, 70%, 50%)`); + gradient.addColorStop(1, `hsl(${(hue + 60) % 360}, 70%, 60%)`); + } else { + gradient.addColorStop(0, controls.gradientColor1.value); + gradient.addColorStop(1, controls.gradientColor2.value); + } - // Random border - controls.borderColor.value = randomColor(); - controls.borderWidth.value = Math.floor(Math.random() * 6); - controls.borderStyle.value = ["solid", "inset", "outset", "ridge"][ - Math.floor(Math.random() * 4) - ]; + context.fillStyle = gradient; + context.fillRect(0, 0, 88, 31); + } else if (controls.bgType.value === "texture") { + const texture = drawTexture( + controls.textureType.value, + controls.textureColor1.value, + controls.textureColor2.value, + parseFloat(controls.textureScale.value), + ); + context.drawImage(texture, 0, 0); + } - // Random text styles - controls.fontBold.checked = Math.random() > 0.5; - controls.fontItalic.checked = Math.random() > 0.5; - controls.fontBold2.checked = Math.random() > 0.5; - controls.fontItalic2.checked = Math.random() > 0.5; + // Rainbow gradient overlay (travels across background) + if (animState && controls.animateBgRainbowGradient?.checked) { + // Map progress to position (-100 to 100) + const position = animState.progress * 200 - 100; - // Update displays - updateValueDisplay("gradient-angle", controls.gradientAngle.value); - updateValueDisplay("text-gradient-angle", controls.textGradientAngle.value); - updateValueDisplay( - "text2-gradient-angle", - controls.text2GradientAngle.value, - ); - updateValueDisplay("border-width", controls.borderWidth.value); + // Create a horizontal gradient that sweeps across + const rainbowGradient = context.createLinearGradient( + position - 50, + 0, + position + 50, + 0, + ); - controls.bgType.dispatchEvent(new Event("change")); - controls.textColorType.dispatchEvent(new Event("change")); - controls.text2ColorType.dispatchEvent(new Event("change")); - drawButton(); - }); + // Create rainbow stops that also cycle through colors + const hueOffset = animState.progress * 360; + rainbowGradient.addColorStop( + 0, + `hsla(${(hueOffset + 0) % 360}, 80%, 50%, 0)`, + ); + rainbowGradient.addColorStop( + 0.2, + `hsla(${(hueOffset + 60) % 360}, 80%, 50%, 0.6)`, + ); + rainbowGradient.addColorStop( + 0.4, + `hsla(${(hueOffset + 120) % 360}, 80%, 50%, 0.8)`, + ); + rainbowGradient.addColorStop( + 0.6, + `hsla(${(hueOffset + 180) % 360}, 80%, 50%, 0.8)`, + ); + rainbowGradient.addColorStop( + 0.8, + `hsla(${(hueOffset + 240) % 360}, 80%, 50%, 0.6)`, + ); + rainbowGradient.addColorStop( + 1, + `hsla(${(hueOffset + 300) % 360}, 80%, 50%, 0)`, + ); - document.getElementById("preset-classic").addEventListener("click", () => { - // Classic 90s web button style - controls.bgType.value = "gradient"; - controls.gradientColor1.value = "#0066cc"; - controls.gradientColor2.value = "#0099ff"; - controls.gradientAngle.value = 90; + context.fillStyle = rainbowGradient; + context.fillRect(0, 0, 88, 31); + } - controls.textColorType.value = "solid"; - controls.textColor.value = "#ffffff"; - controls.text2ColorType.value = "solid"; - controls.text2Color.value = "#ffffff"; + // Draw border + const borderWidth = parseFloat(controls.borderWidth.value); + if (borderWidth > 0) { + const style = controls.borderStyle.value; - controls.borderWidth.value = 2; - controls.borderColor.value = "#000000"; - controls.borderStyle.value = "outset"; + if (style === "solid") { + context.strokeStyle = controls.borderColor.value; + context.lineWidth = borderWidth; + context.strokeRect( + borderWidth / 2, + borderWidth / 2, + 88 - borderWidth, + 31 - borderWidth, + ); + } else if (style === "inset" || style === "outset") { + const light = style === "outset"; + context.strokeStyle = light ? "#ffffff" : "#000000"; + context.lineWidth = borderWidth; + context.beginPath(); + context.moveTo(0, 31); + context.lineTo(0, 0); + context.lineTo(88, 0); + context.stroke(); - controls.fontFamily.value = "Oswald"; - controls.fontFamily2.value = "Lato"; - controls.fontBold.checked = true; - controls.fontBold2.checked = false; - controls.fontItalic.checked = false; - controls.fontItalic2.checked = false; + context.strokeStyle = light ? "#000000" : "#ffffff"; + context.beginPath(); + context.moveTo(88, 0); + context.lineTo(88, 31); + context.lineTo(0, 31); + context.stroke(); + } else if (style === "ridge") { + context.strokeStyle = "#ffffff"; + context.lineWidth = borderWidth / 2; + context.strokeRect( + borderWidth / 4, + borderWidth / 4, + 88 - borderWidth / 2, + 31 - borderWidth / 2, + ); - controls.text.value = "RITUAL.SH"; - controls.text2.value = "FREE THE WEB"; - controls.textEnabled.checked = true; - controls.text2Enabled.checked = true; - controls.fontSize.value = 12; - controls.fontSize2.value = 8; - controls.textY.value = 35; - controls.text2Y.value = 65; + context.strokeStyle = "#000000"; + context.strokeRect( + (borderWidth * 3) / 4, + (borderWidth * 3) / 4, + 88 - borderWidth * 1.5, + 31 - borderWidth * 1.5, + ); + } + } - updateValueDisplay("font-size", 12); - updateValueDisplay("font-size2", 8); - updateValueDisplay("gradient-angle", 90); - updateValueDisplay("text-y", 35); - updateValueDisplay("text2-y", 65); + // Apply pulse effect (scale before drawing text) + if (animState && controls.animatePulse?.checked) { + const maxScale = parseFloat(controls.pulseScale.value); + const minScale = 1.0; + const scale = + minScale + + (maxScale - minScale) * + (Math.sin(animState.getPhase(1.0)) * 0.5 + 0.5); - controls.bgType.dispatchEvent(new Event("change")); - controls.textColorType.dispatchEvent(new Event("change")); - controls.text2ColorType.dispatchEvent(new Event("change")); - drawButton(); - }); + context.save(); + context.translate(44, 15.5); + context.scale(scale, scale); + context.translate(-44, -15.5); + } - document.getElementById("preset-modern").addEventListener("click", () => { - // Modern cyberpunk style with gradient text - controls.bgType.value = "gradient"; - controls.gradientColor1.value = "#0a0a0a"; - controls.gradientColor2.value = "#1a0a2e"; - controls.gradientAngle.value = 135; + // Draw text line 1 + drawTextLine(context, 1, animState); - controls.textColorType.value = "gradient"; - controls.textGradientColor1.value = "#00ffaa"; - controls.textGradientColor2.value = "#00ffff"; - controls.textGradientAngle.value = 90; + // Draw text line 2 + drawTextLine(context, 2, animState); - controls.text2ColorType.value = "gradient"; - controls.text2GradientColor1.value = "#ff00ff"; - controls.text2GradientColor2.value = "#ff6600"; - controls.text2GradientAngle.value = 0; + // Restore context if pulse was applied + if (animState && controls.animatePulse?.checked) { + context.restore(); + } - controls.borderWidth.value = 1; - controls.borderColor.value = "#00ffaa"; - controls.borderStyle.value = "solid"; + // Apply shimmer effect + if (animState && controls.animateShimmer?.checked) { + const shimmerX = animState.progress * 120 - 20; // Sweep from -20 to 100 - controls.fontFamily.value = "Roboto Mono"; - controls.fontFamily2.value = "Roboto Mono"; - controls.fontBold.checked = true; - controls.fontBold2.checked = false; - controls.fontItalic.checked = false; - controls.fontItalic2.checked = false; + const shimmerGradient = context.createLinearGradient( + shimmerX - 15, + 0, + shimmerX + 15, + 31, + ); + shimmerGradient.addColorStop(0, "rgba(255, 255, 255, 0)"); + shimmerGradient.addColorStop(0.5, "rgba(255, 255, 255, 0.3)"); + shimmerGradient.addColorStop(1, "rgba(255, 255, 255, 0)"); - controls.text.value = "RITUAL.SH"; - controls.text2.value = "EST. 2024"; - controls.textEnabled.checked = true; - controls.text2Enabled.checked = true; - controls.fontSize.value = 11; - controls.fontSize2.value = 9; - controls.textY.value = 35; - controls.text2Y.value = 65; + context.fillStyle = shimmerGradient; + context.fillRect(0, 0, 88, 31); + } - updateValueDisplay("font-size", 11); - updateValueDisplay("font-size2", 9); - updateValueDisplay("gradient-angle", 135); - updateValueDisplay("text-gradient-angle", 90); - updateValueDisplay("text2-gradient-angle", 0); - updateValueDisplay("text-y", 35); - updateValueDisplay("text2-y", 65); + // Apply glitch effect + if (animState && controls.animateGlitch?.checked) { + applyGlitchEffect(context, animState); + } - controls.textColorType.dispatchEvent(new Event("change")); - controls.text2ColorType.dispatchEvent(new Event("change")); - controls.bgType.dispatchEvent(new Event("change")); - drawButton(); - }); + // Apply scanline effect + if (animState && controls.animateScanline?.checked) { + applyScanlineEffect(context, animState); + } - // Add event listeners to all controls - Object.values(controls).forEach((control) => { - if (control) { - control.addEventListener("input", drawButton); - control.addEventListener("change", drawButton); + // Apply RGB split effect + if (animState && controls.animateRgbSplit?.checked) { + applyRgbSplitEffect(context, animState); + } - if (control.type === "range") { - control.addEventListener("input", (e) => { - updateValueDisplay(e.target.id, e.target.value); + // Apply noise effect + if (animState && controls.animateNoise?.checked) { + applyNoiseEffect(context, animState); + } + + // Restore context if rotate was applied + if (animState && controls.animateRotate?.checked) { + context.restore(); + } + } + + // Apply glitch effect (scanline displacement) + function applyGlitchEffect(context, animState) { + const intensity = parseFloat(controls.glitchIntensity.value); + const imageData = context.getImageData(0, 0, 88, 31); + + // Randomly glitch ~10% of scanlines per frame + const glitchProbability = 0.1; + const maxOffset = intensity; + + for (let y = 0; y < 31; y++) { + if (Math.random() < glitchProbability) { + const offset = Math.floor((Math.random() - 0.5) * maxOffset * 2); + shiftScanline(imageData, y, offset); + } + } + + context.putImageData(imageData, 0, 0); + } + + // Shift a horizontal scanline by offset pixels (with wrapping) + function shiftScanline(imageData, y, offset) { + const width = imageData.width; + const rowStart = y * width * 4; + const rowData = new Uint8ClampedArray(width * 4); + + // Copy row + for (let i = 0; i < width * 4; i++) { + rowData[i] = imageData.data[rowStart + i]; + } + + // Shift and wrap + for (let x = 0; x < width; x++) { + let sourceX = (x - offset + width) % width; + let destIdx = rowStart + x * 4; + let srcIdx = sourceX * 4; + + imageData.data[destIdx] = rowData[srcIdx]; + imageData.data[destIdx + 1] = rowData[srcIdx + 1]; + imageData.data[destIdx + 2] = rowData[srcIdx + 2]; + imageData.data[destIdx + 3] = rowData[srcIdx + 3]; + } + } + + // Apply scanline effect (CRT-style horizontal lines) + function applyScanlineEffect(context, animState) { + const intensity = parseFloat(controls.scanlineIntensity.value); + const speed = parseFloat(controls.scanlineSpeed.value); + + // Create overlay with scanlines + context.globalCompositeOperation = "multiply"; + context.fillStyle = "rgba(0, 0, 0, " + intensity + ")"; + + // Animate scanline position + const offset = (animState.progress * speed * 31) % 2; + + for (let y = offset; y < 31; y += 2) { + context.fillRect(0, Math.floor(y), 88, 1); + } + + context.globalCompositeOperation = "source-over"; + } + + // Apply RGB split/chromatic aberration effect + function applyRgbSplitEffect(context, animState) { + const intensity = parseFloat(controls.rgbSplitIntensity.value); + const imageData = context.getImageData(0, 0, 88, 31); + const result = context.createImageData(88, 31); + + // Oscillating offset + const phase = Math.sin(animState.getPhase(1.0)); + const offsetX = Math.round(phase * intensity); + + for (let y = 0; y < 31; y++) { + for (let x = 0; x < 88; x++) { + const idx = (y * 88 + x) * 4; + + // Red channel - shift left + const redX = Math.max(0, Math.min(87, x - offsetX)); + const redIdx = (y * 88 + redX) * 4; + result.data[idx] = imageData.data[redIdx]; + + // Green channel - no shift + result.data[idx + 1] = imageData.data[idx + 1]; + + // Blue channel - shift right + const blueX = Math.max(0, Math.min(87, x + offsetX)); + const blueIdx = (y * 88 + blueX) * 4; + result.data[idx + 2] = imageData.data[blueIdx + 2]; + + // Alpha channel + result.data[idx + 3] = imageData.data[idx + 3]; + } + } + + context.putImageData(result, 0, 0); + } + + // Apply noise/static effect + function applyNoiseEffect(context, animState) { + const intensity = parseFloat(controls.noiseIntensity.value); + const imageData = context.getImageData(0, 0, 88, 31); + + for (let i = 0; i < imageData.data.length; i += 4) { + // Random noise value + const noise = (Math.random() - 0.5) * 255 * intensity; + + imageData.data[i] = Math.max( + 0, + Math.min(255, imageData.data[i] + noise), + ); + imageData.data[i + 1] = Math.max( + 0, + Math.min(255, imageData.data[i + 1] + noise), + ); + imageData.data[i + 2] = Math.max( + 0, + Math.min(255, imageData.data[i + 2] + noise), + ); + // Alpha unchanged + } + + context.putImageData(imageData, 0, 0); + } + + // Animated preview state + let previewAnimationId = null; + + // Update preview (static or animated based on settings) + function updatePreview() { + if (hasAnimationsEnabled()) { + startAnimatedPreview(); + } else { + stopAnimatedPreview(); + drawToContext(ctx); + } + updateDownloadButtonLabel(); + } + + // Start animated preview loop + function startAnimatedPreview() { + stopAnimatedPreview(); // Clear any existing + + let frameNum = 0; + let lastFrameTime = performance.now(); + const frameDelay = 1000 / ANIMATION_CONFIG.fps; // ms per frame + + const animate = (currentTime) => { + const elapsed = currentTime - lastFrameTime; + + // Only advance frame if enough time has passed + if (elapsed >= frameDelay) { + const animState = new AnimationState( + frameNum, + ANIMATION_CONFIG.totalFrames, + ); + drawToContext(ctx, animState); + + frameNum = (frameNum + 1) % ANIMATION_CONFIG.totalFrames; + lastFrameTime = currentTime - (elapsed % frameDelay); // Carry over extra time + } + + previewAnimationId = requestAnimationFrame(animate); + }; + + previewAnimationId = requestAnimationFrame(animate); + } + + // Stop animated preview + function stopAnimatedPreview() { + if (previewAnimationId) { + cancelAnimationFrame(previewAnimationId); + previewAnimationId = null; + } + } + + // Main draw function + function drawButton() { + updatePreview(); + } + + // Check if any animations are enabled + function hasAnimationsEnabled() { + return !!( + controls.animateTextWave?.checked || + controls.animateTextWave2?.checked || + controls.animateBgRainbow?.checked || + controls.animateBgRainbowGradient?.checked || + controls.animateTextRainbow?.checked || + controls.animateTextRainbow2?.checked || + controls.animateGlitch?.checked || + controls.animatePulse?.checked || + controls.animateShimmer?.checked || + controls.animateScanline?.checked || + controls.animateRgbSplit?.checked || + controls.animateNoise?.checked || + controls.animateRotate?.checked + ); + } + + // Update download button label + function updateDownloadButtonLabel() { + const btn = document.getElementById("download-button"); + btn.textContent = "Download GIF"; + } + + // Export as animated GIF + async function exportAsGif() { + const downloadBtn = document.getElementById("download-button"); + const originalText = downloadBtn.textContent; + downloadBtn.disabled = true; + downloadBtn.textContent = "Generating GIF..."; + + try { + // Create temporary canvas for frame generation + const frameCanvas = document.createElement("canvas"); + frameCanvas.width = 88; + frameCanvas.height = 31; + const frameCtx = frameCanvas.getContext("2d"); + + // Initialize gif.js + const gif = new GIF({ + workers: 2, + quality: 10, + workerScript: "/js/gif.worker.js", + width: 88, + height: 31, }); + + // Generate frames + const totalFrames = ANIMATION_CONFIG.totalFrames; + for (let i = 0; i < totalFrames; i++) { + const animState = new AnimationState(i, totalFrames); + drawToContext(frameCtx, animState); + + // Add frame to GIF (delay in ms) + gif.addFrame(frameCtx, { + delay: 1000 / ANIMATION_CONFIG.fps, + copy: true, + }); + + // Update progress + const progress = Math.round((i / totalFrames) * 100); + downloadBtn.textContent = `Generating: ${progress}%`; + + // Yield to browser to keep UI responsive + if (i % 5 === 0) { + await new Promise((resolve) => setTimeout(resolve, 0)); + } + } + + // Render GIF + gif.on("finished", (blob) => { + // Download + const link = document.createElement("a"); + link.download = "button-88x31.gif"; + link.href = URL.createObjectURL(blob); + link.click(); + + // Cleanup + URL.revokeObjectURL(link.href); + downloadBtn.disabled = false; + downloadBtn.textContent = originalText; + }); + + gif.on("progress", (progress) => { + const percent = Math.round(progress * 100); + downloadBtn.textContent = `Encoding: ${percent}%`; + }); + + gif.render(); + } catch (error) { + console.error("Error generating GIF:", error); + downloadBtn.disabled = false; + downloadBtn.textContent = originalText; + alert("Error generating GIF. Please try again."); } } - }); - // Initial draw - drawButton(); + // Download function + document + .getElementById("download-button") + .addEventListener("click", async () => { + await exportAsGif(); + }); + + // Preset buttons + document.getElementById("preset-random").addEventListener("click", () => { + const randomColor = () => + "#" + + Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, "0"); + + // Random background + controls.bgType.value = ["solid", "gradient", "texture"][ + Math.floor(Math.random() * 3) + ]; + controls.bgColor.value = randomColor(); + controls.gradientColor1.value = randomColor(); + controls.gradientColor2.value = randomColor(); + controls.gradientAngle.value = Math.floor(Math.random() * 360); + controls.textureColor1.value = randomColor(); + controls.textureColor2.value = randomColor(); + controls.textureType.value = [ + "dots", + "grid", + "diagonal", + "checkerboard", + "noise", + "stars", + ][Math.floor(Math.random() * 6)]; + + // Random text 1 color (50% chance of gradient) + controls.textColorType.value = Math.random() > 0.5 ? "gradient" : "solid"; + controls.textColor.value = randomColor(); + controls.textGradientColor1.value = randomColor(); + controls.textGradientColor2.value = randomColor(); + controls.textGradientAngle.value = Math.floor(Math.random() * 360); + + // Random text 2 color (50% chance of gradient) + controls.text2ColorType.value = + Math.random() > 0.5 ? "gradient" : "solid"; + controls.text2Color.value = randomColor(); + controls.text2GradientColor1.value = randomColor(); + controls.text2GradientColor2.value = randomColor(); + controls.text2GradientAngle.value = Math.floor(Math.random() * 360); + + // Random border + controls.borderColor.value = randomColor(); + controls.borderWidth.value = Math.floor(Math.random() * 6); + controls.borderStyle.value = ["solid", "inset", "outset", "ridge"][ + Math.floor(Math.random() * 4) + ]; + + // Random text styles + controls.fontBold.checked = Math.random() > 0.5; + controls.fontItalic.checked = Math.random() > 0.5; + controls.fontBold2.checked = Math.random() > 0.5; + controls.fontItalic2.checked = Math.random() > 0.5; + + // Update displays + updateValueDisplay("gradient-angle", controls.gradientAngle.value); + updateValueDisplay( + "text-gradient-angle", + controls.textGradientAngle.value, + ); + updateValueDisplay( + "text2-gradient-angle", + controls.text2GradientAngle.value, + ); + updateValueDisplay("border-width", controls.borderWidth.value); + + controls.bgType.dispatchEvent(new Event("change")); + controls.textColorType.dispatchEvent(new Event("change")); + controls.text2ColorType.dispatchEvent(new Event("change")); + drawButton(); + }); + + document.getElementById("preset-classic").addEventListener("click", () => { + // Classic 90s web button style + controls.bgType.value = "gradient"; + controls.gradientColor1.value = "#6e6e6eff"; + controls.gradientColor2.value = "#979797"; + controls.gradientAngle.value = 90; + + controls.textColorType.value = "solid"; + controls.textColor.value = "#000000"; + controls.text2ColorType.value = "solid"; + controls.text2Color.value = "#000"; + + controls.borderWidth.value = 2; + controls.borderColor.value = "#000000"; + controls.borderStyle.value = "outset"; + + controls.fontFamily.value = "VT323"; + controls.fontFamily2.value = "VT323"; + controls.fontBold.checked = false; + controls.fontBold2.checked = false; + controls.fontItalic.checked = false; + controls.fontItalic2.checked = false; + + //controls.text.value = "RITUAL.SH"; + //controls.text2.value = "FREE THE WEB"; + controls.textEnabled.checked = true; + controls.text2Enabled.checked = true; + controls.fontSize.value = 12; + controls.fontSize2.value = 8; + controls.textY.value = 50; + controls.text2Y.value = 65; + + updateValueDisplay("font-size", 12); + updateValueDisplay("font-size2", 8); + updateValueDisplay("gradient-angle", 90); + updateValueDisplay("text-y", 35); + updateValueDisplay("text2-y", 65); + + controls.bgType.dispatchEvent(new Event("change")); + controls.textColorType.dispatchEvent(new Event("change")); + controls.text2ColorType.dispatchEvent(new Event("change")); + drawButton(); + }); + + document.getElementById("preset-modern").addEventListener("click", () => { + controls.bgType.value = "gradient"; + controls.gradientColor1.value = "#0a0a0a"; + controls.gradientColor2.value = "#1a0a2e"; + controls.gradientAngle.value = 135; + + controls.textColorType.value = "gradient"; + controls.textGradientColor1.value = "#00ffaa"; + controls.textGradientColor2.value = "#00ffff"; + controls.textGradientAngle.value = 90; + + controls.text2ColorType.value = "gradient"; + controls.text2GradientColor1.value = "#ff00ff"; + controls.text2GradientColor2.value = "#ff6600"; + controls.text2GradientAngle.value = 0; + + controls.borderWidth.value = 1; + controls.borderColor.value = "#00ffaa"; + controls.borderStyle.value = "solid"; + + controls.fontFamily.value = "Roboto Mono"; + controls.fontFamily2.value = "Roboto Mono"; + controls.fontBold.checked = true; + controls.fontBold2.checked = false; + controls.fontItalic.checked = false; + controls.fontItalic2.checked = false; + + //controls.text.value = "RITUAL.SH"; + //controls.text2.value = "EST. 2024"; + controls.textEnabled.checked = true; + controls.text2Enabled.checked = true; + controls.fontSize.value = 11; + controls.fontSize2.value = 9; + controls.textY.value = 35; + controls.text2Y.value = 65; + + updateValueDisplay("font-size", 11); + updateValueDisplay("font-size2", 9); + updateValueDisplay("gradient-angle", 135); + updateValueDisplay("text-gradient-angle", 90); + updateValueDisplay("text2-gradient-angle", 0); + updateValueDisplay("text-y", 35); + updateValueDisplay("text2-y", 65); + + controls.textColorType.dispatchEvent(new Event("change")); + controls.text2ColorType.dispatchEvent(new Event("change")); + controls.bgType.dispatchEvent(new Event("change")); + drawButton(); + }); + + // Add event listeners to all controls + Object.values(controls).forEach((control) => { + if (control) { + control.addEventListener("input", drawButton); + control.addEventListener("change", drawButton); + + if (control.type === "range") { + control.addEventListener("input", (e) => { + updateValueDisplay(e.target.id, e.target.value); + }); + } + } + }); + + // Animation control show/hide listeners + if (controls.animateTextWave) { + controls.animateTextWave.addEventListener("change", () => { + document.getElementById("wave-controls").style.display = controls + .animateTextWave.checked + ? "block" + : "none"; + }); + } + + if (controls.animateTextWave2) { + controls.animateTextWave2.addEventListener("change", () => { + document.getElementById("wave-controls2").style.display = controls + .animateTextWave2.checked + ? "block" + : "none"; + }); + } + + if (controls.animateBgRainbow) { + controls.animateBgRainbow.addEventListener("change", () => { + document.getElementById("rainbow-bg-controls").style.display = controls + .animateBgRainbow.checked + ? "block" + : "none"; + }); + } + + if (controls.animateTextRainbow) { + controls.animateTextRainbow.addEventListener("change", () => { + document.getElementById("rainbow-text-controls").style.display = + controls.animateTextRainbow.checked ? "block" : "none"; + }); + } + + if (controls.animateTextRainbow2) { + controls.animateTextRainbow2.addEventListener("change", () => { + document.getElementById("rainbow-text2-controls").style.display = + controls.animateTextRainbow2.checked ? "block" : "none"; + }); + } + + if (controls.animateGlitch) { + controls.animateGlitch.addEventListener("change", () => { + document.getElementById("glitch-controls").style.display = controls + .animateGlitch.checked + ? "block" + : "none"; + }); + } + + if (controls.animatePulse) { + controls.animatePulse.addEventListener("change", () => { + document.getElementById("pulse-controls").style.display = controls + .animatePulse.checked + ? "block" + : "none"; + }); + } + + if (controls.animateScanline) { + controls.animateScanline.addEventListener("change", () => { + document.getElementById("scanline-controls").style.display = controls + .animateScanline.checked + ? "block" + : "none"; + }); + } + + if (controls.animateRgbSplit) { + controls.animateRgbSplit.addEventListener("change", () => { + document.getElementById("rgb-split-controls").style.display = controls + .animateRgbSplit.checked + ? "block" + : "none"; + }); + } + + if (controls.animateNoise) { + controls.animateNoise.addEventListener("change", () => { + document.getElementById("noise-controls").style.display = controls + .animateNoise.checked + ? "block" + : "none"; + }); + } + + if (controls.animateRotate) { + controls.animateRotate.addEventListener("change", () => { + document.getElementById("rotate-controls").style.display = controls + .animateRotate.checked + ? "block" + : "none"; + }); + } + + // Initial draw + drawButton(); + } // end setupButtonGenerator })(); diff --git a/assets/sass/pages/button-generator.scss b/assets/sass/pages/button-generator.scss index a6a17ec..c6aac4b 100644 --- a/assets/sass/pages/button-generator.scss +++ b/assets/sass/pages/button-generator.scss @@ -143,7 +143,7 @@ image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges; - border: 1px solid rgba(0, 150, 255, 0.4); + //border: 1px solid rgba(0, 150, 255, 0.4); box-shadow: 0 0 15px rgba(0, 150, 255, 0.2); } @@ -159,7 +159,7 @@ rgba(0, 100, 180, 0.15) 0%, rgba(0, 80, 150, 0.1) 100% ); - padding: 1.5rem; + padding: 1rem; border-radius: 6px; border: 1px solid rgba(0, 150, 255, 0.3); box-shadow: @@ -358,23 +358,48 @@ font-weight: bold; transition: transform 0.3s ease; color: rgba(0, 150, 255, 0.8); + } - .control-group.collapsed & { - transform: rotate(180deg); - } + .control-group.collapsed .toggle-icon { + transform: rotate(180deg); } .control-group-content { overflow: hidden; + max-height: 2000px; transition: max-height 0.3s ease, opacity 0.3s ease; + } - .control-group.collapsed & { - max-height: 0; - opacity: 0; - pointer-events: none; - } + .control-group.collapsed .control-group-content { + max-height: 0; + opacity: 0; + pointer-events: none; + } + + .info-text { + font-size: 0.85rem; + color: rgba(150, 200, 255, 0.7); + font-style: italic; + margin-bottom: 1rem; + padding: 0.5rem; + background: rgba(0, 100, 180, 0.1); + border-left: 3px solid rgba(0, 150, 255, 0.5); + border-radius: 2px; + } + + h4 { + margin-top: 1.5rem; + margin-bottom: 0.75rem; + color: rgba(100, 180, 255, 0.85); + font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.9rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid rgba(0, 150, 255, 0.3); } .checkbox-row { diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index f70ee2c..5355ccb 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -703,26 +703,26 @@ &::before { content: ""; position: absolute; - left: -4rem; + left: -2rem; top: 50%; transform: translateY(-50%); width: 3rem; height: 3px; background: linear-gradient(90deg, transparent, #0096ff); - box-shadow: 0 0 10px rgba(0, 150, 255, 0.8); + //box-shadow: 0 0 10px rgba(0, 150, 255, 0.8); } // Orange line on the right &::after { content: ""; position: absolute; - right: -4rem; + right: -2rem; top: 50%; transform: translateY(-50%); width: 3rem; height: 3px; background: linear-gradient(90deg, #ff7800, transparent); - box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); + //box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); } @include media-up(md) { @@ -730,15 +730,15 @@ &::before, &::after { - width: 5rem; + width: 6rem; } &::before { - left: -6rem; + left: -2rem; } &::after { - right: -6rem; + right: -2rem; } } } diff --git a/content/media/taskmaster-champion-of-champions-4/canvas.png b/content/media/taskmaster-champion-of-champions-4/canvas.png new file mode 100644 index 0000000000000000000000000000000000000000..a96b8e7e35074b3c27d57c1cca1f6d248ce37562 GIT binary patch literal 1625 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 Xfkfkz*D%m$00000NkvXXu0mjf&#)b0 literal 0 HcmV?d00001 diff --git a/content/resources/button-generator/index.md b/content/resources/button-generator/index.md index 0dbad1b..fad3797 100644 --- a/content/resources/button-generator/index.md +++ b/content/resources/button-generator/index.md @@ -10,7 +10,7 @@ draft: false Welcome to my 88x31 button creator, this is a pretty rough and ready implementation so it could be buggy, please let me know if you find any issues. -Currently this only supports static images and exports as png due to the basic `canvas` tag limitations. I have approximate plans for how to make this export gifs and potentially make animated buttons, please look forward to it. +This supports gif despite the basic `canvas` tag limitation courtesy of [gif.js](https://github.com/jnordberg/gif.js) - none of this would be possible without that project. Big thanks to [neonaut's 88x31 archive](https://neonaut.neocities.org/cyber/88x31) and everyone who made the buttons that appear there. You should check it out if you need inspiration for your button! diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index e3146ce..0ac2b0f 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -36,6 +36,7 @@ <!-- Button Generator - only load if page content contains button-generator shortcode --> {{ if or (findRE "{{<\\s*button-generator" .RawContent) (findRE "{{%\\s*button-generator" .RawContent) }} + <script src="{{ "js/gif.js" | relURL }}"></script> {{ $buttonGenerator := resources.Get "js/button-generator.js" | resources.Minify | resources.Fingerprint }} <script src="{{ $buttonGenerator.RelPermalink }}" integrity="{{ $buttonGenerator.Data.Integrity }}"></script> {{ end }} diff --git a/layouts/shortcodes/button-generator.html b/layouts/shortcodes/button-generator.html index 0572f2f..8d65fa3 100644 --- a/layouts/shortcodes/button-generator.html +++ b/layouts/shortcodes/button-generator.html @@ -121,6 +121,62 @@ </div> </div> + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 1 Animation</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-wave" /> + <span>Wave Animation</span> + </label> + <div id="wave-controls" style="display: none"> + <label for="wave-amplitude" + >Amplitude: <span id="wave-amplitude-value">3</span>px</label + > + <input + type="range" + id="wave-amplitude" + min="0" + max="10" + value="3" + step="0.5" + /> + + <label for="wave-speed" + >Speed: <span id="wave-speed-value">1.0</span>x</label + > + <input + type="range" + id="wave-speed" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-rainbow" /> + <span>Rainbow Text</span> + </label> + <div id="rainbow-text-controls" style="display: none"> + <label for="text-rainbow-speed" + >Speed: <span id="text-rainbow-speed-value">1.0</span>x</label + > + <input + type="range" + id="text-rainbow-speed" + min="0.5" + max="5" + value="1.0" + step="0.1" + /> + </div> + </div> + </div> + <div class="control-group"> <h3 class="control-group-header"> <span>Text Line 2</span> @@ -219,6 +275,62 @@ </div> </div> + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 2 Animation</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-wave2" /> + <span>Wave Animation</span> + </label> + <div id="wave-controls2" style="display: none"> + <label for="wave-amplitude2" + >Amplitude: <span id="wave-amplitude2-value">3</span>px</label + > + <input + type="range" + id="wave-amplitude2" + min="0" + max="10" + value="3" + step="0.5" + /> + + <label for="wave-speed2" + >Speed: <span id="wave-speed2-value">1.0</span>x</label + > + <input + type="range" + id="wave-speed2" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-rainbow2" /> + <span>Rainbow Text</span> + </label> + <div id="rainbow-text2-controls" style="display: none"> + <label for="text-rainbow-speed2" + >Speed: <span id="text-rainbow-speed2-value">1.0</span>x</label + > + <input + type="range" + id="text-rainbow-speed2" + min="0.5" + max="5" + value="1.0" + step="0.1" + /> + </div> + </div> + </div> + <div class="control-group"> <h3 class="control-group-header"> <span>Background</span> @@ -287,6 +399,37 @@ </div> </div> + <div class="control-group"> + <h3 class="control-group-header"> + <span>Background Animation</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label class="checkbox-label"> + <input type="checkbox" id="animate-bg-rainbow" /> + <span>Rainbow Flash</span> + </label> + <div id="rainbow-bg-controls" style="display: none"> + <label for="rainbow-speed" + >Speed: <span id="rainbow-speed-value">0.5</span>x</label + > + <input + type="range" + id="rainbow-speed" + min="0.1" + max="3" + value="0.5" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-bg-rainbow-gradient" /> + <span>Rainbow Gradient</span> + </label> + </div> + </div> + <div class="control-group"> <h3 class="control-group-header"> <span>Border</span> @@ -310,6 +453,155 @@ </select> </div> </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Special Effects</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <p class="info-text"> + Almost all animations should stack, so pick as many as you want. + </p> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-glitch" /> + <span>Glitch Effect</span> + </label> + <div id="glitch-controls" style="display: none"> + <label for="glitch-intensity" + >Intensity: <span id="glitch-intensity-value">3</span></label + > + <input + type="range" + id="glitch-intensity" + min="1" + max="10" + value="3" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-pulse" /> + <span>Pulse Effect</span> + </label> + <div id="pulse-controls" style="display: none"> + <label for="pulse-scale" + >Scale: <span id="pulse-scale-value">1.1</span>x</label + > + <input + type="range" + id="pulse-scale" + min="1.0" + max="1.3" + value="1.1" + step="0.05" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-shimmer" /> + <span>Shimmer Effect</span> + </label> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-scanline" /> + <span>Scanline Effect</span> + </label> + <div id="scanline-controls" style="display: none"> + <label for="scanline-intensity" + >Intensity: <span id="scanline-intensity-value">0.3</span></label + > + <input + type="range" + id="scanline-intensity" + min="0.1" + max="1" + value="0.3" + step="0.1" + /> + + <label for="scanline-speed" + >Speed: <span id="scanline-speed-value">1.0</span>x</label + > + <input + type="range" + id="scanline-speed" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-rgb-split" /> + <span>RGB Split Effect</span> + </label> + <div id="rgb-split-controls" style="display: none"> + <label for="rgb-split-intensity" + >Intensity: <span id="rgb-split-intensity-value">2</span>px</label + > + <input + type="range" + id="rgb-split-intensity" + min="1" + max="5" + value="2" + step="0.5" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-noise" /> + <span>Noise Effect</span> + </label> + <div id="noise-controls" style="display: none"> + <label for="noise-intensity" + >Intensity: <span id="noise-intensity-value">0.1</span></label + > + <input + type="range" + id="noise-intensity" + min="0.05" + max="0.5" + value="0.1" + step="0.05" + /> + </div> + {{/* + <label class="checkbox-label"> + <input type="checkbox" id="animate-rotate" /> + <span>Rotate Effect</span> + </label> + <div id="rotate-controls" style="display: none"> + <label for="rotate-angle" + >Max Angle: <span id="rotate-angle-value">5</span>°</label + > + <input + type="range" + id="rotate-angle" + min="2" + max="15" + value="5" + step="1" + /> + + <label for="rotate-speed" + >Speed: <span id="rotate-speed-value">1.0</span>x</label + > + <input + type="range" + id="rotate-speed" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + */}} + </div> + </div> </div> </div> </div> diff --git a/static/js/gif.js b/static/js/gif.js new file mode 100644 index 0000000..2e4d204 --- /dev/null +++ b/static/js/gif.js @@ -0,0 +1,3 @@ +// gif.js 0.2.0 - https://github.com/jnordberg/gif.js +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.GIF=f()}})(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}({1:[function(require,module,exports){function EventEmitter(){this._events=this._events||{};this._maxListeners=this._maxListeners||undefined}module.exports=EventEmitter;EventEmitter.EventEmitter=EventEmitter;EventEmitter.prototype._events=undefined;EventEmitter.prototype._maxListeners=undefined;EventEmitter.defaultMaxListeners=10;EventEmitter.prototype.setMaxListeners=function(n){if(!isNumber(n)||n<0||isNaN(n))throw TypeError("n must be a positive number");this._maxListeners=n;return this};EventEmitter.prototype.emit=function(type){var er,handler,len,args,i,listeners;if(!this._events)this._events={};if(type==="error"){if(!this._events.error||isObject(this._events.error)&&!this._events.error.length){er=arguments[1];if(er instanceof Error){throw er}else{var err=new Error('Uncaught, unspecified "error" event. ('+er+")");err.context=er;throw err}}}handler=this._events[type];if(isUndefined(handler))return false;if(isFunction(handler)){switch(arguments.length){case 1:handler.call(this);break;case 2:handler.call(this,arguments[1]);break;case 3:handler.call(this,arguments[1],arguments[2]);break;default:args=Array.prototype.slice.call(arguments,1);handler.apply(this,args)}}else if(isObject(handler)){args=Array.prototype.slice.call(arguments,1);listeners=handler.slice();len=listeners.length;for(i=0;i<len;i++)listeners[i].apply(this,args)}return true};EventEmitter.prototype.addListener=function(type,listener){var m;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events)this._events={};if(this._events.newListener)this.emit("newListener",type,isFunction(listener.listener)?listener.listener:listener);if(!this._events[type])this._events[type]=listener;else if(isObject(this._events[type]))this._events[type].push(listener);else this._events[type]=[this._events[type],listener];if(isObject(this._events[type])&&!this._events[type].warned){if(!isUndefined(this._maxListeners)){m=this._maxListeners}else{m=EventEmitter.defaultMaxListeners}if(m&&m>0&&this._events[type].length>m){this._events[type].warned=true;console.error("(node) warning: possible EventEmitter memory "+"leak detected. %d listeners added. "+"Use emitter.setMaxListeners() to increase limit.",this._events[type].length);if(typeof console.trace==="function"){console.trace()}}}return this};EventEmitter.prototype.on=EventEmitter.prototype.addListener;EventEmitter.prototype.once=function(type,listener){if(!isFunction(listener))throw TypeError("listener must be a function");var fired=false;function g(){this.removeListener(type,g);if(!fired){fired=true;listener.apply(this,arguments)}}g.listener=listener;this.on(type,g);return this};EventEmitter.prototype.removeListener=function(type,listener){var list,position,length,i;if(!isFunction(listener))throw TypeError("listener must be a function");if(!this._events||!this._events[type])return this;list=this._events[type];length=list.length;position=-1;if(list===listener||isFunction(list.listener)&&list.listener===listener){delete this._events[type];if(this._events.removeListener)this.emit("removeListener",type,listener)}else if(isObject(list)){for(i=length;i-- >0;){if(list[i]===listener||list[i].listener&&list[i].listener===listener){position=i;break}}if(position<0)return this;if(list.length===1){list.length=0;delete this._events[type]}else{list.splice(position,1)}if(this._events.removeListener)this.emit("removeListener",type,listener)}return this};EventEmitter.prototype.removeAllListeners=function(type){var key,listeners;if(!this._events)return this;if(!this._events.removeListener){if(arguments.length===0)this._events={};else if(this._events[type])delete this._events[type];return this}if(arguments.length===0){for(key in this._events){if(key==="removeListener")continue;this.removeAllListeners(key)}this.removeAllListeners("removeListener");this._events={};return this}listeners=this._events[type];if(isFunction(listeners)){this.removeListener(type,listeners)}else if(listeners){while(listeners.length)this.removeListener(type,listeners[listeners.length-1])}delete this._events[type];return this};EventEmitter.prototype.listeners=function(type){var ret;if(!this._events||!this._events[type])ret=[];else if(isFunction(this._events[type]))ret=[this._events[type]];else ret=this._events[type].slice();return ret};EventEmitter.prototype.listenerCount=function(type){if(this._events){var evlistener=this._events[type];if(isFunction(evlistener))return 1;else if(evlistener)return evlistener.length}return 0};EventEmitter.listenerCount=function(emitter,type){return emitter.listenerCount(type)};function isFunction(arg){return typeof arg==="function"}function isNumber(arg){return typeof arg==="number"}function isObject(arg){return typeof arg==="object"&&arg!==null}function isUndefined(arg){return arg===void 0}},{}],2:[function(require,module,exports){var UA,browser,mode,platform,ua;ua=navigator.userAgent.toLowerCase();platform=navigator.platform.toLowerCase();UA=ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/)||[null,"unknown",0];mode=UA[1]==="ie"&&document.documentMode;browser={name:UA[1]==="version"?UA[3]:UA[1],version:mode||parseFloat(UA[1]==="opera"&&UA[4]?UA[4]:UA[2]),platform:{name:ua.match(/ip(?:ad|od|hone)/)?"ios":(ua.match(/(?:webos|android)/)||platform.match(/mac|win|linux/)||["other"])[0]}};browser[browser.name]=true;browser[browser.name+parseInt(browser.version,10)]=true;browser.platform[browser.platform.name]=true;module.exports=browser},{}],3:[function(require,module,exports){var EventEmitter,GIF,browser,extend=function(child,parent){for(var key in parent){if(hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child},hasProp={}.hasOwnProperty,indexOf=[].indexOf||function(item){for(var i=0,l=this.length;i<l;i++){if(i in this&&this[i]===item)return i}return-1},slice=[].slice;EventEmitter=require("events").EventEmitter;browser=require("./browser.coffee");GIF=function(superClass){var defaults,frameDefaults;extend(GIF,superClass);defaults={workerScript:"gif.worker.js",workers:2,repeat:0,background:"#fff",quality:10,width:null,height:null,transparent:null,debug:false,dither:false};frameDefaults={delay:500,copy:false};function GIF(options){var base,key,value;this.running=false;this.options={};this.frames=[];this.freeWorkers=[];this.activeWorkers=[];this.setOptions(options);for(key in defaults){value=defaults[key];if((base=this.options)[key]==null){base[key]=value}}}GIF.prototype.setOption=function(key,value){this.options[key]=value;if(this._canvas!=null&&(key==="width"||key==="height")){return this._canvas[key]=value}};GIF.prototype.setOptions=function(options){var key,results,value;results=[];for(key in options){if(!hasProp.call(options,key))continue;value=options[key];results.push(this.setOption(key,value))}return results};GIF.prototype.addFrame=function(image,options){var frame,key;if(options==null){options={}}frame={};frame.transparent=this.options.transparent;for(key in frameDefaults){frame[key]=options[key]||frameDefaults[key]}if(this.options.width==null){this.setOption("width",image.width)}if(this.options.height==null){this.setOption("height",image.height)}if(typeof ImageData!=="undefined"&&ImageData!==null&&image instanceof ImageData){frame.data=image.data}else if(typeof CanvasRenderingContext2D!=="undefined"&&CanvasRenderingContext2D!==null&&image instanceof CanvasRenderingContext2D||typeof WebGLRenderingContext!=="undefined"&&WebGLRenderingContext!==null&&image instanceof WebGLRenderingContext){if(options.copy){frame.data=this.getContextData(image)}else{frame.context=image}}else if(image.childNodes!=null){if(options.copy){frame.data=this.getImageData(image)}else{frame.image=image}}else{throw new Error("Invalid image")}return this.frames.push(frame)};GIF.prototype.render=function(){var i,j,numWorkers,ref;if(this.running){throw new Error("Already running")}if(this.options.width==null||this.options.height==null){throw new Error("Width and height must be set prior to rendering")}this.running=true;this.nextFrame=0;this.finishedFrames=0;this.imageParts=function(){var j,ref,results;results=[];for(i=j=0,ref=this.frames.length;0<=ref?j<ref:j>ref;i=0<=ref?++j:--j){results.push(null)}return results}.call(this);numWorkers=this.spawnWorkers();if(this.options.globalPalette===true){this.renderNextFrame()}else{for(i=j=0,ref=numWorkers;0<=ref?j<ref:j>ref;i=0<=ref?++j:--j){this.renderNextFrame()}}this.emit("start");return this.emit("progress",0)};GIF.prototype.abort=function(){var worker;while(true){worker=this.activeWorkers.shift();if(worker==null){break}this.log("killing active worker");worker.terminate()}this.running=false;return this.emit("abort")};GIF.prototype.spawnWorkers=function(){var j,numWorkers,ref,results;numWorkers=Math.min(this.options.workers,this.frames.length);(function(){results=[];for(var j=ref=this.freeWorkers.length;ref<=numWorkers?j<numWorkers:j>numWorkers;ref<=numWorkers?j++:j--){results.push(j)}return results}).apply(this).forEach(function(_this){return function(i){var worker;_this.log("spawning worker "+i);worker=new Worker(_this.options.workerScript);worker.onmessage=function(event){_this.activeWorkers.splice(_this.activeWorkers.indexOf(worker),1);_this.freeWorkers.push(worker);return _this.frameFinished(event.data)};return _this.freeWorkers.push(worker)}}(this));return numWorkers};GIF.prototype.frameFinished=function(frame){var i,j,ref;this.log("frame "+frame.index+" finished - "+this.activeWorkers.length+" active");this.finishedFrames++;this.emit("progress",this.finishedFrames/this.frames.length);this.imageParts[frame.index]=frame;if(this.options.globalPalette===true){this.options.globalPalette=frame.globalPalette;this.log("global palette analyzed");if(this.frames.length>2){for(i=j=1,ref=this.freeWorkers.length;1<=ref?j<ref:j>ref;i=1<=ref?++j:--j){this.renderNextFrame()}}}if(indexOf.call(this.imageParts,null)>=0){return this.renderNextFrame()}else{return this.finishRendering()}};GIF.prototype.finishRendering=function(){var data,frame,i,image,j,k,l,len,len1,len2,len3,offset,page,ref,ref1,ref2;len=0;ref=this.imageParts;for(j=0,len1=ref.length;j<len1;j++){frame=ref[j];len+=(frame.data.length-1)*frame.pageSize+frame.cursor}len+=frame.pageSize-frame.cursor;this.log("rendering finished - filesize "+Math.round(len/1e3)+"kb");data=new Uint8Array(len);offset=0;ref1=this.imageParts;for(k=0,len2=ref1.length;k<len2;k++){frame=ref1[k];ref2=frame.data;for(i=l=0,len3=ref2.length;l<len3;i=++l){page=ref2[i];data.set(page,offset);if(i===frame.data.length-1){offset+=frame.cursor}else{offset+=frame.pageSize}}}image=new Blob([data],{type:"image/gif"});return this.emit("finished",image,data)};GIF.prototype.renderNextFrame=function(){var frame,task,worker;if(this.freeWorkers.length===0){throw new Error("No free workers")}if(this.nextFrame>=this.frames.length){return}frame=this.frames[this.nextFrame++];worker=this.freeWorkers.shift();task=this.getTask(frame);this.log("starting frame "+(task.index+1)+" of "+this.frames.length);this.activeWorkers.push(worker);return worker.postMessage(task)};GIF.prototype.getContextData=function(ctx){return ctx.getImageData(0,0,this.options.width,this.options.height).data};GIF.prototype.getImageData=function(image){var ctx;if(this._canvas==null){this._canvas=document.createElement("canvas");this._canvas.width=this.options.width;this._canvas.height=this.options.height}ctx=this._canvas.getContext("2d");ctx.setFill=this.options.background;ctx.fillRect(0,0,this.options.width,this.options.height);ctx.drawImage(image,0,0);return this.getContextData(ctx)};GIF.prototype.getTask=function(frame){var index,task;index=this.frames.indexOf(frame);task={index:index,last:index===this.frames.length-1,delay:frame.delay,transparent:frame.transparent,width:this.options.width,height:this.options.height,quality:this.options.quality,dither:this.options.dither,globalPalette:this.options.globalPalette,repeat:this.options.repeat,canTransfer:browser.name==="chrome"};if(frame.data!=null){task.data=frame.data}else if(frame.context!=null){task.data=this.getContextData(frame.context)}else if(frame.image!=null){task.data=this.getImageData(frame.image)}else{throw new Error("Invalid frame")}return task};GIF.prototype.log=function(){var args;args=1<=arguments.length?slice.call(arguments,0):[];if(!this.options.debug){return}return console.log.apply(console,args)};return GIF}(EventEmitter);module.exports=GIF},{"./browser.coffee":2,events:1}]},{},[3])(3)}); +//# sourceMappingURL=gif.js.map diff --git a/static/js/gif.worker.js b/static/js/gif.worker.js new file mode 100644 index 0000000..269624e --- /dev/null +++ b/static/js/gif.worker.js @@ -0,0 +1,3 @@ +// gif.worker.js 0.2.0 - https://github.com/jnordberg/gif.js +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){var NeuQuant=require("./TypedNeuQuant.js");var LZWEncoder=require("./LZWEncoder.js");function ByteArray(){this.page=-1;this.pages=[];this.newPage()}ByteArray.pageSize=4096;ByteArray.charMap={};for(var i=0;i<256;i++)ByteArray.charMap[i]=String.fromCharCode(i);ByteArray.prototype.newPage=function(){this.pages[++this.page]=new Uint8Array(ByteArray.pageSize);this.cursor=0};ByteArray.prototype.getData=function(){var rv="";for(var p=0;p<this.pages.length;p++){for(var i=0;i<ByteArray.pageSize;i++){rv+=ByteArray.charMap[this.pages[p][i]]}}return rv};ByteArray.prototype.writeByte=function(val){if(this.cursor>=ByteArray.pageSize)this.newPage();this.pages[this.page][this.cursor++]=val};ByteArray.prototype.writeUTFBytes=function(string){for(var l=string.length,i=0;i<l;i++)this.writeByte(string.charCodeAt(i))};ByteArray.prototype.writeBytes=function(array,offset,length){for(var l=length||array.length,i=offset||0;i<l;i++)this.writeByte(array[i])};function GIFEncoder(width,height){this.width=~~width;this.height=~~height;this.transparent=null;this.transIndex=0;this.repeat=-1;this.delay=0;this.image=null;this.pixels=null;this.indexedPixels=null;this.colorDepth=null;this.colorTab=null;this.neuQuant=null;this.usedEntry=new Array;this.palSize=7;this.dispose=-1;this.firstFrame=true;this.sample=10;this.dither=false;this.globalPalette=false;this.out=new ByteArray}GIFEncoder.prototype.setDelay=function(milliseconds){this.delay=Math.round(milliseconds/10)};GIFEncoder.prototype.setFrameRate=function(fps){this.delay=Math.round(100/fps)};GIFEncoder.prototype.setDispose=function(disposalCode){if(disposalCode>=0)this.dispose=disposalCode};GIFEncoder.prototype.setRepeat=function(repeat){this.repeat=repeat};GIFEncoder.prototype.setTransparent=function(color){this.transparent=color};GIFEncoder.prototype.addFrame=function(imageData){this.image=imageData;this.colorTab=this.globalPalette&&this.globalPalette.slice?this.globalPalette:null;this.getImagePixels();this.analyzePixels();if(this.globalPalette===true)this.globalPalette=this.colorTab;if(this.firstFrame){this.writeLSD();this.writePalette();if(this.repeat>=0){this.writeNetscapeExt()}}this.writeGraphicCtrlExt();this.writeImageDesc();if(!this.firstFrame&&!this.globalPalette)this.writePalette();this.writePixels();this.firstFrame=false};GIFEncoder.prototype.finish=function(){this.out.writeByte(59)};GIFEncoder.prototype.setQuality=function(quality){if(quality<1)quality=1;this.sample=quality};GIFEncoder.prototype.setDither=function(dither){if(dither===true)dither="FloydSteinberg";this.dither=dither};GIFEncoder.prototype.setGlobalPalette=function(palette){this.globalPalette=palette};GIFEncoder.prototype.getGlobalPalette=function(){return this.globalPalette&&this.globalPalette.slice&&this.globalPalette.slice(0)||this.globalPalette};GIFEncoder.prototype.writeHeader=function(){this.out.writeUTFBytes("GIF89a")};GIFEncoder.prototype.analyzePixels=function(){if(!this.colorTab){this.neuQuant=new NeuQuant(this.pixels,this.sample);this.neuQuant.buildColormap();this.colorTab=this.neuQuant.getColormap()}if(this.dither){this.ditherPixels(this.dither.replace("-serpentine",""),this.dither.match(/-serpentine/)!==null)}else{this.indexPixels()}this.pixels=null;this.colorDepth=8;this.palSize=7;if(this.transparent!==null){this.transIndex=this.findClosest(this.transparent,true)}};GIFEncoder.prototype.indexPixels=function(imgq){var nPix=this.pixels.length/3;this.indexedPixels=new Uint8Array(nPix);var k=0;for(var j=0;j<nPix;j++){var index=this.findClosestRGB(this.pixels[k++]&255,this.pixels[k++]&255,this.pixels[k++]&255);this.usedEntry[index]=true;this.indexedPixels[j]=index}};GIFEncoder.prototype.ditherPixels=function(kernel,serpentine){var kernels={FalseFloydSteinberg:[[3/8,1,0],[3/8,0,1],[2/8,1,1]],FloydSteinberg:[[7/16,1,0],[3/16,-1,1],[5/16,0,1],[1/16,1,1]],Stucki:[[8/42,1,0],[4/42,2,0],[2/42,-2,1],[4/42,-1,1],[8/42,0,1],[4/42,1,1],[2/42,2,1],[1/42,-2,2],[2/42,-1,2],[4/42,0,2],[2/42,1,2],[1/42,2,2]],Atkinson:[[1/8,1,0],[1/8,2,0],[1/8,-1,1],[1/8,0,1],[1/8,1,1],[1/8,0,2]]};if(!kernel||!kernels[kernel]){throw"Unknown dithering kernel: "+kernel}var ds=kernels[kernel];var index=0,height=this.height,width=this.width,data=this.pixels;var direction=serpentine?-1:1;this.indexedPixels=new Uint8Array(this.pixels.length/3);for(var y=0;y<height;y++){if(serpentine)direction=direction*-1;for(var x=direction==1?0:width-1,xend=direction==1?width:0;x!==xend;x+=direction){index=y*width+x;var idx=index*3;var r1=data[idx];var g1=data[idx+1];var b1=data[idx+2];idx=this.findClosestRGB(r1,g1,b1);this.usedEntry[idx]=true;this.indexedPixels[index]=idx;idx*=3;var r2=this.colorTab[idx];var g2=this.colorTab[idx+1];var b2=this.colorTab[idx+2];var er=r1-r2;var eg=g1-g2;var eb=b1-b2;for(var i=direction==1?0:ds.length-1,end=direction==1?ds.length:0;i!==end;i+=direction){var x1=ds[i][1];var y1=ds[i][2];if(x1+x>=0&&x1+x<width&&y1+y>=0&&y1+y<height){var d=ds[i][0];idx=index+x1+y1*width;idx*=3;data[idx]=Math.max(0,Math.min(255,data[idx]+er*d));data[idx+1]=Math.max(0,Math.min(255,data[idx+1]+eg*d));data[idx+2]=Math.max(0,Math.min(255,data[idx+2]+eb*d))}}}}};GIFEncoder.prototype.findClosest=function(c,used){return this.findClosestRGB((c&16711680)>>16,(c&65280)>>8,c&255,used)};GIFEncoder.prototype.findClosestRGB=function(r,g,b,used){if(this.colorTab===null)return-1;if(this.neuQuant&&!used){return this.neuQuant.lookupRGB(r,g,b)}var c=b|g<<8|r<<16;var minpos=0;var dmin=256*256*256;var len=this.colorTab.length;for(var i=0,index=0;i<len;index++){var dr=r-(this.colorTab[i++]&255);var dg=g-(this.colorTab[i++]&255);var db=b-(this.colorTab[i++]&255);var d=dr*dr+dg*dg+db*db;if((!used||this.usedEntry[index])&&d<dmin){dmin=d;minpos=index}}return minpos};GIFEncoder.prototype.getImagePixels=function(){var w=this.width;var h=this.height;this.pixels=new Uint8Array(w*h*3);var data=this.image;var srcPos=0;var count=0;for(var i=0;i<h;i++){for(var j=0;j<w;j++){this.pixels[count++]=data[srcPos++];this.pixels[count++]=data[srcPos++];this.pixels[count++]=data[srcPos++];srcPos++}}};GIFEncoder.prototype.writeGraphicCtrlExt=function(){this.out.writeByte(33);this.out.writeByte(249);this.out.writeByte(4);var transp,disp;if(this.transparent===null){transp=0;disp=0}else{transp=1;disp=2}if(this.dispose>=0){disp=dispose&7}disp<<=2;this.out.writeByte(0|disp|0|transp);this.writeShort(this.delay);this.out.writeByte(this.transIndex);this.out.writeByte(0)};GIFEncoder.prototype.writeImageDesc=function(){this.out.writeByte(44);this.writeShort(0);this.writeShort(0);this.writeShort(this.width);this.writeShort(this.height);if(this.firstFrame||this.globalPalette){this.out.writeByte(0)}else{this.out.writeByte(128|0|0|0|this.palSize)}};GIFEncoder.prototype.writeLSD=function(){this.writeShort(this.width);this.writeShort(this.height);this.out.writeByte(128|112|0|this.palSize);this.out.writeByte(0);this.out.writeByte(0)};GIFEncoder.prototype.writeNetscapeExt=function(){this.out.writeByte(33);this.out.writeByte(255);this.out.writeByte(11);this.out.writeUTFBytes("NETSCAPE2.0");this.out.writeByte(3);this.out.writeByte(1);this.writeShort(this.repeat);this.out.writeByte(0)};GIFEncoder.prototype.writePalette=function(){this.out.writeBytes(this.colorTab);var n=3*256-this.colorTab.length;for(var i=0;i<n;i++)this.out.writeByte(0)};GIFEncoder.prototype.writeShort=function(pValue){this.out.writeByte(pValue&255);this.out.writeByte(pValue>>8&255)};GIFEncoder.prototype.writePixels=function(){var enc=new LZWEncoder(this.width,this.height,this.indexedPixels,this.colorDepth);enc.encode(this.out)};GIFEncoder.prototype.stream=function(){return this.out};module.exports=GIFEncoder},{"./LZWEncoder.js":2,"./TypedNeuQuant.js":3}],2:[function(require,module,exports){var EOF=-1;var BITS=12;var HSIZE=5003;var masks=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535];function LZWEncoder(width,height,pixels,colorDepth){var initCodeSize=Math.max(2,colorDepth);var accum=new Uint8Array(256);var htab=new Int32Array(HSIZE);var codetab=new Int32Array(HSIZE);var cur_accum,cur_bits=0;var a_count;var free_ent=0;var maxcode;var clear_flg=false;var g_init_bits,ClearCode,EOFCode;function char_out(c,outs){accum[a_count++]=c;if(a_count>=254)flush_char(outs)}function cl_block(outs){cl_hash(HSIZE);free_ent=ClearCode+2;clear_flg=true;output(ClearCode,outs)}function cl_hash(hsize){for(var i=0;i<hsize;++i)htab[i]=-1}function compress(init_bits,outs){var fcode,c,i,ent,disp,hsize_reg,hshift;g_init_bits=init_bits;clear_flg=false;n_bits=g_init_bits;maxcode=MAXCODE(n_bits);ClearCode=1<<init_bits-1;EOFCode=ClearCode+1;free_ent=ClearCode+2;a_count=0;ent=nextPixel();hshift=0;for(fcode=HSIZE;fcode<65536;fcode*=2)++hshift;hshift=8-hshift;hsize_reg=HSIZE;cl_hash(hsize_reg);output(ClearCode,outs);outer_loop:while((c=nextPixel())!=EOF){fcode=(c<<BITS)+ent;i=c<<hshift^ent;if(htab[i]===fcode){ent=codetab[i];continue}else if(htab[i]>=0){disp=hsize_reg-i;if(i===0)disp=1;do{if((i-=disp)<0)i+=hsize_reg;if(htab[i]===fcode){ent=codetab[i];continue outer_loop}}while(htab[i]>=0)}output(ent,outs);ent=c;if(free_ent<1<<BITS){codetab[i]=free_ent++;htab[i]=fcode}else{cl_block(outs)}}output(ent,outs);output(EOFCode,outs)}function encode(outs){outs.writeByte(initCodeSize);remaining=width*height;curPixel=0;compress(initCodeSize+1,outs);outs.writeByte(0)}function flush_char(outs){if(a_count>0){outs.writeByte(a_count);outs.writeBytes(accum,0,a_count);a_count=0}}function MAXCODE(n_bits){return(1<<n_bits)-1}function nextPixel(){if(remaining===0)return EOF;--remaining;var pix=pixels[curPixel++];return pix&255}function output(code,outs){cur_accum&=masks[cur_bits];if(cur_bits>0)cur_accum|=code<<cur_bits;else cur_accum=code;cur_bits+=n_bits;while(cur_bits>=8){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}if(free_ent>maxcode||clear_flg){if(clear_flg){maxcode=MAXCODE(n_bits=g_init_bits);clear_flg=false}else{++n_bits;if(n_bits==BITS)maxcode=1<<BITS;else maxcode=MAXCODE(n_bits)}}if(code==EOFCode){while(cur_bits>0){char_out(cur_accum&255,outs);cur_accum>>=8;cur_bits-=8}flush_char(outs)}}this.encode=encode}module.exports=LZWEncoder},{}],3:[function(require,module,exports){var ncycles=100;var netsize=256;var maxnetpos=netsize-1;var netbiasshift=4;var intbiasshift=16;var intbias=1<<intbiasshift;var gammashift=10;var gamma=1<<gammashift;var betashift=10;var beta=intbias>>betashift;var betagamma=intbias<<gammashift-betashift;var initrad=netsize>>3;var radiusbiasshift=6;var radiusbias=1<<radiusbiasshift;var initradius=initrad*radiusbias;var radiusdec=30;var alphabiasshift=10;var initalpha=1<<alphabiasshift;var alphadec;var radbiasshift=8;var radbias=1<<radbiasshift;var alpharadbshift=alphabiasshift+radbiasshift;var alpharadbias=1<<alpharadbshift;var prime1=499;var prime2=491;var prime3=487;var prime4=503;var minpicturebytes=3*prime4;function NeuQuant(pixels,samplefac){var network;var netindex;var bias;var freq;var radpower;function init(){network=[];netindex=new Int32Array(256);bias=new Int32Array(netsize);freq=new Int32Array(netsize);radpower=new Int32Array(netsize>>3);var i,v;for(i=0;i<netsize;i++){v=(i<<netbiasshift+8)/netsize;network[i]=new Float64Array([v,v,v,0]);freq[i]=intbias/netsize;bias[i]=0}}function unbiasnet(){for(var i=0;i<netsize;i++){network[i][0]>>=netbiasshift;network[i][1]>>=netbiasshift;network[i][2]>>=netbiasshift;network[i][3]=i}}function altersingle(alpha,i,b,g,r){network[i][0]-=alpha*(network[i][0]-b)/initalpha;network[i][1]-=alpha*(network[i][1]-g)/initalpha;network[i][2]-=alpha*(network[i][2]-r)/initalpha}function alterneigh(radius,i,b,g,r){var lo=Math.abs(i-radius);var hi=Math.min(i+radius,netsize);var j=i+1;var k=i-1;var m=1;var p,a;while(j<hi||k>lo){a=radpower[m++];if(j<hi){p=network[j++];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}if(k>lo){p=network[k--];p[0]-=a*(p[0]-b)/alpharadbias;p[1]-=a*(p[1]-g)/alpharadbias;p[2]-=a*(p[2]-r)/alpharadbias}}}function contest(b,g,r){var bestd=~(1<<31);var bestbiasd=bestd;var bestpos=-1;var bestbiaspos=bestpos;var i,n,dist,biasdist,betafreq;for(i=0;i<netsize;i++){n=network[i];dist=Math.abs(n[0]-b)+Math.abs(n[1]-g)+Math.abs(n[2]-r);if(dist<bestd){bestd=dist;bestpos=i}biasdist=dist-(bias[i]>>intbiasshift-netbiasshift);if(biasdist<bestbiasd){bestbiasd=biasdist;bestbiaspos=i}betafreq=freq[i]>>betashift;freq[i]-=betafreq;bias[i]+=betafreq<<gammashift}freq[bestpos]+=beta;bias[bestpos]-=betagamma;return bestbiaspos}function inxbuild(){var i,j,p,q,smallpos,smallval,previouscol=0,startpos=0;for(i=0;i<netsize;i++){p=network[i];smallpos=i;smallval=p[1];for(j=i+1;j<netsize;j++){q=network[j];if(q[1]<smallval){smallpos=j;smallval=q[1]}}q=network[smallpos];if(i!=smallpos){j=q[0];q[0]=p[0];p[0]=j;j=q[1];q[1]=p[1];p[1]=j;j=q[2];q[2]=p[2];p[2]=j;j=q[3];q[3]=p[3];p[3]=j}if(smallval!=previouscol){netindex[previouscol]=startpos+i>>1;for(j=previouscol+1;j<smallval;j++)netindex[j]=i;previouscol=smallval;startpos=i}}netindex[previouscol]=startpos+maxnetpos>>1;for(j=previouscol+1;j<256;j++)netindex[j]=maxnetpos}function inxsearch(b,g,r){var a,p,dist;var bestd=1e3;var best=-1;var i=netindex[g];var j=i-1;while(i<netsize||j>=0){if(i<netsize){p=network[i];dist=p[1]-g;if(dist>=bestd)i=netsize;else{i++;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist<bestd){a=p[2]-r;if(a<0)a=-a;dist+=a;if(dist<bestd){bestd=dist;best=p[3]}}}}if(j>=0){p=network[j];dist=g-p[1];if(dist>=bestd)j=-1;else{j--;if(dist<0)dist=-dist;a=p[0]-b;if(a<0)a=-a;dist+=a;if(dist<bestd){a=p[2]-r;if(a<0)a=-a;dist+=a;if(dist<bestd){bestd=dist;best=p[3]}}}}}return best}function learn(){var i;var lengthcount=pixels.length;var alphadec=30+(samplefac-1)/3;var samplepixels=lengthcount/(3*samplefac);var delta=~~(samplepixels/ncycles);var alpha=initalpha;var radius=initradius;var rad=radius>>radiusbiasshift;if(rad<=1)rad=0;for(i=0;i<rad;i++)radpower[i]=alpha*((rad*rad-i*i)*radbias/(rad*rad));var step;if(lengthcount<minpicturebytes){samplefac=1;step=3}else if(lengthcount%prime1!==0){step=3*prime1}else if(lengthcount%prime2!==0){step=3*prime2}else if(lengthcount%prime3!==0){step=3*prime3}else{step=3*prime4}var b,g,r,j;var pix=0;i=0;while(i<samplepixels){b=(pixels[pix]&255)<<netbiasshift;g=(pixels[pix+1]&255)<<netbiasshift;r=(pixels[pix+2]&255)<<netbiasshift;j=contest(b,g,r);altersingle(alpha,j,b,g,r);if(rad!==0)alterneigh(rad,j,b,g,r);pix+=step;if(pix>=lengthcount)pix-=lengthcount;i++;if(delta===0)delta=1;if(i%delta===0){alpha-=alpha/alphadec;radius-=radius/radiusdec;rad=radius>>radiusbiasshift;if(rad<=1)rad=0;for(j=0;j<rad;j++)radpower[j]=alpha*((rad*rad-j*j)*radbias/(rad*rad))}}}function buildColormap(){init();learn();unbiasnet();inxbuild()}this.buildColormap=buildColormap;function getColormap(){var map=[];var index=[];for(var i=0;i<netsize;i++)index[network[i][3]]=i;var k=0;for(var l=0;l<netsize;l++){var j=index[l];map[k++]=network[j][0];map[k++]=network[j][1];map[k++]=network[j][2]}return map}this.getColormap=getColormap;this.lookupRGB=inxsearch}module.exports=NeuQuant},{}],4:[function(require,module,exports){var GIFEncoder,renderFrame;GIFEncoder=require("./GIFEncoder.js");renderFrame=function(frame){var encoder,page,stream,transfer;encoder=new GIFEncoder(frame.width,frame.height);if(frame.index===0){encoder.writeHeader()}else{encoder.firstFrame=false}encoder.setTransparent(frame.transparent);encoder.setRepeat(frame.repeat);encoder.setDelay(frame.delay);encoder.setQuality(frame.quality);encoder.setDither(frame.dither);encoder.setGlobalPalette(frame.globalPalette);encoder.addFrame(frame.data);if(frame.last){encoder.finish()}if(frame.globalPalette===true){frame.globalPalette=encoder.getGlobalPalette()}stream=encoder.stream();frame.data=stream.pages;frame.cursor=stream.cursor;frame.pageSize=stream.constructor.pageSize;if(frame.canTransfer){transfer=function(){var i,len,ref,results;ref=frame.data;results=[];for(i=0,len=ref.length;i<len;i++){page=ref[i];results.push(page.buffer)}return results}();return self.postMessage(frame,transfer)}else{return self.postMessage(frame)}};self.onmessage=function(event){return renderFrame(event.data)}},{"./GIFEncoder.js":1}]},{},[4]); +//# sourceMappingURL=gif.worker.js.map From 826a597342761b0aa8e9d6c2d81e5c0ffac5fbf5 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 14:16:52 +0000 Subject: [PATCH 23/55] Adding resource icon --- assets/sass/pages/resources.scss | 46 ++++++++++++++++++++- content/resources/button-generator/index.md | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 5355ccb..a44b2f3 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -482,6 +482,38 @@ } } + &.button-generator { + background: #000; + height: 40%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; + overflow: hidden; + + &::before, + &::after { + content: ""; + position: absolute; + width: 90%; + height: 30%; + top: 15%; + left: 5%; + background: #fff; + animation: button 2s ease-in-out infinite; + } + + &::after { + bottom: 15%; + top: auto; + left: 50%; + transform: translateX(-50%); + width: 50%; + animation: button 1.2s ease-in-out infinite; + } + } + // Last.fm stats icon &.lastfm-stats { background: black; @@ -506,13 +538,13 @@ &::before { left: 20px; height: 35px; - animation: equalizer-1 0.8s ease-in-out infinite; + animation: equalizer-1 3s ease-in-out infinite; } &::after { left: 35px; height: 25px; - animation: equalizer-2 0.8s ease-in-out infinite 0.2s; + animation: equalizer-2 1s ease-in-out infinite 0.2s; } } } @@ -527,6 +559,16 @@ } } +@keyframes button { + 0%, + 100% { + scale: 1; + } + 50% { + scale: 0.95; + } +} + @keyframes equalizer-1 { 0%, 100% { diff --git a/content/resources/button-generator/index.md b/content/resources/button-generator/index.md index fad3797..9f5d5d2 100644 --- a/content/resources/button-generator/index.md +++ b/content/resources/button-generator/index.md @@ -2,7 +2,7 @@ title: "88x31 Button Creator" date: 2026-01-08 description: "Make custom 88x31 pixel buttons with text, colors, gradients, and textures" -icon: "button" +icon: "button-generator" demo_url: "" source_url: "" draft: false From 64751b9213e79f668642ef6d36b64e69a8dd8fcd Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 14:38:54 +0000 Subject: [PATCH 24/55] Fixing blog contact styling --- assets/sass/pages/blog.scss | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 0d2d9a2..298cae7 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -566,6 +566,10 @@ border: 1px solid rgba(0, 255, 0, 0.3); border-radius: 4px; + &:before { + content: none; + } + @include media-down(lg) { margin-top: 30px; padding: 15px; @@ -586,6 +590,7 @@ display: flex; flex-direction: column; gap: 15px; + color: greenyellow; } .contact-email, @@ -593,6 +598,10 @@ font-size: 0.95rem; line-height: 1.6; + a:hover { + color: cyan; + } + @include media-down(lg) { font-size: 0.9rem; } @@ -642,6 +651,10 @@ text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); border-radius: 3px; + &:hover { + color: cyan; + } + @include media-down(lg) { font-size: 0.8rem; padding: 6px 12px; From 7640d7512f16cb14e0e0d30da00556671a4fd601 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 16:20:24 +0000 Subject: [PATCH 25/55] Mobile tweaks --- assets/sass/pages/button-generator.scss | 10 ++++++++++ layouts/shortcodes/button-generator.html | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/assets/sass/pages/button-generator.scss b/assets/sass/pages/button-generator.scss index c6aac4b..193610b 100644 --- a/assets/sass/pages/button-generator.scss +++ b/assets/sass/pages/button-generator.scss @@ -67,6 +67,16 @@ 0 0 20px rgba(0, 150, 255, 0.1), inset 0 0 40px rgba(0, 100, 200, 0.05); + @include media-down(md) { + background: linear-gradient( + 135deg, + rgba(0, 100, 180, 1) 0%, + rgb(0, 29, 55) 100% + ); + z-index: 90; + } + + &::before { content: ""; position: absolute; diff --git a/layouts/shortcodes/button-generator.html b/layouts/shortcodes/button-generator.html index 8d65fa3..6babacb 100644 --- a/layouts/shortcodes/button-generator.html +++ b/layouts/shortcodes/button-generator.html @@ -1,7 +1,7 @@ <div id="button-generator-app"> <div class="generator-container"> <div class="preview-section"> - <h3>Preview</h3> + <h3 class="hidden-md-down">Preview</h3> <div class="preview-container"> <div class="preview-wrapper"> <canvas id="button-canvas" width="88" height="31"></canvas> @@ -9,7 +9,7 @@ </div> <button id="download-button" class="btn-primary">Download Button</button> - <div class="presets-container"> + <div class="presets-container hidden-md-down"> <h3>Presets</h3> <button id="preset-random" class="btn-secondary">Random Button</button> <button id="preset-classic" class="btn-secondary">Classic Style</button> From b9cf2c8eed8d30993c890f462977d6d0b3775fc7 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 16:25:57 +0000 Subject: [PATCH 26/55] more mobile tweaks --- assets/sass/pages/resources.scss | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index a44b2f3..0e9ebd8 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -627,6 +627,11 @@ overflow: visible; z-index: 10; + @include media-down(md) { + padding: 0; + padding-bottom: 2rem; + } + // Subtle tech panel grid pattern &::before { content: ""; @@ -767,6 +772,12 @@ //box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); } + @include media-down(md) { + &::before, &::after { + content: none; + } + } + @include media-up(md) { font-size: 3rem; From 6ae307e8be71cd5104478547d97019a8d56522e8 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Thu, 8 Jan 2026 16:39:41 +0000 Subject: [PATCH 27/55] yet more mobile tweaks --- assets/sass/pages/resources.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 0e9ebd8..a6869b0 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -393,6 +393,10 @@ display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1em; + + @include media-down(md) { + grid-template-columns: 1fr; + } } .resource-pin a { From 4ac45367e5d25734db92a1280b733f7dd62475a6 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 09:15:05 +0000 Subject: [PATCH 28/55] Start of the rewrite to a modular system --- layouts/_default/baseof.html | 3 +- layouts/shortcodes/button-generator.html | 587 +---------------- .../shortcodes/button-generator.html.backup | 607 ++++++++++++++++++ .../button-generator/button-generator-core.js | 374 +++++++++++ static/js/button-generator/debug-helper.js | 26 + static/js/button-generator/effect-base.js | 92 +++ static/js/button-generator/effects/EXAMPLE.js | 205 ++++++ .../effects/background-emoji-wallpaper.js | 109 ++++ .../effects/background-gradient.js | 76 +++ .../effects/background-rain.js | 117 ++++ .../effects/background-rainbow.js | 137 ++++ .../effects/background-solid.js | 56 ++ .../effects/background-texture.js | 217 +++++++ static/js/button-generator/effects/border.js | 139 ++++ static/js/button-generator/effects/glitch.js | 93 +++ .../js/button-generator/effects/hologram.js | 170 +++++ static/js/button-generator/effects/noise.js | 68 ++ static/js/button-generator/effects/pulse.js | 62 ++ .../button-generator/effects/rainbow-text.js | 100 +++ .../js/button-generator/effects/rgb-split.js | 85 +++ static/js/button-generator/effects/rotate.js | 72 +++ .../js/button-generator/effects/scanline.js | 79 +++ static/js/button-generator/effects/shimmer.js | 57 ++ .../js/button-generator/effects/spin-text.js | 144 +++++ .../js/button-generator/effects/spotlight.js | 209 ++++++ .../button-generator/effects/text-standard.js | 232 +++++++ .../js/button-generator/effects/wave-text.js | 167 +++++ static/js/button-generator/main.js | 352 ++++++++++ static/js/button-generator/ui-builder.js | 367 +++++++++++ 29 files changed, 4414 insertions(+), 588 deletions(-) create mode 100644 layouts/shortcodes/button-generator.html.backup create mode 100644 static/js/button-generator/button-generator-core.js create mode 100644 static/js/button-generator/debug-helper.js create mode 100644 static/js/button-generator/effect-base.js create mode 100644 static/js/button-generator/effects/EXAMPLE.js create mode 100644 static/js/button-generator/effects/background-emoji-wallpaper.js create mode 100644 static/js/button-generator/effects/background-gradient.js create mode 100644 static/js/button-generator/effects/background-rain.js create mode 100644 static/js/button-generator/effects/background-rainbow.js create mode 100644 static/js/button-generator/effects/background-solid.js create mode 100644 static/js/button-generator/effects/background-texture.js create mode 100644 static/js/button-generator/effects/border.js create mode 100644 static/js/button-generator/effects/glitch.js create mode 100644 static/js/button-generator/effects/hologram.js create mode 100644 static/js/button-generator/effects/noise.js create mode 100644 static/js/button-generator/effects/pulse.js create mode 100644 static/js/button-generator/effects/rainbow-text.js create mode 100644 static/js/button-generator/effects/rgb-split.js create mode 100644 static/js/button-generator/effects/rotate.js create mode 100644 static/js/button-generator/effects/scanline.js create mode 100644 static/js/button-generator/effects/shimmer.js create mode 100644 static/js/button-generator/effects/spin-text.js create mode 100644 static/js/button-generator/effects/spotlight.js create mode 100644 static/js/button-generator/effects/text-standard.js create mode 100644 static/js/button-generator/effects/wave-text.js create mode 100644 static/js/button-generator/main.js create mode 100644 static/js/button-generator/ui-builder.js diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 0ac2b0f..bdd3455 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -37,8 +37,7 @@ <!-- Button Generator - only load if page content contains button-generator shortcode --> {{ if or (findRE "{{<\\s*button-generator" .RawContent) (findRE "{{%\\s*button-generator" .RawContent) }} <script src="{{ "js/gif.js" | relURL }}"></script> - {{ $buttonGenerator := resources.Get "js/button-generator.js" | resources.Minify | resources.Fingerprint }} - <script src="{{ $buttonGenerator.RelPermalink }}" integrity="{{ $buttonGenerator.Data.Integrity }}"></script> + <script type="module" src="{{ "js/button-generator/main.js" | relURL }}"></script> {{ end }} </body> </html> diff --git a/layouts/shortcodes/button-generator.html b/layouts/shortcodes/button-generator.html index 6babacb..2f264b9 100644 --- a/layouts/shortcodes/button-generator.html +++ b/layouts/shortcodes/button-generator.html @@ -17,591 +17,6 @@ </div> </div> - <div class="controls-section"> - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 1</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="button-text">Text</label> - <input - type="text" - id="button-text" - value="RITUAL.SH" - maxlength="20" - /> - - <label class="checkbox-label"> - <input type="checkbox" id="text-enabled" checked /> - <span>Enable Text Line 1</span> - </label> - - <label for="font-size" - >Font Size: <span id="font-size-value">14</span>px</label - > - <input type="range" id="font-size" min="6" max="24" value="14" /> - - <label for="text-x" - >Horizontal Position: <span id="text-x-value">50</span>%</label - > - <input type="range" id="text-x" min="0" max="100" value="50" /> - - <label for="text-y" - >Vertical Position: <span id="text-y-value">35</span>%</label - > - <input type="range" id="text-y" min="0" max="100" value="35" /> - - <label for="text-color-type">Text Color Type</label> - <select id="text-color-type"> - <option value="solid">Solid Color</option> - <option value="gradient">Gradient</option> - </select> - - <div id="text-solid-color"> - <label for="text-color">Text Color</label> - <input type="color" id="text-color" value="#ffffff" /> - </div> - - <div id="text-gradient-color" style="display: none"> - <label for="text-gradient-color1">Gradient Color 1</label> - <input type="color" id="text-gradient-color1" value="#ffffff" /> - - <label for="text-gradient-color2">Gradient Color 2</label> - <input type="color" id="text-gradient-color2" value="#00ffff" /> - - <label for="text-gradient-angle" - >Gradient Angle: - <span id="text-gradient-angle-value">0</span>°</label - > - <input - type="range" - id="text-gradient-angle" - min="0" - max="360" - value="0" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="text-outline" /> - <span>Outline</span> - </label> - <input - type="color" - id="outline-color" - value="#000000" - style="display: none" - /> - - <label for="font-family">Font</label> - <select id="font-family"> - <option value="Lato">Lato</option> - <option value="Roboto">Roboto</option> - <option value="Open Sans">Open Sans</option> - <option value="Montserrat">Montserrat</option> - <option value="Oswald">Oswald</option> - <option value="Bebas Neue">Bebas Neue</option> - <option value="Roboto Mono">Roboto Mono</option> - <option value="VT323">VT323</option> - <option value="Press Start 2P">Press Start 2P</option> - <option value="DSEG7-Classic">DSEG7</option> - </select> - - <div class="checkbox-row"> - <label class="checkbox-label"> - <input type="checkbox" id="font-bold" /> - <span>Bold</span> - </label> - <label class="checkbox-label"> - <input type="checkbox" id="font-italic" /> - <span>Italic</span> - </label> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 1 Animation</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-wave" /> - <span>Wave Animation</span> - </label> - <div id="wave-controls" style="display: none"> - <label for="wave-amplitude" - >Amplitude: <span id="wave-amplitude-value">3</span>px</label - > - <input - type="range" - id="wave-amplitude" - min="0" - max="10" - value="3" - step="0.5" - /> - - <label for="wave-speed" - >Speed: <span id="wave-speed-value">1.0</span>x</label - > - <input - type="range" - id="wave-speed" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-rainbow" /> - <span>Rainbow Text</span> - </label> - <div id="rainbow-text-controls" style="display: none"> - <label for="text-rainbow-speed" - >Speed: <span id="text-rainbow-speed-value">1.0</span>x</label - > - <input - type="range" - id="text-rainbow-speed" - min="0.5" - max="5" - value="1.0" - step="0.1" - /> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 2</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="button-text2">Text</label> - <input type="text" id="button-text2" value="" maxlength="20" /> - - <label class="checkbox-label"> - <input type="checkbox" id="text2-enabled" /> - <span>Enable Text Line 2</span> - </label> - - <label for="font-size2" - >Font Size: <span id="font-size2-value">12</span>px</label - > - <input type="range" id="font-size2" min="6" max="24" value="12" /> - - <label for="text2-x" - >Horizontal Position: <span id="text2-x-value">50</span>%</label - > - <input type="range" id="text2-x" min="0" max="100" value="50" /> - - <label for="text2-y" - >Vertical Position: <span id="text2-y-value">65</span>%</label - > - <input type="range" id="text2-y" min="0" max="100" value="65" /> - - <label for="text2-color-type">Text Color Type</label> - <select id="text2-color-type"> - <option value="solid">Solid Color</option> - <option value="gradient">Gradient</option> - </select> - - <div id="text2-solid-color"> - <label for="text2-color">Text Color</label> - <input type="color" id="text2-color" value="#ffffff" /> - </div> - - <div id="text2-gradient-color" style="display: none"> - <label for="text2-gradient-color1">Gradient Color 1</label> - <input type="color" id="text2-gradient-color1" value="#ffffff" /> - - <label for="text2-gradient-color2">Gradient Color 2</label> - <input type="color" id="text2-gradient-color2" value="#00ffff" /> - - <label for="text2-gradient-angle" - >Gradient Angle: - <span id="text2-gradient-angle-value">0</span>°</label - > - <input - type="range" - id="text2-gradient-angle" - min="0" - max="360" - value="0" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="text2-outline" /> - <span>Outline</span> - </label> - <input - type="color" - id="outline2-color" - value="#000000" - style="display: none" - /> - - <label for="font-family2">Font</label> - <select id="font-family2"> - <option value="Lato">Lato</option> - <option value="Roboto">Roboto</option> - <option value="Open Sans">Open Sans</option> - <option value="Montserrat">Montserrat</option> - <option value="Oswald">Oswald</option> - <option value="Bebas Neue">Bebas Neue</option> - <option value="Roboto Mono">Roboto Mono</option> - <option value="VT323">VT323</option> - <option value="Press Start 2P">Press Start 2P</option> - <option value="DSEG7-Classic">DSEG7</option> - </select> - - <div class="checkbox-row"> - <label class="checkbox-label"> - <input type="checkbox" id="font-bold2" /> - <span>Bold</span> - </label> - <label class="checkbox-label"> - <input type="checkbox" id="font-italic2" /> - <span>Italic</span> - </label> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 2 Animation</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-wave2" /> - <span>Wave Animation</span> - </label> - <div id="wave-controls2" style="display: none"> - <label for="wave-amplitude2" - >Amplitude: <span id="wave-amplitude2-value">3</span>px</label - > - <input - type="range" - id="wave-amplitude2" - min="0" - max="10" - value="3" - step="0.5" - /> - - <label for="wave-speed2" - >Speed: <span id="wave-speed2-value">1.0</span>x</label - > - <input - type="range" - id="wave-speed2" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-rainbow2" /> - <span>Rainbow Text</span> - </label> - <div id="rainbow-text2-controls" style="display: none"> - <label for="text-rainbow-speed2" - >Speed: <span id="text-rainbow-speed2-value">1.0</span>x</label - > - <input - type="range" - id="text-rainbow-speed2" - min="0.5" - max="5" - value="1.0" - step="0.1" - /> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Background</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="bg-type">Background Type</label> - <select id="bg-type"> - <option value="solid">Solid Color</option> - <option value="gradient">Gradient</option> - <option value="texture">Texture</option> - </select> - - <div id="solid-controls"> - <label for="bg-color">Background Color</label> - <input type="color" id="bg-color" value="#0066cc" /> - </div> - - <div id="gradient-controls" style="display: none"> - <label for="gradient-color1">Color 1</label> - <input type="color" id="gradient-color1" value="#0066cc" /> - - <label for="gradient-color2">Color 2</label> - <input type="color" id="gradient-color2" value="#00ccff" /> - - <label for="gradient-angle" - >Angle: <span id="gradient-angle-value">90</span>°</label - > - <input - type="range" - id="gradient-angle" - min="0" - max="360" - value="90" - /> - </div> - - <div id="texture-controls" style="display: none"> - <label for="texture-type">Texture Pattern</label> - <select id="texture-type"> - <option value="dots">Dots</option> - <option value="grid">Grid</option> - <option value="diagonal">Diagonal Lines</option> - <option value="checkerboard">Checkerboard</option> - <option value="noise">Noise</option> - <option value="stars">Stars</option> - </select> - - <label for="texture-color1">Base Color</label> - <input type="color" id="texture-color1" value="#0066cc" /> - - <label for="texture-color2">Pattern Color</label> - <input type="color" id="texture-color2" value="#0099ff" /> - - <label for="texture-scale" - >Pattern Scale: <span id="texture-scale-value">50</span>%</label - > - <input - type="range" - id="texture-scale" - min="10" - max="100" - value="50" - /> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Background Animation</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label class="checkbox-label"> - <input type="checkbox" id="animate-bg-rainbow" /> - <span>Rainbow Flash</span> - </label> - <div id="rainbow-bg-controls" style="display: none"> - <label for="rainbow-speed" - >Speed: <span id="rainbow-speed-value">0.5</span>x</label - > - <input - type="range" - id="rainbow-speed" - min="0.1" - max="3" - value="0.5" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-bg-rainbow-gradient" /> - <span>Rainbow Gradient</span> - </label> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Border</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="border-width" - >Border Width: <span id="border-width-value">2</span>px</label - > - <input type="range" id="border-width" min="0" max="5" value="2" /> - - <label for="border-color">Border Color</label> - <input type="color" id="border-color" value="#000000" /> - - <label for="border-style">Border Style</label> - <select id="border-style"> - <option value="solid">Solid</option> - <option value="inset">Inset (3D)</option> - <option value="outset">Outset (3D)</option> - <option value="ridge">Ridge</option> - </select> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Special Effects</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <p class="info-text"> - Almost all animations should stack, so pick as many as you want. - </p> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-glitch" /> - <span>Glitch Effect</span> - </label> - <div id="glitch-controls" style="display: none"> - <label for="glitch-intensity" - >Intensity: <span id="glitch-intensity-value">3</span></label - > - <input - type="range" - id="glitch-intensity" - min="1" - max="10" - value="3" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-pulse" /> - <span>Pulse Effect</span> - </label> - <div id="pulse-controls" style="display: none"> - <label for="pulse-scale" - >Scale: <span id="pulse-scale-value">1.1</span>x</label - > - <input - type="range" - id="pulse-scale" - min="1.0" - max="1.3" - value="1.1" - step="0.05" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-shimmer" /> - <span>Shimmer Effect</span> - </label> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-scanline" /> - <span>Scanline Effect</span> - </label> - <div id="scanline-controls" style="display: none"> - <label for="scanline-intensity" - >Intensity: <span id="scanline-intensity-value">0.3</span></label - > - <input - type="range" - id="scanline-intensity" - min="0.1" - max="1" - value="0.3" - step="0.1" - /> - - <label for="scanline-speed" - >Speed: <span id="scanline-speed-value">1.0</span>x</label - > - <input - type="range" - id="scanline-speed" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-rgb-split" /> - <span>RGB Split Effect</span> - </label> - <div id="rgb-split-controls" style="display: none"> - <label for="rgb-split-intensity" - >Intensity: <span id="rgb-split-intensity-value">2</span>px</label - > - <input - type="range" - id="rgb-split-intensity" - min="1" - max="5" - value="2" - step="0.5" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-noise" /> - <span>Noise Effect</span> - </label> - <div id="noise-controls" style="display: none"> - <label for="noise-intensity" - >Intensity: <span id="noise-intensity-value">0.1</span></label - > - <input - type="range" - id="noise-intensity" - min="0.05" - max="0.5" - value="0.1" - step="0.05" - /> - </div> - {{/* - <label class="checkbox-label"> - <input type="checkbox" id="animate-rotate" /> - <span>Rotate Effect</span> - </label> - <div id="rotate-controls" style="display: none"> - <label for="rotate-angle" - >Max Angle: <span id="rotate-angle-value">5</span>°</label - > - <input - type="range" - id="rotate-angle" - min="2" - max="15" - value="5" - step="1" - /> - - <label for="rotate-speed" - >Speed: <span id="rotate-speed-value">1.0</span>x</label - > - <input - type="range" - id="rotate-speed" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - */}} - </div> - </div> - </div> + <div class="controls-section"></div> </div> </div> diff --git a/layouts/shortcodes/button-generator.html.backup b/layouts/shortcodes/button-generator.html.backup new file mode 100644 index 0000000..6babacb --- /dev/null +++ b/layouts/shortcodes/button-generator.html.backup @@ -0,0 +1,607 @@ +<div id="button-generator-app"> + <div class="generator-container"> + <div class="preview-section"> + <h3 class="hidden-md-down">Preview</h3> + <div class="preview-container"> + <div class="preview-wrapper"> + <canvas id="button-canvas" width="88" height="31"></canvas> + </div> + </div> + <button id="download-button" class="btn-primary">Download Button</button> + + <div class="presets-container hidden-md-down"> + <h3>Presets</h3> + <button id="preset-random" class="btn-secondary">Random Button</button> + <button id="preset-classic" class="btn-secondary">Classic Style</button> + <button id="preset-modern" class="btn-secondary">Modern Style</button> + </div> + </div> + + <div class="controls-section"> + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 1</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="button-text">Text</label> + <input + type="text" + id="button-text" + value="RITUAL.SH" + maxlength="20" + /> + + <label class="checkbox-label"> + <input type="checkbox" id="text-enabled" checked /> + <span>Enable Text Line 1</span> + </label> + + <label for="font-size" + >Font Size: <span id="font-size-value">14</span>px</label + > + <input type="range" id="font-size" min="6" max="24" value="14" /> + + <label for="text-x" + >Horizontal Position: <span id="text-x-value">50</span>%</label + > + <input type="range" id="text-x" min="0" max="100" value="50" /> + + <label for="text-y" + >Vertical Position: <span id="text-y-value">35</span>%</label + > + <input type="range" id="text-y" min="0" max="100" value="35" /> + + <label for="text-color-type">Text Color Type</label> + <select id="text-color-type"> + <option value="solid">Solid Color</option> + <option value="gradient">Gradient</option> + </select> + + <div id="text-solid-color"> + <label for="text-color">Text Color</label> + <input type="color" id="text-color" value="#ffffff" /> + </div> + + <div id="text-gradient-color" style="display: none"> + <label for="text-gradient-color1">Gradient Color 1</label> + <input type="color" id="text-gradient-color1" value="#ffffff" /> + + <label for="text-gradient-color2">Gradient Color 2</label> + <input type="color" id="text-gradient-color2" value="#00ffff" /> + + <label for="text-gradient-angle" + >Gradient Angle: + <span id="text-gradient-angle-value">0</span>°</label + > + <input + type="range" + id="text-gradient-angle" + min="0" + max="360" + value="0" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="text-outline" /> + <span>Outline</span> + </label> + <input + type="color" + id="outline-color" + value="#000000" + style="display: none" + /> + + <label for="font-family">Font</label> + <select id="font-family"> + <option value="Lato">Lato</option> + <option value="Roboto">Roboto</option> + <option value="Open Sans">Open Sans</option> + <option value="Montserrat">Montserrat</option> + <option value="Oswald">Oswald</option> + <option value="Bebas Neue">Bebas Neue</option> + <option value="Roboto Mono">Roboto Mono</option> + <option value="VT323">VT323</option> + <option value="Press Start 2P">Press Start 2P</option> + <option value="DSEG7-Classic">DSEG7</option> + </select> + + <div class="checkbox-row"> + <label class="checkbox-label"> + <input type="checkbox" id="font-bold" /> + <span>Bold</span> + </label> + <label class="checkbox-label"> + <input type="checkbox" id="font-italic" /> + <span>Italic</span> + </label> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 1 Animation</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-wave" /> + <span>Wave Animation</span> + </label> + <div id="wave-controls" style="display: none"> + <label for="wave-amplitude" + >Amplitude: <span id="wave-amplitude-value">3</span>px</label + > + <input + type="range" + id="wave-amplitude" + min="0" + max="10" + value="3" + step="0.5" + /> + + <label for="wave-speed" + >Speed: <span id="wave-speed-value">1.0</span>x</label + > + <input + type="range" + id="wave-speed" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-rainbow" /> + <span>Rainbow Text</span> + </label> + <div id="rainbow-text-controls" style="display: none"> + <label for="text-rainbow-speed" + >Speed: <span id="text-rainbow-speed-value">1.0</span>x</label + > + <input + type="range" + id="text-rainbow-speed" + min="0.5" + max="5" + value="1.0" + step="0.1" + /> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 2</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="button-text2">Text</label> + <input type="text" id="button-text2" value="" maxlength="20" /> + + <label class="checkbox-label"> + <input type="checkbox" id="text2-enabled" /> + <span>Enable Text Line 2</span> + </label> + + <label for="font-size2" + >Font Size: <span id="font-size2-value">12</span>px</label + > + <input type="range" id="font-size2" min="6" max="24" value="12" /> + + <label for="text2-x" + >Horizontal Position: <span id="text2-x-value">50</span>%</label + > + <input type="range" id="text2-x" min="0" max="100" value="50" /> + + <label for="text2-y" + >Vertical Position: <span id="text2-y-value">65</span>%</label + > + <input type="range" id="text2-y" min="0" max="100" value="65" /> + + <label for="text2-color-type">Text Color Type</label> + <select id="text2-color-type"> + <option value="solid">Solid Color</option> + <option value="gradient">Gradient</option> + </select> + + <div id="text2-solid-color"> + <label for="text2-color">Text Color</label> + <input type="color" id="text2-color" value="#ffffff" /> + </div> + + <div id="text2-gradient-color" style="display: none"> + <label for="text2-gradient-color1">Gradient Color 1</label> + <input type="color" id="text2-gradient-color1" value="#ffffff" /> + + <label for="text2-gradient-color2">Gradient Color 2</label> + <input type="color" id="text2-gradient-color2" value="#00ffff" /> + + <label for="text2-gradient-angle" + >Gradient Angle: + <span id="text2-gradient-angle-value">0</span>°</label + > + <input + type="range" + id="text2-gradient-angle" + min="0" + max="360" + value="0" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="text2-outline" /> + <span>Outline</span> + </label> + <input + type="color" + id="outline2-color" + value="#000000" + style="display: none" + /> + + <label for="font-family2">Font</label> + <select id="font-family2"> + <option value="Lato">Lato</option> + <option value="Roboto">Roboto</option> + <option value="Open Sans">Open Sans</option> + <option value="Montserrat">Montserrat</option> + <option value="Oswald">Oswald</option> + <option value="Bebas Neue">Bebas Neue</option> + <option value="Roboto Mono">Roboto Mono</option> + <option value="VT323">VT323</option> + <option value="Press Start 2P">Press Start 2P</option> + <option value="DSEG7-Classic">DSEG7</option> + </select> + + <div class="checkbox-row"> + <label class="checkbox-label"> + <input type="checkbox" id="font-bold2" /> + <span>Bold</span> + </label> + <label class="checkbox-label"> + <input type="checkbox" id="font-italic2" /> + <span>Italic</span> + </label> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Text Line 2 Animation</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-wave2" /> + <span>Wave Animation</span> + </label> + <div id="wave-controls2" style="display: none"> + <label for="wave-amplitude2" + >Amplitude: <span id="wave-amplitude2-value">3</span>px</label + > + <input + type="range" + id="wave-amplitude2" + min="0" + max="10" + value="3" + step="0.5" + /> + + <label for="wave-speed2" + >Speed: <span id="wave-speed2-value">1.0</span>x</label + > + <input + type="range" + id="wave-speed2" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-text-rainbow2" /> + <span>Rainbow Text</span> + </label> + <div id="rainbow-text2-controls" style="display: none"> + <label for="text-rainbow-speed2" + >Speed: <span id="text-rainbow-speed2-value">1.0</span>x</label + > + <input + type="range" + id="text-rainbow-speed2" + min="0.5" + max="5" + value="1.0" + step="0.1" + /> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Background</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="bg-type">Background Type</label> + <select id="bg-type"> + <option value="solid">Solid Color</option> + <option value="gradient">Gradient</option> + <option value="texture">Texture</option> + </select> + + <div id="solid-controls"> + <label for="bg-color">Background Color</label> + <input type="color" id="bg-color" value="#0066cc" /> + </div> + + <div id="gradient-controls" style="display: none"> + <label for="gradient-color1">Color 1</label> + <input type="color" id="gradient-color1" value="#0066cc" /> + + <label for="gradient-color2">Color 2</label> + <input type="color" id="gradient-color2" value="#00ccff" /> + + <label for="gradient-angle" + >Angle: <span id="gradient-angle-value">90</span>°</label + > + <input + type="range" + id="gradient-angle" + min="0" + max="360" + value="90" + /> + </div> + + <div id="texture-controls" style="display: none"> + <label for="texture-type">Texture Pattern</label> + <select id="texture-type"> + <option value="dots">Dots</option> + <option value="grid">Grid</option> + <option value="diagonal">Diagonal Lines</option> + <option value="checkerboard">Checkerboard</option> + <option value="noise">Noise</option> + <option value="stars">Stars</option> + </select> + + <label for="texture-color1">Base Color</label> + <input type="color" id="texture-color1" value="#0066cc" /> + + <label for="texture-color2">Pattern Color</label> + <input type="color" id="texture-color2" value="#0099ff" /> + + <label for="texture-scale" + >Pattern Scale: <span id="texture-scale-value">50</span>%</label + > + <input + type="range" + id="texture-scale" + min="10" + max="100" + value="50" + /> + </div> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Background Animation</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label class="checkbox-label"> + <input type="checkbox" id="animate-bg-rainbow" /> + <span>Rainbow Flash</span> + </label> + <div id="rainbow-bg-controls" style="display: none"> + <label for="rainbow-speed" + >Speed: <span id="rainbow-speed-value">0.5</span>x</label + > + <input + type="range" + id="rainbow-speed" + min="0.1" + max="3" + value="0.5" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-bg-rainbow-gradient" /> + <span>Rainbow Gradient</span> + </label> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Border</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <label for="border-width" + >Border Width: <span id="border-width-value">2</span>px</label + > + <input type="range" id="border-width" min="0" max="5" value="2" /> + + <label for="border-color">Border Color</label> + <input type="color" id="border-color" value="#000000" /> + + <label for="border-style">Border Style</label> + <select id="border-style"> + <option value="solid">Solid</option> + <option value="inset">Inset (3D)</option> + <option value="outset">Outset (3D)</option> + <option value="ridge">Ridge</option> + </select> + </div> + </div> + + <div class="control-group"> + <h3 class="control-group-header"> + <span>Special Effects</span> + <span class="toggle-icon">−</span> + </h3> + <div class="control-group-content"> + <p class="info-text"> + Almost all animations should stack, so pick as many as you want. + </p> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-glitch" /> + <span>Glitch Effect</span> + </label> + <div id="glitch-controls" style="display: none"> + <label for="glitch-intensity" + >Intensity: <span id="glitch-intensity-value">3</span></label + > + <input + type="range" + id="glitch-intensity" + min="1" + max="10" + value="3" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-pulse" /> + <span>Pulse Effect</span> + </label> + <div id="pulse-controls" style="display: none"> + <label for="pulse-scale" + >Scale: <span id="pulse-scale-value">1.1</span>x</label + > + <input + type="range" + id="pulse-scale" + min="1.0" + max="1.3" + value="1.1" + step="0.05" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-shimmer" /> + <span>Shimmer Effect</span> + </label> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-scanline" /> + <span>Scanline Effect</span> + </label> + <div id="scanline-controls" style="display: none"> + <label for="scanline-intensity" + >Intensity: <span id="scanline-intensity-value">0.3</span></label + > + <input + type="range" + id="scanline-intensity" + min="0.1" + max="1" + value="0.3" + step="0.1" + /> + + <label for="scanline-speed" + >Speed: <span id="scanline-speed-value">1.0</span>x</label + > + <input + type="range" + id="scanline-speed" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-rgb-split" /> + <span>RGB Split Effect</span> + </label> + <div id="rgb-split-controls" style="display: none"> + <label for="rgb-split-intensity" + >Intensity: <span id="rgb-split-intensity-value">2</span>px</label + > + <input + type="range" + id="rgb-split-intensity" + min="1" + max="5" + value="2" + step="0.5" + /> + </div> + + <label class="checkbox-label"> + <input type="checkbox" id="animate-noise" /> + <span>Noise Effect</span> + </label> + <div id="noise-controls" style="display: none"> + <label for="noise-intensity" + >Intensity: <span id="noise-intensity-value">0.1</span></label + > + <input + type="range" + id="noise-intensity" + min="0.05" + max="0.5" + value="0.1" + step="0.05" + /> + </div> + {{/* + <label class="checkbox-label"> + <input type="checkbox" id="animate-rotate" /> + <span>Rotate Effect</span> + </label> + <div id="rotate-controls" style="display: none"> + <label for="rotate-angle" + >Max Angle: <span id="rotate-angle-value">5</span>°</label + > + <input + type="range" + id="rotate-angle" + min="2" + max="15" + value="5" + step="1" + /> + + <label for="rotate-speed" + >Speed: <span id="rotate-speed-value">1.0</span>x</label + > + <input + type="range" + id="rotate-speed" + min="0.5" + max="3" + value="1.0" + step="0.1" + /> + </div> + */}} + </div> + </div> + </div> + </div> +</div> diff --git a/static/js/button-generator/button-generator-core.js b/static/js/button-generator/button-generator-core.js new file mode 100644 index 0000000..1c6126a --- /dev/null +++ b/static/js/button-generator/button-generator-core.js @@ -0,0 +1,374 @@ +import { ButtonEffect } from './effect-base.js'; + +/** + * Animation state class - passed to effects for frame-based rendering + */ +export class AnimationState { + constructor(frameNumber = 0, totalFrames = 40, fps = 20) { + this.frame = frameNumber; + this.totalFrames = totalFrames; + this.progress = frameNumber / totalFrames; // 0 to 1 + this.fps = fps; + this.time = (frameNumber / fps) * 1000; // milliseconds + } + + /** + * Helper to get phase for periodic animations (0 to 2π) + * @param {number} speed - Speed multiplier + * @returns {number} Phase in radians + */ + getPhase(speed = 1.0) { + return this.progress * speed * Math.PI * 2; + } +} + +/** + * Main ButtonGenerator class with effect registry system + */ +export class ButtonGenerator { + constructor(canvas, config = {}) { + this.canvas = canvas; + this.ctx = canvas.getContext('2d'); + + // Animation configuration + this.animConfig = { + fps: config.fps || 20, + duration: config.duration || 2, // seconds + get totalFrames() { + return this.fps * this.duration; + } + }; + + // Effect registry organized by type + this.effects = { + transform: [], + background: [], + border: [], + text: [], + text2: [], + general: [] + }; + + // Registered effects by ID for quick lookup + this.effectsById = new Map(); + + // Control elements cache + this.controlElements = {}; + + // Animation state + this.previewAnimationId = null; + + // Font list for preloading + this.fonts = config.fonts || [ + 'Lato', 'Roboto', 'Open Sans', 'Montserrat', 'Oswald', + 'Bebas Neue', 'Roboto Mono', 'VT323', 'Press Start 2P', 'DSEG7-Classic' + ]; + } + + /** + * Register an effect with the generator + * @param {ButtonEffect} effect - Effect instance to register + */ + registerEffect(effect) { + if (!(effect instanceof ButtonEffect)) { + throw new Error('Effect must extend ButtonEffect class'); + } + + if (this.effectsById.has(effect.id)) { + console.warn(`Effect with ID "${effect.id}" is already registered. Skipping.`); + return; + } + + // Add to type-specific array + const type = effect.type; + if (!this.effects[type]) { + this.effects[type] = []; + } + + this.effects[type].push(effect); + this.effectsById.set(effect.id, effect); + + // Sort by render order + this.effects[type].sort((a, b) => a.renderOrder - b.renderOrder); + + console.log(`Registered effect: ${effect.name} (${effect.id}) [${type}]`); + } + + /** + * Get all registered effects + * @returns {Array<ButtonEffect>} + */ + getAllEffects() { + return Array.from(this.effectsById.values()); + } + + /** + * Get effects by type + * @param {string} type - Effect type + * @returns {Array<ButtonEffect>} + */ + getEffectsByType(type) { + return this.effects[type] || []; + } + + /** + * Initialize and preload fonts + * @returns {Promise} + */ + async preloadFonts() { + const fontPromises = this.fonts.flatMap(font => [ + document.fonts.load(`400 12px "${font}"`), + document.fonts.load(`700 12px "${font}"`), + document.fonts.load(`italic 400 12px "${font}"`) + ]); + + await Promise.all(fontPromises); + console.log('All fonts loaded for canvas'); + } + + /** + * Get current control values from DOM + * @returns {Object} Map of control ID to value + */ + getControlValues() { + const values = {}; + + // Get all registered control IDs from effects + const allControls = new Set(); + this.getAllEffects().forEach(effect => { + effect.controls.forEach(control => { + allControls.add(control.id); + }); + }); + + // Read values from DOM + allControls.forEach(id => { + const element = document.getElementById(id); + if (element) { + if (element.type === 'checkbox') { + values[id] = element.checked; + } else if (element.type === 'range' || element.type === 'number') { + values[id] = parseFloat(element.value); + } else { + values[id] = element.value; + } + } + }); + + return values; + } + + /** + * Draw button with all effects applied + * @param {AnimationState} animState - Animation state (null for static) + * @param {Object} baseControls - Base button controls (text, colors, etc.) + */ + draw(animState = null, baseControls = {}) { + this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); + + const controlValues = { ...baseControls, ...this.getControlValues() }; + const renderData = { + width: this.canvas.width, + height: this.canvas.height, + centerX: this.canvas.width / 2, + centerY: this.canvas.height / 2 + }; + + // Apply effects in order: transform -> background -> border -> text/text2 -> general + const renderOrder = ['transform', 'background', 'border', 'text', 'text2', 'general']; + + // Save context once before transforms + this.ctx.save(); + + renderOrder.forEach(type => { + this.effects[type]?.forEach(effect => { + if (effect.canApply(controlValues)) { + // Transform effects should NOT be wrapped in save/restore + // They need to persist for all subsequent drawing operations + if (type !== 'transform') { + this.ctx.save(); + } + + try { + effect.apply(this.ctx, controlValues, animState, renderData); + } catch (error) { + console.error(`Error applying effect ${effect.id}:`, error); + } + + if (type !== 'transform') { + this.ctx.restore(); + } + } + }); + }); + + // Restore context once after all drawing + this.ctx.restore(); + } + + /** + * Check if any animations are enabled + * @returns {boolean} + */ + hasAnimationsEnabled() { + const controlValues = this.getControlValues(); + return this.getAllEffects().some(effect => + effect.type !== 'background' && // Background effects can be static + effect.isEnabled(controlValues) + ); + } + + /** + * Start animated preview loop + */ + startAnimatedPreview() { + this.stopAnimatedPreview(); + + let frameNum = 0; + let lastFrameTime = performance.now(); + const frameDelay = 1000 / this.animConfig.fps; + + const animate = (currentTime) => { + const elapsed = currentTime - lastFrameTime; + + if (elapsed >= frameDelay) { + const animState = new AnimationState( + frameNum, + this.animConfig.totalFrames, + this.animConfig.fps + ); + this.draw(animState); + + frameNum = (frameNum + 1) % this.animConfig.totalFrames; + lastFrameTime = currentTime - (elapsed % frameDelay); + } + + this.previewAnimationId = requestAnimationFrame(animate); + }; + + this.previewAnimationId = requestAnimationFrame(animate); + } + + /** + * Stop animated preview + */ + stopAnimatedPreview() { + if (this.previewAnimationId) { + cancelAnimationFrame(this.previewAnimationId); + this.previewAnimationId = null; + } + } + + /** + * Update preview (static or animated based on settings) + */ + updatePreview() { + if (this.hasAnimationsEnabled()) { + this.startAnimatedPreview(); + } else { + this.stopAnimatedPreview(); + this.draw(); + } + } + + /** + * Export as animated GIF + * @param {Function} progressCallback - Called with progress (0-1) + * @returns {Promise<Blob>} + */ + async exportAsGif(progressCallback = null) { + return new Promise((resolve, reject) => { + try { + // Create temporary canvas for frame generation + const frameCanvas = document.createElement('canvas'); + frameCanvas.width = this.canvas.width; + frameCanvas.height = this.canvas.height; + const frameCtx = frameCanvas.getContext('2d'); + + // Initialize gif.js + const gif = new GIF({ + workers: 2, + quality: 10, + workerScript: '/js/gif.worker.js', + width: this.canvas.width, + height: this.canvas.height + }); + + // Generate frames + const totalFrames = this.animConfig.totalFrames; + + const generateFrames = async () => { + for (let i = 0; i < totalFrames; i++) { + const animState = new AnimationState(i, totalFrames, this.animConfig.fps); + + // Draw to temporary canvas + frameCtx.clearRect(0, 0, frameCanvas.width, frameCanvas.height); + const tempGenerator = new ButtonGenerator(frameCanvas, { + fps: this.animConfig.fps, + duration: this.animConfig.duration, + fonts: this.fonts + }); + + // Copy effects + this.getAllEffects().forEach(effect => { + tempGenerator.registerEffect(effect); + }); + + tempGenerator.draw(animState); + + gif.addFrame(frameCtx, { + delay: 1000 / this.animConfig.fps, + copy: true + }); + + if (progressCallback) { + progressCallback(i / totalFrames, 'generating'); + } + + // Yield to browser every 5 frames + if (i % 5 === 0) { + await new Promise(resolve => setTimeout(resolve, 0)); + } + } + }; + + generateFrames().then(() => { + gif.on('finished', (blob) => { + resolve(blob); + }); + + gif.on('progress', (progress) => { + if (progressCallback) { + progressCallback(progress, 'encoding'); + } + }); + + gif.render(); + }); + + } catch (error) { + reject(error); + } + }); + } + + /** + * Bind UI controls to redraw on change + */ + bindControls() { + const allControls = new Set(); + this.getAllEffects().forEach(effect => { + effect.controls.forEach(control => { + allControls.add(control.id); + }); + }); + + allControls.forEach(id => { + const element = document.getElementById(id); + if (element) { + element.addEventListener('input', () => this.updatePreview()); + element.addEventListener('change', () => this.updatePreview()); + } + }); + } +} diff --git a/static/js/button-generator/debug-helper.js b/static/js/button-generator/debug-helper.js new file mode 100644 index 0000000..ae4e97d --- /dev/null +++ b/static/js/button-generator/debug-helper.js @@ -0,0 +1,26 @@ +// Debug helper - add this temporarily to main.js to see what's happening + +export function debugControlValues(generator) { + console.log('=== DEBUG: Control Values ==='); + const values = generator.getControlValues(); + + // Text controls + console.log('Text controls:'); + console.log(' button-text:', values['button-text']); + console.log(' text-enabled:', values['text-enabled']); + console.log(' font-size:', values['font-size']); + console.log(' text-x:', values['text-x']); + console.log(' text-y:', values['text-y']); + + // Check which effects are enabled + console.log('\nEnabled effects:'); + generator.getAllEffects().forEach(effect => { + const enabled = effect.isEnabled(values); + if (enabled) { + console.log(` ✓ ${effect.name} (${effect.id})`); + } + }); + + console.log('\nAll control values:', values); + console.log('=== END DEBUG ==='); +} diff --git a/static/js/button-generator/effect-base.js b/static/js/button-generator/effect-base.js new file mode 100644 index 0000000..87489cc --- /dev/null +++ b/static/js/button-generator/effect-base.js @@ -0,0 +1,92 @@ +/** + * Base class for button generator effects + * All effects should extend this class and implement required methods + */ +export class ButtonEffect { + /** + * @param {Object} config - Effect configuration + * @param {string} config.id - Unique identifier for this effect + * @param {string} config.name - Display name for the effect + * @param {string} config.type - Effect type: 'text', 'text2', 'background', 'general', 'border' + * @param {string} config.category - UI category for grouping effects + * @param {number} config.renderOrder - Order in rendering pipeline (lower = earlier) + */ + constructor(config) { + this.config = config; // Store full config for subclasses to access + this.id = config.id; + this.name = config.name; + this.type = config.type; // 'text', 'text2', 'background', 'general', 'border' + this.category = config.category; + this.renderOrder = config.renderOrder || 100; + this.controls = this.defineControls(); + } + + /** + * Define UI controls for this effect + * @returns {Array<Object>} Array of control definitions + * + * Control definition format: + * { + * id: string, // HTML element ID + * type: 'checkbox' | 'range' | 'color' | 'select' | 'text', + * label: string, // Display label + * defaultValue: any, // Default value + * min: number, // For range controls + * max: number, // For range controls + * step: number, // For range controls + * options: Array<{value, label}>, // For select controls + * showWhen: string, // ID of checkbox that controls visibility + * description: string // Optional tooltip/help text + * } + */ + defineControls() { + return []; + } + + /** + * Check if this effect is currently enabled + * @param {Object} controlValues - Current values of all controls + * @returns {boolean} + */ + isEnabled(controlValues) { + // Default: check for a control with ID pattern 'animate-{effectId}' or '{effectId}-enabled' + const enableControl = controlValues[`animate-${this.id}`] || + controlValues[`${this.id}-enabled`]; + return enableControl === true || enableControl === 'true'; + } + + /** + * Apply the effect during rendering + * @param {CanvasRenderingContext2D} context - Canvas context to draw on + * @param {Object} controlValues - Current values of all controls + * @param {AnimationState} animState - Current animation state (null for static) + * @param {Object} renderData - Additional render data (text metrics, colors, etc.) + */ + apply(context, controlValues, animState, renderData) { + throw new Error('Effect.apply() must be implemented by subclass'); + } + + /** + * Get control values specific to this effect + * @param {Object} allControlValues - All control values + * @returns {Object} Only the controls relevant to this effect + */ + getEffectControls(allControlValues) { + const effectControls = {}; + this.controls.forEach(control => { + if (control.id in allControlValues) { + effectControls[control.id] = allControlValues[control.id]; + } + }); + return effectControls; + } + + /** + * Validate that this effect can be applied + * @param {Object} controlValues - Current values of all controls + * @returns {boolean} + */ + canApply(controlValues) { + return this.isEnabled(controlValues); + } +} diff --git a/static/js/button-generator/effects/EXAMPLE.js b/static/js/button-generator/effects/EXAMPLE.js new file mode 100644 index 0000000..c2126d0 --- /dev/null +++ b/static/js/button-generator/effects/EXAMPLE.js @@ -0,0 +1,205 @@ +/** + * EXAMPLE EFFECT + * + * This is a template for creating new effects. + * Copy this file and modify it to create your own custom effects. + * + * This example creates a "spotlight" effect that highlights a circular area + * and darkens the rest of the button. + */ + +import { ButtonEffect } from '../effect-base.js'; + +/** + * Spotlight Effect + * Creates a moving circular spotlight that highlights different areas + */ +export class SpotlightEffect extends ButtonEffect { + constructor() { + super({ + // Unique ID for this effect (used in control IDs) + id: 'spotlight', + + // Display name shown in UI + name: 'Spotlight', + + // Effect type determines render order category + // Options: 'background', 'border', 'text', 'text2', 'general' + type: 'general', + + // Category for organizing effects in UI + category: 'Visual Effects', + + // Render order within type (lower = earlier) + // 1-9: backgrounds, 10-19: borders, 20-29: transforms, + // 30-49: text, 50-79: overlays, 80-99: post-processing + renderOrder: 60 + }); + } + + /** + * Define UI controls for this effect + * These controls will be automatically bound to the generator + */ + defineControls() { + return [ + // Main enable/disable checkbox + { + id: 'animate-spotlight', + type: 'checkbox', + label: 'Spotlight Effect', + defaultValue: false, + description: 'Moving circular spotlight' + }, + + // Spotlight size control + { + id: 'spotlight-size', + type: 'range', + label: 'Spotlight Size', + defaultValue: 20, + min: 10, + max: 50, + step: 1, + showWhen: 'animate-spotlight', // Only show when checkbox is enabled + description: 'Radius of the spotlight' + }, + + // Darkness of the vignette + { + id: 'spotlight-darkness', + type: 'range', + label: 'Darkness', + defaultValue: 0.5, + min: 0, + max: 1, + step: 0.05, + showWhen: 'animate-spotlight', + description: 'How dark the non-spotlight area should be' + }, + + // Speed of movement + { + id: 'spotlight-speed', + type: 'range', + label: 'Movement Speed', + defaultValue: 1, + min: 0.1, + max: 3, + step: 0.1, + showWhen: 'animate-spotlight', + description: 'Speed of spotlight movement' + } + ]; + } + + /** + * Determine if this effect should be applied + * @param {Object} controlValues - Current values of all controls + * @returns {boolean} + */ + isEnabled(controlValues) { + return controlValues['animate-spotlight'] === true; + } + + /** + * Apply the effect to the canvas + * + * @param {CanvasRenderingContext2D} context - Canvas 2D rendering context + * @param {Object} controlValues - Current values of all controls + * @param {AnimationState|null} animState - Animation state (null for static render) + * @param {Object} renderData - Render information: { width, height, centerX, centerY } + */ + apply(context, controlValues, animState, renderData) { + // Skip if no animation (spotlight needs movement) + if (!animState) return; + + // Get control values + const size = controlValues['spotlight-size'] || 20; + const darkness = controlValues['spotlight-darkness'] || 0.5; + const speed = controlValues['spotlight-speed'] || 1; + + // Calculate spotlight position + // Move in a circular pattern using animation phase + const phase = animState.getPhase(speed); + const spotX = renderData.centerX + Math.cos(phase) * 20; + const spotY = renderData.centerY + Math.sin(phase) * 10; + + // Create radial gradient for spotlight effect + const gradient = context.createRadialGradient( + spotX, spotY, 0, // Inner circle (center of spotlight) + spotX, spotY, size // Outer circle (edge of spotlight) + ); + + // Center is transparent (spotlight is bright) + gradient.addColorStop(0, `rgba(0, 0, 0, 0)`); + // Edge fades to dark + gradient.addColorStop(0.5, `rgba(0, 0, 0, ${darkness * 0.3})`); + gradient.addColorStop(1, `rgba(0, 0, 0, ${darkness})`); + + // Apply the gradient as an overlay + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + + // Optional: Add a bright center dot + context.fillStyle = 'rgba(255, 255, 255, 0.3)'; + context.beginPath(); + context.arc(spotX, spotY, 2, 0, Math.PI * 2); + context.fill(); + } + + /** + * Optional: Override canApply for more complex logic + * By default, it just checks isEnabled() + */ + canApply(controlValues) { + // Example: Only apply if text is also enabled + const textEnabled = controlValues['textEnabled']; + return this.isEnabled(controlValues) && textEnabled; + } + + /** + * Optional: Add helper methods for your effect + */ + calculateSpotlightPath(progress, width, height) { + // Example helper method + return { + x: width * progress, + y: height / 2 + }; + } +} + +/** + * Registration function + * This is called to add the effect to the generator + * + * @param {ButtonGenerator} generator - The button generator instance + */ +export function register(generator) { + generator.registerEffect(new SpotlightEffect()); +} + +/** + * USAGE: + * + * 1. Copy this file to a new name (e.g., my-effect.js) + * 2. Modify the class name, id, and effect logic + * 3. Import in main.js: + * import * as myEffect from './effects/my-effect.js'; + * 4. Register in setupApp(): + * myEffect.register(generator); + * 5. Add HTML controls with matching IDs + */ + +/** + * TIPS: + * + * - Use animState.progress for linear animations (0 to 1) + * - Use animState.getPhase(speed) for periodic animations (0 to 2π) + * - Use Math.sin/cos for smooth periodic motion + * - Check if (!animState) at the start if your effect requires animation + * - The context is automatically saved/restored, so feel free to transform + * - Use renderData for canvas dimensions and center point + * - Look at existing effects for more examples + */ diff --git a/static/js/button-generator/effects/background-emoji-wallpaper.js b/static/js/button-generator/effects/background-emoji-wallpaper.js new file mode 100644 index 0000000..e6c38a4 --- /dev/null +++ b/static/js/button-generator/effects/background-emoji-wallpaper.js @@ -0,0 +1,109 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Emoji wallpaper background effect + * Tiles a user-specified emoji across the background + */ +export class EmojiWallpaperEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-emoji-wallpaper', + name: 'Emoji Wallpaper', + type: 'background', + category: 'Background', + renderOrder: 1 + }); + } + + defineControls() { + return [ + { + id: 'emoji-text', + type: 'text', + label: 'Emoji Character', + defaultValue: '✨', + showWhen: 'bg-type', + description: 'Emoji to tile (can be any text)' + }, + { + id: 'emoji-size', + type: 'range', + label: 'Emoji Size', + defaultValue: 12, + min: 6, + max: 24, + step: 1, + showWhen: 'bg-type', + description: 'Size of each emoji' + }, + { + id: 'emoji-spacing', + type: 'range', + label: 'Emoji Spacing', + defaultValue: 16, + min: 8, + max: 32, + step: 2, + showWhen: 'bg-type', + description: 'Space between emojis' + }, + { + id: 'emoji-bg-color', + type: 'color', + label: 'Background Color', + defaultValue: '#1a1a2e', + showWhen: 'bg-type', + description: 'Background color behind emojis' + }, + { + id: 'emoji-opacity', + type: 'range', + label: 'Emoji Opacity', + defaultValue: 30, + min: 10, + max: 100, + step: 5, + showWhen: 'bg-type', + description: 'Transparency of emojis (lower = more transparent)' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['bg-type'] === 'emoji-wallpaper'; + } + + apply(context, controlValues, animState, renderData) { + const emoji = controlValues['emoji-text'] || '✨'; + const size = controlValues['emoji-size'] || 12; + const spacing = controlValues['emoji-spacing'] || 16; + const bgColor = controlValues['emoji-bg-color'] || '#1a1a2e'; + const opacity = (controlValues['emoji-opacity'] || 30) / 100; + + // Fill background color + context.fillStyle = bgColor; + context.fillRect(0, 0, renderData.width, renderData.height); + + // Setup emoji font + context.font = `${size}px Arial`; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.globalAlpha = opacity; + + // Tile emojis + for (let y = 0; y < renderData.height + spacing; y += spacing) { + for (let x = 0; x < renderData.width + spacing; x += spacing) { + // Offset every other row for a brick pattern + const offsetX = (Math.floor(y / spacing) % 2) * (spacing / 2); + context.fillText(emoji, x + offsetX, y); + } + } + + context.globalAlpha = 1.0; + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new EmojiWallpaperEffect()); +} diff --git a/static/js/button-generator/effects/background-gradient.js b/static/js/button-generator/effects/background-gradient.js new file mode 100644 index 0000000..4e8c8c8 --- /dev/null +++ b/static/js/button-generator/effects/background-gradient.js @@ -0,0 +1,76 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Gradient background effect + */ +export class GradientBackgroundEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-gradient', + name: 'Gradient Background', + type: 'background', + category: 'Background', + renderOrder: 1 + }); + } + + defineControls() { + return [ + { + id: 'gradient-color1', + type: 'color', + label: 'Gradient Color 1', + defaultValue: '#ff0000', + showWhen: 'bg-type', + description: 'Start color of gradient' + }, + { + id: 'gradient-color2', + type: 'color', + label: 'Gradient Color 2', + defaultValue: '#0000ff', + showWhen: 'bg-type', + description: 'End color of gradient' + }, + { + id: 'gradient-angle', + type: 'range', + label: 'Gradient Angle', + defaultValue: 90, + min: 0, + max: 360, + step: 1, + showWhen: 'bg-type', + description: 'Angle of gradient direction' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['bg-type'] === 'gradient'; + } + + apply(context, controlValues, animState, renderData) { + const color1 = controlValues['gradient-color1'] || '#ff0000'; + const color2 = controlValues['gradient-color2'] || '#0000ff'; + const angle = (controlValues['gradient-angle'] || 90) * (Math.PI / 180); + + // Calculate gradient endpoints + const x1 = renderData.centerX + Math.cos(angle) * renderData.centerX; + const y1 = renderData.centerY + Math.sin(angle) * renderData.centerY; + const x2 = renderData.centerX - Math.cos(angle) * renderData.centerX; + const y2 = renderData.centerY - Math.sin(angle) * renderData.centerY; + + const gradient = context.createLinearGradient(x1, y1, x2, y2); + gradient.addColorStop(0, color1); + gradient.addColorStop(1, color2); + + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new GradientBackgroundEffect()); +} diff --git a/static/js/button-generator/effects/background-rain.js b/static/js/button-generator/effects/background-rain.js new file mode 100644 index 0000000..fc3a609 --- /dev/null +++ b/static/js/button-generator/effects/background-rain.js @@ -0,0 +1,117 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Raining background effect + * Animated raindrops falling down the button + */ +export class RainBackgroundEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-rain', + name: 'Rain Effect', + type: 'general', + category: 'Background Animations', + renderOrder: 55 // After background, before other effects + }); + + // Initialize raindrop positions (persistent across frames) + this.raindrops = []; + this.initialized = false; + } + + defineControls() { + return [ + { + id: 'animate-rain', + type: 'checkbox', + label: 'Rain Effect', + defaultValue: false + }, + { + id: 'rain-density', + type: 'range', + label: 'Rain Density', + defaultValue: 15, + min: 5, + max: 30, + step: 1, + showWhen: 'animate-rain', + description: 'Number of raindrops' + }, + { + id: 'rain-speed', + type: 'range', + label: 'Rain Speed', + defaultValue: 1.5, + min: 0.5, + max: 3, + step: 0.1, + showWhen: 'animate-rain', + description: 'Speed of falling rain' + }, + { + id: 'rain-color', + type: 'color', + label: 'Rain Color', + defaultValue: '#6ba3ff', + showWhen: 'animate-rain', + description: 'Color of raindrops' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-rain'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const density = controlValues['rain-density'] || 15; + const speed = controlValues['rain-speed'] || 1.5; + const color = controlValues['rain-color'] || '#6ba3ff'; + + // Initialize raindrops on first frame + if (!this.initialized || this.raindrops.length !== density) { + this.raindrops = []; + for (let i = 0; i < density; i++) { + this.raindrops.push({ + x: Math.random() * renderData.width, + y: Math.random() * renderData.height, + length: 2 + Math.random() * 4, + speedMultiplier: 0.8 + Math.random() * 0.4 + }); + } + this.initialized = true; + } + + // Draw raindrops + context.strokeStyle = color; + context.lineWidth = 1; + context.lineCap = 'round'; + + this.raindrops.forEach(drop => { + // Update position + drop.y += speed * drop.speedMultiplier; + + // Reset to top when reaching bottom + if (drop.y > renderData.height + drop.length) { + drop.y = -drop.length; + drop.x = Math.random() * renderData.width; + } + + // Draw raindrop + context.globalAlpha = 0.6; + context.beginPath(); + context.moveTo(drop.x, drop.y); + context.lineTo(drop.x, drop.y + drop.length); + context.stroke(); + context.globalAlpha = 1.0; + }); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new RainBackgroundEffect()); +} diff --git a/static/js/button-generator/effects/background-rainbow.js b/static/js/button-generator/effects/background-rainbow.js new file mode 100644 index 0000000..07dae99 --- /dev/null +++ b/static/js/button-generator/effects/background-rainbow.js @@ -0,0 +1,137 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Rainbow flash background effect + * Animates background through rainbow colors + */ +export class RainbowBackgroundEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-rainbow', + name: 'Rainbow Background', + type: 'background', + category: 'Background Animations', + renderOrder: 2 // After base background + }); + } + + defineControls() { + return [ + { + id: 'animate-bg-rainbow', + type: 'checkbox', + label: 'Rainbow Flash', + defaultValue: false + }, + { + id: 'rainbow-speed', + type: 'range', + label: 'Rainbow Speed', + defaultValue: 1, + min: 0.1, + max: 5, + step: 0.1, + showWhen: 'animate-bg-rainbow', + description: 'Speed of rainbow cycling' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-bg-rainbow'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const speed = controlValues['rainbow-speed'] || 1; + const hue = (animState.progress * speed * 360) % 360; + + const bgType = controlValues['bg-type']; + + if (bgType === 'solid') { + // Solid rainbow + context.fillStyle = `hsl(${hue}, 70%, 50%)`; + context.fillRect(0, 0, renderData.width, renderData.height); + } else if (bgType === 'gradient') { + // Rainbow gradient + const angle = (controlValues['gradient-angle'] || 90) * (Math.PI / 180); + const x1 = renderData.centerX + Math.cos(angle) * renderData.centerX; + const y1 = renderData.centerY + Math.sin(angle) * renderData.centerY; + const x2 = renderData.centerX - Math.cos(angle) * renderData.centerX; + const y2 = renderData.centerY - Math.sin(angle) * renderData.centerY; + + const gradient = context.createLinearGradient(x1, y1, x2, y2); + gradient.addColorStop(0, `hsl(${hue}, 70%, 50%)`); + gradient.addColorStop(1, `hsl(${(hue + 60) % 360}, 70%, 60%)`); + + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + } + } +} + +/** + * Rainbow gradient sweep effect + * Creates a moving rainbow gradient that sweeps across the button + */ +export class RainbowGradientSweepEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-rainbow-gradient', + name: 'Rainbow Gradient Sweep', + type: 'general', + category: 'Background Animations', + renderOrder: 50 // After background and text + }); + } + + defineControls() { + return [ + { + id: 'animate-bg-rainbow-gradient', + type: 'checkbox', + label: 'Rainbow Sweep', + defaultValue: false, + description: 'Moving rainbow gradient overlay' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-bg-rainbow-gradient'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + // Map progress to position (-100 to 100) + const position = animState.progress * 200 - 100; + + // Create a horizontal gradient that sweeps across + const gradient = context.createLinearGradient( + position - 50, + 0, + position + 50, + 0 + ); + + // Create rainbow stops that also cycle through colors + const hueOffset = animState.progress * 360; + gradient.addColorStop(0, `hsla(${(hueOffset + 0) % 360}, 80%, 50%, 0)`); + gradient.addColorStop(0.2, `hsla(${(hueOffset + 60) % 360}, 80%, 50%, 0.6)`); + gradient.addColorStop(0.4, `hsla(${(hueOffset + 120) % 360}, 80%, 50%, 0.8)`); + gradient.addColorStop(0.6, `hsla(${(hueOffset + 180) % 360}, 80%, 50%, 0.8)`); + gradient.addColorStop(0.8, `hsla(${(hueOffset + 240) % 360}, 80%, 50%, 0.6)`); + gradient.addColorStop(1, `hsla(${(hueOffset + 300) % 360}, 80%, 50%, 0)`); + + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + } +} + +// Auto-register effects +export function register(generator) { + generator.registerEffect(new RainbowBackgroundEffect()); + generator.registerEffect(new RainbowGradientSweepEffect()); +} diff --git a/static/js/button-generator/effects/background-solid.js b/static/js/button-generator/effects/background-solid.js new file mode 100644 index 0000000..f16cc68 --- /dev/null +++ b/static/js/button-generator/effects/background-solid.js @@ -0,0 +1,56 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Solid color background effect + */ +export class SolidBackgroundEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-solid', + name: 'Solid Background', + type: 'background', + category: 'Background', + renderOrder: 1 + }); + } + + defineControls() { + return [ + { + id: 'bg-type', + type: 'select', + label: 'Background Type', + defaultValue: 'solid', + options: [ + { value: 'solid', label: 'Solid Color' }, + { value: 'gradient', label: 'Gradient' }, + { value: 'texture', label: 'Texture' }, + { value: 'emoji-wallpaper', label: 'Emoji Wallpaper' } + ] + }, + { + id: 'bg-color', + type: 'color', + label: 'Background Color', + defaultValue: '#4a90e2', + showWhen: 'bg-type', + description: 'Solid background color' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['bg-type'] === 'solid'; + } + + apply(context, controlValues, animState, renderData) { + const color = controlValues['bg-color'] || '#4a90e2'; + context.fillStyle = color; + context.fillRect(0, 0, renderData.width, renderData.height); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new SolidBackgroundEffect()); +} diff --git a/static/js/button-generator/effects/background-texture.js b/static/js/button-generator/effects/background-texture.js new file mode 100644 index 0000000..8afccbd --- /dev/null +++ b/static/js/button-generator/effects/background-texture.js @@ -0,0 +1,217 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Texture background effect + * Provides various procedural texture patterns + */ +export class TextureBackgroundEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-texture', + name: 'Texture Background', + type: 'background', + category: 'Background', + renderOrder: 1 + }); + } + + defineControls() { + return [ + { + id: 'texture-type', + type: 'select', + label: 'Texture Type', + defaultValue: 'dots', + showWhen: 'bg-type', + options: [ + { value: 'dots', label: 'Dots' }, + { value: 'grid', label: 'Grid' }, + { value: 'diagonal', label: 'Diagonal Lines' }, + { value: 'checkerboard', label: 'Checkerboard' }, + { value: 'noise', label: 'Noise' }, + { value: 'stars', label: 'Stars' } + ] + }, + { + id: 'texture-color1', + type: 'color', + label: 'Texture Color 1', + defaultValue: '#000000', + showWhen: 'bg-type', + description: 'Base color' + }, + { + id: 'texture-color2', + type: 'color', + label: 'Texture Color 2', + defaultValue: '#ffffff', + showWhen: 'bg-type', + description: 'Pattern color' + }, + { + id: 'texture-scale', + type: 'range', + label: 'Texture Scale', + defaultValue: 50, + min: 10, + max: 100, + step: 5, + showWhen: 'bg-type', + description: 'Size/density of pattern' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['bg-type'] === 'texture'; + } + + apply(context, controlValues, animState, renderData) { + const type = controlValues['texture-type'] || 'dots'; + const color1 = controlValues['texture-color1'] || '#000000'; + const color2 = controlValues['texture-color2'] || '#ffffff'; + const scale = controlValues['texture-scale'] || 50; + + const texture = this.drawTexture( + type, + color1, + color2, + scale, + renderData.width, + renderData.height + ); + + context.drawImage(texture, 0, 0); + } + + /** + * Draw texture pattern to a temporary canvas + */ + drawTexture(type, color1, color2, scale, width, height) { + const tempCanvas = document.createElement('canvas'); + tempCanvas.width = width; + tempCanvas.height = height; + const ctx = tempCanvas.getContext('2d'); + + // Fill base color + ctx.fillStyle = color1; + ctx.fillRect(0, 0, width, height); + + // Draw pattern + ctx.fillStyle = color2; + const size = Math.max(2, Math.floor(scale / 10)); + + switch (type) { + case 'dots': + this.drawDots(ctx, width, height, size); + break; + case 'grid': + this.drawGrid(ctx, width, height, size); + break; + case 'diagonal': + this.drawDiagonal(ctx, width, height, size); + break; + case 'checkerboard': + this.drawCheckerboard(ctx, width, height, size); + break; + case 'noise': + this.drawNoise(ctx, width, height); + break; + case 'stars': + this.drawStars(ctx, width, height, scale); + break; + } + + return tempCanvas; + } + + /** + * Draw dots pattern + */ + drawDots(ctx, width, height, size) { + for (let y = 0; y < height; y += size * 2) { + for (let x = 0; x < width; x += size * 2) { + ctx.beginPath(); + ctx.arc(x, y, size / 2, 0, Math.PI * 2); + ctx.fill(); + } + } + } + + /** + * Draw grid pattern + */ + drawGrid(ctx, width, height, size) { + // Vertical lines + for (let x = 0; x < width; x += size) { + ctx.fillRect(x, 0, 1, height); + } + // Horizontal lines + for (let y = 0; y < height; y += size) { + ctx.fillRect(0, y, width, 1); + } + } + + /** + * Draw diagonal lines pattern + */ + drawDiagonal(ctx, width, height, size) { + for (let i = -height; i < width; i += size) { + ctx.fillRect(i, 0, 2, height); + ctx.save(); + ctx.translate(i + 1, 0); + ctx.rotate(Math.PI / 4); + ctx.fillRect(0, 0, 2, Math.max(width, height)); + ctx.restore(); + } + } + + /** + * Draw checkerboard pattern + */ + drawCheckerboard(ctx, width, height, size) { + for (let y = 0; y < height; y += size) { + for (let x = 0; x < width; x += size) { + if ((x / size + y / size) % 2 === 0) { + ctx.fillRect(x, y, size, size); + } + } + } + } + + /** + * Draw random noise pattern + */ + drawNoise(ctx, width, height) { + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + if (Math.random() > 0.5) { + ctx.fillRect(x, y, 1, 1); + } + } + } + } + + /** + * Draw stars pattern + */ + drawStars(ctx, width, height, scale) { + const numStars = scale; + for (let i = 0; i < numStars; i++) { + const x = Math.floor(Math.random() * width); + const y = Math.floor(Math.random() * height); + + // Draw plus-shape star + ctx.fillRect(x, y, 1, 1); // Center + ctx.fillRect(x - 1, y, 1, 1); // Left + ctx.fillRect(x + 1, y, 1, 1); // Right + ctx.fillRect(x, y - 1, 1, 1); // Top + ctx.fillRect(x, y + 1, 1, 1); // Bottom + } + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new TextureBackgroundEffect()); +} diff --git a/static/js/button-generator/effects/border.js b/static/js/button-generator/effects/border.js new file mode 100644 index 0000000..79a496f --- /dev/null +++ b/static/js/button-generator/effects/border.js @@ -0,0 +1,139 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Border effect + * Draws borders around the button with various styles + */ +export class BorderEffect extends ButtonEffect { + constructor() { + super({ + id: 'border', + name: 'Border', + type: 'border', + category: 'Border', + renderOrder: 10 + }); + } + + defineControls() { + return [ + { + id: 'border-width', + type: 'range', + label: 'Border Width', + defaultValue: 2, + min: 0, + max: 5, + step: 1, + description: 'Width of border in pixels' + }, + { + id: 'border-color', + type: 'color', + label: 'Border Color', + defaultValue: '#000000' + }, + { + id: 'border-style', + type: 'select', + label: 'Border Style', + defaultValue: 'solid', + options: [ + { value: 'solid', label: 'Solid' }, + { value: 'inset', label: 'Inset (3D)' }, + { value: 'outset', label: 'Outset (3D)' }, + { value: 'ridge', label: 'Ridge' } + ] + } + ]; + } + + isEnabled(controlValues) { + const width = controlValues['border-width'] || 0; + return width > 0; + } + + apply(context, controlValues, animState, renderData) { + const width = controlValues['border-width'] || 0; + if (width === 0) return; + + const color = controlValues['border-color'] || '#000000'; + const style = controlValues['border-style'] || 'solid'; + + if (style === 'solid') { + this.drawSolidBorder(context, width, color, renderData); + } else if (style === 'inset' || style === 'outset') { + this.draw3DBorder(context, width, style === 'outset', renderData); + } else if (style === 'ridge') { + this.drawRidgeBorder(context, width, renderData); + } + } + + /** + * Draw solid border + */ + drawSolidBorder(context, width, color, renderData) { + context.strokeStyle = color; + context.lineWidth = width; + context.strokeRect( + width / 2, + width / 2, + renderData.width - width, + renderData.height - width + ); + } + + /** + * Draw 3D inset/outset border + */ + draw3DBorder(context, width, isOutset, renderData) { + const lightColor = isOutset ? '#ffffff' : '#000000'; + const darkColor = isOutset ? '#000000' : '#ffffff'; + + // Top and left (light) + context.strokeStyle = lightColor; + context.lineWidth = width; + context.beginPath(); + context.moveTo(0, renderData.height); + context.lineTo(0, 0); + context.lineTo(renderData.width, 0); + context.stroke(); + + // Bottom and right (dark) + context.strokeStyle = darkColor; + context.beginPath(); + context.moveTo(renderData.width, 0); + context.lineTo(renderData.width, renderData.height); + context.lineTo(0, renderData.height); + context.stroke(); + } + + /** + * Draw ridge border (double 3D effect) + */ + drawRidgeBorder(context, width, renderData) { + // Outer ridge (light) + context.strokeStyle = '#ffffff'; + context.lineWidth = width / 2; + context.strokeRect( + width / 4, + width / 4, + renderData.width - width / 2, + renderData.height - width / 2 + ); + + // Inner ridge (dark) + context.strokeStyle = '#000000'; + context.strokeRect( + (width * 3) / 4, + (width * 3) / 4, + renderData.width - width * 1.5, + renderData.height - width * 1.5 + ); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new BorderEffect()); +} diff --git a/static/js/button-generator/effects/glitch.js b/static/js/button-generator/effects/glitch.js new file mode 100644 index 0000000..ee78f34 --- /dev/null +++ b/static/js/button-generator/effects/glitch.js @@ -0,0 +1,93 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Glitch effect + * Creates horizontal scanline displacement for a glitchy look + */ +export class GlitchEffect extends ButtonEffect { + constructor() { + super({ + id: 'glitch', + name: 'Glitch', + type: 'general', + category: 'Visual Effects', + renderOrder: 80 + }); + } + + defineControls() { + return [ + { + id: 'animate-glitch', + type: 'checkbox', + label: 'Glitch Effect', + defaultValue: false + }, + { + id: 'glitch-intensity', + type: 'range', + label: 'Glitch Intensity', + defaultValue: 5, + min: 1, + max: 20, + step: 1, + showWhen: 'animate-glitch', + description: 'Maximum pixel offset for glitch' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-glitch'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const intensity = controlValues['glitch-intensity'] || 5; + const imageData = context.getImageData(0, 0, renderData.width, renderData.height); + + // Randomly glitch ~10% of scanlines per frame + const glitchProbability = 0.1; + const maxOffset = intensity; + + for (let y = 0; y < renderData.height; y++) { + if (Math.random() < glitchProbability) { + const offset = Math.floor((Math.random() - 0.5) * maxOffset * 2); + this.shiftScanline(imageData, y, offset, renderData.width); + } + } + + context.putImageData(imageData, 0, 0); + } + + /** + * Shift a horizontal scanline by offset pixels (with wrapping) + */ + shiftScanline(imageData, y, offset, width) { + const rowStart = y * width * 4; + const rowData = new Uint8ClampedArray(width * 4); + + // Copy row + for (let i = 0; i < width * 4; i++) { + rowData[i] = imageData.data[rowStart + i]; + } + + // Shift and wrap + for (let x = 0; x < width; x++) { + let sourceX = (x - offset + width) % width; + let destIdx = rowStart + x * 4; + let srcIdx = sourceX * 4; + + imageData.data[destIdx] = rowData[srcIdx]; + imageData.data[destIdx + 1] = rowData[srcIdx + 1]; + imageData.data[destIdx + 2] = rowData[srcIdx + 2]; + imageData.data[destIdx + 3] = rowData[srcIdx + 3]; + } + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new GlitchEffect()); +} diff --git a/static/js/button-generator/effects/hologram.js b/static/js/button-generator/effects/hologram.js new file mode 100644 index 0000000..e226aa1 --- /dev/null +++ b/static/js/button-generator/effects/hologram.js @@ -0,0 +1,170 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Hologram effect + * Creates a futuristic holographic appearance with glitches and scan lines + */ +export class HologramEffect extends ButtonEffect { + constructor() { + super({ + id: 'hologram', + name: 'Hologram', + type: 'general', + category: 'Visual Effects', + renderOrder: 88 // Near the end, after most other effects + }); + } + + defineControls() { + return [ + { + id: 'animate-hologram', + type: 'checkbox', + label: 'Hologram Effect', + defaultValue: false, + description: 'Futuristic holographic appearance' + }, + { + id: 'hologram-intensity', + type: 'range', + label: 'Effect Intensity', + defaultValue: 50, + min: 10, + max: 100, + step: 5, + showWhen: 'animate-hologram', + description: 'Strength of hologram effect' + }, + { + id: 'hologram-glitch-freq', + type: 'range', + label: 'Glitch Frequency', + defaultValue: 30, + min: 0, + max: 100, + step: 10, + showWhen: 'animate-hologram', + description: 'How often glitches occur' + }, + { + id: 'hologram-color', + type: 'color', + label: 'Hologram Tint', + defaultValue: '#00ffff', + showWhen: 'animate-hologram', + description: 'Color tint for hologram effect' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-hologram'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const intensity = (controlValues['hologram-intensity'] || 50) / 100; + const glitchFreq = (controlValues['hologram-glitch-freq'] || 30) / 100; + const color = controlValues['hologram-color'] || '#00ffff'; + + // Get current canvas content + const imageData = context.getImageData(0, 0, renderData.width, renderData.height); + const data = imageData.data; + + // Parse hologram color for tinting + const hexColor = color.replace('#', ''); + const r = parseInt(hexColor.substr(0, 2), 16); + const g = parseInt(hexColor.substr(2, 2), 16); + const b = parseInt(hexColor.substr(4, 2), 16); + + // Apply holographic tint + for (let i = 0; i < data.length; i += 4) { + // Mix with hologram color + data[i] = data[i] * (1 - intensity * 0.3) + r * intensity * 0.3; // Red + data[i + 1] = data[i + 1] * (1 - intensity * 0.5) + g * intensity * 0.5; // Green (more cyan) + data[i + 2] = data[i + 2] * (1 - intensity * 0.5) + b * intensity * 0.5; // Blue (more cyan) + } + + context.putImageData(imageData, 0, 0); + + // Add horizontal scan lines + context.globalAlpha = 0.05 * intensity; + context.fillStyle = '#000000'; + for (let y = 0; y < renderData.height; y += 2) { + context.fillRect(0, y, renderData.width, 1); + } + context.globalAlpha = 1.0; + + // Add moving highlight scan line + const scanY = (animState.progress * renderData.height) % renderData.height; + const gradient = context.createLinearGradient(0, scanY - 3, 0, scanY + 3); + gradient.addColorStop(0, 'rgba(0, 255, 255, 0)'); + gradient.addColorStop(0.5, `rgba(${r}, ${g}, ${b}, ${0.3 * intensity})`); + gradient.addColorStop(1, 'rgba(0, 255, 255, 0)'); + context.fillStyle = gradient; + context.fillRect(0, scanY - 3, renderData.width, 6); + + // Random glitches + if (Math.random() < glitchFreq * 0.1) { + const glitchY = Math.floor(Math.random() * renderData.height); + const glitchHeight = Math.floor(2 + Math.random() * 4); + const offset = (Math.random() - 0.5) * 6 * intensity; + + const sliceData = context.getImageData(0, glitchY, renderData.width, glitchHeight); + context.putImageData(sliceData, offset, glitchY); + } + + // Add chromatic aberration on edges + if (intensity > 0.3) { + const originalImage = context.getImageData(0, 0, renderData.width, renderData.height); + const aberration = 2 * intensity; + + // Slight red shift right + const redShift = context.getImageData(0, 0, renderData.width, renderData.height); + for (let i = 0; i < redShift.data.length; i += 4) { + const pixelIndex = i / 4; + const x = pixelIndex % renderData.width; + if (x < 3 || x > renderData.width - 3) { + const sourceIndex = ((pixelIndex + Math.floor(aberration)) * 4); + if (sourceIndex < originalImage.data.length) { + redShift.data[i] = originalImage.data[sourceIndex]; + } + } + } + + // Slight blue shift left + const blueShift = context.getImageData(0, 0, renderData.width, renderData.height); + for (let i = 0; i < blueShift.data.length; i += 4) { + const pixelIndex = i / 4; + const x = pixelIndex % renderData.width; + if (x < 3 || x > renderData.width - 3) { + const sourceIndex = ((pixelIndex - Math.floor(aberration)) * 4); + if (sourceIndex >= 0 && sourceIndex < originalImage.data.length) { + blueShift.data[i + 2] = originalImage.data[sourceIndex + 2]; + } + } + } + + context.putImageData(redShift, 0, 0); + context.globalCompositeOperation = 'screen'; + context.globalAlpha = 0.3; + context.putImageData(blueShift, 0, 0); + context.globalCompositeOperation = 'source-over'; + context.globalAlpha = 1.0; + } + + // Add flickering effect + if (Math.random() < 0.05) { + context.globalAlpha = 0.9 + Math.random() * 0.1; + context.fillStyle = 'rgba(255, 255, 255, 0.05)'; + context.fillRect(0, 0, renderData.width, renderData.height); + context.globalAlpha = 1.0; + } + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new HologramEffect()); +} diff --git a/static/js/button-generator/effects/noise.js b/static/js/button-generator/effects/noise.js new file mode 100644 index 0000000..92db24d --- /dev/null +++ b/static/js/button-generator/effects/noise.js @@ -0,0 +1,68 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Noise/Static effect + * Adds random pixel noise for a static/interference look + */ +export class NoiseEffect extends ButtonEffect { + constructor() { + super({ + id: 'noise', + name: 'Noise', + type: 'general', + category: 'Visual Effects', + renderOrder: 90 + }); + } + + defineControls() { + return [ + { + id: 'animate-noise', + type: 'checkbox', + label: 'Noise Effect', + defaultValue: false, + description: 'Random static/interference' + }, + { + id: 'noise-intensity', + type: 'range', + label: 'Noise Intensity', + defaultValue: 0.1, + min: 0.01, + max: 0.5, + step: 0.01, + showWhen: 'animate-noise', + description: 'Amount of noise' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-noise'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const intensity = controlValues['noise-intensity'] || 0.1; + const imageData = context.getImageData(0, 0, renderData.width, renderData.height); + + for (let i = 0; i < imageData.data.length; i += 4) { + // Random noise value + const noise = (Math.random() - 0.5) * 255 * intensity; + + imageData.data[i] = Math.max(0, Math.min(255, imageData.data[i] + noise)); + imageData.data[i + 1] = Math.max(0, Math.min(255, imageData.data[i + 1] + noise)); + imageData.data[i + 2] = Math.max(0, Math.min(255, imageData.data[i + 2] + noise)); + // Alpha unchanged + } + + context.putImageData(imageData, 0, 0); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new NoiseEffect()); +} diff --git a/static/js/button-generator/effects/pulse.js b/static/js/button-generator/effects/pulse.js new file mode 100644 index 0000000..41a745a --- /dev/null +++ b/static/js/button-generator/effects/pulse.js @@ -0,0 +1,62 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Pulse effect + * Scales the button content up and down + */ +export class PulseEffect extends ButtonEffect { + constructor() { + super({ + id: 'pulse', + name: 'Pulse', + type: 'transform', + category: 'Visual Effects', + renderOrder: 1 // Must run before any drawing + }); + } + + defineControls() { + return [ + { + id: 'animate-pulse', + type: 'checkbox', + label: 'Pulse Effect', + defaultValue: false + }, + { + id: 'pulse-scale', + type: 'range', + label: 'Pulse Scale', + defaultValue: 1.2, + min: 1.0, + max: 2.0, + step: 0.05, + showWhen: 'animate-pulse', + description: 'Maximum scale size' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-pulse'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const maxScale = controlValues['pulse-scale'] || 1.2; + const minScale = 1.0; + const scale = minScale + (maxScale - minScale) * + (Math.sin(animState.getPhase(1.0)) * 0.5 + 0.5); + + // Apply transformation (context save/restore handled by caller) + context.translate(renderData.centerX, renderData.centerY); + context.scale(scale, scale); + context.translate(-renderData.centerX, -renderData.centerY); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new PulseEffect()); +} diff --git a/static/js/button-generator/effects/rainbow-text.js b/static/js/button-generator/effects/rainbow-text.js new file mode 100644 index 0000000..e4fa148 --- /dev/null +++ b/static/js/button-generator/effects/rainbow-text.js @@ -0,0 +1,100 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Rainbow text animation effect + * Cycles text color through rainbow hues + */ +export class RainbowTextEffect extends ButtonEffect { + constructor(textLineNumber = 1) { + const suffix = textLineNumber === 1 ? '' : '2'; + super({ + id: `text-rainbow${suffix}`, + name: `Rainbow Text ${textLineNumber}`, + type: textLineNumber === 1 ? 'text' : 'text2', + category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2', + renderOrder: 5, // Apply before wave (lower order) + textLineNumber: textLineNumber // Pass through config + }); + this.textLineNumber = textLineNumber; + } + + defineControls() { + const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? '' : '2'; + return [ + { + id: `animate-text-rainbow${suffix}`, + type: 'checkbox', + label: 'Rainbow Animation', + defaultValue: false + }, + { + id: `text-rainbow-speed${suffix}`, + type: 'range', + label: 'Rainbow Speed', + defaultValue: 1, + min: 0.1, + max: 5, + step: 0.1, + showWhen: `animate-text-rainbow${suffix}`, + description: 'Speed of color cycling' + } + ]; + } + + isEnabled(controlValues) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + return controlValues[`animate-text-rainbow${suffix}`] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; // Rainbow requires animation + + const suffix = this.textLineNumber === 1 ? '' : '2'; + + // Get text configuration + const text = controlValues[`button-text${suffix}`] || ''; + const enabled = controlValues[`text${suffix}-enabled`]; + if (!text || !enabled) return; + + // Check if wave is also enabled - if so, skip (wave will handle rainbow) + if (controlValues[`animate-text-wave${suffix}`]) return; + + const fontSize = controlValues[`font-size${suffix}`] || 12; + const fontWeight = controlValues[`font-bold${suffix}`] ? 'bold' : 'normal'; + const fontStyle = controlValues[`font-italic${suffix}`] ? 'italic' : 'normal'; + const fontFamily = controlValues[`font-family${suffix}`] || 'Arial'; + + const x = (controlValues[`text${suffix}-x`] / 100) * renderData.width; + const y = (controlValues[`text${suffix}-y`] / 100) * renderData.height; + + const speed = controlValues[`text-rainbow-speed${suffix}`] || 1; + + // Calculate rainbow color + const hue = (animState.progress * speed * 360) % 360; + const fillStyle = `hsl(${hue}, 80%, 60%)`; + const strokeStyle = `hsl(${hue}, 80%, 30%)`; + + // Set font + context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + // Draw outline if enabled + if (controlValues[`text${suffix}-outline`]) { + context.strokeStyle = strokeStyle; + context.lineWidth = 2; + context.strokeText(text, x, y); + } + + // Draw text + context.fillStyle = fillStyle; + context.fillText(text, x, y); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new RainbowTextEffect(1)); + generator.registerEffect(new RainbowTextEffect(2)); +} diff --git a/static/js/button-generator/effects/rgb-split.js b/static/js/button-generator/effects/rgb-split.js new file mode 100644 index 0000000..63013a7 --- /dev/null +++ b/static/js/button-generator/effects/rgb-split.js @@ -0,0 +1,85 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * RGB Split / Chromatic Aberration effect + * Separates color channels for a glitchy chromatic aberration look + */ +export class RgbSplitEffect extends ButtonEffect { + constructor() { + super({ + id: 'rgb-split', + name: 'RGB Split', + type: 'general', + category: 'Visual Effects', + renderOrder: 85 + }); + } + + defineControls() { + return [ + { + id: 'animate-rgb-split', + type: 'checkbox', + label: 'RGB Split', + defaultValue: false, + description: 'Chromatic aberration effect' + }, + { + id: 'rgb-split-intensity', + type: 'range', + label: 'Split Intensity', + defaultValue: 2, + min: 1, + max: 10, + step: 0.5, + showWhen: 'animate-rgb-split', + description: 'Pixel offset for color channels' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-rgb-split'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const intensity = controlValues['rgb-split-intensity'] || 2; + const imageData = context.getImageData(0, 0, renderData.width, renderData.height); + const result = context.createImageData(renderData.width, renderData.height); + + // Oscillating offset + const phase = Math.sin(animState.getPhase(1.0)); + const offsetX = Math.round(phase * intensity); + + for (let y = 0; y < renderData.height; y++) { + for (let x = 0; x < renderData.width; x++) { + const idx = (y * renderData.width + x) * 4; + + // Red channel - shift left + const redX = Math.max(0, Math.min(renderData.width - 1, x - offsetX)); + const redIdx = (y * renderData.width + redX) * 4; + result.data[idx] = imageData.data[redIdx]; + + // Green channel - no shift + result.data[idx + 1] = imageData.data[idx + 1]; + + // Blue channel - shift right + const blueX = Math.max(0, Math.min(renderData.width - 1, x + offsetX)); + const blueIdx = (y * renderData.width + blueX) * 4; + result.data[idx + 2] = imageData.data[blueIdx + 2]; + + // Alpha channel + result.data[idx + 3] = imageData.data[idx + 3]; + } + } + + context.putImageData(result, 0, 0); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new RgbSplitEffect()); +} diff --git a/static/js/button-generator/effects/rotate.js b/static/js/button-generator/effects/rotate.js new file mode 100644 index 0000000..88012cc --- /dev/null +++ b/static/js/button-generator/effects/rotate.js @@ -0,0 +1,72 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Rotate effect + * Rotates the button back and forth + */ +export class RotateEffect extends ButtonEffect { + constructor() { + super({ + id: 'rotate', + name: 'Rotate', + type: 'transform', + category: 'Visual Effects', + renderOrder: 2 // Must run before any drawing (after pulse) + }); + } + + defineControls() { + return [ + { + id: 'animate-rotate', + type: 'checkbox', + label: 'Rotate Effect', + defaultValue: false + }, + { + id: 'rotate-angle', + type: 'range', + label: 'Max Angle', + defaultValue: 15, + min: 1, + max: 45, + step: 1, + showWhen: 'animate-rotate', + description: 'Maximum rotation angle in degrees' + }, + { + id: 'rotate-speed', + type: 'range', + label: 'Rotation Speed', + defaultValue: 1, + min: 0.1, + max: 3, + step: 0.1, + showWhen: 'animate-rotate', + description: 'Speed of rotation' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-rotate'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const maxAngle = controlValues['rotate-angle'] || 15; + const speed = controlValues['rotate-speed'] || 1; + const angle = Math.sin(animState.getPhase(speed)) * maxAngle * (Math.PI / 180); + + // Apply transformation (context save/restore handled by caller) + context.translate(renderData.centerX, renderData.centerY); + context.rotate(angle); + context.translate(-renderData.centerX, -renderData.centerY); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new RotateEffect()); +} diff --git a/static/js/button-generator/effects/scanline.js b/static/js/button-generator/effects/scanline.js new file mode 100644 index 0000000..5dab0b7 --- /dev/null +++ b/static/js/button-generator/effects/scanline.js @@ -0,0 +1,79 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Scanline effect + * Creates CRT-style horizontal lines + */ +export class ScanlineEffect extends ButtonEffect { + constructor() { + super({ + id: 'scanline', + name: 'Scanline', + type: 'general', + category: 'Visual Effects', + renderOrder: 75 + }); + } + + defineControls() { + return [ + { + id: 'animate-scanline', + type: 'checkbox', + label: 'Scanline Effect', + defaultValue: false + }, + { + id: 'scanline-intensity', + type: 'range', + label: 'Scanline Intensity', + defaultValue: 0.3, + min: 0.1, + max: 0.8, + step: 0.05, + showWhen: 'animate-scanline', + description: 'Darkness of scanlines' + }, + { + id: 'scanline-speed', + type: 'range', + label: 'Scanline Speed', + defaultValue: 1, + min: 0.1, + max: 3, + step: 0.1, + showWhen: 'animate-scanline', + description: 'Movement speed of scanlines' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-scanline'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const intensity = controlValues['scanline-intensity'] || 0.3; + const speed = controlValues['scanline-speed'] || 1; + + // Create overlay with scanlines + context.globalCompositeOperation = 'multiply'; + context.fillStyle = `rgba(0, 0, 0, ${intensity})`; + + // Animate scanline position + const offset = (animState.progress * speed * renderData.height) % 2; + + for (let y = offset; y < renderData.height; y += 2) { + context.fillRect(0, Math.floor(y), renderData.width, 1); + } + + context.globalCompositeOperation = 'source-over'; + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new ScanlineEffect()); +} diff --git a/static/js/button-generator/effects/shimmer.js b/static/js/button-generator/effects/shimmer.js new file mode 100644 index 0000000..327c124 --- /dev/null +++ b/static/js/button-generator/effects/shimmer.js @@ -0,0 +1,57 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Shimmer effect + * Creates a sweeping light/shine effect across the button + */ +export class ShimmerEffect extends ButtonEffect { + constructor() { + super({ + id: 'shimmer', + name: 'Shimmer', + type: 'general', + category: 'Visual Effects', + renderOrder: 70 + }); + } + + defineControls() { + return [ + { + id: 'animate-shimmer', + type: 'checkbox', + label: 'Shimmer Effect', + defaultValue: false, + description: 'Sweeping light effect' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['animate-shimmer'] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const shimmerX = animState.progress * (renderData.width + 40) - 20; + + const gradient = context.createLinearGradient( + shimmerX - 15, + 0, + shimmerX + 15, + renderData.height + ); + gradient.addColorStop(0, 'rgba(255, 255, 255, 0)'); + gradient.addColorStop(0.5, 'rgba(255, 255, 255, 0.3)'); + gradient.addColorStop(1, 'rgba(255, 255, 255, 0)'); + + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new ShimmerEffect()); +} diff --git a/static/js/button-generator/effects/spin-text.js b/static/js/button-generator/effects/spin-text.js new file mode 100644 index 0000000..d009b2b --- /dev/null +++ b/static/js/button-generator/effects/spin-text.js @@ -0,0 +1,144 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Spinning text animation effect + * Makes each character rotate independently + */ +export class SpinTextEffect extends ButtonEffect { + constructor(textLineNumber = 1) { + const suffix = textLineNumber === 1 ? '' : '2'; + super({ + id: `text-spin${suffix}`, + name: `Spinning Text ${textLineNumber}`, + type: textLineNumber === 1 ? 'text' : 'text2', + category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2', + renderOrder: 8, // Before wave, after rainbow + textLineNumber: textLineNumber + }); + this.textLineNumber = textLineNumber; + } + + defineControls() { + const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? '' : '2'; + return [ + { + id: `animate-text-spin${suffix}`, + type: 'checkbox', + label: 'Spinning Animation', + defaultValue: false + }, + { + id: `spin-speed${suffix}`, + type: 'range', + label: 'Spin Speed', + defaultValue: 1, + min: 0.1, + max: 5, + step: 0.1, + showWhen: `animate-text-spin${suffix}`, + description: 'Speed of character rotation' + }, + { + id: `spin-stagger${suffix}`, + type: 'range', + label: 'Spin Stagger', + defaultValue: 0.3, + min: 0, + max: 1, + step: 0.1, + showWhen: `animate-text-spin${suffix}`, + description: 'Delay between characters' + } + ]; + } + + isEnabled(controlValues) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + return controlValues[`animate-text-spin${suffix}`] === true; + } + + apply(context, controlValues, animState, renderData) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + const text = controlValues[`button-text${suffix}`] || ''; + + if (!text || !controlValues[`text${suffix}-enabled`]) return; + if (!animState) return; + + const speed = controlValues[`spin-speed${suffix}`] || 1; + const stagger = controlValues[`spin-stagger${suffix}`] || 0.3; + const fontSize = controlValues[`font-size${suffix}`] || 14; + const fontFamily = controlValues[`font-family${suffix}`] || 'Arial'; + const fontWeight = controlValues[`text${suffix}-bold`] ? 'bold' : 'normal'; + const fontStyle = controlValues[`text${suffix}-italic`] ? 'italic' : 'normal'; + + context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + // Get text color + let fillStyle; + const colorType = controlValues[`text${suffix}-color-type`] || 'solid'; + if (colorType === 'gradient') { + const color1 = controlValues[`text${suffix}-gradient-color1`] || '#ffffff'; + const color2 = controlValues[`text${suffix}-gradient-color2`] || '#00ffff'; + const angle = (controlValues[`text${suffix}-gradient-angle`] || 90) * (Math.PI / 180); + const centerX = renderData.centerX; + const centerY = renderData.centerY; + const x1 = centerX + Math.cos(angle) * 20; + const y1 = centerY + Math.sin(angle) * 20; + const x2 = centerX - Math.cos(angle) * 20; + const y2 = centerY - Math.sin(angle) * 20; + const gradient = context.createLinearGradient(x1, y1, x2, y2); + gradient.addColorStop(0, color1); + gradient.addColorStop(1, color2); + fillStyle = gradient; + } else { + fillStyle = controlValues[`text${suffix}-color`] || '#ffffff'; + } + + // Calculate base position + const x = controlValues[`text${suffix}-x`] || 50; + const y = controlValues[`text${suffix}-y`] || 50; + const baseX = (x / 100) * renderData.width; + const baseY = (y / 100) * renderData.height; + + // Measure total text width for centering + const totalWidth = context.measureText(text).width; + let currentX = baseX - totalWidth / 2; + + // Draw each character with rotation + for (let i = 0; i < text.length; i++) { + const char = text[i]; + const charWidth = context.measureText(char).width; + const charCenterX = currentX + charWidth / 2; + + // Calculate rotation for this character + const phase = animState.getPhase(speed) + i * stagger * Math.PI * 2; + const rotation = phase % (Math.PI * 2); + + context.save(); + context.translate(charCenterX, baseY); + context.rotate(rotation); + + // Apply outline if enabled + if (controlValues[`text${suffix}-outline`]) { + context.strokeStyle = controlValues[`text${suffix}-outline-color`] || '#000000'; + context.lineWidth = 2; + context.strokeText(char, 0, 0); + } + + context.fillStyle = fillStyle; + context.fillText(char, 0, 0); + context.restore(); + + currentX += charWidth; + } + } +} + +// Auto-register effects for both text lines +export function register(generator) { + generator.registerEffect(new SpinTextEffect(1)); + generator.registerEffect(new SpinTextEffect(2)); +} diff --git a/static/js/button-generator/effects/spotlight.js b/static/js/button-generator/effects/spotlight.js new file mode 100644 index 0000000..b1185e7 --- /dev/null +++ b/static/js/button-generator/effects/spotlight.js @@ -0,0 +1,209 @@ +/** + * EXAMPLE EFFECT + * + * This is a template for creating new effects. + * Copy this file and modify it to create your own custom effects. + * + * This example creates a "spotlight" effect that highlights a circular area + * and darkens the rest of the button. + */ + +import { ButtonEffect } from "../effect-base.js"; + +/** + * Spotlight Effect + * Creates a moving circular spotlight that highlights different areas + */ +export class SpotlightEffect extends ButtonEffect { + constructor() { + super({ + // Unique ID for this effect (used in control IDs) + id: "spotlight", + + // Display name shown in UI + name: "Spotlight", + + // Effect type determines render order category + // Options: 'background', 'border', 'text', 'text2', 'general' + type: "general", + + // Category for organizing effects in UI + category: "Visual Effects", + + // Render order within type (lower = earlier) + // 1-9: backgrounds, 10-19: borders, 20-29: transforms, + // 30-49: text, 50-79: overlays, 80-99: post-processing + renderOrder: 60, + }); + } + + /** + * Define UI controls for this effect + * These controls will be automatically bound to the generator + */ + defineControls() { + return [ + // Main enable/disable checkbox + { + id: "animate-spotlight", + type: "checkbox", + label: "Spotlight Effect", + defaultValue: false, + description: "Moving circular spotlight", + }, + + // Spotlight size control + { + id: "spotlight-size", + type: "range", + label: "Spotlight Size", + defaultValue: 20, + min: 10, + max: 50, + step: 1, + showWhen: "animate-spotlight", // Only show when checkbox is enabled + description: "Radius of the spotlight", + }, + + // Darkness of the vignette + { + id: "spotlight-darkness", + type: "range", + label: "Darkness", + defaultValue: 0.5, + min: 0, + max: 1, + step: 0.05, + showWhen: "animate-spotlight", + description: "How dark the non-spotlight area should be", + }, + + // Speed of movement + { + id: "spotlight-speed", + type: "range", + label: "Movement Speed", + defaultValue: 1, + min: 0.1, + max: 3, + step: 0.1, + showWhen: "animate-spotlight", + description: "Speed of spotlight movement", + }, + ]; + } + + /** + * Determine if this effect should be applied + * @param {Object} controlValues - Current values of all controls + * @returns {boolean} + */ + isEnabled(controlValues) { + return controlValues["animate-spotlight"] === true; + } + + /** + * Apply the effect to the canvas + * + * @param {CanvasRenderingContext2D} context - Canvas 2D rendering context + * @param {Object} controlValues - Current values of all controls + * @param {AnimationState|null} animState - Animation state (null for static render) + * @param {Object} renderData - Render information: { width, height, centerX, centerY } + */ + apply(context, controlValues, animState, renderData) { + // Skip if no animation (spotlight needs movement) + if (!animState) return; + + // Get control values + const size = controlValues["spotlight-size"] || 20; + const darkness = controlValues["spotlight-darkness"] || 0.5; + const speed = controlValues["spotlight-speed"] || 1; + + // Calculate spotlight position + // Move in a circular pattern using animation phase + const phase = animState.getPhase(speed); + const spotX = renderData.centerX + Math.cos(phase) * 20; + const spotY = renderData.centerY + Math.sin(phase) * 10; + + // Create radial gradient for spotlight effect + const gradient = context.createRadialGradient( + spotX, + spotY, + 0, // Inner circle (center of spotlight) + spotX, + spotY, + size, // Outer circle (edge of spotlight) + ); + + // Center is transparent (spotlight is bright) + gradient.addColorStop(0, `rgba(0, 0, 0, 0)`); + // Edge fades to dark + gradient.addColorStop(0.5, `rgba(0, 0, 0, ${darkness * 0.3})`); + gradient.addColorStop(1, `rgba(0, 0, 0, ${darkness})`); + + // Apply the gradient as an overlay + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + + // Optional: Add a bright center dot + context.fillStyle = "rgba(255, 255, 255, 0.3)"; + context.beginPath(); + context.arc(spotX, spotY, 2, 0, Math.PI * 2); + context.fill(); + } + + /** + * Optional: Override canApply for more complex logic + * By default, it just checks isEnabled() + */ + canApply(controlValues) { + // Example: Only apply if text is also enabled + const textEnabled = controlValues["textEnabled"]; + return this.isEnabled(controlValues) && textEnabled; + } + + /** + * Optional: Add helper methods for your effect + */ + calculateSpotlightPath(progress, width, height) { + // Example helper method + return { + x: width * progress, + y: height / 2, + }; + } +} + +/** + * Registration function + * This is called to add the effect to the generator + * + * @param {ButtonGenerator} generator - The button generator instance + */ +export function register(generator) { + generator.registerEffect(new SpotlightEffect()); +} + +/** + * USAGE: + * + * 1. Copy this file to a new name (e.g., my-effect.js) + * 2. Modify the class name, id, and effect logic + * 3. Import in main.js: + * import * as myEffect from './effects/my-effect.js'; + * 4. Register in setupApp(): + * myEffect.register(generator); + * 5. Add HTML controls with matching IDs + */ + +/** + * TIPS: + * + * - Use animState.progress for linear animations (0 to 1) + * - Use animState.getPhase(speed) for periodic animations (0 to 2π) + * - Use Math.sin/cos for smooth periodic motion + * - Check if (!animState) at the start if your effect requires animation + * - The context is automatically saved/restored, so feel free to transform + * - Use renderData for canvas dimensions and center point + * - Look at existing effects for more examples + */ diff --git a/static/js/button-generator/effects/text-standard.js b/static/js/button-generator/effects/text-standard.js new file mode 100644 index 0000000..9caaf58 --- /dev/null +++ b/static/js/button-generator/effects/text-standard.js @@ -0,0 +1,232 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Standard text rendering effect + * Renders static text (when no animations are active) + */ +export class StandardTextEffect extends ButtonEffect { + constructor(textLineNumber = 1) { + const suffix = textLineNumber === 1 ? '' : '2'; + super({ + id: `text-standard${suffix}`, + name: `Standard Text ${textLineNumber}`, + type: textLineNumber === 1 ? 'text' : 'text2', + category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2', + renderOrder: 20, // After animations + textLineNumber: textLineNumber // Pass through config so defineControls can access it + }); + this.textLineNumber = textLineNumber; + } + + defineControls() { + // Access from config since this is called before constructor completes + const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? '' : '2'; + return [ + { + id: `button-text${suffix}`, + type: 'text', + label: `Text Line ${textLineNumber}`, + defaultValue: textLineNumber === 1 ? 'RITUAL.SH' : '' + }, + { + id: `text${suffix}-enabled`, + type: 'checkbox', + label: `Enable Text Line ${textLineNumber}`, + defaultValue: textLineNumber === 1 + }, + { + id: `font-size${suffix}`, + type: 'range', + label: 'Font Size', + min: 6, + max: 24, + defaultValue: textLineNumber === 1 ? 14 : 12 + }, + { + id: `text${suffix}-x`, + type: 'range', + label: 'Horizontal Position', + min: 0, + max: 100, + defaultValue: 50, + description: 'Percentage from left' + }, + { + id: `text${suffix}-y`, + type: 'range', + label: 'Vertical Position', + min: 0, + max: 100, + defaultValue: textLineNumber === 1 ? 35 : 65, + description: 'Percentage from top' + }, + { + id: `text${suffix}-color-type`, + type: 'select', + label: 'Color Type', + defaultValue: 'solid', + options: [ + { value: 'solid', label: 'Solid Color' }, + { value: 'gradient', label: 'Gradient' } + ] + }, + { + id: `text${suffix}-color`, + type: 'color', + label: 'Text Color', + defaultValue: '#ffffff', + showWhen: `text${suffix}-color-type` + }, + { + id: `text${suffix}-gradient-color1`, + type: 'color', + label: 'Gradient Color 1', + defaultValue: '#ffffff', + showWhen: `text${suffix}-color-type` + }, + { + id: `text${suffix}-gradient-color2`, + type: 'color', + label: 'Gradient Color 2', + defaultValue: '#00ffff', + showWhen: `text${suffix}-color-type` + }, + { + id: `text${suffix}-gradient-angle`, + type: 'range', + label: 'Gradient Angle', + min: 0, + max: 360, + defaultValue: 0, + showWhen: `text${suffix}-color-type` + }, + { + id: `text${suffix}-outline`, + type: 'checkbox', + label: 'Outline', + defaultValue: false + }, + { + id: `outline${suffix}-color`, + type: 'color', + label: 'Outline Color', + defaultValue: '#000000', + showWhen: `text${suffix}-outline` + }, + { + id: `font-family${suffix}`, + type: 'select', + label: 'Font', + defaultValue: 'Lato', + options: [ + { value: 'Lato', label: 'Lato' }, + { value: 'Roboto', label: 'Roboto' }, + { value: 'Open Sans', label: 'Open Sans' }, + { value: 'Montserrat', label: 'Montserrat' }, + { value: 'Oswald', label: 'Oswald' }, + { value: 'Bebas Neue', label: 'Bebas Neue' }, + { value: 'Roboto Mono', label: 'Roboto Mono' }, + { value: 'VT323', label: 'VT323' }, + { value: 'Press Start 2P', label: 'Press Start 2P' }, + { value: 'DSEG7-Classic', label: 'DSEG7' } + ] + }, + { + id: `font-bold${suffix}`, + type: 'checkbox', + label: 'Bold', + defaultValue: false + }, + { + id: `font-italic${suffix}`, + type: 'checkbox', + label: 'Italic', + defaultValue: false + } + ]; + } + + isEnabled(controlValues) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + const text = controlValues[`button-text${suffix}`]; + const enabled = controlValues[`text${suffix}-enabled`]; + + // Only render if text exists, is enabled, and no animations are active on this text + const waveActive = controlValues[`animate-text-wave${suffix}`]; + const rainbowActive = controlValues[`animate-text-rainbow${suffix}`]; + + return text && enabled && !waveActive && !rainbowActive; + } + + apply(context, controlValues, animState, renderData) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + + const text = controlValues[`button-text${suffix}`]; + if (!text) return; + + const fontSize = controlValues[`font-size${suffix}`] || 12; + const fontWeight = controlValues[`font-bold${suffix}`] ? 'bold' : 'normal'; + const fontStyle = controlValues[`font-italic${suffix}`] ? 'italic' : 'normal'; + const fontFamily = controlValues[`font-family${suffix}`] || 'Arial'; + + const x = (controlValues[`text${suffix}-x`] / 100) * renderData.width; + const y = (controlValues[`text${suffix}-y`] / 100) * renderData.height; + + // Set font + context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + // Get colors + const colors = this.getTextColors(context, controlValues, text, x, y, fontSize); + + // Draw outline if enabled + if (controlValues[`text${suffix}-outline`]) { + context.strokeStyle = colors.strokeStyle; + context.lineWidth = 2; + context.strokeText(text, x, y); + } + + // Draw text + context.fillStyle = colors.fillStyle; + context.fillText(text, x, y); + } + + /** + * Get text colors (solid or gradient) + */ + getTextColors(context, controlValues, text, x, y, fontSize) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + const colorType = controlValues[`text${suffix}-color-type`] || 'solid'; + + let fillStyle, strokeStyle; + + if (colorType === 'solid') { + fillStyle = controlValues[`text${suffix}-color`] || '#ffffff'; + strokeStyle = controlValues[`outline${suffix}-color`] || '#000000'; + } else { + // Gradient + const angle = (controlValues[`text${suffix}-gradient-angle`] || 0) * (Math.PI / 180); + const textWidth = context.measureText(text).width; + const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2; + const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2; + const x2 = x + textWidth / 2 - (Math.cos(angle) * textWidth) / 2; + const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2; + + const gradient = context.createLinearGradient(x1, y1, x2, y2); + gradient.addColorStop(0, controlValues[`text${suffix}-gradient-color1`] || '#ffffff'); + gradient.addColorStop(1, controlValues[`text${suffix}-gradient-color2`] || '#00ffff'); + fillStyle = gradient; + strokeStyle = controlValues[`outline${suffix}-color`] || '#000000'; + } + + return { fillStyle, strokeStyle }; + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new StandardTextEffect(1)); + generator.registerEffect(new StandardTextEffect(2)); +} diff --git a/static/js/button-generator/effects/wave-text.js b/static/js/button-generator/effects/wave-text.js new file mode 100644 index 0000000..cf36211 --- /dev/null +++ b/static/js/button-generator/effects/wave-text.js @@ -0,0 +1,167 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * Wave text animation effect + * Makes text characters wave up and down in a sine wave pattern + */ +export class WaveTextEffect extends ButtonEffect { + constructor(textLineNumber = 1) { + const suffix = textLineNumber === 1 ? '' : '2'; + super({ + id: `text-wave${suffix}`, + name: `Text Wave ${textLineNumber}`, + type: textLineNumber === 1 ? 'text' : 'text2', + category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2', + renderOrder: 10, + textLineNumber: textLineNumber // Pass through config + }); + this.textLineNumber = textLineNumber; + } + + defineControls() { + const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? '' : '2'; + return [ + { + id: `animate-text-wave${suffix}`, + type: 'checkbox', + label: 'Wave Animation', + defaultValue: false + }, + { + id: `wave-amplitude${suffix}`, + type: 'range', + label: 'Wave Amplitude', + defaultValue: 3, + min: 1, + max: 10, + step: 0.5, + showWhen: `animate-text-wave${suffix}`, + description: 'Height of the wave motion' + }, + { + id: `wave-speed${suffix}`, + type: 'range', + label: 'Wave Speed', + defaultValue: 1, + min: 0.1, + max: 3, + step: 0.1, + showWhen: `animate-text-wave${suffix}`, + description: 'Speed of the wave animation' + } + ]; + } + + isEnabled(controlValues) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + return controlValues[`animate-text-wave${suffix}`] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; // Wave requires animation + + const suffix = this.textLineNumber === 1 ? '' : '2'; + + // Get text configuration + const text = controlValues[`button-text${suffix}`] || ''; + const enabled = controlValues[`text${suffix}-enabled`]; + if (!text || !enabled) return; + + const fontSize = controlValues[`font-size${suffix}`] || 12; + const fontWeight = controlValues[`font-bold${suffix}`] ? 'bold' : 'normal'; + const fontStyle = controlValues[`font-italic${suffix}`] ? 'italic' : 'normal'; + const fontFamily = controlValues[`font-family${suffix}`] || 'Arial'; + + const baseX = (controlValues[`text${suffix}-x`] / 100) * renderData.width; + const baseY = (controlValues[`text${suffix}-y`] / 100) * renderData.height; + + const amplitude = controlValues[`wave-amplitude${suffix}`] || 3; + const speed = controlValues[`wave-speed${suffix}`] || 1; + + // Set font + context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + + // Get colors + const colors = this.getTextColors(context, controlValues, text, baseX, baseY, fontSize, animState); + + // Measure total width for centering + const totalWidth = context.measureText(text).width; + let currentX = baseX - totalWidth / 2; + + // Draw each character with wave offset + for (let i = 0; i < text.length; i++) { + const char = text[i]; + const charWidth = context.measureText(char).width; + + // Calculate wave offset for this character + const phase = animState.getPhase(speed); + const charOffset = i / text.length; + const waveY = Math.sin(phase + charOffset * Math.PI * 2) * amplitude; + + const charX = currentX + charWidth / 2; + const charY = baseY + waveY; + + // Draw outline if enabled + if (controlValues[`text${suffix}-outline`]) { + context.strokeStyle = colors.strokeStyle; + context.lineWidth = 2; + context.strokeText(char, charX, charY); + } + + // Draw character + context.fillStyle = colors.fillStyle; + context.fillText(char, charX, charY); + + currentX += charWidth; + } + } + + /** + * Get text colors (solid, gradient, or rainbow) + */ + getTextColors(context, controlValues, text, x, y, fontSize, animState) { + const suffix = this.textLineNumber === 1 ? '' : '2'; + + let fillStyle, strokeStyle; + + // Check if rainbow text is also enabled + if (animState && controlValues[`animate-text-rainbow${suffix}`]) { + const speed = controlValues[`text-rainbow-speed${suffix}`] || 1; + const hue = (animState.progress * speed * 360) % 360; + fillStyle = `hsl(${hue}, 80%, 60%)`; + strokeStyle = `hsl(${hue}, 80%, 30%)`; + } else { + const colorType = controlValues[`text${suffix}-color-type`] || 'solid'; + + if (colorType === 'solid') { + fillStyle = controlValues[`text${suffix}-color`] || '#ffffff'; + strokeStyle = controlValues[`outline${suffix}-color`] || '#000000'; + } else { + // Gradient + const angle = (controlValues[`text${suffix}-gradient-angle`] || 0) * (Math.PI / 180); + const textWidth = context.measureText(text).width; + const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2; + const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2; + const x2 = x + textWidth / 2 - (Math.cos(angle) * textWidth) / 2; + const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2; + + const gradient = context.createLinearGradient(x1, y1, x2, y2); + gradient.addColorStop(0, controlValues[`text${suffix}-gradient-color1`] || '#ffffff'); + gradient.addColorStop(1, controlValues[`text${suffix}-gradient-color2`] || '#00ffff'); + fillStyle = gradient; + strokeStyle = controlValues[`outline${suffix}-color`] || '#000000'; + } + } + + return { fillStyle, strokeStyle }; + } +} + +// Auto-register effect +export function register(generator) { + generator.registerEffect(new WaveTextEffect(1)); + generator.registerEffect(new WaveTextEffect(2)); +} diff --git a/static/js/button-generator/main.js b/static/js/button-generator/main.js new file mode 100644 index 0000000..805edef --- /dev/null +++ b/static/js/button-generator/main.js @@ -0,0 +1,352 @@ +/** + * Button Generator - Main Application File + * + * This demonstrates how to use the modular button generator system. + * Effects are imported and registered with the generator. + */ + +import { ButtonGenerator } from "./button-generator-core.js"; +import { UIBuilder } from "./ui-builder.js"; + +// Import effects (each effect file exports a register() function) +import * as solidBg from "./effects/background-solid.js"; +import * as gradientBg from "./effects/background-gradient.js"; +import * as textureBg from "./effects/background-texture.js"; +import * as emojiWallpaper from "./effects/background-emoji-wallpaper.js"; +import * as rainbowBg from "./effects/background-rainbow.js"; +import * as rain from "./effects/background-rain.js"; +import * as border from "./effects/border.js"; +import * as standardText from "./effects/text-standard.js"; +import * as waveText from "./effects/wave-text.js"; +import * as rainbowText from "./effects/rainbow-text.js"; +import * as spinText from "./effects/spin-text.js"; +import * as glitch from "./effects/glitch.js"; +import * as pulse from "./effects/pulse.js"; +import * as shimmer from "./effects/shimmer.js"; +import * as scanline from "./effects/scanline.js"; +import * as rgbSplit from "./effects/rgb-split.js"; +import * as noise from "./effects/noise.js"; +import * as rotate from "./effects/rotate.js"; +import * as hologram from "./effects/hologram.js"; +import * as spotlight from "./effects/spotlight.js"; + +/** + * Initialize the button generator application + */ +export function init() { + // Wait for DOM to be ready + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", setupApp); + } else { + setupApp(); + } +} + +/** + * Setup the application + */ +async function setupApp() { + console.log("Initializing Button Generator..."); + + // Setup collapsible sections + setupCollapsible(); + + // Get canvas + const canvas = document.getElementById("button-canvas"); + if (!canvas) { + console.error("Canvas element not found!"); + return; + } + + // Create button generator + const generator = new ButtonGenerator(canvas, { + fps: 20, + duration: 2, + fonts: [ + "Lato", + "Roboto", + "Open Sans", + "Montserrat", + "Oswald", + "Bebas Neue", + "Roboto Mono", + "VT323", + "Press Start 2P", + "DSEG7-Classic", + ], + }); + + // Register all effects + console.log("Registering effects..."); + solidBg.register(generator); + gradientBg.register(generator); + textureBg.register(generator); + emojiWallpaper.register(generator); + rainbowBg.register(generator); + rain.register(generator); + border.register(generator); + standardText.register(generator); + waveText.register(generator); + rainbowText.register(generator); + spinText.register(generator); + glitch.register(generator); + pulse.register(generator); + shimmer.register(generator); + scanline.register(generator); + rgbSplit.register(generator); + noise.register(generator); + rotate.register(generator); + hologram.register(generator); + spotlight.register(generator); + + console.log(`Registered ${generator.getAllEffects().length} effects`); + + // Build UI from effects + console.log("Building UI..."); + const controlsContainer = document.querySelector(".controls-section"); + if (!controlsContainer) { + console.error("Controls container not found!"); + return; + } + + const uiBuilder = new UIBuilder(controlsContainer); + uiBuilder.buildUI(generator.getAllEffects()); + uiBuilder.setupConditionalVisibility(); + + // Preload fonts + console.log("Loading fonts..."); + await generator.preloadFonts(); + + // Bind controls (after UI is built) + generator.bindControls(); + + // Setup additional UI handlers + setupUIHandlers(generator); + + // Setup download button + setupDownloadButton(generator); + + // Setup presets + setupPresets(generator); + + // Initial draw + generator.updatePreview(); + + console.log("Button Generator ready!"); +} + +/** + * Setup collapsible section functionality + */ +function setupCollapsible() { + const headers = document.querySelectorAll(".control-group-header"); + console.log("Found", headers.length, "collapsible headers"); + + headers.forEach((header) => { + header.addEventListener("click", () => { + const controlGroup = header.closest(".control-group"); + if (controlGroup) { + controlGroup.classList.toggle("collapsed"); + } + }); + }); +} + +/** + * Setup UI handlers for conditional visibility + */ +function setupUIHandlers(generator) { + // Note: Conditional visibility is now handled automatically by UIBuilder.setupConditionalVisibility() + // This function is kept for any additional custom handlers if needed in the future +} + +/** + * Setup download button + */ +function setupDownloadButton(generator) { + const downloadBtn = document.getElementById("download-button"); + if (!downloadBtn) return; + + downloadBtn.addEventListener("click", async () => { + const originalText = downloadBtn.textContent; + downloadBtn.disabled = true; + downloadBtn.textContent = "Generating GIF..."; + + try { + const blob = await generator.exportAsGif((progress, stage) => { + if (stage === "generating") { + const percent = Math.round(progress * 100); + downloadBtn.textContent = `Generating: ${percent}%`; + } else if (stage === "encoding") { + const percent = Math.round(progress * 100); + downloadBtn.textContent = `Encoding: ${percent}%`; + } + }); + + // Download the blob + const link = document.createElement("a"); + link.download = "button-88x31.gif"; + link.href = URL.createObjectURL(blob); + link.click(); + URL.revokeObjectURL(link.href); + + downloadBtn.textContent = originalText; + } catch (error) { + console.error("Error generating GIF:", error); + alert("Error generating GIF. Please try again."); + downloadBtn.textContent = originalText; + } finally { + downloadBtn.disabled = false; + } + }); +} + +/** + * Setup preset buttons + */ +function setupPresets(generator) { + // Random preset + const randomBtn = document.getElementById("preset-random"); + if (randomBtn) { + randomBtn.addEventListener("click", () => { + applyRandomPreset(); + generator.updatePreview(); + }); + } + + // Classic preset + const classicBtn = document.getElementById("preset-classic"); + if (classicBtn) { + classicBtn.addEventListener("click", () => { + applyClassicPreset(); + generator.updatePreview(); + }); + } + + // Modern preset + const modernBtn = document.getElementById("preset-modern"); + if (modernBtn) { + modernBtn.addEventListener("click", () => { + applyModernPreset(); + generator.updatePreview(); + }); + } +} + +/** + * Apply random preset + */ +function applyRandomPreset() { + const randomColor = () => + "#" + + Math.floor(Math.random() * 16777215) + .toString(16) + .padStart(6, "0"); + + // Random background + const bgTypeEl = document.getElementById("bg-type"); + if (bgTypeEl) { + bgTypeEl.value = ["solid", "gradient", "texture"][ + Math.floor(Math.random() * 3) + ]; + bgTypeEl.dispatchEvent(new Event("change")); + } + + setControlValue("bg-color", randomColor()); + setControlValue("gradient-color1", randomColor()); + setControlValue("gradient-color2", randomColor()); + setControlValue("gradient-angle", Math.floor(Math.random() * 360)); + + // Random text colors + setControlValue("text-color", randomColor()); + setControlValue("text-gradient-color1", randomColor()); + setControlValue("text-gradient-color2", randomColor()); + setControlValue("text2-color", randomColor()); + setControlValue("text2-gradient-color1", randomColor()); + setControlValue("text2-gradient-color2", randomColor()); + + // Random border + setControlValue("border-color", randomColor()); + setControlValue("border-width", Math.floor(Math.random() * 6)); +} + +/** + * Apply classic 90s preset + */ +function applyClassicPreset() { + setControlValue("bg-type", "gradient"); + setControlValue("gradient-color1", "#6e6e6eff"); + setControlValue("gradient-color2", "#979797"); + setControlValue("gradient-angle", 90); + + setControlValue("text-color-type", "solid"); + setControlValue("text-color", "#000000"); + setControlValue("text2-color-type", "solid"); + setControlValue("text2-color", "#000000"); + + setControlValue("border-width", 2); + setControlValue("border-color", "#000000"); + setControlValue("border-style", "outset"); + + setControlValue("font-family", "VT323"); + setControlValue("font-family2", "VT323"); + + document.getElementById("bg-type")?.dispatchEvent(new Event("change")); +} + +/** + * Apply modern preset + */ +function applyModernPreset() { + setControlValue("bg-type", "gradient"); + setControlValue("gradient-color1", "#0a0a0a"); + setControlValue("gradient-color2", "#1a0a2e"); + setControlValue("gradient-angle", 135); + + setControlValue("text-color-type", "gradient"); + setControlValue("text-gradient-color1", "#00ffaa"); + setControlValue("text-gradient-color2", "#00ffff"); + setControlValue("text-gradient-angle", 90); + + setControlValue("text2-color-type", "gradient"); + setControlValue("text2-gradient-color1", "#ff00ff"); + setControlValue("text2-gradient-color2", "#ff6600"); + + setControlValue("border-width", 1); + setControlValue("border-color", "#00ffaa"); + setControlValue("border-style", "solid"); + + setControlValue("font-family", "Roboto Mono"); + setControlValue("font-family2", "Roboto Mono"); + + document.getElementById("bg-type")?.dispatchEvent(new Event("change")); + document + .getElementById("text-color-type") + ?.dispatchEvent(new Event("change")); + document + .getElementById("text2-color-type") + ?.dispatchEvent(new Event("change")); +} + +/** + * Helper to set control value + */ +function setControlValue(id, value) { + const el = document.getElementById(id); + if (el) { + if (el.type === "checkbox") { + el.checked = value; + } else { + el.value = value; + } + + // Update value display if it exists + const valueDisplay = document.getElementById(id + "-value"); + if (valueDisplay) { + valueDisplay.textContent = value; + } + } +} + +// Auto-initialize when imported +init(); diff --git a/static/js/button-generator/ui-builder.js b/static/js/button-generator/ui-builder.js new file mode 100644 index 0000000..2a051a4 --- /dev/null +++ b/static/js/button-generator/ui-builder.js @@ -0,0 +1,367 @@ +/** + * UI Builder - Dynamically generates control UI from effect definitions + */ + +export class UIBuilder { + constructor(containerElement) { + this.container = containerElement; + this.controlGroups = new Map(); // category -> { element, controls } + } + + /** + * Build the entire UI from registered effects + * @param {Array<ButtonEffect>} effects - All registered effects + */ + buildUI(effects) { + // Clear existing content + this.container.innerHTML = ''; + this.controlGroups.clear(); + + // Group effects by category + const categorized = this.categorizeEffects(effects); + + // Create control groups for each category + for (const [category, categoryEffects] of categorized) { + this.createControlGroup(category, categoryEffects); + } + } + + /** + * Categorize effects by their category property + * @param {Array<ButtonEffect>} effects + * @returns {Map<string, Array<ButtonEffect>>} + */ + categorizeEffects(effects) { + const categories = new Map(); + + effects.forEach(effect => { + const category = effect.category || 'Other'; + if (!categories.has(category)) { + categories.set(category, []); + } + categories.get(category).push(effect); + }); + + // Sort categories in a logical order + const orderedCategories = new Map(); + const categoryOrder = [ + 'Text Line 1', + 'Text Line 2', + 'Background', + 'Background Animations', + 'Border', + 'Visual Effects', + 'General Effects', + 'Special Effects' + ]; + + categoryOrder.forEach(cat => { + if (categories.has(cat)) { + orderedCategories.set(cat, categories.get(cat)); + } + }); + + // Add any remaining categories + categories.forEach((effects, cat) => { + if (!orderedCategories.has(cat)) { + orderedCategories.set(cat, effects); + } + }); + + return orderedCategories; + } + + /** + * Create a collapsible control group + * @param {string} category - Category name + * @param {Array<ButtonEffect>} effects - Effects in this category + */ + createControlGroup(category, effects) { + const groupDiv = document.createElement('div'); + groupDiv.className = 'control-group'; + + // Create header + const header = document.createElement('h3'); + header.className = 'control-group-header'; + header.innerHTML = ` + <span>${category}</span> + <span class="toggle-icon">−</span> + `; + + // Create content container + const content = document.createElement('div'); + content.className = 'control-group-content'; + + // Add controls for each effect in this category + effects.forEach(effect => { + this.addEffectControls(content, effect); + }); + + // Add click handler for collapsing + header.addEventListener('click', () => { + groupDiv.classList.toggle('collapsed'); + }); + + groupDiv.appendChild(header); + groupDiv.appendChild(content); + this.container.appendChild(groupDiv); + + this.controlGroups.set(category, { element: groupDiv, effects }); + } + + /** + * Add controls for a single effect + * @param {HTMLElement} container - Container to add controls to + * @param {ButtonEffect} effect - Effect to create controls for + */ + addEffectControls(container, effect) { + effect.controls.forEach(control => { + const controlEl = this.createControl(control); + if (controlEl) { + container.appendChild(controlEl); + } + }); + } + + /** + * Create a single control element + * @param {Object} controlDef - Control definition from effect + * @returns {HTMLElement} + */ + createControl(controlDef) { + const { id, type, label, defaultValue, min, max, step, options, showWhen, description } = controlDef; + + switch (type) { + case 'checkbox': + return this.createCheckbox(id, label, defaultValue, showWhen); + + case 'range': + return this.createRange(id, label, defaultValue, min, max, step, description, showWhen); + + case 'color': + return this.createColor(id, label, defaultValue, showWhen); + + case 'select': + return this.createSelect(id, label, defaultValue, options, showWhen); + + case 'text': + return this.createTextInput(id, label, defaultValue); + + default: + console.warn(`Unknown control type: ${type}`); + return null; + } + } + + /** + * Create a checkbox control + */ + createCheckbox(id, label, defaultValue, showWhen) { + const wrapper = document.createElement('label'); + wrapper.className = 'checkbox-label'; + + const input = document.createElement('input'); + input.type = 'checkbox'; + input.id = id; + input.checked = defaultValue || false; + + const span = document.createElement('span'); + span.textContent = label; + + wrapper.appendChild(input); + wrapper.appendChild(span); + + if (showWhen) { + wrapper.style.display = 'none'; + wrapper.dataset.showWhen = showWhen; + } + + return wrapper; + } + + /** + * Create a range slider control + */ + createRange(id, label, defaultValue, min, max, step, description, showWhen) { + const container = document.createElement('div'); + + const labelEl = document.createElement('label'); + labelEl.htmlFor = id; + labelEl.innerHTML = `${label}: <span id="${id}-value">${defaultValue}</span>`; + if (description) { + labelEl.title = description; + } + + const input = document.createElement('input'); + input.type = 'range'; + input.id = id; + input.min = min !== undefined ? min : 0; + input.max = max !== undefined ? max : 100; + input.value = defaultValue !== undefined ? defaultValue : 50; + if (step !== undefined) { + input.step = step; + } + + // Update value display on input + input.addEventListener('input', () => { + const valueDisplay = document.getElementById(`${id}-value`); + if (valueDisplay) { + valueDisplay.textContent = input.value; + } + }); + + container.appendChild(labelEl); + container.appendChild(input); + + if (showWhen) { + container.style.display = 'none'; + container.dataset.showWhen = showWhen; + } + + return container; + } + + /** + * Create a color picker control + */ + createColor(id, label, defaultValue, showWhen) { + const container = document.createElement('div'); + + const labelEl = document.createElement('label'); + labelEl.htmlFor = id; + labelEl.textContent = label; + + const input = document.createElement('input'); + input.type = 'color'; + input.id = id; + input.value = defaultValue || '#ffffff'; + + container.appendChild(labelEl); + container.appendChild(input); + + if (showWhen) { + container.style.display = 'none'; + container.dataset.showWhen = showWhen; + } + + return container; + } + + /** + * Create a select dropdown control + */ + createSelect(id, label, defaultValue, options, showWhen) { + const container = document.createElement('div'); + + const labelEl = document.createElement('label'); + labelEl.htmlFor = id; + labelEl.textContent = label; + + const select = document.createElement('select'); + select.id = id; + + options.forEach(opt => { + const option = document.createElement('option'); + option.value = opt.value; + option.textContent = opt.label; + if (opt.value === defaultValue) { + option.selected = true; + } + select.appendChild(option); + }); + + container.appendChild(labelEl); + container.appendChild(select); + + if (showWhen) { + container.style.display = 'none'; + container.dataset.showWhen = showWhen; + } + + return container; + } + + /** + * Create a text input control + */ + createTextInput(id, label, defaultValue) { + const container = document.createElement('div'); + + const labelEl = document.createElement('label'); + labelEl.htmlFor = id; + labelEl.textContent = label; + + const input = document.createElement('input'); + input.type = 'text'; + input.id = id; + input.value = defaultValue || ''; + input.maxLength = 20; + + container.appendChild(labelEl); + container.appendChild(input); + + return container; + } + + /** + * Setup conditional visibility for controls + * Should be called after all controls are created + */ + setupConditionalVisibility() { + // Find all controls with showWhen attribute + const conditionalControls = this.container.querySelectorAll('[data-show-when]'); + + conditionalControls.forEach(control => { + const triggerControlId = control.dataset.showWhen; + const triggerControl = document.getElementById(triggerControlId); + + if (triggerControl) { + const updateVisibility = () => { + if (triggerControl.type === 'checkbox') { + control.style.display = triggerControl.checked ? 'block' : 'none'; + } else if (triggerControl.tagName === 'SELECT') { + // Get the control ID to determine what value to check for + const controlId = control.querySelector('input, select')?.id; + + // For background controls + if (triggerControlId === 'bg-type') { + if (controlId === 'bg-color') { + control.style.display = triggerControl.value === 'solid' ? 'block' : 'none'; + } else if (controlId && (controlId.startsWith('gradient-') || controlId === 'gradient-angle')) { + control.style.display = triggerControl.value === 'gradient' ? 'block' : 'none'; + } else if (controlId && (controlId.startsWith('texture-') || controlId === 'texture-type' || controlId === 'texture-scale')) { + control.style.display = triggerControl.value === 'texture' ? 'block' : 'none'; + } else if (controlId && (controlId.startsWith('emoji-') || controlId === 'emoji-text')) { + control.style.display = triggerControl.value === 'emoji-wallpaper' ? 'block' : 'none'; + } + } + // For text color controls + else if (triggerControlId === 'text-color-type') { + if (controlId === 'text-color') { + control.style.display = triggerControl.value === 'solid' ? 'block' : 'none'; + } else if (controlId && controlId.startsWith('text-gradient-')) { + control.style.display = triggerControl.value === 'gradient' ? 'block' : 'none'; + } + } else if (triggerControlId === 'text2-color-type') { + if (controlId === 'text2-color') { + control.style.display = triggerControl.value === 'solid' ? 'block' : 'none'; + } else if (controlId && controlId.startsWith('text2-gradient-')) { + control.style.display = triggerControl.value === 'gradient' ? 'block' : 'none'; + } + } else { + // Default: show when any value is selected + control.style.display = triggerControl.value ? 'block' : 'none'; + } + } + }; + + // Initial visibility + updateVisibility(); + + // Update on change + triggerControl.addEventListener('change', updateVisibility); + triggerControl.addEventListener('input', updateVisibility); + } + }); + } +} From c0d6bee9c309c1c060f916e7a7f367ca5eef824f Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 13:20:06 +0000 Subject: [PATCH 29/55] New effects, refactor --- assets/sass/pages/button-generator.scss | 45 ++- content/resources/button-generator/index.md | 6 +- .../effects/background-aurora.js | 191 +++++++++ .../effects/background-bubbles.js | 178 +++++++++ .../effects/background-fire.js | 216 ++++++++++ .../effects/background-starfield.js | 166 ++++++++ static/js/button-generator/effects/border.js | 372 +++++++++++++++--- .../js/button-generator/effects/spin-text.js | 90 +++-- .../js/button-generator/effects/spotlight.js | 18 +- .../button-generator/effects/text-shadow.js | 167 ++++++++ .../button-generator/effects/text-standard.js | 197 +++++----- .../js/button-generator/effects/wave-text.js | 17 +- static/js/button-generator/main.js | 11 + static/js/button-generator/ui-builder.js | 161 +++++++- 14 files changed, 1620 insertions(+), 215 deletions(-) create mode 100644 static/js/button-generator/effects/background-aurora.js create mode 100644 static/js/button-generator/effects/background-bubbles.js create mode 100644 static/js/button-generator/effects/background-fire.js create mode 100644 static/js/button-generator/effects/background-starfield.js create mode 100644 static/js/button-generator/effects/text-shadow.js diff --git a/assets/sass/pages/button-generator.scss b/assets/sass/pages/button-generator.scss index 193610b..17c0710 100644 --- a/assets/sass/pages/button-generator.scss +++ b/assets/sass/pages/button-generator.scss @@ -75,7 +75,6 @@ ); z-index: 90; } - &::before { content: ""; @@ -203,7 +202,7 @@ } label { - display: block; + //display: block; margin-top: 0.75rem; margin-bottom: 0.25rem; font-weight: 500; @@ -438,6 +437,7 @@ input[type="checkbox"] { margin: 0; cursor: pointer; + margin-right: 0.25em; } span { @@ -535,4 +535,45 @@ transform: translateY(0); } } + + // Custom tooltip styles + .control-tooltip { + position: fixed; + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.98) 0%, + rgba(0, 100, 180, 0.98) 100% + ); + color: #fff; + padding: 0.5rem 0.75rem; + border-radius: 4px; + font-size: 0.8rem; + pointer-events: none; + z-index: 10000; + max-width: 250px; + box-shadow: + 0 0 20px rgba(0, 150, 255, 0.5), + 0 4px 12px rgba(0, 0, 0, 0.4), + inset 0 0 20px rgba(0, 150, 255, 0.1); + border: 1px solid rgba(0, 150, 255, 0.6); + opacity: 0; + transition: opacity 0.15s ease; + line-height: 1.4; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + + &.visible { + opacity: 1; + } + + &::before { + content: ""; + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%); + border: 6px solid transparent; + border-bottom-color: rgba(0, 120, 200, 0.98); + filter: drop-shadow(0 -2px 4px rgba(0, 0, 0, 0.2)); + } + } } diff --git a/content/resources/button-generator/index.md b/content/resources/button-generator/index.md index 9f5d5d2..a5cc19e 100644 --- a/content/resources/button-generator/index.md +++ b/content/resources/button-generator/index.md @@ -14,10 +14,14 @@ This supports gif despite the basic `canvas` tag limitation courtesy of [gif.js] Big thanks to [neonaut's 88x31 archive](https://neonaut.neocities.org/cyber/88x31) and everyone who made the buttons that appear there. You should check it out if you need inspiration for your button! +**Important note:** Some effects and animations stack, some don't. Some work better with certain lengths of text or variables depending on text length. Experiment, see what happens. + {{< button-generator >}} --- ### Changelog -- 08/01/2025 - Initial release. +- 07/01/2025 - Initial release. +- 08/01/2025 - Total refactor to be modular, added many more effects. +- 09/01/2025 - Rewrote the bevel inset and outset border code so they look a lot nicer at the corners. Updates throughout so that multibit (emoji!) characters should work. diff --git a/static/js/button-generator/effects/background-aurora.js b/static/js/button-generator/effects/background-aurora.js new file mode 100644 index 0000000..464a107 --- /dev/null +++ b/static/js/button-generator/effects/background-aurora.js @@ -0,0 +1,191 @@ +import { ButtonEffect } from "../effect-base.js"; + +/** + * Aurora/Plasma background effect + * Flowing organic color patterns using layered gradients + */ +export class AuroraEffect extends ButtonEffect { + constructor() { + super({ + id: "bg-aurora", + name: "Aurora", + type: "general", + category: "Background Animations", + renderOrder: 55, + }); + } + + defineControls() { + return [ + { + id: "animate-aurora", + type: "checkbox", + label: "Aurora Effect", + defaultValue: false, + }, + { + id: "aurora-speed", + type: "range", + label: "Flow Speed", + defaultValue: 1, + min: 1, + max: 3, + step: 1, + showWhen: "animate-aurora", + description: "Speed of flowing colors", + }, + { + id: "aurora-intensity", + type: "range", + label: "Intensity", + defaultValue: 0.6, + min: 0.2, + max: 1, + step: 0.1, + showWhen: "animate-aurora", + description: "Brightness and opacity", + }, + { + id: "aurora-complexity", + type: "range", + label: "Complexity", + defaultValue: 3, + min: 2, + max: 6, + step: 1, + showWhen: "animate-aurora", + description: "Number of wave layers", + }, + { + id: "aurora-color-scheme", + type: "select", + label: "Color Scheme", + defaultValue: "northern", + options: [ + { value: "northern", label: "Northern Lights" }, + { value: "purple", label: "Purple Dream" }, + { value: "fire", label: "Fire" }, + { value: "ocean", label: "Ocean" }, + { value: "rainbow", label: "Rainbow" }, + ], + showWhen: "animate-aurora", + description: "Color palette", + }, + ]; + } + + isEnabled(controlValues) { + return controlValues["animate-aurora"] === true; + } + + getColorScheme(scheme, hue) { + switch (scheme) { + case "northern": + return [ + { h: 120, s: 70, l: 50 }, // Green + { h: 160, s: 70, l: 50 }, // Teal + { h: 200, s: 70, l: 50 }, // Blue + ]; + case "purple": + return [ + { h: 270, s: 70, l: 50 }, // Purple + { h: 300, s: 70, l: 50 }, // Magenta + { h: 330, s: 70, l: 50 }, // Pink + ]; + case "fire": + return [ + { h: 0, s: 80, l: 50 }, // Red + { h: 30, s: 80, l: 50 }, // Orange + { h: 50, s: 80, l: 50 }, // Yellow-Orange + ]; + case "ocean": + return [ + { h: 180, s: 70, l: 50 }, // Cyan + { h: 200, s: 70, l: 50 }, // Light Blue + { h: 220, s: 70, l: 50 }, // Blue + ]; + case "rainbow": + return [ + { h: (hue + 0) % 360, s: 70, l: 50 }, + { h: (hue + 120) % 360, s: 70, l: 50 }, + { h: (hue + 240) % 360, s: 70, l: 50 }, + ]; + default: + return [ + { h: 120, s: 70, l: 50 }, + { h: 180, s: 70, l: 50 }, + { h: 240, s: 70, l: 50 }, + ]; + } + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const speed = controlValues["aurora-speed"] || 1; + const intensity = controlValues["aurora-intensity"] || 0.6; + const complexity = controlValues["aurora-complexity"] || 3; + const colorScheme = controlValues["aurora-color-scheme"] || "northern"; + + const time = animState.getPhase(speed); + + // Create flowing hue shift that loops properly (only used for rainbow scheme) + // Convert phase (0 to 2π) to hue degrees (0 to 360) + const hueShift = (time / (Math.PI * 2)) * 360; + const colors = this.getColorScheme(colorScheme, hueShift); + + // Draw multiple overlapping gradients to create aurora effect + context.globalCompositeOperation = "screen"; // Blend mode for aurora effect + + for (let i = 0; i < complexity; i++) { + const phase = time + i * ((Math.PI * 2) / complexity); + + // Calculate wave positions + const wave1X = + renderData.centerX + Math.sin(phase) * renderData.width * 0.5; + const wave1Y = + renderData.centerY + Math.cos(phase * 1.3) * renderData.height * 0.5; + + // Create radial gradient + const gradient = context.createRadialGradient( + wave1X, + wave1Y, + 0, + wave1X, + wave1Y, + renderData.width * 0.8, + ); + + // Pick color based on wave index + const colorIdx = i % colors.length; + const color = colors[colorIdx]; + + const baseOpacity = intensity * 0.3; + + // Rainbow scheme already has hueShift applied in getColorScheme + // Other schemes use their fixed colors + gradient.addColorStop( + 0, + `hsla(${color.h}, ${color.s}%, ${color.l}%, ${baseOpacity})`, + ); + gradient.addColorStop( + 0.5, + `hsla(${color.h}, ${color.s}%, ${color.l}%, ${baseOpacity * 0.5})`, + ); + gradient.addColorStop( + 1, + `hsla(${color.h}, ${color.s}%, ${color.l}%, 0)`, + ); + + context.fillStyle = gradient; + context.fillRect(0, 0, renderData.width, renderData.height); + } + + // Reset composite operation + context.globalCompositeOperation = "source-over"; + } +} + +export function register(generator) { + generator.registerEffect(new AuroraEffect()); +} diff --git a/static/js/button-generator/effects/background-bubbles.js b/static/js/button-generator/effects/background-bubbles.js new file mode 100644 index 0000000..be3fc8f --- /dev/null +++ b/static/js/button-generator/effects/background-bubbles.js @@ -0,0 +1,178 @@ +import { ButtonEffect } from "../effect-base.js"; + +/** + * Bubbles rising background effect + * Floating bubbles that rise with drift + */ +export class BubblesEffect extends ButtonEffect { + constructor() { + super({ + id: "bg-bubbles", + name: "Bubbles", + type: "general", + category: "Background Animations", + renderOrder: 55, + }); + + this.bubbles = []; + this.initialized = false; + } + + defineControls() { + return [ + { + id: "animate-bubbles", + type: "checkbox", + label: "Bubbles Effect", + defaultValue: false, + }, + { + id: "bubble-count", + type: "range", + label: "Bubble Count", + defaultValue: 15, + min: 5, + max: 40, + step: 1, + showWhen: "animate-bubbles", + description: "Number of bubbles", + }, + { + id: "bubble-speed", + type: "range", + label: "Rise Speed", + defaultValue: 1, + min: 0.3, + max: 3, + step: 0.1, + showWhen: "animate-bubbles", + description: "Speed of rising bubbles", + }, + { + id: "bubble-drift", + type: "range", + label: "Drift Amount", + defaultValue: 1, + min: 0, + max: 3, + step: 0.1, + showWhen: "animate-bubbles", + description: "Side-to-side drift", + }, + { + id: "bubble-color", + type: "color", + label: "Bubble Color", + defaultValue: "#4da6ff", + showWhen: "animate-bubbles", + description: "Color of bubbles", + }, + ]; + } + + isEnabled(controlValues) { + return controlValues["animate-bubbles"] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const count = controlValues["bubble-count"] || 15; + const speed = controlValues["bubble-speed"] || 1; + const drift = controlValues["bubble-drift"] || 1; + const bubbleColor = controlValues["bubble-color"] || "#4da6ff"; + + // Initialize bubbles on first frame or count change + if (!this.initialized || this.bubbles.length !== count) { + this.bubbles = []; + for (let i = 0; i < count; i++) { + this.bubbles.push({ + x: Math.random() * renderData.width, + startY: Math.random(), // Store as 0-1 ratio instead of pixel value + size: 3 + Math.random() * 8, + speedMultiplier: 0.5 + Math.random() * 1, + driftPhase: Math.random() * Math.PI * 2, + driftSpeed: 0.5 + Math.random() * 1, + }); + } + this.initialized = true; + } + + // Parse color for gradient + const hexToRgb = (hex) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result + ? { + r: parseInt(result[1], 16), + g: parseInt(result[2], 16), + b: parseInt(result[3], 16), + } + : { r: 77, g: 166, b: 255 }; + }; + + const rgb = hexToRgb(bubbleColor); + + // Draw bubbles + this.bubbles.forEach((bubble) => { + // Calculate Y position based on animation progress for perfect looping + // Each bubble has a different starting offset and speed + const bubbleProgress = + (animState.progress * speed * bubble.speedMultiplier + bubble.startY) % + 1; + + // Convert to pixel position (bubbles rise from bottom to top) + const bubbleY = + renderData.height + bubble.size - bubbleProgress * (renderData.height + bubble.size * 2); + + // Drift side to side + const driftOffset = + Math.sin( + animState.getPhase(bubble.driftSpeed * 0.5) + bubble.driftPhase + ) * + drift * + 2; + const currentX = bubble.x + driftOffset; + + // Draw bubble with gradient for 3D effect + const gradient = context.createRadialGradient( + currentX - bubble.size * 0.3, + bubbleY - bubble.size * 0.3, + 0, + currentX, + bubbleY, + bubble.size + ); + + gradient.addColorStop( + 0, + `rgba(${rgb.r + 40}, ${rgb.g + 40}, ${rgb.b + 40}, 0.6)` + ); + gradient.addColorStop( + 0.6, + `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.4)` + ); + gradient.addColorStop(1, `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.2)`); + + context.fillStyle = gradient; + context.beginPath(); + context.arc(currentX, bubbleY, bubble.size, 0, Math.PI * 2); + context.fill(); + + // Add highlight + context.fillStyle = "rgba(255, 255, 255, 0.4)"; + context.beginPath(); + context.arc( + currentX - bubble.size * 0.3, + bubbleY - bubble.size * 0.3, + bubble.size * 0.3, + 0, + Math.PI * 2 + ); + context.fill(); + }); + } +} + +export function register(generator) { + generator.registerEffect(new BubblesEffect()); +} diff --git a/static/js/button-generator/effects/background-fire.js b/static/js/button-generator/effects/background-fire.js new file mode 100644 index 0000000..e6072d5 --- /dev/null +++ b/static/js/button-generator/effects/background-fire.js @@ -0,0 +1,216 @@ +import { ButtonEffect } from "../effect-base.js"; + +/** + * Fire background effect + * Animated flames rising from bottom using particles + */ +export class FireEffect extends ButtonEffect { + constructor() { + super({ + id: "bg-fire", + name: "Fire", + type: "general", + category: "Background Animations", + renderOrder: 55, + }); + + this.particles = []; + this.initialized = false; + } + + defineControls() { + return [ + { + id: "animate-fire", + type: "checkbox", + label: "Fire Effect", + defaultValue: false, + }, + { + id: "fire-intensity", + type: "range", + label: "Intensity", + defaultValue: 50, + min: 20, + max: 100, + step: 5, + showWhen: "animate-fire", + description: "Number of flame particles", + }, + { + id: "fire-height", + type: "range", + label: "Flame Height", + defaultValue: 0.6, + min: 0.3, + max: 1, + step: 0.1, + showWhen: "animate-fire", + description: "How high flames reach", + }, + { + id: "fire-speed", + type: "range", + label: "Speed", + defaultValue: 1, + min: 0.3, + max: 3, + step: 0.1, + showWhen: "animate-fire", + description: "Speed of rising flames", + }, + { + id: "fire-color-scheme", + type: "select", + label: "Color Scheme", + defaultValue: "normal", + options: [ + { value: "normal", label: "Normal Fire" }, + { value: "blue", label: "Blue Flame" }, + { value: "green", label: "Green Flame" }, + { value: "purple", label: "Purple Flame" }, + { value: "white", label: "White Hot" }, + ], + showWhen: "animate-fire", + description: "Flame color", + }, + ]; + } + + isEnabled(controlValues) { + return controlValues["animate-fire"] === true; + } + + getFireColors(scheme) { + switch (scheme) { + case "normal": + return [ + { r: 255, g: 60, b: 0 }, // Red-orange + { r: 255, g: 140, b: 0 }, // Orange + { r: 255, g: 200, b: 0 }, // Yellow + ]; + case "blue": + return [ + { r: 0, g: 100, b: 255 }, // Blue + { r: 100, g: 180, b: 255 }, // Light blue + { r: 200, g: 230, b: 255 }, // Very light blue + ]; + case "green": + return [ + { r: 0, g: 200, b: 50 }, // Green + { r: 100, g: 255, b: 100 }, // Light green + { r: 200, g: 255, b: 150 }, // Very light green + ]; + case "purple": + return [ + { r: 150, g: 0, b: 255 }, // Purple + { r: 200, g: 100, b: 255 }, // Light purple + { r: 230, g: 180, b: 255 }, // Very light purple + ]; + case "white": + return [ + { r: 255, g: 200, b: 150 }, // Warm white + { r: 255, g: 240, b: 200 }, // Light white + { r: 255, g: 255, b: 255 }, // Pure white + ]; + default: + return [ + { r: 255, g: 60, b: 0 }, + { r: 255, g: 140, b: 0 }, + { r: 255, g: 200, b: 0 }, + ]; + } + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const intensity = controlValues["fire-intensity"] || 50; + const height = controlValues["fire-height"] || 0.6; + const speed = controlValues["fire-speed"] || 1; + const colorScheme = controlValues["fire-color-scheme"] || "normal"; + + const colors = this.getFireColors(colorScheme); + const maxHeight = renderData.height * height; + + // Spawn new particles at the bottom + for (let i = 0; i < intensity / 10; i++) { + this.particles.push({ + x: Math.random() * renderData.width, + y: renderData.height, + vx: (Math.random() - 0.5) * 1.5, + vy: -(2 + Math.random() * 3) * speed, + size: 2 + Math.random() * 6, + life: 1.0, + colorIndex: Math.random(), + }); + } + + // Update and draw particles + this.particles = this.particles.filter((particle) => { + // Update position + particle.x += particle.vx; + particle.y += particle.vy; + + // Add some turbulence + particle.vx += (Math.random() - 0.5) * 0.2; + particle.vy *= 0.98; // Slow down as they rise + + // Fade out based on height and time + const heightRatio = + (renderData.height - particle.y) / renderData.height; + particle.life -= 0.015; + + if (particle.life > 0 && particle.y > renderData.height - maxHeight) { + // Choose color based on life (hotter at bottom, cooler at top) + const colorProgress = 1 - particle.life; + const colorIdx = Math.floor(colorProgress * (colors.length - 1)); + const colorBlend = (colorProgress * (colors.length - 1)) % 1; + + const c1 = colors[Math.min(colorIdx, colors.length - 1)]; + const c2 = colors[Math.min(colorIdx + 1, colors.length - 1)]; + + const r = Math.floor(c1.r + (c2.r - c1.r) * colorBlend); + const g = Math.floor(c1.g + (c2.g - c1.g) * colorBlend); + const b = Math.floor(c1.b + (c2.b - c1.b) * colorBlend); + + // Draw particle with gradient + const gradient = context.createRadialGradient( + particle.x, + particle.y, + 0, + particle.x, + particle.y, + particle.size + ); + + gradient.addColorStop( + 0, + `rgba(${r}, ${g}, ${b}, ${particle.life * 0.8})` + ); + gradient.addColorStop( + 0.5, + `rgba(${r}, ${g}, ${b}, ${particle.life * 0.5})` + ); + gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`); + + context.fillStyle = gradient; + context.beginPath(); + context.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2); + context.fill(); + + return true; + } + return false; + }); + + // Limit particle count + if (this.particles.length > intensity * 5) { + this.particles = this.particles.slice(-intensity * 5); + } + } +} + +export function register(generator) { + generator.registerEffect(new FireEffect()); +} diff --git a/static/js/button-generator/effects/background-starfield.js b/static/js/button-generator/effects/background-starfield.js new file mode 100644 index 0000000..1440d32 --- /dev/null +++ b/static/js/button-generator/effects/background-starfield.js @@ -0,0 +1,166 @@ +import { ButtonEffect } from "../effect-base.js"; + +/** + * Starfield background effect + * Twinkling stars with optional shooting stars + */ +export class StarfieldEffect extends ButtonEffect { + constructor() { + super({ + id: "bg-starfield", + name: "Starfield", + type: "general", + category: "Background Animations", + renderOrder: 55, + }); + + this.stars = []; + this.shootingStars = []; + this.initialized = false; + } + + defineControls() { + return [ + { + id: "animate-starfield", + type: "checkbox", + label: "Starfield Effect", + description: + "This might look a bit different when exported, work in progress!", + defaultValue: false, + }, + { + id: "star-density", + type: "range", + label: "Star Density", + defaultValue: 30, + min: 10, + max: 80, + step: 5, + showWhen: "animate-starfield", + description: "Number of stars", + }, + { + id: "star-twinkle-speed", + type: "range", + label: "Twinkle Speed", + defaultValue: 1, + min: 0.1, + max: 3, + step: 0.1, + showWhen: "animate-starfield", + description: "Speed of twinkling", + }, + { + id: "star-shooting-enabled", + type: "checkbox", + label: "Shooting Stars", + defaultValue: true, + showWhen: "animate-starfield", + description: "Enable shooting stars", + }, + { + id: "star-color", + type: "color", + label: "Star Color", + defaultValue: "#ffffff", + showWhen: "animate-starfield", + description: "Color of stars", + }, + ]; + } + + isEnabled(controlValues) { + return controlValues["animate-starfield"] === true; + } + + apply(context, controlValues, animState, renderData) { + if (!animState) return; + + const density = controlValues["star-density"] || 30; + const twinkleSpeed = controlValues["star-twinkle-speed"] || 1; + const shootingEnabled = controlValues["star-shooting-enabled"] !== false; + const starColor = controlValues["star-color"] || "#ffffff"; + + // Initialize stars on first frame or density change + if (!this.initialized || this.stars.length !== density) { + this.stars = []; + for (let i = 0; i < density; i++) { + this.stars.push({ + x: Math.random() * renderData.width, + y: Math.random() * renderData.height, + size: 0.5 + Math.random() * 1.5, + twinkleOffset: Math.random() * Math.PI * 2, + twinkleSpeed: 0.5 + Math.random() * 1.5, + }); + } + this.initialized = true; + } + + // Draw twinkling stars + this.stars.forEach((star) => { + const twinkle = + Math.sin( + animState.getPhase(twinkleSpeed * star.twinkleSpeed) + + star.twinkleOffset, + ) * + 0.5 + + 0.5; + const opacity = 0.3 + twinkle * 0.7; + + context.fillStyle = starColor; + context.globalAlpha = opacity; + context.beginPath(); + context.arc(star.x, star.y, star.size, 0, Math.PI * 2); + context.fill(); + context.globalAlpha = 1.0; + }); + + // Shooting stars + if (shootingEnabled) { + // Randomly spawn shooting stars + if (Math.random() < 0.02 && this.shootingStars.length < 3) { + this.shootingStars.push({ + x: Math.random() * renderData.width, + y: -10, + vx: (Math.random() - 0.5) * 2, + vy: 3 + Math.random() * 2, + life: 1.0, + }); + } + + // Update and draw shooting stars + this.shootingStars = this.shootingStars.filter((star) => { + star.x += star.vx; + star.y += star.vy; + star.life -= 0.02; + + if (star.life > 0) { + // Draw shooting star trail + const gradient = context.createLinearGradient( + star.x, + star.y, + star.x - star.vx * 5, + star.y - star.vy * 5, + ); + gradient.addColorStop(0, `rgba(255, 255, 255, ${star.life * 0.8})`); + gradient.addColorStop(1, "rgba(255, 255, 255, 0)"); + + context.strokeStyle = gradient; + context.lineWidth = 2; + context.beginPath(); + context.moveTo(star.x, star.y); + context.lineTo(star.x - star.vx * 5, star.y - star.vy * 5); + context.stroke(); + + return true; + } + return false; + }); + } + } +} + +export function register(generator) { + generator.registerEffect(new StarfieldEffect()); +} diff --git a/static/js/button-generator/effects/border.js b/static/js/button-generator/effects/border.js index 79a496f..0ed6029 100644 --- a/static/js/button-generator/effects/border.js +++ b/static/js/button-generator/effects/border.js @@ -1,4 +1,4 @@ -import { ButtonEffect } from '../effect-base.js'; +import { ButtonEffect } from "../effect-base.js"; /** * Border effect @@ -7,65 +7,107 @@ import { ButtonEffect } from '../effect-base.js'; export class BorderEffect extends ButtonEffect { constructor() { super({ - id: 'border', - name: 'Border', - type: 'border', - category: 'Border', - renderOrder: 10 + id: "border", + name: "Border", + type: "border", + category: "Border", + renderOrder: 10, }); } defineControls() { return [ { - id: 'border-width', - type: 'range', - label: 'Border Width', + id: "border-width", + type: "range", + label: "Border Width", defaultValue: 2, min: 0, max: 5, step: 1, - description: 'Width of border in pixels' + description: "Width of border in pixels", }, { - id: 'border-color', - type: 'color', - label: 'Border Color', - defaultValue: '#000000' + id: "border-color", + type: "color", + label: "Border Color", + defaultValue: "#000000", }, { - id: 'border-style', - type: 'select', - label: 'Border Style', - defaultValue: 'solid', + id: "border-style", + type: "select", + label: "Border Style", + defaultValue: "solid", options: [ - { value: 'solid', label: 'Solid' }, - { value: 'inset', label: 'Inset (3D)' }, - { value: 'outset', label: 'Outset (3D)' }, - { value: 'ridge', label: 'Ridge' } - ] - } + { value: "solid", label: "Solid" }, + { value: "dashed", label: "Dashed" }, + { value: "dotted", label: "Dotted" }, + { value: "double", label: "Double" }, + { value: "inset", label: "Inset (3D)" }, + { value: "outset", label: "Outset (3D)" }, + { value: "ridge", label: "Ridge" }, + { value: "rainbow", label: "Rainbow (Animated)" }, + { value: "marching-ants", label: "Marching Ants" }, + { value: "checkerboard", label: "Checkerboard" }, + ], + }, + { + id: "border-rainbow-speed", + type: "range", + label: "Rainbow Speed", + defaultValue: 1, + min: 1, + max: 3, + step: 1, + showWhen: "border-style", + description: "Speed of rainbow animation", + }, + { + id: "border-march-speed", + type: "range", + label: "March Speed", + defaultValue: 1, + min: 1, + max: 3, + step: 1, + showWhen: "border-style", + description: "Speed of marching animation", + }, ]; } isEnabled(controlValues) { - const width = controlValues['border-width'] || 0; + const width = controlValues["border-width"] || 0; return width > 0; } apply(context, controlValues, animState, renderData) { - const width = controlValues['border-width'] || 0; + const width = controlValues["border-width"] || 0; if (width === 0) return; - const color = controlValues['border-color'] || '#000000'; - const style = controlValues['border-style'] || 'solid'; + const color = controlValues["border-color"] || "#000000"; + const style = controlValues["border-style"] || "solid"; - if (style === 'solid') { + if (style === "solid") { this.drawSolidBorder(context, width, color, renderData); - } else if (style === 'inset' || style === 'outset') { - this.draw3DBorder(context, width, style === 'outset', renderData); - } else if (style === 'ridge') { + } else if (style === "dashed") { + this.drawDashedBorder(context, width, color, renderData); + } else if (style === "dotted") { + this.drawDottedBorder(context, width, color, renderData); + } else if (style === "double") { + this.drawDoubleBorder(context, width, color, renderData); + } else if (style === "inset" || style === "outset") { + this.draw3DBorder(context, width, color, style === "outset", renderData); + } else if (style === "ridge") { this.drawRidgeBorder(context, width, renderData); + } else if (style === "rainbow") { + const speed = controlValues["border-rainbow-speed"] || 1; + this.drawRainbowBorder(context, width, animState, speed, renderData); + } else if (style === "marching-ants") { + const speed = controlValues["border-march-speed"] || 1; + this.drawMarchingAntsBorder(context, width, animState, speed, renderData); + } else if (style === "checkerboard") { + this.drawCheckerboardBorder(context, width, color, renderData); } } @@ -79,33 +121,65 @@ export class BorderEffect extends ButtonEffect { width / 2, width / 2, renderData.width - width, - renderData.height - width + renderData.height - width, ); } /** * Draw 3D inset/outset border */ - draw3DBorder(context, width, isOutset, renderData) { - const lightColor = isOutset ? '#ffffff' : '#000000'; - const darkColor = isOutset ? '#000000' : '#ffffff'; + draw3DBorder(context, width, color, isOutset, renderData) { + const w = renderData.width; + const h = renderData.height; + const t = width; - // Top and left (light) - context.strokeStyle = lightColor; - context.lineWidth = width; - context.beginPath(); - context.moveTo(0, renderData.height); - context.lineTo(0, 0); - context.lineTo(renderData.width, 0); - context.stroke(); + const normalized = color.toLowerCase(); + const isPureBlack = normalized === "#000000"; + const isPureWhite = normalized === "#ffffff"; - // Bottom and right (dark) - context.strokeStyle = darkColor; - context.beginPath(); - context.moveTo(renderData.width, 0); - context.lineTo(renderData.width, renderData.height); - context.lineTo(0, renderData.height); - context.stroke(); + let lightColor; + let darkColor; + + if (isPureBlack || isPureWhite) { + lightColor = isOutset ? "#ffffff" : "#000000"; + darkColor = isOutset ? "#000000" : "#ffffff"; + } else { + const lighter = this.adjustColor(color, 0.25); + const darker = this.adjustColor(color, -0.25); + + lightColor = isOutset ? lighter : darker; + darkColor = isOutset ? darker : lighter; + } + + context.fillStyle = lightColor; + context.fillRect(0, 0, w - t, t); + context.fillRect(0, t, t, h - t); + + context.fillStyle = darkColor; + context.fillRect(t, h - t, w - t, t); + context.fillRect(w - t, 0, t, h - t); + + this.drawBevelCorners(context, t, w, h, lightColor, darkColor, isOutset); + } + + drawBevelCorners(ctx, t, w, h, light, dark, isOutset) { + // Top-left corner + ctx.fillStyle = dark; + ctx.beginPath(); + ctx.moveTo(0, h); + ctx.lineTo(t, h); + ctx.lineTo(t, h - t); + ctx.closePath(); + ctx.fill(); + + // Bottom-right corner + ctx.fillStyle = light; + ctx.beginPath(); + ctx.moveTo(w - t, 0); + ctx.lineTo(w - t, t); + ctx.lineTo(w, 0); + ctx.closePath(); + ctx.fill(); } /** @@ -113,24 +187,212 @@ export class BorderEffect extends ButtonEffect { */ drawRidgeBorder(context, width, renderData) { // Outer ridge (light) - context.strokeStyle = '#ffffff'; + context.strokeStyle = "#ffffff"; context.lineWidth = width / 2; context.strokeRect( width / 4, width / 4, renderData.width - width / 2, - renderData.height - width / 2 + renderData.height - width / 2, ); // Inner ridge (dark) - context.strokeStyle = '#000000'; + context.strokeStyle = "#000000"; context.strokeRect( (width * 3) / 4, (width * 3) / 4, renderData.width - width * 1.5, - renderData.height - width * 1.5 + renderData.height - width * 1.5, ); } + + adjustColor(hex, amount) { + // hex: "#rrggbb", amount: -1.0 .. 1.0 + let r = parseInt(hex.slice(1, 3), 16); + let g = parseInt(hex.slice(3, 5), 16); + let b = parseInt(hex.slice(5, 7), 16); + + const adjust = (c) => + Math.min(255, Math.max(0, Math.round(c + amount * 255))); + + r = adjust(r); + g = adjust(g); + b = adjust(b); + + return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; + } + + /** + * Draw dashed border + */ + drawDashedBorder(context, width, color, renderData) { + context.strokeStyle = color; + context.lineWidth = width; + context.setLineDash([6, 3]); // 6px dash, 3px gap + context.strokeRect( + width / 2, + width / 2, + renderData.width - width, + renderData.height - width + ); + context.setLineDash([]); // Reset to solid + } + + /** + * Draw dotted border + */ + drawDottedBorder(context, width, color, renderData) { + context.strokeStyle = color; + context.lineWidth = width; + context.setLineDash([2, 3]); // 2px dot, 3px gap + context.lineCap = "round"; + context.strokeRect( + width / 2, + width / 2, + renderData.width - width, + renderData.height - width + ); + context.setLineDash([]); // Reset to solid + context.lineCap = "butt"; + } + + /** + * Draw double border + */ + drawDoubleBorder(context, width, color, renderData) { + const gap = Math.max(1, Math.floor(width / 3)); + const lineWidth = Math.max(1, Math.floor((width - gap) / 2)); + + context.strokeStyle = color; + context.lineWidth = lineWidth; + + // Outer border + context.strokeRect( + lineWidth / 2, + lineWidth / 2, + renderData.width - lineWidth, + renderData.height - lineWidth + ); + + // Inner border + const innerOffset = lineWidth + gap; + context.strokeRect( + innerOffset + lineWidth / 2, + innerOffset + lineWidth / 2, + renderData.width - innerOffset * 2 - lineWidth, + renderData.height - innerOffset * 2 - lineWidth + ); + } + + /** + * Draw rainbow animated border + */ + drawRainbowBorder(context, width, animState, speed, renderData) { + if (!animState) { + // Fallback to solid if no animation + this.drawSolidBorder(context, width, "#ff0000", renderData); + return; + } + + const hue = (animState.progress * speed * 360) % 360; + const color = `hsl(${hue}, 80%, 50%)`; + + context.strokeStyle = color; + context.lineWidth = width; + context.strokeRect( + width / 2, + width / 2, + renderData.width - width, + renderData.height - width + ); + } + + /** + * Draw marching ants animated border + */ + drawMarchingAntsBorder(context, width, animState, speed, renderData) { + if (!animState) { + // Fallback to dashed if no animation + this.drawDashedBorder(context, width, "#000000", renderData); + return; + } + + // Animate the dash offset using phase for smooth looping + const phase = animState.getPhase(speed); + const dashLength = 9; // 4px dash + 5px gap = 9px total + const offset = (phase / (Math.PI * 2)) * dashLength; + + context.strokeStyle = "#000000"; + context.lineWidth = width; + context.setLineDash([4, 5]); + context.lineDashOffset = -offset; + context.strokeRect( + width / 2, + width / 2, + renderData.width - width, + renderData.height - width + ); + context.setLineDash([]); + context.lineDashOffset = 0; + } + + /** + * Draw checkerboard border + */ + drawCheckerboardBorder(context, width, color, renderData) { + const squareSize = Math.max(2, width); + const w = renderData.width; + const h = renderData.height; + + // Parse the color + const r = parseInt(color.slice(1, 3), 16); + const g = parseInt(color.slice(3, 5), 16); + const b = parseInt(color.slice(5, 7), 16); + + // Create light and dark versions + const darkColor = color; + const lightColor = `rgb(${Math.min(255, r + 60)}, ${Math.min( + 255, + g + 60 + )}, ${Math.min(255, b + 60)})`; + + // Draw checkerboard on all four sides + // Top + for (let x = 0; x < w; x += squareSize) { + for (let y = 0; y < width; y += squareSize) { + const checker = Math.floor(x / squareSize + y / squareSize) % 2; + context.fillStyle = checker === 0 ? darkColor : lightColor; + context.fillRect(x, y, squareSize, squareSize); + } + } + + // Bottom + for (let x = 0; x < w; x += squareSize) { + for (let y = h - width; y < h; y += squareSize) { + const checker = Math.floor(x / squareSize + y / squareSize) % 2; + context.fillStyle = checker === 0 ? darkColor : lightColor; + context.fillRect(x, y, squareSize, squareSize); + } + } + + // Left + for (let x = 0; x < width; x += squareSize) { + for (let y = width; y < h - width; y += squareSize) { + const checker = Math.floor(x / squareSize + y / squareSize) % 2; + context.fillStyle = checker === 0 ? darkColor : lightColor; + context.fillRect(x, y, squareSize, squareSize); + } + } + + // Right + for (let x = w - width; x < w; x += squareSize) { + for (let y = width; y < h - width; y += squareSize) { + const checker = Math.floor(x / squareSize + y / squareSize) % 2; + context.fillStyle = checker === 0 ? darkColor : lightColor; + context.fillRect(x, y, squareSize, squareSize); + } + } + } } // Auto-register effect diff --git a/static/js/button-generator/effects/spin-text.js b/static/js/button-generator/effects/spin-text.js index d009b2b..a075844 100644 --- a/static/js/button-generator/effects/spin-text.js +++ b/static/js/button-generator/effects/spin-text.js @@ -1,4 +1,4 @@ -import { ButtonEffect } from '../effect-base.js'; +import { ButtonEffect } from "../effect-base.js"; /** * Spinning text animation effect @@ -6,61 +6,62 @@ import { ButtonEffect } from '../effect-base.js'; */ export class SpinTextEffect extends ButtonEffect { constructor(textLineNumber = 1) { - const suffix = textLineNumber === 1 ? '' : '2'; + const suffix = textLineNumber === 1 ? "" : "2"; super({ id: `text-spin${suffix}`, name: `Spinning Text ${textLineNumber}`, - type: textLineNumber === 1 ? 'text' : 'text2', - category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2', + type: textLineNumber === 1 ? "text" : "text2", + category: textLineNumber === 1 ? "Text Line 1" : "Text Line 2", renderOrder: 8, // Before wave, after rainbow - textLineNumber: textLineNumber + textLineNumber: textLineNumber, }); this.textLineNumber = textLineNumber; } defineControls() { - const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1; - const suffix = textLineNumber === 1 ? '' : '2'; + const textLineNumber = + this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? "" : "2"; return [ { id: `animate-text-spin${suffix}`, - type: 'checkbox', - label: 'Spinning Animation', - defaultValue: false + type: "checkbox", + label: "Spinning Animation", + defaultValue: false, }, { id: `spin-speed${suffix}`, - type: 'range', - label: 'Spin Speed', + type: "range", + label: "Spin Speed", defaultValue: 1, - min: 0.1, + min: 1, max: 5, - step: 0.1, + step: 1, showWhen: `animate-text-spin${suffix}`, - description: 'Speed of character rotation' + description: "Speed of character rotation", }, { id: `spin-stagger${suffix}`, - type: 'range', - label: 'Spin Stagger', + type: "range", + label: "Spin Stagger", defaultValue: 0.3, min: 0, max: 1, step: 0.1, showWhen: `animate-text-spin${suffix}`, - description: 'Delay between characters' - } + description: "Delay between characters", + }, ]; } isEnabled(controlValues) { - const suffix = this.textLineNumber === 1 ? '' : '2'; + const suffix = this.textLineNumber === 1 ? "" : "2"; return controlValues[`animate-text-spin${suffix}`] === true; } apply(context, controlValues, animState, renderData) { - const suffix = this.textLineNumber === 1 ? '' : '2'; - const text = controlValues[`button-text${suffix}`] || ''; + const suffix = this.textLineNumber === 1 ? "" : "2"; + const text = controlValues[`button-text${suffix}`] || ""; if (!text || !controlValues[`text${suffix}-enabled`]) return; if (!animState) return; @@ -68,21 +69,26 @@ export class SpinTextEffect extends ButtonEffect { const speed = controlValues[`spin-speed${suffix}`] || 1; const stagger = controlValues[`spin-stagger${suffix}`] || 0.3; const fontSize = controlValues[`font-size${suffix}`] || 14; - const fontFamily = controlValues[`font-family${suffix}`] || 'Arial'; - const fontWeight = controlValues[`text${suffix}-bold`] ? 'bold' : 'normal'; - const fontStyle = controlValues[`text${suffix}-italic`] ? 'italic' : 'normal'; + const fontFamily = controlValues[`font-family${suffix}`] || "Arial"; + const fontWeight = controlValues[`text${suffix}-bold`] ? "bold" : "normal"; + const fontStyle = controlValues[`text${suffix}-italic`] + ? "italic" + : "normal"; context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; - context.textAlign = 'center'; - context.textBaseline = 'middle'; + context.textAlign = "center"; + context.textBaseline = "middle"; // Get text color let fillStyle; - const colorType = controlValues[`text${suffix}-color-type`] || 'solid'; - if (colorType === 'gradient') { - const color1 = controlValues[`text${suffix}-gradient-color1`] || '#ffffff'; - const color2 = controlValues[`text${suffix}-gradient-color2`] || '#00ffff'; - const angle = (controlValues[`text${suffix}-gradient-angle`] || 90) * (Math.PI / 180); + const colorType = controlValues[`text${suffix}-color-type`] || "solid"; + if (colorType === "gradient") { + const color1 = + controlValues[`text${suffix}-gradient-color1`] || "#ffffff"; + const color2 = + controlValues[`text${suffix}-gradient-color2`] || "#00ffff"; + const angle = + (controlValues[`text${suffix}-gradient-angle`] || 90) * (Math.PI / 180); const centerX = renderData.centerX; const centerY = renderData.centerY; const x1 = centerX + Math.cos(angle) * 20; @@ -94,7 +100,7 @@ export class SpinTextEffect extends ButtonEffect { gradient.addColorStop(1, color2); fillStyle = gradient; } else { - fillStyle = controlValues[`text${suffix}-color`] || '#ffffff'; + fillStyle = controlValues[`text${suffix}-color`] || "#ffffff"; } // Calculate base position @@ -103,13 +109,24 @@ export class SpinTextEffect extends ButtonEffect { const baseX = (x / 100) * renderData.width; const baseY = (y / 100) * renderData.height; + // Split text into grapheme clusters (handles emojis properly) + // Use Intl.Segmenter if available, otherwise fall back to spread operator + let chars; + if (typeof Intl !== 'undefined' && Intl.Segmenter) { + const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); + chars = Array.from(segmenter.segment(text), s => s.segment); + } else { + // Fallback: spread operator handles basic emoji + chars = [...text]; + } + // Measure total text width for centering const totalWidth = context.measureText(text).width; let currentX = baseX - totalWidth / 2; // Draw each character with rotation - for (let i = 0; i < text.length; i++) { - const char = text[i]; + for (let i = 0; i < chars.length; i++) { + const char = chars[i]; const charWidth = context.measureText(char).width; const charCenterX = currentX + charWidth / 2; @@ -123,7 +140,8 @@ export class SpinTextEffect extends ButtonEffect { // Apply outline if enabled if (controlValues[`text${suffix}-outline`]) { - context.strokeStyle = controlValues[`text${suffix}-outline-color`] || '#000000'; + context.strokeStyle = + controlValues[`text${suffix}-outline-color`] || "#000000"; context.lineWidth = 2; context.strokeText(char, 0, 0); } diff --git a/static/js/button-generator/effects/spotlight.js b/static/js/button-generator/effects/spotlight.js index b1185e7..cfd79fb 100644 --- a/static/js/button-generator/effects/spotlight.js +++ b/static/js/button-generator/effects/spotlight.js @@ -70,10 +70,10 @@ export class SpotlightEffect extends ButtonEffect { id: "spotlight-darkness", type: "range", label: "Darkness", - defaultValue: 0.5, + defaultValue: 1, min: 0, max: 1, - step: 0.05, + step: 0.1, showWhen: "animate-spotlight", description: "How dark the non-spotlight area should be", }, @@ -84,9 +84,9 @@ export class SpotlightEffect extends ButtonEffect { type: "range", label: "Movement Speed", defaultValue: 1, - min: 0.1, + min: 1, max: 3, - step: 0.1, + step: 1, showWhen: "animate-spotlight", description: "Speed of spotlight movement", }, @@ -152,16 +152,6 @@ export class SpotlightEffect extends ButtonEffect { context.fill(); } - /** - * Optional: Override canApply for more complex logic - * By default, it just checks isEnabled() - */ - canApply(controlValues) { - // Example: Only apply if text is also enabled - const textEnabled = controlValues["textEnabled"]; - return this.isEnabled(controlValues) && textEnabled; - } - /** * Optional: Add helper methods for your effect */ diff --git a/static/js/button-generator/effects/text-shadow.js b/static/js/button-generator/effects/text-shadow.js new file mode 100644 index 0000000..3daa287 --- /dev/null +++ b/static/js/button-generator/effects/text-shadow.js @@ -0,0 +1,167 @@ +import { ButtonEffect } from "../effect-base.js"; + +/** + * Text Drop Shadow Effect + * Renders text with a drop shadow underneath + * This draws the shadow first, then standard text rendering draws on top + */ +export class TextShadowEffect extends ButtonEffect { + constructor(textLineNumber = 1) { + const suffix = textLineNumber === 1 ? "" : "2"; + super({ + id: `text-shadow${suffix}`, + name: `Drop Shadow ${textLineNumber}`, + type: textLineNumber === 1 ? "text" : "text2", + category: textLineNumber === 1 ? "Text Line 1" : "Text Line 2", + renderOrder: 19, // Before standard text (20), so shadow draws first + textLineNumber: textLineNumber, + }); + this.textLineNumber = textLineNumber; + } + + defineControls() { + const textLineNumber = + this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? "" : "2"; + + return [ + { + id: `text${suffix}-shadow-enabled`, + type: "checkbox", + label: "Drop Shadow", + defaultValue: false, + description: + "Add drop shadow to text - Not compatible with other text effects!!", + }, + { + id: `text${suffix}-shadow-color`, + type: "color", + label: "Shadow Color", + defaultValue: "#000000", + showWhen: `text${suffix}-shadow-enabled`, + description: "Color of the shadow", + }, + { + id: `text${suffix}-shadow-blur`, + type: "range", + label: "Shadow Blur", + defaultValue: 4, + min: 0, + max: 10, + step: 1, + showWhen: `text${suffix}-shadow-enabled`, + description: "Blur radius of shadow", + }, + { + id: `text${suffix}-shadow-offset-x`, + type: "range", + label: "Shadow X Offset", + defaultValue: 2, + min: -10, + max: 10, + step: 1, + showWhen: `text${suffix}-shadow-enabled`, + description: "Horizontal shadow offset", + }, + { + id: `text${suffix}-shadow-offset-y`, + type: "range", + label: "Shadow Y Offset", + defaultValue: 2, + min: -10, + max: 10, + step: 1, + showWhen: `text${suffix}-shadow-enabled`, + description: "Vertical shadow offset", + }, + { + id: `text${suffix}-shadow-opacity`, + type: "range", + label: "Shadow Opacity", + defaultValue: 0.8, + min: 0, + max: 1, + step: 0.1, + showWhen: `text${suffix}-shadow-enabled`, + description: "Opacity of the shadow", + }, + ]; + } + + isEnabled(controlValues) { + const suffix = this.textLineNumber === 1 ? "" : "2"; + const textEnabled = controlValues[`text${suffix}-enabled`]; + const shadowEnabled = controlValues[`text${suffix}-shadow-enabled`]; + return textEnabled && shadowEnabled; + } + + apply(context, controlValues, animState, renderData) { + const suffix = this.textLineNumber === 1 ? "" : "2"; + + const text = controlValues[`button-text${suffix}`] || ""; + if (!text) return; + + // Get shadow settings + const shadowColor = + controlValues[`text${suffix}-shadow-color`] || "#000000"; + const shadowBlur = controlValues[`text${suffix}-shadow-blur`] || 4; + const shadowOffsetX = controlValues[`text${suffix}-shadow-offset-x`] || 2; + const shadowOffsetY = controlValues[`text${suffix}-shadow-offset-y`] || 2; + const shadowOpacity = controlValues[`text${suffix}-shadow-opacity`] || 0.8; + + // Get text rendering settings + const fontSize = controlValues[`font-size${suffix}`] || 14; + const fontFamily = controlValues[`font-family${suffix}`] || "Arial"; + const fontWeight = controlValues[`text${suffix}-bold`] ? "bold" : "normal"; + const fontStyle = controlValues[`text${suffix}-italic`] + ? "italic" + : "normal"; + const textX = (controlValues[`text${suffix}-x`] || 50) / 100; + const textY = (controlValues[`text${suffix}-y`] || 50) / 100; + + // Convert hex to rgba + const hexToRgba = (hex, alpha) => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (result) { + const r = parseInt(result[1], 16); + const g = parseInt(result[2], 16); + const b = parseInt(result[3], 16); + return `rgba(${r}, ${g}, ${b}, ${alpha})`; + } + return `rgba(0, 0, 0, ${alpha})`; + }; + + // Set up text rendering + context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; + context.textAlign = "center"; + context.textBaseline = "middle"; + + // Calculate text position + const x = renderData.width * textX; + const y = renderData.height * textY; + + // Draw the shadow using the shadow API + // This will create a shadow underneath whatever we draw + context.shadowColor = hexToRgba(shadowColor, shadowOpacity); + context.shadowBlur = shadowBlur; + context.shadowOffsetX = shadowOffsetX; + context.shadowOffsetY = shadowOffsetY; + + // Draw a solid shadow by filling with the shadow color + // The shadow API will create the blur effect + context.fillStyle = hexToRgba(shadowColor, shadowOpacity); + context.fillText(text, x, y); + + // Reset shadow for subsequent renders + context.shadowColor = "transparent"; + context.shadowBlur = 0; + context.shadowOffsetX = 0; + context.shadowOffsetY = 0; + } +} + +// Export two instances for text line 1 and text line 2 +export function register(generator) { + generator.registerEffect(new TextShadowEffect(1)); + generator.registerEffect(new TextShadowEffect(2)); +} diff --git a/static/js/button-generator/effects/text-standard.js b/static/js/button-generator/effects/text-standard.js index 9caaf58..fb90f87 100644 --- a/static/js/button-generator/effects/text-standard.js +++ b/static/js/button-generator/effects/text-standard.js @@ -1,4 +1,4 @@ -import { ButtonEffect } from '../effect-base.js'; +import { ButtonEffect } from "../effect-base.js"; /** * Standard text rendering effect @@ -6,180 +6,184 @@ import { ButtonEffect } from '../effect-base.js'; */ export class StandardTextEffect extends ButtonEffect { constructor(textLineNumber = 1) { - const suffix = textLineNumber === 1 ? '' : '2'; + const suffix = textLineNumber === 1 ? "" : "2"; super({ id: `text-standard${suffix}`, name: `Standard Text ${textLineNumber}`, - type: textLineNumber === 1 ? 'text' : 'text2', - category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2', + type: textLineNumber === 1 ? "text" : "text2", + category: textLineNumber === 1 ? "Text Line 1" : "Text Line 2", renderOrder: 20, // After animations - textLineNumber: textLineNumber // Pass through config so defineControls can access it + textLineNumber: textLineNumber, // Pass through config so defineControls can access it }); this.textLineNumber = textLineNumber; } defineControls() { // Access from config since this is called before constructor completes - const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1; - const suffix = textLineNumber === 1 ? '' : '2'; + const textLineNumber = + this.textLineNumber || this.config?.textLineNumber || 1; + const suffix = textLineNumber === 1 ? "" : "2"; return [ { id: `button-text${suffix}`, - type: 'text', + type: "text", label: `Text Line ${textLineNumber}`, - defaultValue: textLineNumber === 1 ? 'RITUAL.SH' : '' - }, - { - id: `text${suffix}-enabled`, - type: 'checkbox', - label: `Enable Text Line ${textLineNumber}`, - defaultValue: textLineNumber === 1 + defaultValue: textLineNumber === 1 ? "RITUAL.SH" : "", }, { id: `font-size${suffix}`, - type: 'range', - label: 'Font Size', + type: "range", + label: "Font Size", min: 6, max: 24, - defaultValue: textLineNumber === 1 ? 14 : 12 + defaultValue: textLineNumber === 1 ? 14 : 12, }, { id: `text${suffix}-x`, - type: 'range', - label: 'Horizontal Position', + type: "range", + label: "Horizontal Position", min: 0, max: 100, defaultValue: 50, - description: 'Percentage from left' + description: "Percentage from left", }, { id: `text${suffix}-y`, - type: 'range', - label: 'Vertical Position', + type: "range", + label: "Vertical Position", min: 0, max: 100, defaultValue: textLineNumber === 1 ? 35 : 65, - description: 'Percentage from top' + description: "Percentage from top", }, { id: `text${suffix}-color-type`, - type: 'select', - label: 'Color Type', - defaultValue: 'solid', + type: "select", + label: "Color Type", + defaultValue: "solid", options: [ - { value: 'solid', label: 'Solid Color' }, - { value: 'gradient', label: 'Gradient' } - ] + { value: "solid", label: "Solid Color" }, + { value: "gradient", label: "Gradient" }, + ], }, { id: `text${suffix}-color`, - type: 'color', - label: 'Text Color', - defaultValue: '#ffffff', - showWhen: `text${suffix}-color-type` + type: "color", + label: "Text Color", + defaultValue: "#ffffff", + showWhen: `text${suffix}-color-type`, }, { id: `text${suffix}-gradient-color1`, - type: 'color', - label: 'Gradient Color 1', - defaultValue: '#ffffff', - showWhen: `text${suffix}-color-type` + type: "color", + label: "Gradient Color 1", + defaultValue: "#ffffff", + showWhen: `text${suffix}-color-type`, }, { id: `text${suffix}-gradient-color2`, - type: 'color', - label: 'Gradient Color 2', - defaultValue: '#00ffff', - showWhen: `text${suffix}-color-type` + type: "color", + label: "Gradient Color 2", + defaultValue: "#00ffff", + showWhen: `text${suffix}-color-type`, }, { id: `text${suffix}-gradient-angle`, - type: 'range', - label: 'Gradient Angle', + type: "range", + label: "Gradient Angle", min: 0, max: 360, defaultValue: 0, - showWhen: `text${suffix}-color-type` + showWhen: `text${suffix}-color-type`, }, { id: `text${suffix}-outline`, - type: 'checkbox', - label: 'Outline', - defaultValue: false + type: "checkbox", + label: "Outline", + defaultValue: false, }, { id: `outline${suffix}-color`, - type: 'color', - label: 'Outline Color', - defaultValue: '#000000', - showWhen: `text${suffix}-outline` + type: "color", + label: "Outline Color", + defaultValue: "#000000", + showWhen: `text${suffix}-outline`, }, { id: `font-family${suffix}`, - type: 'select', - label: 'Font', - defaultValue: 'Lato', + type: "select", + label: "Font", + defaultValue: "Lato", options: [ - { value: 'Lato', label: 'Lato' }, - { value: 'Roboto', label: 'Roboto' }, - { value: 'Open Sans', label: 'Open Sans' }, - { value: 'Montserrat', label: 'Montserrat' }, - { value: 'Oswald', label: 'Oswald' }, - { value: 'Bebas Neue', label: 'Bebas Neue' }, - { value: 'Roboto Mono', label: 'Roboto Mono' }, - { value: 'VT323', label: 'VT323' }, - { value: 'Press Start 2P', label: 'Press Start 2P' }, - { value: 'DSEG7-Classic', label: 'DSEG7' } - ] + { value: "Lato", label: "Lato" }, + { value: "Roboto", label: "Roboto" }, + { value: "Open Sans", label: "Open Sans" }, + { value: "Montserrat", label: "Montserrat" }, + { value: "Oswald", label: "Oswald" }, + { value: "Bebas Neue", label: "Bebas Neue" }, + { value: "Roboto Mono", label: "Roboto Mono" }, + { value: "VT323", label: "VT323" }, + { value: "Press Start 2P", label: "Press Start 2P" }, + { value: "DSEG7-Classic", label: "DSEG7" }, + ], }, { id: `font-bold${suffix}`, - type: 'checkbox', - label: 'Bold', - defaultValue: false + type: "checkbox", + label: "Bold", + defaultValue: false, }, { id: `font-italic${suffix}`, - type: 'checkbox', - label: 'Italic', - defaultValue: false - } + type: "checkbox", + label: "Italic", + defaultValue: false, + }, ]; } isEnabled(controlValues) { - const suffix = this.textLineNumber === 1 ? '' : '2'; + const suffix = this.textLineNumber === 1 ? "" : "2"; const text = controlValues[`button-text${suffix}`]; - const enabled = controlValues[`text${suffix}-enabled`]; - // Only render if text exists, is enabled, and no animations are active on this text + // Only render if text exists and no animations are active on this text const waveActive = controlValues[`animate-text-wave${suffix}`]; const rainbowActive = controlValues[`animate-text-rainbow${suffix}`]; + const spinActive = controlValues[`animate-text-spin${suffix}`]; - return text && enabled && !waveActive && !rainbowActive; + return text && text.trim() !== "" && !waveActive && !rainbowActive && !spinActive; } apply(context, controlValues, animState, renderData) { - const suffix = this.textLineNumber === 1 ? '' : '2'; + const suffix = this.textLineNumber === 1 ? "" : "2"; const text = controlValues[`button-text${suffix}`]; if (!text) return; const fontSize = controlValues[`font-size${suffix}`] || 12; - const fontWeight = controlValues[`font-bold${suffix}`] ? 'bold' : 'normal'; - const fontStyle = controlValues[`font-italic${suffix}`] ? 'italic' : 'normal'; - const fontFamily = controlValues[`font-family${suffix}`] || 'Arial'; + const fontWeight = controlValues[`font-bold${suffix}`] ? "bold" : "normal"; + const fontStyle = controlValues[`font-italic${suffix}`] + ? "italic" + : "normal"; + const fontFamily = controlValues[`font-family${suffix}`] || "Arial"; const x = (controlValues[`text${suffix}-x`] / 100) * renderData.width; const y = (controlValues[`text${suffix}-y`] / 100) * renderData.height; // Set font context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; - context.textAlign = 'center'; - context.textBaseline = 'middle'; + context.textAlign = "center"; + context.textBaseline = "middle"; // Get colors - const colors = this.getTextColors(context, controlValues, text, x, y, fontSize); + const colors = this.getTextColors( + context, + controlValues, + text, + x, + y, + fontSize, + ); // Draw outline if enabled if (controlValues[`text${suffix}-outline`]) { @@ -197,17 +201,18 @@ export class StandardTextEffect extends ButtonEffect { * Get text colors (solid or gradient) */ getTextColors(context, controlValues, text, x, y, fontSize) { - const suffix = this.textLineNumber === 1 ? '' : '2'; - const colorType = controlValues[`text${suffix}-color-type`] || 'solid'; + const suffix = this.textLineNumber === 1 ? "" : "2"; + const colorType = controlValues[`text${suffix}-color-type`] || "solid"; let fillStyle, strokeStyle; - if (colorType === 'solid') { - fillStyle = controlValues[`text${suffix}-color`] || '#ffffff'; - strokeStyle = controlValues[`outline${suffix}-color`] || '#000000'; + if (colorType === "solid") { + fillStyle = controlValues[`text${suffix}-color`] || "#ffffff"; + strokeStyle = controlValues[`outline${suffix}-color`] || "#000000"; } else { // Gradient - const angle = (controlValues[`text${suffix}-gradient-angle`] || 0) * (Math.PI / 180); + const angle = + (controlValues[`text${suffix}-gradient-angle`] || 0) * (Math.PI / 180); const textWidth = context.measureText(text).width; const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2; const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2; @@ -215,10 +220,16 @@ export class StandardTextEffect extends ButtonEffect { const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2; const gradient = context.createLinearGradient(x1, y1, x2, y2); - gradient.addColorStop(0, controlValues[`text${suffix}-gradient-color1`] || '#ffffff'); - gradient.addColorStop(1, controlValues[`text${suffix}-gradient-color2`] || '#00ffff'); + gradient.addColorStop( + 0, + controlValues[`text${suffix}-gradient-color1`] || "#ffffff", + ); + gradient.addColorStop( + 1, + controlValues[`text${suffix}-gradient-color2`] || "#00ffff", + ); fillStyle = gradient; - strokeStyle = controlValues[`outline${suffix}-color`] || '#000000'; + strokeStyle = controlValues[`outline${suffix}-color`] || "#000000"; } return { fillStyle, strokeStyle }; diff --git a/static/js/button-generator/effects/wave-text.js b/static/js/button-generator/effects/wave-text.js index cf36211..b394c61 100644 --- a/static/js/button-generator/effects/wave-text.js +++ b/static/js/button-generator/effects/wave-text.js @@ -87,18 +87,29 @@ export class WaveTextEffect extends ButtonEffect { // Get colors const colors = this.getTextColors(context, controlValues, text, baseX, baseY, fontSize, animState); + // Split text into grapheme clusters (handles emojis properly) + // Use Intl.Segmenter if available, otherwise fall back to spread operator + let chars; + if (typeof Intl !== 'undefined' && Intl.Segmenter) { + const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); + chars = Array.from(segmenter.segment(text), s => s.segment); + } else { + // Fallback: spread operator handles basic emoji + chars = [...text]; + } + // Measure total width for centering const totalWidth = context.measureText(text).width; let currentX = baseX - totalWidth / 2; // Draw each character with wave offset - for (let i = 0; i < text.length; i++) { - const char = text[i]; + for (let i = 0; i < chars.length; i++) { + const char = chars[i]; const charWidth = context.measureText(char).width; // Calculate wave offset for this character const phase = animState.getPhase(speed); - const charOffset = i / text.length; + const charOffset = i / chars.length; const waveY = Math.sin(phase + charOffset * Math.PI * 2) * amplitude; const charX = currentX + charWidth / 2; diff --git a/static/js/button-generator/main.js b/static/js/button-generator/main.js index 805edef..51233e8 100644 --- a/static/js/button-generator/main.js +++ b/static/js/button-generator/main.js @@ -15,8 +15,13 @@ import * as textureBg from "./effects/background-texture.js"; import * as emojiWallpaper from "./effects/background-emoji-wallpaper.js"; import * as rainbowBg from "./effects/background-rainbow.js"; import * as rain from "./effects/background-rain.js"; +import * as starfield from "./effects/background-starfield.js"; +//import * as bubbles from "./effects/background-bubbles.js"; +import * as aurora from "./effects/background-aurora.js"; +import * as fire from "./effects/background-fire.js"; import * as border from "./effects/border.js"; import * as standardText from "./effects/text-standard.js"; +import * as textShadow from "./effects/text-shadow.js"; import * as waveText from "./effects/wave-text.js"; import * as rainbowText from "./effects/rainbow-text.js"; import * as spinText from "./effects/spin-text.js"; @@ -63,6 +68,7 @@ async function setupApp() { fps: 20, duration: 2, fonts: [ + "Arial", "Lato", "Roboto", "Open Sans", @@ -84,8 +90,13 @@ async function setupApp() { emojiWallpaper.register(generator); rainbowBg.register(generator); rain.register(generator); + starfield.register(generator); + //bubbles.register(generator); + aurora.register(generator); + fire.register(generator); border.register(generator); standardText.register(generator); + textShadow.register(generator); waveText.register(generator); rainbowText.register(generator); spinText.register(generator); diff --git a/static/js/button-generator/ui-builder.js b/static/js/button-generator/ui-builder.js index 2a051a4..00ce430 100644 --- a/static/js/button-generator/ui-builder.js +++ b/static/js/button-generator/ui-builder.js @@ -6,6 +6,120 @@ export class UIBuilder { constructor(containerElement) { this.container = containerElement; this.controlGroups = new Map(); // category -> { element, controls } + this.tooltip = null; + this.tooltipTimeout = null; + this.setupTooltip(); + } + + /** + * Create and setup the tooltip element + */ + setupTooltip() { + // Wait for DOM to be ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + this.createTooltipElement(); + }); + } else { + this.createTooltipElement(); + } + } + + createTooltipElement() { + this.tooltip = document.createElement('div'); + this.tooltip.className = 'control-tooltip'; + this.tooltip.style.cssText = ` + position: fixed; + background: linear-gradient(135deg, rgba(0, 120, 200, 0.98) 0%, rgba(0, 100, 180, 0.98) 100%); + color: #fff; + padding: 0.5rem 0.75rem; + border-radius: 4px; + font-size: 0.8rem; + pointer-events: none; + z-index: 10000; + max-width: 250px; + box-shadow: 0 0 20px rgba(0, 150, 255, 0.5), 0 4px 12px rgba(0, 0, 0, 0.4); + border: 1px solid rgba(0, 150, 255, 0.6); + opacity: 0; + transition: opacity 0.15s ease; + line-height: 1.4; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); + `; + document.body.appendChild(this.tooltip); + } + + /** + * Show tooltip for an element + */ + showTooltip(element, text) { + if (!text || !this.tooltip) return; + + clearTimeout(this.tooltipTimeout); + + this.tooltip.textContent = text; + this.tooltip.style.opacity = '1'; + + // Position tooltip above the element + const rect = element.getBoundingClientRect(); + + // Set initial position to measure + this.tooltip.style.left = '0px'; + this.tooltip.style.top = '0px'; + this.tooltip.style.visibility = 'hidden'; + this.tooltip.style.display = 'block'; + + const tooltipRect = this.tooltip.getBoundingClientRect(); + + this.tooltip.style.visibility = 'visible'; + + let left = rect.left + rect.width / 2 - tooltipRect.width / 2; + let top = rect.top - tooltipRect.height - 10; + + // Keep tooltip on screen + const padding = 10; + if (left < padding) left = padding; + if (left + tooltipRect.width > window.innerWidth - padding) { + left = window.innerWidth - tooltipRect.width - padding; + } + if (top < padding) { + top = rect.bottom + 10; + } + + this.tooltip.style.left = `${left}px`; + this.tooltip.style.top = `${top}px`; + } + + /** + * Hide tooltip + */ + hideTooltip() { + if (!this.tooltip) return; + clearTimeout(this.tooltipTimeout); + this.tooltipTimeout = setTimeout(() => { + this.tooltip.style.opacity = '0'; + }, 100); + } + + /** + * Add tooltip handlers to an element + */ + addTooltipHandlers(element, description) { + if (!description) return; + + element.addEventListener('mouseenter', () => { + this.showTooltip(element, description); + }); + + element.addEventListener('mouseleave', () => { + this.hideTooltip(); + }); + + element.addEventListener('mousemove', () => { + // Update position on mouse move for better following + if (this.tooltip && this.tooltip.style.opacity === '1') { + this.showTooltip(element, description); + } + }); } /** @@ -133,19 +247,19 @@ export class UIBuilder { switch (type) { case 'checkbox': - return this.createCheckbox(id, label, defaultValue, showWhen); + return this.createCheckbox(id, label, defaultValue, showWhen, description); case 'range': return this.createRange(id, label, defaultValue, min, max, step, description, showWhen); case 'color': - return this.createColor(id, label, defaultValue, showWhen); + return this.createColor(id, label, defaultValue, showWhen, description); case 'select': - return this.createSelect(id, label, defaultValue, options, showWhen); + return this.createSelect(id, label, defaultValue, options, showWhen, description); case 'text': - return this.createTextInput(id, label, defaultValue); + return this.createTextInput(id, label, defaultValue, showWhen, description); default: console.warn(`Unknown control type: ${type}`); @@ -156,7 +270,7 @@ export class UIBuilder { /** * Create a checkbox control */ - createCheckbox(id, label, defaultValue, showWhen) { + createCheckbox(id, label, defaultValue, showWhen, description) { const wrapper = document.createElement('label'); wrapper.className = 'checkbox-label'; @@ -176,6 +290,9 @@ export class UIBuilder { wrapper.dataset.showWhen = showWhen; } + // Add tooltip handlers to the label wrapper + this.addTooltipHandlers(wrapper, description); + return wrapper; } @@ -188,9 +305,6 @@ export class UIBuilder { const labelEl = document.createElement('label'); labelEl.htmlFor = id; labelEl.innerHTML = `${label}: <span id="${id}-value">${defaultValue}</span>`; - if (description) { - labelEl.title = description; - } const input = document.createElement('input'); input.type = 'range'; @@ -218,13 +332,16 @@ export class UIBuilder { container.dataset.showWhen = showWhen; } + // Add tooltip handlers to the label + this.addTooltipHandlers(labelEl, description); + return container; } /** * Create a color picker control */ - createColor(id, label, defaultValue, showWhen) { + createColor(id, label, defaultValue, showWhen, description) { const container = document.createElement('div'); const labelEl = document.createElement('label'); @@ -244,13 +361,16 @@ export class UIBuilder { container.dataset.showWhen = showWhen; } + // Add tooltip handlers to the label + this.addTooltipHandlers(labelEl, description); + return container; } /** * Create a select dropdown control */ - createSelect(id, label, defaultValue, options, showWhen) { + createSelect(id, label, defaultValue, options, showWhen, description) { const container = document.createElement('div'); const labelEl = document.createElement('label'); @@ -278,13 +398,16 @@ export class UIBuilder { container.dataset.showWhen = showWhen; } + // Add tooltip handlers to the label + this.addTooltipHandlers(labelEl, description); + return container; } /** * Create a text input control */ - createTextInput(id, label, defaultValue) { + createTextInput(id, label, defaultValue, showWhen, description) { const container = document.createElement('div'); const labelEl = document.createElement('label'); @@ -300,6 +423,14 @@ export class UIBuilder { container.appendChild(labelEl); container.appendChild(input); + if (showWhen) { + container.style.display = 'none'; + container.dataset.showWhen = showWhen; + } + + // Add tooltip handlers to the label + this.addTooltipHandlers(labelEl, description); + return container; } @@ -348,6 +479,14 @@ export class UIBuilder { } else if (controlId && controlId.startsWith('text2-gradient-')) { control.style.display = triggerControl.value === 'gradient' ? 'block' : 'none'; } + } + // For border style controls + else if (triggerControlId === 'border-style') { + if (controlId === 'border-rainbow-speed') { + control.style.display = triggerControl.value === 'rainbow' ? 'block' : 'none'; + } else if (controlId === 'border-march-speed') { + control.style.display = triggerControl.value === 'marching-ants' ? 'block' : 'none'; + } } else { // Default: show when any value is selected control.style.display = triggerControl.value ? 'block' : 'none'; From c82cfa1d23b46352e563234f9e1abb6169dd4240 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 13:27:32 +0000 Subject: [PATCH 30/55] Fixing check for field that was removed --- static/js/button-generator/debug-helper.js | 1 - static/js/button-generator/effects/rainbow-text.js | 3 +-- static/js/button-generator/effects/spin-text.js | 2 +- static/js/button-generator/effects/text-shadow.js | 4 ++-- static/js/button-generator/effects/wave-text.js | 3 +-- 5 files changed, 5 insertions(+), 8 deletions(-) diff --git a/static/js/button-generator/debug-helper.js b/static/js/button-generator/debug-helper.js index ae4e97d..8368296 100644 --- a/static/js/button-generator/debug-helper.js +++ b/static/js/button-generator/debug-helper.js @@ -7,7 +7,6 @@ export function debugControlValues(generator) { // Text controls console.log('Text controls:'); console.log(' button-text:', values['button-text']); - console.log(' text-enabled:', values['text-enabled']); console.log(' font-size:', values['font-size']); console.log(' text-x:', values['text-x']); console.log(' text-y:', values['text-y']); diff --git a/static/js/button-generator/effects/rainbow-text.js b/static/js/button-generator/effects/rainbow-text.js index e4fa148..5431dae 100644 --- a/static/js/button-generator/effects/rainbow-text.js +++ b/static/js/button-generator/effects/rainbow-text.js @@ -54,8 +54,7 @@ export class RainbowTextEffect extends ButtonEffect { // Get text configuration const text = controlValues[`button-text${suffix}`] || ''; - const enabled = controlValues[`text${suffix}-enabled`]; - if (!text || !enabled) return; + if (!text || text.trim() === '') return; // Check if wave is also enabled - if so, skip (wave will handle rainbow) if (controlValues[`animate-text-wave${suffix}`]) return; diff --git a/static/js/button-generator/effects/spin-text.js b/static/js/button-generator/effects/spin-text.js index a075844..7c4b3ef 100644 --- a/static/js/button-generator/effects/spin-text.js +++ b/static/js/button-generator/effects/spin-text.js @@ -63,7 +63,7 @@ export class SpinTextEffect extends ButtonEffect { const suffix = this.textLineNumber === 1 ? "" : "2"; const text = controlValues[`button-text${suffix}`] || ""; - if (!text || !controlValues[`text${suffix}-enabled`]) return; + if (!text || text.trim() === '') return; if (!animState) return; const speed = controlValues[`spin-speed${suffix}`] || 1; diff --git a/static/js/button-generator/effects/text-shadow.js b/static/js/button-generator/effects/text-shadow.js index 3daa287..1d7a882 100644 --- a/static/js/button-generator/effects/text-shadow.js +++ b/static/js/button-generator/effects/text-shadow.js @@ -90,9 +90,9 @@ export class TextShadowEffect extends ButtonEffect { isEnabled(controlValues) { const suffix = this.textLineNumber === 1 ? "" : "2"; - const textEnabled = controlValues[`text${suffix}-enabled`]; + const text = controlValues[`button-text${suffix}`]; const shadowEnabled = controlValues[`text${suffix}-shadow-enabled`]; - return textEnabled && shadowEnabled; + return text && text.trim() !== "" && shadowEnabled; } apply(context, controlValues, animState, renderData) { diff --git a/static/js/button-generator/effects/wave-text.js b/static/js/button-generator/effects/wave-text.js index b394c61..61e097c 100644 --- a/static/js/button-generator/effects/wave-text.js +++ b/static/js/button-generator/effects/wave-text.js @@ -65,8 +65,7 @@ export class WaveTextEffect extends ButtonEffect { // Get text configuration const text = controlValues[`button-text${suffix}`] || ''; - const enabled = controlValues[`text${suffix}-enabled`]; - if (!text || !enabled) return; + if (!text || text.trim() === '') return; const fontSize = controlValues[`font-size${suffix}`] || 12; const fontWeight = controlValues[`font-bold${suffix}`] ? 'bold' : 'normal'; From 39e72f56f9e295787cce5c98b5f291bb5507c218 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 13:40:42 +0000 Subject: [PATCH 31/55] Added ability to load in external image --- .../effects/background-external-image.js | 248 ++++++++++++++++++ .../effects/background-solid.js | 3 +- static/js/button-generator/main.js | 2 + static/js/button-generator/ui-builder.js | 8 +- 4 files changed, 259 insertions(+), 2 deletions(-) create mode 100644 static/js/button-generator/effects/background-external-image.js diff --git a/static/js/button-generator/effects/background-external-image.js b/static/js/button-generator/effects/background-external-image.js new file mode 100644 index 0000000..1e9fbf6 --- /dev/null +++ b/static/js/button-generator/effects/background-external-image.js @@ -0,0 +1,248 @@ +import { ButtonEffect } from '../effect-base.js'; + +/** + * External Image Background Effect + * Loads an external image from a URL and displays it as the background + */ +export class ExternalImageBackgroundEffect extends ButtonEffect { + constructor() { + super({ + id: 'bg-external-image', + name: 'External Image Background', + type: 'background', + category: 'Background', + renderOrder: 1 + }); + + // Cache for loaded images + this.imageCache = new Map(); + this.loadingImages = new Map(); + this.generator = null; + + // Set up event listener for image loads + this.boundImageLoadHandler = this.handleImageLoad.bind(this); + window.addEventListener('imageLoaded', this.boundImageLoadHandler); + } + + /** + * Handle image load event + */ + handleImageLoad() { + // Trigger a redraw if we have a generator reference + if (this.generator) { + this.generator.updatePreview(); + } + } + + defineControls() { + return [ + { + id: 'bg-image-url', + type: 'text', + label: 'Image URL', + defaultValue: '', + showWhen: 'bg-type', + description: 'URL of the image to use as background' + }, + { + id: 'bg-image-fit', + type: 'select', + label: 'Image Fit', + defaultValue: 'cover', + options: [ + { value: 'cover', label: 'Cover (fill, crop if needed)' }, + { value: 'contain', label: 'Contain (fit inside)' }, + { value: 'stretch', label: 'Stretch (may distort)' }, + { value: 'center', label: 'Center (actual size)' } + ], + showWhen: 'bg-type', + description: 'How the image should fit in the canvas' + }, + { + id: 'bg-image-opacity', + type: 'range', + label: 'Image Opacity', + defaultValue: 1, + min: 0, + max: 1, + step: 0.05, + showWhen: 'bg-type', + description: 'Transparency of the background image' + } + ]; + } + + isEnabled(controlValues) { + return controlValues['bg-type'] === 'external-image'; + } + + /** + * Start loading an image from a URL with caching + * Triggers async loading but returns immediately + * @param {string} url - Image URL + */ + startLoadingImage(url) { + // Skip if already cached or loading + if (this.imageCache.has(url) || this.loadingImages.has(url)) { + return; + } + + // Mark as loading to prevent duplicate requests + this.loadingImages.set(url, true); + + const img = new Image(); + + img.onload = () => { + this.imageCache.set(url, img); + this.loadingImages.delete(url); + // Trigger a redraw when image loads + const event = new CustomEvent('imageLoaded'); + window.dispatchEvent(event); + }; + + img.onerror = () => { + // Cache the error state to prevent retry spam + this.imageCache.set(url, 'ERROR'); + this.loadingImages.delete(url); + // Trigger a redraw to show error state + const event = new CustomEvent('imageLoaded'); + window.dispatchEvent(event); + }; + + img.src = url; + } + + /** + * Get cached image if available + * @param {string} url - Image URL + * @returns {HTMLImageElement|string|null} Image element, 'ERROR', or null + */ + getCachedImage(url) { + return this.imageCache.get(url) || null; + } + + /** + * Draw image with the specified fit mode + */ + drawImage(context, img, fitMode, opacity, width, height) { + context.globalAlpha = opacity; + + const imgRatio = img.width / img.height; + const canvasRatio = width / height; + + let drawX = 0; + let drawY = 0; + let drawWidth = width; + let drawHeight = height; + + switch (fitMode) { + case 'cover': + // Fill the canvas, cropping if necessary + if (imgRatio > canvasRatio) { + // Image is wider, fit height + drawHeight = height; + drawWidth = height * imgRatio; + drawX = (width - drawWidth) / 2; + } else { + // Image is taller, fit width + drawWidth = width; + drawHeight = width / imgRatio; + drawY = (height - drawHeight) / 2; + } + break; + + case 'contain': + // Fit inside the canvas, showing all of the image + if (imgRatio > canvasRatio) { + // Image is wider, fit width + drawWidth = width; + drawHeight = width / imgRatio; + drawY = (height - drawHeight) / 2; + } else { + // Image is taller, fit height + drawHeight = height; + drawWidth = height * imgRatio; + drawX = (width - drawWidth) / 2; + } + break; + + case 'center': + // Center the image at actual size + drawWidth = img.width; + drawHeight = img.height; + drawX = (width - drawWidth) / 2; + drawY = (height - drawHeight) / 2; + break; + + case 'stretch': + // Stretch to fill (default values already set) + break; + } + + context.drawImage(img, drawX, drawY, drawWidth, drawHeight); + context.globalAlpha = 1; // Reset alpha + } + + apply(context, controlValues, animState, renderData) { + const url = controlValues['bg-image-url'] || ''; + const fitMode = controlValues['bg-image-fit'] || 'cover'; + const opacity = controlValues['bg-image-opacity'] ?? 1; + + // If no URL provided, fill with a placeholder color + if (!url.trim()) { + context.fillStyle = '#cccccc'; + context.fillRect(0, 0, renderData.width, renderData.height); + + // Draw placeholder text + context.fillStyle = '#666666'; + context.font = '8px Arial'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillText('Enter image URL', renderData.centerX, renderData.centerY); + return; + } + + // Start loading if not already cached or loading + const cachedImage = this.getCachedImage(url); + + if (!cachedImage) { + // Not cached yet - start loading + this.startLoadingImage(url); + + // Draw loading state + context.fillStyle = '#3498db'; + context.fillRect(0, 0, renderData.width, renderData.height); + + context.fillStyle = '#ffffff'; + context.font = '6px Arial'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillText('Loading...', renderData.centerX, renderData.centerY); + return; + } + + if (cachedImage === 'ERROR') { + // Failed to load + context.fillStyle = '#ff6b6b'; + context.fillRect(0, 0, renderData.width, renderData.height); + + context.fillStyle = '#ffffff'; + context.font = '6px Arial'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.fillText('Failed to load', renderData.centerX, renderData.centerY - 4); + context.fillText('image', renderData.centerX, renderData.centerY + 4); + return; + } + + // Image is loaded - draw it + this.drawImage(context, cachedImage, fitMode, opacity, renderData.width, renderData.height); + } +} + +// Auto-register effect +export function register(generator) { + const effect = new ExternalImageBackgroundEffect(); + effect.generator = generator; // Store reference for redraws + generator.registerEffect(effect); +} diff --git a/static/js/button-generator/effects/background-solid.js b/static/js/button-generator/effects/background-solid.js index f16cc68..49c2954 100644 --- a/static/js/button-generator/effects/background-solid.js +++ b/static/js/button-generator/effects/background-solid.js @@ -25,7 +25,8 @@ export class SolidBackgroundEffect extends ButtonEffect { { value: 'solid', label: 'Solid Color' }, { value: 'gradient', label: 'Gradient' }, { value: 'texture', label: 'Texture' }, - { value: 'emoji-wallpaper', label: 'Emoji Wallpaper' } + { value: 'emoji-wallpaper', label: 'Emoji Wallpaper' }, + { value: 'external-image', label: 'External Image' } ] }, { diff --git a/static/js/button-generator/main.js b/static/js/button-generator/main.js index 51233e8..c6061dc 100644 --- a/static/js/button-generator/main.js +++ b/static/js/button-generator/main.js @@ -13,6 +13,7 @@ import * as solidBg from "./effects/background-solid.js"; import * as gradientBg from "./effects/background-gradient.js"; import * as textureBg from "./effects/background-texture.js"; import * as emojiWallpaper from "./effects/background-emoji-wallpaper.js"; +import * as externalImage from "./effects/background-external-image.js"; import * as rainbowBg from "./effects/background-rainbow.js"; import * as rain from "./effects/background-rain.js"; import * as starfield from "./effects/background-starfield.js"; @@ -88,6 +89,7 @@ async function setupApp() { gradientBg.register(generator); textureBg.register(generator); emojiWallpaper.register(generator); + externalImage.register(generator); rainbowBg.register(generator); rain.register(generator); starfield.register(generator); diff --git a/static/js/button-generator/ui-builder.js b/static/js/button-generator/ui-builder.js index 00ce430..afafbbe 100644 --- a/static/js/button-generator/ui-builder.js +++ b/static/js/button-generator/ui-builder.js @@ -418,7 +418,11 @@ export class UIBuilder { input.type = 'text'; input.id = id; input.value = defaultValue || ''; - input.maxLength = 20; + + // Only set maxLength for text inputs that aren't URLs + if (id !== 'bg-image-url') { + input.maxLength = 20; + } container.appendChild(labelEl); container.appendChild(input); @@ -464,6 +468,8 @@ export class UIBuilder { control.style.display = triggerControl.value === 'texture' ? 'block' : 'none'; } else if (controlId && (controlId.startsWith('emoji-') || controlId === 'emoji-text')) { control.style.display = triggerControl.value === 'emoji-wallpaper' ? 'block' : 'none'; + } else if (controlId && (controlId.startsWith('bg-image-') || controlId === 'bg-image-url' || controlId === 'bg-image-fit' || controlId === 'bg-image-opacity')) { + control.style.display = triggerControl.value === 'external-image' ? 'block' : 'none'; } } // For text color controls From c3d1ca54087feb0c439698f0cab378bfccc0fb8b Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 13:49:33 +0000 Subject: [PATCH 32/55] Disabling external images due to tainted canvas --- .../effects/background-solid.js | 50 +++++++++---------- static/js/button-generator/main.js | 2 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/static/js/button-generator/effects/background-solid.js b/static/js/button-generator/effects/background-solid.js index 49c2954..6ba8155 100644 --- a/static/js/button-generator/effects/background-solid.js +++ b/static/js/button-generator/effects/background-solid.js @@ -1,4 +1,4 @@ -import { ButtonEffect } from '../effect-base.js'; +import { ButtonEffect } from "../effect-base.js"; /** * Solid color background effect @@ -6,46 +6,46 @@ import { ButtonEffect } from '../effect-base.js'; export class SolidBackgroundEffect extends ButtonEffect { constructor() { super({ - id: 'bg-solid', - name: 'Solid Background', - type: 'background', - category: 'Background', - renderOrder: 1 + id: "bg-solid", + name: "Solid Background", + type: "background", + category: "Background", + renderOrder: 1, }); } defineControls() { return [ { - id: 'bg-type', - type: 'select', - label: 'Background Type', - defaultValue: 'solid', + id: "bg-type", + type: "select", + label: "Background Type", + defaultValue: "solid", options: [ - { value: 'solid', label: 'Solid Color' }, - { value: 'gradient', label: 'Gradient' }, - { value: 'texture', label: 'Texture' }, - { value: 'emoji-wallpaper', label: 'Emoji Wallpaper' }, - { value: 'external-image', label: 'External Image' } - ] + { value: "solid", label: "Solid Color" }, + { value: "gradient", label: "Gradient" }, + { value: "texture", label: "Texture" }, + { value: "emoji-wallpaper", label: "Emoji Wallpaper" }, + // { value: 'external-image', label: 'External Image' } + ], }, { - id: 'bg-color', - type: 'color', - label: 'Background Color', - defaultValue: '#4a90e2', - showWhen: 'bg-type', - description: 'Solid background color' - } + id: "bg-color", + type: "color", + label: "Background Color", + defaultValue: "#4a90e2", + showWhen: "bg-type", + description: "Solid background color", + }, ]; } isEnabled(controlValues) { - return controlValues['bg-type'] === 'solid'; + return controlValues["bg-type"] === "solid"; } apply(context, controlValues, animState, renderData) { - const color = controlValues['bg-color'] || '#4a90e2'; + const color = controlValues["bg-color"] || "#4a90e2"; context.fillStyle = color; context.fillRect(0, 0, renderData.width, renderData.height); } diff --git a/static/js/button-generator/main.js b/static/js/button-generator/main.js index c6061dc..71c400e 100644 --- a/static/js/button-generator/main.js +++ b/static/js/button-generator/main.js @@ -89,7 +89,7 @@ async function setupApp() { gradientBg.register(generator); textureBg.register(generator); emojiWallpaper.register(generator); - externalImage.register(generator); + //externalImage.register(generator); rainbowBg.register(generator); rain.register(generator); starfield.register(generator); From bdecb6bbabc7510edd46f6a5be16b3a871c664b1 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 13:51:25 +0000 Subject: [PATCH 33/55] fixing type --- content/resources/button-generator/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/resources/button-generator/index.md b/content/resources/button-generator/index.md index a5cc19e..5c14182 100644 --- a/content/resources/button-generator/index.md +++ b/content/resources/button-generator/index.md @@ -24,4 +24,4 @@ Big thanks to [neonaut's 88x31 archive](https://neonaut.neocities.org/cyber/88x3 - 07/01/2025 - Initial release. - 08/01/2025 - Total refactor to be modular, added many more effects. -- 09/01/2025 - Rewrote the bevel inset and outset border code so they look a lot nicer at the corners. Updates throughout so that multibit (emoji!) characters should work. +- 09/01/2025 - Rewrote the bevel inset and outset border code so they look a lot nicer at the corners. Updates throughout so that multibyte (emoji!) characters should work. From f8d45f957a662c7610d25847c722d33bbdcd991b Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 14:28:13 +0000 Subject: [PATCH 34/55] Adding kofi link, why not --- assets/sass/pages/blog.scss | 15 +++++++++++++++ assets/sass/pages/resources.scss | 17 ++++++++++++++++- layouts/partials/contact-section.html | 13 +++++-------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/assets/sass/pages/blog.scss b/assets/sass/pages/blog.scss index 298cae7..f3dface 100644 --- a/assets/sass/pages/blog.scss +++ b/assets/sass/pages/blog.scss @@ -591,6 +591,21 @@ flex-direction: column; gap: 15px; color: greenyellow; + + a { + color: #0f0; + text-decoration: none; + border-bottom: 1px dotted rgba(0, 255, 0, 0.5); + transition: all 0.3s ease; + + &:hover { + color: cyan; + border-bottom-style: solid; + border-bottom-color: rgba(0, 255, 0, 0.8); + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + background: rgba(0, 255, 0, 0.05); + } + } } .contact-email, diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index a6869b0..e16bafe 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -777,7 +777,8 @@ } @include media-down(md) { - &::before, &::after { + &::before, + &::after { content: none; } } @@ -1237,6 +1238,20 @@ margin-bottom: 1.5rem; line-height: 1.6; } + + a { + color: #66ccff; + text-decoration: none; + border-bottom: 1px solid rgba(0, 150, 255, 0.3); + transition: all 0.3s ease; + font-family: "Consolas", "Monaco", monospace; + + &:hover { + color: #ff7800; + border-bottom-color: rgba(255, 120, 0, 0.6); + text-shadow: 0 0 8px rgba(0, 150, 255, 0.5); + } + } } .contact-email, diff --git a/layouts/partials/contact-section.html b/layouts/partials/contact-section.html index f06baa7..76797c6 100644 --- a/layouts/partials/contact-section.html +++ b/layouts/partials/contact-section.html @@ -2,9 +2,10 @@ <h2 class="contact-title">Contact</h2> <div class="contact-content"> <p> - If you found this interesting, have any comments, questions, - corrections, or just fancy saying "hello" please feel free to get - in touch. + If you found this interesting, have any comments, questions, corrections, + or just fancy saying "hello" please feel free to get in touch. If you + really enjoyed it you could consider + <a href="https://ko-fi.com/ritual" target="_blank">buying me a coffee</a>. </p> <div class="contact-email"> <span class="contact-label">Email:</span> @@ -13,11 +14,7 @@ <div class="contact-pgp"> <span class="contact-label">PGP Public Key:</span> <div class="pgp-actions"> - <a - href="/publickey.asc" - download - class="pgp-button download-key" - > + <a href="/publickey.asc" download class="pgp-button download-key"> <span class="button-icon">↓</span> Download </a> <button class="pgp-button copy-key pgp-copy-trigger"> From 0d09907da1af4d102288364a8c7b7906e9f22b35 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 17:51:53 +0000 Subject: [PATCH 35/55] Adding manual image background --- assets/js/button-generator.js | 1299 ----------------- assets/sass/pages/button-generator.scss | 69 + .../button-generator/button-generator-core.js | 12 + .../effects/background-external-image.js | 115 +- .../effects/background-solid.js | 2 +- static/js/button-generator/main.js | 2 +- static/js/button-generator/ui-builder.js | 62 +- 7 files changed, 233 insertions(+), 1328 deletions(-) delete mode 100644 assets/js/button-generator.js diff --git a/assets/js/button-generator.js b/assets/js/button-generator.js deleted file mode 100644 index f33b460..0000000 --- a/assets/js/button-generator.js +++ /dev/null @@ -1,1299 +0,0 @@ -(function () { - // Wait for DOM to be ready - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", init); - } else { - init(); - } - - function init() { - setupCollapsible(); - setupButtonGenerator(); - } - - // Collapsible sections functionality - function setupCollapsible() { - const headers = document.querySelectorAll(".control-group-header"); - console.log("Found", headers.length, "collapsible headers"); - - headers.forEach((header) => { - header.addEventListener("click", () => { - const controlGroup = header.closest(".control-group"); - if (controlGroup) { - controlGroup.classList.toggle("collapsed"); - console.log("Toggled collapsed on", header.textContent.trim()); - } - }); - }); - } - - function setupButtonGenerator() { - const canvas = document.getElementById("button-canvas"); - const ctx = canvas.getContext("2d"); - - // Preload all web fonts for canvas rendering - const fonts = [ - "Lato", - "Roboto", - "Open Sans", - "Montserrat", - "Oswald", - "Bebas Neue", - "Roboto Mono", - "VT323", - "Press Start 2P", - "DSEG7-Classic", - ]; - - // Load fonts using CSS Font Loading API - const fontPromises = fonts.flatMap((font) => [ - document.fonts.load(`400 12px "${font}"`), - document.fonts.load(`700 12px "${font}"`), - document.fonts.load(`italic 400 12px "${font}"`), - ]); - - Promise.all(fontPromises).then(() => { - console.log("All fonts loaded for canvas"); - drawButton(); - }); - - // Animation configuration - const ANIMATION_CONFIG = { - fps: 20, - duration: 2, // seconds - get totalFrames() { - return this.fps * this.duration; - }, // 40 frames - }; - - // Animation state class - passed to drawToContext for frame-based rendering - class AnimationState { - constructor(frameNumber = 0, totalFrames = 40) { - this.frame = frameNumber; - this.totalFrames = totalFrames; - this.progress = frameNumber / totalFrames; // 0 to 1 - this.time = (frameNumber / ANIMATION_CONFIG.fps) * 1000; // milliseconds - } - - // Helper to get phase for periodic animations (0 to 2π) - getPhase(speed = 1.0) { - return this.progress * speed * Math.PI * 2; - } - } - - // Get all controls - const controls = { - text: document.getElementById("button-text"), - textEnabled: document.getElementById("text-enabled"), - fontSize: document.getElementById("font-size"), - textX: document.getElementById("text-x"), - textY: document.getElementById("text-y"), - textColorType: document.getElementById("text-color-type"), - textColor: document.getElementById("text-color"), - textGradientColor1: document.getElementById("text-gradient-color1"), - textGradientColor2: document.getElementById("text-gradient-color2"), - textGradientAngle: document.getElementById("text-gradient-angle"), - textOutline: document.getElementById("text-outline"), - outlineColor: document.getElementById("outline-color"), - fontFamily: document.getElementById("font-family"), - fontBold: document.getElementById("font-bold"), - fontItalic: document.getElementById("font-italic"), - text2: document.getElementById("button-text2"), - text2Enabled: document.getElementById("text2-enabled"), - fontSize2: document.getElementById("font-size2"), - text2X: document.getElementById("text2-x"), - text2Y: document.getElementById("text2-y"), - text2ColorType: document.getElementById("text2-color-type"), - text2Color: document.getElementById("text2-color"), - text2GradientColor1: document.getElementById("text2-gradient-color1"), - text2GradientColor2: document.getElementById("text2-gradient-color2"), - text2GradientAngle: document.getElementById("text2-gradient-angle"), - text2Outline: document.getElementById("text2-outline"), - outline2Color: document.getElementById("outline2-color"), - fontFamily2: document.getElementById("font-family2"), - fontBold2: document.getElementById("font-bold2"), - fontItalic2: document.getElementById("font-italic2"), - bgType: document.getElementById("bg-type"), - bgColor: document.getElementById("bg-color"), - gradientColor1: document.getElementById("gradient-color1"), - gradientColor2: document.getElementById("gradient-color2"), - gradientAngle: document.getElementById("gradient-angle"), - textureType: document.getElementById("texture-type"), - textureColor1: document.getElementById("texture-color1"), - textureColor2: document.getElementById("texture-color2"), - textureScale: document.getElementById("texture-scale"), - borderWidth: document.getElementById("border-width"), - borderColor: document.getElementById("border-color"), - borderStyle: document.getElementById("border-style"), - // Animation controls - animateTextWave: document.getElementById("animate-text-wave"), - waveAmplitude: document.getElementById("wave-amplitude"), - waveSpeed: document.getElementById("wave-speed"), - animateTextWave2: document.getElementById("animate-text-wave2"), - waveAmplitude2: document.getElementById("wave-amplitude2"), - waveSpeed2: document.getElementById("wave-speed2"), - animateBgRainbow: document.getElementById("animate-bg-rainbow"), - rainbowSpeed: document.getElementById("rainbow-speed"), - animateBgRainbowGradient: document.getElementById( - "animate-bg-rainbow-gradient", - ), - animateTextRainbow: document.getElementById("animate-text-rainbow"), - textRainbowSpeed: document.getElementById("text-rainbow-speed"), - animateTextRainbow2: document.getElementById("animate-text-rainbow2"), - textRainbowSpeed2: document.getElementById("text-rainbow-speed2"), - animateGlitch: document.getElementById("animate-glitch"), - glitchIntensity: document.getElementById("glitch-intensity"), - animatePulse: document.getElementById("animate-pulse"), - pulseScale: document.getElementById("pulse-scale"), - animateShimmer: document.getElementById("animate-shimmer"), - animateScanline: document.getElementById("animate-scanline"), - scanlineIntensity: document.getElementById("scanline-intensity"), - scanlineSpeed: document.getElementById("scanline-speed"), - animateRgbSplit: document.getElementById("animate-rgb-split"), - rgbSplitIntensity: document.getElementById("rgb-split-intensity"), - animateNoise: document.getElementById("animate-noise"), - noiseIntensity: document.getElementById("noise-intensity"), - animateRotate: document.getElementById("animate-rotate"), - rotateAngle: document.getElementById("rotate-angle"), - rotateSpeed: document.getElementById("rotate-speed"), - }; - - // Update value displays - const updateValueDisplay = (id, value) => { - const display = document.getElementById(id + "-value"); - if (display) display.textContent = value; - }; - - // Show/hide controls based on background type - controls.bgType.addEventListener("change", () => { - const solidControls = document.getElementById("solid-controls"); - const gradientControls = document.getElementById("gradient-controls"); - const textureControls = document.getElementById("texture-controls"); - - solidControls.style.display = "none"; - gradientControls.style.display = "none"; - textureControls.style.display = "none"; - - if (controls.bgType.value === "solid") { - solidControls.style.display = "block"; - } else if (controls.bgType.value === "gradient") { - gradientControls.style.display = "block"; - } else if (controls.bgType.value === "texture") { - textureControls.style.display = "block"; - } - - drawButton(); - }); - - // Show/hide text color controls - controls.textColorType.addEventListener("change", () => { - const textSolidColor = document.getElementById("text-solid-color"); - const textGradientColor = document.getElementById("text-gradient-color"); - - if (controls.textColorType.value === "solid") { - textSolidColor.style.display = "block"; - textGradientColor.style.display = "none"; - } else { - textSolidColor.style.display = "none"; - textGradientColor.style.display = "block"; - } - drawButton(); - }); - - controls.text2ColorType.addEventListener("change", () => { - const text2SolidColor = document.getElementById("text2-solid-color"); - const text2GradientColor = document.getElementById( - "text2-gradient-color", - ); - - if (controls.text2ColorType.value === "solid") { - text2SolidColor.style.display = "block"; - text2GradientColor.style.display = "none"; - } else { - text2SolidColor.style.display = "none"; - text2GradientColor.style.display = "block"; - } - drawButton(); - }); - - // Show/hide outline color - controls.textOutline.addEventListener("change", () => { - controls.outlineColor.style.display = controls.textOutline.checked - ? "block" - : "none"; - drawButton(); - }); - - controls.text2Outline.addEventListener("change", () => { - controls.outline2Color.style.display = controls.text2Outline.checked - ? "block" - : "none"; - drawButton(); - }); - - // Draw texture patterns - function drawTexture(type, color1, color2, scale) { - const tempCanvas = document.createElement("canvas"); - tempCanvas.width = 88; - tempCanvas.height = 31; - const tempCtx = tempCanvas.getContext("2d"); - - tempCtx.fillStyle = color1; - tempCtx.fillRect(0, 0, 88, 31); - - tempCtx.fillStyle = color2; - const size = Math.max(2, Math.floor(scale / 10)); - - switch (type) { - case "dots": - for (let y = 0; y < 31; y += size * 2) { - for (let x = 0; x < 88; x += size * 2) { - tempCtx.beginPath(); - tempCtx.arc(x, y, size / 2, 0, Math.PI * 2); - tempCtx.fill(); - } - } - break; - case "grid": - for (let x = 0; x < 88; x += size) { - tempCtx.fillRect(x, 0, 1, 31); - } - for (let y = 0; y < 31; y += size) { - tempCtx.fillRect(0, y, 88, 1); - } - break; - case "diagonal": - for (let i = -31; i < 88; i += size) { - tempCtx.fillRect(i, 0, 2, 31); - tempCtx.save(); - tempCtx.translate(i + 1, 0); - tempCtx.rotate(Math.PI / 4); - tempCtx.fillRect(0, 0, 2, 100); - tempCtx.restore(); - } - break; - case "checkerboard": - for (let y = 0; y < 31; y += size) { - for (let x = 0; x < 88; x += size) { - if ((x / size + y / size) % 2 === 0) { - tempCtx.fillRect(x, y, size, size); - } - } - } - break; - case "noise": - for (let y = 0; y < 31; y++) { - for (let x = 0; x < 88; x++) { - if (Math.random() > 0.5) { - tempCtx.fillRect(x, y, 1, 1); - } - } - } - break; - case "stars": - for (let i = 0; i < scale; i++) { - const x = Math.floor(Math.random() * 88); - const y = Math.floor(Math.random() * 31); - tempCtx.fillRect(x, y, 1, 1); - tempCtx.fillRect(x - 1, y, 1, 1); - tempCtx.fillRect(x + 1, y, 1, 1); - tempCtx.fillRect(x, y - 1, 1, 1); - tempCtx.fillRect(x, y + 1, 1, 1); - } - break; - } - - return tempCanvas; - } - - // Helper function to draw text line with optional wave animation - function drawTextLine(context, lineNumber, animState) { - const prefix = lineNumber === 1 ? "" : "2"; - const text = controls[`text${prefix}`].value; - const enabled = controls[`text${prefix}Enabled`].checked; - - if (!text || !enabled) return; - - const fontSize = parseFloat(controls[`fontSize${prefix}`].value); - const fontWeight = controls[`fontBold${prefix}`].checked - ? "bold" - : "normal"; - const fontStyle = controls[`fontItalic${prefix}`].checked - ? "italic" - : "normal"; - const fontFamily = controls[`fontFamily${prefix}`].value; - - context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`; - context.textAlign = "center"; - context.textBaseline = "middle"; - - const baseX = (parseFloat(controls[`text${prefix}X`].value) / 100) * 88; - const baseY = (parseFloat(controls[`text${prefix}Y`].value) / 100) * 31; - - // Check if wave animation is enabled - const waveEnabled = - animState && controls[`animateTextWave${prefix}`]?.checked; - - if (waveEnabled) { - drawWaveText( - context, - text, - baseX, - baseY, - fontSize, - animState, - lineNumber, - ); - } else { - drawStandardText( - context, - text, - baseX, - baseY, - fontSize, - animState, - lineNumber, - ); - } - } - - // Draw standard (non-wave) text - function drawStandardText( - context, - text, - x, - y, - fontSize, - animState, - lineNumber, - ) { - const prefix = lineNumber === 1 ? "" : "2"; - - // Get colors (with potential rainbow animation) - const colors = getTextColors( - lineNumber, - animState, - context, - text, - x, - y, - fontSize, - ); - - if (controls[`text${prefix}Outline`].checked) { - context.strokeStyle = colors.strokeStyle; - context.lineWidth = 2; - context.strokeText(text, x, y); - } - - context.fillStyle = colors.fillStyle; - context.fillText(text, x, y); - } - - // Draw wave-animated text (character by character) - function drawWaveText( - context, - text, - baseX, - baseY, - fontSize, - animState, - lineNumber, - ) { - const prefix = lineNumber === 1 ? "" : "2"; - const amplitude = parseFloat(controls[`waveAmplitude${prefix}`].value); - const speed = parseFloat(controls[`waveSpeed${prefix}`].value); - - // Measure total width for centering - const totalWidth = context.measureText(text).width; - let currentX = baseX - totalWidth / 2; - - for (let i = 0; i < text.length; i++) { - const char = text[i]; - const charWidth = context.measureText(char).width; - - // Calculate wave offset for this character - const phase = animState.getPhase(speed); - const charOffset = i / text.length; // 0 to 1 - const waveY = Math.sin(phase + charOffset * Math.PI * 2) * amplitude; - - const charX = currentX + charWidth / 2; - const charY = baseY + waveY; - - // Get colors for this character - const colors = getTextColors( - lineNumber, - animState, - context, - char, - charX, - charY, - fontSize, - ); - - // Draw outline if enabled - if (controls[`text${prefix}Outline`].checked) { - context.strokeStyle = colors.strokeStyle; - context.lineWidth = 2; - context.strokeText(char, charX, charY); - } - - // Draw character - context.fillStyle = colors.fillStyle; - context.fillText(char, charX, charY); - - currentX += charWidth; - } - } - - // Get text colors (solid, gradient, or rainbow) - function getTextColors( - lineNumber, - animState, - context, - text, - x, - y, - fontSize, - ) { - const prefix = lineNumber === 1 ? "" : "2"; - const colorType = controls[`text${prefix}ColorType`].value; - - let fillStyle, strokeStyle; - - // Rainbow text animation overrides other colors - if (animState && controls[`animateTextRainbow${prefix}`]?.checked) { - const hue = - (animState.progress * - parseFloat(controls[`textRainbowSpeed${prefix}`].value) * - 360) % - 360; - fillStyle = `hsl(${hue}, 80%, 60%)`; - strokeStyle = `hsl(${hue}, 80%, 30%)`; - } else if (colorType === "solid") { - fillStyle = controls[`text${prefix}Color`].value; - strokeStyle = controls[`outline${prefix}Color`].value; - } else { - // Gradient - const angle = - parseFloat(controls[`text${prefix}GradientAngle`].value) * - (Math.PI / 180); - const textWidth = context.measureText(text).width; - const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2; - const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2; - const x2 = x + textWidth / 2 - (Math.cos(angle) * textWidth) / 2; - const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2; - - const textGradient = context.createLinearGradient(x1, y1, x2, y2); - textGradient.addColorStop( - 0, - controls[`text${prefix}GradientColor1`].value, - ); - textGradient.addColorStop( - 1, - controls[`text${prefix}GradientColor2`].value, - ); - fillStyle = textGradient; - strokeStyle = controls[`outline${prefix}Color`].value; - } - - return { fillStyle, strokeStyle }; - } - - // Helper function to draw to a single context - function drawToContext(context, animState = null) { - context.clearRect(0, 0, 88, 31); - - // Apply rotate effect (must be before drawing) - if (animState && controls.animateRotate?.checked) { - const maxAngle = parseFloat(controls.rotateAngle.value); - const speed = parseFloat(controls.rotateSpeed.value); - const angle = - Math.sin(animState.getPhase(speed)) * maxAngle * (Math.PI / 180); - - context.save(); - context.translate(44, 15.5); - context.rotate(angle); - context.translate(-44, -15.5); - } - - // Draw background - if (controls.bgType.value === "solid") { - // Rainbow flash for solid colors - if (animState && controls.animateBgRainbow?.checked) { - const hue = - (animState.progress * - parseFloat(controls.rainbowSpeed.value) * - 360) % - 360; - context.fillStyle = `hsl(${hue}, 70%, 50%)`; - } else { - context.fillStyle = controls.bgColor.value; - } - context.fillRect(0, 0, 88, 31); - } else if (controls.bgType.value === "gradient") { - const angle = - parseFloat(controls.gradientAngle.value) * (Math.PI / 180); - const x1 = 44 + Math.cos(angle) * 44; - const y1 = 15.5 + Math.sin(angle) * 15.5; - const x2 = 44 - Math.cos(angle) * 44; - const y2 = 15.5 - Math.sin(angle) * 15.5; - - const gradient = context.createLinearGradient(x1, y1, x2, y2); - - // Rainbow flash for gradients - if (animState && controls.animateBgRainbow?.checked) { - const hue = - (animState.progress * - parseFloat(controls.rainbowSpeed.value) * - 360) % - 360; - gradient.addColorStop(0, `hsl(${hue}, 70%, 50%)`); - gradient.addColorStop(1, `hsl(${(hue + 60) % 360}, 70%, 60%)`); - } else { - gradient.addColorStop(0, controls.gradientColor1.value); - gradient.addColorStop(1, controls.gradientColor2.value); - } - - context.fillStyle = gradient; - context.fillRect(0, 0, 88, 31); - } else if (controls.bgType.value === "texture") { - const texture = drawTexture( - controls.textureType.value, - controls.textureColor1.value, - controls.textureColor2.value, - parseFloat(controls.textureScale.value), - ); - context.drawImage(texture, 0, 0); - } - - // Rainbow gradient overlay (travels across background) - if (animState && controls.animateBgRainbowGradient?.checked) { - // Map progress to position (-100 to 100) - const position = animState.progress * 200 - 100; - - // Create a horizontal gradient that sweeps across - const rainbowGradient = context.createLinearGradient( - position - 50, - 0, - position + 50, - 0, - ); - - // Create rainbow stops that also cycle through colors - const hueOffset = animState.progress * 360; - rainbowGradient.addColorStop( - 0, - `hsla(${(hueOffset + 0) % 360}, 80%, 50%, 0)`, - ); - rainbowGradient.addColorStop( - 0.2, - `hsla(${(hueOffset + 60) % 360}, 80%, 50%, 0.6)`, - ); - rainbowGradient.addColorStop( - 0.4, - `hsla(${(hueOffset + 120) % 360}, 80%, 50%, 0.8)`, - ); - rainbowGradient.addColorStop( - 0.6, - `hsla(${(hueOffset + 180) % 360}, 80%, 50%, 0.8)`, - ); - rainbowGradient.addColorStop( - 0.8, - `hsla(${(hueOffset + 240) % 360}, 80%, 50%, 0.6)`, - ); - rainbowGradient.addColorStop( - 1, - `hsla(${(hueOffset + 300) % 360}, 80%, 50%, 0)`, - ); - - context.fillStyle = rainbowGradient; - context.fillRect(0, 0, 88, 31); - } - - // Draw border - const borderWidth = parseFloat(controls.borderWidth.value); - if (borderWidth > 0) { - const style = controls.borderStyle.value; - - if (style === "solid") { - context.strokeStyle = controls.borderColor.value; - context.lineWidth = borderWidth; - context.strokeRect( - borderWidth / 2, - borderWidth / 2, - 88 - borderWidth, - 31 - borderWidth, - ); - } else if (style === "inset" || style === "outset") { - const light = style === "outset"; - context.strokeStyle = light ? "#ffffff" : "#000000"; - context.lineWidth = borderWidth; - context.beginPath(); - context.moveTo(0, 31); - context.lineTo(0, 0); - context.lineTo(88, 0); - context.stroke(); - - context.strokeStyle = light ? "#000000" : "#ffffff"; - context.beginPath(); - context.moveTo(88, 0); - context.lineTo(88, 31); - context.lineTo(0, 31); - context.stroke(); - } else if (style === "ridge") { - context.strokeStyle = "#ffffff"; - context.lineWidth = borderWidth / 2; - context.strokeRect( - borderWidth / 4, - borderWidth / 4, - 88 - borderWidth / 2, - 31 - borderWidth / 2, - ); - - context.strokeStyle = "#000000"; - context.strokeRect( - (borderWidth * 3) / 4, - (borderWidth * 3) / 4, - 88 - borderWidth * 1.5, - 31 - borderWidth * 1.5, - ); - } - } - - // Apply pulse effect (scale before drawing text) - if (animState && controls.animatePulse?.checked) { - const maxScale = parseFloat(controls.pulseScale.value); - const minScale = 1.0; - const scale = - minScale + - (maxScale - minScale) * - (Math.sin(animState.getPhase(1.0)) * 0.5 + 0.5); - - context.save(); - context.translate(44, 15.5); - context.scale(scale, scale); - context.translate(-44, -15.5); - } - - // Draw text line 1 - drawTextLine(context, 1, animState); - - // Draw text line 2 - drawTextLine(context, 2, animState); - - // Restore context if pulse was applied - if (animState && controls.animatePulse?.checked) { - context.restore(); - } - - // Apply shimmer effect - if (animState && controls.animateShimmer?.checked) { - const shimmerX = animState.progress * 120 - 20; // Sweep from -20 to 100 - - const shimmerGradient = context.createLinearGradient( - shimmerX - 15, - 0, - shimmerX + 15, - 31, - ); - shimmerGradient.addColorStop(0, "rgba(255, 255, 255, 0)"); - shimmerGradient.addColorStop(0.5, "rgba(255, 255, 255, 0.3)"); - shimmerGradient.addColorStop(1, "rgba(255, 255, 255, 0)"); - - context.fillStyle = shimmerGradient; - context.fillRect(0, 0, 88, 31); - } - - // Apply glitch effect - if (animState && controls.animateGlitch?.checked) { - applyGlitchEffect(context, animState); - } - - // Apply scanline effect - if (animState && controls.animateScanline?.checked) { - applyScanlineEffect(context, animState); - } - - // Apply RGB split effect - if (animState && controls.animateRgbSplit?.checked) { - applyRgbSplitEffect(context, animState); - } - - // Apply noise effect - if (animState && controls.animateNoise?.checked) { - applyNoiseEffect(context, animState); - } - - // Restore context if rotate was applied - if (animState && controls.animateRotate?.checked) { - context.restore(); - } - } - - // Apply glitch effect (scanline displacement) - function applyGlitchEffect(context, animState) { - const intensity = parseFloat(controls.glitchIntensity.value); - const imageData = context.getImageData(0, 0, 88, 31); - - // Randomly glitch ~10% of scanlines per frame - const glitchProbability = 0.1; - const maxOffset = intensity; - - for (let y = 0; y < 31; y++) { - if (Math.random() < glitchProbability) { - const offset = Math.floor((Math.random() - 0.5) * maxOffset * 2); - shiftScanline(imageData, y, offset); - } - } - - context.putImageData(imageData, 0, 0); - } - - // Shift a horizontal scanline by offset pixels (with wrapping) - function shiftScanline(imageData, y, offset) { - const width = imageData.width; - const rowStart = y * width * 4; - const rowData = new Uint8ClampedArray(width * 4); - - // Copy row - for (let i = 0; i < width * 4; i++) { - rowData[i] = imageData.data[rowStart + i]; - } - - // Shift and wrap - for (let x = 0; x < width; x++) { - let sourceX = (x - offset + width) % width; - let destIdx = rowStart + x * 4; - let srcIdx = sourceX * 4; - - imageData.data[destIdx] = rowData[srcIdx]; - imageData.data[destIdx + 1] = rowData[srcIdx + 1]; - imageData.data[destIdx + 2] = rowData[srcIdx + 2]; - imageData.data[destIdx + 3] = rowData[srcIdx + 3]; - } - } - - // Apply scanline effect (CRT-style horizontal lines) - function applyScanlineEffect(context, animState) { - const intensity = parseFloat(controls.scanlineIntensity.value); - const speed = parseFloat(controls.scanlineSpeed.value); - - // Create overlay with scanlines - context.globalCompositeOperation = "multiply"; - context.fillStyle = "rgba(0, 0, 0, " + intensity + ")"; - - // Animate scanline position - const offset = (animState.progress * speed * 31) % 2; - - for (let y = offset; y < 31; y += 2) { - context.fillRect(0, Math.floor(y), 88, 1); - } - - context.globalCompositeOperation = "source-over"; - } - - // Apply RGB split/chromatic aberration effect - function applyRgbSplitEffect(context, animState) { - const intensity = parseFloat(controls.rgbSplitIntensity.value); - const imageData = context.getImageData(0, 0, 88, 31); - const result = context.createImageData(88, 31); - - // Oscillating offset - const phase = Math.sin(animState.getPhase(1.0)); - const offsetX = Math.round(phase * intensity); - - for (let y = 0; y < 31; y++) { - for (let x = 0; x < 88; x++) { - const idx = (y * 88 + x) * 4; - - // Red channel - shift left - const redX = Math.max(0, Math.min(87, x - offsetX)); - const redIdx = (y * 88 + redX) * 4; - result.data[idx] = imageData.data[redIdx]; - - // Green channel - no shift - result.data[idx + 1] = imageData.data[idx + 1]; - - // Blue channel - shift right - const blueX = Math.max(0, Math.min(87, x + offsetX)); - const blueIdx = (y * 88 + blueX) * 4; - result.data[idx + 2] = imageData.data[blueIdx + 2]; - - // Alpha channel - result.data[idx + 3] = imageData.data[idx + 3]; - } - } - - context.putImageData(result, 0, 0); - } - - // Apply noise/static effect - function applyNoiseEffect(context, animState) { - const intensity = parseFloat(controls.noiseIntensity.value); - const imageData = context.getImageData(0, 0, 88, 31); - - for (let i = 0; i < imageData.data.length; i += 4) { - // Random noise value - const noise = (Math.random() - 0.5) * 255 * intensity; - - imageData.data[i] = Math.max( - 0, - Math.min(255, imageData.data[i] + noise), - ); - imageData.data[i + 1] = Math.max( - 0, - Math.min(255, imageData.data[i + 1] + noise), - ); - imageData.data[i + 2] = Math.max( - 0, - Math.min(255, imageData.data[i + 2] + noise), - ); - // Alpha unchanged - } - - context.putImageData(imageData, 0, 0); - } - - // Animated preview state - let previewAnimationId = null; - - // Update preview (static or animated based on settings) - function updatePreview() { - if (hasAnimationsEnabled()) { - startAnimatedPreview(); - } else { - stopAnimatedPreview(); - drawToContext(ctx); - } - updateDownloadButtonLabel(); - } - - // Start animated preview loop - function startAnimatedPreview() { - stopAnimatedPreview(); // Clear any existing - - let frameNum = 0; - let lastFrameTime = performance.now(); - const frameDelay = 1000 / ANIMATION_CONFIG.fps; // ms per frame - - const animate = (currentTime) => { - const elapsed = currentTime - lastFrameTime; - - // Only advance frame if enough time has passed - if (elapsed >= frameDelay) { - const animState = new AnimationState( - frameNum, - ANIMATION_CONFIG.totalFrames, - ); - drawToContext(ctx, animState); - - frameNum = (frameNum + 1) % ANIMATION_CONFIG.totalFrames; - lastFrameTime = currentTime - (elapsed % frameDelay); // Carry over extra time - } - - previewAnimationId = requestAnimationFrame(animate); - }; - - previewAnimationId = requestAnimationFrame(animate); - } - - // Stop animated preview - function stopAnimatedPreview() { - if (previewAnimationId) { - cancelAnimationFrame(previewAnimationId); - previewAnimationId = null; - } - } - - // Main draw function - function drawButton() { - updatePreview(); - } - - // Check if any animations are enabled - function hasAnimationsEnabled() { - return !!( - controls.animateTextWave?.checked || - controls.animateTextWave2?.checked || - controls.animateBgRainbow?.checked || - controls.animateBgRainbowGradient?.checked || - controls.animateTextRainbow?.checked || - controls.animateTextRainbow2?.checked || - controls.animateGlitch?.checked || - controls.animatePulse?.checked || - controls.animateShimmer?.checked || - controls.animateScanline?.checked || - controls.animateRgbSplit?.checked || - controls.animateNoise?.checked || - controls.animateRotate?.checked - ); - } - - // Update download button label - function updateDownloadButtonLabel() { - const btn = document.getElementById("download-button"); - btn.textContent = "Download GIF"; - } - - // Export as animated GIF - async function exportAsGif() { - const downloadBtn = document.getElementById("download-button"); - const originalText = downloadBtn.textContent; - downloadBtn.disabled = true; - downloadBtn.textContent = "Generating GIF..."; - - try { - // Create temporary canvas for frame generation - const frameCanvas = document.createElement("canvas"); - frameCanvas.width = 88; - frameCanvas.height = 31; - const frameCtx = frameCanvas.getContext("2d"); - - // Initialize gif.js - const gif = new GIF({ - workers: 2, - quality: 10, - workerScript: "/js/gif.worker.js", - width: 88, - height: 31, - }); - - // Generate frames - const totalFrames = ANIMATION_CONFIG.totalFrames; - for (let i = 0; i < totalFrames; i++) { - const animState = new AnimationState(i, totalFrames); - drawToContext(frameCtx, animState); - - // Add frame to GIF (delay in ms) - gif.addFrame(frameCtx, { - delay: 1000 / ANIMATION_CONFIG.fps, - copy: true, - }); - - // Update progress - const progress = Math.round((i / totalFrames) * 100); - downloadBtn.textContent = `Generating: ${progress}%`; - - // Yield to browser to keep UI responsive - if (i % 5 === 0) { - await new Promise((resolve) => setTimeout(resolve, 0)); - } - } - - // Render GIF - gif.on("finished", (blob) => { - // Download - const link = document.createElement("a"); - link.download = "button-88x31.gif"; - link.href = URL.createObjectURL(blob); - link.click(); - - // Cleanup - URL.revokeObjectURL(link.href); - downloadBtn.disabled = false; - downloadBtn.textContent = originalText; - }); - - gif.on("progress", (progress) => { - const percent = Math.round(progress * 100); - downloadBtn.textContent = `Encoding: ${percent}%`; - }); - - gif.render(); - } catch (error) { - console.error("Error generating GIF:", error); - downloadBtn.disabled = false; - downloadBtn.textContent = originalText; - alert("Error generating GIF. Please try again."); - } - } - - // Download function - document - .getElementById("download-button") - .addEventListener("click", async () => { - await exportAsGif(); - }); - - // Preset buttons - document.getElementById("preset-random").addEventListener("click", () => { - const randomColor = () => - "#" + - Math.floor(Math.random() * 16777215) - .toString(16) - .padStart(6, "0"); - - // Random background - controls.bgType.value = ["solid", "gradient", "texture"][ - Math.floor(Math.random() * 3) - ]; - controls.bgColor.value = randomColor(); - controls.gradientColor1.value = randomColor(); - controls.gradientColor2.value = randomColor(); - controls.gradientAngle.value = Math.floor(Math.random() * 360); - controls.textureColor1.value = randomColor(); - controls.textureColor2.value = randomColor(); - controls.textureType.value = [ - "dots", - "grid", - "diagonal", - "checkerboard", - "noise", - "stars", - ][Math.floor(Math.random() * 6)]; - - // Random text 1 color (50% chance of gradient) - controls.textColorType.value = Math.random() > 0.5 ? "gradient" : "solid"; - controls.textColor.value = randomColor(); - controls.textGradientColor1.value = randomColor(); - controls.textGradientColor2.value = randomColor(); - controls.textGradientAngle.value = Math.floor(Math.random() * 360); - - // Random text 2 color (50% chance of gradient) - controls.text2ColorType.value = - Math.random() > 0.5 ? "gradient" : "solid"; - controls.text2Color.value = randomColor(); - controls.text2GradientColor1.value = randomColor(); - controls.text2GradientColor2.value = randomColor(); - controls.text2GradientAngle.value = Math.floor(Math.random() * 360); - - // Random border - controls.borderColor.value = randomColor(); - controls.borderWidth.value = Math.floor(Math.random() * 6); - controls.borderStyle.value = ["solid", "inset", "outset", "ridge"][ - Math.floor(Math.random() * 4) - ]; - - // Random text styles - controls.fontBold.checked = Math.random() > 0.5; - controls.fontItalic.checked = Math.random() > 0.5; - controls.fontBold2.checked = Math.random() > 0.5; - controls.fontItalic2.checked = Math.random() > 0.5; - - // Update displays - updateValueDisplay("gradient-angle", controls.gradientAngle.value); - updateValueDisplay( - "text-gradient-angle", - controls.textGradientAngle.value, - ); - updateValueDisplay( - "text2-gradient-angle", - controls.text2GradientAngle.value, - ); - updateValueDisplay("border-width", controls.borderWidth.value); - - controls.bgType.dispatchEvent(new Event("change")); - controls.textColorType.dispatchEvent(new Event("change")); - controls.text2ColorType.dispatchEvent(new Event("change")); - drawButton(); - }); - - document.getElementById("preset-classic").addEventListener("click", () => { - // Classic 90s web button style - controls.bgType.value = "gradient"; - controls.gradientColor1.value = "#6e6e6eff"; - controls.gradientColor2.value = "#979797"; - controls.gradientAngle.value = 90; - - controls.textColorType.value = "solid"; - controls.textColor.value = "#000000"; - controls.text2ColorType.value = "solid"; - controls.text2Color.value = "#000"; - - controls.borderWidth.value = 2; - controls.borderColor.value = "#000000"; - controls.borderStyle.value = "outset"; - - controls.fontFamily.value = "VT323"; - controls.fontFamily2.value = "VT323"; - controls.fontBold.checked = false; - controls.fontBold2.checked = false; - controls.fontItalic.checked = false; - controls.fontItalic2.checked = false; - - //controls.text.value = "RITUAL.SH"; - //controls.text2.value = "FREE THE WEB"; - controls.textEnabled.checked = true; - controls.text2Enabled.checked = true; - controls.fontSize.value = 12; - controls.fontSize2.value = 8; - controls.textY.value = 50; - controls.text2Y.value = 65; - - updateValueDisplay("font-size", 12); - updateValueDisplay("font-size2", 8); - updateValueDisplay("gradient-angle", 90); - updateValueDisplay("text-y", 35); - updateValueDisplay("text2-y", 65); - - controls.bgType.dispatchEvent(new Event("change")); - controls.textColorType.dispatchEvent(new Event("change")); - controls.text2ColorType.dispatchEvent(new Event("change")); - drawButton(); - }); - - document.getElementById("preset-modern").addEventListener("click", () => { - controls.bgType.value = "gradient"; - controls.gradientColor1.value = "#0a0a0a"; - controls.gradientColor2.value = "#1a0a2e"; - controls.gradientAngle.value = 135; - - controls.textColorType.value = "gradient"; - controls.textGradientColor1.value = "#00ffaa"; - controls.textGradientColor2.value = "#00ffff"; - controls.textGradientAngle.value = 90; - - controls.text2ColorType.value = "gradient"; - controls.text2GradientColor1.value = "#ff00ff"; - controls.text2GradientColor2.value = "#ff6600"; - controls.text2GradientAngle.value = 0; - - controls.borderWidth.value = 1; - controls.borderColor.value = "#00ffaa"; - controls.borderStyle.value = "solid"; - - controls.fontFamily.value = "Roboto Mono"; - controls.fontFamily2.value = "Roboto Mono"; - controls.fontBold.checked = true; - controls.fontBold2.checked = false; - controls.fontItalic.checked = false; - controls.fontItalic2.checked = false; - - //controls.text.value = "RITUAL.SH"; - //controls.text2.value = "EST. 2024"; - controls.textEnabled.checked = true; - controls.text2Enabled.checked = true; - controls.fontSize.value = 11; - controls.fontSize2.value = 9; - controls.textY.value = 35; - controls.text2Y.value = 65; - - updateValueDisplay("font-size", 11); - updateValueDisplay("font-size2", 9); - updateValueDisplay("gradient-angle", 135); - updateValueDisplay("text-gradient-angle", 90); - updateValueDisplay("text2-gradient-angle", 0); - updateValueDisplay("text-y", 35); - updateValueDisplay("text2-y", 65); - - controls.textColorType.dispatchEvent(new Event("change")); - controls.text2ColorType.dispatchEvent(new Event("change")); - controls.bgType.dispatchEvent(new Event("change")); - drawButton(); - }); - - // Add event listeners to all controls - Object.values(controls).forEach((control) => { - if (control) { - control.addEventListener("input", drawButton); - control.addEventListener("change", drawButton); - - if (control.type === "range") { - control.addEventListener("input", (e) => { - updateValueDisplay(e.target.id, e.target.value); - }); - } - } - }); - - // Animation control show/hide listeners - if (controls.animateTextWave) { - controls.animateTextWave.addEventListener("change", () => { - document.getElementById("wave-controls").style.display = controls - .animateTextWave.checked - ? "block" - : "none"; - }); - } - - if (controls.animateTextWave2) { - controls.animateTextWave2.addEventListener("change", () => { - document.getElementById("wave-controls2").style.display = controls - .animateTextWave2.checked - ? "block" - : "none"; - }); - } - - if (controls.animateBgRainbow) { - controls.animateBgRainbow.addEventListener("change", () => { - document.getElementById("rainbow-bg-controls").style.display = controls - .animateBgRainbow.checked - ? "block" - : "none"; - }); - } - - if (controls.animateTextRainbow) { - controls.animateTextRainbow.addEventListener("change", () => { - document.getElementById("rainbow-text-controls").style.display = - controls.animateTextRainbow.checked ? "block" : "none"; - }); - } - - if (controls.animateTextRainbow2) { - controls.animateTextRainbow2.addEventListener("change", () => { - document.getElementById("rainbow-text2-controls").style.display = - controls.animateTextRainbow2.checked ? "block" : "none"; - }); - } - - if (controls.animateGlitch) { - controls.animateGlitch.addEventListener("change", () => { - document.getElementById("glitch-controls").style.display = controls - .animateGlitch.checked - ? "block" - : "none"; - }); - } - - if (controls.animatePulse) { - controls.animatePulse.addEventListener("change", () => { - document.getElementById("pulse-controls").style.display = controls - .animatePulse.checked - ? "block" - : "none"; - }); - } - - if (controls.animateScanline) { - controls.animateScanline.addEventListener("change", () => { - document.getElementById("scanline-controls").style.display = controls - .animateScanline.checked - ? "block" - : "none"; - }); - } - - if (controls.animateRgbSplit) { - controls.animateRgbSplit.addEventListener("change", () => { - document.getElementById("rgb-split-controls").style.display = controls - .animateRgbSplit.checked - ? "block" - : "none"; - }); - } - - if (controls.animateNoise) { - controls.animateNoise.addEventListener("change", () => { - document.getElementById("noise-controls").style.display = controls - .animateNoise.checked - ? "block" - : "none"; - }); - } - - if (controls.animateRotate) { - controls.animateRotate.addEventListener("change", () => { - document.getElementById("rotate-controls").style.display = controls - .animateRotate.checked - ? "block" - : "none"; - }); - } - - // Initial draw - drawButton(); - } // end setupButtonGenerator -})(); diff --git a/assets/sass/pages/button-generator.scss b/assets/sass/pages/button-generator.scss index 17c0710..46d2af6 100644 --- a/assets/sass/pages/button-generator.scss +++ b/assets/sass/pages/button-generator.scss @@ -330,6 +330,75 @@ cursor: pointer; accent-color: #0096ff; } + + // Custom file input styling + input[type="file"] { + width: 100%; + padding: 0.75rem 1rem; + border: 1px solid rgba(0, 150, 255, 0.4); + border-radius: 4px; + font-size: 0.9rem; + background: rgba(5, 15, 30, 0.7); + color: rgba(200, 220, 255, 0.95); + transition: all 0.3s ease; + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.3); + cursor: pointer; + + &::file-selector-button { + padding: 0.5rem 1rem; + margin-right: 1rem; + border: 1px solid rgba(0, 150, 255, 0.5); + border-radius: 4px; + background: linear-gradient( + 135deg, + rgba(0, 120, 200, 0.3) 0%, + rgba(0, 100, 180, 0.2) 100% + ); + color: #66ccff; + cursor: pointer; + font-size: 0.85rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.08em; + transition: all 0.3s ease; + + &:hover { + background: linear-gradient( + 135deg, + rgba(0, 150, 255, 0.4) 0%, + rgba(255, 100, 0, 0.3) 100% + ); + border-color: rgba(0, 150, 255, 0.8); + box-shadow: + 0 0 15px rgba(0, 150, 255, 0.4), + 0 0 20px rgba(255, 120, 0, 0.2); + color: #fff; + text-shadow: 0 0 10px rgba(0, 150, 255, 0.6); + transform: translateY(-1px); + } + + &:active { + transform: translateY(0); + } + } + + &:hover { + border-color: rgba(0, 150, 255, 0.7); + box-shadow: + 0 0 15px rgba(0, 150, 255, 0.3), + inset 0 2px 8px rgba(0, 0, 0, 0.4); + background: rgba(5, 15, 30, 0.9); + } + + &:focus { + outline: none; + border-color: rgba(0, 150, 255, 0.7); + box-shadow: + 0 0 15px rgba(0, 150, 255, 0.3), + inset 0 2px 8px rgba(0, 0, 0, 0.4); + background: rgba(5, 15, 30, 0.9); + } + } } .control-group-header { diff --git a/static/js/button-generator/button-generator-core.js b/static/js/button-generator/button-generator-core.js index 1c6126a..fbca8eb 100644 --- a/static/js/button-generator/button-generator-core.js +++ b/static/js/button-generator/button-generator-core.js @@ -149,6 +149,18 @@ export class ButtonGenerator { values[id] = element.checked; } else if (element.type === 'range' || element.type === 'number') { values[id] = parseFloat(element.value); + } else if (element.type === 'file') { + // For file inputs, return an object with file metadata and blob URL + if (element.dataset.blobUrl) { + values[id] = { + fileName: element.dataset.fileName, + blobUrl: element.dataset.blobUrl, + fileSize: parseInt(element.dataset.fileSize), + fileType: element.dataset.fileType + }; + } else { + values[id] = null; + } } else { values[id] = element.value; } diff --git a/static/js/button-generator/effects/background-external-image.js b/static/js/button-generator/effects/background-external-image.js index 1e9fbf6..d3ffc9d 100644 --- a/static/js/button-generator/effects/background-external-image.js +++ b/static/js/button-generator/effects/background-external-image.js @@ -37,12 +37,13 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { defineControls() { return [ { - id: 'bg-image-url', - type: 'text', - label: 'Image URL', + id: 'bg-image-file', + type: 'file', + label: 'Image File', defaultValue: '', + accept: 'image/*', showWhen: 'bg-type', - description: 'URL of the image to use as background' + description: 'Select an image file from your computer' }, { id: 'bg-image-fit', @@ -53,11 +54,45 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { { value: 'cover', label: 'Cover (fill, crop if needed)' }, { value: 'contain', label: 'Contain (fit inside)' }, { value: 'stretch', label: 'Stretch (may distort)' }, - { value: 'center', label: 'Center (actual size)' } + { value: 'center', label: 'Center (actual size)' }, + { value: 'manual', label: 'Manual (custom zoom & position)' } ], showWhen: 'bg-type', description: 'How the image should fit in the canvas' }, + { + id: 'bg-image-zoom', + type: 'range', + label: 'Image Zoom', + defaultValue: 100, + min: 10, + max: 500, + step: 5, + showWhen: 'bg-image-fit', + description: 'Zoom level for manual positioning (percentage)' + }, + { + id: 'bg-image-offset-x', + type: 'range', + label: 'Horizontal Position', + defaultValue: 50, + min: 0, + max: 100, + step: 1, + showWhen: 'bg-image-fit', + description: 'Horizontal position of the image (percentage)' + }, + { + id: 'bg-image-offset-y', + type: 'range', + label: 'Vertical Position', + defaultValue: 50, + min: 0, + max: 100, + step: 1, + showWhen: 'bg-image-fit', + description: 'Vertical position of the image (percentage)' + }, { id: 'bg-image-opacity', type: 'range', @@ -77,24 +112,24 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { } /** - * Start loading an image from a URL with caching + * Start loading an image from a blob URL with caching * Triggers async loading but returns immediately - * @param {string} url - Image URL + * @param {string} blobUrl - Blob URL created from uploaded file */ - startLoadingImage(url) { + startLoadingImage(blobUrl) { // Skip if already cached or loading - if (this.imageCache.has(url) || this.loadingImages.has(url)) { + if (this.imageCache.has(blobUrl) || this.loadingImages.has(blobUrl)) { return; } // Mark as loading to prevent duplicate requests - this.loadingImages.set(url, true); + this.loadingImages.set(blobUrl, true); const img = new Image(); img.onload = () => { - this.imageCache.set(url, img); - this.loadingImages.delete(url); + this.imageCache.set(blobUrl, img); + this.loadingImages.delete(blobUrl); // Trigger a redraw when image loads const event = new CustomEvent('imageLoaded'); window.dispatchEvent(event); @@ -102,29 +137,29 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { img.onerror = () => { // Cache the error state to prevent retry spam - this.imageCache.set(url, 'ERROR'); - this.loadingImages.delete(url); + this.imageCache.set(blobUrl, 'ERROR'); + this.loadingImages.delete(blobUrl); // Trigger a redraw to show error state const event = new CustomEvent('imageLoaded'); window.dispatchEvent(event); }; - img.src = url; + img.src = blobUrl; } /** * Get cached image if available - * @param {string} url - Image URL + * @param {string} blobUrl - Blob URL * @returns {HTMLImageElement|string|null} Image element, 'ERROR', or null */ - getCachedImage(url) { - return this.imageCache.get(url) || null; + getCachedImage(blobUrl) { + return this.imageCache.get(blobUrl) || null; } /** * Draw image with the specified fit mode */ - drawImage(context, img, fitMode, opacity, width, height) { + drawImage(context, img, fitMode, opacity, width, height, zoom, offsetX, offsetY) { context.globalAlpha = opacity; const imgRatio = img.width / img.height; @@ -174,6 +209,31 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { drawY = (height - drawHeight) / 2; break; + case 'manual': + // Manual positioning with zoom and offset controls + const zoomFactor = zoom / 100; + + // Start with the larger dimension to ensure coverage + if (imgRatio > canvasRatio) { + // Image is wider relative to canvas + drawHeight = height * zoomFactor; + drawWidth = drawHeight * imgRatio; + } else { + // Image is taller relative to canvas + drawWidth = width * zoomFactor; + drawHeight = drawWidth / imgRatio; + } + + // Calculate the range of movement (how far can we move the image) + const maxOffsetX = drawWidth - width; + const maxOffsetY = drawHeight - height; + + // Convert percentage (0-100) to actual position + // 0% = image fully left/top, 100% = image fully right/bottom + drawX = -(maxOffsetX * offsetX / 100); + drawY = -(maxOffsetY * offsetY / 100); + break; + case 'stretch': // Stretch to fill (default values already set) break; @@ -184,12 +244,15 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { } apply(context, controlValues, animState, renderData) { - const url = controlValues['bg-image-url'] || ''; + const file = controlValues['bg-image-file']; const fitMode = controlValues['bg-image-fit'] || 'cover'; const opacity = controlValues['bg-image-opacity'] ?? 1; + const zoom = controlValues['bg-image-zoom'] ?? 100; + const offsetX = controlValues['bg-image-offset-x'] ?? 50; + const offsetY = controlValues['bg-image-offset-y'] ?? 50; - // If no URL provided, fill with a placeholder color - if (!url.trim()) { + // If no file selected, fill with a placeholder color + if (!file || !file.blobUrl) { context.fillStyle = '#cccccc'; context.fillRect(0, 0, renderData.width, renderData.height); @@ -198,16 +261,16 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { context.font = '8px Arial'; context.textAlign = 'center'; context.textBaseline = 'middle'; - context.fillText('Enter image URL', renderData.centerX, renderData.centerY); + context.fillText('Select an image', renderData.centerX, renderData.centerY); return; } // Start loading if not already cached or loading - const cachedImage = this.getCachedImage(url); + const cachedImage = this.getCachedImage(file.blobUrl); if (!cachedImage) { // Not cached yet - start loading - this.startLoadingImage(url); + this.startLoadingImage(file.blobUrl); // Draw loading state context.fillStyle = '#3498db'; @@ -236,7 +299,7 @@ export class ExternalImageBackgroundEffect extends ButtonEffect { } // Image is loaded - draw it - this.drawImage(context, cachedImage, fitMode, opacity, renderData.width, renderData.height); + this.drawImage(context, cachedImage, fitMode, opacity, renderData.width, renderData.height, zoom, offsetX, offsetY); } } diff --git a/static/js/button-generator/effects/background-solid.js b/static/js/button-generator/effects/background-solid.js index 6ba8155..a835100 100644 --- a/static/js/button-generator/effects/background-solid.js +++ b/static/js/button-generator/effects/background-solid.js @@ -26,7 +26,7 @@ export class SolidBackgroundEffect extends ButtonEffect { { value: "gradient", label: "Gradient" }, { value: "texture", label: "Texture" }, { value: "emoji-wallpaper", label: "Emoji Wallpaper" }, - // { value: 'external-image', label: 'External Image' } + { value: 'external-image', label: 'Image Upload' } ], }, { diff --git a/static/js/button-generator/main.js b/static/js/button-generator/main.js index 71c400e..c6061dc 100644 --- a/static/js/button-generator/main.js +++ b/static/js/button-generator/main.js @@ -89,7 +89,7 @@ async function setupApp() { gradientBg.register(generator); textureBg.register(generator); emojiWallpaper.register(generator); - //externalImage.register(generator); + externalImage.register(generator); rainbowBg.register(generator); rain.register(generator); starfield.register(generator); diff --git a/static/js/button-generator/ui-builder.js b/static/js/button-generator/ui-builder.js index afafbbe..2ab5e1d 100644 --- a/static/js/button-generator/ui-builder.js +++ b/static/js/button-generator/ui-builder.js @@ -261,6 +261,9 @@ export class UIBuilder { case 'text': return this.createTextInput(id, label, defaultValue, showWhen, description); + case 'file': + return this.createFileInput(id, label, defaultValue, showWhen, description, controlDef.accept); + default: console.warn(`Unknown control type: ${type}`); return null; @@ -438,6 +441,57 @@ export class UIBuilder { return container; } + /** + * Create a file input control + */ + createFileInput(id, label, defaultValue, showWhen, description, accept) { + const container = document.createElement('div'); + + const labelEl = document.createElement('label'); + labelEl.htmlFor = id; + labelEl.textContent = label; + + const input = document.createElement('input'); + input.type = 'file'; + input.id = id; + if (accept) { + input.accept = accept; + } + + // Store the file data on the input element + input.addEventListener('change', (e) => { + const file = e.target.files[0]; + if (file) { + // Create a blob URL for the file + const blobUrl = URL.createObjectURL(file); + // Store file metadata on the input element + input.dataset.fileName = file.name; + input.dataset.blobUrl = blobUrl; + input.dataset.fileSize = file.size; + input.dataset.fileType = file.type; + } else { + // Clear the data if no file is selected + delete input.dataset.fileName; + delete input.dataset.blobUrl; + delete input.dataset.fileSize; + delete input.dataset.fileType; + } + }); + + container.appendChild(labelEl); + container.appendChild(input); + + if (showWhen) { + container.style.display = 'none'; + container.dataset.showWhen = showWhen; + } + + // Add tooltip handlers to the label + this.addTooltipHandlers(labelEl, description); + + return container; + } + /** * Setup conditional visibility for controls * Should be called after all controls are created @@ -468,10 +522,16 @@ export class UIBuilder { control.style.display = triggerControl.value === 'texture' ? 'block' : 'none'; } else if (controlId && (controlId.startsWith('emoji-') || controlId === 'emoji-text')) { control.style.display = triggerControl.value === 'emoji-wallpaper' ? 'block' : 'none'; - } else if (controlId && (controlId.startsWith('bg-image-') || controlId === 'bg-image-url' || controlId === 'bg-image-fit' || controlId === 'bg-image-opacity')) { + } else if (controlId && (controlId.startsWith('bg-image-') || controlId === 'bg-image-file' || controlId === 'bg-image-fit' || controlId === 'bg-image-opacity')) { control.style.display = triggerControl.value === 'external-image' ? 'block' : 'none'; } } + // For image fit controls (zoom and position only show when manual mode) + else if (triggerControlId === 'bg-image-fit') { + if (controlId && (controlId === 'bg-image-zoom' || controlId === 'bg-image-offset-x' || controlId === 'bg-image-offset-y')) { + control.style.display = triggerControl.value === 'manual' ? 'block' : 'none'; + } + } // For text color controls else if (triggerControlId === 'text-color-type') { if (controlId === 'text-color') { From 00805f4344c47b37ee1cef56f6c1bf292c385d2b Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 19:16:22 +0000 Subject: [PATCH 36/55] Fixing rain frame alignment --- .../effects/background-rain.js | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/static/js/button-generator/effects/background-rain.js b/static/js/button-generator/effects/background-rain.js index fc3a609..44e7637 100644 --- a/static/js/button-generator/effects/background-rain.js +++ b/static/js/button-generator/effects/background-rain.js @@ -42,10 +42,10 @@ export class RainBackgroundEffect extends ButtonEffect { id: 'rain-speed', type: 'range', label: 'Rain Speed', - defaultValue: 1.5, - min: 0.5, + defaultValue: 2, + min: 1, max: 3, - step: 0.1, + step: 1, showWhen: 'animate-rain', description: 'Speed of falling rain' }, @@ -71,15 +71,27 @@ export class RainBackgroundEffect extends ButtonEffect { const speed = controlValues['rain-speed'] || 1.5; const color = controlValues['rain-color'] || '#6ba3ff'; - // Initialize raindrops on first frame + // Initialize raindrop base properties (seed values for deterministic animation) if (!this.initialized || this.raindrops.length !== density) { this.raindrops = []; for (let i = 0; i < density; i++) { + // Use multiple large prime seeds for better distribution + const seed1 = i * 2654435761; // Large prime multiplier + const seed2 = i * 2246822519 + 3141592653; + const seed3 = i * 3266489917 + 1618033988; + const seed4 = i * 374761393 + 2718281828; + + // Hash-like function for better pseudo-random distribution + const hash = (s) => { + const x = Math.sin(s * 0.0001) * 10000; + return x - Math.floor(x); + }; + this.raindrops.push({ - x: Math.random() * renderData.width, - y: Math.random() * renderData.height, - length: 2 + Math.random() * 4, - speedMultiplier: 0.8 + Math.random() * 0.4 + xOffset: hash(seed1), // 0 to 1 + startY: hash(seed2), // 0 to 1 (initial Y position within loop) + length: 2 + hash(seed3) * 4, + speedMultiplier: 0.8 + hash(seed4) * 0.4 }); } this.initialized = true; @@ -91,20 +103,30 @@ export class RainBackgroundEffect extends ButtonEffect { context.lineCap = 'round'; this.raindrops.forEach(drop => { - // Update position - drop.y += speed * drop.speedMultiplier; + // Calculate position based on current frame for perfect looping + const totalDistance = renderData.height + drop.length * 2; - // Reset to top when reaching bottom - if (drop.y > renderData.height + drop.length) { - drop.y = -drop.length; - drop.x = Math.random() * renderData.width; - } + // Progress through the animation (0 to 1) + const progress = animState.progress; // 0 at frame 0, ~0.975 at frame 39 + + // All drops complete the same number of cycles (based on speed) + // startY provides the offset for varied starting positions + // Round speed to nearest 0.5 to ensure clean cycles + const cycles = Math.round(speed * 2) / 2; // e.g., 1.5 -> 1.5, 1.7 -> 1.5, 2.3 -> 2.5 + const cycleProgress = (progress * cycles + drop.startY) % 1.0; + const y = cycleProgress * totalDistance - drop.length; + + // X position remains constant throughout the loop + const x = drop.xOffset * renderData.width; + + // Vary opacity slightly for depth effect (using speedMultiplier for variation) + const opacity = 0.4 + drop.speedMultiplier * 0.3; // 0.64 to 0.76 // Draw raindrop - context.globalAlpha = 0.6; + context.globalAlpha = opacity; context.beginPath(); - context.moveTo(drop.x, drop.y); - context.lineTo(drop.x, drop.y + drop.length); + context.moveTo(x, y); + context.lineTo(x, y + drop.length); context.stroke(); context.globalAlpha = 1.0; }); From c47225da432c4bbbda56196dbac3dbecb55e970a Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 19:23:28 +0000 Subject: [PATCH 37/55] fix star looping --- .../effects/background-starfield.js | 107 +++++++++++------- 1 file changed, 64 insertions(+), 43 deletions(-) diff --git a/static/js/button-generator/effects/background-starfield.js b/static/js/button-generator/effects/background-starfield.js index 1440d32..4d71d6c 100644 --- a/static/js/button-generator/effects/background-starfield.js +++ b/static/js/button-generator/effects/background-starfield.js @@ -15,7 +15,6 @@ export class StarfieldEffect extends ButtonEffect { }); this.stars = []; - this.shootingStars = []; this.initialized = false; } @@ -25,8 +24,6 @@ export class StarfieldEffect extends ButtonEffect { id: "animate-starfield", type: "checkbox", label: "Starfield Effect", - description: - "This might look a bit different when exported, work in progress!", defaultValue: false, }, { @@ -82,16 +79,31 @@ export class StarfieldEffect extends ButtonEffect { const shootingEnabled = controlValues["star-shooting-enabled"] !== false; const starColor = controlValues["star-color"] || "#ffffff"; - // Initialize stars on first frame or density change + // Initialize stars with deterministic positions (seed-based) if (!this.initialized || this.stars.length !== density) { this.stars = []; for (let i = 0; i < density; i++) { + // Use density as part of the seed so pattern changes with density + // Use multiple large prime seeds for better distribution + const densitySeed = density * 97531; // Density affects all stars + const seed1 = (i * 2654435761) + densitySeed; + const seed2 = (i * 2246822519) + 3141592653 + densitySeed; + const seed3 = (i * 3266489917) + 1618033988 + densitySeed; + const seed4 = (i * 374761393) + 2718281828 + densitySeed; + const seed5 = (i * 1103515245) + 12345 + densitySeed; + + // Hash-like function for better pseudo-random distribution + const hash = (s) => { + const x = Math.sin(s * 0.0001) * 10000; + return x - Math.floor(x); + }; + this.stars.push({ - x: Math.random() * renderData.width, - y: Math.random() * renderData.height, - size: 0.5 + Math.random() * 1.5, - twinkleOffset: Math.random() * Math.PI * 2, - twinkleSpeed: 0.5 + Math.random() * 1.5, + x: hash(seed1) * renderData.width, + y: hash(seed2) * renderData.height, + size: 0.5 + hash(seed3) * 1.5, + twinkleOffset: hash(seed4) * Math.PI * 2, + twinkleSpeed: 0.5 + hash(seed5) * 1.5, }); } this.initialized = true; @@ -116,46 +128,55 @@ export class StarfieldEffect extends ButtonEffect { context.globalAlpha = 1.0; }); - // Shooting stars + // Shooting stars - deterministic based on frame number if (shootingEnabled) { - // Randomly spawn shooting stars - if (Math.random() < 0.02 && this.shootingStars.length < 3) { - this.shootingStars.push({ - x: Math.random() * renderData.width, - y: -10, - vx: (Math.random() - 0.5) * 2, - vy: 3 + Math.random() * 2, - life: 1.0, - }); - } + // Define shooting star spawn schedule (deterministic) + const shootingStarSpawns = [ + { startFrame: 5, seed: 12345 }, + { startFrame: 18, seed: 67890 }, + { startFrame: 31, seed: 24680 }, + ]; - // Update and draw shooting stars - this.shootingStars = this.shootingStars.filter((star) => { - star.x += star.vx; - star.y += star.vy; - star.life -= 0.02; + shootingStarSpawns.forEach((spawn) => { + const framesSinceSpawn = animState.frame - spawn.startFrame; + const duration = 25; // Frames the shooting star is visible - if (star.life > 0) { - // Draw shooting star trail - const gradient = context.createLinearGradient( - star.x, - star.y, - star.x - star.vx * 5, - star.y - star.vy * 5, - ); - gradient.addColorStop(0, `rgba(255, 255, 255, ${star.life * 0.8})`); - gradient.addColorStop(1, "rgba(255, 255, 255, 0)"); + if (framesSinceSpawn >= 0 && framesSinceSpawn < duration) { + // Calculate deterministic properties from seed + const hash = (s) => { + const x = Math.sin(s * 0.0001) * 10000; + return x - Math.floor(x); + }; - context.strokeStyle = gradient; - context.lineWidth = 2; - context.beginPath(); - context.moveTo(star.x, star.y); - context.lineTo(star.x - star.vx * 5, star.y - star.vy * 5); - context.stroke(); + const startX = hash(spawn.seed * 1) * renderData.width; + const startY = -10; + const vx = (hash(spawn.seed * 2) - 0.5) * 2; + const vy = 3 + hash(spawn.seed * 3) * 2; - return true; + // Calculate position based on frames elapsed + const x = startX + vx * framesSinceSpawn; + const y = startY + vy * framesSinceSpawn; + const life = 1.0 - framesSinceSpawn / duration; + + if (life > 0) { + // Draw shooting star trail + const gradient = context.createLinearGradient( + x, + y, + x - vx * 5, + y - vy * 5, + ); + gradient.addColorStop(0, `rgba(255, 255, 255, ${life * 0.8})`); + gradient.addColorStop(1, "rgba(255, 255, 255, 0)"); + + context.strokeStyle = gradient; + context.lineWidth = 2; + context.beginPath(); + context.moveTo(x, y); + context.lineTo(x - vx * 5, y - vy * 5); + context.stroke(); + } } - return false; }); } } From 5a004981a9d495f0d4d290553c6649e204f544c0 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 19:31:11 +0000 Subject: [PATCH 38/55] tweaked rendering order --- static/js/button-generator/button-generator-core.js | 4 ++-- static/js/button-generator/effects/background-aurora.js | 4 ++-- static/js/button-generator/effects/background-bubbles.js | 4 ++-- static/js/button-generator/effects/background-fire.js | 4 ++-- static/js/button-generator/effects/background-rain.js | 4 ++-- static/js/button-generator/effects/background-starfield.js | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/static/js/button-generator/button-generator-core.js b/static/js/button-generator/button-generator-core.js index fbca8eb..744fddf 100644 --- a/static/js/button-generator/button-generator-core.js +++ b/static/js/button-generator/button-generator-core.js @@ -186,8 +186,8 @@ export class ButtonGenerator { centerY: this.canvas.height / 2 }; - // Apply effects in order: transform -> background -> border -> text/text2 -> general - const renderOrder = ['transform', 'background', 'border', 'text', 'text2', 'general']; + // Apply effects in order: transform -> background -> background-animation -> text/text2 -> border -> general + const renderOrder = ['transform', 'background', 'background-animation', 'text', 'text2', 'border', 'general']; // Save context once before transforms this.ctx.save(); diff --git a/static/js/button-generator/effects/background-aurora.js b/static/js/button-generator/effects/background-aurora.js index 464a107..2afad33 100644 --- a/static/js/button-generator/effects/background-aurora.js +++ b/static/js/button-generator/effects/background-aurora.js @@ -9,9 +9,9 @@ export class AuroraEffect extends ButtonEffect { super({ id: "bg-aurora", name: "Aurora", - type: "general", + type: "background-animation", category: "Background Animations", - renderOrder: 55, + renderOrder: 10, }); } diff --git a/static/js/button-generator/effects/background-bubbles.js b/static/js/button-generator/effects/background-bubbles.js index be3fc8f..a72106c 100644 --- a/static/js/button-generator/effects/background-bubbles.js +++ b/static/js/button-generator/effects/background-bubbles.js @@ -9,9 +9,9 @@ export class BubblesEffect extends ButtonEffect { super({ id: "bg-bubbles", name: "Bubbles", - type: "general", + type: "background-animation", category: "Background Animations", - renderOrder: 55, + renderOrder: 10, }); this.bubbles = []; diff --git a/static/js/button-generator/effects/background-fire.js b/static/js/button-generator/effects/background-fire.js index e6072d5..0e3fb2c 100644 --- a/static/js/button-generator/effects/background-fire.js +++ b/static/js/button-generator/effects/background-fire.js @@ -9,9 +9,9 @@ export class FireEffect extends ButtonEffect { super({ id: "bg-fire", name: "Fire", - type: "general", + type: "background-animation", category: "Background Animations", - renderOrder: 55, + renderOrder: 10, }); this.particles = []; diff --git a/static/js/button-generator/effects/background-rain.js b/static/js/button-generator/effects/background-rain.js index 44e7637..eb3b928 100644 --- a/static/js/button-generator/effects/background-rain.js +++ b/static/js/button-generator/effects/background-rain.js @@ -9,9 +9,9 @@ export class RainBackgroundEffect extends ButtonEffect { super({ id: 'bg-rain', name: 'Rain Effect', - type: 'general', + type: 'background-animation', category: 'Background Animations', - renderOrder: 55 // After background, before other effects + renderOrder: 10 }); // Initialize raindrop positions (persistent across frames) diff --git a/static/js/button-generator/effects/background-starfield.js b/static/js/button-generator/effects/background-starfield.js index 4d71d6c..61ed860 100644 --- a/static/js/button-generator/effects/background-starfield.js +++ b/static/js/button-generator/effects/background-starfield.js @@ -9,9 +9,9 @@ export class StarfieldEffect extends ButtonEffect { super({ id: "bg-starfield", name: "Starfield", - type: "general", + type: "background-animation", category: "Background Animations", - renderOrder: 55, + renderOrder: 10, }); this.stars = []; @@ -42,7 +42,7 @@ export class StarfieldEffect extends ButtonEffect { type: "range", label: "Twinkle Speed", defaultValue: 1, - min: 0.1, + min: 1, max: 3, step: 0.1, showWhen: "animate-starfield", From b27f9cfb3946fe257389e630cb1123e98c2f4d1e Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Fri, 9 Jan 2026 20:20:18 +0000 Subject: [PATCH 39/55] adding advanced settings --- .../button-generator/button-generator-core.js | 58 +++- static/js/button-generator/color-quantizer.js | 263 ++++++++++++++++++ static/js/button-generator/main.js | 107 +++++++ 3 files changed, 423 insertions(+), 5 deletions(-) create mode 100644 static/js/button-generator/color-quantizer.js diff --git a/static/js/button-generator/button-generator-core.js b/static/js/button-generator/button-generator-core.js index 744fddf..292bc4f 100644 --- a/static/js/button-generator/button-generator-core.js +++ b/static/js/button-generator/button-generator-core.js @@ -1,5 +1,7 @@ import { ButtonEffect } from './effect-base.js'; +import { ColorQuantizer } from './color-quantizer.js'; + /** * Animation state class - passed to effects for frame-based rendering */ @@ -7,7 +9,7 @@ export class AnimationState { constructor(frameNumber = 0, totalFrames = 40, fps = 20) { this.frame = frameNumber; this.totalFrames = totalFrames; - this.progress = frameNumber / totalFrames; // 0 to 1 + this.progress = totalFrames > 1 ? frameNumber / (totalFrames - 1) : 0; // 0 to 1, inclusive of last frame this.fps = fps; this.time = (frameNumber / fps) * 1000; // milliseconds } @@ -39,6 +41,13 @@ export class ButtonGenerator { } }; + // GIF export configuration + this.gifConfig = { + quality: config.gifQuality || 1, // 1-30, lower is better quality quantization + dither: config.gifDither || false, // false, 'FloydSteinberg', 'FalseFloydSteinberg', 'Stucki', 'Atkinson' + colorCount: config.gifColorCount || 256, // 2-256, number of colors to reduce to (custom quantization) + }; + // Effect registry organized by type this.effects = { transform: [], @@ -250,6 +259,7 @@ export class ButtonGenerator { this.animConfig.fps ); this.draw(animState); + this.applyPreviewQuantization(); frameNum = (frameNum + 1) % this.animConfig.totalFrames; lastFrameTime = currentTime - (elapsed % frameDelay); @@ -280,15 +290,31 @@ export class ButtonGenerator { } else { this.stopAnimatedPreview(); this.draw(); + this.applyPreviewQuantization(); + } + } + + /** + * Apply color quantization to preview if enabled + */ + applyPreviewQuantization() { + const colorCount = this.gifConfig.colorCount; + if (colorCount < 256) { + const quantizedData = ColorQuantizer.quantize(this.canvas, colorCount, 'floyd-steinberg'); + this.ctx.putImageData(quantizedData, 0, 0); } } /** * Export as animated GIF * @param {Function} progressCallback - Called with progress (0-1) + * @param {Object} options - Export options + * @param {number} options.quality - Quality (1-30, lower is better, default: 10) + * @param {boolean|string} options.dither - Dithering algorithm for gif.js + * @param {number} options.colorCount - Number of colors (2-256, default: 256) - uses custom quantization * @returns {Promise<Blob>} */ - async exportAsGif(progressCallback = null) { + async exportAsGif(progressCallback = null, options = {}) { return new Promise((resolve, reject) => { try { // Create temporary canvas for frame generation @@ -297,14 +323,30 @@ export class ButtonGenerator { frameCanvas.height = this.canvas.height; const frameCtx = frameCanvas.getContext('2d'); + // Merge options with defaults + const quality = options.quality !== undefined ? options.quality : this.gifConfig.quality; + const gifDither = options.dither !== undefined ? options.dither : this.gifConfig.dither; + const colorCount = options.colorCount !== undefined ? options.colorCount : this.gifConfig.colorCount; + + // Determine if we need custom quantization + const useCustomQuantization = colorCount < 256; + const customDither = useCustomQuantization ? 'floyd-steinberg' : false; + // Initialize gif.js - const gif = new GIF({ + const gifOptions = { workers: 2, - quality: 10, + quality: quality, workerScript: '/js/gif.worker.js', width: this.canvas.width, height: this.canvas.height - }); + }; + + // Add gif.js dither option if specified (only when not using custom quantization) + if (!useCustomQuantization && gifDither !== false) { + gifOptions.dither = gifDither; + } + + const gif = new GIF(gifOptions); // Generate frames const totalFrames = this.animConfig.totalFrames; @@ -328,6 +370,12 @@ export class ButtonGenerator { tempGenerator.draw(animState); + // Apply custom color quantization if needed + if (useCustomQuantization) { + const quantizedData = ColorQuantizer.quantize(frameCanvas, colorCount, customDither); + frameCtx.putImageData(quantizedData, 0, 0); + } + gif.addFrame(frameCtx, { delay: 1000 / this.animConfig.fps, copy: true diff --git a/static/js/button-generator/color-quantizer.js b/static/js/button-generator/color-quantizer.js new file mode 100644 index 0000000..f2343ba --- /dev/null +++ b/static/js/button-generator/color-quantizer.js @@ -0,0 +1,263 @@ +/** + * Color Quantizer - Custom color reduction with median cut algorithm + * Reduces canvas colors with optional dithering for retro aesthetic + */ + +export class ColorQuantizer { + /** + * Reduce colors in a canvas using median cut algorithm + * @param {HTMLCanvasElement} canvas - Canvas to quantize + * @param {number} colorCount - Target number of colors (2-256) + * @param {string|boolean} dither - Dithering algorithm ('floyd-steinberg', false) + * @returns {ImageData} Quantized image data + */ + static quantize(canvas, colorCount = 256, dither = false) { + const ctx = canvas.getContext('2d'); + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + const pixels = imageData.data; + + if (colorCount >= 256) { + return imageData; // No quantization needed + } + + // Build palette using median cut + const palette = this.buildPalette(pixels, colorCount); + + // Apply palette to image + if (dither === 'floyd-steinberg') { + this.applyPaletteWithDithering(pixels, palette, canvas.width, canvas.height); + } else { + this.applyPalette(pixels, palette); + } + + return imageData; + } + + /** + * Build color palette using median cut algorithm + */ + static buildPalette(pixels, colorCount) { + // Collect unique colors + const colorMap = new Map(); + for (let i = 0; i < pixels.length; i += 4) { + const r = pixels[i]; + const g = pixels[i + 1]; + const b = pixels[i + 2]; + const a = pixels[i + 3]; + + if (a === 0) continue; // Skip transparent pixels + + const key = (r << 16) | (g << 8) | b; + colorMap.set(key, (colorMap.get(key) || 0) + 1); + } + + // Convert to array of color objects with counts + const colors = Array.from(colorMap.entries()).map(([key, count]) => ({ + r: (key >> 16) & 0xFF, + g: (key >> 8) & 0xFF, + b: key & 0xFF, + count: count + })); + + // If we have fewer colors than target, return as-is + if (colors.length <= colorCount) { + return colors.map(c => [c.r, c.g, c.b]); + } + + // Start with all colors in one bucket + let buckets = [colors]; + + // Split buckets until we have desired number + while (buckets.length < colorCount) { + // Find bucket with largest range + let maxRange = -1; + let maxBucketIdx = 0; + let maxChannel = 'r'; + + buckets.forEach((bucket, idx) => { + if (bucket.length <= 1) return; + + const ranges = this.getColorRanges(bucket); + const range = Math.max(ranges.r, ranges.g, ranges.b); + + if (range > maxRange) { + maxRange = range; + maxBucketIdx = idx; + if (ranges.r >= ranges.g && ranges.r >= ranges.b) maxChannel = 'r'; + else if (ranges.g >= ranges.b) maxChannel = 'g'; + else maxChannel = 'b'; + } + }); + + if (maxRange === -1) break; // Can't split further + + // Split the bucket + const bucket = buckets[maxBucketIdx]; + bucket.sort((a, b) => a[maxChannel] - b[maxChannel]); + + const mid = Math.floor(bucket.length / 2); + const bucket1 = bucket.slice(0, mid); + const bucket2 = bucket.slice(mid); + + buckets.splice(maxBucketIdx, 1, bucket1, bucket2); + } + + // Average colors in each bucket to create palette + return buckets.map(bucket => { + let totalWeight = 0; + let sumR = 0, sumG = 0, sumB = 0; + + bucket.forEach(color => { + const weight = color.count; + totalWeight += weight; + sumR += color.r * weight; + sumG += color.g * weight; + sumB += color.b * weight; + }); + + return [ + Math.round(sumR / totalWeight), + Math.round(sumG / totalWeight), + Math.round(sumB / totalWeight) + ]; + }); + } + + /** + * Get color ranges in a bucket + */ + static getColorRanges(bucket) { + let minR = 255, maxR = 0; + let minG = 255, maxG = 0; + let minB = 255, maxB = 0; + + bucket.forEach(color => { + minR = Math.min(minR, color.r); + maxR = Math.max(maxR, color.r); + minG = Math.min(minG, color.g); + maxG = Math.max(maxG, color.g); + minB = Math.min(minB, color.b); + maxB = Math.max(maxB, color.b); + }); + + return { + r: maxR - minR, + g: maxG - minG, + b: maxB - minB + }; + } + + /** + * Find nearest color in palette + */ + static findNearestColor(r, g, b, palette) { + let minDist = Infinity; + let nearest = palette[0]; + + for (const color of palette) { + const dr = r - color[0]; + const dg = g - color[1]; + const db = b - color[2]; + const dist = dr * dr + dg * dg + db * db; + + if (dist < minDist) { + minDist = dist; + nearest = color; + } + } + + return nearest; + } + + /** + * Apply palette without dithering + */ + static applyPalette(pixels, palette) { + for (let i = 0; i < pixels.length; i += 4) { + const r = pixels[i]; + const g = pixels[i + 1]; + const b = pixels[i + 2]; + const a = pixels[i + 3]; + + if (a === 0) continue; + + const nearest = this.findNearestColor(r, g, b, palette); + pixels[i] = nearest[0]; + pixels[i + 1] = nearest[1]; + pixels[i + 2] = nearest[2]; + } + } + + /** + * Apply palette with Floyd-Steinberg dithering + */ + static applyPaletteWithDithering(pixels, palette, width, height) { + // Create error buffer + const errors = new Float32Array(width * height * 3); + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const idx = (y * width + x) * 4; + const errIdx = (y * width + x) * 3; + + const r = pixels[idx]; + const g = pixels[idx + 1]; + const b = pixels[idx + 2]; + const a = pixels[idx + 3]; + + if (a === 0) continue; + + // Add accumulated error + const newR = Math.max(0, Math.min(255, r + errors[errIdx])); + const newG = Math.max(0, Math.min(255, g + errors[errIdx + 1])); + const newB = Math.max(0, Math.min(255, b + errors[errIdx + 2])); + + // Find nearest palette color + const nearest = this.findNearestColor(newR, newG, newB, palette); + + // Set pixel to nearest color + pixels[idx] = nearest[0]; + pixels[idx + 1] = nearest[1]; + pixels[idx + 2] = nearest[2]; + + // Calculate error + const errR = newR - nearest[0]; + const errG = newG - nearest[1]; + const errB = newB - nearest[2]; + + // Distribute error to neighboring pixels (Floyd-Steinberg) + // Right pixel (x+1, y): 7/16 + if (x + 1 < width) { + const rightIdx = (y * width + (x + 1)) * 3; + errors[rightIdx] += errR * 7 / 16; + errors[rightIdx + 1] += errG * 7 / 16; + errors[rightIdx + 2] += errB * 7 / 16; + } + + // Bottom-left pixel (x-1, y+1): 3/16 + if (y + 1 < height && x > 0) { + const blIdx = ((y + 1) * width + (x - 1)) * 3; + errors[blIdx] += errR * 3 / 16; + errors[blIdx + 1] += errG * 3 / 16; + errors[blIdx + 2] += errB * 3 / 16; + } + + // Bottom pixel (x, y+1): 5/16 + if (y + 1 < height) { + const bottomIdx = ((y + 1) * width + x) * 3; + errors[bottomIdx] += errR * 5 / 16; + errors[bottomIdx + 1] += errG * 5 / 16; + errors[bottomIdx + 2] += errB * 5 / 16; + } + + // Bottom-right pixel (x+1, y+1): 1/16 + if (y + 1 < height && x + 1 < width) { + const brIdx = ((y + 1) * width + (x + 1)) * 3; + errors[brIdx] += errR * 1 / 16; + errors[brIdx + 1] += errG * 1 / 16; + errors[brIdx + 2] += errB * 1 / 16; + } + } + } + } +} diff --git a/static/js/button-generator/main.js b/static/js/button-generator/main.js index c6061dc..030236d 100644 --- a/static/js/button-generator/main.js +++ b/static/js/button-generator/main.js @@ -126,6 +126,9 @@ async function setupApp() { uiBuilder.buildUI(generator.getAllEffects()); uiBuilder.setupConditionalVisibility(); + // Add GIF export settings at the bottom + addGifExportSettings(controlsContainer, generator); + // Preload fonts console.log("Loading fonts..."); await generator.preloadFonts(); @@ -148,6 +151,110 @@ async function setupApp() { console.log("Button Generator ready!"); } +/** + * Add GIF export settings controls + */ +function addGifExportSettings(container, generator) { + const groupDiv = document.createElement('div'); + groupDiv.className = 'control-group collapsed'; + + // Header + const header = document.createElement('h3'); + header.className = 'control-group-header'; + header.textContent = 'Advanced Settings'; + groupDiv.appendChild(header); + + const controlsDiv = document.createElement('div'); + controlsDiv.className = 'control-group-controls'; + + // Color count control + const colorsWrapper = document.createElement('div'); + colorsWrapper.className = 'control-wrapper'; + colorsWrapper.innerHTML = ` + <div class="info-text">Note: This only affects frame-by-frame settings, i.e. 8 colours would be 8 colours per frame. I am working on a solution for this.</div> + <label for="gif-colors"> + Color Count + <span class="control-description">(16-32 for the 90s experience)</span> + </label> + <div class="range-container"> + <input type="range" id="gif-colors" min="2" max="256" step="1" value="256"> + <span id="gif-colors-value" class="range-value">256</span> + </div> + `; + controlsDiv.appendChild(colorsWrapper); + + // Quality control + const qualityWrapper = document.createElement('div'); + qualityWrapper.className = 'control-wrapper'; + qualityWrapper.innerHTML = ` + <label for="gif-quality"> + Quantization Quality + <span class="control-description">(lower = better but slower export)</span> + </label> + <div class="range-container"> + <input type="range" id="gif-quality" min="1" max="30" step="1" value="1"> + <span id="gif-quality-value" class="range-value">1</span> + </div> + `; + controlsDiv.appendChild(qualityWrapper); + + // Dither control + const ditherWrapper = document.createElement('div'); + ditherWrapper.className = 'control-wrapper'; + ditherWrapper.innerHTML = ` + <label for="gif-dither"> + Dithering Pattern + <span class="control-description"></span> + </label> + <select id="gif-dither"> + <option value="false">None (Smooth)</option> + <option value="FloydSteinberg">Floyd-Steinberg (Classic)</option> + <option value="FalseFloydSteinberg">False Floyd-Steinberg (Fast)</option> + <option value="Stucki">Stucki (Distributed)</option> + <option value="Atkinson">Atkinson (Retro Mac)</option> + </select> + `; + controlsDiv.appendChild(ditherWrapper); + + groupDiv.appendChild(controlsDiv); + container.appendChild(groupDiv); + + // Setup event listeners for value display + const colorsInput = document.getElementById('gif-colors'); + const colorsValue = document.getElementById('gif-colors-value'); + colorsInput.addEventListener('input', () => { + colorsValue.textContent = colorsInput.value; + }); + + const qualityInput = document.getElementById('gif-quality'); + const qualityValue = document.getElementById('gif-quality-value'); + qualityInput.addEventListener('input', () => { + qualityValue.textContent = qualityInput.value; + }); + + // Update generator config when changed + colorsInput.addEventListener('change', () => { + generator.gifConfig.colorCount = parseInt(colorsInput.value); + generator.updatePreview(); // Update preview to show color quantization + }); + + // Also update preview on slider input for real-time feedback + colorsInput.addEventListener('input', () => { + generator.gifConfig.colorCount = parseInt(colorsInput.value); + generator.updatePreview(); // Real-time preview update + }); + + qualityInput.addEventListener('change', () => { + generator.gifConfig.quality = parseInt(qualityInput.value); + }); + + const ditherSelect = document.getElementById('gif-dither'); + ditherSelect.addEventListener('change', () => { + const value = ditherSelect.value; + generator.gifConfig.dither = value === 'false' ? false : value; + }); +} + /** * Setup collapsible section functionality */ From a05e9bf10bad1f8f770c6eb2cfcf5d73feaf6845 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Sat, 10 Jan 2026 20:21:59 +0000 Subject: [PATCH 40/55] Initial version of lava lamp generator --- assets/sass/pages/resources.scss | 29 +- content/resources/adoptables/index.md | 74 ++ content/resources/lavalamp/index.md | 19 +- .../shortcodes/button-generator.html.backup | 607 -------------- layouts/shortcodes/lavalamp-adoptable.html | 755 ++++++++++++++++++ static/js/adoptables/lavalamp.js | 364 +++++++++ static/lavalamp-demo.html | 371 +++++++++ 7 files changed, 1582 insertions(+), 637 deletions(-) create mode 100644 content/resources/adoptables/index.md delete mode 100644 layouts/shortcodes/button-generator.html.backup create mode 100644 layouts/shortcodes/lavalamp-adoptable.html create mode 100644 static/js/adoptables/lavalamp.js create mode 100644 static/lavalamp-demo.html diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index e16bafe..ac1d762 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -754,12 +754,12 @@ &::before { content: ""; position: absolute; - left: -2rem; + left: -1rem; top: 50%; transform: translateY(-50%); - width: 3rem; - height: 3px; - background: linear-gradient(90deg, transparent, #0096ff); + width: 5px; + height: 100%; + background: linear-gradient(180deg, transparent, #0096ff, transparent); //box-shadow: 0 0 10px rgba(0, 150, 255, 0.8); } @@ -767,12 +767,12 @@ &::after { content: ""; position: absolute; - right: -2rem; + right: -1rem; top: 50%; transform: translateY(-50%); - width: 3rem; - height: 3px; - background: linear-gradient(90deg, #ff7800, transparent); + width: 5px; + height: 100%; + background: linear-gradient(180deg, transparent, #ff7800, transparent); //box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); } @@ -785,19 +785,6 @@ @include media-up(md) { font-size: 3rem; - - &::before, - &::after { - width: 6rem; - } - - &::before { - left: -2rem; - } - - &::after { - right: -2rem; - } } } diff --git a/content/resources/adoptables/index.md b/content/resources/adoptables/index.md new file mode 100644 index 0000000..b2bffcc --- /dev/null +++ b/content/resources/adoptables/index.md @@ -0,0 +1,74 @@ +--- +title: "Adoptables" +date: 2026-01-10 +description: "Customisable widgets and animations for your website" +icon: "adoptables" +draft: false +--- + +Welcome to my adoptables collection! These are customisable widgets you can embed on your own website. Each adoptable comes with a live preview and customisation options. Just configure it how you like, grab the code, and paste it into your site. + +All adoptables are: + +- **Free to use** - No attribution required (but appreciated!) +- **Probably customisable** - Tweak your own colors and settings if applicable +- **Self-contained** - Single script tag, no dependencies +- **Responsive** - Scales to fit your layout + +--- + +## Lava Lamp + +My brother had a blue and yellow lava lamp when I was younger and I was obsessed with it, so making one for my website was an obvious choice... and now I'd like to share it with everyone! + +Using the pixelation effect with very high contrast colours is going to result in an aura on the blobs due to the way the SVG filter works, I am trying to find a solution for this. + +{{< lavalamp-adoptable >}} + +--- + +## Usage Instructions + +1. **Customise** - Use the controls to adjust colors, animation speed, and other settings +2. **Get Code** - Click "Get Embed Code" to generate your custom script tag +3. **Embed** - Copy the code and paste it anywhere in your HTML +4. **Style** - Wrap it in a div and set width/height to control the lamp size + +### Example Container + +The lava lamp will scale to fit its container. You can control the size like this: + +```html +<div style="width: 200px; height: 350px;"> + <script + src="https://ritual.sh/js/adoptables/lavalamp.js" + data-bg-color-1="#F8E45C" + data-bg-color-2="#FF7800" + data-blob-color-1="#FF4500" + data-blob-color-2="#FF6347" + data-case-color="#333333" + data-blob-count="6" + data-speed="1.0" + data-blob-size="1.0" + ></script> +</div> +``` + +### Customisation Options + +- `data-bg-color-1` & `data-bg-color-2`: Background gradient colors +- `data-blob-color-1` & `data-blob-color-2`: Blob gradient colors +- `data-case-color`: Color for the top cap and bottom base +- `data-blob-count`: Number of blobs (3-12) +- `data-speed`: Animation speed multiplier (0.5-1.5x) +- `data-blob-size`: Blob size multiplier (0.5-2.0x) +- `data-pixelate`: Set to "true" for a retro pixelated effect +- `data-pixel-size`: Pixel size for pixelation effect (2-10, default 4) + +--- + +## More Coming Soon + +The companion cube is coming soon! + +Got ideas for adoptables you'd like to see? Let me know! diff --git a/content/resources/lavalamp/index.md b/content/resources/lavalamp/index.md index e18dc73..66b2243 100644 --- a/content/resources/lavalamp/index.md +++ b/content/resources/lavalamp/index.md @@ -5,22 +5,23 @@ description: "A pure CSS lavalamp animation with floating blobs" icon: "lavalamp" demo_url: "" source_url: "" -draft: true +draft: false --- -A mesmerizing lavalamp effect created entirely with HTML and CSS. Features smooth floating animations and gradient blobs that rise and fall like a classic lava lamp. +A mesmerizing lavalamp effect created with HTML, CSS, and JavaScript. Features smooth floating animations and gradient blobs that rise and fall like a classic lava lamp. + +## Want Your Own Customizable Lava Lamp? + +Check out the [Adoptables page](/resources/adoptables/) where you can customize your own lava lamp with your choice of colors, blob count, and animation speed - then get the embed code to add it to your own website! ## Features -- Pure CSS animation (no JavaScript required) -- Customizable colors and blob shapes - Smooth, organic floating motion +- Customizable colors and blob shapes +- Adjustable animation speed and blob count - Responsive design +- Self-contained embed code ## How It Works -The animation uses CSS keyframes to create the illusion of blobs floating up and down. Multiple blob elements with different animation delays create a realistic lava lamp effect. - -## Usage - -Simply copy the HTML and CSS into your project. You can customize the colors by changing the gradient values and adjust the animation speed by modifying the animation duration. +The animation uses CSS keyframes to create the illusion of blobs floating up and down. Multiple blob elements with different animation delays and paths create a realistic lava lamp effect. An SVG filter creates the "goo" effect that makes the blobs merge together smoothly. diff --git a/layouts/shortcodes/button-generator.html.backup b/layouts/shortcodes/button-generator.html.backup deleted file mode 100644 index 6babacb..0000000 --- a/layouts/shortcodes/button-generator.html.backup +++ /dev/null @@ -1,607 +0,0 @@ -<div id="button-generator-app"> - <div class="generator-container"> - <div class="preview-section"> - <h3 class="hidden-md-down">Preview</h3> - <div class="preview-container"> - <div class="preview-wrapper"> - <canvas id="button-canvas" width="88" height="31"></canvas> - </div> - </div> - <button id="download-button" class="btn-primary">Download Button</button> - - <div class="presets-container hidden-md-down"> - <h3>Presets</h3> - <button id="preset-random" class="btn-secondary">Random Button</button> - <button id="preset-classic" class="btn-secondary">Classic Style</button> - <button id="preset-modern" class="btn-secondary">Modern Style</button> - </div> - </div> - - <div class="controls-section"> - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 1</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="button-text">Text</label> - <input - type="text" - id="button-text" - value="RITUAL.SH" - maxlength="20" - /> - - <label class="checkbox-label"> - <input type="checkbox" id="text-enabled" checked /> - <span>Enable Text Line 1</span> - </label> - - <label for="font-size" - >Font Size: <span id="font-size-value">14</span>px</label - > - <input type="range" id="font-size" min="6" max="24" value="14" /> - - <label for="text-x" - >Horizontal Position: <span id="text-x-value">50</span>%</label - > - <input type="range" id="text-x" min="0" max="100" value="50" /> - - <label for="text-y" - >Vertical Position: <span id="text-y-value">35</span>%</label - > - <input type="range" id="text-y" min="0" max="100" value="35" /> - - <label for="text-color-type">Text Color Type</label> - <select id="text-color-type"> - <option value="solid">Solid Color</option> - <option value="gradient">Gradient</option> - </select> - - <div id="text-solid-color"> - <label for="text-color">Text Color</label> - <input type="color" id="text-color" value="#ffffff" /> - </div> - - <div id="text-gradient-color" style="display: none"> - <label for="text-gradient-color1">Gradient Color 1</label> - <input type="color" id="text-gradient-color1" value="#ffffff" /> - - <label for="text-gradient-color2">Gradient Color 2</label> - <input type="color" id="text-gradient-color2" value="#00ffff" /> - - <label for="text-gradient-angle" - >Gradient Angle: - <span id="text-gradient-angle-value">0</span>°</label - > - <input - type="range" - id="text-gradient-angle" - min="0" - max="360" - value="0" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="text-outline" /> - <span>Outline</span> - </label> - <input - type="color" - id="outline-color" - value="#000000" - style="display: none" - /> - - <label for="font-family">Font</label> - <select id="font-family"> - <option value="Lato">Lato</option> - <option value="Roboto">Roboto</option> - <option value="Open Sans">Open Sans</option> - <option value="Montserrat">Montserrat</option> - <option value="Oswald">Oswald</option> - <option value="Bebas Neue">Bebas Neue</option> - <option value="Roboto Mono">Roboto Mono</option> - <option value="VT323">VT323</option> - <option value="Press Start 2P">Press Start 2P</option> - <option value="DSEG7-Classic">DSEG7</option> - </select> - - <div class="checkbox-row"> - <label class="checkbox-label"> - <input type="checkbox" id="font-bold" /> - <span>Bold</span> - </label> - <label class="checkbox-label"> - <input type="checkbox" id="font-italic" /> - <span>Italic</span> - </label> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 1 Animation</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-wave" /> - <span>Wave Animation</span> - </label> - <div id="wave-controls" style="display: none"> - <label for="wave-amplitude" - >Amplitude: <span id="wave-amplitude-value">3</span>px</label - > - <input - type="range" - id="wave-amplitude" - min="0" - max="10" - value="3" - step="0.5" - /> - - <label for="wave-speed" - >Speed: <span id="wave-speed-value">1.0</span>x</label - > - <input - type="range" - id="wave-speed" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-rainbow" /> - <span>Rainbow Text</span> - </label> - <div id="rainbow-text-controls" style="display: none"> - <label for="text-rainbow-speed" - >Speed: <span id="text-rainbow-speed-value">1.0</span>x</label - > - <input - type="range" - id="text-rainbow-speed" - min="0.5" - max="5" - value="1.0" - step="0.1" - /> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 2</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="button-text2">Text</label> - <input type="text" id="button-text2" value="" maxlength="20" /> - - <label class="checkbox-label"> - <input type="checkbox" id="text2-enabled" /> - <span>Enable Text Line 2</span> - </label> - - <label for="font-size2" - >Font Size: <span id="font-size2-value">12</span>px</label - > - <input type="range" id="font-size2" min="6" max="24" value="12" /> - - <label for="text2-x" - >Horizontal Position: <span id="text2-x-value">50</span>%</label - > - <input type="range" id="text2-x" min="0" max="100" value="50" /> - - <label for="text2-y" - >Vertical Position: <span id="text2-y-value">65</span>%</label - > - <input type="range" id="text2-y" min="0" max="100" value="65" /> - - <label for="text2-color-type">Text Color Type</label> - <select id="text2-color-type"> - <option value="solid">Solid Color</option> - <option value="gradient">Gradient</option> - </select> - - <div id="text2-solid-color"> - <label for="text2-color">Text Color</label> - <input type="color" id="text2-color" value="#ffffff" /> - </div> - - <div id="text2-gradient-color" style="display: none"> - <label for="text2-gradient-color1">Gradient Color 1</label> - <input type="color" id="text2-gradient-color1" value="#ffffff" /> - - <label for="text2-gradient-color2">Gradient Color 2</label> - <input type="color" id="text2-gradient-color2" value="#00ffff" /> - - <label for="text2-gradient-angle" - >Gradient Angle: - <span id="text2-gradient-angle-value">0</span>°</label - > - <input - type="range" - id="text2-gradient-angle" - min="0" - max="360" - value="0" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="text2-outline" /> - <span>Outline</span> - </label> - <input - type="color" - id="outline2-color" - value="#000000" - style="display: none" - /> - - <label for="font-family2">Font</label> - <select id="font-family2"> - <option value="Lato">Lato</option> - <option value="Roboto">Roboto</option> - <option value="Open Sans">Open Sans</option> - <option value="Montserrat">Montserrat</option> - <option value="Oswald">Oswald</option> - <option value="Bebas Neue">Bebas Neue</option> - <option value="Roboto Mono">Roboto Mono</option> - <option value="VT323">VT323</option> - <option value="Press Start 2P">Press Start 2P</option> - <option value="DSEG7-Classic">DSEG7</option> - </select> - - <div class="checkbox-row"> - <label class="checkbox-label"> - <input type="checkbox" id="font-bold2" /> - <span>Bold</span> - </label> - <label class="checkbox-label"> - <input type="checkbox" id="font-italic2" /> - <span>Italic</span> - </label> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Text Line 2 Animation</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-wave2" /> - <span>Wave Animation</span> - </label> - <div id="wave-controls2" style="display: none"> - <label for="wave-amplitude2" - >Amplitude: <span id="wave-amplitude2-value">3</span>px</label - > - <input - type="range" - id="wave-amplitude2" - min="0" - max="10" - value="3" - step="0.5" - /> - - <label for="wave-speed2" - >Speed: <span id="wave-speed2-value">1.0</span>x</label - > - <input - type="range" - id="wave-speed2" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-text-rainbow2" /> - <span>Rainbow Text</span> - </label> - <div id="rainbow-text2-controls" style="display: none"> - <label for="text-rainbow-speed2" - >Speed: <span id="text-rainbow-speed2-value">1.0</span>x</label - > - <input - type="range" - id="text-rainbow-speed2" - min="0.5" - max="5" - value="1.0" - step="0.1" - /> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Background</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="bg-type">Background Type</label> - <select id="bg-type"> - <option value="solid">Solid Color</option> - <option value="gradient">Gradient</option> - <option value="texture">Texture</option> - </select> - - <div id="solid-controls"> - <label for="bg-color">Background Color</label> - <input type="color" id="bg-color" value="#0066cc" /> - </div> - - <div id="gradient-controls" style="display: none"> - <label for="gradient-color1">Color 1</label> - <input type="color" id="gradient-color1" value="#0066cc" /> - - <label for="gradient-color2">Color 2</label> - <input type="color" id="gradient-color2" value="#00ccff" /> - - <label for="gradient-angle" - >Angle: <span id="gradient-angle-value">90</span>°</label - > - <input - type="range" - id="gradient-angle" - min="0" - max="360" - value="90" - /> - </div> - - <div id="texture-controls" style="display: none"> - <label for="texture-type">Texture Pattern</label> - <select id="texture-type"> - <option value="dots">Dots</option> - <option value="grid">Grid</option> - <option value="diagonal">Diagonal Lines</option> - <option value="checkerboard">Checkerboard</option> - <option value="noise">Noise</option> - <option value="stars">Stars</option> - </select> - - <label for="texture-color1">Base Color</label> - <input type="color" id="texture-color1" value="#0066cc" /> - - <label for="texture-color2">Pattern Color</label> - <input type="color" id="texture-color2" value="#0099ff" /> - - <label for="texture-scale" - >Pattern Scale: <span id="texture-scale-value">50</span>%</label - > - <input - type="range" - id="texture-scale" - min="10" - max="100" - value="50" - /> - </div> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Background Animation</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label class="checkbox-label"> - <input type="checkbox" id="animate-bg-rainbow" /> - <span>Rainbow Flash</span> - </label> - <div id="rainbow-bg-controls" style="display: none"> - <label for="rainbow-speed" - >Speed: <span id="rainbow-speed-value">0.5</span>x</label - > - <input - type="range" - id="rainbow-speed" - min="0.1" - max="3" - value="0.5" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-bg-rainbow-gradient" /> - <span>Rainbow Gradient</span> - </label> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Border</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <label for="border-width" - >Border Width: <span id="border-width-value">2</span>px</label - > - <input type="range" id="border-width" min="0" max="5" value="2" /> - - <label for="border-color">Border Color</label> - <input type="color" id="border-color" value="#000000" /> - - <label for="border-style">Border Style</label> - <select id="border-style"> - <option value="solid">Solid</option> - <option value="inset">Inset (3D)</option> - <option value="outset">Outset (3D)</option> - <option value="ridge">Ridge</option> - </select> - </div> - </div> - - <div class="control-group"> - <h3 class="control-group-header"> - <span>Special Effects</span> - <span class="toggle-icon">−</span> - </h3> - <div class="control-group-content"> - <p class="info-text"> - Almost all animations should stack, so pick as many as you want. - </p> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-glitch" /> - <span>Glitch Effect</span> - </label> - <div id="glitch-controls" style="display: none"> - <label for="glitch-intensity" - >Intensity: <span id="glitch-intensity-value">3</span></label - > - <input - type="range" - id="glitch-intensity" - min="1" - max="10" - value="3" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-pulse" /> - <span>Pulse Effect</span> - </label> - <div id="pulse-controls" style="display: none"> - <label for="pulse-scale" - >Scale: <span id="pulse-scale-value">1.1</span>x</label - > - <input - type="range" - id="pulse-scale" - min="1.0" - max="1.3" - value="1.1" - step="0.05" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-shimmer" /> - <span>Shimmer Effect</span> - </label> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-scanline" /> - <span>Scanline Effect</span> - </label> - <div id="scanline-controls" style="display: none"> - <label for="scanline-intensity" - >Intensity: <span id="scanline-intensity-value">0.3</span></label - > - <input - type="range" - id="scanline-intensity" - min="0.1" - max="1" - value="0.3" - step="0.1" - /> - - <label for="scanline-speed" - >Speed: <span id="scanline-speed-value">1.0</span>x</label - > - <input - type="range" - id="scanline-speed" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-rgb-split" /> - <span>RGB Split Effect</span> - </label> - <div id="rgb-split-controls" style="display: none"> - <label for="rgb-split-intensity" - >Intensity: <span id="rgb-split-intensity-value">2</span>px</label - > - <input - type="range" - id="rgb-split-intensity" - min="1" - max="5" - value="2" - step="0.5" - /> - </div> - - <label class="checkbox-label"> - <input type="checkbox" id="animate-noise" /> - <span>Noise Effect</span> - </label> - <div id="noise-controls" style="display: none"> - <label for="noise-intensity" - >Intensity: <span id="noise-intensity-value">0.1</span></label - > - <input - type="range" - id="noise-intensity" - min="0.05" - max="0.5" - value="0.1" - step="0.05" - /> - </div> - {{/* - <label class="checkbox-label"> - <input type="checkbox" id="animate-rotate" /> - <span>Rotate Effect</span> - </label> - <div id="rotate-controls" style="display: none"> - <label for="rotate-angle" - >Max Angle: <span id="rotate-angle-value">5</span>°</label - > - <input - type="range" - id="rotate-angle" - min="2" - max="15" - value="5" - step="1" - /> - - <label for="rotate-speed" - >Speed: <span id="rotate-speed-value">1.0</span>x</label - > - <input - type="range" - id="rotate-speed" - min="0.5" - max="3" - value="1.0" - step="0.1" - /> - </div> - */}} - </div> - </div> - </div> - </div> -</div> diff --git a/layouts/shortcodes/lavalamp-adoptable.html b/layouts/shortcodes/lavalamp-adoptable.html new file mode 100644 index 0000000..08de9ad --- /dev/null +++ b/layouts/shortcodes/lavalamp-adoptable.html @@ -0,0 +1,755 @@ +<div id="lavalamp-adoptable-app"> + <div class="adoptable-container"> + <div class="preview-section"> + <h3>Preview</h3> + <div class="preview-grid"> + <div class="preview-item"> + <div class="preview-wrapper preview-flexible"> + <div + id="lavalamp-preview-flex" + class="lavalamp-preview-container" + ></div> + </div> + <span class="preview-label">40px × 60px</span> + </div> + <div class="preview-item"> + <div class="preview-wrapper preview-100"> + <div + id="lavalamp-preview-100" + class="lavalamp-preview-container" + ></div> + </div> + <span class="preview-label">100px × 200px</span> + </div> + <div class="preview-item"> + <div class="preview-wrapper preview-200"> + <div + id="lavalamp-preview-200" + class="lavalamp-preview-container" + ></div> + </div> + <span class="preview-label">200px × 350px</span> + </div> + </div> + </div> + + <div class="controls-section"> + <h3>Customize Your Lava Lamp</h3> + + <div class="control-group"> + <label for="bg-color-1">Background Color 1</label> + <input type="color" id="bg-color-1" value="#F8E45C" /> + </div> + + <div class="control-group"> + <label for="bg-color-2">Background Color 2</label> + <input type="color" id="bg-color-2" value="#FF7800" /> + </div> + + <div class="control-group"> + <label for="blob-color-1">Blob Color 1</label> + <input type="color" id="blob-color-1" value="#FF4500" /> + </div> + + <div class="control-group"> + <label for="blob-color-2">Blob Color 2</label> + <input type="color" id="blob-color-2" value="#FF6347" /> + </div> + + <div class="control-group"> + <label for="case-color">Case Color</label> + <input type="color" id="case-color" value="#333333" /> + </div> + + <div class="control-group"> + <label for="blob-count" + >Number of Blobs: <span id="blob-count-value">6</span></label + > + <input type="range" id="blob-count" min="3" max="12" value="6" /> + </div> + + <div class="control-group"> + <label for="blob-size" + >Blob Size: <span id="blob-size-value">1.0</span>x</label + > + <input + type="range" + id="blob-size" + min="0.5" + max="2.0" + step="0.1" + value="1.0" + /> + </div> + + <div class="control-group"> + <label for="speed">Speed: <span id="speed-value">1.0</span>x</label> + <input + type="range" + id="speed" + min="0.5" + max="1.5" + step="0.1" + value="1.0" + /> + </div> + + <div class="control-group"> + <label> + <input type="checkbox" id="pixelate" /> + Pixelate + </label> + </div> + + <div class="control-group" id="pixel-size-group" style="display: none"> + <label for="pixel-size" + >Pixel Size: <span id="pixel-size-value">4</span>px</label + > + <input type="range" id="pixel-size" min="2" max="10" value="4" /> + </div> + + <div class="control-group"> + <button id="get-code-btn" class="btn-primary">Get Embed Code</button> + </div> + </div> + </div> + + <div id="embed-code-section" style="display: none; margin-top: 2rem"> + <h3>Embed Code</h3> + <p>Copy this code and paste it anywhere on your website:</p> + <div class="highlight"> + <pre><code id="embed-code-display" class="language-html" data-lang="html"></code></pre> + </div> + </div> +</div> + +<style> + .adoptable-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + margin: 2rem 0; + } + + @media (max-width: 768px) { + .adoptable-container { + grid-template-columns: 1fr; + } + } + + .preview-section { + display: flex; + flex-direction: column; + } + + .preview-section h3 { + margin-top: 0; + margin-bottom: 1rem; + } + + .preview-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1rem; + margin-bottom: 1rem; + } + + @media (max-width: 1024px) { + .preview-grid { + grid-template-columns: 1fr; + } + } + + .preview-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5rem; + } + + .preview-wrapper { + border: 2px solid #333; + border-radius: 8px; + padding: 1rem; + background-image: + linear-gradient(45deg, #ccc 25%, transparent 25%), + linear-gradient(-45deg, #ccc 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #ccc 75%), + linear-gradient(-45deg, transparent 75%, #ccc 75%); + background-size: 20px 20px; + background-position: + 0 0, + 0 10px, + 10px -10px, + -10px 0px; + background-color: #fff; + } + + .preview-flexible { + width: 40px; + height: 75px; + padding: 3px; + } + + .preview-100 { + width: 100px; + height: 200px; + } + + .preview-200 { + width: 200px; + height: 350px; + } + + .preview-label { + font-size: 0.875rem; + color: #666; + text-align: center; + } + + .lavalamp-preview-container { + width: 100%; + height: 100%; + } + + .controls-section h3 { + margin-top: 0; + } + + .control-group { + margin-bottom: 1.5rem; + } + + .control-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: bold; + } + + .control-group input[type="color"] { + width: 100%; + height: 40px; + border: 1px solid #ccc; + border-radius: 4px; + cursor: pointer; + } + + .control-group input[type="range"] { + width: 100%; + } + + .btn-primary, + .btn-secondary { + padding: 0.75rem 1.5rem; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 1rem; + font-weight: bold; + } + + .btn-primary { + background: #007bff; + color: white; + } + + .btn-primary:hover { + background: #0056b3; + } + + .btn-secondary { + background: #6c757d; + color: white; + margin-top: 1rem; + } + + .btn-secondary:hover { + background: #545b62; + } + + /* Lava Lamp Styles */ + .lavalamp-adoptable { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + } + + .lavalamp-adoptable .lamp-cap { + width: 60%; + height: 8%; + flex-shrink: 0; + border-radius: 50% 50% 0 0; + box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.3); + position: relative; + z-index: 10; + } + + .lavalamp-adoptable .lamp-body { + position: relative; + width: 100%; + flex: 1; + clip-path: polygon(20% 0, 80% 0, 100% 101%, 0% 101%); + overflow: hidden; + } + + .lavalamp-adoptable .lamp-body::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.15) 20%, + rgba(255, 255, 255, 0.05) 40%, + transparent 60% + ); + pointer-events: none; + } + + .lavalamp-adoptable .blobs-container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + pointer-events: none; + z-index: 2; + } + + .lavalamp-adoptable .blob { + position: absolute; + border-radius: 50%; + animation: lavalamp-float var(--duration) ease-in-out infinite; + opacity: 0.95; + mix-blend-mode: normal; + z-index: 3; + } + + .lavalamp-adoptable .lamp-base { + width: 100%; + height: 15%; + flex-shrink: 0; + border-radius: 0 0 50% 50%; + box-shadow: + inset 0 2px 5% rgba(255, 255, 255, 0.2), + inset 0 -2px 5% rgba(0, 0, 0, 0.5); + position: relative; + display: flex; + justify-content: center; + align-items: center; + } + + @keyframes lavalamp-float { + 0%, + 100% { + transform: translate(var(--start-x), var(--start-y)) scale(1); + } + 25% { + transform: translate(var(--mid1-x), var(--mid1-y)) + scale(var(--scale1, 1.1)); + } + 50% { + transform: translate(var(--mid2-x), var(--mid2-y)) + scale(var(--scale2, 0.9)); + } + 75% { + transform: translate(var(--mid3-x), var(--mid3-y)) + scale(var(--scale3, 1.05)); + } + } +</style> + +<script> + (function () { + "use strict"; + + const previewContainers = { + flex: document.getElementById("lavalamp-preview-flex"), + small: document.getElementById("lavalamp-preview-100"), + large: document.getElementById("lavalamp-preview-200"), + }; + const bgColor1Input = document.getElementById("bg-color-1"); + const bgColor2Input = document.getElementById("bg-color-2"); + const blobColor1Input = document.getElementById("blob-color-1"); + const blobColor2Input = document.getElementById("blob-color-2"); + const caseColorInput = document.getElementById("case-color"); + const blobCountInput = document.getElementById("blob-count"); + const speedInput = document.getElementById("speed"); + const blobSizeInput = document.getElementById("blob-size"); + const pixelateInput = document.getElementById("pixelate"); + const pixelSizeInput = document.getElementById("pixel-size"); + const pixelSizeGroup = document.getElementById("pixel-size-group"); + const blobCountValue = document.getElementById("blob-count-value"); + const speedValue = document.getElementById("speed-value"); + const blobSizeValue = document.getElementById("blob-size-value"); + const pixelSizeValue = document.getElementById("pixel-size-value"); + const getCodeBtn = document.getElementById("get-code-btn"); + const embedCodeSection = document.getElementById("embed-code-section"); + const embedCodeDisplay = document.getElementById("embed-code-display"); + const copyCodeBtn = document.getElementById("copy-code-btn"); + const copyStatus = document.getElementById("copy-status"); + + let lampInstances = { + flex: { lampElements: {}, blobs: [] }, + small: { lampElements: {}, blobs: [] }, + large: { lampElements: {}, blobs: [] }, + }; + + // Initialize the preview lamps + function initPreview() { + Object.keys(previewContainers).forEach((key, index) => { + const previewContainer = previewContainers[key]; + const gooFilterId = `goo-preview-${key}`; + + // Create SVG filters + const svg = document.createElementNS( + "http://www.w3.org/2000/svg", + "svg", + ); + svg.style.position = "absolute"; + svg.style.width = "0"; + svg.style.height = "0"; + const pixelateFilterId = `pixelate-preview-${key}`; + svg.innerHTML = ` + <defs> + <filter id="${gooFilterId}"> + <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" /> + <feColorMatrix + in="blur" + mode="matrix" + values=" + 1 0 0 0 0 + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 18 -7" + result="goo" + /> + <feComposite in="SourceGraphic" in2="goo" operator="atop" /> + </filter> + <filter id="${pixelateFilterId}" x="0%" y="0%" width="100%" height="100%"> + <feFlood x="0" y="0" height="1" width="1"/> + <feComposite width="4" height="4" class="pixelate-composite"/> + <feTile result="a"/> + <feComposite in="SourceGraphic" in2="a" operator="in"/> + <feMorphology operator="dilate" radius="2" class="pixelate-morphology"/> + </filter> + </defs> + `; + + const container = document.createElement("div"); + container.className = "lavalamp-adoptable"; + container.style.width = "100%"; + container.style.height = "100%"; + + const lampCap = document.createElement("div"); + lampCap.className = "lamp-cap"; + + const lampBody = document.createElement("div"); + lampBody.className = "lamp-body"; + + const blobsContainer = document.createElement("div"); + blobsContainer.className = "blobs-container"; + // Initially set the goo filter, will be adjusted after layout + blobsContainer.style.filter = `url(#${gooFilterId})`; + + const lampBase = document.createElement("div"); + lampBase.className = "lamp-base"; + + lampBody.appendChild(blobsContainer); + container.appendChild(svg); + container.appendChild(lampCap); + container.appendChild(lampBody); + container.appendChild(lampBase); + previewContainer.appendChild(container); + + lampInstances[key].lampElements = { + container, + lampCap, + lampBody, + lampBase, + blobsContainer, + svg, + pixelateFilterId, + }; + }); + + // Apply initial styles + updatePreview(); + + // Adjust goo filter for small lamps after layout + adjustGooFilters(); + } + + // Adjust goo filters based on container width (matches embedded script logic) + function adjustGooFilters() { + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements } = instance; + + if (!lampElements.lampBody) return; + + const containerWidth = lampElements.lampBody.offsetWidth; + }); + } + + // Create a blob element for a specific instance + function createBlob(lampBody, lampHeight) { + const blob = document.createElement("div"); + blob.className = "blob"; + + const containerWidth = lampBody.offsetWidth; + const containerHeight = lampHeight || lampBody.offsetHeight; + + const blobSizeMultiplier = parseFloat(blobSizeInput.value); + const size = + (Math.random() * 0.15 + 0.25) * containerWidth * blobSizeMultiplier; + const duration = (Math.random() * 8 + 12) / parseFloat(speedInput.value); + + const maxX = containerWidth - size; + const startX = Math.random() * maxX; + + blob.style.width = `${size}px`; + blob.style.height = `${size}px`; + blob.style.left = `${startX}px`; + blob.style.bottom = "10px"; + blob.style.position = "absolute"; + + blob.style.setProperty("--duration", `${duration}s`); + blob.style.setProperty("--start-x", "0px"); + blob.style.setProperty("--start-y", "0px"); + blob.style.setProperty( + "--mid1-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid1-y", + `${-(Math.random() * 0.15 + 0.25) * containerHeight}px`, + ); + blob.style.setProperty( + "--mid2-x", + `${(Math.random() * 0.4 - 0.2) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid2-y", + `${-(Math.random() * 0.2 + 0.5) * containerHeight}px`, + ); + blob.style.setProperty( + "--mid3-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid3-y", + `${-(Math.random() * 0.15 + 0.8) * containerHeight}px`, + ); + blob.style.setProperty( + "--scale1", + (Math.random() * 0.3 + 1.0).toFixed(2), + ); + blob.style.setProperty( + "--scale2", + (Math.random() * 0.3 + 0.85).toFixed(2), + ); + blob.style.setProperty( + "--scale3", + (Math.random() * 0.3 + 0.95).toFixed(2), + ); + blob.style.animationDelay = `${Math.random() * -20}s`; + + return blob; + } + + // Update blob count for all instances + function updateBlobCount() { + const count = parseInt(blobCountInput.value); + + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements, blobs } = instance; + + if (!lampElements.blobsContainer) return; + + while (blobs.length > count) { + const blob = blobs.pop(); + lampElements.blobsContainer.removeChild(blob); + } + while (blobs.length < count) { + const blob = createBlob(lampElements.lampBody); + updateBlobColors(blob); + blobs.push(blob); + lampElements.blobsContainer.appendChild(blob); + } + }); + } + + // Update blob colors + function updateBlobColors(blob) { + const color1 = blobColor1Input.value; + const color2 = blobColor2Input.value; + blob.style.background = `radial-gradient(circle at 30% 30%, ${color1}, ${color2})`; + } + + // Adjust brightness helper + function adjustBrightness(color, percent) { + const num = parseInt(color.replace("#", ""), 16); + const amt = Math.round(2.55 * percent); + const R = (num >> 16) + amt; + const G = ((num >> 8) & 0x00ff) + amt; + const B = (num & 0x0000ff) + amt; + return ( + "#" + + ( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 + + (B < 255 ? (B < 1 ? 0 : B) : 255) + ) + .toString(16) + .slice(1) + ); + } + + // Update the preview for all instances + function updatePreview() { + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements, blobs } = instance; + + if (!lampElements.lampBody) return; + + // Update background gradient + lampElements.lampBody.style.background = `linear-gradient(180deg, ${bgColor1Input.value} 0%, ${bgColor2Input.value} 100%)`; + + // Update cap and base color + const baseColor = caseColorInput.value; + lampElements.lampCap.style.background = `linear-gradient(180deg, ${adjustBrightness(baseColor, 40)} 0%, ${baseColor} 50%, ${adjustBrightness(baseColor, -20)} 100%)`; + lampElements.lampBase.style.background = `linear-gradient(180deg, ${baseColor} 0%, ${adjustBrightness(baseColor, -20)} 40%, ${adjustBrightness(baseColor, -40)} 100%)`; + lampElements.lampBase.style.borderTop = `1px solid ${adjustBrightness(bgColor2Input.value, -30)}`; + + // Update all blob colors + blobs.forEach((blob) => updateBlobColors(blob)); + }); + + // Update blob count + updateBlobCount(); + } + + // Update pixelation filters for all instances + function updatePixelation() { + const isPixelated = pixelateInput.checked; + const pixelSize = parseInt(pixelSizeInput.value); + + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements } = instance; + + if (isPixelated) { + // Update the filter in the SVG + const filter = lampElements.svg.querySelector( + `#${lampElements.pixelateFilterId}`, + ); + const composite = filter.querySelector("feComposite[width]"); + const morphology = filter.querySelector("feMorphology"); + + composite.setAttribute("width", pixelSize); + composite.setAttribute("height", pixelSize); + morphology.setAttribute("radius", Math.floor(pixelSize / 2)); + + // Apply filter to container + lampElements.container.style.filter = `url(#${lampElements.pixelateFilterId})`; + } else { + // Remove filter + lampElements.container.style.filter = ""; + } + }); + } + + // Generate embed code + function generateEmbedCode() { + const siteUrl = window.location.origin; + let pixelateAttrs = ""; + if (pixelateInput.checked) { + pixelateAttrs = '\n data-pixelate="true"'; + if (parseInt(pixelSizeInput.value) !== 4) { + pixelateAttrs += `\n data-pixel-size="${pixelSizeInput.value}"`; + } + } + return `<script src="${siteUrl}/js/adoptables/lavalamp.js" + data-bg-color-1="${bgColor1Input.value}" + data-bg-color-2="${bgColor2Input.value}" + data-blob-color-1="${blobColor1Input.value}" + data-blob-color-2="${blobColor2Input.value}" + data-case-color="${caseColorInput.value}" + data-blob-count="${blobCountInput.value}" + data-speed="${speedInput.value}" + data-blob-size="${blobSizeInput.value}"${pixelateAttrs}><\/script>`; + } + + // Event listeners + bgColor1Input.addEventListener("input", updatePreview); + bgColor2Input.addEventListener("input", updatePreview); + blobColor1Input.addEventListener("input", updatePreview); + blobColor2Input.addEventListener("input", updatePreview); + caseColorInput.addEventListener("input", updatePreview); + + blobCountInput.addEventListener("input", function () { + blobCountValue.textContent = this.value; + updatePreview(); + }); + + speedInput.addEventListener("input", function () { + speedValue.textContent = parseFloat(this.value).toFixed(1); + // Speed changes require recreating blobs for all instances + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + instance.blobs.forEach((blob) => + instance.lampElements.blobsContainer.removeChild(blob), + ); + instance.blobs = []; + }); + updatePreview(); + }); + + blobSizeInput.addEventListener("input", function () { + blobSizeValue.textContent = parseFloat(this.value).toFixed(1); + // Size changes require recreating blobs for all instances + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + instance.blobs.forEach((blob) => + instance.lampElements.blobsContainer.removeChild(blob), + ); + instance.blobs = []; + }); + updatePreview(); + }); + + pixelateInput.addEventListener("change", function () { + // Show/hide pixel size slider + if (this.checked) { + pixelSizeGroup.style.display = "block"; + } else { + pixelSizeGroup.style.display = "none"; + } + updatePixelation(); + }); + + pixelSizeInput.addEventListener("input", function () { + pixelSizeValue.textContent = this.value; + updatePixelation(); + }); + + getCodeBtn.addEventListener("click", function () { + embedCodeDisplay.textContent = generateEmbedCode(); + embedCodeSection.style.display = "block"; + embedCodeSection.scrollIntoView({ behavior: "smooth" }); + }); + + // Initialize + initPreview(); + })(); +</script> diff --git a/static/js/adoptables/lavalamp.js b/static/js/adoptables/lavalamp.js new file mode 100644 index 0000000..6c55277 --- /dev/null +++ b/static/js/adoptables/lavalamp.js @@ -0,0 +1,364 @@ +(function () { + "use strict"; + + const currentScript = document.currentScript; + if (!currentScript) { + console.error("Lava Lamp: Could not find current script tag"); + return; + } + + // Read configuration from data attributes with defaults + const config = { + bgColor1: currentScript.dataset["bgColor-1"] || "#F8E45C", + bgColor2: currentScript.dataset["bgColor-2"] || "#FF7800", + blobColor1: currentScript.dataset["blobColor-1"] || "#FF4500", + blobColor2: currentScript.dataset["blobColor-2"] || "#FF6347", + caseColor: currentScript.dataset.caseColor || "#333333", + blobCount: parseInt(currentScript.dataset.blobCount) || 6, + speed: parseFloat(currentScript.dataset.speed) || 1.0, + blobSize: parseFloat(currentScript.dataset.blobSize) || 1.0, + pixelate: currentScript.dataset.pixelate === "true" || false, + pixelSize: parseInt(currentScript.dataset.pixelSize) || 4, + }; + + // Helper function to adjust color brightness + function adjustBrightness(color, percent) { + const num = parseInt(color.replace("#", ""), 16); + const amt = Math.round(2.55 * percent); + const R = (num >> 16) + amt; + const G = ((num >> 8) & 0x00ff) + amt; + const B = (num & 0x0000ff) + amt; + return ( + "#" + + ( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 + + (B < 255 ? (B < 1 ? 0 : B) : 255) + ) + .toString(16) + .slice(1) + ); + } + + // Create a host element with shadow DOM for isolation + const host = document.createElement("div"); + host.style.width = "100%"; + host.style.height = "100%"; + host.style.display = "block"; + + if (config.pixelate) { + host.style.overflow = "hidden"; + } + + // Attach shadow DOM + const shadowRoot = host.attachShadow({ mode: "open" }); + + currentScript.parentNode.insertBefore(host, currentScript.nextSibling); + + const gooFilterId = "goo-filter"; + + // Inject CSS into shadow DOM + const style = document.createElement("style"); + style.textContent = ` + .lavalamp-container { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + position: relative; + ${config.pixelate ? "filter: url(#pixelate-filter);" : ""} + } + + .lamp-cap { + width: 60%; + height: 8%; + flex-shrink: 0; + border-radius: 50% 50% 0 0; + box-shadow: inset 0 1px 2px rgba(255, 255, 255, 0.3); + position: relative; + z-index: 10; + } + + .lamp-body { + position: relative; + width: 100%; + flex: 1; + clip-path: polygon(20% 0, 80% 0, 100% 101%, 0% 101%); + overflow: hidden; + } + + .lamp-body::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient( + 90deg, + transparent 0%, + rgba(255, 255, 255, 0.15) 20%, + rgba(255, 255, 255, 0.05) 40%, + transparent 60% + ); + pointer-events: none; + } + + .blobs-container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + filter: url(#${gooFilterId}); + pointer-events: none; + z-index: 2; + } + + .blob { + position: absolute; + border-radius: 50%; + animation: lavalamp-float var(--duration) ease-in-out infinite; + opacity: 0.95; + mix-blend-mode: normal; + z-index: 3; + } + + .lamp-base { + width: 100%; + height: 15%; + flex-shrink: 0; + border-radius: 0 0 50% 50%; + box-shadow: + inset 0 2px 5px rgba(255, 255, 255, 0.2), + inset 0 -2px 5px rgba(0, 0, 0, 0.5); + position: relative; + display: flex; + justify-content: center; + align-items: center; + } + + @keyframes lavalamp-float { + 0%, 100% { + transform: translate(var(--start-x), var(--start-y)) scale(1); + } + 25% { + transform: translate(var(--mid1-x), var(--mid1-y)) scale(var(--scale1, 1.1)); + } + 50% { + transform: translate(var(--mid2-x), var(--mid2-y)) scale(var(--scale2, 0.9)); + } + 75% { + transform: translate(var(--mid3-x), var(--mid3-y)) scale(var(--scale3, 1.05)); + } + } + `; + shadowRoot.appendChild(style); + + // Create the HTML structure + const container = document.createElement("div"); + container.className = "lavalamp-container"; + + // SVG filters for goo effect and pixelation + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.style.position = "absolute"; + svg.style.width = "0"; + svg.style.height = "0"; + svg.innerHTML = ` + <defs> + <filter id="${gooFilterId}"> + <feGaussianBlur in="SourceGraphic" stdDeviation="7" result="blur" /> + <feColorMatrix + in="blur" + mode="matrix" + values=" + 1 0 0 0 0 + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 18 -7" + result="goo" + /> + <feComposite in="SourceGraphic" in2="goo" operator="atop" /> + </filter> + ${ + config.pixelate + ? ` + <filter id="pixelate-filter" x="0%" y="0%" width="100%" height="100%"> + <feFlood x="0" y="0" height="1" width="1"/> + <feComposite width="${config.pixelSize}" height="${config.pixelSize}"/> + <feTile result="a"/> + <feComposite in="SourceGraphic" in2="a" operator="in"/> + <feMorphology operator="dilate" radius="${Math.floor(config.pixelSize / 2)}"/> + </filter> + ` + : "" + } + </defs> + `; + + const lampCap = document.createElement("div"); + lampCap.className = "lamp-cap"; + lampCap.style.background = `linear-gradient(180deg, ${adjustBrightness(config.caseColor, 40)} 0%, ${config.caseColor} 50%, ${adjustBrightness(config.caseColor, -20)} 100%)`; + + const lampBody = document.createElement("div"); + lampBody.className = "lamp-body"; + lampBody.style.background = `linear-gradient(180deg, ${config.bgColor1} 0%, ${config.bgColor2} 100%)`; + + const blobsContainer = document.createElement("div"); + blobsContainer.className = "blobs-container"; + + const lampBase = document.createElement("div"); + lampBase.className = "lamp-base"; + lampBase.style.background = `linear-gradient(180deg, ${config.caseColor} 0%, ${adjustBrightness(config.caseColor, -20)} 40%, ${adjustBrightness(config.caseColor, -40)} 100%)`; + lampBase.style.borderTop = `1px solid ${adjustBrightness(config.bgColor2, -30)}`; + + // Assemble the structure + lampBody.appendChild(blobsContainer); + container.appendChild(svg); + container.appendChild(lampCap); + container.appendChild(lampBody); + container.appendChild(lampBase); + + // Append to shadow DOM + shadowRoot.appendChild(container); + + // Blob creation and animation + let blobs = []; + + function createBlob() { + const blob = document.createElement("div"); + blob.className = "blob"; + + // Get container dimensions + const containerWidth = lampBody.offsetWidth; + const containerHeight = lampBody.offsetHeight; + + // Size relative to container width (25-40% of width) + const size = + (Math.random() * 0.15 + 0.25) * containerWidth * config.blobSize; + const duration = (Math.random() * 8 + 12) / config.speed; + + // Max X position accounting for blob size and container margins + const maxX = Math.max(0, containerWidth - size); + const startX = maxX > 0 ? Math.random() * maxX : 0; + + // Create gradient for blob + const blobGradient = `radial-gradient(circle at 30% 30%, ${config.blobColor1}, ${config.blobColor2})`; + + blob.style.width = `${size}px`; + blob.style.height = `${size}px`; + blob.style.left = `${startX}px`; + blob.style.bottom = "10px"; + blob.style.position = "absolute"; + blob.style.background = blobGradient; + blob.style.setProperty("--duration", `${duration}s`); + blob.style.setProperty("--start-x", "0px"); + blob.style.setProperty("--start-y", "0px"); + + // Movement waypoints + blob.style.setProperty( + "--mid1-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid1-y", + `${-(Math.random() * 0.15 + 0.25) * containerHeight}px`, + ); + + blob.style.setProperty( + "--mid2-x", + `${(Math.random() * 0.4 - 0.2) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid2-y", + `${-(Math.random() * 0.2 + 0.5) * containerHeight}px`, + ); + + blob.style.setProperty( + "--mid3-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid3-y", + `${-(Math.random() * 0.15 + 0.8) * containerHeight}px`, + ); + + // Scale variations + blob.style.setProperty("--scale1", (Math.random() * 0.3 + 1.0).toFixed(2)); + blob.style.setProperty("--scale2", (Math.random() * 0.3 + 0.85).toFixed(2)); + blob.style.setProperty("--scale3", (Math.random() * 0.3 + 0.95).toFixed(2)); + + // Random delay to stagger animations + blob.style.animationDelay = `${Math.random() * -20}s`; + + return blob; + } + + function updateBlobCount() { + while (blobs.length > config.blobCount) { + const blob = blobs.pop(); + blobsContainer.removeChild(blob); + } + while (blobs.length < config.blobCount) { + const blob = createBlob(); + blobs.push(blob); + blobsContainer.appendChild(blob); + } + } + + // Initialize + function init() { + // Wait for container to have dimensions + // Not sure if this is a great idea, what if this never happens for some reason + // This is as good as I can come up with right now + if (lampBody.offsetWidth === 0 || lampBody.offsetHeight === 0) { + setTimeout(init, 100); + return; + } + + // Scale goo filter blur based on container width + const containerWidth = lampBody.offsetWidth; + + // Apply scaled goo filter + let blurAmount, alphaMultiplier, alphaBias; + + if (containerWidth < 80) { + blurAmount = 3; + alphaMultiplier = 12; + alphaBias = -5; + } else { + blurAmount = Math.max(4, Math.min(7, containerWidth / 20)); + alphaMultiplier = 18; + alphaBias = -7; + } + + const gooFilter = svg.querySelector(`#${gooFilterId} feGaussianBlur`); + if (gooFilter) { + gooFilter.setAttribute("stdDeviation", blurAmount); + } + + const colorMatrix = svg.querySelector(`#${gooFilterId} feColorMatrix`); + if (colorMatrix) { + colorMatrix.setAttribute( + "values", + ` + 1 0 0 0 0 + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 ${alphaMultiplier} ${alphaBias} + `, + ); + } + + updateBlobCount(); + } + + // Start when DOM is ready + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); + } else { + init(); + } +})(); diff --git a/static/lavalamp-demo.html b/static/lavalamp-demo.html new file mode 100644 index 0000000..9da0cdb --- /dev/null +++ b/static/lavalamp-demo.html @@ -0,0 +1,371 @@ +<!doctype html> +<html lang="en"> + <head> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <title>Lava Lamp Adoptable - Demo Examples + + + +
+

🌋 Lava Lamp Adoptable Demo

+

+ See the lava lamp in action at various sizes and color schemes! +

+ +
+ +
+
Classic Orange
+
+ +
+
150px × 280px
+
+ + +
+
Purple Dreams
+
+ +
+
120px × 250px
+
+ + +
+
Ocean Blue
+
+ +
+
100px × 200px
+
+ + +
+
Lava Red
+
+ +
+
180px × 320px
+
+ + +
+
Mint Fresh
+
+ +
+
140px × 260px
+
+ + +
+
Sunset Glow
+
+ +
+
110px × 220px
+
+ + +
+
Cyber Green (Pixelated)
+
+ +
+
160px × 300px (6px pixels)
+
+ + +
+
Pink Bubble (Pixelated)
+
+ +
+
130px × 240px (3px pixels)
+
+ + +
+
Tiny Retro (Pixelated)
+
+ +
+
40px × 80px (3px pixels)
+
+
+ +
+

How to Use

+

+ Simply copy and paste this code into your website. The lava lamp will + scale to fit any container size you specify! +

+
<div style="width: 200px; height: 350px;">
+  <script src="https://ritual.sh/js/adoptables/lavalamp.js"
+          data-bg-color-1="#F8E45C"
+          data-bg-color-2="#FF7800"
+          data-blob-color-1="#FF4500"
+          data-blob-color-2="#FF6347"
+          data-case-color="#333333"
+          data-blob-count="6"
+          data-speed="1.0"
+          data-blob-size="1.0"></script>
+</div>
+ +

Customization Options

+
    +
  • + data-bg-color-1 & data-bg-color-2: + Background gradient colors +
  • +
  • + data-blob-color-1 & data-blob-color-2: + Blob gradient colors +
  • +
  • + data-case-color: Color for the top cap and bottom base +
  • +
  • data-blob-count: Number of blobs (3-12)
  • +
  • + data-speed: Animation speed multiplier (0.5-1.5x) +
  • +
  • data-blob-size: Blob size multiplier (0.5-2.0x)
  • +
+ +

+ 🌟 Create your own custom lava lamp at + ritual.sh/resources/adoptables +

+
+
+ + From 8faf555629bf56c23b85b924b1864ec7d644b533 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 11 Jan 2026 08:37:14 +0000 Subject: [PATCH 41/55] Lamp page updates moving code --- assets/js/adoptables/lavalamp-adoptable.js | 386 +++++++++++ assets/sass/pages/button-generator.scss | 118 +--- assets/sass/pages/lavalamp-adoptable.scss | 199 ++++++ assets/sass/pages/resources.scss | 179 ++--- assets/sass/partials/_form-controls.scss | 336 ++++++++++ assets/sass/style.scss | 2 + .../2026-01-06-week-3-bit-chilly-out/index.md | 14 +- .../updates/2026-01-11-button-generator.md | 10 + layouts/_default/baseof.html | 6 + layouts/partials/site-scripts.html | 11 +- layouts/shortcodes/lavalamp-adoptable.html | 631 ------------------ 11 files changed, 1013 insertions(+), 879 deletions(-) create mode 100644 assets/js/adoptables/lavalamp-adoptable.js create mode 100644 assets/sass/pages/lavalamp-adoptable.scss create mode 100644 assets/sass/partials/_form-controls.scss create mode 100644 content/updates/2026-01-11-button-generator.md diff --git a/assets/js/adoptables/lavalamp-adoptable.js b/assets/js/adoptables/lavalamp-adoptable.js new file mode 100644 index 0000000..114dc1a --- /dev/null +++ b/assets/js/adoptables/lavalamp-adoptable.js @@ -0,0 +1,386 @@ +(function () { + "use strict"; + + const previewContainers = { + flex: document.getElementById("lavalamp-preview-flex"), + small: document.getElementById("lavalamp-preview-100"), + large: document.getElementById("lavalamp-preview-200"), + }; + const bgColor1Input = document.getElementById("bg-color-1"); + const bgColor2Input = document.getElementById("bg-color-2"); + const blobColor1Input = document.getElementById("blob-color-1"); + const blobColor2Input = document.getElementById("blob-color-2"); + const caseColorInput = document.getElementById("case-color"); + const blobCountInput = document.getElementById("blob-count"); + const speedInput = document.getElementById("speed"); + const blobSizeInput = document.getElementById("blob-size"); + const pixelateInput = document.getElementById("pixelate"); + const pixelSizeInput = document.getElementById("pixel-size"); + const pixelSizeGroup = document.getElementById("pixel-size-group"); + const blobCountValue = document.getElementById("blob-count-value"); + const speedValue = document.getElementById("speed-value"); + const blobSizeValue = document.getElementById("blob-size-value"); + const pixelSizeValue = document.getElementById("pixel-size-value"); + const getCodeBtn = document.getElementById("get-code-btn"); + const embedCodeSection = document.getElementById("embed-code-section"); + const embedCodeDisplay = document.getElementById("embed-code-display"); + const copyCodeBtn = document.getElementById("copy-code-btn"); + const copyStatus = document.getElementById("copy-status"); + + let lampInstances = { + flex: { lampElements: {}, blobs: [] }, + small: { lampElements: {}, blobs: [] }, + large: { lampElements: {}, blobs: [] }, + }; + + // Initialize the preview lamps + function initPreview() { + Object.keys(previewContainers).forEach((key, index) => { + const previewContainer = previewContainers[key]; + const gooFilterId = `goo-preview-${key}`; + + // Create SVG filters + const svg = document.createElementNS( + "http://www.w3.org/2000/svg", + "svg", + ); + svg.style.position = "absolute"; + svg.style.width = "0"; + svg.style.height = "0"; + const pixelateFilterId = `pixelate-preview-${key}`; + svg.innerHTML = ` + + + + + + + + + + + + + + + `; + + const container = document.createElement("div"); + container.className = "lavalamp-adoptable"; + container.style.width = "100%"; + container.style.height = "100%"; + + const lampCap = document.createElement("div"); + lampCap.className = "lamp-cap"; + + const lampBody = document.createElement("div"); + lampBody.className = "lamp-body"; + + const blobsContainer = document.createElement("div"); + blobsContainer.className = "blobs-container"; + // Initially set the goo filter, will be adjusted after layout + blobsContainer.style.filter = `url(#${gooFilterId})`; + + const lampBase = document.createElement("div"); + lampBase.className = "lamp-base"; + + lampBody.appendChild(blobsContainer); + container.appendChild(svg); + container.appendChild(lampCap); + container.appendChild(lampBody); + container.appendChild(lampBase); + previewContainer.appendChild(container); + + lampInstances[key].lampElements = { + container, + lampCap, + lampBody, + lampBase, + blobsContainer, + svg, + pixelateFilterId, + }; + }); + + // Apply initial styles + updatePreview(); + + // Adjust goo filter for small lamps after layout + adjustGooFilters(); + } + + // Adjust goo filters based on container width (matches embedded script logic) + function adjustGooFilters() { + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements } = instance; + + if (!lampElements.lampBody) return; + + const containerWidth = lampElements.lampBody.offsetWidth; + }); + } + + // Create a blob element for a specific instance + function createBlob(lampBody, lampHeight) { + const blob = document.createElement("div"); + blob.className = "blob"; + + const containerWidth = lampBody.offsetWidth; + const containerHeight = lampHeight || lampBody.offsetHeight; + + const blobSizeMultiplier = parseFloat(blobSizeInput.value); + const size = + (Math.random() * 0.15 + 0.25) * containerWidth * blobSizeMultiplier; + const duration = (Math.random() * 8 + 12) / parseFloat(speedInput.value); + + const maxX = containerWidth - size; + const startX = Math.random() * maxX; + + blob.style.width = `${size}px`; + blob.style.height = `${size}px`; + blob.style.left = `${startX}px`; + blob.style.bottom = "10px"; + blob.style.position = "absolute"; + + blob.style.setProperty("--duration", `${duration}s`); + blob.style.setProperty("--start-x", "0px"); + blob.style.setProperty("--start-y", "0px"); + blob.style.setProperty( + "--mid1-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid1-y", + `${-(Math.random() * 0.15 + 0.25) * containerHeight}px`, + ); + blob.style.setProperty( + "--mid2-x", + `${(Math.random() * 0.4 - 0.2) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid2-y", + `${-(Math.random() * 0.2 + 0.5) * containerHeight}px`, + ); + blob.style.setProperty( + "--mid3-x", + `${(Math.random() * 0.3 - 0.15) * containerWidth}px`, + ); + blob.style.setProperty( + "--mid3-y", + `${-(Math.random() * 0.15 + 0.8) * containerHeight}px`, + ); + blob.style.setProperty( + "--scale1", + (Math.random() * 0.3 + 1.0).toFixed(2), + ); + blob.style.setProperty( + "--scale2", + (Math.random() * 0.3 + 0.85).toFixed(2), + ); + blob.style.setProperty( + "--scale3", + (Math.random() * 0.3 + 0.95).toFixed(2), + ); + blob.style.animationDelay = `${Math.random() * -20}s`; + + return blob; + } + + // Update blob count for all instances + function updateBlobCount() { + const count = parseInt(blobCountInput.value); + + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements, blobs } = instance; + + if (!lampElements.blobsContainer) return; + + while (blobs.length > count) { + const blob = blobs.pop(); + lampElements.blobsContainer.removeChild(blob); + } + while (blobs.length < count) { + const blob = createBlob(lampElements.lampBody); + updateBlobColors(blob); + blobs.push(blob); + lampElements.blobsContainer.appendChild(blob); + } + }); + } + + // Update blob colors + function updateBlobColors(blob) { + const color1 = blobColor1Input.value; + const color2 = blobColor2Input.value; + blob.style.background = `radial-gradient(circle at 30% 30%, ${color1}, ${color2})`; + } + + // Adjust brightness helper + function adjustBrightness(color, percent) { + const num = parseInt(color.replace("#", ""), 16); + const amt = Math.round(2.55 * percent); + const R = (num >> 16) + amt; + const G = ((num >> 8) & 0x00ff) + amt; + const B = (num & 0x0000ff) + amt; + return ( + "#" + + ( + 0x1000000 + + (R < 255 ? (R < 1 ? 0 : R) : 255) * 0x10000 + + (G < 255 ? (G < 1 ? 0 : G) : 255) * 0x100 + + (B < 255 ? (B < 1 ? 0 : B) : 255) + ) + .toString(16) + .slice(1) + ); + } + + // Update the preview for all instances + function updatePreview() { + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements, blobs } = instance; + + if (!lampElements.lampBody) return; + + // Update background gradient + lampElements.lampBody.style.background = `linear-gradient(180deg, ${bgColor1Input.value} 0%, ${bgColor2Input.value} 100%)`; + + // Update cap and base color + const baseColor = caseColorInput.value; + lampElements.lampCap.style.background = `linear-gradient(180deg, ${adjustBrightness(baseColor, 40)} 0%, ${baseColor} 50%, ${adjustBrightness(baseColor, -20)} 100%)`; + lampElements.lampBase.style.background = `linear-gradient(180deg, ${baseColor} 0%, ${adjustBrightness(baseColor, -20)} 40%, ${adjustBrightness(baseColor, -40)} 100%)`; + lampElements.lampBase.style.borderTop = `1px solid ${adjustBrightness(bgColor2Input.value, -30)}`; + + // Update all blob colors + blobs.forEach((blob) => updateBlobColors(blob)); + }); + + // Update blob count + updateBlobCount(); + } + + // Update pixelation filters for all instances + function updatePixelation() { + const isPixelated = pixelateInput.checked; + const pixelSize = parseInt(pixelSizeInput.value); + + Object.keys(lampInstances).forEach((key) => { + const instance = lampInstances[key]; + const { lampElements } = instance; + + if (isPixelated) { + // Update the filter in the SVG + const filter = lampElements.svg.querySelector( + `#${lampElements.pixelateFilterId}`, + ); + const composite = filter.querySelector("feComposite[width]"); + const morphology = filter.querySelector("feMorphology"); + + composite.setAttribute("width", pixelSize); + composite.setAttribute("height", pixelSize); + morphology.setAttribute("radius", Math.floor(pixelSize / 2)); + + // Apply filter to container + lampElements.container.style.filter = `url(#${lampElements.pixelateFilterId})`; + } else { + // Remove filter + lampElements.container.style.filter = ""; + } + }); + } + + // Generate embed code + function generateEmbedCode() { + const siteUrl = window.location.origin; + let pixelateAttrs = ""; + if (pixelateInput.checked) { + pixelateAttrs = '\n data-pixelate="true"'; + if (parseInt(pixelSizeInput.value) !== 4) { + pixelateAttrs += `\n data-pixel-size="${pixelSizeInput.value}"`; + } + } + return ` {{ end }} + + + {{ if or (findRE "{{<\\s*lavalamp-adoptable" .RawContent) (findRE "{{%\\s*lavalamp-adoptable" .RawContent) }} + {{ $lavalampAdoptable := resources.Get "js/adoptables/lavalamp-adoptable.js" | resources.Minify | resources.Fingerprint }} + + {{ end }} diff --git a/layouts/partials/site-scripts.html b/layouts/partials/site-scripts.html index b8ee8b1..d940fd3 100644 --- a/layouts/partials/site-scripts.html +++ b/layouts/partials/site-scripts.html @@ -7,11 +7,18 @@ {{ $filtered := slice }} {{ range $remaining }} {{ $path := .RelPermalink }} - {{ if and (not (strings.Contains $path "/terminal.js")) (not (strings.Contains $path "/init.js")) (not (strings.Contains $path "/button-generator.js")) }} + {{ if and (not (strings.Contains $path "/terminal.js")) (not (strings.Contains $path "/init.js")) (not (strings.Contains $path "/button-generator.js")) (not (strings.Contains $path "/adoptables/")) }} {{ $filtered = $filtered | append . }} {{ end }} {{ end }} -{{ $allFiles := slice $terminalShell | append $filtered | append $init | append $commandFiles | append $subfolderFiles }} +{{ $filteredSubfolders := slice }} +{{ range $subfolderFiles }} + {{ $path := .RelPermalink }} + {{ if not (strings.Contains $path "/adoptables/") }} + {{ $filteredSubfolders = $filteredSubfolders | append . }} + {{ end }} +{{ end }} +{{ $allFiles := slice $terminalShell | append $filtered | append $init | append $commandFiles | append $filteredSubfolders }} {{ $terminalBundle := $allFiles | resources.Concat "js/terminal-bundle.js" | resources.Minify | resources.Fingerprint }} From ecefaa45f3eca43142a78b9d7b43898575276fe6 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 11 Jan 2026 08:39:31 +0000 Subject: [PATCH 42/55] removing seperate lavalamp section --- content/resources/lavalamp/index.md | 27 --------------------------- 1 file changed, 27 deletions(-) delete mode 100644 content/resources/lavalamp/index.md diff --git a/content/resources/lavalamp/index.md b/content/resources/lavalamp/index.md deleted file mode 100644 index 66b2243..0000000 --- a/content/resources/lavalamp/index.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: "HTML/CSS Lavalamp" -date: 2026-01-04 -description: "A pure CSS lavalamp animation with floating blobs" -icon: "lavalamp" -demo_url: "" -source_url: "" -draft: false ---- - -A mesmerizing lavalamp effect created with HTML, CSS, and JavaScript. Features smooth floating animations and gradient blobs that rise and fall like a classic lava lamp. - -## Want Your Own Customizable Lava Lamp? - -Check out the [Adoptables page](/resources/adoptables/) where you can customize your own lava lamp with your choice of colors, blob count, and animation speed - then get the embed code to add it to your own website! - -## Features - -- Smooth, organic floating motion -- Customizable colors and blob shapes -- Adjustable animation speed and blob count -- Responsive design -- Self-contained embed code - -## How It Works - -The animation uses CSS keyframes to create the illusion of blobs floating up and down. Multiple blob elements with different animation delays and paths create a realistic lava lamp effect. An SVG filter creates the "goo" effect that makes the blobs merge together smoothly. From d70ba4d33c95b63d3510cfe05d1c3f69ceb450e9 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 11 Jan 2026 08:41:05 +0000 Subject: [PATCH 43/55] weekly post --- content/blog/2026-01-06-week-3-bit-chilly-out/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/blog/2026-01-06-week-3-bit-chilly-out/index.md b/content/blog/2026-01-06-week-3-bit-chilly-out/index.md index 7ea453a..7c92b80 100644 --- a/content/blog/2026-01-06-week-3-bit-chilly-out/index.md +++ b/content/blog/2026-01-06-week-3-bit-chilly-out/index.md @@ -4,7 +4,7 @@ date: 2026-01-11 tags: - weeknote - weekly update -draft: true +draft: false --- - ❄️ It's been pretty cold and we've had the lightest sprinkling of snow. From 8d13c52d18f3c42a5efc6cc7a64d194e5ea184c4 Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 11 Jan 2026 09:52:49 +0000 Subject: [PATCH 44/55] small mobile pass --- assets/sass/pages/lavalamp-adoptable.scss | 13 +++++++++---- assets/sass/pages/resources.scss | 2 -- layouts/shortcodes/lavalamp-adoptable.html | 8 ++++---- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/assets/sass/pages/lavalamp-adoptable.scss b/assets/sass/pages/lavalamp-adoptable.scss index c05f4d1..dac61c8 100644 --- a/assets/sass/pages/lavalamp-adoptable.scss +++ b/assets/sass/pages/lavalamp-adoptable.scss @@ -70,6 +70,13 @@ .preview-100 { width: 100px; height: 200px; + + @include media-down(md) { + position: absolute; + top: 10px; + right: 10px; + z-index: 900; + } } .preview-200 { @@ -185,12 +192,10 @@ transform: translate(var(--start-x), var(--start-y)) scale(1); } 25% { - transform: translate(var(--mid1-x), var(--mid1-y)) - scale(var(--scale1, 1.1)); + transform: translate(var(--mid1-x), var(--mid1-y)) scale(var(--scale1, 1.1)); } 50% { - transform: translate(var(--mid2-x), var(--mid2-y)) - scale(var(--scale2, 0.9)); + transform: translate(var(--mid2-x), var(--mid2-y)) scale(var(--scale2, 0.9)); } 75% { transform: translate(var(--mid3-x), var(--mid3-y)) diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 727c698..70c7fbf 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -811,7 +811,6 @@ width: 5px; height: 100%; background: linear-gradient(180deg, transparent, #0096ff, transparent); - //box-shadow: 0 0 10px rgba(0, 150, 255, 0.8); } // Orange line on the right @@ -824,7 +823,6 @@ width: 5px; height: 100%; background: linear-gradient(180deg, transparent, #ff7800, transparent); - //box-shadow: 0 0 10px rgba(255, 120, 0, 0.8); } @include media-down(md) { diff --git a/layouts/shortcodes/lavalamp-adoptable.html b/layouts/shortcodes/lavalamp-adoptable.html index 5d7fbbd..c256317 100644 --- a/layouts/shortcodes/lavalamp-adoptable.html +++ b/layouts/shortcodes/lavalamp-adoptable.html @@ -1,9 +1,9 @@
-

Preview

+

Preview

-
+
- 100px × 200px + 100px × 200px
-
+
Date: Sun, 11 Jan 2026 12:31:35 +0000 Subject: [PATCH 45/55] Adding guestbook --- assets/js/guestbook.js | 371 +++++++++++++++++++++++++++++ assets/sass/pages/guestbook.scss | 336 ++++++++++++++++++++++++++ assets/sass/pages/homepage.scss | 46 +++- assets/sass/style.scss | 1 + content/guestbook.md | 11 + layouts/_default/baseof.html | 6 + layouts/guestbook/single.html | 112 +++++++++ layouts/index.html | 14 +- layouts/partials/site-scripts.html | 2 +- 9 files changed, 895 insertions(+), 4 deletions(-) create mode 100644 assets/js/guestbook.js create mode 100644 assets/sass/pages/guestbook.scss create mode 100644 content/guestbook.md create mode 100644 layouts/guestbook/single.html diff --git a/assets/js/guestbook.js b/assets/js/guestbook.js new file mode 100644 index 0000000..747a323 --- /dev/null +++ b/assets/js/guestbook.js @@ -0,0 +1,371 @@ +/** + * Guestbook functionality for ritual.sh + * Custom implementation that calls the guestbook API directly + */ + +class GuestbookManager { + constructor() { + // Configuration - Update this URL when the backend is deployed + this.apiUrl = "https://guestbook.ritual.sh"; + this.perPage = 20; + this.currentPage = 1; + this.totalPages = 1; + + // DOM elements + this.form = document.getElementById("guestbook-form"); + this.entriesList = document.getElementById("entries-list"); + this.entriesLoading = document.getElementById("entries-loading"); + this.entriesError = document.getElementById("entries-error"); + this.pagination = document.getElementById("pagination"); + this.formFeedback = document.getElementById("form-feedback"); + this.submitBtn = document.getElementById("submit-btn"); + + this.init(); + } + + init() { + if (!this.form) return; + + // Attach event listeners + this.form.addEventListener("submit", (e) => this.handleSubmit(e)); + + // Load initial entries + this.loadEntries(); + } + + /** + * Load guestbook entries from the API + */ + async loadEntries(page = 1) { + this.currentPage = page; + + // Show loading state + this.showLoading(); + + try { + const response = await fetch( + `${this.apiUrl}/entries?page=${page}&per_page=${this.perPage}`, + ); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + + // Handle the actual API response structure + if (data.entries !== undefined) { + // API returns entries directly + this.renderEntries(data.entries || []); + this.totalPages = data.total_pages || 1; + + // Create pagination object from the flat response + const pagination = { + current_page: data.page || 1, + total_pages: data.total_pages || 1, + total_entries: data.total || 0, + per_page: data.per_page || this.perPage + }; + this.renderPagination(pagination); + } else if (data.success === false) { + // API returned an error + throw new Error(data.error || "Failed to load entries"); + } else { + throw new Error("Unexpected API response format"); + } + } catch (error) { + console.error("Error loading entries:", error); + this.showError(); + } + } + + /** + * Render entries to the DOM + */ + renderEntries(entries) { + // Hide loading and error states + this.entriesLoading.style.display = "none"; + this.entriesError.style.display = "none"; + this.entriesList.style.display = "block"; + + if (entries.length === 0) { + this.entriesList.innerHTML = ` +
+

No entries yet. Be the first to sign the guestbook!

+ _ +
+ `; + return; + } + + // Build entries HTML + const entriesHTML = entries + .map((entry) => this.renderEntry(entry)) + .join(""); + + this.entriesList.innerHTML = entriesHTML; + } + + /** + * Render a single entry + */ + renderEntry(entry) { + const date = this.formatDate(entry.timestamp); + const nameHTML = entry.website + ? `${this.escapeHtml(entry.name)}` + : this.escapeHtml(entry.name); + + // Display website URL without https:// protocol + const websiteDisplay = entry.website + ? `|${this.formatWebsiteUrl(entry.website)}` + : ""; + + return ` +
+
+ ${nameHTML} + ${websiteDisplay} + +
+
${this.escapeHtml(entry.message)}
+
+ `; + } + + /** + * Render pagination controls + */ + renderPagination(pagination) { + if (pagination.total_pages <= 1) { + this.pagination.style.display = "none"; + return; + } + + this.pagination.style.display = "flex"; + + const prevDisabled = pagination.current_page === 1 ? "disabled" : ""; + const nextDisabled = + pagination.current_page === pagination.total_pages ? "disabled" : ""; + + let pagesHTML = ""; + + // Show page numbers (max 5) + const startPage = Math.max(1, pagination.current_page - 2); + const endPage = Math.min(pagination.total_pages, startPage + 4); + + for (let i = startPage; i <= endPage; i++) { + const active = i === pagination.current_page ? "active" : ""; + pagesHTML += ` + + `; + } + + this.pagination.innerHTML = ` +
+ Page ${pagination.current_page} of ${pagination.total_pages} + (${pagination.total_entries} entries) +
+
+ + ${pagesHTML} + +
+ `; + + // Attach click handlers to pagination buttons + this.pagination.querySelectorAll(".pagination-button").forEach((btn) => { + btn.addEventListener("click", (e) => { + const page = parseInt(e.target.dataset.page); + if (page && page !== pagination.current_page) { + this.loadEntries(page); + // Scroll to top of entries + document + .querySelector(".guestbook-entries-container") + .scrollIntoView({ behavior: "smooth" }); + } + }); + }); + } + + /** + * Handle form submission + */ + async handleSubmit(e) { + e.preventDefault(); + + // Disable submit button + this.submitBtn.disabled = true; + this.submitBtn.querySelector(".button-text").textContent = "[ SENDING... ]"; + + // Clear previous feedback + this.formFeedback.className = "form-feedback"; + this.formFeedback.textContent = ""; + + // Get form data + const formData = new FormData(this.form); + const data = { + name: formData.get("name"), + email: formData.get("email"), + website: formData.get("website"), + message: formData.get("message"), + }; + + try { + const response = await fetch(`${this.apiUrl}/submit`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + + const result = await response.json(); + + // Handle success response + if (result.success || response.ok) { + this.showSuccess( + result.message || + "Entry submitted! It will appear after moderation. Thank you!", + ); + this.form.reset(); + + // If entry was auto-approved, reload entries + if (result.status === "approved") { + setTimeout(() => this.loadEntries(1), 1000); + } + } else { + this.showFormError(result.error || result.message || "Failed to submit entry"); + } + } catch (error) { + console.error("Error submitting entry:", error); + this.showFormError( + "Network error. Please check your connection and try again.", + ); + } finally { + // Re-enable submit button + this.submitBtn.disabled = false; + this.submitBtn.querySelector(".button-text").textContent = "[ SUBMIT ]"; + } + } + + /** + * Show loading state + */ + showLoading() { + this.entriesLoading.style.display = "block"; + this.entriesError.style.display = "none"; + this.entriesList.style.display = "none"; + this.pagination.style.display = "none"; + } + + /** + * Show error state + */ + showError() { + this.entriesLoading.style.display = "none"; + this.entriesError.style.display = "block"; + this.entriesList.style.display = "none"; + this.pagination.style.display = "none"; + } + + /** + * Show form success message + */ + showSuccess(message) { + this.formFeedback.className = "form-feedback success"; + this.formFeedback.textContent = `SUCCESS: ${message}`; + + // Auto-hide after 10 seconds + setTimeout(() => { + this.formFeedback.className = "form-feedback"; + }, 10000); + } + + /** + * Show form error message + */ + showFormError(message) { + this.formFeedback.className = "form-feedback error"; + this.formFeedback.textContent = `ERROR: ${message}`; + + // Auto-hide after 8 seconds + setTimeout(() => { + this.formFeedback.className = "form-feedback"; + }, 8000); + } + + /** + * Format date to readable format + */ + formatDate(dateString) { + if (!dateString) return "Unknown date"; + + const date = new Date(dateString); + + // Check if date is valid + if (isNaN(date.getTime())) { + return "Unknown date"; + } + + const now = new Date(); + const diffTime = Math.abs(now - date); + const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); + + if (diffDays === 0) { + return "Today"; + } else if (diffDays === 1) { + return "Yesterday"; + } else if (diffDays < 7) { + return `${diffDays} days ago`; + } else { + return date.toLocaleDateString("en-GB", { + day: "2-digit", + month: "short", + year: "numeric", + }); + } + } + + /** + * Format website URL for display (remove protocol) + */ + formatWebsiteUrl(url) { + if (!url) return ""; + + try { + const urlObj = new URL(url); + // Return hostname + pathname, removing trailing slash + let display = urlObj.hostname + urlObj.pathname; + return this.escapeHtml(display.replace(/\/$/, "")); + } catch (e) { + // If URL parsing fails, just remove common protocols + return this.escapeHtml( + url.replace(/^https?:\/\//, "").replace(/\/$/, "") + ); + } + } + + /** + * Escape HTML to prevent XSS + */ + escapeHtml(text) { + const div = document.createElement("div"); + div.textContent = text; + return div.innerHTML; + } +} + +// Initialize guestbook when DOM is ready +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", () => { + new GuestbookManager(); + }); +} else { + new GuestbookManager(); +} diff --git a/assets/sass/pages/guestbook.scss b/assets/sass/pages/guestbook.scss new file mode 100644 index 0000000..b640a00 --- /dev/null +++ b/assets/sass/pages/guestbook.scss @@ -0,0 +1,336 @@ +@import "../mixins"; + +.guestbook-page { + color: white; + margin: auto; + + .content-screen { + position: relative !important; + } + + > .guestbook-content { + width: 50%; + margin: auto; + padding: 2rem; + display: grid; + grid-template-columns: 1fr 2fr; + gap: 2rem; + + @include media-down(lg) { + width: 100%; + grid-template-columns: 1fr; + } + > .wide-item { + grid-column: 1 / -1; + } + + .guestbook-floppy { + margin: auto; + padding: 2rem; + width: 90%; + max-width: 300px; + transform: rotate(5deg); + } + } +} + +// Terminal output styling +.terminal-output { + font-family: monospace; + line-height: 1.6; + + .info { + color: #0f0; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + + .dim { + opacity: 0.7; + } + + .warning { + color: #ff9900; + text-shadow: 0 0 5px rgba(255, 153, 0, 0.5); + } + + .error-text { + color: #ff0000; + text-shadow: 0 0 5px rgba(255, 0, 0, 0.5); + } +} + +// Guestbook Form +.guestbook-form { + margin-top: 0.5rem; + + .form-row { + margin-bottom: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.2rem; + + label { + font-family: monospace; + color: #0f0; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + font-size: 0.85rem; + } + + input, + textarea { + background: rgba(0, 0, 0, 0.5); + border: 1px solid rgba(0, 255, 0, 0.3); + color: #0f0; + padding: 0.4rem; + font-family: monospace; + font-size: 0.85rem; + text-shadow: 0 0 3px rgba(0, 255, 0, 0.3); + border-radius: 2px; + outline: none; + transition: all 0.3s ease; + + &::placeholder { + color: rgba(0, 255, 0, 0.4); + text-shadow: none; + } + + &:focus { + border-color: rgba(0, 255, 0, 0.6); + box-shadow: + 0 0 10px rgba(0, 255, 0, 0.3), + inset 0 0 10px rgba(0, 255, 0, 0.1); + } + } + + textarea { + resize: vertical; + min-height: 60px; + } + } + + .form-feedback { + margin: 0.5rem 0; + padding: 0.5rem; + border-radius: 3px; + font-family: monospace; + font-size: 0.8rem; + display: none; + + &.success { + display: block; + background: rgba(0, 255, 0, 0.1); + border: 1px solid rgba(0, 255, 0, 0.3); + color: #0f0; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + + &.error { + display: block; + background: rgba(255, 0, 0, 0.1); + border: 1px solid rgba(255, 0, 0, 0.3); + color: #ff0000; + text-shadow: 0 0 5px rgba(255, 0, 0, 0.5); + } + + &.pending { + display: block; + background: rgba(255, 153, 0, 0.1); + border: 1px solid rgba(255, 153, 0, 0.3); + color: #ff9900; + text-shadow: 0 0 5px rgba(255, 153, 0, 0.5); + } + } + + .terminal-button { + background: rgba(0, 255, 0, 0.1); + border: 2px solid rgba(0, 255, 0, 0.4); + color: #0f0; + padding: 0.5rem 1rem; + font-family: monospace; + font-size: 0.9rem; + font-weight: bold; + cursor: pointer; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + transition: all 0.3s ease; + border-radius: 3px; + letter-spacing: 2px; + + &:hover:not(:disabled) { + background: rgba(0, 255, 0, 0.2); + border-color: rgba(0, 255, 0, 0.7); + box-shadow: + 0 0 15px rgba(0, 255, 0, 0.4), + inset 0 0 10px rgba(0, 255, 0, 0.2); + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + transform: translateY(-1px); + } + + &:active:not(:disabled) { + transform: translateY(0); + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + border-color: rgba(0, 255, 0, 0.2); + } + } +} + +// Entries List +.entries-list { + margin-top: 1rem; + + .entry { + margin-bottom: 2rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid rgba(0, 255, 0, 0.2); + font-family: monospace; + + &:last-child { + border-bottom: none; + } + + .entry-header { + margin-bottom: 0.5rem; + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + align-items: baseline; + + .entry-name { + color: #0f0; + font-weight: bold; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + + a { + color: #0f0; + text-decoration: none; + border-bottom: 1px dotted rgba(0, 255, 0, 0.5); + transition: all 0.3s ease; + + &:hover { + border-bottom-style: solid; + text-shadow: 0 0 10px rgba(0, 255, 0, 0.8); + } + } + } + + .entry-website { + color: rgba(0, 255, 0, 0.6); + font-size: 0.85rem; + font-style: italic; + } + + .entry-separator { + color: rgba(0, 255, 0, 0.4); + } + + .entry-date { + color: rgba(0, 255, 0, 0.6); + font-size: 0.85rem; + margin-left: auto; + } + } + + .entry-message { + color: #0f0; + line-height: 1.6; + opacity: 0.9; + white-space: pre-wrap; + word-wrap: break-word; + } + } +} + +// Loading state +.loading-state { + padding: 2rem; + text-align: center; + font-family: monospace; + + .loading-text { + color: #0f0; + text-shadow: 0 0 5px rgba(0, 255, 0, 0.5); + } + + .loading-dots { + color: #0f0; + animation: loading-blink 1.5s infinite; + } +} + +@keyframes loading-blink { + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.3; + } +} + +// Error state +.error-state { + padding: 2rem; + text-align: center; + font-family: monospace; +} + +// Pagination +.pagination { + margin-top: 2rem; + padding-top: 1.5rem; + border-top: 1px solid rgba(0, 255, 0, 0.2); + display: flex; + justify-content: space-between; + align-items: center; + font-family: monospace; + gap: 1rem; + flex-wrap: wrap; + + .pagination-info { + color: rgba(0, 255, 0, 0.7); + font-size: 0.85rem; + } + + .pagination-controls { + display: flex; + gap: 0.5rem; + } + + .pagination-button { + background: rgba(0, 255, 0, 0.1); + border: 1px solid rgba(0, 255, 0, 0.3); + color: #0f0; + padding: 0.5rem 1rem; + font-family: monospace; + font-size: 0.85rem; + cursor: pointer; + text-shadow: 0 0 3px rgba(0, 255, 0, 0.5); + transition: all 0.3s ease; + border-radius: 2px; + + &:hover:not(:disabled) { + background: rgba(0, 255, 0, 0.2); + border-color: rgba(0, 255, 0, 0.5); + box-shadow: 0 0 10px rgba(0, 255, 0, 0.3); + } + + &:disabled { + opacity: 0.3; + cursor: not-allowed; + } + + &.active { + background: rgba(0, 255, 0, 0.3); + border-color: rgba(0, 255, 0, 0.6); + font-weight: bold; + } + } +} + +// Scrollbar customization for entries container +.guestbook-entries-container .screen-display { + @include scrollbar-custom(#0f0, 5px); +} diff --git a/assets/sass/pages/homepage.scss b/assets/sass/pages/homepage.scss index e06bfd3..b68cb33 100644 --- a/assets/sass/pages/homepage.scss +++ b/assets/sass/pages/homepage.scss @@ -171,7 +171,7 @@ grid-template-columns: 1fr 1fr; @include media-up(lg) { - grid-template-columns: repeat(5, 1fr); + grid-template-columns: repeat(6, 1fr); position: absolute; bottom: 5%; width: 100%; @@ -186,6 +186,46 @@ justify-content: center; } + .nav-floppy { + width: 44%; + transform: rotate(5deg); + position: relative; + + &:hover { + .nav-floppy-text { + opacity: 1; + } + } + + .nav-floppy-text { + position: absolute; + display: block; + bottom: 0; + right: 0; + color: white; + font-size: 20px; + font-weight: bold; + z-index: 8000; + transform: rotate(10deg); + border: 1px solid #0f0; + padding: 2px; + padding-left: 5px; + padding-right: 5px; + border-radius: 5px; + background-color: rgba(0, 0, 0, 0.7); + opacity: 0; + transition: opacity 0.3s ease; + text-align: center; + + @include media-down(lg) { + opacity: 1; + //transform: rotate(0deg); + bottom: 0; + font-size: 14px; + } + } + } + .nav-cube { width: 106.5px; position: relative; @@ -224,6 +264,10 @@ transition: opacity 0.3s ease; text-align: center; + &:hover::after { + opacity: 1; + } + @include media-down(lg) { opacity: 1; //transform: rotate(0deg); diff --git a/assets/sass/style.scss b/assets/sass/style.scss index 527a2dc..8860c32 100644 --- a/assets/sass/style.scss +++ b/assets/sass/style.scss @@ -29,6 +29,7 @@ @import "pages/resources"; @import "pages/button-generator"; @import "pages/lavalamp-adoptable"; +@import "pages/guestbook"; @import url(https://fonts.bunny.net/css?family=abel:400|barlow-condensed:400,500|caveat:400|lato:300,300i,400,400i|neonderthaw:400); diff --git a/content/guestbook.md b/content/guestbook.md new file mode 100644 index 0000000..9c11eae --- /dev/null +++ b/content/guestbook.md @@ -0,0 +1,11 @@ +--- +title: "Guestbook" +author: Dan +type: guestbook +date: 2026-01-11 +comments: false +--- + +I'd love to hear from you if you've passed by my corner of the internet. + +If you want to say hi privately you can [email me](mailto:dan@ritual.sh) instead. diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index ea512a3..d7d98c5 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -45,5 +45,11 @@ {{ $lavalampAdoptable := resources.Get "js/adoptables/lavalamp-adoptable.js" | resources.Minify | resources.Fingerprint }} {{ end }} + + + {{ if eq .Type "guestbook" }} + {{ $guestbook := resources.Get "js/guestbook.js" | resources.Minify | resources.Fingerprint }} + + {{ end }} diff --git a/layouts/guestbook/single.html b/layouts/guestbook/single.html new file mode 100644 index 0000000..68bd581 --- /dev/null +++ b/layouts/guestbook/single.html @@ -0,0 +1,112 @@ +{{ define "main" }} +
+
+ +
+
+
+
+ > guestbook --info
+ {{ .Content }}
+
+ _ +
+
+
+
+ {{ partial "elements/floppy.html" (dict "title" "Guestbook Entries" + "lines" (slice "" "" "DO NOT DELETE" "" "") "bgColor" "#1a4d8f" + "bgColorDark" "#0d2747") }} +
+
+ + +
+
+
> new-entry --interactive
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+
+
+ + +
+
+
+ > list-entries --all
+
+
+ +
+ Loading entries... + _ +
+ + + + + + +
+
+
+
+{{ end }} diff --git a/layouts/index.html b/layouts/index.html index 8b05950..b5ac2f2 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -268,12 +268,22 @@ +
{{ partial "elements/crt-tv.html" . }}
diff --git a/layouts/partials/site-scripts.html b/layouts/partials/site-scripts.html index d940fd3..f61de47 100644 --- a/layouts/partials/site-scripts.html +++ b/layouts/partials/site-scripts.html @@ -7,7 +7,7 @@ {{ $filtered := slice }} {{ range $remaining }} {{ $path := .RelPermalink }} - {{ if and (not (strings.Contains $path "/terminal.js")) (not (strings.Contains $path "/init.js")) (not (strings.Contains $path "/button-generator.js")) (not (strings.Contains $path "/adoptables/")) }} + {{ if and (not (strings.Contains $path "/terminal.js")) (not (strings.Contains $path "/init.js")) (not (strings.Contains $path "/button-generator.js")) (not (strings.Contains $path "/adoptables/")) (not (strings.Contains $path "/guestbook.js")) }} {{ $filtered = $filtered | append . }} {{ end }} {{ end }} From 810d7fe06cfdfbcc763243922a8836b02a46adbd Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 11 Jan 2026 17:01:29 +0000 Subject: [PATCH 46/55] Adding a hit counter --- assets/js/init.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/assets/js/init.js b/assets/js/init.js index 97c9a34..e5f0661 100644 --- a/assets/js/init.js +++ b/assets/js/init.js @@ -3,6 +3,19 @@ window.terminal = new TerminalShell(); +// Analytics tracking +function sendAnalyticsHit() { + fetch('https://api.ritual.sh/analytics/hit', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }).catch(err => { + // Silently fail - don't block page load on analytics errors + console.debug('Analytics tracking failed:', err); + }); +} + // Function to initialize terminal function initTerminal() { // Check if terminal element exists @@ -14,8 +27,12 @@ function initTerminal() { // Wait for DOM to be ready if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", initTerminal); + document.addEventListener("DOMContentLoaded", () => { + initTerminal(); + sendAnalyticsHit(); + }); } else { // DOM already loaded initTerminal(); + sendAnalyticsHit(); } From aa6b1ee3c1717f06f14708b4ee5b24cbc0f4530d Mon Sep 17 00:00:00 2001 From: Dan Date: Sun, 11 Jan 2026 17:14:26 +0000 Subject: [PATCH 47/55] Adding stats display to homepage screen --- assets/js/crt-logs.js | 110 ++++++++++++++++++++++++++++++++++++++++++ layouts/index.html | 12 +---- 2 files changed, 111 insertions(+), 11 deletions(-) create mode 100644 assets/js/crt-logs.js diff --git a/assets/js/crt-logs.js b/assets/js/crt-logs.js new file mode 100644 index 0000000..e7a57ff --- /dev/null +++ b/assets/js/crt-logs.js @@ -0,0 +1,110 @@ +// CRT Log Screen with Analytics +// This script updates the CRT screen with a mix of fake logs and real visitor stats + +function initCRTLogs() { + const crtScreen = document.getElementById('crt-logs'); + + if (!crtScreen) { + return; + } + + const fakeLogs = [ + '[WARN] High load detected - time for coffee break', + '[ERROR] 404: Motivation not found', + '[WARN] Firewall detected actual fire.', + '[ERROR] Keyboard not found. Press F1 to continue.', + ]; + + function formatDate(dateStr) { + const date = new Date(dateStr); + return date.toLocaleString('en-US', { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + hour12: false + }); + } + + function updateCRTScreen(stats) { + const logs = []; + + // Add initial command + logs.push('> tail -f /var/log'); + + // Mix fake logs with real stats + const totalLogs = 10; + const statsPositions = [3, 6, 9]; // Insert stats at these positions + + let fakeLogIndex = 0; + + for (let i = 0; i < totalLogs; i++) { + if (statsPositions.includes(i) && stats) { + // Insert real stats + if (i === 3) { + logs.push(`[STATS] Total visitors: ${stats.totalHits.toLocaleString()}`); + } else if (i === 6) { + logs.push(`[STATS] Unique visitors: ${stats.uniqueVisitors.toLocaleString()}`); + } else if (i === 9 && stats.lastUpdated) { + logs.push(`[STATS] Last updated: ${formatDate(stats.lastUpdated)}`); + } + } else { + // Insert fake log + logs.push(fakeLogs[fakeLogIndex % fakeLogs.length]); + fakeLogIndex++; + } + } + + // Animate logs appearing one by one + crtScreen.innerHTML = '> tail -f /var/log
\n_'; + + let currentIndex = 0; + const lineDelay = 150; // milliseconds between each line + + function addNextLine() { + if (currentIndex < logs.length - 1) { // -1 to skip the initial command we already added + const displayedLogs = logs.slice(1, currentIndex + 2); // Skip initial command, add lines progressively + crtScreen.innerHTML = logs[0] + '
\n' + displayedLogs.join('
\n') + '
\n_'; + currentIndex++; + setTimeout(addNextLine, lineDelay); + } else { + // Final update with cursor + crtScreen.innerHTML = logs.join('
\n') + '
\n_'; + } + } + + setTimeout(addNextLine, lineDelay); + } + + function fetchAnalyticsStats() { + fetch('https://api.ritual.sh/analytics/stats') + .then(response => { + if (!response.ok) { + throw new Error('Failed to fetch stats'); + } + return response.json(); + }) + .then(stats => { + updateCRTScreen(stats); + }) + .catch(err => { + // Silently fail and show fake logs only + console.debug('Failed to load analytics stats:', err); + updateCRTScreen(null); + }); + } + + // Initial load + fetchAnalyticsStats(); + + // Refresh stats every 30 seconds + setInterval(fetchAnalyticsStats, 30000); +} + +// Wait for DOM to be ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initCRTLogs); +} else { + // DOM already loaded + initCRTLogs(); +} diff --git a/layouts/index.html b/layouts/index.html index b5ac2f2..d442f57 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -89,18 +89,8 @@
-
+
> tail -f /var/log
- [INFO] Process OK
- [WARN] High load detected - time for coffee break
- [INFO] Connected to database (it's in a relationship now)
- [ERROR] 404: Motivation not found
- [WARN] Firewall detected actual fire, calling emergency services
- [INFO] Successfully hacked into mainframe (jk it's just localhost)
- [ERROR] Keyboard not found. Press F1 to continue.
- [WARN] Too many open tabs. Browser having existential crisis.
- [INFO] Ping 127.0.0.1 - there's no place like home
- [ERROR] SQL injection attempt detected. Nice try, Bobby Tables.
_
From a9c9ca866c95f5511411621d5debc563930b8732 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 12 Jan 2026 08:43:25 +0000 Subject: [PATCH 48/55] Adding mentions script and initial config --- .webmentions-sent | 0 config.yml | 9 +++ content/now/software.md | 6 +- layouts/_default/list.webmentions.json | 36 ++++++++++ send-webmentions.sh | 97 ++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 .webmentions-sent create mode 100644 layouts/_default/list.webmentions.json create mode 100755 send-webmentions.sh diff --git a/.webmentions-sent b/.webmentions-sent new file mode 100644 index 0000000..e69de29 diff --git a/config.yml b/config.yml index 73ee525..6b6a541 100644 --- a/config.yml +++ b/config.yml @@ -6,6 +6,15 @@ buildFuture: false buildExpired: false enableEmoji: true +outputs: + home: ["HTML", "RSS", "webmentions"] + +outputFormats: + webmentions: + mediaType: "application/json" + baseName: "webmentions" + isPlainText: true + pagination: pagerSize: 5 diff --git a/content/now/software.md b/content/now/software.md index de9a2d0..52456ed 100644 --- a/content/now/software.md +++ b/content/now/software.md @@ -7,13 +7,13 @@ order: 4 **Software:** -- **Browser** - [Zen](https://zen-browser.app/) +- **Browser** - [Zen](https://zen-browser.app/) - **Code Editor** - VS Code - **Audio** - High Tide & Turntable -- **Password Manager** - 1password +- **Password Manager** - [Proton Pass](https://pr.tn/ref/MNB13JYX) **Hardware:** - **Keyboard** - Ducky One 3 Classic 65% w/ Cherry Red switches - **Mouse** - Razer Naga X -- **Microphone** - Marantz MPM-1000 \ No newline at end of file +- **Microphone** - Marantz MPM-1000 diff --git a/layouts/_default/list.webmentions.json b/layouts/_default/list.webmentions.json new file mode 100644 index 0000000..2a04202 --- /dev/null +++ b/layouts/_default/list.webmentions.json @@ -0,0 +1,36 @@ +{{- $links := slice -}} +{{- $excludeDomains := slice "ritual.sh" "last.fm" "www.last.fm" "amazon.co.uk" "www.amazon.co.uk" "amazon.com" "www.amazon.com" -}} + +{{- range .Site.RegularPages -}} + {{- $postUrl := .Permalink -}} + {{- $content := .Content -}} + + {{- /* Extract all hrefs from content */ -}} + {{- $hrefs := findRE `href="([^"]+)"` $content -}} + + {{- range $hrefs -}} + {{- $href := . | replaceRE `href="([^"]+)"` "$1" -}} + + {{- /* Only external links */ -}} + {{- if hasPrefix $href "http" -}} + {{- $shouldExclude := false -}} + + {{- /* Check if href contains any excluded domain */ -}} + {{- range $excludeDomains -}} + {{- if in $href . -}} + {{- $shouldExclude = true -}} + {{- end -}} + {{- end -}} + + {{- if not $shouldExclude -}} + {{- $links = $links | append (dict "source" $postUrl "target" $href) -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- end -}} +[ +{{ range $i, $link := $links -}} +{{ if $i }}, +{{ end }} {"source":"{{ $link.source }}","target":"{{ $link.target }}"} +{{ end -}} +] \ No newline at end of file diff --git a/send-webmentions.sh b/send-webmentions.sh new file mode 100755 index 0000000..7a34f34 --- /dev/null +++ b/send-webmentions.sh @@ -0,0 +1,97 @@ +#!/bin/bash + +# Load environment variables from .env file +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) +else + echo "⚠️ Warning: .env file not found, using defaults" +fi + +# Use environment variables with fallback defaults +WEBMENTIONS_FILE="${WEBMENTIONS_FILE:-public/webmentions.json}" +SENT_CACHE="${SENT_CACHE:-.webmentions-sent}" +API_ENDPOINT="${API_ENDPOINT:-https://api.ritual.sh/webmention/send}" +API_KEY="${API_KEY:-your-secret-key}" + +# Check for dry-run flag +DRY_RUN=false +if [ "$1" = "--dry-run" ] || [ "$1" = "-n" ]; then + DRY_RUN=true + echo "🔍 DRY RUN MODE - No webmentions will be sent" + echo "================================================" +fi + +# Create cache file if it doesn't exist +touch "$SENT_CACHE" + +# Read the webmentions JSON +if [ ! -f "$WEBMENTIONS_FILE" ]; then + echo "No webmentions.json found" + exit 0 +fi + +# Count totals +TOTAL=0 +ALREADY_SENT=0 +TO_SEND=0 + +# Process each link +jq -c '.[]' "$WEBMENTIONS_FILE" | while read -r mention; do + source=$(echo "$mention" | jq -r '.source') + target=$(echo "$mention" | jq -r '.target') + + TOTAL=$((TOTAL + 1)) + + # Create unique key for this source->target pair + key="${source}|${target}" + + # Check if already sent + if grep -Fxq "$key" "$SENT_CACHE"; then + if [ "$DRY_RUN" = true ]; then + echo "⏭️ Already sent: $source -> $target" + else + echo "Already sent: $source -> $target" + fi + ALREADY_SENT=$((ALREADY_SENT + 1)) + continue + fi + + TO_SEND=$((TO_SEND + 1)) + + if [ "$DRY_RUN" = true ]; then + echo "📤 Would send: $source -> $target" + else + echo "Sending webmention: $source -> $target" + + # Send to your API + response=$(curl -s -w "\n%{http_code}" -X POST "$API_ENDPOINT" \ + -H "Authorization: Bearer $API_KEY" \ + -H "Content-Type: application/json" \ + -d "$mention") + + http_code=$(echo "$response" | tail -n1) + + # If successful, add to cache + if [ "$http_code" = "200" ] || [ "$http_code" = "202" ]; then + echo "$key" >> "$SENT_CACHE" + echo "✓ Sent successfully" + else + echo "✗ Failed with status $http_code" + fi + + # Be nice to endpoints - don't spam + sleep 1 + fi +done + +# Summary +echo "" +echo "================================================" +if [ "$DRY_RUN" = true ]; then + echo "🔍 DRY RUN SUMMARY" +else + echo "✅ Webmentions processing complete" +fi +echo "Total links found: $TOTAL" +echo "Already sent: $ALREADY_SENT" +echo "To send: $TO_SEND" \ No newline at end of file From fed9aabcb0145f0ab81e26de899a0609018c1cf1 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 12 Jan 2026 08:52:31 +0000 Subject: [PATCH 49/55] Fixing auth type --- send-webmentions.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/send-webmentions.sh b/send-webmentions.sh index 7a34f34..df445aa 100755 --- a/send-webmentions.sh +++ b/send-webmentions.sh @@ -65,9 +65,8 @@ jq -c '.[]' "$WEBMENTIONS_FILE" | while read -r mention; do # Send to your API response=$(curl -s -w "\n%{http_code}" -X POST "$API_ENDPOINT" \ - -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ - -d "$mention") + -d "{\"auth\":\"$API_KEY\",\"source\":\"$source\",\"target\":\"$target\"}") http_code=$(echo "$response" | tail -n1) From d6a951914671c63ed5c80fab7ce5cd1565a0418b Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 12 Jan 2026 08:56:55 +0000 Subject: [PATCH 50/55] Debug scripts --- send-webmentions-debug.sh | 75 +++++++++++++++++++++++++++++++++++++++ test-webmention-auth.sh | 43 ++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100755 send-webmentions-debug.sh create mode 100755 test-webmention-auth.sh diff --git a/send-webmentions-debug.sh b/send-webmentions-debug.sh new file mode 100755 index 0000000..10a37d4 --- /dev/null +++ b/send-webmentions-debug.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Load environment variables from .env file +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) +else + echo "⚠️ Warning: .env file not found, using defaults" +fi + +# Use environment variables with fallback defaults +WEBMENTIONS_FILE="${WEBMENTIONS_FILE:-public/webmentions.json}" +SENT_CACHE="${SENT_CACHE:-.webmentions-sent}" +API_ENDPOINT="${API_ENDPOINT:-https://api.ritual.sh/webmention/send}" +API_KEY="${API_KEY:-your-secret-key}" + +echo "DEBUG: Configuration" +echo "====================" +echo "API_ENDPOINT: $API_ENDPOINT" +echo "API_KEY: ${API_KEY:0:10}... (first 10 chars)" +echo "WEBMENTIONS_FILE: $WEBMENTIONS_FILE" +echo "" + +# Read the webmentions JSON +if [ ! -f "$WEBMENTIONS_FILE" ]; then + echo "No webmentions.json found at $WEBMENTIONS_FILE" + exit 0 +fi + +# Get first mention for testing +first_mention=$(jq -c '.[0]' "$WEBMENTIONS_FILE") +source=$(echo "$first_mention" | jq -r '.source') +target=$(echo "$first_mention" | jq -r '.target') + +echo "DEBUG: Testing with first webmention" +echo "=====================================" +echo "Source: $source" +echo "Target: $target" +echo "" + +# Create the JSON payload +payload=$(jq -n \ + --arg auth "$API_KEY" \ + --arg source "$source" \ + --arg target "$target" \ + '{auth: $auth, source: $source, target: $target}') + +echo "DEBUG: Payload" +echo "==============" +echo "$payload" | jq '.' +echo "" + +echo "DEBUG: Sending request..." +echo "=========================" + +# Send with verbose output +response=$(curl -v -X POST "$API_ENDPOINT" \ + -H "Content-Type: application/json" \ + -d "$payload" \ + 2>&1) + +echo "" +echo "DEBUG: Full Response" +echo "====================" +echo "$response" +echo "" + +# Extract just the HTTP status and response body +http_status=$(echo "$response" | grep "< HTTP" | tail -1) +response_body=$(echo "$response" | sed -n '/^{/,/^}/p' | tail -1) + +echo "" +echo "DEBUG: Parsed Results" +echo "=====================" +echo "HTTP Status: $http_status" +echo "Response Body: $response_body" diff --git a/test-webmention-auth.sh b/test-webmention-auth.sh new file mode 100755 index 0000000..be8bdeb --- /dev/null +++ b/test-webmention-auth.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +# Simple test to verify API authentication +# Usage: ./test-webmention-auth.sh YOUR_API_KEY + +API_KEY="${1:-your-secret-key}" +API_ENDPOINT="${API_ENDPOINT:-https://api.ritual.sh/webmention/send}" + +echo "Testing webmention API authentication" +echo "======================================" +echo "Endpoint: $API_ENDPOINT" +echo "API Key: ${API_KEY:0:10}... (truncated)" +echo "" + +# Test 1: Wrong auth (should get 401) +echo "Test 1: Wrong auth (expecting 401 Unauthorized)" +echo "------------------------------------------------" +curl -v -X POST "$API_ENDPOINT" \ + -H "Content-Type: application/json" \ + -d '{"auth":"wrong-key","source":"https://ritual.sh/test/","target":"https://example.com/test/"}' \ + 2>&1 | grep -E "(< HTTP|Unauthorized|error)" + +echo "" +echo "" + +# Test 2: Correct auth (should get 400 or 201 depending on whether endpoints exist) +echo "Test 2: Correct auth (expecting 400 'No endpoint found' or 201 success)" +echo "------------------------------------------------------------------------" +curl -v -X POST "$API_ENDPOINT" \ + -H "Content-Type: application/json" \ + -d "{\"auth\":\"$API_KEY\",\"source\":\"https://ritual.sh/test/\",\"target\":\"https://example.com/test/\"}" \ + 2>&1 | grep -E "(< HTTP|success|error|endpoint)" + +echo "" +echo "" + +# Test 3: Missing auth (should get 401) +echo "Test 3: Missing auth (expecting 401 Unauthorized)" +echo "--------------------------------------------------" +curl -v -X POST "$API_ENDPOINT" \ + -H "Content-Type: application/json" \ + -d '{"source":"https://ritual.sh/test/","target":"https://example.com/test/"}' \ + 2>&1 | grep -E "(< HTTP|Unauthorized|error)" From 9878573fe1280deff00cc16fdbdfb4fcc48812e4 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 12 Jan 2026 09:26:10 +0000 Subject: [PATCH 51/55] Removing debug, saving successfully delivered mentions --- .webmentions-sent | 1 + send-webmentions-debug.sh | 75 --------------------------------------- send-webmentions.sh | 6 ++-- test-webmention-auth.sh | 43 ---------------------- 4 files changed, 4 insertions(+), 121 deletions(-) delete mode 100755 send-webmentions-debug.sh delete mode 100755 test-webmention-auth.sh diff --git a/.webmentions-sent b/.webmentions-sent index e69de29..5493921 100644 --- a/.webmentions-sent +++ b/.webmentions-sent @@ -0,0 +1 @@ +https://ritual.sh/blog/2026-01-06-week-3-bit-chilly-out/|https://enclose.horse/ diff --git a/send-webmentions-debug.sh b/send-webmentions-debug.sh deleted file mode 100755 index 10a37d4..0000000 --- a/send-webmentions-debug.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash - -# Load environment variables from .env file -if [ -f .env ]; then - export $(cat .env | grep -v '^#' | xargs) -else - echo "⚠️ Warning: .env file not found, using defaults" -fi - -# Use environment variables with fallback defaults -WEBMENTIONS_FILE="${WEBMENTIONS_FILE:-public/webmentions.json}" -SENT_CACHE="${SENT_CACHE:-.webmentions-sent}" -API_ENDPOINT="${API_ENDPOINT:-https://api.ritual.sh/webmention/send}" -API_KEY="${API_KEY:-your-secret-key}" - -echo "DEBUG: Configuration" -echo "====================" -echo "API_ENDPOINT: $API_ENDPOINT" -echo "API_KEY: ${API_KEY:0:10}... (first 10 chars)" -echo "WEBMENTIONS_FILE: $WEBMENTIONS_FILE" -echo "" - -# Read the webmentions JSON -if [ ! -f "$WEBMENTIONS_FILE" ]; then - echo "No webmentions.json found at $WEBMENTIONS_FILE" - exit 0 -fi - -# Get first mention for testing -first_mention=$(jq -c '.[0]' "$WEBMENTIONS_FILE") -source=$(echo "$first_mention" | jq -r '.source') -target=$(echo "$first_mention" | jq -r '.target') - -echo "DEBUG: Testing with first webmention" -echo "=====================================" -echo "Source: $source" -echo "Target: $target" -echo "" - -# Create the JSON payload -payload=$(jq -n \ - --arg auth "$API_KEY" \ - --arg source "$source" \ - --arg target "$target" \ - '{auth: $auth, source: $source, target: $target}') - -echo "DEBUG: Payload" -echo "==============" -echo "$payload" | jq '.' -echo "" - -echo "DEBUG: Sending request..." -echo "=========================" - -# Send with verbose output -response=$(curl -v -X POST "$API_ENDPOINT" \ - -H "Content-Type: application/json" \ - -d "$payload" \ - 2>&1) - -echo "" -echo "DEBUG: Full Response" -echo "====================" -echo "$response" -echo "" - -# Extract just the HTTP status and response body -http_status=$(echo "$response" | grep "< HTTP" | tail -1) -response_body=$(echo "$response" | sed -n '/^{/,/^}/p' | tail -1) - -echo "" -echo "DEBUG: Parsed Results" -echo "=====================" -echo "HTTP Status: $http_status" -echo "Response Body: $response_body" diff --git a/send-webmentions.sh b/send-webmentions.sh index df445aa..f5ba12e 100755 --- a/send-webmentions.sh +++ b/send-webmentions.sh @@ -69,9 +69,9 @@ jq -c '.[]' "$WEBMENTIONS_FILE" | while read -r mention; do -d "{\"auth\":\"$API_KEY\",\"source\":\"$source\",\"target\":\"$target\"}") http_code=$(echo "$response" | tail -n1) - - # If successful, add to cache - if [ "$http_code" = "200" ] || [ "$http_code" = "202" ]; then + + # If successful (200, 201, or 202), add to cache + if [ "$http_code" = "200" ] || [ "$http_code" = "201" ] || [ "$http_code" = "202" ]; then echo "$key" >> "$SENT_CACHE" echo "✓ Sent successfully" else diff --git a/test-webmention-auth.sh b/test-webmention-auth.sh deleted file mode 100755 index be8bdeb..0000000 --- a/test-webmention-auth.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash - -# Simple test to verify API authentication -# Usage: ./test-webmention-auth.sh YOUR_API_KEY - -API_KEY="${1:-your-secret-key}" -API_ENDPOINT="${API_ENDPOINT:-https://api.ritual.sh/webmention/send}" - -echo "Testing webmention API authentication" -echo "======================================" -echo "Endpoint: $API_ENDPOINT" -echo "API Key: ${API_KEY:0:10}... (truncated)" -echo "" - -# Test 1: Wrong auth (should get 401) -echo "Test 1: Wrong auth (expecting 401 Unauthorized)" -echo "------------------------------------------------" -curl -v -X POST "$API_ENDPOINT" \ - -H "Content-Type: application/json" \ - -d '{"auth":"wrong-key","source":"https://ritual.sh/test/","target":"https://example.com/test/"}' \ - 2>&1 | grep -E "(< HTTP|Unauthorized|error)" - -echo "" -echo "" - -# Test 2: Correct auth (should get 400 or 201 depending on whether endpoints exist) -echo "Test 2: Correct auth (expecting 400 'No endpoint found' or 201 success)" -echo "------------------------------------------------------------------------" -curl -v -X POST "$API_ENDPOINT" \ - -H "Content-Type: application/json" \ - -d "{\"auth\":\"$API_KEY\",\"source\":\"https://ritual.sh/test/\",\"target\":\"https://example.com/test/\"}" \ - 2>&1 | grep -E "(< HTTP|success|error|endpoint)" - -echo "" -echo "" - -# Test 3: Missing auth (should get 401) -echo "Test 3: Missing auth (expecting 401 Unauthorized)" -echo "--------------------------------------------------" -curl -v -X POST "$API_ENDPOINT" \ - -H "Content-Type: application/json" \ - -d '{"source":"https://ritual.sh/test/","target":"https://example.com/test/"}' \ - 2>&1 | grep -E "(< HTTP|Unauthorized|error)" From d382c4ed1fb9f35980bcdea317caa2db179f8763 Mon Sep 17 00:00:00 2001 From: Dan Date: Mon, 12 Jan 2026 09:47:31 +0000 Subject: [PATCH 52/55] Adding webmention UrL to test --- layouts/_default/baseof.html | 1 + 1 file changed, 1 insertion(+) diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index d7d98c5..616ee96 100755 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -3,6 +3,7 @@ + {{ block "title" . }}{{ .Site.Title }} {{ with .Params.Title }} | {{ . }}{{ end }}{{ end }} From 35601848017adb9bf4ac65d60ef3e29df2942556 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Mon, 12 Jan 2026 12:24:05 +0000 Subject: [PATCH 53/55] Adding to people who have linked to project --- assets/sass/pages/resources.scss | 18 +++++++++++++++++- content/resources/button-generator/index.md | 13 +++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/assets/sass/pages/resources.scss b/assets/sass/pages/resources.scss index 70c7fbf..c14ed5f 100644 --- a/assets/sass/pages/resources.scss +++ b/assets/sass/pages/resources.scss @@ -586,7 +586,9 @@ height: 18px; background: #fff; border-radius: 50% 50% 0 0; - box-shadow: -12px 0 0 #fff, 12px 0 0 #fff; + box-shadow: + -12px 0 0 #fff, + 12px 0 0 #fff; animation: bow-sparkle 3s ease-in-out infinite; } } @@ -942,6 +944,20 @@ text-shadow: 0 0 5px rgba(0, 150, 255, 0.3); } + hr { + border: none; + height: 2px; + margin: 2rem 0; + background: linear-gradient( + 90deg, + transparent, + rgba(0, 150, 255, 0.6), + rgba(255, 120, 0, 0.6), + transparent + ); + position: relative; + } + code { background: rgba(0, 100, 180, 0.15); padding: 0.2rem 0.5rem; diff --git a/content/resources/button-generator/index.md b/content/resources/button-generator/index.md index 5c14182..5d074e1 100644 --- a/content/resources/button-generator/index.md +++ b/content/resources/button-generator/index.md @@ -25,3 +25,16 @@ Big thanks to [neonaut's 88x31 archive](https://neonaut.neocities.org/cyber/88x3 - 07/01/2025 - Initial release. - 08/01/2025 - Total refactor to be modular, added many more effects. - 09/01/2025 - Rewrote the bevel inset and outset border code so they look a lot nicer at the corners. Updates throughout so that multibyte (emoji!) characters should work. + +--- + +### Cool People Who Have Linked To This Project + +[tdh.se](https://tdh.se/2026/01/88x31-pixlar-retroknapp/), +[rknight.me](https://rknight.me/blog/88x31-button-curios/), +[fyr.io](https://fyr.io/scrap/2026-01-09), +[brennan.day](https://brennan.day/resources-for-the-personal-web-a-follow-up-guide/), +[kuemmerle.name](https://kuemmerle.name/bastelglueck/), +[craney.uk](https://craney.uk/posts/stuff-this-week-74) + +Am I missing you? Email me or send a webmention! From b56eebbc16fe58604ea0076617afd21370a8fc2b Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Mon, 12 Jan 2026 12:38:47 +0000 Subject: [PATCH 54/55] Updating contact details in human.txt --- layouts/humans.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/layouts/humans.txt b/layouts/humans.txt index 71ec599..68c8639 100644 --- a/layouts/humans.txt +++ b/layouts/humans.txt @@ -1,6 +1,6 @@ /* TEAM */ -Creator: Dan -Contact: dan [at] unbo.lt +Creator: Dan Baker +Contact: dan [at] ritual.sh From: ritual.sh /* THANKS */ @@ -16,7 +16,6 @@ Software: Built with care /* PHILOSOPHY */ No tracking -No analytics No AI training data No engagement metrics No corporate platforms From 7851cd84f29bc67bf14ee6ad75b3d365c5eb26d8 Mon Sep 17 00:00:00 2001 From: Dan <dan@unbo.lt> Date: Mon, 12 Jan 2026 13:19:55 +0000 Subject: [PATCH 55/55] Fixing styling on last.fm export --- assets/sass/partials/_form-controls.scss | 4 ---- .../index.md | 24 +++++++++++++++++++ layouts/shortcodes/lastfm-stats-form.html | 4 +++- 3 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 content/blog/2026-01-12-week-4-got-webmentions/index.md diff --git a/assets/sass/partials/_form-controls.scss b/assets/sass/partials/_form-controls.scss index 5a6b5a3..f134579 100644 --- a/assets/sass/partials/_form-controls.scss +++ b/assets/sass/partials/_form-controls.scss @@ -329,8 +329,4 @@ flex-direction: row; align-items: flex-end; } - - .btn-primary { - align-self: flex-end; - } } diff --git a/content/blog/2026-01-12-week-4-got-webmentions/index.md b/content/blog/2026-01-12-week-4-got-webmentions/index.md new file mode 100644 index 0000000..8194ce9 --- /dev/null +++ b/content/blog/2026-01-12-week-4-got-webmentions/index.md @@ -0,0 +1,24 @@ +--- +title: "Week 4 - Got Webmentions" +date: 2026-01-18 +tags: + - weeknote + - weekly update +draft: false +--- + +- 🗨️ Added the ability to send and receive [webmentions](https://indieweb.org/Webmention) to the blog. Haven't automated displaying of received ones yet, but I'll get there. +- 🧰 Setup a little personal API to handle the guestbook, visitor counter, and aforementioned webmentions. +- 📺 Setup a little dashboard on my homelab for monitoring all of the above! + +## Links I Found Interesting + +- [iCloud Photos Downloader](https://github.com/icloud-photos-downloader/icloud_photos_downloader) and [Photos Backup Anywhere](https://photosbackup.app/) - Two useful looking things for backing up my Photos library + +## Music + +## Next Week + +Not necessary + +Until next week! diff --git a/layouts/shortcodes/lastfm-stats-form.html b/layouts/shortcodes/lastfm-stats-form.html index e7cd3e9..65dd0f8 100644 --- a/layouts/shortcodes/lastfm-stats-form.html +++ b/layouts/shortcodes/lastfm-stats-form.html @@ -17,7 +17,9 @@ </select> </div> - <button id="fetch-stats" class="btn-primary">Get Stats</button> + <div class="form-group"> + <button id="fetch-stats" class="btn-primary">Get Stats</button> + </div> </div> <div id="stats-results" class="stats-results" style="display: none">