Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
dfd108e
Create DatasetDisplay component
guerler Oct 18, 2025
a662f6a
Switch from iframe to div
guerler Oct 18, 2025
5c4046c
Move template content to display component
guerler Oct 18, 2025
700bd07
Switch over to only parsing the chunk
guerler Oct 18, 2025
29e04af
Remove tabular_chunked.mako
guerler Oct 19, 2025
6abfe1a
Remove large_file and binary_file makos
guerler Oct 19, 2025
a058c73
Begin to handle dataset details in dataset display component
guerler Oct 19, 2025
52433de
Add links to download and display complete datasets
guerler Oct 19, 2025
6e46bc8
Collect dataset details from store
guerler Oct 19, 2025
fcedd69
Only show download option for large datasets
guerler Oct 19, 2025
b542078
Remove Tabular Chunked View from bundle entries
guerler Oct 19, 2025
9e77d78
Prepare checking datatype for warning message
guerler Oct 19, 2025
c8f00cc
Adjust truncation messages
guerler Oct 19, 2025
3dc479f
Remove unused constant and default props
guerler Oct 19, 2025
676fbc8
Add truncated peek size to header
guerler Oct 19, 2025
d1ef9cb
Update dataset display
guerler Oct 19, 2025
cbfa383
Add overflow
guerler Oct 19, 2025
8b7a559
Adjust overflow behavior
guerler Oct 19, 2025
800f2da
Refactor nested props in Tabular Chunked View
guerler Oct 19, 2025
1d52a0c
Allow overflow-auto
guerler Oct 19, 2025
c4207cd
Add error message
guerler Oct 20, 2025
e833eaa
Extract actual error message
guerler Oct 20, 2025
d2b8e3f
Move sanitization message to dataset display
guerler Oct 20, 2025
7da711a
Add watcher for dataset id
guerler Oct 20, 2025
b20ef56
Disable frame switching
guerler Oct 20, 2025
4daa0b9
Refactor sanatize message handling
guerler Oct 20, 2025
899e398
Sanitize content
guerler Oct 20, 2025
51ab7d3
Rely on content-type, use consistent header variable names
guerler Oct 20, 2025
e9e7be6
Move tabular chunked view component to datasets, its not a visualization
guerler Oct 20, 2025
48030a4
Restore iframe, inject content
guerler Oct 20, 2025
9a71953
Restore iframe switching
guerler Oct 20, 2025
5a2baf6
Avoid injection, use src to load html pages
guerler Oct 20, 2025
3d3801d
Use preview url
guerler Oct 20, 2025
ddd3799
Remove unused content type ref
guerler Oct 20, 2025
4a012d2
Emit load
guerler Oct 20, 2025
72e03e4
Add content chunked to header
guerler Oct 20, 2025
6fd2687
Load first chunk in tabular chunked view
guerler Oct 20, 2025
2fb7089
Determine overflow on tab not parent level
guerler Oct 20, 2025
025d334
Switch from VisualizationFrame with backend mako to client Visualizat…
guerler Oct 22, 2025
94ce256
Switch to VisualizationWrapper
guerler Oct 22, 2025
869ddb2
Dump legacy saved visualization controller endpoint
guerler Oct 22, 2025
fd2b31c
Remove visualization rendering controller
guerler Oct 22, 2025
9bd5caa
Remove rendered_saved from visualization framework
guerler Oct 22, 2025
3d6d968
Remove server side rendering from visualization framework
guerler Oct 22, 2025
b0a0a0f
Remove mako based visualization layer from visualization framework
guerler Oct 22, 2025
657ce58
Remove visualization mako
guerler Oct 22, 2025
fba19f5
Keep href for now
guerler Oct 22, 2025
ca6a9df
Remove parsing for now unused mako, and html entry types
guerler Oct 22, 2025
831a86f
Remove unused target iframe name from visualization config
guerler Oct 22, 2025
ae1eb72
Remove legacy visualization controller endpoint
guerler Oct 22, 2025
76c5521
Fix path, used to point at legacy controller
guerler Oct 22, 2025
793e528
No need to parse template directory anymore to registry
guerler Oct 22, 2025
4ff8efd
Remove unused imports
guerler Oct 22, 2025
de7886e
Remove unused target iframe specifier
guerler Oct 22, 2025
677f8e4
Remove unused import
guerler Oct 22, 2025
37e7304
Adjust unit test
guerler Oct 22, 2025
e922a81
Remove unused plugin options, simplify handling
guerler Oct 22, 2025
93d5828
Remove unused import
guerler Oct 22, 2025
f023a43
Fix linting
guerler Oct 22, 2025
401f937
Remove unused regex import
guerler Oct 22, 2025
a761d97
Remove unused escape import
guerler Oct 22, 2025
f6dd601
Adjust test cases
guerler Oct 22, 2025
c26b2f1
Remove unused imports
guerler Oct 22, 2025
5250452
Prep consolidation and refactoring of options
guerler Oct 22, 2025
d6db2ca
Add id galaxy_visualization to visualization frame
guerler Oct 22, 2025
6f4a3ba
Add full height class, prep for refactoring
guerler Oct 23, 2025
b274a14
Rename frame to display
guerler Oct 23, 2025
e61715b
Add visualization frame component
guerler Oct 23, 2025
c89a7b7
Remove full height option from embedded visualization container
guerler Oct 23, 2025
c289eff
Reuse Visualization Frame component in Markdown Wrapper
guerler Oct 23, 2025
4471db7
Move visualization wrapper to markdown components
guerler Oct 23, 2025
e174f25
Update nora to 1.2.2
guerler Oct 23, 2025
14915e5
Update mvpapp to 0.0.2
guerler Oct 23, 2025
f1cd08c
Update chiraviz, layout fixes
guerler Oct 23, 2025
1e26e13
Update hivtrace
guerler Oct 23, 2025
9427e7b
Remove unused param options
guerler Oct 23, 2025
0aad49e
Remove unused variable
guerler Oct 24, 2025
a8ae60e
Add ghost virtual webserver for archived htmls
guerler Aug 10, 2025
b85c43d
Add ghost as default viewer for qzv files
guerler Aug 10, 2025
5d42969
Update package version
guerler Aug 10, 2025
0c91cbb
Add blank.html for injected iframe content
guerler Oct 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions client/src/api/schema/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23193,11 +23193,6 @@ export interface components {
specs?: {
[key: string]: unknown;
} | null;
/**
* Target
* @description The target of the plugin.
*/
target: string;
/**
* Title
* @description The title of the plugin.
Expand Down
16 changes: 1 addition & 15 deletions client/src/bundleEntries.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
/**
* The list of horrible globals we expose on window.bundleEntries.
*
* Everything that is exposed on this global variable is something that the python templates
* require for their hardcoded initializations. These objects are going to have to continue
* to exist until such time as we replace the overall application with a Vue component which
* will handle initializations for components individually.
*
* The list of globals we expose on window.bundleEntries.
*/
import { replaceChildrenWithComponent } from "utils/mountVueComponent";

import TabularChunkedView from "components/Visualizations/Tabular/TabularChunkedView.vue";

// legacy/grid_base.mako
export { default as LegacyGridView } from "legacy/grid/grid-view";

// webapps/reports/run_stats.mako
export { create_chart, create_histogram } from "reports/run_stats";

// webapps/galaxy/dataset/{ display | tabular_chunked }.mako
export const createTabularDatasetChunkedView = (options) => {
return replaceChildrenWithComponent(options.parent_elt, TabularChunkedView, { options });
};
101 changes: 101 additions & 0 deletions client/src/components/Dataset/DatasetDisplay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { storeToRefs } from "pinia";
import { computed, ref, watch } from "vue";

import { useDatasetStore } from "@/stores/datasetStore";
import { useUserStore } from "@/stores/userStore";
import { withPrefix } from "@/utils/redirect";
import { errorMessageAsString } from "@/utils/simple-error";
import { bytesToString } from "@/utils/utils";

import Alert from "@/components/Alert.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
import CenterFrame from "@/entry/analysis/modules/CenterFrame.vue";
import TabularChunkedView from "components/Dataset/Tabular/TabularChunkedView.vue";

interface Props {
datasetId: string;
isBinary: boolean;
}

const { getDataset, isLoadingDataset } = useDatasetStore();

const props = defineProps<Props>();

const contentTruncated = ref();
const contentChunked = ref();
const errorMessage = ref();
const sanitizedJobImported = ref();
const sanitizedToolId = ref();

const { isAdmin } = storeToRefs(useUserStore());

const dataset = computed(() => getDataset(props.datasetId));
const datasetUrl = computed(() => `/datasets/${props.datasetId}/display`);
const downloadUrl = computed(() => withPrefix(`${datasetUrl.value}?to_ext=${dataset.value?.file_ext}`));
const isLoading = computed(() => isLoadingDataset(props.datasetId));
const previewUrl = computed(() => withPrefix(`${datasetUrl.value}?preview=True`));

const sanitizedMessage = computed(() => {
const plainText = "Contents are shown as plain text.";
if (sanitizedJobImported.value) {
return `Dataset has been imported. ${plainText}`;
} else if (sanitizedToolId.value) {
return `Dataset created by a tool that is not known to create safe HTML. ${plainText}`;
}
return undefined;
});

watch(
() => props.datasetId,
async () => {
try {
const { headers } = await fetch(previewUrl.value, { method: "HEAD" });
contentChunked.value = headers.get("x-content-chunked");
contentTruncated.value = headers.get("x-content-truncated");
sanitizedJobImported.value = headers.get("x-sanitized-job-imported");
sanitizedToolId.value = headers.get("x-sanitized-tool-id");
errorMessage.value = "";
} catch (e) {
errorMessage.value = errorMessageAsString(e);
console.error(e);
}
},
{ immediate: true },
);
</script>

<template>
<BAlert v-if="errorMessage" variant="danger" show>
{{ errorMessage }}
</BAlert>
<LoadingSpan v-else-if="isLoading || !dataset" message="Loading dataset content" />
<div v-else class="h-100">
<Alert v-if="sanitizedMessage" :dismissible="true" variant="warning" data-description="sanitization warning">
{{ sanitizedMessage }}
<span v-if="isAdmin && sanitizedToolId">
<br />
<router-link data-description="allowlist link" to="/admin/sanitize_allow">Review Allowlist</router-link>
if outputs of {{ sanitizedToolId }} are trusted and should be shown as HTML.
</span>
</Alert>
<div v-if="dataset.deleted" id="deleted-data-message" class="errormessagelarge">
You are viewing a deleted dataset.
</div>
<TabularChunkedView v-if="contentChunked" :options="dataset" />
<div v-else class="h-100">
<div v-if="isBinary">
This is a binary (or unknown to Galaxy) dataset of size {{ bytesToString(dataset.file_size) }}. Preview
is not implemented for this filetype. Displaying as ASCII text.
</div>
<div v-if="contentTruncated" class="warningmessagelarge">
<div>
This dataset is large and only the first {{ bytesToString(contentTruncated) }} is shown below.
</div>
<a :href="downloadUrl">Download</a>
</div>
<CenterFrame :src="previewUrl" @load="$emit('load')" />
</div>
</div>
</template>
50 changes: 19 additions & 31 deletions client/src/components/Dataset/DatasetView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import { bytesToString } from "@/utils/utils";
import DatasetError from "../DatasetInformation/DatasetError.vue";
import LoadingSpan from "../LoadingSpan.vue";
import DatasetAsImage from "./DatasetAsImage/DatasetAsImage.vue";
import DatasetDisplay from "./DatasetDisplay.vue";
import DatasetState from "./DatasetState.vue";
import Heading from "@/components/Common/Heading.vue";
import DatasetAttributes from "@/components/DatasetInformation/DatasetAttributes.vue";
import DatasetDetails from "@/components/DatasetInformation/DatasetDetails.vue";
import VisualizationsList from "@/components/Visualizations/Index.vue";
import VisualizationFrame from "@/components/Visualizations/VisualizationFrame.vue";
import CenterFrame from "@/entry/analysis/modules/CenterFrame.vue";
import VisualizationDisplay from "@/components/Visualizations/VisualizationDisplay.vue";

const datasetStore = useDatasetStore();
const datatypeStore = useDatatypeStore();
Expand Down Expand Up @@ -61,6 +61,13 @@ const downloadUrl = computed(() => withPrefix(`/datasets/${props.datasetId}/disp
const preferredVisualization = computed(
() => dataset.value && datatypeStore.getPreferredVisualization(dataset.value.file_ext),
);
const isBinaryDataset = computed(() => {
if (!dataset.value?.file_ext || !datatypesMapperStore.datatypesMapper) {
return false;
}
return datatypesMapperStore.datatypesMapper.isSubTypeOfAny(dataset.value.file_ext, ["galaxy.datatypes.binary"]);
});

const isImageDataset = computed(() => {
if (!dataset.value?.file_ext || !datatypesMapperStore.datatypesMapper) {
return false;
Expand Down Expand Up @@ -93,7 +100,7 @@ watch(

<template>
<LoadingSpan v-if="isLoading || !dataset" message="Loading dataset details" />
<div v-else class="dataset-view d-flex flex-column h-100">
<div v-else class="dataset-view d-flex flex-column">
<header v-if="!displayOnly" :key="`dataset-header-${dataset.id}`" class="dataset-header flex-shrink-0">
<div class="d-flex">
<Heading
Expand Down Expand Up @@ -181,18 +188,13 @@ watch(
<FontAwesomeIcon :icon="faBug" class="mr-1" /> Error
</BNavItem>
</BNav>
<div v-if="tab === 'preview'" class="h-100">
<VisualizationFrame
<div v-if="tab === 'preview'" class="tab-content-panel">
<VisualizationDisplay
v-if="preferredVisualization"
:dataset-id="datasetId"
:visualization="preferredVisualization"
@load="iframeLoading = false" />
<CenterFrame
v-else-if="isPdfDataset"
:src="`/datasets/${datasetId}/display/?preview=True`"
:is-preview="true"
@load="iframeLoading = false" />
<div v-else-if="isAutoDownloadType" class="auto-download-message p-4">
<div v-else-if="isAutoDownloadType && !isPdfDataset" class="auto-download-message p-4">
<div class="alert alert-info">
<h4>Download Required</h4>
<p>This file type ({{ dataset.file_ext }}) will download automatically when accessed directly.</p>
Expand All @@ -203,23 +205,14 @@ watch(
</div>
</div>
<DatasetAsImage
v-else-if="isImageDataset"
v-else-if="isImageDataset && !isPdfDataset"
:history-dataset-id="datasetId"
:allow-size-toggle="true"
class="p-3" />
<CenterFrame
v-else
:src="`/datasets/${datasetId}/display/?preview=True`"
:is-preview="true"
@load="iframeLoading = false" />
<DatasetDisplay v-else :dataset-id="datasetId" :is-binary="isBinaryDataset" @load="iframeLoading = false" />
</div>
<div v-else-if="tab === 'raw'" class="h-100">
<CenterFrame
v-if="isPdfDataset"
:src="`/datasets/${datasetId}/display/?preview=True`"
:is-preview="true"
@load="iframeLoading = false" />
<div v-else-if="isAutoDownloadType" class="auto-download-message p-4">
<div v-else-if="tab === 'raw'" class="tab-content-panel">
<div v-if="isAutoDownloadType && !isPdfDataset" class="auto-download-message p-4">
<div class="alert alert-info">
<h4>Download Required</h4>
<p>This file type ({{ dataset.file_ext }}) will download automatically when accessed directly.</p>
Expand All @@ -229,11 +222,7 @@ watch(
</a>
</div>
</div>
<CenterFrame
v-else
:src="`/datasets/${datasetId}/display/?preview=True`"
:is-preview="true"
@load="iframeLoading = false" />
<DatasetDisplay v-else :dataset-id="datasetId" :is-binary="isBinaryDataset" @load="iframeLoading = false" />
</div>
<div v-else-if="tab === 'visualize'" class="tab-content-panel">
<VisualizationsList :dataset-id="datasetId" />
Expand Down Expand Up @@ -318,8 +307,7 @@ watch(
.tab-content-panel {
display: flex;
flex-direction: column;
overflow: hidden;
overflow-y: auto;
overflow: auto;
height: 100%;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,11 @@ interface TabularChunk {

interface TabularChunkedViewProps {
options: {
dataset_config: {
id: string;
file_ext: string;
first_data_chunk: TabularChunk;
metadata_columns: number;
metadata_column_types: string[];
metadata_column_names: string[];
};
id: string;
file_ext: string;
metadata_columns: number;
metadata_column_types: string[];
metadata_column_names: string[];
};
}

Expand All @@ -37,32 +34,32 @@ const tabularData = reactive<{ rows: string[][] }>({
});

const columns = computed(() => {
const columns = Array(props.options.dataset_config.metadata_columns);
const columns = Array(props.options.metadata_columns);
// for each column_name, inject header
if (props.options.dataset_config.metadata_column_names?.length > 0) {
props.options.dataset_config.metadata_column_names.forEach((column_name, index) => {
if (props.options.metadata_column_names?.length > 0) {
props.options.metadata_column_names.forEach((column_name, index) => {
columns[index] = column_name;
});
}
return columns;
});

const columnStyle = computed(() => {
const columnStyle = Array(props.options.dataset_config.metadata_columns);
if (props.options.dataset_config.metadata_column_types?.length > 0) {
props.options.dataset_config.metadata_column_types.forEach((column_type, index) => {
const columnStyle = Array(props.options.metadata_columns);
if (props.options.metadata_column_types?.length > 0) {
props.options.metadata_column_types.forEach((column_type, index) => {
columnStyle[index] = column_type === "str" || column_type === "list" ? "string-align" : "number-align";
});
}
return columnStyle;
});

const delimiter = computed(() => {
return props.options.dataset_config.file_ext === "csv" ? "," : "\t";
return props.options.file_ext === "csv" ? "," : "\t";
});

const chunkUrl = computed(() => {
return `${getAppRoot()}dataset/display?dataset_id=${props.options.dataset_config.id}`;
return `${getAppRoot()}dataset/display?dataset_id=${props.options.id}`;
});

// Loading more data on user scroll to (near) bottom.
Expand Down Expand Up @@ -163,11 +160,8 @@ function nextChunk() {
}

onMounted(() => {
// Render first chunk if available.
if (props.options.dataset_config.first_data_chunk) {
processChunk(props.options.dataset_config.first_data_chunk);
loading.value = false;
}
// Fetch and render first chunk
nextChunk();
});
</script>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ onMounted(async () => {
</script>

<template>
<div aria-labelledby="dataset-attributes-heading">
<div class="dataset-attributes" aria-labelledby="dataset-attributes-heading">
<Heading id="dataset-attributes-heading" h1 separator inline size="md">
{{ localize("Edit Dataset Attributes") }}
</Heading>
Expand Down Expand Up @@ -230,3 +230,10 @@ onMounted(async () => {
</div>
</div>
</template>

<style>
.dataset-attributes {
overflow-x: hidden;
overflow-y: auto;
}
</style>
3 changes: 2 additions & 1 deletion client/src/components/DatasetInformation/DatasetDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ onUnmounted(() => {
display: flex;
flex-direction: column;
gap: 1rem;

overflow-x: hidden;
overflow-y: auto;
.dataset-peek {
word-break: break-all;
}
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/Markdown/Sections/MarkdownGalaxy.vue
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ import ToolStd from "./Elements/ToolStd.vue";
import WorkflowDisplay from "./Elements/Workflow/WorkflowDisplay.vue";
import WorkflowImage from "./Elements/Workflow/WorkflowImage.vue";
import WorkflowLicense from "./Elements/Workflow/WorkflowLicense.vue";
import VisualizationWrapper from "./VisualizationWrapper.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
import VisualizationWrapper from "@/components/Visualizations/VisualizationWrapper.vue";
import WorkflowInvocationInputs from "@/components/WorkflowInvocationState/WorkflowInvocationInputs.vue";
import WorkflowInvocationOutputs from "@/components/WorkflowInvocationState/WorkflowInvocationOutputs.vue";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { parseInput, parseOutput } from "@/components/Markdown/Utilities/parseIn
import { stringify } from "@/components/Markdown/Utilities/stringify";
import { useInvocationStore } from "@/stores/invocationStore";
import VisualizationWrapper from "./VisualizationWrapper.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
import VisualizationWrapper from "@/components/Visualizations/VisualizationWrapper.vue";
const DEFAULT_HEIGHT = 400;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { parseInput, parseOutput } from "@/components/Markdown/Utilities/parseIn
import { getAppRoot } from "@/onload";
import { useInvocationStore } from "@/stores/invocationStore";
import VisualizationWrapper from "./VisualizationWrapper.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";
import VisualizationWrapper from "@/components/Visualizations/VisualizationWrapper.vue";
const DEFAULT_HEIGHT = 400;
Expand Down
Loading
Loading