diff --git a/README.md b/README.md index e5068d3..08771e1 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ public/brand/ logo, project icons src/ main.ts single entry, mounts whatever a page contains data/site.ts projects and people. Edit content here - components/ nav, footer, cards, mark draw, reveals, marquee + components/ nav, footer, cards, mark draw, reveals lib/eggzec-block.ts the eggzec Block bitmap display font, as TypeScript styles/ tokens, base, layout, components, pages ``` @@ -49,7 +49,6 @@ Almost everything is data rather than markup, and it all lives in | Projects, taglines, repository and docs and PyPI links | `PROJECTS` | | Human facing name when the repo name is not one | `display` on a project | | Which projects lead the landing page | `LEAD` in `src/data/site.ts` | -| Who builds on our packages | `src/data/used-by.ts` | | Maintainers | `PEOPLE` | | Nav links | `NAV` | | Footer links | `COLUMNS` in `src/components/footer.ts` | @@ -64,12 +63,6 @@ card appears twice. Landing page statistics are counted from the catalog at runtime, so the numbers cannot drift from what is actually listed. -`src/data/used-by.ts` is the one generated file. It comes from GitHub's -dependency graph — the `network/dependents` listing for each repository — -filtered to named organisations and checked one at a time against the API. Only -pydoe, pyswarm, mcerp and soerp have any dependents, so that is what the list -draws on. Regenerate it by hand rather than trusting it to stay current. - ## Design Colours are the Electropop palette on white: diff --git a/index.html b/index.html index 5710c19..d6421d7 100644 --- a/index.html +++ b/index.html @@ -57,11 +57,6 @@ GitHub - -
-

Used by

-
-
diff --git a/public/brand/users/anesthesiasimulation.png b/public/brand/users/anesthesiasimulation.png deleted file mode 100644 index 7021627..0000000 Binary files a/public/brand/users/anesthesiasimulation.png and /dev/null differ diff --git a/public/brand/users/atomicateam.png b/public/brand/users/atomicateam.png deleted file mode 100644 index c0fe0af..0000000 Binary files a/public/brand/users/atomicateam.png and /dev/null differ diff --git a/public/brand/users/bgu-aidnd.png b/public/brand/users/bgu-aidnd.png deleted file mode 100644 index 81dc910..0000000 Binary files a/public/brand/users/bgu-aidnd.png and /dev/null differ diff --git a/public/brand/users/cmu-db.png b/public/brand/users/cmu-db.png deleted file mode 100644 index f12fb30..0000000 Binary files a/public/brand/users/cmu-db.png and /dev/null differ diff --git a/public/brand/users/codecentric.png b/public/brand/users/codecentric.png deleted file mode 100644 index 15931f8..0000000 Binary files a/public/brand/users/codecentric.png and /dev/null differ diff --git a/public/brand/users/curtin-timescales-of-mineral-systems.png b/public/brand/users/curtin-timescales-of-mineral-systems.png deleted file mode 100644 index 5ed7961..0000000 Binary files a/public/brand/users/curtin-timescales-of-mineral-systems.png and /dev/null differ diff --git a/public/brand/users/emsl-computing.png b/public/brand/users/emsl-computing.png deleted file mode 100644 index bb41529..0000000 Binary files a/public/brand/users/emsl-computing.png and /dev/null differ diff --git a/public/brand/users/gemseo.png b/public/brand/users/gemseo.png deleted file mode 100644 index e08202c..0000000 Binary files a/public/brand/users/gemseo.png and /dev/null differ diff --git a/public/brand/users/ibm.png b/public/brand/users/ibm.png deleted file mode 100644 index 14d6e44..0000000 Binary files a/public/brand/users/ibm.png and /dev/null differ diff --git a/public/brand/users/icb-dcm.png b/public/brand/users/icb-dcm.png deleted file mode 100644 index 7d30c01..0000000 Binary files a/public/brand/users/icb-dcm.png and /dev/null differ diff --git a/public/brand/users/icrar.png b/public/brand/users/icrar.png deleted file mode 100644 index f7608eb..0000000 Binary files a/public/brand/users/icrar.png and /dev/null differ diff --git a/public/brand/users/industrial-optimization-group.png b/public/brand/users/industrial-optimization-group.png deleted file mode 100644 index 2733469..0000000 Binary files a/public/brand/users/industrial-optimization-group.png and /dev/null differ diff --git a/public/brand/users/lanl.png b/public/brand/users/lanl.png deleted file mode 100644 index da138db..0000000 Binary files a/public/brand/users/lanl.png and /dev/null differ diff --git a/public/brand/users/llnl.png b/public/brand/users/llnl.png deleted file mode 100644 index f975e4c..0000000 Binary files a/public/brand/users/llnl.png and /dev/null differ diff --git a/public/brand/users/nanograv.png b/public/brand/users/nanograv.png deleted file mode 100644 index e82bcd6..0000000 Binary files a/public/brand/users/nanograv.png and /dev/null differ diff --git a/public/brand/users/nasa.png b/public/brand/users/nasa.png deleted file mode 100644 index a101449..0000000 Binary files a/public/brand/users/nasa.png and /dev/null differ diff --git a/public/brand/users/national-digital-twin.png b/public/brand/users/national-digital-twin.png deleted file mode 100644 index 5210425..0000000 Binary files a/public/brand/users/national-digital-twin.png and /dev/null differ diff --git a/public/brand/users/openmdao.png b/public/brand/users/openmdao.png deleted file mode 100644 index d411a29..0000000 Binary files a/public/brand/users/openmdao.png and /dev/null differ diff --git a/public/brand/users/openworm.png b/public/brand/users/openworm.png deleted file mode 100644 index a2642b7..0000000 Binary files a/public/brand/users/openworm.png and /dev/null differ diff --git a/public/brand/users/paypal.png b/public/brand/users/paypal.png deleted file mode 100644 index e50e0a2..0000000 Binary files a/public/brand/users/paypal.png and /dev/null differ diff --git a/public/brand/users/philipmorrisintl.png b/public/brand/users/philipmorrisintl.png deleted file mode 100644 index 7482905..0000000 Binary files a/public/brand/users/philipmorrisintl.png and /dev/null differ diff --git a/public/brand/users/qubole.png b/public/brand/users/qubole.png deleted file mode 100644 index 4f79e10..0000000 Binary files a/public/brand/users/qubole.png and /dev/null differ diff --git a/public/brand/users/sandialabs.png b/public/brand/users/sandialabs.png deleted file mode 100644 index 893d2b4..0000000 Binary files a/public/brand/users/sandialabs.png and /dev/null differ diff --git a/public/brand/users/seduceproject.png b/public/brand/users/seduceproject.png deleted file mode 100644 index 2bc1582..0000000 Binary files a/public/brand/users/seduceproject.png and /dev/null differ diff --git a/public/brand/users/smtorg.png b/public/brand/users/smtorg.png deleted file mode 100644 index e0f7024..0000000 Binary files a/public/brand/users/smtorg.png and /dev/null differ diff --git a/public/brand/users/terrapower.png b/public/brand/users/terrapower.png deleted file mode 100644 index 129d222..0000000 Binary files a/public/brand/users/terrapower.png and /dev/null differ diff --git a/public/brand/users/wwu-mmll.png b/public/brand/users/wwu-mmll.png deleted file mode 100644 index f153ba4..0000000 Binary files a/public/brand/users/wwu-mmll.png and /dev/null differ diff --git a/src/components/content.ts b/src/components/content.ts index fbdfd15..27d02d4 100644 --- a/src/components/content.ts +++ b/src/components/content.ts @@ -4,7 +4,6 @@ */ import { PEOPLE, PROJECTS } from '../data/site' -import { USED_BY } from '../data/used-by' /** * `
` on /community/. @@ -32,28 +31,6 @@ export function mountPeople(root: ParentNode = document): void { ).join('') } -/** - * `
` in the hero ticker. - * - * Logos rather than names: at a glance a reader recognises a mark faster than - * they read an org login. Filled before the marquee mounts so it measures real - * content, and each one links to the repository the dependency was verified in, - * because a claim like this is only worth making if it can be checked. - */ -export function mountUsedBy(root: ParentNode = document): void { - const host = root.querySelector('[data-used-by]') - if (!host) return - - host.innerHTML = USED_BY.map( - (d) => ` - - - `, - ).join('') -} - /** * Fills `[data-stat]` elements from the catalog rather than from hard-coded * markup, so the landing-page numbers cannot drift from what is actually diff --git a/src/components/marquee.ts b/src/components/marquee.ts deleted file mode 100644 index eedd492..0000000 --- a/src/components/marquee.ts +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Infinite marquee. - * - * Duplicates its track until the strip is at least twice the viewport width, - * then translates by exactly one copy — so the loop is seamless regardless of - * content width. Speed is expressed in px/second, which keeps long and short - * strips moving at the same visual pace. - * - *
…items…
- */ - -import { prefersReducedMotion } from '../lib/env' - -export function mountMarquees(root: ParentNode = document): void { - for (const host of root.querySelectorAll('[data-marquee]')) { - setupMarquee(host) - } -} - -function setupMarquee(host: HTMLElement): void { - const original = Array.from(host.children) as HTMLElement[] - if (original.length === 0) return - - const speed = Number(host.dataset.speed ?? 40) - const direction = Number(host.dataset.direction ?? 1) >= 0 ? 1 : -1 - - const viewport = document.createElement('div') - viewport.className = 'marquee__viewport' - - const track = document.createElement('div') - track.className = 'marquee__track' - - const group = document.createElement('div') - group.className = 'marquee__group' - group.append(...original) - track.append(group) - viewport.append(track) - host.replaceChildren(viewport) - host.classList.add('marquee') - - const measure = () => { - // Reset to a single group before measuring, then top up. - for (const clone of Array.from(track.querySelectorAll('.marquee__group[data-clone]'))) { - clone.remove() - } - - const groupWidth = group.getBoundingClientRect().width - if (groupWidth < 1) return - - const needed = Math.ceil((host.getBoundingClientRect().width * 2) / groupWidth) - for (let i = 0; i < Math.max(1, needed); i++) { - const clone = group.cloneNode(true) as HTMLElement - clone.dataset.clone = 'true' - clone.setAttribute('aria-hidden', 'true') - track.append(clone) - } - - track.style.setProperty('--marquee-shift', `${groupWidth}px`) - track.style.setProperty('--marquee-duration', `${groupWidth / speed}s`) - track.style.setProperty('--marquee-direction', direction > 0 ? 'normal' : 'reverse') - track.dataset.animated = String(!prefersReducedMotion()) - } - - measure() - - // Late-loading logos change the group width — re-measure when they land. - for (const img of host.querySelectorAll('img')) { - if (!img.complete) img.addEventListener('load', measure, { once: true }) - } - - let resizeTimer = 0 - new ResizeObserver(() => { - window.clearTimeout(resizeTimer) - resizeTimer = window.setTimeout(measure, 120) - }).observe(host) -} diff --git a/src/data/used-by.ts b/src/data/used-by.ts deleted file mode 100644 index 8ffacc8..0000000 --- a/src/data/used-by.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Organisations that build on these packages. - * - * Most entries were verified against the package manifests on the repository's - * default branch: a `requirements*.txt`, `setup.py`, `setup.cfg` or - * `pyproject.toml` that names the package. Entries that only appeared in - * GitHub's dependents *index* did not survive — that index lags, and several of - * them had since dropped the dependency or commented it out. - * - * The exception is the block marked below, taken from the "Who uses PyDOE?" - * page in the pydoe documentation. Those repositories no longer declare pyDOE - * on their default branch, so they are kept on the strength of that page rather - * than a manifest we can point at today. - * - * pyDOE2 and pyDOE3 count as pyDOE: both forks have been merged back and are - * maintained here, so a manifest naming either one is a pyDOE user. - * - * pyswarm is matched on a word boundary so that `pyswarms` — an unrelated - * package by a different author — never counts. - * - * Logos are the organisations' own marks, desaturated and normalised to a single - * height by `tools/build-user-logos.py`, so the strip stays even and nothing - * flashes colour while it loads. Orgs whose avatar is a default identicon, a - * photograph, or an image that turns to mush in greyscale are left off rather - * than shown badly. - */ - -export interface Dependent { - /** GitHub org, and the basename of its logo under `/brand/users/`. */ - login: string - name: string - /** The repository the dependency was verified in. */ - repo: string - uses: string -} - -export const USED_BY: Dependent[] = [ - { login: 'nasa', name: 'NASA', repo: 'nasa/GlennOPT', uses: 'pyDOE' }, - { login: 'llnl', name: 'Lawrence Livermore National Laboratory', repo: 'llnl/merlin', uses: 'pyDOE' }, - { login: 'lanl', name: 'Los Alamos National Laboratory', repo: 'lanl/impala', uses: 'pyswarm' }, - { login: 'sandialabs', name: 'Sandia National Laboratories', repo: 'sandialabs/pvOps', uses: 'pyDOE' }, - { login: 'OpenMDAO', name: 'OpenMDAO', repo: 'OpenMDAO/OpenMDAO', uses: 'pyDOE' }, - { login: 'philipmorrisintl', name: 'Philip Morris International', repo: 'philipmorrisintl/GOBench', uses: 'pyswarm' }, - { login: 'cmu-db', name: 'CMU Database Group', repo: 'cmu-db/ottertune', uses: 'pyDOE' }, - { login: 'qubole', name: 'Qubole', repo: 'qubole/uchit', uses: 'pyDOE' }, - { login: 'codecentric', name: 'codecentric', repo: 'codecentric/maximum-entropy', uses: 'pyswarm' }, - { login: 'ICRAR', name: 'ICRAR', repo: 'ICRAR/daliuge', uses: 'pyswarm' }, - { login: 'EMSL-Computing', name: 'EMSL Computing', repo: 'EMSL-Computing/CoreMS', uses: 'pyswarm' }, - { login: 'National-Digital-Twin', name: 'National Digital Twin', repo: 'National-Digital-Twin/NOVA', uses: 'pyswarm' }, - { login: 'gemseo', name: 'GEMSEO', repo: 'gemseo/gemseo', uses: 'pyDOE' }, - { login: 'SMTorg', name: 'SMT', repo: 'SMTorg/smt', uses: 'pyDOE' }, - { login: 'ICB-DCM', name: 'ICB-DCM', repo: 'ICB-DCM/pyPESTO', uses: 'pyswarm' }, - { login: 'openworm', name: 'OpenWorm', repo: 'openworm/ChannelWorm', uses: 'pyswarm' }, - { login: 'wwu-mmll', name: 'Medical Machine Learning Lab', repo: 'wwu-mmll/photonai', uses: 'pyDOE' }, - - // University research groups. - { - login: 'industrial-optimization-group', - name: 'Multiobjective Optimization Group, University of Jyväskylä', - repo: 'industrial-optimization-group/pyRVEA', - uses: 'pyDOE', - }, - { - login: 'Curtin-Timescales-of-Mineral-Systems', - name: 'Timescales of Mineral Systems, Curtin University', - repo: 'Curtin-Timescales-of-Mineral-Systems/UPb-Unmixer', - uses: 'soerp', - }, - { login: 'BGU-AiDnD', name: 'AiDnD, Ben-Gurion University', repo: 'BGU-AiDnD/Debugger', uses: 'pyswarm' }, - { login: 'SeduceProject', name: 'SeDuCe', repo: 'SeduceProject/seduce_ml', uses: 'mcerp' }, - { login: 'atomicateam', name: 'Atomica', repo: 'atomicateam/atomica', uses: 'pyswarm' }, - { - login: 'AnesthesiaSimulation', - name: 'Anesthesia Simulation', - repo: 'AnesthesiaSimulation/Python_Anesthesia_Simulator', - uses: 'pyswarm', - }, - - // From the "Who uses PyDOE?" page in the pydoe docs. Not currently declared - // in these repositories' manifests — see the note at the top of this file. - { login: 'ibm', name: 'IBM', repo: 'ibm/simulai', uses: 'pyDOE' }, - { login: 'paypal', name: 'PayPal', repo: 'paypal/gators', uses: 'pyDOE' }, - { login: 'terrapower', name: 'TerraPower', repo: 'terrapower/armi', uses: 'pyDOE' }, - { login: 'nanograv', name: 'NANOGrav', repo: 'nanograv/holodeck', uses: 'pyDOE' }, -] diff --git a/src/main.ts b/src/main.ts index 759ba38..d92e929 100644 --- a/src/main.ts +++ b/src/main.ts @@ -19,12 +19,11 @@ import { mountFooter } from './components/footer' import { mountCounters, mountReveals } from './components/reveal' import { mountDisplayType } from './components/wordmark' import { mountDrawFields } from './components/draw-field' -import { mountMarquees } from './components/marquee' import { mountPixelRules } from './components/pixel-rule' import { mountPointerEffects } from './components/pointer' import { mountMotes } from './components/motes' import { mountFeaturedGrid, mountCatalog } from './components/project-cards' -import { mountPeople, mountUsedBy, syncStats } from './components/content' +import { mountPeople, syncStats } from './components/content' import { defineEggzecText } from './lib/eggzec-block' import { prefersReducedMotion } from './lib/env' @@ -69,13 +68,11 @@ function boot(): void { mountFeaturedGrid() mountCatalog() mountPeople() - mountUsedBy() syncStats() mountDisplayType() // Behaviour over whatever ended up in the DOM. mountDrawFields() - mountMarquees() mountPixelRules() mountReveals() mountCounters() diff --git a/src/styles/components.css b/src/styles/components.css index c416961..35045f3 100644 --- a/src/styles/components.css +++ b/src/styles/components.css @@ -803,87 +803,6 @@ color: var(--ink-muted); } -/* ---- marquee ----------------------------------------------------------- */ - -.marquee { - --marquee-shift: 0px; - --marquee-duration: 30s; - --marquee-direction: normal; - position: relative; - overflow: hidden; - mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); - -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); -} - -.marquee__viewport { - overflow: hidden; -} - -.marquee__track { - display: flex; - width: max-content; -} - -.marquee__track[data-animated='true'] { - animation: marquee-scroll var(--marquee-duration) linear infinite; - animation-direction: var(--marquee-direction); -} - -.marquee:hover .marquee__track[data-animated='true'] { - animation-play-state: paused; -} - -.marquee__group { - display: flex; - align-items: center; - gap: var(--space-7); - padding-right: var(--space-7); - flex: 0 0 auto; -} - -@keyframes marquee-scroll { - from { - transform: translateX(0); - } - to { - transform: translateX(calc(var(--marquee-shift) * -1)); - } -} - -/* ---- tool chips inside the marquee ------------------------------------- */ - -/* ---- used by ------------------------------------------------------------ */ - -/* Sized for a landing-page logo strip, not for GitHub's own "Used by" widget, - which runs at 24px because it is a dense sidebar list of user avatars. These - are marks meant to be recognised in passing, so they get roughly double that. - - Hover darkens and nothing else. The strip is moving, so anything that changes - the logo's size or position under the cursor reads as a glitch. */ -.used-by { - display: inline-flex; - align-items: center; - padding: 0.4rem 1.35rem; -} - -/* - * Every logo is delivered at one height, so the strip needs a single `height` - * here and nothing else. The width cap is for the occasional wordmark that is - * much wider than it is tall. - */ -.used-by__logo { - width: auto; - height: 68px; - max-width: 210px; - object-fit: contain; - opacity: 0.62; - transition: opacity var(--dur-base) var(--ease-out); -} - -.used-by:hover .used-by__logo { - opacity: 1; -} - /* ---- spotlight surface -------------------------------------------------- */ [data-spotlight] { diff --git a/src/styles/pages.css b/src/styles/pages.css index 3803e93..53a4f58 100644 --- a/src/styles/pages.css +++ b/src/styles/pages.css @@ -110,25 +110,6 @@ margin-top: var(--space-2); } -.hero__ticker { - display: flex; - align-items: center; - gap: var(--space-4); - margin-top: var(--space-6); - padding-top: var(--space-6); - border-top: var(--border) solid var(--line); -} - -.hero__ticker-label { - flex: 0 0 auto; - color: var(--ink-faint); -} - -.hero__ticker-strip { - min-width: 0; - flex: 1; -} - /* ---- install strip ------------------------------------------------------ */ .install { diff --git a/tools/build-user-logos.py b/tools/build-user-logos.py deleted file mode 100644 index 36a68ca..0000000 --- a/tools/build-user-logos.py +++ /dev/null @@ -1,84 +0,0 @@ -""" -Build the "Used by" logo strip. - -Each source logo — an organisation's GitHub avatar, or replacement artwork -dropped into the source directory — is flattened onto white, desaturated, and -scaled to one content height. Desaturating here rather than in CSS keeps the -strip even and stops anything flashing colour while it loads; the single height -lands every logo on one optical line instead of a row of mismatched sizes. - - python tools/build-user-logos.py [--out public/brand/users] - -Source files are named `.`, lowercased; the site looks the -logo up by login. Orgs whose avatar is a default identicon, a photograph, or an -image that turns to mush in greyscale are better left out than shown badly. -""" - -import argparse -import os -import sys - -import cv2 -import numpy as np - -HEIGHT = 128 # content height in px — 2x the CSS height, for retina -MAX_WIDTH = 420 # width cap, so a long wordmark cannot take over the strip - - -def load(path): - """Read an image path that may contain non-ASCII characters on Windows.""" - im = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_UNCHANGED) - if im is None: - raise SystemExit(f"unreadable: {path}") - if im.ndim == 2: - im = cv2.cvtColor(im, cv2.COLOR_GRAY2BGR) - if im.shape[2] == 4: - return im[:, :, :3], im[:, :, 3] - return im, None - - -def process(src, dest): - bgr, alpha = load(src) - - # Avatars ship an alpha channel that only rounds the corners, and the page - # behind the strip is white — so flatten onto white and keep the square. - if alpha is not None: - a = (alpha.astype(np.float32) / 255.0)[:, :, None] - bgr = (bgr.astype(np.float32) * a + 255.0 * (1 - a)).astype(np.uint8) - - gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY) - - h, w = gray.shape - scale = min(HEIGHT / h, MAX_WIDTH / w) - size = (max(1, round(w * scale)), max(1, round(h * scale))) - interp = cv2.INTER_AREA if scale < 1 else cv2.INTER_CUBIC - gray = cv2.resize(gray, size, interpolation=interp) - - ok, buf = cv2.imencode(".png", cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR), - [cv2.IMWRITE_PNG_COMPRESSION, 9]) - if not ok: - raise SystemExit(f"could not encode: {dest}") - buf.tofile(dest) - return size - - -def main(): - ap = argparse.ArgumentParser(description=__doc__) - ap.add_argument("source", help="directory of . logo files") - ap.add_argument("--out", default=os.path.join("public", "brand", "users")) - args = ap.parse_args() - - os.makedirs(args.out, exist_ok=True) - names = sorted(f for f in os.listdir(args.source) - if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp"))) - if not names: - sys.exit(f"no images in {args.source}") - - for name in names: - login = os.path.splitext(name)[0].lower() - w, h = process(os.path.join(args.source, name), os.path.join(args.out, f"{login}.png")) - print(f" {login:38} {w:4}x{h:<4}") - - -if __name__ == "__main__": - main()