diff --git a/AGENTS.md b/AGENTS.md index b5b4f95..bc41f1c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -84,6 +84,12 @@ Instant chart and diagram generation for chat and Telegram. - **Delivery:** Generates local PNGs and outputs `MEDIA:` for native Telegram image rendering. - **Tools:** Standalone CLI at `scripts/chart.py`. +### [create-website](skills/create-website/SKILL.md) +Creative Web Director and Senior Frontend Engineer for single-page applications and interactive showcases. +- **Features:** Builds fancy, responsive, zero-build SPAs (Tailwind CSS Play CDN, Alpine.js reactivity, Lucide icons, Google Fonts) with anti-AI-slop design archetypes (Dark Obsidian, Editorial Warmth, Neo-Brutalism, Modern Clean SaaS). +- **Hosting:** Direct validation and upload to `https://hosting.haumdaucher.de/showcase/...` via the `hosting` skill pipeline, with native Telegram document delivery and QR code generation. +- **Tools:** Standalone validation & publisher CLI at `scripts/publish_website.py`. + ### [grill-me](skills/grill-me/SKILL.md) Relentless, structured interview to stress-test and sharpen plans, architectures, decisions, or ideas before writing code. - **Features:** Decision tree & frontier exploration, Telegram/mobile-optimized batching (1-3 questions per round), facts-first environment research, anti-sycophancy challenge, and final specification/ADR synthesis. diff --git a/skills/create-website/SKILL.md b/skills/create-website/SKILL.md new file mode 100644 index 0000000..a41a027 --- /dev/null +++ b/skills/create-website/SKILL.md @@ -0,0 +1,150 @@ +--- +name: create-website +description: Build and host fancy, production-grade, interactive single-page websites, SPAs, and HTML showcases using modern Zero-Build technologies (Tailwind CSS, Alpine.js, Lucide Icons). Use when the user asks to create, build, design, or show a website, landing page, interactive calculator, showcase, recipe app, dashboard, or web tool. +--- + +# Create Website & Interactive Single-Page Applications 🌐✨ + +This skill empowers OpenClaw to act as a **Creative Web Director and Senior Frontend Engineer**. It generates visually stunning, responsive, fully interactive single-page applications (SPAs) contained within a single HTML file—without requiring complex build steps or Node.js compilers. + +--- + +## ⚡ Core Philosophy: The Zero-Build Reactive Engine + +1. **Self-Contained Single-File**: HTML markup, Tailwind CSS utility styling, Alpine.js reactivity, and Lucide icons reside in a single standalone file. +2. **Zero-Build Latency**: Runs directly in the browser via fast CDNs. Zero compile time, 100% reliable execution across desktop, tablet, and mobile (iOS Safari, Telegram in-app browser). +3. **Anti-AI-Slop Aesthetics**: Reject generic AI defaults (no plain Inter font, no purple-to-blue gradients, no boring centered 3-card monotony). Every site must have an intentional aesthetic direction with strong typography, depth, and micro-interactions. + +--- + +## 🎨 1. Choose a Visual Archetype + +Before writing code, commit strictly to one of the 4 design archetypes (detailed in [references/archetypes.md](references/archetypes.md)): + +* **Dark Obsidian & Cyber-Luxury**: Deep dark canvas (`#090A0F`), glassmorphism cards (`backdrop-blur-xl border border-white/10`), electric cyan/violet/emerald glows. Fonts: *Syne* + *Plus Jakarta Sans*. (Ideal for tech showcases, tools, AI, fitness trackers). +* **Editorial Warmth & Swiss Typographic**: Linen/ivory background (`#FDFBF7`), hairline borders (`border-stone-200`), warm terracotta/forest green accents. Fonts: *Fraunces* (Serif) + *Instrument Sans*. (Ideal for research dossiers, recipes, long-form stories). +* **Neo-Brutalism & High-Energy**: High contrast, thick black borders (`border-2 border-black`), offset hard drop shadows (`shadow-[4px_4px_0px_0px_#000]`), vivid canary yellow and lime accents. Fonts: *Space Grotesk* + *JetBrains Mono*. (Ideal for events, party invites, challenges, games). +* **Modern Clean SaaS & Dynamic Dashboard**: Crisp slate canvas, automatic dark/light mode toggle via Alpine, KPI cards, filterable tables. Fonts: *Outfit* + *Plus Jakarta Sans*. (Ideal for data apps, comparisons, calculators). + +--- + +## 🛠️ 2. The Standard CDN Skeleton + +Use this bulletproof template structure for all generated websites: + +```html + + + + + + Titel der Website + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +``` + +--- + +## ⚡ 3. Add Interactivity with Alpine.js + +Review [references/alpine-patterns.md](references/alpine-patterns.md) for pre-built patterns: +* **Dynamic Tabs**: Seamless view switching without page reloads. +* **Instant Search & Filter**: Real-time filtering of cards/recipes/items based on text input or category pills. +* **Reactive Calculators / Sliders**: Live computation (portions, prices, nutrition, splits) with instant UI updates. +* **Modal Dialogs & Drawers**: Smooth transitions with backdrop blur and escape key handling. +* **Celebration Effects**: Triggering confetti on successful calculations or milestones (`confetti({ particleCount: 80, spread: 70 })`). + +--- + +## 🚀 4. Publishing & Sharing Workflow + +Once the website HTML has been generated and saved locally (e.g. to `/tmp/websites/.html`), publish it to public hosting: + +### Step 1: Run the Publisher Script +```bash +uv run /home/node/mop-skills/mop-skills/skills/create-website/scripts/publish_website.py \ + --file /tmp/websites/.html \ + --category showcase \ + --slug +``` + +This validates the HTML and uploads it to MinIO, returning the public HTTPS URL: +`https://hosting.haumdaucher.de/showcase/{YYYYMMDD}-{slug}.html` + +### Step 2: Present to User in Telegram +Always deliver both the clickable link and the local file for offline access: +```markdown +🚀 **Deine Website ist live!** +🔗 https://hosting.haumdaucher.de/showcase/20260928-ninja-creami-calc.html + +*(Die Datei ist zum lokalen Öffnen ebenfalls beigefügt)* +MEDIA:/tmp/websites/.html +``` + +### Step 3: Offer QR Code +Immediately trigger `ask_user` to offer generating a scannable QR code: +```json +{ + "question": "Möchtest du für diesen Link einen QR-Code generieren?", + "options": ["Ja, QR-Code erstellen", "Nein, danke"] +} +``` +If approved, execute the `qr-code` skill: +```bash +uv run /home/node/mop-skills/mop-skills/skills/qr-code/scripts/generate_qr.py "https://hosting.haumdaucher.de/showcase/20260928-ninja-creami-calc.html" +``` +and send via `MEDIA:`. + +--- + +## 🛡️ Quality Gate & Anti-Slop Checklist +Always check the site against [references/anti-slop-checklist.md](references/anti-slop-checklist.md) before publishing: +1. Are font pairings distinct and imported via Google Fonts? +2. Are all buttons responsive with hover and active states (`active:scale-95`)? +3. Does the layout scale gracefully on small mobile screens (`grid-cols-1 md:grid-cols-2 lg:grid-cols-3`)? +4. Are Lucide icons properly rendered via `lucide.createIcons()`? diff --git a/skills/create-website/references/alpine-patterns.md b/skills/create-website/references/alpine-patterns.md new file mode 100644 index 0000000..1443adb --- /dev/null +++ b/skills/create-website/references/alpine-patterns.md @@ -0,0 +1,210 @@ +# Alpine.js Core Patterns for Single-Page Applications ⚡ + +These zero-build Alpine.js patterns provide reactive state management directly in HTML. Use these snippets to make your single-page websites snappy, interactive, and responsive. + +--- + +## 1. Dark/Light Mode Switcher with Persistence +Place this in ``: + +```html + +``` + +--- + +## 2. Dynamic Tab Navigation +Switch between multiple views without page reloads: + +```html +
+ +
+ + +
+ + +
+ +
+ +
+ +
+
+``` + +--- + +## 3. Instant Search & Category Filter +Real-time filtering of cards or list items: + +```html +
+ +
+
+ + +
+
+ +
+
+ + +
+ +
+
+``` + +--- + +## 4. Live Reactive Slider & Calculator +Instant calculations with automatic UI updates: + +```html +
+
+

Portions-Rechner

+ +
+ +
+ + +
+ + +
+
+ Milch 1.5% oder Hafermilch + +
+
+ Proteinpulver (Whey/Casein) + +
+
+ Pistazienmus (100%) + +
+
+ + +
+``` + +--- + +## 5. Modal Dialog with Escape & Backdrop Blur +Accessible modal overlay: + +```html +
+ + + +
+``` diff --git a/skills/create-website/references/anti-slop-checklist.md b/skills/create-website/references/anti-slop-checklist.md new file mode 100644 index 0000000..630ca50 --- /dev/null +++ b/skills/create-website/references/anti-slop-checklist.md @@ -0,0 +1,38 @@ +# Anti-AI-Slop Quality Checklist 🛡️ + +Before finalizing and publishing any website or HTML artifact, verify every item on this checklist. This ensures the output feels designed by a human senior product designer rather than a generic LLM. + +--- + +## ❌ Forbidden Trappings of "AI Slop" +1. **Never use generic default fonts**: + * 🚫 No raw Inter, Roboto, Arial, or unstyled system-ui. + * ✅ Always import characterful fonts (e.g. *Syne*, *Cabinet Grotesk*, *Fraunces*, *Space Grotesk*, *Plus Jakarta Sans*). +2. **Never use the generic purple-to-indigo gradient**: + * 🚫 `bg-gradient-to-r from-purple-600 to-indigo-600` on white background. + * ✅ Use intentional palettes: deep obsidian with electric cyan accents, warm ivory with terracotta, neo-brutalist yellow with black borders, or slate with emerald glow. +3. **Never generate static, boring centered 3-card rows**: + * 🚫 Three identical white boxes with light gray text centered on a plain gray canvas. + * ✅ Vary card sizes, emphasize a primary card with a border glow or badge, use grid spans (`md:col-span-2`), or add asymmetry. +4. **Never forget interactive micro-interactions**: + * 🚫 Dead buttons that don't react to hover or click. + * ✅ Always add `hover:-translate-y-0.5 hover:shadow-lg transition-all duration-200` and `active:scale-95`. +5. **Never rely on broken external images**: + * 🚫 Using fake URLs like `https://example.com/image.jpg` or placeholder services that fail. + * ✅ Use SVG patterns, Lucide icons, gradient mesh backgrounds, or reliable Unsplash source links with explicit fallback styling. + +--- + +## ✅ Mandatory Pre-Flight Verification + +| Check | Requirement | Verified? | +|---|---|:---:| +| **Viewport & Encoding** | `` | [ ] | +| **Tailwind CDN** | `` included in `` | [ ] | +| **Alpine.js CDN** | `` in `` | [ ] | +| **Lucide Icons** | `` + `lucide.createIcons()` at bottom of `` | [ ] | +| **Google Fonts** | Preconnect + Google Font `` + custom ` +``` + +### Color Palette & Classes +* **Canvas Background**: `bg-[#090A0F]` with subtle radial gradients: + `bg-[radial-gradient(ellipse_80%_80%_at_50%_-20%,rgba(120,119,198,0.15),rgba(255,255,255,0))]` +* **Card Surface (Glassmorphism)**: `bg-white/[0.03] backdrop-blur-xl border border-white/10 hover:border-white/20 transition-all` +* **Secondary Surface**: `bg-[#12151E] border border-white/5` +* **Text Hierarchy**: Headings `text-white font-bold tracking-tight`, Body `text-zinc-400`, Subtle `text-zinc-500` +* **Accent Highlights**: + * Electric Cyan: `text-cyan-400 bg-cyan-500/10 border-cyan-500/20 shadow-[0_0_20px_rgba(6,182,212,0.15)]` + * Neon Violet: `text-purple-400 bg-purple-500/10 border-purple-500/20` + * Emerald Glow: `text-emerald-400 bg-emerald-500/10 border-emerald-500/20` +* **Buttons**: + * Primary: `bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-400 hover:to-blue-500 text-white font-medium px-5 py-2.5 rounded-xl shadow-lg shadow-cyan-500/20 active:scale-95 transition-all` + * Secondary: `bg-white/5 hover:bg-white/10 text-white border border-white/10 px-5 py-2.5 rounded-xl active:scale-95 transition-all` + +--- + +## 2. Editorial Warmth & Swiss Typographic 📰 +*Best for: In-depth research dossiers, long-form journalism, recipe collections, personal manifestos, architectural notes, knowledge collections.* + +### Google Fonts Embed +```html + + + + +``` + +### Color Palette & Classes +* **Canvas Background**: Warm Ivory `bg-[#FDFBF7]` or Linen `bg-[#F7F4EE]` +* **Card Surface**: Paper White `bg-white border border-stone-200/80 shadow-sm rounded-2xl` +* **Text Hierarchy**: Headings `text-stone-900 font-semibold tracking-tight`, Body `text-stone-600 leading-relaxed`, Captions `text-stone-500 text-sm` +* **Accent Highlights**: + * Heritage Rust / Terracotta: `text-[#C2410C] bg-[#FFF7ED] border-[#FDBA74]` + * Deep Forest: `text-[#166534] bg-[#F0FDF4] border-[#BBF7D0]` + * Warm Amber: `text-[#B45309] bg-[#FEF3C7] border-[#FDE68A]` +* **Styling Rules**: + * Generous margins and breathing room (`py-16 md:py-24 max-w-4xl mx-auto`). + * Elegant divider rules (`border-t border-stone-200`). + * Dropcaps or serif quotes with subtle italic styling (`italic text-stone-700`). + +--- + +## 3. Neo-Brutalism & High-Energy ⚡ +*Best for: Interactive party invites, festival schedules, fitness challenges, hackathon pitches, playful calculators, game companions.* + +### Google Fonts Embed +```html + + + + +``` + +### Color Palette & Classes +* **Canvas Background**: High-contrast Pale Pastel `bg-[#FAF8F5]` or Off-Yellow `bg-[#FEFCE8]` +* **Borders & Shadows**: + * Signature Shadow: `border-2 border-black shadow-[4px_4px_0px_0px_#000] hover:shadow-[2px_2px_0px_0px_#000] hover:translate-x-[2px] hover:translate-y-[2px] transition-all` + * Card: `bg-white border-2 border-black shadow-[5px_5px_0px_0px_#000] rounded-xl p-6` +* **Color Accents**: + * Electric Canary: `bg-[#FDE047]` + * Acid Lime: `bg-[#A3E635]` + * Bright Coral: `bg-[#FB7185]` + * Cyan Pill: `bg-[#38BDF8]` +* **Interactive Elements**: + * Badges: `inline-block px-3 py-1 text-xs font-bold uppercase tracking-wider bg-black text-white rounded-md` + * Buttons: `bg-[#FDE047] hover:bg-[#FACC15] text-black font-bold px-6 py-3 border-2 border-black shadow-[4px_4px_0px_0px_#000] active:translate-x-[4px] active:translate-y-[4px] active:shadow-none transition-all rounded-xl cursor-pointer` + +--- + +## 4. Modern Clean SaaS & Dynamic Dashboard 📊 +*Best for: Multi-tab trackers, data analytics, KPI summaries, filterable catalogues, interactive calculators, recipe managers.* + +### Google Fonts Embed +```html + + + + +``` + +### Color Palette & Classes +* **Canvas Background**: Adaptive Light/Dark: + `bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100 transition-colors duration-200` +* **Card Surface**: + `bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-800 rounded-2xl shadow-sm hover:shadow-md transition-all` +* **KPI Metrics**: + * Value: `text-3xl md:text-4xl font-bold font-heading tracking-tight text-slate-900 dark:text-white` + * Label: `text-xs font-medium uppercase tracking-wider text-slate-500 dark:text-slate-400 mt-1` +* **Interactive Controls**: + * Inputs: `bg-slate-100 dark:bg-slate-800 border-none rounded-xl px-4 py-2 text-sm focus:ring-2 focus:ring-indigo-500` + * Active Tabs: `bg-indigo-600 text-white rounded-lg px-4 py-2 font-medium shadow-sm` + * Inactive Tabs: `text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white px-4 py-2 rounded-lg` diff --git a/skills/create-website/scripts/publish_website.py b/skills/create-website/scripts/publish_website.py new file mode 100755 index 0000000..192491a --- /dev/null +++ b/skills/create-website/scripts/publish_website.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python3 +# /// script +# dependencies = [ +# "boto3>=1.34.0", +# ] +# /// +""" +Publish single-page websites to hosting.haumdaucher.de with validation and clean URL reporting. +Integrates with the existing 'hosting' skill publisher. +""" + +import argparse +import os +import re +import subprocess +import sys +from pathlib import Path + +def validate_html(content: str) -> list[str]: + """Basic pre-flight checks to ensure the generated website isn't malformed.""" + warnings = [] + content_lower = content.lower() + + if "" not in content_lower: + warnings.append("Missing declaration.") + if "viewport" not in content_lower: + warnings.append("Missing responsive viewport meta tag.") + if "cdn.tailwindcss.com" not in content_lower and "tailwindcss" not in content_lower: + warnings.append("Tailwind CSS CDN not detected in HTML.") + if "alpine" not in content_lower: + warnings.append("Alpine.js script not detected (interactivity might be missing).") + if "lucide" in content_lower and "lucide.createicons" not in content_lower: + warnings.append("Lucide icons included, but 'lucide.createIcons()' initialization is missing.") + + return warnings + +def resolve_hosting_script() -> Path: + """Finds the publish_file.py script relative to this script or in standard locations.""" + current_dir = Path(__file__).resolve().parent + # Standard relative location in mop-skills + sibling_hosting = current_dir.parent.parent / "hosting" / "scripts" / "publish_file.py" + if sibling_hosting.exists(): + return sibling_hosting + + # Fallback in container + pod_hosting = Path("/home/node/mop-skills/mop-skills/skills/hosting/scripts/publish_file.py") + if pod_hosting.exists(): + return pod_hosting + + raise FileNotFoundError(f"Cannot find publish_file.py in {sibling_hosting} or {pod_hosting}") + +def main(): + parser = argparse.ArgumentParser(description="Publish websites to hosting.haumdaucher.de") + parser.add_argument("--file", "-f", help="Local HTML file to upload") + parser.add_argument("--content", "-c", help="Inline HTML content string") + parser.add_argument("--slug", "-s", required=True, help="Slug identifier for URL (e.g. creami-calculator)") + parser.add_argument("--category", default="showcase", help="Category folder (default: showcase)") + parser.add_argument("--skip-validation", action="store_true", help="Skip HTML quality checks") + parser.add_argument("--url-only", action="store_true", help="Print only public URL") + + args = parser.parse_args() + + if not args.file and args.content is None: + parser.error("You must provide either --file or --content.") + + # Read content for validation + html_content = "" + if args.file: + file_path = Path(args.file) + if not file_path.exists(): + sys.exit(f"Error: File not found: {args.file}") + html_content = file_path.read_text(encoding="utf-8", errors="replace") + else: + html_content = args.content + + # Quality validation + if not args.skip_validation: + warnings = validate_html(html_content) + if warnings: + print("⚠️ Pre-flight HTML Warnings:", file=sys.stderr) + for w in warnings: + print(f" - {w}", file=sys.stderr) + + # Delegate to hosting/scripts/publish_file.py + publisher_script = resolve_hosting_script() + cmd = [ + sys.executable, + str(publisher_script), + "--category", args.category, + "--slug", args.slug, + "--ext", "html", + ] + + if args.file: + cmd.extend(["--file", str(args.file)]) + else: + cmd.extend(["--content", args.content]) + + if args.url_only: + cmd.append("--url-only") + + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + sys.stderr.write(result.stderr) + sys.exit(result.returncode) + + print(result.stdout.strip()) + +if __name__ == "__main__": + main() diff --git a/skills/create-website/tests/test_publish_website.py b/skills/create-website/tests/test_publish_website.py new file mode 100644 index 0000000..df764df --- /dev/null +++ b/skills/create-website/tests/test_publish_website.py @@ -0,0 +1,45 @@ +import unittest +from pathlib import Path +import sys + +# Add scripts directory to path +script_dir = Path(__file__).resolve().parent.parent / "scripts" +sys.path.insert(0, str(script_dir)) + +from publish_website import validate_html, resolve_hosting_script + +class TestPublishWebsite(unittest.TestCase): + def test_validate_html_valid(self): + valid_html = """ + + + + + + + + + + + + + + """ + warnings = validate_html(valid_html) + self.assertEqual(warnings, []) + + def test_validate_html_warnings(self): + broken_html = "Hello" + warnings = validate_html(broken_html) + self.assertIn("Missing declaration.", warnings) + self.assertIn("Missing responsive viewport meta tag.", warnings) + self.assertIn("Tailwind CSS CDN not detected in HTML.", warnings) + self.assertIn("Alpine.js script not detected (interactivity might be missing).", warnings) + + def test_resolve_hosting_script(self): + hosting_script = resolve_hosting_script() + self.assertTrue(hosting_script.exists()) + self.assertEqual(hosting_script.name, "publish_file.py") + +if __name__ == "__main__": + unittest.main()