Commit Graph

29 Commits

Author SHA1 Message Date
Levi Neuwirth 1027b88429 Rich reference popups: arXiv lead figures, prominent Wikipedia images
Reference popups (provider-rendered: arXiv, Wikipedia, CrossRef, …)
get a glanceable layout: wider container (560px), larger title and
body type, and a full-width image banner under the source label.
Internal page previews and item-card popups (new/library pages) keep
the compact layout — the shared popup element toggles
.link-popup--rich per show based on the rendered content.

- arXiv: a new best-effort enrich step fetches the paper's LaTeXML
  HTML rendition and pulls the first figure as a lead image. Enrich is
  time-boxed (1.8s) so the metadata popup is never held hostage; late
  results refresh the cache for the next hover. Figures letterbox with
  object-fit: contain (plots must not crop); Wikipedia photos
  cover-crop with an upper focal point. width/height attrs reserve
  aspect ratio so positioning is stable before the image loads.
- Wikipedia thumbnails request 480px for the banner width.
- nginx: new ^~ /proxy/arxiv-html/ location backed by arxiv.org proper
  (export.arxiv.org serves the Atom API but 429s the /html/ asset
  tree); 404s cached 1d (the common no-HTML-rendition case). All four
  proxy locations switched to ^~ — without it, static-assets.conf's
  per-extension regex location outranks plain prefixes and serves a
  local 404 for any proxied URL ending in an image extension, which is
  exactly how the first figure fetch failed.

Installed and verified live: proxied page (200, 298KB), figure (200
image/png), API unchanged, no-rendition 404 path; the full client
resolution chain (relative src -> proxy path -> guard -> image)
validated against production.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 13:02:15 -04:00
Levi Neuwirth 23250d8782 Fix popup previews: proxy prefix-strip bug, arXiv IDs, Wikipedia images
The root cause of 'PDF/arXiv previews simply do not work' was twofold:

1. nginx/popup-proxy.conf was never installed on the VPS — every
   /proxy/* request (arXiv, PubMed, Internet Archive) returned nginx's
   default 404. Now installed (snippets + http{}-context cache/limit
   zones in conf.d, included in the vhost, nginx -t verified, reloaded).
2. The snippet itself had a latent bug that only surfaced once
   installed: with a VARIABLE upstream, a URI part on proxy_pass is
   passed literally — every request hit the upstream's homepage
   (archive.org HTML where JSON was expected, arXiv 429s, NCBI doc-page
   redirects). Fixed with explicit prefix-strip rewrites; bad cached
   responses purged. All three proxies verified returning real data,
   including a live arXiv title resolve.

Client-side improvements:
- arXiv match covers old-style IDs (cs/9901002, math.GT/0309136,
  cond-mat/...v1) alongside new-style, and .pdf-suffixed /pdf/ URLs
  (regex verified against six forms)
- Wikipedia popups show the article's lead image: pageimages rides
  along the existing extracts call (pithumbsize=320), rendered via a
  new https-only image slot in renderPopup with float styling;
  upload.wikimedia.org added to the CSP's img-src
- pdf-thumbs now walks all of static/ (pdfjs pruned), so /cv.pdf and
  /resume.pdf — the most-linked internal PDFs, previously thumbnail-less
  and therefore popup-less — get hover previews

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 12:06:13 -04:00
Levi Neuwirth 23bc2d0dc1 Frontend tail: keyboard access, idempotence, input edge cases
- gallery.js: math/score focus overlays are keyboard-activatable
  (role=button, tabindex, Enter/Space) and focus return on close lands
  on a focusable trigger (AUDIT §5.7)
- annotations.js: marks are focusable; Enter/Space pins the tooltip
  with focus moved to its Delete button, Escape dismisses — the delete
  affordance is finally reachable without a mouse (§5.7)
- transclude.js: nested transclusions resolve (depth-capped at 3, with
  ancestor-chain cycle rejection rendering the existing error style);
  collapse.js reinit is idempotent via data-collapse-bound (§5.7)
- copy.js excludes the button label from code-less <pre> copies;
  score-reader.js stops rewriting plain loads to ?p=1; search-filters
  treats non-numeric threshold input as inactive instead of a
  match-everything >=0 filter; selection-popup no longer re-summons
  the toolbar while typing capitals in the annotation picker (§5.8)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 11:25:19 -04:00
Levi Neuwirth caa113e036 Frontend: search races, lightbox a11y, popup edge cases
- semantic-search.js: generation token prevents stale results from
  rendering over newer queries; in-flight dedup on the index fetch;
  index/meta size consistency check fails loudly instead of NaN
  ranking (AUDIT §5.5)
- lightbox.js: triggers keyboard-activatable (role=button, tabindex,
  Enter/Space); Tab trapped inside the aria-modal overlay, modeled on
  gallery.js (§5.6)
- nav.js: portal toggle persists via guarded safeStorage so
  storage-blocked contexts can't kill the toggle (§5.7)
- popups.js: provider url() throws (malformed percent-encoding) are
  treated as no-popup; future dates render nothing instead of
  "N days ago" (§5.7)
- search.js: missing PagefindUI degrades to a console warning instead
  of aborting the whole handler (§5.7)
- citations.js: deleted — dead code superseded by popups.js (§5.7)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 09:43:25 -04:00
Levi Neuwirth c64f3d63c0 Fix audit frontend MEDs
- score-reader template: load utils.js before theme.js — without
  lnUtils.safeStorage the saved theme/text-size never restored on
  score pages (AUDIT §5.1)
- search-filters: expand trailing-slash pathnames to .../index.html
  before the epistemicMeta lookup; clean-URL pages were silently
  bypassing every active filter (AUDIT §5.2)
- viz: treat cappuccino as a dark theme so charts stop rendering
  near-black marks on a dark brown background (AUDIT §5.3)
- collapse: namespace section-collapsed keys by pathname (Pandoc
  auto-slugs recur across essays) and go through safeStorage like the
  rest of the site (AUDIT §5.4)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 09:21:47 -04:00
Levi Neuwirth af27479c6e Add now.js: recompute "Last updated" relative phrase client-side
build/Now.hs renders the .now-stamp-relative phrase ("3 days ago") at
build time against the build machine's clock; a page served days later
from cache or a CDN would then read stale. now.js recomputes the
phrase in the browser from the <time datetime> attribute (an
unambiguous YYYY-MM-DD) against the visitor's clock, with bucket
thresholds that mirror Now.hs:relativeTime exactly so the no-JS
fallback and the recomputed value agree.

* static/js/now.js — the recomputation script.
* templates/default.html includes it via $if(now)$ so it only loads
  on the Current page.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 12:06:49 -04:00
Levi Neuwirth 70dda56625 Popups: render the source page's monogram in internal previews
Internal-page hover popups now show the source's monogram alongside
the title / abstract / metadata when one exists. Two-column grid is
gated on .has-monogram so popups for pieces without an authored mark
keep their default single-column body. The serialised SVG comes from
the rendered page's own .frontmatter-mark--monogram figure, excluded
when it is the symmetric-layout placeholder roundel so empty-slot
pieces do not get a fake mark in the preview.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 12:06:35 -04:00
Levi Neuwirth 977c1cecbb yum cappuccino yay! 2026-05-03 21:16:58 -04:00
Levi Neuwirth f41311a3eb Inline code reference previews 2026-05-02 10:40:43 -04:00
Levi Neuwirth cd94227acb Spec dilemma 2026-05-01 21:22:01 -04:00
Levi Neuwirth 6585573dae States/Context/Embeddings fixes 2026-04-26 11:22:57 -04:00
Levi Neuwirth 913a374fb2 Professional content refactor 2026-04-22 11:46:57 -04:00
Levi Neuwirth 908136b646 Navigation refactor 2026-04-19 14:35:41 -04:00
Levi Neuwirth 237380c4be date data 2026-04-17 15:15:04 -04:00
Levi Neuwirth 1a532f881b major visual changes - dingbats, footer, etc 2026-04-17 12:48:22 -04:00
Levi Neuwirth acb3ae7066 visual enhancements 2026-04-15 22:25:38 -04:00
Levi Neuwirth c3fa26f60e mobile fixes 2026-04-12 15:29:35 -04:00
Levi Neuwirth 41bbbd799b favicon, logo, internal popups 2026-04-12 14:57:01 -04:00
Levi Neuwirth e25a311dd9 filters + epistemic popups 2026-04-12 10:40:58 -04:00
Levi Neuwirth d113671e96 epistemic redo 2026-04-11 19:40:58 -04:00
Levi Neuwirth dd61fc0cc4 audit: frontend a11y, JS shared utils, CSS variable definitions 2026-04-10 17:41:21 -04:00
Levi Neuwirth 02b7694bfe forgejo links 2026-04-05 11:32:50 -04:00
Levi Neuwirth 9a01c602bc auto: 2026-04-05T01:18:45Z 2026-04-04 21:18:45 -04:00
Levi Neuwirth b06b1e741c popup improvements, citation fix 2026-03-29 08:02:04 -04:00
Levi Neuwirth b38e9359d5 annotation system 2026-03-26 13:29:37 -04:00
Levi Neuwirth a5495035be epistemic v2 2026-03-26 09:10:35 -04:00
Levi Neuwirth 728afd4c68 affiliation, cabal helper script 2026-03-26 08:14:50 -04:00
Levi Neuwirth 5cfbfbc0ef GPG signing, embedding pipeline, visualization filter, search timing, sig popups
- GPG page signing: dedicated signing subkey in ~/.gnupg-signing, sign-site.sh
  walks _site/**/*.html producing .sig files, preset-signing-passphrase.sh caches
  passphrase via gpg-preset-passphrase; make sign target; make deploy chains it
- Footer sig link: $url$.sig with hover popup showing ASCII armor (popups.js
  sigContent provider; .footer-sig-link bound explicitly to bypass footer exclusion)
- Public key at static/gpg/pubkey.asc
- Embedding pipeline: tools/embed.py encodes _site pages with nomic-embed-text-v1.5
  + FAISS IndexFlatIP, writes data/similar-links.json; staleness check skips when
  JSON is newer than all HTML; make build invokes via uv, skips gracefully if .venv absent
- SimilarLinks.hs: similarLinksField loads similar-links.json with Hakyll dependency
  tracking; renders Related section in page-footer.html
- uv environment: pyproject.toml + uv.lock (CPU-only torch via pytorch-cpu index)
- Visualization filter: Filters/Viz.hs runs Python scripts for .figure (SVG) and
  .visualization (Vega-Lite JSON) fenced divs; viz.js renders with monochrome config
  and MutationObserver dark-mode re-render; viz.css layout
- Search timing: #search-timing element shows elapsed ms via MutationObserver
- Build telemetry timestamps removed from git tracking (now in .gitignore)
- spec.md updated to v9; WRITING.md updated with viz, related, signing, build docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:14:49 -04:00
Levi Neuwirth 714824a0b5 initial deploy! whoop 2026-03-17 21:56:14 -04:00