Skip to content
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
dba1060
Switch from VisualizationFrame with backend mako to client Visualizat…
guerler Oct 22, 2025
ce6f775
Switch to VisualizationWrapper
guerler Oct 22, 2025
5f31037
Dump legacy saved visualization controller endpoint
guerler Oct 22, 2025
a3d47dc
Remove visualization rendering controller
guerler Oct 22, 2025
d672442
Remove rendered_saved from visualization framework
guerler Oct 22, 2025
e80f8d6
Remove server side rendering from visualization framework
guerler Oct 22, 2025
a716dfd
Remove mako based visualization layer from visualization framework
guerler Oct 22, 2025
f177a67
Remove visualization mako
guerler Oct 22, 2025
072a845
Keep href for now
guerler Oct 22, 2025
357253b
Remove parsing for now unused mako, and html entry types
guerler Oct 22, 2025
3a6061b
Remove unused target iframe name from visualization config
guerler Oct 22, 2025
efda303
Remove legacy visualization controller endpoint
guerler Oct 22, 2025
f8b667f
Fix path, used to point at legacy controller
guerler Oct 22, 2025
d2bb6c4
No need to parse template directory anymore to registry
guerler Oct 22, 2025
27674bc
Remove unused imports
guerler Oct 22, 2025
df1af66
Remove unused target iframe specifier
guerler Oct 22, 2025
72fd7e7
Remove unused import
guerler Oct 22, 2025
661ad9d
Adjust unit test
guerler Oct 22, 2025
c881820
Remove unused plugin options, simplify handling
guerler Oct 22, 2025
54d8f5b
Remove unused import
guerler Oct 22, 2025
1a8bf0e
Fix linting
guerler Oct 22, 2025
9438956
Remove unused regex import
guerler Oct 22, 2025
96f539f
Remove unused escape import
guerler Oct 22, 2025
3f73927
Adjust test cases
guerler Oct 22, 2025
3427bdd
Remove unused imports
guerler Oct 22, 2025
f3f7c43
Prep consolidation and refactoring of options
guerler Oct 22, 2025
236d7f0
Add id galaxy_visualization to visualization frame
guerler Oct 22, 2025
10b3956
Add full height class, prep for refactoring
guerler Oct 23, 2025
8a70be8
Rename frame to display
guerler Oct 23, 2025
b1af65f
Add visualization frame component
guerler Oct 23, 2025
9b24cc7
Remove full height option from embedded visualization container
guerler Oct 23, 2025
07ebecb
Reuse Visualization Frame component in Markdown Wrapper
guerler Oct 23, 2025
70a7e5b
Move visualization wrapper to markdown components
guerler Oct 23, 2025
699db58
Update nora to 1.2.2
guerler Oct 23, 2025
e3aef2b
Update mvpapp to 0.0.2
guerler Oct 23, 2025
f242864
Update chiraviz, layout fixes
guerler Oct 23, 2025
354fbbc
Update hivtrace
guerler Oct 23, 2025
f4d663f
Remove unused param options
guerler Oct 23, 2025
e99d30e
Remove unused variable
guerler Oct 24, 2025
704a040
Use api schema to request visualization details
guerler Oct 29, 2025
caa92b3
Remove unnecessary error parser
guerler Oct 29, 2025
53dea64
Update igv
guerler Oct 29, 2025
97d8589
Add hyphyvision
guerler Oct 29, 2025
6f9916a
Allow alternative script entry point type
guerler Oct 30, 2025
6755322
Update hyphyvision to 2.15.10
guerler Oct 30, 2025
f3ceae8
Fix igv test
guerler Oct 30, 2025
626fcd7
Update hyphyvision, add fontawesome icons
guerler Oct 31, 2025
a89ab3a
Allow hyphy_results.json to be displayed in uploader
guerler Oct 31, 2025
f255deb
Assign hyphyvision to hyphy_results.json files
guerler Oct 31, 2025
312704b
Improve error handling
guerler Oct 31, 2025
d2f27e9
Update lib/galaxy/visualization/plugins/registry.py
guerler Oct 31, 2025
bc6e949
Update lib/galaxy/visualization/plugins/plugin.py
guerler Oct 31, 2025
1871971
Update lib/galaxy/visualization/plugins/registry.py
guerler Oct 31, 2025
1d73a19
Update lib/galaxy/visualization/plugins/registry.py
guerler Oct 31, 2025
5dfd6d3
Update lib/galaxy/visualization/plugins/plugin.py
guerler Oct 31, 2025
34ae6bc
Update test/unit/app/visualizations/plugins/test_VisualizationsRegist…
guerler Oct 31, 2025
3d61ead
Update test/unit/app/visualizations/plugins/test_VisualizationsRegist…
guerler Oct 31, 2025
a4d79cd
Update test/unit/app/visualizations/plugins/test_VisualizationsRegist…
guerler Oct 31, 2025
48da2bb
Add typing import to registry.py
guerler Oct 31, 2025
f534435
Add conditional StructuredApp import after last import
guerler Oct 31, 2025
8764d75
Remove template_cache_dir from test
guerler Oct 31, 2025
e571dcd
Apply additional StructuredApp mocking
guerler Oct 31, 2025
c8a0827
Add explicit typing to plugins
guerler Oct 31, 2025
973b6d5
Properly pass title prop to frame component
guerler Oct 31, 2025
fdf6013
Restore root and plugin path
guerler Oct 31, 2025
4e1b346
Use visualization frame for published visualizations
guerler Nov 2, 2025
a449af1
Add explicit types to refs in visualization display
guerler Nov 2, 2025
14d9568
Add visualization config condition
guerler Nov 2, 2025
51abfe9
Refactors VisualizationWrapper props definition
itisAliRH Nov 3, 2025
9966d69
Improves VisualizationDisplay component
itisAliRH Nov 3, 2025
2fca63a
Refines type definitions for props and emits
itisAliRH Nov 3, 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 @@ -23551,11 +23551,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
4 changes: 2 additions & 2 deletions client/src/components/Dataset/DatasetView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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 VisualizationDisplay from "@/components/Visualizations/VisualizationDisplay.vue";

const datasetStore = useDatasetStore();
const datatypeStore = useDatatypeStore();
Expand Down Expand Up @@ -189,7 +189,7 @@ watch(
</BNavItem>
</BNav>
<div v-if="tab === 'preview'" class="tab-content-panel">
<VisualizationFrame
<VisualizationDisplay
v-if="preferredVisualization"
:dataset-id="datasetId"
:visualization="preferredVisualization"
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
99 changes: 99 additions & 0 deletions client/src/components/Markdown/Sections/VisualizationWrapper.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<script setup lang="ts">
import { faExpand, faWindowMaximize } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { BAlert, BButton } from "bootstrap-vue";
import { computed, ref } from "vue";

import VisualizationFrame from "@/components/Visualizations/VisualizationFrame.vue";

const props = withDefaults(
defineProps<{
config: object;
name: string;
title?: string;
Comment thread
guerler marked this conversation as resolved.
Outdated
height?: number;
}>(),
{
height: 400,
},
);

const emit = defineEmits(["change", "load"]);

const errorMessage = ref("");
const expand = ref(false);

const fixedHeight = computed(() =>
expand.value ? {} : { maxHeight: `${props.height}px`, minHeight: `${props.height}px` },
);
</script>

<template>
<div v-if="errorMessage">
<BAlert variant="danger" show>{{ errorMessage }}</BAlert>
</div>
<div v-else class="position-relative h-100">
<div :class="`visualization-pop${expand ? 'out' : 'in'}`">
<VisualizationFrame
title="visualization"
:config="props.config"
:name="props.name"
:style="fixedHeight"
@change="emit('change', $event)"
@load="emit('load')" />
</div>
<BButton
class="visualization-popout-expand"
variant="link"
size="sm"
title="Maximize"
@click="expand = !expand">
<FontAwesomeIcon :icon="faExpand" />
</BButton>
<BButton
v-if="expand"
class="visualization-popout-close"
variant="link"
size="sm"
title="Minimize"
@click="expand = !expand">
<FontAwesomeIcon :icon="faWindowMaximize" />
</BButton>
</div>
</template>

<style lang="scss">
@import "theme/blue.scss";

.visualization-popin {
border: none;
width: 100%;
padding-top: 1.5rem;
}
.visualization-popout {
background: $white;
border: $border-default;
border-radius: $border-radius-base;
height: calc(100vh - 2rem);
left: 1rem;
padding-top: 1.5rem;
position: fixed;
top: 1rem;
width: calc(100vw - 2rem);
z-index: 1000;
}
.visualization-popout-close {
left: 1rem;
position: fixed;
margin: 0.2rem;
padding: 0 0.5rem;
top: 1rem;
z-index: 1001;
}
.visualization-popout-expand {
left: 0;
padding: 0 0.5rem;
position: absolute;
top: 0;
}
</style>
120 changes: 120 additions & 0 deletions client/src/components/Visualizations/VisualizationDisplay.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
<script setup lang="ts">
import { BAlert } from "bootstrap-vue";
import { onMounted, ref } from "vue";
import { onBeforeRouteLeave } from "vue-router/composables";

import { GalaxyApi } from "@/api";

import VisualizationFrame from "./VisualizationFrame.vue";
import LoadingSpan from "@/components/LoadingSpan.vue";

const emit = defineEmits(["load"]);

export interface Props {
datasetId?: string;
visualization: string;
visualizationId?: string;
}

const props = defineProps<Props>();

const errorMessage = ref("");
const iframeRef = ref<HTMLIFrameElement | null>(null);
const isLoading = ref(true);
const hasUnsavedChanges = ref(false);
const visualizationConfig = ref();

function handleLoad() {
isLoading.value = false;
emit("load");
setupChangeDetection();
}

function setupChangeDetection() {
const iframe = iframeRef.value;
if (!iframe?.contentWindow) {
return;
}

setTimeout(() => {
try {
const iframeDoc = iframe.contentDocument;
if (!iframeDoc) {
return;
}

const markAsChanged = () => {
if (!hasUnsavedChanges.value) {
hasUnsavedChanges.value = true;
}
};

// Monitor DOM changes (skip initial load)
setTimeout(() => {
const observer = new MutationObserver(() => markAsChanged());
observer.observe(iframeDoc.body, {
childList: true,
subtree: true,
characterData: true,
});
}, 2000);

// Monitor user input
["input", "change", "keyup", "paste"].forEach((type) => {
iframeDoc.addEventListener(type, markAsChanged, true);
});
} catch (e) {
console.warn("Cannot monitor iframe for changes:", e);
}
}, 1000);
}

onBeforeRouteLeave((to, from, next) => {
if (hasUnsavedChanges.value && !window.confirm("Unsaved changes will be lost. Continue?")) {
next(false);
} else {
next();
}
});

onMounted(async () => {
if (props.visualizationId) {
const { data, error } = await GalaxyApi().GET("/api/visualizations/{id}", {
params: { path: { id: props.visualizationId } },
});
if (error) {
errorMessage.value = error.err_msg;
} else if (data?.latest_revision?.config) {
visualizationConfig.value = data.latest_revision.config;
errorMessage.value = "";
} else {
errorMessage.value = "Failed to access visualization details.";
}
} else {
visualizationConfig.value = { dataset_id: props.datasetId };
}

window.addEventListener("beforeunload", (e) => {
if (hasUnsavedChanges.value) {
e.preventDefault();
e.returnValue = "";
}
});
});
</script>

<template>
<div class="position-relative h-100 overflow-hidden">
<BAlert v-if="errorMessage" variant="danger" show>
{{ errorMessage }}
</BAlert>
<div v-else-if="isLoading" class="iframe-loading bg-light">
<LoadingSpan message="Loading visualization" />
</div>
<VisualizationFrame
v-if="visualizationConfig"
:config="visualizationConfig"
:name="visualization"
@load="handleLoad" />
</div>
</template>
Loading
Loading