diff --git a/.github/workflows/social-images.yml b/.github/workflows/social-images.yml new file mode 100644 index 00000000..e5a02689 --- /dev/null +++ b/.github/workflows/social-images.yml @@ -0,0 +1,94 @@ +name: Social Images + +on: + pull_request: + paths: + - 'content/**/*.md' + +jobs: + generate: + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ github.head_ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5 + with: + python-version: '3.x' + + - name: Install ImageMagick + run: | + sudo apt-get update -qq + sudo apt-get install -y imagemagick + # Ubuntu ships ImageMagick 6 (binary: convert). generate.py expects `magick` + # (IM7 name). Create an alias so the script works without modification. + sudo ln -sf /usr/bin/convert /usr/local/bin/magick + magick -version + + - name: Install Python dependencies + run: pip install -r scripts/social-images/requirements.txt + + - name: Find changed Markdown files + id: diff + run: | + git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }} + + git diff --name-only --diff-filter=AM FETCH_HEAD HEAD \ + | grep '^content/.*\.md$' > /tmp/added_modified.txt || true + + git diff --name-only --diff-filter=DR FETCH_HEAD HEAD \ + | grep '^content/.*\.md$' > /tmp/deleted.txt || true + + echo "has_added=$([ -s /tmp/added_modified.txt ] && echo 'true' || echo 'false')" >> "$GITHUB_OUTPUT" + echo "has_deleted=$([ -s /tmp/deleted.txt ] && echo 'true' || echo 'false')" >> "$GITHUB_OUTPUT" + + - name: Generate social images for added/modified files + if: steps.diff.outputs.has_added == 'true' + run: | + while IFS= read -r f; do + echo "Generating: $f" + python3 scripts/social-images/generate.py --file "$f" --force + done < /tmp/added_modified.txt + + - name: Remove orphaned images for deleted files + if: steps.diff.outputs.has_deleted == 'true' + run: | + while IFS= read -r f; do + # Mirror ContentFile._detect_type (generate.py:170): first segment after content/ + type="${f#content/}" + type="${type%%/*}" + # Mirror ContentFile._derive_slug (generate.py:178): parent dir for index files, else stem + filename="$(basename "$f")" + if [[ "$filename" == "index.md" || "$filename" == "_index.md" ]]; then + slug="$(basename "$(dirname "$f")")" + else + slug="${filename%.md}" + fi + img="static/images/social/${type}/${slug}.png" + echo "Removing orphan: $img" + git rm -f --ignore-unmatch "$img" + done < /tmp/deleted.txt + + - name: Commit and push images + if: github.event.pull_request.head.repo.full_name == github.repository + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add static/images/social/ + if git diff --cached --quiet; then + echo "No image changes to commit." + else + git commit -m "chore(social-images): sync images for changed content" + git push + fi + + - name: Notice for fork PRs + if: github.event.pull_request.head.repo.full_name != github.repository + run: | + echo "::notice::Fork PR — social images not committed automatically. Run 'python3 scripts/social-images/generate.py' locally (needs ImageMagick + Python deps) or ask a maintainer to regenerate." diff --git a/.gitignore b/.gitignore index f16f4566..b9bd6a6f 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,4 @@ haystack-advent content/advent-of-haystack/* !content/advent-of-haystack/_index.md -static/downloads \ No newline at end of file +static/downloads diff --git a/assets/jsconfig.json b/assets/jsconfig.json index b445bbec..2d705b54 100644 --- a/assets/jsconfig.json +++ b/assets/jsconfig.json @@ -3,7 +3,7 @@ "baseUrl": ".", "paths": { "*": [ - "..\\themes\\haystack\\assets\\*" + "../themes/haystack/assets/*" ] } } diff --git a/scripts/social-images/README.md b/scripts/social-images/README.md new file mode 100644 index 00000000..c2b0e03f --- /dev/null +++ b/scripts/social-images/README.md @@ -0,0 +1,121 @@ +# Social image generation + +Generates Open Graph / Twitter Card preview images for Hugo content by compositing text onto PNG templates using ImageMagick. + +Images are placed at `static/images/social/{type}/{slug}.png`. Hugo's `opengraph.html` partial automatically detects a generated image at that path and uses it — no front-matter changes needed in any content file. + +## Prerequisites + +```bash +brew install imagemagick +pip3 install -r scripts/social-images/requirements.txt +``` + +## CI integration + +`.github/workflows/social-images.yml` runs on every PR that changes a `content/**/*.md` file. It generates or removes images for the changed files and commits them back to the PR branch via `github-actions[bot]`. No local tooling required. + +Fork PRs emit a workflow notice instead — run `generate.py` locally or ask a maintainer. + +## Local usage + +```bash +# Preview everything without writing files +python3 scripts/social-images/generate.py --dry-run + +# Generate for all content types +python3 scripts/social-images/generate.py + +# One content type only +python3 scripts/social-images/generate.py --type release-notes + +# Single file +python3 scripts/social-images/generate.py --file content/release-notes/2.25.0.md +``` + +## Adding templates + +Place one PNG per content type in `scripts/social-images/templates/`: + +| File | Used for | +|---|---| +| `blog.png` | `content/blog/` posts | +| `release-notes.png` | `content/release-notes/` pages | +| `tutorials.png` | `content/tutorials/` pages | +| `cookbook.png` | `content/cookbook/` pages | +| `Integration.png` | `content/integrations/` pages | +| `fallback.png` | Everything else | + +Recommended size: **1200 × 630 px** (standard OG image ratio). + +## Configuration + +`config.yaml` controls text placement for each template. Each content type can define multiple named fields (`title`, `description`, etc.) that map to front-matter values. + +Use `exclude:` to skip content types entirely (e.g. blog posts that already have their own thumbnail): + +```yaml +exclude: + - blog + - authors + +templates: + release-notes: + template: scripts/social-images/templates/release-notes.png + fields: + title: + font: scripts/social-images/fonts/HafferBold.ttf + size: 48 # font size in points + color: "#2558ff" + gravity: NorthWest # ImageMagick anchor: NorthWest, Center, South, etc. + left: 66 # pixel offset from the gravity anchor (horizontal) + top: 232 # pixel offset from the gravity anchor (vertical) + max_width: 830 # text wraps within this box width + max_height: 165 # text is clipped beyond this height + description: + font: scripts/social-images/fonts/Inter-Regular.ttf + size: 20 + color: "#2558ff" + gravity: NorthWest + left: 66 + anchor: title # position below the rendered bottom of the title field + gap: 15 # extra pixels of padding after the title + max_width: 830 # text wraps within this box width + max_height: 165 # text is clipped beyond this height +``` + +**Field reference:** + +| Key | Required | Description | +|---|---|---| +| `font` | yes | Path to a TTF file (relative to repo root) | +| `size` | yes | Font size in points (matches Canva pt values at 96 DPI) | +| `color` | yes | Hex color string | +| `gravity` | yes | ImageMagick reference corner: `NorthWest`, `Center`, `South`, etc. | +| `left` | yes* | Horizontal pixel offset from the gravity anchor (*defaults to anchor's `left` when `anchor` is set) | +| `top` | yes* | Vertical pixel offset from the gravity anchor (*not needed when `anchor` is set) | +| `max_width` | yes | Caption box width in pixels (controls line wrapping) | +| `max_height` | yes | Caption box height in pixels (text is clipped if it overflows) | +| `anchor` | no | Name of another field; positions this field's top below that field's rendered bottom edge | +| `gap` | no | Extra pixels of padding below the anchor field (default: 0) | + +**Dynamic positioning with `anchor`:** when set, the canvas position is computed so that exactly `gap` pixels of visual space appear between the anchor's last text pixel and this field's first text pixel: + +``` +canvas_top = anchor_field.top + anchor_bottom_offset + gap - this_field_top_offset +``` + +`gap: 0` means the two text blocks touch with no overlap and no extra space. Both offsets are measured from each field's own caption canvas using ImageMagick's trim geometry. + +Available fonts (in `scripts/social-images/fonts/`): + +- `HafferBold.ttf`, `HafferMedium.ttf`, `HafferRegular.ttf` — Haffer (headings + body) +- `Inter-Regular.ttf` — Inter (body text) + +## How Hugo picks up the images + +`themes/haystack/layouts/partials/opengraph.html` derives the expected image path from the page's content type and slug, then checks for the file with `os.FileExists`. Priority order: + +1. Explicit `images` array in front matter (manual override) +2. Generated social image at `static/images/social/{type}/{slug}.png` +3. Site-wide default from `config.toml` diff --git a/scripts/social-images/config.yaml b/scripts/social-images/config.yaml new file mode 100644 index 00000000..0d5c2ce8 --- /dev/null +++ b/scripts/social-images/config.yaml @@ -0,0 +1,146 @@ +# Social image generation configuration. +# +# Each key under `templates` maps to a content type inferred from the file path +# (e.g. content/blog/ → "blog"). If a content type has no entry here, the +# "fallback" template is used. +# +# Field keys are arbitrary — they must match a front-matter key that the script +# knows how to read (title, description). The script skips a field silently if +# the front-matter value is empty. +# +# Field options: +# font - path to a TTF file (relative to the repo root) +# size - font size in points (matches Canva pt values; rendered at 96 DPI) +# color - hex color string +# gravity - ImageMagick gravity: NorthWest, North, NorthEast, West, Center, +# East, SouthWest, South, SouthEast +# left - pixel offset from the gravity anchor (horizontal; required unless anchor is +# set, in which case it defaults to the anchor field's left value) +# top - pixel offset from the gravity anchor (vertical; required unless anchor is set) +# max_width - caption box width in pixels (controls line wrapping) +# max_height - caption box height in pixels (text is clipped if it overflows) +# anchor - (optional) name of another field whose rendered bottom edge +# determines this field's top; replaces top +# gap - (optional) extra pixels of padding below the anchor (default 0) + +# Content types listed here are skipped entirely (no image generated). +exclude: + - blog + - authors + - ambassador-perks + +templates: + cookbook: + template: scripts/social-images/templates/cookbook.png + fields: + title: + font: scripts/social-images/fonts/HafferBold.ttf + size: 48 + color: "#2558ff" + gravity: NorthWest + left: 66 + top: 232 + max_width: 830 + max_height: 165 + description: + font: scripts/social-images/fonts/Inter-Regular.ttf + size: 20 + color: "#2558ff" + gravity: NorthWest + left: 66 + anchor: title + gap: 15 + max_width: 830 + max_height: 165 + + integrations: + template: scripts/social-images/templates/Integration.png + fields: + title: + font: scripts/social-images/fonts/HafferBold.ttf + size: 48 + color: "#2558ff" + gravity: NorthWest + left: 66 + top: 232 + max_width: 830 + max_height: 165 + description: + font: scripts/social-images/fonts/Inter-Regular.ttf + size: 20 + color: "#2558ff" + gravity: NorthWest + left: 66 + anchor: title + gap: 15 + max_width: 830 + max_height: 165 + + release-notes: + template: scripts/social-images/templates/release-notes.png + fields: + title: + font: scripts/social-images/fonts/HafferBold.ttf + size: 48 + color: "#2558ff" + gravity: NorthWest + left: 66 + top: 232 + max_width: 830 + max_height: 165 + description: + font: scripts/social-images/fonts/Inter-Regular.ttf + size: 20 + color: "#2558ff" + gravity: NorthWest + left: 66 + anchor: title + gap: 15 + max_width: 830 + max_height: 165 + + tutorials: + template: scripts/social-images/templates/tutorials.png + fields: + title: + font: scripts/social-images/fonts/HafferBold.ttf + size: 48 + color: "#2558ff" + gravity: NorthWest + left: 66 + top: 232 + max_width: 830 + max_height: 165 + description: + font: scripts/social-images/fonts/Inter-Regular.ttf + size: 20 + color: "#2558ff" + gravity: NorthWest + left: 66 + anchor: title + gap: 15 + max_width: 830 + max_height: 165 + + fallback: + template: scripts/social-images/templates/fallback.png + fields: + title: + font: scripts/social-images/fonts/HafferBold.ttf + size: 48 + color: "#2558ff" + gravity: NorthWest + left: 66 + top: 232 + max_width: 830 + max_height: 165 + description: + font: scripts/social-images/fonts/Inter-Regular.ttf + size: 20 + color: "#2558ff" + gravity: NorthWest + left: 66 + anchor: title + gap: 15 + max_width: 830 + max_height: 165 diff --git a/scripts/social-images/fonts/HafferBold.ttf b/scripts/social-images/fonts/HafferBold.ttf new file mode 100644 index 00000000..ddf11ac3 Binary files /dev/null and b/scripts/social-images/fonts/HafferBold.ttf differ diff --git a/scripts/social-images/fonts/HafferMedium.ttf b/scripts/social-images/fonts/HafferMedium.ttf new file mode 100644 index 00000000..19037407 Binary files /dev/null and b/scripts/social-images/fonts/HafferMedium.ttf differ diff --git a/scripts/social-images/fonts/HafferRegular.ttf b/scripts/social-images/fonts/HafferRegular.ttf new file mode 100644 index 00000000..8c6400ee Binary files /dev/null and b/scripts/social-images/fonts/HafferRegular.ttf differ diff --git a/scripts/social-images/fonts/Inter-Regular.ttf b/scripts/social-images/fonts/Inter-Regular.ttf new file mode 100644 index 00000000..b7aaca8d Binary files /dev/null and b/scripts/social-images/fonts/Inter-Regular.ttf differ diff --git a/scripts/social-images/generate.py b/scripts/social-images/generate.py new file mode 100644 index 00000000..adafa2d0 --- /dev/null +++ b/scripts/social-images/generate.py @@ -0,0 +1,499 @@ +""" +Generate social preview (OG) images for Hugo content files. + +For each content file the script: + 1. Detects the content type from the file path. + 2. Loads the matching template config from config.yaml (falls back to "fallback"). + 3. Reads the front matter (title, description) with python-frontmatter. + 4. Composites text onto the PNG template using ImageMagick. + 5. Saves the result to static/images/social/{type}/{slug}.png. + +The Hugo opengraph partial picks up generated images automatically by checking +for a file at that predictable path — no front matter changes are needed. + +Usage: + python3 scripts/social-images/generate.py # all content types + python3 scripts/social-images/generate.py --type blog # one type + python3 scripts/social-images/generate.py --file content/release-notes/2.25.0.md + python3 scripts/social-images/generate.py --dry-run # preview only + +Dependencies: + pip install python-frontmatter PyYAML + brew install imagemagick (or equivalent) +""" + +import argparse +import re +import shutil +import subprocess +import sys +from dataclasses import dataclass, field, replace +from pathlib import Path +from typing import Optional + +import frontmatter +import yaml + +REPO_ROOT = Path(__file__).parent.parent.parent +CONFIG_FILE = Path(__file__).parent / "config.yaml" + +# Matches emoji, pictographs, ZWJ sequences, variation selectors, skin-tone modifiers, +# regional indicators, and enclosed characters that ImageMagick cannot render. +_EMOJI_RE = re.compile( + "[" + "\U0001F600-\U0001F64F" # emoticons + "\U0001F300-\U0001F5FF" # misc symbols & pictographs + "\U0001F680-\U0001F6FF" # transport & map + "\U0001F700-\U0001F77F" # alchemical symbols + "\U0001F780-\U0001F7FF" # geometric shapes extended + "\U0001F800-\U0001F8FF" # supplemental arrows-c + "\U0001F900-\U0001F9FF" # supplemental symbols & pictographs + "\U0001FA00-\U0001FA6F" # chess symbols + "\U0001FA70-\U0001FAFF" # symbols & pictographs extended-a + "\U00002702-\U000027B0" # dingbats + "\U000024C2-\U0001F251" # enclosed characters + "\U0001F1E0-\U0001F1FF" # regional indicators (flags) + "\U0000200D" # ZWJ + "\U0000FE0F" # variation selector-16 + "\U0001F3FB-\U0001F3FF" # skin-tone modifiers + "]+", + flags=re.UNICODE, +) + + +def _strip_emoji(text: str) -> str: + """Remove emoji and related Unicode from *text*, then collapse extra whitespace.""" + return re.sub(r" +", " ", _EMOJI_RE.sub("", text)).strip() + + +@dataclass +class FieldConfig: + font: str + size: int + color: str + gravity: str + max_width: float + max_height: float + left: float = 0.0 + top: float = 0.0 + anchor: Optional[str] = None + gap: float = 0.0 + + @classmethod + def from_dict(cls, data: dict) -> "FieldConfig": + return cls( + font=data["font"], + size=data["size"], + color=data["color"], + gravity=data["gravity"], + max_width=data["max_width"], + max_height=data["max_height"], + left=data.get("left", 0.0), + top=data.get("top", 0.0), + anchor=data.get("anchor"), + gap=data.get("gap", 0.0), + ) + + +@dataclass +class TemplateConfig: + template: str + fields: dict = field(default_factory=dict) + + @classmethod + def from_dict(cls, data: dict) -> "TemplateConfig": + return cls( + template=data["template"], + fields={ + name: FieldConfig.from_dict(cfg) + for name, cfg in (data.get("fields") or {}).items() + }, + ) + + +class Config: + def __init__(self, config_file: Path): + with open(config_file) as f: + doc = yaml.safe_load(f) + self._templates: dict[str, TemplateConfig] = { + name: TemplateConfig.from_dict(data) for name, data in doc["templates"].items() + } + self._exclude: set[str] = set(doc.get("exclude", [])) + + def resolve(self, content_type: str) -> Optional[TemplateConfig]: + return self._templates.get(content_type) or self._templates.get("fallback") + + def is_excluded(self, content_type: str) -> bool: + return content_type in self._exclude + + @property + def fallback(self) -> Optional[TemplateConfig]: + return self._templates.get("fallback") + + +@dataclass +class ContentFile: + md_path: Path + content_type: str + slug: str + title: str + description: str + _metadata: dict = field(default_factory=dict) + + @classmethod + def load(cls, md_path: Path, repo_root: Path) -> "ContentFile": + content_type = cls._detect_type(md_path, repo_root) + slug = cls._derive_slug(md_path) + post = frontmatter.load(str(md_path)) + return cls( + md_path=md_path, + content_type=content_type, + slug=slug, + title=_strip_emoji(post.get("title") or post.get("name") or ""), + description=_strip_emoji(post.get("description") or ""), + _metadata=dict(post.metadata), + ) + + @property + def text_values(self) -> dict[str, str]: + result: dict[str, str] = {"title": self.title, "description": self.description} + for key, value in self._metadata.items(): + if key in result: + continue + if isinstance(value, list): + result[key] = ", ".join(str(v) for v in value) + elif value is not None: + result[key] = str(value) + return result + + @staticmethod + def _detect_type(md_path: Path, repo_root: Path) -> str: + try: + rel = md_path.relative_to(repo_root / "content") + except ValueError: + return "fallback" + return rel.parts[0] + + @staticmethod + def _derive_slug(md_path: Path) -> str: + if md_path.name in ("index.md", "_index.md"): + return md_path.parent.name + return md_path.stem + + +class ImageCompositor: + def __init__(self, repo_root: Path): + self._repo_root = repo_root + + def measure_text_height(self, field_cfg: FieldConfig, text: str) -> int: + """Return the pixel distance from the caption box top to the bottom of rendered text. + + Uses ImageMagick's %@ bounding-box format (WxH+X+Y) and returns Y+H so that + the result accounts for any top padding the caption renderer adds above the glyphs. + The last character is replaced with "g" before measuring so the bounding box always + includes a descender, giving consistent heights regardless of actual last-line content. + """ + # Replace the last character with "g" so the bounding box always includes + # a descender, giving consistent heights regardless of actual last-line content. + normalized = text[:-1] + "g" if text else text + safe_text = normalized.replace("\\", "\\\\").replace("'", "\\'") + result = subprocess.run([ + "magick", "-density", "96", + "-background", "none", "-fill", "black", + "-font", str(self._repo_root / field_cfg.font), + "-pointsize", str(field_cfg.size), + "-size", f"{int(field_cfg.max_width)}x{int(field_cfg.max_height)}", + f"caption:{safe_text}", + "-format", "%@\n", "info:", + ], capture_output=True, text=True, check=True) + # %@ returns "WxH+X+Y" — Y is top offset of text, H is glyph height. + # Y + H is the bottom edge of the text relative to the caption box top. + m = re.match(r"\d+x(\d+)\+\d+\+(\d+)", result.stdout.strip()) + if m: + return int(m.group(2)) + int(m.group(1)) + return int(result.stdout.strip()) + + def fit_font_size(self, field_cfg: FieldConfig, text: str) -> int: + """Binary-search for the largest font size where text fits within max_height. + + The size in field_cfg is treated as the maximum; returns a value in [1, size]. + Measurement uses an unconstrained height so ImageMagick clipping cannot mask overflow. + """ + # Remove the height constraint so the bounding box reflects true text height. + unconstrained = replace(field_cfg, max_height=10_000) + lo, hi = 1, field_cfg.size + best = lo + while lo <= hi: + mid = (lo + hi) // 2 + height = self.measure_text_height(replace(unconstrained, size=mid), text) + if height <= field_cfg.max_height: + best = mid + lo = mid + 1 + else: + hi = mid - 1 + return best + + def build_command( + self, + template_path: Path, + fields: dict[str, FieldConfig], + text_values: dict[str, str], + output_path: Path, + ) -> list[str]: + """ + Build an ImageMagick `convert` command that composites one caption layer + per configured field onto the template image. + + Fields may use `anchor` and `gap` instead of a fixed `top` value — the + top edge is then computed as anchor_field.top + rendered_height + gap. + + Font sizes are treated as maximums: each field's size is reduced as needed + so the full text always fits within max_height. + """ + effective_fields = { + name: ( + replace(cfg, size=self.fit_font_size(cfg, text_values[name].replace("\\n", "\n"))) + if text_values.get(name) + else cfg + ) + for name, cfg in fields.items() + } + measured_heights = self._measure_anchors(effective_fields, text_values) + cmd = ["magick", "-density", "96", str(template_path)] + + for field_name, field_cfg in effective_fields.items(): + text = text_values.get(field_name, "") + if not text: + continue + x, y = self._resolve_position(field_cfg, effective_fields, measured_heights) + cmd += self._field_args(field_cfg, text, x, y) + + cmd.append(str(output_path)) + return cmd + + def composite( + self, + template_path: Path, + fields: dict[str, FieldConfig], + text_values: dict[str, str], + output_path: Path, + ) -> None: + """Run ImageMagick. Raises FileNotFoundError or CalledProcessError on failure.""" + cmd = self.build_command(template_path, fields, text_values, output_path) + subprocess.run(cmd, check=True, capture_output=True, text=True) + + def _measure_anchors( + self, fields: dict[str, FieldConfig], text_values: dict[str, str] + ) -> dict[str, int]: + measured: dict[str, int] = {} + for field_cfg in fields.values(): + anchor_name = field_cfg.anchor + if anchor_name and anchor_name not in measured and anchor_name in fields: + anchor_field = fields[anchor_name] + anchor_text = text_values.get(anchor_name, "").replace("\\n", "\n") + if anchor_text: + measured[anchor_name] = self.measure_text_height(anchor_field, anchor_text) + return measured + + def _resolve_position( + self, + field_cfg: FieldConfig, + all_fields: dict[str, FieldConfig], + measured_heights: dict[str, int], + ) -> tuple[float, float]: + if field_cfg.anchor and field_cfg.anchor in all_fields: + anchor_field = all_fields[field_cfg.anchor] + x = field_cfg.left if field_cfg.left else anchor_field.left + y = anchor_field.top + measured_heights.get(field_cfg.anchor, 0) + field_cfg.gap + else: + x, y = field_cfg.left, field_cfg.top + return x, y + + def _field_args( + self, field_cfg: FieldConfig, text: str, x: float, y: float + ) -> list[str]: + text = text.replace("\\n", "\n") + safe_text = text.replace("\\", "\\\\").replace("'", "\\'") + return [ + "(", "-background", "none", + "-fill", field_cfg.color, + "-font", str(self._repo_root / field_cfg.font), + "-pointsize", str(field_cfg.size), + "-size", f"{int(field_cfg.max_width)}x{int(field_cfg.max_height)}", + f"caption:{safe_text}", + ")", "-gravity", field_cfg.gravity, + "-geometry", f"+{int(x)}+{int(y)}", "-composite", + ] + + +class FileProcessor: + def __init__(self, config: Config, compositor: ImageCompositor, repo_root: Path): + self._config = config + self._compositor = compositor + self._repo_root = repo_root + + def _rel(self, path: Path) -> Path: + try: + return path.relative_to(self._repo_root) + except ValueError: + return path + + def process(self, md_path: Path, dry_run: bool, force: bool = False) -> bool: + """Process a single content file. Returns True on success or skip.""" + content_file = ContentFile.load(md_path, self._repo_root) + + if self._config.is_excluded(content_file.content_type): + print(f" skip {self._rel(md_path)} (excluded content type: {content_file.content_type})") + return True + + base_cfg = self._config.resolve(content_file.content_type) + if not base_cfg: + print(f" error {self._rel(md_path)}: no template config and no fallback defined in config.yaml") + return False + + resolution = self._resolve_template(content_file, base_cfg) + if resolution is None: + # Template missing with no fallback — skip message already printed + return True + + template_path, template_cfg = resolution + output_dir = self._repo_root / "static/images/social" / content_file.content_type + output_path = output_dir / f"{content_file.slug}.png" + + if dry_run: + self._print_dry_run(content_file, output_path) + return True + + if output_path.exists() and not force: + print(f" skip {self._rel(md_path)} (already exists, use --force to regenerate)") + return True + + output_path.parent.mkdir(parents=True, exist_ok=True) + try: + self._compositor.composite( + template_path, template_cfg.fields, content_file.text_values, output_path + ) + except FileNotFoundError: + print(" error: ImageMagick `convert` not found. Install with: brew install imagemagick") + return False + except subprocess.CalledProcessError as exc: + print(f" error {self._rel(md_path)}: ImageMagick failed\n{exc.stderr.strip()}") + return False + + print(f" ok {self._rel(md_path)} → {self._rel(output_path)}") + return True + + def _resolve_template( + self, content_file: ContentFile, template_cfg: TemplateConfig + ) -> Optional[tuple[Path, TemplateConfig]]: + template_path = self._repo_root / template_cfg.template + if template_path.exists(): + return template_path, template_cfg + + fallback = self._config.fallback + if fallback: + fallback_path = self._repo_root / fallback.template + if fallback_path.exists(): + print(f" warn {self._rel(content_file.md_path)}: template not found, using fallback") + merged = TemplateConfig( + template=fallback.template, + fields=template_cfg.fields or fallback.fields, + ) + return fallback_path, merged + + print(f" skip {self._rel(content_file.md_path)}: template not found at {self._rel(template_path)}") + return None + + def _print_dry_run(self, content_file: ContentFile, output_path: Path) -> None: + print(f" dry {self._rel(content_file.md_path)}") + print(f" type={content_file.content_type} slug={content_file.slug}") + print(f" output={self._rel(output_path)}") + for field_name, text in content_file.text_values.items(): + preview = (text[:60] + "…") if len(text) > 60 else text + print(f" {field_name}: {preview!r}") + + +class FileCollector: + def __init__(self, repo_root: Path): + self._repo_root = repo_root + + def collect( + self, + content_type: Optional[str] = None, + single_file: Optional[Path] = None, + ) -> list[Path]: + if single_file: + return [single_file.resolve()] + + content_root = self._repo_root / "content" + if content_type: + dirs = [content_root / content_type] + else: + dirs = [d for d in content_root.iterdir() if d.is_dir()] + + files: list[Path] = [] + for d in dirs: + if d.exists(): + files.extend(d.rglob("*.md")) + return sorted(files) + + +def main(): + parser = argparse.ArgumentParser( + description="Generate social preview images for Haystack Hugo content." + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--type", + metavar="TYPE", + help="Only process files of this content type (e.g. blog, release-notes, tutorials).", + ) + group.add_argument( + "--file", + metavar="PATH", + type=Path, + help="Process a single markdown file.", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Print what would be generated without writing any files.", + ) + parser.add_argument( + "--force", + action="store_true", + help="Regenerate images even if the output file already exists.", + ) + args = parser.parse_args() + + if not shutil.which("magick"): + print("Error: ImageMagick `magick` command not found.") + print("Install with: brew install imagemagick") + sys.exit(1) + + config = Config(CONFIG_FILE) + collector = FileCollector(REPO_ROOT) + compositor = ImageCompositor(REPO_ROOT) + processor = FileProcessor(config, compositor, REPO_ROOT) + + files = collector.collect(args.type, args.file) + if not files: + print("No matching content files found.") + sys.exit(0) + + print(f"Processing {len(files)} file(s)…\n") + ok = 0 + failed = 0 + for md_path in files: + if processor.process(md_path, args.dry_run, args.force): + ok += 1 + else: + failed += 1 + + print(f"\nDone: {ok} ok, {failed} failed.") + if failed: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/social-images/requirements.txt b/scripts/social-images/requirements.txt new file mode 100644 index 00000000..0a920eb1 --- /dev/null +++ b/scripts/social-images/requirements.txt @@ -0,0 +1,2 @@ +python-frontmatter +PyYAML diff --git a/scripts/social-images/templates/Integration.png b/scripts/social-images/templates/Integration.png new file mode 100644 index 00000000..9e71dccc Binary files /dev/null and b/scripts/social-images/templates/Integration.png differ diff --git a/scripts/social-images/templates/blog.png b/scripts/social-images/templates/blog.png new file mode 100644 index 00000000..666e4ed8 Binary files /dev/null and b/scripts/social-images/templates/blog.png differ diff --git a/scripts/social-images/templates/cookbook.png b/scripts/social-images/templates/cookbook.png new file mode 100644 index 00000000..ab46d4c3 Binary files /dev/null and b/scripts/social-images/templates/cookbook.png differ diff --git a/scripts/social-images/templates/fallback.png b/scripts/social-images/templates/fallback.png new file mode 100644 index 00000000..06b1ca9f Binary files /dev/null and b/scripts/social-images/templates/fallback.png differ diff --git a/scripts/social-images/templates/release-notes.png b/scripts/social-images/templates/release-notes.png new file mode 100644 index 00000000..b62b8f67 Binary files /dev/null and b/scripts/social-images/templates/release-notes.png differ diff --git a/scripts/social-images/templates/tutorials.png b/scripts/social-images/templates/tutorials.png new file mode 100644 index 00000000..2a129378 Binary files /dev/null and b/scripts/social-images/templates/tutorials.png differ diff --git a/scripts/social-images/test-placement.py b/scripts/social-images/test-placement.py new file mode 100644 index 00000000..180b1ac7 --- /dev/null +++ b/scripts/social-images/test-placement.py @@ -0,0 +1,139 @@ +""" +Visual placement test for social image text fields. + +Generates an image with the configured text AND draws a colored bounding box +around each field so you can see exactly where left/top and max_width/max_height +land on the template. + +Usage: + python3 scripts/social-images/test-placement.py + python3 scripts/social-images/test-placement.py --type blog + python3 scripts/social-images/test-placement.py --type cookbook --title "My Long Recipe Title Here" +""" + +import argparse +import shutil +import subprocess +import sys +from pathlib import Path + +from generate import Config, FieldConfig, ImageCompositor + +REPO_ROOT = Path(__file__).parent.parent.parent +CONFIG_FILE = Path(__file__).parent / "config.yaml" + +SAMPLE_TEXTS = { + "title": "Retrieval-Augmented Generation with Custom Embeddings", + "description": "Learn how to build a production-ready RAG pipeline using Haystack 2.x with custom embedding models and vector stores.", +} + +FIELD_COLORS = ["#ff4444", "#44aaff", "#44ff88", "#ffaa44"] + + +def build_cmd( + compositor: ImageCompositor, + template_path: Path, + fields: dict[str, FieldConfig], + text_values: dict[str, str], + output_path: Path, +) -> list[str]: + """Build the base composite command then inject bounding-box debug overlays.""" + # Use the shared compositor to get the base command (without the output path) + base = compositor.build_command(template_path, fields, text_values, output_path) + # Remove the output path appended by build_command — we'll re-add it at the end + base = base[:-1] + + # Pre-compute positions for bounding boxes using the compositor's internals. + # We replicate the anchor measurement to know x/y per field. + measured_heights = compositor._measure_anchors(fields, text_values) + + for i, (field_name, field_cfg) in enumerate(fields.items()): + text = text_values.get(field_name, "") + if not text: + continue + x, y = compositor._resolve_position(field_cfg, fields, measured_heights) + box_color = FIELD_COLORS[i % len(FIELD_COLORS)] + + base += [ + "-fill", "none", + "-stroke", box_color, + "-strokewidth", "2", + "-draw", f"rectangle {int(x)},{int(y)} {int(x + field_cfg.max_width)},{int(y + field_cfg.max_height)}", + ] + base += [ + "-fill", box_color, + "-font", str(REPO_ROOT / field_cfg.font), + "-pointsize", "18", + "-annotate", f"+{int(x) + 4}+{int(y) + 18}", + f"{field_name} (left={int(x)}, top={int(y)})", + ] + + base.append(str(output_path)) + return base + + +def main(): + parser = argparse.ArgumentParser(description="Visual placement test for social image fields.") + parser.add_argument("--type", default="cookbook", metavar="TYPE", help="Content type to test (default: cookbook)") + parser.add_argument("--title", metavar="TEXT", help="Override sample title text") + parser.add_argument("--description", metavar="TEXT", help="Override sample description text") + args = parser.parse_args() + + if not shutil.which("magick"): + print("Error: ImageMagick `magick` not found. Install with: brew install imagemagick") + sys.exit(1) + + config = Config(CONFIG_FILE) + compositor = ImageCompositor(REPO_ROOT) + + template_cfg = config.resolve(args.type) + if not template_cfg: + print(f"Error: no template config found for type '{args.type}'") + sys.exit(1) + + template_path = REPO_ROOT / template_cfg.template + if not template_path.exists(): + fallback = config.fallback + fallback_path = REPO_ROOT / fallback.template if fallback else None + if fallback_path and fallback_path.exists(): + print(f"Template not found, using fallback: {fallback_path.name}") + template_path = fallback_path + else: + print(f"Error: no template found at {template_path}") + sys.exit(1) + + output_dir = REPO_ROOT / "static" / "images" / "social" / args.type + output_dir.mkdir(parents=True, exist_ok=True) + output_path = output_dir / "_placement-test.png" + + text_values = dict(SAMPLE_TEXTS) + if args.title: + text_values["title"] = args.title + if args.description: + text_values["description"] = args.description + + fields = template_cfg.fields + print(f"Type: {args.type}") + print(f"Template: {template_path}") + print(f"Output: {output_path}") + print() + for field_name, field_cfg in fields.items(): + if field_cfg.anchor: + print(f" {field_name}: left=inherited from '{field_cfg.anchor}', top=anchored below '{field_cfg.anchor}' + gap={int(field_cfg.gap)}, " + f"max_width={int(field_cfg.max_width)}, max_height={int(field_cfg.max_height)}") + else: + print(f" {field_name}: left={int(field_cfg.left)}, top={int(field_cfg.top)}, " + f"max_width={int(field_cfg.max_width)}, max_height={int(field_cfg.max_height)}") + print() + + cmd = build_cmd(compositor, template_path, fields, text_values, output_path) + try: + subprocess.run(cmd, check=True, capture_output=True, text=True) + print(f"Generated: {output_path}") + except subprocess.CalledProcessError as exc: + print(f"ImageMagick failed:\n{exc.stderr.strip()}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/static/images/social/advent-of-haystack/advent-of-haystack.png b/static/images/social/advent-of-haystack/advent-of-haystack.png new file mode 100644 index 00000000..429e7fac Binary files /dev/null and b/static/images/social/advent-of-haystack/advent-of-haystack.png differ diff --git a/static/images/social/ambassadors/ambassadors.png b/static/images/social/ambassadors/ambassadors.png new file mode 100644 index 00000000..e2730d3c Binary files /dev/null and b/static/images/social/ambassadors/ambassadors.png differ diff --git a/static/images/social/benchmarks/benchmarks.png b/static/images/social/benchmarks/benchmarks.png new file mode 100644 index 00000000..9edbdff7 Binary files /dev/null and b/static/images/social/benchmarks/benchmarks.png differ diff --git a/static/images/social/benchmarks/v0.10.0.png b/static/images/social/benchmarks/v0.10.0.png new file mode 100644 index 00000000..f2cc1eb5 Binary files /dev/null and b/static/images/social/benchmarks/v0.10.0.png differ diff --git a/static/images/social/benchmarks/v0.5.0.png b/static/images/social/benchmarks/v0.5.0.png new file mode 100644 index 00000000..e4da852f Binary files /dev/null and b/static/images/social/benchmarks/v0.5.0.png differ diff --git a/static/images/social/benchmarks/v0.6.0.png b/static/images/social/benchmarks/v0.6.0.png new file mode 100644 index 00000000..0f8a8b57 Binary files /dev/null and b/static/images/social/benchmarks/v0.6.0.png differ diff --git a/static/images/social/benchmarks/v0.7.0.png b/static/images/social/benchmarks/v0.7.0.png new file mode 100644 index 00000000..47c80df9 Binary files /dev/null and b/static/images/social/benchmarks/v0.7.0.png differ diff --git a/static/images/social/benchmarks/v0.8.0.png b/static/images/social/benchmarks/v0.8.0.png new file mode 100644 index 00000000..70b94f7e Binary files /dev/null and b/static/images/social/benchmarks/v0.8.0.png differ diff --git a/static/images/social/benchmarks/v0.9.0.png b/static/images/social/benchmarks/v0.9.0.png new file mode 100644 index 00000000..512a74d3 Binary files /dev/null and b/static/images/social/benchmarks/v0.9.0.png differ diff --git a/static/images/social/benchmarks/v1.9.0.png b/static/images/social/benchmarks/v1.9.0.png new file mode 100644 index 00000000..ebac8946 Binary files /dev/null and b/static/images/social/benchmarks/v1.9.0.png differ diff --git a/static/images/social/cookbook/cookbook.png b/static/images/social/cookbook/cookbook.png new file mode 100644 index 00000000..8bc084f5 Binary files /dev/null and b/static/images/social/cookbook/cookbook.png differ diff --git a/static/images/social/gsoc/contributor-guidance.png b/static/images/social/gsoc/contributor-guidance.png new file mode 100644 index 00000000..65d3dd0c Binary files /dev/null and b/static/images/social/gsoc/contributor-guidance.png differ diff --git a/static/images/social/gsoc/gsoc.png b/static/images/social/gsoc/gsoc.png new file mode 100644 index 00000000..f9f1656b Binary files /dev/null and b/static/images/social/gsoc/gsoc.png differ diff --git a/static/images/social/gsoc/projects.png b/static/images/social/gsoc/projects.png new file mode 100644 index 00000000..b8396b36 Binary files /dev/null and b/static/images/social/gsoc/projects.png differ diff --git a/static/images/social/integrations/aimlapi.png b/static/images/social/integrations/aimlapi.png new file mode 100644 index 00000000..1aff89da Binary files /dev/null and b/static/images/social/integrations/aimlapi.png differ diff --git a/static/images/social/integrations/amazon-bedrock.png b/static/images/social/integrations/amazon-bedrock.png new file mode 100644 index 00000000..39556a5c Binary files /dev/null and b/static/images/social/integrations/amazon-bedrock.png differ diff --git a/static/images/social/integrations/amazon-sagemaker.png b/static/images/social/integrations/amazon-sagemaker.png new file mode 100644 index 00000000..70bfb6dd Binary files /dev/null and b/static/images/social/integrations/amazon-sagemaker.png differ diff --git a/static/images/social/integrations/anthropic.png b/static/images/social/integrations/anthropic.png new file mode 100644 index 00000000..165c6214 Binary files /dev/null and b/static/images/social/integrations/anthropic.png differ diff --git a/static/images/social/integrations/apify.png b/static/images/social/integrations/apify.png new file mode 100644 index 00000000..1891b999 Binary files /dev/null and b/static/images/social/integrations/apify.png differ diff --git a/static/images/social/integrations/arcadedb.png b/static/images/social/integrations/arcadedb.png new file mode 100644 index 00000000..dc0735dd Binary files /dev/null and b/static/images/social/integrations/arcadedb.png differ diff --git a/static/images/social/integrations/arize-phoenix.png b/static/images/social/integrations/arize-phoenix.png new file mode 100644 index 00000000..e8b2eb11 Binary files /dev/null and b/static/images/social/integrations/arize-phoenix.png differ diff --git a/static/images/social/integrations/arize.png b/static/images/social/integrations/arize.png new file mode 100644 index 00000000..fe2ea5d8 Binary files /dev/null and b/static/images/social/integrations/arize.png differ diff --git a/static/images/social/integrations/assemblyai.png b/static/images/social/integrations/assemblyai.png new file mode 100644 index 00000000..b74cec0c Binary files /dev/null and b/static/images/social/integrations/assemblyai.png differ diff --git a/static/images/social/integrations/astradb.png b/static/images/social/integrations/astradb.png new file mode 100644 index 00000000..c2e67649 Binary files /dev/null and b/static/images/social/integrations/astradb.png differ diff --git a/static/images/social/integrations/azure-ai-search.png b/static/images/social/integrations/azure-ai-search.png new file mode 100644 index 00000000..c355fdbe Binary files /dev/null and b/static/images/social/integrations/azure-ai-search.png differ diff --git a/static/images/social/integrations/azure-cosmos-db.png b/static/images/social/integrations/azure-cosmos-db.png new file mode 100644 index 00000000..e8688fb4 Binary files /dev/null and b/static/images/social/integrations/azure-cosmos-db.png differ diff --git a/static/images/social/integrations/azure-doc-intelligence.png b/static/images/social/integrations/azure-doc-intelligence.png new file mode 100644 index 00000000..70b81635 Binary files /dev/null and b/static/images/social/integrations/azure-doc-intelligence.png differ diff --git a/static/images/social/integrations/azure.png b/static/images/social/integrations/azure.png new file mode 100644 index 00000000..2047f3c1 Binary files /dev/null and b/static/images/social/integrations/azure.png differ diff --git a/static/images/social/integrations/bright-data.png b/static/images/social/integrations/bright-data.png new file mode 100644 index 00000000..ff3d8d91 Binary files /dev/null and b/static/images/social/integrations/bright-data.png differ diff --git a/static/images/social/integrations/browserbase.png b/static/images/social/integrations/browserbase.png new file mode 100644 index 00000000..6e764ca9 Binary files /dev/null and b/static/images/social/integrations/browserbase.png differ diff --git a/static/images/social/integrations/burr.png b/static/images/social/integrations/burr.png new file mode 100644 index 00000000..e8507172 Binary files /dev/null and b/static/images/social/integrations/burr.png differ diff --git a/static/images/social/integrations/cerebras.png b/static/images/social/integrations/cerebras.png new file mode 100644 index 00000000..e7eecd54 Binary files /dev/null and b/static/images/social/integrations/cerebras.png differ diff --git a/static/images/social/integrations/chainlit.png b/static/images/social/integrations/chainlit.png new file mode 100644 index 00000000..14bfd060 Binary files /dev/null and b/static/images/social/integrations/chainlit.png differ diff --git a/static/images/social/integrations/chroma-documentstore.png b/static/images/social/integrations/chroma-documentstore.png new file mode 100644 index 00000000..dd1536f3 Binary files /dev/null and b/static/images/social/integrations/chroma-documentstore.png differ diff --git a/static/images/social/integrations/cohere.png b/static/images/social/integrations/cohere.png new file mode 100644 index 00000000..01c71b51 Binary files /dev/null and b/static/images/social/integrations/cohere.png differ diff --git a/static/images/social/integrations/comet-api.png b/static/images/social/integrations/comet-api.png new file mode 100644 index 00000000..9aa1d545 Binary files /dev/null and b/static/images/social/integrations/comet-api.png differ diff --git a/static/images/social/integrations/context-ai.png b/static/images/social/integrations/context-ai.png new file mode 100644 index 00000000..a8f0046b Binary files /dev/null and b/static/images/social/integrations/context-ai.png differ diff --git a/static/images/social/integrations/couchbase-document-store.png b/static/images/social/integrations/couchbase-document-store.png new file mode 100644 index 00000000..01d08774 Binary files /dev/null and b/static/images/social/integrations/couchbase-document-store.png differ diff --git a/static/images/social/integrations/deepeval.png b/static/images/social/integrations/deepeval.png new file mode 100644 index 00000000..a608f87b Binary files /dev/null and b/static/images/social/integrations/deepeval.png differ diff --git a/static/images/social/integrations/deepl.png b/static/images/social/integrations/deepl.png new file mode 100644 index 00000000..791920fd Binary files /dev/null and b/static/images/social/integrations/deepl.png differ diff --git a/static/images/social/integrations/docling.png b/static/images/social/integrations/docling.png new file mode 100644 index 00000000..e52a4839 Binary files /dev/null and b/static/images/social/integrations/docling.png differ diff --git a/static/images/social/integrations/duckduckgo-api-websearch.png b/static/images/social/integrations/duckduckgo-api-websearch.png new file mode 100644 index 00000000..29e204ff Binary files /dev/null and b/static/images/social/integrations/duckduckgo-api-websearch.png differ diff --git a/static/images/social/integrations/elasticsearch-document-store.png b/static/images/social/integrations/elasticsearch-document-store.png new file mode 100644 index 00000000..072d8ee0 Binary files /dev/null and b/static/images/social/integrations/elasticsearch-document-store.png differ diff --git a/static/images/social/integrations/elevenlabs.png b/static/images/social/integrations/elevenlabs.png new file mode 100644 index 00000000..e49468f3 Binary files /dev/null and b/static/images/social/integrations/elevenlabs.png differ diff --git a/static/images/social/integrations/exa.png b/static/images/social/integrations/exa.png new file mode 100644 index 00000000..ca2c7c4f Binary files /dev/null and b/static/images/social/integrations/exa.png differ diff --git a/static/images/social/integrations/faiss.png b/static/images/social/integrations/faiss.png new file mode 100644 index 00000000..47b27a93 Binary files /dev/null and b/static/images/social/integrations/faiss.png differ diff --git a/static/images/social/integrations/fastembed.png b/static/images/social/integrations/fastembed.png new file mode 100644 index 00000000..816e6b7f Binary files /dev/null and b/static/images/social/integrations/fastembed.png differ diff --git a/static/images/social/integrations/fastrag.png b/static/images/social/integrations/fastrag.png new file mode 100644 index 00000000..7e863d6a Binary files /dev/null and b/static/images/social/integrations/fastrag.png differ diff --git a/static/images/social/integrations/featherlessai.png b/static/images/social/integrations/featherlessai.png new file mode 100644 index 00000000..30dd9e64 Binary files /dev/null and b/static/images/social/integrations/featherlessai.png differ diff --git a/static/images/social/integrations/firecrawl.png b/static/images/social/integrations/firecrawl.png new file mode 100644 index 00000000..66b7a8cd Binary files /dev/null and b/static/images/social/integrations/firecrawl.png differ diff --git a/static/images/social/integrations/flow-judge.png b/static/images/social/integrations/flow-judge.png new file mode 100644 index 00000000..953f4af5 Binary files /dev/null and b/static/images/social/integrations/flow-judge.png differ diff --git a/static/images/social/integrations/github.png b/static/images/social/integrations/github.png new file mode 100644 index 00000000..d69dc8b9 Binary files /dev/null and b/static/images/social/integrations/github.png differ diff --git a/static/images/social/integrations/google-ai.png b/static/images/social/integrations/google-ai.png new file mode 100644 index 00000000..15c70ee9 Binary files /dev/null and b/static/images/social/integrations/google-ai.png differ diff --git a/static/images/social/integrations/google-genai.png b/static/images/social/integrations/google-genai.png new file mode 100644 index 00000000..c2bb68e3 Binary files /dev/null and b/static/images/social/integrations/google-genai.png differ diff --git a/static/images/social/integrations/google-vertex-ai.png b/static/images/social/integrations/google-vertex-ai.png new file mode 100644 index 00000000..c69a8565 Binary files /dev/null and b/static/images/social/integrations/google-vertex-ai.png differ diff --git a/static/images/social/integrations/groq.png b/static/images/social/integrations/groq.png new file mode 100644 index 00000000..a90123e8 Binary files /dev/null and b/static/images/social/integrations/groq.png differ diff --git a/static/images/social/integrations/hanlp.png b/static/images/social/integrations/hanlp.png new file mode 100644 index 00000000..1a6a96da Binary files /dev/null and b/static/images/social/integrations/hanlp.png differ diff --git a/static/images/social/integrations/huggingface.png b/static/images/social/integrations/huggingface.png new file mode 100644 index 00000000..f4896ff2 Binary files /dev/null and b/static/images/social/integrations/huggingface.png differ diff --git a/static/images/social/integrations/instructor-embedder.png b/static/images/social/integrations/instructor-embedder.png new file mode 100644 index 00000000..81f4481e Binary files /dev/null and b/static/images/social/integrations/instructor-embedder.png differ diff --git a/static/images/social/integrations/integrations.png b/static/images/social/integrations/integrations.png new file mode 100644 index 00000000..253696a2 Binary files /dev/null and b/static/images/social/integrations/integrations.png differ diff --git a/static/images/social/integrations/isaacus.png b/static/images/social/integrations/isaacus.png new file mode 100644 index 00000000..de628f32 Binary files /dev/null and b/static/images/social/integrations/isaacus.png differ diff --git a/static/images/social/integrations/jina.png b/static/images/social/integrations/jina.png new file mode 100644 index 00000000..241a4030 Binary files /dev/null and b/static/images/social/integrations/jina.png differ diff --git a/static/images/social/integrations/kreuzberg.png b/static/images/social/integrations/kreuzberg.png new file mode 100644 index 00000000..88f8e89c Binary files /dev/null and b/static/images/social/integrations/kreuzberg.png differ diff --git a/static/images/social/integrations/lancedb.png b/static/images/social/integrations/lancedb.png new file mode 100644 index 00000000..781e6a76 Binary files /dev/null and b/static/images/social/integrations/lancedb.png differ diff --git a/static/images/social/integrations/langfuse.png b/static/images/social/integrations/langfuse.png new file mode 100644 index 00000000..6666ec67 Binary files /dev/null and b/static/images/social/integrations/langfuse.png differ diff --git a/static/images/social/integrations/lara.png b/static/images/social/integrations/lara.png new file mode 100644 index 00000000..f38aa718 Binary files /dev/null and b/static/images/social/integrations/lara.png differ diff --git a/static/images/social/integrations/libreoffice-file-converter.png b/static/images/social/integrations/libreoffice-file-converter.png new file mode 100644 index 00000000..a1e6ae34 Binary files /dev/null and b/static/images/social/integrations/libreoffice-file-converter.png differ diff --git a/static/images/social/integrations/llama_cpp.png b/static/images/social/integrations/llama_cpp.png new file mode 100644 index 00000000..dd612b54 Binary files /dev/null and b/static/images/social/integrations/llama_cpp.png differ diff --git a/static/images/social/integrations/llama_stack.png b/static/images/social/integrations/llama_stack.png new file mode 100644 index 00000000..2f0ab313 Binary files /dev/null and b/static/images/social/integrations/llama_stack.png differ diff --git a/static/images/social/integrations/llamafile.png b/static/images/social/integrations/llamafile.png new file mode 100644 index 00000000..cd33bf4a Binary files /dev/null and b/static/images/social/integrations/llamafile.png differ diff --git a/static/images/social/integrations/lmformatenforcer.png b/static/images/social/integrations/lmformatenforcer.png new file mode 100644 index 00000000..4513afe3 Binary files /dev/null and b/static/images/social/integrations/lmformatenforcer.png differ diff --git a/static/images/social/integrations/markitdown.png b/static/images/social/integrations/markitdown.png new file mode 100644 index 00000000..2fe934df Binary files /dev/null and b/static/images/social/integrations/markitdown.png differ diff --git a/static/images/social/integrations/mastodon-fetcher.png b/static/images/social/integrations/mastodon-fetcher.png new file mode 100644 index 00000000..2115c628 Binary files /dev/null and b/static/images/social/integrations/mastodon-fetcher.png differ diff --git a/static/images/social/integrations/mcp.png b/static/images/social/integrations/mcp.png new file mode 100644 index 00000000..fb0821d1 Binary files /dev/null and b/static/images/social/integrations/mcp.png differ diff --git a/static/images/social/integrations/meta_llama.png b/static/images/social/integrations/meta_llama.png new file mode 100644 index 00000000..62bc6a4a Binary files /dev/null and b/static/images/social/integrations/meta_llama.png differ diff --git a/static/images/social/integrations/milvus-document-store.png b/static/images/social/integrations/milvus-document-store.png new file mode 100644 index 00000000..34ea7029 Binary files /dev/null and b/static/images/social/integrations/milvus-document-store.png differ diff --git a/static/images/social/integrations/mistral.png b/static/images/social/integrations/mistral.png new file mode 100644 index 00000000..191cc12a Binary files /dev/null and b/static/images/social/integrations/mistral.png differ diff --git a/static/images/social/integrations/mixedbread-ai.png b/static/images/social/integrations/mixedbread-ai.png new file mode 100644 index 00000000..973e8162 Binary files /dev/null and b/static/images/social/integrations/mixedbread-ai.png differ diff --git a/static/images/social/integrations/mlflow.png b/static/images/social/integrations/mlflow.png new file mode 100644 index 00000000..dec42fdb Binary files /dev/null and b/static/images/social/integrations/mlflow.png differ diff --git a/static/images/social/integrations/mongodb.png b/static/images/social/integrations/mongodb.png new file mode 100644 index 00000000..da8e3096 Binary files /dev/null and b/static/images/social/integrations/mongodb.png differ diff --git a/static/images/social/integrations/monsterapi.png b/static/images/social/integrations/monsterapi.png new file mode 100644 index 00000000..2c37abb2 Binary files /dev/null and b/static/images/social/integrations/monsterapi.png differ diff --git a/static/images/social/integrations/needle.png b/static/images/social/integrations/needle.png new file mode 100644 index 00000000..e1dd4386 Binary files /dev/null and b/static/images/social/integrations/needle.png differ diff --git a/static/images/social/integrations/neo4j-document-store.png b/static/images/social/integrations/neo4j-document-store.png new file mode 100644 index 00000000..265e8e09 Binary files /dev/null and b/static/images/social/integrations/neo4j-document-store.png differ diff --git a/static/images/social/integrations/notion-extractor.png b/static/images/social/integrations/notion-extractor.png new file mode 100644 index 00000000..675420ec Binary files /dev/null and b/static/images/social/integrations/notion-extractor.png differ diff --git a/static/images/social/integrations/nvidia.png b/static/images/social/integrations/nvidia.png new file mode 100644 index 00000000..cef5e902 Binary files /dev/null and b/static/images/social/integrations/nvidia.png differ diff --git a/static/images/social/integrations/ollama.png b/static/images/social/integrations/ollama.png new file mode 100644 index 00000000..dc1540d8 Binary files /dev/null and b/static/images/social/integrations/ollama.png differ diff --git a/static/images/social/integrations/opea.png b/static/images/social/integrations/opea.png new file mode 100644 index 00000000..fc0f9e9c Binary files /dev/null and b/static/images/social/integrations/opea.png differ diff --git a/static/images/social/integrations/openai.png b/static/images/social/integrations/openai.png new file mode 100644 index 00000000..ef0715db Binary files /dev/null and b/static/images/social/integrations/openai.png differ diff --git a/static/images/social/integrations/openlit.png b/static/images/social/integrations/openlit.png new file mode 100644 index 00000000..7a568c7c Binary files /dev/null and b/static/images/social/integrations/openlit.png differ diff --git a/static/images/social/integrations/openrouter.png b/static/images/social/integrations/openrouter.png new file mode 100644 index 00000000..60dacf26 Binary files /dev/null and b/static/images/social/integrations/openrouter.png differ diff --git a/static/images/social/integrations/opensearch-document-store.png b/static/images/social/integrations/opensearch-document-store.png new file mode 100644 index 00000000..b74b80b8 Binary files /dev/null and b/static/images/social/integrations/opensearch-document-store.png differ diff --git a/static/images/social/integrations/openstreetmap.png b/static/images/social/integrations/openstreetmap.png new file mode 100644 index 00000000..cda4f4d2 Binary files /dev/null and b/static/images/social/integrations/openstreetmap.png differ diff --git a/static/images/social/integrations/openwebui.png b/static/images/social/integrations/openwebui.png new file mode 100644 index 00000000..5675cd62 Binary files /dev/null and b/static/images/social/integrations/openwebui.png differ diff --git a/static/images/social/integrations/opik.png b/static/images/social/integrations/opik.png new file mode 100644 index 00000000..41db8afd Binary files /dev/null and b/static/images/social/integrations/opik.png differ diff --git a/static/images/social/integrations/optimum.png b/static/images/social/integrations/optimum.png new file mode 100644 index 00000000..68372743 Binary files /dev/null and b/static/images/social/integrations/optimum.png differ diff --git a/static/images/social/integrations/paddleocr.png b/static/images/social/integrations/paddleocr.png new file mode 100644 index 00000000..fd8d21fe Binary files /dev/null and b/static/images/social/integrations/paddleocr.png differ diff --git a/static/images/social/integrations/pgvector-documentstore.png b/static/images/social/integrations/pgvector-documentstore.png new file mode 100644 index 00000000..aad2cc0d Binary files /dev/null and b/static/images/social/integrations/pgvector-documentstore.png differ diff --git a/static/images/social/integrations/pinecone-document-store.png b/static/images/social/integrations/pinecone-document-store.png new file mode 100644 index 00000000..8f485670 Binary files /dev/null and b/static/images/social/integrations/pinecone-document-store.png differ diff --git a/static/images/social/integrations/praisonai.png b/static/images/social/integrations/praisonai.png new file mode 100644 index 00000000..1ebacfd2 Binary files /dev/null and b/static/images/social/integrations/praisonai.png differ diff --git a/static/images/social/integrations/prior-labs.png b/static/images/social/integrations/prior-labs.png new file mode 100644 index 00000000..241eb88f Binary files /dev/null and b/static/images/social/integrations/prior-labs.png differ diff --git a/static/images/social/integrations/pyversity.png b/static/images/social/integrations/pyversity.png new file mode 100644 index 00000000..2048f185 Binary files /dev/null and b/static/images/social/integrations/pyversity.png differ diff --git a/static/images/social/integrations/qdrant-document-store.png b/static/images/social/integrations/qdrant-document-store.png new file mode 100644 index 00000000..69ac515b Binary files /dev/null and b/static/images/social/integrations/qdrant-document-store.png differ diff --git a/static/images/social/integrations/ragas.png b/static/images/social/integrations/ragas.png new file mode 100644 index 00000000..c8b76d2e Binary files /dev/null and b/static/images/social/integrations/ragas.png differ diff --git a/static/images/social/integrations/ray.png b/static/images/social/integrations/ray.png new file mode 100644 index 00000000..de2e9872 Binary files /dev/null and b/static/images/social/integrations/ray.png differ diff --git a/static/images/social/integrations/sambanova.png b/static/images/social/integrations/sambanova.png new file mode 100644 index 00000000..44afca8b Binary files /dev/null and b/static/images/social/integrations/sambanova.png differ diff --git a/static/images/social/integrations/serpex.png b/static/images/social/integrations/serpex.png new file mode 100644 index 00000000..11474e4f Binary files /dev/null and b/static/images/social/integrations/serpex.png differ diff --git a/static/images/social/integrations/singlestore.png b/static/images/social/integrations/singlestore.png new file mode 100644 index 00000000..48fca100 Binary files /dev/null and b/static/images/social/integrations/singlestore.png differ diff --git a/static/images/social/integrations/snowflake.png b/static/images/social/integrations/snowflake.png new file mode 100644 index 00000000..7d09f530 Binary files /dev/null and b/static/images/social/integrations/snowflake.png differ diff --git a/static/images/social/integrations/stackit.png b/static/images/social/integrations/stackit.png new file mode 100644 index 00000000..0e1fc65f Binary files /dev/null and b/static/images/social/integrations/stackit.png differ diff --git a/static/images/social/integrations/titanml-takeoff.png b/static/images/social/integrations/titanml-takeoff.png new file mode 100644 index 00000000..08dc5c63 Binary files /dev/null and b/static/images/social/integrations/titanml-takeoff.png differ diff --git a/static/images/social/integrations/togetherai.png b/static/images/social/integrations/togetherai.png new file mode 100644 index 00000000..f7f4ad6d Binary files /dev/null and b/static/images/social/integrations/togetherai.png differ diff --git a/static/images/social/integrations/tonic-textual.png b/static/images/social/integrations/tonic-textual.png new file mode 100644 index 00000000..40f20a6d Binary files /dev/null and b/static/images/social/integrations/tonic-textual.png differ diff --git a/static/images/social/integrations/traceloop.png b/static/images/social/integrations/traceloop.png new file mode 100644 index 00000000..e4edf50f Binary files /dev/null and b/static/images/social/integrations/traceloop.png differ diff --git a/static/images/social/integrations/trafilatura.png b/static/images/social/integrations/trafilatura.png new file mode 100644 index 00000000..def34aec Binary files /dev/null and b/static/images/social/integrations/trafilatura.png differ diff --git a/static/images/social/integrations/unstructured-file-converter.png b/static/images/social/integrations/unstructured-file-converter.png new file mode 100644 index 00000000..3c3933a9 Binary files /dev/null and b/static/images/social/integrations/unstructured-file-converter.png differ diff --git a/static/images/social/integrations/valkey.png b/static/images/social/integrations/valkey.png new file mode 100644 index 00000000..f42d878d Binary files /dev/null and b/static/images/social/integrations/valkey.png differ diff --git a/static/images/social/integrations/valyu.png b/static/images/social/integrations/valyu.png new file mode 100644 index 00000000..d60b5982 Binary files /dev/null and b/static/images/social/integrations/valyu.png differ diff --git a/static/images/social/integrations/vllm.png b/static/images/social/integrations/vllm.png new file mode 100644 index 00000000..c5fb871e Binary files /dev/null and b/static/images/social/integrations/vllm.png differ diff --git a/static/images/social/integrations/voyage.png b/static/images/social/integrations/voyage.png new file mode 100644 index 00000000..8740af34 Binary files /dev/null and b/static/images/social/integrations/voyage.png differ diff --git a/static/images/social/integrations/watsonx.png b/static/images/social/integrations/watsonx.png new file mode 100644 index 00000000..a3703fc4 Binary files /dev/null and b/static/images/social/integrations/watsonx.png differ diff --git a/static/images/social/integrations/weaviate-document-store.png b/static/images/social/integrations/weaviate-document-store.png new file mode 100644 index 00000000..20355c90 Binary files /dev/null and b/static/images/social/integrations/weaviate-document-store.png differ diff --git a/static/images/social/integrations/weights-and-bias-tracer.png b/static/images/social/integrations/weights-and-bias-tracer.png new file mode 100644 index 00000000..844c9bbc Binary files /dev/null and b/static/images/social/integrations/weights-and-bias-tracer.png differ diff --git a/static/images/social/overview/demo.png b/static/images/social/overview/demo.png new file mode 100644 index 00000000..667b5387 Binary files /dev/null and b/static/images/social/overview/demo.png differ diff --git a/static/images/social/overview/intro.png b/static/images/social/overview/intro.png new file mode 100644 index 00000000..94be3cac Binary files /dev/null and b/static/images/social/overview/intro.png differ diff --git a/static/images/social/overview/overview.png b/static/images/social/overview/overview.png new file mode 100644 index 00000000..ebd0f8e3 Binary files /dev/null and b/static/images/social/overview/overview.png differ diff --git a/static/images/social/overview/quick-start.png b/static/images/social/overview/quick-start.png new file mode 100644 index 00000000..044f508c Binary files /dev/null and b/static/images/social/overview/quick-start.png differ diff --git a/static/images/social/overview/roadmap.png b/static/images/social/overview/roadmap.png new file mode 100644 index 00000000..0beccce6 Binary files /dev/null and b/static/images/social/overview/roadmap.png differ diff --git a/static/images/social/overview/use-cases.png b/static/images/social/overview/use-cases.png new file mode 100644 index 00000000..3f2b32bc Binary files /dev/null and b/static/images/social/overview/use-cases.png differ diff --git a/static/images/social/pages/community.png b/static/images/social/pages/community.png new file mode 100644 index 00000000..73ec4df9 Binary files /dev/null and b/static/images/social/pages/community.png differ diff --git a/static/images/social/pages/hacktoberfest.png b/static/images/social/pages/hacktoberfest.png new file mode 100644 index 00000000..3797bca8 Binary files /dev/null and b/static/images/social/pages/hacktoberfest.png differ diff --git a/static/images/social/pages/nlp-resources.png b/static/images/social/pages/nlp-resources.png new file mode 100644 index 00000000..4eeabd77 Binary files /dev/null and b/static/images/social/pages/nlp-resources.png differ diff --git a/static/images/social/pages/pages.png b/static/images/social/pages/pages.png new file mode 100644 index 00000000..e6c1f492 Binary files /dev/null and b/static/images/social/pages/pages.png differ diff --git a/static/images/social/release-notes/2.0.0.png b/static/images/social/release-notes/2.0.0.png new file mode 100644 index 00000000..621004e7 Binary files /dev/null and b/static/images/social/release-notes/2.0.0.png differ diff --git a/static/images/social/release-notes/2.0.1.png b/static/images/social/release-notes/2.0.1.png new file mode 100644 index 00000000..1ef99a6b Binary files /dev/null and b/static/images/social/release-notes/2.0.1.png differ diff --git a/static/images/social/release-notes/2.1.0.png b/static/images/social/release-notes/2.1.0.png new file mode 100644 index 00000000..09594743 Binary files /dev/null and b/static/images/social/release-notes/2.1.0.png differ diff --git a/static/images/social/release-notes/2.1.1.png b/static/images/social/release-notes/2.1.1.png new file mode 100644 index 00000000..a6acf0dc Binary files /dev/null and b/static/images/social/release-notes/2.1.1.png differ diff --git a/static/images/social/release-notes/2.1.2.png b/static/images/social/release-notes/2.1.2.png new file mode 100644 index 00000000..f70d592c Binary files /dev/null and b/static/images/social/release-notes/2.1.2.png differ diff --git a/static/images/social/release-notes/2.10.0.png b/static/images/social/release-notes/2.10.0.png new file mode 100644 index 00000000..eb7da994 Binary files /dev/null and b/static/images/social/release-notes/2.10.0.png differ diff --git a/static/images/social/release-notes/2.10.1.png b/static/images/social/release-notes/2.10.1.png new file mode 100644 index 00000000..203922dd Binary files /dev/null and b/static/images/social/release-notes/2.10.1.png differ diff --git a/static/images/social/release-notes/2.10.2.png b/static/images/social/release-notes/2.10.2.png new file mode 100644 index 00000000..139ee615 Binary files /dev/null and b/static/images/social/release-notes/2.10.2.png differ diff --git a/static/images/social/release-notes/2.10.3.png b/static/images/social/release-notes/2.10.3.png new file mode 100644 index 00000000..f7b7abf0 Binary files /dev/null and b/static/images/social/release-notes/2.10.3.png differ diff --git a/static/images/social/release-notes/2.11.0.png b/static/images/social/release-notes/2.11.0.png new file mode 100644 index 00000000..4e5417a2 Binary files /dev/null and b/static/images/social/release-notes/2.11.0.png differ diff --git a/static/images/social/release-notes/2.11.1.png b/static/images/social/release-notes/2.11.1.png new file mode 100644 index 00000000..b944b2fd Binary files /dev/null and b/static/images/social/release-notes/2.11.1.png differ diff --git a/static/images/social/release-notes/2.11.2.png b/static/images/social/release-notes/2.11.2.png new file mode 100644 index 00000000..8b7af536 Binary files /dev/null and b/static/images/social/release-notes/2.11.2.png differ diff --git a/static/images/social/release-notes/2.12.0.png b/static/images/social/release-notes/2.12.0.png new file mode 100644 index 00000000..9271e6f2 Binary files /dev/null and b/static/images/social/release-notes/2.12.0.png differ diff --git a/static/images/social/release-notes/2.12.1.png b/static/images/social/release-notes/2.12.1.png new file mode 100644 index 00000000..7f69ec33 Binary files /dev/null and b/static/images/social/release-notes/2.12.1.png differ diff --git a/static/images/social/release-notes/2.12.2.png b/static/images/social/release-notes/2.12.2.png new file mode 100644 index 00000000..5a332a16 Binary files /dev/null and b/static/images/social/release-notes/2.12.2.png differ diff --git a/static/images/social/release-notes/2.13.0.png b/static/images/social/release-notes/2.13.0.png new file mode 100644 index 00000000..c823f16a Binary files /dev/null and b/static/images/social/release-notes/2.13.0.png differ diff --git a/static/images/social/release-notes/2.13.1.png b/static/images/social/release-notes/2.13.1.png new file mode 100644 index 00000000..e7650b66 Binary files /dev/null and b/static/images/social/release-notes/2.13.1.png differ diff --git a/static/images/social/release-notes/2.13.2.png b/static/images/social/release-notes/2.13.2.png new file mode 100644 index 00000000..85362996 Binary files /dev/null and b/static/images/social/release-notes/2.13.2.png differ diff --git a/static/images/social/release-notes/2.14.0.png b/static/images/social/release-notes/2.14.0.png new file mode 100644 index 00000000..c2605704 Binary files /dev/null and b/static/images/social/release-notes/2.14.0.png differ diff --git a/static/images/social/release-notes/2.14.1.png b/static/images/social/release-notes/2.14.1.png new file mode 100644 index 00000000..a53c0a2d Binary files /dev/null and b/static/images/social/release-notes/2.14.1.png differ diff --git a/static/images/social/release-notes/2.14.2.png b/static/images/social/release-notes/2.14.2.png new file mode 100644 index 00000000..a065b317 Binary files /dev/null and b/static/images/social/release-notes/2.14.2.png differ diff --git a/static/images/social/release-notes/2.14.3.png b/static/images/social/release-notes/2.14.3.png new file mode 100644 index 00000000..f39722a8 Binary files /dev/null and b/static/images/social/release-notes/2.14.3.png differ diff --git a/static/images/social/release-notes/2.15.0.png b/static/images/social/release-notes/2.15.0.png new file mode 100644 index 00000000..b8d27e31 Binary files /dev/null and b/static/images/social/release-notes/2.15.0.png differ diff --git a/static/images/social/release-notes/2.15.1.png b/static/images/social/release-notes/2.15.1.png new file mode 100644 index 00000000..4dbfaaaf Binary files /dev/null and b/static/images/social/release-notes/2.15.1.png differ diff --git a/static/images/social/release-notes/2.15.2.png b/static/images/social/release-notes/2.15.2.png new file mode 100644 index 00000000..6e9aecdc Binary files /dev/null and b/static/images/social/release-notes/2.15.2.png differ diff --git a/static/images/social/release-notes/2.16.0.png b/static/images/social/release-notes/2.16.0.png new file mode 100644 index 00000000..a11ee34a Binary files /dev/null and b/static/images/social/release-notes/2.16.0.png differ diff --git a/static/images/social/release-notes/2.16.1.png b/static/images/social/release-notes/2.16.1.png new file mode 100644 index 00000000..474bf78d Binary files /dev/null and b/static/images/social/release-notes/2.16.1.png differ diff --git a/static/images/social/release-notes/2.17.0.png b/static/images/social/release-notes/2.17.0.png new file mode 100644 index 00000000..35b2bf76 Binary files /dev/null and b/static/images/social/release-notes/2.17.0.png differ diff --git a/static/images/social/release-notes/2.17.1.png b/static/images/social/release-notes/2.17.1.png new file mode 100644 index 00000000..76cb01a1 Binary files /dev/null and b/static/images/social/release-notes/2.17.1.png differ diff --git a/static/images/social/release-notes/2.18.0.png b/static/images/social/release-notes/2.18.0.png new file mode 100644 index 00000000..4e9ab76a Binary files /dev/null and b/static/images/social/release-notes/2.18.0.png differ diff --git a/static/images/social/release-notes/2.18.1.png b/static/images/social/release-notes/2.18.1.png new file mode 100644 index 00000000..32c7e136 Binary files /dev/null and b/static/images/social/release-notes/2.18.1.png differ diff --git a/static/images/social/release-notes/2.19.0.png b/static/images/social/release-notes/2.19.0.png new file mode 100644 index 00000000..5549e39d Binary files /dev/null and b/static/images/social/release-notes/2.19.0.png differ diff --git a/static/images/social/release-notes/2.2.0.png b/static/images/social/release-notes/2.2.0.png new file mode 100644 index 00000000..e2748b6a Binary files /dev/null and b/static/images/social/release-notes/2.2.0.png differ diff --git a/static/images/social/release-notes/2.2.1.png b/static/images/social/release-notes/2.2.1.png new file mode 100644 index 00000000..cfe7405e Binary files /dev/null and b/static/images/social/release-notes/2.2.1.png differ diff --git a/static/images/social/release-notes/2.2.2.png b/static/images/social/release-notes/2.2.2.png new file mode 100644 index 00000000..9038dbab Binary files /dev/null and b/static/images/social/release-notes/2.2.2.png differ diff --git a/static/images/social/release-notes/2.2.3.png b/static/images/social/release-notes/2.2.3.png new file mode 100644 index 00000000..9aeb948d Binary files /dev/null and b/static/images/social/release-notes/2.2.3.png differ diff --git a/static/images/social/release-notes/2.2.4.png b/static/images/social/release-notes/2.2.4.png new file mode 100644 index 00000000..f58196ee Binary files /dev/null and b/static/images/social/release-notes/2.2.4.png differ diff --git a/static/images/social/release-notes/2.20.0.png b/static/images/social/release-notes/2.20.0.png new file mode 100644 index 00000000..6eb249fc Binary files /dev/null and b/static/images/social/release-notes/2.20.0.png differ diff --git a/static/images/social/release-notes/2.21.0.png b/static/images/social/release-notes/2.21.0.png new file mode 100644 index 00000000..612e33ae Binary files /dev/null and b/static/images/social/release-notes/2.21.0.png differ diff --git a/static/images/social/release-notes/2.22.0.png b/static/images/social/release-notes/2.22.0.png new file mode 100644 index 00000000..64df4e4e Binary files /dev/null and b/static/images/social/release-notes/2.22.0.png differ diff --git a/static/images/social/release-notes/2.23.0.png b/static/images/social/release-notes/2.23.0.png new file mode 100644 index 00000000..0c49d428 Binary files /dev/null and b/static/images/social/release-notes/2.23.0.png differ diff --git a/static/images/social/release-notes/2.24.0.png b/static/images/social/release-notes/2.24.0.png new file mode 100644 index 00000000..cd1a8246 Binary files /dev/null and b/static/images/social/release-notes/2.24.0.png differ diff --git a/static/images/social/release-notes/2.24.1.png b/static/images/social/release-notes/2.24.1.png new file mode 100644 index 00000000..8d5e29a4 Binary files /dev/null and b/static/images/social/release-notes/2.24.1.png differ diff --git a/static/images/social/release-notes/2.25.0.png b/static/images/social/release-notes/2.25.0.png new file mode 100644 index 00000000..676ae522 Binary files /dev/null and b/static/images/social/release-notes/2.25.0.png differ diff --git a/static/images/social/release-notes/2.25.1.png b/static/images/social/release-notes/2.25.1.png new file mode 100644 index 00000000..fb116ee0 Binary files /dev/null and b/static/images/social/release-notes/2.25.1.png differ diff --git a/static/images/social/release-notes/2.25.2.png b/static/images/social/release-notes/2.25.2.png new file mode 100644 index 00000000..132798df Binary files /dev/null and b/static/images/social/release-notes/2.25.2.png differ diff --git a/static/images/social/release-notes/2.26.0.png b/static/images/social/release-notes/2.26.0.png new file mode 100644 index 00000000..2cd29c35 Binary files /dev/null and b/static/images/social/release-notes/2.26.0.png differ diff --git a/static/images/social/release-notes/2.27.0.png b/static/images/social/release-notes/2.27.0.png new file mode 100644 index 00000000..20af75ed Binary files /dev/null and b/static/images/social/release-notes/2.27.0.png differ diff --git a/static/images/social/release-notes/2.28.0.png b/static/images/social/release-notes/2.28.0.png new file mode 100644 index 00000000..aa4468b3 Binary files /dev/null and b/static/images/social/release-notes/2.28.0.png differ diff --git a/static/images/social/release-notes/2.29.0.png b/static/images/social/release-notes/2.29.0.png new file mode 100644 index 00000000..bd7c64fe Binary files /dev/null and b/static/images/social/release-notes/2.29.0.png differ diff --git a/static/images/social/release-notes/2.3.0.png b/static/images/social/release-notes/2.3.0.png new file mode 100644 index 00000000..00389b67 Binary files /dev/null and b/static/images/social/release-notes/2.3.0.png differ diff --git a/static/images/social/release-notes/2.3.1.png b/static/images/social/release-notes/2.3.1.png new file mode 100644 index 00000000..027a2d58 Binary files /dev/null and b/static/images/social/release-notes/2.3.1.png differ diff --git a/static/images/social/release-notes/2.30.0.png b/static/images/social/release-notes/2.30.0.png new file mode 100644 index 00000000..535496a4 Binary files /dev/null and b/static/images/social/release-notes/2.30.0.png differ diff --git a/static/images/social/release-notes/2.30.1.png b/static/images/social/release-notes/2.30.1.png new file mode 100644 index 00000000..0cddb174 Binary files /dev/null and b/static/images/social/release-notes/2.30.1.png differ diff --git a/static/images/social/release-notes/2.30.2.png b/static/images/social/release-notes/2.30.2.png new file mode 100644 index 00000000..271eb65a Binary files /dev/null and b/static/images/social/release-notes/2.30.2.png differ diff --git a/static/images/social/release-notes/2.4.0.png b/static/images/social/release-notes/2.4.0.png new file mode 100644 index 00000000..74b20a16 Binary files /dev/null and b/static/images/social/release-notes/2.4.0.png differ diff --git a/static/images/social/release-notes/2.5.0.png b/static/images/social/release-notes/2.5.0.png new file mode 100644 index 00000000..495b61e2 Binary files /dev/null and b/static/images/social/release-notes/2.5.0.png differ diff --git a/static/images/social/release-notes/2.5.1.png b/static/images/social/release-notes/2.5.1.png new file mode 100644 index 00000000..5b0b6407 Binary files /dev/null and b/static/images/social/release-notes/2.5.1.png differ diff --git a/static/images/social/release-notes/2.6.0.png b/static/images/social/release-notes/2.6.0.png new file mode 100644 index 00000000..13c3f417 Binary files /dev/null and b/static/images/social/release-notes/2.6.0.png differ diff --git a/static/images/social/release-notes/2.6.1.png b/static/images/social/release-notes/2.6.1.png new file mode 100644 index 00000000..9018cd72 Binary files /dev/null and b/static/images/social/release-notes/2.6.1.png differ diff --git a/static/images/social/release-notes/2.7.0.png b/static/images/social/release-notes/2.7.0.png new file mode 100644 index 00000000..ad6ab865 Binary files /dev/null and b/static/images/social/release-notes/2.7.0.png differ diff --git a/static/images/social/release-notes/2.8.0.png b/static/images/social/release-notes/2.8.0.png new file mode 100644 index 00000000..69301168 Binary files /dev/null and b/static/images/social/release-notes/2.8.0.png differ diff --git a/static/images/social/release-notes/2.8.1.png b/static/images/social/release-notes/2.8.1.png new file mode 100644 index 00000000..026b94f1 Binary files /dev/null and b/static/images/social/release-notes/2.8.1.png differ diff --git a/static/images/social/release-notes/2.9.0.png b/static/images/social/release-notes/2.9.0.png new file mode 100644 index 00000000..5158142d Binary files /dev/null and b/static/images/social/release-notes/2.9.0.png differ diff --git a/static/images/social/release-notes/release-notes.png b/static/images/social/release-notes/release-notes.png new file mode 100644 index 00000000..9c00390c Binary files /dev/null and b/static/images/social/release-notes/release-notes.png differ diff --git a/static/images/social/release-notes/v2.20.0.png b/static/images/social/release-notes/v2.20.0.png new file mode 100644 index 00000000..629c1755 Binary files /dev/null and b/static/images/social/release-notes/v2.20.0.png differ diff --git a/static/images/social/release-notes/v2.22.0.png b/static/images/social/release-notes/v2.22.0.png new file mode 100644 index 00000000..3243acb7 Binary files /dev/null and b/static/images/social/release-notes/v2.22.0.png differ diff --git a/static/images/social/release-notes/v2.25.1.png b/static/images/social/release-notes/v2.25.1.png new file mode 100644 index 00000000..50910826 Binary files /dev/null and b/static/images/social/release-notes/v2.25.1.png differ diff --git a/static/images/social/spring-into-haystack/challenge.png b/static/images/social/spring-into-haystack/challenge.png new file mode 100644 index 00000000..e045e53f Binary files /dev/null and b/static/images/social/spring-into-haystack/challenge.png differ diff --git a/static/images/social/spring-into-haystack/spring-into-haystack.png b/static/images/social/spring-into-haystack/spring-into-haystack.png new file mode 100644 index 00000000..85dfe7e8 Binary files /dev/null and b/static/images/social/spring-into-haystack/spring-into-haystack.png differ diff --git a/static/images/social/tutorials/27_First_RAG_Pipeline.png b/static/images/social/tutorials/27_First_RAG_Pipeline.png new file mode 100644 index 00000000..65d84f7d Binary files /dev/null and b/static/images/social/tutorials/27_First_RAG_Pipeline.png differ diff --git a/static/images/social/tutorials/28_Structured_Output_With_OpenAI.png b/static/images/social/tutorials/28_Structured_Output_With_OpenAI.png new file mode 100644 index 00000000..596400d7 Binary files /dev/null and b/static/images/social/tutorials/28_Structured_Output_With_OpenAI.png differ diff --git a/static/images/social/tutorials/29_Serializing_Pipelines.png b/static/images/social/tutorials/29_Serializing_Pipelines.png new file mode 100644 index 00000000..3e9f9b97 Binary files /dev/null and b/static/images/social/tutorials/29_Serializing_Pipelines.png differ diff --git a/static/images/social/tutorials/30_File_Type_Preprocessing_Index_Pipeline.png b/static/images/social/tutorials/30_File_Type_Preprocessing_Index_Pipeline.png new file mode 100644 index 00000000..d39d93ed Binary files /dev/null and b/static/images/social/tutorials/30_File_Type_Preprocessing_Index_Pipeline.png differ diff --git a/static/images/social/tutorials/31_Metadata_Filtering.png b/static/images/social/tutorials/31_Metadata_Filtering.png new file mode 100644 index 00000000..f20d6073 Binary files /dev/null and b/static/images/social/tutorials/31_Metadata_Filtering.png differ diff --git a/static/images/social/tutorials/32_Classifying_Documents_and_Queries_by_Language.png b/static/images/social/tutorials/32_Classifying_Documents_and_Queries_by_Language.png new file mode 100644 index 00000000..805733a0 Binary files /dev/null and b/static/images/social/tutorials/32_Classifying_Documents_and_Queries_by_Language.png differ diff --git a/static/images/social/tutorials/33_Hybrid_Retrieval.png b/static/images/social/tutorials/33_Hybrid_Retrieval.png new file mode 100644 index 00000000..b850be25 Binary files /dev/null and b/static/images/social/tutorials/33_Hybrid_Retrieval.png differ diff --git a/static/images/social/tutorials/34_Extractive_QA_Pipeline.png b/static/images/social/tutorials/34_Extractive_QA_Pipeline.png new file mode 100644 index 00000000..884c9d8a Binary files /dev/null and b/static/images/social/tutorials/34_Extractive_QA_Pipeline.png differ diff --git a/static/images/social/tutorials/35_Evaluating_RAG_Pipelines.png b/static/images/social/tutorials/35_Evaluating_RAG_Pipelines.png new file mode 100644 index 00000000..6697e085 Binary files /dev/null and b/static/images/social/tutorials/35_Evaluating_RAG_Pipelines.png differ diff --git a/static/images/social/tutorials/36_Building_Fallbacks_with_Conditional_Routing.png b/static/images/social/tutorials/36_Building_Fallbacks_with_Conditional_Routing.png new file mode 100644 index 00000000..b68f40d0 Binary files /dev/null and b/static/images/social/tutorials/36_Building_Fallbacks_with_Conditional_Routing.png differ diff --git a/static/images/social/tutorials/37_Simplifying_Pipeline_Inputs_with_Multiplexer.png b/static/images/social/tutorials/37_Simplifying_Pipeline_Inputs_with_Multiplexer.png new file mode 100644 index 00000000..0a00275e Binary files /dev/null and b/static/images/social/tutorials/37_Simplifying_Pipeline_Inputs_with_Multiplexer.png differ diff --git a/static/images/social/tutorials/39_Embedding_Metadata_for_Improved_Retrieval.png b/static/images/social/tutorials/39_Embedding_Metadata_for_Improved_Retrieval.png new file mode 100644 index 00000000..da8167b7 Binary files /dev/null and b/static/images/social/tutorials/39_Embedding_Metadata_for_Improved_Retrieval.png differ diff --git a/static/images/social/tutorials/40_Building_Chat_Application_with_Function_Calling.png b/static/images/social/tutorials/40_Building_Chat_Application_with_Function_Calling.png new file mode 100644 index 00000000..179d1417 Binary files /dev/null and b/static/images/social/tutorials/40_Building_Chat_Application_with_Function_Calling.png differ diff --git a/static/images/social/tutorials/41_Query_Classification_with_TransformersTextRouter_and_TransformersZeroShotTextRouter.png b/static/images/social/tutorials/41_Query_Classification_with_TransformersTextRouter_and_TransformersZeroShotTextRouter.png new file mode 100644 index 00000000..b67d281a Binary files /dev/null and b/static/images/social/tutorials/41_Query_Classification_with_TransformersTextRouter_and_TransformersZeroShotTextRouter.png differ diff --git a/static/images/social/tutorials/42_Sentence_Window_Retriever.png b/static/images/social/tutorials/42_Sentence_Window_Retriever.png new file mode 100644 index 00000000..40883d32 Binary files /dev/null and b/static/images/social/tutorials/42_Sentence_Window_Retriever.png differ diff --git a/static/images/social/tutorials/43_Building_a_Tool_Calling_Agent.png b/static/images/social/tutorials/43_Building_a_Tool_Calling_Agent.png new file mode 100644 index 00000000..5c7f09e2 Binary files /dev/null and b/static/images/social/tutorials/43_Building_a_Tool_Calling_Agent.png differ diff --git a/static/images/social/tutorials/44_Creating_Custom_SuperComponents.png b/static/images/social/tutorials/44_Creating_Custom_SuperComponents.png new file mode 100644 index 00000000..c1688b87 Binary files /dev/null and b/static/images/social/tutorials/44_Creating_Custom_SuperComponents.png differ diff --git a/static/images/social/tutorials/45_Creating_a_Multi_Agent_System.png b/static/images/social/tutorials/45_Creating_a_Multi_Agent_System.png new file mode 100644 index 00000000..6b7acc23 Binary files /dev/null and b/static/images/social/tutorials/45_Creating_a_Multi_Agent_System.png differ diff --git a/static/images/social/tutorials/46_Multimodal_RAG.png b/static/images/social/tutorials/46_Multimodal_RAG.png new file mode 100644 index 00000000..323414be Binary files /dev/null and b/static/images/social/tutorials/46_Multimodal_RAG.png differ diff --git a/static/images/social/tutorials/47_Human_in_the_Loop_Agent.png b/static/images/social/tutorials/47_Human_in_the_Loop_Agent.png new file mode 100644 index 00000000..a83228dd Binary files /dev/null and b/static/images/social/tutorials/47_Human_in_the_Loop_Agent.png differ diff --git a/static/images/social/tutorials/48_Conversational_RAG.png b/static/images/social/tutorials/48_Conversational_RAG.png new file mode 100644 index 00000000..13e48701 Binary files /dev/null and b/static/images/social/tutorials/48_Conversational_RAG.png differ diff --git a/static/images/social/tutorials/guide_evaluation.png b/static/images/social/tutorials/guide_evaluation.png new file mode 100644 index 00000000..1fd7f4c3 Binary files /dev/null and b/static/images/social/tutorials/guide_evaluation.png differ diff --git a/static/images/social/tutorials/tutorials.png b/static/images/social/tutorials/tutorials.png new file mode 100644 index 00000000..bfbd79c5 Binary files /dev/null and b/static/images/social/tutorials/tutorials.png differ diff --git a/themes/haystack/layouts/partials/opengraph.html b/themes/haystack/layouts/partials/opengraph.html index 6f2dae70..85af2c20 100644 --- a/themes/haystack/layouts/partials/opengraph.html +++ b/themes/haystack/layouts/partials/opengraph.html @@ -48,18 +48,33 @@ " /> -{{- with $.Params.images -}} - {{- range first 6 . }} +{{- $slug := "" -}} +{{- with .File -}} + {{- $slug = .ContentBaseName -}} + {{- if or (eq $slug "index") (eq $slug "_index") -}} + {{- $slug = path.Base (strings.TrimSuffix .Dir "/") -}} + {{- end -}} +{{- end -}} +{{- $socialImageStatic := printf "static/images/social/%s/%s.png" .Section $slug -}} +{{- $socialImageURL := printf "/images/social/%s/%s.png" .Section $slug -}} +{{- $hasSocialImage := and $slug (os.FileExists $socialImageStatic) -}} + +{{- if $.Params.images -}} + {{- range first 6 $.Params.images }} {{ end -}} +{{- else if $hasSocialImage -}} + {{- else -}} {{- with $.Site.Params.images }} {{ end -}} {{- end -}} -{{- with $.Params.images -}} - +{{- if $.Params.images -}} + +{{- else if $hasSocialImage -}} + {{- else -}} {{- with $.Site.Params.images }}