From 844031c4ed65cef23e209fe369b9ea6119c69a7b Mon Sep 17 00:00:00 2001 From: Francesc Torradeflot Date: Tue, 16 Dec 2025 10:27:35 +0100 Subject: [PATCH 1/5] Remove Swan-specific section --- docs/user/jupyter_extension_user.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/user/jupyter_extension_user.md b/docs/user/jupyter_extension_user.md index f2c0795eed8..77b4ef52ee7 100644 --- a/docs/user/jupyter_extension_user.md +++ b/docs/user/jupyter_extension_user.md @@ -150,14 +150,6 @@ with open(my_data, 'r') as f: data = np.load(f) ``` -## Session Configuration - -When spawning a JupyterLab session: -1. You can choose to activate the Rucio extension -2. A configuration form allows you to select: - - Which experiment's Rucio cluster to connect to - - Which RSE to use as local storage for your files - ## Terminal Usage To use Rucio commands in your terminal within the JupyterLab environment: From 32bdb3cb8e8ded1f276538d8d139a2c33ec959c1 Mon Sep 17 00:00:00 2001 From: Francesc Torradeflot Date: Tue, 16 Dec 2025 13:17:44 +0100 Subject: [PATCH 2/5] Modify notebook injection part --- docs/user/jupyter_extension_user.md | 17 ++++++++--------- website/static/img/rucio_jupyter_add.png | Bin 45957 -> 35711 bytes 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/user/jupyter_extension_user.md b/docs/user/jupyter_extension_user.md index 77b4ef52ee7..8703b67a965 100644 --- a/docs/user/jupyter_extension_user.md +++ b/docs/user/jupyter_extension_user.md @@ -9,9 +9,7 @@ The **Rucio JupyterLab Extension** brings scientific data management directly in ## How It Works -The extension integrates Rucio's data discovery and management capabilities into JupyterLab through a sidebar interface. When you spawn a JupyterLab session, you can choose to activate the extension and configure which experiment's Rucio cluster to connect to, as well as select a dedicated Rucio Storage Element (RSE) to be used as local storage for files. - -The extension leverages the existing Rucio Storage Elements (RSEs) provided by your experiment, typically available at Tier-0 infrastructure facilities. +The **Rucio Jupyterlab Extension** integrates Rucio's data discovery and management capabilities into JupyterLab through a sidebar interface. The extension supports interaction with many Rucio instances. A Rucio Storage Element (RSE) for each instance can be chosen to serve as the destination for local file storage, leveraging the existing RSEs provided by your experiment. From the JupyterLab sidebar, you can: - **Browse Rucio datasets** by scope and name @@ -132,21 +130,22 @@ Once data becomes available on your session's RSE: 1. **Open a notebook** in your JupyterLab session 2. An **Add to notebook** link appears in the extension sidebar -3. Click the link to add the dataset to your notebook -4. **Provide a variable name** for the dataset (e.g., `my_data`) +3. Click the link to add the data to your notebook +4. **Provide a variable name** for the data (e.g., `my_data`) 5. Click **Confirm** -6. A new variable containing the path to the dataset is automatically added to your notebook +6. A new variable containing the path to the data is automatically added to your notebook ![image](/img/rucio_jupyter_add.png) -The variable will contain the local file path where the data is available, allowing you to use it directly in your analysis code: +The variable will contain the local file path where the data is available. It will be a single string with the path, in case of files, or a list of paths, in case of datasets and containers. This allows you to use the data directly in your analysis code: ```python # After adding data via the extension import numpy as np -# my_data is now a variable containing the path to your dataset -with open(my_data, 'r') as f: +# my_data is now a variable containing the local paths to the files in the data +# my_data = ["/tmp/rucio_xrd1/test/80/25/file1"] +with open(my_data[0], 'r') as f: data = np.load(f) ``` diff --git a/website/static/img/rucio_jupyter_add.png b/website/static/img/rucio_jupyter_add.png index d619c23a1bf0220880eb1f593a2efc5bfe2e4ed6..ff588d201f233b91329179340d0568c40ce61907 100644 GIT binary patch literal 35711 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 Date: Tue, 16 Dec 2025 13:47:23 +0100 Subject: [PATCH 3/5] Review jupyterlab extension operator docs --- docs/operator/jupyter_extension_operator.md | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/docs/operator/jupyter_extension_operator.md b/docs/operator/jupyter_extension_operator.md index c4783aeb645..8e92e112344 100644 --- a/docs/operator/jupyter_extension_operator.md +++ b/docs/operator/jupyter_extension_operator.md @@ -45,17 +45,7 @@ In this mode, files are transferred by Rucio to a storage mounted to the Jupyter **Requirements:** - At least one Rucio instance -- A storage system attached to the JupyterLab installation via FUSE (Filesystem in Userspace): - - The storage must be mounted on the host machine or Kubernetes nodes where JupyterLab pods run - - Common FUSE implementations include: - - **EOS**: CERN's disk-based storage system (via [`eos fuse mount`](https://eos-docs.web.cern.ch/configuration/fuse.html)) - - **CephFS**: Distributed filesystem (via [`ceph-fuse`](https://docs.ceph.com/en/latest/man/8/ceph-fuse/)) - - **XRootD**: High-performance data access protocol (via [`xrootdfs`](https://manpages.debian.org/testing/xrootd-fuse/xrootdfs.1.en.html)) - - The mounted storage must be registered as a Rucio Storage Element (RSE) in your Rucio instance - - Should be shared among multiple users with read permissions for all users - - It's recommended that quotas be disabled, as the extension does not handle quota errors gracefully - - For mounting examples, see the [ESCAPE VRE infrastructure documentation](https://github.com/vre-hub/vre/tree/master/infrastructure) +- A POSIX-compliant filesystem mount attached to the JupyterLab server registered as a Rucio Storage Element (RSE) in your Rucio instance **Configuration Parameters:** - `destination_rse`: The name of the Rucio Storage Element mounted to the JupyterLab server From 34962588db0c5f2ee64089e851f072a7eb5916be Mon Sep 17 00:00:00 2001 From: Francesc Torradeflot Date: Tue, 16 Dec 2025 23:15:55 +0100 Subject: [PATCH 4/5] Automate retrieval of jupyterlab extension configuration from main repo --- .gitignore | 1 + docs/operator/jupyter_extension_operator.md | 398 +------------------- website/docusaurus.config.js | 26 +- website/package.json | 1 + 4 files changed, 29 insertions(+), 397 deletions(-) diff --git a/.gitignore b/.gitignore index 38a947a4865..c7c5fd8e89b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # Exclude specific directories in /docs/ /docs/bin /docs/release-notes/ +/docs/_jupyterlab-extension/ # Exclude all directories in /tools/run_in_docker/ /tools/run_in_docker/*/ diff --git a/docs/operator/jupyter_extension_operator.md b/docs/operator/jupyter_extension_operator.md index 8e92e112344..fba8aaebabd 100644 --- a/docs/operator/jupyter_extension_operator.md +++ b/docs/operator/jupyter_extension_operator.md @@ -35,401 +35,7 @@ For JupyterLab 3.x, please use the latest supported version. pip install rucio-jupyterlab==0.10.0 ``` -## Operation Modes -The extension supports two distinct operation modes, which determine how files are made available to users. +import RemoteReadme from '../_jupyterlab-extension/CONFIGURATION.md'; -### Replica Mode - -In this mode, files are transferred by Rucio to a storage mounted to the JupyterLab server. This is the recommended mode for shared environments. - -**Requirements:** -- At least one Rucio instance -- A POSIX-compliant filesystem mount attached to the JupyterLab server registered as a Rucio Storage Element (RSE) in your Rucio instance - -**Configuration Parameters:** -- `destination_rse`: The name of the Rucio Storage Element mounted to the JupyterLab server -- `rse_mount_path`: The base path where the RSE is mounted -- `path_begins_at`: Index indicating which part of the PFN should be appended to the mount path (defaults to 0) -- `replication_rule_lifetime_days`: Optional lifetime for replication rules in days - -### Download Mode - -In this mode, the extension downloads files directly to the user's home directory or another local storage location. This mode is useful when your JupyterLab installation does not have a Rucio Storage Element (RSE) mounted. - -**Requirements:** -- At least one Rucio instance -- Network access to Rucio Storage Elements (RSE) from the notebook server to download files -- Sufficient local storage space available in the user directory for downloaded files - -## Configuration - -The extension can be configured locally or remotely via a JSON configuration file. - -### Configuration File Location - -The extension is configured via a `.json` file, usually located in `$HOME/.jupyter/` and named `jupyter_server_config.json`. This file must be present before the Jupyter server session starts and can be added via: -- Jupyter `before-notebook.d` hooks -- Docker `CMD` instruction or entrypoint scripts - -### Base Local Configuration - -In your Jupyter configuration (e.g., `~/.jupyter/jupyter_server_config.json`), add the following snippet for a basic setup: - -```json -{ - "RucioConfig": { - "instances": [ - { - "name": "experiment.cern.ch", - "display_name": "Experiment", - "rucio_base_url": "https://rucio", - "rucio_auth_url": "https://rucio", - "rucio_ca_cert": "/path/to/rucio_ca.pem", - "destination_rse": "XRD1-EOS", - "rse_mount_path": "/eos/rucio", - "path_begins_at": 4, - "mode": "replica" - } - ] - } -} -``` - -### Advanced Local Configuration - -For production environments with multiple instances and advanced features: - -```json -{ - "RucioConfig": { - "instances": [ - { - "name": "experiment1.cern.ch", - "display_name": "Experiment1", - "rucio_base_url": "https://rucio1", - "rucio_auth_url": "https://rucio1", - "rucio_ca_cert": "/path/to/rucio_ca.pem", - "site_name": "CERN", - "vo": "experiment1-vo", - "mode": "replica", - "wildcard_enabled": true, - "oidc_auth": "env", - "oidc_env_name": "RUCIO_ACCESS_TOKEN1" - }, - { - "name": "experiment2.cern.ch", - "display_name": "Experiment2", - "rucio_base_url": "https://rucio2", - "rucio_auth_url": "https://rucio2", - "rucio_ca_cert": "/path/to/rucio_ca.pem", - "site_name": "CERN", - "vo": "experiment2-vo", - "mode": "replica", - "wildcard_enabled": true, - "oidc_auth": "env", - "oidc_env_name": "RUCIO_ACCESS_TOKEN2" - } - ], - "default_instance": "experiment1.cern.ch", - "default_auth_type": "oidc", - "log_level": "debug" - } -} -``` - -### Remote Configuration - -To use remote configuration, define instances locally with a reference to an external configuration file: - -```json -{ - "RucioConfig": { - "instances": [ - { - "name": "experiment.cern.ch", - "display_name": "Experiment", - "$url": "https://url-to-rucio-configuration/config.json" - } - ] - } -} -``` - -In the JSON file pointed to by `$url`, use a configuration similar to: - -```json -{ - "rucio_base_url": "https://rucio", - "destination_rse": "XRD1-EOS", - "rucio_auth_url": "https://rucio", - "rucio_ca_cert": "/path/to/rucio_ca.pem", - "rse_mount_path": "/eos/rucio", - "path_begins_at": 4, - "mode": "replica", - ... -} -``` - -**Note:** Attributes `name`, `display_name`, and `mode` must be defined locally (either in the configuration file or as environment variables). If an attribute is defined in both local and remote configuration, the local one takes precedence. - -For a complete list of configuration parameters, see the next section or the [Rucio JupyterLab Extension GitHub repository](https://github.com/rucio/jupyterlab-extension/blob/master/CONFIGURATION.md) - -## Configuration Parameters - -### Instance Configuration - -#### Name - `name` -A unique machine-readable identifier for the Rucio instance. It is recommended to use FQDN (Fully Qualified Domain Name). Must be declared locally in the configuration file or set via the `RUCIO_NAME` environment variable. - -Example: `atlas.cern.ch`, `cms.cern.ch` - -#### Display Name - `display_name` -A user-friendly name displayed in the extension interface. Must be declared locally in the configuration file or set via the `RUCIO_DISPLAY_NAME` environment variable. - -Example: `ATLAS`, `CMS` - -#### Mode - `mode` -The operation mode of the extension. Must be declared locally in the configuration file or set via the `RUCIO_MODE` environment variable. - -Allowed values: -- `replica`: Files are transferred to a mounted storage -- `download`: Files are downloaded to user directory - -#### Rucio Base URL - `rucio_base_url` -Base URL for the Rucio instance accessible from the JupyterLab server, **without trailing slash**. - -Example: `https://rucio` - -#### Rucio Auth URL - `rucio_auth_url` -Base URL for the Rucio authentication service (if separate) accessible from the JupyterLab server, **without trailing slash**. - -Example: `https://rucio-auth` - -#### Rucio CA Certificate File Path - `rucio_ca_cert` -Path to Rucio server certificate file, accessible via filesystem mount. Optional in Replica mode, mandatory in Download mode. - -Example: `/opt/rucio/rucio_ca.pem` - -#### App ID - `app_id` -Rucio App ID. Optional. - -Example: `swan` - -#### Site Name - `site_name` -Site name of the JupyterLab instance. Optional. Allows Rucio to determine whether to serve a proxied PFN or not. - -Example: `ATLAS` - -### Virtual Organizations - -#### VO Name - `vo` -VO (Virtual Organization) of the instance. Optional, for use in multi-VO installations only. If VOMS is enabled, this value will be supplied as `--voms` option when invoking `voms-proxy-init`. - -Example: `def` - -#### VOMS Enabled - `voms_enabled` -Boolean flag to enable VOMS proxy certificate generation. When set to `true`, the extension uses `voms-proxy-init` to generate a proxy certificate for authenticated RSE access. - -Default: `false` - -#### VOMS `certdir` Path - `voms_certdir_path` -If VOMS is enabled, sets the `--certdir` option for `voms-proxy-init`. Refer to `voms-proxy-init` documentation. - -Example: `/etc/grid-security/certificates` - -#### VOMS `vomses` Path - `voms_vomses_path` -If VOMS is enabled, sets the `--vomses` option for `voms-proxy-init`. Refer to `voms-proxy-init` documentation. - -Example: `/etc/grid-security/vomses` - -**WARNING:** Earlier versions of `voms-proxy-init` do not support the `--vomses` option. In that case, this option must be omitted. - -### Storage Elements and Search - -#### Destination RSE - `destination_rse` -The name of the Rucio Storage Element mounted to the JupyterLab server. Mandatory in Replica mode. - -Example: `SWAN-EOS` - -#### RSE Mount Path - `rse_mount_path` -The base path where the RSE is mounted to the server. Mandatory in Replica mode. - -Example: `/eos/rucio` - -#### File Path Starting Index - `path_begins_at` -This configuration indicates which part of the PFN (Physical File Name) should be appended to the mount path. Only applicable in Replica mode. Defaults to `0`. - -**Example:** For a PFN of `root://xrd1:1094//rucio/test/49/ad/f1.txt` and mount path `/eos/rucio`: -- `path_begins_at: 1` means start from the 2nd slash in the PFN -- Resulting path: `/eos/rucio/test/49/ad/f1.txt` - -#### Replication Rule Lifetime (in days) - `replication_rule_lifetime_days` -Replication rule lifetime in days. Optional, only applicable in Replica mode. - -Example: `365` - -#### Wildcard Search Enabled - `wildcard_enabled` -Boolean flag to enable wildcard DID (Dataset Identifier) search. When enabled, users can search using wildcard patterns like `scope:*`. - -Default: `false` - -### Authentication Configuration - -#### OpenID Connect Auth Source - `oidc_auth` -Specifies where the extension retrieves the OIDC token. Optional. - -Allowed values: -- `file`: Read token from a file -- `env`: Read token from an environment variable - -#### OpenID Connect Token Filename - `oidc_file_name` -Specifies an absolute path to a file containing the OIDC access token. Required if `oidc_auth` is set to `file`. - -Example: `/var/run/secrets/oidc_token` - -#### OpenID Connect Token Environment Variable Name - `oidc_env_name` -Specifies the environment variable name containing the OIDC access token. Required if `oidc_auth` is set to `env`. - -Example: `RUCIO_ACCESS_TOKEN` - -**IMPORTANT:** The `oidc_auth` parameter and either `oidc_file_name` or `oidc_env_name` are necessary if OIDC token authentication is to be used. - -### Global Configuration - -#### Default Instance - `default_instance` -The instance to be pre-selected in the settings menu of the extension. - -Example: `atlas.cern.ch` - -#### Default Authentication Type - `default_auth_type` -Default authentication method. Possible values: -- `oidc`: OpenID Connect tokens -- `x509`: X.509 user certificate -- `x509_proxy`: X.509 proxy certificate -- `userpass`: Username and password - -#### Logging Level - `log_level` -Specifies the verbosity of logs. Possible values: -- `debug`: Most verbose -- `info`: Informational messages -- `warning`: Warnings and errors -- `error`: Errors only - -## IPython Kernel Extension - -To allow users to access file paths from within notebooks, the kernel extension must be enabled. - -### Manual Activation - -To enable the kernel extension inside a notebook, use the IPython magic: - -```python -%load_ext rucio_jupyterlab.kernels.ipython -``` - -### Automatic Activation - -To enable it by default for all users, add the following to the IPython configuration (e.g., `~/.ipython/profile_default/ipython_kernel_config.py`): - -```python -c.IPKernelApp.extensions = ['rucio_jupyterlab.kernels.ipython'] -``` - -## OpenID Connect Authentication Setup - -OIDC authentication requires special configuration at the operator level, as it cannot be configured by users directly. - -### Important Notes - -- Users will only see the "OpenID Connect" authentication option if OIDC is properly configured by operators -- The extension does not handle user authentication directly; the OIDC token must be obtained through an external mechanism -- In multi-user setups with JupyterHub, operators must make the OIDC token accessible via file or environment variable -- JupyterHub must implement periodic token refresh to prevent expiration during active sessions - -### Configuring JupyterHub with OIDC - -#### Single User Dockerfile and Variables - -The single-user container image must include: -1. The Rucio JupyterLab extension installed -2. OIDC token environment variables properly configured -3. A configuration script (like `configure.py`) to write environment variables to Jupyter configuration - -#### JupyterHub Helm Chart Configuration - -For Kubernetes deployments using the [Zero to JupyterHub](https://z2jh.jupyter.org/en/stable/) Helm Chart: - -**1. Set the custom single-user image:** - -```yaml -singleuser: - image: : -``` - -**2. Add a custom authenticator to `hub.extraConfig`:** - -The `hub.extraConfig` section allows you to inject custom Python code into the JupyterHub configuration. Here, we define a custom authenticator class that handles OIDC token exchange with Rucio. This authenticator intercepts the user authentication flow, exchanges the OIDC token for a Rucio-specific token, and injects it into the spawned user environment. - -See an example implementation in the [ESCAPE VRE Helm Chart](https://github.com/vre-hub/vre/blob/527982d0a9beeb6098dbb9f73e16ff65f4796b88/infrastructure/cluster/flux/jhub/jhub-release.yaml#L71). - - -**3. Add authenticator configuration to `hub.config`:** - -The `hub.config` section configures the custom `RucioAuthenticator` class defined above. These settings specify the OIDC provider endpoints and client credentials needed for authentication. The authenticator uses these values to communicate with your identity provider and perform token exchanges. - -See configuration example in the [ESCAPE VRE Helm Chart](https://github.com/vre-hub/vre/blob/527982d0a9beeb6098dbb9f73e16ff65f4796b88/infrastructure/cluster/flux/jhub/jhub-release.yaml#L55). - -```yaml -hub: - config: - RucioAuthenticator: - client_id: - client_secret: - authorize_url: - token_url: - userdata_url: - username_key: preferred_username - scope: - - openid - - profile - - email -``` - -**4. Add extension parameters to `singleuser.extraEnv`:** - -The `singleuser.extraEnv` section defines environment variables that will be injected into each user's JupyterLab pod. The Rucio JupyterLab extension reads these variables to configure itself automatically. This approach is particularly useful in containerized environments where configuration via environment variables is preferred over static configuration files. - -See environment configuration in the [ESCAPE VRE Helm Chart](https://github.com/vre-hub/vre/blob/527982d0a9beeb6098dbb9f73e16ff65f4796b88/infrastructure/cluster/flux/jhub/jhub-release.yaml#L211). - -```yaml -singleuser: - extraEnv: - RUCIO_MODE: "replica" - RUCIO_WILDCARD_ENABLED: "1" - RUCIO_BASE_URL: "" - RUCIO_AUTH_URL: "" - RUCIO_WEBUI_URL: "" - RUCIO_DISPLAY_NAME: "" - RUCIO_NAME: "" - RUCIO_SITE_NAME: "" - RUCIO_OIDC_AUTH: "env" - RUCIO_OIDC_ENV_NAME: "RUCIO_ACCESS_TOKEN" - RUCIO_DEFAULT_AUTH_TYPE: "oidc" - RUCIO_LOG_LEVEL: "warning" - RUCIO_OAUTH_ID: "" - RUCIO_DEFAULT_INSTANCE: "" - RUCIO_DESTINATION_RSE: "EOS RSE" - RUCIO_RSE_MOUNT_PATH: "/eos/eos-rse" - RUCIO_PATH_BEGINS_AT: "4" - RUCIO_CA_CERT: "" - OAUTH2_TOKEN: "FILE:/tmp/eos_oauth.token" -``` - -**5. Build the Docker image and install the Helm Chart with the specified values.** - -*Note: This configuration works in replica mode and maps an EOS RSE as the target RSE, FUSE mounted on the JupyterHub nodes.* - -## Further Reading - -For more detailed information on the Rucio JupyterLab Extension, visit the [rucio/jupyterlab-extension](https://github.com/rucio/jupyterlab-extension) GitHub repository. + diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 0783a33f115..183d3e95295 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -48,7 +48,31 @@ module.exports={ } ] ], - "plugins": [], + "plugins": [ + [ + "docusaurus-plugin-remote-content", + { + name: "jupyterlab-extension", + sourceBaseUrl: "https://raw.githubusercontent.com/rucio/jupyterlab-extension/refs/heads/master/", + outDir: "../docs/_jupyterlab-extension", + documents: ["CONFIGURATION.md"], + modifyContent: (filename, content) => { + // Remove first-level headings from the fetched markdown files + // Define a regex to match H1 lines + const h1Regex = /^#\s+(.*)$/m; + + // Remove the H1 line from the content + const cleanContent = content.replace(h1Regex, ''); + + // Return the modified content + return { + filename, + content: `${cleanContent}` + }; + }, + }, + ], + ], "themeConfig": { "navbar": { "title": "Rucio Documentation", diff --git a/website/package.json b/website/package.json index c751d6604dc..accd786eb85 100644 --- a/website/package.json +++ b/website/package.json @@ -18,6 +18,7 @@ "@docusaurus/theme-mermaid": "3.8.1", "@mdx-js/react": "^3.0.0", "clsx": "^2.1.1", + "docusaurus-plugin-remote-content": "^4.0.0", "react": "^19.1.0", "react-dom": "^19.1.0" } From 0e9e5977cd58bb3c4204d296abf8650742fccbf6 Mon Sep 17 00:00:00 2001 From: Francesc Torradeflot Date: Tue, 16 Dec 2025 23:40:08 +0100 Subject: [PATCH 5/5] Fix broken relative links in jupyterlab extension configuration --- website/docusaurus.config.js | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 183d3e95295..5f7b9585095 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -62,7 +62,34 @@ module.exports={ const h1Regex = /^#\s+(.*)$/m; // Remove the H1 line from the content - const cleanContent = content.replace(h1Regex, ''); + let cleanContent = content.replace(h1Regex, ''); + + const remoteLinkBase = 'https://github.com/rucio/jupyterlab-extension/blob/master'; + + // 3. Fix Relative Links + // Match: [Link Text](URL) + // We use a callback function to check the URL before replacing + cleanContent = cleanContent.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, text, url) => { + + // CASE A: Ignore absolute links (http/https), anchors (#), and emails (mailto) + if (url.startsWith('http') || url.startsWith('#') || url.startsWith('mailto:')) { + return match; + } + + // CASE B: Handle specific relative prefixes like './' + // This cleans up the path so you don't get "blob/master/./doc/file" + let cleanPath = url; + if (cleanPath.startsWith('./')) { + cleanPath = cleanPath.substring(2); + } + + // Construct the new absolute URL + // Ensure there is no double slash if your base ends with / + const separator = remoteLinkBase.endsWith('/') ? '' : '/'; + const newUrl = `${remoteLinkBase}${separator}${cleanPath}`; + + return `[${text}](${newUrl})`; + }); // Return the modified content return {