Skip to content

Update experiment navigation to include experiment name in URLs#1417

Merged
aliasaria merged 6 commits intomainfrom
add/store-experiment-name-in-url
Mar 3, 2026
Merged

Update experiment navigation to include experiment name in URLs#1417
aliasaria merged 6 commits intomainfrom
add/store-experiment-name-in-url

Conversation

@aliasaria
Copy link
Copy Markdown
Member

@aliasaria aliasaria commented Feb 27, 2026

Store the experiment name in the URL like:

http://localhost:1212/#/experiment/beta/interactive

This allows users to share links with others

Summary by CodeRabbit

  • New Features

    • URLs and navigation now include the experiment name, ensuring pages (notes, chat, tasks, interactive, documents, settings, training) are per-experiment and tracked consistently.
  • Refactor

    • Routing reorganized into nested experiment routes for clearer structure and improved navigation.
    • Sidebar and menu navigation synchronize URL and selection when switching or creating experiments so the visible page stays in context.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 27, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f1c5db7 and 016a7b2.

📒 Files selected for processing (4)
  • src/renderer/components/Experiment/Foundation/CurrentFoundationInfo.tsx
  • src/renderer/components/Experiment/SelectExperimentMenu.tsx
  • src/renderer/components/MainAppPanel.tsx
  • src/renderer/components/Nav/Sidebar.tsx

📝 Walkthrough

Walkthrough

Routing and navigation were switched to use experiment-name-based dynamic paths. A nested /experiment/:experimentName layout was added, navigation handlers and sidebar links now include the encoded experiment name, and analytics page paths are normalized to redact specific experiment names.

Changes

Cohort / File(s) Summary
Route Structure & Layout
src/renderer/components/MainAppPanel.tsx
Added ExperimentLayout that reads :experimentName, syncs it to experiment context, and restructured routes to nest experiment pages under /experiment/:experimentName. Normalized analytics paths by redacting experiment names.
Experiment Selection & Creation
src/renderer/components/Experiment/SelectExperimentMenu.tsx, src/renderer/components/Welcome/Welcome.tsx
Switched selection handler to use experiment name for URL updates; when creating experiments from recipes, navigation now goes to /experiment/{name}/notes. SelectExperimentMenu uses useLocation to preserve/restitch the rest of the path.
Sidebar & SubNav Matching
src/renderer/components/Nav/Sidebar.tsx, src/renderer/components/Nav/SubNavItem.tsx
Sidebar link paths now derive from encodeURIComponent(experimentInfo.name) as basePath. SubNavItem gains optional matchPattern prop and uses it for active-route matching.
Experiment-specific Nav Calls
src/renderer/components/Experiment/Foundation/CurrentFoundationInfo.tsx, src/renderer/components/TasksGallery/TasksGallery.tsx
Updated navigation calls to include encoded experiment name in paths (e.g., embedding-model, interactive, tasks).

Sequence Diagram(s)

sequenceDiagram
  participant User as User
  participant Menu as SelectExperimentMenu
  participant Router as Router (navigate)
  participant Layout as ExperimentLayout
  participant Context as ExperimentContext
  participant Analytics as PageTracker

  User->>Menu: click experiment item (name)
  Menu->>Router: navigate to /experiment/{encodedName}/{restOfPath}
  Router->>Layout: route -> ExperimentLayout with :experimentName
  Layout->>Context: setExperimentId(experimentId)  // sync context from name
  Layout->>Analytics: analytics.page(normalizePath(currentPath))
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately and clearly describes the main change: updating experiment navigation URLs to include the experiment name as a path parameter, which is reflected across multiple files in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add/store-experiment-name-in-url

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

@aliasaria
Copy link
Copy Markdown
Member Author

To test this, start the app, select an experiment and navigate around. Copy the URL. Then change experiments. Then try loading the copied URL you had and the experiment should change

@paragon-review
Copy link
Copy Markdown

Paragon Summary

This pull request review identified 4 issues across 2 categories in 7 files. The review analyzed code changes, potential bugs, security vulnerabilities, performance issues, and code quality concerns using automated analysis tools.

This PR updates the experiment navigation to include the experiment name in URLs, enabling users to share direct links to specific experiments with others.

Key changes:

  • Experiment name now embedded in URLs for shareable links
  • Updated navigation components (Sidebar, SubNavItem, MainAppPanel) to support name-based routing
  • Modified experiment selection/display components to sync with URL state
  • URL pattern: #/experiment/{name}/{tab} format

Confidence score: 3/5

  • This PR has moderate risk due to 2 high-priority issues that should be addressed
  • Score reflects significant bugs, performance issues, or architectural concerns
  • Review high-priority findings carefully before merging

7 files reviewed, 4 comments

Severity breakdown: High: 2, Medium: 2


Tip: @paragon-run <instructions> to chat with our agent or push fixes!

Dashboard


const handleEmbeddingModelClick = () => {
navigate('/experiment/embedding-model', {
navigate(`/experiment/${experimentInfo?.name}/embedding-model`, {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Navigation uses optional chaining without a fallback

Navigation uses optional chaining without a fallback. Users are redirected to broken literal undefined URLs. Guard the navigation call with an explicit check.

View Details

Location: src/renderer/components/Experiment/Foundation/CurrentFoundationInfo.tsx (lines 376)

Analysis

Navigation uses optional chaining without a fallback

What fails Navigation redirects to the literal string 'undefined' in the URL path.
Result The browser navigates to an invalid path like /experiment/undefined/embedding-model.
Expected Navigation should abort or wait until the experiment name is fully loaded.
Impact Users land on broken pages with 404 errors or blank screens if data is loading.
How to reproduce
Trigger navigation to the embedding model or interactive route while experimentInfo is still loading.
AI Fix Prompt
Fix this issue: Navigation uses optional chaining without a fallback. Users are redirected to broken literal undefined URLs. Guard the navigation call with an explicit check.

Location: src/renderer/components/Experiment/Foundation/CurrentFoundationInfo.tsx (lines 376)
Problem: Navigation redirects to the literal string 'undefined' in the URL path.
Current behavior: The browser navigates to an invalid path like /experiment/undefined/embedding-model.
Expected: Navigation should abort or wait until the experiment name is fully loaded.
Steps to reproduce: Trigger navigation to the embedding model or interactive route while experimentInfo is still loading.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

// If currently on an experiment page, update the URL to reflect the new experiment
const match = location.pathname.match(/^\/experiment\/[^/]+\/(.+)$/);
if (match) {
navigate(`/experiment/${experimentName}/${match[1]}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: Experiment names are inserted into URLs without encoding

Experiment names are inserted into URLs without encoding. Names with spaces or special characters create invalid routes. Wrap the parameters with URI encoding.

View Details

Location: src/renderer/components/Experiment/SelectExperimentMenu.tsx (lines 157)

Analysis

Experiment names are inserted into URLs without encoding

What fails URLs break when experiment names contain spaces or special characters.
Result The app generates an unencoded URL that fails to match the application's route definitions.
Expected The URL should be properly encoded using encodeURIComponent.
Impact Users cannot navigate to, share, or deep-link experiments with complex names.
How to reproduce
Create or select an experiment named 'My Beta Test' with a space in it.
Patch Details
-      navigate(`/experiment/${experimentName}/${match[1]}`);
+      navigate(`/experiment/${encodeURIComponent(experimentName)}/${match[1]}`);
AI Fix Prompt
Fix this issue: Experiment names are inserted into URLs without encoding. Names with spaces or special characters create invalid routes. Wrap the parameters with URI encoding.

Location: src/renderer/components/Experiment/SelectExperimentMenu.tsx (lines 157)
Problem: URLs break when experiment names contain spaces or special characters.
Current behavior: The app generates an unencoded URL that fails to match the application's route definitions.
Expected: The URL should be properly encoded using encodeURIComponent.
Steps to reproduce: Create or select an experiment named 'My Beta Test' with a space in it.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

// If there's no supports array or it's empty, default to 'chat'
useEffect(() => {
if (location.pathname === '/experiment/chat') {
if (location.pathname.endsWith('/chat')) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The chat route matching logic is too broad

The chat route matching logic is too broad. Unrelated chat routes will accidentally trigger experiment mode resets. Use a strict regex to match the exact prefix.

View Details

Location: src/renderer/components/MainAppPanel.tsx (lines 151)

Analysis

The chat route matching logic is too broad. Unrelated chat routes will accidentally trigger experime

What fails Mode reset logic automatically triggers for any URL ending in /chat.
Result The experiment chat mode is reset unexpectedly.
Expected Mode reset should strictly occur only for valid experiment chat routes.
Impact Causes unintended side effects, UI glitches, and state corruption on unrelated pages.
How to reproduce
Navigate to a non-experiment route anywhere in the application that ends in /chat.
Patch Details
-    if (location.pathname.endsWith('/chat')) {
+    if (location.pathname.match(/^\/experiment\/[^/]+\/chat$/)) {
AI Fix Prompt
Fix this issue: The chat route matching logic is too broad. Unrelated chat routes will accidentally trigger experiment mode resets. Use a strict regex to match the exact prefix.

Location: src/renderer/components/MainAppPanel.tsx (lines 151)
Problem: Mode reset logic automatically triggers for any URL ending in /chat.
Current behavior: The experiment chat mode is reset unexpectedly.
Expected: Mode reset should strictly occur only for valid experiment chat routes.
Steps to reproduce: Navigate to a non-experiment route anywhere in the application that ends in /chat.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

Comment thread src/renderer/components/Nav/Sidebar.tsx Outdated

function ExperimentMenuItems({ experimentInfo }: ExperimentMenuItemsProps) {
const experimentReady = Boolean(experimentInfo?.name);
const basePath = `/experiment/${experimentInfo?.name || ''}`;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Bug: The sidebar path generator uses an empty string fallback

The sidebar path generator uses an empty string fallback. This creates invalid double slash URLs when state is empty. Build paths conditionally to fix this.

View Details

Location: src/renderer/components/Nav/Sidebar.tsx (lines 39)

Analysis

The sidebar path generator uses an empty string fallback

What fails The sidebar generates invalid navigation URLs containing double slashes.
Result The generated links point to invalid paths like /experiment//interactive.
Expected Links should be valid or conditionally disabled until the experiment name is ready.
Impact Users clicking links during load states or empty states get routed to broken pages.
How to reproduce
View the sidebar menu while the experiment state is initializing or missing.
Patch Details
-  const basePath = `/experiment/${experimentInfo?.name || ''}`;
+  const basePath = experimentInfo?.name ? `/experiment/${encodeURIComponent(experimentInfo.name)}` : '';
AI Fix Prompt
Fix this issue: The sidebar path generator uses an empty string fallback. This creates invalid double slash URLs when state is empty. Build paths conditionally to fix this.

Location: src/renderer/components/Nav/Sidebar.tsx (lines 39)
Problem: The sidebar generates invalid navigation URLs containing double slashes.
Current behavior: The generated links point to invalid paths like /experiment//interactive.
Expected: Links should be valid or conditionally disabled until the experiment name is ready.
Steps to reproduce: View the sidebar menu while the experiment state is initializing or missing.

Provide a code fix.


Tip: Reply with @paragon-run to automatically fix this issue

Copy link
Copy Markdown
Contributor

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/renderer/components/Experiment/SelectExperimentMenu.tsx`:
- Around line 152-158: The dropdown handler createHandleClose currently passes
an experiment name into setExperimentId (mixing name vs ID) and elsewhere calls
createHandleClose(newId) without invoking the returned function; fix by ensuring
setExperimentId always receives the canonical experiment ID (not name) — convert
the selected name to its ID before calling setExperimentId inside
createHandleClose or change the dropdown to pass the ID — and change callers
that do createHandleClose(newId) to call the returned function (e.g.,
createHandleClose(newId)() or refactor to return void). Update both
createHandleClose and the other occurrence around the later use (near the
reported second location) to consistently use IDs and to invoke the handler so
navigate(`/experiment/${experimentId}/...`) receives the correct identifier.

In `@src/renderer/components/MainAppPanel.tsx`:
- Around line 319-351: Add a child Route for the missing "embedding-model" path
under the same parent route that currently contains ExperimentNotes, Interact,
TrainLoRA, etc. Specifically, in MainAppPanel.tsx add a Route with
path="embedding-model" and element={<EmbeddingModel />} (or the correct
component that renders embedding model details) so that navigation from
CurrentFoundationInfo.tsx (which navigates to
/experiment/${experimentInfo?.name}/embedding-model) resolves; place it
alongside the existing Route entries (e.g., near ExperimentNotes and Interact)
and ensure you import the EmbeddingModel component if not already imported.
- Around line 51-57: The analytics call in MainAppPanel normalizes the path but
still sends a raw URL via window.location.href (leaking experimentName); update
the logic around normalizedPath and the analytics.page call to also produce a
normalizedUrl by replacing the same /^\/experiment\/[^/]+/ segment in
window.location.href (or reconstruct a safe URL using location.origin +
normalizedPath) and pass that normalizedUrl to analytics.page instead of
window.location.href so both path and url are redacted.

In `@src/renderer/components/Nav/Sidebar.tsx`:
- Line 39: The basePath construction in Sidebar.tsx uses raw experimentInfo.name
which can break routing for names with slashes, spaces, or query chars; update
the basePath creation to URL-encode the experiment name (use encodeURIComponent
on experimentInfo.name when present) so the computed basePath (`basePath`) is
safe for use in links and routing (keep the fallback behavior when name is
missing).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fae46b4 and f1c5db7.

📒 Files selected for processing (7)
  • src/renderer/components/Experiment/Foundation/CurrentFoundationInfo.tsx
  • src/renderer/components/Experiment/SelectExperimentMenu.tsx
  • src/renderer/components/MainAppPanel.tsx
  • src/renderer/components/Nav/Sidebar.tsx
  • src/renderer/components/Nav/SubNavItem.tsx
  • src/renderer/components/TasksGallery/TasksGallery.tsx
  • src/renderer/components/Welcome/Welcome.tsx

Comment on lines +152 to +158
const createHandleClose = (experimentName: string) => () => {
setExperimentId(experimentName);
// If currently on an experiment page, update the URL to reflect the new experiment
const match = location.pathname.match(/^\/experiment\/[^/]+\/(.+)$/);
if (match) {
navigate(`/experiment/${experimentName}/${match[1]}`);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

setExperimentId now receives experiment name here, creating inconsistent identifier semantics.

This file still uses IDs in other flows, but dropdown selection now writes name into context. That inconsistency can break experiment switching behavior. Also, Line 212 currently calls createHandleClose(newId) without invoking the returned handler.

Proposed fix
-  const createHandleClose = (experimentName: string) => () => {
-    setExperimentId(experimentName);
+  const createHandleClose =
+    (experimentId: string | number, experimentName: string) => () => {
+      setExperimentId(String(experimentId));
     // If currently on an experiment page, update the URL to reflect the new experiment
     const match = location.pathname.match(/^\/experiment\/[^/]+\/(.+)$/);
     if (match) {
       navigate(`/experiment/${experimentName}/${match[1]}`);
     }
-  };
+  };

...
-                        onClick={createHandleClose(experiment.name)}
+                        onClick={createHandleClose(experiment.id, experiment.name)}

Also applies to: 392-392

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/Experiment/SelectExperimentMenu.tsx` around lines 152
- 158, The dropdown handler createHandleClose currently passes an experiment
name into setExperimentId (mixing name vs ID) and elsewhere calls
createHandleClose(newId) without invoking the returned function; fix by ensuring
setExperimentId always receives the canonical experiment ID (not name) — convert
the selected name to its ID before calling setExperimentId inside
createHandleClose or change the dropdown to pass the ID — and change callers
that do createHandleClose(newId) to call the returned function (e.g.,
createHandleClose(newId)() or refactor to return void). Update both
createHandleClose and the other occurrence around the later use (near the
reported second location) to consistently use IDs and to invoke the handler so
navigate(`/experiment/${experimentId}/...`) receives the correct identifier.

Comment on lines +51 to 57
const normalizedPath = location.pathname.replace(
/^\/experiment\/[^/]+/,
'/experiment/:experimentName',
);
analytics.page({
path: location.pathname,
path: normalizedPath,
url: window.location.href,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Redaction is incomplete: analytics still receives the raw experiment name via url.

path is normalized, but url: window.location.href still includes the real experiment name, so tracking still leaks it.

Proposed fix
       analytics.page({
         path: normalizedPath,
-        url: window.location.href,
         search: location.search,
         title: document.title,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const normalizedPath = location.pathname.replace(
/^\/experiment\/[^/]+/,
'/experiment/:experimentName',
);
analytics.page({
path: location.pathname,
path: normalizedPath,
url: window.location.href,
const normalizedPath = location.pathname.replace(
/^\/experiment\/[^/]+/,
'/experiment/:experimentName',
);
analytics.page({
path: normalizedPath,
search: location.search,
title: document.title,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/MainAppPanel.tsx` around lines 51 - 57, The analytics
call in MainAppPanel normalizes the path but still sends a raw URL via
window.location.href (leaking experimentName); update the logic around
normalizedPath and the analytics.page call to also produce a normalizedUrl by
replacing the same /^\/experiment\/[^/]+/ segment in window.location.href (or
reconstruct a safe URL using location.origin + normalizedPath) and pass that
normalizedUrl to analytics.page instead of window.location.href so both path and
url are redacted.

Comment on lines +319 to +351
<Route path="notes" element={<ExperimentNotes />} />
<Route
path="chat"
element={
<Interact
setRagEngine={setRagEngine}
mode={selectedInteractSubpage}
setMode={setSelectedInteractSubpage}
supports={modelSupports}
chatHistory={chatHistory}
setChatHistory={setChatHistory}
/>
}
/>
<Route
path="model_architecture_visualization"
element={
<Interact
setRagEngine={setRagEngine}
mode="model_layers"
setMode={setSelectedInteractSubpage}
supports={modelSupports}
chatHistory={chatHistory}
setChatHistory={setChatHistory}
/>
}
/>
<Route path="training" element={<TrainLoRA />} />
<Route path="tasks" element={<Tasks />} />
<Route path="interactive" element={<Interactive />} />
<Route path="documents" element={<Documents />} />
<Route path="settings" element={<Settings />} />
</Route>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Feb 27, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing embedding-model child route creates a dead navigation path.

src/renderer/components/Experiment/Foundation/CurrentFoundationInfo.tsx navigates to /experiment/${experimentInfo?.name}/embedding-model (Line 376), but this route tree does not define an embedding-model child under /experiment/:experimentName.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/MainAppPanel.tsx` around lines 319 - 351, Add a child
Route for the missing "embedding-model" path under the same parent route that
currently contains ExperimentNotes, Interact, TrainLoRA, etc. Specifically, in
MainAppPanel.tsx add a Route with path="embedding-model" and
element={<EmbeddingModel />} (or the correct component that renders embedding
model details) so that navigation from CurrentFoundationInfo.tsx (which
navigates to /experiment/${experimentInfo?.name}/embedding-model) resolves;
place it alongside the existing Route entries (e.g., near ExperimentNotes and
Interact) and ensure you import the EmbeddingModel component if not already
imported.

Copy link
Copy Markdown
Member

@deep1401 deep1401 Feb 27, 2026

Choose a reason for hiding this comment

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

This one seems legit? I think we'll get rid of the component though so not sure if you want to fix it? @aliasaria

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@deep1401, thanks for confirming! If you're planning to remove the component, there's no need to add the route—just make sure to clean up the navigation reference in CurrentFoundationInfo.tsx when you remove it. That'll keep things consistent. 👍

Comment thread src/renderer/components/Nav/Sidebar.tsx Outdated
…s invalid double slash URLs when state is empty. Build paths conditionally to fix this.
@aliasaria aliasaria merged commit 7a97447 into main Mar 3, 2026
2 of 4 checks passed
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