Static marketing site and public release-surface publisher for openstudio.org.in.
This repo is now the only public publisher for:
/releases/latest.json/releases/stable/latest.json/releases/ai-runtime/latest.json/releases/ai-runtime/stable/latest.json/appcast/windows-stable.xml/appcast/macos-stable.xml/appcast/linux-stable.xml/download/windows/latest/download/macos/latest/download/linux/latest/download/ai-runtime/windows/latest/download/ai-runtime/macos/latest/download/ai-runtime/macos/arm64/latest/download/ai-runtime/macos/x64/latest/download/ai-runtime/linux/latest/download/ai-runtime/linux/x64/latest/download/ai-runtime/linux/arm64/latest
GitHub Releases remain the binary origin. This repo publishes release metadata and appcasts, then exposes stable redirect surfaces that resolve to GitHub-hosted binaries.
The Studio Paper redesign is the sole website. Source folders describe their responsibility, without a website-version prefix. /v2/* remains a compatibility redirect for existing preview links.
src/App.tsx: canonical routes, legacy aliases and lazy page boundaries.src/pages/: lazy website page components, plus the not-found page and development-only artwork tool.src/components/layout/:SiteShell.tsx,SiteHeader.tsx,SiteFooter.tsxand the footer-lead context. The shell owns shared layout and privacy controls.src/components/ui/: shared website presentation primitives.src/features/docs/content/: one typed, lazy-loaded module per guide. Each records the app revision and release/development status it describes.src/features/daw-preview/: real static illustration layouts with a deferred animation engine, scheduler and pinned upstream UI. Each lazy page imports its own renderers; text/legal pages exclude them. See itsvendor/README.mdbefore syncing.src/hooks/: shared release, platform, media-query, section-reveal and navigation hooks.src/constants/routes.ts: canonicalSITE_PATHS,docPathandblogPostPath;src/data/siteContent.tscontains shared repository/contact/media references.src/lib/: formatting, analytics, loading and SEO helpers.src/data/: shared legal policy, model setup, generated GitHub release data and blog metadata. Legal policy text has one source inlegal.ts.src/components/ResponsiveImage.tsx: browser-selected image variants with original-image fallback; no image GraphQL service.src/styles/site.css: shared design tokens, typography, responsive component styles and animation effects in Tailwind's component layer. Ordinary page layouts use Tailwind utilities; runtime artwork geometry remains inline.src/prerender.tsxandscripts/prerender-site.mjs: render the actual page components into static HTML, route-specific head metadata, sitemap and exact hosting rewrites.shared/andnetlify/functions/: release/runtime contracts and stable download endpoints.shared/github-snapshot.ts: the repository snapshot types and nested JSON parser shared by build generation, build-time generation and browser refreshes.
See repository instructions, source structure and cleanup verification, guide authoring, and migration plan and verification for maintenance rules and regression checks.
Contributor counts refer to the desktop app repository, including GitHub's
recognized co-authors. shared/github-contributors.ts reads the same public
repository summary shown by GitHub. Its total is independent of the limited avatar
list. This public web endpoint is not a versioned REST API, so its schema is
validated and errors preserve the existing verified snapshot through the normal
fallback path; no names/counts or inferred commit totals are hardcoded. If GitHub
changes this endpoint, update the parser and fixture tests before accepting new
data. The REST /contributors list omits the co-author credit in the app's
squashed release commit and previously caused the website to show one instead of
two. Stars, releases and other statistics still come from GitHub's REST API.
See analytics setup and verification for the public build
variables VITE_GA_MEASUREMENT_ID, VITE_CLARITY_PROJECT_ID, and
VITE_ANALYTICS_ENABLED. Disable GA Enhanced Measurement's history page views
when using this site's manual router tracking. After changing routing or tracking,
run npm run verify:analytics against an analytics-enabled deployment; consent
tests alone cannot detect duplicate events created by account settings.
The final pre-push review records clean-checkout validation
and the remaining deployment/release checks for this migration.
The review follow-up records the subsequent navigation,
upgrade guidance, image delivery and data-validation fixes, including the pending
native plugin screenshot captures.
The illustration loading follow-up replaces the
temporary screenshots with the illustrations' own rest frames and records the
coordinated MiniMax/Stable Audio release wording.
Use Node.js 22.12 or newer (CI uses Node 22). Dev/build startup fetches current GitHub repository and release data. Clean builds require internet access; local builds can reuse a previously verified snapshot less than 24 hours old during a temporary API outage or quota limit. GITHUB_TOKEN increases the API rate limit, and CI supplies its read-only token. Run npm run sync-github-data separately when refreshing generated release data.
A build also retrieves the published release manifests and appcasts when local
release inputs are absent. Valid existing inputs can be reused locally; Netlify
deployments refresh them. Missing, invalid or unavailable release inputs fail
the build instead of publishing broken update links. CI provides its read-only
token as both GITHUB_TOKEN and GH_TOKEN for these separate fetch paths.
npm ci
npm run devnpm run stage-release-publish-inputs
npm run validate-release-publish-inputs
npm run build
npm run previewnpm run build fetches current GitHub data, generates branding, the social PNG, responsive images and blog HTML, stages and validates release inputs, generates the download routing catalog, runs strict TypeScript checking, builds the client and prerenders all canonical pages. Run npm run lint and npm test for the remaining CI checks. Both OG generation and browser tests require npx playwright install chromium before the first build. GitHub Actions uses --with-deps to install Linux system packages too; Netlify uses the browser-only installation because its build user cannot elevate to root.
The TypeScript build covers client code, configuration, all shared TypeScript
modules and all Netlify functions. Network and cached repository snapshots must
pass the shared runtime parser before use; malformed browser refreshes preserve
the valid build snapshot.
Run npm run build before npm test in a clean checkout; tests consume the generated GitHub payloads and production documents. CI runs lint, build and the complete test suite after installing Chromium.
- Browser tests cover all canonical routes at 390, 768 and 1440 px, legacy redirects/404s, navigation and keyboard behavior, privacy consent, lazy-load recovery and GitHub release label/link consistency.
- SEO browser tests visit every sitemap page with JavaScript disabled and enabled, compare the head metadata and structured data, validate social-image dimensions, and check metadata cleanup during navigation and 404 recovery.
- Focused browser regressions cover normal-motion two-piece loading, AI card/table layout, current-page mobile-menu activation, and Features/NAM image selection at standard and high-density resolutions. Build tests check upgrade guidance before JavaScript; contract tests reject malformed repository snapshots and verify retry behavior.
- Illustration tests delay or fail the animation-engine request, verify that the same scene elements and dimensions remain, and cover prerendered artwork with JavaScript disabled. Phone tests check real playback on fresh visits, delayed loads and uncached navigation, plus offscreen pausing, reduced motion and every NAM tile. Ordinary screenshot/thumbnail delivery tests remain separate.
- The suite also includes unit, source-contract and build tests. A reported total is not an E2E-only count. Desktop app tests live in the separate app repository.
- Browsers currently run in Chromium. The eight-width visual comparison recorded in the audit is a manual review artifact, not an automated screenshot-regression suite. Firefox/WebKit coverage and CI screenshot baselines are follow-up improvements.
- Loading performance is an explicit
npm run verify:perfcheck; the current CI workflow does not run that matrix automatically.
Production builds prerender all canonical marketing, legal, documentation and
blog pages with readable content, unique titles/descriptions, absolute canonical
URLs, Open Graph/Twitter tags and page-owned JSON-LD. PageSeo keeps the same
metadata during client navigation. Sitemap dates come from authored guide/article
updates; omit lastmod when the content modification date is unknown.
Keep schemas in src/lib/structuredData.ts and the blog metadata helpers aligned
with visible content. Never invent reviews, ratings, release versions or dates for
search features. Old routes use permanent redirects; missing pages return 404
and noindex. /og-card is available only on the Vite development server for
npm run generate-og, and returns 404 in production. src/pages/OgCardPage.tsx
is the editable design source; crawlers fetch the generated PNG, never that route.
Every build regenerates it using a temporary local Vite server and Chromium,
waits for fonts and images to decode, and fails if rendering fails. No manually
started dev server is needed. npm run generate-og also refreshes image metadata.
The PNG's content hash automatically versions the sharing URL in both static and
client metadata, including structured data. Blog posts retain their own images.
Social tags appear before loader scripts/styles in the published HTML. External
preview caches may still need a fresh scrape after deploying changed artwork.
After deployment, verify production HTTP status/robots headers and submit the
canonical sitemap in Search Console. Deploy-preview noindex headers are
intentional. Local validation does not confirm Google indexing or rich-result
eligibility. See the dated SEO audit for coverage and the
deployment follow-up.
After building, run the repeatable mobile loading check against dist/:
npm run build
npm run verify:mobile-perfThe single-route command checks the home page by default. It starts Vite preview itself, opens a fresh 390 x 844 Pixel 5 Chromium context, and applies 4x CPU slowdown plus a 1.6 Mbps/150 ms mobile network profile. Select the desktop profile for the same focused check at 1440 x 900 with 2x CPU slowdown and a conservative 10 Mbps/40 ms connection:
npm run verify:mobile-perf -- --profile desktop --route /featuresRun the core loading matrix across Home, Features, Download, AI, and Blog on both mobile and desktop profiles with:
npm run verify:perfMatrix measurements run sequentially in isolated browser contexts. The package command uses a two-second post-reveal observation window so deferred requests near the loading boundary are counted consistently. Supplying --profile mobile or --profile desktop with --matrix narrows the matrix to that profile.
The gate fails when the real React main/hero is not visible after the HTML loader, or when app-ready time, post-ready intro time, intro-hidden time, Largest Contentful Paint (LCP), CLS, long tasks, requests, encoded transfer, redirects, console errors, page errors, failed requests, or HTTP errors exceed their budgets. Because prerendered content can paint behind the full-screen intro, the reported LCP retains the browser's buffered native candidate but is floored at the time the client route actually becomes visible. The post-ready intro budget prevents the older multi-second loader hold from returning even when application startup itself varies between machines.
An already-hosted build can be checked without starting a local server:
npm run verify:mobile-perf -- --url https://openstudio.org.in --route /Use npm run verify:mobile-perf -- --help for all CLI flags. Every tunable flag also has an environment-variable form:
MOBILE_PERF_URL,MOBILE_PERF_ROUTE,MOBILE_PERF_HERO_SELECTOR,MOBILE_PERF_JSONMOBILE_PERF_PROFILEMOBILE_PERF_MAX_APP_READY_MS,MOBILE_PERF_MAX_INTRO_AFTER_READY_MS,MOBILE_PERF_MAX_INTRO_HIDDEN_MS,MOBILE_PERF_MAX_CONTENT_VISIBLE_MS,MOBILE_PERF_MAX_LCP_MSMOBILE_PERF_MAX_CLS,MOBILE_PERF_MAX_LONG_TASK_COUNT,MOBILE_PERF_MAX_LONG_TASK_MS,MOBILE_PERF_MAX_LONG_TASK_TOTAL_MSMOBILE_PERF_MAX_REQUESTS,MOBILE_PERF_MAX_TRANSFER_KBMOBILE_PERF_CPU_RATE,MOBILE_PERF_DOWNLOAD_KBPS,MOBILE_PERF_UPLOAD_KBPS,MOBILE_PERF_LATENCY_MS,MOBILE_PERF_SETTLE_MS,MOBILE_PERF_TIMEOUT_MS
The checked-in mobile LCP budget is 4 seconds; the desktop budget is 3.6 seconds. Both profiles measure the redesigned page heading. Desktop permits up to 45 requests and 1,000 KiB because it intentionally loads larger visual assets, while retaining stricter startup and long-task budgets. These laboratory budgets should be recalibrated from repeated cold runs when the hosting transport or route asset strategy changes. Override only a budget that has a measured reason to differ.
A machine-readable result can be saved under the existing artifact area with --json output/playwright/mobile-performance.json. Legacy single-route mode retains the original result object. Matrix mode writes an envelope with the profile, route, and result for each measurement.
The desktop release pipeline generates these files and publishes them as GitHub Release assets. The release dispatch workflow stages the selected tag. Ordinary production Netlify builds fetch and validate the latest published stable desktop release before staging, so a website-only deployment preserves update feeds and runtime downloads. See release order and verification. Locally, run npm run fetch-release-publish-inputs before building to exercise the same path.
Expected staged deploy-input files:
release-input/releases/latest.jsonrelease-input/releases/stable/latest.jsonrelease-input/releases/ai-runtime/latest.jsonrelease-input/releases/ai-runtime/stable/latest.jsonrelease-input/appcast/windows-stable.xmlrelease-input/appcast/macos-stable.xmlrelease-input/appcast/linux-stable.xmlwhenreleases/stable/latest.jsonincludesplatforms.linux
Expected desktop release asset filenames:
OpenStudio-release-latest.jsonOpenStudio-release-stable-latest.jsonOpenStudio-ai-runtime-latest.jsonOpenStudio-ai-runtime-stable-latest.jsonOpenStudio-appcast-windows-stable.xmlOpenStudio-appcast-macos-stable.xmlOpenStudio-appcast-linux-stable.xmlwhen Linux app metadata is published
Common AI runtime binary asset names referenced by metadata:
OpenStudio-AI-Runtime-windows-base-x64.zipOpenStudio-AI-Runtime-windows-directml-x64.zipwhen a downloadable backend archive is published instead of an install planOpenStudio-AI-Runtime-windows-cuda-x64.zipwhen a downloadable backend archive is published instead of an install planOpenStudio-AI-Runtime-macos-arm64.zipOpenStudio-AI-Runtime-macos-x64.zipOpenStudio-AI-Runtime-linux-x64.zipOpenStudio-AI-Runtime-linux-arm64.zip
Published output paths:
/releases/latest.json/releases/stable/latest.json/releases/ai-runtime/latest.json/releases/ai-runtime/stable/latest.json/appcast/windows-stable.xml/appcast/macos-stable.xml/appcast/linux-stable.xmlwhen provided
Validation rules:
- app release root/stable manifests must both exist and match after JSON normalization
- AI runtime root/stable manifests must both exist and match after JSON normalization
- app release JSON must include
schemaVersion,channel,version,publishedAt,releasePageUrl,platforms.windows, andplatforms.macos;platforms.linuxis optional - AI runtime JSON must include
schemaVersion,channel,appVersion,runtimeVersion,publishedAt,platforms.windows, andplatforms.macos;platforms.linuxis optional - AI runtime Windows metadata may be published as a legacy flat
platforms.windowsentry, an old nested backend-asset shape underplatforms.windows.backends, the newplatforms.windows.baseplusplatforms.windows.backends.<backend>.installPlanshape, or a mixed transition manifest that contains both legacy and new fields - AI runtime macOS metadata may be published either as the legacy flat
platforms.macosentry or as the current nestedplatforms.macos.arm64andplatforms.macos.x64entries - AI runtime Linux metadata may be published either as a legacy flat
platforms.linuxentry or as nestedplatforms.linux.x64andplatforms.linux.arm64entries. Optionalplatforms.linux.backends.cudaand.rocminstall plans are validated and preserved. - downloadable manifest asset entries must include
url,sha256,size, andfileName - Windows backend install-plan entries must expose an
installPlanobject and are published verbatim - Windows and macOS stable appcasts must be present, valid XML, and align with the stable app manifest enclosure data; the Linux appcast is required when Linux app metadata is published
Illustrative AI runtime manifest shape (example versions, hashes and sizes below are not current release data):
{
"schemaVersion": 4,
"channel": "stable",
"appVersion": "0.0.22",
"runtimeVersion": "2026.04.05",
"publishedAt": "2026-04-05T00:00:00.000Z",
"platforms": {
"windows": {
"base": {
"url": "https://github.com/sdevil7th/OpenStudio/releases/download/v0.0.22/OpenStudio-AI-Runtime-windows-base-x64.zip",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"size": 123,
"fileName": "OpenStudio-AI-Runtime-windows-base-x64.zip"
},
"backends": {
"cuda": {
"installPlan": {
"version": 1,
"steps": [
{
"action": "detect",
"backend": "cuda"
},
{
"action": "install",
"package": "openstudio-cuda-runtime"
}
]
}
},
"directml": {
"installPlan": {
"version": 1,
"steps": [
{
"action": "detect",
"backend": "directml"
},
{
"action": "install",
"package": "openstudio-directml-runtime"
}
]
}
}
}
},
"macos": {
"arm64": {
"url": "https://github.com/sdevil7th/OpenStudio/releases/download/v0.0.22/OpenStudio-AI-Runtime-macos-arm64.zip",
"sha256": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"size": 123,
"fileName": "OpenStudio-AI-Runtime-macos-arm64.zip"
},
"x64": {
"url": "https://github.com/sdevil7th/OpenStudio/releases/download/v0.0.22/OpenStudio-AI-Runtime-macos-x64.zip",
"sha256": "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
"size": 123,
"fileName": "OpenStudio-AI-Runtime-macos-x64.zip"
}
},
"linux": {
"x64": {
"url": "https://github.com/sdevil7th/OpenStudio/releases/download/v0.0.22/OpenStudio-AI-Runtime-linux-x64.zip",
"sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"size": 123,
"fileName": "OpenStudio-AI-Runtime-linux-x64.zip"
},
"arm64": {
"url": "https://github.com/sdevil7th/OpenStudio/releases/download/v0.0.22/OpenStudio-AI-Runtime-linux-arm64.zip",
"sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"size": 123,
"fileName": "OpenStudio-AI-Runtime-linux-arm64.zip"
}
}
}
}GitHub JSON is served from static build snapshots; the old function URLs remain rate-limited compatibility redirects. Statistics and release snapshots refresh when the website is rebuilt/deployed. The browser reads the static URLs directly and makes no GitHub proxy function calls.
Build/runtime env:
GITHUB_TOKEN- Optional for public GitHub access; recommended for builds to reduce API rate-limit risk. CI supplies its read-only token.
OPENSTUDIO_FETCH_RELEASE_METADATA- Set to
truein the production Netlify context to fetch published manifests/appcasts before staging. Local builds can opt in to the same path.
- Set to
OPENSTUDIO_RELEASE_METADATA_DIR- Optional. Defaults to
release-input.
- Optional. Defaults to
OPENSTUDIO_REQUIRE_RELEASE_METADATA- Optional in local dev.
- Set to
truefor release-publish builds so missing or malformed metadata/appcasts fail the build.
OPENSTUDIO_DESKTOP_REPO- Optional. Defaults to
sdevil7th/OpenStudio. - Used by GitHub build helpers and by the publish workflow contract.
- Optional. Defaults to
Website workflow secrets:
NETLIFY_AUTH_TOKENNETLIFY_SITE_IDOPENSTUDIO_RELEASE_SOURCE_TOKEN- Optional API token for published-metadata discovery (
GH_TOKENis its fallback). Public release-asset downloads do not receive this credential. Set this separately fromGITHUB_TOKENwhen raising the production metadata-fetch quota.
- Optional API token for published-metadata discovery (
Desktop-side secret outside this repo:
- a token with permission to send
repository_dispatchevents to this website repo
Canonical pages are emitted into dist/_redirects at build time. /v2/* redirects to the corresponding canonical route, /blogs/* to /blog/*, /github to /community, and /contact to /community#contact. Unknown routes receive 404.html with HTTP 404 and noindex. Local production preview applies the same page rewrites and aliases, serves the GitHub-derived build payloads, and exercises app/runtime download routing against the same bundled catalog. It does not emulate Netlify rate-limit enforcement. See API abuse protection for the static/CDN architecture, Free-plan limits and bounded verification procedure.
/download/windows/latest,/download/macos/latest, and/download/linux/latestare generated at build time from validated/releases/stable/latest.json, with the fetched GitHub release snapshot providing the fallback for absent platform entries. Requests receive CDN redirects without invoking a function./download/ai-runtime/windows/latestresolves toplatforms.windows.base.urlwhen the new Windows manifest shape is present, otherwise it falls back to the legacy flatplatforms.windows.urlentry when available.- Windows backend install-plan metadata is preserved verbatim in the published JSON at
/releases/ai-runtime/latest.jsonand/releases/ai-runtime/stable/latest.json. /download/ai-runtime/windows/latestdoes not treatplatforms.windows.backends.cudaorplatforms.windows.backends.directmlas downloadable URLs when those entries only containinstallPlan./download/ai-runtime/macos/arm64/latestand/download/ai-runtime/macos/x64/latestresolve from the published AI runtime manifest and should be preferred when the caller knows the target architecture./download/ai-runtime/macos/latestremains a best-effort convenience redirect. It still supports the legacy flat macOS manifest entry, and for the new nested shape it will honor?arch=arm64or?arch=x64when present, otherwise it only redirects when it can infer the architecture safely./download/ai-runtime/linux/x64/latestand/download/ai-runtime/linux/arm64/latestresolve from the published AI runtime manifest and should be preferred when the caller knows the target architecture./download/ai-runtime/linux/latestsupports the legacy flat Linux manifest entry and otherwise falls back tox64, thenarm64, when no architecture is provided.- Netlify never hosts the
.exe,.dmg,.AppImage, or AI runtime archive files.
This repo includes a release-publish workflow triggered by repository_dispatch.
Assumed event name:
openstudio_release_published
Assumed payload shape:
{
"tag": "v1.2.3",
"channel": "stable",
"desktopRepo": "sdevil7th/OpenStudio"
}Workflow behavior:
- validate the dispatch payload and reject non-
stablechannels - validate
desktopRepomatches the configuredOPENSTUDIO_DESKTOP_REPO - download the generated metadata/appcast assets from the desktop GitHub Release for
tag, including the Linux appcast when present - map them into
release-input/ - run
npm ci - run
npm run validate-release-publish-inputs -- --root release-input - run
npm run buildwithOPENSTUDIO_REQUIRE_RELEASE_METADATA=true - deploy
dist/to Netlify
- Netlify config lives in netlify.toml.
- Release metadata and appcasts are published as static files from the site build output.
- The latest download paths are real Netlify redirects/functions, not React routes.
- All metadata and appcast surfaces are served with:
Cache-Control: no-store, no-cache, must-revalidate, max-age=0
After deploy, verify:
curl https://openstudio.org.in/releases/latest.json
curl https://openstudio.org.in/releases/stable/latest.json
curl https://openstudio.org.in/releases/ai-runtime/latest.json
curl https://openstudio.org.in/releases/ai-runtime/stable/latest.json
curl https://openstudio.org.in/appcast/windows-stable.xml
curl https://openstudio.org.in/appcast/macos-stable.xml
curl https://openstudio.org.in/appcast/linux-stable.xml
curl -I https://openstudio.org.in/download/windows/latest
curl -I https://openstudio.org.in/download/macos/latest
curl -I https://openstudio.org.in/download/linux/latest
curl -I https://openstudio.org.in/download/ai-runtime/windows/latest
curl -I https://openstudio.org.in/download/ai-runtime/macos/latest
curl -I https://openstudio.org.in/download/ai-runtime/macos/arm64/latest
curl -I https://openstudio.org.in/download/ai-runtime/macos/x64/latest
curl -I https://openstudio.org.in/download/ai-runtime/linux/latest
curl -I https://openstudio.org.in/download/ai-runtime/linux/x64/latest
curl -I https://openstudio.org.in/download/ai-runtime/linux/arm64/latestCheck that:
- metadata/appcast files are live
- metadata/appcast files are uncached
- app download redirects resolve through the published app release metadata to GitHub Release asset URLs
- AI runtime redirects resolve through the published AI runtime metadata to GitHub Release asset URLs
Responsive WebP derivatives are generated only for referenced screenshot/blog sources. Browsers select their size from srcset; source images remain a fallback if a variant fails. Generated files are recreated by the build. The self-hosted font subset is versioned at google-fonts-20260915 and retains the original bytes and licenses for the four used families.
- The approved 2160 px master lives in
assets/branding/openstudio-logo-source.png.npm run generate-brandingproduces the website icons inpublic/assets/openstudio/branding/. - Screenshot assets live in
public/assets/openstudio/screenshots/ - Share image contract lives at
public/assets/openstudio/branding/og-image.png; published metadata adds an automatically generated?v=<content-hash>. - See the branding inventory, download verification and visual comparison for all website/app placements and the GitHub data flow.