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
54 changes: 54 additions & 0 deletions .github/workflows/site-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Marketing Site Build

on:
pull_request:
paths:
- 'web/**'
- 'package.json'
- 'pnpm-workspace.yaml'
- '.github/workflows/site-build.yml'
push:
branches: [main]
paths:
- 'web/**'
- 'package.json'
- 'pnpm-workspace.yaml'

concurrency:
group: site-${{ github.ref }}
cancel-in-progress: true

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Set up pnpm
# No `version:` — packageManager in package.json is the canonical source.
uses: pnpm/action-setup@v4

- name: pnpm cache
uses: actions/cache@v4
with:
path: ~/.pnpm-store
key: pnpm-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: pnpm-${{ runner.os }}-

- name: Install
run: pnpm install --frozen-lockfile

- name: Build site
run: pnpm -F @qg/site build

- name: Upload dist artifact
uses: actions/upload-artifact@v4
with:
name: site-dist
path: web/site/dist
retention-days: 7
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
"description": "Workspace root for QueryGym websites (leaderboard + marketing). Not published.",
"scripts": {
"build:leaderboard": "pnpm -F @qg/leaderboard build:data && pnpm -F @qg/leaderboard build",
"dev:leaderboard": "pnpm -F @qg/leaderboard dev"
"dev:leaderboard": "pnpm -F @qg/leaderboard dev",
"build:site": "pnpm -F @qg/site build",
"dev:site": "pnpm -F @qg/site dev"
},
"engines": {
"node": ">=20",
Expand Down
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions web/site/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";

export default defineConfig({
site: "https://querygym.com",
output: "static",
integrations: [tailwind({ applyBaseStyles: false })],
vite: {
ssr: {
noExternal: ["@qg/shared"],
},
},
});
23 changes: 23 additions & 0 deletions web/site/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@qg/site",
"version": "0.0.0",
"private": true,
"type": "module",
"description": "Astro site for querygym.com — marketing/landing site for the toolkit.",
"scripts": {
"build": "astro build",
"dev": "astro dev",
"preview": "astro preview",
"check": "astro check"
},
"dependencies": {
"@qg/shared": "workspace:*",
"astro": "^5.0.0",
"@astrojs/tailwind": "^5.1.5",
"tailwindcss": "^3.4.0"
},
"devDependencies": {
"typescript": "^5.6.0",
"@types/node": "^20.0.0"
}
}
8 changes: 8 additions & 0 deletions web/site/public/_redirects
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Cloudflare Pages-style _redirects file. Same-origin redirects served from
# querygym.com. Cross-origin redirects (to leaderboard.querygym.com) live in
# Cloudflare Bulk Redirects on the zone, not here.

# Old Jekyll URLs → new pages
/leaderboard https://leaderboard.querygym.com/ 301
/leaderboard.html https://leaderboard.querygym.com/ 301
/QueryGym/* https://querygym.com/:splat 301
Binary file added web/site/public/querygym-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions web/site/src/components/CitationCard.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
interface Props {
title: string;
venue: string;
authors: string;
bibtex: string;
url?: string;
}

const { title, venue, authors, bibtex, url } = Astro.props;
---

<article class="qg-card">
<div class="qg-pill">{venue}</div>
<h3 class="mt-3 text-lg font-semibold">{title}</h3>
<p class="mt-1 text-sm text-qg-fg-muted">{authors}</p>
{
url && (
<a
class="mt-2 inline-block text-sm font-medium text-qg-accent hover:underline"
href={url}
target="_blank"
rel="noopener noreferrer"
>
{url} ↗
</a>
)
}

<div class="mt-4">
<details>
<summary class="cursor-pointer text-sm font-medium text-qg-fg-muted hover:text-qg-fg">
BibTeX
</summary>
<pre class="mt-2 overflow-x-auto rounded bg-qg-bg p-3 text-xs"><code>{bibtex}</code></pre>
</details>
</div>
</article>
35 changes: 35 additions & 0 deletions web/site/src/components/CodeBlock.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
interface Props {
language?: string;
filename?: string;
}
const { language = "bash", filename } = Astro.props;
---

<div class="overflow-hidden rounded-lg border border-qg-border bg-qg-bg-soft">
<div class="flex items-center justify-between border-b border-qg-border bg-qg-bg px-4 py-2 text-xs text-qg-fg-muted">
<span class="qg-mono">{filename ?? language}</span>
<button
type="button"
class="qg-copy-btn rounded border border-qg-border bg-qg-bg-soft px-2 py-0.5 text-xs hover:border-qg-accent"
>
copy
</button>
</div>
<pre class="overflow-x-auto px-4 py-4 text-sm"><code class="qg-mono"><slot /></code></pre>
</div>

<script>
document.querySelectorAll<HTMLButtonElement>(".qg-copy-btn").forEach((btn) => {
btn.addEventListener("click", () => {
const pre = btn.closest("div")?.nextElementSibling as HTMLElement | null;
const code = pre?.querySelector("code");
if (!code) return;
navigator.clipboard.writeText(code.textContent ?? "").then(() => {
const original = btn.textContent;
btn.textContent = "copied";
setTimeout(() => (btn.textContent = original ?? "copy"), 1200);
});
});
});
</script>
77 changes: 77 additions & 0 deletions web/site/src/components/EcosystemMap.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
/**
* Visual map of the QueryGym ecosystem: Library / Dashboard / Leaderboard / Docs.
* Static SVG-free version using CSS grid + gradient borders.
*/

const cards = [
{
label: "Library",
sub: "querygym (pip)",
body: "The toolkit itself. Methods, prompt bank, searchers, CLI.",
href: "https://pypi.org/project/querygym/",
cta: "pip install querygym",
primary: false,
},
{
label: "Dashboard",
sub: "dashboard.querygym.com",
body: "Hosted product. Run methods through a UI, compare results live, share runs. API keys + billing.",
href: "https://dashboard.querygym.com",
cta: "Open Dashboard ↗",
primary: true,
},
{
label: "Leaderboard",
sub: "leaderboard.querygym.com",
body: "SIGIR 2026 reproducibility results across IR benchmarks. Every row backed by a citable JSON.",
href: "https://leaderboard.querygym.com",
cta: "View Leaderboard →",
primary: false,
},
{
label: "Docs",
sub: "querygym.readthedocs.io",
body: "API reference, methods reference, contributor guide, schema docs.",
href: "https://querygym.readthedocs.io",
cta: "Read the Docs →",
primary: false,
},
];
---

<section class="qg-section">
<h2 class="text-3xl font-bold md:text-4xl">The ecosystem</h2>
<p class="mt-3 max-w-2xl text-qg-fg-muted">
QueryGym is split into four surfaces. They share the same data contract,
so a run from the dashboard or a third-party submitter lands in the same
leaderboard as the toolkit's own results.
</p>

<div class="mt-10 grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{
cards.map((c) => (
<a
href={c.href}
target={c.href.startsWith("http") ? "_blank" : undefined}
rel={c.href.startsWith("http") ? "noopener noreferrer" : undefined}
class:list={[
"qg-card flex flex-col justify-between !p-5",
c.primary && "!border-qg-accent",
]}
>
<div>
<div class="text-xs uppercase tracking-wide text-qg-fg-muted">
{c.sub}
</div>
<div class:list={["mt-1 text-xl font-semibold", c.primary && "text-qg-accent"]}>
{c.label}
</div>
<p class="mt-2 text-sm text-qg-fg-muted">{c.body}</p>
</div>
<div class="mt-4 text-sm font-medium">{c.cta}</div>
</a>
))
}
</div>
</section>
60 changes: 60 additions & 0 deletions web/site/src/components/FeatureGrid.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
const features = [
{
title: "Single Prompt Bank",
body:
"One YAML registry of every prompt with version, license, and authorship metadata. Cite the exact text used in any run.",
icon: "📚",
},
{
title: "Pluggable searchers",
body:
"Drop-in adapters for Pyserini, PyTerrier, BEIR, MS MARCO, and any custom retriever. Bring your own index.",
icon: "🔌",
},
{
title: "Stable run schema",
body:
"Every run emits a versioned JSON conforming to a public JSON Schema — same shape across the toolkit, dashboard, and third-party submitters.",
icon: "🧬",
},
{
title: "OpenAI-compatible LLMs",
body:
"Works with any OpenAI-compatible endpoint. Gpt-4.1, Qwen, Mistral, vLLM, Ollama — switch with a config change.",
icon: "🧠",
},
{
title: "Reproducible by design",
body:
"Every leaderboard row links a JSON, a TREC run file, and the reformulated queries. Re-evaluate from a fresh clone.",
icon: "🔁",
},
{
title: "Citable artifacts",
body:
"Backed by two papers (WWW 2026 Demos, SIGIR 2026 Reproducibility) and a tagged reproducibility corpus on GitHub.",
icon: "📄",
},
];
---

<section class="qg-section">
<h2 class="text-3xl font-bold md:text-4xl">What you get</h2>
<p class="mt-3 max-w-2xl text-qg-fg-muted">
QueryGym pairs a small, opinionated library with a contract-driven
reproducibility pipeline. The toolkit, the dashboard, and the leaderboard
all share the same data shape.
</p>
<ul class="mt-10 grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{
features.map((f) => (
<li class="qg-card">
<div class="text-2xl">{f.icon}</div>
<h3 class="mt-3 text-lg font-semibold">{f.title}</h3>
<p class="mt-2 text-sm text-qg-fg-muted">{f.body}</p>
</li>
))
}
</ul>
</section>
Loading
Loading