MyGo 云音乐
用 Go 与 MyGo 写成的第三方网易云音乐桌面客户端。原生界面,没有 WebView;边下边播,无损与 Hi-Res,逐字歌词与「律动」,刘海播放器。
Translation pending
Daily generative art from real atmospheric data. 17 artist lenses, 9 with custom FLUX.1-dev LoRA fine-tunes.
MediaAstro · TypeScriptGitHub @jtannahillOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Production Astro 6 site for art.jamestannahill.com: daily generative weather art. Static build, Pagefind search, deploys to S3/CloudFront on push.
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Source of art.jamestannahill.com.
Astro 6, fully static, deployed on every push to main.
The design system (colors, type, components, motion, accessibility rules)
is recorded in DESIGN.md.
• Astro 6.2: static output, file-based routing, View Transitions
• TypeScript + Astro Content Collections with Zod schemas
• DynamoDB → JSON snapshots at build time (no DB calls at runtime)
• Pagefind for local-first search across every piece's rationale
• Mapbox GL for the world-map page
• @astrojs/rss for /rss.xml
• CI: GitHub Actions: pull data → type-check → build → S3 sync → CloudFront invalidateRead the full README ↗
Source of art.jamestannahill.com.
Astro 6, fully static, deployed on every push to main.
The design system (colors, type, components, motion, accessibility rules)
is recorded in DESIGN.md.
@astrojs/rss for /rss.xmlEvery push to main runs .github/workflows/deploy.yml (it also runs
daily at 07:17 UTC, after the upstream pipeline, and on manual
workflow_dispatch):
npm run pull-data: scan DynamoDB art-generator table, write
src/data/generated/{weather,palettes}.json (gitignored)astro check: TypeScript validation across .astro filesnpm run build:no-pull: astro build produces dist/ (~3,000 pages),
then pagefind --site dist --output-subdir _pagefind builds the local
search indexscripts/backfill-thumbs.py (via uv run): generates any WebP
variants the resize Lambda has not written yet. Never fails the deploy.aws s3 sync ./dist/ to the site/ prefix of the S3 bucket, in two
passes: HTML and data with a 5-minute cache, _astro/* and images as
immutable (no --delete: preserves pipeline-written PNG/WebP/SVG assets)aws cloudfront create-invalidation --paths "/*"End-to-end: ~45 s on a clean runner.
Pieces are generated by the upstream art-weather-render Lambda
(in art-generator),
which writes weather/{run_id}/{slug}/preview-2048.png to S3.
The art-image-resize Lambda (S3 PUT-triggered, also in art-generator)
builds the web previews preview-{480,960,1200,1920}.webp from the
upscaled 4K master (preview-4k.png), falling back to preview-2048.png
only when no master exists. The 1200 variant is the og:image. The asset-mirror Lambda
(art-site-rebuild, the retired-and-shrunk legacy renderer) copies
everything from weather/ to site/weather/ once per daily pipeline
run so CloudFront's origin path serves the assets.
src/components/Thumb.astro renders <picture> with WebP <source srcset>
at three widths (480/960/1920) plus PNG fallback. Card grids load ~30 KB
WebP at 480 w instead of ~1.5 MB PNG.
Piece pages credit the model from the piece's renderer field (the
visible "Model" and "Format" rows plus the JSON-LD artMedium):
flux-dev-lora:* is "FLUX.1-dev + custom LoRA", flux-1.1-pro is
"Flux 1.1 Pro", and anything else is a Claude SVG (Claude Sonnet 4 via
Amazon Bedrock).
npm install
npm run pull-data # AWS creds from ~/.aws/credentials
npm run dev # http://localhost:4321
npm run build # pull-data + astro build + Pagefind index
npm run build:no-pull # same, reusing the existing src/data/generated/*.json
npm test # tsx --test src/lib/seo.test.ts (14 tests)
npm run build pulls data from AWS, so it needs DynamoDB read access to
the art-generator table. Use build:no-pull to rebuild offline from the
last pull. The tests cover titles and descriptions in src/lib/seo.ts,
including piece H1s and that no title contains an em-dash.
| Var | Purpose |
|---|---|
PUBLIC_MAPBOX_TOKEN | Public client token for the /map/ globe. Schema-validated via astro:env. |
CI reads from repo secrets. Local dev: copy .env.example to .env.
scripts/
pull-data.ts DynamoDB → src/data/generated/*.json
src/
content.config.ts Zod schemas for weather + palettes collections
components/
Thumb.astro <picture> with WebP srcset
Methodology.astro Bloomberg block for LoRA-trained artists
ConsentBanner.astro Analytics consent banner
data/
artists.ts 16-artist roster + 7 LoRA configs
counts.ts Single source of truth for every site-wide count
faq.ts About-page FAQ (drives JSON-LD + accordion)
lib/
seo.ts Page titles, descriptions, piece H1s
seo.test.ts node:test suite for seo.ts (npm test)
layouts/
Base.astro Shared header, footer, search modal, subscribe form
pages/
index.astro Homepage: today's run, then the paid Generate panel
about.astro /about/
archive/[...page].astro Paginated /archive/ (12 pages of 8 runs each)
artist/index.astro /artist/ index (artist-card mosaic)
artist/[key]/[...page].astro Per-artist gallery (60/page) + methodology block
weather/[run_id]/ Per-run page
weather/[run_id]/[slug]/ Single-piece page + print drawer (~2,700 of these)
palettes/ /palettes/ + per-location detail
comparison/index.astro Style comparison (10 cells × 3+ artists)
map/index.astro Mapbox dark globe with clustering
studies/index.astro Studies (empty until DynamoDB has them)
search.astro /search/ standalone Pagefind page
rss.xml.ts RSS feed
feed.xml.ts Legacy /feed.xml, same handler as rss.xml
llms.txt.ts /llms.txt
api/{today,runs,artists,palettes}.json.ts Static JSON endpoints
404.astro
DESIGN.md is the design record: tokens, type, components
and the rules below. Current behavior worth knowing before editing:
| as the separator and never an em-dash. This is
enforced by src/lib/seo.test.ts. Unnamed grid cells title by their
coordinates, e.g. "Region 30°N, 150°E", instead of a bare "Region".aria-pressed; sold-out sizes are
disabled.Base.astro) is modal with a focus trap and
returns focus on close.view-transition-name only
when clicked, so grid pages do not snapshot every thumb on navigation.prefers-reduced-motion rules and visible focus rings.art-generator: upstream
Lambdas + DynamoDB pipeline. Daily Step Function runs:
art-weather-ingest → art-weather-render × 10 →
art-satellite-ingest → art-palette-extract →
(art-image-resize triggers on each PNG) → art-site-rebuild (asset
mirror) → this repo's Astro build (manual or push-triggered).
CloudFront: one distribution with origin path /site/.
S3: the shared art-generator S3 bucket behind CloudFront. Astro publishes to site/,
pipeline writes pieces under weather/ + palettes/, mirror copies them.
IAM: a dedicated CI deploy user with scoped s3:*Object on
site/*, dynamodb:Scan on art-generator, and
cloudfront:CreateInvalidation on the distro.
Security headers: CloudFront response headers policy
art-jamestannahill-security-csp, mirrored in infra/response-headers-policy.json. The CSP is the source of
truth for which third-party hosts the site may load: Mapbox, Google Fonts
(style-src + font-src), and Google Analytics (script-src for
googletagmanager, connect-src + img-src for the collect endpoints).
Adding a third-party script without adding it here means it silently
never runs. To apply a change (POLICY_ID is that policy's ID, from
aws cloudfront list-response-headers-policies --type custom):
aws cloudfront update-response-headers-policy \
--id "$POLICY_ID" \
--if-match "$(aws cloudfront get-response-headers-policy-config \
--id "$POLICY_ID" --query ETag --output text)" \
--response-headers-policy-config file://infra/response-headers-policy.json
GA4 property art.jamestannahill.com (measurement ID G-5KG25EY8YL),
loaded from public/scripts/analytics.js and gated by
public/scripts/consent.js. gtag loads only after the visitor accepts.
Page views: gtag('config', ...) sends the first one, and
astro:after-swap sends one per ClientRouter navigation. after-swap does
not fire on the initial load, so views are not double-counted. consent.js
binds from astro:page-load only, for the same reason.
Country detection reads a jt_country cookie set by the
art-viewer-country-cookie CloudFront Function (viewer-response,
infra/viewer-country-cookie.js) from the CloudFront-Viewer-Country
header. /cdn-cgi/trace remains as a fallback but 404s here: this is
CloudFront, not Cloudflare.
CloudFront only populates CloudFront-Viewer-* headers when a policy asks
for them, so the default behavior uses cache policy
art-caching-optimized-viewer-country (mirrored in infra/cache-policy.json) rather than the managed
CachingOptimized policy. It is CachingOptimized plus that one header, which
means the header is part of the cache key and pages cache per country.
Reverting to a policy without the header silently makes the banner show to
everyone again, because the function has no country to copy.
Originally a parallel staging build at /next/ while the Jinja2
renderer in art-generator/lambdas/site_rebuild/ owned the apex.
Cut over on 2026-05-06. The legacy Lambda was shrunk to a ~50-line
asset-mirror; templates were retired.