12 KiB
Contributing
Pull requests are paused for now. 🙏 StreamXR is made by one person, who's focusing on bug fixes and the roadmap, so pull requests aren't being accepted at the moment and will be closed. Issues are very welcome: bug reports, headset reports, feature requests and translations (sent as an issue: see "Translating"). The rules below are for when pull requests open again.
Thanks for wanting to help. This project is built to stay small, readable and easy to extend, so contributions are welcome when they keep it that way. Please read this whole page before opening a pull request: pull requests that skip it are closed without review.
The short version
- Open an issue first and wait for a reply before writing code. Describe what you want to change and why. This saves you from building something that won't be merged.
- One change per pull request. A bug fix, a feature, or a headset's support; not all three.
- Test it on real hardware in a real VR session, and say which headset, browser and VR runtime you used.
- Follow the code rules below. Read the surrounding code and write like it.
- You must understand every line you submit. AI tools are fine to use, but you are the author: be ready to explain any line in review. Code that looks generated and unexamined (unused code, invented APIs, comments that don't match the code, changes unrelated to the issue) is closed.
Ways to help without writing code
These are often more useful than code. Every issue goes through a form (New issue on GitHub), so it arrives with what's needed to act on it:
- Headset / controller compatibility. If the extension doesn't work right on your hardware, paste the Copy details output (the form says how). That's usually enough for the fix to be written here.
- YouTube changed something. YouTube changes its pages often. If something that used to work stops (Up Next is empty, a panel shows nothing), a report with the video or channel link is very valuable.
- Video looks wrong in VR. A video that shows stretched, doubled, as squares or the wrong way round: the video's title, its channel and Copy details help fix its format detection.
- Bug report, with steps to reproduce, the video's title and channel, and the Copy details output.
- New feature request, Support for another site (check the DRM note in the form first), Documentation problem, and Question or setup help.
- Security problems go privately, never in an issue: see SECURITY.md.
- Translations. StreamXR's own words (buttons, panel names, messages) come from string tables, so adding your language needs no code: see "Translating" below.
Translating
Everything StreamXR writes itself is in three English tables. What the site writes (video titles, "1.2M views", comments) is not: it already arrives in your site language.
src/core/shared/strings-en.js: the core (controls panel, keyboard, help, the Laser / Keyboard / Language settings tabs).src/streamxr/shared/strings-en.js: StreamXR (video formats, the "Watch in VR" popup, starting VR, the 2D Cinema settings tab).src/sites/youtube/page/provider/strings-en.js: YouTube's panels (keys start withyt.).
Spanish (strings-es.js next to each) is a finished example, and needs a native speaker's
review: corrections are welcome.
To add a language (say French, fr; a regional one is fine too, like pt-BR):
- Copy each table next to itself as
strings-fr.js, and change'en'in itsns.i18n.add('en', { ... })line to'fr'. - Translate the texts. Never change the keys (the left side). Keep every
{name}placeholder (move it where your sentence needs it). A text like{ one: '{n} reply', other: '{n} replies' }has plural forms: give the forms your language uses (zero,one,two,few,many,other;otheris required). A key you leave out shows in English, so a partial translation still works. - Keep button words and row names short: the controls panel's buttons are small. Longer words are drawn smaller to fit, down to 60%, and then cut with "…". Look at your language in VR and pick shorter words where it gets small (Spanish uses "Restablecer" rather than "Predeterminado" for Default).
- List the files in
manifest.json, each right after its English one:src/core/shared/strings-fr.jsandsrc/streamxr/shared/strings-fr.jsin the first (page world) entry, andsrc/core/ui/shared/strings-fr.jsandsrc/streamxr/ui/shared/strings-fr.jsin the second (isolated world) entry;src/sites/youtube/page/provider/strings-fr.jsin the first entry.
- Run
node tools/sync-shared.js(it makes the twoui/shared/strings-fr.jscopies). - Check it in VR: Settings → Language → your language. Panels switch at once; the popup on the video page uses it the next time it opens. Your browser set to that language picks it by default.
The language's name in the Language tab comes from the browser (Intl.DisplayNames), in
the language itself ("Español").
Sending it in: while pull requests are paused, open a Translation issue: it has a box for each of the three word lists, so you paste your translated lines straight into it (no files needed), and they'll be added from there. Steps 4-6 are done then.
Code rules
Most of these come from how the project is laid out: read ARCHITECTURE.md before changing structure (it has the folder map, the layers and where common changes go).
- No libraries, no build step, no ES modules. Every file is a plain script
(function (ns) { ... })(globalThis.__streamxr = globalThis.__streamxr || {});that adds one piece to the shared namespace. Load order lives inmanifest.json. The one exception is vendored third-party code inthird_party/(Google's Material Color Utilities, wrapped as a plain script with its license and a note of the changes); adding another needs agreeing in an issue first. - Keep things where they belong:
- Only
src/sites/youtube/page/youtube-*.jsread, click or request anything from YouTube. When YouTube's page or data changes, the fix goes there. - The shell (the core
src/core/and StreamXRsrc/streamxr/: everything not undersrc/sites/) never names a site: site code lives insrc/sites/<site>/(YouTube's:src/sites/youtube/), and each site registers its provider in the site registry (src/core/page/sites.js). - The VR interface (
src/core/page/vr-panel/) knows nothing about YouTube: it is a shell meant to drive other sites too (see ARCHITECTURE.md). The chevron panels are built from five styles (vr-panel/styles/: Info, Catalog, Queue, Profile, Options) that draw a plain model and report standard events. YouTube fills them fromsrc/sites/youtube/page/provider/(one controller per panel, registered inpanels.js). A new panel feature goes into a style as a generic option (or a slot), and YouTube's side goes into its controller; a style never gets a YouTube-specific field. - The shell reaches YouTube only through its provider
(
src/sites/youtube/page/provider/provider.js, interface at its top): the VR session, the controls panel and format detection ask it, andsrc/streamxr/page/main.jspicks the page's site from the registry. - Tunable numbers go in the config files (
vr-panel/config.js,controllers/config.js,SETTINGSincinema-screen.js/pause-overlay.js), not scattered through code. - What each controller input does lives in one place:
src/core/shared/controls.js. - Words shown to the user are never written in the code: add a key to the English table
of the part that draws it (
src/core/shared/strings-en.js,src/streamxr/shared/strings-en.js, or the site'sprovider/strings-en.js) and drawns.i18n.t('key')(see "Translating" and ARCHITECTURE.md, "Words"). The English tables are in American English (color, center, gray), as are the README and the docs users read. - Colors on the panels are never written in the code either: use a Material 3 color
role from
src/core/page/vr-panel/tokens.js(TC.<role>, orTA(role, alpha)for a translucent layer). A site's own colors go in its provider'stheme. - Browser-specific code (
chrome.*) stays isolated (today:src/sites/youtube/ui/skip-helper.js), so a Firefox port stays possible.
- Only
- Comment as you go. Every file starts with what it's for. Every function says what it does, and what it returns or which units/pixels it uses when that isn't obvious. Non-obvious steps get a line. Comments describe what the code does now, not its history (that's what git is for). GLSL shaders are explained in a comment above the shader string.
- Don't edit generated files:
src/core/page/controllers/models.js(made bytools/controller-models/),src/core/ui/shared/andsrc/streamxr/ui/shared/(made bytools/sync-shared.js). After changing anything insrc/core/shared/orsrc/streamxr/shared/, runnode tools/sync-shared.js. - Record user-visible changes in
CHANGELOG.md(under the new version), and updateARCHITECTURE.mdwhen you change how things are structured. - Bump
versioninmanifest.jsonin every pull request that changes the extension, so testers can confirm the reload picked it up. - Keep permissions minimal. The extension asks only for youtube.com. A change that needs more permissions needs a very good reason, discussed in its issue first.
- Don't detect headsets by refresh rate (it's a user setting). See the README's "Adding a headset" section for what to use instead.
- Match the existing style: 2-space indentation, single quotes, semicolons,
constby default, small functions. No reformatting of code you didn't otherwise change.
Before you open the pull request
Two things run on every pull request and push, so you see problems early:
- Automatic summary (
.github/workflows/pr-summary.yml): fills the bottom of your PR description with the linked issue, the commit history and the changed files, and flags a missing version bump or CHANGELOG entry, new files not inmanifest.json, shared files without their regenerated copies, edited generated files, permission changes, and shell code that names a site. - Checks (
.github/workflows/pr-checks.yml):node --checkon every script, the manifests parse, andnode tools/sync-shared.js --check.
Fix what they flag before asking for review. The rest is up to you:
- There's an issue for it, and it got a reply agreeing to the change.
- It does one thing, and the description says what and why.
- Every file you load is listed in
manifest.jsonin the right order. node --checkpasses on the files you changed, andnode tools/sync-shared.jsruns clean if you touched ashared/folder (src/core/shared/,src/streamxr/shared/).- You tested it in a real VR session (headset, browser, runtime listed in the PR).
- Comments are updated for everything you changed.
manifest.jsonversion bumped, andCHANGELOG.mdupdated.
Forks
The code is MIT, so you're free to fork it and change it for your own use. A few things come with every fork:
- Keep the license notices, including the attribution header in
src/core/page/controllers/models.js. Those controller models are adapted from Valve's CAD under CC BY-NC-SA 4.0: any fork that keeps them can't be used commercially (see the README's License section). - Please use a different name if your fork changes things substantially, so its bugs aren't reported here.
- If your fork adds support for a headset, consider opening an issue here: good, well-structured support can be brought back for everyone.
License of contributions
By contributing, you agree that your contribution is released under this project's MIT license (and, for anything derived from the controller models, CC BY-NC-SA 4.0).