
Surprise Templates
- 9 installs
- 21 repo stars
- Updated August 3, 2026
- starchild-ai-agent/official-skills
Helps with ai & agent building tasks during AI-assisted development.
About
surprise-templates is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- surprise-templates
- AI & Agent Building
- AI-coding skill
Surprise Templates by the numbers
- 9 all-time installs (skills.sh)
- Ranked #12,152 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/starchild-ai-agent/official-skills --skill surprise-templatesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 21 |
| Last updated | August 3, 2026 |
| Repository | starchild-ai-agent/official-skills ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Surprise Me — Template System
This skill provides 119 pre-built templates and 20 color themes.
The agent MUST use an existing template. Never write HTML/CSS/JS from scratch.
---
1. Template Selection Flow
Step 1 — Collect User Signals
From the user research phase you will have signals such as:
- Connected data sources:
wallet,twitter,github,email - Interests:
defi,nft,trading,developer,productivity, etc. - Mentioned tokens, tools, or topics
- User's display language (zh-CN, en, ja, ko, etc.)
- User's avatar URL or brand color (if available)
- Previously delivered template IDs (from conversation history or user profile)
Step 2 — Match & Randomly Select Template
Compare user signals against each template's matchSignals array (see §4 Template Catalog).
Step 2a — Build candidate list: 1. Score every template by counting how many of the user's signals appear in its matchSignals. 2. Keep all templates with score ≥ 2 (or ≥ 1 if fewer than 3 templates reach 2). 3. Remove any template the user has already received (see §1.1 Anti-Repeat).
Step 2b — Randomly select from candidates:
- Do NOT pick the first match or the highest-scored match deterministically.
- From the candidate list, randomly select one template. Use a mental coin-flip, current-second parity, or any non-deterministic method available to you.
- If multiple templates share the same top score, they are equally valid — pick one at random.
- The goal is: two users with identical signals should usually get different templates.
Step 2c — Data source preference (tie-breaking only):
- If you must narrow candidates further:
wallet→ prefer Category A/E;twitter→ prefer Category C/E;github→ prefer Category B/E. - This is a soft preference, not a hard filter. Randomness takes priority over category preference.
Step 2d — No-signal fallback (no wallet, no GitHub, no Twitter):
- When the user has NO connected data sources and NO specific interest signals:
1. Use the full Category D list (28 standalone tools) as the candidate pool. 2. Randomly select one from Category D. Do not default to the same template. 3. Additionally consider universally appealing templates: morning-briefing, pomodoro, kanban, quick-notes, fear-greed, market-overview, crypto-quiz. 4. If the user has mentioned any topic at all (even casually), use it to filter Category D by matchSignals before random selection.
Step 3 — Copy Template to Output
cp -r skills/surprise-templates/templates/{template-id}/ output/surprise-me/Step 4 — Randomly Select Theme & Apply
Step 4a — Theme randomization: 1. Consult the Theme Selection Guidelines (§5) to identify 3–5 themes that fit the template's category. 2. Randomly pick one from those fitting themes. Do NOT always use the first suggested theme. 3. If no category guideline applies, randomly pick from all 20 themes. 4. Vary between dark/light themes across sessions for the same user.
Step 4b — Apply theme + placeholders: 1. In output/surprise-me/style.css, replace CSS variable values with the chosen theme's colors 2. In output/surprise-me/index.html, replace {{PLACEHOLDER}} tokens with personalized content (see §3 Placeholder Rules) 3. Add Google Fonts <link> for the theme's fonts.heading, fonts.body, and fonts.mono 4. Update chartColors in script.js if the template uses Chart.js
Step 5 — Personalize Beyond Placeholders
After basic placeholder replacement, apply these additional personalizations:
1. Creative APP_TITLE — Don't just use the template's default name. Craft a title that reflects the user's identity. Examples:
"DeFi Yield Optimizer"→"Alex 的 DeFi 收益雷达"or"Alex's Alpha Farm""Tech Radar"→"Alex 的技术风向标"or"Alex's Stack Compass"- Incorporate the user's name, interests, or style into the title.
2. Realistic mock data — Templates contain placeholder data (chart values, table rows, etc.). Adjust these to feel authentic:
- If user tracks ETH/SOL, use realistic price ranges for those tokens (not $999 or $123).
- If user is a developer, populate code snippets with languages from their tech stack.
- Match numerical ranges to real-world values (gas fees in gwei, APY in reasonable %, etc.).
3. Language localization — Translate ALL visible UI text (headings, labels, buttons, tooltips, empty states) to the user's display language. This includes:
- Navigation items, card titles, chart labels, footer text
- Placeholder text in search bars and inputs
- Error messages and empty state descriptions
4. Avatar & brand color injection (if available):
- If user has an avatar URL, inject it as a profile image in the header/sidebar.
- If user has a brand color, use it as
--accent-1override on top of the selected theme.
Step 6 — Preview Serve
cd output/surprise-me && python3 -m http.server 8080Open http://localhost:8080 to verify, then deliver to user.
---
1.1 Anti-Repeat Mechanism
To prevent the same user from seeing the same template on repeated "Surprise Me" requests:
1. Check history — Before selecting, review the conversation history for any previously delivered template IDs. Also check if the user profile stores a surprise_history array. 2. Exclude seen templates — Remove all previously delivered template IDs from the candidate list. 3. Cycle through categories — If the user has exhausted all templates in their best-matching category, move to the next category (e.g., A → E → D). 4. Theme rotation — Even if forced to reuse a template (all candidates exhausted), apply a different theme than last time. 5. Full reset — If all 119 templates have been shown (unlikely), reset the history and start fresh.
---
2. Prohibited Actions
| Rule | Detail |
|---|---|
| No from-scratch HTML/CSS/JS | You MUST use an existing template. Do not create new pages from zero. |
| No placeholder guessing | Only replace placeholders defined in the template's manifest.json. |
| No theme color hardcoding | All colors must come from the selected theme JSON file. |
| No API-key endpoints | Templates only call public, CORS-enabled, key-free APIs. |
| No emoji icons | Templates use Lucide inline SVGs, never emoji for structural UI elements. |
---
3. Placeholder Replacement Rules
Each template's manifest.json lists its placeholders object. Common placeholders:
| Placeholder | Source | Example Value |
|---|---|---|
{{APP_TITLE}} | Template name or personalized title | "Alex's Trading Dashboard" |
{{USER_NAME}} | User's display name | "Alex" |
{{WALLET_ADDRESS}} | User's connected wallet | "0x1234...abcd" |
{{TWITTER_HANDLE}} | User's Twitter username | "@alexcrypto" |
{{GITHUB_USERNAME}} | User's GitHub username | "alexdev" |
{{TRACKED_TOKENS}} | User's tokens of interest (JSON array) | ["ETH","SOL","ARB"] |
{{HERO_IMAGE_URL}} | AI-generated hero image URL or local path | "./hero-bg.png" |
{{INTERESTS}} | User's interest keywords | "DeFi, NFT, Layer2" |
{{CITY}} | User's city for weather/time | "Tokyo" |
{{TECH_STACK}} | User's tech stack | "React, TypeScript, Solidity" |
{{RESEARCH_AREAS}} | User's research interests | "LLM, Diffusion Models" |
{{COMPETITORS}} | Competitor accounts to track | "@competitor1, @competitor2" |
Rules:
- Replace
{{PLACEHOLDER}}literally inindex.html(andscript.jsif referenced) - If a placeholder value is unavailable, use a sensible default (e.g.,
"Explorer"forUSER_NAME) TRACKED_TOKENSshould be a JS array inscript.jscontext, a comma-separated string in HTML context
---
4. Template Catalog (119 templates)
Templates are in skills/surprise-templates/templates/{id}/.
Each template directory contains: index.html, style.css, script.js, manifest.json, and optionally hero-bg.png.
Category A — Wallet & On-Chain Analytics (32 templates)
Require wallet signal. Dashboards for portfolio, trading, DeFi, NFT, and on-chain data.
| ID | Name | matchSignals | Placeholders |
|---|---|---|---|
airdrop-checker | Airdrop Eligibility Checker | wallet, airdrop, eligibility, claim | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
competitor-analysis | Competitor Token Analysis | wallet, competitor, analysis, comparison, token | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
correlation-matrix | Portfolio Correlation Matrix | wallet, portfolio, correlation, risk, diversification | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
crosschain-advisor | Cross-Chain Migration Advisor | wallet, bridge, crosschain, migration, advisor | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
crypto-events | Crypto Events Calendar | wallet, events, calendar, conference, upgrade | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
defi-yield | DeFi Yield Optimizer | wallet, defi, yield, farming, staking | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
dex-arb | DEX vs CEX Arbitrage | wallet, dex, cex, arbitrage, price-spread | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
ecosystem-dashboard | Ecosystem Dashboard | wallet, ecosystem, chain, base, arbitrum | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
funding-rate | Funding Rate Dashboard | wallet, funding-rate, perpetual, futures, arbitrage | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
gas-tracker | Gas Fee Tracker | wallet, gas, ethereum, transaction | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
impermanent-loss | Impermanent Loss Calculator | wallet, defi, impermanent-loss, liquidity, calculator | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
liquidation-heatmap | Liquidation Heatmap | wallet, liquidation, leverage, risk, futures | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
market-cycle | Market Cycle Indicators | wallet, market-cycle, macro, indicator, phase | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
meme-radar | Meme Coin Radar | wallet, meme, degen, trending, social | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
nft-gallery | NFT Collection Gallery | wallet, nft, collection, gallery, art | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
nft-market-trends | NFT Market Trends | wallet, nft, market, trends, analytics | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
nft-rarity | NFT Rarity Explorer | wallet, nft, rarity, collection, traits | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
onchain-timeline | On-Chain Activity Timeline | wallet, onchain, history, timeline, activity | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
personalized-news | Personalized Crypto News Feed | wallet, news, personalized, crypto, feed | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
portfolio-radar | Portfolio Risk Radar | wallet, portfolio, investment, risk, allocation, diversification | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
portfolio-timemachine | Portfolio Time Machine | wallet, portfolio, investment, historical | APP_TITLE, USER_NAME, TRACKED_TOKENS, PORTFOLIO_VALUE, HERO_IMAGE_URL |
protocol-risk | Protocol Risk Exposure Map | wallet, defi, protocol, risk, exposure | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
regulation-tracker | Crypto Regulation Tracker | wallet, regulation, compliance, legal, news | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
smart-money | Smart Money Tracker | wallet, smart-money, whale, institutional, tracking | APP_TITLE, USER_NAME, TRACKED_TOKENS, WALLET_ADDRESS, HERO_IMAGE_URL |
stablecoin-yield | Stablecoin Yield Comparison | wallet, stablecoin, yield, apy, comparison | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
token-momentum | Token Momentum Scanner | wallet, momentum, rsi, macd, technical-analysis | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
token-sentiment | Token Sentiment Indicators | wallet, sentiment, fear-greed, token, mood | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
token-unlock | Token Unlock Calendar | wallet, token, unlock, vesting, calendar | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
trading-intel | Trading Intelligence Dashboard | wallet, defi, trading, crypto, token | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
whale-alert | Whale Alert Monitor | wallet, whale, alert, large-transfer, monitoring | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
whale-comparison | Whale Comparison Dashboard | wallet, whale, defi, portfolio | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
Category B — Developer & GitHub Tools (23 templates)
Require github signal. Tools for developers, code references, and tech exploration.
| ID | Name | matchSignals | Placeholders |
|---|---|---|---|
api-playground | API Playground | github, api, testing, playground, http | APP_TITLE, BASE_URL, DEFAULT_HEADERS |
blockchain-toolkit | Blockchain Dev Toolkit | github, blockchain, tools, development, reference | APP_TITLE, PREFERRED_CHAIN, DEV_LEVEL |
code-snippets | Code Snippet Library | github, code, snippets, patterns, reference | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
color-scheme | Color Scheme Generator | github, design, color, palette, css | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
css-playground | CSS Animation Playground | github, css, animation, frontend, playground | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
dapp-ideas | DApp Idea Generator | github, dapp, ideas, web3, creative | APP_TITLE, PREFERRED_CHAIN, INTEREST_AREA |
dependency-monitor | Dependency Update Monitor | github, dependencies, security, updates, npm | APP_TITLE, USER_NAME, GITHUB_USERNAME, HERO_IMAGE_URL |
framework-migration | Framework Migration Guide | github, framework, migration, upgrade, guide | APP_TITLE, SOURCE_FRAMEWORK, TARGET_FRAMEWORK, PROJECT_NAME |
gas-optimization | Gas Optimization Cheatsheet | github, solidity, gas, optimization, reference | APP_TITLE, SOLIDITY_VERSION, TARGET_CHAIN |
github-stars | GitHub Stars Explorer | github, stars, repositories, analysis, interests | APP_TITLE, USER_NAME, GITHUB_USERNAME, HERO_IMAGE_URL |
hackathon-matcher | Hackathon Matcher | github, hackathon, competition, team, events | APP_TITLE, USER_NAME, TECH_STACK, HERO_IMAGE_URL |
indie-dev | Indie Developer Dashboard | github, indie, startup, saas, solo-developer | APP_TITLE, USER_NAME, TECH_STACK, HERO_IMAGE_URL |
interview-prep | Technical Interview Prep | github, interview, preparation, coding, algorithms | APP_TITLE, USER_NAME, TECH_STACK, HERO_IMAGE_URL |
learning-path | Learning Path Generator | github, learning, path, skills, education | APP_TITLE, TECH_STACK, USER_LEVEL |
opensource-finder | Open Source Opportunity Finder | github, opensource, contribution, good-first-issue, community | APP_TITLE, USER_NAME, GITHUB_USERNAME, TECH_STACK, HERO_IMAGE_URL |
opensource-monetize | Open Source Monetization Guide | github, opensource, monetization, revenue, business | APP_TITLE, PROJECT_NAME, GITHUB_STARS |
producthunt-tracker | Product Hunt Tracker | github, producthunt, launches, products, trending | APP_TITLE, FOCUS_CATEGORIES, USER_NAME |
regex-tester | Regex Pattern Tester | github, regex, pattern, testing, developer | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
side-project-ideas | Side Project Idea Generator | github, ideas, project, creative, generator | APP_TITLE, TECH_PREFERENCES, DIFFICULTY |
smart-contract-scanner | Smart Contract Security Scanner | github, solidity, smart-contract, security, audit | APP_TITLE, USER_NAME, CONTRACT_ADDRESS, HERO_IMAGE_URL |
tech-blogs | Tech Blog Aggregator | github, blog, articles, reading, tech | APP_TITLE, FEED_TOPICS, USER_NAME |
tech-conferences | Tech Conference Finder | github, conference, events, tech, meetup | APP_TITLE, FOCUS_TOPICS, REGION |
tech-radar | Tech Stack Trend Radar | github, developer, programming, code, tech, stack, framework | APP_TITLE, USER_NAME, TECH_STACK, GITHUB_USERNAME, HERO_IMAGE_URL |
web3-jobs | Web3 Job Board | github, web3, jobs, remote, hiring | APP_TITLE, PREFERRED_CHAINS, USER_SKILLS |
Category C — Twitter & Social Intelligence (18 templates)
Require twitter signal. Social analytics, content strategy, and trend tracking.
| ID | Name | matchSignals | Placeholders |
|---|---|---|---|
ai-research | AI Research Digest | twitter, ai, research, papers, models | APP_TITLE, USER_NAME, RESEARCH_AREAS |
airdrop-intel | Airdrop Intel Feed | twitter, airdrop, intel, alpha, opportunities | APP_TITLE, USER_NAME |
competitor-monitor | Competitor Monitor | twitter, competitor, monitoring, social, tracking | APP_TITLE, USER_NAME, COMPETITORS |
content-calendar | Content Calendar — Posting Strategy | twitter, content, calendar, schedule, posting | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
ct-alpha | CT Alpha Feed | twitter, crypto-twitter, alpha, signals, defi | APP_TITLE, USER_NAME, TWITTER_HANDLE, TRACKED_TOKENS, HERO_IMAGE_URL |
devtools-radar | Developer Tools Radar | twitter, devtools, releases, updates, radar | APP_TITLE, USER_NAME |
event-tracker | Event Tracker | twitter, events, tracking, upcoming, calendar | APP_TITLE, USER_NAME, EVENT_TYPES |
industry-news | Industry News Radar | twitter, news, industry, realtime, feed | APP_TITLE, USER_NAME, INDUSTRIES |
interest-radar | Interest Radar — Topic Analysis | twitter, interests, topics, content, radar | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
kol-tracker | KOL Tracker | twitter, kol, influencer, opinion-leader, tracking | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
narrative-tracker | Narrative Tracker | twitter, narrative, trend, crypto, macro | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
remote-jobs | Remote Job Board | twitter, remote, jobs, work, hiring | APP_TITLE, USER_NAME, SKILLS |
social-graph | Social Graph — Network Analysis | twitter, social, network, connections, graph | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
startup-funding | Startup Funding Tracker | twitter, startup, funding, investment, venture | APP_TITLE, USER_NAME |
tech-launches | Tech Product Launch Feed | twitter, producthunt, hackernews, launches, tech | APP_TITLE, USER_NAME |
thread-ideas | Thread Ideas Generator | twitter, thread, ideas, content, writing | APP_TITLE, USER_NAME, NICHE |
token-launches | Token Launch Tracker | twitter, launch, ido, token-sale, upcoming | APP_TITLE, USER_NAME |
token-mentions | Token Mentions Tracker | twitter, token, mentions, kol, tracking | APP_TITLE, USER_NAME, TRACKED_TOKENS |
topic-dashboard | Trending Topics Dashboard | twitter, trending, topics, news, dashboard | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
viral-analyzer | Viral Tweet Analyzer | twitter, viral, engagement, analytics, growth | APP_TITLE, USER_NAME, TWITTER_HANDLE, HERO_IMAGE_URL |
Category D — Standalone Tools & Utilities (28 templates)
No specific data source required. General-purpose tools, calculators, and utilities.
| ID | Name | matchSignals | Placeholders |
|---|---|---|---|
ascii-art | ASCII Art Generator | tool, ascii, art, text, generator | APP_TITLE, HERO_IMAGE_URL |
block-explorer-lite | Block Explorer Lite | blockchain, explorer, address, transaction, search | APP_TITLE, DEFAULT_CHAIN, HERO_IMAGE_URL |
bookmark-manager | Bookmark Manager | productivity, bookmarks, organize, links, reading | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
contract-reader | Smart Contract Reader | blockchain, contract, abi, reader, tool | APP_TITLE, HERO_IMAGE_URL |
cron-builder | Cron Expression Builder | developer, cron, scheduler, expression, tool | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
crypto-quiz | Crypto Knowledge Quiz | education, quiz, crypto, knowledge, game | APP_TITLE, HERO_IMAGE_URL |
dca-calculator | DCA Calculator | calculator, dca, investment, strategy, crypto | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
defi-compare | DeFi Protocol Comparison | defi, compare, protocol, tvl, apy | APP_TITLE, HERO_IMAGE_URL |
fear-greed | Crypto Fear & Greed Index | crypto, market, sentiment, fear, greed, bitcoin | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
habit-tracker | Habit Tracker | productivity, habits, tracking, streak, daily | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
ico-calendar | ICO/IDO Calendar | calendar, ico, ido, token-sale, upcoming | APP_TITLE, HERO_IMAGE_URL |
json-formatter | JSON Formatter & Validator | productivity, json, formatter, validator, developer | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
kanban | Kanban Board | productivity, kanban, tasks, project, board | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
markdown-editor | Markdown Editor | productivity, markdown, editor, writing, documentation | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
market-overview | Market Overview Dashboard | market, overview, crypto, stocks, forex | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
meme-generator | Meme Generator | tool, meme, generator, image, fun | APP_TITLE, HERO_IMAGE_URL |
morning-briefing | Morning Intelligence Briefing | news, briefing, morning, daily, summary, update, digest | APP_TITLE, USER_NAME, INTERESTS, CITY, HERO_IMAGE_URL |
password-gen | Password Generator | productivity, password, security, generator, tool | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
pixel-editor | Pixel Art Editor | tool, pixel, art, editor, creative | APP_TITLE, HERO_IMAGE_URL |
pnl-calculator | P&L Calculator | calculator, pnl, profit-loss, trading, crypto | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
pomodoro | Pomodoro Timer | productivity, timer, pomodoro, focus, task | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
qrcode-gen | QR Code Generator | productivity, qrcode, generator, tool, share | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
quick-notes | Quick Notes | productivity, notes, markdown, quick, writing | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
staking-yield | Staking Yield Calculator | calculator, staking, yield, apy, comparison | APP_TITLE, DEFAULT_TOKEN, HERO_IMAGE_URL |
tax-estimator | Crypto Tax Estimator | calculator, tax, crypto, estimation, tool | APP_TITLE, DEFAULT_COUNTRY, HERO_IMAGE_URL |
timezone-converter | Timezone Converter | productivity, timezone, converter, remote, global | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
token-compare | Token Compare Tool | compare, token, analysis, market-cap, tvl | APP_TITLE, USER_NAME, TRACKED_TOKENS, HERO_IMAGE_URL |
typing-test | Typing Speed Test | productivity, typing, speed, test, keyboard | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
unit-converter | Unit Converter | productivity, converter, units, currency, tool | APP_TITLE, DEFAULT_CATEGORY, HERO_IMAGE_URL |
web3-glossary | Web3 Glossary | web3, glossary, terms, education, reference | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
Category E — Multi-Source & Cross-Platform (16 templates)
Require 2+ data sources (e.g., wallet + twitter, github + twitter). Rich, multi-dimensional dashboards.
| ID | Name | matchSignals | Placeholders |
|---|---|---|---|
ai-builder | AI Builder Toolkit | twitter, github, ai, machine-learning, developer | APP_TITLE, USER_NAME, GITHUB_USERNAME, HERO_IMAGE_URL |
alpha-signal | Alpha Signal Board | twitter, wallet, alpha, signal, trading | APP_TITLE, USER_NAME, TWITTER_HANDLE, WALLET_ADDRESS, HERO_IMAGE_URL |
creator-analytics | Creator Analytics Dashboard | twitter, github, creator, analytics, content | APP_TITLE, USER_NAME, TWITTER_HANDLE, GITHUB_USERNAME, HERO_IMAGE_URL |
crosschain-bridge | Cross-Chain Bridge Optimizer | wallet, bridge, crosschain, multichain, transfer | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
defi-dev-console | DeFi Developer Console | github, wallet, defi, solidity, developer | APP_TITLE, USER_NAME, GITHUB_USERNAME, WALLET_ADDRESS, HERO_IMAGE_URL |
dev-brand | Developer Brand Page | github, twitter, brand, portfolio, developer | APP_TITLE, USER_NAME, GITHUB_USERNAME, TWITTER_HANDLE, HERO_IMAGE_URL |
efficiency-hub | Efficiency & Finance Hub | wallet, email, calendar, portfolio, productivity | APP_TITLE, USER_NAME, WALLET_ADDRESS, TRACKED_TOKENS, HERO_IMAGE_URL |
enterprise-intel | Enterprise Intelligence Dashboard | email, twitter, enterprise, competitor, intelligence | APP_TITLE, USER_NAME, COMPANY_NAME, HERO_IMAGE_URL |
ml-tracker | ML Experiment Tracker | github, twitter, ml, experiment, python | APP_TITLE, USER_NAME, GITHUB_USERNAME, HERO_IMAGE_URL |
nft-community | NFT Community Radar | wallet, twitter, nft, community, social | APP_TITLE, USER_NAME, WALLET_ADDRESS, TWITTER_HANDLE, HERO_IMAGE_URL |
solana-radar | Solana Ecosystem Radar | wallet, twitter, solana, ecosystem, defi | APP_TITLE, USER_NAME, WALLET_ADDRESS, HERO_IMAGE_URL |
starchild-demo | Starchild Capabilities Showcase | starchild, demo, showcase, capabilities, features | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
ui-components | UI Components Gallery | github, frontend, ui, components, design | APP_TITLE, USER_NAME, HERO_IMAGE_URL |
web3-builder | Web3 Builder Dashboard | github, wallet, web3, builder, developer | APP_TITLE, USER_NAME, GITHUB_USERNAME, WALLET_ADDRESS, HERO_IMAGE_URL |
---
5. Theme Catalog (20 themes)
Theme files are in skills/surprise-templates/themes/{id}.json.
Each theme JSON contains:
colors— CSS variable values (--bg-primary,--bg-secondary,--bg-card,--text-primary,--text-secondary,--accent-1,--accent-2,--accent-3,--border,--glow,--gradient-hero)fonts—heading,body,monofont familieschartColors— array of 6 hex colors for Chart.js
Dark Themes (13)
| ID | Name | Vibe |
|---|---|---|
cyber-neon | Cyber Neon | Cyberpunk — deep black + neon green/purple |
ocean-depth | Ocean Depth | Deep sea — dark blue + cyan/coral |
midnight-gold | Midnight Gold | Luxury finance — midnight blue + gold/amber |
aurora-borealis | Aurora Borealis | Northern lights — deep purple + aurora green/pink |
forest-canopy | Forest Canopy | Forest — dark green + moss/amber |
volcanic-ember | Volcanic Ember | Volcanic — dark gray + lava red/orange |
neon-tokyo | Neon Tokyo | Tokyo neon — pure black + neon pink/cyan |
deep-space | Deep Space | Space — space black + nebula purple/blue |
coral-reef | Coral Reef | Coral reef — dark teal + coral/gold |
industrial-steel | Industrial Steel | Industrial — steel gray + orange/white |
electric-blue | Electric Blue | Electric — dark blue + electric blue/white |
rose-gold | Rose Gold | Rose gold — dark rose + rose gold/white |
matrix-green | Matrix Green | Matrix — pure black + matrix green/dark green |
Light Themes (5)
| ID | Name | Vibe |
|---|---|---|
arctic-frost | Arctic Frost | Arctic — ice white + ice blue/silver |
lavender-dream | Lavender Dream | Lavender — light purple + lavender/rose |
desert-sand | Desert Sand | Desert — sand + ochre/sky blue |
paper-ink | Paper & Ink | Paper — paper white + ink black/red |
candy-pop | Candy Pop | Candy — pink white + candy colors/bright |
Monochrome (2)
| ID | Name | Vibe |
|---|---|---|
monochrome-noir | Monochrome Noir | Black & white + single accent color |
sunset-gradient | Sunset Gradient | Warm orange + pink-purple/gold |
Theme Selection Guidelines
- Crypto/trading dashboards →
cyber-neon,neon-tokyo,midnight-gold,deep-space - Developer tools →
matrix-green,industrial-steel,paper-ink - Productivity/utilities →
arctic-frost,lavender-dream,desert-sand - NFT/creative →
aurora-borealis,coral-reef,sunset-gradient - Finance/enterprise →
midnight-gold,monochrome-noir,rose-gold - Fun/casual →
candy-pop,volcanic-ember,ocean-depth
---
6. How to Apply a Theme
Read the theme JSON file and replace CSS variables in style.css:
# Example: applying cyber-neon theme
THEME=$(cat skills/surprise-templates/themes/cyber-neon.json)In style.css, find the :root { ... } block and replace each CSS variable value:
:root {
--bg-primary: #0a0a0f; /* from theme.colors.--bg-primary */
--bg-secondary: #12121a; /* from theme.colors.--bg-secondary */
--bg-card: rgba(18,18,26,0.85);
--text-primary: #e0e0e8;
--text-secondary: #8888a0;
--accent-1: #39ff14;
--accent-2: #bf00ff;
--accent-3: #00e5ff;
--border: rgba(57,255,20,0.15);
--glow: 0 0 20px rgba(57,255,20,0.3), 0 0 60px rgba(57,255,20,0.1);
--gradient-hero: linear-gradient(135deg, #0a0a0f 0%, #1a0a2e 50%, #0a0a0f 100%);
}Also update the font <link> in index.html:
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=DM+Sans:wght@400;500;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">And update chartColors in script.js where Chart.js datasets reference colors.
---
7. Quick Reference — End-to-End Example
Example A — User with signals
User signals: wallet connected, interests = ["defi", "yield"], tokens = ["ETH", "AAVE"]
Language: zh-CN, Avatar: https://example.com/alex.png
History: previously received "trading-intel"
1. Build candidates → score all templates against ["wallet", "defi", "yield"]
Candidates (score ≥ 2): defi-yield(3), stablecoin-yield(2), impermanent-loss(2),
funding-rate(2), dex-arb(2), portfolio-radar(2)
Exclude history: trading-intel (already seen)
2. Random select → impermanent-loss (randomly picked from 6 candidates)
3. Copy → cp -r templates/impermanent-loss/ output/surprise-me/
4. Random theme → from [midnight-gold, cyber-neon, deep-space, neon-tokyo],
randomly pick → deep-space
5. Replace placeholders:
- {{APP_TITLE}} → "Alex 的无常损失计算器" (creative, localized title)
- {{USER_NAME}} → "Alex"
- {{TRACKED_TOKENS}} → ["ETH", "AAVE"]
- {{HERO_IMAGE_URL}} → "./hero-bg.png"
6. Personalize beyond placeholders:
- Translate all UI labels to zh-CN (按钮、标题、图表标签)
- Inject avatar as profile image in header
- Adjust mock data: use realistic ETH/AAVE price ranges
7. Apply theme colors to style.css :root block
8. Add Google Fonts link for theme fonts
9. Serve → cd output/surprise-me && python3 -m http.server 8080Example B — User with no signals (fallback)
User signals: none (no wallet, no GitHub, no Twitter, no specific interests)
Language: en
1. No matching signals → use Category D (28 standalone tools) as candidate pool
2. Random select → pomodoro (randomly picked from 28 candidates)
3. Copy → cp -r templates/pomodoro/ output/surprise-me/
4. Random theme → from all 20 themes, randomly pick → lavender-dream
5. Replace placeholders:
- {{APP_TITLE}} → "Your Focus Timer" (creative title)
- {{USER_NAME}} → "Explorer"
- {{HERO_IMAGE_URL}} → "./hero-bg.png"
6. Personalize: adjust default session durations, translate if needed
7. Apply theme + fonts
8. Serve → cd output/surprise-me && python3 -m http.server 8080<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{APP_TITLE}} — AI Builder Toolkit Dashboard" />
<title>{{APP_TITLE}}</title>
<!-- Fonts: Orbitron (heading) + Exo 2 (body) + Fira Code (code) -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Exo+2:wght@300;400;500;600;700&family=Fira+Code:wght@400;500;600;700&display=swap"
rel="stylesheet"
/>
<!-- Chart.js -->
<script src="https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"></script>
<!-- GSAP + ScrollTrigger -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script>
<!-- Styles -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Noise texture overlay -->
<div class="noise-overlay" aria-hidden="true"></div>
<!-- Theme Toggle -->
<button class="theme-toggle" id="theme-toggle" type="button" aria-label="Toggle light/dark theme">
<svg class="theme-toggle__icon theme-toggle__sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="5"/><path d="M12 1v2"/><path d="M12 21v2"/><path d="M4.22 4.22l1.42 1.42"/><path d="M18.36 18.36l1.42 1.42"/><path d="M1 12h2"/><path d="M21 12h2"/><path d="M4.22 19.78l1.42-1.42"/><path d="M18.36 5.64l1.42-1.42"/>
</svg>
<svg class="theme-toggle__icon theme-toggle__moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
<!-- ==================== Terminal Hero ==================== -->
<header class="terminal-hero" role="banner">
<div class="terminal-hero__chrome">
<div class="terminal-hero__dots">
<span></span><span></span><span></span>
</div>
<div class="terminal-hero__title-bar">{{APP_TITLE}} — AI Command Center</div>
<div class="terminal-hero__actions">
<span class="terminal-hero__minimize">─</span>
<span class="terminal-hero__maximize">□</span>
<span class="terminal-hero__close">×</span>
</div>
</div>
<div class="terminal-hero__body">
<div class="terminal-hero__prompt">
<span class="terminal-hero__user">{{USER_NAME}}@ai-lab</span>
<span class="terminal-hero__separator">:</span>
<span class="terminal-hero__path">~/models</span>
<span class="terminal-hero__dollar">$</span>
</div>
<div class="terminal-hero__ticker" id="model-ticker">
<span class="terminal-hero__cursor">▊</span>
</div>
<div class="terminal-hero__stats" id="hero-stats">
<div class="terminal-hero__stat">
<span class="terminal-hero__stat-label">MODELS TRACKED</span>
<span class="terminal-hero__stat-value" id="stat-models">--</span>
</div>
<div class="terminal-hero__stat">
<span class="terminal-hero__stat-label">REPOS INDEXED</span>
<span class="terminal-hero__stat-value" id="stat-repos">--</span>
</div>
<div class="terminal-hero__stat">
<span class="terminal-hero__stat-label">PAPERS TODAY</span>
<span class="terminal-hero__stat-value" id="stat-papers">--</span>
</div>
</div>
</div>
</header>
<!-- ==================== Dashboard Grid ==================== -->
<main class="dashboard" role="main">
<div class="dashboard__grid">
<!-- Panel 1: Hot AI Repos -->
<section class="ai-panel ai-panel--repos" id="panel-repos">
<div class="ai-panel__header">
<h2 class="ai-panel__title">
<svg class="ai-panel__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4"/><path d="M9 18c-4.51 2-5-2-7-2"/></svg>
Hot AI Repos
</h2>
<span class="ai-panel__badge">GitHub</span>
</div>
<div class="ai-panel__body" id="repos-list">
<div class="ai-panel__loading">Fetching repos…</div>
</div>
</section>
<!-- Panel 2: Model Comparison -->
<section class="ai-panel ai-panel--comparison" id="panel-comparison">
<div class="ai-panel__header">
<h2 class="ai-panel__title">
<svg class="ai-panel__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2v20M2 12h20"/><circle cx="12" cy="12" r="10"/></svg>
Model Comparison
</h2>
<span class="ai-panel__badge">Live</span>
</div>
<div class="ai-panel__body">
<div class="comparison-table-wrap">
<table class="comparison-table" id="model-table">
<thead>
<tr>
<th>Model</th>
<th>Provider</th>
<th>Input $/1M</th>
<th>Output $/1M</th>
<th>Speed</th>
<th>Context</th>
</tr>
</thead>
<tbody id="model-table-body"></tbody>
</table>
</div>
</div>
</section>
<!-- Panel 3: AI Paper Trends -->
<section class="ai-panel ai-panel--papers" id="panel-papers">
<div class="ai-panel__header">
<h2 class="ai-panel__title">
<svg class="ai-panel__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>
Trending Papers
</h2>
<span class="ai-panel__badge">arXiv</span>
</div>
<div class="ai-panel__body" id="papers-list">
<div class="ai-panel__loading">Loading papers…</div>
</div>
</section>
<!-- Panel 4: Open Source Model Rankings -->
<section class="ai-panel ai-panel--rankings" id="panel-rankings">
<div class="ai-panel__header">
<h2 class="ai-panel__title">
<svg class="ai-panel__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg>
Open Source Rankings
</h2>
<span class="ai-panel__badge">HuggingFace</span>
</div>
<div class="ai-panel__body">
<canvas id="rankings-chart" height="280"></canvas>
</div>
</section>
<!-- Panel 5: API Price Calculator -->
<section class="ai-panel ai-panel--calculator" id="panel-calculator">
<div class="ai-panel__header">
<h2 class="ai-panel__title">
<svg class="ai-panel__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="4" y="2" width="16" height="20" rx="2"/><line x1="8" y1="6" x2="16" y2="6"/><line x1="8" y1="10" x2="10" y2="10"/><line x1="12" y1="10" x2="14" y2="10"/><line x1="16" y1="10" x2="16" y2="10"/><line x1="8" y1="14" x2="10" y2="14"/><line x1="12" y1="14" x2="14" y2="14"/><line x1="16" y1="14" x2="16" y2="14"/><line x1="8" y1="18" x2="16" y2="18"/></svg>
API Cost Calculator
</h2>
</div>
<div class="ai-panel__body">
<div class="calculator">
<div class="calculator__field">
<label class="calculator__label" for="calc-model">Model</label>
<select class="calculator__select" id="calc-model">
<option value="gpt4o">GPT-4o</option>
<option value="claude">Claude 3.5 Sonnet</option>
<option value="gemini">Gemini 1.5 Pro</option>
<option value="llama">Llama 3.1 405B</option>
</select>
</div>
<div class="calculator__field">
<label class="calculator__label" for="calc-input">Input Tokens (M)</label>
<input class="calculator__input" type="number" id="calc-input" value="1" min="0.1" step="0.1" />
</div>
<div class="calculator__field">
<label class="calculator__label" for="calc-output">Output Tokens (M)</label>
<input class="calculator__input" type="number" id="calc-output" value="0.5" min="0.1" step="0.1" />
</div>
<div class="calculator__result">
<span class="calculator__result-label">Estimated Cost</span>
<span class="calculator__result-value" id="calc-result">$0.00</span>
</div>
</div>
</div>
</section>
<!-- Panel 6: Latest Model Releases -->
<section class="ai-panel ai-panel--releases" id="panel-releases">
<div class="ai-panel__header">
<h2 class="ai-panel__title">
<svg class="ai-panel__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg>
Latest Releases
</h2>
<span class="ai-panel__badge">New</span>
</div>
<div class="ai-panel__body" id="releases-list">
<div class="ai-panel__loading">Loading releases…</div>
</div>
</section>
</div>
</main>
<!-- Footer -->
<footer class="site-footer">
<p class="site-footer__text">{{APP_TITLE}} · Built by <strong>{{USER_NAME}}</strong> · Data from GitHub & AI providers</p>
</footer>
<script src="script.js"></script>
</body>
</html>
{
"id": "ai-builder",
"name": "AI Builder Toolkit",
"category": "E",
"matchSignals": ["twitter", "github", "ai", "machine-learning", "developer"],
"description": "AI 构建者工具包:最新模型发布、热门 AI 仓库、模型对比、论文趋势、开源排行",
"placeholders": {
"APP_TITLE": "应用标题",
"USER_NAME": "用户名",
"GITHUB_USERNAME": "GitHub 用户名",
"HERO_IMAGE_URL": "主视觉图片 URL(可选,回退到 CSS 渐变)"
},
"apis": [
{"name": "GitHub API", "endpoint": "api.github.com", "cors": true, "rateLimit": "60/hour"},
{"name": "Built-in AI Model Data", "endpoint": "mock", "cors": true}
],
"themes": ["cyber-neon", "deep-space", "midnight-gold"],
"features": ["dark-mode", "responsive", "charts", "terminal-ui", "theme-toggle"],
"catalogIds": ["E4"]
}
/* ============================================================
AI Builder Toolkit — script.js
APIs: GitHub (search repos) + Built-in AI model data
Animation: GSAP 3 + ScrollTrigger
Entrance: D13 blur(8px) → blur(0)
============================================================ */
(function () {
'use strict';
/* ---------- Register GSAP Plugins ---------- */
gsap.registerPlugin(ScrollTrigger);
/* ---------- Reduced Motion Check ---------- */
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
/* ---------- Configuration ---------- */
const CONFIG = {
githubUsername: (() => {
const raw = '{{GITHUB_USERNAME}}';
return raw.startsWith('{{') ? 'openai' : raw;
})(),
maxRetries: 2,
retryBaseDelay: 1500,
};
const API = {
githubSearch: (q) =>
`https://api.github.com/search/repositories?q=${encodeURIComponent(q)}&sort=stars&order=desc&per_page=6`,
};
/* ---------- Mock Data: AI Models ---------- */
const AI_MODELS = [
{ name: 'GPT-4o', provider: 'OpenAI', inputPrice: 2.50, outputPrice: 10.00, speed: '~80 tok/s', context: '128K' },
{ name: 'Claude 3.5 Sonnet', provider: 'Anthropic', inputPrice: 3.00, outputPrice: 15.00, speed: '~90 tok/s', context: '200K' },
{ name: 'Gemini 1.5 Pro', provider: 'Google', inputPrice: 1.25, outputPrice: 5.00, speed: '~70 tok/s', context: '2M' },
{ name: 'Llama 3.1 405B', provider: 'Meta', inputPrice: 0.80, outputPrice: 0.80, speed: '~40 tok/s', context: '128K' },
{ name: 'Mistral Large', provider: 'Mistral', inputPrice: 2.00, outputPrice: 6.00, speed: '~65 tok/s', context: '128K' },
{ name: 'Command R+', provider: 'Cohere', inputPrice: 3.00, outputPrice: 15.00, speed: '~55 tok/s', context: '128K' },
];
const MODEL_RELEASES = [
{ name: 'GPT-4o Mini', detail: 'Smaller, faster, cheaper — 128K context', date: '2024-07-18' },
{ name: 'Claude 3.5 Sonnet', detail: 'New benchmark leader in coding tasks', date: '2024-06-20' },
{ name: 'Llama 3.1 405B', detail: 'Largest open-source model to date', date: '2024-07-23' },
{ name: 'Gemini 1.5 Flash', detail: '1M context at ultra-low cost', date: '2024-05-14' },
{ name: 'Mistral NeMo', detail: '12B params, Apache 2.0 license', date: '2024-07-18' },
{ name: 'Phi-3 Medium', detail: 'Microsoft 14B, strong reasoning', date: '2024-05-21' },
];
const TRENDING_PAPERS = [
{ title: 'Scaling Laws for Neural Language Models Revisited', category: 'cs.CL', citations: 342, date: '2024-07' },
{ title: 'Constitutional AI: Harmlessness from AI Feedback', category: 'cs.AI', citations: 891, date: '2024-06' },
{ title: 'Mixture of Experts Meets Instruction Tuning', category: 'cs.LG', citations: 256, date: '2024-07' },
{ title: 'Direct Preference Optimization: Your LM is a Reward Model', category: 'cs.CL', citations: 1203, date: '2024-05' },
{ title: 'Efficient Memory Transformers via Sparse Attention', category: 'cs.LG', citations: 178, date: '2024-07' },
{ title: 'Vision-Language Models for Autonomous Agents', category: 'cs.CV', citations: 445, date: '2024-06' },
{ title: 'Retrieval-Augmented Generation for Knowledge-Intensive Tasks', category: 'cs.IR', citations: 2100, date: '2024-04' },
];
const OPEN_SOURCE_RANKINGS = {
labels: ['Llama 3.1', 'Mistral', 'Phi-3', 'Qwen 2', 'Gemma 2', 'Command R', 'Yi-1.5', 'DeepSeek V2'],
scores: [88.7, 85.2, 82.1, 84.5, 81.3, 79.8, 80.2, 86.1],
};
const TICKER_MESSAGES = [
'> GPT-4o-mini released — 15x cheaper than GPT-4o',
'> Llama 3.1 405B now available — largest open-source LLM',
'> Claude 3.5 Sonnet tops SWE-bench coding benchmark',
'> Gemini 1.5 Flash: 1M context window at $0.075/1M tokens',
'> Mistral NeMo 12B: Apache 2.0, multilingual, 128K context',
'> DeepSeek-V2 achieves GPT-4 level at 1/10th the cost',
];
const PRICE_MAP = {
gpt4o: { input: 2.50, output: 10.00 },
claude: { input: 3.00, output: 15.00 },
gemini: { input: 1.25, output: 5.00 },
llama: { input: 0.80, output: 0.80 },
};
/* ============================================================
GSAP ANIMATION SYSTEM
============================================================ */
function animateHeroEntrance() {
if (prefersReducedMotion) {
gsap.set('.terminal-hero', { opacity: 1 });
return;
}
const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });
tl.from('.terminal-hero__chrome', { opacity: 0, y: -20, duration: 0.5 })
.from('.terminal-hero__body', { opacity: 0, y: 20, duration: 0.6 }, '-=0.2')
.from('.terminal-hero__stats .terminal-hero__stat', {
opacity: 0, y: 15, stagger: 0.1, duration: 0.4,
}, '-=0.3');
}
/** D13: blur(8px) → blur(0) entrance for panels */
function animatePanelEntrance() {
if (prefersReducedMotion) {
gsap.set('.ai-panel', { opacity: 1, filter: 'blur(0px)' });
return;
}
const panels = gsap.utils.toArray('.ai-panel');
panels.forEach((panel) => {
gsap.to(panel, {
opacity: 1,
filter: 'blur(0px)',
duration: 0.8,
ease: 'power2.out',
scrollTrigger: {
trigger: panel,
start: 'top 88%',
toggleActions: 'play none none none',
},
});
});
}
/* ============================================================
THEME TOGGLE
============================================================ */
function initThemeToggle() {
const btn = document.getElementById('theme-toggle');
if (!btn) return;
const html = document.documentElement;
const stored = localStorage.getItem('ai-builder-theme');
if (stored) html.setAttribute('data-theme', stored);
btn.addEventListener('click', () => {
const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark';
html.setAttribute('data-theme', next);
localStorage.setItem('ai-builder-theme', next);
});
}
/* ============================================================
TICKER ANIMATION
============================================================ */
function initTicker() {
const el = document.getElementById('model-ticker');
if (!el) return;
let idx = 0;
function typeMessage(msg) {
el.innerHTML = '';
let i = 0;
const cursor = document.createElement('span');
cursor.className = 'terminal-hero__cursor';
cursor.textContent = '▊';
function typeChar() {
if (i < msg.length) {
el.textContent = msg.substring(0, i + 1);
el.appendChild(cursor);
i++;
setTimeout(typeChar, 30 + Math.random() * 20);
} else {
setTimeout(() => {
idx = (idx + 1) % TICKER_MESSAGES.length;
typeMessage(TICKER_MESSAGES[idx]);
}, 4000);
}
}
typeChar();
}
typeMessage(TICKER_MESSAGES[idx]);
}
/* ============================================================
DATA FETCHING
============================================================ */
async function fetchWithRetry(url, retries = CONFIG.maxRetries) {
for (let i = 0; i <= retries; i++) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
if (i === retries) throw err;
await new Promise((r) => setTimeout(r, CONFIG.retryBaseDelay * (i + 1)));
}
}
}
/* ---------- GitHub Repos ---------- */
async function loadRepos() {
const container = document.getElementById('repos-list');
if (!container) return;
try {
const data = await fetchWithRetry(API.githubSearch('artificial intelligence machine learning'));
const repos = (data.items || []).slice(0, 6);
document.getElementById('stat-repos').textContent = data.total_count
? data.total_count.toLocaleString()
: '100K+';
container.innerHTML = repos.map((r) => {
const langColor = getLangColor(r.language);
return `
<div class="repo-card">
<a class="repo-card__name" href="${r.html_url}" target="_blank" rel="noopener">${r.full_name}</a>
<p class="repo-card__desc">${r.description || 'No description'}</p>
<div class="repo-card__meta">
${r.language ? `<span class="repo-card__meta-item"><span class="repo-card__lang-dot" style="background:${langColor}"></span>${r.language}</span>` : ''}
<span class="repo-card__meta-item">⭐ ${formatNumber(r.stargazers_count)}</span>
<span class="repo-card__meta-item">🍴 ${formatNumber(r.forks_count)}</span>
</div>
</div>`;
}).join('');
} catch {
container.innerHTML = '<div class="error-state">Failed to load repos. Check network.</div>';
}
}
/* ---------- Model Comparison Table ---------- */
function loadModelComparison() {
const tbody = document.getElementById('model-table-body');
if (!tbody) return;
document.getElementById('stat-models').textContent = AI_MODELS.length.toString();
tbody.innerHTML = AI_MODELS.map((m) => `
<tr>
<td class="model-name">${m.name}</td>
<td>${m.provider}</td>
<td class="price-highlight">$${m.inputPrice.toFixed(2)}</td>
<td class="price-highlight">$${m.outputPrice.toFixed(2)}</td>
<td>${m.speed}</td>
<td>${m.context}</td>
</tr>
`).join('');
}
/* ---------- Trending Papers ---------- */
function loadPapers() {
const container = document.getElementById('papers-list');
if (!container) return;
document.getElementById('stat-papers').textContent = TRENDING_PAPERS.length.toString();
container.innerHTML = TRENDING_PAPERS.map((p) => `
<div class="paper-item">
<div class="paper-item__title">${p.title}</div>
<div class="paper-item__meta">
<span class="paper-item__category">${p.category}</span>
<span>${p.citations} citations</span>
<span>${p.date}</span>
</div>
</div>
`).join('');
}
/* ---------- Open Source Rankings Chart ---------- */
function loadRankingsChart() {
const canvas = document.getElementById('rankings-chart');
if (!canvas) return;
const style = getComputedStyle(document.documentElement);
const accent = style.getPropertyValue('--color-accent').trim();
const textMuted = style.getPropertyValue('--color-text-muted').trim();
const border = style.getPropertyValue('--color-border').trim();
new Chart(canvas, {
type: 'bar',
data: {
labels: OPEN_SOURCE_RANKINGS.labels,
datasets: [{
label: 'MMLU Score',
data: OPEN_SOURCE_RANKINGS.scores,
backgroundColor: OPEN_SOURCE_RANKINGS.scores.map((_, i) => {
const colors = [accent, '#a78bfa', '#34d399', '#fbbf24', '#f87171', '#60a5fa', '#fb923c', '#e879f9'];
return colors[i % colors.length];
}),
borderRadius: 4,
borderSkipped: false,
}],
},
options: {
responsive: true,
maintainAspectRatio: false,
indexAxis: 'y',
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: '#1a1630',
titleFont: { family: 'Fira Code' },
bodyFont: { family: 'Fira Code' },
},
},
scales: {
x: {
min: 70,
max: 95,
grid: { color: border },
ticks: { color: textMuted, font: { family: 'Fira Code', size: 10 } },
},
y: {
grid: { display: false },
ticks: { color: textMuted, font: { family: 'Exo 2', size: 11 } },
},
},
},
});
}
/* ---------- Model Releases ---------- */
function loadReleases() {
const container = document.getElementById('releases-list');
if (!container) return;
container.innerHTML = MODEL_RELEASES.map((r) => `
<div class="release-item">
<div class="release-item__dot"></div>
<div class="release-item__content">
<div class="release-item__name">${r.name}</div>
<div class="release-item__detail">${r.detail}</div>
</div>
<div class="release-item__date">${r.date}</div>
</div>
`).join('');
}
/* ---------- API Price Calculator ---------- */
function initCalculator() {
const modelSelect = document.getElementById('calc-model');
const inputTokens = document.getElementById('calc-input');
const outputTokens = document.getElementById('calc-output');
const resultEl = document.getElementById('calc-result');
if (!modelSelect || !inputTokens || !outputTokens || !resultEl) return;
function calculate() {
const model = PRICE_MAP[modelSelect.value];
if (!model) return;
const inCost = parseFloat(inputTokens.value) * model.input;
const outCost = parseFloat(outputTokens.value) * model.output;
const total = inCost + outCost;
resultEl.textContent = `$${total.toFixed(2)}`;
}
modelSelect.addEventListener('change', calculate);
inputTokens.addEventListener('input', calculate);
outputTokens.addEventListener('input', calculate);
calculate();
}
/* ============================================================
HELPERS
============================================================ */
function formatNumber(n) {
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
return n.toString();
}
function getLangColor(lang) {
const colors = {
Python: '#3572A5', JavaScript: '#f1e05a', TypeScript: '#3178c6',
Jupyter: '#DA5B0B', Rust: '#dea584', Go: '#00ADD8',
C: '#555555', 'C++': '#f34b7d', Java: '#b07219',
Kotlin: '#A97BFF', Swift: '#F05138', Ruby: '#701516',
};
return colors[lang] || '#8b8b8b';
}
/* ============================================================
INIT
============================================================ */
function init() {
initThemeToggle();
initTicker();
loadModelComparison();
loadPapers();
loadReleases();
loadRankingsChart();
initCalculator();
loadRepos();
// Animations
animateHeroEntrance();
requestAnimationFrame(() => {
animatePanelEntrance();
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
/* ============================================================
AI Builder Toolkit — style.css
Design Dials
─────────────────────────────────────────────
Surface = 5 (Tinted Dark, deep indigo)
Accent = 4 (Pink/Magenta, #ec4899)
Type = 1 (Geometric Sans)
Family = 7 (Retro-futuristic)
─────────────────────────────────────────────
Scene: "An AI researcher in a dark lab with indigo ambient
lighting and pink neon accents, experimenting with models,
innovative and intense"
Skeleton : A9 Dashboard Grid 3-col
Hover : C25 brightness(1.08)
Entrance : D13 blur(8px) → blur(0)
Hero : H6 Terminal Header
Card : .ai-panel
Fonts : Orbitron (heading) + Exo 2 (body) + Fira Code (code)
============================================================ */
/* ---------- Fluid Type Scale (Major Third 1.25) ---------- */
:root {
--text-xs: clamp(0.64rem, 0.61rem + 0.15vw, 0.72rem);
--text-sm: clamp(0.80rem, 0.75rem + 0.24vw, 0.94rem);
--text-base: clamp(1.00rem, 0.91rem + 0.43vw, 1.25rem);
--text-lg: clamp(1.25rem, 1.11rem + 0.68vw, 1.67rem);
--text-xl: clamp(1.56rem, 1.36rem + 1.03vw, 2.22rem);
--text-2xl: clamp(1.95rem, 1.64rem + 1.56vw, 2.96rem);
--text-3xl: clamp(2.44rem, 1.97rem + 2.35vw, 3.95rem);
}
/* ---------- Fluid Spacing Scale ---------- */
:root {
--space-3xs: clamp(0.25rem, 0.23rem + 0.11vw, 0.31rem);
--space-2xs: clamp(0.50rem, 0.46rem + 0.22vw, 0.63rem);
--space-xs: clamp(0.75rem, 0.68rem + 0.33vw, 0.94rem);
--space-s: clamp(1.00rem, 0.91rem + 0.43vw, 1.25rem);
--space-m: clamp(1.50rem, 1.37rem + 0.65vw, 1.88rem);
--space-l: clamp(2.00rem, 1.83rem + 0.87vw, 2.50rem);
--space-xl: clamp(3.00rem, 2.74rem + 1.30vw, 3.75rem);
--space-2xl: clamp(4.00rem, 3.65rem + 1.74vw, 5.00rem);
--space-3xl: clamp(6.00rem, 5.48rem + 2.61vw, 7.50rem);
}
/* ---------- Dark (default) — Deep Indigo Lab ---------- */
:root,
[data-theme="dark"] {
--color-bg: #0c0a1a;
--color-surface: #151228;
--color-surface-hover: #1e1a38;
--color-border: rgba(236, 72, 153, 0.12);
--color-border-hover: rgba(236, 72, 153, 0.28);
--color-text: #e8e4f0;
--color-text-secondary: rgba(232, 228, 240, 0.55);
--color-text-muted: rgba(232, 228, 240, 0.3);
--color-accent: #ec4899;
--color-accent-dim: rgba(236, 72, 153, 0.15);
--color-terminal-bg: #0a0818;
--color-terminal-chrome: #1a1630;
--color-terminal-text: #c4b5fd;
--color-success: #34d399;
--color-warning: #fbbf24;
--color-danger: #f87171;
--chart-1: #ec4899; --chart-2: #a78bfa; --chart-3: #34d399;
--chart-4: #fbbf24; --chart-5: #f87171; --chart-6: #60a5fa;
--font-heading: 'Orbitron', sans-serif;
--font-body: 'Exo 2', sans-serif;
--font-code: 'Fira Code', monospace;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--letter-tight: -0.03em;
--letter-tighter: -0.05em;
}
/* ---------- Light Theme ---------- */
[data-theme="light"] {
--color-bg: #f0ecf8;
--color-surface: #ffffff;
--color-surface-hover: #f5f0ff;
--color-border: rgba(108, 60, 150, 0.12);
--color-border-hover: rgba(108, 60, 150, 0.25);
--color-text: #1a1030;
--color-text-secondary: rgba(26, 16, 48, 0.6);
--color-text-muted: rgba(26, 16, 48, 0.35);
--color-accent: #d6336c;
--color-accent-dim: rgba(214, 51, 108, 0.1);
--color-terminal-bg: #1a1030;
--color-terminal-chrome: #2a1e48;
--color-terminal-text: #c4b5fd;
--color-success: #059669;
--color-warning: #d97706;
--color-danger: #dc2626;
--chart-1: #d6336c; --chart-2: #7c3aed; --chart-3: #059669;
--chart-4: #d97706; --chart-5: #dc2626; --chart-6: #2563eb;
}
/* ---------- Reduced Motion ---------- */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html {
scroll-behavior: smooth;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: var(--font-body);
font-size: var(--text-base);
line-height: 1.6;
color: var(--color-text);
background: var(--color-bg);
letter-spacing: var(--letter-tight);
overflow-x: hidden;
}
/* ---------- Noise Overlay ---------- */
.noise-overlay {
position: fixed;
inset: 0;
z-index: 9999;
pointer-events: none;
opacity: 0.025;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
background-repeat: repeat;
}
/* ---------- Theme Toggle ---------- */
.theme-toggle {
position: fixed;
top: var(--space-s);
right: var(--space-s);
z-index: 1000;
width: 44px;
height: 44px;
display: grid;
place-items: center;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer;
color: var(--color-text);
transition: background-color 0.2s ease, border-color 0.2s ease;
}
.theme-toggle:hover {
background: var(--color-surface-hover);
border-color: var(--color-border-hover);
}
.theme-toggle__icon { width: 20px; height: 20px; }
[data-theme="dark"] .theme-toggle__sun { display: block; }
[data-theme="dark"] .theme-toggle__moon { display: none; }
[data-theme="light"] .theme-toggle__sun { display: none; }
[data-theme="light"] .theme-toggle__moon { display: block; }
/* ============================================================
TERMINAL HERO (H6)
============================================================ */
.terminal-hero {
margin: var(--space-l) auto;
max-width: 960px;
padding: 0 var(--space-s);
}
.terminal-hero__chrome {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-2xs) var(--space-xs);
background: var(--color-terminal-chrome);
border-radius: var(--radius-md) var(--radius-md) 0 0;
border: 1px solid var(--color-border);
border-bottom: none;
}
.terminal-hero__dots {
display: flex;
gap: 6px;
}
.terminal-hero__dots span {
width: 12px;
height: 12px;
border-radius: 50%;
}
.terminal-hero__dots span:nth-child(1) { background: var(--color-danger); }
.terminal-hero__dots span:nth-child(2) { background: var(--color-warning); }
.terminal-hero__dots span:nth-child(3) { background: var(--color-success); }
.terminal-hero__title-bar {
font-family: var(--font-code);
font-size: var(--text-xs);
color: var(--color-text-secondary);
letter-spacing: 0.05em;
text-transform: uppercase;
}
.terminal-hero__actions {
display: flex;
gap: var(--space-2xs);
font-family: var(--font-code);
font-size: var(--text-sm);
color: var(--color-text-muted);
}
.terminal-hero__body {
background: var(--color-terminal-bg);
border: 1px solid var(--color-border);
border-radius: 0 0 var(--radius-md) var(--radius-md);
padding: var(--space-m);
font-family: var(--font-code);
min-height: 200px;
}
.terminal-hero__prompt {
display: flex;
gap: 0;
font-size: var(--text-sm);
margin-bottom: var(--space-xs);
flex-wrap: wrap;
}
.terminal-hero__user { color: var(--color-accent); font-weight: 600; }
.terminal-hero__separator { color: var(--color-text-muted); }
.terminal-hero__path { color: #60a5fa; }
.terminal-hero__dollar { color: var(--color-text-secondary); margin-left: var(--space-3xs); }
.terminal-hero__ticker {
font-size: var(--text-sm);
color: var(--color-terminal-text);
margin-bottom: var(--space-m);
min-height: 1.6em;
line-height: 1.6;
}
.terminal-hero__cursor {
animation: blink 1s step-end infinite;
color: var(--color-accent);
}
@keyframes blink {
50% { opacity: 0; }
}
.terminal-hero__stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-xs);
padding-top: var(--space-s);
border-top: 1px solid var(--color-border);
}
.terminal-hero__stat {
text-align: center;
}
.terminal-hero__stat-label {
display: block;
font-size: var(--text-xs);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: var(--space-3xs);
}
.terminal-hero__stat-value {
display: block;
font-size: var(--text-xl);
font-family: var(--font-heading);
font-weight: 700;
color: var(--color-accent);
letter-spacing: var(--letter-tighter);
}
/* ============================================================
DASHBOARD GRID (A9 — 3-col)
============================================================ */
.dashboard {
max-width: 1280px;
margin: 0 auto;
padding: var(--space-l) var(--space-s) var(--space-3xl);
}
.dashboard__grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-m);
}
@media (max-width: 1024px) {
.dashboard__grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.dashboard__grid {
grid-template-columns: 1fr;
}
}
/* ============================================================
AI PANEL (.ai-panel)
============================================================ */
.ai-panel {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
min-width: 0;
transition: filter 0.2s ease, border-color 0.2s ease;
/* D13 entrance: start blurred */
filter: blur(8px);
opacity: 0;
}
/* C25 Hover: brightness(1.08) */
.ai-panel:hover {
filter: brightness(1.08);
border-color: var(--color-border-hover);
}
.ai-panel--comparison {
grid-column: span 2;
}
@media (max-width: 1024px) {
.ai-panel--comparison {
grid-column: span 1;
}
}
.ai-panel__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-s) var(--space-s) 0;
}
.ai-panel__title {
font-family: var(--font-heading);
font-size: var(--text-sm);
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-text);
display: flex;
align-items: center;
gap: var(--space-2xs);
}
.ai-panel__icon {
width: 18px;
height: 18px;
flex-shrink: 0;
color: var(--color-accent);
}
.ai-panel__badge {
font-family: var(--font-code);
font-size: var(--text-xs);
padding: 2px 8px;
border-radius: var(--radius-sm);
background: var(--color-accent-dim);
color: var(--color-accent);
font-weight: 500;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.ai-panel__body {
padding: var(--space-s);
}
.ai-panel__loading {
font-family: var(--font-code);
font-size: var(--text-sm);
color: var(--color-text-muted);
text-align: center;
padding: var(--space-l) 0;
}
/* ---------- Repo Card ---------- */
.repo-card {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
padding: var(--space-xs);
border-radius: var(--radius-sm);
border: 1px solid var(--color-border);
margin-bottom: var(--space-2xs);
transition: background-color 0.2s ease;
}
.repo-card:hover {
background: var(--color-surface-hover);
}
.repo-card__name {
font-family: var(--font-heading);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-accent);
letter-spacing: var(--letter-tight);
text-decoration: none;
}
.repo-card__name:hover { text-decoration: underline; }
.repo-card__desc {
font-size: var(--text-xs);
color: var(--color-text-secondary);
line-height: 1.4;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.repo-card__meta {
display: flex;
gap: var(--space-s);
font-family: var(--font-code);
font-size: var(--text-xs);
color: var(--color-text-muted);
}
.repo-card__meta-item {
display: flex;
align-items: center;
gap: 4px;
}
.repo-card__lang-dot {
width: 8px;
height: 8px;
border-radius: 50%;
display: inline-block;
}
/* ---------- Comparison Table ---------- */
.comparison-table-wrap {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.comparison-table {
width: 100%;
border-collapse: collapse;
font-family: var(--font-code);
font-size: var(--text-xs);
}
.comparison-table th {
text-align: left;
padding: var(--space-2xs) var(--space-xs);
font-weight: 600;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: 0.08em;
border-bottom: 1px solid var(--color-border);
white-space: nowrap;
}
.comparison-table td {
padding: var(--space-2xs) var(--space-xs);
border-bottom: 1px solid var(--color-border);
color: var(--color-text-secondary);
white-space: nowrap;
}
.comparison-table tr:hover td {
background: var(--color-surface-hover);
}
.comparison-table .model-name {
color: var(--color-text);
font-weight: 600;
}
.comparison-table .price-highlight {
color: var(--color-accent);
font-weight: 600;
}
/* ---------- Paper Item ---------- */
.paper-item {
padding: var(--space-xs);
border-bottom: 1px solid var(--color-border);
}
.paper-item:last-child { border-bottom: none; }
.paper-item__title {
font-family: var(--font-body);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text);
margin-bottom: var(--space-3xs);
line-height: 1.4;
}
.paper-item__meta {
font-family: var(--font-code);
font-size: var(--text-xs);
color: var(--color-text-muted);
display: flex;
gap: var(--space-s);
}
.paper-item__category {
color: var(--color-accent);
font-weight: 500;
}
/* ---------- Calculator ---------- */
.calculator {
display: flex;
flex-direction: column;
gap: var(--space-s);
}
.calculator__field {
display: flex;
flex-direction: column;
gap: var(--space-3xs);
}
.calculator__label {
font-family: var(--font-heading);
font-size: var(--text-xs);
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-muted);
}
.calculator__select,
.calculator__input {
font-family: var(--font-code);
font-size: var(--text-sm);
padding: var(--space-2xs) var(--space-xs);
background: var(--color-bg);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
color: var(--color-text);
outline: none;
transition: border-color 0.2s ease;
}
.calculator__select:focus,
.calculator__input:focus {
border-color: var(--color-accent);
}
.calculator__result {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-s);
background: var(--color-accent-dim);
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
}
.calculator__result-label {
font-family: var(--font-heading);
font-size: var(--text-xs);
text-transform: uppercase;
letter-spacing: 0.08em;
color: var(--color-text-secondary);
}
.calculator__result-value {
font-family: var(--font-heading);
font-size: var(--text-xl);
font-weight: 700;
color: var(--color-accent);
letter-spacing: var(--letter-tighter);
}
/* ---------- Release Item ---------- */
.release-item {
display: flex;
align-items: flex-start;
gap: var(--space-xs);
padding: var(--space-xs);
border-bottom: 1px solid var(--color-border);
}
.release-item:last-child { border-bottom: none; }
.release-item__dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-accent);
margin-top: 6px;
flex-shrink: 0;
}
.release-item__content {
flex: 1;
}
.release-item__name {
font-family: var(--font-heading);
font-size: var(--text-sm);
font-weight: 600;
color: var(--color-text);
letter-spacing: var(--letter-tight);
}
.release-item__detail {
font-size: var(--text-xs);
color: var(--color-text-secondary);
margin-top: 2px;
}
.release-item__date {
font-family: var(--font-code);
font-size: var(--text-xs);
color: var(--color-text-muted);
white-space: nowrap;
}
/* ---------- Error State ---------- */
.error-state {
text-align: center;
padding: var(--space-m);
color: var(--color-text-muted);
font-size: var(--text-sm);
}
.error-state__icon {
width: 32px;
height: 32px;
margin: 0 auto var(--space-xs);
display: block;
color: var(--color-danger);
}
/* ---------- Footer ---------- */
.site-footer {
text-align: center;
padding: var(--space-l) var(--space-s);
border-top: 1px solid var(--color-border);
}
.site-footer__text {
font-family: var(--font-code);
font-size: var(--text-xs);
color: var(--color-text-muted);
letter-spacing: 0.02em;
}
/* ---------- Chart Canvas ---------- */
#rankings-chart {
width: 100% !important;
max-height: 280px;
}
/* --- Global select arrow spacing fix --- */
select {
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23aaa' d='M6 8L1 3h10z'/%3E%3C/svg%3E") !important;
background-repeat: no-repeat !important;
background-position: right 12px center !important;
background-size: 12px !important;
padding-right: 36px !important;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{APP_TITLE}} — AI Research Digest" />
<title>{{APP_TITLE}}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&family=Figtree:wght@400;500;600;700&family=Fira+Code:wght@400;500;600&display=swap" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="noise-overlay" aria-hidden="true"></div>
<button class="theme-toggle" id="theme-toggle" type="button" aria-label="Toggle light/dark theme">
<svg class="theme-toggle__icon theme-toggle__sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="5"/><path d="M12 1v2"/><path d="M12 21v2"/><path d="M4.22 4.22l1.42 1.42"/><path d="M18.36 18.36l1.42 1.42"/><path d="M1 12h2"/><path d="M21 12h2"/><path d="M4.22 19.78l1.42-1.42"/><path d="M18.36 5.64l1.42-1.42"/></svg>
<svg class="theme-toggle__icon theme-toggle__moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
<!-- Hero (H10 Inline) -->
<header class="hero" role="banner">
<div class="container hero__container">
<p class="hero__label">AI Research Digest</p>
<h1 class="hero__title">Latest AI<br/>Research Papers</h1>
<p class="hero__subtitle">Curated papers across NLP, Computer Vision, Reinforcement Learning, and Multimodal AI.</p>
</div>
</header>
<!-- Main Layout (A6 Sidebar+Content) -->
<section class="section" id="main">
<div class="container">
<div class="layout-sidebar">
<aside class="sidebar" id="sidebar">
<h3 class="sidebar__title">Categories</h3>
<div class="sidebar__list" id="sidebar-list"></div>
<h3 class="sidebar__title sidebar__title--mt">Model Releases</h3>
<div class="model-list" id="model-list"></div>
</aside>
<main class="content">
<div class="paper-grid" id="paper-grid"></div>
</main>
</div>
</div>
</section>
<footer class="footer"><div class="container"><p class="footer__text">AI Research Digest — Built with mock data</p></div></footer>
<script src="script.js"></script>
</body>
</html>
{
"id": "ai-research",
"name": "AI Research Digest",
"category": "C",
"matchSignals": ["twitter", "ai", "research", "papers", "models"],
"description": "AI 研究摘要:分类侧边栏、论文卡片、模型发布列表、研究趋势",
"placeholders": {
"APP_TITLE": "应用标题",
"USER_NAME": "用户名(可选)",
"RESEARCH_AREAS": "关注领域(如 NLP, CV, RL, Multimodal)"
},
"apis": [],
"themes": ["pure-white-violet", "lavender-ink", "deep-violet"],
"features": ["dark-mode", "responsive", "sidebar", "categories", "paper-cards"],
"catalogIds": ["C16"]
}
/* ============================================================
AI Research Digest — script.js
Mock data · GSAP 3 + ScrollTrigger
============================================================ */
(function () {
'use strict';
gsap.registerPlugin(ScrollTrigger);
var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
var CATEGORIES = [
{ id: 'all', name: 'All Papers', count: 12 },
{ id: 'nlp', name: 'NLP', count: 4 },
{ id: 'cv', name: 'Computer Vision', count: 3 },
{ id: 'rl', name: 'Reinforcement Learning', count: 2 },
{ id: 'multimodal', name: 'Multimodal', count: 3 }
];
var PAPERS = [
{ title: 'Scaling Laws for Neural Language Models: Beyond Chinchilla', abstract: 'We revisit compute-optimal training and find that data quality scaling laws diverge from Chinchilla predictions at the 1T parameter scale. New efficiency frontiers emerge with mixture-of-experts architectures.', authors: 'Chen et al.', date: 'Jun 2025', category: 'nlp' },
{ title: 'DINOv3: Self-Supervised Vision Transformers with Adaptive Masking', abstract: 'Building on DINOv2, we introduce adaptive masking strategies that improve downstream task performance by 12% on ImageNet-1K while reducing pre-training compute by 30%.', authors: 'Oquab et al.', date: 'May 2025', category: 'cv' },
{ title: 'Constitutional AI 2.0: Scalable Alignment Through Debate', abstract: 'We extend constitutional AI with multi-agent debate protocols, achieving RLHF-level alignment with 5x less human feedback. The approach scales to 100B+ parameter models.', authors: 'Bai et al.', date: 'Jun 2025', category: 'nlp' },
{ title: 'World Models for Robotic Manipulation: A Unified Framework', abstract: 'We present a unified world model architecture that enables zero-shot transfer of manipulation skills across robot embodiments, achieving 89% success rate on 50 diverse tasks.', authors: 'Brohan et al.', date: 'May 2025', category: 'rl' },
{ title: 'Gemini Ultra 2: Natively Multimodal Reasoning', abstract: 'We introduce Gemini Ultra 2 with native multimodal chain-of-thought reasoning. The model achieves SOTA on 15 benchmarks spanning text, image, video, and audio understanding.', authors: 'Google DeepMind', date: 'Jun 2025', category: 'multimodal' },
{ title: 'Efficient Attention: Linear Complexity Transformers at Scale', abstract: 'We propose a new attention mechanism achieving O(n) complexity while maintaining 98% of standard attention quality. Enables 1M+ context windows on consumer hardware.', authors: 'Dao et al.', date: 'May 2025', category: 'nlp' },
{ title: 'SegmentAnything 2: Real-Time Video Segmentation', abstract: 'SAM2 extends zero-shot segmentation to video with temporal consistency. Processes 4K video at 30fps with state-of-the-art accuracy on DAVIS and YouTube-VOS benchmarks.', authors: 'Kirillov et al.', date: 'Jun 2025', category: 'cv' },
{ title: 'RLHF Without Human Feedback: Self-Play Alignment', abstract: 'We demonstrate that language models can align themselves through self-play, eliminating the need for human preference data. Results match RLHF on TruthfulQA and HHH benchmarks.', authors: 'Burns et al.', date: 'May 2025', category: 'rl' },
{ title: 'Unified Vision-Language Planning for Embodied Agents', abstract: 'A single model that can see, reason, and act in both simulated and real environments. Achieves 73% success on ALFRED benchmark without task-specific fine-tuning.', authors: 'Reed et al.', date: 'Jun 2025', category: 'multimodal' },
{ title: '3D Scene Understanding from Single Images with Diffusion Models', abstract: 'We leverage diffusion model priors for monocular 3D scene reconstruction, achieving photorealistic novel view synthesis from a single input image.', authors: 'Liu et al.', date: 'May 2025', category: 'cv' },
{ title: 'Sparse Mixture of Experts at 10T Scale', abstract: 'We train a 10 trillion parameter sparse MoE model with only 200B active parameters per token. The model achieves new SOTA on MMLU, HellaSwag, and ARC benchmarks.', authors: 'Fedus et al.', date: 'Jun 2025', category: 'nlp' },
{ title: 'AudioPalm 2: Universal Audio Understanding and Generation', abstract: 'A single model for speech recognition, translation, synthesis, and music generation. Supports 100+ languages with near-human quality on MOS evaluations.', authors: 'Rubenstein et al.', date: 'Jun 2025', category: 'multimodal' }
];
var MODELS = [
{ name: 'GPT-5', org: 'OpenAI', date: 'Jun 2025' },
{ name: 'Claude 4', org: 'Anthropic', date: 'May 2025' },
{ name: 'Gemini Ultra 2', org: 'Google', date: 'Jun 2025' },
{ name: 'Llama 4', org: 'Meta', date: 'May 2025' },
{ name: 'Mistral Large 3', org: 'Mistral AI', date: 'Jun 2025' }
];
var activeCategory = 'all';
var $ = function (sel) { return document.querySelector(sel); };
function initTheme() {
var saved = localStorage.getItem('ai-research-theme');
if (saved === 'dark') document.documentElement.setAttribute('data-theme', 'dark');
}
initTheme();
$('#theme-toggle').addEventListener('click', function () {
var isDark = document.documentElement.getAttribute('data-theme') === 'dark';
document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
localStorage.setItem('ai-research-theme', isDark ? 'light' : 'dark');
});
function renderSidebar() {
var list = $('#sidebar-list');
list.innerHTML = CATEGORIES.map(function (c) {
return '<div class="sidebar__item ' + (c.id === activeCategory ? 'active' : '') + '" data-cat="' + c.id + '">' + c.name + '<span class="sidebar__item-count">' + c.count + '</span></div>';
}).join('');
list.querySelectorAll('.sidebar__item').forEach(function (item) {
item.addEventListener('click', function () {
activeCategory = item.dataset.cat;
renderSidebar();
renderPapers();
});
});
}
function renderModels() {
var el = $('#model-list');
el.innerHTML = MODELS.map(function (m) {
return '<div class="model-item"><div class="model-item__name">' + m.name + '</div><div class="model-item__org">' + m.org + '</div><div class="model-item__date">' + m.date + '</div></div>';
}).join('');
}
function renderPapers() {
var items = activeCategory === 'all' ? PAPERS : PAPERS.filter(function (p) { return p.category === activeCategory; });
var grid = $('#paper-grid');
grid.innerHTML = items.map(function (p) {
return '<div class="paper-card"><div class="paper-card__category">' + p.category.toUpperCase() + '</div><div class="paper-card__title">' + p.title + '</div><div class="paper-card__abstract">' + p.abstract + '</div><div class="paper-card__meta"><span class="paper-card__authors">' + p.authors + '</span><span class="paper-card__date">' + p.date + '</span></div></div>';
}).join('');
if (!prefersReducedMotion) {
gsap.from('.paper-card', { opacity: 0, y: 20, duration: 0.4, stagger: 0.06, ease: 'power3.out', clearProps: 'opacity,transform' });
}
}
function initAnimations() {
if (prefersReducedMotion) return;
var heroTl = gsap.timeline({ defaults: { ease: 'power3.out' } });
heroTl.from('.hero__label', { opacity: 0, y: 20, duration: 0.5 })
.from('.hero__title', { opacity: 0, y: 30, duration: 0.7 }, '-=0.3')
.from('.hero__subtitle', { opacity: 0, y: 20, duration: 0.5 }, '-=0.3');
gsap.from('.sidebar__item', {
scrollTrigger: { trigger: '#main', start: 'top 80%', once: true },
opacity: 0, x: -20, duration: 0.3, stagger: 0.05, ease: 'power3.out'
});
gsap.from('.model-item', {
scrollTrigger: { trigger: '#main', start: 'top 70%', once: true },
opacity: 0, x: -20, duration: 0.3, stagger: 0.06, ease: 'power3.out'
});
}
function init() {
renderSidebar();
renderModels();
renderPapers();
initAnimations();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
/* ============================================================
AI Research Digest — style.css
Design Dials
─────────────────────────────────────────
Surface : 2 — Pure White
Skeleton : A6 — Sidebar + Content
Hover : C7 — border glow
Entrance : D4 — stagger
Hero : H10 — Inline
Accent : #7c3aed (violet)
Fonts : Outfit (display) · Figtree (body) · Fira Code (mono)
Radius : 10px max
─────────────────────────────────────────
Scene (dark): "A research library at dusk — violet-lit
screens display neural network diagrams, stacks of
arxiv papers glow softly, a holographic model
architecture rotates above the desk."
============================================================ */
:root {
--text-xs: clamp(0.64rem, 0.61rem + 0.15vw, 0.72rem);
--text-sm: clamp(0.80rem, 0.75rem + 0.24vw, 0.94rem);
--text-base: clamp(1.00rem, 0.91rem + 0.43vw, 1.25rem);
--text-lg: clamp(1.25rem, 1.11rem + 0.68vw, 1.67rem);
--text-xl: clamp(1.56rem, 1.36rem + 1.03vw, 2.22rem);
--text-2xl: clamp(1.95rem, 1.64rem + 1.56vw, 2.96rem);
--text-3xl: clamp(2.44rem, 1.97rem + 2.35vw, 3.95rem);
}
:root {
--space-3xs: clamp(0.25rem, 0.23rem + 0.11vw, 0.31rem);
--space-2xs: clamp(0.50rem, 0.46rem + 0.22vw, 0.63rem);
--space-xs: clamp(0.75rem, 0.68rem + 0.33vw, 0.94rem);
--space-s: clamp(1.00rem, 0.91rem + 0.43vw, 1.25rem);
--space-m: clamp(1.50rem, 1.37rem + 0.65vw, 1.88rem);
--space-l: clamp(2.00rem, 1.83rem + 0.87vw, 2.50rem);
--space-xl: clamp(3.00rem, 2.74rem + 1.30vw, 3.75rem);
--space-2xl: clamp(4.00rem, 3.65rem + 1.74vw, 5.00rem);
--space-3xl: clamp(6.00rem, 5.48rem + 2.61vw, 7.50rem);
}
/* Light (default) — Pure White */
:root {
--color-bg: #ffffff;
--color-surface: #f9f9fb;
--color-surface-hover: #f0f0f5;
--color-border: #e5e5ea;
--color-border-hover: #d0d0d8;
--color-text: #111118;
--color-text-secondary: #555560;
--color-text-muted: #999;
--color-accent: #7c3aed;
--color-accent-soft: rgba(124, 58, 237, 0.08);
--color-success: #16a34a;
--color-danger: #dc2626;
--skeleton-a: rgba(0, 0, 0, 0.03);
--skeleton-b: rgba(0, 0, 0, 0.07);
--font-display: 'Outfit', system-ui, sans-serif;
--font-body: 'Figtree', system-ui, sans-serif;
--font-mono: 'Fira Code', 'SF Mono', monospace;
--radius: 10px;
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
--glow-color: rgba(124, 58, 237, 0.25);
}
/* Dark — Deep Violet */
[data-theme="dark"] {
--color-bg: #0e0b18;
--color-surface: #181425;
--color-surface-hover: #221e32;
--color-border: rgba(124, 58, 237, 0.15);
--color-border-hover: rgba(124, 58, 237, 0.30);
--color-text: #e8e4f0;
--color-text-secondary: rgba(232, 228, 240, 0.60);
--color-text-muted: rgba(232, 228, 240, 0.35);
--color-accent: #a78bfa;
--color-accent-soft: rgba(167, 139, 250, 0.10);
--skeleton-a: rgba(255, 255, 255, 0.03);
--skeleton-b: rgba(255, 255, 255, 0.07);
--glow-color: rgba(167, 139, 250, 0.30);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-family: var(--font-body); font-size: 100%; line-height: 1.6; color: var(--color-text); background: var(--color-bg); -webkit-font-smoothing: antialiased; scroll-behavior: smooth; }
body { min-height: 100dvh; overflow-x: hidden; }
.noise-overlay { position: fixed; inset: 0; z-index: 9999; pointer-events: none; opacity: 0.02; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); background-repeat: repeat; }
.container { width: 100%; max-width: 1100px; margin: 0 auto; padding: 0 var(--space-m); }
.theme-toggle { position: fixed; top: var(--space-m); right: var(--space-m); z-index: 100; width: 44px; height: 44px; display: grid; place-items: center; background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); cursor: pointer; transition: background 0.3s var(--ease-out); }
.theme-toggle:hover { background: var(--color-surface-hover); }
.theme-toggle__icon { width: 20px; height: 20px; color: var(--color-text); }
.theme-toggle__moon { display: none; }
[data-theme="dark"] .theme-toggle__sun { display: none; }
[data-theme="dark"] .theme-toggle__moon { display: block; }
/* Hero (H10 Inline) */
.hero { padding: var(--space-2xl) 0 var(--space-xl); }
.hero__label { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-accent); margin-bottom: var(--space-s); }
.hero__title { font-family: var(--font-display); font-size: var(--text-3xl); font-weight: 700; letter-spacing: -0.03em; line-height: 1.1; margin-bottom: var(--space-s); }
.hero__subtitle { font-size: var(--text-lg); color: var(--color-text-secondary); max-width: 540px; }
.section { padding: var(--space-l) 0; }
/* Layout (A6 Sidebar+Content) */
.layout-sidebar { display: grid; grid-template-columns: 220px 1fr; gap: var(--space-l); }
/* Sidebar */
.sidebar { position: sticky; top: var(--space-m); align-self: start; }
.sidebar__title { font-family: var(--font-display); font-size: var(--text-sm); font-weight: 600; letter-spacing: -0.01em; margin-bottom: var(--space-xs); color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.sidebar__title--mt { margin-top: var(--space-l); }
.sidebar__list { display: flex; flex-direction: column; gap: 0; }
.sidebar__item { padding: var(--space-2xs) var(--space-xs); border-radius: 6px; font-size: var(--text-sm); font-weight: 500; color: var(--color-text-secondary); cursor: pointer; transition: background 0.2s var(--ease-out), color 0.2s var(--ease-out); }
.sidebar__item:hover { background: var(--color-surface-hover); color: var(--color-text); }
.sidebar__item.active { background: var(--color-accent-soft); color: var(--color-accent); font-weight: 600; }
.sidebar__item-count { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-text-muted); margin-left: var(--space-2xs); }
/* Model List */
.model-list { display: flex; flex-direction: column; gap: var(--space-2xs); }
.model-item { padding: var(--space-2xs) var(--space-xs); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: 6px; transition: border-color 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out); }
.model-item:hover { border-color: var(--color-accent); box-shadow: 0 0 12px var(--glow-color); }
.model-item__name { font-family: var(--font-display); font-size: var(--text-sm); font-weight: 600; }
.model-item__org { font-size: var(--text-xs); color: var(--color-text-muted); }
.model-item__date { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--color-accent); }
/* Paper Grid */
.paper-grid { display: flex; flex-direction: column; gap: var(--space-s); }
.paper-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius); padding: var(--space-m); transition: border-color 0.3s var(--ease-out), box-shadow 0.3s var(--ease-out); }
.paper-card:hover { border-color: var(--color-accent); box-shadow: 0 0 16px var(--glow-color); }
.paper-card__category { font-family: var(--font-mono); font-size: var(--text-xs); letter-spacing: 0.06em; text-transform: uppercase; color: var(--color-accent); margin-bottom: var(--space-2xs); font-weight: 600; }
.paper-card__title { font-family: var(--font-display); font-size: var(--text-base); font-weight: 700; letter-spacing: -0.01em; margin-bottom: var(--space-2xs); line-height: 1.35; }
.paper-card__abstract { font-size: var(--text-sm); color: var(--color-text-secondary); line-height: 1.6; margin-bottom: var(--space-xs); }
.paper-card__meta { display: flex; gap: var(--space-s); flex-wrap: wrap; font-size: var(--text-xs); color: var(--color-text-muted); }
.paper-card__authors { font-weight: 500; }
.paper-card__date { font-family: var(--font-mono); }
.footer { padding: var(--space-xl) 0; text-align: center; }
.footer__text { font-size: var(--text-sm); color: var(--color-text-muted); }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
@media (max-width: 768px) { .layout-sidebar { grid-template-columns: 1fr; } .sidebar { position: static; } }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{APP_TITLE}} — Airdrop eligibility checker & tracker" />
<title>{{APP_TITLE}}</title>
<!-- Fonts: Clash Display (headings) + Switzer (body) + Fira Code (data) -->
<link rel="preconnect" href="https://api.fontshare.com" />
<link
href="https://api.fontshare.com/v2/css?f[]=clash-display@400;500;600;700&f[]=switzer@400;500;600&display=swap"
rel="stylesheet"
/>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500;600;700&display=swap" rel="stylesheet" />
<!-- GSAP + ScrollTrigger -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script>
<!-- Styles -->
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- Noise texture overlay -->
<div class="noise-overlay" aria-hidden="true"></div>
<!-- ==================== Theme Toggle ==================== -->
<button class="theme-toggle" id="theme-toggle" type="button" aria-label="Toggle light/dark theme">
<svg class="theme-toggle__icon theme-toggle__sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="5"/><path d="M12 1v2"/><path d="M12 21v2"/><path d="M4.22 4.22l1.42 1.42"/><path d="M18.36 18.36l1.42 1.42"/><path d="M1 12h2"/><path d="M21 12h2"/><path d="M4.22 19.78l1.42-1.42"/><path d="M18.36 5.64l1.42-1.42"/>
</svg>
<svg class="theme-toggle__icon theme-toggle__moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
</button>
<!-- ==================== H15 Score Hero ==================== -->
<header class="hero" role="banner">
<div class="hero__bg-image" aria-hidden="true"></div>
<div class="hero__crosshatch" aria-hidden="true"></div>
<div class="container hero__container">
<div class="hero__content">
<div class="hero__status-bar">
<div class="hero__pulse">
<span class="hero__pulse-dot" aria-hidden="true"></span>
<span>SCANNING</span>
</div>
<time class="hero__time" id="hero-time" datetime="">Loading...</time>
</div>
<p class="hero__eyebrow">Welcome back, <strong>{{USER_NAME}}</strong></p>
<h1 class="hero__title">{{APP_TITLE}}</h1>
<!-- Score Hero: large circular score as core visual -->
<div class="hero__score-block">
<div class="hero__score-ring" id="hero-score-ring">
<svg class="hero__score-svg" viewBox="0 0 120 120" aria-hidden="true">
<circle class="hero__score-track" cx="60" cy="60" r="52" />
<circle class="hero__score-fill" id="hero-score-arc" cx="60" cy="60" r="52" />
</svg>
<div class="hero__score-inner">
<span class="hero__score-value" id="hero-score">--</span>
<span class="hero__score-label">SCORE</span>
</div>
</div>
<div class="hero__score-stats">
<div class="hero__stat">
<span class="hero__stat-value text-up" id="hero-confirmed">--</span>
<span class="hero__stat-label">Confirmed</span>
</div>
<div class="hero__stat">
<span class="hero__stat-value" id="hero-pending">--</span>
<span class="hero__stat-label">Pending</span>
</div>
<div class="hero__stat">
<span class="hero__stat-value text-down" id="hero-expired">--</span>
<span class="hero__stat-label">Expired</span>
</div>
</div>
</div>
</div>
</div>
</header>
<!-- ==================== A15 Checklist Layout ==================== -->
<main class="dashboard" role="main">
<div class="container">
<!-- Checklist Layout: left progress + right details -->
<div class="checklist-layout">
<!-- Left: Progress / General Checklist -->
<aside class="checklist-sidebar">
<section class="check-item-group" aria-label="Eligibility Checklist">
<div class="check-item-group__header">
<h2 class="check-item-group__title">
<svg class="check-item-group__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>
</svg>
General Eligibility
</h2>
<span class="check-item-group__badge" id="checklist-progress">--</span>
</div>
<div class="checklist" id="checklist">
<!-- Populated by script.js -->
</div>
</section>
<section class="check-item-group" aria-label="Boost Your Eligibility">
<div class="check-item-group__header">
<h2 class="check-item-group__title">
<svg class="check-item-group__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
Boost Actions
</h2>
<span class="check-item-group__badge">Actions</span>
</div>
<div class="actions-list" id="actions-list">
<!-- Populated by script.js -->
</div>
</section>
</aside>
<!-- Right: Airdrop Details -->
<div class="checklist-main">
<section class="check-item-group" aria-label="Airdrop Opportunities">
<div class="check-item-group__header">
<h2 class="check-item-group__title">
<svg class="check-item-group__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
</svg>
Airdrop Opportunities
</h2>
<div class="check-item-group__badges">
<span class="check-item-group__badge check-item-group__badge--live">
<span class="check-item-group__badge-dot"></span>
LIVE
</span>
<span class="check-item-group__badge" id="airdrop-count">--</span>
</div>
</div>
<div class="airdrop-list" id="airdrop-list">
<!-- Populated by script.js -->
</div>
</section>
<section class="check-item-group" aria-label="Claimed History">
<div class="check-item-group__header">
<h2 class="check-item-group__title">
<svg class="check-item-group__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="1" y="4" width="22" height="16" rx="2" ry="2"/><line x1="1" y1="10" x2="23" y2="10"/>
</svg>
Claimed History
</h2>
<span class="check-item-group__badge">Past</span>
</div>
<div class="claimed-list" id="claimed-list">
<!-- Populated by script.js -->
</div>
</section>
</div>
</div>
</div>
</main>
<!-- ==================== Footer ==================== -->
<footer class="footer" role="contentinfo">
<div class="container footer__container">
<p class="footer__text">
Airdrop data is simulated for demonstration • Always verify eligibility on official project sites
</p>
<p class="footer__text footer__text--mono">Wallet: {{WALLET_ADDRESS}}</p>
</div>
</footer>
<!-- Script -->
<script src="script.js"></script>
</body>
</html>
{
"id": "airdrop-checker",
"name": "Airdrop Eligibility Checker",
"category": "A",
"matchSignals": ["wallet", "airdrop", "eligibility", "claim"],
"description": "检查钱包对热门空投的资格状态",
"placeholders": {
"APP_TITLE": "应用标题",
"USER_NAME": "用户名",
"WALLET_ADDRESS": "钱包地址(可选)",
"HERO_IMAGE_URL": "主视觉图片 URL(可选,回退到 CSS 渐变)"
},
"apis": [
{"name": "Built-in Mock Data", "endpoint": "N/A — embedded airdrop eligibility data", "cors": true}
],
"themes": ["cyber-neon", "electric-blue", "neon-tokyo", "matrix-green", "coral-reef"],
"features": ["dark-mode", "responsive", "eligibility-check", "mock-data"],
"catalogIds": ["A7"]
}
/* ============================================================
Airdrop Eligibility Checker — script.js
Layout: A15 Checklist + H15 Score Hero
Entrance: D11 rotate(-2deg) → rotate(0)
APIs: All mock data (airdrop eligibility APIs require auth)
Animation: GSAP 3 + ScrollTrigger (CDN)
============================================================ */
(function () {
'use strict';
gsap.registerPlugin(ScrollTrigger);
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const ICON_ALERT = '<svg class="error-state__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>';
const ICON_CHECK = '<svg class="check-item__icon check-item__icon--pass" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>';
const ICON_X = '<svg class="check-item__icon check-item__icon--fail" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>';
/* ============================================================
MOCK DATA
============================================================ */
const AIRDROPS = [
{ id: 'layerzero-s2', name: 'LayerZero Season 2', symbol: 'ZRO', status: 'pending', estValue: '$420-$1,200', deadline: '2025-09-30', conditions: [
{ label: 'Bridge 5+ times via LayerZero', met: true, value: '12 txns' },
{ label: 'Use 3+ supported chains', met: true, value: '5 chains' },
{ label: 'Volume > $1,000', met: true, value: '$3,450' },
{ label: 'Hold ZRO tokens', met: false, value: '0 ZRO' },
]},
{ id: 'zksync-s2', name: 'zkSync Season 2', symbol: 'ZK', status: 'upcoming', estValue: '$300-$800', deadline: '2025-12-31', conditions: [
{ label: '10+ transactions on zkSync Era', met: true, value: '24 txns' },
{ label: 'Interact with 5+ dApps', met: true, value: '7 dApps' },
{ label: 'Bridge to zkSync Era', met: true, value: '3 bridges' },
{ label: 'Provide liquidity on DEX', met: false, value: 'None' },
{ label: 'Hold position > 90 days', met: true, value: '142 days' },
]},
{ id: 'scroll-s1', name: 'Scroll Airdrop', symbol: 'SCR', status: 'confirmed', estValue: '$180-$500', deadline: '2025-08-15', conditions: [
{ label: 'Bridge to Scroll mainnet', met: true, value: '2 bridges' },
{ label: '5+ transactions', met: true, value: '18 txns' },
{ label: 'Interact with 3+ protocols', met: true, value: '5 protocols' },
]},
{ id: 'monad', name: 'Monad Testnet', symbol: 'MON', status: 'pending', estValue: '$500-$2,000', deadline: '2026-03-31', conditions: [
{ label: 'Join Monad testnet', met: true, value: 'Active' },
{ label: 'Complete 20+ test txns', met: false, value: '8 txns' },
{ label: 'Participate in community', met: true, value: 'Discord L3' },
{ label: 'Deploy test contract', met: false, value: 'Not done' },
]},
{ id: 'berachain', name: 'Berachain', symbol: 'BERA', status: 'pending', estValue: '$600-$1,500', deadline: '2026-06-30', conditions: [
{ label: 'Use Berachain testnet', met: true, value: 'Active' },
{ label: 'Provide liquidity in BEX', met: false, value: 'None' },
{ label: 'Mint Bong Bears NFT', met: false, value: 'Not held' },
{ label: 'Delegate BGT tokens', met: false, value: '0 BGT' },
]},
{ id: 'eigenlayer-s2', name: 'EigenLayer S2', symbol: 'EIGEN', status: 'confirmed', estValue: '$250-$700', deadline: '2025-07-31', conditions: [
{ label: 'Restake ETH/LST', met: true, value: '2.5 ETH' },
{ label: 'Hold for 30+ days', met: true, value: '95 days' },
]},
{ id: 'starknet-provisions', name: 'Starknet Provisions', symbol: 'STRK', status: 'expired', estValue: '$150-$400', deadline: '2025-03-15', conditions: [
{ label: 'Early Ethereum user', met: true, value: 'Qualified' },
{ label: 'Claim before deadline', met: false, value: 'Missed' },
]},
{ id: 'celestia-genesis', name: 'Celestia Genesis', symbol: 'TIA', status: 'expired', estValue: '$800-$2,500', deadline: '2024-10-31', conditions: [
{ label: 'Staked ATOM before snapshot', met: false, value: 'No ATOM' },
]},
];
const CLAIMED_HISTORY = [
{ name: 'Arbitrum', symbol: 'ARB', amount: '1,250 ARB', usd: '$1,487', date: 'Mar 2023' },
{ name: 'Optimism S1', symbol: 'OP', amount: '776 OP', usd: '$1,164', date: 'Jun 2022' },
{ name: 'EigenLayer S1', symbol: 'EIGEN', amount: '110 EIGEN', usd: '$385', date: 'May 2024' },
{ name: 'Jito', symbol: 'JTO', amount: '420 JTO', usd: '$1,260', date: 'Dec 2023' },
{ name: 'Jupiter', symbol: 'JUP', amount: '850 JUP', usd: '$680', date: 'Jan 2024' },
];
const RECOMMENDED_ACTIONS = [
{ title: 'Provide liquidity on zkSync DEX', desc: 'Add liquidity to SyncSwap or Mute.io to qualify for zkSync S2 airdrop', impact: '+15 pts' },
{ title: 'Buy & hold ZRO tokens', desc: 'Hold LayerZero tokens to boost eligibility for Season 2 distribution', impact: '+10 pts' },
{ title: 'Deploy contract on Monad testnet', desc: 'Deploy a simple smart contract to complete Monad testnet requirements', impact: '+12 pts' },
{ title: 'Stake in Berachain BEX', desc: 'Provide liquidity in Berachain DEX and delegate BGT for airdrop eligibility', impact: '+20 pts' },
{ title: 'Bridge assets to Scroll', desc: 'Increase Scroll activity by bridging and swapping on native DEXs', impact: '+8 pts' },
];
const GENERAL_CHECKLIST = [
{ label: 'Wallet age > 1 year', met: true, value: '2.3 yrs' },
{ label: 'Total transactions > 100', met: true, value: '347 txns' },
{ label: 'Unique protocols used > 10', met: true, value: '23 protocols' },
{ label: 'Multi-chain activity (3+ chains)', met: true, value: '6 chains' },
{ label: 'NFT holder', met: true, value: '12 NFTs' },
{ label: 'Governance participation', met: false, value: '0 votes' },
{ label: 'Testnet participation', met: true, value: '3 testnets' },
{ label: 'DeFi TVL > $500', met: true, value: '$2,180' },
];
/* ============================================================
GSAP ANIMATIONS — D11 rotate entrance
============================================================ */
function animateHeroEntrance() {
if (prefersReducedMotion) { gsap.set('.hero__content > *', { opacity: 1, y: 0 }); return; }
const tl = gsap.timeline({ defaults: { ease: 'power3.out' } });
tl.from('.hero__status-bar', { opacity: 0, y: 20, duration: 0.6 })
.from('.hero__eyebrow', { opacity: 0, y: 12, duration: 0.4 }, '-=0.3')
.from('.hero__title', { opacity: 0, y: 30, duration: 0.7, ease: 'power4.out' }, '-=0.3')
.from('.hero__score-block', { opacity: 0, scale: 0.9, duration: 0.8 }, '-=0.3');
}
function animateCardEntrance() {
if (prefersReducedMotion) { gsap.set('.check-item-group', { opacity: 1, rotation: 0 }); return; }
/* D11: rotate(-2deg) → rotate(0) */
gsap.utils.toArray('.check-item-group').forEach((el, i) => {
gsap.to(el, {
opacity: 1,
rotation: 0,
duration: 0.7,
delay: i * 0.1,
ease: 'power3.out',
scrollTrigger: { trigger: el, start: 'top 88%', once: true },
});
});
}
function initHeroParallax() {
if (prefersReducedMotion) return;
gsap.to('.hero__crosshatch', {
yPercent: 15, ease: 'none',
scrollTrigger: { trigger: '.hero', start: 'top top', end: 'bottom top', scrub: 1 },
});
}
/* ============================================================
UTILITIES
============================================================ */
function showSkeleton(id, count, type) {
const c = document.getElementById(id);
if (!c) return;
let h = '';
for (let i = 0; i < count; i++) {
if (type === 'airdrop') h += '<div class="airdrop-item"><div class="skeleton" style="width:36px;height:36px;border-radius:50%"></div><div style="flex:1"><div class="skeleton skeleton--text"></div><div class="skeleton" style="width:40%;height:0.7rem"></div></div><div class="skeleton" style="width:60px;height:16px"></div><div class="skeleton" style="width:80px;height:22px;border-radius:4px"></div></div>';
else if (type === 'check') h += '<div class="check-item"><div class="skeleton" style="width:18px;height:18px;border-radius:50%"></div><div class="skeleton skeleton--text" style="flex:1"></div><div class="skeleton" style="width:50px;height:14px"></div></div>';
else if (type === 'action') h += '<div class="action-item"><div class="skeleton" style="width:24px;height:24px;border-radius:50%"></div><div style="flex:1"><div class="skeleton skeleton--text"></div><div class="skeleton" style="width:80%;height:0.7rem"></div></div></div>';
else if (type === 'claimed') h += '<div class="claimed-item"><div class="skeleton" style="width:28px;height:28px;border-radius:50%"></div><div style="flex:1"><div class="skeleton skeleton--text"></div></div><div class="skeleton" style="width:60px;height:16px"></div></div>';
}
c.innerHTML = h;
}
function showError(id, msg, retryFn) {
const c = document.getElementById(id);
if (!c) return;
c.innerHTML = `<div class="error-state">${ICON_ALERT}<p class="error-state__message">${msg}</p>${retryFn ? '<button class="error-state__retry" data-retry="true">Retry</button>' : ''}</div>`;
if (retryFn) { const btn = c.querySelector('[data-retry]'); if (btn) btn.addEventListener('click', retryFn); }
}
function startClock() {
const el = document.getElementById('hero-time');
if (!el) return;
function tick() { el.textContent = new Date().toLocaleString('en-US', { weekday: 'short', year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); }
tick(); setInterval(tick, 1000);
}
/* ============================================================
RENDERERS
============================================================ */
function computeScore() {
const total = GENERAL_CHECKLIST.length;
const passed = GENERAL_CHECKLIST.filter(c => c.met).length;
return Math.round((passed / total) * 100);
}
function renderHeroScore() {
const score = computeScore();
const confirmed = AIRDROPS.filter(a => a.status === 'confirmed').length;
const pending = AIRDROPS.filter(a => a.status === 'pending' || a.status === 'upcoming').length;
const expired = AIRDROPS.filter(a => a.status === 'expired').length;
const scoreEl = document.getElementById('hero-score');
const confirmedEl = document.getElementById('hero-confirmed');
const pendingEl = document.getElementById('hero-pending');
const expiredEl = document.getElementById('hero-expired');
if (scoreEl) scoreEl.textContent = score;
if (confirmedEl) confirmedEl.textContent = confirmed.toString();
if (pendingEl) pendingEl.textContent = pending.toString();
if (expiredEl) expiredEl.textContent = expired.toString();
/* Animate score ring */
const arc = document.getElementById('hero-score-arc');
if (arc) {
const circumference = 2 * Math.PI * 52; /* r=52 */
const offset = circumference - (score / 100) * circumference;
arc.style.strokeDasharray = circumference;
arc.style.strokeDashoffset = offset;
}
}
function renderChecklist() {
const container = document.getElementById('checklist');
const progressBadge = document.getElementById('checklist-progress');
if (!container) return;
showSkeleton('checklist', 6, 'check');
setTimeout(() => {
const passed = GENERAL_CHECKLIST.filter(c => c.met).length;
if (progressBadge) progressBadge.textContent = `${passed}/${GENERAL_CHECKLIST.length}`;
let html = '';
GENERAL_CHECKLIST.forEach((c) => {
const icon = c.met ? ICON_CHECK : ICON_X;
html += `<div class="check-item">${icon}<span class="check-item__label">${c.label}</span><span class="check-item__value">${c.value}</span></div>`;
});
container.innerHTML = html;
if (!prefersReducedMotion) {
gsap.from('.check-item', { opacity: 0, y: 12, stagger: 0.05, duration: 0.3, ease: 'power3.out' });
}
}, 500);
}
function renderAirdropList() {
const container = document.getElementById('airdrop-list');
const countBadge = document.getElementById('airdrop-count');
if (!container) return;
showSkeleton('airdrop-list', 5, 'airdrop');
setTimeout(() => {
if (countBadge) countBadge.textContent = AIRDROPS.length + ' found';
let html = '';
AIRDROPS.forEach((a) => {
const statusClass = `airdrop-item__status--${a.status}`;
const statusLabel = { confirmed: 'Confirmed', pending: 'Pending', expired: 'Expired', upcoming: 'Upcoming' }[a.status] || a.status;
html += `<div class="airdrop-item">
<div class="airdrop-item__logo">${a.symbol.substring(0, 2)}</div>
<div class="airdrop-item__info">
<div class="airdrop-item__name">${a.name}</div>
<div class="airdrop-item__deadline">Deadline: ${a.deadline}</div>
</div>
<div class="airdrop-item__value">
<div class="airdrop-item__est-value">${a.estValue}</div>
<div class="airdrop-item__est-label">Est. value</div>
</div>
<div class="airdrop-item__status ${statusClass}">${statusLabel}</div>
</div>`;
});
container.innerHTML = html;
if (!prefersReducedMotion) {
gsap.from('.airdrop-item', { opacity: 0, x: -16, stagger: 0.06, duration: 0.4, ease: 'power3.out' });
}
}, 400);
}
function renderActions() {
const container = document.getElementById('actions-list');
if (!container) return;
showSkeleton('actions-list', 4, 'action');
setTimeout(() => {
let html = '';
RECOMMENDED_ACTIONS.forEach((a, i) => {
html += `<div class="action-item">
<div class="action-item__number">${i + 1}</div>
<div class="action-item__content">
<div class="action-item__title">${a.title}</div>
<div class="action-item__desc">${a.desc}</div>
</div>
<div class="action-item__impact">${a.impact}</div>
</div>`;
});
container.innerHTML = html;
if (!prefersReducedMotion) {
gsap.from('.action-item', { opacity: 0, y: 16, stagger: 0.08, duration: 0.5, ease: 'power3.out' });
}
}, 600);
}
function renderClaimed() {
const container = document.getElementById('claimed-list');
if (!container) return;
showSkeleton('claimed-list', 4, 'claimed');
setTimeout(() => {
let html = '';
CLAIMED_HISTORY.forEach((c) => {
html += `<div class="claimed-item">
<div class="claimed-item__icon">${c.symbol.substring(0, 2)}</div>
<div class="claimed-item__info">
<div class="claimed-item__name">${c.name}</div>
<div class="claimed-item__date">${c.date}</div>
</div>
<div class="claimed-item__amount">${c.amount}</div>
<div class="claimed-item__usd">${c.usd}</div>
</div>`;
});
container.innerHTML = html;
if (!prefersReducedMotion) {
gsap.from('.claimed-item', { opacity: 0, x: 16, stagger: 0.06, duration: 0.4, ease: 'power3.out' });
}
}, 700);
}
/* ---------- Hero Image Fallback ---------- */
function setupHeroImage() {
const url = '{{HERO_IMAGE_URL}}';
if (url && !url.startsWith('{{')) {
const bg = document.querySelector('.hero__bg-image');
if (bg) { bg.style.backgroundImage = `url('${url}')`; bg.style.opacity = '0.15'; }
}
}
/* ---------- Theme Toggle ---------- */
function initTheme() {
const stored = localStorage.getItem('airdrop-checker-theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const theme = stored || (prefersDark ? 'dark' : 'light');
if (theme === 'light') document.documentElement.setAttribute('data-theme', 'light');
else document.documentElement.removeAttribute('data-theme');
const btn = document.getElementById('theme-toggle');
if (btn) {
btn.addEventListener('click', () => {
const isLight = document.documentElement.getAttribute('data-theme') === 'light';
if (isLight) { document.documentElement.removeAttribute('data-theme'); localStorage.setItem('airdrop-checker-theme', 'dark'); }
else { document.documentElement.setAttribute('data-theme', 'light'); localStorage.setItem('airdrop-checker-theme', 'light'); }
});
}
}
/* ============================================================
INIT
============================================================ */
function init() {
initTheme();
startClock();
setupHeroImage();
animateHeroEntrance();
animateCardEntrance();
initHeroParallax();
renderHeroScore();
renderChecklist();
renderAirdropList();
renderActions();
renderClaimed();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
else init();
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="{{APP_TITLE}} — Airdrop Intel Feed" />
<title>{{APP_TITLE}}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Victor+Mono:wght@400;500;600;700&family=Lexend:wght@400;500;600;700&display=swap" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="noise-overlay" aria-hidden="true"></div>
<button class="theme-toggle" id="theme-toggle" type="button" aria-label="Toggle light/dark theme">
<svg class="theme-toggle__icon theme-toggle__sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="5"/><path d="M12 1v2"/><path d="M12 21v2"/><path d="M4.22 4.22l1.42 1.42"/><path d="M18.36 18.36l1.42 1.42"/><path d="M1 12h2"/><path d="M21 12h2"/><path d="M4.22 19.78l1.42-1.42"/><path d="M18.36 5.64l1.42-1.42"/></svg>
<svg class="theme-toggle__icon theme-toggle__moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
<!-- Hero (H12 Alert Banner) -->
<header class="hero" role="banner">
<div class="hero__alert-bar">
<span class="hero__alert-dot" aria-hidden="true"></span>
<span class="hero__alert-text">ALPHA FEED</span>
<span class="hero__alert-count" id="alert-count">8 active signals</span>
</div>
<div class="container hero__container">
<h1 class="hero__title">Airdrop<br/>Intel Feed</h1>
<p class="hero__subtitle">Track confirmed and suspected airdrops. Conditions, estimated value, and deadlines.</p>
</div>
</header>
<!-- Priority Filter -->
<section class="section" id="filters">
<div class="container">
<div class="filter-tabs" id="filter-tabs"></div>
</div>
</section>
<!-- Signal Feed (A10 Notification/Alert) -->
<section class="section" id="signal-feed">
<div class="container">
<div class="signal-list" id="signal-list"></div>
</div>
</section>
<footer class="footer"><div class="container"><p class="footer__text">Airdrop Intel Feed — Built with mock data</p></div></footer>
<script src="script.js"></script>
</body>
</html>
{
"id": "airdrop-intel",
"name": "Airdrop Intel Feed",
"category": "C",
"matchSignals": ["twitter", "airdrop", "intel", "alpha", "opportunities"],
"description": "空投情报流:空投信号流、状态标签、优先级排序、条件与预估价值",
"placeholders": {
"APP_TITLE": "应用标题",
"USER_NAME": "用户名(可选)"
},
"apis": [],
"themes": ["neutral-dark", "gold-signal", "stealth-ops"],
"features": ["dark-mode", "responsive", "feed", "priority-sorting", "status-tags"],
"catalogIds": ["C14"]
}
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{APP_TITLE}}</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Victor+Mono:wght@400;500;600;700&family=Lexend:wght@300;400;500;600&display=swap" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.7/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="app-header">
<div class="header-left">
<h1 class="logo">{{APP_TITLE}}</h1>
</div>
<div class="header-right">
<span class="user-greeting">@{{TWITTER_HANDLE}}</span>
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme">
<svg class="icon-sun" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
<svg class="icon-moon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
</div>
</header>
<main class="main-content">
<!-- Alert Banner Hero (H12) -->
<section class="alert-banner" id="alertBanner">
<div class="banner-left">
<div class="banner-pulse"></div>
<div class="banner-info">
<p class="banner-label">Strongest Signal Today</p>
<h2 class="banner-token" id="bannerToken">—</h2>
<p class="banner-reason" id="bannerReason">Loading signals...</p>
</div>
</div>
<div class="banner-right">
<div class="banner-stat">
<span class="banner-stat-value" id="bannerCount">—</span>
<span class="banner-stat-label">Signals</span>
</div>
<div class="banner-stat">
<span class="banner-stat-value" id="bannerStrength">—</span>
<span class="banner-stat-label">Avg Score</span>
</div>
</div>
</section>
<!-- Signal Filter -->
<section class="filter-bar" id="filterBar">
<button class="filter-btn active" data-filter="all">All</button>
<button class="filter-btn" data-filter="kol">KOL Mention</button>
<button class="filter-btn" data-filter="onchain">On-chain</button>
<button class="filter-btn" data-filter="price">Price Break</button>
<button class="filter-btn" data-filter="new">New Listing</button>
</section>
<!-- Signal Feed (A19) -->
<section class="signal-feed" id="signalFeed"></section>
<!-- Token Analysis Chart -->
<section class="analysis-section" id="analysisSection">
<h2 class="section-title">Token Signal Distribution</h2>
<div class="chart-container">
<canvas id="tokenChart" height="200"></canvas>
</div>
</section>
<!-- Source Credibility -->
<section class="credibility-section" id="credibilitySection">
<h2 class="section-title">Source Credibility Ranking</h2>
<div class="credibility-list" id="credibilityList"></div>
</section>
</main>
<footer class="app-footer">
<p>Built for <strong>{{USER_NAME}}</strong> · Alpha Signal Board</p>
</footer>
<script src="script.js"></script>
</body>
</html>
{
"id": "alpha-signal",
"name": "Alpha Signal Board",
"category": "E",
"matchSignals": ["twitter", "wallet", "alpha", "signal", "trading"],
"description": "Alpha 信号板:KOL 提及、链上异动、价格突破、信号强度评分、来源可信度",
"placeholders": {
"APP_TITLE": "应用标题",
"USER_NAME": "用户名",
"TWITTER_HANDLE": "Twitter/X 用户名",
"WALLET_ADDRESS": "钱包地址(0x...)",
"HERO_IMAGE_URL": "主视觉图片 URL(可选,回退到 CSS 渐变)"
},
"apis": [
{"name": "CoinGecko", "endpoint": "api.coingecko.com/api/v3", "cors": true, "rateLimit": "30/min"}
],
"themes": ["midnight-gold", "warm-ember", "deep-space"],
"features": ["dark-mode", "responsive", "charts", "real-time-updates"],
"catalogIds": ["E2"]
}