From 61aa1b484d1d8f91341aa5c725b4907e3e57ee0b Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Thu, 27 Mar 2025 08:36:24 +0100 Subject: [PATCH 1/8] Fix undo in merger mode (#8463) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * rename some variables * more renaming * more renaming * fix undo in merger mode * format * change any to number --------- Co-authored-by: Michael Büßemeyer Co-authored-by: MichaelBuessemeyer <39529669+MichaelBuessemeyer@users.noreply.github.com> --- frontend/javascripts/oxalis/merger_mode.ts | 150 ++++++++++++++------- 1 file changed, 98 insertions(+), 52 deletions(-) diff --git a/frontend/javascripts/oxalis/merger_mode.ts b/frontend/javascripts/oxalis/merger_mode.ts index a4ab205ea95..b5876bc6178 100644 --- a/frontend/javascripts/oxalis/merger_mode.ts +++ b/frontend/javascripts/oxalis/merger_mode.ts @@ -23,17 +23,23 @@ import type { AdditionalCoordinate } from "types/api_flow_types"; import type { CreateNodeAction } from "./model/actions/skeletontracing_actions"; type MergerModeState = { + // Representative Segment Id is a mapped id. treeIdToRepresentativeSegmentId: Record; idMapping: Map; - nodesPerSegment: Record; + + // Unmapped Segment Id -> Count + nodesPerUnmappedSegment: Record; nodes: Array; + // A properly initialized merger mode should always // have a segmentationLayerName. However, some edge cases // become easier when we handle the null case, anyway. // In theory, the UI should not allow to enable the merger mode // without a visible segmentation layer. segmentationLayerName: string | null | undefined; - nodeSegmentMap: Record; + + // Node Id -> Unmapped Segment Id + nodeToUnmappedSegmentMap: Record; prevTracing: SkeletonTracing; }; const unregisterKeyHandlers: UnregisterHandler[] = []; @@ -41,55 +47,92 @@ const unsubscribeFunctions: Array<() => void> = []; let isCodeActive = false; function mapSegmentToRepresentative( - segId: number, + unmappedSegmentId: number, treeId: number, mergerModeState: MergerModeState, ) { - const representative = getRepresentativeForTree(treeId, segId, mergerModeState); - mergerModeState.idMapping.set(segId, representative); + const representative = getRepresentativeForTree(treeId, unmappedSegmentId, mergerModeState); + mergerModeState.idMapping.set(unmappedSegmentId, representative); } -function getRepresentativeForTree(treeId: number, segId: number, mergerModeState: MergerModeState) { +function getRepresentativeForTree( + treeId: number, + unmappedSegmentId: number, + mergerModeState: MergerModeState, +) { const { treeIdToRepresentativeSegmentId } = mergerModeState; let representative = treeIdToRepresentativeSegmentId[treeId]; // Use the passed segment id as a representative, if the tree was never seen before if (representative == null) { - representative = segId; + representative = unmappedSegmentId; treeIdToRepresentativeSegmentId[treeId] = representative; } return representative; } -function deleteIdMappingOfSegment(segId: number, treeId: number, mergerModeState: MergerModeState) { +function removeUnmappedSegmentIdFromMapping( + unmappedSegmentId: number, + treeId: number, + mergerModeState: MergerModeState, +) { + if (mergerModeState.idMapping.get(unmappedSegmentId) === unmappedSegmentId) { + // The representative was removed from the mapping. Delete it. + delete mergerModeState.treeIdToRepresentativeSegmentId[treeId]; + + // Relabel ids that were mapped to the old representative (and find + // a new one). + let newRepresentative; + for (const [key, value] of mergerModeState.idMapping) { + if (key === unmappedSegmentId) { + // This is the value that is about to be removed. + continue; + } + if (value === unmappedSegmentId) { + if (newRepresentative == null) { + newRepresentative = key; + } + mergerModeState.idMapping.set(key, newRepresentative); + } + } + + if (newRepresentative != null) { + mergerModeState.treeIdToRepresentativeSegmentId[treeId] = newRepresentative; + } + } // Remove segment from color mapping - mergerModeState.idMapping.delete(segId); - delete mergerModeState.treeIdToRepresentativeSegmentId[treeId]; + mergerModeState.idMapping.delete(unmappedSegmentId); } /* This function is used to increment the reference count / number of nodes mapped to the given segment */ -function increaseNodesOfSegment(segementId: number, mergerModeState: MergerModeState) { - const { nodesPerSegment } = mergerModeState; - const currentValue = nodesPerSegment[segementId]; +function increaseNodesOfUnmappedSegment( + unmappedSegmentId: number, + mergerModeState: MergerModeState, +) { + const { nodesPerUnmappedSegment } = mergerModeState; + const currentValue = nodesPerUnmappedSegment[unmappedSegmentId]; if (currentValue == null) { - nodesPerSegment[segementId] = 1; + nodesPerUnmappedSegment[unmappedSegmentId] = 1; } else { - nodesPerSegment[segementId] = currentValue + 1; + nodesPerUnmappedSegment[unmappedSegmentId] = currentValue + 1; } - return nodesPerSegment[segementId]; + return nodesPerUnmappedSegment[unmappedSegmentId]; } /* This function is used to decrement the reference count / number of nodes mapped to the given segment. */ -function decreaseNodesOfSegment(segementId: number, mergerModeState: MergerModeState): number { - const { nodesPerSegment } = mergerModeState; - const currentValue = nodesPerSegment[segementId]; - nodesPerSegment[segementId] = currentValue - 1; - return nodesPerSegment[segementId]; +function decreaseNodesOfUnmappedSegment( + unmappedSegmentId: number, + mergerModeState: MergerModeState, +): number { + const { nodesPerUnmappedSegment } = mergerModeState; + const currentValue = nodesPerUnmappedSegment[unmappedSegmentId]; + nodesPerUnmappedSegment[unmappedSegmentId] = currentValue - 1; + return nodesPerUnmappedSegment[unmappedSegmentId]; } function getAllNodesWithTreeId(): Array { @@ -125,7 +168,7 @@ async function createNodeOverwrite( } const { position: untransformedPosition, additionalCoordinates } = action; - const segmentId = await getSegmentId( + const unmappedSegmentId = await getUnmappedSegmentId( store.getState(), segmentationLayerName, untransformedPosition, @@ -134,7 +177,7 @@ async function createNodeOverwrite( // If there is no segment id, the node was set outside of all segments. // Drop the node creation action in that case. - if (!segmentId) { + if (!unmappedSegmentId) { api.utils.showToast("warning", messages["tracing.merger_mode_node_outside_segment"]); } else { call(action); @@ -157,13 +200,13 @@ async function onCreateNode( additionalCoordinates: AdditionalCoordinate[] | null, updateMapping: boolean = true, ) { - const { idMapping, segmentationLayerName, nodeSegmentMap } = mergerModeState; + const { idMapping, segmentationLayerName, nodeToUnmappedSegmentMap } = mergerModeState; if (segmentationLayerName == null) { return; } - const segmentId = await getSegmentId( + const unmappedSegmentId = await getUnmappedSegmentId( Store.getState(), segmentationLayerName, untransformedPosition, @@ -173,15 +216,15 @@ async function onCreateNode( // It can still happen that there are createNode diffing actions for nodes which // are placed outside of a segment, for example when merging trees that were created // outside of merger mode. Ignore those nodes. - if (!segmentId) { + if (!unmappedSegmentId) { return; } - // Set segment id - nodeSegmentMap[nodeId] = segmentId; + nodeToUnmappedSegmentMap[nodeId] = unmappedSegmentId; + // Count references - increaseNodesOfSegment(segmentId, mergerModeState); - mapSegmentToRepresentative(segmentId, treeId, mergerModeState); + increaseNodesOfUnmappedSegment(unmappedSegmentId, mergerModeState); + mapSegmentToRepresentative(unmappedSegmentId, treeId, mergerModeState); if (updateMapping) { // Update mapping @@ -189,7 +232,7 @@ async function onCreateNode( } } -async function getSegmentId( +async function getUnmappedSegmentId( state: OxalisState, segmentationLayerName: string, untransformedPosition: Vector3, @@ -229,12 +272,15 @@ async function onDeleteNode( return; } - const segmentId = mergerModeState.nodeSegmentMap[nodeWithTreeId.nodeId]; - const numberOfNodesMappedToSegment = decreaseNodesOfSegment(segmentId, mergerModeState); + const unmappedSegmentId = mergerModeState.nodeToUnmappedSegmentMap[nodeWithTreeId.nodeId]; + const numberOfNodesInUnmappedSegment = decreaseNodesOfUnmappedSegment( + unmappedSegmentId, + mergerModeState, + ); - if (numberOfNodesMappedToSegment === 0) { - // Reset color of all segments that were mapped to this tree - deleteIdMappingOfSegment(segmentId, nodeWithTreeId.treeId, mergerModeState); + if (numberOfNodesInUnmappedSegment === 0) { + // Reset color of the unmapped segment that was mapped to this tree + removeUnmappedSegmentIdFromMapping(unmappedSegmentId, nodeWithTreeId.treeId, mergerModeState); if (updateMapping) { api.data.setMapping(segmentationLayerName, mergerModeState.idMapping, { @@ -246,7 +292,7 @@ async function onDeleteNode( async function onUpdateNode(mergerModeState: MergerModeState, node: UpdateActionNode) { const { position: untransformedPosition, id, treeId } = node; - const { segmentationLayerName, nodeSegmentMap } = mergerModeState; + const { segmentationLayerName, nodeToUnmappedSegmentMap } = mergerModeState; if (segmentationLayerName == null) { return; @@ -254,17 +300,17 @@ async function onUpdateNode(mergerModeState: MergerModeState, node: UpdateAction const state = Store.getState(); - const segmentId = await getSegmentId( + const unmappedSegmentId = await getUnmappedSegmentId( state, segmentationLayerName, untransformedPosition, state.flycam.additionalCoordinates, ); - if (nodeSegmentMap[id] !== segmentId) { + if (nodeToUnmappedSegmentMap[id] !== unmappedSegmentId) { // If the segment of the node changed, it is like the node got deleted and a copy got created somewhere else. // Thus we use the onNodeDelete and onNodeCreate method to update the mapping. - if (nodeSegmentMap[id] != null) { + if (nodeToUnmappedSegmentMap[id] != null) { await onDeleteNode( mergerModeState, { nodeId: id, treeId, actionTracingId: mergerModeState.prevTracing.tracingId }, @@ -272,7 +318,7 @@ async function onUpdateNode(mergerModeState: MergerModeState, node: UpdateAction ); } - if (segmentId != null && segmentId > 0) { + if (unmappedSegmentId != null && unmappedSegmentId > 0) { await onCreateNode( mergerModeState, id, @@ -281,9 +327,9 @@ async function onUpdateNode(mergerModeState: MergerModeState, node: UpdateAction node.additionalCoordinates, false, ); - } else if (nodeSegmentMap[id] != null) { - // The node is not inside a segment anymore. Thus we delete it from the nodeSegmentMap. - delete nodeSegmentMap[id]; + } else if (nodeToUnmappedSegmentMap[id] != null) { + // The node is not inside a segment anymore. Thus we delete it from the nodeToUnmappedSegmentMap. + delete nodeToUnmappedSegmentMap[id]; } api.data.setMapping(segmentationLayerName, mergerModeState.idMapping, { @@ -359,7 +405,7 @@ async function mergeSegmentsOfAlreadyExistingTrees( mergerModeState: MergerModeState, onProgressUpdate: (arg0: number) => void, ) { - const { nodes, segmentationLayerName, nodeSegmentMap, idMapping } = mergerModeState; + const { nodes, segmentationLayerName, nodeToUnmappedSegmentMap, idMapping } = mergerModeState; const numbOfNodes = nodes.length; if (index >= numbOfNodes) { @@ -398,14 +444,14 @@ async function mergeSegmentsOfAlreadyExistingTrees( return; } - const segmentId = await api.data.getDataValue(segmentationLayerName, segmentPosition); + const unmappedSegmentId = await api.data.getDataValue(segmentationLayerName, segmentPosition); - if (segmentId != null && segmentId > 0) { + if (unmappedSegmentId != null && unmappedSegmentId > 0) { // Store the segment id - nodeSegmentMap[node.id] = segmentId; + nodeToUnmappedSegmentMap[node.id] = unmappedSegmentId; // Add to agglomerate - increaseNodesOfSegment(segmentId, mergerModeState); - mapSegmentToRepresentative(segmentId, treeId, mergerModeState); + increaseNodesOfUnmappedSegment(unmappedSegmentId, mergerModeState); + mapSegmentToRepresentative(unmappedSegmentId, treeId, mergerModeState); } }; @@ -432,10 +478,10 @@ function resetState(mergerModeState: Partial = {}) { const defaults: MergerModeState = { treeIdToRepresentativeSegmentId: {}, idMapping: new Map(), - nodesPerSegment: {}, + nodesPerUnmappedSegment: {}, nodes: getAllNodesWithTreeId(), segmentationLayerName, - nodeSegmentMap: {}, + nodeToUnmappedSegmentMap: {}, prevTracing: getSkeletonTracing(state.tracing).get(), }; // Keep the object identity when resetting From 6dc1de524f406cb7a5267be99cef26d39ecff583 Mon Sep 17 00:00:00 2001 From: Tom Herold Date: Fri, 28 Mar 2025 10:03:44 +0100 Subject: [PATCH 2/8] Fix navbar menu highlighting (#8473) * fix highlighting the correct navbar item * update changelog --- CHANGELOG.released.md | 1 + frontend/javascripts/navbar.tsx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.released.md b/CHANGELOG.released.md index 21d23f6e4bb..6e869982974 100644 --- a/CHANGELOG.released.md +++ b/CHANGELOG.released.md @@ -12,6 +12,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released ### Fixed - Fixed rare bug where saving got stuck. [#8409](https://github.com/scalableminds/webknossos/pull/8409) +- Fixed the correct highlighting of navbar menu items depending on the current open page. [#8473](https://github.com/scalableminds/webknossos/pull/8473) ## [25.02.0](https://github.com/scalableminds/webknossos/releases/tag/25.02.0) - 2025-02-17 [Commits](https://github.com/scalableminds/webknossos/compare/25.01.0...25.02.0) diff --git a/frontend/javascripts/navbar.tsx b/frontend/javascripts/navbar.tsx index c8ed964e5f8..9b89ce9f601 100644 --- a/frontend/javascripts/navbar.tsx +++ b/frontend/javascripts/navbar.tsx @@ -26,7 +26,7 @@ import classnames from "classnames"; import type React from "react"; import { useEffect, useRef, useState } from "react"; import { connect, useSelector } from "react-redux"; -import { Link, useHistory } from "react-router-dom"; +import { Link, useLocation } from "react-router-dom"; import { getBuildInfo, @@ -821,7 +821,7 @@ function Navbar({ navbarHeight, isAnnotationOwner, }: Props) { - const history = useHistory(); + const historyLocation = useLocation(); const handleLogout = async (event: React.SyntheticEvent) => { event.preventDefault(); @@ -938,7 +938,7 @@ function Navbar({ ); // Don't highlight active menu items, when showing the narrow version of the navbar, // since this makes the icons appear more crowded. - const selectedKeys = collapseAllNavItems ? [] : [history.location.pathname]; + const selectedKeys = collapseAllNavItems ? [] : [historyLocation.pathname]; const separator =
; return ( From ba3f99b3b3e9ad33bdd79b093b09bb6df087feef Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Tue, 1 Apr 2025 17:22:23 +0200 Subject: [PATCH 3/8] Release 25.03.0 --- CHANGELOG.released.md | 36 ++++++++++++++++++++++++++++++++++++ CHANGELOG.unreleased.md | 29 +---------------------------- MIGRATIONS.released.md | 17 ++++++++++++++--- MIGRATIONS.unreleased.md | 7 +------ 4 files changed, 52 insertions(+), 37 deletions(-) diff --git a/CHANGELOG.released.md b/CHANGELOG.released.md index 6e869982974..0100df7d7e2 100644 --- a/CHANGELOG.released.md +++ b/CHANGELOG.released.md @@ -7,6 +7,42 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`. For upgrade instructions, please check the [migration guide](MIGRATIONS.released.md). +## [25.03.0](https://github.com/scalableminds/webknossos/releases/tag/25.03.0) - 2025-04-01 +[Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...25.03.0) + +### Added +- Added support for datasets with the following data types: int8, int16, int32, uint32 (support for color was added, support for segmentation already existed before) and int64 (segmentation only). [#8325](https://github.com/scalableminds/webknossos/pull/8325) +- Added a command palette that allows navigating between pages, switching tools and accessing some user settings via Ctrl+P. [#8447](https://github.com/scalableminds/webknossos/pull/8447/) +- Super users can now share the trained AI models with other organizations. [#8418](https://github.com/scalableminds/webknossos/pull/8418) +- Failed jobs may be retried by super-users. [#8377](https://github.com/scalableminds/webknossos/pull/8377) +- Optimized server-side storage of skeleton annotation layers. [#8423](https://github.com/scalableminds/webknossos/pull/8423) +- Added support for adding N5 datasets with compact-style multiscale metadata. [#8456](https://github.com/scalableminds/webknossos/pull/8456) + +### Changed +- When using a zarr link to a wk-served data layer as another layer’s source, the user’s token is used to access the data. [#8322](https://github.com/scalableminds/webknossos/pull/8322/) +- Compound annotations (created when viewing all annotations of a task) no longer permanently store data in the FossilDB. [#8422](https://github.com/scalableminds/webknossos/pull/8422) +- When creating multiple tasks at once (bulk task creation), they now all need to have the same task type. [#8405](https://github.com/scalableminds/webknossos/pull/8405) +- Improved performance when changing the layout/viewports. [#8448](https://github.com/scalableminds/webknossos/pull/8448) +- Annotation upload will now always add a skeleton annotation layer, even if the downloaded annotation was volume-only. [#8466](https://github.com/scalableminds/webknossos/pull/8466) + +### Fixed +- Fixed a bug that would lock a non-existing mapping to an empty segmentation layer under certain conditions. [#8401](https://github.com/scalableminds/webknossos/pull/8401) +- Fixed the alignment of the button that allows restricting floodfill operations to a bounding box. [#8388](https://github.com/scalableminds/webknossos/pull/8388) +- Fixed a bug for rotated dataset where volume tools were disabled although the dataset was rendered untransformed. [#8432](https://github.com/scalableminds/webknossos/pull/8432) +- Fixed rare bug where saving got stuck. [#8409](https://github.com/scalableminds/webknossos/pull/8409) +- Fixed some rendering bugs for float datasets that used a large dynamic range. [#8325](https://github.com/scalableminds/webknossos/pull/8325) +- Fixed a bug where reverting annotations could get stuck if some of its layers had been deleted in the meantime. [#8405](https://github.com/scalableminds/webknossos/pull/8405) +- When removing a segment from the segment list, a corresponding precomputed mesh was not removed automatically. [#8428](https://github.com/scalableminds/webknossos/pull/8428) +- Fixed a bug where newly added remote datasets would always appear in root folder, regardless of actual selected folder. [#8425](https://github.com/scalableminds/webknossos/pull/8425) +- Fixed a bug where the python libs functionality `wk.RemoteDataset.explore_and_add_remote` would error. [#8425](https://github.com/scalableminds/webknossos/pull/8425) +- Fixed a bug where various UI dialogs would be dark mode even the user preferred a light theme. [#8445](https://github.com/scalableminds/webknossos/pull/8445) +- Fixed an issue with icon spacing on the task dashboard page. [#8452](https://github.com/scalableminds/webknossos/pull/8452) +- Fixed a spacing issue in the statusbar. [#8455](https://github.com/scalableminds/webknossos/pull/8455) +- Fixed a bug where the "Create Animation" modal did not open when selecting the corresponding feature from the navbar menu. [#8444](https://github.com/scalableminds/webknossos/pull/8444) +- Fixed that the brightness/contrast settings (in the histogram) could not be changed if the histogram data could not be loaded for some reason. [#8459](https://github.com/scalableminds/webknossos/pull/8459) +- Fixed that downloading + reupload of an annotation, in which the skeleton tools were never used, actually made them unreachable. [#8466](https://github.com/scalableminds/webknossos/pull/8466) +- Fixed a bug where task creation with volume zip as input would fail. [#8468](https://github.com/scalableminds/webknossos/pull/8468) + ## [25.02.1](https://github.com/scalableminds/webknossos/releases/tag/25.02.1) - 2025-02-26 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.0...25.02.1) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 29762aebbf1..81c98c45d40 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -8,40 +8,13 @@ and this project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MIC For upgrade instructions, please check the [migration guide](MIGRATIONS.released.md). ## Unreleased -[Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...HEAD) +[Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...HEAD) ### Added -- Added support for datasets with the following data types: int8, int16, int32, uint32 (support for color was added, support for segmentation already existed before) and int64 (segmentation only). [#8325](https://github.com/scalableminds/webknossos/pull/8325) -- Added a command palette that allows navigating between pages, switching tools and accessing some user settings via Ctrl+P. [#8447](https://github.com/scalableminds/webknossos/pull/8447/) -- Super users can now share the trained AI models with other organizations. [#8418](https://github.com/scalableminds/webknossos/pull/8418) -- Failed jobs may be retried by super-users. [#8377](https://github.com/scalableminds/webknossos/pull/8377) -- Optimized server-side storage of skeleton annotation layers. [#8423](https://github.com/scalableminds/webknossos/pull/8423) -- Added support for adding N5 datasets with compact-style multiscale metadata. [#8456](https://github.com/scalableminds/webknossos/pull/8456) ### Changed -- When using a zarr link to a wk-served data layer as another layer’s source, the user’s token is used to access the data. [#8322](https://github.com/scalableminds/webknossos/pull/8322/) -- Compound annotations (created when viewing all annotations of a task) no longer permanently store data in the FossilDB. [#8422](https://github.com/scalableminds/webknossos/pull/8422) -- When creating multiple tasks at once (bulk task creation), they now all need to have the same task type. [#8405](https://github.com/scalableminds/webknossos/pull/8405) -- Improved performance when changing the layout/viewports. [#8448](https://github.com/scalableminds/webknossos/pull/8448) -- Annotation upload will now always add a skeleton annotation layer, even if the downloaded annotation was volume-only. [#8466](https://github.com/scalableminds/webknossos/pull/8466) ### Fixed -- Fixed a bug that would lock a non-existing mapping to an empty segmentation layer under certain conditions. [#8401](https://github.com/scalableminds/webknossos/pull/8401) -- Fixed the alignment of the button that allows restricting floodfill operations to a bounding box. [#8388](https://github.com/scalableminds/webknossos/pull/8388) -- Fixed a bug for rotated dataset where volume tools were disabled although the dataset was rendered untransformed. [#8432](https://github.com/scalableminds/webknossos/pull/8432) -- Fixed rare bug where saving got stuck. [#8409](https://github.com/scalableminds/webknossos/pull/8409) -- Fixed some rendering bugs for float datasets that used a large dynamic range. [#8325](https://github.com/scalableminds/webknossos/pull/8325) -- Fixed a bug where reverting annotations could get stuck if some of its layers had been deleted in the meantime. [#8405](https://github.com/scalableminds/webknossos/pull/8405) -- When removing a segment from the segment list, a corresponding precomputed mesh was not removed automatically. [#8428](https://github.com/scalableminds/webknossos/pull/8428) -- Fixed a bug where newly added remote datasets would always appear in root folder, regardless of actual selected folder. [#8425](https://github.com/scalableminds/webknossos/pull/8425) -- Fixed a bug where the python libs functionality `wk.RemoteDataset.explore_and_add_remote` would error. [#8425](https://github.com/scalableminds/webknossos/pull/8425) -- Fixed a bug where various UI dialogs would be dark mode even the user preferred a light theme. [#8445](https://github.com/scalableminds/webknossos/pull/8445) -- Fixed an issue with icon spacing on the task dashboard page. [#8452](https://github.com/scalableminds/webknossos/pull/8452) -- Fixed a spacing issue in the statusbar. [#8455](https://github.com/scalableminds/webknossos/pull/8455) -- Fixed a bug where the "Create Animation" modal did not open when selecting the corresponding feature from the navbar menu. [#8444](https://github.com/scalableminds/webknossos/pull/8444) -- Fixed that the brightness/contrast settings (in the histogram) could not be changed if the histogram data could not be loaded for some reason. [#8459](https://github.com/scalableminds/webknossos/pull/8459) -- Fixed that downloading + reupload of an annotation, in which the skeleton tools were never used, actually made them unreachable. [#8466](https://github.com/scalableminds/webknossos/pull/8466) -- Fixed a bug where task creation with volume zip as input would fail. [#8468](https://github.com/scalableminds/webknossos/pull/8468) ### Removed diff --git a/MIGRATIONS.released.md b/MIGRATIONS.released.md index 2d505137a71..badf9eb4a71 100644 --- a/MIGRATIONS.released.md +++ b/MIGRATIONS.released.md @@ -6,11 +6,22 @@ See `MIGRATIONS.unreleased.md` for the changes which are not yet part of an offi This project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`. User-facing changes are documented in the [changelog](CHANGELOG.released.md). +## [25.03.0](https://github.com/scalableminds/webknossos/releases/tag/25.03.0) - 2025-04-01 +[Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...25.03.0) + + - FossilDB now needs to be opened with additional column family `skeletonTreeBodies`. [#8423](https://github.com/scalableminds/webknossos/pull/8423) + +### Postgres Evolutions: +- [126-mag-real-paths.sql](conf/evolutions/126-mag-real-paths.sql) +- [127-job-retried-by-super-user.sql](conf/evolutions/127-job-retried-by-super-user.sql) +- [128-allow-ai-model-sharing.sql](conf/evolutions/128-allow-ai-model-sharing.sql) + + ## [25.02.1](https://github.com/scalableminds/webknossos/releases/tag/25.02.1) - 2025-02-26 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.0...25.02.1) ### Postgres Evolutions: - +None. ## [25.02.0](https://github.com/scalableminds/webknossos/releases/tag/25.02.0) - 2025-02-17 [Commits](https://github.com/scalableminds/webknossos/compare/25.01.0...25.02.0) @@ -25,7 +36,7 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). - Example command for the migration: `PG_PASSWORD=myPassword python main.py --src localhost:7500 --dst localhost:7155 --num_threads 20 --postgres webknossos@localhost:5430/webknossos` ### Postgres Evolutions: - +None. ## [25.01.0](https://github.com/scalableminds/webknossos/releases/tag/25.01.0) - 2025-01-22 [Commits](https://github.com/scalableminds/webknossos/compare/24.12.0...25.01.0) @@ -59,7 +70,7 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). - For self-hosted versions of WEBKNOSSOS, you can choose whether switching to webknossos.org will be recommended to you, e.g. when uploading a dataset. To configure this, change `recommendWkorgInstance` in your `application.conf`. ### Postgres Evolutions: - +None. ## [24.08.1](https://github.com/scalableminds/webknossos/releases/tag/24.08.1) - 2024-09-03 [Commits](https://github.com/scalableminds/webknossos/compare/24.08.0...24.08.1) diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index 81b2c3bb441..e41a2fca08e 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -6,11 +6,6 @@ This project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`. User-facing changes are documented in the [changelog](CHANGELOG.released.md). ## Unreleased -[Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...HEAD) - - - FossilDB now needs to be opened with additional column family `skeletonTreeBodies`. [#8423](https://github.com/scalableminds/webknossos/pull/8423) +[Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...HEAD) ### Postgres Evolutions: -- [126-mag-real-paths.sql](conf/evolutions/126-mag-real-paths.sql) -- [127-job-retried-by-super-user.sql](conf/evolutions/127-job-retried-by-super-user.sql) -- [128-allow-ai-model-sharing.sql](conf/evolutions/128-allow-ai-model-sharing.sql) From f5fbb7986f0353ea0bf01016193bd9ed70e4cf08 Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Wed, 2 Apr 2025 12:13:45 +0200 Subject: [PATCH 4/8] add highlights --- CHANGELOG.released.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.released.md b/CHANGELOG.released.md index 0100df7d7e2..003ab455cb1 100644 --- a/CHANGELOG.released.md +++ b/CHANGELOG.released.md @@ -10,9 +10,11 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released ## [25.03.0](https://github.com/scalableminds/webknossos/releases/tag/25.03.0) - 2025-04-01 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...25.03.0) -### Added -- Added support for datasets with the following data types: int8, int16, int32, uint32 (support for color was added, support for segmentation already existed before) and int64 (segmentation only). [#8325](https://github.com/scalableminds/webknossos/pull/8325) +### Highlights - Added a command palette that allows navigating between pages, switching tools and accessing some user settings via Ctrl+P. [#8447](https://github.com/scalableminds/webknossos/pull/8447/) +- Added support for datasets with the following data types: int8, int16, int32, uint32 (support for color was added, support for segmentation already existed before) and int64 (segmentation only). [#8325](https://github.com/scalableminds/webknossos/pull/8325) + +### Added - Super users can now share the trained AI models with other organizations. [#8418](https://github.com/scalableminds/webknossos/pull/8418) - Failed jobs may be retried by super-users. [#8377](https://github.com/scalableminds/webknossos/pull/8377) - Optimized server-side storage of skeleton annotation layers. [#8423](https://github.com/scalableminds/webknossos/pull/8423) From f136b8b44dc7d23670b694ff40248e2b899c4e45 Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Thu, 10 Apr 2025 15:00:31 +0200 Subject: [PATCH 5/8] fix changelog and migration guide for 25.03.0 --- CHANGELOG.released.md | 8 ++++++++ CHANGELOG.unreleased.md | 8 -------- MIGRATIONS.released.md | 3 ++- MIGRATIONS.unreleased.md | 2 -- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.released.md b/CHANGELOG.released.md index 003ab455cb1..2ae3cd19066 100644 --- a/CHANGELOG.released.md +++ b/CHANGELOG.released.md @@ -19,6 +19,8 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Failed jobs may be retried by super-users. [#8377](https://github.com/scalableminds/webknossos/pull/8377) - Optimized server-side storage of skeleton annotation layers. [#8423](https://github.com/scalableminds/webknossos/pull/8423) - Added support for adding N5 datasets with compact-style multiscale metadata. [#8456](https://github.com/scalableminds/webknossos/pull/8456) +- Added a credit system making payment for long running jobs possible. For now it is in testing phase. [#8352](https://github.com/scalableminds/webknossos/pull/8352) +- The maximum available storage of an organization is now enforced during upload. [#8385](https://github.com/scalableminds/webknossos/pull/8385) ### Changed - When using a zarr link to a wk-served data layer as another layer’s source, the user’s token is used to access the data. [#8322](https://github.com/scalableminds/webknossos/pull/8322/) @@ -44,6 +46,12 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - Fixed that the brightness/contrast settings (in the histogram) could not be changed if the histogram data could not be loaded for some reason. [#8459](https://github.com/scalableminds/webknossos/pull/8459) - Fixed that downloading + reupload of an annotation, in which the skeleton tools were never used, actually made them unreachable. [#8466](https://github.com/scalableminds/webknossos/pull/8466) - Fixed a bug where task creation with volume zip as input would fail. [#8468](https://github.com/scalableminds/webknossos/pull/8468) +- Fixed visual alignment of actions in ai model list. [#8474](https://github.com/scalableminds/webknossos/pull/8474) +- Improve formatting of credits amount in organization management page [#8487](https://github.com/scalableminds/webknossos/pull/8487) +- Re-enabled jobs planned to be paid with credits for organizations without a paid plan. [#8478](https://github.com/scalableminds/webknossos/pull/8478) +- Fixed that the dataset extent tooltip in the right details bar in the dashboard did not properly update when switching datasets. [#8477](https://github.com/scalableminds/webknossos/pull/8477) +- Fixed a bug where task creation with volume zip as input would fail. [#8468](https://github.com/scalableminds/webknossos/pull/8468) +- Fixed that a warning message about a newer version of an annotation was shown multiple times. [#8486](https://github.com/scalableminds/webknossos/pull/8486) ## [25.02.1](https://github.com/scalableminds/webknossos/releases/tag/25.02.1) - 2025-02-26 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.0...25.02.1) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 3bafd18da7f..81c98c45d40 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -11,18 +11,10 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released [Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...HEAD) ### Added -- Added a credit system making payment for long running jobs possible. For now it is in testing phase. [#8352](https://github.com/scalableminds/webknossos/pull/8352) -- The maximum available storage of an organization is now enforced during upload. [#8385](https://github.com/scalableminds/webknossos/pull/8385) ### Changed ### Fixed -- Fixed visual alignment of actions in ai model list. [#8474](https://github.com/scalableminds/webknossos/pull/8474) -- Improve formatting of credits amount in organization management page [#8487](https://github.com/scalableminds/webknossos/pull/8487) -- Re-enabled jobs planned to be paid with credits for organizations without a paid plan. [#8478](https://github.com/scalableminds/webknossos/pull/8478) -- Fixed that the dataset extent tooltip in the right details bar in the dashboard did not properly update when switching datasets. [#8477](https://github.com/scalableminds/webknossos/pull/8477) -- Fixed a bug where task creation with volume zip as input would fail. [#8468](https://github.com/scalableminds/webknossos/pull/8468) -- Fixed that a warning message about a newer version of an annotation was shown multiple times. [#8486](https://github.com/scalableminds/webknossos/pull/8486) ### Removed diff --git a/MIGRATIONS.released.md b/MIGRATIONS.released.md index badf9eb4a71..034ac6f1a6b 100644 --- a/MIGRATIONS.released.md +++ b/MIGRATIONS.released.md @@ -15,7 +15,8 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). - [126-mag-real-paths.sql](conf/evolutions/126-mag-real-paths.sql) - [127-job-retried-by-super-user.sql](conf/evolutions/127-job-retried-by-super-user.sql) - [128-allow-ai-model-sharing.sql](conf/evolutions/128-allow-ai-model-sharing.sql) - +- [129-credit-transactions.sql](conf/evolutions/129-credit-transactions.sql) +- [130-replace-text-types.sql](conf/evolutions/130-replace-text-types.sql) ## [25.02.1](https://github.com/scalableminds/webknossos/releases/tag/25.02.1) - 2025-02-26 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.0...25.02.1) diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index 3ffdd80c7a8..e41a2fca08e 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -9,5 +9,3 @@ User-facing changes are documented in the [changelog](CHANGELOG.released.md). [Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...HEAD) ### Postgres Evolutions: -- [129-credit-transactions.sql](conf/evolutions/129-credit-transactions.sql) -- [130-replace-text-types.sql](conf/evolutions/130-replace-text-types.sql) From 8553667f05ae3b9c3aff855c006654807dc3e166 Mon Sep 17 00:00:00 2001 From: Florian M Date: Mon, 7 Apr 2025 10:00:14 +0200 Subject: [PATCH 6/8] Restructure list annotations query to guide Postgres to a more efficient query plan (#8498) * Restructure list annotations query to guide Postgres to a more efficient query plan * changelog --------- Co-authored-by: MichaelBuessemeyer <39529669+MichaelBuessemeyer@users.noreply.github.com> --- CHANGELOG.unreleased.md | 1 + app/models/annotation/Annotation.scala | 149 +++++++++++++++---------- 2 files changed, 94 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 81c98c45d40..01757d4a865 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -15,6 +15,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released ### Changed ### Fixed +- Fixed a bug where the annotation list would sometimes load very long if you have many annotations. [#8498](https://github.com/scalableminds/webknossos/pull/8498) ### Removed diff --git a/app/models/annotation/Annotation.scala b/app/models/annotation/Annotation.scala index 5be6bfb3be5..fc38cc930f1 100755 --- a/app/models/annotation/Annotation.scala +++ b/app/models/annotation/Annotation.scala @@ -399,62 +399,99 @@ class AnnotationDAO @Inject()(sqlClient: SqlClient, annotationLayerDAO: Annotati stateQuery = getStateQuery(isFinished) userQuery = forUser.map(u => q"a._user = $u").getOrElse(q"TRUE") typQuery = q"a.typ = ${AnnotationType.Explorational}" - - query = q"""WITH - -- teams_agg is extracted to avoid left-join fanout. - -- Note that only one of the joins in it has 1:n, so they can happen together - teams_agg AS ( - SELECT - a._id AS _annotation, - ARRAY_REMOVE(ARRAY_AGG(t._id), null) AS team_ids, - ARRAY_REMOVE(ARRAY_AGG(t.name), null) AS team_names, - ARRAY_REMOVE(ARRAY_AGG(o._id), null) AS team_organization_ids - FROM webknossos.annotations a - LEFT JOIN webknossos.annotation_sharedteams ast ON ast._annotation = a._id - LEFT JOIN webknossos.teams_ t ON ast._team = t._id - LEFT JOIN webknossos.organizations_ o ON t._organization = o._id - GROUP BY a._id - ) - SELECT - a._id, - a.name, - a.description, - a._user, - u.firstname, - u.lastname, - a.othersmayedit, - teams_agg.team_ids, - teams_agg.team_names, - teams_agg.team_organization_ids, - a.modified, - a.tags, - a.state, - a.isLockedByOwner, - d.name, - a.typ, - a.visibility, - a.tracingtime, - o._id, - ARRAY_REMOVE(ARRAY_AGG(al.tracingid), null) AS tracing_ids, - ARRAY_REMOVE(ARRAY_AGG(al.name), null) AS tracing_names, - ARRAY_REMOVE(ARRAY_AGG(al.typ :: varchar), null) AS tracing_typs, - ARRAY_REMOVE(ARRAY_AGG(al.statistics), null) AS annotation_layer_statistics - FROM webknossos.annotations_ AS a - JOIN webknossos.users_ u ON u._id = a._user - JOIN teams_agg ON teams_agg._annotation = a._id - JOIN webknossos.datasets_ d ON d._id = a._dataset - JOIN webknossos.organizations_ AS o ON o._id = d._organization - JOIN webknossos.annotation_layers AS al ON al._annotation = a._id - WHERE $stateQuery AND $accessQuery AND $userQuery AND $typQuery - GROUP BY - a._id, a.name, a.description, a._user, a.othersmayedit, a.modified, - a.tags, a.state, a.islockedbyowner, a.typ, a.visibility, a.tracingtime, - u.firstname, u.lastname, - teams_agg.team_ids, teams_agg.team_names, teams_agg.team_organization_ids, - d.name, o._id - ORDER BY a._id DESC - LIMIT $limit - OFFSET ${pageNumber * limit}""" + query = q""" + -- We need to separate the querying of the annotation with all its inner joins from the 1:n join to collect the shared teams + -- This is to prevent left-join fanout. + -- Note that only one of the left joins in it has 1:n, so they can happen together + -- The WITH is structured this way round to get in the LIMIT early and not fetch the shared teams of all annotations first. + WITH an AS( -- select annotations with the relevant properties first + SELECT + a._id, + a.name, + a.description, + a._user, + u.firstname, + u.lastname, + a.othersmayedit, + a.modified, + a.tags, + a.state, + a.isLockedByOwner, + d.name AS datasetName, + a.typ, + a.visibility, + a.tracingtime, + o._id AS organizationId, + ARRAY_REMOVE(ARRAY_AGG(al.tracingid), null) AS tracing_ids, + ARRAY_REMOVE(ARRAY_AGG(al.name), null) AS tracing_names, + ARRAY_REMOVE(ARRAY_AGG(al.typ :: varchar), null) AS tracing_typs, + ARRAY_REMOVE(ARRAY_AGG(al.statistics), null) AS annotation_layer_statistics + FROM webknossos.annotations_ AS a + JOIN webknossos.users_ u ON u._id = a._user + JOIN webknossos.datasets_ d ON d._id = a._dataset + JOIN webknossos.organizations_ AS o ON o._id = d._organization + JOIN webknossos.annotation_layers AS al ON al._annotation = a._id + WHERE $stateQuery AND $accessQuery AND $userQuery AND $typQuery + GROUP BY + a._id, a.name, a.description, a._user, a.othersmayedit, a.modified, + a.tags, a.state, a.islockedbyowner, a.typ, a.visibility, a.tracingtime, + u.firstname, u.lastname, + d.name, o._id + ORDER BY a._id DESC + LIMIT $limit + OFFSET ${pageNumber * limit} + ) + SELECT -- select now add the shared teams, and propagate everything to the output + an._id, + an.name, + an.description, + an._user, + an.firstname, + an.lastname, + an.othersmayedit, + ARRAY_REMOVE(ARRAY_AGG(t._id), null) AS team_ids, + ARRAY_REMOVE(ARRAY_AGG(t.name), null) AS team_names, + ARRAY_REMOVE(ARRAY_AGG(o._id), null) AS team_organization_ids, + an.modified, + an.tags, + an.state, + an.isLockedByOwner, + an.datasetName, + an.typ, + an.visibility, + an.tracingtime, + an.organizationId, + an.tracing_ids, + an.tracing_names, + an.tracing_typs, + an.annotation_layer_statistics + FROM an + LEFT JOIN webknossos.annotation_sharedteams ast ON ast._annotation = an._id + LEFT JOIN webknossos.teams_ t ON ast._team = t._id + LEFT JOIN webknossos.organizations_ o ON t._organization = o._id + GROUP BY + an._id, + an.name, + an.description, + an._user, + an.firstname, + an.lastname, + an.othersmayedit, + an.modified, + an.tags, + an.state, + an.isLockedByOwner, + an.datasetName, + an.typ, + an.visibility, + an.tracingtime, + an.organizationId, + an.tracing_ids, + an.tracing_names, + an.tracing_typs, + an.annotation_layer_statistics + ORDER BY an._id DESC + """ rows <- run(query.as[AnnotationCompactInfo]) } yield rows.toList From bdb1277c900a20539904e78eee86f076e9ea8670 Mon Sep 17 00:00:00 2001 From: Florian M Date: Wed, 9 Apr 2025 14:08:58 +0200 Subject: [PATCH 7/8] Fix concurrency bug in skeleton saving (#8513) * Fix concurrency bug in skeleton saving * changelog --- CHANGELOG.unreleased.md | 1 + .../tracingstore/tracings/skeleton/SkeletonTracingService.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 01757d4a865..5b4bd77112d 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -16,6 +16,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released ### Fixed - Fixed a bug where the annotation list would sometimes load very long if you have many annotations. [#8498](https://github.com/scalableminds/webknossos/pull/8498) +- Fixed a bug where sometimes large skeletons were not saved correctly, making them inaccessible on the next load. [#8513](https://github.com/scalableminds/webknossos/pull/8513) ### Removed diff --git a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala index dcd3045fde1..4ff2eed084c 100644 --- a/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala +++ b/webknossos-tracingstore/app/com/scalableminds/webknossos/tracingstore/tracings/skeleton/SkeletonTracingService.scala @@ -43,7 +43,7 @@ class SkeletonTracingService @Inject()( for { _ <- Fox.serialCombined(treeIdsToFlush) { treeId => for { - treeBody <- extractTreeBody(tracing, treeId) + treeBody <- extractTreeBody(tracing, treeId).toFox _ <- tracingDataStore.skeletonTreeBodies.put(f"$tracingId/$treeId", version, treeBody) } yield () } From 22adf0111953281f6bcf4e9770326fe0eefa5128 Mon Sep 17 00:00:00 2001 From: Philipp Otto Date: Thu, 10 Apr 2025 15:07:03 +0200 Subject: [PATCH 8/8] update changelog and migration guide for release 25.03.1 --- CHANGELOG.released.md | 7 +++++++ CHANGELOG.unreleased.md | 4 +--- MIGRATIONS.released.md | 6 ++++++ MIGRATIONS.unreleased.md | 2 +- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.released.md b/CHANGELOG.released.md index 2ae3cd19066..bd5f3d710fa 100644 --- a/CHANGELOG.released.md +++ b/CHANGELOG.released.md @@ -7,6 +7,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`. For upgrade instructions, please check the [migration guide](MIGRATIONS.released.md). +## [25.03.1](https://github.com/scalableminds/webknossos/releases/tag/25.03.1) - 2025-04-10 +[Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...25.03.1) + +### Fixed +- Fixed a bug where the annotation list would sometimes load very long if you have many annotations. [#8498](https://github.com/scalableminds/webknossos/pull/8498) +- Fixed a bug where sometimes large skeletons were not saved correctly, making them inaccessible on the next load. [#8513](https://github.com/scalableminds/webknossos/pull/8513) + ## [25.03.0](https://github.com/scalableminds/webknossos/releases/tag/25.03.0) - 2025-04-01 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...25.03.0) diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 5b4bd77112d..fceb8c059d3 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -8,15 +8,13 @@ and this project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MIC For upgrade instructions, please check the [migration guide](MIGRATIONS.released.md). ## Unreleased -[Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...HEAD) +[Commits](https://github.com/scalableminds/webknossos/compare/25.03.1...HEAD) ### Added ### Changed ### Fixed -- Fixed a bug where the annotation list would sometimes load very long if you have many annotations. [#8498](https://github.com/scalableminds/webknossos/pull/8498) -- Fixed a bug where sometimes large skeletons were not saved correctly, making them inaccessible on the next load. [#8513](https://github.com/scalableminds/webknossos/pull/8513) ### Removed diff --git a/MIGRATIONS.released.md b/MIGRATIONS.released.md index 034ac6f1a6b..0aafa52751a 100644 --- a/MIGRATIONS.released.md +++ b/MIGRATIONS.released.md @@ -6,6 +6,12 @@ See `MIGRATIONS.unreleased.md` for the changes which are not yet part of an offi This project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`. User-facing changes are documented in the [changelog](CHANGELOG.released.md). +## [25.03.1](https://github.com/scalableminds/webknossos/releases/tag/25.03.1) - 2025-04-10 +[Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...25.03.1) + +### Postgres Evolutions: +None. + ## [25.03.0](https://github.com/scalableminds/webknossos/releases/tag/25.03.0) - 2025-04-01 [Commits](https://github.com/scalableminds/webknossos/compare/25.02.1...25.03.0) diff --git a/MIGRATIONS.unreleased.md b/MIGRATIONS.unreleased.md index e41a2fca08e..0a1e9a25048 100644 --- a/MIGRATIONS.unreleased.md +++ b/MIGRATIONS.unreleased.md @@ -6,6 +6,6 @@ This project adheres to [Calendar Versioning](http://calver.org/) `0Y.0M.MICRO`. User-facing changes are documented in the [changelog](CHANGELOG.released.md). ## Unreleased -[Commits](https://github.com/scalableminds/webknossos/compare/25.03.0...HEAD) +[Commits](https://github.com/scalableminds/webknossos/compare/25.03.1...HEAD) ### Postgres Evolutions: