Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@ jobs:
mkdir -p site/simulations
cp landing/simulations/index.html site/simulations/index.html 2>/dev/null || true

- name: Copy landing favicon and assets
# Landing index.html references /favicon.svg, /favicon.ico,
# /apple-touch-icon.png, and /assets/og-image.png. Without these
# the X/Twitter share preview 404s and the browser tab falls back
# to the MkDocs Material favicon. Merge into the existing
# site/assets dir rather than replacing it (Material ships its
# own assets there).
run: |
cp landing/favicon.svg site/favicon.svg
cp landing/favicon.ico site/favicon.ico
cp landing/apple-touch-icon.png site/apple-touch-icon.png
mkdir -p site/assets
cp -R landing/assets/. site/assets/

- name: Install selectools (for builder assembly)
run: pip install -e .

Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ mypy_output.txt

# Hypothesis test artifacts
.hypothesis/

.playwright-mcp/
Binary file added landing/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added landing/favicon.ico
Binary file not shown.
90 changes: 9 additions & 81 deletions landing/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,8 @@
<meta name="description" content="Selectools is a Python library for building production-ready AI agents with tool calling, RAG, and multi-agent orchestration. 152 models, 50 evaluators, visual builder. Supports OpenAI, Anthropic, Gemini, Ollama. Free and open source." />
<link rel="canonical" href="https://selectools.dev/" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<script>
/* OVERDRIVE FUSED — wordmark variant: read ?logo=1|2|3, persist
to localStorage, set body[data-logo] BEFORE paint to avoid FOUC */
(function() {
try {
var p = new URLSearchParams(window.location.search).get('logo');
var stored = localStorage.getItem('sel-logo');
var logo = (p === '1' || p === '2' || p === '3') ? p : (stored || '1');
if (p) localStorage.setItem('sel-logo', logo);
document.documentElement.setAttribute('data-logo', logo);
document.addEventListener('DOMContentLoaded', function() {
document.body.setAttribute('data-logo', logo);
});
} catch (e) { document.documentElement.setAttribute('data-logo', '1'); }
})();
</script>
<link rel="icon" type="image/x-icon" href="favicon.ico" sizes="any" />
<link rel="apple-touch-icon" sizes="180x180" href="apple-touch-icon.png" />
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large" />
<meta name="keywords" content="python ai agent, tool calling, rag, hybrid search, openai, anthropic, gemini, ollama, ai agent framework, llm tools, python llm, multi-agent, agent graph, langchain alternative" />
<meta name="author" content="NichevLabs" />
Expand All @@ -33,6 +19,7 @@
<meta property="og:site_name" content="Selectools" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="https://selectools.dev/assets/og-image.png" />
<meta property="og:image:secure_url" content="https://selectools.dev/assets/og-image.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
Expand Down Expand Up @@ -3907,61 +3894,12 @@
}

/* ════════════════════════════════════════════════════════════════
OVERDRIVE FUSED — wordmarks (3 switchable variants)
The body carries data-logo="1|2|3" set on load by reading the
?logo= URL param (default "1"). Each variant renders inside the
nav-logo slot; only the active one is displayed.
OVERDRIVE FUSED — wordmark (terminal banner art)
Single variant: 3-row box-drawing wordmark in the nav-logo slot.
════════════════════════════════════════════════════════════════ */
.wm { display: none; align-items: center; }
[data-logo="1"] .wm--1,
[data-logo="2"] .wm--2,
[data-logo="3"] .wm--3 { display: inline-flex; }

/* Variant 1 — [•] selectools */
.wm--1 {
gap: 8px;
font-family: var(--font-ui);
font-weight: 800;
font-size: 17px;
color: #fff;
letter-spacing: -0.03em;
}
.wm--1__brackets {
font-family: var(--font-mono);
font-weight: 600;
color: var(--exec-color);
font-size: 16px;
display: inline-flex;
align-items: center;
gap: 3px;
}
.wm--1__brackets::before { content: "["; }
.wm--1__brackets::after { content: "]"; }
.wm--1__brackets .exec-dot { width: 6px; height: 6px; }

/* Variant 2 — s▌electools (cursor inside the word) */
.wm--2 {
font-family: var(--font-ui);
font-weight: 800;
font-size: 17px;
color: #fff;
letter-spacing: -0.03em;
gap: 0;
}
.wm--2__pre,
.wm--2__post { display: inline-block; }
.wm--2__caret {
display: inline-block;
width: 0.18em;
height: 0.95em;
background: var(--exec-color);
box-shadow: 0 0 6px var(--exec-glow);
vertical-align: text-bottom;
animation: exec-blink 1.05s steps(2, jump-none) infinite;
margin: 0 1px;
}
.wm { display: inline-flex; align-items: center; }

/* Variant 3 — terminal banner art (3-row box-drawing) */
/* Terminal banner art (3-row box-drawing) */
.wm--3 {
font-family: var(--font-mono);
color: var(--exec-color);
Expand Down Expand Up @@ -4351,8 +4289,7 @@
.exec-caret { animation: none; opacity: 1; }
.exec-scan.in-view::after { animation: none; display: none; }

/* OVERDRIVE FUSED — wordmarks: kill blink + scan animations */
.wm--2__caret { animation: none !important; opacity: 1; }
/* OVERDRIVE FUSED — wordmark: kill scan animation */
.wm--3__banner.is-typing { mask-image: none !important; -webkit-mask-image: none !important; }

/* OVERDRIVE FUSED — #faq: kill answer fade-in */
Expand Down Expand Up @@ -4417,16 +4354,7 @@
<div class="wrap">
<a href="#" class="nav-logo" aria-label="selectools home">
<span class="vh">selectools</span>
<!-- Variant 1: [•] selectools -->
<span class="wm wm--1" aria-hidden="true">
<span class="wm--1__brackets"><span class="exec-dot exec-dot--sm"></span></span>
<span>selectools</span>
</span>
<!-- Variant 2: s▌electools -->
<span class="wm wm--2" aria-hidden="true">
<span class="wm--2__pre">s</span><span class="wm--2__caret"></span><span class="wm--2__post">electools</span>
</span>
<!-- Variant 3: terminal banner art (3-row box-drawing) -->
<!-- Wordmark: terminal banner art (3-row box-drawing) -->
<span class="wm wm--3" aria-hidden="true"><pre class="wm--3__banner">┌─┐┌─┐┬ ┌─┐┌─┐┌┬┐┌─┐┌─┐┬ ┌─┐
└─┐├┤ │ ├┤ │ │ │ ││ ││ └─┐
└─┘└─┘┴─┘└─┘└─┘ ┴ └─┘└─┘┴─┘└─┘</pre></span>
Expand Down
127 changes: 127 additions & 0 deletions scripts/build_favicons.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env python3
"""Render the selectools favicon to PNG (apple-touch-icon) and ICO (legacy fallback).

The source-of-truth design lives in landing/favicon.svg as a 32x32 viewBox:
- Rounded square background, fill #0f172a, corner radius 6
- Cyan (#22d3ee) brackets [ ] flanking a center dot
- Dot at (16, 16) with r=3

Rather than rendering the SVG (which depends on JetBrains Mono being installed),
we redraw the design from primitives so it's reproducible in any environment with
just Pillow. Output goes back into landing/ where the deploy workflow picks it up.

Run: python3 scripts/build_favicons.py
"""
from __future__ import annotations

from pathlib import Path

from PIL import Image, ImageDraw

REPO_ROOT = Path(__file__).resolve().parents[1]
LANDING = REPO_ROOT / "landing"

BG = (15, 23, 42, 255) # #0f172a
FG = (34, 211, 238, 255) # #22d3ee


def draw_master(size: int) -> Image.Image:
"""Draw the favicon at an arbitrary square size, scaled from the 32x32 design."""
img = Image.new("RGBA", (size, size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)

# Scale factor from the 32-unit SVG viewBox to the target pixel size.
s = size / 32.0

# Background: rounded square. SVG has rx=6 on a 32x32 — scale proportionally.
radius = round(6 * s)
draw.rounded_rectangle((0, 0, size - 1, size - 1), radius=radius, fill=BG)

# Bracket geometry (in 32-unit design space):
# bracket "stem" is 1.6 wide, 14 tall, centered vertically (y=9..23)
# bracket "arms" stick out 3 units to the right (left bracket) or left (right)
stem_w = 1.6
arm_h = 1.6
arm_w = 3.0
bracket_top = 9
bracket_bottom = 23

def bracket(x_stem: float, arm_dir: int) -> None:
# arm_dir: +1 = arms extend right (left bracket), -1 = arms extend left (right bracket)
# Vertical stem
draw.rectangle(
(
round(x_stem * s),
round(bracket_top * s),
round((x_stem + stem_w) * s),
round(bracket_bottom * s),
),
fill=FG,
)
# Top arm
arm_x0 = x_stem if arm_dir > 0 else x_stem + stem_w - arm_w
arm_x1 = arm_x0 + arm_w
draw.rectangle(
(
round(arm_x0 * s),
round(bracket_top * s),
round(arm_x1 * s),
round((bracket_top + arm_h) * s),
),
fill=FG,
)
# Bottom arm
draw.rectangle(
(
round(arm_x0 * s),
round((bracket_bottom - arm_h) * s),
round(arm_x1 * s),
round(bracket_bottom * s),
),
fill=FG,
)

# Left bracket (arms extend right)
bracket(x_stem=7.0, arm_dir=+1)
# Right bracket (arms extend left)
bracket(x_stem=23.4, arm_dir=-1)

# Center dot — design has r=3 in 32-unit space
cx, cy, r = 16, 16, 3
draw.ellipse(
(
round((cx - r) * s),
round((cy - r) * s),
round((cx + r) * s),
round((cy + r) * s),
),
fill=FG,
)

return img


def main() -> None:
# Draw at 512 once, then downsample for each target size — antialiasing wins.
master = draw_master(512)

# apple-touch-icon: iOS Home Screen, Safari pinned tab. 180x180 PNG is the
# canonical size; everything else is a downscale from there in practice.
apple = master.resize((180, 180), Image.Resampling.LANCZOS)
apple_path = LANDING / "apple-touch-icon.png"
apple.save(apple_path, "PNG", optimize=True)
print(f"wrote {apple_path.relative_to(REPO_ROOT)} ({apple_path.stat().st_size} bytes)")

# favicon.ico: legacy fallback for very old browsers + Windows tile cache.
# Multi-size container: 16, 32, 48 — Pillow generates each from the master.
ico_path = LANDING / "favicon.ico"
master.save(
ico_path,
format="ICO",
sizes=[(16, 16), (32, 32), (48, 48)],
)
print(f"wrote {ico_path.relative_to(REPO_ROOT)} ({ico_path.stat().st_size} bytes)")


if __name__ == "__main__":
main()