Any file added via a data: URL (e.g. via make_ids_csv_data_url/make_colors_csv_data_url) gets silently corrupted when the overall config is shared through VitessceConfig.web_app()'s vitessce.io/#?url=data:,... link mechanism (used in tissue-map-tools). The app shows no console error — it just falls back to rendering the landing page, as if no config were present at all.
Root cause
The browser decodes the outer shared link twice before an inner data: URL is ever supposed to be decoded on its own:
URLSearchParams decodes the url hash param to extract the outer data:,{...} link.
fetch() performs its own percent-decoding of a data: URI's payload when actually fetching it.
An inner data: URL (e.g. for a small CSV) is only encoded to survive one decode pass. The second, unrelated pass in step 2 prematurely resolves its %0D/%0A sequences into raw control characters, landing them directly inside the outer JSON string — which then fails JSON.parse() with Bad control character in string literal.
from vitessce import VitessceConfig, make_ids_csv_data_url
vc = VitessceConfig(schema_version="1.0.17", name="Repro")
dataset = vc.add_dataset("Repro")
dataset.add_file(
file_type="obsFeatureMatrix.csv",
url=make_ids_csv_data_url([612, 3351, 4328]),
coordination_values={"obsType": "cell", "featureType": "feature", "featureValueType": "value"},
)
# ... add remaining views/coordination ...
vc.web_app()
Any file added via a data: URL (e.g. via make_ids_csv_data_url/make_colors_csv_data_url) gets silently corrupted when the overall config is shared through VitessceConfig.web_app()'s vitessce.io/#?url=data:,... link mechanism (used in tissue-map-tools). The app shows no console error — it just falls back to rendering the landing page, as if no config were present at all.
Root cause
The browser decodes the outer shared link twice before an inner data: URL is ever supposed to be decoded on its own:
URLSearchParams decodes the url hash param to extract the outer data:,{...} link.
fetch() performs its own percent-decoding of a data: URI's payload when actually fetching it.
An inner data: URL (e.g. for a small CSV) is only encoded to survive one decode pass. The second, unrelated pass in step 2 prematurely resolves its %0D/%0A sequences into raw control characters, landing them directly inside the outer JSON string — which then fails JSON.parse() with Bad control character in string literal.