From 6e6138c2fb6523510cb1b2f88a6ff6a88a4a89fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Tue, 10 Feb 2026 19:40:45 +0200 Subject: [PATCH 01/46] Automate tag creation and GitHub releases Update deploy workflow to automatically: - Extract version from CHANGELOG.md after merge to main - Create and push git tag (e.g., v1.4.0) - Create GitHub release from CHANGELOG notes This eliminates manual tag creation step from release process. Workflow now: 1. Bump version on feature branch 2. Fill CHANGELOG.md 3. Create PR and merge 4. Tag and release created automatically --- .github/workflows/deploy.yml | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f6ad3fc..42b1a14 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -63,8 +63,26 @@ jobs: --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} \ --paths "/*" + - name: Extract version from CHANGELOG + id: version + if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_run' + run: | + VERSION=$(grep -m 1 '## \[' CHANGELOG.md | sed 's/## \[\(.*\)\].*/\1/') + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Found version: $VERSION" + + - name: Create and push tag + if: steps.version.outputs.version != '' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "v${{ steps.version.outputs.version }}" + git push origin "v${{ steps.version.outputs.version }}" + echo "Tag v${{ steps.version.outputs.version }} created and pushed" + sleep 2 + - name: Create GitHub Release - if: startsWith(github.ref, 'refs/tags/v') + if: steps.version.outputs.version != '' run: | python3 scripts/create_release.py env: @@ -76,6 +94,7 @@ jobs: echo "🌐 Website: https://obscvrat.fi" echo "πŸ“¦ S3 Bucket: ${{ secrets.S3_BUCKET_NAME }}" echo "πŸ”„ CloudFront invalidation created" - if [[ "${{ github.ref }}" == refs/tags/v* ]]; then - echo "🏷️ GitHub Release created" + if [[ -n "${{ steps.version.outputs.version }}" ]]; then + echo "🏷️ Tag v${{ steps.version.outputs.version }} created" + echo "πŸ“¦ GitHub Release created" fi From 8b59911b109ab18c65e638a4057ef22395c0899c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 11:14:50 +0200 Subject: [PATCH 02/46] Fix broken link in AGENTS.md Remove space in .kiro/instructions/ link that was preventing proper navigation to agent-specific instructions directory. --- AGENTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 0ae6836..f381e70 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -530,7 +530,7 @@ See `docs/DEPLOYMENT.md` for detailed deployment instructions. ## Additional Resources ### For AI Agents -- [.kiro/instructions/](. kiro/instructions/) - Agent-specific instructions +- [.kiro/instructions/](.kiro/instructions/) - Agent-specific instructions - `build.md` - Build Agent code style and testing - `commit.md` - Commit Agent git workflow - `test.md` - Test Agent validation commands From f49e0a18233e2157610778e737492db07a2bbafb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 11:14:59 +0200 Subject: [PATCH 03/46] Center hero logo on mobile devices Adjust hero logo positioning to remove horizontal offset on mobile screens. Moves transform from individual logo elements to container for cleaner responsive behavior. --- website/static/css/main.css | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/website/static/css/main.css b/website/static/css/main.css index 9fa9e31..f64188c 100644 --- a/website/static/css/main.css +++ b/website/static/css/main.css @@ -153,7 +153,7 @@ align-items: center; justify-content: center; filter: drop-shadow(0 0 60px rgba(0, 0, 0, 0.3)); - transform: translateX(3%) translateY(-2%); + transform: translateX(0) translateY(-2%); animation: fadeIn 0.8s ease-in-out forwards, pulseBlur 12s ease-in-out infinite; } @@ -961,10 +961,14 @@ min-height: 70vh; } - .hero-logo { + .hero-logo-container { + transform: translateX(0) translateY(-2%); + } + + .hero-logo-top, + .hero-logo-bottom { width: 65%; max-width: 100%; - transform: translateX(5%) translateY(-3%); } .latest-gigs, From 407d58b5e1732188346267fb0de96c7870ff07c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 15:11:47 +0200 Subject: [PATCH 04/46] Add ADR-018 for gear inventory management system Document architecture decision for gear inventory feature including: - YAML-based data storage in website/data/gear/ - Hugo template with expandable list and multi-select filters - CLI management script with fzf integration - AI-assisted gear addition via @add-gear prompt - SEO exclusion via robots.txt Files modified: - docs/adr/018-gear-inventory-management-and-display-system.md - new ADR - docs/adr/README.md - add ADR-018 to index --- ...inventory-management-and-display-system.md | 363 ++++++++++++++++++ docs/adr/README.md | 1 + 2 files changed, 364 insertions(+) create mode 100644 docs/adr/018-gear-inventory-management-and-display-system.md diff --git a/docs/adr/018-gear-inventory-management-and-display-system.md b/docs/adr/018-gear-inventory-management-and-display-system.md new file mode 100644 index 0000000..d52d90d --- /dev/null +++ b/docs/adr/018-gear-inventory-management-and-display-system.md @@ -0,0 +1,363 @@ +# 18. Gear Inventory Management and Display System + +**Status:** Proposed + +**Date:** 2026-02-15 + +## Context + +The Obscvrat website needs a dedicated page to document musical gear (pedals, synthesizers, instruments, modular equipment). Requirements: + +- Store detailed information: name, manufacturer, category, type(s), analog/digital, settings, description, links +- Easy content management via CLI tool (`make gear`) +- Auto-fill specifications via web search when possible +- Searchable and filterable by manufacturer, type, or keyword +- Hidden from search engines (accessible only via link in about page) +- Dynamic display with expandable list view +- No images initially (may add later) + +The page should serve as a personal inventory and reference, not a public showcase. It needs structured data storage for easy querying and filtering, while maintaining the dark minimal aesthetic of the site. + +## Decision + +We will implement a **YAML-based gear inventory system** with interactive CLI management and dynamic web display. + +### Data Structure + +**Storage:** YAML data files in `website/data/gear/` (one file per item) + +**File naming:** `manufacturer-model-slug.yaml` + +**Data schema:** +```yaml +name: "Big Muff Pi" +manufacturer: "Electro-Harmonix" +category: "Pedal" # Pedal, Synth, Instrument, Modular, Other +types: # Multiple types allowed (tags) + - "Distortion" + - "Fuzz" +technology: "Analog" # Analog, Digital, Hybrid +settings: # Knob/control names only + - "Volume" + - "Tone" + - "Sustain" +description: "Classic fuzz pedal with thick, creamy sustain..." +manufacturer_url: "https://www.ehx.com/products/big-muff-pi/" +pedal_url: "https://reverb.com/p/electro-harmonix-big-muff-pi" +``` + +### Management Tool + +**Command:** `make gear` + +**Features:** +1. **Add gear:** + - User provides: manufacturer + name + - Script searches web for specifications (type, technology, settings, URLs) + - Auto-fills YAML fields where possible + - Prompts user for missing information + - Creates YAML file in `website/data/gear/` + +2. **List gear:** + - Show all gear (manufacturer + name only) + - Filter by category (Pedals, Synths, etc.) + - Filter by manufacturer + - Filter by type (Distortion, Delay, etc.) + +3. **Search gear:** + - Keyword search across all fields + - Works on filtered subsets + +4. **Edit gear:** + - Select from list + - Opens YAML in $EDITOR + +5. **Delete gear:** + - Select from list + - Confirm deletion + +**Web Search Integration:** +- Use web search API to find gear specifications +- Search query: "[manufacturer] [name] specifications" +- Parse results for: type, analog/digital, controls/settings, URLs +- If auto-fill fails, prompt user for manual entry + +### Display Page + +**URL:** `/gear/` (hidden from search engines) + +**Layout:** +- **Default view:** List of manufacturer + name only +- **Filtering controls:** Dropdowns/buttons for category, manufacturer, type +- **Search box:** Keyword filter +- **Expandable items:** Click to expand inline, showing full details +- **Collapsed state:** Just "Manufacturer - Name" +- **Expanded state:** All fields (type, technology, settings, description, links) + +**Styling:** +- Match dark minimal aesthetic (ADR-007) +- Typo font for body text, Fira Mono for structure +- Subtle expand/collapse animation +- External links open in new tab + +**JavaScript:** +- Client-side filtering (no page reload) +- Expand/collapse functionality +- Search across visible items +- Filter combinations (e.g., "Pedals" + "Delay" + "Analog") + +### SEO & Privacy + +**Hidden from search engines:** +- Add to `robots.txt`: `Disallow: /gear/` +- Exclude from sitemap generation +- No meta tags for indexing + +**Access:** +- Only link from about page: "broken circuits, and [analog filth](/gear/)β€”" +- No navigation menu entry +- Direct URL access allowed (not password-protected) + +### Implementation Components + +1. **Python script:** `scripts/manage_gear.py` + - Interactive CLI menu + - Web search integration + - YAML file management + - Validation and error handling + +2. **Hugo template:** `website/layouts/gear/list.html` + - Reads YAML data from `website/data/gear/` + - Generates expandable list + - Includes filtering JavaScript + +3. **CSS:** Expand/collapse styling in `website/static/css/main.css` + +4. **robots.txt:** Add `/gear/` disallow rule + +5. **About page:** Add link to `/gear/` + +## Alternatives Considered + +### Alternative 1: Single Markdown File + +Store all gear in one markdown file with manual formatting. + +**Pros:** +- Simplest approach +- No script needed +- Easy to edit + +**Cons:** +- No structured data (can't filter/search programmatically) +- Hard to maintain as list grows +- No auto-fill capabilities +- Manual formatting tedious + +**Why rejected:** Lacks structure and automation needed for detailed inventory + +### Alternative 2: Single Type Classification + +Each item has only one type (primary classification). + +```yaml +type: "Fuzz" # Must choose one +``` + +**Pros:** +- Simpler data model +- Clearer categorization +- Easier filtering logic + +**Cons:** +- Many pedals serve multiple purposes (Big Muff is both distortion AND fuzz) +- Less accurate representation +- User must choose arbitrary primary type +- Harder to find gear under secondary use case + +**Why rejected:** Multi-purpose gear is common; tags provide better flexibility + +### Alternative 3: Database (SQLite) + +Store gear in SQLite database. + +**Pros:** +- Powerful querying +- Relational data +- ACID transactions + +**Cons:** +- Overkill for static site +- Binary format (not git-friendly) +- Requires database management +- Not human-readable +- Adds complexity + +**Why rejected:** YAML files are simpler, git-friendly, and sufficient for needs + +### Alternative 4: Manual Entry Only (No Web Search) + +User manually enters all information for each item. + +**Pros:** +- No web search API dependency +- No API key needed +- Full control over data + +**Cons:** +- Tedious and time-consuming +- Error-prone (typos, inconsistent formatting) +- Discourages adding gear +- No time savings + +**Why rejected:** Auto-fill significantly improves UX and reduces friction + +### Alternative 5: Modal Popup for Details + +Click item opens modal overlay with full details. + +**Pros:** +- Cleaner visual separation +- Focus on single item +- Common UI pattern + +**Cons:** +- Requires closing modal to view another item +- More clicks to compare items +- Harder to scan multiple items quickly +- More complex JavaScript + +**Why rejected:** Expandable list allows easier comparison and scanning + +## Consequences + +### Positive + +- **Structured data:** YAML enables programmatic filtering and searching +- **Auto-fill:** Web search saves time entering specifications +- **Flexible categorization:** Multiple types per item (tags) accurately represents multi-purpose gear +- **Easy management:** CLI tool simplifies adding/editing gear +- **Dynamic filtering:** JavaScript filtering provides smooth UX without page reloads +- **Hidden from search:** Privacy maintained while still accessible via direct link +- **Expandable list:** Easy to scan and compare items +- **Scalable:** Can add hundreds of items without performance issues +- **Git-friendly:** YAML files track changes clearly +- **Future-proof:** Can add images, signal chains, or other features later + +### Negative + +- **Web search dependency:** Auto-fill requires web search API (may fail or require API key) +- **Manual fallback:** If auto-fill fails, user must enter all info manually +- **JavaScript required:** Filtering won't work without JavaScript enabled +- **Maintenance:** Must keep script updated if data schema changes +- **Learning curve:** User must understand YAML structure for manual edits +- **No images initially:** Visual reference missing (can add later) + +### Neutral + +- **One file per item:** More files in repository but better organization +- **Hidden page:** Not discoverable via navigation, only via about page link +- **Multiple types:** More flexible but requires careful tagging +- **CLI tool:** Requires terminal access (not web-based admin) + +## Notes + +### Web Search API Options + +**Option 1: Tavily Search API** +- Kiro CLI has built-in web search capability +- No additional API key needed +- Returns structured results + +**Option 2: DuckDuckGo (via Python library)** +- Free, no API key +- Simple integration +- May have rate limits + +**Option 3: Manual entry fallback** +- If search fails, prompt user for each field +- Always available as backup + +**Recommendation:** Use Kiro CLI web search (Tavily) with manual fallback. + +### Type Categories + +**Common pedal types:** +- Distortion, Overdrive, Fuzz +- Delay, Reverb, Echo +- Chorus, Flanger, Phaser +- Tremolo, Vibrato +- Compressor, Limiter +- EQ, Filter +- Looper, Sampler +- Noise, Glitch + +**Synth categories:** +- Analog Synth, Digital Synth, Hybrid +- Modular, Semi-Modular +- Drum Machine, Sampler +- Sequencer, Groovebox + +### Future Enhancements + +- Add images of gear (photos or manufacturer images) +- Signal chain diagrams showing how gear connects +- Track gear used on specific releases/performances +- Maintenance logs (repairs, modifications, settings changes) +- Purchase date and price tracking +- Condition notes (mint, good, modified, etc.) +- Favorite settings presets +- Audio samples or demo links + +### Implementation Checklist + +**Phase 1: Data Structure** +- [ ] Create `website/data/gear/` directory +- [ ] Define YAML schema +- [ ] Create example gear file for testing + +**Phase 2: Management Script** +- [ ] Create `scripts/manage_gear.py` +- [ ] Implement add/list/search/edit/delete functions +- [ ] Integrate web search API +- [ ] Add manual entry fallback +- [ ] Test with real gear data + +**Phase 3: Display Page** +- [ ] Create `website/layouts/gear/list.html` +- [ ] Implement expandable list layout +- [ ] Add filtering controls (category, manufacturer, type) +- [ ] Add search box +- [ ] Style with dark minimal aesthetic + +**Phase 4: JavaScript** +- [ ] Implement client-side filtering +- [ ] Add expand/collapse functionality +- [ ] Add search functionality +- [ ] Test filter combinations + +**Phase 5: Integration** +- [ ] Add `/gear/` to robots.txt +- [ ] Update about page with link +- [ ] Test hidden page behavior +- [ ] Verify search engine exclusion + +**Phase 6: Testing** +- [ ] Add multiple gear items +- [ ] Test all filtering combinations +- [ ] Test search functionality +- [ ] Test expand/collapse +- [ ] Test on mobile devices + +## Related Decisions + +- **ADR-007:** Homepage Design System - dark minimal aesthetic applies to gear page +- **ADR-011:** YAML Data Files for Content Management - same approach used here +- **ADR-009:** Live Performance Media Management - similar YAML + CLI pattern + +## References + +- YAML specification: https://yaml.org/spec/ +- Hugo data files: https://gohugo.io/templates/data-templates/ +- Web search APIs: Tavily, DuckDuckGo +- Expandable list UI patterns: https://www.w3.org/WAI/ARIA/apg/patterns/accordion/ diff --git a/docs/adr/README.md b/docs/adr/README.md index b347ba1..5d7a627 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -40,6 +40,7 @@ decision made, alternatives considered, and consequences. - **[015](015-image-optimization-with-hugo-processing.md)** - Image Optimization (Accepted, 2026-02-05) - **[016](016-cloudfront-analytics-for-website-traffic-monitoring.md)** - CloudFront Analytics (Accepted, 2026-02-09) - **[017](017-makefile-as-universal-command-interface.md)** - Makefile as Universal Interface (Accepted, 2026-02-10) +- **[018](018-gear-inventory-management-and-display-system.md)** - Gear Inventory Management (Proposed, 2026-02-15) ## Creating a New ADR From 59d4693e50159d4cf2a78a35c94e37174d1da337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 15:11:59 +0200 Subject: [PATCH 05/46] Add gear management CLI and Makefile integration Implement command-line tool for managing gear inventory with: - Add/list/search/edit/delete operations - fzf integration for interactive selection - YAML validation and nested settings support - AI-assisted addition prompt Files added: - scripts/manage_gear.py - CLI management tool - mk/gear.mk - Makefile module for 'make gear' command - .kiro/prompts/add-gear.md - AI-assisted gear addition Files modified: - Makefile - include gear.mk module - requirements-dev.txt - add ddgs>=1.0.0 for web search --- .kiro/prompts/add-gear.md | 75 +++++++++ Makefile | 2 + mk/gear.mk | 8 + requirements-dev.txt | 3 + scripts/manage_gear.py | 316 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 404 insertions(+) create mode 100644 .kiro/prompts/add-gear.md create mode 100644 mk/gear.mk create mode 100755 scripts/manage_gear.py diff --git a/.kiro/prompts/add-gear.md b/.kiro/prompts/add-gear.md new file mode 100644 index 0000000..fa389b9 --- /dev/null +++ b/.kiro/prompts/add-gear.md @@ -0,0 +1,75 @@ +# Add Gear (AI-Assisted) + +Quickly add musical gear to your inventory using AI-powered web search and auto-fill. + +## Usage + +``` +@add-gear MANUFACTURER MODEL +``` + +## Examples + +``` +@add-gear BOSS BD-2 Blues Driver +@add-gear Electro-Harmonix Big Muff Pi +@add-gear Moog Mother-32 +``` + +## What It Does + +1. Searches the web for gear specifications +2. Extracts: + - Category (Pedal/Synth) + - Types (Distortion, Overdrive, Delay, etc.) + - Technology (Analog/Digital/Hybrid) + - Controls/Settings + - Description + - Manufacturer and product URLs +3. Creates YAML file in `website/data/gear/` +4. Confirms with you before saving + +## Manual Alternative + +For manual entry or to manage existing gear: +```bash +make gear +``` + +## Prompt + +You are helping add musical gear to the Obscvrat gear inventory. + +When the user provides manufacturer and model name: + +1. Search the web for specifications +2. Extract the following information: + - **Category**: Pedal or Synth + - **Types**: Array of types (e.g., ["Distortion", "Overdrive"]) + - **Technology**: Analog, Digital, or Hybrid + - **Settings**: Array of control names (e.g., ["Volume", "Tone", "Gain"]) + - **Description**: Brief description (1-2 sentences) + - **URL**: Official manufacturer product page (prefer manufacturer over retailers) + +3. Create YAML file in `website/data/gear/` with format: +```yaml +name: "BD-2 Blues Driver" +manufacturer: "BOSS" +category: "Pedal" +types: + - "Overdrive" + - "Distortion" +technology: "Analog" +settings: + - "Level" + - "Tone" + - "Gain" +description: "Classic overdrive pedal with warm, tube-like tone" +url: "https://www.boss.info/us/products/bd-2/" +``` + +4. **IMPORTANT: Show the user what you found and ask "Is this correct? (y/n)"** +5. Only save the file after user confirms +6. Save the file with slug: `manufacturer-model.yaml` + +Be thorough in your web search. If you can't find specific information, ask the user. diff --git a/Makefile b/Makefile index 28b82b5..4c15cd5 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ # Include all modular makefiles include mk/adr.mk include mk/generate.mk +include mk/gear.mk include mk/hooks.mk include mk/live.mk include mk/media.mk @@ -21,6 +22,7 @@ help: ## Show this help @echo " make live - Manage live performances" @echo " make media - Manage media (pictures, videos)" @echo " make music - Manage music/albums" + @echo " make gear - Manage gear inventory" @echo " make generate - Generate markdown from YAML data" @echo " make bump-version - Bump version in CHANGELOG.md" @echo " make create-release - Create GitHub release from CHANGELOG.md" diff --git a/mk/gear.mk b/mk/gear.mk new file mode 100644 index 0000000..b88f105 --- /dev/null +++ b/mk/gear.mk @@ -0,0 +1,8 @@ +.PHONY: gear + +gear: ## Manage gear inventory (pedals, synths, instruments) + @if [ -f .venv/bin/python3 ]; then \ + .venv/bin/python3 scripts/manage_gear.py; \ + else \ + python3 scripts/manage_gear.py; \ + fi diff --git a/requirements-dev.txt b/requirements-dev.txt index bd7c1fb..c3c1bff 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -21,3 +21,6 @@ html5lib>=1.1 # HTML5 parser and validator # Secret scanning (pure Python) detect-secrets>=1.5.0 # Yelp's secret scanner + +# Web search for gear management +ddgs>=1.0.0 # DuckDuckGo search API (renamed from duckduckgo-search) diff --git a/scripts/manage_gear.py b/scripts/manage_gear.py new file mode 100755 index 0000000..f44b4c1 --- /dev/null +++ b/scripts/manage_gear.py @@ -0,0 +1,316 @@ +#!/usr/bin/env python3 +""" +Gear inventory management tool for Obscvrat website. +Manages YAML data files for musical gear (pedals, synths, instruments). + +For AI-assisted gear addition, just tell Kiro: "add BOSS BD-2 to gear" +""" + +import os +import sys +import yaml +import re +import shutil +import subprocess +from pathlib import Path +from typing import List, Optional + +# Set UTF-8 encoding for stdin/stdout +if sys.stdin.encoding != 'utf-8': + sys.stdin.reconfigure(encoding='utf-8') +if sys.stdout.encoding != 'utf-8': + sys.stdout.reconfigure(encoding='utf-8') + +GEAR_DIR = Path(__file__).parent.parent / "website" / "data" / "gear" + +def fzf_select(options: List[str], prompt: str = "Select") -> Optional[str]: + """Use fzf for selection if available, otherwise fall back to numbered menu.""" + if not options: + return None + + # Check if fzf is available + if shutil.which("fzf"): + try: + result = subprocess.run( + [ + "fzf", + "--prompt", f"{prompt}> ", + "--height", "40%", + "--reverse", + "--border", + "--no-mouse" + ], + input="\n".join(options), + capture_output=True, + text=True, + check=False, + env={**os.environ, "FZF_DEFAULT_OPTS": ""} + ) + if result.returncode == 0: + return result.stdout.strip() + return None + except Exception: + pass + + # Fallback: numbered selection + print(f"\n{prompt}:") + for i, option in enumerate(options, 1): + print(f"{i}) {option}") + + try: + selection = input("\nSelect number (or 0 to cancel): ").strip() + if selection == "0": + return None + idx = int(selection) - 1 + if 0 <= idx < len(options): + return options[idx] + except (ValueError, EOFError, KeyboardInterrupt): + pass + + return None + +def slugify(text): + """Convert text to URL-friendly slug.""" + text = text.lower().strip() + text = re.sub(r'[^\w\s-]', '', text) + text = re.sub(r'[-\s]+', '-', text) + return text + +def load_gear(): + """Load all gear from YAML files.""" + gear_list = [] + if not GEAR_DIR.exists(): + return gear_list + + for file in GEAR_DIR.glob("*.yaml"): + if file.name == '.gitkeep': + continue + with open(file, 'r') as f: + data = yaml.safe_load(f) + data['_filename'] = file.name + gear_list.append(data) + + return sorted(gear_list, key=lambda x: (x['manufacturer'], x['name'])) + +def save_gear(data): + """Save gear data to YAML file.""" + GEAR_DIR.mkdir(parents=True, exist_ok=True) + + slug = slugify(f"{data['manufacturer']}-{data['name']}") + filename = GEAR_DIR / f"{slug}.yaml" + + with open(filename, 'w') as f: + yaml.dump(data, f, default_flow_style=False, sort_keys=False, allow_unicode=True) + + return filename + +def add_gear(): + """Add new gear interactively.""" + print("\n=== Add New Gear ===\n") + print("πŸ’‘ Tip: For AI-assisted entry, tell Kiro: 'add [manufacturer] [model] to gear'\n") + + manufacturer = input("Manufacturer: ").strip() + if not manufacturer: + print("❌ Manufacturer required") + return + + name = input("Name/Model: ").strip() + if not name: + print("❌ Name required") + return + + # Category selection + categories = ["Pedal", "Synth"] + category = fzf_select(categories, "Select category") + if not category: + print("❌ Cancelled") + return + + # Types + print("\nTypes (comma-separated):") + print("Examples: Distortion, Overdrive, Delay, Reverb, Chorus") + types_input = input("Types: ").strip() + types = [t.strip() for t in types_input.split(',') if t.strip()] + + # Technology + technologies = ["Analog", "Digital", "Hybrid"] + technology = fzf_select(technologies, "Select technology") + if not technology: + print("❌ Cancelled") + return + + # Settings + print("\nSettings/Controls (comma-separated):") + print("Example: Volume, Tone, Gain") + settings_input = input("Settings: ").strip() + settings = [s.strip() for s in settings_input.split(',') if s.strip()] + + # Description + description = input("\nDescription (optional): ").strip() + + # URL + url = input("URL (manufacturer page, optional): ").strip() + + # Build data + data = { + 'name': name, + 'manufacturer': manufacturer, + 'category': category, + 'types': types, + 'technology': technology, + 'settings': settings, + 'description': description, + 'url': url or '' + } + + # Save + filename = save_gear(data) + print(f"\nβœ… Gear saved: {filename.name}") + +def list_gear(filter_category=None, filter_manufacturer=None, filter_type=None): + """List all gear with optional filters.""" + gear_list = load_gear() + + if not gear_list: + print("\nπŸ“¦ No gear found") + return + + # Apply filters + if filter_category: + gear_list = [g for g in gear_list if g['category'] == filter_category] + if filter_manufacturer: + gear_list = [g for g in gear_list if filter_manufacturer.lower() in g['manufacturer'].lower()] + if filter_type: + gear_list = [g for g in gear_list if any(filter_type.lower() in t.lower() for t in g.get('types', []))] + + if not gear_list: + print("\nπŸ“¦ No gear matches filters") + return + + print(f"\n=== Gear List ({len(gear_list)} items) ===\n") + for i, gear in enumerate(gear_list, 1): + types_str = ', '.join(gear.get('types', [])) + print(f"{i}. {gear['manufacturer']} - {gear['name']}") + print(f" Category: {gear['category']} | Types: {types_str} | Tech: {gear['technology']}") + + return gear_list + +def search_gear(): + """Search gear by keyword.""" + keyword = input("\nSearch keyword: ").strip().lower() + if not keyword: + return + + gear_list = load_gear() + results = [] + + for gear in gear_list: + searchable = f"{gear['manufacturer']} {gear['name']} {gear.get('description', '')} {' '.join(gear.get('types', []))}" + if keyword in searchable.lower(): + results.append(gear) + + if not results: + print(f"\nπŸ“¦ No gear found matching '{keyword}'") + return + + print(f"\n=== Search Results ({len(results)} items) ===\n") + for i, gear in enumerate(results, 1): + print(f"{i}. {gear['manufacturer']} - {gear['name']}") + +def edit_gear(): + """Edit existing gear.""" + gear_list = load_gear() + if not gear_list: + print("\nπŸ“¦ No gear found") + return + + # Build options for fzf + options = [f"{g['manufacturer']} - {g['name']}" for g in gear_list] + selected = fzf_select(options, "Select gear to edit") + + if not selected: + print("❌ Cancelled") + return + + # Find selected gear + idx = options.index(selected) + gear = gear_list[idx] + filename = GEAR_DIR / gear['_filename'] + + editor = os.environ.get('EDITOR', 'nano') + os.system(f"{editor} {filename}") + print(f"βœ… Edited: {filename.name}") + +def delete_gear(): + """Delete gear.""" + gear_list = load_gear() + if not gear_list: + print("\nπŸ“¦ No gear found") + return + + # Build options for fzf + options = [f"{g['manufacturer']} - {g['name']}" for g in gear_list] + selected = fzf_select(options, "Select gear to delete") + + if not selected: + print("❌ Cancelled") + return + + # Find selected gear + idx = options.index(selected) + gear = gear_list[idx] + + confirm = input(f"Delete '{gear['manufacturer']} - {gear['name']}'? (yes/no): ").strip().lower() + + if confirm == 'yes': + filename = GEAR_DIR / gear['_filename'] + filename.unlink() + print(f"βœ… Deleted: {gear['manufacturer']} - {gear['name']}") + else: + print("❌ Cancelled") + +def main_menu(): + """Display main menu.""" + while True: + options = [ + "Add gear", + "List all gear", + "List by category", + "List by manufacturer", + "Search gear", + "Edit gear", + "Delete gear", + "Exit" + ] + + choice = fzf_select(options, "OBSCVRAT GEAR MANAGEMENT") + + if not choice or choice == "Exit": + print("\nπŸ‘‹ Goodbye!") + break + elif choice == "Add gear": + add_gear() + elif choice == "List all gear": + list_gear() + elif choice == "List by category": + categories = ["Pedal", "Synth"] + cat = fzf_select(categories, "Filter by category") + if cat: + list_gear(filter_category=cat) + elif choice == "List by manufacturer": + mfr = input("\nFilter by manufacturer: ").strip() + if mfr: + list_gear(filter_manufacturer=mfr) + elif choice == "Search gear": + search_gear() + elif choice == "Edit gear": + edit_gear() + elif choice == "Delete gear": + delete_gear() + +if __name__ == '__main__': + try: + main_menu() + except KeyboardInterrupt: + print("\n\nπŸ‘‹ Goodbye!") + sys.exit(0) From efef05891074b833e530e946d58a517f5b24b4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 15:12:11 +0200 Subject: [PATCH 06/46] Add gear inventory page with Hugo template Create gear catalog page with: - Expandable list of gear items - Multi-select filters (category, manufacturer, type) - Search functionality - Visual filter tags with rust smoke effect - SVG filter for rough, hand-drawn borders - Nested settings support Files added: - website/layouts/gear/list.html - gear page template - website/content/gear/_index.md - gear page content - website/data/gear/*.yaml - gear data files (BD-2, RingerBringer, Data Corrupter) Features: - JavaScript filtering with OR logic within type, AND across types - Rough border effect using SVG feTurbulence filter - Dark minimal aesthetic with rust-colored filter tags --- website/content/gear/_index.md | 3 + website/data/gear/.gitkeep | 1 + website/data/gear/boss-bd-2-blues-driver.yaml | 12 + .../earthquaker-devices-data-corrupter.yaml | 25 ++ .../data/gear/warm-audio-ringerbringer.yaml | 14 + website/layouts/gear/list.html | 284 ++++++++++++++++++ 6 files changed, 339 insertions(+) create mode 100644 website/content/gear/_index.md create mode 100644 website/data/gear/.gitkeep create mode 100644 website/data/gear/boss-bd-2-blues-driver.yaml create mode 100644 website/data/gear/earthquaker-devices-data-corrupter.yaml create mode 100644 website/data/gear/warm-audio-ringerbringer.yaml create mode 100644 website/layouts/gear/list.html diff --git a/website/content/gear/_index.md b/website/content/gear/_index.md new file mode 100644 index 0000000..26411b0 --- /dev/null +++ b/website/content/gear/_index.md @@ -0,0 +1,3 @@ +--- +title: "Gear" +--- diff --git a/website/data/gear/.gitkeep b/website/data/gear/.gitkeep new file mode 100644 index 0000000..91dc512 --- /dev/null +++ b/website/data/gear/.gitkeep @@ -0,0 +1 @@ +# Gear data files stored here diff --git a/website/data/gear/boss-bd-2-blues-driver.yaml b/website/data/gear/boss-bd-2-blues-driver.yaml new file mode 100644 index 0000000..86c8399 --- /dev/null +++ b/website/data/gear/boss-bd-2-blues-driver.yaml @@ -0,0 +1,12 @@ +name: "BD-2 Blues Driver" +manufacturer: "BOSS" +category: "Pedal" +types: + - "Overdrive" +technology: "Analog" +settings: + - "Level" + - "Tone" + - "Gain" +description: "Classic overdrive pedal with warm, tube-like tone and responsive touch sensitivity. Known for its smooth, natural breakup and ability to retain guitar character." +url: "https://www.boss.info/us/products/bd-2/" diff --git a/website/data/gear/earthquaker-devices-data-corrupter.yaml b/website/data/gear/earthquaker-devices-data-corrupter.yaml new file mode 100644 index 0000000..0bbcea9 --- /dev/null +++ b/website/data/gear/earthquaker-devices-data-corrupter.yaml @@ -0,0 +1,25 @@ +name: "Data Corrupter" +manufacturer: "EarthQuaker Devices" +category: "Pedal" +types: + - "Harmonizer" + - "PLL" +technology: "Analog" +settings: + Voice Mixer: + - "Square" + - "Subharmonic" + - "Oscillator" + Master Oscillator (PLL): + - "Rotary Switch (8-position)" + - "Root Switch (3-position)" + Frequency Modulator: + - "Glide Switch (2-position)" + - "Rate" + Subharmonic: + - "Rotary Switch (8-position)" + - "Root Switch (2-position)" + Output: + - "Level" +description: "Monophonic harmonizing PLL (Phase-Locked Loop) pedal that creates wild, glitchy synthesizer-like tones. Features voice mixer with square wave fuzz, subharmonics, and modulated master oscillator for chaotic, robotic sounds." +url: "https://www.earthquakerdevices.com/data-corrupter" diff --git a/website/data/gear/warm-audio-ringerbringer.yaml b/website/data/gear/warm-audio-ringerbringer.yaml new file mode 100644 index 0000000..dd4ad9f --- /dev/null +++ b/website/data/gear/warm-audio-ringerbringer.yaml @@ -0,0 +1,14 @@ +name: "RingerBringer" +manufacturer: "Warm Audio" +category: "Pedal" +types: + - "Ring Modulator" +technology: "Analog" +settings: + - "Amount" + - "Rate" + - "Drive" + - "Mix" + - "Frequency" +description: "Analog ring modulator pedal delivering metallic, bell-like tones and dissonant harmonics. Creates unique modulation effects from subtle shimmer to extreme robotic sounds." +url: "https://www.warmaudio.com/ringerbringer/" diff --git a/website/layouts/gear/list.html b/website/layouts/gear/list.html new file mode 100644 index 0000000..c04639d --- /dev/null +++ b/website/layouts/gear/list.html @@ -0,0 +1,284 @@ +{{ define "main" }} + + + + + + + +
+

Gear

+ +
+
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ {{ range $index, $file := (readDir "data/gear") }} + {{ if strings.HasSuffix $file.Name ".yaml" }} + {{ $gear := index $.Site.Data.gear (strings.TrimSuffix ".yaml" $file.Name) }} + {{ if $gear }} +
+ +
+ {{ $gear.manufacturer }} - {{ $gear.name }} + β–Ό +
+ +
+
+ {{ $gear.category }} + {{ if $gear.types }} + {{ delimit $gear.types ", " }} + {{ end }} + {{ $gear.technology }} +
+ + {{ if $gear.description }} +

{{ $gear.description }}

+ {{ end }} + + {{ if $gear.settings }} +
+ Controls: + {{ if reflect.IsMap $gear.settings }} + {{/* Nested settings with sections */}} + {{ range $section, $controls := $gear.settings }} +
+ {{ $section }}: {{ delimit $controls ", " }} +
+ {{ end }} + {{ else }} + {{/* Flat list of settings */}} + {{ delimit $gear.settings ", " }} + {{ end }} +
+ {{ end }} + + +
+
+ {{ end }} + {{ end }} + {{ end }} +
+ + + + + +{{ end }} From a9ba4e50f05b3d5ce66190c313a90f3db112cde8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 15:12:26 +0200 Subject: [PATCH 07/46] Add gear page CSS with rust smoke filter tags Implement dark minimal styling for gear page: - Rust-colored smoke effect on filter tags - Complex radial gradients (12 steps per smoke pocket) - Black gradient from left edge (0-15%) - Smoke pockets positioned at 35-85% horizontally - Rough border effect on inputs and gear items - Expandable gear items with smooth transitions - Responsive layout with flexbox Filter tag styling: - 3 smoke pockets with rust colors fading to transparent - Base layer with radial gradient black - Hover state with enhanced smoke effect - Remove button with color transition Gear item styling: - Rough borders using SVG filter - Clean text without distortion effects - Border hidden between header and details when expanded - Nested settings display support Files modified: - website/static/css/main.css - add 345 lines of gear page styles --- website/static/css/main.css | 342 ++++++++++++++++++++++++++++++++++++ 1 file changed, 342 insertions(+) diff --git a/website/static/css/main.css b/website/static/css/main.css index f64188c..4df416d 100644 --- a/website/static/css/main.css +++ b/website/static/css/main.css @@ -990,3 +990,345 @@ min-width: 55px; } } + +/* Gear Page Styles */ +.gear-page { + max-width: 1200px; + margin: 0 auto; + padding: 2rem 1rem; +} + +.gear-page h1 { + font-family: 'Fira Mono', monospace; + font-size: 2rem; + margin-bottom: 2rem; + color: var(--primary-color); +} + +.gear-controls { + display: flex; + gap: 1rem; + margin-bottom: 2rem; + flex-wrap: wrap; +} + +.gear-controls input, +.gear-controls select { + padding: 0.5rem; + background: rgba(229, 229, 229, 0.05); + border: 1px solid rgba(229, 229, 229, 0.3); + color: var(--primary-color); + font-family: 'Fira Mono', monospace; + font-size: 0.9rem; +} + +.input-wrapper { + filter: url(#rough); + display: inline-block; +} + +.active-filters { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-bottom: 1.5rem; + min-height: 2rem; +} + +.filter-tag { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.9rem; + background: + linear-gradient(to right, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 15%), + radial-gradient(ellipse at 35% 45%, + rgba(180, 90, 70, 0.75) 0%, + rgba(165, 82, 62, 0.62) 8%, + rgba(150, 75, 55, 0.5) 14%, + rgba(135, 68, 50, 0.4) 19%, + rgba(120, 60, 45, 0.3) 24%, + rgba(105, 52, 38, 0.22) 28%, + rgba(90, 45, 32, 0.15) 32%, + rgba(75, 38, 27, 0.1) 35%, + rgba(60, 30, 20, 0.06) 38%, + rgba(45, 22, 15, 0.03) 41%, + rgba(30, 15, 10, 0.01) 44%, + transparent 48%), + radial-gradient(ellipse at 55% 30%, + rgba(140, 70, 50, 0.6) 0%, + rgba(127, 63, 45, 0.5) 8%, + rgba(115, 57, 41, 0.4) 14%, + rgba(102, 51, 37, 0.32) 19%, + rgba(90, 45, 32, 0.24) 24%, + rgba(77, 38, 27, 0.18) 28%, + rgba(65, 32, 23, 0.12) 32%, + rgba(52, 26, 18, 0.08) 35%, + rgba(40, 20, 14, 0.04) 38%, + rgba(27, 13, 9, 0.02) 41%, + rgba(15, 7, 5, 0.005) 44%, + transparent 46%), + radial-gradient(ellipse at 45% 70%, + rgba(120, 60, 45, 0.55) 0%, + rgba(108, 54, 40, 0.46) 8%, + rgba(96, 48, 36, 0.38) 14%, + rgba(84, 42, 31, 0.3) 19%, + rgba(72, 36, 27, 0.23) 24%, + rgba(60, 30, 22, 0.16) 28%, + rgba(48, 24, 18, 0.11) 32%, + rgba(36, 18, 13, 0.07) 35%, + rgba(24, 12, 9, 0.03) 38%, + rgba(12, 6, 4, 0.01) 41%, + transparent 44%), + radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 0.75) 0%, rgba(0, 0, 0, 0.9) 70%, rgba(0, 0, 0, 0.95) 100%); + border-radius: 4px; + font-family: 'Fira Mono', monospace; + font-size: 0.85rem; + color: var(--primary-color); + cursor: pointer; + transition: all 0.2s ease; + box-shadow: + inset 0 0 8px rgba(160, 80, 60, 0.15), + 0 2px 6px rgba(0, 0, 0, 0.5); +} + +.filter-tag:hover { + background: + linear-gradient(to right, rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0) 15%), + radial-gradient(ellipse at 40% 50%, + rgba(200, 100, 80, 0.85) 0%, + rgba(185, 92, 74, 0.72) 8%, + rgba(170, 85, 68, 0.6) 14%, + rgba(155, 77, 62, 0.5) 19%, + rgba(140, 70, 56, 0.4) 24%, + rgba(125, 62, 50, 0.31) 28%, + rgba(110, 55, 44, 0.23) 32%, + rgba(95, 47, 38, 0.16) 35%, + rgba(80, 40, 32, 0.1) 38%, + rgba(65, 32, 26, 0.05) 41%, + rgba(40, 20, 16, 0.02) 44%, + transparent 48%), + radial-gradient(ellipse at 60% 25%, + rgba(160, 80, 60, 0.7) 0%, + rgba(147, 73, 55, 0.59) 8%, + rgba(135, 67, 50, 0.49) 14%, + rgba(122, 61, 45, 0.4) 19%, + rgba(110, 55, 41, 0.31) 24%, + rgba(97, 48, 36, 0.23) 28%, + rgba(85, 42, 31, 0.17) 32%, + rgba(72, 36, 27, 0.11) 35%, + rgba(60, 30, 22, 0.06) 38%, + rgba(47, 23, 17, 0.03) 41%, + rgba(25, 12, 9, 0.01) 44%, + transparent 46%), + radial-gradient(ellipse at 50% 75%, + rgba(140, 70, 55, 0.65) 0%, + rgba(128, 64, 50, 0.55) 8%, + rgba(116, 58, 45, 0.46) 14%, + rgba(104, 52, 40, 0.37) 19%, + rgba(92, 46, 36, 0.29) 24%, + rgba(80, 40, 31, 0.21) 28%, + rgba(68, 34, 26, 0.15) 32%, + rgba(56, 28, 22, 0.1) 35%, + rgba(44, 22, 17, 0.05) 38%, + rgba(32, 16, 12, 0.02) 41%, + rgba(16, 8, 6, 0.008) 44%, + transparent 44%), + radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.85) 70%, rgba(0, 0, 0, 0.92) 100%); + transform: translateY(-1px); + box-shadow: + inset 0 0 10px rgba(180, 90, 70, 0.2), + 0 3px 8px rgba(0, 0, 0, 0.6); +} + +.filter-tag-remove { + font-size: 1.3rem; + line-height: 1; + color: rgba(229, 229, 229, 0.5); + transition: color 0.2s ease; + font-weight: bold; +} + +.filter-tag:hover .filter-tag-remove { + color: var(--primary-color); +} + +.gear-controls input { + flex: 1; + min-width: 200px; + width: 100%; +} + +.gear-controls select { + min-width: 150px; +} + +.input-wrapper { + filter: url(#rough); + display: inline-block; +} + +.input-wrapper:has(input) { + flex: 1; + min-width: 200px; +} + +.gear-controls input:focus, +.gear-controls select:focus { + outline: none; + border-color: rgba(229, 229, 229, 0.4); +} + +.gear-list { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.gear-item { + background: rgba(229, 229, 229, 0.03); + border: 1px solid rgba(229, 229, 229, 0.1); + transition: all 0.2s ease; + position: relative; +} + +.gear-item::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border: 1px solid rgba(229, 229, 229, 0.1); + filter: url(#rough); + pointer-events: none; +} + +.gear-item:hover { + background: rgba(229, 229, 229, 0.05); + border-color: rgba(229, 229, 229, 0.2); +} + +.gear-header { + padding: 1rem; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + user-select: none; + filter: none; +} + +.gear-title { + font-family: 'Fira Mono', monospace; + font-size: 1rem; + color: var(--primary-color); + filter: none; +} + +.gear-expand { + color: rgba(229, 229, 229, 0.5); + font-size: 0.8rem; + transition: transform 0.2s ease; +} + +.gear-item.expanded .gear-expand { + transform: rotate(180deg); +} + +.gear-item.expanded .gear-details { + border-top: none; +} + +.gear-details { + display: none; + padding: 0 1rem 1rem 1rem; + border-top: 1px solid rgba(229, 229, 229, 0.1); +} + +.gear-meta { + display: flex; + gap: 1rem; + margin-bottom: 1rem; + flex-wrap: wrap; +} + +.gear-category, +.gear-types, +.gear-tech { + font-family: 'Fira Mono', monospace; + font-size: 0.8rem; + padding: 0.25rem 0.5rem; + background: rgba(229, 229, 229, 0.1); + border: 1px solid rgba(229, 229, 229, 0.2); + color: rgba(229, 229, 229, 0.7); +} + +.gear-description { + margin-bottom: 1rem; + line-height: 1.6; + color: rgba(229, 229, 229, 0.8); + filter: none; +} + +.gear-settings { + margin-bottom: 1rem; + font-size: 0.9rem; + color: rgba(229, 229, 229, 0.7); +} + +.gear-settings strong { + color: var(--primary-color); +} + +.settings-section { + margin-left: 1rem; + margin-top: 0.25rem; +} + +.settings-section em { + color: rgba(229, 229, 229, 0.8); + font-style: normal; + font-weight: 500; +} + +.gear-links { + display: flex; + gap: 1rem; +} + +.gear-links a { + font-family: 'Fira Mono', monospace; + font-size: 0.85rem; + color: var(--secondary-color); + text-decoration: none; + border-bottom: 1px solid rgba(229, 229, 229, 0.3); + transition: all 0.2s ease; +} + +.gear-links a:hover { + color: var(--primary-color); + border-bottom-color: var(--primary-color); +} + +#no-results { + text-align: center; + padding: 3rem 1rem; + color: rgba(229, 229, 229, 0.5); +} + +@media (max-width: 768px) { + .gear-controls { + flex-direction: column; + } + + .gear-controls input, + .gear-controls select { + width: 100%; + } + + .gear-meta { + flex-direction: column; + gap: 0.5rem; + } +} From 60ab5aaded43a4d4e6396c69e620e9992db76510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Sun, 15 Feb 2026 15:12:37 +0200 Subject: [PATCH 08/46] Link gear page from about and exclude from search engines Add navigation to gear page and configure SEO: - Link "analog filth" text to /gear/ page (same tab) - Create link render hook to only open external links in new tab - Add /gear/ to robots.txt Disallow list Files modified: - website/content/about/_index.md - add gear link - website/layouts/about/section.html - remove target=_blank JavaScript - website/static/robots.txt - add /gear/ to Disallow Files added: - website/layouts/_default/_markup/render-link.html - link render hook --- website/content/about/_index.md | 2 +- website/layouts/_default/_markup/render-link.html | 7 +++++++ website/layouts/about/section.html | 10 +++++++--- website/static/robots.txt | 1 + 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 website/layouts/_default/_markup/render-link.html diff --git a/website/content/about/_index.md b/website/content/about/_index.md index 10e532c..96d5a9b 100644 --- a/website/content/about/_index.md +++ b/website/content/about/_index.md @@ -10,7 +10,7 @@ Obscvrat is a harsh noise and experimental sound project rooted in putrid underg
-Emerging from a background in noise culture, and black metal aesthetics, Obscvrat rejects polish in favor of abrasion. Recordings are built from crumbling textures, infected field recordings, broken circuits, and analog filthβ€” +Emerging from a background in noise culture, and black metal aesthetics, Obscvrat rejects polish in favor of abrasion. Recordings are built from crumbling textures, infected field recordings, broken circuits, and [analog filth](/gear/)β€” Each set functions as a **controlled collapse**: from distorted signal chains, unstable feedback systems, field recordings, and fractured rhythmsβ€”designed to a ritual of corrosion, pestilence, and repetition. diff --git a/website/layouts/_default/_markup/render-link.html b/website/layouts/_default/_markup/render-link.html new file mode 100644 index 0000000..5924a20 --- /dev/null +++ b/website/layouts/_default/_markup/render-link.html @@ -0,0 +1,7 @@ +{{- $u := urls.Parse .Destination -}} +{{- $isExternal := or (strings.HasPrefix .Destination "http://") (strings.HasPrefix .Destination "https://") -}} + + {{- .Text | safeHTML -}} + diff --git a/website/layouts/about/section.html b/website/layouts/about/section.html index 5b84150..4319cee 100644 --- a/website/layouts/about/section.html +++ b/website/layouts/about/section.html @@ -531,10 +531,14 @@

{{ .Title }}

From 1426900be19d1d7f53611b84c2e697c5aeb3c340 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 16 Feb 2026 19:20:14 +0200 Subject: [PATCH 15/46] Add three new pedals to gear inventory Add the following pedals: - Meris Ottobit Jr. (Bitcrusher/Sequencer, Hybrid) - Meris Hydra (Modulation/Chorus/Flanger/Phaser, Hybrid) - BOSS HM-2 Heavy Metal (Distortion/Fuzz, Analog) Files added: - website/data/gear/meris-ottobit-jr.yaml - website/data/gear/meris-hydra.yaml - website/data/gear/boss-hm-2-heavy-metal.yaml --- website/data/gear/boss-hm-2-heavy-metal.yaml | 14 ++++++++++++++ website/data/gear/meris-hydra.yaml | 18 ++++++++++++++++++ website/data/gear/meris-ottobit-jr.yaml | 16 ++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 website/data/gear/boss-hm-2-heavy-metal.yaml create mode 100644 website/data/gear/meris-hydra.yaml create mode 100644 website/data/gear/meris-ottobit-jr.yaml diff --git a/website/data/gear/boss-hm-2-heavy-metal.yaml b/website/data/gear/boss-hm-2-heavy-metal.yaml new file mode 100644 index 0000000..8ae646c --- /dev/null +++ b/website/data/gear/boss-hm-2-heavy-metal.yaml @@ -0,0 +1,14 @@ +name: "HM-2 Heavy Metal" +manufacturer: "BOSS" +category: "pedal" +technology: "Analog" +types: + - "Distortion" + - "Fuzz" +url: "https://www.boss.info/us/products/hm-2/" +description: "Legendary chainsaw distortion pedal with aggressive high-gain tone and powerful EQ" +controls: + - "Level" + - "Low" + - "High" + - "Distortion" diff --git a/website/data/gear/meris-hydra.yaml b/website/data/gear/meris-hydra.yaml new file mode 100644 index 0000000..cbbdb6d --- /dev/null +++ b/website/data/gear/meris-hydra.yaml @@ -0,0 +1,18 @@ +name: Hydra +manufacturer: Meris +category: pedal +technology: hybrid +types: + - Modulation + - Chorus + - Flanger + - Phaser +url: https://www.meris.us/product/hydra/ +description: Three-voice modulation pedal with chorus, flanger, phaser, and vibrato modes +controls: + - Key + - Micro Tune + - Mix + - Pitch 1 + - Pitch 2 + - Pitch 3 diff --git a/website/data/gear/meris-ottobit-jr.yaml b/website/data/gear/meris-ottobit-jr.yaml new file mode 100644 index 0000000..8e7d30e --- /dev/null +++ b/website/data/gear/meris-ottobit-jr.yaml @@ -0,0 +1,16 @@ +name: Ottobit Jr. +manufacturer: Meris +category: pedal +technology: hybrid +types: + - Bitcrusher + - Sequencer +url: https://www.meris.us/product/ottobit-jr/ +description: Bit crusher and sequencer pedal with stutter, filter, and arpeggiator effects +controls: + - Sample Rate + - Filter + - Bits + - Stutter + - Sequencer + - Sequencer Mult From caa814a5031e457eb4da6e6c5819d0ef80ff6839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 16 Feb 2026 19:20:31 +0200 Subject: [PATCH 16/46] Replace toggle button text with icon indicators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace "Expand All" / "Collapse All" text with visual icons: - Show both β–² (collapse) and β–Ό (expand) icons - Highlight active icon (opacity 1.0) - Dim inactive icon (opacity 0.3) - Icons indicate current action (what will happen on click) Files modified: - website/layouts/gear/list.html - add icon spans, update toggle logic --- website/layouts/gear/list.html | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/website/layouts/gear/list.html b/website/layouts/gear/list.html index dfb4161..61b3e52 100644 --- a/website/layouts/gear/list.html +++ b/website/layouts/gear/list.html @@ -43,7 +43,10 @@

Gear

- +
@@ -168,7 +171,17 @@

Gear

} }); - this.textContent = allExpanded ? 'Expand All' : 'Collapse All'; + // Toggle icon highlighting + const expandIcon = this.querySelector('.expand-icon'); + const collapseIcon = this.querySelector('.collapse-icon'); + + if (allExpanded) { + expandIcon.classList.add('active'); + collapseIcon.classList.remove('active'); + } else { + expandIcon.classList.remove('active'); + collapseIcon.classList.add('active'); + } }); } }); From 870aef66a0892b9547571b51020b854b4068ec83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 16 Feb 2026 19:20:45 +0200 Subject: [PATCH 17/46] Add icon styling for toggle button Style toggle button icons with active/inactive states: - Flexbox layout for centered icon display - Active icon: full opacity (1.0) - Inactive icon: dimmed (0.3 opacity) - Smooth opacity transitions - Fix duplicate border-color declaration Files modified: - website/static/css/main.css - add icon styles and flexbox layout --- website/static/css/main.css | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/website/static/css/main.css b/website/static/css/main.css index 4c42134..f55a270 100644 --- a/website/static/css/main.css +++ b/website/static/css/main.css @@ -1064,14 +1064,27 @@ cursor: pointer; transition: all 0.2s ease; width: 100%; + display: flex; + gap: 0.5rem; + justify-content: center; + align-items: center; +} + +.toggle-all-btn .expand-icon, +.toggle-all-btn .collapse-icon { + opacity: 0.3; + transition: opacity 0.2s ease; +} + +.toggle-all-btn .expand-icon.active, +.toggle-all-btn .collapse-icon.active { + opacity: 1; } .toggle-all-btn:hover { background: rgba(229, 229, 229, 0.1); border-color: rgba(229, 229, 229, 0.4); } - border-color: rgba(229, 229, 229, 0.4); -} .input-wrapper { filter: url(#rough); From ddbc9e3a4b4ec2b6ce6d7743ad046fcf08723067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 16 Feb 2026 19:20:57 +0200 Subject: [PATCH 18/46] Update add-gear prompt to require image examination Improve gear addition workflow to prevent incorrect controls: - Require web search for specifications - Examine product images to identify physical controls - Read control labels directly from photos - Never guess controls from similar products - Ask user if images aren't clear - Fix field name from 'type' to 'technology' in example This prevents adding incorrect control information by requiring visual verification of the actual hardware. Files modified: - .kiro/prompts/add-gear.md - add image examination requirements --- .kiro/prompts/add-gear.md | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/.kiro/prompts/add-gear.md b/.kiro/prompts/add-gear.md index fa389b9..4fcd807 100644 --- a/.kiro/prompts/add-gear.md +++ b/.kiro/prompts/add-gear.md @@ -18,16 +18,17 @@ Quickly add musical gear to your inventory using AI-powered web search and auto- ## What It Does -1. Searches the web for gear specifications -2. Extracts: +1. **Searches the web** for gear specifications and product images +2. **Examines product photos** to identify physical controls on the device +3. Extracts: - Category (Pedal/Synth) - Types (Distortion, Overdrive, Delay, etc.) - Technology (Analog/Digital/Hybrid) - - Controls/Settings + - Controls/Settings (from product images and specs) - Description - Manufacturer and product URLs -3. Creates YAML file in `website/data/gear/` -4. Confirms with you before saving +4. Creates YAML file in `website/data/gear/` +5. Confirms with you before saving ## Manual Alternative @@ -42,16 +43,20 @@ You are helping add musical gear to the Obscvrat gear inventory. When the user provides manufacturer and model name: -1. Search the web for specifications -2. Extract the following information: +1. **Search the web** for specifications and product information +2. **Find and examine product images** to identify the physical controls on the device + - Look for official product photos showing the control panel + - Read control labels directly from the images + - Verify control names match what's visible on the actual hardware +3. Extract the following information: - **Category**: Pedal or Synth - **Types**: Array of types (e.g., ["Distortion", "Overdrive"]) - **Technology**: Analog, Digital, or Hybrid - - **Settings**: Array of control names (e.g., ["Volume", "Tone", "Gain"]) + - **Controls**: Array of control names AS LABELED ON THE DEVICE (e.g., ["Volume", "Tone", "Gain"]) - **Description**: Brief description (1-2 sentences) - **URL**: Official manufacturer product page (prefer manufacturer over retailers) -3. Create YAML file in `website/data/gear/` with format: +4. Create YAML file in `website/data/gear/` with format: ```yaml name: "BD-2 Blues Driver" manufacturer: "BOSS" @@ -60,7 +65,7 @@ types: - "Overdrive" - "Distortion" technology: "Analog" -settings: +controls: - "Level" - "Tone" - "Gain" @@ -68,8 +73,10 @@ description: "Classic overdrive pedal with warm, tube-like tone" url: "https://www.boss.info/us/products/bd-2/" ``` -4. **IMPORTANT: Show the user what you found and ask "Is this correct? (y/n)"** -5. Only save the file after user confirms -6. Save the file with slug: `manufacturer-model.yaml` +5. **IMPORTANT: Show the user what you found and ask "Is this correct? (y/n)"** +6. Only save the file after user confirms +7. Save the file with slug: `manufacturer-model.yaml` -Be thorough in your web search. If you can't find specific information, ask the user. +**CRITICAL: Always examine product images to verify control names. Do not guess or infer controls from similar products.** + +If you cannot find clear images showing the controls, ask the user to provide them. From a2a7302bf646425fd542bf3138cad9f8c68be2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Tue, 17 Feb 2026 16:29:36 +0200 Subject: [PATCH 19/46] Add six new items to gear inventory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the following gear: - MetsÀÀn Noise Nihil (Analog Delay/Feedback) - Rucci Electronics Bent Time Drive (Analog Delay/Distortion/Circuit Bending) - Sunmachine Effects Colossus (Analog Distortion/Fuzz) - JMT Synth DNVO-1 (Analog Drone Synth) - TH/FX Noisedevices Rita's Cracked Pointe Shoe (Analog Noise Generator/Distortion) - TC Electronic Flashback II X4 (Digital Delay) Files added: - website/data/gear/metsaan-noise-nihil.yaml - website/data/gear/rucci-electronics-bent-time-drive.yaml - website/data/gear/sunmachine-effects-colossus.yaml - website/data/gear/jmt-synth-dnvo-1.yaml - website/data/gear/thfx-noisedevices-ritas-cracked-pointe-shoe.yaml - website/data/gear/tc-electronic-flashback-ii-x4.yaml --- website/data/gear/jmt-synth-dnvo-1.yaml | 31 +++++++++++++++++++ .../{meris-hydra.yaml => meris-hedra.yaml} | 0 website/data/gear/metsaan-noise-nihil.yaml | 12 +++++++ .../rucci-electronics-bent-time-drive.yaml | 18 +++++++++++ .../gear/sunmachine-effects-colossus.yaml | 24 ++++++++++++++ .../gear/tc-electronic-flashback-ii-x4.yaml | 14 +++++++++ ...oisedevices-ritas-cracked-pointe-shoe.yaml | 10 ++++++ 7 files changed, 109 insertions(+) create mode 100644 website/data/gear/jmt-synth-dnvo-1.yaml rename website/data/gear/{meris-hydra.yaml => meris-hedra.yaml} (100%) create mode 100644 website/data/gear/metsaan-noise-nihil.yaml create mode 100644 website/data/gear/rucci-electronics-bent-time-drive.yaml create mode 100644 website/data/gear/sunmachine-effects-colossus.yaml create mode 100644 website/data/gear/tc-electronic-flashback-ii-x4.yaml create mode 100644 website/data/gear/thfx-noisedevices-ritas-cracked-pointe-shoe.yaml diff --git a/website/data/gear/jmt-synth-dnvo-1.yaml b/website/data/gear/jmt-synth-dnvo-1.yaml new file mode 100644 index 0000000..b22d335 --- /dev/null +++ b/website/data/gear/jmt-synth-dnvo-1.yaml @@ -0,0 +1,31 @@ +name: "DNVO-1" +manufacturer: "JMT Synth" +category: Synth +technology: Analog +types: + - "Drone Synth" + - "Noise Generator" +url: "https://www.jmtsynth.com/dnvo-1" +description: "Analog drone synthesizer with three sub-oscillators, filter, envelope, and noise generator" +controls: + - "Volume" + - "CV In" + - "Mode Switch (3-position)" + - "Filter" + - "Wave On Switch (2-position)" + - "Frequency" + - "Wave Level" + - "Attack" + - "Release" + - "DCSV Center Switch (2-position)" + - "Trig.1" + - "Trig.2" + - "Trig.3" + - "Sub1 Frequency Switch (2-position)" + - "Sub1 Frequency" + - "Sub2 Frequency Switch (2-position)" + - "Sub2 Frequency" + - "Sub3 Frequency Switch (2-position)" + - "Sub3 Frequency" + - "Sub4 Frequency Switch (2-position)" + - "Noise" diff --git a/website/data/gear/meris-hydra.yaml b/website/data/gear/meris-hedra.yaml similarity index 100% rename from website/data/gear/meris-hydra.yaml rename to website/data/gear/meris-hedra.yaml diff --git a/website/data/gear/metsaan-noise-nihil.yaml b/website/data/gear/metsaan-noise-nihil.yaml new file mode 100644 index 0000000..cac9700 --- /dev/null +++ b/website/data/gear/metsaan-noise-nihil.yaml @@ -0,0 +1,12 @@ +name: "Noise Nihil" +manufacturer: "MetsÀÀn" +category: Pedal +technology: Analog +types: + - "Delay" + - "Feedback" +url: "https://metsn.tumblr.com/post/796406320075063296/noise-nihil-massive-distortion-by-delay-ic-abuse" +description: "Massive distortion by PT2399 delay IC abuse. Destroyer of signals with corrupted feedback, massive amplification, and zero attenuation" +controls: + - "Feedback" + - "Time" diff --git a/website/data/gear/rucci-electronics-bent-time-drive.yaml b/website/data/gear/rucci-electronics-bent-time-drive.yaml new file mode 100644 index 0000000..ce3b44d --- /dev/null +++ b/website/data/gear/rucci-electronics-bent-time-drive.yaml @@ -0,0 +1,18 @@ +name: "Bent Time Drive" +manufacturer: "Rucci Electronics" +category: Synth +technology: Analog +types: + - "Delay" + - "Distortion" + - "Circuit Bending" +url: "https://handmadeelectronicinstruments.com/product/bent-time-drive/" +description: "Circuit-bent delay and drive pedal with momentary and switched bend points for glitchy, unpredictable sounds" +controls: + - "Drive" + - "Time" + - "Feedback" + - "Filter" + - "Volume" + - "3 Bent Points Switches (2 position)" + - "3 Bent Points Buttons" diff --git a/website/data/gear/sunmachine-effects-colossus.yaml b/website/data/gear/sunmachine-effects-colossus.yaml new file mode 100644 index 0000000..e816ea7 --- /dev/null +++ b/website/data/gear/sunmachine-effects-colossus.yaml @@ -0,0 +1,24 @@ +name: "Colossus" +manufacturer: "Sunmachine Effects" +category: Pedal +technology: Analog +types: + - "Distortion" + - "Fuzz" +url: "https://sunmachine-effects.blogspot.com/p/colossus.html" +description: "Multi-mode distortion and fuzz device with three circuits: Power Amp Distortion, Doom Drone Device, and Fuzz" +controls: + - "Tone" + - "Mode Switch (3-position)" + - "Mosfet Switch (3-position)" + - "Blend" + - "Filter" + - "Low Gain Switch (2-position)" + - "Color Switch (3-position)" + - "Low Pass" + - "Titan Gain" + - "Titan Volume" + - "Sunn Model T Volume" + - "Sunn Model T Gain" + - "Fuzz Volume" + - "Fuzz Dist" diff --git a/website/data/gear/tc-electronic-flashback-ii-x4.yaml b/website/data/gear/tc-electronic-flashback-ii-x4.yaml new file mode 100644 index 0000000..d53f22a --- /dev/null +++ b/website/data/gear/tc-electronic-flashback-ii-x4.yaml @@ -0,0 +1,14 @@ +name: "Flashback II X4" +manufacturer: "TC Electronic" +category: Pedal +technology: Digital +types: + - "Delay" +url: "https://www.tcelectronic.com/de/product.html?modelCode=0709-AIP" +description: "Multi-mode digital delay pedal with MASH footswitch technology and four presets" +controls: + - "Delay Type" + - "Subdiv" + - "Delay" + - "Feedback" + - "Level" diff --git a/website/data/gear/thfx-noisedevices-ritas-cracked-pointe-shoe.yaml b/website/data/gear/thfx-noisedevices-ritas-cracked-pointe-shoe.yaml new file mode 100644 index 0000000..07a1373 --- /dev/null +++ b/website/data/gear/thfx-noisedevices-ritas-cracked-pointe-shoe.yaml @@ -0,0 +1,10 @@ +name: "Rita's Cracked Pointe Shoe" +manufacturer: "TH/FX Noisedevices" +category: Pedal +technology: Analog +types: + - "Noise Generator" +url: "https://thfxnoisedevices.com/ritas-cracked-pointe-shoe-2/" +description: "Minimal noise generator and distortion pedal with single intensity control" +controls: + - "Intensity" From 7a39be54d29c5c4717560c94fdc9e6d64d0422bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Tue, 17 Feb 2026 16:30:01 +0200 Subject: [PATCH 20/46] Standardize gear data field names and capitalization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Standardize all gear YAML files: - Rename 'settings' field to 'controls' (11 files) - Capitalize category values: 'pedal' β†’ 'Pedal', 'synth' β†’ 'Synth' - Capitalize technology values: 'analog' β†’ 'Analog', 'digital' β†’ 'Digital', 'hybrid' β†’ 'Hybrid' - Remove quotes for consistency This ensures consistent field naming and display across all gear items. Files modified: - website/data/gear/*.yaml (all gear files) --- website/data/gear/boss-bd-2-blues-driver.yaml | 6 +++--- website/data/gear/boss-hm-2-heavy-metal.yaml | 4 ++-- website/data/gear/death-by-audio-space-bender.yaml | 6 +++--- .../gear/earthquaker-devices-data-corrupter.yaml | 6 +++--- .../data/gear/earthquaker-devices-hizumitas.yaml | 6 +++--- website/data/gear/electro-harmonix-freeze.yaml | 6 +++--- .../gear/intensive-care-audio-vena-cava-filter.yaml | 6 +++--- website/data/gear/masf-pedals-wata-fuzz.yaml | 6 +++--- website/data/gear/meris-hedra.yaml | 13 +++++-------- website/data/gear/meris-ottobit-jr.yaml | 4 ++-- website/data/gear/walovoid-filth-grinder-ii.yaml | 6 +++--- website/data/gear/walovoid-the-distroyer.yaml | 6 +++--- website/data/gear/warm-audio-ringerbringer.yaml | 6 +++--- .../data/gear/zvex-effects-super-duper-2-in-1.yaml | 6 +++--- 14 files changed, 42 insertions(+), 45 deletions(-) diff --git a/website/data/gear/boss-bd-2-blues-driver.yaml b/website/data/gear/boss-bd-2-blues-driver.yaml index 86c8399..034d69b 100644 --- a/website/data/gear/boss-bd-2-blues-driver.yaml +++ b/website/data/gear/boss-bd-2-blues-driver.yaml @@ -1,10 +1,10 @@ name: "BD-2 Blues Driver" manufacturer: "BOSS" -category: "Pedal" +category: Pedal types: - "Overdrive" -technology: "Analog" -settings: +technology: Analog +controls: - "Level" - "Tone" - "Gain" diff --git a/website/data/gear/boss-hm-2-heavy-metal.yaml b/website/data/gear/boss-hm-2-heavy-metal.yaml index 8ae646c..f1b48d4 100644 --- a/website/data/gear/boss-hm-2-heavy-metal.yaml +++ b/website/data/gear/boss-hm-2-heavy-metal.yaml @@ -1,7 +1,7 @@ name: "HM-2 Heavy Metal" manufacturer: "BOSS" -category: "pedal" -technology: "Analog" +category: Pedal +technology: Analog types: - "Distortion" - "Fuzz" diff --git a/website/data/gear/death-by-audio-space-bender.yaml b/website/data/gear/death-by-audio-space-bender.yaml index dde6e03..ebe2b7d 100644 --- a/website/data/gear/death-by-audio-space-bender.yaml +++ b/website/data/gear/death-by-audio-space-bender.yaml @@ -1,9 +1,9 @@ name: "Space Bender" manufacturer: "Death By Audio" -category: "Pedal" +category: Pedal types: ["Modulation", "Chorus", "Vibrato"] -technology: "Analog" -settings: +technology: Analog +controls: - "Speed" - "Depth" - "Delay Time" diff --git a/website/data/gear/earthquaker-devices-data-corrupter.yaml b/website/data/gear/earthquaker-devices-data-corrupter.yaml index 0bbcea9..cef3c75 100644 --- a/website/data/gear/earthquaker-devices-data-corrupter.yaml +++ b/website/data/gear/earthquaker-devices-data-corrupter.yaml @@ -1,11 +1,11 @@ name: "Data Corrupter" manufacturer: "EarthQuaker Devices" -category: "Pedal" +category: Pedal types: - "Harmonizer" - "PLL" -technology: "Analog" -settings: +technology: Analog +controls: Voice Mixer: - "Square" - "Subharmonic" diff --git a/website/data/gear/earthquaker-devices-hizumitas.yaml b/website/data/gear/earthquaker-devices-hizumitas.yaml index 6b32220..e41b00a 100644 --- a/website/data/gear/earthquaker-devices-hizumitas.yaml +++ b/website/data/gear/earthquaker-devices-hizumitas.yaml @@ -1,9 +1,9 @@ name: "Hizumitas" manufacturer: "EarthQuaker Devices" -category: "Pedal" +category: Pedal types: ["Fuzz", "Distortion"] -technology: "Analog" -settings: +technology: Analog +controls: - "Sustain" - "Tone" - "Level" diff --git a/website/data/gear/electro-harmonix-freeze.yaml b/website/data/gear/electro-harmonix-freeze.yaml index 6612e5f..cbbaf50 100644 --- a/website/data/gear/electro-harmonix-freeze.yaml +++ b/website/data/gear/electro-harmonix-freeze.yaml @@ -1,9 +1,9 @@ name: "Freeze" manufacturer: "Electro-Harmonix" -category: "Pedal" +category: Pedal types: ["Sound Sustainer", "Effect"] -technology: "Digital" -settings: +technology: Digital +controls: - "Mode Switch (3-position)" - "Level" description: "Infinite sustain pedal that captures and holds a note or chord indefinitely. Creates drone-like textures and ambient soundscapes with three freeze modes for different playing styles." diff --git a/website/data/gear/intensive-care-audio-vena-cava-filter.yaml b/website/data/gear/intensive-care-audio-vena-cava-filter.yaml index 3a72340..bb6f7a4 100644 --- a/website/data/gear/intensive-care-audio-vena-cava-filter.yaml +++ b/website/data/gear/intensive-care-audio-vena-cava-filter.yaml @@ -1,9 +1,9 @@ name: "Vena Cava Filter" manufacturer: "Intensive Care Audio" -category: "Pedal" +category: Pedal types: ["Filter", "Effect"] -technology: "Analog" -settings: +technology: Analog +controls: - "Wave Function Rotary Switch (8-positions)" - "Freq" - "Ring" diff --git a/website/data/gear/masf-pedals-wata-fuzz.yaml b/website/data/gear/masf-pedals-wata-fuzz.yaml index c0081bc..55f4475 100644 --- a/website/data/gear/masf-pedals-wata-fuzz.yaml +++ b/website/data/gear/masf-pedals-wata-fuzz.yaml @@ -1,9 +1,9 @@ name: "wata fUZZ" manufacturer: "M.A.S.F. Pedals" -category: "Pedal" +category: Pedal types: ["Fuzz", "Distortion"] -technology: "Analog" -settings: +technology: Analog +controls: - "Volume" - "Tone" - "Fuzz" diff --git a/website/data/gear/meris-hedra.yaml b/website/data/gear/meris-hedra.yaml index cbbdb6d..7511240 100644 --- a/website/data/gear/meris-hedra.yaml +++ b/website/data/gear/meris-hedra.yaml @@ -1,13 +1,10 @@ -name: Hydra +name: Hedra manufacturer: Meris -category: pedal -technology: hybrid +category: Pedal +technology: Hybrid types: - - Modulation - - Chorus - - Flanger - - Phaser -url: https://www.meris.us/product/hydra/ + - Pitch Shifter +url: https://www.meris.us/product/hedra-pedal/ description: Three-voice modulation pedal with chorus, flanger, phaser, and vibrato modes controls: - Key diff --git a/website/data/gear/meris-ottobit-jr.yaml b/website/data/gear/meris-ottobit-jr.yaml index 8e7d30e..2ecaa87 100644 --- a/website/data/gear/meris-ottobit-jr.yaml +++ b/website/data/gear/meris-ottobit-jr.yaml @@ -1,7 +1,7 @@ name: Ottobit Jr. manufacturer: Meris -category: pedal -technology: hybrid +category: Pedal +technology: Hybrid types: - Bitcrusher - Sequencer diff --git a/website/data/gear/walovoid-filth-grinder-ii.yaml b/website/data/gear/walovoid-filth-grinder-ii.yaml index a59d20e..3ac4487 100644 --- a/website/data/gear/walovoid-filth-grinder-ii.yaml +++ b/website/data/gear/walovoid-filth-grinder-ii.yaml @@ -1,9 +1,9 @@ name: "Filth Grinder II" manufacturer: "Walovoid" -category: "Pedal" +category: Pedal types: ["Fuzz", "Octaver"] -technology: "Analog" -settings: +technology: Analog +controls: - "Mode Switch (2-position)" - "Low" - "High" diff --git a/website/data/gear/walovoid-the-distroyer.yaml b/website/data/gear/walovoid-the-distroyer.yaml index 8b99e02..2ae91d1 100644 --- a/website/data/gear/walovoid-the-distroyer.yaml +++ b/website/data/gear/walovoid-the-distroyer.yaml @@ -1,9 +1,9 @@ name: "The Distroyer" manufacturer: "Walovoid" -category: "Pedal" +category: Pedal types: ["Distortion", "Octataver"] -technology: "Analog" -settings: +technology: Analog +controls: - "Depth Rotary Switch (4-position)" - "Gain" - "High" diff --git a/website/data/gear/warm-audio-ringerbringer.yaml b/website/data/gear/warm-audio-ringerbringer.yaml index dd4ad9f..6c143a5 100644 --- a/website/data/gear/warm-audio-ringerbringer.yaml +++ b/website/data/gear/warm-audio-ringerbringer.yaml @@ -1,10 +1,10 @@ name: "RingerBringer" manufacturer: "Warm Audio" -category: "Pedal" +category: Pedal types: - "Ring Modulator" -technology: "Analog" -settings: +technology: Analog +controls: - "Amount" - "Rate" - "Drive" diff --git a/website/data/gear/zvex-effects-super-duper-2-in-1.yaml b/website/data/gear/zvex-effects-super-duper-2-in-1.yaml index c6a32fc..904ebe4 100644 --- a/website/data/gear/zvex-effects-super-duper-2-in-1.yaml +++ b/website/data/gear/zvex-effects-super-duper-2-in-1.yaml @@ -1,9 +1,9 @@ name: "Super Duper 2-in-1" manufacturer: "ZVEX Effects" -category: "Pedal" +category: Pedal types: ["Boost", "Overdrive"] -technology: "Analog" -settings: +technology: Analog +controls: Channel 1: - "Volume" - "Crackle" From 85a63b87efc98f532e2304a9720d382103bd5578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Tue, 17 Feb 2026 16:30:18 +0200 Subject: [PATCH 21/46] Update gear template for controls field and separate type badges Template improvements: - Change from 'settings' to 'controls' field - Display each type in separate badge (not comma-delimited) - Simplify template logic This allows each gear type (Delay, Distortion, etc.) to be displayed as individual badges instead of combined in one element. Files modified: - website/layouts/gear/list.html --- website/layouts/gear/list.html | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/website/layouts/gear/list.html b/website/layouts/gear/list.html index 61b3e52..5c434ee 100644 --- a/website/layouts/gear/list.html +++ b/website/layouts/gear/list.html @@ -73,7 +73,9 @@

Gear

{{ $gear.category }} {{ if $gear.types }} - {{ delimit $gear.types ", " }} + {{ range $gear.types }} + {{ . }} + {{ end }} {{ end }} {{ $gear.technology }}
@@ -82,19 +84,19 @@

Gear

{{ $gear.description }}

{{ end }} - {{ if $gear.settings }} + {{ if $gear.controls }}
Controls: - {{ if reflect.IsMap $gear.settings }} - {{/* Nested settings with sections */}} - {{ range $section, $controls := $gear.settings }} + {{ if reflect.IsMap $gear.controls }} + {{/* Nested controls with sections */}} + {{ range $section, $controls := $gear.controls }}
{{ $section }}: {{ delimit $controls ", " }}
{{ end }} {{ else }} - {{/* Flat list of settings */}} - {{ delimit $gear.settings ", " }} + {{/* Flat list of controls */}} + {{ delimit $gear.controls ", " }} {{ end }}
{{ end }} From 8fee37fb628868e3c0cb36c9838036feef70502a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Tue, 17 Feb 2026 16:30:35 +0200 Subject: [PATCH 22/46] Add color differentiation to gear metadata badges Style improvements for gear badges: - Category (Pedal/Synth): Light gray background - Types (Delay, Distortion, etc.): Warm rust/brown tint - Technology (Analog/Digital/Hybrid): Cool blue tint - Remove duplicate flex-direction on mobile Each badge type now has subtle color variation for better visual distinction while maintaining the dark minimal aesthetic. Files modified: - website/static/css/main.css --- website/static/css/main.css | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/website/static/css/main.css b/website/static/css/main.css index f55a270..e882439 100644 --- a/website/static/css/main.css +++ b/website/static/css/main.css @@ -1336,11 +1336,24 @@ font-family: 'Fira Mono', monospace; font-size: 0.8rem; padding: 0.25rem 0.5rem; - background: rgba(229, 229, 229, 0.1); border: 1px solid rgba(229, 229, 229, 0.2); color: rgba(229, 229, 229, 0.7); } +.gear-category { + background: rgba(229, 229, 229, 0.1); +} + +.gear-types { + background: rgba(180, 120, 80, 0.15); + border-color: rgba(180, 120, 80, 0.3); +} + +.gear-tech { + background: rgba(100, 150, 180, 0.15); + border-color: rgba(100, 150, 180, 0.3); +} + .gear-description { margin-bottom: 1rem; line-height: 1.6; @@ -1409,7 +1422,6 @@ } .gear-meta { - flex-direction: column; gap: 0.5rem; } } From 681fad1c54e16ec31164f043d6488b89a2465c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Tue, 17 Feb 2026 16:30:54 +0200 Subject: [PATCH 23/46] Add Hugo build to generate command Update 'make generate' to automatically compile the Hugo site after generating markdown from YAML data. This ensures gear changes are immediately compiled into the site without requiring a separate build step. Files modified: - mk/generate.mk --- mk/generate.mk | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mk/generate.mk b/mk/generate.mk index 043b8c0..1506a61 100644 --- a/mk/generate.mk +++ b/mk/generate.mk @@ -2,13 +2,14 @@ .PHONY: generate bump-version create-release -generate: ## Generate markdown from YAML data (usage: make generate [live|music|media]) +generate: ## Generate markdown from YAML data (usage: make generate [live|music|media|gear]) @target="$(filter-out $@,$(MAKECMDGOALS))"; \ if [ -z "$$target" ]; then \ ./scripts/generate-markdown.sh all; \ else \ ./scripts/generate-markdown.sh "$$target"; \ - fi + fi; \ + cd website && hugo --quiet bump-version: ## Bump version in CHANGELOG.md (usage: make bump-version [TYPE=patch|minor|major]) @python3 scripts/bump_version.py $(or $(TYPE),patch) From a8fbc3226835829f7ca2ace537853982890cdb66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Wed, 18 Feb 2026 22:24:30 +0200 Subject: [PATCH 24/46] Add MCP server for gear management Implement Model Context Protocol server for managing gear inventory: Features: - add_gear: Add new gear with type-safe validation - list_gear: List all gear with optional filters - search_gear: Search by name, manufacturer, types, description - update_gear: Update existing gear fields (new capability) - delete_gear: Remove gear from inventory (new capability) Benefits: - Type-safe data validation (required fields, enums) - Consistent YAML formatting - Duplicate prevention - Atomic file operations - Better error handling Configuration: - .kiro/mcp.json: Workspace MCP server configuration - Auto-loads when Kiro CLI starts - Uses uv for automatic dependency management Files added: - .mcp/gear-server/server.py - MCP server implementation - .mcp/gear-server/pyproject.toml - Python dependencies - .mcp/gear-server/README.md - Usage documentation - .mcp/gear-server/SETUP.md - Setup guide - .mcp/gear-server/test_server.py - Test script - .kiro/mcp.json - MCP configuration Files modified: - AGENTS.md - Add MCP servers section - .kiro/prompts/add-gear.md - Update to use MCP tools --- .kiro/mcp.json | 12 ++ .kiro/prompts/add-gear.md | 50 +++--- .mcp/gear-server/README.md | 104 ++++++++++++ .mcp/gear-server/SETUP.md | 132 +++++++++++++++ .mcp/gear-server/pyproject.toml | 13 ++ .mcp/gear-server/server.py | 289 ++++++++++++++++++++++++++++++++ .mcp/gear-server/test_server.py | 60 +++++++ AGENTS.md | 24 +++ 8 files changed, 664 insertions(+), 20 deletions(-) create mode 100644 .kiro/mcp.json create mode 100644 .mcp/gear-server/README.md create mode 100644 .mcp/gear-server/SETUP.md create mode 100644 .mcp/gear-server/pyproject.toml create mode 100755 .mcp/gear-server/server.py create mode 100755 .mcp/gear-server/test_server.py diff --git a/.kiro/mcp.json b/.kiro/mcp.json new file mode 100644 index 0000000..d5e4069 --- /dev/null +++ b/.kiro/mcp.json @@ -0,0 +1,12 @@ +{ + "mcpServers": { + "gear": { + "command": "uv", + "args": ["run", ".mcp/gear-server/server.py"], + "cwd": "${workspaceFolder}", + "env": { + "GEAR_DATA_DIR": "${workspaceFolder}/website/data/gear" + } + } + } +} diff --git a/.kiro/prompts/add-gear.md b/.kiro/prompts/add-gear.md index 4fcd807..a409e7e 100644 --- a/.kiro/prompts/add-gear.md +++ b/.kiro/prompts/add-gear.md @@ -27,9 +27,19 @@ Quickly add musical gear to your inventory using AI-powered web search and auto- - Controls/Settings (from product images and specs) - Description - Manufacturer and product URLs -4. Creates YAML file in `website/data/gear/` +4. **Uses MCP gear server** to add gear with validation 5. Confirms with you before saving +## MCP Server + +This prompt uses the `gear` MCP server which provides: +- **add_gear** - Add with validation +- **update_gear** - Update existing gear +- **list_gear** - List with filters +- **search_gear** - Search inventory + +See `.mcp/gear-server/README.md` for details. + ## Manual Alternative For manual entry or to manage existing gear: @@ -39,7 +49,7 @@ make gear ## Prompt -You are helping add musical gear to the Obscvrat gear inventory. +You are helping add musical gear to the Obscvrat gear inventory using the MCP gear server. When the user provides manufacturer and model name: @@ -56,27 +66,27 @@ When the user provides manufacturer and model name: - **Description**: Brief description (1-2 sentences) - **URL**: Official manufacturer product page (prefer manufacturer over retailers) -4. Create YAML file in `website/data/gear/` with format: -```yaml -name: "BD-2 Blues Driver" -manufacturer: "BOSS" -category: "Pedal" -types: - - "Overdrive" - - "Distortion" -technology: "Analog" -controls: - - "Level" - - "Tone" - - "Gain" -description: "Classic overdrive pedal with warm, tube-like tone" -url: "https://www.boss.info/us/products/bd-2/" +4. **Use the add_gear MCP tool** to add the gear: +```python +add_gear( + name="BD-2 Blues Driver", + manufacturer="BOSS", + category="Pedal", + technology="Analog", + types=["Overdrive", "Distortion"], + controls=["Level", "Tone", "Gain"], + url="https://www.boss.info/us/products/bd-2/", + description="Classic overdrive pedal with warm, tube-like tone" +) ``` 5. **IMPORTANT: Show the user what you found and ask "Is this correct? (y/n)"** -6. Only save the file after user confirms -7. Save the file with slug: `manufacturer-model.yaml` +6. Only call add_gear after user confirms +7. If you cannot find clear images showing the controls, ask the user to provide them **CRITICAL: Always examine product images to verify control names. Do not guess or infer controls from similar products.** -If you cannot find clear images showing the controls, ask the user to provide them. +**If missing optional fields (controls, url, description):** +- You can still add the gear with required fields only +- Inform the user which fields are missing +- They can be added later with update_gear tool diff --git a/.mcp/gear-server/README.md b/.mcp/gear-server/README.md new file mode 100644 index 0000000..3e58db4 --- /dev/null +++ b/.mcp/gear-server/README.md @@ -0,0 +1,104 @@ +# Gear Management MCP Server + +Model Context Protocol (MCP) server for managing the Obscvrat gear inventory. + +## Features + +- **add_gear** - Add new gear with validation +- **list_gear** - List all gear with optional filters +- **search_gear** - Search by name, manufacturer, types, or description +- **update_gear** - Update existing gear fields +- **delete_gear** - Remove gear from inventory + +## Configuration + +Add to `.kiro/mcp.json`: + +```json +{ + "mcpServers": { + "gear": { + "command": "uv", + "args": ["run", ".mcp/gear-server/server.py"], + "cwd": "${workspaceFolder}", + "env": { + "GEAR_DATA_DIR": "${workspaceFolder}/website/data/gear" + } + } + } +} +``` + +## Usage + +The server provides tools that can be invoked by AI agents: + +### Add Gear + +```python +add_gear( + name="BD-2 Blues Driver", + manufacturer="BOSS", + category="Pedal", + technology="Analog", + types=["Overdrive", "Distortion"], + controls=["Level", "Tone", "Gain"], + url="https://www.boss.info/us/products/bd-2/", + description="Classic overdrive pedal with warm, tube-like tone" +) +``` + +### List Gear + +```python +# List all gear +list_gear() + +# Filter by category +list_gear(category="Pedal") + +# Filter by manufacturer +list_gear(manufacturer="BOSS") +``` + +### Search Gear + +```python +search_gear(query="delay") +``` + +### Update Gear + +```python +update_gear( + slug="boss-bd-2-blues-driver", + controls=["Level", "Tone", "Gain", "Mode"] +) +``` + +### Delete Gear + +```python +delete_gear(slug="boss-bd-2-blues-driver") +``` + +## Data Validation + +The server enforces: +- Required fields: name, manufacturer, category, technology +- Category enum: Pedal, Synth +- Technology enum: Analog, Digital, Hybrid +- Consistent YAML formatting +- Duplicate prevention + +## Development + +Install dependencies: +```bash +uv pip install -e .mcp/gear-server +``` + +Test the server: +```bash +uv run .mcp/gear-server/server.py +``` diff --git a/.mcp/gear-server/SETUP.md b/.mcp/gear-server/SETUP.md new file mode 100644 index 0000000..5aca053 --- /dev/null +++ b/.mcp/gear-server/SETUP.md @@ -0,0 +1,132 @@ +# MCP Gear Server Setup + +## What Was Created + +1. **MCP Server** (`.mcp/gear-server/`) + - `server.py` - Main MCP server implementation + - `pyproject.toml` - Python dependencies + - `README.md` - Documentation + - `test_server.py` - Test script + +2. **Configuration** (`.kiro/mcp.json`) + - Workspace-specific MCP server configuration + - Automatically loaded by Kiro CLI + +3. **Documentation Updates** + - `AGENTS.md` - Added MCP servers section + - `.kiro/prompts/add-gear.md` - Updated to use MCP tools + +## Available Tools + +### add_gear +Add new gear with validation. + +**Required:** name, manufacturer, category, technology +**Optional:** types, controls, url, description + +**Example:** +```python +add_gear( + name="BD-2 Blues Driver", + manufacturer="BOSS", + category="Pedal", + technology="Analog", + types=["Overdrive"], + controls=["Level", "Tone", "Gain"], + url="https://...", + description="..." +) +``` + +### list_gear +List all gear with optional filters. + +**Filters:** category, manufacturer, technology + +**Example:** +```python +list_gear(category="Pedal") +``` + +### search_gear +Search by name, manufacturer, types, or description. + +**Example:** +```python +search_gear(query="delay") +``` + +### update_gear +Update existing gear fields. + +**Example:** +```python +update_gear( + slug="boss-bd-2-blues-driver", + controls=["Level", "Tone", "Gain", "Mode"] +) +``` + +### delete_gear +Remove gear from inventory. + +**Example:** +```python +delete_gear(slug="boss-bd-2-blues-driver") +``` + +## How It Works + +1. **Kiro CLI starts the server** when you begin a chat session +2. **Server runs in background**, communicates via stdio +3. **Tools are available** to all agents automatically +4. **No manual management** needed + +## Benefits + +- **Type-safe validation** - Enforces required fields and enums +- **Consistent formatting** - All YAML files formatted identically +- **Duplicate prevention** - Checks if gear already exists +- **Atomic operations** - All-or-nothing writes +- **Better error handling** - Structured error messages +- **Update capability** - Can modify existing gear (new feature!) + +## Next Steps + +The MCP server is ready to use! In your next chat session: + +1. Kiro CLI will automatically start the gear server +2. You can use `@add-gear` prompt which now uses MCP tools +3. Or directly ask me to add/list/search/update gear + +**Example usage:** +``` +You: Add BOSS HM-2 Heavy Metal +Me: [uses add_gear tool with validated data] + βœ… Added boss-hm-2-heavy-metal.yaml +``` + +## Testing + +To test the server manually: +```bash +cd .mcp/gear-server +uv run server.py +``` + +The server will start and wait for JSON-RPC messages on stdin. + +## Troubleshooting + +If the server doesn't start: +1. Check `.kiro/mcp.json` configuration +2. Ensure `uv` is installed: `brew install uv` +3. Check Kiro CLI logs for errors + +## Future Enhancements + +Potential additions: +- **validate_gear** - Check existing gear files for issues +- **export_gear** - Export inventory to different formats +- **import_gear** - Bulk import from CSV/JSON +- **stats_gear** - Show inventory statistics diff --git a/.mcp/gear-server/pyproject.toml b/.mcp/gear-server/pyproject.toml new file mode 100644 index 0000000..f94150c --- /dev/null +++ b/.mcp/gear-server/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "gear-server" +version = "0.1.0" +description = "MCP server for managing Obscvrat gear inventory" +requires-python = ">=3.9" +dependencies = [ + "mcp>=1.0.0", + "pyyaml>=6.0", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" diff --git a/.mcp/gear-server/server.py b/.mcp/gear-server/server.py new file mode 100755 index 0000000..500fb00 --- /dev/null +++ b/.mcp/gear-server/server.py @@ -0,0 +1,289 @@ +#!/usr/bin/env python3 +"""MCP server for managing Obscvrat gear inventory.""" + +import asyncio +import os +import sys +from pathlib import Path +from typing import Any, Dict, List + +import yaml +from mcp.server import Server +from mcp.types import TextContent, Tool + +# Get gear data directory from environment or default +GEAR_DATA_DIR = Path(os.getenv("GEAR_DATA_DIR", "website/data/gear")) + +server = Server("gear-manager") + + +def slugify(text: str) -> str: + """Convert text to URL-friendly slug.""" + return text.lower().replace(" ", "-").replace("/", "-") + + +def generate_filename(name: str, manufacturer: str) -> str: + """Generate YAML filename from gear name and manufacturer.""" + slug = f"{slugify(manufacturer)}-{slugify(name)}" + return f"{slug}.yaml" + + +def read_gear_file(filepath: Path) -> Dict[str, Any]: + """Read and parse a gear YAML file.""" + with open(filepath) as f: + return yaml.safe_load(f) + + +def write_gear_file(filepath: Path, data: Dict[str, Any]) -> None: + """Write gear data to YAML file.""" + with open(filepath, "w") as f: + yaml.dump(data, f, default_flow_style=False, sort_keys=False, allow_unicode=True) + + +@server.list_tools() +async def list_tools() -> List[Tool]: + """List available gear management tools.""" + return [ + Tool( + name="add_gear", + description="Add new gear to inventory. Required: name, manufacturer, category, technology. Optional: types, controls, url, description.", + inputSchema={ + "type": "object", + "properties": { + "name": {"type": "string", "description": "Gear name (e.g., 'BD-2 Blues Driver')"}, + "manufacturer": {"type": "string", "description": "Manufacturer name (e.g., 'BOSS')"}, + "category": {"type": "string", "enum": ["Pedal", "Synth"], "description": "Gear category"}, + "technology": {"type": "string", "enum": ["Analog", "Digital", "Hybrid"], "description": "Technology type"}, + "types": {"type": "array", "items": {"type": "string"}, "description": "Gear types (e.g., ['Distortion', 'Overdrive'])"}, + "controls": {"type": "array", "items": {"type": "string"}, "description": "Control names as labeled on device"}, + "url": {"type": "string", "description": "Official product URL"}, + "description": {"type": "string", "description": "Brief description (1-2 sentences)"} + }, + "required": ["name", "manufacturer", "category", "technology"] + } + ), + Tool( + name="list_gear", + description="List all gear in inventory with optional filters.", + inputSchema={ + "type": "object", + "properties": { + "category": {"type": "string", "description": "Filter by category (Pedal/Synth)"}, + "manufacturer": {"type": "string", "description": "Filter by manufacturer"}, + "technology": {"type": "string", "description": "Filter by technology (Analog/Digital/Hybrid)"} + } + } + ), + Tool( + name="search_gear", + description="Search gear by name, manufacturer, types, or description.", + inputSchema={ + "type": "object", + "properties": { + "query": {"type": "string", "description": "Search query"} + }, + "required": ["query"] + } + ), + Tool( + name="update_gear", + description="Update existing gear. Provide slug and fields to update.", + inputSchema={ + "type": "object", + "properties": { + "slug": {"type": "string", "description": "Gear filename without .yaml (e.g., 'boss-bd-2-blues-driver')"}, + "types": {"type": "array", "items": {"type": "string"}}, + "controls": {"type": "array", "items": {"type": "string"}}, + "url": {"type": "string"}, + "description": {"type": "string"} + }, + "required": ["slug"] + } + ), + Tool( + name="delete_gear", + description="Delete gear from inventory.", + inputSchema={ + "type": "object", + "properties": { + "slug": {"type": "string", "description": "Gear filename without .yaml"} + }, + "required": ["slug"] + } + ) + ] + + +@server.call_tool() +async def call_tool(name: str, arguments: Dict) -> List[TextContent]: + """Handle tool calls.""" + + if name == "add_gear": + # Generate filename + filename = generate_filename(arguments["name"], arguments["manufacturer"]) + filepath = GEAR_DATA_DIR / filename + + # Check if already exists + if filepath.exists(): + return [TextContent( + type="text", + text=f"❌ Gear already exists: {filename}\nUse update_gear to modify it." + )] + + # Build gear data + gear_data = { + "name": arguments["name"], + "manufacturer": arguments["manufacturer"], + "category": arguments["category"], + "technology": arguments["technology"] + } + + # Add optional fields + if "types" in arguments: + gear_data["types"] = arguments["types"] + if "url" in arguments: + gear_data["url"] = arguments["url"] + if "description" in arguments: + gear_data["description"] = arguments["description"] + if "controls" in arguments: + gear_data["controls"] = arguments["controls"] + + # Write file + write_gear_file(filepath, gear_data) + + missing = [] + if "controls" not in arguments: + missing.append("controls") + if "url" not in arguments: + missing.append("url") + if "description" not in arguments: + missing.append("description") + + result = f"βœ… Added gear: {filename}" + if missing: + result += f"\n⚠️ Missing optional fields: {', '.join(missing)}" + + return [TextContent(type="text", text=result)] + + elif name == "list_gear": + # Read all gear files + gear_files = sorted(GEAR_DATA_DIR.glob("*.yaml")) + gear_list = [] + + for filepath in gear_files: + gear = read_gear_file(filepath) + + # Apply filters + if "category" in arguments and gear.get("category") != arguments["category"]: + continue + if "manufacturer" in arguments and gear.get("manufacturer") != arguments["manufacturer"]: + continue + if "technology" in arguments and gear.get("technology") != arguments["technology"]: + continue + + types_str = ", ".join(gear.get("types", [])) if gear.get("types") else "N/A" + gear_list.append( + f"β€’ {gear['manufacturer']} {gear['name']} ({gear['category']}, {gear['technology']}) - {types_str}" + ) + + if not gear_list: + return [TextContent(type="text", text="No gear found matching filters.")] + + result = f"Found {len(gear_list)} items:\n\n" + "\n".join(gear_list) + return [TextContent(type="text", text=result)] + + elif name == "search_gear": + query = arguments["query"].lower() + gear_files = sorted(GEAR_DATA_DIR.glob("*.yaml")) + matches = [] + + for filepath in gear_files: + gear = read_gear_file(filepath) + + # Search in name, manufacturer, types, description + searchable = [ + gear.get("name", ""), + gear.get("manufacturer", ""), + " ".join(gear.get("types", [])), + gear.get("description", "") + ] + + if any(query in field.lower() for field in searchable): + types_str = ", ".join(gear.get("types", [])) if gear.get("types") else "N/A" + matches.append( + f"β€’ {gear['manufacturer']} {gear['name']} ({types_str})\n {filepath.stem}" + ) + + if not matches: + return [TextContent(type="text", text=f"No gear found matching '{arguments['query']}'.")] + + result = f"Found {len(matches)} matches:\n\n" + "\n\n".join(matches) + return [TextContent(type="text", text=result)] + + elif name == "update_gear": + filepath = GEAR_DATA_DIR / f"{arguments['slug']}.yaml" + + if not filepath.exists(): + return [TextContent( + type="text", + text=f"❌ Gear not found: {arguments['slug']}.yaml" + )] + + # Read existing data + gear_data = read_gear_file(filepath) + + # Update fields + updated_fields = [] + if "types" in arguments: + gear_data["types"] = arguments["types"] + updated_fields.append("types") + if "controls" in arguments: + gear_data["controls"] = arguments["controls"] + updated_fields.append("controls") + if "url" in arguments: + gear_data["url"] = arguments["url"] + updated_fields.append("url") + if "description" in arguments: + gear_data["description"] = arguments["description"] + updated_fields.append("description") + + if not updated_fields: + return [TextContent(type="text", text="⚠️ No fields to update.")] + + # Write updated data + write_gear_file(filepath, gear_data) + + return [TextContent( + type="text", + text=f"βœ… Updated {arguments['slug']}.yaml\nFields updated: {', '.join(updated_fields)}" + )] + + elif name == "delete_gear": + filepath = GEAR_DATA_DIR / f"{arguments['slug']}.yaml" + + if not filepath.exists(): + return [TextContent( + type="text", + text=f"❌ Gear not found: {arguments['slug']}.yaml" + )] + + filepath.unlink() + return [TextContent(type="text", text=f"βœ… Deleted {arguments['slug']}.yaml")] + + return [TextContent(type="text", text=f"Unknown tool: {name}")] + + +async def main(): + """Run the MCP server.""" + from mcp.server.stdio import stdio_server + + async with stdio_server() as (read_stream, write_stream): + await server.run( + read_stream, + write_stream, + server.create_initialization_options() + ) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/.mcp/gear-server/test_server.py b/.mcp/gear-server/test_server.py new file mode 100755 index 0000000..cb80f7f --- /dev/null +++ b/.mcp/gear-server/test_server.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +"""Test the gear MCP server.""" + +import json +import subprocess +import sys + +def test_server(): + """Test that the server starts and responds to list_tools.""" + + # Start the server + proc = subprocess.Popen( + ["uv", "run", ".mcp/gear-server/server.py"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + # Send initialize request + init_request = { + "jsonrpc": "2.0", + "id": 1, + "method": "initialize", + "params": { + "protocolVersion": "2024-11-05", + "capabilities": {}, + "clientInfo": {"name": "test", "version": "1.0.0"} + } + } + + proc.stdin.write(json.dumps(init_request) + "\n") + proc.stdin.flush() + + # Read response + response = proc.stdout.readline() + print("Initialize response:", response) + + # Send list_tools request + tools_request = { + "jsonrpc": "2.0", + "id": 2, + "method": "tools/list" + } + + proc.stdin.write(json.dumps(tools_request) + "\n") + proc.stdin.flush() + + # Read response + response = proc.stdout.readline() + print("Tools list response:", response) + + # Cleanup + proc.terminate() + proc.wait() + + print("\nβœ… Server test passed!") + +if __name__ == "__main__": + test_server() diff --git a/AGENTS.md b/AGENTS.md index f381e70..cfbc894 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,30 @@ kiro-cli settings chat.enableSubagent true This is a one-time global setting that enables subagent functionality across all projects. +## MCP Servers + +The project uses Model Context Protocol (MCP) servers to provide specialized tools for agents. + +### Gear Management Server + +Located in `.mcp/gear-server/`, provides tools for managing the gear inventory: + +- **add_gear** - Add new gear with validation +- **list_gear** - List all gear with optional filters +- **search_gear** - Search by name, manufacturer, types, or description +- **update_gear** - Update existing gear fields +- **delete_gear** - Remove gear from inventory + +**Configuration:** `.kiro/mcp.json` + +**Usage:** Tools are automatically available to agents. See `.mcp/gear-server/README.md` for details. + +**Benefits:** +- Type-safe data validation +- Consistent YAML formatting +- Duplicate prevention +- Atomic file operations + ## Agent Workflow The project uses a specialized five-agent architecture (see ADR-010): From 6fc2e37790e81b533954d6f7cc92a299d9d420af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Thu, 19 Feb 2026 10:51:06 +0200 Subject: [PATCH 25/46] Rename prompt from @add-gear to @gear-add Rename .kiro/prompts/add-gear.md to gear-add.md for better naming consistency with gear-related commands. Updated usage examples in documentation to reflect new prompt name. --- .kiro/prompts/{add-gear.md => gear-add.md} | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) rename .kiro/prompts/{add-gear.md => gear-add.md} (92%) diff --git a/.kiro/prompts/add-gear.md b/.kiro/prompts/gear-add.md similarity index 92% rename from .kiro/prompts/add-gear.md rename to .kiro/prompts/gear-add.md index a409e7e..737b22f 100644 --- a/.kiro/prompts/add-gear.md +++ b/.kiro/prompts/gear-add.md @@ -5,15 +5,15 @@ Quickly add musical gear to your inventory using AI-powered web search and auto- ## Usage ``` -@add-gear MANUFACTURER MODEL +@gear-add MANUFACTURER MODEL ``` ## Examples ``` -@add-gear BOSS BD-2 Blues Driver -@add-gear Electro-Harmonix Big Muff Pi -@add-gear Moog Mother-32 +@gear-add BOSS BD-2 Blues Driver +@gear-add Electro-Harmonix Big Muff Pi +@gear-add Moog Mother-32 ``` ## What It Does @@ -80,6 +80,8 @@ add_gear( ) ``` +**IMPORTANT: Format control names in Title Case (e.g., "Freq", "Depth", "Time" not "FREQ", "DEPTH", "TIME")** + 5. **IMPORTANT: Show the user what you found and ask "Is this correct? (y/n)"** 6. Only call add_gear after user confirms 7. If you cannot find clear images showing the controls, ask the user to provide them From 5f58a9b8bb007ce931b47e103f708d0550bc8ae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Thu, 19 Feb 2026 10:51:30 +0200 Subject: [PATCH 26/46] Remove gallery fields from music pages Remove unused gallery image arrays from Fridlyst Rauhoitettu and World Rat Conspiracy album pages. Gallery functionality not currently implemented in the music page template. --- website/content/music/fridlyst-rauhoitettu.md | 3 --- website/content/music/world-rat-conspiracy.md | 8 -------- 2 files changed, 11 deletions(-) diff --git a/website/content/music/fridlyst-rauhoitettu.md b/website/content/music/fridlyst-rauhoitettu.md index f445969..53712f9 100644 --- a/website/content/music/fridlyst-rauhoitettu.md +++ b/website/content/music/fridlyst-rauhoitettu.md @@ -6,9 +6,6 @@ bandcamp_url: "https://obscvrat.bandcamp.com/album/fridlyst-rauhoitettu" discogs_url: "https://www.discogs.com/release/33857796-Heppakirjat-BRYSKT-Obscvrat-Fridlyst-Rauhoitettu" release_type: "album" cover: "/images/music/fridlyst-rauhoitettu-cover.jpg" -gallery: - - "/images/music/fridlyst-rauhoitettu-inside-1.jpeg" - - "/images/music/fridlyst-rauhoitettu-inside-2.jpeg" label: "Γ„LΓ„ LΓ–I TAPES" label_url: "https://alaloi.bandcamp.com/" catalog_number: "Γ„LΓ„009" diff --git a/website/content/music/world-rat-conspiracy.md b/website/content/music/world-rat-conspiracy.md index 5e9ea71..615668e 100644 --- a/website/content/music/world-rat-conspiracy.md +++ b/website/content/music/world-rat-conspiracy.md @@ -7,14 +7,6 @@ label_url: "https://deepthroatrecords.bandcamp.com/" catalog_number: "DTR150" country: "Italy" cover: "/images/music/world-rat-conspiracy-cover.jpg" -gallery: - - "/images/music/world-rat-conspiracy-inside-1.jpg" - - "/images/music/world-rat-conspiracy-inside-2.jpg" - - "/images/music/world-rat-conspiracy-inside-3.jpg" - - "/images/music/world-rat-conspiracy-inside-4.jpg" - - "/images/music/world-rat-conspiracy-inside-5.jpg" - - "/images/music/world-rat-conspiracy-inside-6.jpg" - - "/images/music/world-rat-conspiracy-inside-7.jpg" bandcamp_album: "2673369501" bandcamp_url: "https://obscvrat.bandcamp.com/album/world-rat-conspiracy" discogs_url: "https://www.discogs.com/release/35199499-Obscvrat-Rotat-Whiterat-World-Rat-Conspiracy" From e1044378150efb32b6ee11b912203b8716302e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Thu, 19 Feb 2026 10:51:42 +0200 Subject: [PATCH 27/46] Add five new pedals to gear inventory Add the following pedals with specifications and controls: - Death By Audio Rooms: Stereo reverb with 6 algorithms - Death By Audio Evil Filter: Multimode filter with fuzz - EarthQuaker Devices Acapulco Gold: Simple distortion pedal - EarthQuaker Devices Plumes: Tube-like overdrive with 3 clipping modes - Fairfield Circuitry Accountant: JFET feedback compressor All controls formatted in Title Case as per project convention. --- .../data/gear/death-by-audio-evil-filter.yaml | 15 +++++++++++++++ website/data/gear/death-by-audio-rooms.yaml | 17 +++++++++++++++++ .../gear/earthquaker-devices-acapulco-gold.yaml | 9 +++++++++ .../data/gear/earthquaker-devices-plumes.yaml | 12 ++++++++++++ .../gear/fairfield-circuitry-accountant.yaml | 11 +++++++++++ 5 files changed, 64 insertions(+) create mode 100644 website/data/gear/death-by-audio-evil-filter.yaml create mode 100644 website/data/gear/death-by-audio-rooms.yaml create mode 100644 website/data/gear/earthquaker-devices-acapulco-gold.yaml create mode 100644 website/data/gear/earthquaker-devices-plumes.yaml create mode 100644 website/data/gear/fairfield-circuitry-accountant.yaml diff --git a/website/data/gear/death-by-audio-evil-filter.yaml b/website/data/gear/death-by-audio-evil-filter.yaml new file mode 100644 index 0000000..3814783 --- /dev/null +++ b/website/data/gear/death-by-audio-evil-filter.yaml @@ -0,0 +1,15 @@ +name: "Evil Filter" +manufacturer: "Death By Audio" +category: Pedal +types: ["Filter", "Fuzz"] +technology: Analog +controls: + - "Filter Frequency" + - "Filter Output" + - "Filter Selector Rotary Switch (3-position)" + - "Resonance Switch (2-position)" + - "Filter Resonance" + - "Fuzz Output" + - "Fuzz Shape Switch (2-position)" +description: "Psycho multimode filter with medical-grade IC chip offering high pass, bandpass, and low pass modes with screaming resonance, plus two fuzz types and CV/expression control." +url: "https://deathbyaudio.com/products/evil-filter" diff --git a/website/data/gear/death-by-audio-rooms.yaml b/website/data/gear/death-by-audio-rooms.yaml new file mode 100644 index 0000000..1f8a13f --- /dev/null +++ b/website/data/gear/death-by-audio-rooms.yaml @@ -0,0 +1,17 @@ +name: "Rooms" +manufacturer: "Death By Audio" +category: Pedal +types: ["Reverb"] +technology: Digital +controls: + - "Freq" + - "Depth" + - "Time" + - "Dry" + - "Fx" + - "Alt" + - "F" + - "D" + - "T" +description: "Stereo multi-function digital reverb with six algorithms (ROOM, DIGIT, PEAK, GATE, WAVE, GONG), studio-quality mixer section, and expression pedal control." +url: "https://deathbyaudio.com/collections/reverb/products/rooms" diff --git a/website/data/gear/earthquaker-devices-acapulco-gold.yaml b/website/data/gear/earthquaker-devices-acapulco-gold.yaml new file mode 100644 index 0000000..eaabdec --- /dev/null +++ b/website/data/gear/earthquaker-devices-acapulco-gold.yaml @@ -0,0 +1,9 @@ +name: "Acapulco Gold" +manufacturer: "EarthQuaker Devices" +category: Pedal +types: ["Distortion"] +technology: Analog +controls: + - "Volume" +description: "Dirt-simple distortion modeled after a cranked vintage Model T amplifier, delivering openness, clarity, and crunch with just a single volume control." +url: "https://www.earthquakerdevices.com/acapulco-gold" diff --git a/website/data/gear/earthquaker-devices-plumes.yaml b/website/data/gear/earthquaker-devices-plumes.yaml new file mode 100644 index 0000000..f85246b --- /dev/null +++ b/website/data/gear/earthquaker-devices-plumes.yaml @@ -0,0 +1,12 @@ +name: "Plumes" +manufacturer: "EarthQuaker Devices" +category: Pedal +types: ["Overdrive"] +technology: Analog +controls: + - "Level" + - "Gain" + - "Tone" + - "Mode Switch (3-Position)" +description: "All-analog tube-like overdrive with 3 clipping voices, loads of headroom, and reimagined tone control for sculpting low end, clear top end, and focused midrange." +url: "https://www.earthquakerdevices.com/plumes" diff --git a/website/data/gear/fairfield-circuitry-accountant.yaml b/website/data/gear/fairfield-circuitry-accountant.yaml new file mode 100644 index 0000000..a680237 --- /dev/null +++ b/website/data/gear/fairfield-circuitry-accountant.yaml @@ -0,0 +1,11 @@ +name: "Accountant" +manufacturer: "Fairfield Circuitry" +category: Pedal +types: ["Compressor"] +technology: Analog +controls: + - "Volume" + - "Pad (3-position)" + - "Ratio (3-position)" +description: "All-analog JFET feedback compressor with simple control interface for threshold, compression ratio, and makeup gain. Works with guitars, bass, vocals, synths, and whole mixes." +url: "https://www.fairfieldcircuitry.com/products/the-accountant" From 170f36f75e7c108e6da8b56a6ac0cc6c6712c5f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 23 Feb 2026 13:51:54 +0200 Subject: [PATCH 28/46] Add four new pedals to gear inventory - TC Electronic Ditto Looper: Minimalist looper with 5min loop time - TC Electronic Hall of Fame Reverb: Digital reverb with 10 types and TonePrint - Fairfield Circuitry Long Life: Parametric EQ with CV inputs - Death By Audio Fuzz War: Extreme fuzz with multi-curve tone control All pedals include controls, technology type, and manufacturer URLs. --- website/data/gear/death-by-audio-fuzz-war.yaml | 11 +++++++++++ .../data/gear/fairfield-circuitry-long-life.yaml | 15 +++++++++++++++ website/data/gear/tc-electronic-ditto-looper.yaml | 9 +++++++++ .../gear/tc-electronic-hall-of-fame-reverb.yaml | 12 ++++++++++++ 4 files changed, 47 insertions(+) create mode 100644 website/data/gear/death-by-audio-fuzz-war.yaml create mode 100644 website/data/gear/fairfield-circuitry-long-life.yaml create mode 100644 website/data/gear/tc-electronic-ditto-looper.yaml create mode 100644 website/data/gear/tc-electronic-hall-of-fame-reverb.yaml diff --git a/website/data/gear/death-by-audio-fuzz-war.yaml b/website/data/gear/death-by-audio-fuzz-war.yaml new file mode 100644 index 0000000..65cca20 --- /dev/null +++ b/website/data/gear/death-by-audio-fuzz-war.yaml @@ -0,0 +1,11 @@ +name: "Fuzz War" +manufacturer: "Death By Audio" +category: Pedal +types: ["Fuzz", "Distortion"] +technology: Analog +controls: + - "Volume" + - "Fuzz" + - "Tone" +description: "Extreme fuzz pedal with massive sustain and output. Features active multi-curve tone control ranging from deep bass sludge to screaming highs, with enough gain to go from boost to full-on fuzz war." +url: "https://deathbyaudio.com/products/fuzz-war" diff --git a/website/data/gear/fairfield-circuitry-long-life.yaml b/website/data/gear/fairfield-circuitry-long-life.yaml new file mode 100644 index 0000000..d33d92a --- /dev/null +++ b/website/data/gear/fairfield-circuitry-long-life.yaml @@ -0,0 +1,15 @@ +name: "Long Life" +manufacturer: "Fairfield Circuitry" +category: Pedal +types: ["EQ", "Filter"] +technology: Analog +controls: + - "Q" + - "Gain" + - "Tilt" + - "Vol" + - "Freq" + - "CV Q" + - "CV F" +description: "Parametric EQ pedal with voltage control inputs. Features adjustable Q, frequency, gain, and tilt controls with CV inputs for dynamic frequency and Q modulation." +url: "https://www.fairfieldcircuitry.com/products/long-life" diff --git a/website/data/gear/tc-electronic-ditto-looper.yaml b/website/data/gear/tc-electronic-ditto-looper.yaml new file mode 100644 index 0000000..636aa9a --- /dev/null +++ b/website/data/gear/tc-electronic-ditto-looper.yaml @@ -0,0 +1,9 @@ +name: "Ditto Looper" +manufacturer: "TC Electronic" +category: Pedal +types: ["Looper"] +technology: Digital +controls: + - "Level" +description: "Simple, compact looper pedal with 5 minutes of loop time and unlimited overdubs. Single knob controls playback level, footswitch handles record/play/stop functions." +url: "https://www.tcelectronic.com/product.html?modelCode=P0CKP" diff --git a/website/data/gear/tc-electronic-hall-of-fame-reverb.yaml b/website/data/gear/tc-electronic-hall-of-fame-reverb.yaml new file mode 100644 index 0000000..a26ecc0 --- /dev/null +++ b/website/data/gear/tc-electronic-hall-of-fame-reverb.yaml @@ -0,0 +1,12 @@ +name: "Hall Of Fame Reverb" +manufacturer: "TC Electronic" +category: Pedal +types: ["Reverb", "Stereo"] +technology: Digital +controls: + - "Decay" + - "Tone" + - "FX Level" + - "Reverb Type" +description: "Versatile digital reverb pedal with 10 reverb types including spring, hall, and plate. Features TonePrint technology for custom reverb settings via smartphone app." +url: "https://www.tcelectronic.com/product.html?modelCode=P0CM5" From c2c7061d49c542a7c2d41e399ec91eb63ca0fd05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 23 Feb 2026 13:52:03 +0200 Subject: [PATCH 29/46] Add Stereo type to stereo-capable reverb pedals Mark Death By Audio Rooms as Stereo type to enable filtering by stereo capability. This allows users to search for pedals with stereo inputs/outputs. --- website/data/gear/death-by-audio-rooms.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/data/gear/death-by-audio-rooms.yaml b/website/data/gear/death-by-audio-rooms.yaml index 1f8a13f..2406c9c 100644 --- a/website/data/gear/death-by-audio-rooms.yaml +++ b/website/data/gear/death-by-audio-rooms.yaml @@ -1,7 +1,7 @@ name: "Rooms" manufacturer: "Death By Audio" category: Pedal -types: ["Reverb"] +types: ["Reverb", "Stereo"] technology: Digital controls: - "Freq" From 219020f1bc95f7edf328a4708cfad2985de958bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=20Kivel=C3=A4?= Date: Mon, 23 Feb 2026 13:52:13 +0200 Subject: [PATCH 30/46] Add clickable filtering to gear page attributes Make category, type, and technology badges clickable to add them as active filters. This provides a more intuitive way to filter gear by clicking on attributes directly from gear items. Changes: - Add onclick handlers to category, type, and technology badges - Add technology filter dropdown and filtering logic - Add hover effects to indicate clickable elements - Use event.stopPropagation() to prevent gear expansion on click Users can now filter by clicking badges or using the dropdown menus. --- website/layouts/gear/list.html | 48 +++++++++++++++++++++++++++++++--- website/static/css/main.css | 11 ++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/website/layouts/gear/list.html b/website/layouts/gear/list.html index 5c434ee..933868a 100644 --- a/website/layouts/gear/list.html +++ b/website/layouts/gear/list.html @@ -42,6 +42,11 @@

Gear

+
+ + +
+