Skip to content

Add nightly version#356

Merged
mateuszsikora merged 10 commits intomainfrom
ms-add-nightly-version
Dec 31, 2025
Merged

Add nightly version#356
mateuszsikora merged 10 commits intomainfrom
ms-add-nightly-version

Conversation

@mateuszsikora
Copy link
Contributor

@mateuszsikora mateuszsikora commented Dec 28, 2025

Summary by CodeRabbit

  • New Features

    • Nightly releases now appear in the version dropdown with a dedicated "Nightly" label.
  • Behavior Changes

    • Version selection and labels now prefer nightly when the current location matches the nightly identifier.
    • Resource URLs (PDF, source directory, etc.) resolve to a nightly-specific path when the nightly identifier is selected.
    • Short/partial version inputs will now resolve to nightly when they match the nightly identifier.
  • Chore

    • Metadata is preloaded from a remote metadata URL instead of the local file.

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link

netlify bot commented Dec 28, 2025

Deploy Preview for graypaper-reader ready!

Name Link
🔨 Latest commit 4c6fdf3
🔍 Latest deploy log https://app.netlify.com/projects/graypaper-reader/deploys/69553cf2b5775e00086a65de
😎 Deploy Preview https://deploy-preview-356--graypaper-reader.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@mateuszsikora mateuszsikora marked this pull request as draft December 28, 2025 17:44
@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

📝 Walkthrough

Walkthrough

Adds optional nightly version metadata and resolves matching hashes to the "nightly" channel across metadata URL builders, location hashing, and version labeling; metadata preload source switched to a remote URL.

Changes

Cohort / File(s) Summary
Metadata provider
src/components/MetadataProvider/MetadataProvider.tsx
Adds nightly?: IVersionInfo to IMetadata; introduces resolveVersion mapping so URL getters (pdf, synctex, texDirectory) map a matching hash to "nightly"; memoization updated to return null only when metadata is absent; legacyReaderRedirect unchanged.
Version display
src/components/Version/Version.tsx
Includes metadata.nightly in versions list; current-version resolution prefers nightly when hashes match; adds getVersionLabel(version, latest, nightly?); VersionOption now receives optional nightly and uses unified labeling (Nightly / Latest / vX).
Location resolution
src/components/LocationProvider/LocationProvider.tsx, src/components/LocationProvider/utils/locationParamsToHash.ts
fullVersion lookup gains a fallback to metadata.nightly?.hash; locationParamsToHash maps an explicit fullVersion without a name to "nightly" if it matches nightly.hash, otherwise falls back to prior behavior.
Static preload
index.html
Changes metadata preload href from /metadata.json to https://gp.fluffylabs.dev/metadata.json.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Browser
  participant MetadataProvider
  participant LocationProvider
  participant VersionComponent

  Browser->>MetadataProvider: preload / fetch metadata.json
  MetadataProvider->>MetadataProvider: parse metadata (versions, latest, nightly)
  Browser->>LocationProvider: request resolve from URL params
  LocationProvider->>MetadataProvider: read versions & nightly.hash
  alt fullVersion matches a version
    LocationProvider->>LocationProvider: resolve fullVersion -> version
  else fullVersion matches nightly.hash
    LocationProvider->>LocationProvider: resolve fullVersion -> nightly.hash (treated as "nightly")
  end
  Browser->>VersionComponent: render version UI with resolved fullVersion
  VersionComponent->>MetadataProvider: request labels (latest, nightly)
  MetadataProvider-->>VersionComponent: supply labels (Nightly/Latest/vX)
  VersionComponent-->>Browser: render labels and options
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • DrEverr

Poem

🐰 Nightly tiptoes on a hashed-on string,
I nibble labels as the renderings sing,
Latest and nightly, now clearly in view,
URLs follow the hash to the new,
A rabbit applauds: small hops, big clue.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add nightly version' accurately describes the main change: introducing nightly version support across the codebase through new IVersionInfo field, version resolution logic, and UI updates.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dbbc1c0 and 05a182d.

📒 Files selected for processing (1)
  • src/components/Version/Version.tsx
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/Version/Version.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/Version/Version.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/Version/Version.tsx
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/Version/Version.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/Version/Version.tsx
🧬 Code graph analysis (1)
src/components/Version/Version.tsx (1)
src/components/MetadataProvider/MetadataProvider.tsx (1)
  • IVersionInfo (7-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (5)
src/components/Version/Version.tsx (5)

20-23: LGTM! Clean versions array construction.

The array correctly combines non-legacy versions with the optional nightly version, and the conditional spread prevents undefined from being added to the list.


24-28: Good improvement with fallback logic.

The fallback chain now handles the case where locationParams.version doesn't exist in metadata.versions, which addresses the previous concern. The implementation assumes that metadata.latest is always a valid key in metadata.versions, which is reasonable for well-formed metadata.


51-51: Excellent deduplication!

The delegation to getVersionLabel successfully eliminates code duplication and provides a single source of truth for version label formatting.


104-107: Clean component implementation.

The VersionOption component is simple and properly delegates to the getVersionLabel helper, maintaining good separation of concerns.


109-120: Core labeling logic is correct and well-structured.

The getVersionLabel helper properly handles all version types:

  • Identifies and labels nightly versions
  • Identifies and labels latest versions
  • Formats versioned releases with names when applicable
  • Consistently formats output with short hash and localized date

The logic correctly excludes version names from nightly labels (line 115), which makes sense given that "Nightly" already serves as the primary identifier.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/MetadataProvider/MetadataProvider.tsx (1)

14-20: Add runtime validation for the nightly field to prevent runtime errors.

The nightly field is required in the IMetadata interface (line 16), and Version.tsx directly accesses metadata.nightly without null checks (lines 20–22, 93). If the external metadata.json doesn't include the nightly field, the application will crash at runtime.

The type assertion on line 52 (as IMetadata) won't catch this at runtime. Add validation to ensure the field exists:

 useEffect(() => {
   const fetchMetadata = async () => {
     try {
-      setMetadata((await (await fetch(METADATA_JSON)).json()) as IMetadata);
+      const data = (await (await fetch(METADATA_JSON)).json()) as IMetadata;
+      if (!data.nightly) {
+        throw new Error("metadata.json missing required 'nightly' field");
+      }
+      setMetadata(data);
     } catch (e) {
       console.error("Couldn't load metadata.", e);
     }
   };

Alternatively, make the field optional only if you provide a fallback in Version.tsx for when it's missing.

🧹 Nitpick comments (2)
src/components/Version/Version.tsx (1)

46-54: Consider extracting duplicated label logic.

The label construction logic (lines 49-52 and 113-116) is nearly identical. Extracting it into a shared helper would reduce duplication.

🔎 Proposed refactor
+function getVersionLabel(version: IVersionInfo, latest: string, nightly: string): string {
+  const isNightly = version.hash === nightly;
+  const isLatest = version.hash === latest;
+  let label = isNightly ? "Nightly" : isLatest ? "Latest" : "v";
+  if (version.name && !isNightly) {
+    label += `: ${version.name}`;
+  }
+  return label;
+}
+
 function shortHash(h: string) {

Then use getVersionLabel(currentVersion, metadata.latest, metadata.nightly.hash) in both places.

Also applies to: 113-116

src/components/MetadataProvider/MetadataProvider.tsx (1)

49-59: Add validation for the metadata structure.

The fetch logic performs a type assertion without validating that the returned JSON includes all required fields. If metadata.json is missing the nightly field, the error won't surface until the field is accessed elsewhere, making debugging harder.

Consider adding validation after fetching:

🔎 Proposed validation logic
   useEffect(() => {
     const fetchMetadata = async () => {
       try {
-        setMetadata((await (await fetch(METADATA_JSON)).json()) as IMetadata);
+        const data = (await (await fetch(METADATA_JSON)).json()) as IMetadata;
+        
+        // Validate required fields
+        if (!data.latest || !data.nightly || !data.versions) {
+          throw new Error("Invalid metadata structure: missing required fields");
+        }
+        
+        setMetadata(data);
       } catch (e) {
         console.error("Couldn't load metadata.", e);
       }
     };

     fetchMetadata();
   }, []);

Alternatively, if you make nightly optional (as suggested in the previous comment), update the validation accordingly:

   useEffect(() => {
     const fetchMetadata = async () => {
       try {
-        setMetadata((await (await fetch(METADATA_JSON)).json()) as IMetadata);
+        const data = (await (await fetch(METADATA_JSON)).json()) as IMetadata;
+        
+        // Validate required fields
+        if (!data.latest || !data.versions) {
+          throw new Error("Invalid metadata structure: missing required fields");
+        }
+        
+        setMetadata(data);
       } catch (e) {
         console.error("Couldn't load metadata.", e);
       }
     };

     fetchMetadata();
   }, []);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f13225c and 89604d8.

📒 Files selected for processing (2)
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
📚 Learning: 2025-10-22T20:36:10.440Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 330
File: src/components/Outline/OutlineLink.tsx:17-21
Timestamp: 2025-10-22T20:36:10.440Z
Learning: This repo targets React 19+; prefer passing refs as a normal prop to function components and avoid React.forwardRef in new code. File context: src/components/Outline/OutlineLink.tsx.

Applied to files:

  • src/components/Version/Version.tsx
🧬 Code graph analysis (1)
src/components/Version/Version.tsx (1)
src/components/MetadataProvider/MetadataProvider.tsx (1)
  • IVersionInfo (7-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (3)
src/components/Version/Version.tsx (3)

68-77: Verify warning behavior for nightly versions.

The warning "The current version is not the latest" will display when viewing the nightly version since nightly.hash !== metadata.latest. Is this the intended UX? Nightly users might find this warning confusing since nightly is typically newer than latest.

If nightly should not trigger this warning, consider:

- {currentVersionHash !== metadata.latest && (
+ {currentVersionHash !== metadata.latest && !isNightly && (

107-123: LGTM!

The VersionOption component is well-structured with proper TypeScript typing and follows functional component patterns. The label logic correctly handles all three version states (nightly, latest, standard).


86-96: LGTM!

The dropdown rendering correctly includes the nightly version in the list and properly propagates the nightly hash to each VersionOption for accurate label display.

@github-actions
Copy link
Contributor

github-actions bot commented Dec 28, 2025

Visual Regression Test Report ✅ Passed

Github run id: 20621695892

🔗 Artifacts: Download

@mateuszsikora mateuszsikora marked this pull request as ready for review December 31, 2025 11:19
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/Version/Version.tsx (1)

24-28: Critical: Potential crash if locationParams.version is invalid.

If locationParams.version doesn't match metadata.nightly.hash AND isn't a key in metadata.versions, then currentVersion will be undefined. Line 28 will then crash when accessing currentVersion.hash.

This issue was flagged in a previous review but remains unresolved.

🔎 Proposed fix with fallback to latest
- const currentVersion =
-   metadata.nightly && locationParams.version === metadata.nightly.hash
-     ? metadata.nightly
-     : metadata.versions[locationParams.version];
+ const currentVersion =
+   metadata.nightly && locationParams.version === metadata.nightly.hash
+     ? metadata.nightly
+     : metadata.versions[locationParams.version] ?? metadata.versions[metadata.latest];
🧹 Nitpick comments (1)
index.html (1)

6-6: Consider using environment variables for metadata URL to support offline local development.

The metadata URL is hardcoded as https://gp.fluffylabs.dev/metadata.json in MetadataProvider.tsx and used for runtime fetching via fetch(METADATA_JSON) in a useEffect. While the preload in index.html serves as a performance hint, the actual metadata load depends on this hardcoded remote URL. This would prevent local development if the remote host is unavailable or if developers need to test with custom or offline metadata.

Introducing an environment variable (e.g., VITE_METADATA_URL) with a default production value would allow developers to override the metadata source during local development and testing, similar to how the test infrastructure already uses PLAYWRIGHT_HOST and PLAYWRIGHT_PORT for flexibility.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 89604d8 and dbbc1c0.

📒 Files selected for processing (5)
  • index.html
  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/LocationProvider/utils/locationParamsToHash.ts
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Feature folders should keep components, hooks, and styles together in src/
Prefer hooks over Higher-Order Components (HOCs)
Colocate helper functions with their consumer components
Use PascalCase for component names
Favor semantic wrappers over long Tailwind utility class strings; complement Tailwind with @fluffylabs/shared-ui components

Files:

  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
  • src/components/LocationProvider/utils/locationParamsToHash.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use TypeScript as the primary language with functional React components
Use camelCase for function and variable names
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
  • src/components/LocationProvider/utils/locationParamsToHash.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use 2-space indentation

Files:

  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
  • src/components/LocationProvider/utils/locationParamsToHash.ts
**/*.{js,jsx,ts,tsx}

⚙️ CodeRabbit configuration file

When reviewing Tailwind CSS classes, ensure they follow Tailwind 4.x conventions and suggest modern 4.x alternatives for deprecated patterns.

Files:

  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
  • src/components/LocationProvider/utils/locationParamsToHash.ts
🧠 Learnings (2)
📚 Learning: 2025-12-28T13:25:05.152Z
Learnt from: chmurson
Repo: FluffyLabs/graypaper-reader PR: 355
File: src/components/NoteManager/components/NoteLayout.tsx:39-39
Timestamp: 2025-12-28T13:25:05.152Z
Learning: In Tailwind CSS 4.x, max-h-68 is a valid utility that sets max-height: 17rem (272px) and is part of the extended spacing scale. When reviewing TSX files, verify components use this class consistently and that tailwind.config.js includes the 68 spacing key in the extended spacing scale if you rely on it.

Applied to files:

  • src/components/LocationProvider/LocationProvider.tsx
  • src/components/MetadataProvider/MetadataProvider.tsx
  • src/components/Version/Version.tsx
📚 Learning: 2025-12-26T10:17:47.372Z
Learnt from: CR
Repo: FluffyLabs/graypaper-reader PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-26T10:17:47.372Z
Learning: Gray Paper PDFs are managed via the `graypaper-archive/` submodule and copied to `public/` during predev/prebuild via `scripts/cp-gp.js`

Applied to files:

  • index.html
🧬 Code graph analysis (2)
src/components/LocationProvider/LocationProvider.tsx (1)
src/components/LocationProvider/utils/constants.ts (1)
  • VERSION_SEGMENT_INDEX (1-1)
src/components/Version/Version.tsx (1)
src/components/MetadataProvider/MetadataProvider.tsx (1)
  • IVersionInfo (7-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: visual-tests
🔇 Additional comments (5)
src/components/LocationProvider/LocationProvider.tsx (1)

81-85: LGTM! Nightly version fallback properly implemented.

The extended fallback logic correctly checks for nightly hash when the primary version lookup fails. The use of optional chaining ensures safe access to metadata.nightly?.hash.

src/components/LocationProvider/utils/locationParamsToHash.ts (1)

16-19: LGTM! Version name derivation correctly handles nightly.

The logic properly maps nightly hash to the "nightly" label and falls back appropriately through the chain: named version → nightly → latest → empty string.

src/components/MetadataProvider/MetadataProvider.tsx (2)

3-4: Remote metadata host is now hardcoded.

The METADATA_HOST is now hardcoded to https://gp.fluffylabs.dev, matching the preload URL in index.html. Ensure this works correctly in all environments (dev, staging, production).

Related to the index.html change on line 6.


61-75: LGTM! Excellent abstraction for version resolution.

The resolveVersion helper cleanly maps nightly hashes to the "nightly" label and applies it consistently across all URL getters. The restructured memoization correctly returns a populated context when metadata exists.

src/components/Version/Version.tsx (1)

20-23: LGTM! Versions array correctly includes nightly.

The versions array properly filters out legacy versions and conditionally includes nightly when it exists. Good use of optional spreading.

@mateuszsikora mateuszsikora merged commit 98d8d6d into main Dec 31, 2025
9 checks passed
@mateuszsikora mateuszsikora deleted the ms-add-nightly-version branch December 31, 2025 15:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants