mirror of
https://github.com/confident-ai/deepeval.git
synced 2026-10-12 02:12:15 +00:00
udpate readme
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
<p align="center">
|
||||
<img src="https://github.com/confident-ai/deepeval/blob/main/docs/static/img/deepeval.png" alt="DeepEval Logo" width="100%">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="assets/hero/wordmark-dark.svg">
|
||||
<img alt="DeepEval." src="assets/hero/wordmark-light.svg" width="520">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.0 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.0 KiB |
@@ -37,6 +37,7 @@
|
||||
"@types/node": "^25.6.0",
|
||||
"@types/react": "^19.2.14",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"opentype.js": "^2.0.0",
|
||||
"postcss": "^8.5.10",
|
||||
"sass": "^1.99.0",
|
||||
"tailwindcss": "^4.2.2",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<svg width="132" height="132" viewBox="0 0 132 132" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g filter="url(#filter0_i_0_1)">
|
||||
<rect x="9" y="9" width="114" height="112" fill="#FCFCFB"/>
|
||||
<rect x="9" y="9" width="114" height="112"/>
|
||||
<rect x="1" y="1" width="130" height="130" fill="#FCFCFB"/>
|
||||
<rect x="1" y="1" width="130" height="130"/>
|
||||
</g>
|
||||
<rect x="10" y="10" width="112" height="110" stroke="#4400FF" stroke-width="2"/>
|
||||
<rect x="0.5" y="0.5" width="131" height="131" stroke="#4400FF" stroke-width="1" fill="none"/>
|
||||
<path d="M61.088 36.4C65.3707 36.4 69.1547 37.2213 72.44 38.864C75.7253 40.448 78.4533 42.6773 80.624 45.552C82.8533 48.368 84.5253 51.6533 85.64 55.408C86.7547 59.104 87.312 63.0347 87.312 67.2C87.312 72.8907 86.3147 78.0827 84.32 82.776C82.3253 87.4107 79.392 91.1067 75.52 93.864C71.648 96.6213 66.8373 98 61.088 98H39.616C38.384 98 37.328 97.5893 36.448 96.768C35.568 95.888 35.128 94.8027 35.128 93.512V40.888C35.128 39.5973 35.568 38.5413 36.448 37.72C37.328 36.84 38.384 36.4 39.616 36.4H61.088ZM60.208 89.552C64.3733 89.552 67.7467 88.5253 70.328 86.472C72.9093 84.4187 74.7867 81.72 75.96 78.376C77.192 74.9733 77.808 71.248 77.808 67.2C77.808 64.208 77.456 61.392 76.752 58.752C76.1067 56.0533 75.0507 53.6773 73.584 51.624C72.176 49.512 70.3573 47.8693 68.128 46.696C65.9573 45.464 63.3173 44.848 60.208 44.848H43.576L44.368 44.056V90.432L43.84 89.552H60.208ZM100.496 97.912C99.0295 97.912 97.8855 97.5013 97.0642 96.68C96.3015 95.8587 95.9202 94.7147 95.9202 93.248V91.664C95.9202 90.1973 96.3015 89.0533 97.0642 88.232C97.8855 87.4107 99.0295 87 100.496 87H101.464C102.931 87 104.046 87.4107 104.808 88.232C105.571 89.0533 105.952 90.1973 105.952 91.664V93.248C105.952 94.7147 105.571 95.8587 104.808 96.68C104.046 97.5013 102.931 97.912 101.464 97.912H100.496Z" fill="black"/>
|
||||
<line x1="116" y1="131.5" x2="132" y2="131.5" stroke="black"/>
|
||||
<line x1="131.5" y1="132" x2="131.5" y2="116" stroke="black"/>
|
||||
@@ -14,7 +14,7 @@
|
||||
<line x1="116" y1="0.5" x2="132" y2="0.5" stroke="black"/>
|
||||
<line x1="131.5" y1="16" x2="131.5" stroke="black"/>
|
||||
<defs>
|
||||
<filter id="filter0_i_0_1" x="9" y="9" width="114" height="112" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<filter id="filter0_i_0_1" x="1" y="1" width="130" height="130" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* One-off generator for the README hero assets.
|
||||
*
|
||||
* Produces 4 SVGs under `assets/hero/`:
|
||||
* - wordmark-light.svg / wordmark-dark.svg (DeepEval icon + "DeepEval.")
|
||||
* - tagline-light.svg / tagline-dark.svg ("The LLM Evaluation Framework")
|
||||
*
|
||||
* The README references them via <picture> + prefers-color-scheme so GitHub
|
||||
* swaps the right variant for each viewer's theme. Each SVG embeds CSS
|
||||
* keyframe animations (logo scale-in, letter stagger fade, tagline fade-up)
|
||||
* that GitHub's image proxy preserves for animated SVGs.
|
||||
*
|
||||
* Quicksand SemiBold is downloaded from the Google Fonts CDN and cached
|
||||
* locally under `docs/scripts/fonts/`. Glyphs are converted to outline
|
||||
* <path> elements via opentype.js so no font dependency exists at render
|
||||
* time — the SVG is pixel-identical in every renderer (GitHub camo,
|
||||
* Safari, etc.) regardless of font availability.
|
||||
*
|
||||
* Run: yarn build-readme-hero (from docs/)
|
||||
*/
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import opentype from 'opentype.js';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = path.resolve(__dirname, '../..');
|
||||
const OUT_DIR = path.join(REPO_ROOT, 'assets/hero');
|
||||
const FONT_DIR = path.join(__dirname, 'fonts');
|
||||
const FONT_PATH = path.join(FONT_DIR, 'Quicksand-SemiBold.ttf');
|
||||
|
||||
// google-webfonts-helper packages each Google Font weight as a static TTF
|
||||
// (Google's own repo only ships the variable Quicksand[wght].ttf, whose
|
||||
// default instance is Light/300 — opentype.js can't reliably interpolate
|
||||
// that to SemiBold without bundling the full variable-axis math). The
|
||||
// gwfh zip contains a single static `quicksand-v37-latin-600.ttf` with
|
||||
// the SemiBold outlines we want.
|
||||
const FONT_ZIP_URL =
|
||||
'https://gwfh.mranftl.com/api/fonts/quicksand?download=zip&subsets=latin&variants=600&formats=ttf';
|
||||
|
||||
const ACCENT = '#4400FF'; // DeepEval brand purple
|
||||
const LIGHT_FG = '#0A0A0A';
|
||||
const DARK_FG = '#FAFAFA';
|
||||
|
||||
async function ensureFont() {
|
||||
try {
|
||||
await fs.access(FONT_PATH);
|
||||
return;
|
||||
} catch {}
|
||||
await fs.mkdir(FONT_DIR, { recursive: true });
|
||||
console.log('Downloading Quicksand SemiBold…');
|
||||
const res = await fetch(FONT_ZIP_URL);
|
||||
if (!res.ok) throw new Error(`Font download failed: ${res.status}`);
|
||||
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'quicksand-'));
|
||||
const zipPath = path.join(tmp, 'q.zip');
|
||||
await fs.writeFile(zipPath, Buffer.from(await res.arrayBuffer()));
|
||||
execFileSync('unzip', ['-q', '-o', zipPath, '-d', tmp]);
|
||||
const entries = await fs.readdir(tmp);
|
||||
const ttfName = entries.find((n) => n.endsWith('.ttf'));
|
||||
if (!ttfName) throw new Error('No TTF inside font zip');
|
||||
await fs.copyFile(path.join(tmp, ttfName), FONT_PATH);
|
||||
await fs.rm(tmp, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the wordmark SVG: just "DeepEval." with the period in the brand
|
||||
* purple. Each letter sits in its own <g> so we can stagger the entrance
|
||||
* animation per-letter.
|
||||
*/
|
||||
async function buildWordmark(font, mode) {
|
||||
const fg = mode === 'dark' ? DARK_FG : LIGHT_FG;
|
||||
const text = 'DeepEval.';
|
||||
const fontSize = 96;
|
||||
|
||||
// Lay out each glyph individually so we can animate them separately.
|
||||
// We use opentype's per-glyph advance widths so kerning is preserved.
|
||||
const glyphs = [];
|
||||
let cursor = 0;
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const ch = text[i];
|
||||
const p = font.getPath(ch, cursor, 0, fontSize);
|
||||
glyphs.push({ ch, d: p.toPathData(2), x: cursor });
|
||||
const advance =
|
||||
(font.charToGlyph(ch).advanceWidth / font.unitsPerEm) * fontSize;
|
||||
if (i < text.length - 1) {
|
||||
const kern = font.getKerningValue(
|
||||
font.charToGlyph(ch),
|
||||
font.charToGlyph(text[i + 1]),
|
||||
);
|
||||
cursor += advance + (kern / font.unitsPerEm) * fontSize;
|
||||
} else {
|
||||
cursor += advance;
|
||||
}
|
||||
}
|
||||
|
||||
const wholePath = font.getPath(text, 0, 0, fontSize);
|
||||
const bbox = wholePath.getBoundingBox();
|
||||
const textWidth = bbox.x2 - bbox.x1;
|
||||
const textHeight = bbox.y2 - bbox.y1;
|
||||
|
||||
const padX = 4;
|
||||
const padY = 12;
|
||||
const totalW = textWidth + padX * 2;
|
||||
const totalH = textHeight + padY * 2;
|
||||
// Translate so the text's visual bbox sits inside the padded canvas:
|
||||
// text glyphs are positioned with baseline at y=0, so we translate by
|
||||
// (padY - bbox.y1) on Y to drop the top of the bbox at padY.
|
||||
const tx = padX - bbox.x1;
|
||||
const ty = padY - bbox.y1;
|
||||
|
||||
let letterGs = '';
|
||||
glyphs.forEach((g, i) => {
|
||||
const delay = (i * 45 + 100).toFixed(0);
|
||||
const fill = g.ch === '.' ? ACCENT : fg;
|
||||
letterGs += `<g class="hero-letter" style="animation-delay:${delay}ms" fill="${fill}"><path d="${g.d}" transform="translate(${tx.toFixed(2)},${ty.toFixed(2)})"/></g>`;
|
||||
});
|
||||
|
||||
const css = `
|
||||
.hero-letter { opacity: 0; transform: translateY(8px); animation: heroLetterIn 500ms cubic-bezier(.2,.8,.2,1) forwards; }
|
||||
@keyframes heroLetterIn { to { opacity: 1; transform: translateY(0); } }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.hero-letter { opacity: 1 !important; transform: none !important; animation: none !important; }
|
||||
}
|
||||
`;
|
||||
|
||||
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${Math.ceil(totalW)} ${Math.ceil(totalH)}" width="${Math.ceil(totalW)}" height="${Math.ceil(totalH)}" role="img" aria-label="DeepEval.">
|
||||
<style>${css}</style>
|
||||
${letterGs}
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await ensureFont();
|
||||
await fs.mkdir(OUT_DIR, { recursive: true });
|
||||
|
||||
const font = opentype.parse((await fs.readFile(FONT_PATH)).buffer);
|
||||
|
||||
for (const mode of ['light', 'dark']) {
|
||||
const wordmark = await buildWordmark(font, mode);
|
||||
await fs.writeFile(path.join(OUT_DIR, `wordmark-${mode}.svg`), wordmark);
|
||||
console.log(`wrote wordmark-${mode}.svg`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Binary file not shown.
@@ -3333,6 +3333,11 @@ oniguruma-to-es@^4.3.4:
|
||||
regex "^6.1.0"
|
||||
regex-recursion "^6.0.2"
|
||||
|
||||
opentype.js@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/opentype.js/-/opentype.js-2.0.0.tgz#b87fcec8107075aabb6f50fe5e6bb4d8db6c8c7d"
|
||||
integrity sha512-kCyjv6xdDY1W/jLWZ/L3QhhTlKUqDZMQ5+Jdlw12b3dXkKNpYBqqlMMj0YDQPShWFTMwgZI1hG14kN3XUDSg/A==
|
||||
|
||||
package-manager-detector@^1.3.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/package-manager-detector/-/package-manager-detector-1.6.0.tgz#70d0cf0aa02c877eeaf66c4d984ede0be9130734"
|
||||
|
||||
Reference in New Issue
Block a user