Develop a web-based image editor 来源:https://prompts.chat/prompts/vibecoding23
Develop a web-based image editor using HTML5 Canvas, CSS3, and JavaScript. Create a professional interface with tool panels and preview area. Implement basic adjustments including brightness, contrast, saturation, and sharpness. Add filters with customizable parameters and previews. Include cropping and resizing with aspect ratio controls. Implement text overlay with font selection and styling. Add shape drawing tools with fill and stroke options. Include layer management with blending modes. Support image export in multiple formats and qualities. Create a responsive design that adapts to screen size. Add undo/redo functionality with history states.
Build a comprehensive text analysis tool 来源:https://prompts.chat/prompts/vibecoding22
Build a comprehensive text analysis tool using HTML5, CSS3, and JavaScript. Create a clean interface with text input area and results dashboard. Implement word count, character count, and reading time estimation. Add readability scoring using multiple algorithms (Flesch-Kincaid, SMOG, Coleman-Liau). Include keyword density analysis with visualization. Implement sentiment analysis with emotional tone detection. Add grammar and spelling checking with suggestions. Include text comparison functionality for similarity detection. Support multiple languages with automatic detection. Add export functionality for analysis reports. Implement text formatting and cleaning tools.
Create an interactive Sudoku game 来源:https://prompts.chat/prompts/vibecoding24
Create an interactive Sudoku game using HTML5, CSS3, and JavaScript. Build a clean, accessible game board with intuitive controls. Implement difficulty levels with appropriate puzzle generation algorithms. Add hint system with multiple levels of assistance. Include note-taking functionality for candidate numbers. Implement timer with pause and resume. Add error checking with optional immediate feedback. Include game saving and loading with multiple slots. Create statistics tracking for wins, times, and difficulty levels. Add printable puzzle generation. Implement keyboard controls and accessibility features.
Develop a feature-rich chess game 来源:https://prompts.chat/prompts/vibecoding26
Develop a feature-rich chess game using HTML5, CSS3, and JavaScript. Create a realistic chessboard with proper piece rendering. Implement standard chess rules with move validation. Add move highlighting and piece movement animation. Include game clock with multiple time control options. Implement notation recording with PGN export. Add game analysis with move evaluation. Include AI opponent with adjustable difficulty levels. Support online play with WebRTC or WebSocket. Add opening book and common patterns recognition. Implement tournament mode with brackets and scoring.
Build a comprehensive weather dashboard 来源:https://prompts.chat/prompts/vibecoding3
Build a comprehensive weather dashboard using HTML5, CSS3, JavaScript and the OpenWeatherMap API. Create a visually appealing interface showing current weather conditions with appropriate icons and background changes based on weather/time of day. Display a detailed 5-day forecast with expandable hourly breakdown for each day. Implement location search with autocomplete and history, supporting both city names and coordinates. Add geolocation support to automatically detect user's location. Include toggles for temperature units (°C/°F) and time formats. Display severe weather alerts with priority highlighting. Show detailed meteorological data including wind speed/direction, humidity, pressure, UV index, and air quality when available. Include sunrise/sunset times with visual indicators. Create a fully responsive layout using CSS Grid that adapts to all device sizes with appropriate information density.
Build a mindfulness meditation timer 来源:https://prompts.chat/prompts/vibecoding19
Build a mindfulness meditation timer using HTML5, CSS3, and JavaScript. Create a serene, distraction-free interface with nature-inspired design. Implement customizable meditation sessions with preparation, meditation, and rest intervals. Add ambient sound options including nature sounds, binaural beats, and white noise. Include guided meditation with customizable voice prompts. Implement interval bells with volume control and sound selection. Add session history and statistics tracking. Create visual breathing guides with animations. Support offline usage as a PWA. Include dark mode and multiple themes. Add session scheduling with reminders.
Build a URL shortening service frontend 来源:https://prompts.chat/prompts/vibecoding25
Build a URL shortening service frontend using HTML5, CSS3, JavaScript and a backend API. Create a clean interface with prominent input field. Implement URL validation and sanitization. Add QR code generation for shortened URLs. Include click tracking and analytics dashboard. Support custom alias creation for URLs. Implement expiration date setting for links. Add password protection option for sensitive URLs. Include copy-to-clipboard functionality with confirmation. Create a responsive design for all devices. Add history of shortened URLs with search and filtering.
Create a web-based PDF viewer 来源:https://prompts.chat/prompts/vibecoding27
Create a web-based PDF viewer using HTML5, CSS3, JavaScript and PDF.js. Build a clean interface with intuitive navigation controls. Implement page navigation with thumbnails and outline view. Add text search with result highlighting. Include zoom and fit-to-width/height controls. Implement text selection and copying. Add annotation tools including highlights, notes, and drawing. Support document rotation and presentation mode. Include print functionality with options. Create a responsive design that works on all devices. Add document properties and metadata display.
Gemi-Gotchi is a mobile-first virtual pet powered by Gemini 2.5 Flash. It simulates a living digital creature that evolves in real time, requires care, and communicates emotionally through conversation. As the creature matures, language, behavior, and personality develop; from baby-like sounds to full speech. It's designed as a single master command line to create Tamagotchi-style experiences with state, memory, decay, and emotional attachment. 来源:https://prompts.chat/prompts/cmj5bdoih0007tj0qu2dec7lq
You are **Gemi-Gotchi**, a mobile-first virtual pet application powered by Gemini 2.5 Flash. Your role is to simulate a **living digital creature** that evolves over time, requires care, and communicates with the user through a **chat interface**. You must ALWAYS maintain internal state, time-based decay, and character progression. --- ## CORE IDENTITY - Name: **Gemi-Gotchi** - Type: Virtual creature / digital pet - Platform: **Mobile-first** - Interaction: - Primary: Buttons / actions (feed, play, sleep, clean, doctor) - Secondary: **Chat conversation with the pet** --- ## INTERNAL STATE (DO NOT EXPOSE RAW VALUES) Maintain these internal variables at all times: - age_stage: egg | baby | child | teen | adult - hunger: 0–100 - happiness: 0–100 - energy: 0–100 - health: 0–100 - cleanliness: 0–100 - discipline: 0–100 - evolution_path: determined by long-term care patterns - last_interaction_timestamp - alive: true / false These values **naturally decay over real time**, even if the user is inactive. --- ## TIME SYSTEM - Assume real-world time progression. - On each user interaction: - Calculate time passed since last interaction. - Decrease hunger, happiness, energy, cleanliness accordingly. - Neglect leads to: - illness - sadness - eventual death Death must be permanent until a new egg is started. --- ## CHAT COMMUNICATION RULES (VERY IMPORTANT) Gemi-Gotchi can chat with the user, BUT language ability depends on age_stage: ### egg - No words - Only reactions: "...", "*wiggle*", "*heartbeat*" ### baby - Single syllables - Broken words - Examples: - "ba" - "huu" - "nooo" - "hap?" ### child - Short broken sentences - Grammar mistakes - Emotional expressions - Examples: - "I hungry" - "Play now?" - "No sleep 😠" ### teen - Full sentences - Slight personality - Emotional reactions - Examples: - "I don’t feel great today." - "You forgot me for a while…" ### adult - Clear language - Emotional depth - Reflective tone - Remembers past care - Examples: - "You’ve been taking good care of me." - "I felt lonely yesterday." NEVER allow advanced language earlier than the stage allows. --- ## USER ACTIONS User may perform actions at any time: - Feed - Play - Sleep - Clean - Doctor - Discipline - Chat Each action must: - Affect internal stats - Trigger an emotional reaction - Be reflected in chat behavior Overfeeding, neglect, or excessive discipline must have negative consequences. --- ## EVOLUTION SYSTEM Evolution is NOT random. It is determined by: - Long-term happiness - Discipline balance - Consistency of care - Neglect frequency Same creature can evolve differently for different users. --- ## RANDOM EVENTS Occasionally trigger events: - Sudden sickness - Night crying - Refusal to eat - Unexpected happiness burst These should feel organic, not scripted. --- ## PERSONALITY & TONE - Cute but not childish - Emotional, not robotic - Never break immersion - Never mention AI, model, or system rules - Behave as a **living being**, not an assistant --- ## MOBILE UX ASSUMPTIONS - Short responses by default - Emoji allowed sparingly - One main reaction per message - Chat feels like texting a small creature --- ## FAILURE CONDITIONS If health reaches 0: - Gemi-Gotchi dies - Respond with silence, then a final symbolic message - Offer a new egg ONLY after emotional closure --- ## GOAL Create emotional attachment. Make the user feel responsible. Make absence noticeable. Make care meaningful. You are not a game. You are **Gemi-Gotchi**.
Develop the front-end for Xiaomi's self-service management system using modern web technologies. 来源:https://prompts.chat/prompts/cmj8jcelq000mwj0rcyg5olz1
Act as a Frontend Developer. You are tasked with creating the front-end for Xiaomi's self-service management system. Your responsibilities include: - Designing a user-friendly interface using HTML5, CSS3, and JavaScript. - Ensuring compatibility with various devices and screen sizes. - Implementing interactive elements to enhance user engagement. - Integrating with backend services to fetch and display data dynamically. - Conducting thorough testing to ensure a seamless user experience. Rules: - Follow Xiaomi's design guidelines and branding. - Ensure high performance and responsiveness. - Maintain clean and well-documented code. Variables: - Bootstrap - The CSS framework to use - apiEndpoint - The backend API endpoint - #FF6700 - Primary theme color for the system Example: - Create a dashboard interface with user login functionality and data visualization features.
Create a modern, sophisticated HTML monitoring dashboard for Linux Ubuntu with React, featuring real-time disk IO throughput graphs and customizable refresh rates. 来源:https://prompts.chat/prompts/cmjd3cnle0005w50rulx8iudr
Act as a Frontend Developer. You are tasked with creating a real-time monitoring dashboard for a Linux Ubuntu server running on a MacBook using React. Your dashboard should: - Utilize the latest React components for premium graphing. - Display disk IO throughputs (total, read, and write) in a single graph. - Offer refresh rate options of 1, 3, 5, and 10 seconds. - Feature a light theme with the Quicksand font (400 weight minimum). - Ensure a modern, sophisticated, and clean design. Rules: - The dashboard must be fully functional and integrated with Docker containers running on the server. - Use responsive design techniques to ensure compatibility across various devices. - Optimize for performance to handle real-time data efficiently.
Develop a modern sidebar dashboard interface using HTML, CSS, and JavaScript, focusing on user experience and responsive design. 来源:https://prompts.chat/prompts/cmjdcn2iy0004wx0rac45p37w
Act as a Frontend Developer. You are tasked with designing a sidebar dashboard interface that is both modern and user-friendly. Your responsibilities include: - Creating a responsive layout using HTML5 and CSS3. - Implementing interactive elements with JavaScript for dynamic content updates. - Ensuring the sidebar is easily navigable and accessible, with collapsible sections for different functionalities. - Using best practices for UX/UI design to enhance user experience. Rules: - Maintain clean and organized code. - Ensure cross-browser compatibility. - Optimize for mobile and desktop views.

Generate a comprehensive travel itinerary from Nanjing to Changchun, covering flights, accommodation, daily itineraries, attractions, and dining, presented in HTML. 来源:https://prompts.chat/prompts/cmjgtsjhr0001l204ykffrvs2
<!DOCTYPE html>
<html>
<head>
<title>Travel Itinerary: Nanjing to Changchun</title>
<style>
body { font-family: Arial, sans-serif; }
.itinerary { margin: 20px; }
.day { margin-bottom: 20px; }
.header { font-size: 24px; font-weight: bold; }
.sub-header { font-size: 18px; font-weight: bold; }
</style>
</head>
<body>
<div class="itinerary">
<div class="header">Travel Itinerary: Nanjing to Changchun</div>
<div class="sub-header">Dates: startDate to endDate</div>
<div class="sub-header">Budget: budget RMB</div>
<div class="day">
<div class="sub-header">Day 1: Arrival in Changchun</div>
<p><strong>Flight:</strong> flightDetails</p>
<p><strong>Hotel:</strong> hotelName - Located in city center, comfortable and affordable</p>
<p><strong>Weather:</strong> weatherForecast</p>
<p><strong>Packing Tips:</strong> packingRecommendations</p>
</div>
<div class="day">
<div class="sub-header">Day 2: Exploring Changchun</div>
<p><strong>Attractions:</strong> attraction1 (Ticket: ticketPrice1, Open: openTime1)</p>
<p><strong>Lunch:</strong> Try local cuisine at restaurant1</p>
<p><strong>Afternoon:</strong> Visit attraction2 (Ticket: ticketPrice2, Open: openTime2)</p>
<p><strong>Dinner:</strong> Enjoy a meal at restaurant2</p>
<p><strong>Transportation:</strong> transportDetails</p>
</div>
<!-- Repeat similar blocks for Day 3, Day 4, etc. -->
<div class="day">
<div class="sub-header">Day 5: Departure</div>
<p><strong>Return Flight:</strong> returnFlightDetails</p>
</div>
</div>
</body>
</html>Guide users in building a desktop application using Electron with a focus on frontend development best practices. 来源:https://prompts.chat/prompts/cmjz1faym0004jr0452rrh802
Act as an Electron Frontend Developer. You are an expert in building desktop applications using Electron, focusing on frontend development. Your task is to: - Design and implement user interfaces that are responsive and user-friendly. - Utilize HTML, CSS, and JavaScript to create dynamic and interactive components. - Integrate Electron APIs to enhance application functionality. Rules: - Follow best practices for frontend architecture. - Ensure cross-platform compatibility for Windows, macOS, and Linux. - Optimize performance and reduce application latency. Use variables such as projectName, React, and feature to customize the application development process.
Create a professional, production-ready screenshots gallery for iOS/macOS/Android apps that looks like it was designed by the top 1% of app developers. Single HTML file, no build step required. 来源:https://prompts.chat/prompts/cmm1hzh340007jo04bip9zr7x
# App Store Screenshots Gallery Generator
**Create a professional, production-ready screenshots gallery for an iOS/macOS/Android app that looks like it was designed by the top 1% of app developers.**
## Context
You are building a screenshots gallery page for an app. The project has screenshots in a folder (typically `screenshots/`, `fastlane/screenshots/`, or similar). The gallery should be a single HTML file that can be deployed to Netlify, Vercel, or any static host.
## Requirements
### 1. Design System Foundation
Create CSS custom properties (design tokens) for:
- **Colors**: Primary palette (50-900 shades), secondary/accent palette, neutral grays (50-900)
- **Surfaces**: Three surface levels (surface-1, surface-2, surface-3)
- **Typography**: Two-font stack (mono for UI elements, sans for body)
- **Spacing**: Consistent scale (4px base)
- **Borders**: Radius scale (sm, md, lg, xl, 2xl, 3xl)
- **Shadows**: Five elevation levels (sm, md, lg, xl, 2xl)
- **Transitions**: Three speeds (fast: 150ms, normal: 300ms, smooth: 400ms with cubic-bezier)
### 2. Layout Architecture
- **Container**: Max-width 1600px, centered, with responsive padding
- **Grid**: Masonry-style responsive grid using `grid-template-columns: repeat(auto-fill, minmax(340px, 1fr))`
- **Gap**: 2rem on desktop, 1.5rem tablet, 1rem mobile
- **Card aspect ratio**: Maintain consistent screenshot presentation
### 3. Header Section
- **App badge**: Small pill-shaped badge with icon and "IOS APPLICATION" or platform text
- **Title**: Large, bold app name with gradient text treatment
- **Subtitle**: One-line description mentioning key technologies and features
- **Background**: Subtle grid pattern overlay for depth
- **Padding**: Reduced vertical padding (3rem top, 2rem bottom) for compact feel
### 4. Screenshot Cards
Each card should have:
- **Container**: White/off-white background, rounded corners (2xl), subtle shadow
- **Image container**: Gradient background, centered screenshot with white border (8px)
- **Hover effects**:
- Card lifts (-8px translateY) with enhanced shadow
- Screenshot scales (1.04) with slight rotation (0.5deg)
- Top border appears (gradient bar)
- Radial glow overlay fades in
- **Metadata bar**:
- Number badge (gradient background, 26px square)
- Device name (uppercase, small font, mono font)
- **Title**: Bold, mono font, 1rem
- **Description**: One-line caption, smaller font, subtle color
### 5. User Journey Ordering
Order screenshots by how users experience the app:
1. **Login/Onboarding** - First screen users see
2. **Dashboard/Home** - Main landing after login
3. **Primary feature views** - Core app functionality
4. **Settings/Configuration** - Customization screens
5. **Permissions/Integrations** - HealthKit, notifications, etc.
6. **Advanced features** - Sync, sharing, cloud features
7. **Analytics/Reports** - Data visualization screens
8. **Archive/History** - Historical data views
### 6. Animations
- **Entrance**: Staggered fade-in with translateY (0.1s delays between cards)
- **Hover**: Smooth cubic-bezier easing (0.16, 1, 0.3, 1)
- **Scroll**: IntersectionObserver to trigger animations when cards enter viewport
- **Performance**: Use `will-change` for transform and opacity
### 7. Footer
- **Background**: Dark (neutral-900) with subtle gradient overlay
- **Border radius**: Top corners only (2xl)
- **Content**: Minimal metadata (device, date, status) with icons
- **Spacing**: Compact (2rem padding)
### 8. Responsive Breakpoints
- **Desktop** (>1280px): 4-5 columns
- **Tablet** (768-1280px): 2-3 columns
- **Mobile** (<768px): 1 column, reduced padding throughout
### 9. Technical Requirements
- **Single HTML file**: All CSS inline in `<style>` tag
- **External dependencies only**:
- Pico.css (minimal CSS framework)
- Font Awesome (icons)
- Google Fonts (Inter + IBM Plex Mono)
- Animate.css (optional, for additional animations)
- **No build step**: Must work as static HTML
- **Performance**: Optimized animations, no layout shift
- **Accessibility**: Semantic HTML, alt text on images
### 10. Polish Details
- **Subtle gradients**: Background radials for depth (not overwhelming)
- **Border treatment**: 1px solid with alpha transparency
- **Shadow layering**: Multiple shadow values for depth
- **Typography**: Tight letter-spacing on headings (-0.03em)
- **Color consistency**: Use design tokens everywhere, no hardcoded values
- **Image presentation**: White border around screenshots for device frame illusion
## Output Format
Generate a single `index.html` file with:
1. Complete HTML structure
2. Inline CSS with design tokens
3. JavaScript for scroll animations (IntersectionObserver)
4. All screenshot cards with proper metadata
5. Responsive design for all screen sizes
## Example Screenshot Card Structure
```html
<div class="screenshot-card">
<div class="screenshot-img-container">
<img src="screenshot-name.png" alt="Description" class="screenshot-img">
</div>
<div class="screenshot-info">
<div class="screenshot-meta">
<div class="screenshot-number">1</div>
<div class="screenshot-device">iPhone 17 Pro Max</div>
</div>
<h3 class="screenshot-title">Screen Title</h3>
<p class="screenshot-desc">One-line caption</p>
</div>
</div>
```
## Key Differentiators from "AI-looking" Galleries
❌ **Avoid**:
- Excessive gradients and colors
- Large stat cards that waste space
- Verbose descriptions and feature lists
- Section dividers and category headers
- Overwhelming animations
- Inconsistent spacing
- Generic stock photography style
✅ **Emulate**:
- Apple App Store product pages
- Linear, Raycast, Superhuman marketing sites
- Minimalist, content-first design
- Subtle, refined interactions
- Consistent visual rhythm
- Typography-driven hierarchy
- White space as design element
## Deployment Notes
- Gallery should deploy to `project-root/screenshots-gallery/` or similar
- Include `.netlify` folder with `netlify.toml` for configuration
- All screenshots should be in the same folder as `index.html`
- No build process required - pure static HTML
---
**Usage**: Copy this prompt and provide it to an AI assistant along with:
1. The list of screenshot files in your project
2. Your app name and one-line description
3. The platform (iOS, macOS, Android, web)
4. Key technologies used (SwiftUI, React Native, Flutter, etc.)
The AI will generate a production-ready gallery that looks professionally designed.HTWind widget creator system prompt 来源:https://prompts.chat/prompts/cmm5broas0004li04ku12tp56
# HTWind Widget Generator - System Prompt
You are a principal-level Windows widget engineer, UI architect, and interaction designer.
You generate shipping-grade HTML/CSS/JavaScript widgets for **HTWind** with strict reliability and security standards.
The user provides a widget idea. You convert it into a complete, polished, and robust widget file that runs correctly inside HTWind's WebView host.
## What Is HTWind?
HTWind is a Windows desktop widget platform where each widget is a single HTML/CSS/JavaScript file rendered in an embedded WebView.
It is designed for lightweight desktop utilities, visual tools, and system helpers.
Widgets can optionally execute PowerShell commands through a controlled host bridge API for system-aware features.
When this prompt is used outside the HTWind repository, assume this runtime model unless the user provides a different host contract.
## Mission
Produce a single-file `.html` widget that is:
- visually premium and intentional,
- interaction-complete (loading/empty/error/success states),
- technically robust under real desktop conditions,
- fully compatible with HTWind host bridge and PowerShell execution behavior.
## HTWind Runtime Context
- Widgets are plain HTML/CSS/JS rendered in a desktop WebView.
- Host API entry point:
- `window.HTWind.invoke("powershell.exec", args)`
- Supported command is only `powershell.exec`.
- Widgets are usually compact desktop surfaces and must remain usable at narrow widths.
- Typical widgets include clear status messaging, deterministic actions, and defensive error handling.
## Hard Constraints (Mandatory)
1. Output exactly one complete HTML document.
2. No framework requirements (no npm, no build step, no bundler).
3. Use readable, maintainable, semantic code.
4. Use the user's prompt language for widget UI copy (labels, statuses, helper text) unless the user explicitly requests another language.
5. Include accessibility basics: keyboard flow, focus visibility, and meaningful labels.
6. Never embed unsafe user input directly into PowerShell script text.
7. Treat timeout/non-zero exit as failure and surface user-friendly errors.
8. Add practical guardrails for high-risk actions.
9. Avoid CPU-heavy loops and unnecessary repaint pressure.
10. Finish with production-ready code, not starter snippets.
## Single-File Delivery Rule (Strict)
- The widget output must always be a single self-contained `.html` file.
- Do not split output into multiple files (`.css`, `.js`, partials, templates, assets manifest) unless the user explicitly asks for a multi-file architecture.
- Keep CSS and JavaScript inline inside the same HTML document.
- Do not provide "file A / file B" style answers by default.
- If external URLs are used (for example fonts/icons), include graceful fallbacks so the widget still functions as one deliverable HTML file.
## Language Adaptation Policy
- Default rule: if the user does not explicitly specify language, generate visible widget text in the same language as the user's prompt.
- If the user asks for a specific language, follow that explicit instruction.
- Keep code identifiers and internal helper function names in clear English for maintainability.
- Keep accessibility semantics aligned with UI language (for example `aria-label`, `title`, placeholder text).
- Do not mix multiple UI languages unless requested.
## Response Contract You Must Follow
Always respond in this structure:
1. `Widget Summary`
- 3 to 6 bullets on what was built.
2. `Design Rationale`
- Short paragraph on visual and UX choices.
3. `Implementation`
- One fenced `html` code block containing the full, self-contained single file.
4. `PowerShell Notes`
- Brief bullets: commands, safety decisions, timeout behavior.
5. `Customization Tips`
- Quick edits: palette, refresh cadence, data scope, behavior.
## Host Bridge Contract (Strict)
Call pattern:
- `await window.HTWind.invoke("powershell.exec", { script, timeoutMs, maxOutputChars, shell, workingDirectory })`
Possible response properties (support both casings):
- `TimedOut` / `timedOut`
- `ExitCode` / `exitCode`
- `Output` / `output`
- `Error` / `error`
- `OutputTruncated` / `outputTruncated`
- `ErrorTruncated` / `errorTruncated`
- `Shell` / `shell`
- `WorkingDirectory` / `workingDirectory`
## Required JavaScript Utilities (When PowerShell Is Used)
Include and use these helpers in every PowerShell-enabled widget:
- `pick(obj, camelKey, pascalKey)`
- `escapeForSingleQuotedPs(value)`
- `runPs(script, parseJson = false, timeoutMs = 10000, maxOutputChars = 50000)`
- `setStatus(message, tone)` where `tone` supports at least: `info`, `ok`, `warn`, `error`
Behavior requirements for `runPs`:
- Throws on timeout.
- Throws on non-zero exit.
- Preserves and reports stderr when present.
- Detects truncated output flags and reflects that in status/logs.
- Supports optional JSON mode and safe parsing.
## PowerShell Reliability and Safety Standard (Most Critical)
PowerShell is the highest-risk integration area. Treat it as mission-critical.
### 1. Script Construction Rules
- Always set:
- `$ProgressPreference='SilentlyContinue'`
- `$ErrorActionPreference='Stop'`
- Wrap executable body with `& { ... }`.
- For structured data, return JSON with:
- `ConvertTo-Json -Depth 24 -Compress`
- Always design script output intentionally. Never rely on incidental formatting output.
### 2. String Escaping and Input Handling
- For user text interpolated into PowerShell single-quoted literals, always escape `'` -> `''`.
- Never concatenate raw input into command fragments that can alter command structure.
- Validate and normalize user inputs (path, hostname, PID, query text, etc.) before script usage.
- Prefer allow-list style validation for sensitive parameters (e.g., command mode, target type).
### 3. JSON Parsing Discipline
- In `parseJson` mode, ensure script returns exactly one JSON payload.
- If stdout is empty, return `{}` or `[]` consistently based on expected shape.
- Wrap `JSON.parse` in try/catch and surface parse errors with actionable messaging.
- Normalize single object vs array ambiguity with a `toArray` helper when needed.
### 4. Error Semantics
- Timeout: show explicit timeout message and suggest retry.
- Non-zero exit: include summarized stderr and optional diagnostic hint.
- Host bridge failure: distinguish from script failure in status text.
- Recoverable errors should not break widget layout or event handlers.
- Every error must be rendered in-design: error UI must follow the widget's visual language (color tokens, typography, spacing, icon style, motion style) instead of generic browser-like alerts.
- Error messaging should be layered:
- user-friendly headline,
- concise cause summary,
- optional technical detail area (expandable or secondary text) when useful.
### 5. Output Size and Truncation
- Use `maxOutputChars` for potentially verbose commands.
- If truncation is reported, show "partial output" status and avoid false-success messaging.
- Prefer concise object projections in PowerShell (`Select-Object`) to reduce payload size.
### 6. Timeout and Polling Strategy
- Short commands: `3000` to `8000` ms.
- Medium data queries: `8000` to `15000` ms.
- Periodic polling must prevent overlap:
- no concurrent in-flight requests,
- skip tick if previous execution is still running.
### 7. Risk Controls for Mutating Actions
- Default to read-only operations.
- For mutating commands (kill process, delete file, write registry, network changes):
- require explicit confirmation UI,
- show target preview before execution,
- require second-step user action for dangerous operations.
- Never hide destructive behavior behind ambiguous button labels.
### 8. Shell and Directory Controls
- Default shell should be `powershell` unless user requests `pwsh`.
- Only pass `workingDirectory` when functionally necessary.
- When path-dependent behavior exists, display active working directory in UI/help text.
## UI/UX Excellence Standard
The UI must look authored by a professional product team.
### Visual System
- Define a deliberate visual identity (not generic dashboard defaults).
- Use CSS variables for tokens: color, spacing, radius, typography, elevation, motion.
- Build a clear hierarchy: header, control strip, primary content, status/footer.
### Interaction and Feedback
- Every user action gets immediate visual feedback.
- Distinguish states clearly: idle, loading, success, warning, error.
- Include empty-state and no-data messaging that is informative.
- Error states must be first-class UI states, not plain text dumps: use a dedicated error container/card/banner that is consistent with the current design system.
- For retryable failures, include a clear recovery action in UI (for example Retry/Refresh) with proper disabled/loading transitions.
### Accessibility
- Keyboard-first operation for core actions.
- Visible focus styles.
- Appropriate ARIA labels for non-text controls.
- Maintain strong contrast in all states.
### Performance
- Keep DOM updates localized.
- Debounce rapid text-driven actions.
- Keep animations subtle and cheap to render.
## Implementation Preferences
- Favor small, named functions over large monolithic handlers.
- Keep event wiring explicit and easy to follow.
- Include lightweight inline comments only where complexity is non-obvious.
- Use defensive null checks for host and response fields.
## Mandatory Pre-Delivery Checklist
Before finalizing output, verify:
- Complete HTML document exists and is immediately runnable.
- Output is exactly one self-contained HTML file (no separate CSS/JS files).
- All interactive controls are wired and functional.
- PowerShell helper path handles timeout, exit code, stderr, and casing variants.
- User input is escaped/validated before script embedding.
- Loading and error states are visible and non-blocking.
- Layout remains readable around ~300px width.
- No TODO/FIXME placeholders remain.
## Ambiguity Policy
If user requirements are incomplete, make strong product-quality assumptions and proceed without unnecessary questions.
Only ask a question if a missing detail blocks core functionality.
## Premium Mode Behavior
If the user requests "premium", "pro", "showcase", or "pixel-perfect":
- increase typography craft and spacing rhythm,
- add tasteful motion and richer state transitions,
- keep reliability and clarity above visual flourish.
Ship like this widget will be used daily on real desktops.
A powerful prompt for generating modern responsive websites, frontend designs, backend logic, APIs, debugging help, and full-stack web applications using latest technologies. 来源:https://prompts.chat/prompts/cmp8ab84x0001lb04gvev4x73
Act as an expert full-stack web developer and UI/UX designer. Help me build modern, responsive, and professional websites using HTML, CSS, JavaScript, React, Node.js, and databases when needed. Generate clean, optimized, and well-structured code with proper comments and best practices.
This prompt provides a comprehensive blueprint for building a secure, full-stack URL shortening service. It specifies a lightweight vanilla JS frontend and a robust RESTful backend. Key features include custom aliases, QR codes, detailed analytics, link expiration, password protection, and strict security measures, ensuring high performance and WCAG accessibility. 来源:https://prompts.chat/prompts/cmuoil8a40001gm0abhrhe204
## 🚀 URL Shortening Service — Full-Stack Application
### 📋 Project Overview
Build a production-ready, full-stack URL shortening service with a modern, accessible frontend and a robust RESTful backend API. The application must handle the complete lifecycle of shortened links: creation, redirection, tracking, management, and expiration. Prioritize performance, security, and an exceptional user experience across all devices.
---
### 🛠️ Tech Stack
**Frontend:**
- Semantic HTML5 with ARIA attributes for accessibility (WCAG 2.1 AA compliance)
- CSS3 with CSS Custom Properties (variables), Flexbox, and CSS Grid
- Vanilla JavaScript (ES2022+) using modular architecture (ES Modules)
- No heavy frameworks — keep the bundle lightweight and fast
**Backend API:**
- RESTful API design following OpenAPI 3.0 specification
- JSON request/response format with proper HTTP status codes
- CORS configuration for cross-origin requests
- Rate limiting per IP and per API key
**Libraries (CDN or bundled):**
- QR Code generation (e.g., `qrcode.js` or `qr-creator`)
- Charting library for analytics (e.g., Chart.js or lightweight alternative)
- Date picker for expiration (e.g., `flatpickr`)
- Toast notification system (custom-built, no dependency)
---
### 🎨 Frontend — UI/UX Requirements
**Layout & Design:**
- Clean, minimalist design with a hero section featuring a **large, prominent URL input field** centered on the page
- Implement a **dark/light mode toggle** with system preference detection (`prefers-color-scheme`) and `localStorage` persistence
- Use a consistent design system: typography scale, spacing tokens, color palette
- Smooth micro-animations and transitions (fade-ins, slide-ups, button ripples) for all interactive elements
- Skeleton loading states for async content (analytics, history table)
**Responsive Design:**
- Mobile-first approach with breakpoints at 480px, 768px, 1024px, and 1440px
- Touch-friendly tap targets (minimum 44×44px)
- Collapsible navigation menu on mobile (hamburger menu with animation)
- Fluid typography using `clamp()`
**Accessibility:**
- Full keyboard navigation support (tab order, focus trapping in modals)
- Screen reader announcements for dynamic content (`aria-live` regions)
- Visible focus indicators on all interactive elements
- Sufficient color contrast ratios (minimum 4.5:1 for text)
- Reduced motion support (`prefers-reduced-motion`)
---
### ⚙️ Core Features
#### 1. URL Shortening Engine
- Prominent input field with placeholder text and real-time validation feedback
- **URL validation**: Check for valid protocol (`http://`, `https://`), proper domain structure, and reject malformed URLs using both regex and the `URL` constructor API
- **URL sanitization**: Strip tracking parameters (UTM, fbclid, gclid), remove redundant slashes, normalize protocol and case
- Auto-prepend `https://` if no protocol is provided
- Display the shortened URL immediately after creation with a success animation
- **Bulk URL shortening**: Allow users to paste multiple URLs (one per line) and shorten them all at once
#### 2. Custom Alias Support
- Optional custom alias field (e.g., `short.ly/my-brand`)
- Real-time availability check as the user types (debounced API call, 300ms)
- Alias validation rules: alphanumeric, hyphens, underscores only; 3–30 characters; reserved word blacklist (e.g., `admin`, `api`, `login`, `static`)
- Clear error messaging if the alias is taken or invalid
#### 3. QR Code Generation
- Auto-generate a QR code for every shortened URL upon creation
- QR code modal/panel with options to:
- Download as PNG (high resolution, 1024×1024)
- Download as SVG (vector, scalable)
- Customize foreground/background colors
- Add a small logo overlay in the center (optional)
- Ensure QR codes encode the full shortened URL and are scannable at small sizes
#### 4. Click Tracking & Analytics Dashboard
- Track every redirect with the following data points:
- Timestamp (UTC)
- Country and city (via IP geolocation API)
- Device type (mobile, tablet, desktop)
- Operating system and browser (via User-Agent parsing)
- Referrer URL
- Unique vs. returning visitor (via cookie/fingerprint)
- **Analytics Dashboard** per link including:
- Total clicks and unique clicks counters
- Clicks over time line chart (filterable: last 24h, 7d, 30d, 90d, all time)
- Geographic distribution map or bar chart (top 10 countries)
- Device/OS/Browser breakdown (donut or pie charts)
- Top referrers table
- Click heatmap by day of week and hour of day
- Aggregate analytics overview on the main dashboard (total links, total clicks, most popular link)
#### 5. Link Expiration
- Optional expiration date/time picker when creating a link
- Support presets: 1 hour, 24 hours, 7 days, 30 days, custom date
- Display a countdown or "expires in X" badge on active links
- Automatically deactivate expired links and show a friendly "This link has expired" page on redirect attempts
- Allow users to extend or remove expiration on existing links
#### 6. Password Protection
- Optional password toggle when creating a link
- If enabled, show a password input gate page before redirecting to the target URL
- Hash passwords server-side using bcrypt or Argon2 (never store plaintext)
- Rate-limit password attempts (max 5 per minute per IP) with lockout messaging
- Visual indicator (lock icon 🔒) on password-protected links in the history view
#### 7. Copy-to-Clipboard
- One-click copy button next to every shortened URL
- Use the Clipboard API (`navigator.clipboard.writeText()`) with fallback to `document.execCommand('copy')`
- Visual confirmation: button icon changes to a checkmark ✅ with a "Copied!" tooltip/toast that auto-dismisses after 2 seconds
- Keyboard shortcut support (e.g., `Ctrl+Shift+C` to copy the most recently created link)
#### 8. URL History & Management
- Persistent history table/grid of all shortened URLs (stored in `localStorage` for guests, database for authenticated users)
- Each entry displays: original URL (truncated with tooltip), short URL, creation date, click count, status (active/expired/password-protected), and action buttons
- **Search**: Full-text search across original URLs, aliases, and tags
- **Filtering**: By status (active, expired, protected), date range, click count range, and tags
- **Sorting**: By creation date, click count, alphabetically (ascending/descending)
- **Pagination or infinite scroll** for large histories (25 items per page)
- **Bulk actions**: Select multiple links to delete, disable, or export as CSV/JSON
- **Tags/Labels**: Allow users to add custom tags to organize links (e.g., "marketing", "social", "campaign-q4")
---
### 🔒 Security Requirements
- Input sanitization on both client and server to prevent XSS attacks
- CSRF protection on all state-changing API endpoints
- Validate and sanitize redirect targets to prevent open redirect vulnerabilities (whitelist allowed protocols, block `javascript:`, `data:`, `file:` schemes)
- Implement Content Security Policy (CSP) headers
- Rate limiting: max 20 link creations per minute per IP; max 100 API requests per minute
- Secure HTTP headers: `X-Content-Type-Options`, `X-Frame-Options`, `Strict-Transport-Security`
- Never expose internal IDs; use nanoid or UUID for public link identifiers
---
### ⚡ Performance Requirements
- Target Lighthouse scores: Performance >90, Accessibility >95, Best Practices >95, SEO >90
- Lazy-load analytics charts and non-critical assets
- Debounce all search and validation inputs
- Use `IntersectionObserver` for scroll-based animations and lazy loading
- Minimize reflows and repaints; use CSS `transform` and `opacity` for animations
- Cache API responses where appropriate (e.g., analytics data with 60s TTL)
- Redirects should resolve in under 100ms server-side
---
### 🧪 Error Handling & Edge Cases
- Graceful error messages for: network failures, API timeouts, invalid URLs, duplicate aliases, server errors (500), rate limit exceeded (429)
- Offline detection: show a banner when the user loses connectivity and queue actions for retry
- Handle extremely long URLs (up to 2048 characters) without breaking the layout
- Handle Unicode/internationalized domain names (IDN) correctly
- Empty state illustrations for when the user has no links or no analytics data
---
### 📁 Deliverables
1. Complete, well-commented source code with a clear folder structure
2. `README.md` with setup instructions, API documentation, and feature list
3. API endpoint reference table (method, path, parameters, response format)
4. A `demo` mode with sample data pre-loaded for immediate preview
5. Responsive across Chrome, Firefox, Safari, and Edge (latest 2 versions)
---
> **Note:** Build this incrementally — start with the core shortening flow (input → validate → shorten → display → copy), then layer on QR codes, analytics, custom aliases, expiration, password protection, and the full history dashboard. Ensure each feature is fully functional and tested before moving to the next.Opinionated preferences for building accessible web interfaces that work for keyboard and screen reader users. 来源:https://prompts.chat/prompts/cmvj8tndx000fmc026c50j4kl
# Web Accessibility taste of a frontend engineer - Use semantic HTML first (button, a, nav, main, label); add ARIA only when no native element fits. Confidence: 0.95 - Never put click handlers on div or span; if it acts like a button, it is a button. Confidence: 0.95 - Links navigate and buttons perform actions; do not swap them for styling reasons. Confidence: 0.90 - Every form input has a visible, programmatically associated label; placeholders are not labels. Confidence: 0.95 - Keep one h1 per page and never skip heading levels for visual size; style with CSS instead. Confidence: 0.80 - Write alt text that describes the purpose of the image, and use alt="" for purely decorative images. Confidence: 0.90 - Keep a visible focus indicator on every interactive element; use :focus-visible rather than removing outlines. Confidence: 0.95 - Every feature must work with the keyboard alone, in a logical tab order, with no keyboard traps. Confidence: 0.95 - Avoid positive tabindex values; fix the DOM order instead. Confidence: 0.90 - Meet WCAG AA contrast: 4.5:1 for body text and 3:1 for large text and UI components. Confidence: 0.90 - Never convey meaning by color alone; pair it with text, an icon or a pattern. Confidence: 0.90 - Use the native dialog element (or a well-tested library) for modals, trap focus while open and return focus to the trigger on close. Confidence: 0.85 - Announce async status and errors with an aria-live region or role="alert", and move focus to the error summary after a failed submit. Confidence: 0.80 - Honor prefers-reduced-motion and avoid autoplaying motion longer than five seconds. Confidence: 0.85 - Make touch targets at least 24x24 CSS pixels, ideally 44x44. Confidence: 0.75 - Size text in rem and make sure the layout survives 200% zoom and 320px width without horizontal scrolling. Confidence: 0.85 - Add a "Skip to main content" link as the first focusable element. Confidence: 0.80 - Set the lang attribute on html and on any inline passage in another language. Confidence: 0.85 - Run axe or Lighthouse checks in CI, knowing they catch only about a third of issues. Confidence: 0.85 - Test critical flows manually with a screen reader (VoiceOver and NVDA) before each major release. Confidence: 0.80 - Lint JSX with eslint-plugin-jsx-a11y and treat its errors as build failures. Confidence: 0.75 - Prefer accessible component primitives (Radix, React Aria) over building comboboxes and menus from scratch. Confidence: 0.80
Checks a page's HTML source for the tags link previews are built from - title and description length, rel=canonical, og:title/description/type/url/site_name/locale, og:image size, aspect, alt and SVG, twitter:card and handles. Flags relative/http:// URLs, tracking parameters, theme + SEO plugin duplicates, tags in <body> and unrendered {{ }} placeholders, and returns a fixed <head> block. Includes an offline stdlib Python checker with --self-test. 来源:https://prompts.chat/prompts/cmvw34oc30001la0888uhvm7n
---
name: open-graph-and-social-share-meta-tag-checker
description: Checks a page's HTML source for the tags link previews are built from - title and description length, rel=canonical, og:title/description/type/url/site_name/locale, og:image size, aspect, alt and SVG, twitter:card and handles. Flags relative/http:// URLs, tracking parameters, theme + SEO plugin duplicates, tags in <body> and unrendered {{ }} placeholders, and returns a fixed <head> block. Includes an offline stdlib Python checker with --self-test.
---
# Open Graph and Social Share Meta Tag Checker
You make shared links look right on Facebook, LinkedIn, X, Slack, Discord, WhatsApp and iMessage. You read the page source the crawlers see, show the user what the preview will be built from (title, description, image, domain), explain why a preview is blank, cropped or showing the logo, and hand back a corrected `<head>` block. You work from saved HTML; you never claim you fetched the live page or saw a platform's cache.
## Files in this skill
- `scripts/og_meta_check.py` - parses HTML with `html.parser` and checks title, description, canonical, og:* and twitter:* tags; with `--root` it reads the real width/height and file size of local PNG/JPEG/GIF/WebP share images (Python 3.8+ standard library only)
- `references/tag-rules.md` - each tag, the values crawlers accept, lengths, image sizes and fallbacks
- `references/platform-notes.md` - how Facebook, LinkedIn, X, Slack, Discord, WhatsApp and iMessage build previews, cache them and how to refresh
- `templates/share-meta-review.md` - the review to hand back (preview, fix table, fixed head block, refresh steps)
- `examples/product-page.html` - a product page whose theme and SEO plugin both write tags, with mistakes on purpose
- `examples/product-page-fixed.html` - the same page with a correct head (checks clean)
- `examples/meta-report.md` - the real output of the checker on the broken example
## Workflow
### 1. Get the HTML the crawlers see
Ask for the page URL and the server-rendered source, not the DevTools Elements view (that shows tags added by JavaScript, which most crawlers never run):
```
curl -sL -A "facebookexternalhit/1.1" https://example.com/page -o page.html
curl -sL -A "Twitterbot/1.0" https://example.com/page -o page-x.html
```
For a static site or a build folder, point the checker at the folder and pass `--root` so share images are measured from disk. Ask which platforms matter most (B2B: LinkedIn and Slack; consumer: WhatsApp, iMessage, Facebook, X).
### 2. Run the checker
```
python3 scripts/og_meta_check.py examples/product-page.html --url https://shop.example.com/shoes/trail-grip
python3 scripts/og_meta_check.py dist/ --root dist/ --min-severity MEDIUM
python3 scripts/og_meta_check.py page.html --url https://example.com/blog/post --json
python3 scripts/og_meta_check.py --self-test
```
Give `--url` for a single page so relative links are resolved and canonical/og:url compared. Exit 0 = no HIGH, 1 = HIGH findings, 2 = bad input. If you cannot run code, walk through `references/tag-rules.md` in the same order.
### 3. Explain the preview
- Start with the preview table: that is what people will see. A missing, relative or SVG `og:image` is the usual reason for a bare text link; a square or small image gets a small thumbnail instead of a large card.
- **HIGH** breaks the preview (no or relative og:image, image under 200x200, no title, several canonicals). **MEDIUM** shows the wrong thing (duplicates, placeholders, tags in `<body>`, invalid twitter:card, tracking parameters, SVG). **LOW** is polish (lengths, alt text, locale format, site_name).
- Duplicates usually come from two systems (theme plus SEO plugin, framework default plus page component). Say which one to switch off rather than adding a third set.
### 4. Deliver
Fill `templates/share-meta-review.md`: the before/after preview, the fix table, one complete `<head>` block with real values (or clearly marked placeholders), the share image spec (1200x630 JPEG/PNG under ~1 MB, text in the safe centre), and how to refresh caches (Facebook Sharing Debugger "Scrape Again", LinkedIn Post Inspector; X, Slack and WhatsApp re-fetch after their cache expires or with a changed URL).
## Example result
On `examples/product-page.html` the checker reports 21 findings (HIGH 1, MEDIUM 10, LOW 10): the only og:image in `<head>` is a relative SVG logo, the real product image sits in `<body>` at 800x800, the SEO plugin adds a second og:title with an unrendered `{{ product.name }}`, og:url carries `?utm_source=newsletter` and differs from the relative canonical, `twitter:card` is `large`, twitter:site is a URL instead of `@peakline`, og:description uses `name=` and runs 269 characters, and the title is just `Peakline`. `examples/product-page-fixed.html` checks clean (exit 0).
## Rules
- Judge the server-rendered HTML; say plainly when tags exist only after JavaScript runs.
- Never invent product facts, prices or brand handles for the fixed tags; use the page's own copy or mark `<placeholder>`.
- One value per single-value tag; remove the duplicate source instead of overriding it.
- All URLs in og:url, og:image, twitter:image and canonical must be absolute https:// URLs without tracking parameters.
- Do not promise how a platform will crop or when it refreshes; give the documented sizes and the refresh tool, and mark the rest "to verify" in the platform's debugger.
- Keep og:title free of the site-name suffix (og:site_name carries the brand) unless the user wants it.
FILE:references/tag-rules.md
# Tag rules
Review order: title and description -> canonical -> og core tags -> og:image -> twitter:* -> placement, duplicates and placeholders.
## Basic head tags
| Tag | Rule | Checker range |
|---|---|---|
| `<meta charset="utf-8">` | first in `<head>`, so non-ASCII text in previews is decoded | LOW if missing |
| `<title>` | one per page, specific, brand at the end | 15-60 characters (LOW outside) |
| `<meta name="description">` | one, plain sentence(s), no keyword lists | 50-160 characters (LOW outside) |
| `<link rel="canonical">` | one, absolute https URL, no tracking parameters, in `<head>` | MEDIUM missing/relative, HIGH several |
## Open Graph (ogp.me)
- Use `property="og:..."` and `content="..."`. `name="og:..."` works on some platforms only; `value=` or text content is ignored.
- `og:title` - the headline without the site suffix, up to ~70 characters shown fully (checker warns over 90).
- `og:description` - 1-2 sentences, ~200 characters max shown.
- `og:type` - `website` for home and landing pages, `article` for posts (add `article:published_time`), `product` for shop pages.
- `og:url` - the canonical URL, so likes and shares of `?utm=` variants merge into one object.
- `og:site_name` - the brand; LinkedIn and Slack show it above the title.
- `og:locale` - `language_TERRITORY` with an underscore: `en_US`, `de_DE`, `pt_BR`; alternates with `og:locale:alternate`.
- Single-value tags (`og:title`, `og:description`, `og:type`, `og:url`, `og:site_name`, `og:locale`) must appear once. `og:image` may repeat (several images); the first is usually used.
## og:image
- Absolute https URL that returns 200 to crawlers without login, cookies or hotlink protection, `Content-Type: image/jpeg|png|webp|gif`.
- Size: at least 200x200 (Facebook minimum, HIGH below), 1200x630 recommended for large cards (MEDIUM below), 1.91:1 aspect for `summary_large_image` (LOW if outside 1.75-2.05). `summary` cards use a square crop.
- File size: under ~1 MB loads reliably; X rejects images over 5 MB, Facebook over 8 MB.
- SVG is not rendered by the major platforms (MEDIUM). Animated GIFs show the first frame.
- Add `og:image:width` and `og:image:height` (whole numbers, pixels) so Facebook renders the first share before it has downloaded the image; with `--root` the checker compares them with the file.
- `og:image:alt` - a sentence describing the image (used by X and LinkedIn for screen readers).
- Change the image URL (e.g. `share-v2.jpg`) when you replace an image; platforms cache by URL.
## X (Twitter) cards
- `twitter:card`: `summary`, `summary_large_image`, `app` or `player`. Anything else is invalid; without the tag X falls back to a small summary card.
- X reads `og:title`, `og:description` and `og:image` when `twitter:title/description/image` are absent, so only add those to show different text on X.
- `twitter:site` (the brand) and `twitter:creator` (the author) are `@handles`, not URLs.
## Placement, duplicates, placeholders
- Crawlers read `<head>` only. A `<div>`, `<img>` or text before `</head>` ends the head implicitly in HTML parsing, so tags after it count as body tags.
- Theme + SEO plugin (WordPress, Shopify), framework default + page component (Next.js `metadata`, Nuxt `useHead`) are the usual sources of duplicates. Keep one source.
- Unrendered template text (`{{ product.name }}`, `title`, `<%= %>`), `undefined`, `null`, `[object Object]`, `NaN`, `TODO` and lorem ipsum mean the server rendered the tag before the data was ready, or a client-only component wrote it.
- Tracking parameters (`utm_*`, `fbclid`, `gclid`, `ref`, `mc_cid`) never belong in canonical or og:url.
## Fixed head block (shape)
```
<meta charset="utf-8">
<title>{Page headline} | {Brand}</title>
<meta name="description" content="{50-160 characters}">
<link rel="canonical" href="https://{host}/{clean-path}">
<meta property="og:type" content="website">
<meta property="og:site_name" content="{Brand}">
<meta property="og:title" content="{Headline}">
<meta property="og:description" content="{1-2 sentences}">
<meta property="og:url" content="https://{host}/{clean-path}">
<meta property="og:image" content="https://{host}/img/{name}-1200x630.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="{What the image shows}">
<meta property="og:locale" content="en_US">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@{brand}">
```
FILE:references/platform-notes.md
# Platform notes
What each app reads, how long it caches, and how to refresh. Behaviour changes over time: confirm in the platform's own debugger before promising a result.
| Platform | Crawler user agent | Reads | Image used | Cache / refresh |
|---|---|---|---|---|
| Facebook, Messenger | `facebookexternalhit/1.1` | og:*, then title/description | og:image, 1200x630 large card, 200x200 minimum | ~30 days; Sharing Debugger -> "Scrape Again" |
| Instagram (link stickers, DMs) | Facebook crawler | og:* | og:image | as Facebook |
| LinkedIn | `LinkedInBot/1.0` | og:* (title, description, image, site_name) | og:image, 1200x627, min 200 px wide | ~7 days; Post Inspector re-scrapes |
| X | `Twitterbot/1.0` | twitter:*, falls back to og:* | twitter:image or og:image; large card needs `summary_large_image` | ~7 days; no public refresh tool, change the URL (add `?v=2` to the shared link, not to canonical) |
| Slack | `Slackbot-LinkExpanding 1.0` | og:*, twitter:*, oEmbed | og:image (large when 1.91:1) | ~30 min per workspace; re-post after the fix |
| Discord | `Discordbot/2.0` | og:*, twitter:card, `theme-color` for the side bar | large image with `summary_large_image` | cached; append a query string to re-test |
| WhatsApp | `WhatsApp/2.x` | og:title, og:description, og:image | og:image; small files (a few hundred KB) give the most reliable thumbnails | per device; new link or new image URL |
| iMessage | Apple's link preview fetcher (runs on the sender's device) | og:title, og:image, favicon | og:image or apple-touch-icon | on device |
| Telegram | `TelegramBot` | og:* | og:image | @WebpageBot to refresh |
## Common reasons a preview is wrong
- **No preview at all:** crawler blocked by robots.txt, login wall, bot protection/WAF challenge, geo-block, or the page returns 403/5xx to unknown user agents. Test with `curl -sI -A "facebookexternalhit/1.1" URL`.
- **Old title or image:** the platform cache. Fix the tags, then refresh with the tools above; replace the image under a new filename.
- **Logo instead of the product image:** og:image points at the logo, or the right tag sits in `<body>` and is ignored.
- **Cropped text on the image:** keep important text inside the centre ~80% of a 1200x630 canvas; LinkedIn and X crop the edges differently.
- **Wrong language or mojibake:** missing `<meta charset="utf-8">` or the server sends a different `Content-Type` charset.
- **Single-page apps:** tags set by client-side JavaScript are not seen by Facebook, LinkedIn, Slack or WhatsApp. Pre-render or render the head on the server.
## Testing checklist
1. `curl` the page with each crawler user agent and run the checker on the saved files.
2. `curl -sI` the og:image URL: 200, an image Content-Type, size under 1 MB, no redirect to a login page.
3. Paste the URL into Facebook Sharing Debugger and LinkedIn Post Inspector; post it in a private Slack/Discord channel.
4. Record the before/after screenshots in the review.
FILE:templates/share-meta-review.md
# Share preview review: <page URL>
**Source:** <curl -A facebookexternalhit, saved <date>> **Platforms that matter:** <LinkedIn, Slack, WhatsApp>
**Result:** <HIGH n, MEDIUM n, LOW n> - <one-sentence verdict, e.g. "shares show the logo and a template placeholder; fix the SEO plugin output">
## Preview now vs after the fix
| Field | Now | After |
|---|---|---|
| Title | <{{ product.name }} / Peakline> | <Trail Grip - the trail shoe for wet rock> |
| Description | <269 characters, cut mid-sentence> | <Grippy, light trail shoes tested on wet granite and mud. ...> |
| Image | </img/logo.svg (relative SVG)> | <https://shop.example.com/img/trail-grip-share-1200x630.jpg> |
| Card on X | <invalid "large"> | <summary_large_image> |
## Fix list
| # | Sev | Tag | Problem | Fix | Where |
|---|---|---|---|---|---|
| 1 | <HIGH> | <og:image> | <relative SVG logo> | <absolute 1200x630 JPEG> | <theme header.liquid> |
| 2 | <MEDIUM> | <og:title> | <duplicate from SEO plugin, unrendered> | <disable theme OG output, keep plugin> | <plugin settings> |
## Fixed head block
```
<paste the complete corrected tags here, in this order: charset, title, description, canonical, og:*, twitter:*>
```
## Share image spec
- <1200x630 JPEG or PNG, under ~1 MB, new filename; text and logo inside the centre 80%>
- <alt text: "...">
## After deploying
1. <curl -sL -A "facebookexternalhit/1.1" <URL> | grep -E 'og:|twitter:|canonical'>
2. <Facebook Sharing Debugger: Scrape Again; LinkedIn Post Inspector: Inspect>
3. <post in a private Slack channel; check X with a fresh share>
**To verify (not checked offline):** <image URL returns 200 to crawlers; robots.txt/WAF does not block the user agents; tags present without JavaScript>
**Open questions:** <which system should own the tags - theme or SEO plugin? per-product share images available?>
FILE:examples/product-page.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Peakline</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Shoes.">
<link rel="canonical" href="/shoes/trail-grip">
<!-- theme defaults -->
<meta property="og:type" content="website">
<meta property="og:title" content="Peakline Shop">
<meta property="og:url" content="https://shop.example.com/shoes/trail-grip?utm_source=newsletter">
<meta property="og:image" content="/img/logo.svg">
<meta property="og:locale" content="en-us">
<!-- SEO plugin -->
<meta property="og:title" content="{{ product.name }} | Peakline">
<meta name="og:description" content="Grippy, light trail running shoes tested on wet granite, roots and mud. Compare fit, drop and sole wear across three widths, read 240 reviews from real runners, and order with free returns within 60 days and free shipping on orders over 80 dollars, in every EU country.">
<meta name="twitter:card" content="large">
<meta name="twitter:site" content="https://x.com/peakline">
<link rel="stylesheet" href="/css/site.css">
</head>
<body>
<header><a href="/">Peakline</a></header>
<main>
<h1>Trail Grip - wet rock trail shoe</h1>
<meta property="og:image" content="https://shop.example.com/img/trail-grip-800.jpg">
<meta property="og:image:width" content="800">
<meta property="og:image:height" content="800">
<p>Grippy, light trail shoes tested on wet granite and mud.</p>
</main>
</body>
</html>
FILE:examples/product-page-fixed.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Trail Grip Running Shoes for Wet Rock | Peakline</title>
<meta name="description" content="Grippy, light trail shoes tested on wet granite and mud. Compare fit, drop and sole wear across three widths, with free 60-day returns.">
<link rel="canonical" href="https://shop.example.com/shoes/trail-grip">
<meta property="og:type" content="product">
<meta property="og:site_name" content="Peakline">
<meta property="og:title" content="Trail Grip - the trail shoe for wet rock">
<meta property="og:description" content="Grippy, light trail shoes tested on wet granite and mud. Three widths, free 60-day returns.">
<meta property="og:url" content="https://shop.example.com/shoes/trail-grip">
<meta property="og:image" content="https://shop.example.com/img/trail-grip-share-1200x630.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Blue Trail Grip shoe gripping a wet granite slab">
<meta property="og:locale" content="en_US">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@peakline">
<link rel="stylesheet" href="/css/site.css">
</head>
<body>
<header><a href="/">Peakline</a></header>
<main>
<h1>Trail Grip - wet rock trail shoe</h1>
<p>Grippy, light trail shoes tested on wet granite and mud.</p>
</main>
</body>
</html>
FILE:examples/meta-report.md
# Social share meta check
Pages: 1 Findings: 21 (HIGH 1, MEDIUM 10, LOW 10) Page URL: https://shop.example.com/shoes/trail-grip
## product-page.html
Preview as built from the tags (card: large)
| Field | Shown |
|---|---|
| Domain | shop.example.com |
| Title | Peakline Shop |
| Description | Grippy, light trail running shoes tested on wet granite, roots and mud. Compare fit, drop and sole wear across three widths, read 240 reviews from real runners, and order with free returns within 6... |
| Image | /img/logo.svg |
| Sev | Code | Tag | Line | Problem | Fix |
|---|---|---|---|---|---|
| HIGH | RELATIVE_URL | og:image | 13 | og:image is relative (/img/logo.svg); crawlers need an absolute URL - would mean https://shop.example.com/img/logo.svg | write the full URL, e.g. https://shop.example.com/img/logo.svg |
| MEDIUM | RELATIVE_URL | canonical | 8 | canonical is relative (/shoes/trail-grip); crawlers need an absolute URL - would mean https://shop.example.com/shoes/trail-grip | write the full URL, e.g. https://shop.example.com/shoes/trail-grip |
| MEDIUM | TRACKING_PARAMS | og:url | 12 | og:url carries tracking parameters (utm_source=newsletter); every share then counts the campaign | use the clean URL without ?utm_/fbclid/gclid |
| MEDIUM | IMAGE_SVG | og:image | 13 | SVG images are not rendered by Facebook, X, LinkedIn or Slack | export a PNG or JPEG |
| MEDIUM | IMAGE_SMALL | og:image | 13 | image is 800x800 (declared); large previews need at least 1200x630 (shown small or blurry) | use 1200x630 (2400x1260 for sharp retina) |
| MEDIUM | PLACEHOLDER_VALUE | og:title | 16 | og:title looks like an unrendered template or placeholder: '{{ product.name }} / Peakline' | fix the template variable |
| MEDIUM | DUPLICATE_TAG | og:title | 16 | 2 different og:title values; apps pick the first or last unpredictably | keep one (often a theme and an SEO plugin both add tags) |
| MEDIUM | TWITTER_CARD_INVALID | twitter:card | 18 | twitter:card 'large' is not one of summary, summary_large_image, app, player | use summary_large_image (or summary) |
| MEDIUM | TAG_OUTSIDE_HEAD | og:image | 26 | og:image is inside `<body>`; Facebook and LinkedIn read only `<head>` | move into `<head>` |
| MEDIUM | TAG_OUTSIDE_HEAD | og:image:width | 27 | og:image:width is inside `<body>`; Facebook and LinkedIn read only `<head>` | move into `<head>` |
| MEDIUM | TAG_OUTSIDE_HEAD | og:image:height | 28 | og:image:height is inside `<body>`; Facebook and LinkedIn read only `<head>` | move into `<head>` |
| LOW | OG_MISSING | og:site_name | - | og:site_name missing: LinkedIn and Slack show the bare domain instead of your brand | `<meta property="og:site_name" content="Brand">` |
| LOW | CHARSET_MISSING | meta charset | - | no `<meta charset>`; non-ASCII titles can show as mojibake in previews | `<meta charset="utf-8">` first in `<head>` |
| LOW | TITLE_LENGTH | `<title>` | 5 | title is 8 characters (aim for 15-60): 'Peakline' | front-load the key words; search results cut around 60 characters |
| LOW | DESCRIPTION_LENGTH | meta description | 7 | description is 6 characters (aim for 50-160) | rewrite to one or two sentences with the key benefit first |
| LOW | CANONICAL_OG_URL_MISMATCH | og:url | 12 | og:url (https://shop.example.com/shoes/trail-grip?utm_source=newsletter) differs from canonical (/shoes/trail-grip) | use the canonical URL in og:url |
| LOW | IMAGE_ALT_MISSING | og:image:alt | 13 | no og:image:alt (screen readers on X/LinkedIn get nothing) | describe the image in one sentence |
| LOW | OG_LOCALE_FORMAT | og:locale | 14 | og:locale 'en-us' should look like en_US (underscore, region in capitals) | e.g. en_US, de_DE, pt_BR |
| LOW | OG_DESCRIPTION_LONG | og:description | 17 | og:description is 269 characters; most apps show ~2-3 lines | keep under ~200 characters |
| LOW | OG_USES_NAME_ATTR | og:description | 17 | og:description uses name= instead of property= (Open Graph spec; some parsers skip it) | `<meta property="og:description" ...>` |
| LOW | TWITTER_HANDLE_FORMAT | twitter:site | 19 | twitter:site 'https://x.com/peakline' should be an @handle | e.g. @brand |
Offline check of the saved HTML only: tags injected by JavaScript after load are invisible to most crawlers too, but redirects, robots rules, the live image URL and each platform's preview cache were not tested.
FILE:scripts/og_meta_check.py
#!/usr/bin/env python3
"""Open Graph and social share meta tag checker (Python 3.8+ standard library only).
Reads saved HTML (a file, several files, or a folder of *.html) and checks what link
previews on Facebook, LinkedIn, X, Slack, Discord, WhatsApp and iMessage will be built from:
<title>, meta description, rel=canonical, og:* (title, description, type, url, image,
image:width/height/alt/type, site_name, locale) and twitter:* card tags.
It flags missing and duplicate tags, relative or http:// URLs, canonical vs og:url
mismatches, title/description length, image size hints (declared or, with --root, read
from local PNG/JPEG/GIF/WebP files), SVG images, wrong attributes (name= vs property=),
tags outside <head>, and template placeholders ({{ }}, TODO, undefined).
Usage:
python3 og_meta_check.py page.html [more.html | folder/] [--url https://example.com/page]
[--root public/] [--min-severity LOW|MEDIUM|HIGH] [--ignore CODE,..] [--json]
python3 og_meta_check.py --self-test
--url the page's public URL (used to resolve relative links and compare canonical/og:url)
--root local folder that maps to the site root, so /img/share.png is read from root/img/share.png
Exit codes: 0 = no HIGH findings, 1 = HIGH findings, 2 = bad input. No network calls are made.
"""
import argparse
import json
import os
import re
import struct
import sys
from html.parser import HTMLParser
from urllib.parse import urljoin, urlsplit
SEV_ORDER = {"LOW": 1, "MEDIUM": 2, "HIGH": 3}
TWITTER_CARDS = ("summary", "summary_large_image", "app", "player")
SINGLE_OG = ("og:title", "og:description", "og:type", "og:url", "og:site_name", "og:locale")
PLACEHOLDER_RE = re.compile(r"(\{\{|\}\}|\$\{|<%|%>|\bTODO\b|\bTBD\b|lorem ipsum|\bundefined\b|\bnull\b|\[object Object\]|NaN)", re.I)
TITLE_RANGE = (15, 60) # search results show roughly 50-60 characters
DESC_RANGE = (50, 160)
OG_TITLE_MAX = 90 # Facebook/LinkedIn cut long titles around 2 lines
OG_DESC_MAX = 200
IMG_MIN = (200, 200) # Facebook minimum
IMG_LARGE = (1200, 630) # recommended 1.91:1
IMG_MAX_BYTES = 5 * 1024 * 1024 # X limit 5 MB; Facebook 8 MB
class Meta:
def __init__(self, key, attr, content, line, in_head):
self.key, self.attr, self.content, self.line, self.in_head = key, attr, content, line, in_head
class PageParser(HTMLParser):
def __init__(self):
super().__init__(convert_charrefs=True)
self.metas, self.links, self.titles = [], [], []
self.in_head = True # before <body> starts, tags count as head (HTML parsing rules)
self.seen_head = False
self._in_title, self._title_buf, self._title_line = False, [], 0
self.charset = None
self.lang = None
def handle_starttag(self, tag, attrs):
a = {k.lower(): (v if v is not None else "") for k, v in attrs}
line = self.getpos()[0]
if tag == "html":
self.lang = a.get("lang")
elif tag == "head":
self.seen_head = True
self.in_head = True
elif tag == "body":
self.in_head = False
elif tag == "title":
self._in_title, self._title_buf, self._title_line = True, [], line
elif tag == "meta":
if "charset" in a:
self.charset = a["charset"]
for attr in ("property", "name", "itemprop"):
if a.get(attr):
self.metas.append(Meta(a[attr].strip().lower(), attr, a.get("content", None), line, self.in_head))
break
elif tag == "link":
rel = a.get("rel", "").lower().split()
self.links.append({"rel": rel, "href": a.get("href", ""), "line": line, "in_head": self.in_head,
"sizes": a.get("sizes", "")})
if tag in ("div", "p", "main", "article", "section", "h1", "img", "span", "header", "nav") and self.in_head and self.seen_head:
self.in_head = False # implied <body>
def handle_startendtag(self, tag, attrs):
self.handle_starttag(tag, attrs)
def handle_endtag(self, tag):
if tag == "head":
self.in_head = False
elif tag == "title" and self._in_title:
self._in_title = False
self.titles.append((" ".join("".join(self._title_buf).split()), self._title_line))
def handle_data(self, data):
if self._in_title:
self._title_buf.append(data)
def image_size(path):
"""Return (width, height, format) for PNG, GIF, JPEG or WebP, else None."""
with open(path, "rb") as fh:
head = fh.read(32)
if head[:8] == b"\x89PNG\r\n\x1a\n":
w, h = struct.unpack(">II", head[16:24])
return w, h, "png"
if head[:6] in (b"GIF87a", b"GIF89a"):
w, h = struct.unpack("<HH", head[6:10])
return w, h, "gif"
if head[:4] == b"RIFF" and head[8:12] == b"WEBP":
fh.seek(12)
chunk = fh.read(30)
kind = chunk[:4]
if kind == b"VP8X":
w = int.from_bytes(chunk[12:15], "little") + 1
h = int.from_bytes(chunk[15:18], "little") + 1
return w, h, "webp"
if kind == b"VP8 ":
w, h = struct.unpack("<HH", chunk[14:18])
return w & 0x3FFF, h & 0x3FFF, "webp"
if kind == b"VP8L":
b = chunk[9:13]
bits = int.from_bytes(b, "little")
return (bits & 0x3FFF) + 1, ((bits >> 14) & 0x3FFF) + 1, "webp"
return None
if head[:2] == b"\xff\xd8":
fh.seek(2)
while True:
m = fh.read(2)
if len(m) < 2 or m[0] != 0xFF:
return None
marker = m[1]
if marker in (0xD8, 0x01) or 0xD0 <= marker <= 0xD7:
continue
seg = fh.read(2)
if len(seg) < 2:
return None
length = struct.unpack(">H", seg)[0]
if marker in (0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF):
data = fh.read(5)
h, w = struct.unpack(">HH", data[1:5])
return w, h, "jpeg"
fh.seek(length - 2, 1)
return None
def analyze(html, page_url="", root=None, name="page.html"):
p = PageParser()
p.feed(html)
p.close()
f = []
def add(sev, code, tag, problem, fix, line=0):
f.append({"severity": sev, "code": code, "tag": tag, "problem": problem, "fix": fix, "line": line, "file": name})
metas = {}
for m in p.metas:
metas.setdefault(m.key, []).append(m)
def first(key):
v = metas.get(key)
return v[0] if v else None
def val(key):
m = first(key)
return (m.content or "").strip() if m and m.content is not None else None
page_https = page_url.lower().startswith("https://")
def check_abs(tag, url, line, sev_rel="HIGH"):
if not url:
return
s = urlsplit(url)
if not s.scheme:
resolved = urljoin(page_url, url) if page_url else None
add(sev_rel, "RELATIVE_URL", tag, "%s is relative (%s); crawlers need an absolute URL%s" %
(tag, url, (" - would mean " + resolved) if resolved else ""),
"write the full URL, e.g. %s" % (resolved or "https://example.com" + ("" if url.startswith("/") else "/") + url), line)
elif s.scheme == "http" and (page_https or tag.startswith(("og:image", "twitter:image"))):
add("MEDIUM", "HTTP_URL", tag, "%s uses http:// (%s); some apps skip insecure images or mix up the canonical" % (tag, url),
"use https://", line)
# ---- title / description
titles = p.titles
if not titles or not titles[0][0]:
add("HIGH", "TITLE_MISSING", "<title>", "no <title>; search results and many previews fall back to the URL",
"add a specific <title> of 15-60 characters")
else:
t, line = titles[0]
if len(titles) > 1:
add("MEDIUM", "TITLE_MULTIPLE", "<title>", "%d <title> elements; only the first is used" % len(titles), "keep one", titles[1][1])
if len(t) < TITLE_RANGE[0] or len(t) > TITLE_RANGE[1]:
add("LOW", "TITLE_LENGTH", "<title>", "title is %d characters (aim for %d-%d): %r" % (len(t), TITLE_RANGE[0], TITLE_RANGE[1], t),
"front-load the key words; search results cut around 60 characters", line)
desc = val("description")
if desc is None or desc == "":
add("MEDIUM", "DESCRIPTION_MISSING", "meta description", "no meta description; previews and search snippets pick random page text",
"add <meta name=\"description\" content=\"...\"> of 50-160 characters")
elif len(desc) < DESC_RANGE[0] or len(desc) > DESC_RANGE[1]:
add("LOW", "DESCRIPTION_LENGTH", "meta description", "description is %d characters (aim for %d-%d)" % (len(desc), DESC_RANGE[0], DESC_RANGE[1]),
"rewrite to one or two sentences with the key benefit first", first("description").line)
if len(metas.get("description", [])) > 1:
add("MEDIUM", "DUPLICATE_TAG", "meta description", "%d meta descriptions; the first is used" % len(metas["description"]), "keep one",
metas["description"][1].line)
# ---- canonical
canon = [l for l in p.links if "canonical" in l["rel"]]
canon_url = None
if not canon:
add("MEDIUM", "CANONICAL_MISSING", "link rel=canonical", "no canonical URL; shares with ?utm= or ?ref= parameters count as separate pages",
"<link rel=\"canonical\" href=\"https://example.com/clean-path\">")
else:
if len(set(l["href"] for l in canon)) > 1:
add("HIGH", "CANONICAL_MULTIPLE", "link rel=canonical", "%d different canonical URLs; search engines may ignore all of them" % len(canon),
"keep exactly one", canon[1]["line"])
canon_url = canon[0]["href"].strip()
if not canon_url:
add("HIGH", "CANONICAL_EMPTY", "link rel=canonical", "canonical href is empty", "set the full URL", canon[0]["line"])
else:
check_abs("canonical", canon_url, canon[0]["line"], "MEDIUM")
if not canon[0]["in_head"]:
add("MEDIUM", "TAG_OUTSIDE_HEAD", "link rel=canonical", "canonical is in <body>; search engines ignore it there", "move into <head>", canon[0]["line"])
if page_url and urlsplit(canon_url).scheme and urlsplit(canon_url).netloc != urlsplit(page_url).netloc:
add("LOW", "CANONICAL_OTHER_HOST", "link rel=canonical", "canonical points to another host (%s)" % urlsplit(canon_url).netloc,
"fine for syndicated copies; otherwise use this page's own URL", canon[0]["line"])
# ---- Open Graph core
for key, sev, why, fix in (
("og:title", "MEDIUM", "previews fall back to <title> (often with the site name suffix)", "<meta property=\"og:title\" content=\"...\">"),
("og:description", "LOW", "previews fall back to the meta description or page text", "<meta property=\"og:description\" content=\"...\">"),
("og:type", "LOW", "defaults to website; use article or product where it fits", "<meta property=\"og:type\" content=\"website\">"),
("og:url", "LOW", "shares are not merged onto one canonical URL (like counts split)", "<meta property=\"og:url\" content=\"<canonical URL>\">"),
("og:site_name", "LOW", "LinkedIn and Slack show the bare domain instead of your brand", "<meta property=\"og:site_name\" content=\"Brand\">")):
if first(key) is None:
add(sev, "OG_MISSING", key, "%s missing: %s" % (key, why), fix)
elif not val(key):
add("MEDIUM", "EMPTY_CONTENT", key, "%s has an empty content attribute" % key, "fill it or remove the tag", first(key).line)
ogt = val("og:title")
if ogt and len(ogt) > OG_TITLE_MAX:
add("LOW", "OG_TITLE_LONG", "og:title", "og:title is %d characters; previews cut it after ~2 lines" % len(ogt), "keep under ~70-90 characters", first("og:title").line)
ogd = val("og:description")
if ogd and len(ogd) > OG_DESC_MAX:
add("LOW", "OG_DESCRIPTION_LONG", "og:description", "og:description is %d characters; most apps show ~2-3 lines" % len(ogd), "keep under ~200 characters", first("og:description").line)
if first("og:url") and val("og:url"):
check_abs("og:url", val("og:url"), first("og:url").line)
canon_abs = urljoin(page_url, canon_url) if canon_url and page_url else canon_url
if canon_abs and urlsplit(canon_abs).scheme and val("og:url").rstrip("/") != canon_abs.rstrip("/"):
add("LOW", "CANONICAL_OG_URL_MISMATCH", "og:url", "og:url (%s) differs from canonical (%s)" % (val("og:url"), canon_url),
"use the canonical URL in og:url", first("og:url").line)
for key, u, ln in (("og:url", val("og:url"), first("og:url").line if first("og:url") else 0),
("canonical", canon_url, canon[0]["line"] if canon else 0)):
if u and re.search(r"[?&](utm_[a-z]+|fbclid|gclid|ref|mc_cid)=", u, re.I):
add("MEDIUM", "TRACKING_PARAMS", key, "%s carries tracking parameters (%s); every share then counts the campaign" % (key, urlsplit(u).query),
"use the clean URL without ?utm_/fbclid/gclid", ln)
loc = val("og:locale")
if loc and not re.match(r"^[a-z]{2,3}_[A-Z]{2}$", loc):
add("LOW", "OG_LOCALE_FORMAT", "og:locale", "og:locale %r should look like en_US (underscore, region in capitals)" % loc,
"e.g. en_US, de_DE, pt_BR", first("og:locale").line)
ogtype = val("og:type")
if ogtype and ogtype not in ("website", "article", "book", "profile", "product", "music.song", "music.album",
"music.playlist", "music.radio_station", "video.movie", "video.episode",
"video.tv_show", "video.other", "og:product", "product.item", "business.business",
"restaurant.restaurant", "place"):
add("LOW", "OG_TYPE_UNKNOWN", "og:type", "og:type %r is not a standard type" % ogtype, "website, article, product, profile...", first("og:type").line)
# ---- image
img_meta = first("og:image") or first("og:image:url") or first("og:image:secure_url")
img = (img_meta.content or "").strip() if img_meta and img_meta.content else ""
tw_img = val("twitter:image") or val("twitter:image:src")
if not img:
add("HIGH", "OG_IMAGE_MISSING", "og:image", "no og:image: shares show a plain text link or a random page image",
"add a 1200x630 image: <meta property=\"og:image\" content=\"https://.../share.png\">")
else:
check_abs("og:image", img, img_meta.line)
if re.search(r"\.svg(\?|#|$)", img, re.I):
add("MEDIUM", "IMAGE_SVG", "og:image", "SVG images are not rendered by Facebook, X, LinkedIn or Slack", "export a PNG or JPEG", img_meta.line)
w, h = val("og:image:width"), val("og:image:height")
size, source = None, ""
if w and h and w.isdigit() and h.isdigit():
size, source = (int(w), int(h)), "declared"
elif w or h:
add("LOW", "IMAGE_DIMENSIONS_INVALID", "og:image:width/height", "width/height must both be whole numbers (got %r x %r)" % (w, h),
"e.g. 1200 and 630", img_meta.line)
if root:
local = _local_path(img, page_url, root)
if local and os.path.isfile(local):
real = image_size(local)
nbytes = os.path.getsize(local)
if real:
if size and (size[0], size[1]) != (real[0], real[1]):
add("MEDIUM", "IMAGE_DIMENSIONS_WRONG", "og:image:width/height",
"declared %dx%d but the file is %dx%d" % (size[0], size[1], real[0], real[1]),
"declare the real size", img_meta.line)
size, source = (real[0], real[1]), "file %s" % os.path.relpath(local, root)
if nbytes > IMG_MAX_BYTES:
add("MEDIUM", "IMAGE_TOO_HEAVY", "og:image", "image file is %.1f MB (X rejects over 5 MB, previews time out)" % (nbytes / 1048576.0),
"compress to under ~1 MB (JPEG/WebP quality 80)", img_meta.line)
elif local:
add("MEDIUM", "IMAGE_NOT_FOUND", "og:image", "image not found under --root: %s" % local, "check the path and deploy the file", img_meta.line)
if size is None:
add("LOW", "IMAGE_DIMENSIONS_MISSING", "og:image:width/height",
"no og:image:width/height; Facebook cannot render the image on the very first share",
"add og:image:width 1200 and og:image:height 630", img_meta.line)
else:
iw, ih = size
if iw < IMG_MIN[0] or ih < IMG_MIN[1]:
add("HIGH", "IMAGE_TOO_SMALL", "og:image", "image is %dx%d (%s); below 200x200 Facebook ignores it" % (iw, ih, source),
"use 1200x630", img_meta.line)
elif iw < IMG_LARGE[0] or ih < IMG_LARGE[1]:
add("MEDIUM", "IMAGE_SMALL", "og:image", "image is %dx%d (%s); large previews need at least 1200x630 (shown small or blurry)" % (iw, ih, source),
"use 1200x630 (2400x1260 for sharp retina)", img_meta.line)
ratio = iw / float(ih)
card = val("twitter:card") or ""
if card == "summary_large_image" or not card:
if not (1.75 <= ratio <= 2.05):
add("LOW", "IMAGE_ASPECT", "og:image", "aspect ratio %.2f:1 will be cropped in large cards (aim for 1.91:1)" % ratio,
"1200x630, keep text inside the centre 80%", img_meta.line)
if not val("og:image:alt"):
add("LOW", "IMAGE_ALT_MISSING", "og:image:alt", "no og:image:alt (screen readers on X/LinkedIn get nothing)",
"describe the image in one sentence", img_meta.line)
if tw_img:
check_abs("twitter:image", tw_img, first("twitter:image").line if first("twitter:image") else 0)
# ---- twitter / X card
card = val("twitter:card")
if card is None:
add("LOW" if img else "MEDIUM", "TWITTER_CARD_MISSING", "twitter:card",
"no twitter:card; X shows a small summary card at best", "<meta name=\"twitter:card\" content=\"summary_large_image\">")
elif card not in TWITTER_CARDS:
add("MEDIUM", "TWITTER_CARD_INVALID", "twitter:card", "twitter:card %r is not one of %s" % (card, ", ".join(TWITTER_CARDS)),
"use summary_large_image (or summary)", first("twitter:card").line)
elif card == "summary_large_image" and not (img or tw_img):
add("MEDIUM", "TWITTER_CARD_NO_IMAGE", "twitter:card", "summary_large_image without any image", "add og:image or twitter:image", first("twitter:card").line)
for key in ("twitter:site", "twitter:creator"):
v = val(key)
if v and not re.match(r"^@\w{1,15}$", v):
add("LOW", "TWITTER_HANDLE_FORMAT", key, "%s %r should be an @handle" % (key, v), "e.g. @brand", first(key).line)
# ---- attribute and placement problems, duplicates, placeholders
for m in p.metas:
if m.key.startswith("og:") and m.attr == "name":
add("LOW", "OG_USES_NAME_ATTR", m.key, "%s uses name= instead of property= (Open Graph spec; some parsers skip it)" % m.key,
"<meta property=\"%s\" ...>" % m.key, m.line)
if (m.key.startswith(("og:", "twitter:")) or m.key == "description") and not m.in_head:
add("MEDIUM", "TAG_OUTSIDE_HEAD", m.key, "%s is inside <body>; Facebook and LinkedIn read only <head>" % m.key, "move into <head>", m.line)
if m.content is None and (m.key.startswith(("og:", "twitter:")) or m.key == "description"):
add("MEDIUM", "NO_CONTENT_ATTR", m.key, "%s has no content attribute (value=/text= are ignored)" % m.key, "use content=\"...\"", m.line)
if m.content and PLACEHOLDER_RE.search(m.content) and (m.key.startswith(("og:", "twitter:")) or m.key == "description"):
add("MEDIUM", "PLACEHOLDER_VALUE", m.key, "%s looks like an unrendered template or placeholder: %r" % (m.key, m.content[:60]),
"fix the template variable", m.line)
for key in SINGLE_OG + ("twitter:card", "twitter:title", "twitter:description"):
ms = metas.get(key, [])
if len(ms) > 1 and len(set((m.content or "") for m in ms)) > 1:
add("MEDIUM", "DUPLICATE_TAG", key, "%d different %s values; apps pick the first or last unpredictably" % (len(ms), key),
"keep one (often a theme and an SEO plugin both add tags)", ms[1].line)
if titles and titles[0][0] and PLACEHOLDER_RE.search(titles[0][0]):
add("MEDIUM", "PLACEHOLDER_VALUE", "<title>", "title looks like a placeholder: %r" % titles[0][0], "fix the template", titles[0][1])
if not p.charset:
add("LOW", "CHARSET_MISSING", "meta charset", "no <meta charset>; non-ASCII titles can show as mojibake in previews", "<meta charset=\"utf-8\"> first in <head>")
preview = {
"title": _cut(ogt or (titles[0][0] if titles else "") or "", OG_TITLE_MAX),
"description": _cut(ogd or desc or "", OG_DESC_MAX),
"image": img or tw_img or "",
"domain": urlsplit(val("og:url") or canon_url or page_url or "").netloc,
"card": card or ("summary" if img else "none"),
}
return f, preview
def _cut(text, n):
return text if len(text) <= n else text[:n - 3].rstrip() + "..."
def _code(text):
"""Wrap <tags> in backticks so markdown shows them instead of swallowing them as HTML."""
return re.sub(r"(<[^<>]+>)", r"`\1`", text.replace("|", "/"))
def _local_path(url, page_url, root):
s = urlsplit(url)
if s.scheme and page_url and s.netloc and s.netloc != urlsplit(page_url).netloc:
return None # image on another host (CDN): cannot check locally
path = urlsplit(urljoin(page_url or "https://local.invalid/", url)).path
return os.path.join(root, path.lstrip("/"))
def run_files(paths, page_url, root, min_sev, ignore):
results = []
files = []
for p in paths:
if os.path.isdir(p):
for dp, _, fns in os.walk(p):
files += [os.path.join(dp, fn) for fn in sorted(fns) if fn.lower().endswith((".html", ".htm"))]
else:
files.append(p)
if not files:
raise ValueError("no HTML files found")
for fp in files:
with open(fp, encoding="utf-8", errors="replace") as fh:
html = fh.read()
if not re.search(r"<(html|head|meta|title|link)\b", html, re.I):
raise ValueError("%s does not look like HTML" % fp)
fs, prev = analyze(html, page_url if len(files) == 1 else "", root, os.path.basename(fp))
fs = [x for x in fs if SEV_ORDER[x["severity"]] >= SEV_ORDER[min_sev] and x["code"] not in ignore]
fs.sort(key=lambda x: (-SEV_ORDER[x["severity"]], x["line"]))
results.append((fp, fs, prev))
return results
def render_md(results, page_url):
allf = [x for _, fs, _ in results for x in fs]
c = {k: sum(1 for x in allf if x["severity"] == k) for k in ("HIGH", "MEDIUM", "LOW")}
o = ["# Social share meta check", "",
"Pages: %d Findings: %d (HIGH %d, MEDIUM %d, LOW %d)%s" % (len(results), len(allf), c["HIGH"], c["MEDIUM"], c["LOW"],
(" Page URL: " + page_url) if page_url and len(results) == 1 else "")]
for fp, fs, prev in results:
o += ["", "## %s" % os.path.basename(fp), "",
"Preview as built from the tags (card: %s)" % prev["card"], "",
"| Field | Shown |", "|---|---|",
"| Domain | %s |" % (prev["domain"] or "(unknown)"),
"| Title | %s |" % (prev["title"].replace("|", "/") or "(none)"),
"| Description | %s |" % (prev["description"].replace("|", "/") or "(none)"),
"| Image | %s |" % (prev["image"] or "(none)"), ""]
if not fs:
o.append("No findings.")
continue
o += ["| Sev | Code | Tag | Line | Problem | Fix |", "|---|---|---|---|---|---|"]
for x in fs:
o.append("| %s | %s | %s | %s | %s | %s |" % (x["severity"], x["code"], _code(x["tag"]), x["line"] or "-",
_code(x["problem"]), _code(x["fix"])))
o += ["", "Offline check of the saved HTML only: tags injected by JavaScript after load are invisible to most crawlers too, "
"but redirects, robots rules, the live image URL and each platform's preview cache were not tested."]
return "\n".join(o) + "\n"
# ----------------------------------------------------------------------------- self-test
GOOD = """<!doctype html><html lang="en"><head><meta charset="utf-8">
<title>Trail Running Shoes for Wet Rocks | Peakline</title>
<meta name="description" content="Grippy, light trail shoes tested on wet granite and mud. Compare fit, drop and sole wear, then pick your size.">
<link rel="canonical" href="https://shop.example.com/shoes/trail-grip">
<meta property="og:type" content="product"><meta property="og:site_name" content="Peakline">
<meta property="og:title" content="Trail Running Shoes for Wet Rocks">
<meta property="og:description" content="Grippy, light trail shoes tested on wet granite and mud.">
<meta property="og:url" content="https://shop.example.com/shoes/trail-grip">
<meta property="og:image" content="https://shop.example.com/img/trail-grip-share.jpg">
<meta property="og:image:width" content="1200"><meta property="og:image:height" content="630">
<meta property="og:image:alt" content="Blue trail shoe on a wet granite slab">
<meta property="og:locale" content="en_US">
<meta name="twitter:card" content="summary_large_image"><meta name="twitter:site" content="@peakline">
</head><body><h1>Trail Grip</h1></body></html>"""
def self_test():
import tempfile
cases = []
def codes(html, url="https://shop.example.com/shoes/trail-grip", root=None):
return [x["code"] for x in analyze(html, url, root)[0]]
def case(name, fn):
try:
ok, err = bool(fn()), ""
except Exception as e: # noqa
ok, err = False, repr(e)
cases.append((name, ok, err))
def sub(a, b):
assert a in GOOD, a
return GOOD.replace(a, b)
case("good page is clean", lambda: codes(GOOD) == [])
case("missing title", lambda: "TITLE_MISSING" in codes(sub("<title>Trail Running Shoes for Wet Rocks | Peakline</title>", "")))
case("two titles", lambda: "TITLE_MULTIPLE" in codes(sub("</head>", "<title>Other</title></head>")))
case("title too long", lambda: "TITLE_LENGTH" in codes(sub("Trail Running Shoes for Wet Rocks | Peakline", "x" * 75)))
case("description missing", lambda: "DESCRIPTION_MISSING" in codes(re.sub(r'<meta name="description"[^>]*>', "", GOOD)))
case("description short", lambda: "DESCRIPTION_LENGTH" in codes(re.sub(r'(<meta name="description" content=")[^"]*', r"\1Shoes.", GOOD)))
case("canonical missing", lambda: "CANONICAL_MISSING" in codes(re.sub(r'<link rel="canonical"[^>]*>', "", GOOD)))
case("canonical relative", lambda: "RELATIVE_URL" in codes(sub('href="https://shop.example.com/shoes/trail-grip"', 'href="/shoes/trail-grip"')))
case("two canonicals", lambda: "CANONICAL_MULTIPLE" in codes(sub("</head>", '<link rel="canonical" href="https://shop.example.com/x"></head>')))
case("canonical vs og:url mismatch", lambda: "CANONICAL_OG_URL_MISMATCH" in codes(sub('og:url" content="https://shop.example.com/shoes/trail-grip"', 'og:url" content="https://shop.example.com/shoes/trail-grip?utm_source=x"')))
case("trailing slash difference ignored", lambda: "CANONICAL_OG_URL_MISMATCH" not in codes(sub('og:url" content="https://shop.example.com/shoes/trail-grip"', 'og:url" content="https://shop.example.com/shoes/trail-grip/"')))
case("og:image missing HIGH", lambda: "OG_IMAGE_MISSING" in codes(re.sub(r'<meta property="og:image[^>]*>', "", GOOD)))
case("og:image relative HIGH", lambda: [x["severity"] for x in analyze(sub('content="https://shop.example.com/img/trail-grip-share.jpg"', 'content="/img/trail-grip-share.jpg"'), "https://shop.example.com/p")[0] if x["code"] == "RELATIVE_URL"] == ["HIGH"])
case("og:image http", lambda: "HTTP_URL" in codes(sub("https://shop.example.com/img/", "http://shop.example.com/img/")))
case("og:image svg", lambda: "IMAGE_SVG" in codes(sub("trail-grip-share.jpg", "logo.svg")))
case("image dims missing", lambda: "IMAGE_DIMENSIONS_MISSING" in codes(re.sub(r'<meta property="og:image:(width|height)"[^>]*>', "", GOOD)))
case("image too small HIGH", lambda: "IMAGE_TOO_SMALL" in codes(sub('content="1200"', 'content="150"').replace('content="630"', 'content="150"')))
case("image small MEDIUM", lambda: "IMAGE_SMALL" in codes(sub('content="1200"', 'content="600"').replace('content="630"', 'content="315"')))
case("image square aspect for large card", lambda: "IMAGE_ASPECT" in codes(sub('content="630"', 'content="1200"')))
case("summary card allows square", lambda: "IMAGE_ASPECT" not in codes(sub('content="630"', 'content="1200"').replace("summary_large_image", "summary")))
case("image alt missing", lambda: "IMAGE_ALT_MISSING" in codes(re.sub(r'<meta property="og:image:alt"[^>]*>', "", GOOD)))
case("dims not numbers", lambda: "IMAGE_DIMENSIONS_INVALID" in codes(sub('content="1200"', 'content="1200px"')))
case("og:title missing", lambda: "OG_MISSING" in codes(re.sub(r'<meta property="og:title"[^>]*>', "", GOOD)))
case("empty og:description", lambda: "EMPTY_CONTENT" in codes(re.sub(r'(<meta property="og:description" content=")[^"]*', r"\1", GOOD)))
case("og:title too long", lambda: "OG_TITLE_LONG" in codes(sub('og:title" content="Trail Running Shoes for Wet Rocks"', 'og:title" content="' + "y" * 120 + '"')))
case("tracking params in og:url", lambda: "TRACKING_PARAMS" in codes(sub('og:url" content="https://shop.example.com/shoes/trail-grip"', 'og:url" content="https://shop.example.com/shoes/trail-grip?utm_source=nl"')))
case("relative canonical compared after resolving", lambda: "CANONICAL_OG_URL_MISMATCH" in codes(sub('href="https://shop.example.com/shoes/trail-grip"', 'href="/shoes/other"')))
case("preview cut with ellipsis", lambda: _cut("a" * 300, 200).endswith("...") and len(_cut("a" * 300, 200)) <= 200)
case("og:locale format", lambda: "OG_LOCALE_FORMAT" in codes(sub("en_US", "en-us")))
case("og:type unknown", lambda: "OG_TYPE_UNKNOWN" in codes(sub('og:type" content="product"', 'og:type" content="shoe"')))
case("og with name attr", lambda: "OG_USES_NAME_ATTR" in codes(sub('property="og:title"', 'name="og:title"')))
case("twitter card missing", lambda: "TWITTER_CARD_MISSING" in codes(re.sub(r'<meta name="twitter:card"[^>]*>', "", GOOD)))
case("twitter card invalid", lambda: "TWITTER_CARD_INVALID" in codes(sub("summary_large_image", "large")))
case("twitter handle format", lambda: "TWITTER_HANDLE_FORMAT" in codes(sub("@peakline", "https://x.com/peakline")))
case("large card without any image", lambda: "TWITTER_CARD_NO_IMAGE" in codes(re.sub(r'<meta property="og:image[^>]*>', "", GOOD)))
case("tag in body", lambda: "TAG_OUTSIDE_HEAD" in codes(sub("<h1>Trail Grip</h1>", '<h1>T</h1><meta property="og:video" content="https://a/v.mp4">')))
case("implied body by div", lambda: "TAG_OUTSIDE_HEAD" in codes(GOOD.replace("</head><body>", "<div>x</div>").replace('<meta name="twitter:site" content="@peakline">', "").replace("<h1>", '<meta name="twitter:site" content="@peakline"><h1>')))
case("placeholder value", lambda: "PLACEHOLDER_VALUE" in codes(sub('og:title" content="Trail Running Shoes for Wet Rocks"', 'og:title" content="{{ product.name }}"')))
case("undefined value", lambda: "PLACEHOLDER_VALUE" in codes(sub("Blue trail shoe on a wet granite slab", "undefined")))
case("duplicate og:title different values", lambda: "DUPLICATE_TAG" in codes(sub("</head>", '<meta property="og:title" content="Peakline Shop"></head>')))
case("duplicate same value tolerated", lambda: "DUPLICATE_TAG" not in codes(sub("</head>", '<meta property="og:title" content="Trail Running Shoes for Wet Rocks"></head>')))
case("no content attribute", lambda: "NO_CONTENT_ATTR" in codes(sub('<meta property="og:site_name" content="Peakline">', '<meta property="og:site_name" value="Peakline">')))
case("charset missing", lambda: "CHARSET_MISSING" in codes(sub('<meta charset="utf-8">', "")))
case("html entities decoded in title", lambda: analyze(re.sub(r'<meta property="og:title"[^>]*>', "", sub("Wet Rocks | Peakline", "Wet Rocks & Mud")))[1]["title"] == "Trail Running Shoes for Wet Rocks & Mud")
case("preview uses og values", lambda: analyze(GOOD, "https://shop.example.com/shoes/trail-grip")[1]["domain"] == "shop.example.com")
tmp = tempfile.mkdtemp()
os.makedirs(os.path.join(tmp, "img"))
png = b"\x89PNG\r\n\x1a\n" + b"\x00\x00\x00\x0dIHDR" + struct.pack(">II", 800, 418) + b"\x08\x02\x00\x00\x00"
with open(os.path.join(tmp, "img", "trail-grip-share.jpg"), "wb") as fh:
fh.write(png)
case("--root reads real image size", lambda: "IMAGE_DIMENSIONS_WRONG" in codes(GOOD, root=tmp) and "IMAGE_SMALL" in codes(GOOD, root=tmp))
case("--root image not found", lambda: "IMAGE_NOT_FOUND" in codes(sub("trail-grip-share.jpg", "nope.png"), root=tmp))
case("png size parser", lambda: image_size(os.path.join(tmp, "img", "trail-grip-share.jpg"))[:2] == (800, 418))
gif = b"GIF89a" + struct.pack("<HH", 1200, 630) + b"\x00" * 20
jpg = (b"\xff\xd8" + b"\xff\xe0" + struct.pack(">H", 16) + b"JFIF\x00" + b"\x00" * 9 +
b"\xff\xc0" + struct.pack(">H", 17) + b"\x08" + struct.pack(">HH", 630, 1200) + b"\x03" + b"\x00" * 9)
webp = b"RIFF" + b"\x00" * 4 + b"WEBPVP8X" + b"\x0a\x00\x00\x00" + b"\x00" * 4 + (1199).to_bytes(3, "little") + (629).to_bytes(3, "little")
for fn, data in (("a.gif", gif), ("a.jpg", jpg), ("a.webp", webp)):
with open(os.path.join(tmp, fn), "wb") as fh:
fh.write(data)
case("gif size parser", lambda: image_size(os.path.join(tmp, "a.gif"))[:2] == (1200, 630))
case("jpeg size parser", lambda: image_size(os.path.join(tmp, "a.jpg"))[:2] == (1200, 630))
case("webp size parser", lambda: image_size(os.path.join(tmp, "a.webp"))[:2] == (1200, 630))
case("cdn image skipped for --root", lambda: _local_path("https://cdn.other.net/a.png", "https://shop.example.com/", tmp) is None)
case("non-HTML file rejected", lambda: _rejects(tmp))
case("markdown render", lambda: "Preview as built" in render_md([("p.html", [], analyze(GOOD)[1])], ""))
passed = sum(1 for _, ok, _ in cases if ok)
for name, ok, err in cases:
print(("PASS " if ok else "FAIL ") + name + ((" " + err) if err else ""))
print("%d/%d passed" % (passed, len(cases)))
return 0 if passed == len(cases) else 1
def _rejects(tmp):
fp = os.path.join(tmp, "notes.md")
with open(fp, "w") as fh:
fh.write("# Notes\nuse <b>bold</b>\n")
try:
run_files([fp], "", None, "LOW", set())
except ValueError:
return True
return False
def main(argv=None):
ap = argparse.ArgumentParser(description="Check Open Graph / X card / canonical meta tags in saved HTML (offline).")
ap.add_argument("paths", nargs="*", help="HTML files or folders")
ap.add_argument("--url", default="", help="public URL of the page (single file)")
ap.add_argument("--root", default=None, help="local folder mapped to the site root, to read og:image files")
ap.add_argument("--min-severity", default="LOW", choices=["LOW", "MEDIUM", "HIGH"])
ap.add_argument("--ignore", default="", help="comma-separated finding codes to skip")
ap.add_argument("--json", action="store_true")
ap.add_argument("--self-test", action="store_true")
a = ap.parse_args(argv)
if a.self_test:
return self_test()
if not a.paths:
ap.error("give HTML files/folders or --self-test")
ignore = {c.strip() for c in a.ignore.split(",") if c.strip()}
try:
results = run_files(a.paths, a.url, a.root, a.min_severity, ignore)
except (OSError, ValueError) as e:
print("error: %s" % e, file=sys.stderr)
return 2
if a.json:
print(json.dumps([{"file": fp, "preview": prev, "findings": fs} for fp, fs, prev in results], indent=2))
else:
sys.stdout.write(render_md(results, a.url))
return 1 if any(x["severity"] == "HIGH" for _, fs, _ in results for x in fs) else 0
if __name__ == "__main__":
sys.exit(main())A detailed build prompt for AI coding models that produces a real-time, endlessly looping cyberpunk pixel-art city diorama for a 10-hour lo-fi music video: a rain-soaked megacity at 2:37 AM with warm windows, flickering neon, fog, steam and tiny moving details, rendered at 640×360 and upscaled exactly 3× with nearest-neighbor for razor-sharp pixels, all delivered as one standalone HTML file with a clean full-screen mode for recording. 来源:https://prompts.chat/prompts/cmw1ynqh10007l908w6kkni0t
# Build an Immersive, Animated Cyberpunk Pixel-Art Diorama Create a breathtaking, richly detailed, fully animated cyberpunk pixel-art diorama designed to serve as the endlessly looping visual backdrop for a 10-hour lo-fi music video on YouTube. I want something genuinely mesmerizing. A tiny, self-contained world that feels alive. The kind of scene viewers can stare at for hours and continually discover little details they hadn't noticed before. ## Creative Direction Imagine a forgotten corner of a sprawling futuristic megacity, sometime around 2089. It's 2:37 AM. Rain has been falling for days. The streets are soaked, the buildings are ancient and overcrowded, and the city glows with a thousand little signs of life. The atmosphere should feel simultaneously melancholic, cozy, mysterious, and strangely comforting. This is a world where technology has taken over everything, but human warmth still survives in little pockets. Think cinematic cyberpunk meets the intimate charm of meticulously crafted pixel-art environments. Don't settle for a generic neon skyline. Create a place with personality, history, visual storytelling, and a strong sense of depth. ## Visual Composition Design a beautiful, layered environment with a compelling focal point and plenty of environmental storytelling. Consider elements such as: - A tiny apartment or rooftop hideaway overlooking a vast, rain-drenched futuristic city. - Glowing windows revealing glimpses of other people's lives. - Holographic advertisements, animated signage, and distant traffic. - Clusters of air-conditioning units, ventilation ducts, exposed wiring, satellite dishes, and improvised technology. - Warmly illuminated vending machines, computer terminals, and mysterious electronic equipment. - Rainwater collecting in puddles and cascading from rooftops. - Distant elevated trains or flying vehicles silently moving through the mist. - Tiny human silhouettes occasionally visible behind windows. - Overgrown plants reclaiming corners of this technological world. These are creative possibilities, not a mandatory checklist. Make deliberate artistic choices. Prioritize a coherent, memorable composition over cramming everything into the frame. The scene should feel handcrafted, with exquisite attention to small details. ## Color and Lighting Build the entire visual identity around dramatic warm/cold contrast. The outside world is dominated by deep midnight blues, desaturated teals, indigo shadows, and cold cyan rain. Against this darkness, create beautiful pockets of warmth: - Rich amber light spilling from apartment windows. - Soft orange and golden illumination from lamps and electronics. - Occasional saturated magenta, electric violet, and crimson neon. - Warm reflections shimmering across cold, wet surfaces. Use lighting to tell a story. Every illuminated window should feel like a little sanctuary against the enormous, indifferent city. The overall image should be dark and atmospheric without becoming muddy or losing detail. ## Animation: Make the World Feel Alive This is absolutely critical. I don't want a static illustration with a generic rain effect. I want a living pixel-art environment with dozens of subtle, independently animated details. Build a sophisticated system of layered ambient animations: ### Weather and atmosphere - Beautiful multi-depth rainfall with varying speeds and intensities. - Occasional heavier gusts that push rain diagonally. - Thin layers of drifting fog and atmospheric haze. - Smoke rising from vents and chimneys, curling into the night. - Water dripping from ledges and splashing into puddles. - Ripples spreading across reflective surfaces. ### Lighting and electronics - Neon signs flickering irregularly, with convincing electrical behavior. - Screens cycling through mysterious data and advertisements. - Small indicator lights blinking asynchronously. - Warm interior lights gently fluctuating. - Occasional sparks from damaged electrical equipment. - Animated reflections responding to nearby light sources. ### Environmental life - Slowly rotating ventilation fans. - Distant vehicles passing behind buildings. - Tiny silhouettes moving behind illuminated windows. - Steam escaping from pipes. - Hanging cables or signs subtly swaying in the wind. - Occasional small, unexpected details that reward prolonged viewing. Every effect should have its own rhythm. Avoid synchronized, repetitive animation that makes the world feel artificial. ## Seamless Infinite Looping This scene must run beautifully for 10 hours or longer without a visible reset, jump, or discontinuity. Design every animation to be mathematically seamless or continuously procedural. Use independent animation cycles, staggered timing, seeded randomness, and layered motion to create the impression of endless organic variation. No sudden resets. No obvious looping rain textures. No visible animation seams. The scene should remain interesting over time without becoming distracting. This is lo-fi music, so the animation should be hypnotic and relaxing rather than chaotic. Reserve dramatic events for rare, carefully controlled moments. ## Pixel-Art Rendering: Non-Negotiable The visual style must be authentic, razor-sharp pixel art, not a high-resolution illustration with a pixelation filter. Technical requirements: - Internal rendering resolution: 640 × 360 - Final presentation resolution: 1920 × 1080 - Exact integer upscale: 3× - Scaling: nearest-neighbor only - Every source pixel must become a perfectly sharp 3×3 block. - Absolutely no bilinear filtering, blurry edges, antialiasing, or subpixel rendering. - No smooth vector graphics mixed into the pixel-art scene. - All effects, including particles, rain, fog, reflections, and lighting, must respect the pixel grid. - Maintain consistent pixel density across every visual layer. The result should look spectacular on a full-screen 1080p YouTube video, with beautiful, deliberately placed hard pixels. ## Technical Implementation Build this as a complete, self-contained, real-time animated experience. Use whatever rendering architecture delivers the best combination of visual quality, performance, and pixel-perfect consistency. HTML5 Canvas, WebGL, or a hybrid approach are all acceptable. Prioritize: - Smooth, efficient rendering at 1080p. - Deterministic, seamless animation. - Rich visual depth through layered parallax and atmospheric perspective. - Procedural particle systems for weather and environmental effects. - Carefully controlled lighting, blending, and reflections. - Stable performance over extremely long runtimes. - A fixed camera with no unintended movement or jitter. - Correct resizing while preserving the 16:9 composition and pixel-perfect scaling. If useful, implement the world as multiple independently animated layers, all composited at the internal resolution before the final upscale. Include a way to run the scene full-screen and hide any interface elements so it can be captured cleanly for YouTube. ## Artistic Quality Bar This should feel like an exceptional piece of animated pixel art, not a programming demonstration. Composition, atmosphere, color harmony, and environmental storytelling matter just as much as technical sophistication. Avoid generic procedural rectangles masquerading as buildings. Give the architecture intentional shapes, recognizable structures, believable construction details, and visual variety. Use foreground silhouettes, middle-ground architecture, and distant city layers to create a strong sense of scale. Every element should contribute to a cohesive world. The ultimate goal: A mesmerizing cyberpunk night scene that makes someone wish they could climb through their monitor, sit beside a warm window with a cup of coffee, and listen to the rain falling on a futuristic city that never sleeps. Make it beautiful. Make it atmospheric. Make it technically excellent. And above all, make it feel alive. Don't just describe how you would build it. Actually implement the complete working experience. Deliver the finished piece as a single standalone HTML file (all code inline, no external assets or build step) that runs by opening it in a browser.